《C++反汇编与逆向分析技术揭秘》之十——构造函数

时间:2022-09-04 09:11:01

对象生成时会自动调用构造函数。只要找到了定义对象的地方,就找到了构造函数调用的时机。不同作用域的对象的生命周期不同,如局部对象、全局对象、静态对象等的生命周期各不相同,只要知道了对象的生命周期,便可以推断出构造函数的调用时机

  • 局部对象

《C++反汇编与逆向分析技术揭秘》之十——构造函数

反汇编:

获取对象首地址并调用构造函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

对象的地址为:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

进入构造函数,先是push一堆寄存器:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

还原ecx寄存器,并初始化:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

构造函数属于成员函数,在调用时要用到this指针。

如何识别?1、构造函数时这个对象在作用域内调用的第一个成员函数,根据this指针可以区分每个对象;2、返回this指针是构造函数的特征之一(这是识别局部变量构造函数的必要条件)

  • 堆对象

堆对象的识别重点在于识别堆空间的申请与使用

举例:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

new以及new的大小:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

返回的地址:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

调用构造函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

先判断new是否从成功,如果失败就跳过构造函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

执行构造函数初始化对象:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

定位到对象的第一个成员变量,并赋值为0xb:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

new对象返回对象的首地址时,也检查了是否为NULL,进行了一次判断,如果失败就跳过构造函数,所以可以从这一点入手,找到堆对象的构造函数

考虑下我们这里有两个成员变量的情况:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

new的大小变了:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

给第二个成员变量赋值时的定位变了:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

  • 参数对象

当对象作为函数参数时,调用一个特殊的构造函数——拷贝构造函数。拷贝构造函数只有一个参数,即为对象的引用

例子如下:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

1、初始化一个MyString对象,先调用了一个autoclassinit函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

类对象的地址为:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

构造函数内部在初始化成员m_pString时调用了memset函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

这里的ecx就是类对象的首地址。

2、调用构造函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

ecx保存的是类对象,给成员赋值:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

3、调用成员函数SetString:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

里边会先获取字符串长度:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

然后再去拷贝:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

参数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

返回:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

4、Show(MyString)利用了拷贝构造函数(本小节重点)

先调用一个拷贝构造函数,拷贝MyString。即一个新的CMyString对象调用拷贝构造函数,这个拷贝构造函数的参数是前面的MyString对象。

拷贝构造函数的调用,且新的对象的地址就是esp的值

《C++反汇编与逆向分析技术揭秘》之十——构造函数

拷贝构造函数中,会用this作为返回值

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

其中ecx是新的CMyString对象的首地址:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

eax是前面定义的、这里被拷贝的MyString对象的地址:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

返回的是生成的新对象的地址,也是esp的值:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

调用完拷贝构造函数后紧接着调用Show函数,并没有push操作来压入参数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

其参数就是新返回的对象的成员(这里我们发现,刚刚返回的新的CMyString的地址就是esp的值):

《C++反汇编与逆向分析技术揭秘》之十——构造函数

验证:

如果CMyString中有两个成员变量,那么在给Show传参的时候,就压入了两个数据:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

传给Show的参数其实并不是通过push操作压入的参数,而是通过拷贝构造函数帮忙压入的。因为拷贝构造函数新构造的那个对象的地址就是esp所指向的位置,所以拷贝构造函数执行完成之后生成的数据就直接放在了栈顶位置

  • 返回对象

返回时需要对返回对象进行拷贝,因此同样会使用到拷贝构造函数

举例:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

除了那个autoclassInit函数之外,其实就调用了GetMyString这么一个函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

那么可以说明给MyString对象赋值的情况是在GetMyString中完成的。而我们可以知道,GetMyString中并没有刻意实现给MyString赋值的功能,那么究竟是如何做到的呢?

GetMyString()并没有参数,但是还是push了一个eax,这个eax是由autoclassInit函数返回的仅仅是一个地址,也就是MyString的地址(但是没有调用MyString的构造函数进行初始化):

《C++反汇编与逆向分析技术揭秘》之十——构造函数

随后以这个eax为参数调用了GetMyString,进入其中我们来到return MyString的部分,这里调用了一个拷贝构造函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

寄存器的值为:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

