列舉 Windows 所有進程(ToolHelp)

引子

由於這陣子必須得做幾個小東西才行,估計着呢,是要做個 Windows 的任務管理器出來才行,

但是在功能上呢,又必須得比 Windows 任務管理器強大一點,

說實在的,在 Windows 7 裏面的 Windows 任務管理器在功能上已經很強大了,

而我這裏說的強大一點呢,並不是說要在功能上比 Windows 7 的任務管理器還有多一些,

而是在仿照 Windows 任務管理器的同時實現一些 Windows 任務管理器還沒有實現的功能,

比如在內存的管理上,Windows 任務管理器並沒有針對於每一個進程均有對應的內存顯示,

所以在這裏就可以加上一個功能,比如可以實現,當用戶選定一個進程後,

我可以採用 GDI 繪製出該進程所使用的內存曲線圖,或者該進程內部的內存結構分佈圖等等,

當然上面的功能可能只是要實現的一個部分而已,至於其他的功能還有待分析才能夠確定,

因爲本來要做的這個東西也並不是工作上所需,只是個人所要求而已,所以什麼需求之類的都沒有,

只是想到什麼做什麼而已,呵呵,所謂一切隨意 ~~~

因爲要做的是 Windows 的任務管理器,而且還有一些特別的處理,所以在做法上自然有些特別,

比如有很多東西都不能夠直接在用戶層上獲取到,而必須深入到內核層才能夠獲取到這些信息,

比如內存的具體分配,甚至是關鍵性進程的終止等等,這些都必須在內核層才能實現,

而這很明顯得通過驅動程序來實現,而在用戶層的話自然就一個界面而已,對於這個界面呢,可以隨意採用什麼做,

Java 我沒試過怎麼跟驅動程序通信,但是 C# VC 我都還是試過的,所以敲定在 VC C# 中選一個,

對於 C# 呢,說實在的,在 GDI+ 上繪圖,我還是覺得很方便的,至少比起 GDI 是方便很多,

唯一的遺憾就是奶奶的內存耗得有點過分,如果我的 Timer 時間間隔比較小的話,總是感覺它會受不了,

GDI 的話貌似會好點,所以敲定使用 VC 來做用戶界面,

所以對於這個任務管理器的實現呢,基本思路還是比較明確的,

首先通過在應用程序(VC 做的用戶界面程序)中設置一個定時器,

而後在每個時間間隔裏發送命令給驅動程序(WDM),

然後通過驅動程序執行命令後再和應用程序通信,這樣就可以將得到的數據傳遞給用戶程序,

然後用戶程序就可以利用最新獲取的數據來更新用戶界面了。

這裏的發送的命令呢,基本上就兩種,

一種爲查詢命令,該命令用來實現查詢功能,比如指定進程的內存消耗等,

然後還有一個呢就是執行命令,比如終止一個進程,

其實上面的這個查詢內存以及終止進程功能在用戶模式下也是可以實現的,

但是相對於在內核模式下而言,用戶模式下所做的操作是非常有限的,

而在用戶層的話,很明顯,是需要顯示所有的進程信息的,

並且在和驅動層進行通信的時候,需要考慮傳遞用戶選定的進程的句柄,

所以在用戶層是需要保存有所有的進程的句柄的,爲了簡單起見,

這裏可以不通過驅動來實現進程信息的獲取,而是通過其他的方式來獲取。

本篇博文的一個介紹點就是獲取到系統當前狀態下所有的進程等信息

(這些信息包括進程信息,進程使用的模塊信息,進程所擁有的線程信息)。

ToolHelp  API  概述              

ToolHelp  API  看起來貌似蠻多東西呢,其實呢,也就那麼幾個函數而已,

呵呵,因爲不多,所以我等下全部把它們貼出來咯,

首先呢,ToolHelp 這些 API 所在的文件路徑爲:

C:\Program Files\Microsoft SDKs\Windows\v7.0A\include\tlhelp32.h

當我們在使用的時候,需要包括頭文件 <tlhelp32.h>,這樣才能夠引用到 ToolHelp API

ToolHelp API 中分爲這麼幾個部分(參考下面的截圖即可),

然後就是來一塊一塊的介紹了。

