(简称逆向工程核心原理第二十一章)
HookMain
冷静分析(好久没碰typedef都快忘了)
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
| #include "stdio.h" #include "conio.h" #include "windows.h"
#define DEF_DLL_NAME "KeyHook.dll" #define DEF_HOOKSTART "HookStart" #define DEF_HOOKSTOP "HookStop"
typedef void (*PFN_HOOKSTART)(); typedef void (*PFN_HOOKSTOP)();
void main() { HMODULE hDll = NULL; PFN_HOOKSTART HookStart = NULL; PFN_HOOKSTOP HookStop = NULL; char ch = 0;
hDll = LoadLibraryA(DEF_DLL_NAME); if( hDll == NULL ) { printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError()); return; }
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART); HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
HookStart();
printf("press 'q' to quit!\n"); while( _getch() != 'q' ) ;
HookStop(); FreeLibrary(hDll); }
|
KeyHook
DllMain
动态链接库的可选入口点,都进程启动或者终止进程或线程,它会使用进程的第一个线程,为每个加载的DLL调用入口点函数
(说白了就是加载卸载dll都要先执行DllMain)
DllMain 入口点
1 2 3 4 5
| BOOL WINAPI DllMain( _In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved );
|
于是乎在KeyHook.dll是这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved) { switch( dwReason ) { case DLL_PROCESS_ATTACH: g_hInstance = hinstDLL; break;
case DLL_PROCESS_DETACH: break; }
return TRUE; }
|
SetWindowsHookEx
SetWindowsHookEx
钩子过程(hook procedure)是由操作系统调用的回调函数。安装消息钩子时,钩子过程还需存在某个DLL内部,且该DLL的实例句柄即使hMod
PS: 若dwThreadId参数被设置为0,则安装的钩子是全局钩子,它会影响到运行中的所有进程
像这样使用SetWindowsHookEx设置钩子,操作系统会将相关DLL文件强行注入进程,非常方便
1 2 3 4 5 6
| HHOOK SetWindowsHookExA( [in] int idHook, [in] HOOKPROC lpfn, [in] HINSTANCE hmod, [in] DWORD dwThreadId );
|
于是在KeyHook.dll里是这样的,注意declspec代表是导出函数的关键字
也是从这调用了SetWindowsHookEx
1 2 3 4 5
| __declspec(dllexport) void HookStart() { g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0); }
|
KeyboardProc 回调函数
调用导出函数HookStart时,SetWindowsHookEx就会将KeyboardProc放入钩链!
- 在调用HookStart时,SetWindowsHookEx就会将KeyboardProc添加到钩链,安装好钩子,无论哪个进程,只要发生键盘输入事件,OS就强制把KeyHook.dll注入相应进程
- 发生键盘事件,就会首先调用执行KeyHook.KeyboardProc()
- KeyboardProc函数中发生输入事件,就会比较当前进程是否是Notepad.exe
- 如果是就直接返回1,终于该函数,这意味着截获且删除消息(这样键盘消息就不会传递到notepad.exe程序的相关队列)
- 如果不是notepad就执行CallNextHookEx,这意味着消息会被传递到另一个程序或者钩链的另一个钩子
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
|
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { char szPath[MAX_PATH] = {0,}; char *p = NULL;
if( nCode >= 0 ) { if( !(lParam & 0x80000000) ) { GetModuleFileNameA(NULL, szPath, MAX_PATH); p = strrchr(szPath, '\\');
if( !_stricmp(p + 1, DEF_PROCESS_NAME) ) return 1; } }
return CallNextHookEx(g_hHook, nCode, wParam, lParam); }
|
逐个分析完,再看看这简单的KeyHook.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
| #include "stdio.h" #include "windows.h"
#define DEF_PROCESS_NAME "notepad.exe"
HINSTANCE g_hInstance = NULL; HHOOK g_hHook = NULL; HWND g_hWnd = NULL;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved) { switch( dwReason ) { case DLL_PROCESS_ATTACH: g_hInstance = hinstDLL; break;
case DLL_PROCESS_DETACH: break; }
return TRUE; }
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { char szPath[MAX_PATH] = {0,}; char *p = NULL;
if( nCode >= 0 ) { if( !(lParam & 0x80000000) ) { GetModuleFileNameA(NULL, szPath, MAX_PATH); p = strrchr(szPath, '\\');
if( !_stricmp(p + 1, DEF_PROCESS_NAME) ) return 1; } }
return CallNextHookEx(g_hHook, nCode, wParam, lParam); }
#ifdef __cplusplus extern "C" { #endif
__declspec(dllexport) void HookStart() { g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0); }
__declspec(dllexport) void HookStop() { if( g_hHook ) { UnhookWindowsHookEx(g_hHook); g_hHook = NULL; } } #ifdef __cplusplus } #endif
|
调试HookMain.exe
通过搜索字符串找到关键函数处
现在是去找KeyHook.HookStart函数
进入到HookStart函数,可以发现在KeyboardProc的地址为10001020
调试Notepad.exe内的KeyHook.dll
1. Attch Notepad.exe
2. 设置Break on new moudle
开启该选项后,每当有新的DLL装入,就会弹出加载的DLL
3. 开启钩子
已经挂起了钩子,现在只要触发键盘事件,就会强制注入KeyHook.dll
4. DLL强制注入
在OD按F9让notepad处于可输入状态,在notepad输入字符,OD会自动弹出,可以发现KeyHook.dll已经注入
5. 调试KeyboardProc函数
在调试HookMain的时候知道的KeyHook.KeyboardProc的地址,于是断点在这按个F9即可跑到这
逐条调试,即可理解大部分意思,不过arg.1和local.66我还不太清楚具体是什么意思,应该是全局和局部的
再附上一张作者写的流程