也就是说,是函数外面创建的那个对象,复制了函数内部的临时的对象。并返回12FF5C:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

这里我们可以看出,并不是GetMyString得到一个对象后,再去拷贝给某个定义好的等待接收的变量,而是在GetMyString过程中,就给main中的MyString变量构建好了(借助传递地址参数)

  • 全局对象与静态对象

我们必须清楚的知道全局对象与静态对象构造的时机。所有的全局对象都会在同一地点调用构造函数进行初始化,即_cinit函数。_cinit的_initterm函数中逐一初始化了全局对象。

定位方法一——直接定位法:mainCRTStartup->_cinit->_initterm->构造代理函数

定位方法二——利用栈回溯:全局对象的地址固定,可以先对这些数据下读写断点。

定位方法三——定位atexit:对atexit下断点。

进入:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

先找到:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

在下面这个循环中,会调用构造代理函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

进入这个call eax函数中,跟踪找到了一个构造函数,但并不一定是我们定义的那全局对象的构造函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

继续找,找到第一个全局对象,一般先是调用构造函数再有一个注册析构函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

构造函数内部:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

构建时先是初始化一块地址:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

返回值:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

调用了带一个参数的构造函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

其中赋值:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

继续找找到第二个全局对象:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

考虑,如果是一个全局变量数组,那么构造函数是如何被调用的呢?比如:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

那么不会是分别调用三次call eax函数,每次构造一个对象。而是调用一次call eax函数,里边调用一个构造代理函数来分别构建三个对象:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

进入构造代理函数内部,发现下面的循环逐一调用构造函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

其逻辑类似于书上P249所述。

  • 每个对象都有默认构造函数吗

举例:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

这里不会为CMyString提供默认的构造函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

这里采用的做法是给ecx寄存器传入一个地址,给SetInt传入一个参数,然后把这个参数的值写入对应的地址:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

但是如果你在类中定义了构造函数,哪怕是什么也没做,也会调用构造函数的,因为你一旦自己定义了一个构造函数,就不会被提供任何默认构造函数了

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

所以我们一直要讨论的,就是没有提供构造函数的情况下,编译器是否会给你提供默认构造函数。

以下几种情况会提供默认的构造函数:

1、本类中存在虚函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

默认的构造函数里进行了虚表的初始化操作:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

虚表中的内容如下:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

2、本类中定义的成员对象有虚函数

如果类的成员对象只是普通的成员函数,则该类没有构造函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

但是如果成员对象由虚函数:

《C++反汇编与逆向分析技术揭秘》之十——构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数

构造函数内部调用了成员对象的构造函数,其目的显然是为了初始化虚表

《C++反汇编与逆向分析技术揭秘》之十——构造函数

3、父类中存在虚函数(道理同上)

4、父类中定义的对象带有构造函数

5、本类中定义的对象带有构造函数

《C++反汇编与逆向分析技术揭秘》之十——构造函数的更多相关文章

  1. C++反汇编与逆向分析技术揭秘

    C++反汇编-继承和多重继承   学无止尽,积土成山,积水成渊-<C++反汇编与逆向分析技术揭秘> 读书笔记 一.单类继承 在父类中声明为私有的成员,子类对象无法直接访问,但是在子类对象的 ...

  2. 《C&plus;&plus;反汇编与逆向分析技术揭秘》--算术运算和赋值

    一.加法 1.Debug下: 14: int nVarOne0 = 1 + 5 - 3 * 6;//编译时计算得到结果 00C0550E C7 45 F8 F4 FF FF FF mov dword ...

  3. 《C&plus;&plus;反汇编与逆向分析技术揭秘》--认识启动函数,找到用户入口

    <C++反汇编与逆向分析>和<程序员的自我修养>都是以VC6的代码作为例子讲解的.这里是在vs2017下,CRT代码有些区别,但整体流程上都是初始化环境,设置参数,最后转到用户 ...

  4. 《C&plus;&plus;反汇编与逆向分析技术揭秘》——观察各种表达式的求值过程

    ---恢复内容开始--- 加法: 示例: 常量相加,则在编译期间就计算出两个常量相加后的结果,直接将这个结果参与运算,减少了运行期的计算.当有变量参与运算时,会先取出内存中的数据,放入通用寄存器中,再 ...

  5. 《C&plus;&plus;反汇编与逆向分析技术揭秘》——基本数据类型的表现形式

    ---恢复内容开始--- 基本的浮点数指令 示例代码: Visual Studio 2013的反汇编代码是: 对于movss,表示移动标量单精度浮点值 将标量单精度浮点值从源操作数(第二个操作数)移到 ...

  6. 《C&plus;&plus;反汇编与逆向分析技术揭秘》——函数的工作原理

    各种调用方式的考察 示例: cdecl方式是调用者清空堆栈: 如果执行的是fastcall: 借助两个寄存器传递参数: 参数1和2借助局部变量来存储: 返回值 如果返回值是结构体: 返回值存放在eax ...

  7. 《C&plus;&plus;反汇编与逆向分析技术揭秘》——流程控制语句的识别

    if...else...语句 示例: if构成多分支语句 switch 有序线性的switch: 3E82D8位置存放了一个表,标明了要跳转到的地址: 这里的每四字节都标明的是每个case块的首地址: ...

  8. 《C&plus;&plus;反汇编与逆向分析技术揭秘》之12——继承

    识别类和类之间的关系 在父类中声明为私有的成员,虽然子类对象无法直接访问,但是在子类对象的内存结构中,父类私有的成员数据依然存在. 在没有提供构造函数的时候,系统会尝试提供默认的构造函数: 当子类中没 ...

  9. 《C&plus;&plus;反汇编与逆向分析技术揭秘》之11——虚函数

    虚函数的机制 当类中定义有虚函数时,编译器会将该类中所有虚函数的首地址保存在一张地址表中,这张表被称为虚函数地址表.编译器还会在类中添加一个虚表指针. 举例: CVirtual类的构造函数中没有进行任 ...

随机推荐

  1. 「译」JUnit 5 系列:扩展模型(Extension Model)

    原文地址:http://blog.codefx.org/design/architecture/junit-5-extension-model/ 原文日期:11, Apr, 2016 译文首发:Lin ...

  2. MVC html&period;actionlink

    Html.ActionLink 在 LinkExtensions 类中,ActionLink方法参数说明: 简单来说捏 就是这样滴 参数                      类型         ...

  3. WebStorm在Mac上的快捷键(部分)

    整理一下在Mac上使用WS这款IDE的快捷键 shift + Enter 软回车 ,无论在前一行代码的什么位置,都能定位到下一行. command 显示/隐藏 左侧面板 command + b / 点 ...

  4. photoshop sdk

    http://blogs.adobe.com/photoshop/2013/09/introducing-adobe-generator-for-photoshop-cc.html

  5. 【转】asp&period;net获取当前页面的url地址

    设当前页完整地址是:http://www.jb51.net/aaa/bbb.aspx?id=5&name=kelli "http://"是协议名 "www.jb5 ...

  6. CH 5102Mobile Service题解

    题目: 用动态规划很容易将完成任务量作为dp的阶段,通过指派服务员,从当前i-1个任务转移到i个任务: 我们可以用一个四维数组f[i][x][y][z]来表示在完成当前任务i时,三个机器人分别在x,y ...

  7. 013 mysql中find&lowbar;in&lowbar;set&lpar;&rpar;函数的使用

    在工作中遇见过,对于新知识,在这里写一写文档. 1.作用 举个例子,也许不理解,在看完后面的SQL示例,再来看就明白了: 有个文章表里面有个type字段,它存储的是文章类型,有 1头条.2推荐.3热点 ...

  8. Oracle 小函数的使用

    1.Oracle 正则表达式 经常会有一种需求是查询某个字符在字符串中的数量,可以使用正则表达式regexp_count函数 比如 SELECT regexp_count('0,1,1',',') f ...

  9. 福大软工1816 &&num;183&semi;软工之404NoteFound团队选题报告

    目录 NABCD分析引用 N(Need,需求): A(Approach,做法): B(Benefit,好处): C(Competitors,竞争): D(Delivery,交付): 初期 中期 个人贡 ...

  10. &lbrack;转&rsqb;谈谈Linux下动态库查找路径的问题

    http://blog.chinaunix.net/uid-23069658-id-4028681.html 学习到了一个阶段之后,就需要不断的总结.沉淀.清零,然后才能继续“上路”.回想起自己当年刚 ...