初探vftable
vftable是用来存放虚函数地址的,下面简单探索一下vftable中存储的内容
#include <iostream>
#include <cstdio>
class Base
{
public:
Base(){}
virtual void BaseFunc1()
{
}
};
class Chil : public Base
{
public:
Chil(){}
virtual void ChilFun1()
{
}
};
int main()
{
Chil *c = new Chil();
printf("Base:BaseFunc1=%p\n", &Base::BaseFunc1);
printf("Chil:ChilFun1=%p\n", &Chil::ChilFun1);
return 0;
}
输出:
Base:BaseFunc1=001012D0
Chil:ChilFun1=00101064
这一串代码中有两个虚函数,所以vftable中应该是占用8个字节,每4个字节为一个函数地址,下面通过vptr找到vftable的内容
0x00109C40 cc 11 10 00 c6 12 10 00
这里非常奇怪,就是vftable中的内容跟输出的地址是不一样的,这就很疑惑了,两个都是同一个函数的地址,为什么会存在地址不一样的情况
汇编
为了解惑,就只有看汇编代码,以其中一个函数为例子
printf("Base:BaseFunc1=%p\n", &Base::BaseFunc1);
00102F50 push offset Base::`vcall'{0}' (01012D0h)
00102F55 push offset string "Base:BaseFunc1=%p\n" (0109C4Ch)
00102F5A call _printf (0101055h)
00102F5F add esp,8
------------------------------------------------------------------------
Base::`vcall'{0}':
001012D0 jmp Base::`vcall'{0}' (0102CEAh)
------------------------------------------------------------------------
Base::`vcall'{0}':
00102CEA mov eax,dword ptr [ecx]
00102CEC jmp dword ptr [eax]
可以清楚的看到,它经过了一系列的vcall调用后,最终还是调用的vftable中的地址,在最后两行可以清晰的看到
vftable内存的内容
偶偶与我的程序程序编译了,所以vftable的内容改变了,下面是新的
0x000D9C40 cc 11 0d 00 c6 12 0d 00
//在汇编中找到代码
Base::BaseFunc1:
000D11CC jmp Base::BaseFunc1 (0D2D00h)
//在继续往下找
000D2D00 push ebp
virtual void BaseFunc1()
{
000D2D01 mov ebp,esp
000D2D03 sub esp,0CCh
000D2D09 push ebx
000D2D0A push esi
000D2D0B push edi
000D2D0C push ecx
000D2D0D lea edi,[ebp-0CCh]
000D2D13 mov ecx,33h
000D2D18 mov eax,0CCCCCCCCh
000D2D1D rep stos dword ptr es:[edi]
000D2D1F pop ecx
000D2D20 mov dword ptr [this],ecx
000D2D23 mov ecx,offset _14314AF1_vftable地址@cpp (0DE05Eh)
000D2D28 call @__CheckForDebuggerJustMyCode@4 (0D128Ah)
}
总结
函数的地址跟vftable中的地址是不一样的,函数地址是通过vcall去调用vftable的,vftable是直接调用的是