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?
Long story short of the problems and solutions I have done before solving the issues:
The FPU & XMM registers had to be saved, otherwise other problems would come up (Solved by using fxsave and fxrstor)
fxsave and fxstor seem to require the stack to be 16bit aligned, so you need to save the stack before allocating the space for the fxsave, and after because the function might change the stack unpredicably (I have no idea why)
then you must load in the ESP the prefunction stack pointer, do fxrstor and in the end restore the stack to pre fxsave state
__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
}