DLL Injection DLL Injection
DLL InjectionDLL InjectionDLL InjectionDLL Injection
使用 SetWindowsHookEx 來設定 Hook function
HHOOK hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hinstDll, 0);
當有任何 message 被 post 過來 , 就會呼叫我們的攔截程式
當有任何 message 被 post 過來 , 就會呼叫我們的攔截程式
我們指定的攔截程式碼(Hook procedure)
我們指定的攔截程式碼(Hook procedure)
包含攔截 function 的DLL
包含攔截 function 的DLL
指定要 Monitor 所有的 thread
指定要 Monitor 所有的 thread
指定 hook 的形式
WH_CALLWNDPROC: 設定當目標收到 Message 之前 , 呼叫你的 function WH_CALLWNDPROCRET: 當目標已經把 Message 處理完 , 呼叫你的 function
可以設定的值 ( 部分 ) 參考 MSDN可以設定的值 ( 部分 ) 參考 MSDN
z
系統如何應對 SetWindowsHookEx?
BOOL WINAPI SetDIPSHook(DWORD dwThreadId){ // 安裝 hook function GetMsgProc 到指定的 thread HHOOK hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hinstDll,\ dwThreadId);}
Step 1: Process B Send 一個 message 給一個視窗處理
Step 1: Process B Send 一個 message 給一個視窗處理 Step 2:
系統檢查是否有 WH_GETMESSAGE 攔截被安裝
Step 2: 系統檢查是否有
WH_GETMESSAGE 攔截被安裝
Step 3: 系統檢查 GetMsgProc 是否被映射到 Process B 的位址空間中
Step 3: 系統檢查 GetMsgProc 是否被映射到 Process B 的位址空間中
Step 4: 載入包含 GetMsgProc 的 DLL
並 遞增 DLL counter
Step 4: 載入包含 GetMsgProc 的 DLL
並 遞增 DLL counter
Step 5: 1. 呼叫 GetMsgProc () 2. 再度遞增 DLL counter
Step 5: 1. 呼叫 GetMsgProc () 2. 再度遞增 DLL counter
返回時 , 遞減 DLLcounter
返回時 , 遞減 DLLcounter
Why? 防止 Process B 在執行 GetMsgProc 時 , 有其他 thread 呼叫 UnhookWindowsHookEx, 導致 DLL UnMap Why? 防止 Process B 在執行 GetMsgProc 時 , 有其他 thread 呼叫 UnhookWindowsHookEx, 導致 DLL UnMap
把 GetMsgProc 安裝到目標 process 中
把 GetMsgProc 安裝到目標 process 中
00 1111 22
00
解除 Hook• BOOL UnhookWindowsHookEx(HHOOK hhook);
Step 1: 找出系統中 , 所有被注入該 DLL 的 process
Step 1: 找出系統中 , 所有被注入該 DLL 的 process
Step 2: 遞減那些 process 對 DLL 的
reference counter
Step 2: 遞減那些 process 對 DLL 的
reference counter
範例程式 : 新版本操控程式 : SetMouseMessageHook
呼叫 SetHook(dwThreadID);呼叫 SetHook(dwThreadID);
DLL 掛鉤程式 : MouseProcLibrary.dll
MyHook_MouseProc( );MyHook_MouseProc( );
SetHook(dwThreadID) 實作SetHook(dwThreadID) 實作
滑鼠訊息掛鉤程式
安裝掛鉤
目標視窗 : TargetWindow
while(GetMessage(…)){ …}
while(GetMessage(…)){ …}
Message-loop
只要滑鼠有動作 ,就會先呼叫 掛鉤
安裝掛鉤到指定的 thread
範例程式
• 將桌面的 icon 位置存起來
• 將桌面的 icon 位置讀回來SimpleDIP.cpp
ProgmanProgmanProgram ManagerProgram Manager
到底是誰在管理桌面 ?
spy 拉到最後一項
修正修正
WinAPI WINMAIN(…){ … SetDIPSHook …}
WinAPI WINMAIN(…){ … SetDIPSHook …}
SetDIPSHook{ SetWindowsHookEx(…)}
SetDIPSHook{ SetWindowsHookEx(…)}
Explorer 相關的程式碼(SysListView32)
Explorer 相關的程式碼(SysListView32)
SetDIPSHook{ SetWindowsHookEx(…)}
SetDIPSHook{ SetWindowsHookEx(…)}
GetMsgProc(){
}
GetMsgProc(){
}GetMsgProc(){
}
GetMsgProc(){
}
1
載入 DLPSLIB.dll( 寄生程式 )
載入 DLPSLIB.dll( 寄生程式 )
DLPSLIB.dll DLPSLIB.dll
2
建立隱藏的 dialog建立隱藏的 dialog34
通知注射完成(Message)
通知注射完成(Message)
5
要求處理事情要求處理事情
存取資料存取資料6
先看看架構 : 總共有三個檔案互動
SimpleDIP.exe
目標 process
隱藏的 dialog
設定要 將 GetMsgProc()注射進 目標 Process
Post Message 給 process以啟動目標 Process 呼叫GetMsgProc();
開始寫程式嘍 !!
1
23
加入 resource
修改 ID = IDC_SAVE修改 ID = IDC_SAVE
修改 ID = IDC_RESTORE修改 ID = IDC_RESTORE
1
2
修改 ID = IDD_DIALOG1修改 ID = IDD_DIALOG1
加入 Message Handle#include <Windows.h> // Windows 程式必要的標頭檔#include <WindowsX.h>#include "resource.h" // 對應到你剛剛建立的 Dialog template#include <tchar.h> // for TCHAR 字元
int WINAPI _tWinMain( ..) { TCHAR cWhatToDo; switch (DialogBox(hinstExe, MAKEINTRESOURCE(IDD_DIALOG1), NULL,Dlg_Proc)) { case IDC_SAVE: cWhatToDo = TEXT('S'); break; case IDC_RESTORE: cWhatToDo = TEXT('R'); break; }}
Step 1: 建立一個 Dialog 讓使用者選擇 是否要將 [ 桌面的 icon] 位置存起來
自訂的Dialog 訊息處理函式( 見下頁 )
自訂的Dialog 訊息處理函式( 見下頁 )
視窗主程式視窗主程式
{ Message Handle function 的 prototype 片段 方便巨集定義的片段 …}
將傳回 使用者按鍵 的選項將傳回 使用者按鍵 的選項
當使用者按下按鈕時// Dialog 的訊息處理主要函式 BOOL WINAPI Dlg_Proc( HWND hwnd, UINT uMsg, …) { switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); } return(FALSE);}
// 方便巨集 chHANDLE_DLGMSG 定義// 利用 SetDlgMsgResult 將 Dialog 的 message 導向指定的// function#define chHANDLE_DLGMSG(hwnd, message, fn) case (message): return (SetDlgMsgResult(hwnd, uMsg, \ HANDLE_##message((hwnd), (wParam), (lParam), (fn))))
// 方便巨集 chHANDLE_DLGMSG 定義// 利用 SetDlgMsgResult 將 Dialog 的 message 導向指定的// function#define chHANDLE_DLGMSG(hwnd, message, fn) case (message): return (SetDlgMsgResult(hwnd, uMsg, \ HANDLE_##message((hwnd), (wParam), (lParam), (fn))))
void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, …) { switch (id) { case IDC_SAVE: case IDC_RESTORE: EndDialog(hwnd, id); break; }}
當使用者按下按鈕後 , destroys the modal Dialog box
程式藉由 id判斷到底是哪一個按鈕被按下
程式藉由 id判斷到底是哪一個按鈕被按下
休息一下
• 你可以把 Dialog 架框建好
將寄生的 DLL 程式注入指定的 process
int WINAPI _tWinMain( ..) {
// Step 1: 建立視窗 , 讀取使用者的需求
TCHAR cWhatToDo; switch (DialogBox(hinstExe, MAKEINTRESOURCE(IDD_DIALOG1), NULL,Dlg_Proc)) { case IDC_SAVE: cWhatToDo = TEXT('S'); break; case IDC_RESTORE: cWhatToDo = TEXT('R'); break; } // Step 2 ~ 6 : 處理與操縱 寄生的 DLL function
}
注入的寄生程式寫在下面
// Step 2: 取得桌面管理程式 (ProgMan)HWND hwndLV = GetFirstChild(GetFirstChild(FindWindow(TEXT("Progman"), NULL))); // Step 3: 將 DIPLib.dll 注射到 Explorer's 位址空間SetDIPSHook(GetWindowThreadProcessId(hwndLV, NULL));
// Step 4: 等待寄生程式的回應 MSG msg;GetMessage(&msg, NULL, 0, 0);
// Step 5: 因為寄生程式會建立一個隱藏的視窗 Richter DIPS123// 我們將利用這個視窗 , 操控寄生程式HWND hwndDIPS = FindWindow(NULL, TEXT("Richter DIPS123"));SendMessage(hwndDIPS, WM_APP, (WPARAM) hwndLV, (cWhatToDo == TEXT('S')));// Step 6: 操作完成 , 移除寄生程式SendMessage(hwndDIPS, WM_CLOSE, 0, 0);SetDIPSHook(0);
也可以用 Kernel Object 做同步 , 這裡使用 Message
也可以用 Kernel Object 做同步 , 這裡使用 Message
這是 DIPLib.dll 中的一個 function這是 DIPLib.dll 中
的一個 function
SysListView32SysListView32
Link DLL
WinAPI WINMAIN(…){ … SetDIPSHook …}
WinAPI WINMAIN(…){ … SetDIPSHook …}
SetDIPSHook{ SetWindowsHookEx(…)}
SetDIPSHook{ SetWindowsHookEx(…)}
Explorer 相關的程式碼(SysListView32)
Explorer 相關的程式碼(SysListView32)
SetDIPSHook{ SetWindowsHookEx(…)}
SetDIPSHook{ SetWindowsHookEx(…)}
GetMsgProc(){
}
GetMsgProc(){
}GetMsgProc(){
}
GetMsgProc(){
}
1
載入 DLPSLIB.dll( 寄生程式 )
載入 DLPSLIB.dll( 寄生程式 )
DLPSLIB.dll DLPSLIB.dll
2
建立隱藏的 dialog建立隱藏的 dialog34
通知注射完成(Message)
通知注射完成(Message)
5
要求處理事情要求處理事情
存取資料存取資料6
互動流程
SimpleDIP.exe
設定要 將 GetMsgProc()注射進 目標 Process
Post Message 給 process以啟動目標 Process 呼叫GetMsgProc();
互動流程
BOOL WINAPI SetDIPSHook( DWORD dwThreadId) { // 安裝 hook function GetMsgProc 到指定的 thread SetWindowsHookEx(…, GetMsgProc,…,dwThreadId);} Expoler.exe thread
idExpoler.exe thread
id
Expoler 的視窗處理 thread 會載入DIPSLib.dll
Hook function 已經存在於 Expoler的 address space
呼叫 Hook function, GetMsgProc
Exploer processExploer process
引發的一連串動作
寄生在 Expoler 的Function
寄生在 Expoler 的Function
SetDIPSHook 的部分// [ 設定 / 解除 ] 寄生 DLL// dwThreadId == 0 則解除BOOL WINAPI SetDIPSHook(DWORD dwThreadId) { BOOL fOk = FALSE; if (dwThreadId != 0) { // Step 1:
// 將我們的 thread ID 存起來 , 以便 當 server window 被建立起來時 , // GetMsgProc 可以 post a message 回來
g_dwThreadIdDIPS = GetCurrentThreadId();
// Step 2: g_hhook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hinstDll, dwThreadId); fOk = (g_hhook != NULL);
if (fOk) { // Step 3:
fOk = PostThreadMessage(dwThreadId, WM_NULL, 0, 0); } } else { // 當 dwThreadId ==0 時 , 表示我們要移除 hook 了 fOk = UnhookWindowsHookEx(g_hhook); g_hhook = NULL; } return(fOk);}
隨便送一個 no operation message, WM_NULL 給指定的 thread, 以便啟動我們的 hook function , GetMsgProc
隨便送一個 no operation message, WM_NULL 給指定的 thread, 以便啟動我們的 hook function , GetMsgProc
將 hook function GetMsgProc 安裝到指定的 thread 中
監控端的 thread
目標 thread要注入的 function
( 見下頁 )
也就是要被注射的 DLL
也就是要被注射的 DLL
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
static BOOL fFirstTime = TRUE;
if (fFirstTime) { // The DLL just got injected. fFirstTime = FALSE;
// Step 1: 建立一個 Dialog 用來處理 client 的 request // 所有送到這個 dialog message 都會交給 Dlg_Proc 管理 // 目前這個 Dialog 的 title = "Richter DIPS123" CreateDialog(g_hinstDll, MAKEINTRESOURCE(IDD_DIPS), NULL, Dlg_Proc); // Step 2: 告訴 DIPS application 目前 server 已經啟動 // 並且等待接受 requests PostThreadMessage(g_dwThreadIdDIPS, WM_NULL, 0, 0); }
// 將 hook 資訊傳給下一個可能的 hook return(CallNextHookEx(g_hhook, nCode, wParam, lParam));}
GetMsgProc 的部分
監控端的 thread
DLPSLIB 設定為 DLL Project
設定 dll 檔輸出位置設定 dll 檔輸出位置
設定 lib 檔輸出位置設定 lib 檔輸出位置
• End延伸閱讀 : 1. Monitor system events ms-help://MS.MSDNQTR.2004JAN.1033/winui/winui/windowsuserinterface /windowing/hooks/usinghooks.htm#system_events 2. . TaskSwitcher application ms-help://MS.MSDNQTR.2004JAN.1033/dnwxp/html/xpvisualstyles.htm
進一步的解析Message 巨集
我們已經知道利用
可以將 WM_COMMAND message 轉向呼叫我們自己寫的 Dlg_OnCommand function 處理
chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);
現在來看看詳細呼叫的情況
解析 chHANDLE_DLGMSGBOOL WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); }
return(FALSE);}
BOOL WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); }
return(FALSE);}
#define chHANDLE_DLGMSG(hwnd, message, fn) case (message): return (SetDlgMsgResult(hwnd, uMsg,\ HANDLE_##message((hwnd), (wParam), (lParam), (fn))))
Dialog 訊息處理函式Dialog 訊息處理函式
case (WM_COMMAND): return(SetDlgMsgResult( hwnd, uMsg,\ HANDLE_WM_COMMAND(hwnd,wParam,lParam,Dlg_OnCommand))
case (WM_COMMAND): return(SetDlgMsgResult( hwnd, uMsg,\ HANDLE_WM_COMMAND(hwnd,wParam,lParam,Dlg_OnCommand))
原型定義在 cmnhdr.h原型定義在 cmnhdr.h
你的 function 位址
你的 function 位址
接下一頁
解析 chHANDLE_DLGMSGcase (WM_COMMAND): return(SetDlgMsgResult( hwnd, uMsg,\ HANDLE_WM_COMMAND(hwnd,wParam,lParam,Dlg_OnCommand))
case (WM_COMMAND): return(SetDlgMsgResult( hwnd, uMsg,\ HANDLE_WM_COMMAND(hwnd,wParam,lParam,Dlg_OnCommand))
21
#define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) \ ((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L)
原型定義在 windowx.h for message crackers原型定義在 windowx.h for message crackers
你的 function 位址
接下一頁
Dlg_OnCommand(hwnd, \ (int)(LOWORD(wParam,(HWND)(lParam),(UINT)HIWORD(wParam)),0L)
Dlg_OnCommand(hwnd, \ (int)(LOWORD(wParam,(HWND)(lParam),(UINT)HIWORD(wParam)),0L)
你的 function 位址
#define SetDlgMsgResult(hwnd, msg, result) (( \ (msg) == WM_CTLCOLORMSGBOX || \ (msg) == WM_CTLCOLOREDIT || \ (msg) == WM_CTLCOLORLISTBOX || \ (msg) == WM_CTLCOLORBTN || \ (msg) == WM_CTLCOLORDLG || \ (msg) == WM_CTLCOLORSCROLLBAR || \ (msg) == WM_CTLCOLORSTATIC || \ (msg) == WM_COMPAREITEM || \ (msg) == WM_VKEYTOITEM || \ (msg) == WM_CHARTOITEM || \ (msg) == WM_QUERYDRAGICON || \ (msg) == WM_INITDIALOG \ ) ? (BOOL)(result) : (SetWindowLongPtr((hwnd), DWLP_MSGRESULT, (LPARAM)(LRESULT)(result)), TRUE))
windowx.h for USER Macro APIswindowx.h for USER Macro APIs
解析一下 : 若 msg 是左邊的那些 , 則直接呼叫你的 function 否則 , 利用 SetWindowLongPtr 修改 Dialog MessageResult 屬性
(SetWindowLongPtr(hwnd,DWLP_MSGRESULT, (LPARAM)(LRESULT)(Dlg_OnCommand(hwnd, \ (int)(LOWORD(wParam,(HWND)(lParam),(UINT)HIWORD(wParam)),0L)), TRUE))
(SetWindowLongPtr(hwnd,DWLP_MSGRESULT, (LPARAM)(LRESULT)(Dlg_OnCommand(hwnd, \ (int)(LOWORD(wParam,(HWND)(lParam),(UINT)HIWORD(wParam)),0L)), TRUE))
展開的結果展開的結果
指定 Dialog 的 Message 改由 Dlg_OnCommand 呼叫指定 Dialog 的 Message 改由 Dlg_OnCommand 呼叫
2
全部合在一起BOOL WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); }
return(FALSE);}
BOOL WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); }
return(FALSE);}
case (WM_COMMAND): return (SetWindowLongPtr(hwnd,DWLP_MSGRESULT, (LPARAM)(LRESULT)(Dlg_OnCommand(hwnd, \ (int)(LOWORD(wParam,(HWND)(lParam),(UINT)HIWORD(wParam)),0L)), TRUE));
case (WM_COMMAND): return (SetWindowLongPtr(hwnd,DWLP_MSGRESULT, (LPARAM)(LRESULT)(Dlg_OnCommand(hwnd, \ (int)(LOWORD(wParam,(HWND)(lParam),(UINT)HIWORD(wParam)),0L)), TRUE)); 指定 Dialog 的 Message 改由 Dlg_OnCommand 呼叫指定 Dialog 的 Message 改由 Dlg_OnCommand 呼叫