设为首页 加入收藏

TOP

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

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

以代码清单12-5的Debug版为例,使用IDA对其进行分析,先找到某个子类的构造函数。由于子类的构造函数必然会先调用父类的构造函数,因此我们利用交叉参考功能即可查询出所有引用这个父类构造函数的指令的位置,这当然包括这个父类的所有直接子类构造函数的位置,借此即可判定父类派生的所有直接子类,如图12-6所示。

 
 
(点击查看大图)图12-6 父类派生关系图

接下来分析sub_4011E0函数的功能,反汇编代码如下所示:

  1. ; 注意这里的引用提示:是在sub_4011C0函数中调用本函数,稍后会带领读者去这个地址"探险"  
  2. .text:004011E0 sub_4011E0 proc near ; CODE XREF: sub_4011C0+3↑p  
  3. .text:004011E0  
  4. .text:004011E0 var_10dword ptr -10h  
  5. .text:004011E0 var_Cdword ptr -0Ch  
  6. .text:004011E0 var_4dword ptr -4  
  7. .text:004011E0  
  8. .text:004011E0  push 0FFFFFFFFh  
  9. .text:004011E2  push offset unknown_libname_36 ; Microsoft VisualC 2-9/net runtime  
  10. .text:004011E7    mov eax, large fs:0  
  11. .text:004011ED    push eax  
  12. .text:004011EE    mov large fs:0, esp  
  13. .text:004011F5    push ecx  
  14. .text:004011F6    push esi  ; 以上注册异常处理,保留寄存器环境  
  15. .text:004011F7    mov esi, ecx  
  16. .text:004011F9    mov [esp+14h+var_10], esi  
  17. ; 在虚表指针处写入子类虚表地址  
  18. .text:004011FD    mov dword ptr [esi], offset vTable_40C0D0  
  19. .text:00401203    mov [esp+14h+var_4], 0 ; 计数器置为0  
  20. .text:0040120B    call ds:pfnGetCChinese  
  21. .text:00401211    push eax  ; 获取字符串,并向printf传递参数  
  22. .text:00401212    push offset Format ; "%s::ShowSpeak()\r\n"  
  23. .text:00401217    call _printf  
  24. .text:0040121C    add esp, 8    ; 执行printf,并平衡参数  
  25. .text:0040121F    mov ecx, esi ; 传递this指针  
  26. .text:00401221    mov [esp+14h+var_4], 0FFFFFFFFh ; 将计数器置为-1  
  27. ; 在虚表指针处写入父类虚表地址  
  28. .text:00401229     mov dword ptr [esi], offset vTable_40C0DC  
  29. .text:0040122F     call ds:pfnGetCPerson  
  30. .text:00401235     push eax ; 获取字符串,并向printf传递参数  
  31. .text:00401236     push offset Format ; "%s::ShowSpeak()\r\n"  
  32. .text:0040123B     call _printf  
  33. ; 流水线优化,因为mov large fs:0, ecx和当前指令依赖同一个寄存器ecx,会造成指令相关性,所以  
  34. ; 提前到add esp, 8之上,以提高流水线的并行能力  
  35. .text:00401240     mov ecx, [esp+1Ch+var_C]  
  36. .text:00401244     add esp, 8   ; 执行printf,并平衡参数  
  37. .text:00401247     mov large fs:0, ecx ; 恢复环境并还原SEH  
  38. .text:0040124E     pop esi  
  39. .text:0040124F     add esp, 10h  
  40. .text:00401252     retn  
  41. .text:00401252 sub_4011E0 endp 

以上代码中存在虚表的写入操作,其写入顺序和前面分析的构造函数相反,先写入子类自身的虚表,然后写入父类的虚表,满足了析构函数的充分条件。我们将虚构函数命名为Destructor_4011E0,IDA会提示符号名称过长,不必理会,单击“确定”按钮即可。

Destructor_4011E0被sub_4011C0调用,因此接下来分析sub_4011C0,这个函数有一个参数,IDA给出的名称为arg_0。


  1. ; 查看引用参考可得知,这个函数是在虚表vTable_40C0D0中定义的第一个虚函数  
  2. .text:004011C0 sub_4011C0 proc near ; DATA XREF: .rdata:vTable_40C0D0o  
  3. .text:004011C0  
  4. .text:004011C0 arg_0byte ptr  4  
  5. .text:004011C0  
  6. .text:004011C0     push esi  
  7. .text:004011C1     mov esi, ecx ; esi保留了this指针  
  8. .text:004011C3     call Destructor_4011E0 ; 先调用析构函数  
  9. .text:004011C8     test [esp+4+arg_0], 1  
  10. ; 如果参数为1,则以对象首地址为目标释放内存,否则本函数仅仅执行对象的析构函数  
  11. .text:004011CD     jz  short loc_4011D8  
  12. .text:004011CF     push esi ; 传入对象的首地址  
  13. .text:004011D0     call  3@YAXPAX@Z ; operator delete(void *)  
  14. .text:004011D5     add esp, 4   ; 调用delete,并平衡参数  
  15. .text:004011D8  
  16. .text:004011D8 loc_4011D8: ; CODE XREF: sub_4011C0+Dj  
  17. .text:004011D8     mov eax, esi  
  18. .text:004011DA     pop esi  
  19. .text:004011DB     retn 4  
  20. .text:004011DB sub_4011C0 endp 

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

评论

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