挂钩函数时,为什么覆盖前 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 之类的解决方案。