反汇编角度分析VC面向对象机制资料讲解_第1页
反汇编角度分析VC面向对象机制资料讲解_第2页
反汇编角度分析VC面向对象机制资料讲解_第3页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、/target:从内存角度熟悉VC+面向对象机制作为MFC编程的基础/author:by Reduta/descritption:IA32 + win sp3 + vc6.0 /OD 1.10一:构造函数的之争1.1:构造函数是有返回值的返回当前对象的this 指针基本打开每一本C+的教程,都会对构造函数有如此类似的描述“构造函数无返回值,可进行函数重载” ,但是事实上又如何呢?答案是构造函数具备返回值,返回值为当前对象的 this 指针。编写如下代码 :/示例#include <iostream>using namespace std;class testint a;public

2、:test();void show();test:test()a=1;void test:show()cout<<a<<endl;int main()test hacker;/调用构造函数eax为构造函数返回值_asm/借助返回值修改对象数据成员mov dword ptr ss:eax,2 /查看修改是否成功 hacker.show();return 0;执行上面的程序,对象hacker的数据成员a的值将变为2,将程序载后0D,看关键代码:1刎二戸,'XII 科 1TTTffrpesSinsFr rndumib 目盹115牺raBDEH! CCCGCCCI;FC

3、i flFFGFFFPIT1QIJ fdM:aGl;GGCI;C> stos dword ptr #s: leiptr伽口叩d詡4m羽Fcbp-*EHKii aoifiFFieEDK 6B380E58 IEBN 7FFD3!10Wmou dMJord ptr <s:|lsp aiiSFFStanhb FETjc.dtau尸d ntr 玉玉hiT1执行到401590时,注意观察eax与ecx的值,如图1所示,004015888D4D FC0040158BE8 AFFCFFFF0040159036:C700 02000000lea ecx,dword ptr ss:ebp-4 call

4、 api.0040123F mov dword ptr ss:eax,200401597 8D4D FClea ecx,dword ptr ss:ebp-40040159A E8 0FFCFFFFcall api.004011AE;hacker 的 this 指针;构造函数;修改对象的数据成员;hacker 的 this 指针;hacker 的 show 函数eax和ecx的值是一样的,说明构造函数的确是有返回值,且返回值为当前对象的this指针,重新载入OD,在构造函数处,F7跟进,如图2 所示puh ahpOOUR1SD18BECmou ebp T&sp1010150383EC U

5、ksub esp,OO4015D459push ebx00401507弭push esi0Q40150857puh edi000150951push ecx9O401SDA8D7D SClea edl,dword ptr ss:ebp-UU0dUUl5DDB9 11800000iwu ecic ,11flfl015E2B8 CCCCCCCCnou eax fCCCCCCCC0OU015E7F3 : ABrep stos dword ptr ps:ediOOU01SE959popoonoiEA盼D FCiwu duiord ptr ss: &bp-U ,ecx0Q4fl1ED8BHr F

