David
David

Reputation: 71

PAGE_GUARD with VirtualProtect not raising an exception on execute access

I'm trying to hook a function using PAGE_GUARD but it does not raise any exception when the page/address is called.

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;
}

The code above will only show PAGE_GUARD set and then Not hooked each second, without raising any kind of exception.

I've also made sure that HookMe() is in a different memory page than ExceptionHandler(...) and ExceptionTesting(LPVOID)

Causing any kind of exception such as ACCESS_VIOLATION(as seen in the comment above the infinite loop) will result in ExceptionHandler being called.

Upvotes: 2

Views: 1425

Answers (3)

RbMm
RbMm

Reputation: 33754

about VirtualProtect

Changes the protection on a region of committed pages in the virtual address space of the calling process.

PAGES - not single byte. we can set PAGE_GUARD attribute at least on page (0x1000) byte only. as result when you try set PAGE_GUARD to some function - you set guard attribute not only to it but to many bytes around it (before and after). in case code such your (anyway your code is pseudo code, which not compile even) - faster of all guard exception will be just after VirtualProtect return - on next instruction after call. if you want only single function affect by guard page - you need place it in separate exe section, say with #pragma code_seg. also can note - that not needs any infinite loops or separate threads create for test

//#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);
    }
}

Upvotes: 1

1201ProgramAlarm
1201ProgramAlarm

Reputation: 32717

The documentation for VirtualProtect says that addresses being protected must be part of a reserved region acquired by using VirtualAlloc (or VirtualAllocEx). Code within your program was not allocated this way.

Also, the protection is done on a page basis (usually 4K), so likely all of the code in your example above would be protected, and the guard would go off immediately when the call to VirtualProtect returned - not when Hook was called.

Upvotes: 2

davidbak
davidbak

Reputation: 6019

It is possible, depending on your compiler, that the call to HookMe is inlined. Inspect the generated code. You should be able to defeat this with something like __declspec(noinline) on the declaration of HookMe. (MS VC++). Note that you can take the address of a function even if it is inlined at all calls!

Upvotes: 2

Related Questions