擷取Windows系統的進程運行資訊
作者:胡小文
自從出現windows系統以來,各種各樣的軟體層出不窮。購買或者免費下載以後,輕輕點擊Install或者Setup以後,稍作些許簡單配置就完成安裝的整個過程。顯然這給使用者帶來了許多方便,但對使用者來說這些軟體似乎是個"黑匣子",展現在使用者面前的僅僅是華麗的外表和簡潔的操作,至於軟體運行過程中調用了哪些檔案和模組檔案就無法知道了。或許真正希望瞭解這些的使用者並不是很多,甚至對某些使用者來說是不必要的,但是對於一個系統安全性系統管理員來說這是一件很重要的事情。本文重點闡述如何擷取系統的所有進程以及進程運行過程中所調用的模組檔案(象EXE、DLL檔案),實際是對Windows系統所提供的工作管理員進一步完善,開發工具採用Delphi語言。
一、函數介紹
在Windows系統中動態連結程式庫kernel32.dll提供了擷取和處理系統進程的許多介面函數,Delphi語言把這些函數介面封裝到Tlhelp32.pas中,供Delphi使用者開發程序呼叫。要詳細瞭解相關知識可以查閱Tlhelp32.pas原檔案和Windows SDK提供的協助檔案。其中同本文涉及的介面函數主要有CreateToolhelp32Snapshot、process32first、process32next、module32first、module32next五個函數以及TMODULEENTRY32、TPROCESSENTRY32兩個資料結構。
1、CreateToolhelp32Snapshot
該函數是要實現上述目的最核心的一個函數,它可以擷取系統運行進程(Process)列表、線程(Thread)列表和指定運行進程的堆(Heap)列表、調用模組(Module)列表。如果函數運行成功將返回一個非零"Snapshot"控制代碼,通過該控制代碼調用相關WinAPI函數就可以實現上述目的,其函數格式為:
HANDLE WINAPI CreateToolhelp32Snapshot(DWORD dwFlags,
DWORD th32ProcessID);
參數說明:
dwFlags參數:對函數建立的"Snapshot"所包含的清單類型,可選項包括:
TH32CS_SNAPHEAPLIST:所建立的Snapshot包含堆列表
TH32CS_SNAPMODULE :所建立的Snapshot包含調用模組列表
TH32CS_SNAPTHREAD :所建立的Snapshot包含線程列表;
TH32CS_SNAPPROCESS :所建立的Snapshot包含進程列表;
TH32CS_SNAPALL :所建立的Snapshot包含上述所有列表;
th32ProcessID參數:進程控制代碼參數,可以為零表示當前進程,該參數只對dwFlags包含TH32CS_SNAPMODULE、TH32CS_SNAPHEAPLIST可選項時起作用。當dwFlags為TH32CS_SNAPPROCESS,th32ProcessID為零時函數得到系統的所有進程列表。
2、Process32First、Process32Next、Module32First、Module32Next
這四個函數都是對"Snapshot"所包含的列表進行息擷取,根據函數字面的英文意義,不難理解各函數的含義和區別,四個函數的格式分別為:
BOOL WINAPI Process32First(HANDLE hSnapshot,
LPPROCESSENTRY32 lppe);
BOOL WINAPI Process32Next(HANDLE hSnapshot,
LPPROCESSENTRY32 lppe);
BOOL WINAPI Module32First(HANDLE hSnapshot,
LPMODULEENTRY32 lpme);
BOOL WINAPI Module32Next(HANDLE hSnapshot,
LPMODULEENTRY32 lpme);
3、TMODULEENTRY32、TPROCESSENTRY32
這兩個資料結構中TPROCESSENTRY32是在Process32First、Process32Next兩個函數所用到的資料結構,TMODULEENTRY32是在Module32First、Module32Next所用到的資料結構,兩個資料結構分別如下:
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID; //進程控制代碼
DWORD th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
char szExeFile[MAX_PATH];
} PROCESSENTRY32;
typedef struct tagMODULEENTRY32 {
DWORD dwSize;
DWORD th32ModuleID;
DWORD th32ProcessID;
DWORD GlblcntUsage;
DWORD ProccntUsage;
BYTE * modBaseAddr;
DWORD modBaseSize;
HMODULE hModule;
char szModule[MAX_MODULE_NAME32 + 1];
char szExePath[MAX_PATH]; //調用模組的含路徑檔案名稱
} MODULEENTRY32;
在使用上面兩個資料結構要特彆強調一點,那就是函數使用這兩個資料結構的變數時要先設定dwSize的值,分別用sizeof(TPROCESSENTRY32)和sizeof(TMODULEENTRY32)。
由於篇幅有限以上所提到的函數和資料結構可以查看Windows SDK協助檔案擷取更詳細的資訊。
二、實現原理
要實現獲得系統的所有運行進程和每個運行進程所調用模組的資訊,實際上只要使用兩重迴圈,外迴圈擷取系統的所有進程列表,內迴圈擷取每個進程所調用模組列表。用以下四組API調用實現:
1、建立系統的所有進程列表
ProcessList:= CreateToolhelp32Snapshot
(TH32CS_SNAPPROCESS,0);
2、提取進程清單項目資訊儲存在TPROCESSENTRY32 pe中
Process32First(ProcessList,pe)
Process32Next(ProcessList,pe)
3、建立指定進程所有調用模組列表
ModuleList:=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
pe.processID);
4、提取調用模組清單項目資訊儲存在TMODULEENTRY32 pm中
Module32First(ModuleList,pm);
Module32Next(ModuleList,pm);
三、核心源碼
有了以上知識以後,我們就可以很容易地實現擷取系統的所有進程以及各進程運行過程所調用的模組檔案了。具體步驟如下:
1、運行Delphi,進入Delphi的IDE開發環境;
2、建立Application;
3、在預設的Form1中添加Treeview和Button控制項;
4、設定Button的OnClick屬性為ProcessEnum;
5、把以下代碼輸入ProcessEnum過程中;
6、編譯運行;
核心代碼如下所示:
uses Tlhelp32;
procedure TForm1.ProcessEnum(Sender: TObject);
var
ProcessList :Thandle;
pe :TPROCESSENTRY32;
node :TTreenode;
processnumber :integer;
procedure ModuleEnum(processid:Dword);
var
ModuleList :Thandle;
pm :TMODULEENTRY32;
begin
ModuleList:=CreateToolhelp32Snapshot
(TH32CS_SNAPMODULE,processID);
pm.dwSize:=sizeof(TMODULEENTRY32);
if module32first(ModuleList,pm)
then begin
treeview1.Items.addchild(node,pm.szexepath);
while module32next(ModuleList,pm) do
treeview1.items.addchild(node,pm.szexepath);
end;
closehandle(ModuleList);
end; // ModuleEnum
begin // ProcessEnum
processnumber:=0;
treeview1.Items.Clear;
ProcessList:=CreateToolhelp32Snapshot
(TH32CS_SNAPPROCESS,0);
pe.dwSize:=sizeof(TPROCESSENTRY32);
if process32first(ProcessList,pe) then
begin
node:=treeview1.Items.Add(nil,pe.szexefile);
ModuleEnum(pe.th32ProcessID);
inc(processnumber);
while process32next(ProcessList,pe) do
begin
node:=treeview1.Items.Add(nil,pe.szexefile);
ModuleEnum(pe.th32ProcessID);
inc(processnumber);
end;
end;
edit1.text:=’系統進程:’+inttostr(processnumber);
closehandle(ProcessList);
end;