程序本身用VC编译:
// virtualandoverloadding.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
class parent
{
public:
int int1()
{
return 1;
}
virtual int int1(int x)
{
return x;
}
int int1(char c)
{
return c;
}
};
class child:public parent
{
public:
int int1(int x)
{
return x*2;
}
};
int main(int argc, char* argv[])
{
parent *p=new child();
p->int1();
p->int1(2);
p->int1('c');
return 0;
}
分析:
进入MAIN后
下面是动态的堆栈跟踪分析
PUSH EBP
EBP
MOV EBP,ESP
EBP,ESP->EBP
PUSH 0FFH
ESP ->0FFH
0FFH
0FFH
0FFH
EBP ->EBP
PUSH OFFSET _ehandler$_main(004109bb);SEH的构造,从堆栈图中特别清楚的.
ESP ->_ehandler$_main
0FFH
0FFH
0FFH
0FFH
EBP ->EBP
mov eax,fs:[00000000]
push eax
ESP ->fs:[00000000]
_ehandler$_main
0FFH
0FFH
0FFH
0FFH
EBP ->EBP
mov dword ptr fs:[0],esp
sub esp,50h
esp ->...
...
...
...
50H bytes
...
fs:[00000000];这个结构已经呈现在我们面前
_ehandler$_main
0FFH
0FFH
0FFH
0FFH
EBP ->EBP
push ebx
push esi
push edi
esp ->edi
esi
ebx
...
...
...
...
50H bytes
...
fs:[00000000]
_ehandler$_main
0FFH
0FFH
0FFH
0FFH
EBP ->EBP
lea edi,[ebp-5Ch]
mov ecx,14h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi]
;注意这里的算法,开始分配的空间一共50H个字节共有80BYTE,所以这里用14H就是;20次循环
push 4
esp ->
04h
00h
00h
00h
edi
esi
ebx
...
...
...
...
50H bytes
...
fs:[00000000]
_ehandler$_main
0FFH
0FFH
0FFH
0FFH
EBP ->EBP
call operator new(00401360)
add esp 4;调用者恢复堆栈
进入operator new
operator new:
push ebp
mov ebp,esp
push 1
mov eax,dword ptr [cb]
push eax
call _nh_malloc (00401a70);malloc的参数是上面PUSH 4来的
add esp,8;临时空间的回收还是函数本身来负责的
pop ebp
ret
回到MAIN
mov dword ptr [ebp-18h],eax
mov dword ptr [ebp-4],0
cmp dword ptr [ebp-18h],0
je main+54h (004010a4);这里用到了一个刚才忽视的单元EBP-4不知道为啥了,兄弟们知道的上啊...
mov ecx,dword ptr [ebp-18h]
call @ILT+15(child::child) (00401014)
终于进入了构造函数
child::child:
push ebp
mov ebp,esp
sub esp,44h
push ebx
push esi
push edi
push ecx;其实这里使用ecx来传递参数有些fastcall的意思了
lea edi,[ebp-44h]
mov ecx,11h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi];故技重施?唉...
pop ecx
mov dword ptr [ebp-4],ecx
mov ecx,dword ptr [ebp-4];这里编译器犯傻了,呵呵,倒腾了一次ECX
call @ILT+5(parent::parent) (0040100a);调用了父类构造器
;不怕累的继续跟我来
parent::parent:
push ebp
mov ebp,esp
sub esp,44h
push ebx
push esi
push edi
push ecx
lea edi,[ebp-44h]
mov ecx,11h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi]
pop ecx
mov dword ptr [ebp-4],ecx
mov eax,dword ptr [ebp-4]
mov dword ptr [eax],offset parent::`vftable' (00422020);看,默认构造器最最核心的部分终于出现:初始化vtable
mov eax,dword ptr [ebp-4]
pop edi
pop esi
pop ebp
ret
回到CHILD构造器
mov eax,dword ptr [ebp-4]
mov dword ptr [eax],offset child::`vftable' (0042201c)
mov eax,dword ptr [ebp-4]
pop edi
pop esi
pop ebx
add esp,44h
cmp ebp,esp
call __chkesp (00401850)
mov esp,ebp
pop ebp
ret
一阵漫游之后,我们回到MAIN中,把变量送入到该送的地方去
0040109F 89 45 E4 mov dword ptr [ebp-1Ch],eax
004010A2 EB 07 jmp main+5Bh (004010ab)
004010A4 C7 45 E4 00 00 00 00 mov dword ptr [ebp-1Ch],0
004010AB 8B 45 E4 mov eax,dword ptr [ebp-1Ch]
004010AE 89 45 EC mov dword ptr [ebp-14h],eax
004010B1 C7 45 FC FF FF FF FF mov dword ptr [ebp-4],0FFFFFFFFh
004010B8 8B 4D EC mov ecx,dword ptr [ebp-14h]
004010BB 89 4D F0 mov dword ptr [ebp-10h],ecx;变量p栖身之所
004010BE 8B 4D F0 mov ecx,dword ptr [ebp-10h];最后居然有四处存放了NEW的返回指针,效率啊!我的天呢...
我们来看函数调用,注意虚函数的方式
004010C1 E8 58 FF FF FF call @ILT+25(parent::int1) (0040101e)
004010C6 8B F4 mov esi,esp
004010C8 6A 02 push 2
004010CA 8B 55 F0 mov edx,dword ptr [ebp-10h]
004010CD 8B 02 mov eax,dword ptr [edx];查表动作
004010CF 8B 4D F0 mov ecx,dword ptr [ebp-10h]
004010D2 FF 10 call dword ptr [eax];虚函数,有趣的是,如果你直接用CHILD指针进行调用,编译器优化为静态调用
004010D4 3B F4 cmp esi,esp
004010D6 E8 75 07 00 00 call __chkesp (00401850)
004010DB 6A 63 push 63h
004010DD 8B 4D F0 mov ecx,dword ptr [ebp-10h]
004010E0 E8 2A FF FF FF call @ILT+10(parent::int1) (0040100f)
准备退出了...
004010E5 33 C0 xor eax,eax
004010E7 8B 4D F4 mov ecx,dword ptr [ebp-0Ch]
004010EA 64 89 0D 00 00 00 00 mov dword ptr fs:[0],ecx;先恢复SEH
004010F1 5F pop edi
004010F2 5E pop esi
004010F3 5B pop ebx;寄存器退栈
004010F4 83 C4 5C add esp,5Ch
004010F7 3B EC cmp ebp,esp;释放临时空间
004010F9 E8 52 07 00 00 call __chkesp (00401850)
004010FE 8B E5 mov esp,ebp
00401100 5D pop ebp
00401101 C3 ret