6、CInon eaxtduord ptr ss:et>p-40OU015Fn0700 01oooaoanou duord ptr ds:&axr1(JBhb佔F6&R屿 FCmoo eax,dord ptr ss:&bp-U1 AlitH *iF0匚匚卩叩川O04O15FA5Epop esiafldSFBSBpop ebxB04ei5FC8BE5mov esp9ebpO04O15FE5Dpop ebp004015FFG3retn,用红框标识的为关键代码,即构造函数,最后都会执行一句指令,将当前对象的this指针保存到eax中,而eax是函数的返回值。不管是debug

7、版的程序还是release版程序都会执行 类似的指令,即无论何时,构造函数将返回当前对象的this指针。1.2:构造函数并不一定是函数一一inline内联的影响看到构造函数并不一定是函数,你可能觉得可笑,这主要受inline内联的影响。如果将上面的构造函数定义在类作用域内即如下代码,此时测试release版本程序会发现,执行结果依然为1,如图3所示,Press Any ket; toc ontInueclas testint d i public:test(Ka=1;uqid);void test:show() <cout«-a«epdl;>lnt main(0

8、<test hacker;/调用_asn"借助返回值修即修改未遂,release版与debug版程序的最大区别在于代码优化,使用release版,基本所有的inline函数都能得到扩展,即此时构造函数并不是一个函数,而只是指令扩展。将release版程序载入OD,跟进主函数,发现如下代码:0040102055push ebp004010218BECmov ebp,esp0040102351push ecx00401024C745 FC 01000000mov dword ptr ss:ebp-4,1 ;构造函数代码处0040102B36:C700 02000000mov dwo

9、rd ptr ss:eax,2004010328D4D FClea ecx,dword ptr ss:ebp-400401035E8 C6FFFFFFcall api.004010000040103A33C0xor eax,eax0040103C8BE5mov esp,ebp0040103E5Dpop ebp0040103FC3retn并没有将test ()构造函数进仃显示调用,此时的test()构造函数只有一句代码即mov dwordptr ss:ebp-4,1。1.3: C+初始化的过程几乎每本C+的教程都会告诉我们对象是有构造函数初始化数据成员,但是并不能系统的告诉我们初始化的具体过程,

10、本部分着重分析 C+面向对象数据成员初始化的一般过程及各种初始化方式的特点。首先来讨论一下C+初始化的一般方法全局对象初始化、初始化列表、构造函数、static成员的初始化。static数据成员是在类内声明,类外定义的,在定义的时候并不遵守访问限 定符的限定,一旦定义,static数据成员即遵守访问限定的限制,这里不过分讨论static成员,我们来看一下剩余的几种初始化方式,它们实际上组成了如图4的初始化层次结构。础+討软扔弟化执轩為程全局对象的初贻化 科点有猱筠完咸)III初抬花列表(特点是无序诅行)IIII鞫造詁数(谀序静造画霖定叉的代哥用1II全局初始化过程class testint a

11、;public:test()a=1;cout<<"c on structor!"<<e ndl;;test cao;int mai n()cout<<"i n main !"<<e ndl;test dan;return 0;执行程序如图5所示,即在执行main函数之前,需要先调用构造函数初始化全局对象 动完成的,调用的函数为 _cinit(),这是在CRT中的一个函数, /初始化列表无序进行ca o,这个过程是有系统自mscvrt.dll 提供。class testint a;const int b;in

12、t c;char d;public:test():a(1),c(2),d(0x61),b(10);int main()test hacker; return 0;初始化列表是 C+ 提供的一种用于初始化 const 数据常量的,同时他也能初始化非 const 数据常量,上面的代码反汇编后,主要的代码如下 :0040108A894D FCmov dword ptr ss:ebp-4,eCx0040108D8B45 FCmov eax,dword ptr ss:ebp-400401090C700 01000000mov dword ptr ds:eax,1;a=1004010968B4D FCmo

13、v eCx,dword ptr ss:ebp-400401099C741 04 0A000000mov dword ptr ds:eCx+4,0A;b=10004010A08B55 FCmov edx,dword ptr ss:ebp-4004010A3C742 08 02000000mov dword ptr ds:edx+8,2;C=2004010AA8B45 FCmov eax,dword ptr ss:ebp-4004010ADC640 0C 61mov byte ptr ds:eax+C,61;d='a'004010B18B45 FCmov eax,dword ptr

14、 ss:ebp-4我们可以清楚的看到, 初始化列表的初始化并不是按照代码的书写顺序先初始化 a 再初始 化C,而是无序的初始化数据成员,这种顺序是无法通过 C+进行调整的。/构造函数初始化有序进行Class test int a;Const int b;int C; Char d;publiC:test():b(10)d=0x61;C=a=1;int main()test haCker;return 0;构造函数用来初始化, 一定会按照预先设定的代码执行, 因此构造函数的初始化顺序是有 序进行的。主要的反汇编代码如下:0040108A894D FC0040108D8B45 FC00401090

15、C740 04 0A000000004010978B4D FC0040109AC641 0C 610040109E8B55 FCmov dword ptr ss:ebp-4,eCxmov eax,dword ptr ss:ebp-4mov dword ptr ds:eax+4,0A;将 Const 变量初始化mov eCx,dword ptr ss:ebp-4mov byte ptr ds:eCx+C,61;d='a'mov edx,dword ptr ss:ebp-4004010A1C702 01000000 mov dword ptr ds:edx,1;a=1;004010

16、A7 8B45 FCmov eax,dword ptr ss:ebp-4004010AAC740 08 01000000 mov dword ptr ds:eax+8,1;c=1004010B1 8B45 FC mov eax,dword ptr ss:ebp-4对比不难发现, 先执行初始化列表, 然后执行构造函数, 因此初始化列表是在构造函数之 前执行的。二: this 指针到底在做什么this 指针在诸多的 C+ 书中都会有类似的描述“始终指向当前正在调用的对象”,那么它到底指向什么呢?答案是它有 ecx 保存,始终指向当前对象的第一个数据成员。2.1: C+中变量内存分布C+ 中的变量或

17、者称为对象在内存的分布是不一样的, 一方面作用域与存储类 ras(e register auto static extern)决定了变量分布于内存的堆还是栈,另一方面,先给谁分配内存的顺序也 是有区别的。大致的规则如下A:单一变量,先声明先的变量,先分配内存如下代码int main()int a;int b;会先分配a变量需要的内在,然后分配 b变量需要的内存,因此,a的地址是最大的,即 为ebp-4,而b的地是小的 ebp-8。B:多变量的集合,比如数组、struct结构体、自定义的对象,先声明的变量后分配内存。如下代码:int main()int a3;a0的地址最小的,最后分配内存,a3

18、的地址是最大的,先分配内存。理解了上面的顺序我们再来看 this 指针到底做了什么?2.2: this 指针到底在作什么this 指针始终指向当前对象的第一个数据成员,即告诉对象的行为(函数)要操作的地址 是什么,用ecx寄存器保存其值。/理解 this 指针class testpublic:int a;int b;int c;test()a=b=c;int mai n()un sig ned thisaddr;test hacker;_asm取this指针mov dword ptr ss:ebp-4,ecxcout< <&hacker.a<<"&qu

19、ot;<<&hacker.b<<""<<&hacker.c<<""<<hex<<thisaddr<<e ndl;return 0;执行结果如图6所示,刖H012FF78H01ZFF740012FF7812ff70Presskey to centa的地址与this指针的值是一样的,即this指针始终指向对象的第一个数据成员的内存地址。 当然这在继承中略有不同,这点不同在于vbtable的引入。三:虚基类继承时发生了什么继承是什么呢?继承解实际上抽象了具有相同

20、特性的类,这使得类之间有了一定的层次,最上层的是最核心的,即所有的类的交集, 比如C+的输入输出类即为此种层次结构。如图7描述了 C+的继承关系,如图所示使用虚基类的情况是一个平行四边形的继承关系时,暂时称为平行四边形法则。/虚基类示例 class base public:int a;class d1:virtual public base public: int b;class d2:virtual public base public:int c;class d3:public d1,public d2 public:int sum;int main()d3 ob3;ob3.a=10;ob

21、3.b=20;ob3.c=30;ob3.sum=ob3.a+ob3.b+ob3.c; cout<<ob3.sum<<endl;return 0;3.1:vbtable (虚基类表)是如何工作的将上面的程序载入 OD ,跟进主函数,关键代码如下 /反汇编代码00401598 6A 01 push 1制标志0040159A 8D4D E8 ss:ebp-18第一个元素 a 的内存地址;这个参数是一个控lea ecx,dword ptr ; this 指针指向 d1 的 vbtable 地址,即等同于0040159DE8FEFAFFFFcallapi.004010A0;构造函

22、数依次调用基类子类的构造函数004015A28B45E8moveax,dwordptrss:ebp-18;d1 vbtable004015A58B4804movecx,dwordptrds:eax+4;eax+4=offset004015A8C7440DE80A00000>movdwordptrss:ebp+ecx-18,0A; this+offset此时我们执行完构造函数,观察栈区如图8所示,地址数值注释p012FF6OCCI CClcccccc ccccecB012FF6S ob9Q012FF6C0O1ZFF7n Ob380012FF7U Ot)3*CIO00OH46E01C0000

23、1EOffset api d9:<vbtable -offset api.dS;'0O12FF79 Ob3+1B0O12FF7C oto3*1ii一00003C ooooon此时构造函数将虚基类的vbtable压入栈中,我们dd vbtable的内容,如图9所示。地址数值I注释OU46E030CB46EC3SO0U6E03C0tHl6Efl4U89680806nOBOOBOS00000008BB0Bfl09G综合分析,得出vbtable的结构如下:vbtable strucx1 dw ?offset dw ?x2 dw ?vbtable ends其中offset指明了当前数据成员

24、距离惟一副本的偏移,在此程序中即为d1中vbtable所在栈地址与其数据成员a的偏移地址,即this虚基类通过vbtable中偏移+4的偏移量来定位多副本程序,从而实现子类中只有一个 虚基类的副本。3.2:vbtable是如何引入的重新载入程序,在 40159d处跟进构造函数,会发现如下代码:;保存基址;开辟新栈帧sub为新栈帧分配相应的内存00401620 >55 push ebp00401621 8BE mov ebp,esp0040162383EC 44esp,440040162653push ebx0040162756push esi0040162857push edi00401

25、62951pushecx;保存ecx,因为ecx要作为初始化的计数器0040162A8D7D BClea edi,dword ptr ss:ebp-440040162DB9 11000000mov ecx,1100401632B8CCCCCCCCmov eax,CCCCCCCC00401637F3:ABrep stos dword ptr es:edi0040163959popecx;this指针出栈0040163A894DFCmov dwordptrss:ebp-4,ecx;保存ecx0040163D837D08 00cmp dwordptrss:ebp+8,0;参数1与0比较以判断是否使用

26、虚基类0040164174 13je short api.00401656004016438B45 FCmov eax,dword ptr ss:ebp-400401646C70028E04600movdwordptr ds:eax,offsetapi.d3:'vbtable'd2 的 vbtable0040164C8B4D FCmov ecx,dword ptr ss:ebp-40040164FC741081CE04600mov dwordptr ds:ecx+8,offsetapi.d3:'vbtable'd1 的 vbtable分析40163d处的代码,为

27、 cmp dword ptr ss:ebp+8,0,对于任何一个函数来说,它的栈区 结构应该如图10所示,C+函数橫区分布简略圉即站e寄存證血内容匝童列裘回览址 MrHB开始为画議琴数而此时的ebp+8即为参数1,因此在使用虚基类时,总会先push 1用于控制判断是否有虚基类,将将虚基类的vbtable保存到其多副本数据成员区,在本程序中即为d1和d2数据成员的a位置处。综上所述,当使用虚基类时,构造函数会通过一个标志控制参数,将vbtable的地址保存到多副本数据成员的内存位置,对应于本程序的变量a,当访问具有多副本变量时,通过vbtable+4处的偏移值+this指针即可,这保证了继承时子

28、类中只有基类的一个数据成员副本。四:多态到底为何物实际上C+面向对象程序设计的重要的问题是解决类与类之间的关系, 可以简单的理解为完全不同的类、大部分不同的类、大部分相同的类、 可以简称为类的四象,如图11所示,类与类之间的关系, 完全相同的类。我们C+面自对象的类E9象描迷结合太极四象更容易理解类与类的关系。多态即为只有一小部分相似的类的关系,反过来说即为大部分不同的类关系,这种关系通过运行时的vftable来完成。示例class testpublic:virtual void vfun c()cout<<"base's vfun c"<<

29、e ndl;;class d1:public testpublic:void vfun c()cout<<"d1's vfun c!"<<e ndl;class d2:public testpublic:void vfun c()cout<<"d2's vfun c!"<<e ndl;int mai n() test a,*p; d1 b; d2 c; p=&a; p->vfunc(); p=&b; p->vfunc(); p=&c; p->vfun

30、c(); return 0; 将上面的函数反汇编一下。/反汇编上面的程序分析虚函数机制004012C88D4DFCleaecx,dwordptrss:ebp-4; this 指针 a004012CBE8 6CFDFFFFcall api.0040103C004012D08D4DF4leaecx,dwordptrss:ebp-C; this 指针 b004012D3E8 69FDFFFFcall api.00401041004012D88D4DF0leaecx,dwordptrss:ebp-10; this 指针 c004012DBE8 48FDFFFFcall api.00401028004012E08D45FCleaeax,dwordptrss:ebp-4; 对

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论