今有c++程序:
class CA
{
public:
CA(int n1=0):_n1(n1){}
virtual void vf1(){}
private:
int _n1;
};
class CAsub:CA
{
public:
CAsub(int n1=0,int n2=1):CA(n1),_n2(n2){}
virtual void vf1(){}
virtual void vf2(){}
private:
int _n2;
};
int main(int argc, char* argv[])
{
CAsub casub;//此处设断点跟入, goto 分析1:
casub->vf1();//此处设断点跟入, goto 分析2:
CAsub *pcasub=new CAsub;
pcasub->vf1();//此处设断点跟入, goto 分析3
pcasub->vf2();//此处设断点跟入, goto 分析3
return 0;
}
vc6.0反汇编如下(各种编译器细节可能不一样,但方法应该都是类似的):
==================================================分析1:(构造函数分析)
...//main函数
00401068 push 1 ;从右到左压入参数,传说中的__stdcall方式?
0040106A push 0
0040106C lea ecx,[ebp-0Ch] ;传说中的this指针
0040106F call @ILT+10(CAsub::CAsub) (0040100f) ;调用构造函数
...
...//CAsub的构造函数
004010A0 push ebp
004010A1 mov ebp,esp
004010A3 sub esp,44h
004010A6 push ebx
004010A7 push esi
004010A8 push edi
004010A9 push ecx
004010AA lea edi,[ebp-44h]
004010AD mov ecx,11h
004010B2 mov eax,0CCCCCCCCh
004010B7 rep stos dword ptr [edi]
//以上部分的分析见前一篇文章
004010B9 pop ecx
004010BA mov dword ptr [ebp-4],ecx ;this指针被存放到栈内临时区的第一个位置
004010BD mov eax,dword ptr [ebp+8] ;取参数n1,注意在ebp与n1间还有一个四字节返回地址
004010C0 push eax
004010C1 mov ecx,dword ptr [ebp-4]
004010C4 call @ILT+25(CA::CA) (0040101e) ;n1再次压入堆栈,调用基类CA的构造函数(this指针相同)
004010C9 mov ecx,dword ptr [ebp-4]
004010CC mov edx,dword ptr [ebp+0Ch] ;取参数n2
004010CF mov dword ptr [ecx+8],edx ;初始化casub的n2参数(因为虚函数表指针占用了开头的4字节,所以是this+8)
004010D2 mov eax,dword ptr [ebp-4]
004010D5 mov dword ptr [eax],offset CAsub::`vftable' (0041f01c) ;初始话虚函数表指针
//以下部分没有分析价值
004010DB mov eax,dword ptr [ebp-4]
004010DE pop edi
004010DF pop esi
004010E0 pop ebx
004010E1 add esp,44h
004010E4 cmp ebp,esp
004010E6 call __chkesp (004011f0)
004010EB mov esp,ebp
004010ED pop ebp
004010EE ret 8
...
==================================================分析2:(类对象如何调用虚拟函数)
; casub.vf1();
00401089 lea ecx,[ebp-18h]
0040108C call @ILT+15(CAsub::vf1) (00401014)
//由上面的汇编代码看出,并没有什么动态绑定,编译器直接调用了类CAsub的vf1函数地址
==================================================分析3:(类指针对象如何调用虚拟函数)
; pcasub->vf1();
004010D6 mov edx,dword ptr [ebp-1Ch] ;pacsub入edx
004010D9 mov eax,dword ptr [edx] ;pacsub的虚函数表指针入eaxp[也即虚拟表的入口地址]
004010DB mov esi,esp ;
004010DD mov ecx,dword ptr [ebp-1Ch] ;this指针(pacsub)
004010E0 call dword ptr [eax] ;此处解释为[eax+0],因为vf1在表中的第1项
...
; pcasub->vf2();
004010D6 mov edx,dword ptr [ebp-1Ch]
004010D9 mov eax,dword ptr [edx]
004010DB mov esi,esp
004010DD mov ecx,dword ptr [ebp-1Ch]
004010E0 call dword ptr [eax+4] ;vf2在表中的第2项