300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Android SO逆向-对象的构造函数与析构函数

Android SO逆向-对象的构造函数与析构函数

时间:2021-03-06 15:04:55

相关推荐

Android SO逆向-对象的构造函数与析构函数

0x00

这一节我们主要讨论对象的构造函数和析构函数的汇编实现。

0x01

我们先直接看C++代码:

#include "com_example_ndkreverse4_Lesson4.h"#include "Test.h"#include <android/log.h>#define LOG_TAG "lesson4"#define ALOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))JNIEXPORT void JNICALL Java_com_example_ndkreverse4_Lesson4_main(JNIEnv * env, jobject jobject) {Test a2(10);a2.Display();}

Test.h定义如下:

#ifndef _TEST_H_#define _TEST_H_class Test{public:// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的// 默认的构造函数Test();Test(int num);void Display();~Test();private:int num_;};#endif // _TEST_H_

Test.cpp定义如下:

#include "Test.h"#include <iostream>using namespace std;// 不带参数的构造函数称为默认构造函数Test::Test(){num_ = 0;cout<<"Initializing Default"<<endl;}Test::Test(int num){num_ = num;cout<<"Initializing "<<num_<<endl;}Test::~Test(){cout<<"Destroy "<<num_<<endl;}void Test::Display(){cout<<"num="<<num_<<endl;}

0x02

下面我们使用ida来打开so,对汇编代码做出解释。

.text:000109B4 EXPORT Java_com_example_ndkreverse4_Lesson4_main.text:000109B4 Java_com_example_ndkreverse4_Lesson4_main.text:000109B4.text:000109B4 var_14= -0x14.text:000109B4.text:000109B4 PUSH {R0-R2,R4,R5,LR} ;分配堆栈,R0~R2的位置就是被分配为堆栈的空间.text:000109B6 LDRR4, =(__stack_chk_guard_ptr - 0x109BE).text:000109B8 MOVS R1, #0xA ;初始化值为10.text:000109BA ADDR4, PC ; __stack_chk_guard_ptr.text:000109BC LDRR4, [R4] ; __stack_chk_guard.text:000109BE MOVR0, SP ;堆栈的地址赋值给R0.text:000109C0 LDRR3, [R4] ;堆栈检查的信息.text:000109C2 STRR3, [SP,#0x18+var_14] ;把堆栈检查的信息入栈.text:000109C4 BL_ZN4TestC2Ei ; Test::Test(int) ;此时R0为SP的地址,R1为0xA(10),调用构造函数.text:000109C8 MOVR0, SP ;把sp的地址赋值给R0.text:000109CA BL_ZN4Test7DisplayEv ; Test::Display(void) ;调用Display方法.text:000109CE MOVR0, SP; this 把sp的的地址赋值给R0.text:000109D0 BL_ZN4TestD2Ev ; Test::~Test() ;调用析构函数.text:000109D4 LDRR2, [SP,#0x18+var_14].text:000109D6 LDRR3, [R4].text:000109D8 CMPR2, R3.text:000109DA BEQlocret_109E0.text:000109DC BLj_j___stack_chk_fail.text:000109E0 ; ---------------------------------------------------------------------------.text:000109E0.text:000109E0 locret_109E0 ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+26j.text:000109E0 POP{R0-R2,R4,R5,PC}

我们看到在调用构造函数,Display函数,析构函数时,R0的地址都是堆栈的地址,也就是C++层的this指针。

下面我们接着看各个函数的实现。

1、构造函数

