首页 > 技术专题 > Plugins > windbg获取某软件的聊天记录
2016
04-10

windbg获取某软件的聊天记录

现学现用之windbg的高级玩法外篇一:获取某软件的聊天记录

不知道直接点名会不会请去喝茶,我不爱喝茶,所以就不直接点名了。
测试环境
某软件版本:2013 正式版sp2            在这里简称Tx
调试软件:windbg 6.12
逆向分析工具:ida 6.0
系统版本:xp sp3
其他工具:xuetr0.45

声明:本软件只是用来演示windbg的用法,本人是纯洁的。所以假设各位童鞋也和楼主一样,否则请自愿离开
本文只限于技术交流,如果有人根据本文做出非法用途的工具与楼主无关。

好,废话少说,开练。
首先,我通过xuetr看到有软件获取聊天内容的方法是Hook了KernelUtil!Util::Msg::SaveMsg194。
楼主是个懒人,不想再另辟新径去找其他方法了。咱们就通过KernelUtil!Util::Msg::SaveMsg194函数来分析怎么获取聊天内容吧。
把kernelUtil放到ida里看了一下,分析出函数定义:
int __cdecl Util::Msg::SaveMsg194(wchar_t const *, unsigned long, unsigned long, unsigned long, struct ITXMsgPack *, struct ITXData *)
由于这次分析ida是配角,所以大部分分析的工作就交给windbg了。每个参数是干啥的,等会就能见分晓

开启某软件,然后用windbg附加调试。
查看KernelUtil!Util::Msg::SaveMsg194

代码:
0:000> x KernelUtil!Util::Msg::SaveMsg194
3189b75a KernelUtil!Util::Msg::SaveMsg194 ()
3189bb3a KernelUtil!Util::Msg::SaveMsg194 ()
[/cpp]

发现有两处,不管了,都下上断点

代码:
0:025> bm KernelUtil!Util::Msg::SaveMsg194
  1: 3189b75a @!"KernelUtil!Util::Msg::SaveMsg194"
  2: 3189bb3a @!"KernelUtil!Util::Msg::SaveMsg194"
0:025> bl
 1 e 3189b75a     0001 (0001)  0:**** KernelUtil!Util::Msg::SaveMsg194
 2 e 3189bb3a     0001 (0001)  0:**** KernelUtil!Util::Msg::SaveMsg194
[/cpp]

继续执行

代码:
0:025> g
[/cpp]

我在聊天窗口中发送了消息 ddlxtest2,并按回车,此时果然被断下了

代码:
Breakpoint 2 hit
eax=4c12fd1a ebx=00000000 ecx=31877650 edx=0012db48 esi=3016a33c edi=056c0864
eip=3189bb3a esp=0012db54 ebp=0012dfdc iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
KernelUtil!Util::Msg::SaveMsg194:
3189bb3a 6a2c            push    2Ch
[/cpp]

果然那个XX软件没骗我,这个函数就是跟聊天内容相关啊,咱们看一下参数,刚惨咱们看到定义,共有6个参数。
//看一下栈转储

代码:
0:000> dd esp
0012db54  03409695 035c918c 5556ae** 4c12fd**
0012db64  5556ae** 05aa6868 059b3908 996b7eb3
0012db74  300b5467 300d64b9 056c0860 300a771a
0012db84  05929680 05929680 0012dba0 71a2265b
0012db94  00000020 01376978 059b3908 0012dc0c
0012dba4  07779a18 30820a35 035c918c 0012dbd0
0012dbb4  00000010 05aa6868 059b3908 00000020
0012dbc4  01376978 0012dc0c 0012dc38 0012dc10
[/cpp]

第一个参数是一个宽字符串

代码:
0:000> du poi(esp+4)
035c918c  "buddy" //擦这个不是聊天内容
[/cpp]

第二个参数是个ulong类型的数字

代码:
0:000> ? poi(esp+8)
Evaluate expression: 14317***** = 5556ae** //14317*****正是楼主发送的对方账号。账号做了打码处理
[/cpp]

第三个参数是个ulong类型的数字

代码:
0:000> ? poi(esp+c)
Evaluate expression: 12763***** = 4c12fd** //127631****正是正在调试的账号
[/cpp]

第四个参数是个ulong类型的数字

代码:
0:000> ? poi(esp+10)
Evaluate expression: 14317***** = 5556ae**
[/cpp]

第五个参数是个struct ITXMsgPack *结构指针。 ITXMsgPack是干啥的?不知道,看名字定义是消息包,由名字推出聊天内容就应该在这个结构内

代码:
0:000> dd poi(esp+14)
05aa6868  318f9fa8 318f9f98 00000006 3016a23c
05aa6878  3016a22c 00000000 00040000 00000000
05aa6888  05ad35f8 05aa0000 059d8ec0 059d8ec4
05aa6898  059d8ec4 00000000 00080004 0408012c
05aa68a8  059d8db8 059d8db8 059d8db8 00000000
05aa68b8  700a0c76 00000101 00040002 040e0120
05aa68c8  05aa0000 003f0178 0002000f 04080122
05aa68d8  00008000 00000004 0000000d 0000000b
[/cpp]

第六个参数是个struct ITXData *结构指针。 ITXData是干啥的?不知道,看名字像是一个存储数据的东东,内容也可能在这个结构里面

