我写了一个中间函数钩子,我注意到函数的原始局部变量被我在函数中声明的变量所覆盖。
这是我的代码:
DWORD jbPlantTrap = 0x48ED53 + 7;
DWORD jbPlantTrapSkip = 0x48EDD6;
__declspec(naked) void hkPlantTrap()
{
__asm
{
PUSHAD
PUSHFD
}
// this variable is overwriting the original [EBP-4]
unsigned char* player;
unsigned char* packet;
__asm
{
MOV ECX, DWORD PTR SS : [EBP + 0x8]
MOV player, ECX
MOV EDX, DWORD PTR SS : [EBP + 0x0C]
MOV packet, EDX
}
if (*(WORD*)(packet + 2) == 114 && sub_46261E(player, 104))
{
__asm
{
POPFD
POPAD
JMP[jbPlantTrapSkip]
}
}
else
{
// go back to original jump
__asm
{
POPFD
POPAD
MOV DWORD PTR SS : [EBP - 0x34] , 0x0
JMP[jbPlantTrap]
}
}
}
如何防止它这样做,所以我在绕行时声明的变量根本不会影响原始功能?
[像player
如何覆盖[EBP-4]
并弄乱原始函数上的变量,导致异常行为。
我的挂钩函数供参考:
void __cdecl PlaceJMP(BYTE *pAddress, DWORD dwJumpTo, DWORD dwLen)
{
DWORD dwOldProtect, dwBkup, dwRelAddr;
VirtualProtect(pAddress, dwLen, PAGE_EXECUTE_READWRITE, &dwOldProtect);
dwRelAddr = (DWORD)(dwJumpTo - (DWORD)pAddress) - 5;
*pAddress = 0xE9;
*((DWORD *)(pAddress + 0x1)) = dwRelAddr;
for (DWORD x = 0x5; x < dwLen; x++)
*(pAddress + x) = 0x90;
VirtualProtect(pAddress, dwLen, dwOldProtect, &dwBkup);
}
相关来源:
CPU Disasm
Address Hex dump Command Comments
0048ED53 |> \C745 CC 00000 MOV DWORD PTR SS:[LOCAL.13],0
0048ED5A |. EB 09 JMP SHORT 0048ED65
0048ED5C |> 8B4D CC /MOV ECX,DWORD PTR SS:[EBP-34]
0048ED5F |. 83C1 01 |ADD ECX,1
0048ED62 |. 894D CC |MOV DWORD PTR SS:[EBP-34],ECX
0048ED65 |> 837D CC 02 |CMP DWORD PTR SS:[EBP-34],2
0048ED69 |. 73 6B |JNB SHORT 0048EDD6
0048ED6B |. 8B55 CC |MOV EDX,DWORD PTR SS:[EBP-34]
0048ED6E |. 8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]
0048ED71 |. 66:8B8C50 E43 |MOV CX,WORD PTR DS:[EDX*2+EAX+37E4]
0048ED79 |. 51 |PUSH ECX ; /Arg1
0048ED7A |. E8 11E8FFFF |CALL 0048D590 ; \file.0048D590
0048ED7F |. 83C4 04 |ADD ESP,4
0048ED82 |. 8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
0048ED85 |. 837D D4 00 |CMP DWORD PTR SS:[EBP-2C],0
0048ED89 |.^ 74 49 |JE SHORT 0048EDD4
0048ED8B |. 8B55 D4 |MOV EDX,DWORD PTR SS:[EBP-2C]
0048ED8E |. 33C0 |XOR EAX,EAX
0048ED90 |. 66:8B82 3A270 |MOV AX,WORD PTR DS:[EDX+273A]
0048ED97 |. 8B4D E4 |MOV ECX,DWORD PTR SS:[EBP-1C]
0048ED9A |. 81E1 FFFF0000 |AND ECX,0000FFFF
0048EDA0 |. 3BC1 |CMP EAX,ECX
0048EDA2 |.^ 75 30 |JNE SHORT 0048EDD4
0048EDA4 |. 8B55 D4 |MOV EDX,DWORD PTR SS:[EBP-2C]
0048EDA7 |. 33C0 |XOR EAX,EAX
0048EDA9 |. 66:8B82 4B270 |MOV AX,WORD PTR DS:[EDX+274B]
0048EDB0 |. 8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
0048EDB3 |. 33D2 |XOR EDX,EDX
0048EDB5 |. 66:8B51 0C |MOV DX,WORD PTR DS:[ECX+0C]
0048EDB9 |. 3BC2 |CMP EAX,EDX
0048EDBB |.^ 75 17 |JNE SHORT 0048EDD4
0048EDBD |. 6A 05 |PUSH 5 ; /Arg2 = 5
0048EDBF |. 8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8] ; |
0048EDC2 |. 66:8B48 0C |MOV CX,WORD PTR DS:[EAX+0C] ; |
0048EDC6 |. 51 |PUSH ECX ; |Arg1
0048EDC7 |. E8 A3FEFFFF |CALL 0048EC6F ; \file.0048EC6F
0048EDCC |. 83C4 08 |ADD ESP,8
0048EDCF |. E9 22030000 |JMP 0048F0F6
0048EDD4 |>^ EB 86 \JMP SHORT 0048ED5C
0048EDD6 |> C745 B8 00000 MOV DWORD PTR SS:[EBP-48],0
UPDATE
将变量声明移到函数外部可以使其按预期工作,但我想避免这种情况。我还有什么其他选择?
DWORD jbPlantTrap = 0x48ED53 + 7;
DWORD jbPlantTrapSkip = 0x48EDD6;
unsigned char* player1; // moved outside function
unsigned char* packet1; // moved outside function
__declspec(naked) void hkPlantTrap() {
...
使用中间函数挂钩和declspec裸函数时,必须全部用汇编语言编写。您应该在裸函数之外声明任何变量,然后在程序集中引用这些变量,以免破坏堆栈。
或者,您可以增加堆栈大小,做一些事情,然后在返回之前放松堆栈,但是说实话,这会很烦人。