| Windows2000中有个工具taskmgr.exe就可以比较详细的查看当前系统进程信息,但是那是Windows GUI程序,有时候是不是觉得命令行下的东西更方便呢?其实已经有不少命令行下的枚举系统进程的工具了,M$的Resource Kit中好象也有,但去了解他们是怎么实现的,自己动手做出来,是不是更有意思呢:)
进程通常被定义为一个正在运行的程序的实例,它由两部分组成:
<1>操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方。
<2>地址空间。它包含所有可执行模块或DLL模块的代码和数据。它还包含动态内存分配的空间,如线程的堆栈和堆分配空间。
枚举系统进程的实现方法大概有四种,其中有一种可以用来枚举远程NT系统的进程,前提是有远程系统的管理员权限。
<<第一部分:调用PSAPI函数枚举系统进程>>
M$的Windows NT开发小组开发了自己Process Status函数,包含在PSAPI.DLL文件中,这些函数只能在高于NT4.0以后的版本中使用。PSAPI一共有14个函数[实际PSAPI.DLL输出函数有19个,但其中有5个函数有两个版本,分别是ANSI和Unicode版本],通过调用这些函数,我们可以很方便的取得系统进程的所有信息,例如进程名、进程ID、父进程ID、进程优先级、映射到进程空间的模块列表等等。为了方便起见,以下的例子程序只获取进程的名字和ID。
简单的程序如下:
/*************************************************************************
Module:ps.c
说明:调用PSAPI函数枚举系统进程名和ID,Only for NT/2000
*************************************************************************/ #include "psapi.h" #pragma comment(lib,"psapi.lib") void PrintProcessNameAndID( DWORD processID ) { char szProcessName[MAX_PATH] = "unknown"; //取得进程的句柄 HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID ); //取得进程名称 if ( hProcess ) { HMODULE hMod; DWORD cbNeeded; if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) ) GetModuleBaseName( hProcess, hMod, szProcessName, sizeof(szProcessName) ); } //回显进程名称和ID printf( "\n%-20s%-20d", szProcessName, processID ); CloseHandle( hProcess ); }
void main( ) { DWORD aProcesses[1024], cbNeeded, cProcesses; unsigned int i; //枚举系统进程ID列表 if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) ) return; // Calculate how many process identifiers were returned. //计算进程数量 cProcesses = cbNeeded / sizeof(DWORD); // 输出每个进程的名称和ID for ( i = 0; i < cProcesses; i ) PrintProcessNameAndID( aProcesses[i] );
return; }
<<第二部分:调用ToolHelp API枚举本地系统进程>> 在第一部分提到的PSAPI函数只能枚举NT系统的进程,在Windows9x环境下我们可以通过调用ToolHelp API函数来达到枚举系统进程的目的。M$的Windows NT开发小组因为不喜欢ToolHelp函数,所以没有将这些函数添加给Windows NT,所以他们开发了自己的Process Status函数,就是第一部分提到的PSAPI了。但是后来M$已经将ToolHelp函数添加给了Windows 2000。ToolHelp共有12个函数,通过调用这些函数可以方面的取得本地系统进程的详细信息,以下这个简单的例子只调用了三个函数,获取我们所需要系统进程名字和进程ID。程序如下: /**********************************************************************
Module:ps.c
说明:调用ToolHelp函数枚举本地系统进程名和ID,Only for 9x/2000
**********************************************************************/ int main() { HANDLE hProcessSnap = NULL; PROCESSENTRY32 pe32 = {0}; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == (HANDLE)-1) { printf("\nCreateToolhelp32Snapshot() failed:%d",GetLastError()); return 1; }
pe32.dwSize = sizeof(PROCESSENTRY32); printf("\nProcessName ProcessID"); if (Process32First(hProcessSnap, &pe32)) { do { printf("\n%-20s%d",pe32.szExeFile,pe32.th32ProcessID); }while (Process32Next(hProcessSnap, &pe32)); } else { printf("\nProcess32Firstt() failed:%d",GetLastError()); }
CloseHandle (hProcessSnap);
return 0; }
<<第三部分:调用NTDLL.DLL中未公开API枚举本地系统进程>>
第一部分和第二部分说的是调用MS公开的API来枚举系统进程,在NTDLL.DLL中其实有一个未公开API,也可以用来枚举系统进程。此方法是从别处看来的,我可没这本事自己发现哦,出处记不清楚了,好像是pwdump2 中的源代码中的一部分吧。 OK!那个未公开API就是NtQuerySystemInformation,使用方法如下: //////////////////////////////////////////////////////////////////////////////// typedef unsigned long NTSTATUS; typedef unsigned short USHORT; typedef unsigned long ULONG; typedef unsigned long DWORD; typedef long LONG; typedef __int64 LONGLONG; typedef struct { USHORT Length; USHORT MaxLen; USHORT *Buffer; } UNICODE_STRING;
struct process_info { ULONG NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ProcessName; ULONG BasePriority; ULONG ProcessId; };
typedef NTSTATUS (__stdcall *NtQuerySystemInformation1)( IN ULONG SysInfoClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG RetLen );
int main() { HINSTANCE hNtDll; NtQuerySystemInformation1 NtQuerySystemInformation; NTSTATUS rc; ULONG ulNeed = 0; void *buf = NULL; size_t len = 0; struct process_info *p ; int done;
hNtDll = LoadLibrary ("NTDLL"); if (!hNtDll) return 0; NtQuerySystemInformation = (NtQuerySystemInformation1)GetProcAddress (hNtDll, "NtQuerySystemInformation"); if (!NtQuerySystemInformation) return 0; do { len = 0×1000; buf = realloc (buf, len); if (!buf) return 0; rc = NtQuerySystemInformation (5, buf, len, &ulNeed); } while (rc == 0xc0000004); // STATUS_INFO_LEN_MISMATCH
if (rc <0) { free (buf); return 0; }
printf("\nProcessName ProcessID"); p = (struct process_info *)buf; done = 0;
/> while (!done) { if ((p->ProcessName.Buffer != 0)) { printf("\n%-20S%d",p->ProcessName.Buffer,p->ProcessId); } done = p->NextEntryDelta == 0; p = (struct process_info *)(((char *)p) p->NextEntryDelta); } free (buf); FreeLibrary (hNtDll); return 0; }
<<第四部分:从PDH中取得本地/远程系统进程信息>>
前面说的三种方法都只能枚举本地的系统进程,如何枚举远程系统的进程呢?目前我只知道从PDH中取得进程信息。
OK!我先简单的说说PDH是什么东西,hoho~难的偶也不会。PDH是英文Performance Data Helper的缩写,Windows NT一直在更新这个称为Performance Data的数据库,这个数据库包含了大量的信息,例如CPU使用率,内存使用率,系统进程信息等等一大堆有用的信息,可以通过注册表函数来访问。注意哦,Windows 9x中并没有配置这个数据库。但是,这个数据库中的信息布局很复杂,很多人并不愿意使用它,包括我。而且刚开始的时候,它也没有自己特定的函数,只能通过现有的注册表函数来操作。后来,为了使该数据库的使用变得容易,MS开发了一组Performance Data Helper函数,包含在PDH.DLL文件中。
Windows 2000默认是允许远程注册表操作的,所以我们就可以通过连接远程系统的注册表,从它的PDH中取得我们所需要的系统进程信息了,当然这需要远程系统的Admin权限。
OK!我们下面所举的例子是直接利用注册表函数来从本地/远程系统的PDH数据库中取得我们所需要的数据的,我们并没有利用PDH API。
程序代码如下:
/**************************************************************************
Module:ps.c
Author:mikeblas@nwlink.com
Modify:ey4s
Http://www.ey4s.org
Date:2001/6/23
**************************************************************************/ #define INITIAL_SIZE 51200 #define EXTEND_SIZE 12800 #define REGKEY_PERF "software\\microsoft\\windows nt\\currentversion\\perflib" #define REGSUBKEY_COUNTERS "Counters" #define PROCESS_COUNTER "process" #define PROCESSID_COUNTER "id process" #define UNKNOWN_TASK "unknown" #define MaxProcessNum 52//最大进程数量 #pragma comment(lib,"mpr.lib")
typedef struct ProcessInfo { char ProcessName[128]; DWORD dwProcessID; }pi;
void banner(); int ConnIPC(char *,char *,char *); DWORD GetProcessInfo(pi *,char *,char *,char *);
int main(int argc,char **argv) { int i,iRet; pi TaskList[MaxProcessNum]; banner(); if(argc==1) { iRet=GetProcessInfo(TaskList,NULL,NULL,NULL); printf("\nProcess Info for [LOCAL]:"); } else if(argc==4) { iRet=GetProcessInfo(TaskList,argv[1],argv[2],argv[3]); printf("\nProcess Info for [%s]:",argv[1]); }
else
{
printf("\nUsage:%s ",argv[0]);
return 1;
}
if(iRet>0)
for(i=0,printf("\nProcessName ProcessID");
i printf("\n%-20s %d",TaskList[i].ProcessName,TaskList[i].dwProcessID),i );
return 0;
}
DWORD GetProcessInfo(pi *ProList,char *ip,char *user,char *pass)
{
DWORD rc,dwType,dwSize,i,dwProcessIdTitle,dwProcessIdCounter,dwRet=-1;
HKEY hKeyNames;
LPSTR buf = NULL,p,p2;
CHAR szSubKey[1024],szProcessName[MAX_PATH];
PPERF_DATA_BLOCK pPerf;
PPERF_OBJECT_TYPE pObj;
PPERF_INSTANCE_DEFINITION pInst;
PPERF_COUNTER_BLOCK pCounter;
PPERF_COUNTER_DEFINITION pCounterDef;
HKEY ghPerfKey =NULL, // get perf data from this key
ghMachineKey = NULL; // get title index from this key
BOOL bRemote=FALSE;
// Look for the list of counters. Always use the neutral // English version, regardless of the local language. We // are looking for some particular keys, and we are always // going to do our looking in English. We are not going // to show the user the counter names, so there is no need // to go find the corresponding name in the local language.
__try { if((ip)&&(user)&&(pass)) { if(ConnIPC(ip,user,pass)!=0) { printf("\nConnect to %s failed.",ip); __leave; } else bRemote=TRUE; } //连接本地or远程注册表 if(RegConnectRegistry(ip,HKEY_PERFORMANCE_DATA, &ghPerfKey)!=ERROR_SUCCESS) { printf("\nRegConnectRegistry() 1 failed:%d",GetLastError()); __leave; } if(RegConnectRegistry(ip,HKEY_LOCAL_MACHINE,&ghMachineKey)!=ERROR_SUCCESS) { printf("\nRegConnectRegistry() 2 failed:%d",GetLastError()); __leave; }
sprintf( szSubKey, "%s\\x", REGKEY_PERF,MAKELANGID( LANG_ENGLISH, SUBLANG_NEUTRAL)); if(RegOpenKeyEx(ghMachineKey,szSubKey,0,KEY_READ,&hKeyNames)!=ERROR_SUCCESS) __leave;
// 从counter names取得需要的缓冲区大小 if(RegQueryValueEx(hKeyNames,REGSUBKEY_COUNTERS,NULL,&dwType,NULL,&dwSize)!= ERROR_SUCCESS) __leave; //分配内存 buf = (LPSTR) malloc( dwSize ); if (buf == NULL) __leave; memset( buf, 0, dwSize ); // read the counter names from the registry if(RegQueryValueEx(ghPerfKey,REGSUBKEY_COUNTERS,NULL,&dwType,(LPBYTE) buf,&dwSize)!= ERROR_SUCCESS) __leave; // now loop thru the counter names looking for the following counters: // 1. "Process" process name // 2. "ID Process" process id
// the buffer contains multiple null terminated strings and then // finally null terminated at the end. the strings are in pairs of // counter number and counter name.
p = buf; while (*p) { if (p>buf) for( p2=p-2; isdigit(*p2); p2–) ; if (stricmp(p, PROCESS_COUNTER) == 0) { // look backwards for the counter number
for( p2=p-2; isdigit(*p2); p2–) ; strcpy( szSubKey, p2 1 ); } else if (stricmp(p, PROCESSID_COUNTER) == 0) { // look backwards for the counter number for( p2=p-2; isdigit(*p2); p2–) ; dwProcessIdTitle = atol( p2 1 ); } // next string p = (strlen(p) 1); } // free the counter names buffer free( buf ); // allocate the initial buffer for the performance data dwSize = INITIAL_SIZE; buf = (LPSTR) malloc( dwSize );
while (TRUE) { if (buf == NULL) __leave; memset( buf, 0, dwSize ); rc=RegQueryValueEx(ghPerfKey,szSubKey,NULL,&dwType,(LPBYTE) buf,&dwSize); pPerf = (PPERF_DATA_BLOCK) buf; // check for success and valid perf data block signature if ((rc == ERROR_SUCCESS) && (dwSize > 0) && (pPerf)->Signature[0] == (WCHAR)’P’ && (pPerf)->Signature[1] == (WCHAR)’E’ && (pPerf)->Signature[2] == (WCHAR)’R’ && (pPerf)->Signature[3] == (WCHAR)’F’ ) break; // if buffer is not big enough, reallocate and try again if (rc == ERROR_MORE_DATA) { dwSize = EXTEND_SIZE; buf = (LPSTR) realloc( buf, dwSize ); } else __leave; } // set the perf_object_type pointer pObj = (PPERF_OBJECT_TYPE) ((DWORD)pPerf pPerf->HeaderLength); //loop thru the performance counter definition records looking //for the process id counter and then save its offset
pCounterDef = (PPERF_COUNTER_DEFINITION) ((DWORD)pObj pObj->HeaderLength);
for (i=0; i<(DWORD)pObj->NumCounters; i ) { if (pCounterDef->CounterNameTitleIndex == dwProcessIdTitle) { dwProcessIdCounter = pCounterDef->CounterOffset; break; } pCounterDef ; }
pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pObj pObj->DefinitionLength); // loop thru the performance instance data extracting each process name // and process id for (i=0; i < (DWORD)pObj->NumInstances-1 && i { // pointer to the process name p = (LPSTR) ((DWORD)pInst pInst->NameOffset); // convert it to ascii rc = WideCharToMultiByte( CP_ACP,0,(LPCWSTR)p,-1,szProcessName,sizeof(szProcessName),NULL,NULL);
// if we cant convert the string then use a default value if (!rc) strcpy( ProList[i].ProcessName, UNKNOWN_TASK ); else strncpy(ProList[i].ProcessName, szProcessName,sizeof(ProList[i].ProcessName)-1); // get the process id pCounter = (PPERF_COUNTER_BLOCK) ((DWORD)pInst pInst->ByteLength); ProList[i].dwProcessID = *((LPDWORD) ((DWORD)pCounter dwProcessIdCounter)); // next process
pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pCounter pCounter->ByteLength);
}
dwRet=i;
}//end of try
__finally
{
if (buf) free( buf );
RegCloseKey( hKeyNames );
RegCloseKey( HKEY_PERFORMANCE_DATA );
if(bRemote) { char tmp[52],tmp2[96];
strncpy(tmp,ip,sizeof(tmp)-1);
wsprintf(tmp2,"\\\\%s\\ipc$",tmp);
WNetCancelConnection2(tmp2,CONNECT_UPDATE_PROFILE,TRUE); }
} return dwRet; }
//////////////////////////////////////////////////////////////////////////////// int ConnIPC(char *RemoteName,char *User,char *Pass)
{ NETRESOURCE nr; char RN[50]="\\\\";
strncat(RN,RemoteName,sizeof(RN)-11); strcat(RN,"\\ipc$"); nr.dwType=RESOURCETYPE_ANY; nr.lpLocalName=NULL; nr.lpRemoteName=RN; nr.lpProvider=NULL;
if(WNetAddConnection2(&nr,Pass,User,FALSE)==NO_ERROR) return 0; else return 1; }
////////////////////////////////////////////////////////////////////////////////
void banner()
{ printf("\nPsList ==>Local and Remote process list" "\nPower by ey4s" "\nhttp://www.ey4s.org" "\n2001/6/22\n"); }
程序在Windows2000、VC 6.0环境下编译,运行良好。注意哦,远程机器要允许IPC连接和远程操作注册表才可以哦,并且需要Admin权限.
CSDN VC编程经验总结
|
最新评论