代码:
0:000> dd poi(esp+18)
059b3908  3016a270 3016a260 3016a24c 3016a23c
059b3918  3016a22c 00000001 00000000 00010000
059b3928  00016300 00000001 095af8f8 095afc40
059b3938  01896740 0000000f 0008000a 040c0110
059b3948  30168f40 00000001 059b3948 00000000
059b3958  c54dd1c9 4511ad06 d6749aa1 42b25ff5
059b3968  00000000 00000000 00000000 00000000
059b3978  00000000 3028475c 07777b70 059d8248
[/cpp]

看了所有的参数,看来账号已经可以拿到了,但是没有一个显式存储聊天内容的。看来最头疼的就是怎么拿聊天内容了。
这一下犯难了,分析时最难的是出现数据结构,需要对数据结构进行分析,最头疼了。既然说要干沉他,那就不能就此罢休
先看一下后两个参数是什么
看一下第五个参数第一个数值:318f9fa8,看看他是一个什么地址:

代码:
0:000> !address 318f9fa8                     
Failed to map Heaps (error 80004005)
Usage:                  Image
Allocation Base:        31800000
Base Address:           318f3000
End Address:            31932000
Region Size:            0003f000
Type:                   01000000    MEM_IMAGE
State:                  00001000    MEM_COMMIT
Protect:                00000002    PAGE_READONLY
More info:              lmv m KernelUtil
More info:              !lmi KernelUtil
More info:              ln 0x318f9fa8
[/cpp]

看来第五个是一个类对象了,318f9fa8告诉我们是在一个模块内,由此可以猜出是个虚函数表。验证一下:

代码:
0:000> dds 318f9fa8 l5
318f9fa8  31886259 KernelUtil!Util::Msg::ConvertCharFormatMsgPackToRichEdit+0xf18
318f9fac  318da1e0 KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x500
318f9fb0  31884d3a KernelUtil!Util::Msg::SetMsgDB2StorageMode+0x98
318f9fb4  318dd504 KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x3824
318f9fb8  318dd516 KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x3836
[/cpp]

果然是虚函数表。咱们在看看第六个参数:

代码:
0:000> !address 3016a270 
Usage:                  Image
Allocation Base:        30000000
Base Address:           3014e000
End Address:            301c1000
Region Size:            00073000
Type:                   01000000    MEM_IMAGE
State:                  00001000    MEM_COMMIT
Protect:                00000002    PAGE_READONLY
More info:              lmv m Common
More info:              !lmi Common
More info:              ln 0x3016a270
[/cpp]

也是一个类实例。难道要让我分析他们的虚函数么?一个一个看这也太考验楼主耐心了。怎么办!突然灵感一现,为啥不直接搜内容字符串呢?

代码:
0:000> s -u 0 f000000 "ddlxtest2"
01475e98  0064 0064 006c 0078 0074 0065 0073 0074  d.d.l.x.t.e.s.t.
0779c308  0064 0064 006c 0078 0074 0065 0073 0074  d.d.l.x.t.e.s.t.
[/cpp]

这个为啥搜unicode字符串而不是ascii字符串呢?这个,哈哈,根据我的经验,聊天软件为了支持其他语言,通常会使用unicode字符串。要不像日文这类就无法显示了
搜出来两条,这个比较乐观,只有两个,对于分析来说容易一些。那哪个才是给Util::Msg::SaveMsg194函数准备的呢?
那就看看这两个地址都在哪被引用了

代码:
0:000> s -u 0 f000000 01475e98
0:000> s -u 0 f000000 0779c308
[/cpp]

没有结果,看来不乐观,这么看来,这两个地址都是一个结构内字符串数组,而不是一个指针。
没办法了么?这个时候只能靠猜了(其实调试的过程就是一个不断假设不断否定的过程),假设这两个地址一定会被访问。
那下个访问断点把

代码:
0:000> ba r4 0779c308
0:000> ba r4 01475e98
[/cpp]

看能不能被断下,继续执行

代码:
0:000> g
[/cpp]

竟然马上就断下来了,太好了,假设是正确的。

代码:
Breakpoint 0 hit
eax=00000064 ebx=05acfc00 ecx=00000012 edx=00000002 esi=0779c308 edi=05acfc26
eip=78145078 esp=0012d8e8 ebp=0012d8f0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
MSVCR80!LeadUpVec+0x3c:
78145078 8807            mov     byte ptr [edi],al          ds:0023:05acfc26=00
[/cpp]

向上反汇编看看:

代码:
0:000> ub
MSVCR80!LeadUpVec+0x24 [F:\dd\vctools\crt_bld\SELF_X86\crt\src\intel\memcpy.asm @ 245]:
78145060 83c703          add     edi,3
78145063 83f908          cmp     ecx,8
78145066 72cc            jb      MSVCR80!memcpy+0x84 (78145034)
78145068 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
7814506a ff249524511478  jmp     dword ptr MSVCR80!TrailUpVec (78145124)[edx*4]
78145071 8d4900          lea     ecx,[ecx]
78145074 23d1            and     edx,ecx
78145076 8a06            mov     al,byte ptr [esi]
[/cpp]

地址78145076就是读取字符串位置,刚才咱们下了两个访问断点,这个是哪个的?看一下esi就知道了

代码:
0:000> r esi
esi=0779c308
[/cpp]

哈哈,看来是访问的第一个地址。似乎看到了曙光。
看一下调用栈(下面的分析都是基于此调用栈的分析)

