【乾貨】Windows進程注入之SetWindowsHookEx

SetWindowsHookEx

百度百科segmentfault

鉤子(Hook),是Windows消息處理機制的一個平臺,應用程序能夠在上面設置子程以監視指定窗口的某種消息,並且所監視的窗口能夠是其餘進程所建立的。當消息到達後,在目標窗口處理函數以前處理它。鉤子機制容許應用程序截獲處理Windows消息或特定事件

瞭解 Windows 窗口程序的朋友都知道-窗口程序是靠消息驅動的,一切過程皆是消息,本來的消息傳遞過程是:系統接收到消息-將消息放入系統消息隊列-將消息放入線程消息隊列-線程中處理該消息,而鉤子函數SetWindowsHookEx的做用就是攔截消息,用自定義的函數處理消息windows

  • 設置全局鉤子,會攔截全部窗口線程的消息
  • 設置線程鉤子,則只會攔截特定線程的消息

注意:當攔截當前線程消息時能夠在當前代碼中寫回調函數,當攔截其它線程消息時則必須調用dll中回調函數函數

參數含義spa

HHOOK WINAPI SetWindowsHookEx(
int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId);
/*
idHook:鉤子的類型,即它處理的消息類型
lpfn:指向dll中的函數或者指向當前進程中的一段代碼
hMod:dll的句柄
dwThreadId:線程ID,當此參數爲0時表示設置全局鉤子
*/

全局鉤子

注意事項
設置全局鉤子只須要將SetWindowsHookEx的第四個參數設爲0便可,全局鉤子會攔截每個窗口線程的消息,而後自動加載該 dll 並調用 dll 中的回調函數。注意32位程序加載32位dll注入32位進程,64位同理線程

完整代碼
下面是一個包含 DllMain 函數的標準 dll 模板,咱們先編譯一個 dll 方便以後注入code

#include <windows.h>
#include <tchar.h>

BOOL test()
{
    MessageBox(NULL, TEXT("SetWindowsHookEx Success!"), TEXT("Tips"), MB_OK);
    return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpvRevered)
{
    // MessageBox(NULL, TEXT("This is DllMain!"), TEXT("Tips"), MB_OK);
    switch (dwReason)
    {
    case DLL_PROCESS_ATTACH:
        /* 只有第一次會進到這裏,以後再次Attach不會產生DLL_PROCESS_ATTACH,只會增長DLL的調用次數 */
        MessageBox(NULL, TEXT("Process Load Dll Success!"), TEXT("Tips"), MB_OK);
        break;
    case DLL_PROCESS_DETACH:
        // MessageBox(NULL, TEXT("Process Unload Dll Success!"), TEXT("Tips"), MB_OK);
        break;
    case DLL_THREAD_ATTACH:
        // MessageBox(NULL, TEXT("Thread load Dll Success!"), TEXT("Tips"), MB_OK);
        break;
    case DLL_THREAD_DETACH:
        // MessageBox(NULL, TEXT("Thread Unload Dll Success!"), TEXT("Tips"), MB_OK);
        break;
    }
    return TRUE;
}

使用命令gcc test2.c -shared -o test2.dll編譯成 dll 文件,而後設置全局鉤子向其它線程注入該 dll,鉤子代碼以下隊列

#include <windows.h>

int main(int argc, char *argv)
{
    int i;
    HMODULE hdll = LoadLibrary("test2.dll");
    HOOKPROC hproc = (HOOKPROC)GetProcAddress(hdll, "test");
    HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, hproc, hdll, 0);
    for(i=0;i<30;i++){
        Sleep(2000);
    }
    UnhookWindowsHookEx(hook);
    return 0;
}

使用命令gcc Inject_dll1_1.c -o Inject_dll1_1編譯成可執行文件,Sleep的做用是保證當前進程存活,進程結束全局鉤子就會被卸載進程

運行截圖
雙擊程序運行,顯示當前進程已加載該dll事件

clipboard.png

而後在桌面按下回車鍵ip

clipboard.png

在任意窗口按下回車鍵

clipboard.png

特定線程鉤子

前置技能
遍歷Windows進程
遍歷Windows進程模塊

注意事項
安裝全局鉤子不太穩定,由於攔截了全部窗口的消息,處理不當就會形成系統崩潰!因此爲了穩定性咱們可使用特定的線程ID來安裝線程消息鉤子,鉤子代碼和上面的代碼差很少,核心就是要獲取線程的ID

我主要是用CreateToolhelp32Snapshot建立進程快照而後配合TH32CS_SNAPPROCESS TH32CS_SNAPTHREAD這兩個結構體獲取線程ID!固然還能夠用GetWindowsThreadProcessId配合窗口句柄快速獲取進程ID而後獲得線程ID

完整代碼
dll 和上面全局鉤子的 dll 相同,核心的鉤子代碼以下

#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>

int getThreadID(int pid)
{
    THREADENTRY32 te32;
    te32.dwSize = sizeof(te32);
    HANDLE Snapshot_thread = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if (Snapshot_thread != INVALID_HANDLE_VALUE)
    {
        if (Thread32First(Snapshot_thread, &te32))
        {
            do
            {
                if (te32.th32OwnerProcessID == pid)
                {
                    return te32.th32ThreadID;
                }
            } while (Thread32Next(Snapshot_thread, &te32));
        }
    }
    CloseHandle(Snapshot_thread);
    return 0;
}

int getPID(char *target)
{
    PROCESSENTRY32 pe32;
    MODULEENTRY32 me32;
    HANDLE hProcess, hSnapshot_proc, hSnapshot_mod;
    pe32.dwSize = sizeof(pe32);
    hSnapshot_proc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (Process32First(hSnapshot_proc, &pe32))
    {
        do
        {
            if (!strcmp(pe32.szExeFile, target))
            {
                return pe32.th32ProcessID;
            }
        } while (Process32Next(hSnapshot_proc, &pe32));
    }
    CloseHandle(hSnapshot_proc);
    return 0;
}

int main(int argc, char *argv)
{
    int i;
    HMODULE hdll = LoadLibrary("test2.dll");
    HOOKPROC hproc = (HOOKPROC)GetProcAddress(hdll, "test");
    HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, hproc, hdll, getThreadID(getPID("calc.exe")));
    for (i = 0; i < 30; i++)
    {
        Sleep(2000);
    }
    UnhookWindowsHookEx(hook);
    return 0;
}

編譯命令gcc Inject_dll1_2.c -o Inject_dll1_2

運行截圖
我注入的是計算器的線程,因此須要先打開計算器

clipboard.png

而後運行該鉤子程序

clipboard.png

到計算器窗口按下回車鍵

clipboard.png

END

相關文章
相關標籤/搜索