【问题标题】:PAGE_GUARD with VirtualProtect not raising an exception on execute access带有 VirtualProtect 的 PAGE_GUARD 不会在执行访问时引发异常
【发布时间】:2018-11-06 14:23:31
【问题描述】:

我正在尝试使用 PAGE_GUARD 挂钩一个函数,但在调用页面/地址时它不会引发任何异常。

void HookMe(){
    printf("Not hooked\n");
}
void GoodFnc(){
    printf("Hooked!\n");
}
long ExceptionHandler(PEXCEPTION_POINTERS ex){
    printf("ExceptionHandler called\n");
}
/*Called by CreateThread in main*/
DWORD WINAPI ExceptionTesting(LPVOID) {
    DWORD old = 0;
    AddVectoredExceptionHandler(1, ExceptionHandler);
    if (VirtualProtect((LPVOID)HookMe, 1, PAGE_EXECUTE_READWRITE | PAGE_GUARD, &old))
        printf("PAGE_GUARD set\n");
    //This was for testing:
    //*(char*)0 = 0;//ExceptionHandler gets called when ACCESS_VIOLATION happens
    while (1) {
        HookMe();
        Sleep(1000);
    }
    return 0;
}

上面的代码每秒只会显示PAGE_GUARD set,然后是Not hooked,不会引发任何异常。

我还确保HookMe()ExceptionHandler(...)ExceptionTesting(LPVOID) 位于不同的内存页面中

导致任何类型的异常,例如 ACCESS_VIOLATION(如在无限循环上方的注释中所见)将导致 ExceptionHandler 被调用。

【问题讨论】:

  • AFAIK, VirtualProtect 不会抛出任何异常。您应该检查函数是否失败或成功,如果失败则调用GetLastError
  • 它确实成功了(printf("PAGE_GUARD set\n"); 被调用)。虽然在循环中调用 HookMe() 时没有引发任何异常
  • 你在调试器下测试吗?无论如何,您 page_guard 设置的不是单个函数地址,而是 1 页(0x1000 字节)。在这种保护之后,您的所有代码都可以成为页面保护。
  • 并且您的代码无论如何都无效,甚至不会被编译。这是伪代码吗?

标签: c++ winapi exception


【解决方案1】:

根据您的编译器,对HookMe 的调用可能是内联的。检查生成的代码。您应该能够在 HookMe 的声明中使用 __declspec(noinline) 之类的东西来解决这个问题。 (MS VC++)。请注意,即使在所有调用中都内联了函数,您也可以获取函数的地址!

【讨论】:

    【解决方案2】:

    documentation for VirtualProtect 表示受保护的地址必须是使用VirtualAlloc(或VirtualAllocEx)获取的保留区域的一部分。您的程序中的代码不是以这种方式分配的。

    此外,保护是基于页面(通常为 4K)完成的,因此上述示例中的 所有 代码可能会受到保护,并且当调用VirtualProtect 返回 - 不是在调用 Hook 时。

    【讨论】:

    • 对于您的第一点,代码很可能被放置在使用VirtualAlloc 获取的区域中 - 只是该分配是由加载器完成的!但是您的第二点非常有趣-我想知道那里发生了什么。您链接的 MSDN 页面的备注的最后一段可能与此处相关:没有调用 FlushInstructionCache,因此可能与那里的不一致有关。
    • 顺便说一句 w.r.t.您的用户资料,@1201 - Omnikey 键盘规则!尤其是旧的,功能键在顶部和左侧,上帝放它们的地方!
    • 你的第一点是错误的。页面保护可以设置到任何内存
    【解决方案3】:

    关于VirtualProtect

    更改对已提交页面区域的保护 调用进程的虚拟地址空间。

    PAGES - 不是单字节。我们可以至少在页面 (0x1000) 字节上设置PAGE_GUARD 属性。结果,当您尝试将 PAGE_GUARD 设置为某个函数时 - 您不仅为它设置了保护属性,而且还为它周围的许多字节设置了保护属性(之前和之后)。如果您的代码是这样的(无论如何您的代码是伪代码,甚至无法编译) - 所有防护异常中的更快将在VirtualProtect return 之后 - 在调用后的下一条指令上。如果您只希望保护页面影响单个功能 - 您需要将其放在单独的 exe 部分,例如#pragma code_seg。还可以注意 - 不需要为测试创建任何无限循环或单独的线程

    //#pragma code_seg(".guard")
    void HookMe(){
        MessageBoxW(0, 0, L"HookMe", MB_ICONINFORMATION);
    }
    #pragma code_seg()
    
    LONG NTAPI ExceptionHandler(::PEXCEPTION_POINTERS pep)
    {
        if (pep->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
        {
            WCHAR msg[64];
            swprintf(msg, L"guard violation at %p (%p)", pep->ExceptionRecord->ExceptionAddress, HookMe);
            MessageBoxW(0, msg, L"ExceptionHandler", MB_ICONWARNING);
            return EXCEPTION_CONTINUE_EXECUTION;
        }
    
        return EXCEPTION_CONTINUE_SEARCH;
    }
    
    void gtest()
    {
        if (PVOID pv = AddVectoredExceptionHandler(TRUE, ExceptionHandler))
        {
            ULONG op;
            if (VirtualProtect(HookMe, 1, PAGE_EXECUTE_READ|PAGE_GUARD, &op))
            {
                HookMe();
            }
            RemoveVectoredExceptionHandler(pv);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-27
      • 1970-01-01
      相关资源
      最近更新 更多