c++assemblydllhookinline-assembly

Use c++ dll to hook to an instruction and still use the registers (x86)


NOTE: This is decompiled ghidra code, the imageBase start at 0x00400000, so we are working with Program.exe+001a0d39 relative to image base.

005a0d39 01  87  94       ADD        dword ptr [EDI  + 0x194 ], EAX
         01  00  00

EDI contains the pointer to a struct, and it is adding the value to the 0x194 offset.

This is the original instruction, what I want to accomplish is update EAX to another, more dynamic value, calculated based on various factors: the objective is to only modify the EAX and continue execution with the same context as before, EDI and EAX will be the argument to my function, required in order to apply the change.

EAX and EDI obtain specific context data that is needed to the new function.

After that we need to continue execution, going to the next instruction which is:

005a0d3f 8b  cf           MOV        this ,EDI

So replacing the ADD(6 byte) with a CALL(5byte) to a function defined in my .dll file, (the spare byte can just become a NOP).

This is my approach for patching the OP codes:

void WriteCall(BYTE* location, BYTE* newFunction) {
    DWORD oldProtect;

    VirtualProtect(location, 6, PAGE_EXECUTE_READWRITE, &oldProtect);

    location[0] = 0xE8; // CALL

    intptr_t relAddr = (intptr_t)(newFunction - location) - 5;

    *reinterpret_cast<int32_t*>(location + 1) = static_cast<int32_t>(relAddr);

    location[5] = 0x90; // fill spare with NOP

    VirtualProtect(location, 6, oldProtect, &oldProtect);
}

And using cheat engine, it seems to be doing its job well, the newFunction is:

int jump_back;
extern "C" __declspec(dllexport) __declspec(naked) noinl void MyFun_Wrapper() {
    __asm { // note: I am usin MCVS
        pushfd // Save flags

        // Save general-purpose registers
        push ebx
        push ecx
        push edx
        push esi
        push edi
        push ebp
        push esp
        //we don't need to push the EAX as we need to update it anyway

        push edi                  // param subject to add (2nd parameter)
        push eax                  // param added number (1st parameter)
        call MyFun
        add esp, 8                // Clean up parameters

        pop esp
        pop ebp
        pop edi
        pop esi
        pop edx
        pop ecx
        pop ebx

        add[edi + 0x194], eax    // Update XP with function return value

        popfd                    // Restore flags

        jmp jump_back
    }

}

jump_back is set to the instruction after the old ADD, in this case = base + 001a0d3f (it is set as the first instruction at DLL_PROCESS_ATTACH)

this is what MyFun looks like: extern "C" __declspec(dllexport) noinl int __cdecl MyFun(int eax, my::clazz* edi)

As I am doing some float operations inside the functions, I also tried to save and load said registers, but to no avail.

After some debugging it looks like my function inside my DLL is exiting cleanly. The problem is that once the program continues its normal execution it crashes immediately after, my suspect is that there is something that it isn't getting saved properly or some step I skipped.

May I know, What's your opinion?


Solution

  • Long story short of the problems and solutions I have done before solving the issues:

    __asm {
        pushfd // Save flags
    
        // -- Save general-purpose registers --
        push ebx
        push ecx
        push edx
        push esi
        push edi
        push ebp
    
        mov esi, esp // backup ESP originale, senza modifiche
    
        // -- Save full FP + XMM state --
        sub esp, 512 + 16              // Allocate 512 bytes for fxsave buffer
        and esp, 0xFFFFFFF0         // Align ESP to 16 bytes (required for FXSAVE)
    
        fxsave[esp]                // Save FPU, SSE, and MXCSR state
    
        mov ebp, esp
    
        push edi                  // param pet
        push eax                  // param added_xp
        call CalculateCorrectedXP
    
        mov esp, ebp
    
        //   Restore full FP + XMM state --
        fxrstor[esp]
    
        mov esp, esi                // Restore original ESP (before fxsave)
    
        // -- Restore general-purpose registers --
        pop ebp
        pop edi
        pop esi
        pop edx
        pop ecx
        pop ebx
    
        add[edi + 0x194], eax    // Update XP with function return value
    
        popfd                    // Restore flags
    
        jmp jump_back
    }