同样的章节,示例44,由于我编译的情况与书本上略有差异,故在些记录一下。
区别在于:对Get_VTbl的调用,在我这里直接给赋值了。
试验平台:XP SP3,VS2008,命令行下编译,编译选项加上了优化选项“/O2”
书本上的示例代码如下(略有修改):
#include
class A{
public:
virtual void f(){
printf("A_F\n");
}
};
class B{
public:
virtual void f(){
printf("B_F\n");
}
virtual void g(){
printf("B_G\n");
}
};
class C:public A,public B{
public:
void f(){
printf("C_F\n");
}
};
int main(){
A *a=new A;
B *b=new B;
C *c=new C;
a->f();
b->f();
b->g();
c->f();
c->g();
return 0;
}
利用IDA5.2反汇编,自行分析一下后如下:
.text:00401050 sub_401050 proc near ; CODE XREF: start-5Cp
.text:00401050 push ebx
.text:00401051 push esi
.text:00401052 push edi
.text:00401053 push 4
.text:00401055 call new ; 为实例分配内存
.text:0040105A add esp, 4
.text:0040105D test eax, eax ; 测试内存分配是否成功
.text:0040105F jz short loc_40106B
.text:00401061 mov dword ptr [eax], offset A_VTBL ;
.text:00401061 ; 分配成功后则直接赋一个值到EAX,
.text:00401061 ; 这个就是指指向VTBL的指针
.text:00401067 mov ebx, eax
.text:00401069 jmp short loc_40106D
.text:0040106B ; ---------------------------------------------------------------------------
.text:0040106B
.text:0040106B loc_40106B: ; CODE XREF: sub_401050+Fj
.text:0040106B xor ebx, ebx
.text:0040106D
.text:0040106D loc_40106D: ; CODE XREF: sub_401050+19j
.text:0040106D push 4
.text:0040106F call new
.text:00401074 add esp, 4
.text:00401077 test eax, eax
.text:00401079 jz short loc_401085
.text:0040107B mov dword ptr [eax], offset B_VTBL ; 同上
.text:00401081 mov esi, eax
.text:00401083 jmp short loc_401087
.text:00401085 ; ---------------------------------------------------------------------------
.text:00401085
.text:00401085 loc_401085: ; CODE XREF: sub_401050+29j
.text:00401085 xor esi, esi
.text:00401087
.text:00401087 loc_401087: ; CODE XREF: sub_401050+33j
.text:00401087 push 8
.text:00401089 call new
.text:0040108E add esp, 4
.text:00401091 test eax, eax
.text:00401093 jz short loc_4010AD
.text:00401095 mov dword ptr [eax+4], offset B_VTBL ; B::f()
.text:0040109C mov dword ptr [eax], offset ??_7C@@6BC@@@ ; C::f()
.text:004010A2 mov dword ptr [eax+4], offset C_VTBL ; 转向调用了401030处:
.text:004010A2 ; sub ecx, 4
.text:004010A2 ; jmp sub_401030
.text:004010A9 mov edi, eax
.text:004010AB jmp short loc_4010AF
.text:004010AD ; ---------------------------------------------------------------------------
.text:004010AD
.text:004010AD loc_4010AD: ; CODE XREF: sub_401050+43j
.text:004010AD xor edi, edi
.text:004010AF
.text:004010AF loc_4010AF: ; CODE XREF: sub_401050+5Bj
.text:004010AF mov eax, [ebx]
.text:004010B1 mov edx, [eax]
.text:004010B3 mov ecx, ebx
.text:004010B5 call edx
.text:004010B7 mov eax, [esi]
.text:004010B9 mov edx, [eax]
.text:004010BB mov ecx, esi
.text:004010BD call edx
.text:004010BF mov eax, [esi]
.text:004010C1 mov edx, [eax+4]
.text:004010C4 mov ecx, esi
.text:004010C6 call edx
.text:004010C8 mov eax, [edi]
.text:004010CA mov edx, [eax]
.text:004010CC mov ecx, edi
.text:004010CE call edx
.text:004010D0 mov eax, [edi+4]
.text:004010D3 mov edx, [eax+4]
.text:004010D6 lea ecx, [edi+4]
.text:004010D9 call edx
.text:004010DB pop edi
.text:004010DC pop esi
.text:004010DD xor eax, eax
.text:004010DF pop ebx
.text:004010E0 retn
.text:004010E0 sub_401050 endp
上面的分析略过了调用,下面是函数表:
.rdata:0040A150 ; class A; [SI] O: 0, A: 0 (Class Informer)
.rdata:0040A150 dd offset ??_R4A@@6B@ ; const A::`RTTI Complete Object Locator'
.rdata:0040A154 A_VTBL dd offset sub_401000 ; DATA XREF: sub_401050+11o
.rdata:0040A154 ; A::f()
.rdata:0040A158 aA_f db 'A_F',0Ah,0 ; DATA XREF: sub_401000o
.rdata:0040A15D align 10h
.rdata:0040A160 ;
.rdata:0040A160 ; class B; [SI] O: 0, A: 0 (Class Informer)
.rdata:0040A160 dd offset ??_R4B@@6B@ ; const B::`RTTI Complete Object Locator'
.rdata:0040A164 B_VTBL dd offset sub_401010 ; DATA XREF: sub_401050+2Bo
.rdata:0040A164 ; sub_401050+45o
.rdata:0040A164 ; B::f()
.rdata:0040A168 dd offset sub_401020 ; B::g()
.rdata:0040A16C aB_f db 'B_F',0Ah,0 ; DATA XREF: sub_401010o
.rdata:0040A171 align 4
.rdata:0040A174 aB_g db 'B_G',0Ah,0 ; DATA XREF: sub_401020o
.rdata:0040A179 align 4
.rdata:0040A17C ;
.rdata:0040A17C ; class C; [MI] O: 4, A: 1 (Class Informer)
.rdata:0040A17C dd offset ??_R4C@@6BB@@@ ; const C::`RTTI Complete Object Locator'{for `B'}
.rdata:0040A180 C_VTBL dd offset sub_401040 ; DATA XREF: sub_401050+52o
.rdata:0040A180 ; 转向调用了401030处:
.rdata:0040A180 ; sub ecx, 4
.rdata:0040A180 ; jmp sub_401030
.rdata:0040A184 dd offset sub_401020 ; B::g()
.rdata:0040A188 ;
.rdata:0040A188 ; class C: A, B; [MI] O: 0, A: 1 (Class Informer)
.rdata:0040A188 dd offset ??_R4C@@6BC@@@ ; const C::`RTTI Complete Object Locator'{for `C'}
.rdata:0040A18C ; const C::`vftable'{for `C'}
.rdata:0040A18C ??_7C@@6BC@@@ dd offset sub_401030 ; DATA XREF: sub_401050+4Co
.rdata:0040A18C ; C::f()
.rdata:0040A190 aC_f db 'C_F',0Ah,0 ; DATA XREF: sub_401030o
.rdata:0040A195 align 4
.rdata:0040A198 aBadAllocation db 'bad allocation',0 ; DATA XREF: .data:0040D030o
.rdata:0040A198 ; .data:off_40D04Co ...
.rdata:0040A1A7 align 4
这里同样验证了书上所说:
类C的虚函数表包含3个元素,从类B继承的虚函数f()的引用,但该元素又立即被编译器用C类的函数C::f()的形实转换程序给替换了。
.rdata:0040A180 C_VTBL dd offset sub_401040 ; DATA XREF: sub_401050+52o
.rdata:0040A180 ; 转向调用了401030处:
.rdata:0040A180 ; sub ecx, 4
.rdata:0040A180 ; jmp sub_401030