KiUserCallbackDispatcher

KiUserCallbackDispatcher

是否有任何方法可以让 DisableUserModeCallbackFilter (或类似文件)在Windows 10上运行?

它应该允许从用户模式代码引发的异常跨用户/内核边界传播,并且它在Windows 7之前的Windows版本中具有修复程序,但是我似乎无法使其在更新的版本中运行版本。

这是一个似乎在Windows 10 x64上出错但在Windows XP x86上没有错误的测试程序:

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

#pragma comment(lib, "user32")

WNDPROC oldproc = NULL;

LRESULT CALLBACK newproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    _ftprintf(stderr, _T("OMG\n"));
    fflush(stderr);
    throw 0;
    return oldproc(hwnd, uMsg, wParam, lParam);
}

int _tmain(int argc, TCHAR *argv[])
{
    HWND hWnd = CreateWindowEx(0, TEXT("STATIC"), TEXT("Name"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, NULL, NULL);
    oldproc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)newproc);
    try {
        UpdateWindow(hWnd);
    } catch (int ex) {
        _ftprintf(stderr, _T("Error: %d\n"), ex);
        fflush(stderr);
    }
}

最佳答案

当窗口过程被调用时-内核在内核堆栈中以及在用户模式下将附加的堆栈框架推入特殊的“函数”(比普通函数更快地标记标签)KiUserCallbackDispatcher,后者调用窗口过程并通过特殊的api调用最终返回到内核ZwCallbackReturn弹出内核堆栈框架。请注意,在调用ZwCallbackReturn之后,我们将不返回下一条指令,而是返回进入内核的位置,从该位置调用用户模式回调(通常从GetMessagePeekMessage)。
无论如何,我们必须弹出内核堆栈框架-因此调用ZwCallbackReturn。因此在KiUserCallbackDispatcher上出现异常异常(exception)的情况-在设计上是错误的,因此不能正常工作。在这种情况下,谁叫ZwCallbackReturn?如果它将从__finally中的KiUserCallbackDispatcher处理程序调用-此中断展开过程(ZwCallbackReturn我所说的永不返回,但像跳远一样-用另一个堆栈指针和所有寄存器将我们移到另一个位置)。即使您从ZwCallbackReturn手动调用catch-调用后您将在哪里?
因此需要在KiUserCallbackDispatcher SEH处理程序之前处理异常,或者如果需要取消分配一些资源,则最少需要__finally块。KiUserCallbackDispatcher使用SEH处理函数来调用ZwCallbackReturn,即使异常将在回调中也是如此。但是,此SEH处理程序的行为可能会受到RTL_USER_PROCESS_PARAMETERS.Flags中未记录的标志的影响:
伪代码:

void KiUserCallbackDispatcher(...)
{
    __try {

        //...
    } __except(KiUserCallbackExceptionFilter(GetExceptionInformation())) {
KiUserCallbackDispatcherContinue:
        ZwCallbackReturn(0, 0, 0);
    }
}

void LdrpLogFatalUserCallbackException(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT ContextRecord);

int KiUserCallbackExceptionFilter(PEXCEPTION_POINTERS pep)
{
    if ( NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->Flags & 0x80000)
    {
        return EXCEPTION_EXECUTE_HANDLER;
    }

    LdrpLogFatalUserCallbackException(pep->ExceptionRecord, pep->ContextRecord);

    return EXCEPTION_CONTINUE_EXECUTION;
}
如果未设置此标志(0x80000)(默认情况下)-调用LdrpLogFatalUserCallbackException。它在内部调用UnhandledExceptionFilter,如果不返回EXCEPTION_CONTINUE_EXECUTION -用ZwRaiseExceptionSTATUS_FATAL_USER_CALLBACK_EXCEPTION调用 FirstChance = FALSE (这意味着此异常未传递给应用程序-仅作为调试器作为最后的机会异常)
如果设置此标志-过滤器返回EXCEPTION_EXECUTE_HANDLER-调用 RtlUnwindEx (使用TargetIp = KiUserCallbackDispatcherContinue)-结果将调用__finally处理程序,并在ZwCallbackReturn(0, 0, 0);结尾
在任何情况下,异常都不会传递给SEH函数,该函数在堆栈中的KiUserCallbackDispatcher(因为此处将处理异常)
因此,我们需要在KiUserCallbackDispatcher下方的堆栈中处理异常,或将标志设置为0x80000-在这种情况下,如果我们不在KiUserCallbackDispatcher之前处理异常-我们的__finally块(如果存在)将在ZwCallbackReturn之前执行,从而完成回调。
SetProcessUserModeExceptionPolicy 在最近的Windows版本(从win8开始)中未导出,但是下一个此api的年度代码为下一个(确切代码):
#define PROCESS_CALLBACK_FILTER_ENABLED     0x1

BOOL WINAPI SetProcessUserModeExceptionPolicy(DWORD dwFlags)
{
    PLONG pFlags = (PLONG)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->Flags;
    if (dwFlags & PROCESS_CALLBACK_FILTER_ENABLED)
    {
        _bittestandset(pFlags, 19); // |= 0x80000
    }
    else
    {
        _bittestandreset(pFlags, 19); // &= ~0x80000
    }

    return TRUE;
}
测试:
WNDPROC oldproc;

LRESULT CALLBACK newproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)oldproc);
    __try {
        *(int*)0=0;
        //RaiseException(STATUS_ACCESS_VIOLATION, 0, 0, 0);

    } __finally {
        DbgPrint("in finally\n");
        CallWindowProc(oldproc, hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

void test()
{
    RtlGetCurrentPeb()->ProcessParameters->Flags |= 0x80000;

    if (HWND hwnd = CreateWindowExW(0, WC_EDIT, L"***", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, NULL, NULL))
    {
        oldproc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)newproc);
        __try {
            ShowWindow(hwnd, SW_SHOW);
        }__except(EXCEPTION_EXECUTE_HANDLER){
            DbgPrint("no sense. never will be called\n");
        }

        MSG msg;
        while (0 < GetMessage(&msg, hwnd, 0, 0))
        {
            DispatchMessage(&msg);
        }
    }
}
尝试注释或取消注释RtlGetCurrentPeb()->ProcessParameters->Flags |= 0x80000;行(或相同的NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->Flags & 0x80000)并比较效果。
无论如何,顶级SEH过滤器(在调用wndproc之前)永远不会被调用

07-25 23:46