Как создать функцию батута для крючка

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

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

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

person Stratus    schedule 17.02.2012    source источник
comment
Я собирался опубликовать ссылку на GD, но только что заметил, что вы тоже там являетесь участником. Вы пробовали использовать их функцию поиска? Там есть масса примеров :)   -  person Tom Knapen    schedule 18.02.2012
comment
Вы можете проверить код EasyHook, я считаю, что он с открытым исходным кодом. Есть и другие примеры. Если вы планируете использовать это в развернутом приложении, я бы рекомендовал использовать библиотеку (например, EasyHook), которая уже может обрабатывать рекурсию на крючке / батуте, многопоточность и некоторые другие забавные вещи.   -  person ssube    schedule 18.02.2012
comment
@Tom Knapen Я искал GD, MPGH и несколько других сайтов, прежде чем опубликовать. Поиск 'trampoline' на GD возвращает несколько постов, немного связанных, но не то, что я ищу.   -  person Stratus    schedule 18.02.2012
comment
Ваш код очень полезен новичкам, спасибо   -  person Mickey Shine    schedule 22.04.2013


Ответы (2)


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

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

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

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

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

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

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

person arx    schedule 17.02.2012
comment
Спасибо, но это в значительной степени то, что я уже нашел, и это не помогает мне его написать. - person Stratus; 18.02.2012
comment
@Stratus: Что тебе не хватает? Выделите немного исполняемой памяти. Скопируйте n байтов из пролога функции в выделенную память и выполните переход к прологу функции + n. n - размер инструкций, которые необходимо скопировать, чтобы освободить не менее 5 байтов в прологе функции. Это батут. Есть и другие недостатки (например, не копируйте инструкции, изменяющие IP), но в основном это все. - person arx; 18.02.2012
comment
x64 имеет большой скачок, если вы предоставите ему регистр, например RAX. чтобы быть потокобезопасным, вы правы, но если вам не нужна потокобезопасность или вы не хотите иметь дело с упомянутыми сложностями и не хотите использовать стороннюю библиотеку, вы можете защитить области с критическими областями. если вы не пишете код, критичный для производительности, или его нет в горячем пути ofc. - person TakeMeAsAGuest; 18.03.2019

Стандартное руководство по подключению defacto взято из jbremer и доступно здесь

Вот простой обход x86 и ловушка для батута, основанная на этом руководстве с использованием функции Direct3D EndScene () в качестве примера:

bool Detour32(char* src, char* dst, const intptr_t len)
{
    if (len < 5) return false;

    DWORD  curProtection;
    VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &curProtection);

    intptr_t  relativeAddress = (intptr_t)(dst - (intptr_t)src) - 5;

    *src = (char)'\xE9';
    *(intptr_t*)((intptr_t)src + 1) = relativeAddress;

    VirtualProtect(src, len, curProtection, &curProtection);
    return true;
}

char* TrampHook32(char* src, char* dst, const intptr_t len)
{
    // Make sure the length is greater than 5
    if (len < 5) return 0;

    // Create the gateway (len + 5 for the overwritten bytes + the jmp)
    void* gateway = VirtualAlloc(0, len + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    //Write the stolen bytes into the gateway
    memcpy(gateway, src, len);

    // Get the gateway to destination addy
    intptr_t  gatewayRelativeAddr = ((intptr_t)src - (intptr_t)gateway) - 5;

    // Add the jmp opcode to the end of the gateway
    *(char*)((intptr_t)gateway + len) = 0xE9;

    // Add the address to the jmp
    *(intptr_t*)((intptr_t)gateway + len + 1) = gatewayRelativeAddr;

    // Perform the detour
    Detour32(src, dst, len);

    return (char*)gateway;
}

typedef HRESULT(APIENTRY* tEndScene)(LPDIRECT3DDEVICE9 pDevice);
tEndScene oEndScene = nullptr;

HRESULT APIENTRY hkEndScene(LPDIRECT3DDEVICE9 pDevice)
{
    //do stuff in here
    return oEndScene(pDevice);
}

//just an example
int main()
{
    oEndScene = (tEndScene)TrampHook32((char*)d3d9Device[42], (char*)hkEndScene, 7);
}
person GuidedHacking    schedule 28.03.2020