首發於Enaium的個人博客
測試用例
首先我使用CLion寫了一個簡單的程序,這個程序會加載一個dinput8.dll,然後調用一個函數顯示一段文字,然後等待用户按下任意鍵。這個程序的代碼如下:
#include<windows.h>
#include<iostream>
int display(const char *text) {
std::cout << text << std::endl;
std::cout << "Press any key to continue..." << std::endl;
std::cin.ignore();
return 0;
}
int main() {
LoadLibrary("dinput8.dll");
printf("Address: %p\n", display);
display("Hello World!");
}
我們所做的就是 Hook 這個display函數,然後在這個函數被調用時,將這個文字改為另一個文字。之後將項目進行編譯,別忘了在CMakeLists.txt中添加set(CMAKE_EXE_LINKER_FLAGS "-static"),這樣我們就可以得到一個靜態鏈接的可執行文件。之後我們就可以開始 Hook 這個函數了。
DLL 劫持
首先我們需要了解一下 DLL 的加載機制,Windows 系統在加載 DLL 時,會按照一定的順序搜索 DLL 文件,如果找到了就加載,如果沒有找到就會報錯。這個順序是怎麼樣的呢?我們可以通過查看官方文檔來了解。簡單來説就是首先搜索應用程序目錄,然後搜索系統目錄,最後搜索環境變量中指定的路徑。所以我們可以使用這個機制來劫持 DLL。
包裝 DLL
上面我們知道了 DLL 的加載機制,那麼我們就可以知道如何讓程序加載我們自己的 DLL,加載我們的 DLL 後,我們需要讓這個 DLL 也擁有原 DLL 的功能,這裏我們需要對原 DLL 進行包裝。我們可以使用wrap_dll這個工具來包裝 DLL。這是個Python腳本,所以你必須安裝了Python,之後這個腳本還需要dumpbin.exe和undname.exe這兩個工具,這兩個工具是 Visual Studio 自帶的,所以你需要安裝了Visual Studio,並且需要將C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\bin\Hostx64\x64設置到環境變量中。之後你就可以使用這個腳本了。
python .\wrap_dll.py "C:\Windows\System32\dinput8.dll"
運行完成這個條命令後,你會得到一個dinput8項目,這個就是我們包裝的 DLL。我們可以先把項目中的empty.h和hook_macro.h給刪掉,這兩個文件對我們沒有用。
MinHook
MinHook 是一個輕量級的鈎子庫,可以用來 Hook 函數。我們可以在minhook下載到這個庫。下載完成後我們在 dinput8 這個項目中新建一個文件夾MinHook,然後將 MinHook 的include文件夾和src文件夾複製到這個文件夾中。之後我們在CMakeLists.txt中添加這個庫。
ADD_LIBRARY(
dinput8
SHARED
dinput8_asm.asm
dinput8.cpp
dinput8.def
MinHook/include/MinHook.h
MinHook/src/hde/hde32.c
MinHook/src/hde/hde32.h
MinHook/src/hde/hde64.c
MinHook/src/hde/hde64.h
MinHook/src/hde/pstdint.h
MinHook/src/hde/table32.h
MinHook/src/hde/table64.h
MinHook/src/buffer.c
MinHook/src/buffer.h
MinHook/src/hook.c
MinHook/src/trampoline.c
MinHook/src/trampoline.h
)
之後我們就可以開始 Hook 這個函數了。
Hook 函數
首先我們需要再項目中執行cmake .,之後會生成一個Visual Studio的項目,我們使用Visual Studio打開這個項目,這裏需要注意一下,需要通過打開解決方案的方式打開這個項目,不要直接打開這個項目文件夾。之後我們打開dinput8.cpp,刪掉_hook_setup這個函數,接着我們需要將加載庫的地址改為系統的LoadLibrary函數,這樣我們就可以加載原 DLL 了。之後我們就可以開始 Hook 這個函數了。
#include <windows.h>
#include <stdio.h>
HINSTANCE mHinst = 0, mHinstDLL = 0;
extern "C" UINT_PTR mProcs[6] = {0};
LPCSTR mImportNames[] = {
"DirectInput8Create",
"DllCanUnloadNow",
"DllGetClassObject",
"DllRegisterServer",
"DllUnregisterServer",
"GetdfDIJoystick",
};
#ifndef _DEBUG
inline void log_info(const char* info) {
}
#else
FILE* debug;
inline void log_info(const char* info) {
fprintf(debug, "%s\n", info);
fflush(debug);
}
#endif
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
mHinst = hinstDLL;
if (fdwReason == DLL_PROCESS_ATTACH) {
mHinstDLL = LoadLibrary("C:/Windows/System32/dinput8.dll");
if (!mHinstDLL) {
return FALSE;
}
for (int i = 0; i < 6; ++i) {
mProcs[i] = (UINT_PTR)GetProcAddress(mHinstDLL, mImportNames[i]);
}
#ifdef _DEBUG
debug = fopen("./debug.log", "a");
#endif
} else if (fdwReason == DLL_PROCESS_DETACH) {
#ifdef _DEBUG
fclose(debug);
#endif
FreeLibrary(mHinstDLL);
}
return TRUE;
}
extern "C" void DirectInput8Create_wrapper();
extern "C" void DllCanUnloadNow_wrapper();
extern "C" void DllGetClassObject_wrapper();
extern "C" void DllRegisterServer_wrapper();
extern "C" void DllUnregisterServer_wrapper();
extern "C" void GetdfDIJoystick_wrapper();
接着我們可以在當fdwReason等於DLL_PROCESS_ATTACH時,也就是當 DLL 被加載時,我們進行 Hook。
首先我們需要編寫一個原函數的函數指針,這個函數指針的類型需要和原函數的類型一樣,這裏我們使用typedef來定義這個函數指針。
typedef int (*display_t)(const char*);
之後我們需要定義一個函數指針,這個函數指針用來指向我們 Hook 後的函數。
display_t display = nullptr;
之後我們創建一個Hook函數,這個函數的參數和返回值都需要和原函數一樣。
int display_hook(const char *text) {
return display("Hello MinHook!");
}
最後我們使用MinHook來Hook這個函數。
首先我們需要初始化MinHook,之後使用MH_CreateHook來創建一個 Hook,傳入原函數的地址,Hook 函數的地址,和一個函數指針的指針,之後我們就可以使用MH_EnableHook來啓用這個 Hook 了。
MH_Initialize();
MH_CreateHook((LPVOID)0x00007ff62fb51634, &display_hook, reinterpret_cast<LPVOID*>(&display));
MH_EnableHook(nullptr);
這裏的0x00007ff62fb51634是我們運行這個測試程序後得到的display函數的地址,你可以通過打印這個函數的地址來得到這個地址。除了這個方法我們還可以使用x64dbg這個工具來得到這個地址。
我們打開x64dbg通過符號切換到這個程序,之後使用字符串搜索Hello World!,之後我們找到call這個指令,然後我們就可以得到這個函數的地址了。
之後編譯我們的項目,然後將生成的 DLL 放到我們的測試程序的目錄下,之後我們就可以運行這個程序了。
項目地址: dll-hijack-minhook