windows PAGE_GUARD保护堆栈页,但不执行异常处理程序

2ul0zpep  于 2023-01-21  发布在  Windows
关注(0)|答案(1)|浏览(249)

我通过VirtualProtect为当前线程的堆栈页面添加了PAGE_GUARD保护,但是,在访问堆栈时,我安装的异常处理程序从未执行。
我假设操作系统只是默默地接受了保护页违规异常,因为它使用保护页来管理堆栈增长和堆栈溢出。
在这种情况下,是否有可能捕获这些异常?
这是我的C代码

#include <stdio.h>
#include <Windows.h>
#include <assert.h>

LONG WINAPI BlanketExceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) {

    printf("exception code: %lx\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
    return EXCEPTION_CONTINUE_SEARCH;
}

int main(void) {

    // Install exception handler
    SetUnhandledExceptionFilter(BlanketExceptionHandler);

    ULONG_PTR stackBot = 0, stackTop = 0;   

    GetCurrentThreadStackLimits(&stackBot, &stackTop);
    assert(stackBot < stackTop);

    // turn all pages in the stack into guard-pages
    DWORD savedProtect = 0;
    if(!VirtualProtect((LPVOID) stackBot, stackTop - stackBot, PAGE_READWRITE | PAGE_GUARD, &savedProtect)){
        fprintf(stderr, "[Error]: Could not add guard pages: %ld\n", GetLastError());
        return 1;
    }

    // access some stack memory page. This should trigger the registered exception handler!
    *(DWORD*) (stackTop - 0x1500) = 0xdeadbeef;

    return 0;
}

需要使用编译代码

cl /nologo main.c /link /STACK:0x100000,0x100000

运行该代码不会从BlanketExceptionHandler中得到任何输出,但是,使用VirtualQuery时,我观察到堆栈页具有正确的保护。
我也试过通过AddVectoredExceptionHandler安装异常处理程序,但也不起作用。

ecbunoof

ecbunoof1#

在这种情况下,是否有可能捕获这些异常?
否。如果您访问保护页,则例外(作为任何异常)首先将由内核代码处理。如果异常发生在当前线程堆栈范围内-内核处理程序从页面中删除PAGE_GUARD,其中exeception是,确保在此页下方-存在几个PAGE_GUARD并在NT_TIB中调整StackLimit := PAGE_ALIGN(MemmoryAccessAddress)(您可以选中此选项)。并且不调用用户模式异常处理程序(如果这不是堆栈中的最后一页)
代码示例:

PCSTR GetStateSz(ULONG State)
{
    switch (State)
    {
    case MEM_FREE: return "FREE";
    case MEM_COMMIT: return "COMMIT";
    case MEM_RESERVE: return "RESERVE";
    }
    return "?";
}

PCSTR GetTypeSz(ULONG Type)
{
    switch (Type)
    {
    case MEM_IMAGE: return "IMAGE";
    case MEM_MAPPED: return "MAPPED";
    case MEM_PRIVATE: return "PRIVATE";
    }
    return "?";
}

void FormatProtect(ULONG Protect, PSTR psz, ULONG cch)
{
    static const ULONG pp[] = { 
        PAGE_NOACCESS, 
        PAGE_READONLY, 
        PAGE_READWRITE, 
        PAGE_WRITECOPY, 
        PAGE_EXECUTE, 
        PAGE_EXECUTE_READ, 
        PAGE_EXECUTE_READWRITE,
        PAGE_EXECUTE_WRITECOPY,
        PAGE_GUARD,
        PAGE_NOCACHE,
        PAGE_WRITECOMBINE,
    };

    static const PCSTR ss[] = { 
        "NOACCESS", 
        "READONLY", 
        "READWRITE", 
        "WRITECOPY", 
        "EXECUTE", 
        "EXECUTE_READ",
        "EXECUTE_READWRITE",
        "EXECUTE_WRITECOPY",
        "GUARD",
        "NOCACHE",
        "WRITECOMBINE",
    };

    ULONG n = _countof(pp);

    do 
    {
        if (Protect & pp[--n])
        {
            int len = sprintf_s(psz, cch, " | %s", ss[n]);
            if (0 >= len)
            {
                break;
            }

            psz += len, cch -= len;
        }
    } while (n);
}

ULONG WINAPI PrintProtect_I(PVOID BaseAddress)
{
    PVOID AllocationBase = BaseAddress;
    ::MEMORY_BASIC_INFORMATION mbi;
    while (VirtualQuery(BaseAddress, &mbi, sizeof(mbi)) && 
        mbi.AllocationBase == AllocationBase)
    {
        CHAR szProtect[0x100];
        FormatProtect(mbi.Protect, szProtect, _countof(szProtect));
        DbgPrint("[%p, %p) [%X] %s %s { %s }\n", mbi.BaseAddress, 
            BaseAddress = (PBYTE)mbi.BaseAddress + mbi.RegionSize, 
            mbi.RegionSize >> PAGE_SHIFT, GetStateSz(mbi.State), GetTypeSz(mbi.Type), szProtect);
    }

    return 0;
}

void PrintProtect(PVOID BaseAddress)
{
    if (HANDLE hThread = CreateThread(0, 0, PrintProtect_I, BaseAddress, 0, 0))
    {
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
    }
    PNT_TIB tib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
    DbgPrint("[%p, %p) << Current Stack\n\n", tib->StackLimit, tib->StackBase);
}

ULONG WINAPI SetGuard(PVOID pv)
{
    ULONG op;
    return VirtualProtect(pv, PAGE_SIZE, PAGE_EXECUTE_READWRITE|PAGE_GUARD, &op);
}

void GDemo()
{
    PVOID stack = alloca(0x10000);
    PBYTE pb = (PBYTE)PAGE_ALIGN((PBYTE)stack + 0x8000);
    ULONG_PTR a, b;
    GetCurrentThreadStackLimits(&a, &b);
    DbgPrint("[%p, %p) << Stack Region\n\n", a, b);
    PrintProtect((PVOID)a);
    if (HANDLE hThread = CreateThread(0, 0, SetGuard, pb, 0, 0))
    {
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
        PrintProtect((PVOID)a);
        *pb = 0;
        PrintProtect((PVOID)a);
    }
}

和输出:

[000000EAAE4C0000, 000000EAAE5C0000) << Stack Region

[000000EAAE4C0000, 000000EAAE5AB000) [EB] RESERVE PRIVATE {  }
[000000EAAE5AB000, 000000EAAE5AE000) [3] COMMIT PRIVATE {  | GUARD | READWRITE }
[000000EAAE5AE000, 000000EAAE5C0000) [12] COMMIT PRIVATE {  | READWRITE }
[000000EAAE5AE000, 000000EAAE5C0000) << Current Stack

[000000EAAE4C0000, 000000EAAE5AB000) [EB] RESERVE PRIVATE {  }
[000000EAAE5AB000, 000000EAAE5AE000) [3] COMMIT PRIVATE {  | GUARD | READWRITE }
[000000EAAE5AE000, 000000EAAE5B7000) [9] COMMIT PRIVATE {  | READWRITE }
[000000EAAE5B7000, 000000EAAE5B8000) [1] COMMIT PRIVATE {  | GUARD | EXECUTE_READWRITE }
[000000EAAE5B8000, 000000EAAE5C0000) [8] COMMIT PRIVATE {  | READWRITE }
[000000EAAE5AE000, 000000EAAE5C0000) << Current Stack

[000000EAAE4C0000, 000000EAAE5AB000) [EB] RESERVE PRIVATE {  }
[000000EAAE5AB000, 000000EAAE5AE000) [3] COMMIT PRIVATE {  | GUARD | READWRITE }
[000000EAAE5AE000, 000000EAAE5B4000) [6] COMMIT PRIVATE {  | READWRITE }
[000000EAAE5B4000, 000000EAAE5B7000) [3] COMMIT PRIVATE {  | GUARD | READWRITE }
[000000EAAE5B7000, 000000EAAE5B8000) [1] COMMIT PRIVATE {  | EXECUTE_READWRITE }
[000000EAAE5B8000, 000000EAAE5C0000) [8] COMMIT PRIVATE {  | READWRITE }
[000000EAAE5B7000, 000000EAAE5C0000) << Current Stack

相关问题