Как создать батутную функцию для крючка - PullRequest
12 голосов
/ 17 февраля 2012

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

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;
}

1 Ответ

9 голосов
/ 18 февраля 2012

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

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

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

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

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

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

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

...