从逆向分析角度看C++虚函数
来源:互联网 发布:微谱数据库免费入口 编辑:程序博客网 时间:2024/05/22 00:39
谈到虚函数,我想很多朋友都应该知道虚函数表指针VPTR和虚函数表VTABLE,如果不清楚的朋友,建议先看看侯捷先生翻译的《深度探索C++对象模型》:)
刚开始的时候,我仅仅知道虚函数的多态机制是通过VPTR和VTABLE操控的,完全地相信书上所描述的,并没有亲自去证明过,或许是因为那时候我还没有接触到逆向分析吧:)
提几个问题:
1. VPTR的大小如何确定?
2. VPTR在类实例中的偏移值是多少?
3. VPTR是如何索引到需要调用的函数的?
4. VTABLE中函数的顺序是如何确定的?
了解上述问题的朋友,可以关闭这个窗口了:)
考虑如下代码:
#include <cstdio>class Base{public:int i;char c;double d;Base(){i = 4;c = 'A';d = 2.0;}virtual void Virtual_Func_A(){printf("Virtual_Func_A()\n");}virtual void Virtual_Func_B(){printf("Virtual_Func_B()\n");}};int main(void){Base* b = new Base;b->Virtual_Func_A();b->Virtual_Func_A();return 0;}
分析工具:VC 6.0
分析过程:
在解答这个问题前,读者有必要了解一下“数据对齐”的概念,如果不了解的读者,可以参考下:)
http://blog.csdn.net/yeweiouyang/article/details/8636458
注意,Inter cpu 采用的是小端法
VPTR对程序员来说是隐形的,在Win32平台下,不考虑数据对齐的情况,VPTR占用4个字节,用于保存VTABLE的地址
考虑如下程序:
#include <cstdio>class Base{public:virtual void Common_Func(){printf("Base::Common_Func()\n");}virtual void Base_Func(){printf("Base::Func()\n");}};class Derived : public Base{public:virtual void Common_Func(){printf("Derived::Common_Func()\n");}virtual void Derived_Func(){printf("Derived::Func()\n");}};int main(void){Base* b = new Base;b->Common_Func();b->Base_Func();Derived* d = new Derived;d->Common_Func();d->Base_Func();d->Derived_Func();return 0;}
分析工具:IDA Pro
静态反汇编:
.text:00401000 push esi.text:00401001 push 4 ; 为 Base::VPTR 申请 4 个字节的栈空间.text:00401003 call ??2@YAPAXI@Z ; operator new(uint).text:00401008 add esp, 4.text:0040100B test eax, eax.text:0040100D jz short loc_401019.text:0040100F mov dword ptr [eax], offset Base_VPTR ; *b 的堆空间放入 Base::VPTR.text:00401015 mov esi, eax.text:00401017 jmp short loc_40101B
对应的offset Base_VPTR:
.rdata:004060BC Base_VPTR dd offset Base_Common_Func ; DATA XREF: _main+Fo.rdata:004060C0 dd offset Base_Func.rdata:004060C4 align 8
不难发现Base_VPTR正是VTABLE的首地址,即Base::VPTR所指向之处
由这一句:
.text:0040100F mov dword ptr [eax], offset Base_VPTR ;
不难发现,VPTR存放的位置是Base类实例b的开始处,即偏移值为0
.text:00401019.text:00401019 loc_401019: ; CODE XREF: _main+Dj.text:00401019 xor esi, esi.text:0040101B.text:0040101B loc_40101B: ; CODE XREF: _main+17j.text:0040101B mov eax, [esi] ; eax = Base::VTABLE.text:0040101D mov ecx, esi.text:0040101F call dword ptr [eax] ; call Base::Common_Func().text:00401021 mov edx, [esi] ; edx = Base::VTABLE.text:00401023 mov ecx, esi.text:00401025 call dword ptr [edx+4] ; call Base::Base_Func().text:00401028 push 4 ; 为 Derived::VPTR 申请 4 个字节的栈空间.text:0040102A call ??2@YAPAXI@Z ; operator new(uint).text:0040102F add esp, 4.text:00401032 test eax, eax.text:00401034 jz short loc_401040.text:00401036 mov dword ptr [eax], offset Derived_VPTR ; *d 的堆空间放入 Derived_VPTR.text:0040103C mov esi, eax.text:0040103E jmp short loc_401042.text:00401040 ; ---------------------------------------------------------------------------.text:00401040.text:00401040 loc_401040: ; CODE XREF: _main+34j.text:00401040 xor esi, esi.text:00401042.text:00401042 loc_401042: ; CODE XREF: _main+3Ej.text:00401042 mov eax, [esi] ; eax = Derived::VTABLE.text:00401044 mov ecx, esi.text:00401046 call dword ptr [eax] ; call Derived::Common_Func().text:00401048 mov edx, [esi] ; edx = Derived::VTABLE.text:0040104A mov ecx, esi.text:0040104C call dword ptr [edx+4] ; call Base_Func(),Base_Func()是从Base类中继承而来的.text:0040104F mov eax, [esi] ; eax = Derived::VTABLE.text:00401051 mov ecx, esi.text:00401053 call dword ptr [eax+8] ; call Derived::Derived_Func().text:00401056 xor eax, eax.text:00401058 pop esi.text:00401059 retn.text:00401059 _main endp
对应的Derived_VPTR:
.rdata:004060B0 Derived_VPTR dd offset Derived_Common_Func ; DATA XREF: _main+36o……(IDA 并没有将所有函数指针显示出来)
经过分析,Base和Derived的VTABLE结构大致如下:
在VTABLE中保存有函数的地址(即函数指针),而非函数本身或函数名,在Win32平台下,函数指针占用4个字节,当通过类实例调用虚函数时,VPTR就会在VTABLE中进行索引,若找到对应的虚函数,就会取出函数指针并进行调用
- 从逆向分析角度看C++虚函数
- 从逆向分析角度看C++的析构函数
- 从逆向分析角度看C++拷贝构造函数
- 从逆向分析角度看C++ 中的引用
- 从内存角度看C函数的调用过程
- 从编译器的角度看C代码
- 从汇编角度看Linux C函数的调用约定和参数传递的细节
- 从编译器的角度分析继承中的虚函数
- 从地址角度看
- 从汇编角度看英特尔x86函数调用规范
- C++的virtual函数(从编译器的角度看)
- 从编译器的角度看C++的virtual函数
- 从反汇编的角度看C++语法(构造函数)
- 从汇编角度看C语言类型转换
- Swift要点:从Objective-C开发者的角度看Swift
- c++虚函数逆向分析
- 从程序员角度看ELF
- 从C++角度看Java
- SEO者固步自封的势态越发严重是谁的错
- 人生若只如初见
- 无向图 双连通分量的模板
- 锁定表操作
- 企业类网站如何在百度首页生存?
- 从逆向分析角度看C++虚函数
- 非深入探寻Java反射机制 (Constructor)
- hdu-1004-Let the Balloon Rise
- SQL 查找重复记录
- 正多边形
- 如何分析挖掘竞争对手的方针方法与策略
- HDU-2600
- 有向图 强连通分量
- 二叉树的创建与各种遍历详解