image

快照 (Snapshot)解析

至於這個快照嘛,呵呵,解釋起來還算是有點麻煩,

但是可以這樣理解,快照就是給當前的系統所處的狀態拍了張照片,

那麼自然,這張照片裏面就存放了當前系統在拍照那會兒所處的狀態,這就是快照了。

所以如果要訪問系統的當前狀態,只需要給它拍一張快照就可以進行訪問了。

拍快照的實現函數(CreateToolhelp32Snapshot):

// The th32ProcessID argument is only used 
// if TH32CS_SNAPHEAPLIST or TH32CS_SNAPMODULE is specified. 
// th32ProcessID == 0 means the current process.
 
// NOTE that all of the snapshots are global except for the heap and module
//      lists which are process specific. To enumerate the heap or module
//      state for all WIN32 processes call with TH32CS_SNAPALL and the
//      current process. Then for each process in the TH32CS_SNAPPROCESS
//      list that isn't the current process, do a call with just
//      TH32CS_SNAPHEAPLIST and/or TH32CS_SNAPMODULE.
HANDLE WINAPI CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID);
 
// dwFlags
#define TH32CS_SNAPHEAPLIST 0x00000001
#define TH32CS_SNAPPROCESS  0x00000002
#define TH32CS_SNAPTHREAD   0x00000004
#define TH32CS_SNAPMODULE   0x00000008
#define TH32CS_SNAPMODULE32 0x00000010
#define TH32CS_INHERIT      0x80000000
#define TH32CS_SNAPALL      (TH32CS_SNAPHEAPLIST | 
                             TH32CS_SNAPPROCESS | 
                             TH32CS_SNAPTHREAD | 
                             TH32CS_SNAPMODULE)

拍快照那是很顯然的,但是如果我們要給系統當前狀態拍個全景的話,那麼系統當前狀態的信息就多呢,

而如果我們只要進程的信息,而你拍了個全景,裏面有線程信息,有模塊信息,有堆信息,

這不明擺着是浪費嘛,所以自然得有個快照類型,而這個快照類型就是由參數 dwFlags 來決定了。

這個參數的取值也在上面列出來了,都還比較好理解,這裏就不做解釋了,

反正在這裏只需要記住一條,只要我需要系統的狀態信息,我就給它拍快照,

需要進程信息就拍進程快照,需要線程信息就拍線程快照就可以了。

並且在拍完快照不需要使用快照了之後需要關閉快照句柄,

MSDN 裏頭說是在 TlHelp32.h 中有 CloseToolhelp32Snapshot 函數,

但是筆者在這個文件中沒有發現這個函數,所以只能調用 CloseHandle 來實現關閉了,

而且 MSDN 裏面貌似也是這樣使用的,所以就不管他了。

還有注意一點就是前面的英文註釋中有一條,即如果 th32ProcessID 傳入 0 時代表當前進程。

Heap Walking 解析

typedef struct tagHEAPLIST32
{
    SIZE_T            dwSize;
    DWORD            th32ProcessID;   // owning process
    ULONG_PTR        th32HeapID;      // heap (in owning process's context!)
    DWORD            dwFlags;
 
} HEAPLIST32;
 
typedef HEAPLIST32 *  PHEAPLIST32;
typedef HEAPLIST32 *  LPHEAPLIST32;
 
// dwFlags
#define HF32_DEFAULT      1  // process's default heap
#define HF32_SHARED       2  // is shared heap

        

上面的這個 HEAPLIST32 結構體描述了指定的進程所使用的堆鏈表的進入點,

下面就來解釋 HEAPLIST32 中的結構成員的含義了。

  1. dwSize:                            HEAPLIST32 結構體的大小, 在使用 Heap32ListFirst 之前需要將這個成員設置好 。
  2. th32ProcessID:              這個參數代表一個進程的 ID,即進程標識符 。
  3. th32HeapID:                  這個參數代表堆標識符 。
  4. dwFlags:                         取值參考上面的代碼 。

在使用當中呢,其實上面的這個結構體只需要填充第一個字段 dwSize 即可,

其他字段都是通過調用函數 Heap32ListFirst 或者是 Heap32ListNext 來填充的。