代码:
0:000> kb n 10
 # ChildEBP RetAddr  Args to Child              
00 0012d8f0 30030e71 05acfc26 0779c308 00000014 MSVCR80!LeadUpVec+0x3c [F:\dd\vctools\crt_bld\SELF_X86\crt\src\intel\memcpy.asm @ 259]
WARNING: Stack unwind information not available. Following frames may be wrong.
01 0012d910 318e1b49 059bc360 0779c308 00000014 Common!Util::Extension::CreateExtensionSort+0x572b
02 0012d958 318dda9c 019bc360 b2d95feb 05aa688c KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x7e69
03 0012d988 318dfedb 05847a70 059bc360 0012da4c KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x3dbc
04 0012d9b8 318dff6b 0012d9ec b2d95f83 05aa6868 KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x61fb
05 0012d9e0 0328d5e0 05aa6868 059bc360 996b7b57 KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x628b
06 0012da38 0326ca46 01ab9aa4 062e2cb0 0770c4b8 IM!DllUnregisterServer+0x2a994
07 0012da98 3189b727 057a2310 05aa6868 01ab9aa4 IM!DllUnregisterServer+0x9dfa
08 0012daf4 3189beab 035c918c 07779a18 05aa6868 KernelUtil!Util::MsgImport::FindAutoQQMsgFileFullPath+0x4a6
09 0012db50 03409695 035c918c 5556ae** 4c12fd** KernelUtil!Util::Msg::SaveMsg194+0x371
0a 0012dfdc 033d52c0 035c918c 05aa6868 00000000 IM!DllUnregisterServer+0x1a6a49
0b 0012e07c 033d6f08 05aa6868 00000000 00000001 IM!DllUnregisterServer+0x172674
0c 0012e0a0 033d7125 05aa6868 00000000 05a56438 IM!DllUnregisterServer+0x1742bc
0d 0012e0d4 6629a5eb 056c0864 05aa6868 00000000 IM!DllUnregisterServer+0x1744d9
0e 0012e12c 64c20f8d 059d19a0 05aa6868 05599278 AppFramework!DllUnregisterServer+0x9851c
0f 0012e140 662903e9 05a560c8 05aa6868 05ae3f88 ChatFrameApp!DllUnregisterServer+0x1fb6a
[/cpp]

通过看堆栈我们可以发现,当前调用栈还在KernelUtil!Util::Msg::SaveMsg194函数内部(栈帧编号09)。
有了这个我们应该怎么看呢?还记得断下的访问地址么?对就是0779c308。看看有没有函数的参数是0779c308。
果然有发现,栈帧01的第二个参数就是0779c308。

代码:
01 0012d910 318e1b49 059bc360 0779c308 00000014 Common!Util::Extension::CreateExtensionSort+0x572b
[/cpp]

那咱们就从帧编号01处开始排查,这时楼主使用的是反向剥洋葱法,从内向外,一步一步剥光它,这个方法,在现实生活中,很难实施,有兴趣的欢迎体验
调用Common!Util::Extension::CreateExtensionSort+0x572b函数的指令地址是318e1b49上一条指令,咱们向上反汇编看一下第二个参数是从哪来的

代码:
0:000> ub 318e1b49 l10
KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x7e42:
318e1b22 8d4de8          lea     ecx,[ebp-18h]
318e1b25 51              push    ecx
318e1b26 56              push    esi
318e1b27 ff5050          call    dword ptr [eax+50h]
318e1b2a 8b06            mov     eax,dword ptr [esi]
318e1b2c 8b4dec          mov     ecx,dword ptr [ebp-14h]
318e1b2f 8945e4          mov     dword ptr [ebp-1Ch],eax
318e1b32 0fb745e8        movzx   eax,word ptr [ebp-18h]
318e1b36 57              push    edi
318e1b37 50              push    eax
318e1b38 83c114          add     ecx,14h
318e1b3b ff15f4348f31    call    dword ptr [KernelUtil!ValidateBugReport+0xef2e (318f34f4)]
318e1b41 50              push    eax
318e1b42 8b45e4          mov     eax,dword ptr [ebp-1Ch]
318e1b45 56              push    esi
318e1b46 ff5050          call    dword ptr [eax+50h]
[/cpp]

此处汇编指出第二个参数是push eax(地址:318e1b41),eax是从[KernelUtil!ValidateBugReport+0xef2e]函数返回的返回值,并且上一条指令是add ecx,14h
由此可知,[KernelUtil!ValidateBugReport+0xef2e]函数是一个类成员函数,ecx+14h就是一个类对象了。并且这个对象是ecx类对象的内部成员变量。
看看[KernelUtil!ValidateBugReport+0xef2e]是个神马函数:

代码:
0:000> dds KernelUtil!ValidateBugReport+0xef2e l1
318f34f4  300d9ab1 Common!CTXStringW::operator wchar_t const *
[/cpp]

原来ecx+14h是一个CTXStringW类对象。
看看这个函数

代码:
0:000> uf 300d9ab1 
Common!CTXStringW::GetString:
300d9162 56              push    esi
300d9163 8bf1            mov     esi,ecx
300d9165 e8dcfdffff      call    Common!CTXStringW::Refresh (300d8f46)
300d916a 8b06            mov     eax,dword ptr [esi]
300d916c 5e              pop     esi
300d916d c3              ret
[/cpp]

