MSDN官方文檔給出的定義:
HHOOK SetWindowsHookEx( int idHook, // hook type
HOOKPROC lpfn, // hook procedure
HINSTANCE hMod, // handle to application instance
DWORD dwThreadId // thread identifier);
idHook:掛鉤類型,即處理的消息類型;我此處測試的是WH_KEYBOARD
lpfn:掛鉤函數的地址指針。如果是全局鉤子,即dwThreadId爲0或者Hook的進程不是當前進程,那麼lpfn必須指向DLL中的掛鉤函數。除此之外,lpfn可指向當前進程的一段掛鉤函數代碼。當鉤到消息後,執行此函數。爲適用性起見,我在代碼中用的是從DLL中導出的函數地址。
hMod:應用程序的的實例句柄,指向包含lpfn指針的DLL。
dwThreadId:如果爲0,則表示該鉤子與所有線程相關聯,爲全局掛鉤。
函數失敗,返回NULL,成功返回鉤子句柄。
實現思路:
- 從控制檯得到想要實施注入的目標進程名字
- 得到當前進程所在的目錄(GetCurrentDirectory),並保存
- 得到當前進程的位數 (IsWow64Process)
- 根據進程名字得到當前進程的Id
- 根據進程Id得到當前進程的完整路徑
- 通過進程完整路徑對PE文件解析得到目標進程位數
- 目標與當前進程的位數進行匹配,決定加載哪一個dll(x86 or x64)
- 根據當前進程目錄,得到dll完整路徑
- 通過目標進程Id返回目標進程中所有線程的Id
- 通過LoadLibrary加載目標動態庫,得動態庫模塊句柄
- GetProcAddress得到導出函數的函數指針
- SetWindowsHookEx實施對某個線程的注入
- 收尾工作,卸載鉤子
#include"SetWindowsHookEx.h"
#include"Helper.h"
int _tmain(int argc, TCHAR* argv[], TCHAR *envp[])
{
//控制檯識別中文
setlocale(LC_ALL, "Chinese-simplified");
TCHAR ProcessImageName[MAX_PATH] = { 0 };//保存進程名字
TCHAR CurrentFullPath[MAX_PATH] = { 0 }; //當前進程的完整路徑
TCHAR TargetProcessFullPath[MAX_PATH] = { 0 };//目標進程的完整路徑
ULONG_PTR TargetProcessPathLength = MAX_PATH;
ULONG ProcessId = 0;//目標進程Id
vector<HANDLE> ThreadId;//線程ID
HANDLE ProcessHandle = INVALID_HANDLE_VALUE;//進程句柄
HMODULE ModuleBase = NULL;//Dll模塊句柄
FARPROC InjectFunction = NULL;//接導出函數的指針
HHOOK HookHandle = NULL;//SetWindowsHookEx返回值
BOOL IsOk = FALSE;
//注入的啓動程序和目標程序的位數
BOOL SourceIsWow64 = FALSE;
BOOL TargetIsWow64 = FALSE;
_tprintf(_T("輸入一個進程ImageName\r\n"));
TCHAR RcceiveChar = _gettchar();//接受字符串
int i = 0;//用來偏移ProcessName字符數組
while (RcceiveChar != '\n')
{
ProcessImageName[i++] = RcceiveChar;
RcceiveChar = _gettchar();
}
GetCurrentDirectory(MAX_PATH, CurrentFullPath);//保存當前進程的完整路徑
IsWow64Process(GetCurrentProcess(), &SourceIsWow64);//得到當前進程位數
ProcessId = KtGetProcessIdentify(ProcessImageName);//通過進程名得到進程Id
if (ProcessId == 0)
{
return 0;
}
IsOk = KtGetProcessFullPath(TargetProcessFullPath,
&TargetProcessPathLength, ProcessId, FALSE);
if (IsOk == FALSE)
{
return 0;
}
//判斷目標進程位數
KtIsWow64Process(TargetProcessFullPath, &TargetIsWow64);
if (SourceIsWow64 == TRUE && TargetIsWow64 == TRUE)
{
_tcscat_s(CurrentFullPath, _T("\\Dll.dll"));
}
else if (SourceIsWow64 == FALSE && TargetIsWow64 == FALSE)
{
_tcscat_s(CurrentFullPath, _T("\\Dll.dll"));
}
//_tcscat_s(CurrentFullPath, _T("\\Dll.dll"));虛擬機測試
//KtGetThreadIdentify我自己封裝的內部使用CreateToolhelp32Snapshot系列的函數處理的
if (KtGetThreadIdentify((HANDLE)ProcessId, ThreadId) == FALSE)
{
goto Exit;
}
//加載動態庫
ModuleBase = LoadLibrary(CurrentFullPath);
if (ModuleBase == NULL)
{
goto Exit;
}
//獲得導出函數指針
InjectFunction = GetProcAddress(ModuleBase, "InjectFunction");
if (InjectFunction == NULL)
{
goto Exit;
}
for (int i = 0; i < ThreadId.size(); ++i)
{
//對其中的某個線程實施注入
HookHandle = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)InjectFunction, ModuleBase, (DWORD)ThreadId[i]);
if (HookHandle != NULL)
{
//只要有一個線程注入成功,就退出
break;
}
}
_tprintf(_T("Input AnyKey To Exit"));
getchar();
Exit:
if (HookHandle != NULL)
{
//卸載鉤子
UnhookWindowsHookEx(HookHandle);
HookHandle = NULL;
}
if (!!(ThreadId.size()))
{
vector<HANDLE>().swap(ThreadId);
}
if (ModuleBase != NULL)
{
FreeLibrary(ModuleBase);
ModuleBase = NULL;
}
}
注入成功截圖:按鍵就會產生彈窗效果
64位測試截圖:
卸載之後我也進行了鍵盤按鍵,不會彈出來了。
不忘初心,方得始終