Несколько программ аварийно завершают работу при отсоединении с помощью UnhookWindowsHookEx().

Я делаю глобальный хук, чтобы добавить свою DLL в цепочку хуков:

HHOOK handle = SetWindowsHookEx(WH_CALLWNDPROC, addr, dll, 0);

Внутри моей DLL я использую Detours для перехвата нескольких вызовов функций WINAPI. Все работает нормально, кроме вызовов WaitForSingleObject. Всякий раз, когда я добавляю WaitForSingleObject в обходные функции, происходит сбой нескольких программ, когда я отсоединяю свою DLL (Chrome, Skype, ...). Вот как выглядит DLL:

DWORD (WINAPI* Real_WaitForSingleObject)( HANDLE hHandle, DWORD dwMilliseconds) = WaitForSingleObject;
DWORD WINAPI Mine_WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved) {

    switch(Reason) {
        case DLL_PROCESS_ATTACH: 
            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());
            DetourAttach(&(PVOID&)Real_WaitForSingleObject, Mine_WaitForSingleObject);
            DetourTransactionCommit();
            break;
        case DLL_PROCESS_DETACH: 
            DetourTransactionBegin(); 
            DetourUpdateThread(GetCurrentThread());
            DetourDetach(&(PVOID&)Real_WaitForSingleObject, Mine_WaitForSingleObject);
            DetourTransactionCommit();
            break;
        case DLL_THREAD_ATTACH:

            break;
        case DLL_THREAD_DETACH:

            break;
    }
    return TRUE;
}
DWORD WINAPI Mine_WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {

    return Real_WaitForSingleObject(hHandle, dwMilliseconds);
}

extern "C" __declspec(dllexport) int meconnect(int code, WPARAM wParam, LPARAM lParam) {

    return CallNextHookEx(NULL, code, wParam, lParam);
}

Может ли кто-нибудь помочь мне понять, почему это происходит и как я могу обойти эту проблему? Спасибо!


person moccajoghurt    schedule 14.11.2015    source источник
comment
Неудивительно, что такой вопиющий взлом привел к проблемам.   -  person David Heffernan    schedule 15.11.2015
comment
Предположительно, когда вы удаляете хук, DLL выгружается. Вы удаляете объезд, но это не остановит потоки, которые уже находятся в середине объездного вызова, от сбоя. Единственная причина, по которой WaitForSingleObject может привести к сбою, заключается в том, что он имеет тенденцию блокироваться в течение длительного времени. (Другие обходные функции будут сбоить только изредка, потому что большую часть времени они не будут выполняться точно в той точке, в которой DLL выгружается.)   -  person Harry Johnston    schedule 15.11.2015
comment
Есть ли способ безопасно выгрузить DLL, не вызывая сбоя обходных вызовов? Разве DetourDetach() не должен предотвращать такие сбои?   -  person moccajoghurt    schedule 15.11.2015
comment
Я не понимаю, как это могло быть, особенно в этом контексте. Но вам следует ознакомиться с документацией.   -  person Harry Johnston    schedule 15.11.2015
comment
То, что вы делаете, выходит за рамки возможного. Вы не должны ожидать, что сможете это сделать. Как вы могли ожидать, что код продолжит выполняться после его выгрузки? Как можно безопасно выгрузить код?   -  person David Heffernan    schedule 15.11.2015
comment
Читали ли вы документацию по DllMain, в частности документ по рекомендациям для библиотеки Dynamic-Link? Ваш код предполагает, что вы этого не сделали.   -  person IInspectable    schedule 15.11.2015


Ответы (2)


Я думаю, это происходит из-за того, что многие программы (Chrome, Skype, ...) имеют пул потоков, где фоновые потоки ожидают в WaitForSingleObject() чего-то интересного для них, и когда это происходит, этот поток [s] проснуться и сделать что-нибудь.

Итак, ваш поток A вызывает DetourDetach, в то время как другой поток B того же процесса в настоящее время находится внутри Mine_WaitForSingleObject(). Затем DLL выгружается, и все падает. Вы можете проверить с помощью отладчика, подключиться к этому проблемному процессу, установить точку останова в DLL_PROCESS_DETACH, и когда точка останова сработает, просмотреть стеки других потоков для Mine_WaitForSingleObject.

Я не знаю, как это исправить. Но вы можете попробовать один из способов — перечислить потоки и вызвать DetourUpdateThread() для каждого потока процесса. Таким образом, возможно, Detours что-нибудь с этим сделают.

person Soonts    schedule 15.11.2015
comment
DetourUpdateThread перезаписывает указатели инструкций в переписанном коде либо в целевой функции, либо в функции батута. Другими словами, если он собирается удалить батут, но поток выполняет батут, то он перенаправляет поток для выполнения соответствующих инструкций в исходной функции. Он не раскручивает стек (во всяком случае, он не знает, как это сделать), поэтому, если Mine_WaitForSingleObject находится в стеке, он все еще будет внутри после того, как вы обновите поток. - person Raymond Chen; 15.11.2015

Вы обходите функцию, которую использует почти любой процесс. И это особенно опасно, поскольку очень вероятно, что такой процесс имеет активный вызов этой функции. Блокирующий вызов практически в любом случае. Как только он разблокируется, код вернется в ваш объезд, которого больше нет.

Кабум.

На самом деле единственный способ разгрузить обходной путь — выйти из системы, чтобы все процессы, которые можно было обойти, больше не выполнялись.

person Hans Passant    schedule 15.11.2015