看来,字符串就存储在第一个变量内,并且是一个指针。要想从CTXStringW中获取wchar_t const *字符串,只需poi(ecx)即可。
OK,现在咱们知道了ecx+14h是一个CTXStringW对象,聊天内容字符串就存在此对象中,那ecx所指向的对象是哪里来的呢?
继续看上面的反汇编,发现ecx最后一次被赋值是在
318e1b2c 8b4dec          mov     ecx,dword ptr [ebp-14h]
ebp-14h中的值是哪里来的呢?继续向上反汇编

代码:
0:000> ub 318e1b22 l21
KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x7de4:
318e1ac4 c20400          ret     4
318e1ac7 6a14            push    14h
318e1ac9 b8fb148f31      mov     eax,offset KernelUtil!ValidateBugReport+0xcf35 (318f14fb)
318e1ace e8481e0000      call    KernelUtil!CFileResumeInfoMgr::DeleteResumeFile+0x717 (318e391b)
318e1ad3 894dec          mov     dword ptr [ebp-14h],ecx
318e1ad6 8b7508          mov     esi,dword ptr [ebp+8]
318e1ad9 33ff            xor     edi,edi
318e1adb 3bf7            cmp     esi,edi
318e1add 750a            jne     KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x7e09 (318e1ae9)
318e1adf b805400080      mov     eax,80004005h
318e1ae4 e9a9020000      jmp     KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x80b2 (318e1d92)
318e1ae9 83c114          add     ecx,14h
318e1aec ff15ec308f31    call    dword ptr [KernelUtil!ValidateBugReport+0xeb26 (318f30ec)]
318e1af2 84c0            test    al,al
318e1af4 8b1dd4308f31    mov     ebx,dword ptr [KernelUtil!ValidateBugReport+0xeb0e (318f30d4)]
318e1afa 754d            jne     KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x7e69 (318e1b49)
318e1afc 8b4dec          mov     ecx,dword ptr [ebp-14h]
318e1aff 83c114          add     ecx,14h
318e1b02 c6450b01        mov     byte ptr [ebp+0Bh],1
318e1b06 ffd3            call    ebx
318e1b08 03c0            add     eax,eax
318e1b0a 57              push    edi
318e1b0b 0fb7c0          movzx   eax,ax
318e1b0e 6a01            push    1
318e1b10 8d4d0b          lea     ecx,[ebp+0Bh]
318e1b13 51              push    ecx
318e1b14 8945e8          mov     dword ptr [ebp-18h],eax
318e1b17 8b06            mov     eax,dword ptr [esi]
318e1b19 56              push    esi
318e1b1a ff5050          call    dword ptr [eax+50h]
318e1b1d 8b06            mov     eax,dword ptr [esi]
318e1b1f 57              push    edi
318e1b20 6a02            push    2
[/cpp]

这个函数还挺长。看完后终于找到ebp-14h的来历:

代码:
318e1ad3 894dec          mov     dword ptr [ebp-14h],ecx
[/cpp]

原来就是KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x7de7函数的类对象。看来只看此函数是不知道ecx是那冒出来的,
继续看下一个调用栈
KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x7de7函数是从318dda9c的上一条地址调用过来的,咱们对318dda9c向上反汇编看看ecx的来历
02 0012d958 318dda9c 019bc360 b2d95feb 05aa688c KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x7e69

