云游戲的鍵鼠捕捉用到了 Hook 這個技術,就順便翻了一下核心編程,寫下來備忘。
使用注冊表注入
- x32 :
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows - x64 :
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows
這個位置下 AppInit_DLLs 和 LoadAppInit_DLLs。 將后者置為 1,然后在前者的值輸入待注入 DLL 名稱(多個 DLL 用 Space 分隔,第一個可以帶路徑,后邊的不行)。
- 優點:方法簡單
- 缺點:只有引用了 User32.dll 的才會被注入,而且被注入進程一啟動就會注入,結束才會反注入,注入周期不可控
使用 Windows Hook 注入
核心方法:
SetWindowsHookExUnhookWindowsHookEx
設置掛鉤的時候,可以指定掛鉤類型、線程 ID 、回調方法、DLL 句柄。這些已經可以準確的注入到一個進程中了。通過 Set、Unhook 兩個方法可以準確的控制注入周期。
- 優點:準確的控制注入周期
- 缺點:依賴消息循環,沒消息循環的線程沒法注入
遠程線程注入(大殺器)
核心方法:
CreateRemoteThreadVirtualAllocExVirtualFreeExReadProcessMemoryWriteProcessMemory
可以用 CreateRemoteThread 在指定進程中創建一個線程,讓它執行我們自己的代碼,這樣可以讓遠程線程 Load 一個我們自己的 DLL,這就可以為所欲為了……需要注意的是,CreateRemoteThread 的參數 PTHREAD_START_ROUTINE 這個函數地址,需要調用 GetProcAddress 。如果直接傳函數地址,是本進程的……顯然是不對的。
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
而函數參數也要使用 VirtualAllocEx 來分配內存,然后 WriteProcessMemory 寫入。這才是遠程進程可以獲取的。
- 優點:可以為所欲為
- 缺點:寫起來有點復雜
同名 DLL 替換
如果已知一個程序必然載入一個 DLL ,則可以做一個同名 DLL 然后將方法跟原 DLL 保持一致,在方法調用中間做點其他手腳。這種如果程序校驗 DLL ,就沒戲。或者直接修改程序的 EXE 的導入段,這就要求對 PE 結構非常熟悉。
作為調試器注入
核心編程沒說的太細,看起來要寫 CPU 代碼,沒太細看。感覺也挺復雜,不太實用。
修改子進程的主線程開始位置代碼
如果要注入的進程是子進程,可以創建它的時候掛起它,然后從 exe 模塊中拿到子進程的主線程起始地址,把這里記下來,之后改成執行自己的代碼,這個時候恢復子進程主線程,就可以執行自己的代碼了,然后再把之前保存的執行一波。看起來也挺復雜,核心編程也沒給具體例子。
API 攔截
第一種:把要攔截的 API 起始位置的幾個字節保存起來,然后將此位置改寫為 CPU 的 JUMP 指令,跳轉到自己的方法。不過這種方法非常危險,不建議用。
第二種:修改模塊導入段攔截 API
核心方法:
ImageDirectoryEntryToDataWriteProcessMemoryVirtualProtect
獲取到模塊的導入段信息,之后查找到指定方法,用自己的方法替換原來的方法。如果 WriteProcessMemory 失敗,就用 VirtualProtect 修改一下頁面保護屬性。