.text:00010CD0 ; Test::Test(int).text:00010CD0 EXPORT _ZN4TestC2Ei.text:00010CD0 _ZN4TestC2Ei ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+10p.text:00010CD0 PUSH {R3-R5,LR}.text:00010CD2 MOVS R5, R0 ;R0指向的地址就是刚刚分配的堆栈的地址,即C++层次上的this指针.text:00010CD4 LDRR4, =(_ZSt4cout_ptr - 0x10CDE).text:00010CD6 STRR1, [R0] ;把初始化值10赋值压入堆栈,R0指向的地址就是刚刚分配的堆栈的地址.text:00010CD8 LDRR1, =(aInitializing - 0x10CE2).text:00010CDA ADDR4, PC ; _ZSt4cout_ptr.text:00010CDC LDRR4, [R4] ; std::cout.text:00010CDE ADDR1, PC; "Initializing ".text:00010CE0 MOVS R0, R4; this ;R0和下面这个函数我们不深究.text:00010CE2 BL_ZNSo14_M_put_nowidenEPKc ; std::ostream::_M_put_nowiden(char const*).text:00010CE6 LDRR1, [R5] ;从堆栈中取出初始化值10.text:00010CE8 MOVS R0, R4 ;R0和下面的函数我们不深究,总之R1做为其中一个参数,调用后面的函数是把10输出的控制台.text:00010CEA BL_ZNSt4priv9__put_numIcSt11char_traitsIcElEERSt13basic_ostreamIT_T0_ES7_T1_ ; std::priv::__put_num<char,std::char_traits<char>,long>(std::basic_ostream<char,std::char_traits<char>> &,long).text:00010CEE BL_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ ; std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &).text:00010CF2 MOVS R0, R5.text:00010CF4 POP{R3-R5,PC}

这个构造函数的主要作用是把初始化值10存放到堆栈中。

2、Display函数

.text:00010D2C ; Test::Display(void).text:00010D2C EXPORT _ZN4Test7DisplayEv.text:00010D2C _ZN4Test7DisplayEv ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+16p.text:00010D2C PUSH {R3-R5,LR}.text:00010D2E MOVS R5, R0;R0指向的地址就是刚刚分配的堆栈的地址,即C++层次上的this指针.text:00010D30 LDRR4, =(_ZSt4cout_ptr - 0x10D38).text:00010D32 LDRR1, =(aNum - 0x10D3C).text:00010D34 ADDR4, PC ; _ZSt4cout_ptr.text:00010D36 LDRR4, [R4] ; std::cout.text:00010D38 ADDR1, PC; "num=".text:00010D3A MOVS R0, R4; this.text:00010D3C BL_ZNSo14_M_put_nowidenEPKc ; std::ostream::_M_put_nowiden(char const*).text:00010D40 LDRR1, [R5] ;从堆栈中取出刚刚存的值10,调用下面的函数输出到控制台.text:00010D42 MOVS R0, R4.text:00010D44 BL_ZNSt4priv9__put_numIcSt11char_traitsIcElEERSt13basic_ostreamIT_T0_ES7_T1_ ; std::priv::__put_num<char,std::char_traits<char>,long>(std::basic_ostream<char,std::char_traits<char>> &,long).text:00010D48 BL_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ ; std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &).text:00010D4C POP{R3-R5,PC}

Display函数主要需要看的地方是堆栈中取出刚刚存的值10,然后输出到控制台。

3、析构函数

.text:00010D00 ; _DWORD __fastcall Test::~Test(Test *__hidden this).text:00010D00 EXPORT _ZN4TestD2Ev.text:00010D00 _ZN4TestD2Ev ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+1Cp.text:00010D00 PUSH {R3-R5,LR}.text:00010D02 MOVS R5, R0.text:00010D04 LDRR4, =(_ZSt4cout_ptr - 0x10D0C).text:00010D06 LDRR1, =(aDestroy - 0x10D10).text:00010D08 ADDR4, PC ; _ZSt4cout_ptr.text:00010D0A LDRR4, [R4] ; std::cout.text:00010D0C ADDR1, PC; "Destroy ".text:00010D0E MOVS R0, R4; this.text:00010D10 BL_ZNSo14_M_put_nowidenEPKc ; std::ostream::_M_put_nowiden(char const*).text:00010D14 LDRR1, [R5] ;从堆栈中取出刚刚存的值10,调用下面的函数输出到控制台.text:00010D16 MOVS R0, R4.text:00010D18 BL_ZNSt4priv9__put_numIcSt11char_traitsIcElEERSt13basic_ostreamIT_T0_ES7_T1_ ; std::priv::__put_num<char,std::char_traits<char>,long>(std::basic_ostream<char,std::char_traits<char>> &,long).text:00010D1C BL_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ ; std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &).text:00010D20 MOVS R0, R5.text:00010D22 POP{R3-R5,PC}

