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 longULONG;
typedef unsigned longDWORD;
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:mikeblas@nwlink.com
Modify:ey4s
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&n