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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 02:05:29