You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何读取当前进程内存并确定遍历范围以查找函数签名?

我尝试读取当前进程的内存以查找特定字节模式(函数签名),通过调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 20:58:28