代码:
0:000> ub 318dda9c  l40
KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x3d1c:
318dd9fc 50              push    eax
318dd9fd ff5108          call    dword ptr [ecx+8]
318dda00 8bc3            mov     eax,ebx
318dda02 e8ec5f0000      call    KernelUtil!CFileResumeInfoMgr::DeleteResumeFile+0x7ef (318e39f3)
318dda07 c20800          ret     8
318dda0a 6a08            push    8
318dda0c b8d3178f31      mov     eax,offset KernelUtil!ValidateBugReport+0xd20d (318f17d3)
318dda11 e8055f0000      call    KernelUtil!CFileResumeInfoMgr::DeleteResumeFile+0x717 (318e391b)
318dda16 8b4d08          mov     ecx,dword ptr [ebp+8]
318dda19 8b7904          mov     edi,dword ptr [ecx+4]
318dda1c 8b750c          mov     esi,dword ptr [ebp+0Ch]
318dda1f 33db            xor     ebx,ebx
318dda21 3bfb            cmp     edi,ebx
318dda23 7471            je      KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x3db6 (318dda96)
318dda25 895df0          mov     dword ptr [ebp-10h],ebx
318dda28 8d4df0          lea     ecx,[ebp-10h]
318dda2b 895dfc          mov     dword ptr [ebp-4],ebx
318dda2e e86411f9ff      call    KernelUtil!CTXCCProtocolStrategy::GetUin+0x203 (3186eb97)
318dda33 8b0f            mov     ecx,dword ptr [edi]
318dda35 50              push    eax
318dda36 57              push    edi
318dda37 ff5160          call    dword ptr [ecx+60h]
318dda3a 8b06            mov     eax,dword ptr [esi]
318dda3c 53              push    ebx
318dda3d 6a01            push    1
318dda3f 8d4d0f          lea     ecx,[ebp+0Fh]
318dda42 51              push    ecx
318dda43 56              push    esi
318dda44 885d0f          mov     byte ptr [ebp+0Fh],bl
318dda47 ff5050          call    dword ptr [eax+50h]
318dda4a 8d4df0          lea     ecx,[ebp-10h]
318dda4d e8a911f9ff      call    KernelUtil!CTXCCProtocolStrategy::GetUin+0x267 (3186ebfb)
318dda52 53              push    ebx
318dda53 0fb7c0          movzx   eax,ax
318dda56 6a02            push    2
318dda58 8d4dec          lea     ecx,[ebp-14h]
318dda5b 51              push    ecx
318dda5c 8945ec          mov     dword ptr [ebp-14h],eax
318dda5f 8b06            mov     eax,dword ptr [esi]
318dda61 56              push    esi
318dda62 ff5050          call    dword ptr [eax+50h]
318dda65 395df0          cmp     dword ptr [ebp-10h],ebx
318dda68 750b            jne     KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x3d95 (318dda75)
318dda6a 8d45f0          lea     eax,[ebp-10h]
318dda6d 50              push    eax
318dda6e ff1524358f31    call    dword ptr [KernelUtil!ValidateBugReport+0xef5e (318f3524)]
318dda74 59              pop     ecx
318dda75 8b06            mov     eax,dword ptr [esi]
318dda77 53              push    ebx
318dda78 ff75f0          push    dword ptr [ebp-10h]
318dda7b 56              push    esi
318dda7c ff5054          call    dword ptr [eax+54h]
318dda7f 8b45f0          mov     eax,dword ptr [ebp-10h]
318dda82 834dfcff        or      dword ptr [ebp-4],0FFFFFFFFh
318dda86 3bc3            cmp     eax,ebx
318dda88 7409            je      KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x3db3 (318dda93)
318dda8a 8b08            mov     ecx,dword ptr [eax]
318dda8c 50              push    eax
318dda8d ff5108          call    dword ptr [ecx+8]
318dda90 895df0          mov     dword ptr [ebp-10h],ebx
318dda93 8b4d08          mov     ecx,dword ptr [ebp+8]
318dda96 8b01            mov     eax,dword ptr [ecx]
318dda98 56              push    esi
318dda99 ff505c          call    dword ptr [eax+5Ch]
[/cpp]

由此可知ecx是从
318dda93 8b4d08          mov     ecx,dword ptr [ebp+8]
处被赋值的,下面就找找ebp+8的值哪里来的。为啥要从下向上看?就是要找到ecx寄存器最后一次被赋值的地方!
看了一遍之后,没找到给ebp+8赋值的地方。那说明ebp+8是参数传过来的。那是第几个参数呢?没看到push ebp ,mov ebp,esp啊,我看到此函数第三行有一个调用
318dda11 e8055f0000      call    KernelUtil!CFileResumeInfoMgr::DeleteResumeFile+0x717 (318e391b)

代码:
0:000> uf 318e391b
KernelUtil!CFileResumeInfoMgr::DeleteResumeFile+0x717:
318e391b 50              push    eax
318e391c 64ff3500000000  push    dword ptr fs:[0]
318e3923 8d44240c        lea     eax,[esp+0Ch] //esp+c,这里为啥要加C呢,上两条指令已经加上返回地址,调用此函数到运行到这里,共压栈
                                                                                                //压栈3此,共计0xC个字节,esp+0Ch正好是调用此函数前的栈帧
318e3927 2b64240c        sub     esp,dword ptr [esp+0Ch]
318e392b 53              push    ebx
318e392c 56              push    esi
318e392d 57              push    edi
318e392e 8928            mov     dword ptr [eax],ebp
318e3930 8be8            mov     ebp,eax //此处给ebp赋值,eax经过上面的分析可知。ebp此时的赋值,相当于在调用此函数前执行了mov ebp,esp
                                                                                //再加上,上一个函数执行了一条push 8,此时也就知道了上一个函数的ebp+8其实是指向的第一个参数