typedef struct tagHEAPENTRY32
{
    SIZE_T            dwSize;
    HANDLE            hHandle;        // Handle of this heap block
    ULONG_PTR        dwAddress;        // Linear address of start of block
    SIZE_T            dwBlockSize;    // Size of block in bytes
    DWORD            dwFlags;
    DWORD            dwLockCount;
    DWORD            dwResvd;
    DWORD            th32ProcessID;    // owning process
    ULONG_PTR        th32HeapID;        // heap block is in
 
} HEAPENTRY32;
 
typedef HEAPENTRY32 *  PHEAPENTRY32;
typedef HEAPENTRY32 *  LPHEAPENTRY32;
 
// dwFlags
#define LF32_FREE     0x00000002
#define LF32_FIXED    0x00000001
#define LF32_MOVEABLE 0x00000004
//LF32_FIXED        The memory block has a fixed (unmovable) location.
//LF32_FREE         The memory block is not used.
//LF32_MOVEABLE     The memory block location can be moved.
                

下面就來解釋 HEAPENTRY32 中的結構成員的含義了。

  1. dwSize:                            HEAPENTRY32 結構體的大小, 在使用 Heap32First 之前需要將這個成員設置好 。
  2. hHandle:                         這個參數指向一個堆塊 。
  3. dwAddress:                    這個參數代表堆塊的線性起始地址 。
  4. dwBlockSize:                 當前這個堆塊的大小 。
  5. dwFlags:                         取值參考上面的代碼 。
  6. dwLockCount:              該參數已不再使用,不管它 。
  7. dwResvd:                       該參數也已不再使用 。
  8. th32ProcessID:             該參數即代表使用這個堆塊的進程 ID
  9. th32HeapID:                 當前這個堆塊的標識符。

在使用當中呢,其實上面的這個結構體只需要填充第一個字段 dwSize 即可,

其他字段都是通過調用函數 Heap32First 或者是 Heap3Next 來填充的。

然後我們就來看屬於 Heap Walking 這一塊的 API 了。

BOOL WINAPI Heap32ListFirst(HANDLE hSnapshot, LPHEAPLIST32 lphl);
 
BOOL WINAPI Heap32ListNext(HANDLE hSnapshot, LPHEAPLIST32 lphl);
BOOL WINAPI Heap32First(LPHEAPENTRY32 lphe, DWORD th32ProcessID, ULONG_PTR th32HeapID);
 
BOOL WINAPI Heap32Next(LPHEAPENTRY32 lphe);
 
BOOL WINAPI Toolhelp32ReadProcessMemory(
    DWORD   th32ProcessID,
    LPCVOID lpBaseAddress,
    LPVOID  lpBuffer,
    SIZE_T  cbRead,
    SIZE_T *lpNumberOfBytesRead
    );

             

Heap Walking 使用

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <iostream>
 
using namespace std;
 
//獲取指定進程下的堆信息 
BOOL ListProcessHeaps(DWORD dwOwnerPID);
 
int main()
{
    ListProcessHeaps(GetCurrentProcessId());
 
    cout<<endl<<endl;
 
    system("pause");
}
 
 
//獲取進程堆信息
BOOL ListProcessHeaps(DWORD dwOwnerPID)
{
    HEAPLIST32  hl;
    HANDLE      hHeapSnap = INVALID_HANDLE_VALUE;
    //創建指定進程下的堆快照 
    hHeapSnap = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, dwOwnerPID);
    if (hHeapSnap == INVALID_HANDLE_VALUE)
    {
        return false;
    }
 
    //填充結構成員 
    hl.dwSize = sizeof(HEAPLIST32);
    if(Heap32ListFirst(hHeapSnap, &hl))
    {
        do
        {
            //堆中的一個塊
            HEAPENTRY32        he;
 
            ZeroMemory(&he, sizeof(HEAPENTRY32));
 
            he.dwSize = sizeof(HEAPENTRY32);
 
            //遍歷當前進程,指定堆 ID 下的所有塊
            if(Heap32First(&he, GetCurrentProcessId(), hl.th32HeapID))
            {
                printf("\nHeap ID:  %d\n", hl.th32HeapID);
                do
                {
                    printf("Block size: %d\n", he.dwBlockSize);
                    he.dwSize = sizeof(HEAPENTRY32);
 
                } while(Heap32Next(&he));
            }
            hl.dwSize = sizeof(HEAPLIST32);
 
        } while (Heap32ListNext(hHeapSnap, &hl));
    }
    CloseHandle(hHeapSnap); 
 
    return true;
}
程序效果展示:
可以看到 Test.exe 的父進程是 Dev-C++.exe,
這是因爲筆者的這個 Demo 是通過 Dev-C++ 來編譯運行的.
image
image
image

           

              

