so I'm trying to hook a function for a game but there is one small problem. If registers such as eax, ebx, ecx, and edx are interchangeable, how come the first code sample below is crashing the game process but the second code does not crash and works as intended?
// Crashes game process
void __declspec(naked) HOOK_UnfreezePlayer()
{
__asm push eax
if ( !state->player.frozen || !state->ready )
__asm jmp hk_Disabled
__asm
{
mov eax, g_dwBase_Addr
mov ebx, [eax + LOCAL_PLAYER_INFO_OFFSET]
add ebx, 0x4
mov ecx, [ebx]
add ecx, 0x40
lea edx, [esi + 0x0C]
cmp edx, ecx
je hk_Return
hk_Disabled:
movss [esi + 0x0C], xmm0
hk_Return:
pop eax
mov ecx, g_dwBase_Addr
add ecx, RETURN_UnfreezePlayer
jmp ecx
}
}
// Works
void __declspec(naked) HOOK_UnfreezePlayer()
{
__asm push eax
if ( !state->player.frozen || !state->ready )
__asm jmp hk_Disabled
__asm
{
mov ecx, g_dwBase_Addr
mov edx, [ecx + LOCAL_PLAYER_INFO_OFFSET]
add edx, 0x4
mov ebp, [edx]
add ebp, 0x40
lea ecx, [esi + 0x0C]
cmp ecx, ebp
je hk_Return
hk_Disabled:
movss [esi + 0x0C], xmm0
hk_Return:
pop eax
mov ecx, g_dwBase_Addr
add ecx, RETURN_UnfreezePlayer
jmp ecx
}
}
I think the crash might be caused by my assembly code overwriting important data in the registers eax, ebx, ecx, etc.. What if the game is storing an important value in eax for example and then that data is lost because my if statement is moving a struct pointer into eax? Is there a way to preserve the contents of these registers and restore them to their original value before returning?
Registers are certainly not interchangable when hooking an already compiled program, as the meaning of the individual registers is defined by the code of the hooked program and the location of the hook in that code. Therefore, you must examine the hooked code and the location of the hook in order to determine whether the hooked code relies on the contents of certain registers being preserved.
With the push eax
instruction at the start and the pop eax
instruction at the end, you are already preserving the contents of the EAX register and restoring it afterwards. You can do the same with the EBX and EDX registers or simply use the PUSHAD/POPAD instructions to save all general-purpose registers. Depending on the location of the hook in the game, you may also have to preserve the EFLAGS register, which requires the PUSHFD/POPFD instructions.
Saving and restoring the ECX register would not be so easy, as the hook is using that register to calculate the address to jump to after it is finished.
However, since you say that the second code sample works while the first code sample causes the hooked program to crash, it is likely that the problem is only with the EBX register being modified. This is because the first code sample modifies the EBX register, whereas the second code sample does not.
Therefore, the likely solution to your problem would be to preserve the EBX register the same way as the EAX register is being preserved. In order to do so, you simply have to add a push ebx
instruction at the same location of the push eax
instruction and to add a pop ebx
instruction at the same location as the pop eax
instruction. However, please note that due to the way the stack works, the push and pop instructions must be in reverse order, like this:
Hook start:
push eax
push ebx
Hook end:
pop ebx
pop eax