使用ReadProcessMemory读跨进程指针触发STATUS_ACCESS_VIOLATION异常
Windows平台使用ReadProcessMemory解引用跨进程读取指针触发STATUS_ACCESS_VIOLATION异常
问题描述
我正在练习使用Windows平台ReadProcessMemory API实现跨进程内存读取,当前实现目标分为两步:
- 读取目标进程中指定指针变量本身存储的地址值
- 基于第一步读到的地址,进一步读取该地址指向的实际整数值
目前第一步读取指针自身值的逻辑已经可以正常运行,但每次执行第二步解引用操作、读取指针指向地址存储的数值时,程序都会抛出Exception: STATUS_ACCESS_VIOLATION异常。我尝试过用= new int;的方式预先初始化接收指针值的变量,异常仍然没有解决。
本次测试涉及的材料清单:
- 被读取的目标测试程序源码
dummy.cpp:包含多级测试指针、循环打印内存地址的逻辑 - 实现内存读取逻辑的程序源码
reader.cpp - 程序运行截图
- 异常触发后生成的CORE转储日志
所有程序均使用MinGW工具链编译,源码内所有代码、技术标识未做修改。
根因排查与修复方案
STATUS_ACCESS_VIOLATION本质是内存访问违规,按以下优先级排查即可定位问题:
- 直接在本地进程解引用跨进程指针
这是使用ReadProcessMemory的最典型新手错误:第一步读到的指针值属于目标进程的虚拟地址空间,不是当前reader程序进程内的有效地址。如果直接写*read_ptr这类本地解引用代码,相当于在自身进程地址空间访问未分配、无权限的地址,必然触发访问违规。
你之前用= new int;初始化本地变量的操作完全无效,因为new分配的是reader进程自身的内存,和目标进程地址空间完全隔离,不可能解决跨进程访问的问题。核心规则:跨进程场景下永远不要直接解引用从目标进程读到的指针值,所有对目标进程内存的访问都必须调用
ReadProcessMemory,将目标进程的地址作为lpBaseAddress参数传入。
两步读取的正确写法参考:// hProcess为已打开的、持有PROCESS_VM_READ权限的目标进程句柄 intptr_t* target_ptr_addr = (intptr_t*)0x12345678; // 目标进程中指针变量自身的地址 intptr_t ptr_stored_addr = 0; // 存储第一步读到的、指针指向的目标进程地址 int actual_int_value = 0; // 存储最终读取到的整数值 SIZE_T bytes_read = 0; // 第一步:读取指针变量本身存储的地址值 BOOL read_ptr_ret = ReadProcessMemory( hProcess, target_ptr_addr, &ptr_stored_addr, sizeof(ptr_stored_addr), &bytes_read ); if (!read_ptr_ret || bytes_read != sizeof(ptr_stored_addr)) { // 增加错误处理,比如调用GetLastError()打印错误码 return -1; } // 第二步:读取指针指向地址的整数值,注意第二个参数传入第一步读到的目标进程地址 BOOL read_value_ret = ReadProcessMemory( hProcess, (LPCVOID)ptr_stored_addr, &actual_int_value, sizeof(actual_int_value), &bytes_read ); if (!read_value_ret || bytes_read != sizeof(actual_int_value)) { // 增加错误处理 return -1; } - 进程架构不匹配导致指针截断
如果目标进程和reader程序的编译架构不统一(比如目标是64位程序、指针占8字节,reader编译为32位程序、指针占4字节),第一步读取指针值时会发生截断,拿到的是残缺的非法地址,传入ReadProcessMemory自然触发访问违规。
排查方式:打开任务管理器确认目标进程是否带有*32后缀,编译两个程序时统一添加架构参数:64位编译加-m64,32位编译加-m32,同时用intptr_t类型存储地址值,避免指针长度不匹配。 - 进程句柄权限不足
调用OpenProcess打开目标进程时如果没有传入PROCESS_VM_READ权限,ReadProcessMemory会调用失败,如果没有检查返回值直接使用未初始化的地址做后续读取,也会触发访问违规。打开进程的正确写法参考:HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, target_pid); if (hProcess == NULL) { // 处理打开失败逻辑,比如权限不足、PID不存在 return -1; } - 硬编码地址受ASLR影响失效
如果硬编码了上次运行时拿到的指针变量地址,Windows的ASLR(地址空间随机化)机制会让程序每次加载的基址发生变化,硬编码的地址在下次运行时大概率是非法地址,需要每次运行时先获取目标进程模块基址,再通过固定偏移计算指针变量的真实地址。
内容的提问来源于stack exchange,提问作者noobcode0000
相关产品推荐
相关产品推荐

