Inline 原理与流程
Inline hook 就是在运行的流程中插入跳转指令(call/jmp)来抢夺程序运行流程的一个方法。
在编程中我们常用if else或其他语句来控制程序的流程,而在汇编中使用的是cmp和jmp等指令,
cmp是比较,jmp是无条件跳转。
jmp指令后面跟随一个地址,像这样:jmp 0x1234567
inline hook中用到的就是jmp指令,在c中的函数编译之后函数名实际上就是地址,也就是函数所在的内存位置,假设我们想hook一个函数,就跳转到这个函数的首地址,将他的第一条指令修改为jmp指令,这将会覆盖函数原本存在的指令,jmp的地址就是我们自己写的函数。
在执行我们自己写的函数之后,先执行函数中被我们(jmp)覆盖的指令之后,再次调用jmp指令,跳转回去hook之前的代码中执行,不要忘记备份寄存器。
需要注意的地方
- inline hook 的一般流程:
源程序流 -> jmpIn -> 保存寄存器 -> 具体处理 -> 恢复寄存器 -> jmpOut -> 源程序流
被跳转指令覆盖的代码可以在 jmpIn之后 或者 jmpOut之前
- 被覆盖的指令大小和大于等于5,因为我们要塞进去5个字节(0xE9 + Address)。只要你处理跳转指令覆盖的指令,理论上可以 inline hook 函数中的任意位置。
- x86函数调用的开头一般都是下面这个样子的,能很方便的 inline hook
1 2 3
| mov edi, edi push ebp mov ebp, esp
|
jmp 公式:
高地址向低地址跳 – 向前跳:
E9指令地址(4011d2)减去跳转指令地址(401000)
等于偏移大小,偏移大小取反,加一,减五,就是
E9后面的偏移地址
低地址向高地址跳 – 向后跳:
跳转指令地址(4011C1)减去E9偏移地址(401040)
等于偏移大小,偏移大小减五,就是E9后面的偏移地址
Inline Hook实例分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| #include "stdafx.h" __declspec(naked) int add(int a, int b) { __asm { mov edi, edi push ebp mov ebp, esp sub esp, 8 mov eax, [ebp + 0x08] mov [ebp - 4], eax mov eax, [ebp + 0x0C] mov [ebp - 8], eax mov eax, [ebp - 4] add eax, [ebp - 8] mov esp, ebp pop ebp ret } } __declspec(naked) void hookProxy() { __asm { nop nop nop nop nop } __asm { add [esp + 0x08], 1 add [esp + 0x0C], 1 } __asm { nop nop nop nop nop } } void inlineHook() { char* pMovedBytes = &((char*)hookProxy)[0]; char* pJmpBackCode = &((char*)hookProxy)[0xF]; memcpy((char*)hookProxy, (char*)add, 5); char jmpInBuffer[5]; jmpInBuffer[0] = 0xE9; int* pJmpInOffset = (int*)&jmpInBuffer[1]; *pJmpInOffset = (char*)hookProxy - ((char*)add + 5); memcpy((char*)add, jmpInBuffer, 5); char jmpOutBuffer[5]; jmpOutBuffer[0] = 0xE9; int* pJmpOutOffset = (int*)&jmpOutBuffer[1]; *pJmpOutOffset = ((char*)add + 5) - ((char*)pJmpBackCode + 5); memcpy(pJmpBackCode, jmpOutBuffer, 5); } int _tmain(int argc, _TCHAR* argv[]) { _asm int 3; DWORD oldProtect; if( VirtualProtect((LPVOID)add, 4096, PAGE_EXECUTE_READWRITE, &oldProtect) ) { inlineHook(); } printf("result = %d \n", add(1, 2)); system("pause"); return 0; }
|
解析过程
把Add函数的前5个字节搬到了 hookProxy的前5个字节。
然后再 Add函数的前5个字节填充跳转到 hookProxy 的跳转指令
在hookProxy的末尾5个字节填充跳转回 Add函数 + 5 的跳转指令
原本 Add 函数的结构是3,被 hook 之后会变成 5
有兴趣的读者可以上机调试一番