挂钩函数时,为什么覆盖前 5 个字节并不重要?
When hooking a function, why does it not matter if you overwrite the first 5 bytes?
挂钩函数时,您需要覆盖原始函数的 5 个字节,栈帧的初始设置无关紧要,因为签名和调用约定与您的替换函数的原始函数相同函数,但我不明白的是你正在覆盖原始函数的前几个字节。 55 (push ebp) 8B EC (mov ebp, esp),所以你还需要 2 个?为什么覆盖那些对函数至关重要的提取 2 个字节并不重要?
00440112 ; int __cdecl function(int, FILE *)
.text:00440112 var_410 = byte ptr -410h
.text:00440112 var_310 = byte ptr -310h
.text:00440112 var_210 = byte ptr -210h
.text:00440112 var_110 = byte ptr -110h
.text:00440112 var_10 = byte ptr -10h
.text:00440112 var_4 = dword ptr -4
.text:00440112 arg_0 = dword ptr 8
.text:00440112 arg_4 = dword ptr 0Ch
.text:00440112
.text:00440112 55 push ebp
.text:00440113 8B EC mov ebp, esp
.text:00440115 81 EC 10 04 00 00 sub esp, 410h
.text:0044011B 53 push ebx
.text:0044011C 56 push esi
.text:0044011D 8B 75 0C mov esi, [ebp+arg_4]
.text:00440120 80 7E 04 00 cmp byte ptr [esi+4], 0
.text:00440124 57 push edi
.text:00440125 C7 86 10 01 00 00 63 00+ mov dword ptr [esi+110h], 63h
.text:0044012F 74 0F jz short loc_440140
.text:00440131 83 3D 7A 4E 4B 00 00 cmp dword_4B4E7A, 0
.text:00440138 7F 06 jg short loc_440140
.text:0044013A 83 65 FC 00 and [ebp+var_4], 0
.text:0044013E EB 07 jmp short loc_440147
进行上述组装。 'E9 ?? ?? ?? ??' 将替换 '55 8B EC 81 EC'(?? ?? ?? ?? 是要跳转到的函数的地址),所以我看到它的方式是在堆栈上分配 0x410 用于当调用的新函数跳回到 0x44011B 处的原始函数时,不会出现局部变量;为什么这无关紧要?我没有看到我错过了什么。
如果您想重现允许您使用此方法挂钩函数的功能代码,请参阅 MyUsername112358 的回答
如您所见,如果您打算能够执行原始代码,则不能只在 5 字节边界处切断。您将不得不反汇编并复制整个覆盖前 5 个字节的指令。
你可以看看我的ia32hook
, which uses OllyDbg's disassembler to find out where to cut off. The relevant snippet:
#include "ollydisasm/disasm.h"
static inline unsigned long CleanBiteOff(const unsigned char *ptr, size_t amount)
{
static t_disasm disasm;
unsigned long size = 0;
do {size += Disasm(ptr+size, 16, ptr+size, &disasm, DISASM_SIZE);} while(size < amount);
return size;
}
返回的size
就是你需要复制的字节数。在复制这些字节的蹦床结束时,跳回到 (char*)func+size
,一切都恢复正常。
您链接的答案也没有刷新指令缓存,如果指令指针恰好指向您要修补的字节,将会出现严重问题。您应该暂停线程,并断言 eip
没有指向您要修补的字节。
如果这不是出于学习目的,请考虑改用 Microsoft Detours 之类的解决方案。
挂钩函数时,您需要覆盖原始函数的 5 个字节,栈帧的初始设置无关紧要,因为签名和调用约定与您的替换函数的原始函数相同函数,但我不明白的是你正在覆盖原始函数的前几个字节。 55 (push ebp) 8B EC (mov ebp, esp),所以你还需要 2 个?为什么覆盖那些对函数至关重要的提取 2 个字节并不重要?
00440112 ; int __cdecl function(int, FILE *)
.text:00440112 var_410 = byte ptr -410h
.text:00440112 var_310 = byte ptr -310h
.text:00440112 var_210 = byte ptr -210h
.text:00440112 var_110 = byte ptr -110h
.text:00440112 var_10 = byte ptr -10h
.text:00440112 var_4 = dword ptr -4
.text:00440112 arg_0 = dword ptr 8
.text:00440112 arg_4 = dword ptr 0Ch
.text:00440112
.text:00440112 55 push ebp
.text:00440113 8B EC mov ebp, esp
.text:00440115 81 EC 10 04 00 00 sub esp, 410h
.text:0044011B 53 push ebx
.text:0044011C 56 push esi
.text:0044011D 8B 75 0C mov esi, [ebp+arg_4]
.text:00440120 80 7E 04 00 cmp byte ptr [esi+4], 0
.text:00440124 57 push edi
.text:00440125 C7 86 10 01 00 00 63 00+ mov dword ptr [esi+110h], 63h
.text:0044012F 74 0F jz short loc_440140
.text:00440131 83 3D 7A 4E 4B 00 00 cmp dword_4B4E7A, 0
.text:00440138 7F 06 jg short loc_440140
.text:0044013A 83 65 FC 00 and [ebp+var_4], 0
.text:0044013E EB 07 jmp short loc_440147
进行上述组装。 'E9 ?? ?? ?? ??' 将替换 '55 8B EC 81 EC'(?? ?? ?? ?? 是要跳转到的函数的地址),所以我看到它的方式是在堆栈上分配 0x410 用于当调用的新函数跳回到 0x44011B 处的原始函数时,不会出现局部变量;为什么这无关紧要?我没有看到我错过了什么。
如果您想重现允许您使用此方法挂钩函数的功能代码,请参阅 MyUsername112358 的回答
如您所见,如果您打算能够执行原始代码,则不能只在 5 字节边界处切断。您将不得不反汇编并复制整个覆盖前 5 个字节的指令。
你可以看看我的ia32hook
, which uses OllyDbg's disassembler to find out where to cut off. The relevant snippet:
#include "ollydisasm/disasm.h"
static inline unsigned long CleanBiteOff(const unsigned char *ptr, size_t amount)
{
static t_disasm disasm;
unsigned long size = 0;
do {size += Disasm(ptr+size, 16, ptr+size, &disasm, DISASM_SIZE);} while(size < amount);
return size;
}
返回的size
就是你需要复制的字节数。在复制这些字节的蹦床结束时,跳回到 (char*)func+size
,一切都恢复正常。
您链接的答案也没有刷新指令缓存,如果指令指针恰好指向您要修补的字节,将会出现严重问题。您应该暂停线程,并断言 eip
没有指向您要修补的字节。
如果这不是出于学习目的,请考虑改用 Microsoft Detours 之类的解决方案。