如何读取当前进程内存并确定遍历范围以查找函数签名?
我尝试读取当前进程的内存以查找特定字节模式(函数签名),通过调用GetModuleHandleA()获取基地址指针后,想要遍历内存并匹配字节序列,但不清楚可遍历的最大内存范围。
我曾尝试读取进程文件的大小并将其作为循环上限,以下是我目前编写的代码:
const char buf[] = { 0x40, 0x35, 0x02, 0x00, 0x78, 0x00 }; char ProcessFilePath[MAX_PATH]; GetModuleFileNameA(NULL, ProcessFilePath, MAX_PATH); HANDLE hFile = CreateFileA(ProcessFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return -1; LARGE_INTEGER size; GetFileSizeEx(hFile, &size); const unsigned char* lpImage = (unsigned char*)GetModuleHandleA(NULL); int maincounter = 0; int patternsize = sizeof(buf); for (int maincounter = 0; maincounter < size.LowPart; maincounter++) { printf("%02x", lpImage[maincounter]); }
请问如何正确读取全部进程内存?确定进程内存大小的正确方式是什么?需要说明的是,我的目标是通过匹配函数签名的字节序列定位函数在内存中的位置。
正确的内存范围确定与遍历方法
1. 为什么用磁盘文件大小遍历是错误的
进程磁盘文件的大小和内存中镜像的大小完全不同:
- 模块加载到内存时,PE节区会根据
VirtualSize和SizeOfRawData的差异进行扩展(比如代码节、数据节在内存中可能占用更大空间) - 内存中还包含磁盘文件没有的区域(比如导入表、导出表的内存结构,或者动态分配的堆/栈)
- 仅遍历磁盘文件大小,要么会漏掉内存中的有效区域,要么会越界访问无效内存。
2. 获取当前模块的有效内存范围(推荐)
如果目标是查找当前模块内的函数签名,使用GetModuleInformation函数可以直接获取模块在内存中的准确大小:
- 需包含头文件
psapi.h,链接时加入psapi.lib - 调用该函数获取
MODULEINFO结构,其中SizeOfImage字段就是模块在内存中的总大小
修改后的代码示例:
#include <psapi.h> #pragma comment(lib, "psapi.lib") const char buf[] = { 0x40, 0x35, 0x02, 0x00, 0x78, 0x00 }; HMODULE hModule = GetModuleHandleA(NULL); MODULEINFO modInfo; if (!GetModuleInformation(GetCurrentProcess(), hModule, &modInfo, sizeof(modInfo))) { return -1; } const unsigned char* lpImage = (unsigned char*)modInfo.lpBaseOfDll; SIZE_T totalSize = modInfo.SizeOfImage; int patternSize = sizeof(buf); // 遍历当前模块的有效内存区域,避免越界 for (SIZE_T i = 0; i <= totalSize - patternSize; i++) { // 匹配字节模式 if (memcmp(lpImage + i, buf, patternSize) == 0) { printf("找到匹配,地址:%p\n", lpImage + i); // 可选择继续查找下一个匹配或退出 } }
3. 遍历整个进程的所有内存区域(跨模块查找场景)
如果需要查找进程中所有内存区域的签名,需用VirtualQueryEx枚举所有内存页,过滤出可读的有效区域后再搜索:
核心步骤:
- 从进程用户态起始地址(32位通常为
0x00000000,64位需调整)开始,调用VirtualQueryEx获取每个内存页的信息 - 筛选出已提交且可读的内存区域(避免访问无效内存触发异常)
- 在每个有效区域内执行字节模式匹配
示例代码片段:
#include <windows.h> const char buf[] = { 0x40, 0x35, 0x02, 0x00, 0x78, 0x00 }; int patternSize = sizeof(buf); HANDLE hProcess = GetCurrentProcess(); unsigned char* addr = (unsigned char*)0; // 32位进程用户态内存上限为0x7FFFFFFF,64位需改为0x00007FFFFFFFFFFF while (addr < (unsigned char*)0x7FFFFFFF) { MEMORY_BASIC_INFORMATION mbi; if (VirtualQueryEx(hProcess, addr, &mbi, sizeof(mbi)) == 0) { break; } // 仅处理已提交且可读的内存区域 if ((mbi.State == MEM_COMMIT) && ((mbi.Protect & PAGE_READONLY) || (mbi.Protect & PAGE_READWRITE) || (mbi.Protect & PAGE_EXECUTE_READ) || (mbi.Protect & PAGE_EXECUTE_READWRITE))) { SIZE_T regionSize = mbi.RegionSize; for (SIZE_T i = 0; i <= regionSize - patternSize; i++) { if (memcmp((unsigned char*)mbi.BaseAddress + i, buf, patternSize) == 0) { printf("找到匹配,地址:%p\n", (unsigned char*)mbi.BaseAddress + i); } } } addr += mbi.RegionSize; }
关键注意事项
- 访问内存时必须检查权限,不可读的内存页直接访问会触发访问违规(Access Violation)
- 64位进程需要调整内存遍历的上限值
- 字节模式匹配时,需考虑位置无关代码中的动态偏移,避免匹配失败
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

