美文网首页
C++逆向学习(四) 类

C++逆向学习(四) 类

作者: 编程小世界 | 来源:发表于2019-06-22 18:52 被阅读0次

测试代码

基类 base ,派生类 derived ,分别有成员变量、成员函数、虚函数

#include<stdio.h>#include<stdlib.h>classbase {public:inta;doubleb;    base() {this->a =1;this->b =2.3;printf("base constructor\n");    }voidfunc(){printf("%d  %lf\n", a, b);    }virtualvoidv_func(){printf("base v_func()\n");    }    ~base() {printf("base destructor\n");    }};classderived :publicbase {public:    derived() {printf("derived constructor\n");    }virtualvoidv_func(){printf("derived v_func()");    }    ~derived() {printf("derived destructor\n");    }};intmain(intargc,char** argv){    base a;    a.func();    a.v_func();    base* b = (base*)newderived();    b->func();    b->v_func();return0;}

编译: g++ test.cpp -o test

IDA视角

IDA打开,如下:

this指针

可以看到, base:: 的每个函数都传入了一个参数 (base*)&v5 ,正是类实例的 this指针

以下是普通成员函数 func() 的调用过程

rdi 作为第一个参数,存放 this 指针,而 windows 下是寄存器 rcx

this 指针是识别类成员函数的一个关键

如果看到C++生成的exe文件中,如果 rcx 寄存器还没有被初始化就直接使用,很可能是类的成员函数

构造、析构

考虑构造函数时的过程

其中 *this = off_400C18 ,即先把类的虚表地址赋值给类实例的首字段

补充一些

注意虚表前还有一个 typeinfo ,在 g++ 的实现中,真正的 typeinfo信息在虚表之后,虚表的前一个字段存放了 typeinfo 的地址

typeinfo 是编译器生成的特殊类型信息,包括对象继承关系、对象本身的描述等

Aclass* ptra=new Bclass;int ** ptrvf=(int**)(ptra);RTTICompleteObjectLocator str=*((RTTICompleteObjectLocator*)(*((int*)ptrvf[0]-1)));  //vptr-1

这段获取对象RTTI信息相关的代码也显示了这一点

回到构造和析构函数

在构造函数调用中,显然需要将虚表的地址赋值给类实例的虚表指针,从代码上来看也是这样

但是,我们观察base类的析构函数

析构时也首先重新赋值了虚表指针,看起来可能有点多此一举

但如果析构函数中调用了虚函数,此行为可以保证正确;至于如果不重新赋值会有错误行为的情况就不展开了

虚表指针的赋值是识别的一个关键,排除开发者故意伪造编译器生成的代码来误导分析,基本可以确定是 构造函数 或者 析构函数

同样的,找到了虚表,也就可以根据IDA的交叉引用,找到对应的 构造函数 和 析构函数

构造、析构代理函数

全局对象和静态对象的构造时机相同,可以说是被隐藏了起来,在main函数之前由构造代理函数统一构造

