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;
}
下面我们使用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 LDR R4, =(__stack_chk_guard_ptr - 0x109BE)
.text:000109B8 MOVS R1, #0xA ;初始化值为10
.text:000109BA ADD R4, PC ; __stack_chk_guard_ptr
.text:000109BC LDR R4, [R4] ; __stack_chk_guard
.text:000109BE MOV R0, SP ;堆栈的地址赋值给R0
.text:000109C0 LDR R3, [R4] ;堆栈检查的信息
.text:000109C2 STR R3, [SP,#0x18+var_14] ;把堆栈检查的信息入栈
.text:000109C4 BL _ZN4TestC2Ei ; Test::Test(int) ;此时R0为SP的地址,R1为0xA(10),调用构造函数
.text:000109C8 MOV R0, SP ;把sp的地址赋值给R0
.text:000109CA BL _ZN4Test7DisplayEv ; Test::Display(void) ;调用Display方法
.text:000109CE MOV R0, SP ; this 把sp的的地址赋值给R0
.text:000109D0 BL _ZN4TestD2Ev ; Test::~Test() ;调用析构函数
.text:000109D4 LDR R2, [SP,#0x18+var_14]
.text:000109D6 LDR R3, [R4]
.text:000109D8 CMP R2, R3
.text:000109DA BEQ locret_109E0
.text:000109DC BL j_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 LDR R4, =(_ZSt4cout_ptr - 0x10CDE)
.text:00010CD6 STR R1, [R0] ;把初始化值10赋值压入堆栈,R0指向的地址就是刚刚分配的堆栈的地址
.text:00010CD8 LDR R1, =(aInitializing - 0x10CE2)
.text:00010CDA ADD R4, PC ; _ZSt4cout_ptr
.text:00010CDC LDR R4, [R4] ; std::cout
.text:00010CDE ADD R1, 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 LDR R1, [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_co