Есть ли способ заставить DisableUserModeCallbackFilter работать в Windows 10?

Есть ли способ получить DisableUserModeCallbackFilter (или аналогичный) для работы на Windows 10?

Предполагается, что исключения, генерируемые из кода пользовательского режима, распространяются через границы пользователя и ядра, и в нем было исправление в более ранних версиях Windows вплоть до Windows 7, но, похоже, я не могу заставить его работать на более поздних версиях. версии.

Вот тестовая программа, которая, похоже, выдает ошибку в 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);
}
}

-1

Решение

когда вызывается оконная процедура — ядро ​​помещает дополнительный кадр стека в стек ядра и в пользовательском режиме называется специальной «функцией» (даже быстрее, чем метка, чем обычная функция) KiUserCallbackDispatcher, которые вызывают оконную процедуру и, наконец, возвращаются в ядро ​​с помощью специального вызова API ZwCallbackReturn фрейм стека ядра обратите внимание, что после звонка ZwCallbackReturn мы возвращаемся не к следующей инструкции после нее, а в том месте, откуда мы входим в ядро, откуда будет вызываться обратный вызов пользовательского режима (обычно из GetMessage или же PeekMessage).

в любом случае мы должны вытолкнуть кадр стека ядра — так что вызовите ZwCallbackReturn, так что случай с исключением раскрутки через KiUserCallbackDispatcher — это неправильно по замыслу и не должно работать. кого в этом случае называют 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 — называется ZwRaiseException с STATUS_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) никогда не будет вызван

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]