Masimaro's blog - 进程模块枚举
type: channel
link: https://www.masimaro.top/tag/%E8%BF%9B%E7%A8%8B%E6%A8%A1%E5%9D%97%E6%9E%9A%E4%B8%BE/
items: 枚举进程中的模块

枚举进程中的模块
type: item
date: 2017-05-16T16:24:00+08:00
description: 在Windows中枚举进程中的模块主要是其中加载的dll，在VC上主要有2种方式，一种是解析PE文件中导入表，从导入表中获取它将要静态加载的dll，一种是利用查询进程地址空间中的模块，根据模块的句柄来得到对应的dll，最后再补充一种利用Windows中的NATIVE API获取进程内核空间中的模块,下面根据给出这些方式的具体的代码片段：解析PE文件来获取其中的dll在之前介绍PE文件时说过PE文件中中存在一个导入表，表中记录了程序中加载的导入dll以及这些dll中函数的信息，这个结构的定义如下：typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; }; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; } IMAGE_IMPORT_DESCRIPTOR;我只需要获取这个结构并且根据RVA计算出它在文件中的偏移即可找到对应名称，利用之前PE解析器中的CPeFileInfo类来解析它即可，下面是具体的代码：void EnumModulesByPe(LPCTSTR pszPath) { CPeFileInfo peFile; peFile.strFilePath = pszPath; peFile.LoadFile(); if (!peFile.IsPeFile()) { printf("is not a pe file!\n"); return ; } peFile.InitDataDirectoryTable(); PIMAGE_IMPORT_DESCRIPTOR pImportTable = peFile.GetImportDescriptor(); while(!peFile.IsEndOfImportTable(pImportTable)) { printf("%s\n", peFile.RVA2fOffset(pImportTable->Name, (DWORD)peFile.pImageBase)); pImportTable++; } }利用之前的PE解析的类，首先给类中的文件路径赋值，然后加载到内存，并初始化它的数据目录表信息，从表中取出导入表的结构，根据结构中的Name字段的值来计算它的真实地址，即可解析出它里面的模块，这里我们只能解析出PE文件中自身保存的信息，如果dll是在程序运行之时调用LoadLibrary动态加载的，利用这个方法是找不到的。解析进程地址空间中的模块这个方法首先通过OpenProcess函数获取对应进程的句柄，然后调用EnumProcessModules枚举进程地址空间中当前存在的模块，这个函数会返回一个HMODULE句柄的数组，我们遍历这个数组，对其中的每个句柄调用GetModuleFileNameEx(很多模块GetModuleFileName获取不到，具体原因我没有深入研究)获取对应的文件路径。下面是具体的代码： HMODULE* phMods = NULL; HANDLE hProcess = NULL; DWORD dwNeeded = 0; DWORD i = 0; TCHAR szModName[MAX_PATH] = {}; hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId ); if (NULL == hProcess) { printf("不能打开进程[ID:0x%x]句柄,错误码:0x%08x\n",dwProcessId); return; } EnumProcessModules(hProcess, NULL, 0, &dwNeeded); phMods = (HMODULE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwProcessId); if( EnumProcessModules(hProcess, phMods, dwNeeded, &dwNeeded)) { for ( i = 0; i < (dwNeeded / sizeof(HMODULE)); i++ ) { ZeroMemory(szModName,MAX_PATH*sizeof(TCHAR)); //在这如果使用GetModuleFileName，有的模块名称获取不到，函数返回无法找到该模块的错误 if ( GetModuleFileNameEx(hProcess, phMods…