【问题标题】:VirtualProtectEx, ERROR_INVALID_PARAMETER (error 87)VirtualProtectEx,ERROR_INVALID_PARAMETER(错误 87)
【发布时间】:2020-07-06 12:42:16
【问题描述】:

我正在尝试编写一个可以通过进程内存的函数。我注意到 ReadProcessMemory 在权限设置为 PAGE_NOACCESS 或 PAGE_GUARD 的区域上会失败。我决定使用 VirtualProtectEx 临时更改这些页面的权限,以便能够阅读它们。这似乎在大多数情况下都有效,但总会有少数情况下 VirtualProtectEx 会因 ERROR_INVALID_PARAMETER 而失败。我对所有参数进行了三重检查,它们似乎是正确的,我什至添加了在失败时再次调用 VirtualQueryEx 的代码,以确保传递的参数仍然有效。是什么原因造成的,我该如何解决?我在下面添加了一些最小(尽可能少)的代码来重现问题。

int protect_test(DWORD pid) {

    HANDLE phandle;

    struct _MEMORY_BASIC_INFORMATION mbi;
    SIZE_T mbi_size = sizeof(struct _MEMORY_BASIC_INFORMATION);
    DWORD state;
    SIZE_T regionsize;

    int bytes_retrieved;
    void* lpAddress;
    void* lpBaseAddress;
    void* lpAddress2;
    int error;
    struct _SYSTEM_INFO lpSystemInfo;
    DWORD pagesize;

    DWORD protect;
    DWORD newprotect;
    DWORD lpflOldProtect;
    DWORD lpExitCode = 0;

    // get the page size
    GetSystemInfo(&lpSystemInfo);
    pagesize = lpSystemInfo.dwPageSize;

    // get handle to process
    if ((phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)) == NULL) {
        return(-1);
    }

    // main loop
    lpAddress = 0;
    while (!((bytes_retrieved = VirtualQueryEx(phandle, lpAddress, &mbi, mbi_size)) == 0 && (error = GetLastError()) == ERROR_INVALID_PARAMETER)) {

        // Check for error -2
        if (GetExitCodeProcess(phandle, &lpExitCode) && lpExitCode != 259) {
            // process was closed abruptly 
            return -2;
        }

        // handle VirtualQueryEx fail
        if (bytes_retrieved == 0) {
            lpBaseAddress = lpAddress;
            lpAddress2 = (unsigned long long) lpAddress + pagesize;
            lpAddress = lpAddress2;
            continue;
        }

        // set variables so we don't have to refernce mbi directly 
        lpBaseAddress = mbi.BaseAddress;
        regionsize = mbi.RegionSize;
        lpAddress2 = (unsigned long long)lpBaseAddress + regionsize;
        state = mbi.State;
        protect = mbi.Protect;


        if ( state == MEM_COMMIT && ((protect & PAGE_NOACCESS) || (protect & PAGE_GUARD)) ) {

            // some debug print
            //printf(" State: 0x%x Protection: 0x%x Regionsize: 0x%llx %p - %p\n", state, protect, regionsize, lpBaseAddress, (unsigned long long)lpAddress2 - 1);

            // The problematic VirtualProtectEx call
            newprotect = PAGE_EXECUTE_READWRITE;
            if (VirtualProtectEx(phandle, lpBaseAddress, regionsize, newprotect, &lpflOldProtect) == NULL) {
                printf("   Failed to change region's protection to 0x%x. Base address: 0x%p Errorcode: 0x%x\n", newprotect, lpBaseAddress, GetLastError());
                printf("   VirtualQuery returns %d. The base address returned was 0x%o. The regionsize returned is 0x%llx\n", VirtualQueryEx(phandle, lpBaseAddress, &mbi, mbi_size), mbi.BaseAddress, mbi.RegionSize);
                return(1);
            }

            // set things back
            if (VirtualProtectEx(phandle, lpBaseAddress, regionsize, lpflOldProtect, &lpflOldProtect) == 0) {
                printf("   Failed to change region's protection back to its previous state\n", pid);
            }

        }

        // update lpAddress
        lpAddress = lpAddress2;
    }

    return 0;
}

【问题讨论】:

  • RtlGetLastNtStatus() 返回什么?
  • 它返回 0xC0000045
  • The specified page protection was not valid. - 你确定 c0000045 吗?可能是 c000004e 吗?还有Type是什么?
  • 所以这是部分(MEM_MAPPED),这是可能的,基于初始部分保护得到了这个代码。无论如何 - 为什么PAGE_EXECUTE_READWRITE 而不是删除PAGE_NOACCESS|PAGE_GUARD 所以protect & ~(PAGE_NOACCESS|PAGE_GUARD)
  • 所以此部分保护不能更改。

标签: windows api permissions virtual-memory virtualquery


【解决方案1】:

您无法更改该页面的权限。

使用

if ( state == MEM_COMMIT && ((protect & PAGE_NOACCESS) || (protect & PAGE_GUARD)) )

您只需要过滤掉坏内存,您无需更改具有其他状态和保护的页面的权限。其他页面类型中的“隐藏数据”非常罕见,只有 0.000001% 的可能性,甚至不值得考虑,除非您有理由怀疑它在使用一些非常先进的保护机制的情况下。

【讨论】:

    【解决方案2】:

    响应 GuidedHacking 以供未来人们寻找正确信息。

    具有执行/读取保护的页面可能仍会返回,并且需要更改为例如PAGE_EXECUTE_READWRITE 用于写入。代码也是错误的。它有效地将 MEM_COMMIT together 与 protected 进行比较。显然不是任何扫描进程以获取有用内存的人想要做的事情。

    更正和简化:

    if (state == MEM_COMMIT && !(protect & (PAGE_NOACCESS|PAGE_GUARD))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-07
      • 2020-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多