stevemk14ebr / PolyHook_2_0

C++20, x86/x64 Hooking Libary v2.0
MIT License
1.58k stars 222 forks source link

A question about how to hook into external libraries. #174

Closed bbsuuo closed 1 year ago

bbsuuo commented 1 year ago

I am trying to hook into the V8 library in a Node.js project from an external source. As I am just starting to learn about hooking techniques, I have some questions that I would like to ask everyone.

Firstly, these libraries are not referenced in my project. Therefore, I have conducted a function analysis using the following code, attempting to find some functions that I can start hooking into.

std::string undecorateName(const char* decoratedName)
{
    char undecoratedName[1024];
    UnDecorateSymbolName(decoratedName, undecoratedName, sizeof(undecoratedName), UNDNAME_COMPLETE);
    return std::string(undecoratedName);
}

void ExportedFunctionsAbndHooking(HMODULE hModule)
{

    ULONG size = 0;
    PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToDataEx(hModule, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size, NULL);

    if (exportDirectory == NULL)
    {
        LOG_INFO("No export directory found for module");
        return;
    }

    DWORD* functions = (DWORD*)((BYTE*)hModule + exportDirectory->AddressOfFunctions);
    DWORD* names = (DWORD*)((BYTE*)hModule + exportDirectory->AddressOfNames);
    WORD* ordinals = (WORD*)((BYTE*)hModule + exportDirectory->AddressOfNameOrdinals);

    for (DWORD i = 0; i < exportDirectory->NumberOfNames; i++)
    {
        char* functionName = (char*)((BYTE*)hModule + names[i]);
        DWORD functionAddress = (DWORD)((BYTE*)hModule + functions[ordinals[i]]);

        std::string mangledName = std::string(functionName);
        std::string undecoratedName = undecorateName(functionName);
        if (ts::GlobalProcessState::isFirstProcess()) {

             //LOG_INFO(R"(Debug Function Name : '{}' MangleName : '{}' ,Address '{}')", undecoratedName, mangledName, ts::utill::dword_to_string(functionAddress));
        }

        checkFunctionAndHooking(undecoratedName, functionAddress);
    }
}

//Hook V8
std::unique_ptr<PLH::x86Detour> detourV8Init;
uint64_t TrampolineV8Initialize;

bool HookV8Initialize() {
    LOG_INFO("V8 INITIALIZE !!");
    typedef bool(*InitializeFnType)();
    InitializeFnType initializeFn = reinterpret_cast<InitializeFnType>(TrampolineV8Initialize);
    return initializeFn();
}

void checkFunctionAndHooking(const std::string& undecoratedName, DWORD functionAddress)
{
    if (undecoratedName == "public: static bool __cdecl v8::V8::Initialize(void)") {
        LOG_INFO("Found v8::V8::Initialize at address {}", ts::utill::dword_to_string(functionAddress));
        detourV8Init = std::make_unique<PLH::x86Detour>(functionAddress, (uint64_t)HookV8Initialize, &TrampolineV8Initialize);

        if (!detourV8Init->hook()) 
        {
            LOG_WARN("V8 Initialize Hook Failure");
        }
        else 
        {
            LOG_INFO("Hooked V8 Succuess!");
        }
    }

    //v8::Isolate::New 'public: static class v8::Isolate * __cdecl v8::Isolate::New(struct v8::Isolate::CreateParams const &)' MangleName : '?New@Isolate@v8@@SAPAV12@ABUCreateParams@12@@Z' ,Address '263746512'
    else if (undecoratedName == "public: static class v8::Isolate * __cdecl v8::Isolate::New(struct v8::Isolate::CreateParams const &)") {
        LOG_INFO("Found v8::Isolate::New at address {}", ts::utill::dword_to_string(functionAddress));
    }
    //v8::Context::New  'public: static class v8::Local<class v8::Context> __cdecl v8::Context::New(class v8::Isolate *,class v8::ExtensionConfiguration *,class v8::MaybeLocal<class v8::ObjectTemplate>,class v8::MaybeLocal<class v8::Value>,struct v8::DeserializeInternalFieldsCallback)' MangleName : '?New@Context@v8@@SA?AV?$Local@VContext@v8@@@2@PAVIsolate@2@PAVExtensionConfiguration@2@V?$MaybeLocal@VObjectTemplate@v8@@@2@V?$MaybeLocal@VValue@v8@@@2@UDeserializeInternalFieldsCallback@2@@Z' ,Address '263580112'
    else if (undecoratedName == "public: static class v8::Local<class v8::Context> __cdecl v8::Context::New(class v8::Isolate *,class v8::ExtensionConfiguration *,class v8::MaybeLocal<class v8::ObjectTemplate>,class v8::MaybeLocal<class v8::Value>,struct v8::DeserializeInternalFieldsCallback)") {
        LOG_INFO("Found v8::Context::New at address {}", ts::utill::dword_to_string(functionAddress));
    }
}

As you can see, for functions that do not use external libraries, I am able to successfully hook and generate results. Here are my logs. Process ID: 28136 │ INFO│ 01:15:56.236 │ 174:hook.cpp ┃ V8 INITIALIZE !!

However, for something like the constructor of v8::Isolate, I need to obtain a type definition in order to hook into it.This makes the work I need to do very complex, and I have to check many types when writing the code

I'm not sure if it's feasible to directly compile the V8 project and then link it as a dynamic library to my library, replacing this linked library when it's actually used. Is this approach viable? Because when I was writing a hook for Unity, I could create a fake DLL for use, as long as the function signatures were consistent.However, it can also be quite troublesome when writing hooks for Unity's IL2CPP.

