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); }
|
可以看到使用的是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: 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函数。
基本上也就调试结束了。