Process Walking 解析

 

下面貼出的就是在 TlHelp32.h  中的所有的關於進程這一塊的代碼了,

我還是會分爲一部分一部分的貼出,這樣比較好介紹。

typedef struct tagPROCESSENTRY32
{
    DWORD        dwSize;
    DWORD        cntUsage;
    DWORD        th32ProcessID;          // this process
    ULONG_PTR    th32DefaultHeapID;
    DWORD        th32ModuleID;           // associated exe
    DWORD        cntThreads;
    DWORD        th32ParentProcessID;    // this process's parent process
    LONG         pcPriClassBase;         // Base priority of process's threads
    DWORD        dwFlags;
    CHAR         szExeFile[MAX_PATH];    // Path
 
} PROCESSENTRY32;
 
typedef PROCESSENTRY32 *  PPROCESSENTRY32;
typedef PROCESSENTRY32 *  LPPROCESSENTRY32;

下面就來解釋 PROCESSENTRY32 中的結構成員的含義了。

  1. dwSize:                            PROCESSENTRY32 結構體的大小, 在使用 Process32First 之前需要將這個成員設置好。
  2. cntUsage:                       這個參數呢,不管它了,也不需要設置,因爲這個值的結果必須爲 1 。
  3. th32ProcessID:              這個參數就很明顯了,代表一個進程的 ID,即進程標識符。
  4. th32DefaultHeapID:    這個參數的話代表了進程的默認堆標識符。
  5. th32ModuleID:              這個參數代表了進程的模塊標識符。
  6. cntThreads:                    代表進程所啓動的線程數目。
  7. th32ParentProcessID: 代表該進程的父進程 ID
  8. pcPriClassBase:             該值代表由該進程所創建的線程所擁有的基本優先級。
  9. dwFlags:                         保留,暫未使用。
  10. szExeFile:                        返回該進程的可執行文件所在的路徑。
  11. th32MemoryBase:       該可執行文件所加載的基地址。
  12. th32AccessKey:            訪問標識符。

在使用當中呢,其實上面的這個結構體只需要填充第一個字段 dwSize 即可,

其他字段都是通過調用函數 Process32First 或者是 Process32Next 來填充的。

然後我們就來看屬於 Process Walking 這一塊的 API 了。

不過這裏只是貼出 API 的原型,對於使用的話,放到代碼中介紹來的更快。

BOOL WINAPI Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
 
BOOL WINAPI Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);

可以看到,其實真的就只有這麼點東西而已,其中要使用的也就兩個 API 而已,簡單吧~~~

Process Walking 使用

對於這個使用嘛,個人覺得直接把代碼貼出來,然後附上註釋,附上截圖就 OK 了,

所以在這裏也還是以這種方式來介紹。

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <iostream>
 
using namespace std;
 
//獲取系統當前的所有進程 
BOOL GetProcessList();
 
int main()
{
    GetProcessList();
 
    cout<<endl<<endl;
    
    system("pause");
}
 
