Перехват API используя MS Detours

Тема в разделе "WASM.WIN32", создана пользователем Begemot, 2 янв 2007.

  1. Begemot

    Begemot New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2006
    Сообщения:
    79
    Пробую организовать простой перехват АПИ через Detours.
    Метод внедрения - глобальный хук.
    Все перехватывается, но снятие хука и перехвата проходит как-то странно:
    Иногда, библиотека хука не выгружается из некоторых процессов. Причем глюк может быть, а может и не быть.
    Установка хука и перехвата выглядет так:

    Код (Text):
    1. static LRESULT CALLBACK msghook(int nCode, WPARAM wParam, LPARAM lParam)
    2. {
    3.     return CallNextHookEx(hook, nCode, wParam, lParam);
    4. }
    5.  
    6. int setMyHook()
    7. {
    8.    hook = SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)msghook,hInstance,0);
    9.    if(!hook) return 13;
    10.    return 0;
    11. }
    12.  
    13. int clearMyHook()
    14. {
    15.     BOOL unhooked = UnhookWindowsHookEx(hook);
    16.     return 0;
    17. }
    18.  
    19. BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
    20. {
    21.     (void)hinst;
    22.     (void)reserved;
    23.  
    24.     if (dwReason == DLL_PROCESS_ATTACH) {
    25.         hInstance = hinst;
    26.         DetourRestoreAfterWith();
    27.  
    28.         DetourTransactionBegin();
    29.         DetourUpdateThread(GetCurrentThread());
    30.         DetourAttach(&(PVOID&)Real_connect, ConnectIntercepted);
    31.         DetourTransactionCommit();
    32.         GetConfigPath();
    33.     }
    34.     else if (dwReason == DLL_PROCESS_DETACH) {
    35.         DetourTransactionBegin();
    36.         DetourUpdateThread(GetCurrentThread());
    37.         DetourDetach(&(PVOID&)Real_connect, ConnectIntercepted);
    38.         DetourTransactionCommit();
    39.     }
    40.     return TRUE;
    41. }
    Работаю со всем этим из-под сервиса, но такое поведение наблюдалось и в обычных приложениях...
    Кто-нибудь знает как обеспечить полную выгрузку всех копий Длл с хуком?
     
  2. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Begemot
    From MSDN:
     
  3. Begemot

    Begemot New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2006
    Сообщения:
    79
    Плехо :dntknw:
    Есть варианты как это обрабатывать?
     
  4. Guest

    Guest Guest

    Публикаций:
    0
    У меня с Detours были проблемы и посеръезнее. А почему ты используешь именно Detours?
    Решение проблемы: Из вышесказанного следует что нужно заставить процесс выполнить FreeLibrary, попробуй заюзать CreateRemoteThread который выгрузит библиотеку.
     
  5. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Begemot
    Можно попробовать самовыгрузку. Т.е. после снятия хука контроллирующая прога посылает какой-нибудь сигнал (SetEvent, например), а DLL сама вызывает в FreeLibrary.
    Но тут есть тонкости. Во-первых, может потребоваться несколько вызовов FreeLibrary, т.к. не факт, что LoadLibrary для этой длл вызвана 1 раз, во-вторых, последний вызов должен быть не FreeLibrary, а FreeLibraryAndExitThread.
     
  6. Begemot

    Begemot New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2006
    Сообщения:
    79
    im1111
    Единственное что удалось найти, да и прога уже закончена...
    А можно подробнее? Если можно, с примером...


    green
    Не понял, сколько раз она может быть вызвана, это же хук? Я имею ввиду: Длл подгружается системой во все процессы с оконным интерфейсом, сам процесс не особо об этом в курсе, т.е. и LoadLibrary вызываться не должно...процессом....=>одного FreeLibrary на процесс должно быть достаточно?
     
  7. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Begemot
    Я не знаю, как реализованы глобальные хуки, но предполагаю, что используется стандартный механизм загрузки длл. Думаю, что при вызове SetWindowsHookEx выставляется какой-то глобальный "hook pending" флаг, который проверяется соответствующим кодом каждого процесса (в данном случае где-то в ф-ции GetMessage), который при необходимости подгружает нужную длл.

    Скорее всего LoadLibrary вызывается 1 раз (в каждом процессе), но полной уверенности нет, надо пробовать...
     
  8. Begemot

    Begemot New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2006
    Сообщения:
    79
    green
    Ты не мог бы на коде показать реализацию предложенного метода(SetEvent)?
    Буду крайне благодарен
     
  9. Guest

    Guest Guest

    Публикаций:
    0
    Begemot
    Извини с примером сложновато, но идея такая: создать в удалленном процессе тред который выгрузит твою библиотеку. Данная АПИ должна с этим справится, единственная проблема в том что она присутствует начиная NT по XP/2003.
     
  10. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Begemot
    В деталях это примерно так:

    В управляющей проге:
    1. создаёшь (CreateEvent) именованный (ну, скажем, "HookCleaner") event с ручным управлением, , начальное состояние - nonsignaled (хотя пофиг).
    2. перед установкой хука (SetWindowsHookEx), устанавливаешь этот event в nonsignaled состояние (SetEvent).
    после снятия хука (UnhookWindowsHookEx) устанавливаешь этот event в signaled состояние (SetEvent).

    В хуковой DLL (в DllMain):
    1. по DLL_PROCESS_ATTACH:
    1.1. открываешь event "HookCleaner" (OpenEvent)
    1.2. cоздаёшь поток (CreateThread), который:
    1.2.1. ожидает этот event (WaitForSingleObject)
    1.2.2. выгружает DLL (FreeLibraryAndExitThread)
    2. по DLL_PROCESS_DETACH:
    2.1. закрываешь event "HookCleaner" (CloseHandle).
     
  11. Begemot

    Begemot New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2006
    Сообщения:
    79
    Не помогло.
    Сделал так.
    В Длл:
    Код (Text):
    1. .....
    2. .....
    3. HANDLE hGlobalEvent;
    4. DWORD dll_exit_thread_id;
    5. ....
    6. ....
    7. static LRESULT CALLBACK msghook(int nCode, WPARAM wParam, LPARAM lParam)
    8. {
    9.     return CallNextHookEx(hook, nCode, wParam, lParam);
    10. }
    11.  
    12. int setMyHook()
    13. {
    14.    hook = SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)msghook,hInstance,0);
    15.    if(!hook) return 13;
    16.    return 0;
    17. }
    18.  
    19. int clearMyHook()
    20. {
    21.     BOOL unhooked = UnhookWindowsHookEx(hook);
    22.     return 0;
    23. }
    24.  
    25. DWORD WINAPI dll_exit_thread(LPVOID param)
    26. {
    27.     HMODULE h;
    28.     DWORD dwWaitResult;
    29.     dwWaitResult = WaitForSingleObject(hGlobalEvent, INFINITE);
    30.     if (dwWaitResult==WAIT_OBJECT_0) {
    31.         h=GetModuleHandle("fire.dll");
    32.         FreeLibraryAndExitThread(h,0);
    33.     }
    34.     return 0;
    35. }
    36.  
    37. BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
    38. {
    39.     (void)hinst;
    40.     (void)reserved;
    41.     HANDLE h;
    42.  
    43.     if (dwReason == DLL_PROCESS_ATTACH) {
    44.         hInstance = hinst;
    45.         DetourRestoreAfterWith();
    46.         DetourTransactionBegin();
    47.         DetourUpdateThread(GetCurrentThread());
    48.         DetourAttach(&(PVOID&)Real_connect, ConnectIntercepted);
    49.         DetourTransactionCommit();
    50.         GetConfigPath();
    51.         hGlobalEvent=OpenEvent(EVENT_ALL_ACCESS, FALSE, "StopFire");
    52.         h=CreateThread(0,0xfffff,dll_exit_thread,0,0,&dll_exit_thread_id);
    53.     }
    54.     else if (dwReason == DLL_PROCESS_DETACH) {
    55.         DetourTransactionBegin();
    56.         DetourUpdateThread(GetCurrentThread());
    57.         DetourDetach(&(PVOID&)Real_connect, ConnectIntercepted);
    58.         DetourTransactionCommit();
    59.     }
    60.     return TRUE;
    61. }
    В управляющем сервисе:

    Код (Text):
    1. namespace Work
    2. {
    3. ..........
    4. ..........
    5.     void Hook();
    6.     void UnHook();
    7.     HANDLE hGlobalWriteEvent;
    8. ..........
    9. ..........
    10. }
    11. ..........
    12. ..........
    13. void Work::Hook()
    14. {
    15.     if (hLibInst) {
    16.         LPFNCINT clearMyHook = (LPFNCINT)GetProcAddress(hLibInst, "clearMyHook");
    17.         if (clearMyHook) {
    18.             clearMyHook();
    19.         }
    20.         FreeLibrary(hLibInst);
    21.     }
    22.     hLibInst = LoadLibrary(_T("fire.dll"));
    23.     if(hLibInst) {
    24.         hGlobalWriteEvent = CreateEvent(
    25.         NULL,         // no security attributes
    26.         TRUE,         // manual-reset event
    27.         FALSE,        // initial state is signaled
    28.         "StopFire"    // object name
    29.         );
    30.         ResetEvent(hGlobalWriteEvent);
    31.         LPFNCINT setMyHook = (LPFNCINT)GetProcAddress(hLibInst, "setMyHook");
    32.         if (!setMyHook)
    33.             FreeLibrary(hLibInst);
    34.         setMyHook();
    35.     }
    36. }
    37.  
    38. void Work::UnHook()
    39. {
    40.     if (!hLibInst) {
    41.         hLibInst = LoadLibrary(_T("fire.dll"));
    42.     }
    43.     SetEvent(hGlobalWriteEvent);
    44.     LPFNCINT clearMyHook = (LPFNCINT)GetProcAddress(hLibInst, "clearMyHook");
    45.     clearMyHook();
    46.     FreeLibrary(hLibInst);
    47. }
    Результат нулевой, где я ошибся?
     
  12. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Begemot
    Ты проверил, что весь код ф-ции dll_exit_thread
    выполняется без ошибок (я имею ввиду возвращаемые значения вызываемых апишных ф-ций)?

    Если да, то, видимо, всё-таки счётчик ссылок на DLL > 1.
    Попробуй (только для эксперимента - это ненадёжно) вместо
    Код (Text):
    1. FreeLibraryAndExitThread(h, 0);
    делать
    Код (Text):
    1. while (FreeLibrary(h));
     
  13. Begemot

    Begemot New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2006
    Сообщения:
    79
    Так сервис падает на этапе остановки
     
  14. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Begemot
    Так ты проверил насчёт dll_exit_thread ?
     
  15. Begemot

    Begemot New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2006
    Сообщения:
    79
    Да, там все чики-пуки
     
  16. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    и
    Код (Text):
    1. while (FreeLibrary(h));
    тоже не приводит к выгрузке длл?
     
  17. Begemot

    Begemot New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2006
    Сообщения:
    79
    Как-то все непонятно. Оставшиеся Длл могут все-таки выгрузиться, но значительно позже...

    Нет, не приводит, но в контексте вышесказанного
     
  18. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Begemot
    Странно... Не знаю тогда, в чём может быть дело...
    надо ковыряться, сейчас нет на это времени, извини
    ---
    Подключись к процессу, в котором наблюдается особо длительная задержка с выгрузкой длл (или длл вообще не выгружается), убедись, что нормально отработала dll_exit_thread. Если DLL_PROCESS_DETACH не приходит, посмотри кол. ссылок на ДЛЛ в базе загрузчика (PEB::LoaderData). В крайнем случае пройдись пошагово по FreeLibrary... В общем, искать надо, чего там не так...
     
  19. Begemot

    Begemot New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2006
    Сообщения:
    79
    green
    Спасибо, буду изучать