当开发者需要在程序中扩展或者新增功能时,但是没有源码,在API Hooking的条件下,可以不知道源码而去修改程序执行流

Detours Hooking

Detours,是一个用于在 x86 机器上检测任意 Win32 函数的库,Detours 通过重写目标函数映像来拦截 Win32 函数,Detours 包还包含用于将任意 DLL 和数据段(payload)附加到任何 Win32 二进制文件的实用程序,现在也支持x64。

middle_detoursHook_1.PNG

Detour Function完全由攻击者控制

middle_detoursHook_2.PNG

这里 TargetFunction+5 的原因是因为jmp指令的长度是5字节,原来的代码从push ebp开始到push edi也是5字节

Demo

hookme.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib, "user32.lib")

int main(void){

printf("hookme.exe: Starting.\n");

MessageBox(NULL, "First message", "HOOKS", MB_OK);
MessageBox(NULL, "Second message", "HOOKS", MB_OK);
MessageBox(NULL, "Third message", "HOOKS", MB_OK);

printf("hookme.exe: Roger and out!\n");

return 0;
}

现在要Hook MessageBox

hookem.cpp

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
#include <stdio.h>
#include <windows.h>
#include "detours.h"
#pragma comment(lib, "user32.lib")

// pointer to original MessageBox
// 要hook messageBox,需要messagebox的地址
int (WINAPI * pOrigMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) = MessageBox;
BOOL Hookem(void);
BOOL UnHookem(void);

// Hooking function
// Detour Function
int HookedMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) {

printf("HookedMessageBox() called. No popup on screen!\n");

return IDOK;
}

// Set hooks on MessageBox
BOOL Hookem(void) {

LONG err;
// 通过Detour进行的Hook都是经Transaction实现的
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)pOrigMessageBox, HookedMessageBox);
err = DetourTransactionCommit();

printf("MessageBox() hooked! (res = %d)\n", err);

return TRUE;
}

// Revert all changes to original code
BOOL UnHookem(void) {

LONG err;

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)pOrigMessageBox, HookedMessageBox);
err = DetourTransactionCommit();

printf("Hook removed from MessageBox() with result = %d\n", err);

return TRUE;
}

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved) {

switch (dwReason) {
case DLL_PROCESS_ATTACH:
// dll加载的时候Hook
Hookem();
break;

case DLL_THREAD_ATTACH:
break;

case DLL_THREAD_DETACH:
break;

case DLL_PROCESS_DETACH:
// dll卸载的时候unhook
UnHookem();
break;
}

return TRUE;
}

编译成dll然后注入

也可以xdbg里在messagebox处打断点,看执行的汇编代码变化

IAT Hooking

修改导入表里函数的地址,通过获取当前进程的指定API的地址,然后拿去和IAT表里的匹配,如果匹配上就修改地址

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
#include <stdio.h>
#include <windows.h>
#include <dbghelp.h>

#pragma comment(lib, "user32.lib")
#pragma comment (lib, "dbghelp.lib")

// pointer to original MessageBox
int (WINAPI * pOrigMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) = MessageBox;


// Hooking function
int HookedMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) {

printf("HookedMessageBox() called. No popup on screen!\n");

pOrigMessageBox(hWnd, "You loose!", "TROLOLOLO", uType);
return IDOK;
}


// Set hook on origFunc()
BOOL Hookem(char * dll, char * origFunc, PROC hookingFunc) {

ULONG size;
DWORD i;
BOOL found = FALSE;

// get a HANDLE to a main module == BaseImage
HANDLE baseAddress = GetModuleHandle(NULL);

// get Import Table of main module
PIMAGE_IMPORT_DESCRIPTOR importTbl = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToDataEx(
baseAddress,
TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&size,
NULL);

// find imports for target dll
for (i = 0; i < size ; i++){
char * importName = (char *)((PBYTE) baseAddress + importTbl[i].Name);
if (_stricmp(importName, dll) == 0) {
found = TRUE;
break;
}
}
if (!found)
return FALSE;

// Optimization: get original address of function to hook
// and use it as a reference when searching through IAT directly
PROC origFuncAddr = (PROC) GetProcAddress(GetModuleHandle(dll), origFunc);

// Search IAT
PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA) ((PBYTE) baseAddress + importTbl[i].FirstThunk);
while (thunk->u1.Function) {
PROC * currentFuncAddr = (PROC *) &thunk->u1.Function;

// found
if (*currentFuncAddr == origFuncAddr) {

// make sure memory is writable
DWORD oldProtect = 0;
VirtualProtect((LPVOID) currentFuncAddr, 4096, PAGE_READWRITE, &oldProtect);

// set the hook
*currentFuncAddr = (PROC)hookingFunc;

// revert protection setting back
VirtualProtect((LPVOID) currentFuncAddr, 4096, oldProtect, &oldProtect);

printf("IAT function %s() hooked!\n", origFunc);
return TRUE;
}
thunk++;
}

