Android SO逆向-对象的继承和虚函数

0x00

这一节我们要讨论对象的继承和虚函数的汇编实现。

0x01

我们先直接看汇编代码:

#include "com_example_ndkreverse6_Lesson6.h"
#include <android/log.h>
#define LOG_TAG "lesson6"
#define ALOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))

class Base {
public:
	virtual void display() { //虚函数,virtual声明的函数,向上转型后的对象才能调用到子类同名的方法
		ALOGD("Base:%d, BaseChar:%d", base_, baseChar_);
	}

	Base(int base) {
		base_ = base;
		baseChar_ = 8;
		ALOGD("Base ...");
	}

	virtual ~Base() { //虚析构函数,只有声明成virtual,向上转型的对象先调用子类的析构函数,再调用父类的析构函数
		ALOGD("~Base ...");
	}
private:
	int base_;
	char baseChar_;
};
class Derived: public Base {
public:
	virtual void display() { //覆盖父类的方法
		ALOGD("Derived:%d, DerivedChar:%d", derived_,  derivedChar_);
		Base::display(); //使用父类的方法,由于是覆盖,所以同名,要用这种方式来引用
	}

	Derived(int derived) :
			Base(derived) {
		derived_ = derived;
		derivedChar_ = 10;
		ALOGD("Derived ...");
	}

	~Derived() {
		ALOGD("~Derived ...");
	}
private:
	int derived_;
	char derivedChar_;
};

JNIEXPORT void JNICALL Java_com_example_ndkreverse6_Lesson6_main
(JNIEnv * env, jobject jobject) {
	Base* d = new Derived(18);
	d->display();
	delete d;
}

那么,运行后执行的结果如下:

D/lesson6 (28959): Base ...
D/lesson6 (28959): Derived ...
D/lesson6 (28959): Derived:18, DerivedChar:10
D/lesson6 (28959): Base:18, BaseChar:8
D/lesson6 (28959): ~Derived ...
D/lesson6 (28959): ~Base ...

0x02

下面我们使用ida来打开so,对汇编代码做出解释。该汇编代码使用的是调试状态下的汇编代码。