我们看到析构函数对于堆栈没有做什么操作。等执行完Java_com_example_ndkreverse4_Lesson4_main函数后,会执行出栈操作,自动释放了刚才分配给这个对象的空间。

0x03

我们刚才在栈空间分配了对象,下面我们再堆空间分配对象,看看汇编代码是怎么处理的。

首先看下C++代码。

JNIEXPORT void JNICALL Java_com_example_ndkreverse4_Lesson4_main(JNIEnv * env, jobject jobject) {Test* a3 = new Test(20); // new operatora3->Display();delete a3;}

对应的反汇编代码:

.text:000109B4 EXPORT Java_com_example_ndkreverse4_Lesson4_main.text:000109B4 Java_com_example_ndkreverse4_Lesson4_main.text:000109B4 PUSH {R4,LR}.text:000109B6 MOVS R0, #4; unsigned int ;在堆上分配一个4字节的空间.text:000109B8 BL_Znwj ; operator new(uint).text:000109BC MOVS R4, R0;R0是刚分配的堆空间的首地址.text:000109BE MOVS R1, #0x14 ;R1是初始化值20.text:000109C0 BL_ZN4TestC2Ei ; Test::Test(int).text:000109C4 MOVS R0, R4.text:000109C6 BL_ZN4Test7DisplayEv ; Test::Display(void).text:000109CA MOVS R0, R4; this.text:000109CC BL_ZN4TestD2Ev ; Test::~Test().text:000109D0 MOVS R0, R4; void *.text:000109D2 BL_ZdlPv; operator delete(void *).text:000109D6 POP{R4,PC}

.text:00010CC0 ; Test::Test(int).text:00010CC0 EXPORT _ZN4TestC2Ei.text:00010CC0 _ZN4TestC2Ei ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+Cp.text:00010CC0 PUSH {R3-R5,LR}.text:00010CC2 MOVS R5, R0;R0是刚分配的堆空间的首地址.text:00010CC4 LDRR4, =(_ZSt4cout_ptr - 0x10CCE).text:00010CC6 STRR1, [R0] ;把20放在堆空间中.text:00010CC8 LDRR1, =(aInitializing - 0x10CD2).text:00010CCA ADDR4, PC ; _ZSt4cout_ptr.text:00010CCC LDRR4, [R4] ; std::cout.text:00010CCE ADDR1, PC; "Initializing ".text:00010CD0 MOVS R0, R4; this.text:00010CD2 BL_ZNSo14_M_put_nowidenEPKc ; std::ostream::_M_put_nowiden(char const*).text:00010CD6 LDRR1, [R5].text:00010CD8 MOVS R0, R4.text:00010CDA BL_ZNSt4priv9__put_numIcSt11char_traitsIcElEERSt13basic_ostreamIT_T0_ES7_T1_ ; std::priv::__put_num<char,std::char_traits<char>,long>(std::basic_ostream<char,std::char_traits<char>> &,long).text:00010CDE BL_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ ; std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &).text:00010CE2 MOVS R0, R5.text:00010CE4 POP{R3-R5,PC}

在堆上和在栈上分配的对象的不同在于分配空间的地址,一个在堆上分配,一个在栈上分配。

0x04

这个例子我们在Test.cpp构造函数中初始化了一个对象。

Test.cpp

#include "Test.h"#include <iostream>using namespace std;// 不带参数的构造函数称为默认构造函数Test::Test():objB(30){num_ = 0;cout<<"Initializing Default"<<endl;}Test::Test(int num):objB(30){num_ = num;cout<<"Initializing "<<num_<<endl;}Test::~Test(){cout<<"Destroy "<<num_<<endl;}void Test::Display(){cout<<"num="<<num_<<endl;}

Test.h