return FALSE;
}

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved) {

switch (dwReason) {
case DLL_PROCESS_ATTACH:
Hookem("user32.dll", "MessageBoxA", (PROC) HookedMessageBox);
break;

case DLL_THREAD_ATTACH:
break;

case DLL_THREAD_DETACH:
break;

case DLL_PROCESS_DETACH:
break;
}

return TRUE;
}

In-line Hooking

通过修改机器码的方式来实现hook

可能会读取或者修改call指令执行之前所压入栈的内容。那么,我们可以将call指令替换成jmp指令,jmp到我们自己编写的函数,在函数里call原来的函数,函数结束后再jmp回到原先call指令的下一条指令

middle_detoursHook_3.PNG

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
#include <stdio.h>
#include <windows.h>
#include <dbghelp.h>

#pragma comment(lib, "user32.lib")
#pragma comment (lib, "dbghelp.lib")

#define ORIG_BYTES_SIZE 14

BOOL Hookem(FARPROC hookingFunc);
// pointer to original MessageBox
typedef int (WINAPI * OrigMessageBox_t)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
OrigMessageBox_t pOrigMessageBox = NULL;

// storage for original bytes from MessageBox
char OriginalBytes[ORIG_BYTES_SIZE] = { 0 };


// Hooking function
int HookedMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) {
SIZE_T bytesOut = 0;

printf("HookedMessageBox() called. No popup on screen!\n");
// 跳回原来的函数
WriteProcessMemory(GetCurrentProcess(), (LPVOID)pOrigMessageBox, OriginalBytes, ORIG_BYTES_SIZE, &bytesOut);
pOrigMessageBox(hWnd, lpText, lpCaption, uType);
Hookem((FARPROC) HookedMessageBox);

return IDOK;
}


// Set a hook by patching code
BOOL Hookem(FARPROC hookingFunc) {

SIZE_T bytesIn = 0;
SIZE_T bytesOut = 0;

// save original address of MessageBoxA
pOrigMessageBox = (OrigMessageBox_t) GetProcAddress(GetModuleHandle("user32.dll"), "MessageBoxA");

// copy ORIG_BYTES_SIZE btes of original code from MessageBoxA
ReadProcessMemory(GetCurrentProcess(), pOrigMessageBox, OriginalBytes, ORIG_BYTES_SIZE, &bytesIn);

// src: https://www.ragestorm.net/blogs/?p=107
// create a patch <14 bytes> with JMP [RIP+0]; <ADDR64>
// \xFF\x25\x00\x00\x00\x00 这6个字节是jmp的字节码
// \x00\x11\x22\x33\x44\0x55\x66\x77
char patch[14] = { 0 };
memcpy(patch, "\xFF\x25", 2);
memcpy(patch + 6, &hookingFunc, 8);

// patch the MessageBoxA
WriteProcessMemory(GetCurrentProcess(), (LPVOID) pOrigMessageBox, patch, sizeof(patch), &bytesOut);

printf("IAT MessageBoxA() hooked!\n");
printf("HookedMessageBox @ %p ; OriginalBytes @ %p\n", HookedMessageBox, OriginalBytes);

return FALSE;
}


BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved) {

switch (dwReason) {
case DLL_PROCESS_ATTACH:
Hookem((FARPROC) HookedMessageBox);
break;

case DLL_THREAD_ATTACH:
break;

case DLL_THREAD_DETACH:
break;

case DLL_PROCESS_DETACH:
break;
}

return TRUE;
}

但是Hook的函数再往原先的函数跳比较难以实现,包括参数、上下文等不太好恢复