Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android SO逆向-對象的繼承和虛函數

Android SO逆向-對象的繼承和虛函數

編輯:關於Android編程

0x00

這一節我們要討論對象的繼承和虛函數的匯編實現。

 

0x01

我們先直接看匯編代碼:

 

#include "com_example_ndkreverse6_Lesson6.h"
#include 
#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

 

總結:父類的成員位於低地址,子類的成員位於高地址,虛表指針位於對象的首地址,構造對象時,先構造父類,虛表指針指向父類的虛表,再構造子類,虛表指針指向子類的虛表。析構對象時,先析構子類,虛表指針指向子類的虛表,再析構父類,虛表指針指向父類的虛表。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved