【反汇编分析】C++成员函数和虚函数
来源:互联网 发布:变声软件怎么实现 编辑:程序博客网 时间:2024/06/04 22:59
本节通过反汇编研究C++非static成员函数和虚函数的执行流程;
代码片段如下
class Animal{public: virtual void print() { cout << "Animal::print "<< endl; } void print2() { cout << "Animal::print2 "<< endl; }};class Dog : public Animal{public: Dog():_age(1) { } virtual void print() { cout << "Dog::print " << _age << endl; } void print2() { cout << "Dog::print2 "<< endl; }private: int _age;};int main(void){ Dog d; Animal* p1 = &d; p1->print(); p1->print2(); cout << "_age address: "<< ((int*)&d+1) << " value: " << *((int*)&d+1)<< endl; cout << "virtual fun address: "<< (int*)&d << endl; cout << "virtual fun[1] address: "<< (int*)*(int*)&d << endl; cout << "virtual fun[2] address: "<< ((int*)*(int*)&d+1) << endl; }输出如下:
sykpour@sykpour:~/Desktop$ ./testDog::print 1Animal::print2 _age address: 0xbfc5c7f8 value: 1virtual fun address: 0xbfc5c7f4virtual fun[1] address: 0x8048c30virtual fun[2] address: 0x8048c34说明几点:
(1)本节只会研究p1->print()和p1->print2()的反汇编代码,对于mian函数的下面cout输出部分,只是为了验证;
(2)我们发现在x86-32下,参数传递主要还是存放在栈中来传递,而在x86-64中由于通用寄存器由8个增加到16个,更多的参数是通过寄存器来传递的;
Dog类的虚函数表示意图如下:
print和print2调用的函数栈帧如下:
main函数的反汇编
print和print2相关部分反汇编如下
0804882c <main>: 804882c:8d 4c 24 04 lea 0x4(%esp),%ecx 8048830:83 e4 f0 and $0xfffffff0,%esp 8048833:ff 71 fc pushl -0x4(%ecx) 8048836:55 push %ebp #放入ebp 8048837:89 e5 mov %esp,%ebp 8048839:53 push %ebx 804883a:51 push %ecx 804883b:83 ec 10 sub $0x10,%esp 804883e:83 ec 0c sub $0xc,%esp 8048841:8d 45 ec lea -0x14(%ebp),%eax 8048844:50 push %eax 8048845:e8 0a 02 00 00 call 8048a54 <_ZN3DogC1Ev> #调用dog的构造函数 804884a:83 c4 10 add $0x10,%esp 804884d:8d 45 ec lea -0x14(%ebp),%eax 8048850:89 45 f4 mov %eax,-0xc(%ebp) #将Dog d的地址赋值给p1 8048853:8b 45 f4 mov -0xc(%ebp),%eax #取p1 8048856:8b 00 mov (%eax),%eax #取virtual虚函数表的地址 8048858:8b 00 mov (%eax),%eax #取第一个虚函数的函数地址 804885a:83 ec 0c sub $0xc,%esp 804885d:ff 75 f4 pushl -0xc(%ebp) #将p1压入作为参数 #取0x8048c3处的内容执行,即执行第一个虚函数,其实是函数地址08048a7e,即_ZN3Dog5printEv的函数地址,调用Dog::print 8048860:ff d0 call *%eax 8048862:83 c4 10 add $0x10,%esp 8048865:83 ec 0c sub $0xc,%esp 8048868:ff 75 f4 pushl -0xc(%ebp) 804886b:e8 a8 01 00 00 call 8048a18 <_ZN6Animal6print2Ev> #调用Animal::print2 8048870:83 c4 10 add $0x10,%esp 8048873:8d 45 ec lea -0x14(%ebp),%eax
Dog的构造函数反汇编如下
08048a54 <_ZN3DogC1Ev>: 8048a54:55 push %ebp 8048a55:89 e5 mov %esp,%ebp 8048a57:83 ec 08 sub $0x8,%esp 8048a5a:8b 45 08 mov 0x8(%ebp),%eax #取Dog d的地址 8048a5d:83 ec 0c sub $0xc,%esp 8048a60:50 push %eax 8048a61:e8 e0 ff ff ff call 8048a46 <_ZN6AnimalC1Ev> #调用Animal的构造函数 8048a66:83 c4 10 add $0x10,%esp 8048a69:8b 45 08 mov 0x8(%ebp),%eax 8048a6c:c7 00 30 8c 04 08 movl $0x8048c30,(%eax) #Dog虚函数表地址 8048a72:8b 45 08 mov 0x8(%ebp),%eax 8048a75:c7 40 04 01 00 00 00 movl $0x1,0x4(%eax) #_age为1 8048a7c:c9 leave 8048a7d:c3 ret
Animal的构造函数反汇编如下
08048a46 <_ZN6AnimalC1Ev>: 8048a46:55 push %ebp 8048a47:89 e5 mov %esp,%ebp 8048a49:8b 45 08 mov 0x8(%ebp),%eax 8048a4c:c7 00 40 8c 04 08 movl $0x8048c40,(%eax) #Animal虚函数表地址 8048a52:5d pop %ebp 8048a53:c3 ret
Dog虚函数表反汇编如下
08048c28 <_ZTV3Dog>: 8048c28:00 00 add %al,(%eax) 8048c2a:00 00 add %al,(%eax) 8048c2c:4c dec %esp 8048c2d:8c 04 08 mov %es,(%eax,%ecx,1) 8048c30:7e 8a jle 8048bbc <_IO_stdin_used+0x28> 8048c32:04 08 add $0x8,%al 8048c34:00 00 add %al,(%eax)说明几点:
(1)gcc采用小端存放,因此0x8048c30处的内容为0x08048a7e;具体如下
8048c30:7e 8a jle 8048bbc <_IO_stdin_used+0x28> 8048c32:04 08 add $0x8,%al 小 大
(2)我们知道虚函数表的地址为0x8048c30,而print为Dog虚函数表的第一个函数,因此call *%eax实际上执行的是0x8048c30地址存放的函数地址,即为08048a7e地址,正好对应Dog::print的函数地址;
Dog::print函数反汇编如下
08048a7e <_ZN3Dog5printEv>: 8048a7e:55 push %ebp 8048a7f:89 e5 mov %esp,%ebp 8048a81:53 push %ebx 8048a82:83 ec 04 sub $0x4,%esp 8048a85:8b 45 08 mov 0x8(%ebp),%eax #获得Dog d的地址 8048a88:8b 58 04 mov 0x4(%eax),%ebx #此时_age的值在ebx中 8048a8b:83 ec 08 sub $0x8,%esp 8048a8e:68 b7 8b 04 08 push $0x8048bb7 8048a93:68 80 91 04 08 push $0x8049180 #调用cout的地址 8048a98:e8 1f fc ff ff call 80486bc <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 8048a9d:83 c4 10 add $0x10,%esp 8048aa0:83 ec 08 sub $0x8,%esp 8048aa3:53 push %ebx 8048aa4:50 push %eax 8048aa5:e8 b2 fb ff ff call 804865c <_ZNSolsEi@plt> 8048aaa:83 c4 10 add $0x10,%esp 8048aad:83 ec 08 sub $0x8,%esp 8048ab0:68 ec 86 04 08 push $0x80486ec 8048ab5:50 push %eax 8048ab6:e8 21 fc ff ff call 80486dc <_ZNSolsEPFRSoS_E@plt> 8048abb:83 c4 10 add $0x10,%esp 8048abe:8b 5d fc mov -0x4(%ebp),%ebx 8048ac1:c9 leave 8048ac2:c3 ret
Animal::print2函数反汇编如下
08048a18 <_ZN6Animal6print2Ev>: 8048a18:55 push %ebp 8048a19:89 e5 mov %esp,%ebp 8048a1b:83 ec 08 sub $0x8,%esp 8048a1e:83 ec 08 sub $0x8,%esp 8048a21:68 a7 8b 04 08 push $0x8048ba7 8048a26:68 80 91 04 08 push $0x8049180 8048a2b:e8 8c fc ff ff call 80486bc <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 8048a30:83 c4 10 add $0x10,%esp 8048a33:83 ec 08 sub $0x8,%esp 8048a36:68 ec 86 04 08 push $0x80486ec 8048a3b:50 push %eax 8048a3c:e8 9b fc ff ff call 80486dc <_ZNSolsEPFRSoS_E@plt> 8048a41:83 c4 10 add $0x10,%esp 8048a44:c9 leave 8048a45:c3 ret
0 0
- 【反汇编分析】C++成员函数和虚函数
- C调用简单函数的反汇编分析记录
- c&c++反汇编与逆向分析学习笔记(4)--启动函数和用户入口
- 反汇编分析之:虚函数,虚表,多态
- 反汇编深入分析函数调用
- 反汇编深入分析函数调用
- 反汇编深入分析函数调用
- 反汇编深入分析函数调用
- 反汇编代码分析--函数调用
- C++反汇编代码分析--函数调用
- C++反汇编代码分析--函数调用
- 反汇编深入分析函数调用
- C++反汇编代码分析--函数调用
- C++反汇编代码分析--函数调用
- 反汇编代码分析--函数调用
- 反汇编及函数调用堆栈分析
- cpp反汇编分析之构造函数
- 【反汇编分析】函数栈帧
- 优秀产品经理的18种能力
- android后台发送邮件
- 【JAVA学习】4.Java对象构建理解
- java 泛型详解
- 经典面试题(四)附答案 算法+数据结构+代码 微软Microsoft、谷歌Google、百度、腾讯
- 【反汇编分析】C++成员函数和虚函数
- 替换空格
- RxJava 操作符 interval
- 2015互联网大会全球八站启动 ,首站4月28日北京亮相~
- Remove Duplicates from Sorted Array
- join方法
- mysql查询表中的列名
- 网络互联参考模型(详解)
- 线段树解析