评: QQ中的util函数暴露了很多信息啊~~
*******************************************************
*标题:【原创】逆向qqspy,实现qq2010聊天信息获取 *
*作者:踏雪流云 *
*日期:2010年9月1号 *
*声明:本文章的目的仅为技术交流讨论 *
*******************************************************
小弟第一次发帖,写的不好或者错误的地方还望指正~~~
前些日子,偶然看到这个qqspy的程序可以获取qq的聊天记录,本人比较好奇,于是决定分析分析它是如何实现的;终于皇天不负有心人,被小弟看出了端倪,特来与大家分享。。。
程序下载地址为:http://www.myqqspy.cn/
程序说明如下:
家庭上网侦察员6.3家庭上网侦察员 V6.3 又名:QQ间谍、QQ聊天记录工具、QQ聊天监视工具、QQ2010聊天记录工具、QQ2009聊天记录工具、QQ聊天记录器、MSN聊天记录器 是目前唯一一款兼容 WINDOWS XP、VISTA、WINDOWS7,又支持QQ、MSN聊天软件的一款聊天监控软件,完美兼容QQ2010、MSN最新版,欢迎免费试用,试用满意后付款!
我下载的是试用版进行分析,共有5个文件,分别是:qqspy.exe、db.dll、im32.dll、msimg32.dll、base.ini。下面先简单介绍下各个文件的作用:
qqspy.exe主程序,负责聊天记录的显示与功能的设置。
db.dll数据模块,封装了SQLite功能,把聊天记录存储为SQLite数据库格式文件;并使用ADVAPI32对此SQLite数据库进行SHA加密保护。
im32.dll聊天记录获取模块,负责聊天记录的获取并保存在聊天记录文件中。
msimg32貌似是QQ录音机软件模块的dll,QQ在启动的时候会加载msimg32.dll文件;而此文件已被修改,负责加载im32.dll模块,这样im32.dll模块就可被QQ加载了。
base.ini数据记录文件,记录QQ聊天记录,记录密码、配置、注册等信息,SQLite数据库格式,并用SHA算法加密。
将上述5个文件都放入QQ的bin目录下,开始进行分析:
首先,im32.dll被加了壳,先脱壳;然后,用OD加载im32.dll文件,ctrl+N打开当前模块中的名称:
发现kernel32.WriteProcessMemory和kernel32.VirtualProtect两个API比较可以,这让我想到了APIHook技术,在kernel32.WriteProcessMemory行回车,来到函数参考:
10001F73 |. 52 push edx ; /ProcessId 10001F74 |. 6A 00 push 0 ; |Inheritable = FALSE 10001F76 |. 6A 38 push 38 ; |Access = VM_OPERATION|VM_READ|VM_WRITE 10001F78 |. FF15 AC400210 call dword ptr [<&KERNEL32.OpenProces>; /OpenProcess 10001F7E |. 8B4E 04 mov ecx, dword ptr [esi+4] 10001F81 |. 6A 00 push 0 ; /pBytesWritten = NULL 10001F83 |. 8BF8 mov edi, eax ; | 10001F85 |. 8B46 0C mov eax, dword ptr [esi+C] ; | 10001F88 |. 50 push eax ; |BytesToWrite 10001F89 |. 51 push ecx ; |Buffer 10001F8A |. 53 push ebx ; |Address 10001F8B |. 57 push edi ; |hProcess 10001F8C |. FF15 C8400210 call dword ptr [<&KERNEL32.WriteProce>; /WriteProcessMemory 10001F92 |. 8B0E mov ecx, dword ptr [esi] 10001F94 |. 6A 00 push 0 ; /pOldProtect = NULL 10001F96 |. 8BE8 mov ebp, eax ; | 10001F98 |. 8B4424 0C mov eax, dword ptr [esp+C] ; | 10001F9C |. 50 push eax ; |NewProtect 10001F9D |. 51 push ecx ; |Size 10001F9E |. 53 push ebx ; |Address 10001F9F |. FF15 BC400210 call dword ptr [<&KERNEL32.VirtualPro>; /VirtualProtect 10001FA5 |. 57 push edi ; /hObject 10001FA6 |. FF15 50400210 call dword ptr [<&KERNEL32.CloseHandl>; /CloseHandle
这时,我们来到KernelUt.Util::Msg::SaveMsg函数位置:
3182B3B0 >/$- E9 4B4CB6D0 jmp 02390000 3182B3B5 |. 68 88DB8531 push 3185DB88 3182B3BA |. 64:A1 0000000>mov eax, dword ptr fs:[0] 3182B3C0 |. 50 push eax 3182B3C1 |. 83EC 38 sub esp, 38 3182B3C4 |. 53 push ebx 3182B3C5 |. 56 push esi 3182B3C6 |. 57 push edi
02390000 F0:FF05 C049FE0>lock inc dword ptr [FE49C0] 02390007 60 pushad 02390008 9C pushfd 02390009 64:A1 18000000 mov eax, dword ptr fs:[18] 0239000F 8B40 34 mov eax, dword ptr [eax+34] 02390012 50 push eax 02390013 8BD4 mov edx, esp 02390015 68 00000000 push 0 0239001A B9 8849FE00 mov ecx, 0FE4988 0239001F E8 0C2DC0FE call im32.00F92D30 02390024 5A pop edx 02390025 64:A1 18000000 mov eax, dword ptr fs:[18] 0239002B 83C0 34 add eax, 34 0239002E 8910 mov dword ptr [eax], edx 02390030 9D popfd 02390031 61 popad 02390032 F0:FF0D C049FE0>lock dec dword ptr [FE49C0] 02390039 C3 retn
00F92D30 /. 55 push ebp 00F92D31 |. 8BEC mov ebp, esp 00F92D33 |. 83E4 F8 and esp, FFFFFFF8 00F92D36 |. 83EC 30 sub esp, 30 00F92D39 |. 56 push esi 00F92D3A |. 8BF1 mov esi, ecx 00F92D3C |. 8B46 1C mov eax, dword ptr [esi+1C] 00F92D3F |. 57 push edi 00F92D40 |. 8BFA mov edi, edx 00F92D42 |. 894424 28 mov dword ptr [esp+28], eax 00F92D46 |. FF15 0C48FC00 call dword ptr [FC480C] ; ntdll.NtCurrentTeb 00F92D4C |. 8B57 14 mov edx, dword ptr [edi+14] 00F92D4F |. 894424 24 mov dword ptr [esp+24], eax 00F92D53 |. 8B45 08 mov eax, dword ptr [ebp+8] 00F92D56 |. 83C2 04 add edx, 4 00F92D59 |. 837E 20 00 cmp dword ptr [esi+20], 0 00F92D5D |. 8D4C24 30 lea ecx, dword ptr [esp+30] 00F92D61 |. C74424 30 000>mov dword ptr [esp+30], 0 00F92D69 |. 894C24 2C mov dword ptr [esp+2C], ecx 00F92D6D |. 897424 34 mov dword ptr [esp+34], esi 00F92D71 |. 895424 14 mov dword ptr [esp+14], edx 00F92D75 |. 897C24 18 mov dword ptr [esp+18], edi 00F92D79 |. C74424 20 000>mov dword ptr [esp+20], 0 00F92D81 |. 894424 1C mov dword ptr [esp+1C], eax 00F92D85 |. 75 22 jnz short 00F92DA9 00F92D87 |. 833D F047FC00>cmp dword ptr [FC47F0], 0 00F92D8E |. 75 19 jnz short 00F92DA9 00F92D90 |. 8B56 04 mov edx, dword ptr [esi+4] 00F92D93 |. 8B02 mov eax, dword ptr [edx] 00F92D95 |. 8D7E 04 lea edi, dword ptr [esi+4] 00F92D98 |. 8BCF mov ecx, edi 00F92D9A |. FFD0 call eax 00F92D9C |. 84C0 test al, al 00F92D9E |. 74 09 je short 00F92DA9 00F92DA0 |. E8 3B010000 call 00F92EE0 00F92DA5 |. 85C0 test eax, eax 00F92DA7 |. 74 16 je short 00F92DBF 00F92DA9 |> 8B16 mov edx, dword ptr [esi] 00F92DAB |. 8B52 04 mov edx, dword ptr [edx+4] 00F92DAE |. 8D4424 14 lea eax, dword ptr [esp+14] 00F92DB2 |. 50 push eax 00F92DB3 |. 8BCE mov ecx, esi 00F92DB5 |. FFD2 call edx 00F92DB7 |. 5F pop edi 00F92DB8 |. 5E pop esi 00F92DB9 |. 8BE5 mov esp, ebp 00F92DBB |. 5D pop ebp 00F92DBC |. C2 0400 retn 4 00F92DBF |> 8B46 18 mov eax, dword ptr [esi+18] 00F92DC2 |. 83E0 03 and eax, 3 00F92DC5 |. 74 1D je short 00F92DE4 00F92DC7 |. 8D4C24 0C lea ecx, dword ptr [esp+C] 00F92DCB |. 51 push ecx 00F92DCC |. 8B4F 10 mov ecx, dword ptr [edi+10] 00F92DCF |. 894424 14 mov dword ptr [esp+14], eax 00F92DD3 |. 8D4424 14 lea eax, dword ptr [esp+14] 00F92DD7 |. 57 push edi 00F92DD8 |. 894424 14 mov dword ptr [esp+14], eax 00F92DDC |. E8 FFEDFFFF call 00F91BE0 00F92DE1 |. 83C4 08 add esp, 8 00F92DE4 |> F646 18 01 test byte ptr [esi+18], 1 00F92DE8 |. 75 15 jnz short 00F92DFF 00F92DEA |. 837C24 20 00 cmp dword ptr [esp+20], 0 00F92DEF |. 75 0E jnz short 00F92DFF 00F92DF1 |. 8B16 mov edx, dword ptr [esi] 00F92DF3 |. 8B52 04 mov edx, dword ptr [edx+4] 00F92DF6 |. 8D4424 14 lea eax, dword ptr [esp+14] 00F92DFA |. 50 push eax 00F92DFB |. 8BCE mov ecx, esi 00F92DFD |. FFD2 call edx ; im32.00F92E40 00F92DFF |> 8B46 18 mov eax, dword ptr [esi+18] 00F92E02 |. A8 04 test al, 4 00F92E04 |. 74 2C je short 00F92E32 00F92E06 |. A8 01 test al, 1 00F92E08 |. 75 28 jnz short 00F92E32 00F92E0A |. F64424 20 01 test byte ptr [esp+20], 1 00F92E0F |. 74 21 je short 00F92E32 00F92E11 |. 8D4C24 0C lea ecx, dword ptr [esp+C] 00F92E15 |. 51 push ecx 00F92E16 |. 8B4F 10 mov ecx, dword ptr [edi+10] 00F92E19 |. 8D4424 14 lea eax, dword ptr [esp+14] 00F92E1D |. 57 push edi 00F92E1E |. C74424 18 040>mov dword ptr [esp+18], 4 00F92E26 |. 894424 14 mov dword ptr [esp+14], eax 00F92E2A |. E8 B1EDFFFF call 00F91BE0 00F92E2F |. 83C4 08 add esp, 8 00F92E32 |> 5F pop edi 00F92E33 |. 5E pop esi 00F92E34 |. 8BE5 mov esp, ebp 00F92E36 |. 5D pop ebp 00F92E37 /. C2 0400 retn 4
执行之前:
0013F490 30021738 返回到 Common.30021738 0013F494 06C964CC UNICODE "Addr-0x30016730"
0013F490 30021738 返回到 Common.30021738 0013F494 06C964CC UNICODE "xxxxxxxx" 这个就是对方QQ号码的地址
到此,已经成功的获取到了对方的QQ号码,方法是调用KernelUt.Util::Msg::SaveMsg函数后,出现在栈中。
下面分析如何找到获取聊天信息的部分:
首先,发现im32.dll里面,有多处调用kernel32.OutputDebugStringA API,于是想到使用Dbgview工具,来查看打印信息:
图中可以看出insert into chatlog (id,acc1,acc2,nickname,msg,time,issend,isgroup,chattype,sendover)打印出了聊天信息;因此,OD中查找上述字符串,找到insert into chatlog (id,acc1,acc2,nickname,msg,time,issend,isgroup,chattype,sendover) values(null,%d,%d,'%s','%s','%s',%d,%d,'%s',0)这组字符串就是了。回车,来到调用处,下断,运行QQ,聊天后,便命中断点;alt+K查看调用堆栈,发现第三行的函数有蹊跷,因为那个函数体里面调用了KernelUt.Util::Msg::GetMsgAbstract这个函数;于是在这里下断,聊天后,便命中断点;执行完这个函数就找到了我们的聊天信息了,哈哈~~~
至此,qqspy实现查看聊天记录的流程就水落石出了,总结如下:
1.利用msimg32.dll加载im32.dll。
2.在im32.dll中,Hook KernelUt.Util::Msg::SaveMsg 函数。
3.在Hook函数中,调用原KernelUt.Util::Msg::SaveMsg 函数,找到对方QQ号码。
4.在Hook函数中,调用KernelUt.Util::Msg::GetMsgAbstract函数,获取聊天信息。
是不是很简单,呵呵~~~小弟献丑了。。。利用此方法,可以将获取到的聊天信息实时发给对方或将聊天记录保存在文件中,每次启动发给对方等。
源代码很简单,就不发上来了~~~
测试环境:
效果如下图所示: