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

ReadProcessMemory无法读取特定AllocationProtect值页面的原因排查

内存扫描器中ReadProcessMemory无法读取Protect值为1或260的页面问题解析

问题概述

开发内存扫描器时,ReadProcessMemory()可读取进程90%的内存页,但当mbi.Protect值为1或260时读取失败,返回ERROR 299(ERROR_PARTIAL_COPY)且BytesRead为0。已通过管理员权限运行程序、设置调试权限,并以VM_READ权限打开进程,但仍无法读取这些特定页面。

原因分析

这种无法读取的情况是正常现象,属于Windows内存保护机制的限制,并非操作有误:

  • Protect值1(PAGE_NOACCESS):
    该属性标记的页面完全不可访问,系统会阻止任何读写操作,即使拥有调试权限也无法绕过,是内存安全防护的核心机制之一。
  • Protect值260(PAGE_GUARD | PAGE_READWRITE):
    十进制260对应十六进制0x104,是PAGE_GUARD(一次性保护标记)与PAGE_READWRITE的组合。PAGE_GUARD页面首次被访问时会触发目标进程的异常,系统会自动移除该标记,但跨进程读取时无法处理目标进程的异常,因此ReadProcessMemory直接返回失败。

代码优化建议

针对现有代码,可通过以下调整避免无效读取、提升稳定性:

  1. 提前过滤不可读页面
    在读取前判断保护属性,直接跳过无法读取的页面:
    # 定义保护属性常量(可从依赖代码中获取或自行定义)
    PAGE_NOACCESS = 0x1
    PAGE_GUARD = 0x100
    
    if mbi.State == MEM_COMMIT:
        # 跳过不可访问或带GUARD标记的页面
        if (mbi.Protect & PAGE_NOACCESS) != 0 or (mbi.Protect & PAGE_GUARD) != 0:
            Adress += mbi.RegionSize
            continue
        # 后续读取逻辑...
    
  2. 修复内存分配异常处理
    原代码中create_string_buffer分配大内存页失败时仅用pass跳过,会导致后续读取使用未初始化缓冲区,建议捕获异常后直接跳过该页:
    try:
        ContentsBuffer = create_string_buffer(mbi.RegionSize)
    except MemoryError:
        print(f"无法分配缓冲区,页大小: {mbi.RegionSize}")
        Adress += mbi.RegionSize
        continue
    
  3. 完善句柄与错误处理
    修复setDebugPriv函数中的句柄泄漏和错误调用问题:
    def setDebugPriv():    
        token_handle = wintypes.HANDLE()
        if not OpenProcessToken(        
              GetCurrentProcess(),                     
              TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,   
              byref(token_handle),                     
        ):
           print("Error:", kernel32.GetLastError())
           return False    
    
        try:
            luidvalue = win32security.LookupPrivilegeValue(None, win32security.SE_DEBUG_NAME)
            se_debug_name_value = LUID(luidvalue)
            LAA = LUID_AND_ATTRIBUTES(se_debug_name_value, SE_PRIVILEGE_ENABLED)
            tkp = TOKEN_PRIVILEGES(1, LAA)
    
            if not AdjustTokenPrivileges(
                token_handle,           
                False,                  
                byref(tkp),             
                sizeof(tkp),           
                None,                  
                None,                  
            ):
                print("Error:", kernel32.GetLastError())
                return False
        finally:
            CloseHandle(token_handle)  # 确保句柄被关闭,避免泄漏
        return True        
    

总结

无法读取这两类页面是Windows内存保护的正常表现,无需强行尝试读取。通过提前过滤不可读属性的页面,可有效提升扫描器的运行效率和稳定性。

内容的提问来源于stack exchange,提问作者João P 2018

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 17:57:18