#ifndef _TEST_H_#define _TEST_H_#include "ObjectB.h"class Test{public:// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的// 默认的构造函数Test();Test(int num);void Display();~Test();private:int num_;ObjectB objB;};#endif // _TEST_H_

其中的类ObjectB如下,ObjectB.h:

#ifndef _OBJECTB_H_#define _OBJECTB_H_class ObjectB{public:// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的// 默认的构造函数ObjectB(int objb);~ObjectB();private:int objb_;};#endif

ObjectB.cpp:

#include "ObjectB.h"#include <iostream>using namespace std;// 不带参数的构造函数称为默认构造函数ObjectB::ObjectB(int objb) : objb_(objb){cout<<"ObjectB ..."<<endl;}ObjectB::~ObjectB(){cout<<"~ObjectB ..."<<endl;}

接着我们来这段C++代码的汇编形式。

.text:00010A4C EXPORT Java_com_example_ndkreverse4_Lesson4_main.text:00010A4C Java_com_example_ndkreverse4_Lesson4_main.text:00010A4C.text:00010A4C var_1C= -0x1C.text:00010A4C var_14= -0x14.text:00010A4C.text:00010A4C PUSH {R4,R5,LR}.text:00010A4E LDRR4, =(__stack_chk_guard_ptr - 0x10A58).text:00010A50 SUBSP, SP, #0x14 ;开辟堆栈用于存储对象Test.text:00010A52 ADDR5, SP, #0x20+var_1C ;堆栈首地址加4赋值给R5.text:00010A54 ADDR4, PC ; __stack_chk_guard_ptr.text:00010A56 LDRR4, [R4] ; __stack_chk_guard.text:00010A58 MOVS R1, #0xA ;R1为10.text:00010A5A LDRR3, [R4].text:00010A5C MOVS R0, R5;堆栈首地址加4,即this指针.text:00010A5E STRR3, [SP,#0x20+var_14].text:00010A60 BL_ZN4TestC2Ei ; Test::Test(int) 两个参数R0为this指针,R1为10.text:00010A64 MOVS R0, R5.text:00010A66 BL_ZN4Test7DisplayEv ; Test::Display(void).text:00010A6A MOVS R0, R5; this.text:00010A6C BL_ZN4TestD2Ev ; Test::~Test().text:00010A70 LDRR2, [SP,#0x20+var_14].text:00010A72 LDRR3, [R4].text:00010A74 CMPR2, R3.text:00010A76 BEQloc_10A7C.text:00010A78 BLj_j___stack_chk_fail.text:00010A7C ; ---------------------------------------------------------------------------.text:00010A7C.text:00010A7C loc_10A7C ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+2Aj.text:00010A7C ADDSP, SP, #0x14.text:00010A7E POP{R4,R5,PC}

这个和上面的例子主要的区别在于调用构造函数的初始化,在我们这个例子中,构造函数初始化时会初始化一个对象ObjectB。

.text:00010D74 ; Test::Test(int).text:00010D74 EXPORT _ZN4TestC2Ei.text:00010D74 _ZN4TestC2Ei ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+14p.text:00010D74 PUSH {R3-R5,LR}.text:00010D76 MOVS R5, R0;堆栈的地址赋值给R5.text:00010D78 MOVS R4, R1;10赋值给R4.text:00010D7A ADDS R0, #4; this 堆栈地址加4,这个空间是要存储ObjectB的.text:00010D7C MOVS R1, #0x1E ; int 初始化值30.text:00010D7E BL_ZN7ObjectBC2Ei ; ObjectB::ObjectB(int) .text:00010D82 STRR4, [R5] ;把10放在堆栈的地址所指向的空间中.text:00010D84 LDRR4, =(_ZSt4cout_ptr - 0x10D8C).text:00010D86 LDRR1, =(aInitializing - 0x10D90).text:00010D88 ADDR4, PC ; _ZSt4cout_ptr.text:00010D8A LDRR4, [R4] ; std::cout.text:00010D8C ADDR1, PC; "Initializing ".text:00010D8E MOVS R0, R4; this.text:00010D90 BL_ZNSo14_M_put_nowidenEPKc ; std::ostream::_M_put_nowiden(char const*).text:00010D94 LDRR1, [R5].text:00010D96 MOVS R0, R4.text:00010D98 BL_ZNSt4priv9__put_numIcSt11char_traitsIcElEERSt13basic_ostreamIT_T0_ES7_T1_ ; std::priv::__put_num<char,std::char_traits<char>,long>(std::basic_ostream<char,std::char_traits<char>> &,long).text:00010D9C BL_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ ; std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &).text:00010DA0 MOVS R0, R5.text:00010DA2 POP{R3-R5,PC}

调用了ObjectB的构造函数,传递的两个参数一个是30,一个是堆栈地址+4,也就是要存储初始化值30的地址空间。

.text:00010E0C EXPORT _ZN7ObjectBC2Ei.text:00010E0C _ZN7ObjectBC2Ei; CODE XREF: Test::Test(void)+8p.text:00010E0C ; Test::Test(int)+Ap.text:00010E0C PUSH {R3-R5,LR}.text:00010E0E MOVS R5, R0;堆栈的地址加4赋值给R5.text:00010E10 LDRR4, =(_ZSt4cout_ptr - 0x10E1A).text:00010E12 STRR1, [R0] ;把30放在堆栈的地址加4所指向的空间中.text:00010E14 LDRR1, =(aObjectb___ - 0x10E1E).text:00010E16 ADDR4, PC ; _ZSt4cout_ptr.text:00010E18 LDRR4, [R4] ; std::cout.text:00010E1A ADDR1, PC; "ObjectB ...".text:00010E1C MOVS R0, R4; this.text:00010E1E BL_ZNSo14_M_put_nowidenEPKc ; std::ostream::_M_put_nowiden(char const*).text:00010E22 MOVS R0, R4.text:00010E24 BL_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ ; std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &).text:00010E28 MOVS R0, R5.text:00010E2A POP{R3-R5,PC}

0x05

如果一个类没有构造函数,在C++层我们总是理解为会有一个默认的构造函数,但是在汇编层次是不会有这个。

我们再一次改造Test.h:

#ifndef _TEST_H_#define _TEST_H_#include "ObjectB.h"class Test{public:// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的// 默认的构造函数void Display();~Test();private:int num_;};#endif // _TEST_H_

Test.cpp:

#include "Test.h"#include <iostream>using namespace std;Test::~Test(){cout<<"Destroy "<<num_<<endl;}void Test::Display(){cout<<"num="<<num_<<endl;}

Lesson4.cpp:

#include "com_example_ndkreverse4_Lesson4.h"#include "Test.h"#include <android/log.h>#define LOG_TAG "lesson4"#define ALOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))JNIEXPORT void JNICALL Java_com_example_ndkreverse4_Lesson4_main(JNIEnv * env, jobject jobject) {Test a2;a2.Display();}

汇编代码如下:

