QQ 自从 2009 开始,在窗口上做了很多手脚,使得许多聊天监控软件都失效了。主要是
以前那些聊天监控软件使用的是窗口消息,偏偏 QQ 的窗口是画上去的,以前的注入窗口然
后调用 GetWindowText的办法压根就不行了。不过后来大家为了解决这个问题,就采用了一
些很通用但是效果不大好的手法。比如说我在网吧看到一种办法,就是每隔一段时间,对
QQ 聊天窗口模拟按键,应该是采用的“全选、复制、粘贴(到某处)”的办法,但是这个实
在是太影响“用户体验”了,而且明显就能看出来。当然还有一种就是网上一些小软件,采
用“拍照”的方式,其实就是截图,这个效果也好不了多少,CPU 占用高不说,使用者体验
也不好。不过这些都是无奈之举,直到后来有款流传的QQSpy出来。
QQSpy 采用的明显就技术得多了,主要是利用的 QQ 库里面的函数,从 QQSpy 的经验可
以学到,以后分析 QQ 函数应该“特别关注”KernelUnit.dll 和 AppUnit.dll,这两个库里
面有许多丰富的函数,有的从名字就能看出个大概。QQSpy 的指明了方向:SaveMsg
(Util::Msg::SaveMsg)。
SaveMsg 这个函数就是记录聊天信息的函数。例如我们和别人聊天,还是别人和我聊天,
都会通过这个函数。群里面的聊天函数名字也是它,不过是一个重载版本。QQSpy的办法就
是采用的 HOOK SaveMsg函数,从SaveMsg函数中读到聊天内容,对方以及己方的QQ号,然
后通过一些辅助函数的处理。这里的辅助函数其实也是来自于QQ提供的库。
我们先来看看QQSpy最重要的实现函数,SaveMsg的 HOOK:
BOOL __cdecl NewSaveMsg_1(LPCWSTR lpStr,
DWORD dTo_Num,
DWORD dFrom_Num,
DWORD data3,
struct ITXMsgPack * TXMsgPack,
struct ITXData* TXData )
{
long lSelfQQNum = TrueGetSelfUin();
//调试打印输出,时间
time_t Time;
struct tm *local;
WCHAR wszStringTime[20] = {0};
Time = (time_t)TrueGetMsgTime(TXMsgPack);
local = localtime(&Time);
swprintf(wszStringTime,L"%0.2d:%0.2d:%0.2d",
local->tm_hour,local->tm_min,local->tm_sec);
LPWSTR lpName1 = NULL,lpName2 = NULL;
if (TrueGetPublicName)
{
TrueGetPublicName(&lpName1, dFrom_Num);
TrueGetPublicName(&lpName2, dTo_Num);
}
WCHAR wszStringBuffer[MAX_PATH] = {0};
//发消息人是自己
if(lSelfQQNum == dFrom_Num)
{
swprintf(wszStringBuffer,L"%ws(%d) %ws",lpName1,dFrom_Num,wszStringTime
);
OutputDebugStringW(wszStringBuffer);
}
//发消息人不是自己
if(lSelfQQNum != dFrom_Num && dTo_Num == lSelfQQNum)
{
swprintf(wszStringBuffer,L"%ws(%d) %ws",lpName1,dFrom_Num,wszStringTime
);
OutputDebugStringW(wszStringBuffer);
}
CString strBuffer;
LPWSTR *lpBuffer =(LPWSTR
*)TrueGetMsgAbstract(strBuffer.GetBufferSetLength(4096), TXMsgPack);
//调试打印输出,内容
OutputDebugStringW(*lpBuffer);
return OldSaveMsg_1(lpStr, dTo_Num, dFrom_Num, data3, TXMsgPack, TXData);
}
我并没有完全按照原版的还原,至少信息是用调试信息打印的,所以要看需要调用
DebugView。这里面几个函数说明一下:
(1) 函数GetPublicName就是通过传入的QQ号,可以显示出昵称。
(2) 函数 GetMsgTime 就是获得聊天时间的,QQ 聊天的时间不是本地读的,是服务器传
的,最好的证明就是你把本地时间改了也不会对聊天时间有什么影响。
(3) 主要函数 GetMsgAbstract,就是将 TXMsgPack 类的数据转为 WCHAR,这个函数完全
参考的 QQSpy 写的。
然后,我将该函数HOOK之后,与某人聊天,情况如下图 1所示:

效果至少比网吧的那个潜在的聊天监控“体验起来”还是好多了。另外,这个方法,即 |