//獲取到進程列表 
BOOL GetProcessList()
{
    HANDLE             hProcessSnap;
    HANDLE             hProcess;
    PROCESSENTRY32     pe32;
 
    //對系統中當前所有的進程拍下快照 
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(hProcessSnap == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
 
    //在使用 PROCESSENTRY32 結構之間需要先設置好該結構的大小 
    pe32.dwSize = sizeof(PROCESSENTRY32);
 
    //獲取第一個進程 
    if(!Process32First(hProcessSnap, &pe32))
    {
        CloseHandle(hProcessSnap);
        return FALSE;
    }
 
    //採用 Do - While 遍歷所有進程 
    do
    {
        printf("\n-----------------------------------------------------");
        printf("\n  PROCESS NAME:     = %s", pe32.szExeFile);
        printf("\n  parent process ID = 0x%08X", pe32.th32ParentProcessID);
        printf("\n  process ID        = 0x%08X", pe32.th32ProcessID);
        printf("\n  thread count      = %d", pe32.cntThreads);
        printf("\n  Priority Base     = %d", pe32.pcPriClassBase);
 
        //遍歷獲取下一個進程 
    } while(Process32Next(hProcessSnap, &pe32));
 
    CloseHandle(hProcessSnap);
    return TRUE;
}
         
程序效果展示:
可以看到 Test.exe 的父進程是 Dev-C++.exe,
這是因爲筆者的這個 Demo 是通過 Dev-C++ 來編譯運行的.
 
image

Thread Walking 解析

typedef struct tagTHREADENTRY32
{
    DWORD   dwSize;
    DWORD   cntUsage;
    DWORD   th32ThreadID;       // this thread
    DWORD   th32OwnerProcessID; // Process this thread is associated with
    LONG    tpBasePri;
    LONG    tpDeltaPri;
    DWORD   dwFlags;
 
} THREADENTRY32;
 
typedef THREADENTRY32 *  PTHREADENTRY32;
typedef THREADENTRY32 *  LPTHREADENTRY32;

下面就來解釋 PTHREADENTRY32 中的結構成員的含義了。

  1. dwSize:                            PTHREADENTRY32 結構體的大小, 在使用 Thread32First 之前需要將這個成員設置好。
  2. cntUsage:                       這個參數不管它了,也不需要設置,總是爲 0
  3. th32ThreadID:               這個參數代表一個線程的 ID,即線程標識符。
  4. th32OwnerProcessID這個參數的話代表了該線程所屬的進程的標識符。
  5. tpBasePri :                      這個參數代表了分配給這個線程的基本優先級。
  6. tpDeltaPri :                     這個參數總是 0 ,不需要理會 。
  7. dwFlags :                         這個參數也總是 0 ,不需要理會 。

在使用當中呢,其實上面的這個結構體只需要填充第一個字段 dwSize 即可,

其他字段都是通過調用函數 Thread32First 或者是 Thread32Next 來填充的。

然後我們就來看屬於 Thread Walking 這一塊的 API 了。

BOOL WINAPI Thread32First(HANDLE hSnapshot, LPTHREADENTRY32 lpte);
 
BOOL WINAPI Thread32Next(HANDLE hSnapshot, LPTHREADENTRY32 lpte);

可以看到,同進程那一塊一樣,在線程這裏也只有兩個 API

這裏順便提一下,在使用這些 API 的過程中都必須傳遞進去相對於的快照句柄。

Thread Walking 使用

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <iostream>
 
using namespace std;
 
//獲取系統當前的所有進程 
BOOL GetProcessList();
 
//獲取當前進程下的所有的線程信息 
BOOL ListProcessThreads(DWORD dwOwnerPID);
 
int main()
{
    GetProcessList();
 
    cout<<endl<<endl;
 
    system("pause");
}
 
//獲取到進程列表 
BOOL GetProcessList()
{
    HANDLE             hProcessSnap;
    HANDLE             hProcess;
    PROCESSENTRY32     pe32;
 
    //對系統中當前所有的進程拍下快照 
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(hProcessSnap == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
 
    //在使用 PROCESSENTRY32 結構之間需要先設置好該結構的大小 
    pe32.dwSize = sizeof(PROCESSENTRY32);
 
    //獲取第一個進程 
    if(!Process32First(hProcessSnap, &pe32))
    {
        CloseHandle(hProcessSnap);
        return FALSE;
    }
 
    //採用 Do - While 遍歷所有進程 
    do
    {
        printf("\n-----------------------------------------------------");          
        printf("\n  PROCESS NAME:     = %s", pe32.szExeFile);
        printf("\n  parent process ID = 0x%08X", pe32.th32ParentProcessID);
        printf("\n  process ID        = 0x%08X", pe32.th32ProcessID);
        printf("\n  thread count      = %d", pe32.cntThreads);
        printf("\n  Priority Base     = %d", pe32.pcPriClassBase);
 
        //列舉出指定進程下的所有線程 
        ListProcessThreads(pe32.th32ProcessID);
 
        //遍歷獲取下一個進程 
    } while(Process32Next(hProcessSnap, &pe32));
 
    CloseHandle(hProcessSnap);
    return TRUE;
}
 
 
 
//獲取指定進程下的所有的線程信息 
BOOL ListProcessThreads(DWORD dwOwnerPID) 
{ 
    HANDLE            hThreadSnap = INVALID_HANDLE_VALUE; 
    THREADENTRY32    te32; 
 
    //給當前行的下所有的線程進行拍照 
    hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 
    if(hThreadSnap == INVALID_HANDLE_VALUE) 
    {
        return FALSE; 
    }
 
    te32.dwSize = sizeof(THREADENTRY32 ); 
 
    //獲取指定進程的第一個線程 
    if(!Thread32First(hThreadSnap, &te32)) 
    {
        CloseHandle(hThreadSnap);
 
        return( FALSE );
    }
 
    do 
    { 
        //用來覈對當前線程是否屬於指定進程
        if(te32.th32OwnerProcessID == dwOwnerPID)
        {
            printf("\n\n     THREAD ID      = 0x%08X", te32.th32ThreadID);
            printf("\n     base priority  = %d", te32.tpBasePri);
            printf("\n     delta priority = %d", te32.tpDeltaPri);
        }
 
        //遍歷指定進程的下一個線程 
    } while(Thread32Next(hThreadSnap, &te32)); 
 
    CloseHandle(hThreadSnap);
    return TRUE;
}

程序效果展示:

可以看到我們當前運行的這個 Test.exe 的進程中只有一個線程,
這肯定只有一個線程啦,因爲我們在程序中就一個主線程,又沒有創建其他線程。

image

Module Walking 解析

typedef struct tagMODULEENTRY32
{
    DWORD   dwSize;
    DWORD   th32ModuleID;       // This module
    DWORD   th32ProcessID;      // owning process
    DWORD   GlblcntUsage;       // Global usage count on the module
    DWORD   ProccntUsage;       // Module usage count in th32ProcessID's context
    BYTE  * modBaseAddr;        // Base address of module in th32ProcessID's context
    DWORD   modBaseSize;        // Size in bytes of module starting at modBaseAddr
    HMODULE hModule;            // The hModule of this module in th32ProcessID's context
    char    szModule[MAX_MODULE_NAME32 + 1];
    char    szExePath[MAX_PATH];
 
} MODULEENTRY32;
 
typedef MODULEENTRY32 *  PMODULEENTRY32;
typedef MODULEENTRY32 *  LPMODULEENTRY32;

下面就來解釋 PMODULEENTRY32 中的結構成員的含義了。

  1. dwSize:                            PMODULEENTRY32 結構體的大小, 在使用 Module32First 之前需要將這個成員設置好 。
  2. th32ModuleID :             這個參數已不再使用,不管它了,也不需要設置
  3. th32ProcessID:              這個參數就很明顯了,代表一個進程的 ID,即進程標識符 。
  4. GlblcntUsage:                這個參數代表這個模塊在整個系統中加載的數目 , 也不用理會 。
  5. ProccntUsage:               這個參數代表和前面的參數差不多,只不過不再是整個系統,而是指當前進程的上下文中,也不理會 。
  6. modBaseAddr:              代表模塊在進程上下文中的基地址 。
  7. modBaseSize:                代表該模塊的大小 。
  8. hModule :                       該值代表模塊句柄 。
  9. szModule :                      該參數代表模塊名。
  10. szExePath:                       回該模塊所在的路徑。

在使用當中呢,其實上面的這個結構體只需要填充第一個字段 dwSize 即可,

其他字段都是通過調用函數 Module32First 或者是 Module32Next 來填充的。

然後我們就來看屬於 Module Walking 這一塊的 API 了。

BOOL WINAPI Module32First(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
 
BOOL WINAPI Module32Next(HANDLE hSnapshot, LPMODULEENTRY32 lpme);

Module Walking 使用

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <iostream>
 
using namespace std;
 
//獲取系統當前的所有進程 
BOOL GetProcessList();
 
//獲取指定進程所引用的模塊信息 
BOOL ListProcessModules(DWORD dwPID);
 
 
int main()
{
    GetProcessList();
 
    cout<<endl<<endl;
 
    system("pause");
}
 
//獲取到進程列表 
BOOL GetProcessList()
{
    HANDLE             hProcessSnap;
    HANDLE             hProcess;
    PROCESSENTRY32     pe32;
 
    //對系統中當前所有的進程拍下快照 
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(hProcessSnap == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
 
    //在使用 PROCESSENTRY32 結構之間需要先設置好該結構的大小 
    pe32.dwSize = sizeof(PROCESSENTRY32);
 
    //獲取第一個進程 
    if(!Process32First(hProcessSnap, &pe32))
    {
        CloseHandle(hProcessSnap);
        return FALSE;
    }
 
    //採用 Do - While 遍歷所有進程 
    do
    {
        printf("\n-----------------------------------------------------");          
        printf("\n  PROCESS NAME:     = %s", pe32.szExeFile);
        printf("\n  parent process ID = 0x%08X", pe32.th32ParentProcessID);
        printf("\n  process ID        = 0x%08X", pe32.th32ProcessID);
        printf("\n  thread count      = %d", pe32.cntThreads);
        printf("\n  Priority Base     = %d", pe32.pcPriClassBase);
 
        //列出與該進程相關聯的模塊信息
        ListProcessModules(pe32.th32ProcessID);
 
        //遍歷獲取下一個進程 
    } while(Process32Next(hProcessSnap, &pe32));
 
    CloseHandle(hProcessSnap);
    return TRUE;
}
 
 
 
//獲取指定進程引用的所有的模塊信息 
BOOL ListProcessModules(DWORD dwPID)
{
    HANDLE                hModuleSnap = INVALID_HANDLE_VALUE;
    MODULEENTRY32        me32;
 
    //給進程所引用的模塊信息設定一個快照 
    hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
    if(hModuleSnap == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
 
    me32.dwSize = sizeof(MODULEENTRY32);
 
    if(!Module32First(hModuleSnap, &me32))
    {
        CloseHandle(hModuleSnap);
        return FALSE;
    }
 
    do
    {
        printf("\n\n     MODULE NAME:     %s", me32.szModule);
        printf("\n     executable     = %s", me32.szExePath);
        printf("\n     process ID     = 0x%08X", me32.th32ProcessID);
        printf("\n     ref count (g)  =     0x%04X", me32.GlblcntUsage);
        printf("\n     ref count (p)  =     0x%04X", me32.ProccntUsage);
        printf("\n     base address   = 0x%08X", (DWORD)me32.modBaseAddr);
        printf("\n     base size      = %d", me32.modBaseSize);
 
    } while(Module32Next(hModuleSnap, &me32));
 
    CloseHandle(hModuleSnap);
    return TRUE;
}

程序效果展示:

可以看到我們當前運行的這個 Test.exe 的進程引用了很多個模塊,
有可執行文件 Test.exe ,ntdll.dll ,kernel32.dll ,
以及 kernelbase.dll , msvcrt.dll

image

image

其實上面的這些 DLL 都是必須得,在 Test.exe 中引用了 kernel32.dllmsvcrt.dll

而在 Kernel32.dll 中則引用了 ntdll.dllkernelbase.dll

從下面的截圖中就可以看出這點。

image

image

總結

上面呢通過一個一個的 Demo 來介紹了 ToolHelp 的使用,ToolHelp 聽上去貌似蠻那個的,

其實說白了也就是 12 個 API 在用來用去的,通過前面的代碼,也可以看出來,

通過 ToolHelp 來獲取信息還是蠻方便的,

上面的代碼呢,我也是通過 MSDN 獲得,然後通過自己的一些改寫以及整理出來的,

個人覺得還是蠻有參考價值的,有興趣的可以留個紀念 。                                 

版權所有,迎轉載,但轉載請註明:     轉載自    Zachary.XiaoZhen - 夢想的天空

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章