.text:000109C0 Java_com_example_ndkreverse4_Lesson4_main.text:000109C0.text:000109C0 var_14= -0x14.text:000109C0.text:000109C0 PUSH {R0-R2,R4,R5,LR}.text:000109C2 LDRR4, =(__stack_chk_guard_ptr - 0x109CA).text:000109C4 MOVR0, SP.text:000109C6 ADDR4, PC ; __stack_chk_guard_ptr.text:000109C8 LDRR4, [R4] ; __stack_chk_guard.text:000109CA LDRR3, [R4].text:000109CC STRR3, [SP,#0x18+var_14].text:000109CE BL_ZN4Test7DisplayEv ; Test::Display(void).text:000109D2 MOVR0, SP; this.text:000109D4 BL_ZN4TestD2Ev ; Test::~Test().text:000109D8 LDRR2, [SP,#0x18+var_14].text:000109DA LDRR3, [R4].text:000109DC CMPR2, R3.text:000109DE BEQlocret_109E4.text:000109E0 BLj_j___stack_chk_fail

我们看到这里没有默认的构造函数。

那么如果一个类没有构造函数,什么时候编译器会为这个类生产默认的构造函数呢?有三种情况:

1、父类有构造函数

2、本类的成员对象有构造函数,就像如果我们需要在Test类中要构造ObjectB类。

3、需要构造虚函数表

0x06

如果生成的对象是一个数组,并且析构对象数组,那么汇编代码是怎么处理的呢?

我们先来看C++代码,我们依然是修改Test.cpp,Test.h,Lesson4.cpp:

Test.cpp:

#include "Test.h"#include <iostream>using namespace std;// 不带参数的构造函数称为默认构造函数Test::Test(){num_ = 0;cout<<"Initializing Default"<<endl;}Test::Test(int num){num_ = num;cout<<"Initializing "<<num_<<endl;}Test::~Test(){cout<<"Destroy "<<num_<<endl;}void Test::Display(){cout<<"num="<<num_<<endl;}

Test.h:

#ifndef _TEST_H_#define _TEST_H_#include "ObjectB.h"class Test{public:// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的// 默认的构造函数Test();Test(int num);void Display();~Test();private:int num_;//ObjectB objB;};#endif // _TEST_H_

Lesson4.cpp:

#include "com_example_ndkreverse4_Lesson4.h"#include "Test.h"#include <android/log.h>#define LOG_TAG "lesson4"#define ALOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))JNIEXPORT void JNICALL Java_com_example_ndkreverse4_Lesson4_main(JNIEnv * env, jobject jobject) {//Test a2;//a2.Display();Test* a3 = new Test[5];delete[] a3;}

对应的汇编代码:

.text:00010A4C EXPORT Java_com_example_ndkreverse4_Lesson4_main.text:00010A4C Java_com_example_ndkreverse4_Lesson4_main.text:00010A4C PUSH {R3-R7,LR}.text:00010A4E MOVS R0, #0x1C ; unsigned int 生成一个长度为28个字节的堆空间,注意5乘以int四个字节,实际上对象大小只占20个字节.text:00010A50 BL_Znaj ; operator new[](uint).text:00010A54 MOVS R3, #4;R3被赋值为4.text:00010A56 MOVS R6, R0;R6被赋值为堆空间的首地址.text:00010A58 STRR3, [R0] ;把4赋值给堆空间的首地址.text:00010A5A ADDS R6, #8;R6被赋值为堆空间的首地址+8.text:00010A5C MOVS R3, #5;R3被赋值为5.text:00010A5E MOVS R7, R0;R7被赋值为堆空间的首地址.text:00010A60 MOVS R5, R0;R5被赋值为堆空间的首地址.text:00010A62 MOVS R4, R6;R4被赋值为堆空间的首地址+8.text:00010A64 STRR3, [R0,#4];把5赋值给堆空间的首地址+4.text:00010A66 ADDS R7, #0x1C ;R7被赋值为堆空间的首地址+0x1C,也就是分配的堆空间的最后的位置.text:00010A68.text:00010A68 loc_10A68 ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+26j.text:00010A68 MOVS R0, R4; this R0被赋值为堆空间的首地址+8.text:00010A6A ADDS R4, #4;R4被赋值为堆空间的首地址+12.text:00010A6C BL_ZN4TestC2Ev ; Test::Test(void) 进入构造函数.text:00010A70 CMPR4, R7;看看R4和R7是否相等,相等表示已经初始化完了5个对象.text:00010A72 BNEloc_10A68 ;如果不相等,就跳转到loc_10A68继续执行初始化操作.text:00010A74 CMPR6, #0;否则,执行完初始化后,R6的值与0比较,R6的值目前为堆空间的首地址+8.text:00010A76 BEQlocret_10A92 ;如果相等,就直接退出.text:00010A78 LDRR3, [R5,#4];否则取出堆空间的首地址+4的内容,此时为5,赋值给R3.text:00010A7A LSLS R4, R3, #2;然后R3乘以4,得到20再赋值给R4.text:00010A7C ADDS R4, R6, R4;R4被赋值为堆空间的首地址+8+20,也就是分配的28个字节的堆空间的末地址.text:00010A7E.text:00010A7E loc_10A7E ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+3Ej.text:00010A7E CMPR4, R6;比较R4和R6的值,R4是堆空间的首地址+28,R6的值目前为堆空间的首地址+8.text:00010A80 BEQloc_10A8C ;如果相等,就退出,说明所有对象都析构了.text:00010A82 SUBS R4, #4;否则,R4是堆空间的首地址+28-4,这段代码的含义是从后往前析构数组中的对象.text:00010A84 MOVS R0, R4; this R0指向了要析构的对象.text:00010A86 BL_ZN4TestD2Ev ; Test::~Test().text:00010A8A B loc_10A7E ;跳转到loc_10A7E.text:00010A8C ; ---------------------------------------------------------------------------.text:00010A8C.text:00010A8C loc_10A8C ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+34j.text:00010A8C MOVS R0, R5; void * R5是堆空间的首地址.text:00010A8E BL_ZdaPv; operator delete[](void *) 删除堆空间.text:00010A92.text:00010A92 locret_10A92 ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+2Aj.text:00010A92 POP{R3-R7,PC}

.text:00010D50 ; _DWORD Test::Test(Test *__hidden this).text:00010D50 EXPORT _ZN4TestC2Ev.text:00010D50 _ZN4TestC2Ev ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+20p.text:00010D50 PUSH {R3-R5,LR}.text:00010D52 MOVS R3, #0;R3被初始化为0.text:00010D54 MOVS R5, R0;R0为堆空间的首地址+8.text:00010D56 LDRR4, =(_ZSt4cout_ptr - 0x10D60).text:00010D58 LDRR1, =(aInitializingDe - 0x10D64).text:00010D5A STRR3, [R0] ;堆空间的首地址+8 被初始化为0.text:00010D5C ADDR4, PC ; _ZSt4cout_ptr.text:00010D5E LDRR4, [R4] ; std::cout.text:00010D60 ADDR1, PC; "Initializing Default".text:00010D62 MOVS R0, R4; this.text:00010D64 BL_ZNSo14_M_put_nowidenEPKc ; std::ostream::_M_put_nowiden(char const*).text:00010D68 MOVS R0, R4.text:00010D6A BL_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ ; std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &).text:00010D6E MOVS R0, R5.text:00010D70 POP{R3-R5,PC}

.text:00010DAC ; _DWORD __fastcall Test::~Test(Test *__hidden this).text:00010DAC EXPORT _ZN4TestD2Ev.text:00010DAC _ZN4TestD2Ev ; CODE XREF: Java_com_example_ndkreverse4_Lesson4_main+3Ap.text:00010DAC PUSH {R3-R5,LR}.text:00010DAE MOVS R5, R0;从后往前获取对象的地址.text:00010DB0 LDRR4, =(_ZSt4cout_ptr - 0x10DB8).text:00010DB2 LDRR1, =(aDestroy - 0x10DBC).text:00010DB4 ADDR4, PC ; _ZSt4cout_ptr.text:00010DB6 LDRR4, [R4] ; std::cout.text:00010DB8 ADDR1, PC; "Destroy ".text:00010DBA MOVS R0, R4; this.text:00010DBC BL_ZNSo14_M_put_nowidenEPKc ; std::ostream::_M_put_nowiden(char const*).text:00010DC0 LDRR1, [R5] ;从对象的地址中取出数据.text:00010DC2 MOVS R0, R4.text:00010DC4 BL_ZNSt4priv9__put_numIcSt11char_traitsIcElEERSt13basic_ostreamIT_T0_ES7_T1_ ; std::priv::__put_num<char,std::char_traits<char>,long>(std::basic_ostream<char,std::char_traits<char>> &,long).text:00010DC8 BL_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ ; std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &).text:00010DCC MOVS R0, R5.text:00010DCE POP{R3-R5,PC}

我们看到在堆空间分配了28个字节,前四个字节存放的是4,之后四个字节存放的5,也就是数组中对象的个数,最后20个字节存放的是5个对象。构造函数从第一个对象构造到第五个对象。析构函数从第五个对象析构到第一个对象。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。