.text:00003088                 EXPORT Java_com_example_ndkreverse6_Lesson6_main
.text:00003088 Java_com_example_ndkreverse6_Lesson6_main
.text:00003088                 PUSH    {R4-R6,LR}
.text:0000308A                 MOVS    R0, #0x14       ; unsigned int R0被分配为20,表示要分配20个字节的内存单元
.text:0000308C                 BL      _Znwj           ; operator new(uint)  分配20个字节大小的内存单元
.text:00003090                 LDR     R3, =(_ZTV4Base_ptr - 0x3098) ;直接调用Base的构造函数
.text:00003092                 MOVS    R6, #0x12       ;R6被初始化为18
.text:00003094                 ADD     R3, PC ; _ZTV4Base_ptr
.text:00003096                 LDR     R3, [R3]        ; `vtable for‘Base 取出Base的虚表指针,指向.data.rel.ro,即0x70428960
.text:00003098                 MOVS    R4, R0          ;R0为刚分配的20个字节大小的内存单元的首地址,现在赋值给R4
.text:0000309A                 ADDS    R3, #8          ;R3+8再赋值给R3,R3指向了0x70428968
.text:0000309C                 STR     R3, [R0]        ;将0x70428968赋值给刚分配的内存单元的首地址(所指向的内存单元)
.text:0000309E                 MOVS    R3, #8          ;R3被赋值为8
.text:000030A0                 LDR     R5, =(aLesson6 - 0x30AA)
.text:000030A2                 LDR     R2, =(aBase____0 - 0x30B0)
.text:000030A4                 STR     R6, [R0,#4]     ;把18赋值给刚分配的内存单元的首地址+4
.text:000030A6                 ADD     R5, PC          ; "lesson6" R5指向了位于.rodata段中lesson6
.text:000030A8                 MOVS    R1, R5          ;把R5赋值给R1做为参数
.text:000030AA                 STRB    R3, [R0,#8]     ;把8赋值给刚分配的内存单元的首地址+8
.text:000030AC                 ADD     R2, PC          ; "Base ..." R2指向了位于.rodata段中Base ...
.text:000030AE                 MOVS    R0, #3          ;R0被初始化为3
.text:000030B0                 BL      j_j___android_log_print ;输出Base ...
.text:000030B4                 LDR     R3, =(_ZTV7Derived_ptr - 0x30BE) ;开始调用Derived的构造函数
.text:000030B6                 LDR     R2, =(aDerived____0 - 0x30C2)
.text:000030B8                 MOVS    R1, R5          ;R1被赋值为R5,指向位于.rodata段中的lesson6
.text:000030BA                 ADD     R3, PC ; _ZTV7Derived_ptr
.text:000030BC                 LDR     R3, [R3]        ; `vtable for‘Derived 取出Derived的虚表指针,指向.data.rel.ro,即0x70428978
.text:000030BE                 ADD     R2, PC          ; "Derived ..." R2指向位于.rodata段中Derived
.text:000030C0                 STR     R6, [R4,#0xC]   ;把18赋值给刚分配的内存单元的首地址+12
.text:000030C2                 ADDS    R3, #8          ;R3赋值为Derived的虚表指针+8,即0x70428980
.text:000030C4                 STR     R3, [R4]        ;把0x70428980赋值给刚分配的内存单元的首地址
.text:000030C6                 MOVS    R3, #0xA        ;把R3赋值给10
.text:000030C8                 MOVS    R0, #3          ;把R0赋值给3
.text:000030CA                 STRB    R3, [R4,#0x10]  ;把10赋值给刚分配的内存单元的首地址+16
.text:000030CC                 BL      j_j___android_log_print ;目前R0,R1,R2都被初始化为正确的值了,开始调用j_j___android_log_print
.text:000030D0                 LDR     R3, [R4]        ;取出虚表指针,即位于.data.rel.ro段的地址0x70428980
.text:000030D2                 MOVS    R0, R4          ;把R0赋值为R4,作为第一个参数,R4为刚分配的内存单元的首地址,即this指针
.text:000030D4                 LDR     R3, [R3]        ;取出虚表指针所指向的内容,即Derived的Display方法的地址
.text:000030D6                 BLX     R3              ;跳转到Derived的Display方法
.text:000030D8                 LDR     R3, [R4]        ;取出虚表指针,即位于.data.rel.ro段的地址0x70428980
.text:000030DA                 MOVS    R0, R4          ;把R0赋值为R4,作为第一个参数,R4为刚分配的内存单元的首地址,即this指针
.text:000030DC                 LDR     R3, [R3,#8]     ;取出虚表指针+8所指向的内容,即Derived的~Derived方法的地址
.text:000030DE                 BLX     R3              ;跳转到Derived的~Derived方法
.text:000030E0                 POP     {R4-R6,PC}
.text:704220E4 off_704220E4    DCD _ZTV4Base_ptr - 0x70422098
.text:704220E4                                         ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+8r
.text:704220E8 off_704220E8    DCD aLesson6 - 0x704220AA
.text:704220E8                                         ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+18r
.text:704220E8                                         ; "lesson6"
.text:704220EC off_704220EC    DCD aBase____0 - 0x704220B0
.text:704220EC                                         ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+1Ar
.text:704220EC                                         ; "Base ..."
.text:704220F0 off_704220F0    DCD _ZTV7Derived_ptr - 0x704220BE
.text:704220F0                                         ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+2Cr
.text:704220F4 off_704220F4    DCD aDerived____0 - 0x704220C2
.text:704220F4                                         ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+2Er
.text:704220F4                                         ; "Derived ..."
.got:70428F24                 AREA .got, DATA
.got:70428F24                 ; ORG 0x70428F24
.got:70428F24 _ZTV4Base_ptr   DCD _ZTV4Base           ; DATA XREF: Base::~Base()+Ao
.got:70428F24                                         ; Base::~Base()+Cr ...
.got:70428F24                                         ; `vtable for‘Base
.got:70428F28 _ZTV7Derived_ptr DCD _ZTV7Derived       ; DATA XREF: Derived::~Derived()+Ao
.got:70428F28                                         ; Derived::~Derived()+Cr ...
.got:70428F28                                         ; `vtable for‘Derived
.data.rel.ro:70428960 ; `vtable for‘Base
.data.rel.ro:70428960 _ZTV4Base       DCB    0                ; DATA XREF: Base::~Base()+Co
.data.rel.ro:70428960                                         ; Java_com_example_ndkreverse6_Lesson6_main+Eo ...
.data.rel.ro:70428961                 DCB    0
.data.rel.ro:70428962                 DCB    0
.data.rel.ro:70428963                 DCB    0
.data.rel.ro:70428964                 DCD _ZTI4Base           ; `typeinfo for‘Base
.data.rel.ro:70428968                 DCD _ZN4Base7displayEv+1
.data.rel.ro:7042896C                 DCD _ZN4BaseD2Ev+1
.data.rel.ro:70428970                 DCD _ZN4BaseD0Ev+1
.data.rel.ro:70428974                 ALIGN 8
.data.rel.ro:70428978                 WEAK _ZTV7Derived
.data.rel.ro:70428978 ; `vtable for‘Derived
.data.rel.ro:70428978 _ZTV7Derived    DCB    0                ; DATA XREF: Derived::~Derived()+Co
.data.rel.ro:70428978                                         ; Java_com_example_ndkreverse6_Lesson6_main+34o ...
.data.rel.ro:70428979                 DCB    0
.data.rel.ro:7042897A                 DCB    0
.data.rel.ro:7042897B                 DCB    0
.data.rel.ro:7042897C                 DCD _ZTI7Derived        ; `typeinfo for‘Derived
.data.rel.ro:70428980                 DCD _ZN7Derived7displayEv+1
.data.rel.ro:70428984                 DCD _ZN7DerivedD2Ev+1
.data.rel.ro:70428988                 DCD _ZN7DerivedD0Ev+1
.rodata:704267B0 ; `typeinfo name for‘Base
.rodata:704267B0 _ZTS4Base       DCB "4Base",0           ; DATA XREF: .data.rel.ro:_ZTI4Base+4o
.rodata:704267B6                 ALIGN 4
.rodata:704267B8                 WEAK _ZTS7Derived
.rodata:704267B8 ; `typeinfo name for‘Derived
.rodata:704267B8 _ZTS7Derived    DCB "7Derived",0        ; DATA XREF: .data.rel.ro:_ZTI7Derived+4o
.rodata:704267C1                 ALIGN 4
.rodata:704267C4 aLesson6        DCB "lesson6",0         ; DATA XREF: Base::display(void)+Eo
.rodata:704267C4                                         ; .text:off_70421FD4o ...
.rodata:704267CC aBaseDBasecharD DCB "Base:%d, BaseChar:%d",0
.rodata:704267CC                                         ; DATA XREF: Base::display(void)+10o
.rodata:704267CC                                         ; .text:off_70421FD8o
.rodata:704267E1 aDerivedDDerive DCB "Derived:%d, DerivedChar:%d",0
.rodata:704267E1                                         ; DATA XREF: Derived::display(void)+Eo
.rodata:704267E1                                         ; .text:off_70422000o
.rodata:704267FC aBase___        DCB "~Base ...",0       ; DATA XREF: Base::~Base()+10o
.rodata:704267FC                                         ; .text:off_7042202Co
.rodata:70426806 aDerived___     DCB "~Derived ...",0    ; DATA XREF: Derived::~Derived()+10o
.rodata:70426806                                         ; .text:off_70422060o
.rodata:70426813 aBase____0      DCB "Base ...",0        ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+24o
.rodata:70426813                                         ; .text:off_704220ECo
.rodata:7042681C aDerived____0   DCB "Derived ...",0     ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+36o
.rodata:7042681C                                         ; .text:off_704220F4o

我们看到执行Display方法时,首先从对象的首地址取出虚表指针,然后再从虚表指针所指向的内存单元中取出具体的Display指令的地址,然后跳转到对应的指令执行。

.text:70421FDC ; _DWORD Derived::display(Derived *__hidden this)
.text:70421FDC                 WEAK _ZN7Derived7displayEv
.text:70421FDC _ZN7Derived7displayEv                   ; DATA XREF: .data.rel.ro:_ZTV7Derived+8o
.text:70421FDC
.text:70421FDC var_10          = -0x10
.text:70421FDC
.text:70421FDC                 PUSH    {R0,R1,R4,LR}
.text:70421FDE                 MOVS    R4, R0           ;R4被赋值给R0,即刚分配的内存单元的首地址
.text:70421FE0                 LDRB    R3, [R0,#0x10]   ;从内存单元中取出子类的第二个变量,也就是DerivedChar的值
.text:70421FE2                 LDR     R1, =(aLesson6 - 0x70421FEC)
.text:70421FE4                 LDR     R2, =(aDerivedDDerive - 0x70421FEE)
.text:70421FE6                 STR     R3, [SP,#0x10+var_10] ;作为第五个参数存放在堆栈中传递
.text:70421FE8                 ADD     R1, PC          ; "lesson6"
.text:70421FEA                 ADD     R2, PC          ; "Derived:%d, DerivedChar:%d"
.text:70421FEC                 LDR     R3, [R4,#0xC]   ; 第四个参数是子类的第一个变量,也就是Derived的值
.text:70421FEE                 MOVS    R0, #3
.text:70421FF0                 BL      j_j___android_log_print
.text:70421FF4                 MOVS    R0, R4          ; this ;返回this指针
.text:70421FF6                 BL      _ZN4Base7displayEv ; Base::display(void)
.text:70421FFA                 POP     {R0,R1,R4,PC}

接着,我们继续析构函数,思路和Display方法是一样的,首先从对象的首地址取出虚表指针,然后再从虚表指针+8所指向的内存单元中取出具体的Derived类析构函数的地址,然后跳转到对应的指令执行。

.text:70422064 ; _DWORD __fastcall Derived::~Derived(Derived *__hidden this)
.text:70422064                 WEAK _ZN7DerivedD0Ev
.text:70422064 _ZN7DerivedD0Ev                         ; DATA XREF: .data.rel.ro:_ZTV7Derived+10o
.text:70422064                 PUSH    {R4,LR}
.text:70422066                 MOVS    R4, R0          ;R4被赋值为R0,即对象的首地址
.text:70422068                 BL      _ZN7DerivedD2Ev ; Derived::~Derived()
.text:7042206C                 MOVS    R0, R4          ; void * R0被赋值为对象的首地址
.text:7042206E                 BL      _ZdlPv          ; operator delete(void *) 真正的释放堆空间
.text:70422072                 MOVS    R0, R4
.text:70422030 ; _DWORD __fastcall Derived::~Derived(Derived *__hidden this)
.text:70422030                 WEAK _ZN7DerivedD2Ev
.text:70422030 _ZN7DerivedD2Ev                         ; CODE XREF: Derived::~Derived()+4p
.text:70422030                                         ; DATA XREF: .data.rel.ro:_ZTV7Derived+Co
.text:70422030                 PUSH    {R4,LR}         ; Alternative name is ‘Derived::~Derived()‘
.text:70422032                 MOVS    R4, R0          ;R4被赋值为R0,即对象的首地址
.text:70422034                 LDR     R3, =(_ZTV7Derived_ptr - 0x7042203E)
.text:70422036                 LDR     R1, =(aLesson6 - 0x70422042)
.text:70422038                 LDR     R2, =(aDerived___ - 0x70422044)
.text:7042203A                 ADD     R3, PC ; _ZTV7Derived_ptr
.text:7042203C                 LDR     R3, [R3]        ; `vtable for‘Derived R3 取出Derived的虚表指针,指向.data.rel.ro,即0x70428978
.text:7042203E                 ADD     R1, PC          ; "lesson6"
.text:70422040                 ADD     R2, PC          ; "~Derived ..."
.text:70422042                 ADDS    R3, #8          ;R3赋值为Derived的虚表指针+8,即0x70428980
.text:70422044                 STR     R3, [R0]        ;把0x70428980赋值给刚分配的内存单元的首地址
.text:70422046                 MOVS    R0, #3
.text:70422048                 BL      j_j___android_log_print
.text:7042204C                 MOVS    R0, R4          ; this R0被赋值为对象的首地址
.text:7042204E                 BL      _ZN4BaseD2Ev    ; Base::~Base() 调用父类的析构函数
.text:70422052                 MOVS    R0, R4          ;返回对象的首地址
.text:70422054                 POP     {R4,PC}
.text:70422004 ; _DWORD __fastcall Base::~Base(Base *__hidden this)
.text:70422004                 WEAK _ZN4BaseD2Ev
.text:70422004 _ZN4BaseD2Ev                            ; CODE XREF: Derived::~Derived()+1Ep
.text:70422004                                         ; Base::~Base()+4p
.text:70422004                                         ; DATA XREF: ...
.text:70422004                 PUSH    {R4,LR}         ; Alternative name is ‘Base::~Base()‘
.text:70422006                 MOVS    R4, R0          ;R4被赋值为对象的首地址
.text:70422008                 LDR     R3, =(_ZTV4Base_ptr - 0x70422012)
.text:7042200A                 LDR     R1, =(aLesson6 - 0x70422016)
.text:7042200C                 LDR     R2, =(aBase___ - 0x70422018)
.text:7042200E                 ADD     R3, PC ; _ZTV4Base_ptr
.text:70422010                 LDR     R3, [R3]        ; `vtable for‘Base 取出Base的虚表指针,指向.data.rel.ro,即0x70428960
.text:70422012                 ADD     R1, PC          ; "lesson6"
.text:70422014                 ADD     R2, PC          ; "~Base ..."
.text:70422016                 ADDS    R3, #8          ;R3+8再赋值给R3,R3指向了0x70428968
.text:70422018                 STR     R3, [R0]        ;将0x70428968赋值给刚分配的内存单元的首地址(所指向的内存单元)
.text:7042201A                 MOVS    R0, #3          ;R0是第一个参数,赋值为3
.text:7042201C                 BL      j_j___android_log_print
.text:70422020                 MOVS    R0, R4          ;返回对象的首地址
.text:70422022                 POP     {R4,PC}

0x03

总结:父类的成员位于低地址,子类的成员位于高地址,虚表指针位于对象的首地址,构造对象时,先构造父类,虚表指针指向父类的虚表,再构造子类,虚表指针指向子类的虚表。析构对象时,先析构子类,虚表指针指向子类的虚表,再析构父类,虚表指针指向父类的虚表。

时间: 2024-10-07 15:10:29

Android SO逆向-对象的继承和虚函数的相关文章

&lt;转&gt;C++继承中虚函数的使用

转自:http://blog.csdn.net/itolfn/article/details/7412364 一:继承中的指针问题. 1. 指向基类的指针可以指向派生类对象,当基类指针指向派生类对象时,这种指针只能访问派生对象从基类继承 而来的那些成员,不能访问子类特有的元素 ,除非应用强类型转换,例如有基类B和从B派生的子类D,则B *p;D  dd; p=&dd;是可以的,指针p只能访问从基类派生而来的成员,不能访问派生类D特有的成员.因为基类不 知道派生类中的这些成员. 2. 不能使派生类

C++继承、虚函数处的面试题

昨天,收到 SenseTime公司面试官的电话面试(一天面了三家公司,收获挺多的),通话时间将近1个半小时,面试过程中暴露出很多知识上的漏洞,本篇文章针对面试过程中继承以及虚函数方面的知识做一总结,查缺补漏,希望对大家有帮助. 单继承下的虚函数表 //单继承下虚函数表:是如何组织的 class A{ public: virtual void func(){ cout << "A::func" << endl; } virtual void funcA(){ co

Android SO逆向-对象的拷贝构造函数

0x00 这一节我们主要讨论对象的拷贝构造函数的汇编实现. 0x01 我们直接看C++代码: Test.h: #ifndef _TEST_H_ #define _TEST_H_ #include <android/log.h> #define LOG_TAG "lesson5" #define ALOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) class Test

C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别

1.虚函数(impure virtual) C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现. 子类可以重写父类的虚函数实现子类的特殊化. 如下就是一个父类中的虚函数: class A { public: virtual void out2(string s) { cout<<"A(out2):"<<s<<endl; } }; 2.纯虚函数(pure virtual) C++中包含纯虚函数的类,被称为是“抽象类

继承、虚函数注意点

哪些函数不能被继承: 构造函数.析构函数.拷贝构造函数.赋值操作符重载函数(赋值构造函数) 哪些函数不能为虚函数:(定义虚函数一般是为了使用多态性质) 普通函数(非成员函数):不属于类的成员,不能用于多态,只能被重载. 静态成员函数:静态成员函数属于类的信息,被所有对象共享,不归某个对象所有,没有动态绑定的必要性. 构造函数: 友元函数:c++不支持友元函数的继承,因此不能在派生类中被覆盖,没有必要为虚函数 内联成员函数:内联函数在编译阶段内联展开,减少函数调用的代价,而虚函数用于多态,在运行时

C++学习笔记(十二):类继承、虚函数、纯虚函数、抽象类和嵌套类

类继承 在C++类继承中,一个派生类可以从一个基类派生,也可以从多个基类派生. 从一个基类派生的继承称为单继承:从多个基类派生的继承称为多继承. 1 //单继承的定义 2 class B:public A 3 { 4 < 派生类新定义成员> 5 }; 6 //多继承的定义 7 class C:public A,private B 8 { 9 < 派生类新定义成员> 10 }; 我们这篇主要说单继承. 派生类共有三种C++类继承方式: 公有继承(public) 基类的公有成员和保护成

虚继承和虚函数继承

虚继承主要用于菱形 形式的继承形式 虚继承是为了在多继承的时候避免引发歧义, 比如类A有个就是a,B继承了A,C也继承了A,当D多继承B,C时,就会有歧义产生了,所以要使用虚拟继承避免重复拷贝. 虚函数继承是解决多态性的,当用基类指针指向派生类对象的时候,基类指针调用虚函数的时候会自动调用派生类的虚函数,这就是多态性,也叫动态编联 虚函数继承: class A { virtual void fun() {cout < <'A' < <endl;}; }; class B : pub

C++ 类在继承中虚函数、纯虚函数、普通函数,三者的区别

1.虚函数(impure virtual) C++的虚函数主要作用是"运行时多态",父类中提供虚函数的实现,为子类提供默认的函数实现. 子类可以重写父类的虚函数实现子类的特殊化. 如下就是一个父类中的虚函数: class A { public:     virtual void out2(string s)     {         cout<<"A(out2):"<<s<<endl;     } }; 2.纯虚函数(pure

继承中虚函数的缺省参数值问题

如果类继承中重新定义了虚函数,那么虚函数中的缺省参数不要重新定义. 用一句话来解释原因就是:虚函数是动态绑定的(dynamically bound),但是缺省参数却是静态绑定的(statically bound). 静态类型和动态类型 首先需要了解什么是对象的静态类型和动态类型,对象的所谓静态类型(static type),就是它在程序中被声明时所采用的类型. 以下面的类为例: class Shape { public: enum ShapeColor{Red,Green,Blue}; virt