码迷,mamicode.com
首页 > 移动开发 > 详细

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

时间:2016-04-22 18:57:02      阅读:217      评论:0      收藏:0      [点我收藏+]

标签:

    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

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

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

标签:

原文地址:http://blog.csdn.net/jltxgcy/article/details/51217318

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!