• 如何在dump文件里找到真正的类this指针


    我们都知道,C++的类成员函数调用离不this指针,this指针其实是作为隐形参数传递到成员函数的,VC++编译器是借助ecx寄存器来传递的。也正式这一点,很多调试器借助它来获取this指针。

    在我们正向单步调试时,Windbg可以正确获取到this指针

    0:000> dx Debugger.Sessions[0].Processes[5684].Threads[10640].Stack.Frames[21].SwitchTo();dv /t /v
    Debugger.Sessions[0].Processes[5684].Threads[10640].Stack.Frames[21].SwitchTo()
    0035ca04          class xtModel * this = 0xff2d3c48
    又如:

    0:000> dx Debugger.Sessions[0].Processes[5684].Threads[10640].Stack.Frames[32].SwitchTo();dv /t /v
    Debugger.Sessions[0].Processes[5684].Threads[10640].Stack.Frames[32].SwitchTo()
    0035d204          class MainWind * this = 0x28c204f0

    但是当我们进行dmp分析,很多情况下windbg是获取不到的,因为进入到成员函数后,ecx被拿去做其他用途,最典型的就是在这个成员函数里调用其他类的成员函数,ecx被用来传递这个类的指针。又或是被用做循环计算器了,等等。总之是根据实际代码情况,由编译器决定。

    通常情况下,我们拿dmp文件,用windbg打开,在确定崩溃调用栈后,还要查找真正崩溃的原因,也就是要查看相关数据。很多时候我们是要观察类成员的,那么这时候就是知道this指针了。幸运的情况下,我们切换栈帧后,windbg能自动获取并显示出this指针,单不是根据ecx来的,如下:0:000> .frame /r 20;dv /t /v
    20 0035d264 55e3337e XXX!MainWind::XXX+0xd1
    eax=0035c570 ebx=0000000f ecx=00000003 edx=00000000 esi=0000000f edi=763cb83f
    eip=55d4a5c1 esp=0035d1f4 ebp=0035d264 iopl=0         nv up ei pl nz ac po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
    XXX!MainWind::XXX+0xd1
    55d4a5c1 83c40c          add     esp,0Ch
    0035d204          class MainWind * this = 0x28c204f0
    我们可以看到windbg自动得到this指针,但跟寄存器ecx的值不一样。很显然,ecx被做它用而被修改了,那么自动获取的this = 0x28c204f0的对不对呢?
    我们看下XXX!MainWind::XXX的实现,如下:

    0:000> uf XXX!MainWind::XXX
    XXX!MainWind::XXX
    14148 55d4a4f0 55              push    ebp
    14148 55d4a4f1 8bec            mov     ebp,esp
    14148 55d4a4f3 6aff            push    0FFFFFFFFh
    14148 55d4a4f5 68a1967756      push    offset _aullshr+0x55091 (567796a1)
    14148 55d4a4fa 64a100000000    mov     eax,dword ptr fs:[00000000h]
    14148 55d4a500 50              push    eax
    14148 55d4a501 83ec54          sub     esp,54h
    14148 55d4a504 a1e453a956      mov     eax,dword ptr [__security_cookie (56a953e4)]
    14148 55d4a509 33c5            xor     eax,ebp
    14148 55d4a50b 8945f0          mov     dword ptr [ebp-10h],eax
    14148 55d4a50e 50              push    eax
    14148 55d4a50f 8d45f4          lea     eax,[ebp-0Ch]
    14148 55d4a512 64a300000000    mov     dword ptr fs:[00000000h],eax
    14148 55d4a518 894da0          mov     dword ptr [ebp-60h],ecx
    14149 55d4a51b e820041800      call    55eca940
    14149 55d4a520 8945a4          mov     dword ptr [ebp-5Ch],eax
    14150 55d4a523 8d45d8          lea     eax,[ebp-28h]
    14150 55d4a526 50              push    eax
    14150 55d4a527 8b4da4          mov     ecx,dword ptr [ebp-5Ch]

    我们注意到,在函数的开头将ecx的值保存到栈里ebp-60h的地址,由前面的信息我们知道ebp=0035d264,那么ebp-60= 0035d204
    0:000> dd 0035d204
    0035d204  28c204f0 e3ed2f50 facf4fe0 00000000
    0035d214  00000020 0035d2c4 00000011 0000001f
    0035d224  e0d36fe0 763cb83f 5b1bbb10 5aeffcf3
    0035d234  00000011 0000001f 706f7264 00000000
    0035d244  00000009 00000000 00000004 0000000f
    0035d254  abfbcb80 0035d834 567796a1 00000002
    0035d264  0035d274 55e3337e 56af86a8 b221afec
    0035d274  0035d288 55e12ca1 28c204f0 56af86a8
    可知是this指针是对的。

    我们看看,windbg获取错误的this指针情况,如下:

    0:000> .frame /r 16;dv /t /v
    16 0036d394 53735682 XXX!C::MainWndTest+0x266
    eax=c86ff008 ebx=0036d3d8 ecx=00000008 edx=00000008 esi=00000002 edi=3f088b10
    eip=536f0076 esp=0036d35c ebp=0036d394 iopl=0         nv up ei ng nz na po cy
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010283
    XXX!MainWndTest+0x266:
    536f0076 8bcf            mov     ecx,edi
    @ecx              class C* this = 0x00000008

    看到了吗,直接从ecx拿this指针,很明显不对。根据上面的思路。我们先看看XXX!C::MainWndTest的实现

    0:000> uf XXX!C::MainWndTest

    XXX!C::MainWndTest
    4044 536efe10 55              push    ebp
     4044 536efe11 8bec            mov     ebp,esp
     4044 536efe13 6aff            push    0FFFFFFFFh
     4044 536efe15 6888e57753      push    offset memmove_s+0xdcd4 (5377e588)
     4044 536efe1a 64a100000000    mov     eax,dword ptr fs:[00000000h]
     4044 536efe20 50              push    eax
     4044 536efe21 83ec14          sub     esp,14h
     4044 536efe24 53              push    ebx
     4044 536efe25 56              push    esi
     4044 536efe26 57              push    edi
     4044 536efe27 a110337b53      mov     eax,dword ptr [__security_cookie (537b3310)]
     4044 536efe2c 33c5            xor     eax,ebp
     4044 536efe2e 50              push    eax
     4044 536efe2f 8d45f4          lea     eax,[ebp-0Ch]
     4044 536efe32 64a300000000    mov     dword ptr fs:[00000000h],eax
     4044 536efe38 8bf9            mov     edi,ecx


    可以看到把 ecx的值放到edi寄存器,可知this=3f088b10。经验证,是对的

    总结:

    要想真正得到正确的this,需要看具体实现:有没有修改ecx;若修改ecx,必然会先把ecx保存,我们要找到保存的地址,也就得到了正确的ecx

  • 相关阅读:
    二叉树中序遍历及后序遍历(下)
    完全二叉树和三序遍历算法(先序)(上)
    分布式调度——zookeeper
    Linux版JDK环境安装
    Redis的事务
    Redis的持久化——RDB与AOF
    分布式缓存——Redis
    非关系型数据库
    分布式存储——MySQL
    分布式系统
  • 原文地址:https://www.cnblogs.com/yilang/p/13402625.html
Copyright © 2020-2023  润新知