318e3932 a160419331      mov     eax,dword ptr [KernelUtil!CppSQLite3DB::`vftable'+0x2c484 (31934160)]
318e3937 33c5            xor     eax,ebp
318e3939 50              push    eax
318e393a ff75fc          push    dword ptr [ebp-4]
318e393d c745fcffffffff  mov     dword ptr [ebp-4],0FFFFFFFFh
318e3944 8d45f4          lea     eax,[ebp-0Ch]
318e3947 64a300000000    mov     dword ptr fs:[00000000h],eax
318e394d c3              ret
[/cpp]

分析了KernelUtil!CFileResumeInfoMgr::DeleteResumeFile+0x717函数知道ebp+8就是第一个参数。ecx=[ebp+8]
咱看一下第一个参数的值是多少

代码:
03 0012d988 318dfedb 05847a70 059bc360 0012da4c KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x3dbc
[/cpp]

第一个参数是05847a70,也就是ecx=05847a70。聊天内容存储在ecx+14h的类变量里,取出聊天内容的方法为str=*(wchar**)(ecx+14h)
好。我们来验证一下是不是这样

代码:
0:000> du poi(05847a70 +14)
0779c308  "ddlxtest2."
[/cpp]

哈哈,现在很是兴奋啊,离目标不远了。

继续看一下第一个参数是哪里来的

代码:
03 0012d988 318dfedb 05847a70 059bc360 0012da4c KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x3dbc
[/cpp]
代码:
0:000> ub 318dfedb l10
KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x61d7:
318dfeb7 4d              dec     ebp
318dfeb8 f8              clc
318dfeb9 51              push    ecx
318dfeba 56              push    esi
318dfebb ff5014          call    dword ptr [eax+14h]
318dfebe 8b4d08          mov     ecx,dword ptr [ebp+8]
318dfec1 6a02            push    2
318dfec3 8d45f8          lea     eax,[ebp-8]
318dfec6 50              push    eax
318dfec7 e80252faff      call    KernelUtil!Util::Msg::SetMsgDB2StorageMode+0x42c (318850ce)
318dfecc 8b4d08          mov     ecx,dword ptr [ebp+8]
318dfecf e8afecf8ff      call    KernelUtil!CTXCCProtocolStrategy::GetUin+0x1ef (3186eb83)
318dfed4 8b0e            mov     ecx,dword ptr [esi]
318dfed6 50              push    eax
318dfed7 56              push    esi
318dfed8 ff5118          call    dword ptr [ecx+18h]
[/cpp]

第一个参数是push    esi得来的,esi是哪里来的呢?

代码:
0:000> ub 318dfedb l40-7
KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x6167:
318dfe47 55              push    ebp
318dfe48 8bec            mov     ebp,esp
318dfe4a 83ec18          sub     esp,18h
318dfe4d 8365f800        and     dword ptr [ebp-8],0
318dfe51 57              push    edi
318dfe52 8d7924          lea     edi,[ecx+24h]
318dfe55 57              push    edi
318dfe56 ff7704          push    dword ptr [edi+4]
318dfe59 8d4de8          lea     ecx,[ebp-18h]
318dfe5c c645ff00        mov     byte ptr [ebp-1],0
318dfe60 e83e1ff9ff      call    KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7ca (31871da3)
318dfe65 8b45e8          mov     eax,dword ptr [ebp-18h]
318dfe68 57              push    edi
318dfe69 ff7708          push    dword ptr [edi+8]
318dfe6c 8945f0          mov     dword ptr [ebp-10h],eax
318dfe6f 8b45ec          mov     eax,dword ptr [ebp-14h]
318dfe72 8d4de8          lea     ecx,[ebp-18h]
318dfe75 8945f4          mov     dword ptr [ebp-0Ch],eax
318dfe78 e8261ff9ff      call    KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7ca (31871da3)
318dfe7d 8d45e8          lea     eax,[ebp-18h]
318dfe80 50              push    eax
318dfe81 8d4df0          lea     ecx,[ebp-10h]
318dfe84 e8d546fcff      call    KernelUtil!DllUnregisterServer+0x113c (318a455e)
318dfe89 84c0            test    al,al
318dfe8b 7573            jne     KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x6220 (318dff00)
318dfe8d 56              push    esi
318dfe8e 8d4df0          lea     ecx,[ebp-10h]
318dfe91 e8344afeff      call    KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x1698 (318c48ca)
318dfe96 8b30            mov     esi,dword ptr [eax] //发现有个esi赋值操作
318dfe98 85f6            test    esi,esi
318dfe9a 743f            je      KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x61fb (318dfedb)
318dfe9c 8b06            mov     eax,dword ptr [esi]
318dfe9e 8d4dff          lea     ecx,[ebp-1]
318dfea1 51              push    ecx
318dfea2 56              push    esi
318dfea3 ff500c          call    dword ptr [eax+0Ch]
318dfea6 8b4d08          mov     ecx,dword ptr [ebp+8]
318dfea9 6a01            push    1
318dfeab 8d45ff          lea     eax,[ebp-1]
318dfeae 50              push    eax
318dfeaf e81a52faff      call    KernelUtil!Util::Msg::SetMsgDB2StorageMode+0x42c (318850ce)
318dfeb4 8b06            mov     eax,dword ptr [esi]
318dfeb6 8d4df8          lea     ecx,[ebp-8]
318dfeb9 51              push    ecx
318dfeba 56              push    esi
318dfebb ff5014          call    dword ptr [eax+14h]
318dfebe 8b4d08          mov     ecx,dword ptr [ebp+8]
318dfec1 6a02            push    2
318dfec3 8d45f8          lea     eax,[ebp-8]
318dfec6 50              push    eax
318dfec7 e80252faff      call    KernelUtil!Util::Msg::SetMsgDB2StorageMode+0x42c (318850ce)
318dfecc 8b4d08          mov     ecx,dword ptr [ebp+8]
318dfecf e8afecf8ff      call    KernelUtil!CTXCCProtocolStrategy::GetUin+0x1ef (3186eb83)
318dfed4 8b0e            mov     ecx,dword ptr [esi]
318dfed6 50              push    eax
318dfed7 56              push    esi
318dfed8 ff5118          call    dword ptr [ecx+18h]
[/cpp]
代码:
318dfe91 e8344afeff      call    KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x1698 (318c48ca)
318dfe96 8b30            mov     esi,dword ptr [eax] 
[/cpp]

是对esi进行赋值操作。
那函数KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x1698是干嘛的?

代码:
0:000> uf KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x1698
KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x1698:
318c48ca 56              push    esi
318c48cb 8bf1            mov     esi,ecx
318c48cd 833e00          cmp     dword ptr [esi],0
318c48d0 57              push    edi
318c48d1 8b3dec378f31    mov     edi,dword ptr [KernelUtil!ValidateBugReport+0xf226 (318f37ec)]
318c48d7 7502            jne     KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x16a9 (318c48db)

KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x16a7:
318c48d9 ffd7            call    edi

KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x16a9:
318c48db 8b06            mov     eax,dword ptr [esi]
318c48dd 8b4e04          mov     ecx,dword ptr [esi+4]
318c48e0 3b4808          cmp     ecx,dword ptr [eax+8]
318c48e3 7202            jb      KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x16b5 (318c48e7)

KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x16b3:
318c48e5 ffd7            call    edi

KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x16b5:
318c48e7 8b4604          mov     eax,dword ptr [esi+4]
318c48ea 5f              pop     edi
318c48eb 5e              pop     esi
318c48ec c3              ret
[/cpp]

此函数的伪代码,大致是这样:
eax = *(void**)(ecx+4)

代码:
318dfe8e 8d4df0          lea     ecx,[ebp-10h]
318dfe91 e8344afeff      call    KernelUtil!CDRStrCodecBaseHttp2Cs::CodeStr+0x1698 (318c48ca)
318dfe96 8b30            mov     esi,dword ptr [eax]
[/cpp]

根据上下文推出上面的代码相当于 esi = *(void**)(ebp-0Ch) //-10h+4h = -0Ch
找一下谁对ebp-0Ch进行的赋值

代码:
318dfe75 8945f4          mov     dword ptr [ebp-0Ch],eax
[/cpp]

eax是怎么来的呢?

代码:
318dfe6f 8b45ec          mov     eax,dword ptr [ebp-14h]
[/cpp]

这么说,esi = *(void**)(ebp-0Ch)= *(void**)(ebp-14h)
ebp-14中的值怎么来的?
找了一下没找到,但是看到一处函数调用

代码:
318dfe60 e83e1ff9ff      call    KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7ca (31871da3)
[/cpp]

可能秘密在这里。

代码:
0:000> uf 31871da3
KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7ca:
31871da3 55              push    ebp
31871da4 8bec            mov     ebp,esp
31871da6 53              push    ebx
31871da7 8b5d08          mov     ebx,dword ptr [ebp+8]
31871daa 56              push    esi
31871dab 57              push    edi
31871dac 8b7d0c          mov     edi,dword ptr [ebp+0Ch]
31871daf 8bf1            mov     esi,ecx
31871db1 832600          and     dword ptr [esi],0
31871db4 85ff            test    edi,edi
31871db6 740a            je      KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7e9 (31871dc2)

KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7df:
31871db8 395f04          cmp     dword ptr [edi+4],ebx
31871dbb 7705            ja      KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7e9 (31871dc2)

KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7e4:
31871dbd 3b5f08          cmp     ebx,dword ptr [edi+8]
31871dc0 7606            jbe     KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7ef (31871dc8)

KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7e9:
31871dc2 ff15ec378f31    call    dword ptr [KernelUtil!ValidateBugReport+0xf226 (318f37ec)]

KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7ef:
31871dc8 893e            mov     dword ptr [esi],edi
31871dca 5f              pop     edi
31871dcb 895e04          mov     dword ptr [esi+4],ebx
31871dce 8bc6            mov     eax,esi
31871dd0 5e              pop     esi
31871dd1 5b              pop     ebx
31871dd2 5d              pop     ebp
31871dd3 c20800          ret     8
[/cpp]

上面的函数的伪代码大致如下
class class0
{
public:
class0( void* p1, void* p2 )
{
m_ptr1 = p2;
m_ptr2 = p1;
}
private:
void* m_ptr1;
void* m_ptr2;
}
KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7ca函数就是class0的构造函数
再根据上下文

代码:
318dfe51 57              push    edi
318dfe52 8d7924          lea     edi,[ecx+24h]
318dfe55 57              push    edi
318dfe56 ff7704          push    dword ptr [edi+4]
318dfe59 8d4de8          lea     ecx,[ebp-18h]
318dfe5c c645ff00        mov     byte ptr [ebp-1],0
318dfe60 e83e1ff9ff      call    KernelUtil!CCcSeqManager::IsCmdSeqRecved+0x7ca (31871da3)
318dfe65 8b45e8          mov     eax,dword ptr [ebp-18h]
318dfe68 57              push    edi
318dfe69 ff7708          push    dword ptr [edi+8]
318dfe6c 8945f0          mov     dword ptr [ebp-10h],eax
318dfe6f 8b45ec          mov     eax,dword ptr [ebp-14h]
318dfe72 8d4de8          lea     ecx,[ebp-18h]
318dfe75 8945f4          mov     dword ptr [ebp-0Ch],eax
[/cpp]

可知
[ebp-18] = ecx+24h
[ebp-14] = [ecx+24+4]
再加上 esi = *(void**)(ebp-0Ch) = *(void**)(ebp-14h)
现在可以确定esi与ecx之间的关系了
esi = *(void**)*(void**)(ecx+24+4)

这个函数是最难理解的了。 确定了esi与ecx的关系,那ecx是怎么来的呢?

代码:
04 0012d9b8 318dff6b 0012d9ec b2d95f83 05aa6868 KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x61fb
[/cpp]

看一下上层函数是谁

代码:
0:000> ub 318dff6b l25
KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x6220:
318dff00 33c0            xor     eax,eax
318dff02 40              inc     eax
318dff03 5f              pop     edi
318dff04 c9              leave
318dff05 c20400          ret     4
318dff08 6a00            push    0
318dff0a b85aab8e31      mov     eax,offset KernelUtil!ValidateBugReport+0x6594 (318eab5a)
318dff0f e8073a0000      call    KernelUtil!CFileResumeInfoMgr::DeleteResumeFile+0x717 (318e391b) //此函数上面已经给出说明,此函数导致ebp+8指向第一个参数
318dff14 8b750c          mov     esi,dword ptr [ebp+0Ch]
318dff17 85f6            test    esi,esi
318dff19 7507            jne     KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x6242 (318dff22)
318dff1b b857000780      mov     eax,80070057h
318dff20 eb7d            jmp     KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x62bf (318dff9f)
318dff22 83650c00        and     dword ptr [ebp+0Ch],0
318dff26 8365fc00        and     dword ptr [ebp-4],0
318dff2a 6800020000      push    200h
318dff2f 8d4d0c          lea     ecx,[ebp+0Ch]
318dff32 e889d8ffff      call    KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x3ae0 (318dd7c0)
318dff37 8b4d08          mov     ecx,dword ptr [ebp+8]
318dff3a 8d450c          lea     eax,[ebp+0Ch]
318dff3d 50              push    eax
318dff3e e829f0ffff      call    KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x528c (318def6c)
318dff43 85c0            test    eax,eax
318dff45 7518            jne     KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x627f (318dff5f)
318dff47 8b450c          mov     eax,dword ptr [ebp+0Ch]
318dff4a 834dfcff        or      dword ptr [ebp-4],0FFFFFFFFh
318dff4e 85c0            test    eax,eax
318dff50 7406            je      KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x6278 (318dff58)
318dff52 8b08            mov     ecx,dword ptr [eax]
318dff54 50              push    eax
318dff55 ff5108          call    dword ptr [ecx+8]
318dff58 b805400080      mov     eax,80004005h
318dff5d eb40            jmp     KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x62bf (318dff9f)
318dff5f 8b4d08          mov     ecx,dword ptr [ebp+8] //对ecx赋值, ebp+8就是第一个参数。
318dff62 8d450c          lea     eax,[ebp+0Ch]
318dff65 50              push    eax
318dff66 e8dcfeffff      call    KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x6167 (318dfe47)
[/cpp]

看完之后确定ecx 是通过第一个参数传递过来的,第一个参数是什么呢?

代码:
05 0012d9e0 0328d5e0 05aa6868 059bc360 996b7b57 KernelUtil!CDRStrCodecBaseHttp2Cs::AddRef+0x628b
[/cpp]

是05aa6868!!!!!
05aa6868是什么?就是KernelUtil!Util::Msg::SaveMsg194函数的第五个参数啊!!!功夫不负有心人,终于找到内容从哪个参数中取了。
慢着,通过第五个参数怎么获取内容呢?在绿一下
经过分析,struct ITXMsgPack大致长成这个样子:
struct ITXMsgPack
{
char szUnknown[0x28];
UnknownSt1* pst1;
...
}
struct UnknownSt1
{
UnknownSt2* pst2;
}
struct UnknownSt2
{
char szUnknown[0x14];
wchar* pMsg;
...
}
验证一下,没有验证就无法证明推理是正确的

代码:
0:000> du poi(poi(poi(05aa6868+0x28))+14)
0779c308  "ddlxtest2."
[/cpp]

到此分析完了。本来就要结束了,但是感觉需要再升华一下。

我打算用用windbg监控聊天信息,每次聊天都输出日志,日志信息包括自己的账号,对方的账号,以及聊天内容
//设置一个记录断点

代码:
0:012> bp 3189bb3a ".printf \"lcoalId:%u tergatId:%u msg:%mu\\n\" ,poi(esp+c),poi(esp+10),poi(poi(poi(poi(esp+14)+0x28))+14);gc;"
breakpoint 0 redefined
0:012> bl
 0 e 3189bb3a     0001 (0001)  0:**** KernelUtil!Util::Msg::SaveMsg194 ".printf \"lcoalId:%u tergatId:%u msg:%mu\\n\" ,poi(esp+c),poi(esp+10),poi(poi(poi(poi(esp+14)+0x28))+14);gc;"
0:012> g
//下面是输出的记录信息
lcoalId:127631**** tergatId:143174**** msg:ddlxtest6
lcoalId:127631**** tergatId:143174**** msg:ddlxtest7
lcoalId:127631**** tergatId:183498**** msg:hello pediy
[/cpp]

本篇只分析了发送,接受消息不走3189bb3a函数。有兴趣的就分析一下接受消息。

多余的话
本次分析最大的难点在于怎么拿取聊天内容。
楼主通过反向剥洋葱分析法,通过对每层调用栈的刨析,最终找到通过ITXMsgPack拿取聊天内容的方法。
奉劝各位刚接触逆向的朋友,遇到问题不要着急,答案就在附近,仔细寻找就能找到他!
不管是用Od还是用Windbg,他们都是工具而已。
最终用来解决问题的是我们的大脑。所以不管使用什么工具,只要有思路就可以找到你想要的答案。
总之一句话,方法和技巧才是找到问题根源的最终武器。工具只是验证你的方法是否正确。

最后编辑:
作者:小企鹅
坚持+积累+学习
捐 赠如果您觉得这篇文章有用处,请支持作者!鼓励作者写出更好更多的文章!

留下一个回复