Hook API

先说说最近干什么,最近一直在分析《恶代》的样本,简单看了看免杀,分析到Lab9了,Lab10中有个隐藏进程的知识点,或许可以和HOOK结合起来,然后就是看书上HOOK API的知识点,实际上之前就看过了,一直没总结,然后就是忙实验室招新的问题,带带学弟学妹打打今年的极客,免费工具人,不过感觉目前今年极客re部分的题出的不是太好。

本文章记录《逆理》书中关于HOOK API的相关知识。

DLL注入来钩取API

这种技术有限制,就是只能勾取程序包含的dll中的API函数。

本文将通过HOOK计算器的SetWindowsText这个函数来使计算器输入数字,但显示中文字符。

xdbg调试

我们先直接用xdbg调试的方法实现。

首先这个SetWindowsText函数,有SetWindowsTextA,SetWindowsTextW,实际上也就是ASSCII和UNICODE的区别,这个计算器也就是用的SetWindowsTextW。

然后来说说这个首先要找这个程序什么地方调用了这个SetWindowsTextW函数,我用xdbg只能找到SetWindowsTextW这个函数的内部,没找到调用,于是我用ida的交叉引用去找到了调用。

接下来我们在去打断点,然后开始用xdbg去调试,最后断在了这里。

我们修改字符为中文字符

然后f9运行,查看结果。

变成了七。

DLL注入实现

接下来使用DLL注入实现Hook。这个过程需要对pe结构有一定的了解,比如说怎么去定位到想要的函数地址。

Hook原理

先说说Hook原理

  • 钩取calc.exe中user32.dll的SetWindowTextW函数。
  • 将其替换为我们自己写的My_SWT,然后修改传入SetWindowTextW的数据,也就是将第二个参数修改为中文字符。
  • 然后继续调用原SetWindowTextW函数。

书上的图片,可以帮助理解。

代码部分

我跟着书上的代码,自己也来写了写。

My_SWT函数,这个函数用来将数字变中文字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

BOOL WINAPI My_SWT(HWND hWnd, LPWSTR lpString)
{
const wchar_t* table = L"零一二三四五六七八九";
wchar_t temp;
int len, i, index;
len = wcslen(lpString);
for (i = 0; i < len; i++)
{
if (L'0' <= lpString[i] && lpString[i] <= L'9')
{
temp = lpString[i];
index = _wtoi((const wchar_t*)&temp);
lpString[i] = table[index];
}
}

return ((PFSETWINDOWTEXTW)orig_SWT)(hWnd, lpString);
}
//可以写个test去使用这个函数看看有没有错误。

可以看到使用的是wchar_t,因为这个exe使用的是SetWindowTextW,每个数字其实占两个字节,循环里面做的就是把字符数字转为10进制的数,然后在定义好的中文表中取值。最后再次调用真正的SetWindowTextW函数。

myhook函数

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

BOOL myhook(LPCSTR DLLname, PROC ori_func, PROC new_func)
{
HMODULE hMod;
PBYTE pAddr;
DWORD dwRVA;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
LPCSTR szLibName;
PIMAGE_THUNK_DATA IAT;
DWORD lpflOldProtect=NULL;

hMod = GetModuleHandle(NULL);
pAddr = (PBYTE)hMod;

pAddr += *((DWORD*)&pAddr[0x3C]);

dwRVA = *((DWORD*)&pAddr[0x80]);

pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod + dwRVA);

for (; pImportDesc->Name; pImportDesc++)
{

szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);
if (!_stricmp(szLibName, DLLname))
{

IAT = (PIMAGE_THUNK_DATA)((DWORD)hMod + pImportDesc->FirstThunk);
for (; IAT->u1.Function; IAT++)
{
if (IAT->u1.Function == (DWORD)ori_func)
{
//更改内存属性为可读可写
VirtualProtect((LPVOID)&IAT->u1.Function,4,PAGE_EXECUTE_READWRITE,&lpflOldProtect);
IAT->u1.Function = (DWORD)new_func;
//恢复内存属性
VirtualProtect((LPVOID)&IAT->u1.Function, 4, lpflOldProtect, &lpflOldProtect);

MessageBoxA(NULL, "The_Itach1", "The_Itach1", MB_OK);

return TRUE;
}
}
}
}
return FALSE;
}

上面的代码就是利用pe结构的知识,来找到calc.exe进程中SetWindowTextW的地址,然后换为我们的My_SWT函数地址,需要知道的是,这个函数所属的user32.dll是已经加载到了calc.exe的4GB的虚拟内存中了的,所以是映射,类似于CreateFileMapping这种,所以是用RVA去一步步找到函数地址。

全部代码myhook.dll

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

#include "pch.h"
#include "stdio.h"
#include "windows.h"
#include "wchar.h"

FARPROC orig_SWT = NULL;

typedef BOOL(WINAPI* PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);


BOOL WINAPI My_SWT(HWND hWnd, LPWSTR lpString)
{
const wchar_t* table = L"零一二三四五六七八九";
wchar_t temp;
int len, i, index;
len = wcslen(lpString);
for (i = 0; i < len; i++)
{
if (L'0' <= lpString[i] && lpString[i] <= L'9')
{
temp = lpString[i];
index = _wtoi((const wchar_t*)&temp);
lpString[i] = table[index];
}
}

return ((PFSETWINDOWTEXTW)orig_SWT)(hWnd, lpString);
}

BOOL myhook(LPCSTR DLLname, PROC ori_func, PROC new_func)
{
HMODULE hMod;
PBYTE pAddr;
DWORD dwRVA;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
LPCSTR szLibName;
PIMAGE_THUNK_DATA IAT;
DWORD lpflOldProtect=NULL;

hMod = GetModuleHandle(NULL);
pAddr = (PBYTE)hMod;

pAddr += *((DWORD*)&pAddr[0x3C]);

dwRVA = *((DWORD*)&pAddr[0x80]);

pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod + dwRVA);

for (; pImportDesc->Name; pImportDesc++)
{

szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);
if (!_stricmp(szLibName, DLLname))
{

IAT = (PIMAGE_THUNK_DATA)((DWORD)hMod + pImportDesc->FirstThunk);
for (; IAT->u1.Function; IAT++)
{
if (IAT->u1.Function == (DWORD)ori_func)
{
//更改内存属性为可读可写
VirtualProtect((LPVOID)&IAT->u1.Function,4,PAGE_EXECUTE_READWRITE,&lpflOldProtect);
IAT->u1.Function = (DWORD)new_func;
//恢复内存属性
VirtualProtect((LPVOID)&IAT->u1.Function, 4, lpflOldProtect, &lpflOldProtect);

MessageBoxA(NULL, "The_Itach1", "The_Itach1", MB_OK);

return TRUE;
}
}
}
}
return FALSE;
}


BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
//最初的SetWindowTextW的地址
orig_SWT = GetProcAddress(GetModuleHandle(L"user32.dll"),"SetWindowTextW");

myhook("user32.dll",orig_SWT,(PROC)My_SWT);
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

接下来用InjectDll.exe注入然后看看效果

xdbg调试Dllmain

这里调试看看究竟怎么回事,把xdbg选项设置好后,在dllmian断下来。

Dllmain函数

经过一些下断点调试后

然后进人我们的My_SWT函数。

基本上也就调试结束了。