PoEdu - Windows阶段班 【Po学校】Lesson005_进程_017_进程的遍历
- 历史原因:WindowsAPI没有枚举进程的API
- 早期机制:进程信息存放在数据库,从注册表来访问;汇编里以RegQuery函数来获取。
- 中期加入了ToolHelper库,提供针对进程与线程的操作函数;
- 后期加入了Process Status库,提供针对进程的函数,有如:EnumProcess函数等。系统集成PSAPI.DLL里面。
- 进程与线程的遍历操作,多使用ToolHelper库函数,兼容性好。(从win95开始就存在了)
-
Tool Help 系列结构体
-
PROCESSENTRY32 结构体 : 进程结构体
- 进程 结构体成员:
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32, *PPROCESSENTRY32; - dwSize
- 结构的大小,以字节为单位。调用Process32First方法前设置这个成员sizeof(processentry32)。如果你不process32first dwSize初始化失败。
- cntUsage 此成员不再使用,并且总是设置为零。
- th32ProcessID
- 进程ID(PID)
- th32DefaultHeapID 此成员不再使用,并且总是设置为零。
- th32ModuleID 此成员不再使用,并且总是设置为零。
- cntThreads
- 进程启动的执行线程数。
- th32ParentProcessID
- 创建这个过程的父进程的ID。
- pcPriClassBase 此进程创建的任何线程的基本优先级。
- dwFlags 此成员不再使用,并且总是设置为零。
- szExeFile
- 当前进程的文件路径。
- 进程 结构体成员:
-
THREADENTRY32 结构体 :线程结构体
- 线程 结构体成员:
typedef struct tagTHREADENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ThreadID;
DWORD th32OwnerProcessID;
LONG tpBasePri;
LONG tpDeltaPri;
DWORD dwFlags;
} THREADENTRY32, *PTHREADENTRY32;
- dwSize
- 结构体大小
- cntUsage 此成员不再使用,并且总是设置为零。
- th32ThreadID
- 线程ID:TID,标识符兼容CreateProcess函数返回的。
- th32OwnerProcessID
- 拥有这根线程的进程PID
- tpBasePri
- 分配给线程的内核基优先级级别。优先级是从0到31的一个数字,其中0表示最低可能的线程优先级。更多信息,见keQueryPriorityThread。
- tpDeltaPri 此成员不再使用,并且总是设置为零。
- dwFlags 此成员不再使用,并且总是设置为零。
- dwSize
- 线程 结构体成员:
-
MODULEENTRY32 结构体 : 模块结构体
- 什么是模块?
- 一个进程由多个模块组成
- 任何一个程序启动,都需要加载ntdll.dll;任何一个界面,都需要加载user32.dll;因为里面有我们需要的API。我们自己的exe文件,加上ntdll.dll,再加上user32.dll,程序才能完整。
- 一个进程由多个模块组成
- 模块结构体成员
typedef struct tagMODULEENTRY32 {
DWORD dwSize;
DWORD th32ModuleID;
DWORD th32ProcessID;
DWORD GlblcntUsage;
DWORD ProccntUsage;
BYTE *modBaseAddr;
DWORD modBaseSize;
HMODULE hModule;
TCHAR szModule[MAX_MODULE_NAME32 + 1];
TCHAR szExePath[MAX_PATH];
} MODULEENTRY32, *PMODULEENTRY32;
- dwSize 结构体大小
- th32ModuleID 此成员不再使用,并且总是设置为一个。
- th32ProcessID 要检查模块的进程的标识符。
- GlblcntUsage 该模块的负载计算,这不是一般意义的,而且通常等于0xffff。
- ProccntUsage 该模块的负荷计算(同glblcntusage),这不是一般意义的,而且通常等于0xffff。
- modBaseAddr 在拥有进程的上下文中模块的基址。
- modBaseSize 模块的大小,以字节为单位。
- hModule 在拥有进程的上下文中对模块的句柄.
- szModule The module name.
- szExePath The module path.
- 什么是模块?
-
HEAPENTRY32 结构体 : 堆结构体
- 堆结构体成员:
typedef struct tagHEAPENTRY32 {
SIZE_T dwSize;
HANDLE hHandle;
ULONG_PTR dwAddress;
SIZE_T dwBlockSize;
DWORD dwFlags;
DWORD dwLockCount;
DWORD dwResvd;
DWORD th32ProcessID;
ULONG_PTR th32HeapID;
} HEAPENTRY32, *PHEAPENTRY32;
- dwSize
- hHandle 堆句柄
- dwBlockSize 堆块的大小,用字节表示。
- dwFlags 当前堆状态:固定的、未被使用、或者移动的、等状态。
- dwLockCount 此成员不再使用,并且总是设置为零。
- dwResvd 保留的;不使用或改变的。
- th32ProcessID
- th32HeapID
- 堆结构体成员:
-
HEAPLIST32 结构体 : 堆链表结构体
- 堆链表结构体成员:
typedef struct tagHEAPLIST32 {
SIZE_T dwSize;
DWORD th32ProcessID;
ULONG_PTR th32HeapID;
DWORD dwFlags;
} HEAPLIST32, *PHEAPLIST32;
- dwSize
- th32ProcessID
- th32HeapID
- dwFlags
- 堆链表结构体成员:
-
-
ToolHelp 库函数
- CreateToolhelp32Snapshot() 创建进程快照
HANDLE WINAPI CreateToolhelp32Snapshot(
_In_ DWORD dwFlags,
_In_ DWORD th32ProcessID
);
- 获取指定进程的快照,以及这些进程使用的堆、模块和线程。
- 快照不是实时的,只是拍照时的状态。
- 参数 dwFlags :决定我们能调用哪些函数。
- TH32CS_INHERIT:指示是可继承的快照句柄。
- TH32CS_SNAPALL:包括所有进程和线程,再加上堆的 th32ProcessID 中指定的进程和模块。等效于指定 TH32CS_SNAPHEAPLIST,TH32CS_SNAPMODULE,TH32CS_SNAPPROCESS 和 TH32CS_SNAPTHREAD 的值组合使用或运算 (|)。
- TH32CS_SNAPHEAPLIST:包括所有堆的快照中的 th32ProcessID 中指定的进程。若要枚举的堆,请参阅 Heap32ListFirst。
- TH32CS_SNAPMODULE:包括所有模块的快照中的 th32ProcessID 中指定的进程。若要枚举模块,请参阅 Module32First。如果函数失败与 ERROR_BAD_LENGTH,重试功能,直到成功为止。64 位 Windows: 在 32 位进程中使用此标志,而在 64 位进程中使用 64 位的进程。若要包括 64 位进程从 th32ProcessID 中指定的进程的 32 位模块,请使用 TH32CS_SNAPMODULE32 标志。
- TH32CS_SNAPMODULE32:包括所有的 32 位模块的 th32ProcessID 在快照时从 64 位进程调用中指定的进程。此标志可以结合 TH32CS_SNAPMODULE 或 TH32CS_SNAPALL。如果函数失败与 ERROR_BAD_LENGTH,重试功能,直到成功为止。
- TH32CS_SNAPPROCESS:包含在快照中系统中的所有进程。若要枚举的进程,请参阅 Process32First。
- TH32CS_SNAPTHREAD:包含在快照中系统中的所有线程。若要枚举的线程,请参阅 Thread32First。若要标识属于特定进程的线程,线程进行枚举时比较 THREADENTRY32 结构的 th32OwnerProcessID 成员及其进程标识符。
- 参数 th32ProcessID:
- 要包含在快照中的进程的进程标识符。这个参数可以是零,以表明当前进程。当指定的 TH32CS_SNAPHEAPLIST、 TH32CS_SNAPMODULE、 TH32CS_SNAPMODULE32 或 TH32CS_SNAPALL 的值,将使用此参数。否则为它将被忽略,所有流程都包含在快照中。
- 如果指定的进程是空闲进程或 CSRSS 之一处理,此函数将失败和最后的错误代码是 ERROR_ACCESS_DENIED,因为它们的访问限制阻止用户级代码打开它们。
- 如果指定的进程是一个 64 位的过程和调用方是 32 位的进程,此函数将失败和最后的错误代码是 ERROR_PARTIAL_COPY (299)。
- 返回值:如果此函数成功,它返回一个打开句柄到指定快照。如果函数失败,则返回 INVALID_HANDLE_VALUE
- 简单解释:如果你想枚举进程,dwFlags用TH32CS_SNAPPROCESS,此时th32ProcessID会被忽略,要枚举模块,用TH32CS_SNAPMODULE(64位程序枚举32位进程的模块时用TH32CS_SNAPMODULE32),枚举模块时th32ProcessID表示进程ID
- Heap32First ()
- 检索已由进程分配的堆的第一个块的信息。
- Heap32ListFirst ()
- 检索由指定进程分配的第一个堆的信息。
- Heap32ListNext ()
- 检索关于由进程分配的下一个堆的信息。
- Heap32Next ()
- 检索关于由进程分配的堆的下一个块的信息。
- Module32First ()
- 检索与流程相关联的第一个模块的信息。
- Module32Next ()
- 检索与进程或线程相关的下一个模块的信息。
- Process32First ()
BOOL WINAPI Process32First(
_In_ HANDLE hSnapshot,
_Inout_ LPPROCESSENTRY32 lppe
);
- 获取进程快照中的第一个进程信息
- hSnapshot:调用 CreateToolhelp32Snapshot 函数返回的快照句柄。
- lppe:指向 PROCESSENTRY32 结构的指针。它包含进程信息,如名称的可执行文件、 进程标识符和父进程的进程标识符。
- 返回值:如果进程列表中的第一项已被复制到缓冲区将返回 TRUE,失败返回FALSE。如果没有进程存在或快照不包含进程信息时GetLastError将返回 ERROR_NO_MORE_FILES 错误值。调用应用程序必须设置 PROCESSENTRY32 的 dwSize 成员的大小,以字节为单位的结构。
- Process32Next ()
BOOL WINAPI Process32Next(
_In_ HANDLE hSnapshot,
_Out_ LPPROCESSENTRY32 lppe
);
- 获取下一个进程信息
- hSnapshot:调用 CreateToolhelp32Snapshot 函数返回的快照句柄。
- lppe:指向 PROCESSENTRY32 结构的指针。它包含进程信息,如名称的可执行文件、 进程标识符和父进程的进程标识符。(见上面)
- 返回值:如果进程列表中的下一项已被复制到缓冲区将返回 TRUE,失败返回FALSE。如果没有进程存在或快照不包含进程信息时GetLastError将返回 ERROR_NO_MORE_FILES 错误值。
- Thread32First ()
- 检索有关系统快照中遇到的任何进程的第一个线程的信息。
- Thread32Next ()
- 检索有关系统内存快照中遇到的任何进程的下一个线程的信息。
- Toolhelp32ReadProcessMemory ()
BOOL WINAPI Toolhelp32ReadProcessMemory(
_In_ DWORD th32ProcessID,
_In_ LPCVOID lpBaseAddress,
_Out_ LPVOID lpBuffer,
_In_ SIZE_T cbRead,
_Out_ SIZE_T lpNumberOfBytesRead
); - 只能读,不能写;
- 只需进程PID,不需要取得句柄;
- 远程读取进程内容;
- 将分配给另一进程的内存复制到应用程序提供的缓冲区中。
-
代码实践 遍历QQ进程里面的所有模块:
#include <windows.h> #include <tchar.h> #include <TlHelp32.h> int main() { PROCESSENTRY32 pe32 = {0}; pe32.dwSize = sizeof(PROCESSENTRY32); HANDLE hSnapshot; hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if (hSnapshot != INVALID_HANDLE_VALUE) { if (!Process32First(hSnapshot, &pe32)) { DWORD dwError = GetLastError(); if (dwError == ERROR_NO_MORE_FILES) { _tprintf(TEXT("Error ! :%d \r\n"), dwError); } } do { // _tprintf(TEXT("PID :%d \t"), pe32.th32ProcessID); // _tprintf(TEXT("File :%s \r\n"), pe32.szExeFile); if (!_tcscmp(TEXT("QQ.exe"),pe32.szExeFile)) { MODULEENTRY32 me32 = { 0 }; me32.dwSize = sizeof(MODULEENTRY32); HANDLE hModuleSnop = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pe32.th32ProcessID); if (hModuleSnop != INVALID_HANDLE_VALUE) { if (Module32First(hModuleSnop, &me32)) { DWORD dwError = GetLastError(); if (dwError == ERROR_NO_MORE_FILES) { _tprintf(TEXT("Error ! :%d \r\n"), dwError); } do { _tprintf(TEXT("\tModuleName :%s \r\n"), me32.szModule); _tprintf(TEXT("\tModuleBase :0x%x \r\n"), me32.modBaseAddr); _tprintf(TEXT("\tModulePath :%s \r\n"), me32.szExePath); } while (Module32Next(hModuleSnop, &me32)); CloseHandle(hModuleSnop); } } } } while (Process32Next(hSnapshot, &pe32)); CloseHandle(hSnapshot); } system("pause"); return 0; }
- CreateToolhelp32Snapshot() 创建进程快照