Inline Hook

Inline 原理与流程

Inline hook 就是在运行的流程中插入跳转指令(call/jmp)来抢夺程序运行流程的一个方法。

在编程中我们常用if else或其他语句来控制程序的流程,而在汇编中使用的是cmp和jmp等指令,

cmp是比较,jmp是无条件跳转。

jmp指令后面跟随一个地址,像这样:jmp 0x1234567

inline hook中用到的就是jmp指令,在c中的函数编译之后函数名实际上就是地址,也就是函数所在的内存位置,假设我们想hook一个函数,就跳转到这个函数的首地址,将他的第一条指令修改为jmp指令,这将会覆盖函数原本存在的指令,jmp的地址就是我们自己写的函数。

在执行我们自己写的函数之后,先执行函数中被我们(jmp)覆盖的指令之后,再次调用jmp指令,跳转回去hook之前的代码中执行,不要忘记备份寄存器。

需要注意的地方

  1. inline hook 的一般流程:
    源程序流 -> jmpIn -> 保存寄存器 -> 具体处理 -> 恢复寄存器 -> jmpOut -> 源程序流
    ​ 被跳转指令覆盖的代码可以在 jmpIn之后 或者 jmpOut之前
  2. 被覆盖的指令大小和大于等于5,因为我们要塞进去5个字节(0xE9 + Address)。只要你处理跳转指令覆盖的指令,理论上可以 inline hook 函数中的任意位置。
  3. 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()
{
// moved bytes
__asm
{
nop
nop
nop
nop
nop
}

// save regs
// __asm
// {
// pushad
// pushfd
// }


// do something (or call specific function.)
__asm
{
add [esp + 0x08], 1
add [esp + 0x0C], 1
}

// store old regs
// __asm
// {
// popfd
// popad
// }

// jump back code
__asm
{
nop
nop
nop
nop
nop
}
}


void inlineHook()
{
char* pMovedBytes = &((char*)hookProxy)[0];
char* pJmpBackCode = &((char*)hookProxy)[0xF];

// move old bytes
memcpy((char*)hookProxy, (char*)add, 5);

// fill jump in code
char jmpInBuffer[5];
jmpInBuffer[0] = 0xE9;

int* pJmpInOffset = (int*)&jmpInBuffer[1];
*pJmpInOffset = (char*)hookProxy - ((char*)add + 5);

memcpy((char*)add, jmpInBuffer, 5);

// fill jump out code
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;
}

解析过程

  1. 把Add函数的前5个字节搬到了 hookProxy的前5个字节。

  2. 然后再 Add函数的前5个字节填充跳转到 hookProxy 的跳转指令

  3. 在hookProxy的末尾5个字节填充跳转回 Add函数 + 5 的跳转指令

  4. 原本 Add 函数的结构是3,被 hook 之后会变成 5

有兴趣的读者可以上机调试一番

Author: YuanBi
Link: https://www.basicbit.cn/2018/01/16/2018-11-18-Inline Hook/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.