作者: eyas
版權所有:http://www.ey4s.org
中文版本出處:http://www.gamehigh.net/
轉載請與作者聯系
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
#include
#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
**********************************************************************/
#include
#include
#include
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,使用方法如下:
////////////////////////////////////////////////////////////////////////////////
#include
#include
#include
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 += 0x1000;
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:[email protected]
Modify:ey4s<[email protected]>
Http://www.ey4s.org
Date:2001/6/23
**************************************************************************/
#include
#include
#include
#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
return 1;
}
if(iRet>0)
for(i=0,printf("\nProcessName ProcessID");
i<IRET;>
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\\%03x", 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<MAXPROCESSNUM;
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<[email protected]>"
"\nhttp://www.ey4s.org"
"\n2001/6/22\n");
}
////////////////////////////////////////////////////////////////////////////////
程序在Windows2000、VC++6.0環境下編譯,運行良好。注意哦,遠程機器要允許IPC連接和遠程操作注冊表才可以哦,並且需要Admin權限.