使用ReadProcessMemory时进程A与B读取内存值不一致问题排查
我碰到过不少类似的跨进程内存读取不一致的问题,结合你给出的细节,咱们一步步拆解可能的原因和排查方向:
地址计算逻辑错误(最大概率)
你提到IMPORT_TABLE_OFFSET设为1,这里要敲个警钟:PE文件的导入表偏移不是直接从MZ头(processBaseAdress)开始累加的。正常来说,导入表的实际内存地址应该是进程基址 + PE可选头中ImageDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress。
更关键的是:进程A的DLL是运行在进程A的地址空间里的,如果你在DLL代码里用的是相对于DLL自身基址的偏移,而不是进程A的MZ头基址,那你以为的“同一地址”其实完全是两个不同的内存位置!只是初始值碰巧一致,后续进程A修改了其中一个,自然就出现差异了。内存页的动态变更或访问权限问题
有些内存页会被系统自动分页换出,或者进程A自身修改了该页的访问权限。比如进程A的DLL读取时,内存页是驻留的且可访问,但进程B调用ReadProcessMemory时,该页可能刚被换出,或者权限被改成了不可读,导致读取失败——如果你没检查ReadProcessMemory的返回值,就会继续用之前的缓存值,看起来像是“值不一样”。ASLR地址随机化的干扰
如果系统开启了ASLR(现在默认都是开启的),进程A的镜像可能没有加载到默认基址上。如果你在进程B里获取的processBaseAdress不正确(比如用了硬编码的默认基址,而不是通过工具快照或API动态获取的),那计算出来的目标地址自然和进程A内部实际的地址不匹配。
快速排查步骤
- 对比实际访问地址:在进程A的DLL里,把读取的内存地址用
printf("%p", target_addr)打印出来;同时在进程B里,把要读取的地址也打印出来,直接对比这两个地址是否完全一致——如果不一样,问题直接锁定在地址计算上。 - 检查ReadProcessMemory的返回值:每次调用后都要判断返回值是否为TRUE,若失败,调用
GetLastError查看具体错误(比如ERROR_ACCESS_DENIED、ERROR_INVALID_ADDRESS),这能快速定位权限或地址有效性问题。 - 用调试器验证:用x64dbg或WinDbg附加到进程A,实时监控目标地址的内存变化,同时在进程B读取时,对比调试器里的当前值,就能明确是哪一方的读取逻辑出了问题。
内容的提问来源于stack exchange,提问作者Neo

