设为首页 加入收藏

TOP

12.1 识别类和类之间的关系(8)
2013-10-07 14:31:13 来源: 作者: 【 】 浏览:59
Tags:12.1 识别 类和 之间 关系

12.1 识别类和类之间的关系(8)

接着按N键将off_40C0DC重命名,这里先命名为vTable_40C0DC,在接下来的分析中如果找到更详细的信息,还可以继续修改这个名称,使代码的可读性更强。

  1. .text:004010B5     mov dword ptr [esi], offset vTable_40C0DC 

既然是对虚表指针进行初始化,就要满足构造函数的充分条件,但是我们看到这里并没有调用构造函数,而是直接在main函数中完成了虚表指针的初始化,这说明构造函数被编译器内联优化了。接下来我们来看一个内存间接调用:

  1. .text:004010BB     call ds:off_40C0E4 

off_40C0E4中的内容如下:
  1. .rdata:0040C0DC vTable_40C0DC dd offset sub_401170 ; DATA XREF: _main+35↑o  
  2. .rdata:0040C0DC     ; sub_40ACFB:loc_401120↑o sub_401170+3↑o sub_4011E0+49↑o  
  3. .rdata:0040C0E0     dd offset sub_401140  
  4. .rdata:0040C0E4 off_40C0E4 dd offset sub_401160 ; DATA XREF: _main+3Br 

不难发现,这个地址就在刚才我们分析的虚函数表的首地址附近,这很可能是虚表中的一部分!不过现在只能是怀疑,我们还没有证据。先看看这个函数的功能。双击地址0040C0E4 处“off_40C0E4 dd offset sub_401160”中的sub_401160,定位到sub_401160的代码实现处,此处内容如下所示:
  1. .text:00401160 sub_401160 proc near  
  2. .text:00401160     mov eax, offset aCperson ; "CPerson" ; 功能很简单,返回名称字符串  
  3. .text:00401165     retn  
  4. .text:00401165 sub_401160 endp 

顺手修改sub_401160的名称,这里先修改为GetCPerson,以后有更多信息时再进一步修改。对应地,由于在off_40C0E4中保存了函数GetCPerson的地址,说明它是一个函数指针,因此也可以将其名称修改为pfnGetCPerson,修改完毕后如下所示:
  1. .rdata:0040C0E4 pfnGetCPerson dd offset GetCPerson ; DATA XREF: _main+3Br 

接着分析其后的代码:
  1. .text:004010C1     push eax  
  2. .text:004010C2     push offset aSShowspeak ; "%s::ShowSpeak()\r\n"  
  3. .text:004010C7     call _printf  
  4. .text:004010CC     add esp, 8 ; 调用printf,并平衡参数  
  5. .text:004010CF     mov ecx, esi  
  6. .text:004010D1     mov byte ptr [esp+14h+var_4], 1 ; 计数器加1  
  7. .text:004010D6     mov dword ptr [esi], offset off_40C0D0 ;写入虚表指针,分析过程与上;面的内容一致,略  
  8. .text:004010DC     call ds:off_40C0D8 ; 内存间接调用 

双击off_40C0D8,查看调用目标:
  1. .rdata:0040C0D8 off_40C0D8 dd offset sub_4011B0 ; DATA XREF: _main+5Cr 

off_40C0D8中保存了函数sub_4011B0的地址,双击sub_4011B0,其功能如下所示:
  1. .text:004011B0 sub_4011B0 proc near  
  2. .text:004011B0     mov eax, offset aCchinese ; "CChinese" ; 功能很简单,返回名称字符串  
  3. .text:004011B5     retn  
  4. .text:004011B5 sub_4011B0 endp 

修改一下这个函数的名称,这里改为GetCChinese,也对应修改函数指针off_40C0D8的名称为pfnGetCChinese,修改完毕后如下所示:
  1. .rdata:0040C0D8 pfnGetCChinese dd offset GetCChinese ; DATA XREF: _main+5Cr 

接着分析后面的代码:

  1. .text:004010E2     push eax  
  2. .text:004010E3     push offset aSShowspeak ; "%s::ShowSpeak()\r\n"  
  3. .text:004010E8     call _printf  
  4. .text:004010ED     add esp, 8 ; 调用printf并平衡参数  
  5. .text:004010F0     jmp short loc_4010F4 ; 跳过else分支  
  6. .text:004010F2 ; --------------------------------------------------------------  
  7. .text:004010F2  
  8. .text:004010F2 loc_4010F2: ; CODE XREF: _main+31j  
  9. .text:004010F2     xor esi, esi ; 如果new调用的返回值为0,则esi为0 

到此为止,我们分析了new调用后的整个分支结构。当new调用成功时,会执行对象的构造函数,而编译器对这里的构造函数进行了内联优化,但这不会影响我们对构造函数的鉴定。首先存在写入虚表指针的充分条件,同时也满足前面章节讨论的必要条件,还要出现在new调用的正确分支中,因此,我们可以把new调用的正确分支中的代码判定为构造函数的内联方式。在new调用的正确分支内,由于esi所指向的对象有两次写入虚表指针的代码,如下所示:
  1. .text:004010B5     mov dword ptr [esi], offset vTable_40C0DC  
  2. ;中间代码略  
  3. .text:004010D6     mov dword ptr [esi], offset vTable_40C0D0 

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇12.1 识别类和类之间的关系(9) 下一篇12.1 识别类和类之间的关系(7)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: