C++反汇编第四讲,反汇编中识别继承关系,父类,子类,成员对象
讲解目录:
1.各类在内存中的表现形式 备注: 主要复习开发知识,和反汇编没有关系,但是是理解反汇编的前提.
2.子类继承父类
2.1 子类中有虚函数,父类中有虚函数 : 都有的情况下
2.2 子类中没有虚函数,父类中有虚函数 : 子类没有,父类有的情况 2.1 2.2的情况都是一样的.
2.3 子类中有虚函数,父类中没有虚函数 : 子有父没有的的情况下
2.4 子类父类都没有虚函数的情况下
第二专题大总结. 熟悉反汇编可以直接看这个总结,
3.结合第二专题的成员对象有无虚表行为
3.1成员对象有虚表的情况
3.2成员对象没有虚表的情况
第三专题大总结
4.重载运算符的识别
5.纯虚函数的反汇编
6.模版识别.
一丶各类在内存中的表现形式(复习开发知识)
讲解之前,我们首先要明白C/C++中的类的内存结构.继承之后的内存结构
普通类的内存结构:
高级代码:
class MyTest
{
public:
MyTest();
~MyTest();
public:
int m_int;
}; MyTest::MyTest(){} MyTest::~MyTest(){} int main(int argc, char* argv[])
{
MyTest test; //定义对象
return ;
}
对应内存结构图
这是普通的一个类的内存结构图,因为我们只有一个成员,大小是一个4字节的,所以初始化为CC
总结: 普通类根据成员进行申请内存.
带有虚关键字的类(可能有虚函数或者虚构造)
PS: 类声明同上,但是析构前边加上了virtual 关键字,变为了虚析构
内存结构图:
可以看出,申请了八个字节,启动前4个字节是虚表指针,指向了虚表
后四个字节才是真正的为成员申请的内存.
总结: 带有虚函数(虚关键字)的时候,内存中会把前4个字节当做虚表指针,并且在构造的时候初始化.
子类继承父类,(都有虚函数的情况下)重要:
高级代码:
class MyFather
{
public:
MyFather();
virtual ~MyFather();
public:
int m_int;
}; MyFather::MyFather(){} MyFather::~MyFather(){} class MyChild : public MyFather //继承
{
public:
MyChild();
virtual ~MyChild();
float m_flt;
}; MyChild::MyChild(){} MyChild::~MyChild(){} int main(int argc, char* argv[])
{
MyChild test; //定义对象
return ;
}
内存结构图
总共申请了12个字节,前4个字节是虚表指针,后4个字节是父类的m_int成员,在后面才是子类的真正的成员.
说到这里我们就要说下复写虚表指针的操作.
首先我们知道: 子类构造的时候,会先构造父类,也就是说,父类的内存会先申请,并且把虚表指针填写到前4个字节位置, 而构造完毕父类之后,构造自己的时候,这时候虚表指针又写入子类的虚表指针了.产生了覆盖了.
流程图:
看上面图可以知道,我们子类继承父类,并且填写了虚表指针为子类的,此时 则可以写成 父类指针指向子类 例如: Myfather *pFa = new MyChild; pfa指向的位置就是父类区域的起始位置,
而且不会超过父类区域,所以是安全的,此时因为构造完毕,虚表指针是子类的,所以调用虚函数的时候,则是调用子类的虚函数了.
而且也说明了 为什么子类指针不能指向父类.这样会产生越界问题.
总结:
子类继承父类时候,有虚函数的时候,会先把头4字节申请出来填写为虚表指针, 而且会产生复写(重复写入). 第一次, 构造父类,填写为父类指针,第二次 构造完父类则会填写为子类的虚表指针.
二丶子类继承父类反汇编中的结构
2.1 子类中有虚函数,父类中有虚函数 : 都有的情况下
高级代码:
class MyFather
{
public:
MyFather();
virtual ~MyFather();
public:
int m_int;
}; MyFather::MyFather(){} MyFather::~MyFather(){} class MyChild : public MyFather
{
public:
MyChild();
virtual ~MyChild();
float m_flt;
}; MyChild::MyChild(){} MyChild::~MyChild(){} int main(int argc, char* argv[])
{
MyChild test; //定义对象
return ;
}
Debug下的反汇编
PS: 代码太多,只说明这个反汇编在哪个函数中
1.main函数中找到构造
2.构造中生成的反汇编
可以看出,构造中又有一个Call,这个Call是构造父类的,构造完毕之后填写自己的虚表指针.
3.父类构造
父类构造填写虚表指针,也就是对象的前4个字节修改为父类的虚表指针.而后通过第二步,得出,当构造完父类之后,其前4个字节会被子类重新写入.也就产生了复写过程
总结:
1.子类构造的时候会先构造父类,父类构造中先填写虚表指针.
2.父类构造完成之后,子类会重新写入虚表指针.
3..子类继承父类,都有虚函数的情况下,会产生复写行为, 对象首地址4个字节处填写虚表.
2.2 子类中没有虚函数,父类中有虚函数 : 子类没有,父类有的情况
PS: 高级代码中,子类类声明去掉了虚函数
Debug下的反汇编代码:
1.main函数下构造的反汇编
2.构造内部反汇编
看到这一步我们明白了,首先构造父类,因为父类有虚函数,所以肯定会有虚表指针填写,而下方也填写了一次虚表指针.由此得出
父类有虚函数,子类没有虚函数则子类也会有虚表.也会产生复写行为.
总结:
父有,子没有,子类也会有虚表,而且也会产生虚表指针复写行为.
且只要父类有虚函数,不管子类有没有虚函数,子类都会产生虚表,且会复写虚表指针.
2.3 子类有虚函数,父类没有虚函数
高级代码: 子类中定义了虚函数,父类则把虚函数去掉了.
Debug下的反汇编代码
1.main函数下构造
2.构造内部
看其内部得出,父类没有虚函数的情况下,其对象 +4位置,跳过前边的4个字节,来构造父类,构造完毕之后填写子类虚表指针.
3.父类构造内部
父类构造内部没有产生虚表指针填写行为
总结:
子类有虚表,父类没有,则会跳过虚表指针的位置来构造父类,当构造完毕父类之后前4个字节填写子类的虚表指针.
2.4 子类,父类都没有虚函数的情况下
直接构造内存,没有虚表,也不会产生虚表指针复写,可以当做结构体还原.
第二专题大总结
1.父类有虚函数,子类不管有没有虚函数,都会有虚表
2.父类有虚函数构造的时候会填写虚表指针,且子类也会填写虚表指针,两者会产生虚表指针复写行为
3.子类中有虚函数,父类没有,则会跳过虚表指针来构造父类,其子类会在构造完毕父类之后填写虚表指针,不会产生虚表指针复写行为.
三丶结合第二专题的成员对象有无虚表行为
3.1成员对象没有虚表的情况下
高级代码:
class MyMemberObj //成员对象
{
public:
MyMemberObj(){}
~MyMemberObj(){}
}; class MyFather //父类
{
public:
MyFather();
~MyFather();
public:
int m_int;
}; MyFather::MyFather(){} MyFather::~MyFather(){} class MyChild : public MyFather //子类继承父类
{
public:
MyChild();
virtual ~MyChild();
MyMemberObj m_memberobj; //成员对象
float m_flt;
}; MyChild::MyChild(){} MyChild::~MyChild(){} int main(int argc, char* argv[])
{
MyChild test; //定义对象
return ;
}
Debug下的反汇编
1.main函数下的构造
2.构造内部
1.构造父类,因为父类没有虚函数,所以+4构造一下,且父类有一个成员,所以申请了4个字节空间
2.成员变量的构造+8的位置开始构造,父类构造完毕之后构造,且此时成员对象没有虚函数.
3.子类在自己的头4个字节位置处填写虚表指针.
3.成员对象构造内部
成员对象内部不会产生写虚表的行为.
总结:
成员对象没有虚函数的情况下,会在合适偏移位置处进行构造,注意合适位置处的用语,如果你是子类的成员对象,肯定会先构造父类,父类成员很多,则你的偏移位置则不固定.
3.2成员对象有虚表的情况下.
Debug下的汇编代码:
因为其类之加了一个虚关键字,析构变为了虚析构,产生了虚表的动作.所以其汇编代码1,2步没有改变,同上.
不同的是构造的时候,成员对象有了虚函数,构造的时候则会填写虚表.
总结:
1.有成员对象的时候其成员对象内部没有虚表产生,则会在合适位置构造成员对象.
2.有成员对象的时候,其成员对象内部有虚表产生,则在合适位置填写虚表指针,并且构造成员对象.
四丶反汇编中重载运算符的识别
在说重载运算符的时候,我们首先熟悉一下运算符重载的高级代码:
简单的运算符重载
函数类型 operator 运算符名称 (形参表列)
{
// 对运算符的重载处理
}
高深一点的可以参考博客,这里不再重复讲解.复习开发知识可以参考博客链接 http://c.biancheng.net/cpp/biancheng/view/215.html
高级代码:
int operator+(MyChild& a,MyFather& b)
{
return (int)a.m_flt + b.m_int;
}
int main(int argc, char* argv[])
{
MyChild a; //定义对象
MyFather b;
cout << a + b << endl;
return ;
}
在反汇编中,其实运算符重载就是调用函数.只不过换了一种函数的认知方式.
其实不难.当做函数还原就好.
说道这里,我们可以说下运算符重载的额外认知.
比如我们熟悉的
1.数学中的中缀式 a + b / c - d * e 这种表达式就是中缀表达式
2.波兰式 -+a/bc*de 中缀转化为了波兰式,我们学习数据结构的树的时候就学习过这种方式,这个是编译原理中的.适用于计算机的识别.
怎么转换的
Sub(add(a,Div(b,c),Imul(d,e); 转为汇编代码,比如a + b /c 我们则写成 add(a,div(b,c),然后转为汇编表达式即可.最终的结果则是上面写的波兰式.只不过按照语义,变为符号化了.
五丶纯虚函数的反汇编
我们知道,纯虚函数是为了子类实现了,自己不能实现,但是反汇编代码中其实实现了,只不过里面调用了提示错误的API.就是为了你不小心调用的时候提示不能创建xxx对象的实例.等等一些列的错误.
高级代码:
class MyFather //父类
{
public:
MyFather();
~MyFather();
virtual void show() = ; //纯虚函数
}; MyFather::MyFather(){} MyFather::~MyFather(){} class MyChild : public MyFather //子类继承父类
{
public:
MyChild();
virtual ~MyChild();
virtual void show();
}; MyChild::MyChild(){} MyChild::~MyChild(){} void MyChild::show()
{
cout << << endl;
} int main(int argc, char* argv[])
{
MyChild a; //定义对象
a.show();
return ;
}
Debug下反汇编
我们直接看纯虚函数内部了,在子类构造的时候父类会构造,父类构造自己的时候会填写虚表指针,我们直接找父类的虚表指针即可.然后定位虚表中的第二项.
第一项是父类的虚析构,第二项才是我们的.
纯虚函数在低版本就是19h,并且调用__amsg_exit,且如果弄了签名,则是__purecall
高版本不太一样,高版本不是简单的这样调用了(vs系列)它会保存当时的寄存器信息啊,什么的,然后写日志用的.反正结果是一样的.
高版本自己可以试试看一看有什么不同.
六丶模版识别.
模版和运算符重载一样,都是函数,编译为反汇编的代码都是函数调用.而且函数和函数的重载不同,它生成的反汇编代码有多处.
高级代码:
template <typename T>
T MySub(T a,T b)
{
return a - b;
} int main(int argc, char* argv[])
{
printf("%d\r\n",MySub(,));
printf("%f\r\n",MySub(3.0f,1.0f));
printf("%lf\r\n",MySub(8.3,4.3)); return ;
}
运行结果:
Debug下反汇编.
虽然都是一样调用,但是其内部是不同的.每个函数都有自己的汇编代码.
C++反汇编第四讲,反汇编中识别继承关系,父类,子类,成员对象的更多相关文章
-
C++反汇编第三讲,反汇编中识别继承关系,父类,子类,成员对象
讲解目录: 1.各类在内存中的表现形式 备注: 主要复习开发知识,和反汇编没有关系,但是是理解反汇编的前提. 2.子类继承父类 2.1 子类中有虚函数,父类中有虚函数 : 都有的情况下 ...
-
C++反汇编第四讲,认识多重继承,菱形继承的内存结构,以及反汇编中的表现形式.
目录: 1.多重继承在内存中的表现形式 多重继承在汇编中的表现形式 2.菱形继承 普通的菱形继承 虚继承 汇编中的表现形式 一丶多重继承在内存中的表现形式 高级代码: class Father1 { ...
-
PC逆向之代码还原技术,第四讲汇编中减法的代码还原
目录 PC逆向之代码还原技术,第四讲汇编中减法的代码还原 一丶汇编简介 二丶高级代码对应汇编观看. 1.代码还原解析: 三丶根据高级代码IDA反汇编的完整代码 四丶知识总结 PC逆向之代码还原技术,第 ...
-
Style在Android中的继承关系
Style在Android中的继承关系 Android的Styles(样式)和Themes(主题)非常类似Web开发里的CSS,方便开发者将页面内容和布局呈现分开.Style和Theme在Androi ...
-
在Entity Framework 中实现继承关系映射到数据库表
继承关系映射到数据库表中有多种方式: 第一种:TPH(table-per-hiaerachy) 每一层次一张表 (只有一张表) 仅使用名为父类的类型名的一张表,它包含了各个子类的所有属性信息,使用区分 ...
-
Java 中的多态,一次讲个够之继承关系中的多态
多态是继封装.继承之后,面向对象的第三大特性. 现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态. Java作为面向对象的语言,同样可以描述一个 ...
-
Dom中的继承关系
首先声明,一些内容基于个人猜测,如果哪里有错误,请立即联系在下! 我们用js操作Dom时,会经常用到一些个方法比如基于获取到的元素选择其子元素: <!DOCTYPE html> <h ...
-
初步学习C++中的继承关系
继承机制是面向对象程序设计使代码能够复用的最重要的手段,它同意程序猿在保持原有类特性的基础上进行扩展,添加功能. 这样产生新的类,称派生类.继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂 ...
-
java中的继承关系
1.定义 java中的继承是单一的,一个子类只能拥有一个父类:java中所有类的父类是java.lang.Object,除了这个类之外,每个类只能有一个父类: 而一个父类可以有多个子类,可以被多个子类 ...
随机推荐
-
PPT文档页数显示的增加和更新
在PPT的右下角增加页数的显示能够帮助演讲者把握进度,所以会经常遇到需要把页数显示在右下角的情况,这次在制作ppt的时候也遇到了.因此在这里总结一下设置方法. 一.在右下角显示当前页数和总页数 1)获 ...
-
jedis操作redis全指南
package com.wujintao.redis; import java.util.Date; import java.util.HashMap; import java.util.Iterat ...
-
navicat 随笔提示的快捷键
1.ctrl+q 打开查询窗口2.ctrl+/ 注释sql语句3.ctrl+shift +/ 解除注释4.ctrl+r 运行查询窗口的sql语句5.ctrl+shift+r 只运行选中的sql语句6. ...
-
Android开发有用的站点
在github上面找到一个个人认为比較好的站点,好在能够方便下载开发工具.我的AndroidStudio就是在上面下载的.安装了一直在使用.该 网址主要收集整理Android开发所需的Android ...
-
认识jeecms开源项目
1. JEECMS源代码基本结构及相关技术简介: 参考:http://blog.csdn.net/caozhenyu/article/details/47005623
-
自动生成 Makefile (automake/autoconf 入门)
作为Linux 下的程序开发人员,大家一定都遇到过Makefile ,用make 命令来编译自己写的程序确实是很方便.一般情况下,大家都是手工写一个简单Makefile ,如果要想写出一个符合*软件 ...
-
访问vsts私有nuget
访问vsts私有nuget Intro 有时候我们可能要自己搭建一个 nuget,如果不对外公开,即包浏览也是需要权限的,那我们应该怎么做才可以支持在哪里都可以正常的还原包呢? 我是在 VSTS(Vi ...
-
[dev] udp socket的read长度问题
场景描述 我的两个程序需要彼此通信.采用unix socket来实现. 并为了简单起见使用了DGRAM,也就是udp通信. 问题描述 1. 用法是这样的 收包的一端使用epoll监听,发包端发送一个2 ...
-
zw量化交易·实盘操作·系列培训班
参见: <zw量化交易·实盘操作·系列培训班> http://blog.sina.com.cn/s/blog_7100d4220102w0q5.html
-
Excel实用录入技巧
一.文本录入技巧 输入开头为0的序号 当直接输入单元格中的数字第一个为0时系统会默认去掉 只需要经单元格格式改为文本或者在单元格输入前使用英文状态下的单引号(‘) 例如:'0001 >>& ...