Narrator的文章

  • 编程实现遍历进程遍历线程遍历进程加载模块

    背景在我们开发程序的时候,经常需要在程序中实现遍历进程、遍历线程和遍历进程加载模块等获取系统信息的操作。
    本文是基于Win32 API函数实现这三个常用操作,当然,方法肯定不止有一种。比如,我写的《在VS2013中编程使用WMI》这篇文章中,使用的是WMI去遍历进程的。
    现在,我把这个小程序实现的思路以及实现过程,写成文档分享给大家。
    函数介绍CreateToolhelp32Snapshot 函数
    可以通过获取进程信息为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程建立一个快照。
    函数声明
    HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID );
    参数

    dwFlags指定快照中包含的系统内容,这个参数能够使用下列数值(常量)中的一个或多个:



    VALUE
    MEANING




    TH32CS_INHERIT
    声明快照句柄是可继承的


    TH32CS_SNAPALL
    在快照中包含系统中所有的进程和线程


    TH32CS_SNAPHEAPLIST
    在快照中包含在th32ProcessID中指定的进程的所有的堆


    TH32CS_SNAPMODULE
    在快照中包含在th32ProcessID中指定的进程的所有的模块


    TH32CS_SNAPPROCESS
    在快照中包含系统中所有的进程


    TH32CS_SNAPTHREAD
    在快照中包含系统中所有的线程




    th32ProcessID指定将要快照的进程ID。如果该参数为0表示快照当前进程。该参数只有在设置了TH32CS_SNAPHEAPLIST或者TH32CS_SNAPMODULE后才有效,在其他情况下该参数被忽略,所有的进程都会被快照。
    返回值

    调用成功,返回快照的句柄;调用失败,返回INVALID_HANDLE_VALUE 。

    Process32First 和 Process32Next 函数当我们利用函数CreateToolhelp32Snapshot()获得当前运行所有进程的快照后,我们可以利用Process32First函数来获得第一个进程的句柄,Process32Next函数来获得下一个进程的句柄。
    Thread32First 和 Thread32Next 函数当我们利用函数CreateToolhelp32Snapshot()获得当前运行所有线程的快照后,我们可以利用Thread32First函数来获得第一个线程的句柄,Thread32Next函数来获得下一个线程的句柄。
    Module32First 和 Module32Next 函数当我们利用函数CreateToolhelp32Snapshot()获得指定进程的快照后,我们可以利用Module32First函数来获得进程第一个模块的句柄,Module32Next函数来获得进程下一个模块的句柄。
    实现原理大家可以从上面的函数介绍可以看出,重点理解CreateToolhelp32Snapshot这个函数的操作。我们遍历进程、遍历线程以及遍历进程加载模块等3个操作,都是基于CreateToolhelp32Snapshot函数进行下一步实现的。
    对于遍历进程的实现原理是:

    首先,使用CreateToolhelp32Snapshot 函数获取所有进程的快照
    然后,根据进程快照,使用Process32First 和 Process32Next 函数进行遍历快照,并获取快照信息
    最后,关闭上面获取的快照的句柄

    对于遍历线程的实现原理是:

    首先,使用CreateToolhelp32Snapshot 函数获取所有线程的快照
    然后,根据线程快照,使用Thread32First 和 Thread32Next 函数进行遍历快照,并获取快照信息
    最后,关闭上面获取的快照的句柄

    对于遍历进程模块的实现原理是:

    首先,使用CreateToolhelp32Snapshot 函数获取指定进程的所有模块快照。
    然后,根据模块快照,使用Module32First 和 Module32Next 函数进行遍历快照,并获取快照信息
    最后,关闭上面获取的快照的句柄。

    程序实现遍历进程BOOL EnumProcess(){ PROCESSENTRY32 pe32 = { 0 }; pe32.dwSize = sizeof(PROCESSENTRY32); // 获取全部进程快照 HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (INVALID_HANDLE_VALUE == hProcessSnap) { ShowError("CreateToolhelp32Snapshot"); return FALSE; } // 获取快照中第一条信息 BOOL bRet = ::Process32First(hProcessSnap, &pe32); while (bRet) { // 显示 Process ID printf("[%d]\t", pe32.th32ProcessID); // 显示 进程名称 printf("[%s]\n", pe32.szExeFile); // 获取快照中下一条信息 bRet = ::Process32Next(hProcessSnap, &pe32); } // 关闭句柄 ::CloseHandle(hProcessSnap); return TRUE;}
    遍历线程BOOL EnumThread(){ THREADENTRY32 te32 = { 0 }; te32.dwSize = sizeof(THREADENTRY32); // 获取全部线程快照 HANDLE hThreadSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (INVALID_HANDLE_VALUE == hThreadSnap) { ShowError("CreateToolhelp32Snapshot"); return FALSE; } // 获取快照中第一条信息 BOOL bRet = ::Thread32First(hThreadSnap, &te32); while (bRet) { // 显示 Owner Process ID printf("[%d]\t", te32.th32OwnerProcessID); // 显示 Thread ID printf("[%d]\n", te32.th32ThreadID); // 获取快照中下一条信息 bRet = ::Thread32Next(hThreadSnap, &te32); } // 关闭句柄 ::CloseHandle(hThreadSnap); return TRUE;}
    遍历指定进程模块BOOL EnumProcessModule(DWORD dwProcessId){ MODULEENTRY32 me32 = { 0 }; me32.dwSize = sizeof(MODULEENTRY32); // 获取指定进程全部模块的快照 HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); if (INVALID_HANDLE_VALUE == hModuleSnap) { ShowError("CreateToolhelp32Snapshot"); return FALSE; } // 获取快照中第一条信息 BOOL bRet = ::Module32First(hModuleSnap, &me32); while (bRet) { // 显示 Process ID printf("[%d]\t", me32.th32ProcessID); // 显示 模块加载基址 printf("[0x%p]\t", me32.modBaseAddr); // 显示 模块名称 printf("[%s]\n", me32.szModule); // 获取快照中下一条信息 bRet = ::Module32Next(hModuleSnap, &me32); } // 关闭句柄 ::CloseHandle(hModuleSnap); return TRUE;}
    程序测试在 main 函数中调用上述封装好的函数,进行测试。main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ // 遍历进程 if (FALSE == EnumProcess()) { printf("Enum Process Error!\n"); } system("pause"); system("cls"); // 遍历线程 if (FALSE == EnumThread()) { printf("Enum Thread Error!\n"); } system("pause"); system("cls"); // 遍历指定进程模块 if (FALSE == EnumProcessModule(6876)) { printf("Enum Process Module Error!\n"); } system("pause"); return 0;}
    测试结果:
    运行程序,首先成功显示系统上所有进程的信息:

    然后,成功显示系统上所有线程的信息:

    最后,成功显示指定进程“6876”的所有模块信息:

    总结这个小程序看似功能比较多,感觉比较复杂,实际上,它们三者的实现方式和思路都是相同的。你只要会一个,那么这三个应该都会了,这便是举一反三啊。
    其中,这个小程序重点理解CreateToolhelp32Snapshot 函数的参数就好,对于快照的遍历,根据你获取的快照信息不同,调用的快照遍历函数也不同。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2018-12-20 12:37:37

发送私信

英雄不问出处,强者面前全是路

11
文章数
7
评论数
eject