12.1 识别类和类之间的关系(9)
我们可以借此得到派生关系,在构造函数中先填写父类的虚表,然后按继承的层次关系逐层填写子类的虚表,由此可以判定vTable_40C0DC是父类的虚表,vTable_40C0D0是子类的虚表。以写入虚表的指令为界限,可以粗略划分出父类的构造函数和子类的构造函数的实现代码,但是细节上要按照程序逻辑找到界限之内其他函数传递参数的几行代码,并排除在外,如下所示:
- ; 先定位到new调用的正确分支处
- .text:00401099 call 2@YAPAXI@Z ; operator new(uint) ; 调用new
- .text:0040109E mov esi, eax
- .text:004010A0 add esp, 4
- .text:004010A3 mov [esp+14h+var_10], esi
- .text:004010A7 test esi, esi ; 判定new调用后的返回值
- .text:004010A9 mov [esp+14h+var_4], 0
- .text:004010B1 jz short loc_4010F2 ; 返回值为0,则跳转到错误逻辑处
- ; 从这里开始就是正确的逻辑,同时也是父类构造函数的起始代码处
- .text:004010B3 mov ecx, esi
- .text:004010B5 mov dword ptr [esi], offset vTable_40C0DC
- .text:004010BB call ds:pfnGetCPerson
- .text:004010C1 push eax
- .text:004010C2 push offset Format ; "%s::ShowSpeak()\r\n"
- .text:004010C7 call _printf
- .text:004010CC add esp, 8
- ; 注意这里的传参(this指针),从这里开始就不是父类的构造函数实现代码了
- .text:004010CF mov ecx, esi
- .text:004010D1 mov byte ptr [esp+14h+var_4], 1
- .text:004010D6 mov dword ptr [esi], offset vTable_40C0D0
- .text:004010DC call ds:pfnGetCChinese
- .text:004010E2 push eax
- .text:004010E3 push offset Format ; "%s::ShowSpeak()\r\n"
- .text:004010E8 call _printf
- .text:004010ED add esp, 8
- ; new调用的正确分支末尾,同时也是子类构造函数的结束处
- .text:004010F0 jmp short loc_4010F4
继续看后面的代码:- .text:004010F4
- .text:004010F4 loc_4010F4: ; CODE XREF: _main+70↑j
- .text:004010F4 mov eax, [esi] ; 取得虚表指针
- .text:004010F6 mov ecx, esi ; 传递this指针
- .text:004010F8 mov [esp+14h+var_4], 0FFFFFFFFh ; 修改计数器
- .text:00401100 call dword ptr [eax+4] ; 调用虚表第二项的函数
分析一下这里的虚函数调用,先看看最后一次写入虚表的地址,单击esi,往上观察高亮处,寻找最后一次写入的指令,如图12-3所示。
|
| (点击查看大图)图12-3 寻找最后一次写入虚表的指令 |
细心的读者一定找到了!没错,正是004010D6地址处!指令“call dword ptr [eax+4]”揭示出虚表中至少有两个元素。接下来分析在004010D6处写入虚表vTable_40C0D0中的第二项内容到底是什么。
- .rdata:0040C0D0 vTable_40C0D0 dd offset sub_4011C0 ;
虚表偏移0处,也就是虚表的第一项 - .rdata:0040C0D4 off_40C0D4 dd offset sub_401140 ;
虚表偏移4处,也就是虚表的第二项 - .rdata:0040C0D8 pfnGetCChinese dd offset GetCChinese ;
现在不能确定这一项是否为虚表的内容
双击sub_401140,得到以下代码:
- .text:00401140 sub_401140 proc near
- ; 未赋值就直接使用ecx,说明ecx是在传递参数
- .text:00401140 mov eax, [ecx] ; eax得到虚表
- .text:00401142 call dword ptr [eax+8] ; 调用虚表第三项,形成了多态