2012-02-17 11 views
11

Я заинтересован в подключении, и я решил посмотреть, могу ли я подключить некоторые функции. Меня не интересовало использование библиотеки, такой как обходные пути, потому что я хочу иметь опыт самостоятельного использования. С некоторыми источниками, которые я нашел в Интернете, мне удалось создать код ниже. Это принципиально, но все работает хорошо. Однако при подключении функций, вызываемых несколькими потоками, это оказывается крайне неустойчивым. Если два вызова выполняются почти в одно и то же время, это сработает. После некоторых исследований я думаю, что мне нужно создать функцию батута. После долгих часов я не смог найти ничего другого, кроме общего описания того, что такое батут. Я не мог найти ничего конкретного о написании функции батута или о том, как они действительно работали. Если кто-нибудь может помочь мне написать один, опубликовать некоторые источники или, по крайней мере, указать мне в правильном направлении, рекомендуя некоторые статьи, сайты, книги и т. Д. Я был бы очень признателен.Как создать функцию батута для крюка

Ниже приведен код, который я написал. Это действительно базовая идея, но я надеюсь, что другие могут учиться на этом.

test.cpp

#include "stdafx.h" 

Hook hook; 

typedef int (WINAPI *tMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); 

DWORD hMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) 
{ 
    hook.removeHook(); 
    tMessageBox oMessageBox = (tMessageBox)hook.funcPtr; 
    int ret =oMessageBox(hWnd, lpText, "Hooked!", uType); 
    hook.applyHook(&hMessageBox); 

    return ret; 
} 

void hookMessageBox() 
{ 
    printf("Hooking MessageBox...\n"); 
    if(hook.findFunc("User32.dll", "MessageBoxA")) 
    { 
     if(hook.applyHook(&hMessageBox)) 
     { 
      printf("hook applied! \n\n"); 
     } else printf("hook could not be applied\n"); 
    } 
} 

hook.cpp

#include "stdafx.h" 

bool Hook::findFunc(char* libName, char* funcName) 
{ 
    Hook::funcPtr = (void*)GetProcAddress(GetModuleHandleA(libName), funcName); 
    return (Hook::funcPtr != NULL); 
} 

bool Hook::removeHook() 
{ 
    DWORD dwProtect; 
    if(VirtualProtect(Hook::funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect)) 
     { 
     WriteProcessMemory(GetCurrentProcess(), (LPVOID)Hook::funcPtr, Hook::origData, 6, 0); 
     VirtualProtect(Hook::funcPtr, 6, dwProtect, NULL); 
     return true; 
    } else return false; 
} 

bool Hook::reapplyHook() 
{ 
    DWORD dwProtect; 
    if(VirtualProtect(funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect)) 
     { 
     WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, Hook::hookData, 6, 0); 
     VirtualProtect(funcPtr, 6, dwProtect, NULL); 
     return true; 
    } else return false; 
} 

bool Hook::applyHook(void* hook) 
{ 
    return setHookAtAddress(Hook::funcPtr, hook); 
} 

bool Hook::setHookAtAddress(void* funcPtr, void* hook) 
{ 
    Hook::funcPtr = funcPtr; 
    BYTE jmp[6] = { 0xE9, //jmp 
        0x00, 0x00, 0x00, 0x00, //address 
        0xC3 //retn 
       }; 

    DWORD dwProtect; 

    if(VirtualProtect(funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect)) // make memory writable 
    { 

     ReadProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, Hook::origData, 6, 0); // save old data 
     DWORD offset = ((DWORD)hook - (DWORD)funcPtr - 5); //((to)-(from)-5) 
     memcpy(&jmp[1], &offset, 4); // write address into jmp 
     memcpy(Hook::hookData, jmp, 6); // save hook data 
     WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, jmp, 6, 0); // write jmp 
     VirtualProtect(funcPtr, 6, dwProtect, NULL); // reprotect 

     return true; 
    } else return false; 
} 
+0

Я собирался опубликовать ссылку на GD, но я просто заметил, что вы тоже являетесь ее членом. Вы пытались использовать свою функцию поиска? В нем много примеров :) –

+0

Вы можете проверить код EasyHook, я считаю, что это с открытым исходным кодом. Существует также множество других примеров. Если вы планируете использовать это в развернутом приложении, я бы рекомендовал использовать библиотеку (например, EasyHook), которая уже может обрабатывать рекурсию на крючке/батуте, потоке и некоторых забавных материалах. – ssube

+0

@Tom Knapen Я искал GD, MPGH и несколько других сайтов, прежде чем отправил. Поиск «trampoline» в GD возвращает несколько слегка связанных сообщений, но не то, что я ищу. – Stratus

ответ

7

Если вы хотите, чтобы ваш крюк, чтобы быть в безопасности, когда вызывается несколькими потоками, вы не хотите быть постоянно расцепления и rehooking оригинальный API.

Батут - это просто код, который вы создаете, который воспроизводит функциональные возможности первых нескольких байтов исходного API (который вы перезаписываете с помощью вашего перехода), а затем перескакивает в API после перезаписанных байтов.

Вместо того, чтобы отцеплять API, позвонив и перетянув его, вы просто вызываете батут.

Это довольно сложно сделать на x86, потому что вам нужен (довольно минимальный) дизассемблер, чтобы найти границы команд. Вам также необходимо проверить, что код, который вы копируете в свой батут, не делает ничего относительно указателя инструкции (например, jmp, ветвь или вызов).

Этого достаточно, чтобы совершать звонки в потокобезопасную цепочку, но вы не можете создать hook, если несколько потоков используют API. Для этого вам нужно подключить функцию с двухбайтным скачком (который можно записать атомарно). API Windows часто предшествует несколько NOP (которые могут быть перезаписаны с помощью большого скачка), чтобы обеспечить цель для этого близкого перехода.

Выполнение этого на x64: много сложнее. Вы не можете просто исправить эту функцию с помощью 64-битного даун-прыжка (потому что его нет, а инструкции по его симуляции часто слишком длинны). И, в зависимости от того, что делает ваш батут, вам может потребоваться добавить его в информацию об отключении стека ОС.

Я надеюсь, что это не слишком общее.

+0

Спасибо, но это было в значительной степени тем, что я уже нашел, и не помогает мне написать его. – Stratus

+0

@Stratus: Что вам не хватает? Выделите некоторую исполняемую память. Скопируйте n байтов из пролога функции в выделенную память и следуйте за ней с помощью перехода к функции пролог + n. n - размер инструкций, которые необходимо скопировать для освобождения не менее 5 байтов в прологе функции. Это батут. Есть и другие морщины (например, не копируйте инструкции, которые изменяют IP), но это в основном. – arx