I'm not very familiar with C++, so I would like to ask for advice on what direction I should take for this situation .I hope to avoid some detours,

bbsuuo commented 1 year ago

After a day of studying, I've come to understand that C++ is a statically compiled language, which requires it to be compiled into machine code before running. This is vastly different from C#, and as such, it cannot have reflection-like features similar to C#. Therefore, my current approach is to use void* to simulate the type of a function. This is my current code:

//Hook V8 std::unique_ptr detourV8Init; std::unique_ptr detourV8IsolateNew; std::unique_ptr detourV8ContextNew;

uint64_t TrampolineV8Initialize;
uint64_t TrampolineV8IsolateNew;
uint64_t TrampolineV8ContextNew;

bool HookV8Initialize() {
    LOG_INFO("V8Hook : V8 Initialize");
    typedef bool(*InitializeFnType)();
    InitializeFnType initializeFn = reinterpret_cast<InitializeFnType>(TrampolineV8Initialize);
    return initializeFn();
}

void* HookV8IsolateNew(void* params) 
{
    LOG_INFO("V8Hook :New V8 Isolate");
    typedef void* (*NewFunc)(void*);
    NewFunc newFunc = reinterpret_cast<NewFunc>(TrampolineV8IsolateNew);
    return newFunc(params);
}

void* HookV8ContextNew(void* isolate, void* extensionConfiguration, void* maybeLocalObjectTemplate, void* maybeLocalValue, void* deserializeInternalFieldsCallback) {
    LOG_INFO("V8Hook :New V8 Context");
    typedef void* (*NewFunc)(void*, void*, void*, void*, void*);
    NewFunc newFunc = reinterpret_cast<NewFunc>(TrampolineV8ContextNew);
    return newFunc(isolate, extensionConfiguration, maybeLocalObjectTemplate, maybeLocalValue, deserializeInternalFieldsCallback);
}

void checkFunctionAndHooking(const std::string& undecoratedName, DWORD functionAddress)
{
    if (undecoratedName == "public: static bool __cdecl v8::V8::Initialize(void)") {
        LOG_INFO("Found v8::V8::Initialize at address {}", ts::utill::dword_to_string(functionAddress));
        detourV8Init = std::make_unique<PLH::x86Detour>(functionAddress, (uint64_t)HookV8Initialize, &TrampolineV8Initialize);

        if (!detourV8Init->hook()) 
        {
            LOG_WARN("V8 Initialize Hook Failure");
        }
        else 
        {
            LOG_INFO("Hooked V8 Succuess!");
        }
    }

    //v8::Isolate::New 'public: static class v8::Isolate * __cdecl v8::Isolate::New(struct v8::Isolate::CreateParams const &)' MangleName : '?New@Isolate@v8@@SAPAV12@ABUCreateParams@12@@Z' ,Address '263746512'
    else if (undecoratedName == "public: static class v8::Isolate * __cdecl v8::Isolate::New(struct v8::Isolate::CreateParams const &)") {
        LOG_INFO("Found v8::Isolate::New at address {}", ts::utill::dword_to_string(functionAddress));
        detourV8IsolateNew = std::make_unique<PLH::x86Detour>(functionAddress, (uint64_t)HookV8IsolateNew, &TrampolineV8IsolateNew);

        if (!detourV8IsolateNew->hook())
        {
            LOG_WARN("V8 Isolate New Hook Failure");
        }
        else
        {
            LOG_INFO("Hooked V8 Isolate New Succuess!");
        }
    }
    //v8::Context::New  'public: static class v8::Local<class v8::Context> __cdecl v8::Context::New(class v8::Isolate *,class v8::ExtensionConfiguration *,class v8::MaybeLocal<class v8::ObjectTemplate>,class v8::MaybeLocal<class v8::Value>,struct v8::DeserializeInternalFieldsCallback)' MangleName : '?New@Context@v8@@SA?AV?$Local@VContext@v8@@@2@PAVIsolate@2@PAVExtensionConfiguration@2@V?$MaybeLocal@VObjectTemplate@v8@@@2@V?$MaybeLocal@VValue@v8@@@2@UDeserializeInternalFieldsCallback@2@@Z' ,Address '263580112'
    else if (undecoratedName == "public: static class v8::Local<class v8::Context> __cdecl v8::Context::New(class v8::Isolate *,class v8::ExtensionConfiguration *,class v8::MaybeLocal<class v8::ObjectTemplate>,class v8::MaybeLocal<class v8::Value>,struct v8::DeserializeInternalFieldsCallback)") {
        LOG_INFO("Found v8::Context::New at address {}", ts::utill::dword_to_string(functionAddress));
        detourV8ContextNew = std::make_unique<PLH::x86Detour>(functionAddress, (uint64_t)HookV8ContextNew, &TrampolineV8ContextNew);

        if (!detourV8ContextNew->hook())
        {
            LOG_WARN("V8 Context New Hook Failure");
        }
        else
        {
            LOG_INFO("Hooked V8 Context New Succuess!");
        }
    }
}

Currently, I am trying to call member functions. In theory, I should be able to call member functions in a similar way. typedef void (MyClass::FuncType)(int, double); FuncType func = &MyClass::func; MyClass obj; (obj.func)(123, 4.56);

Because I have printed out all function signatures in some way, this code writing method, although clumsy, is work.

Since my understanding of C++ is not profound, if there is a more convenient way, I hope you can give me some suggestions.