测试代码:

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<iostream>usingnamespacestd;classt {public:char* str;    t() {cout<<"constructor"<str =newchar[16];memcpy(this->str,"hello",12);    }    ~t() {cout<str <

编译: visual studio 2019 x64 release

IDA打开,根据输出,下断点后发现t类全局变量构造函数输出信息调用于 initterm_0 函数

一段 initterm_0 的代码实现如下:

while(pfbegin < pfend) {//pfbegin == __xc_a , pfend == __xc_zif(*pfbegin !=NULL) {        (**pfbegin)();//调用每一个初始化或构造代理函数++pfbegin();    }}

执行 (**pfbegin)() 后并不会进入全局对象的构造函数中,而是进入编译器提供的 构造代理函数

最简单的找到全局对象构造函数的方法:因为构造代理函数中会 注册析构函数 ,其注册方式是使用 atexit ,我们对 atexit 下断点,调试过程中很容易在附近找到全局对象构造的构造函数

如图所示, 10 即为对象数组的大小,并且最后一个参数传入了构造函数指针 t::t()

析构代理函数比较类似,就不多分析了,同样以 atexit 为切入点

t::_t 即为 t 类的析构函数

虚函数调用

代码中我们用 base* 指针指向了 new derived() ,在IDA里如下

v3作为derived类实例的地址,存放的正好是虚表指针,而 v_func() 正好在虚表的第一个位置,参数 v3 则是例行传入 this 指针

已经有很多文章讲过虚函数调用过程了,这里就只是简单说一下

虚基类继承

主要分析一下 菱形继承 的内存布局,代码如下:

#include<stdio.h>#include<stdlib.h>//间接基类classA {public:virtualvoidfunction(){printf("A virtual function\n");    }inta;};//直接基类classB :virtualpublicA {//虚继承public:virtualvoidfunc(){printf("B virtual func()\n");    }intb;};//直接基类classC :virtualpublicA {//虚继承public:virtualvoidfunc(){printf("C virtual func()");    }intc;};//派生类classD :publicB,publicC {public:virtualvoidfunction(){printf("D virtual function()");    }intd;};intmain(intargc,char** argv){    A* A_ptr = (A*)newD();    A_ptr->function();return0;}

编译: visual studio 2019 x64 release

B、C类都虚继承了A类,然后D类多重继承于B、C类

布局如图:

具体实现是在B、C类里不再保存A类的内容,而是保存一份 偏移地址 ,然后将A类的数据保存在一个公共位置处,降低数据冗余

为方便说明,使用 g++ 编译并用IDA打开

main函数比较清晰,跟进D类的构造函数

虚表占8字节,int占4字节,考虑字节对齐,实际B、C类都占了16字节

接着用gdb跟进一下,断在 (**func)(func) 上

已经分析过,D类的首字段即存放了B类的虚表,也就是 RBX==0x614c20是D类实例地址

IDA可以看到 0x400A90==A::vtable ,也就是先找到A类的虚表

而A类虚表实际存放的函数指针值,由于虚函数机制被 D::function()覆盖,会实际调用到D类对应的函数

补充

关于如何让IDA里的分析更清晰,添加结构体、类的信息来帮助IDA的内容,网上已经有很多,这里不再多说了

推荐一本书《深度探索C++对象模型》,里面有很多类布局的历史实现,以及这些布局设计时对空间、时间效率的权衡

看我主页简介免费C++学习资源,视频教程、职业规划、面试详解、学习路线、开发工具

每晚8点直播讲解C++编程技术。

相关文章

  • C++逆向学习(四) 类

    测试代码 基类base,派生类derived,分别有成员变量、成员函数、虚函数 #include #include...

  • pwnable.kr之uaf && c++虚函数

    c++的逆向还是要熟悉下。 一、关于c++虚函数 虚函数使得c++能够实现多态,每个类有一个虚表,每个对象在实现的...

  • 13|第十三课:逆向工程

    一、历史回顾 (一)、历史回顾 二、Mybatis逆向工程详解 1、逆向工程 表、类、接口、mapper.xml四...

  • 【C++温故知新】函数

    这是C++类重新复习学习笔记的第 四 篇,同专题的其他文章可以移步:https://www.jianshu.com...

  • 《C++ Primer》第12章 类

    《C++ Primer》 第四版 第12章 类

  • NDK开发---C++学习(六):继承、多态

    前言 前面我们已经介绍过了C++中的类与函数,不熟悉的,可以去看看NDK开发---C++学习(三):类与函数(上)...

  • iOS逆向课程笔记(四)

    7.逆向工具集和安装和使用 iOS逆向工程的工具大致可分为四类: 检测工具如:Reveal、tcpdump等 反编...

  • ssm

    mybatis逆向工程 逆向工程配置文件 generatorConfig.xml文件 逆向工程代码 测试类(可以在...

  • C++ 类虚函数原理

    学习过C++的童鞋都知道C++类成员函数可以分为虚函数和非虚函数,(java程序员就请绕过这个问题,因为java类...

  • iOS逆向学习

    参考文章:iOS逆向开发记录:iOS逆向之手机越狱iOS逆向之介绍iOS逆向之文件系统结构iOS逆向之文件权限及类...

网友评论

      本文标题:C++逆向学习(四) 类

      本文链接:https://www.haomeiwen.com/subject/vrbvqctx.html