Release与Debug模式下Rust跨进程内存读取程序行为异常
Release模式下ReadProcessMemory读取结果为0的问题解决
问题描述
使用windows-rs读取目标进程内存时,Debug模式(opt-level=0)能正确输出值123457,但切换到Release模式(opt-level≥1)后输出变为0,调整opt-level为0时问题消失。
核心原因分析
代码中存在两处关键的未定义行为,Debug模式下因内存布局宽松未触发问题,但Release模式的优化会放大这些错误:
VirtualProtectEx参数错误:传递&mut 0 as *mut _作为原保护属性的输出指针,0是常量,取其地址属于无效内存访问,会导致未定义行为。GetLastError使用不当:Win32函数仅在失败时设置LastError,OpenProcess调用unwrap()成功后调用GetLastError()会获取到不确定的值,干扰后续错误判断。- 类型不匹配的强制转换:
ReadProcessMemory的bytesRead参数要求usize类型指针,代码中使用i32变量强制转换,可能导致内存访问异常。
修复方案
针对上述问题,修改代码如下:
use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory; use windows::Win32::System::Memory::{VirtualProtectEx, PAGE_PROTECTION_FLAGS, PAGE_READWRITE}; use windows::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS, Sleep}; use windows::Win32::Foundation::{BOOL, HANDLE, WIN32_ERROR}; fn main() { const PID: u32 = 808; let handle: HANDLE; unsafe { // 正确处理OpenProcess的返回结果,避免无效的GetLastError调用 handle = match OpenProcess(PROCESS_ALL_ACCESS, false, PID) { Ok(h) => h, Err(e) => { println!("OpenProcess失败: {:?}", e); return; } }; } let mut my_int = 0; unsafe { let target_addr = 0xd79bdef564 as *const i32; let mut old_protect = PAGE_PROTECTION_FLAGS(0); let data_size = std::mem::size_of::<i32>(); // 修改内存页保护属性,使用合法变量存储原保护值 let success = VirtualProtectEx( handle, target_addr.cast(), data_size, PAGE_READWRITE, &mut old_protect, ); if !success.as_bool() { let err = WIN32_ERROR::from_raw(GetLastError()); println!("VirtualProtectEx失败: {:?}", err); return; } // 读取进程内存,使用正确类型的bytes_read变量 let mut bytes_read = 0usize; let success = ReadProcessMemory( handle, target_addr.cast(), &mut my_int as *mut _ as *mut _, data_size, Some(&mut bytes_read), ); if !success.as_bool() { let err = WIN32_ERROR::from_raw(GetLastError()); println!("ReadProcessMemory失败: {:?}", err); // 恢复原保护属性再退出 VirtualProtectEx(handle, target_addr.cast(), data_size, old_protect, &mut old_protect); return; } // 读取完成后恢复目标进程的内存保护属性 let _ = VirtualProtectEx(handle, target_addr.cast(), data_size, old_protect, &mut old_protect); } println!("{}", my_int); unsafe { Sleep(2000); } }
关键修复点说明
- 合法的内存保护参数:用
old_protect变量存储原内存保护属性,避免传递无效指针。 - 正确的错误处理:通过
match处理OpenProcess的返回值,仅在函数失败时调用GetLastError()。 - 类型安全:使用
std::mem::size_of替代硬编码的字节数,确保bytes_read为usize类型,匹配Win32函数要求。 - 恢复内存保护:读取完成后恢复目标进程的内存页属性,避免影响目标进程的正常运行。
内容的提问来源于stack exchange,提问作者kik3r
相关产品推荐
相关产品推荐

