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
{
public:
   Test();
   Test(int num);
   Test(const Test& other);
   void Display();
   ~Test();
public:
   int num_;
};
#endif// _TEST_H_

Test.cpp:

#include"Test.h"

Test::Test() : num_(0)
{
  //num_ = 0;
	ALOGD("Initializing Default\n");
}
Test::Test(int num) : num_(num)
{
   //num_ = num;
	ALOGD("Initializing:%d\n", num_);
}
Test::Test(const Test& other) : num_(other.num_)
{
   //num_ = other.num_;
	ALOGD("Initializing with other:%d\n", num_);
}
Test::~Test()
{
	ALOGD("Destroy:%d\n", num_);
}
void Test::Display()
{
	ALOGD("num=:%d\n", num_);
}

Lesson5.cpp:

#include "com_example_ndkreverse5_Lesson5.h"
#include "Test.h"

Test TestFun(Test t)
{
   t.num_=12;
   return t;
}
Test& TestFun2(Test t)
{
   return t;
}
Test TestFun3(Test& t)
{
   return t;
}
Test& TestFun4(Test& t)
{
   return t;
}

JNIEXPORT void JNICALL Java_com_example_ndkreverse5_Lesson5_main
  (JNIEnv * env, jobject jobject) {
	Test t(10);
	Test t2 = TestFun(t);
	ALOGD("........\n");
}

0x02

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

.text:00001010                 EXPORT Java_com_example_ndkreverse5_Lesson5_main
.text:00001010 Java_com_example_ndkreverse5_Lesson5_main
.text:00001010
.text:00001010 var_C           = -0xC
.text:00001010 var_8           = -8
.text:00001010
.text:00001010                 PUSH    {R4-R7,LR}
.text:00001012                 LDR     R4, =(__stack_chk_guard_ptr - 0x101C)
.text:00001014                 SUB     SP, SP, #0x14                 ;开辟20个字节的堆栈空间,sp指向堆栈的首地址
.text:00001016                 ADD     R6, SP, #0x14+var_C           ;堆栈的首地址+8赋值给R6
.text:00001018                 ADD     R4, PC ; __stack_chk_guard_ptr
.text:0000101A                 LDR     R4, [R4] ; __stack_chk_guard
.text:0000101C                 MOVS    R1, #0xA                      ;R1为初始化值10
.text:0000101E                 LDR     R3, [R4]
.text:00001020                 MOV     R0, SP                        ;R0被赋值为堆栈的首地址
.text:00001022                 STR     R3, [SP,#0x14+var_8]          ;把栈保护信息的值存入堆栈的首地址+0xC指向的空间
.text:00001024                 ADD     R7, SP, #4                    ;R7被赋值为堆栈的首地址+4
.text:00001026                 BL      _ZN4TestC2Ei    ; Test::Test(int) ;R0,R1是传入构造函数的两个参数,调用构造函数
.text:0000102A                 MOV     R1, SP                        ;R1被赋值为堆栈的首地址
.text:0000102C                 MOVS    R0, R6                        ;R0被赋值为堆栈的首地址+8
.text:0000102E                 BL      _ZN4TestC2ERKS_ ; Test::Test(Test const&) ;第一次调用拷贝构造函数
.text:00001032                 MOVS    R1, R6                        ;R1被赋值为堆栈的首地址+8
.text:00001034                 MOVS    R0, R7                        ;R0被赋值为堆栈的首地址+4
.text:00001036                 BL      _Z7TestFun4Test ; TestFun(Test) ;调用了TestFun函数
.text:0000103A                 MOVS    R0, R6          ; this        ;R0被赋值为堆栈的首地址+8
.text:0000103C                 BL      _ZN4TestD2Ev    ; Test::~Test() ;第一次调用析构函数
.text:00001040                 LDR     R1, =(aLesson5 - 0x104C)
.text:00001042                 LDR     R2, =(a________ - 0x104A)
.text:00001044                 MOVS    R0, #3
.text:00001046                 ADD     R2, PC          ; "........\n"
.text:00001048                 ADD     R1, PC          ; "lesson5"
.text:0000104A                 BL      j_j___android_log_print
.text:0000104E                 MOVS    R0, R7          ; this ;R0被赋值为堆栈的首地址+4
.text:00001050                 BL      _ZN4TestD2Ev    ; Test::~Test() ;第二次调用构造函数
.text:00001054                 MOV     R0, SP          ; this ;R0被赋值为堆栈的首地址
.text:00001056                 BL      _ZN4TestD2Ev    ; Test::~Test()
.text:0000105A                 LDR     R2, [SP,#0x14+var_8]
.text:0000105C                 LDR     R3, [R4]
.text:0000105E                 CMP     R2, R3
.text:00001060                 BEQ     loc_1066
.text:00001062                 BL      j_j___stack_chk_fail
.text:00001066 ; ---------------------------------------------------------------------------
.text:00001066
.text:00001066 loc_1066                                ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+50j
.text:00001066                 ADD     SP, SP, #0x14
.text:00001068                 POP     {R4-R7,PC}

调用构造函数:

.text:0000109C ; Test::Test(int)
.text:0000109C                 EXPORT _ZN4TestC2Ei
.text:0000109C _ZN4TestC2Ei                            ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+16p
.text:0000109C                 PUSH    {R4,LR}
.text:0000109E                 MOVS    R4, R0        ;R4被赋值为堆栈的首地址
.text:000010A0                 MOVS    R3, R1        ;R3被赋值为初始化值10
.text:000010A2                 STR     R1, [R4]      ;把10赋值给堆栈的首地址所指向的内存单元
.text:000010A4                 LDR     R2, =(aInitializingD - 0x10B0)
.text:000010A6                 LDR     R1, =(aLesson5 - 0x10AE)
.text:000010A8                 MOVS    R0, #3
.text:000010AA                 ADD     R1, PC          ; "lesson5"
.text:000010AC                 ADD     R2, PC          ; "Initializing:%d\n"
.text:000010AE                 BL      j_j___android_log_print
.text:000010B2                 MOVS    R0, R4
.text:000010B4                 POP     {R4,PC}

第一次调用拷贝构造函数:

.text:000010C0 ; Test::Test(Test const&)
.text:000010C0                 EXPORT _ZN4TestC2ERKS_
.text:000010C0 _ZN4TestC2ERKS_                         ; CODE XREF: TestFun(Test)+8p
.text:000010C0                                         ; TestFun3(Test &)+4p ...
.text:000010C0                 PUSH    {R4,LR}
.text:000010C2                 MOVS    R4, R0          ;R4被赋值为堆栈的首地址+8
.text:000010C4                 LDR     R3, [R1]        ;把堆栈首地址的内容也就是初始化值10赋值给R3
.text:000010C6                 LDR     R2, =(aInitializingWi - 0x10D0)
.text:000010C8                 LDR     R1, =(aLesson5 - 0x10D2)
.text:000010CA                 STR     R3, [R0]        ;把10赋值给堆栈的首地址+8所执行的内存单元
.text:000010CC                 ADD     R2, PC          ; "Initializing with other:%d\n"
.text:000010CE                 ADD     R1, PC          ; "lesson5"
.text:000010D0                 MOVS    R0, #3
.text:000010D2                 BL      j_j___android_log_print
.text:000010D6                 MOVS    R0, R4
.text:000010D8                 POP     {R4,PC}

调用Testfun函数:

.text:00000FF0 ; TestFun(Test)
.text:00000FF0                 EXPORT _Z7TestFun4Test
.text:00000FF0 _Z7TestFun4Test                         ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+26p
.text:00000FF0                 MOVS    R3, #0xC       ;R3被赋值为12
.text:00000FF2                 PUSH    {R4,LR}
.text:00000FF4                 MOVS    R4, R0         ;R4被赋值为堆栈的首地址+4
.text:00000FF6                 STR     R3, [R1]       ;12被赋值为堆栈的首地址+8所指向的内存单元
.text:00000FF8                 BL      _ZN4TestC2ERKS_ ; Test::Test(Test const&) ;第二次调用拷贝构造函数,R0为堆栈的首地址+4,R1为堆栈的首地址+8
.text:00000FFC                 MOVS    R0, R4
.text:00000FFE                 POP     {R4,PC}

第二次调用拷贝构造函数:

.text:000010C0 ; Test::Test(Test const&)
.text:000010C0                 EXPORT _ZN4TestC2ERKS_
.text:000010C0 _ZN4TestC2ERKS_                         ; CODE XREF: TestFun(Test)+8p
.text:000010C0                                         ; TestFun3(Test &)+4p ...
.text:000010C0                 PUSH    {R4,LR}
.text:000010C2                 MOVS    R4, R0          ;R4被赋值为堆栈的首地址+4
.text:000010C4                 LDR     R3, [R1]        ;把堆栈首地址+8的内容也就是12赋值给R3
.text:000010C6                 LDR     R2, =(aInitializingWi - 0x10D0)
.text:000010C8                 LDR     R1, =(aLesson5 - 0x10D2)
.text:000010CA                 STR     R3, [R0]        ;把12赋值给堆栈的首地址+4所执行的内存单元
.text:000010CC                 ADD     R2, PC          ; "Initializing with other:%d\n"
.text:000010CE                 ADD     R1, PC          ; "lesson5"
.text:000010D0                 MOVS    R0, #3
.text:000010D2                 BL      j_j___android_log_print
.text:000010D6                 MOVS    R0, R4         ;把对象的地址(堆栈的首地址+4)返回去
.text:000010D8                 POP     {R4,PC}

第一次调用析构函数:

.text:000010E4 ; _DWORD __fastcall Test::~Test(Test *__hidden this)
.text:000010E4                 EXPORT _ZN4TestD2Ev
.text:000010E4 _ZN4TestD2Ev                            ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+2Cp
.text:000010E4                                         ; Java_com_example_ndkreverse5_Lesson5_main+40p ...
.text:000010E4                 PUSH    {R4,LR}
.text:000010E6                 MOVS    R4, R0          ;R4被赋值为堆栈的首地址+8
.text:000010E8                 LDR     R1, =(aLesson5 - 0x10F2)
.text:000010EA                 LDR     R2, =(aDestroyD - 0x10F4)
.text:000010EC                 LDR     R3, [R4]        ;堆栈的首地址+8所指向的内存单元值12赋值给R3
.text:000010EE                 ADD     R1, PC          ; "lesson5"
.text:000010F0                 ADD     R2, PC          ; "Destroy:%d\n"
.text:000010F2                 MOVS    R0, #3
.text:000010F4                 BL      j_j___android_log_print
.text:000010F8                 MOVS    R0, R4
.text:000010FA                 POP     {R4,PC}

总结:一共生成了三个对象,第一个对象存储在堆栈首地址所指向的内存中,第二个对象存储在堆栈首地址+8所指向的内存中,第三个对象存储在堆栈的首地址+4所指向的内存中。析构时,当从Testfun函数返回时,首先析构了第二个对象,从main函数返回时,先析构了第三个对象,最后析构了第一个对象。

0x03
    如果Lesson5.cpp,Java_com_example_ndkreverse5_Lesson5_main调用的函数变成如下的形式:

#include "com_example_ndkreverse5_Lesson5.h"
#include "Test.h"

Test TestFun(Test t)
{
   t.num_=12;
   return t;
}
Test& TestFun2(Test t)
{
   return t;
}
Test TestFun3(Test& t)
{
   return t;
}
Test& TestFun4(Test& t)
{
   return t;
}

JNIEXPORT void JNICALL Java_com_example_ndkreverse5_Lesson5_main
  (JNIEnv * env, jobject jobject) {
	Test t(10);
	TestFun(t);
	ALOGD("........\n");
}

那么打印出的log为:

D/lesson5 ( 4437): Initializing:10
D/lesson5 ( 4437): Initializing with other:10
D/lesson5 ( 4437): Initializing with other:12
D/lesson5 ( 4437): Destroy:12
D/lesson5 ( 4437): Destroy:12
D/lesson5 ( 4437): ........
D/lesson5 ( 4437): Destroy:10

汇编代码有什么不同呢?

.text:00001010                 EXPORT Java_com_example_ndkreverse5_Lesson5_main
.text:00001010 Java_com_example_ndkreverse5_Lesson5_main
.text:00001010
.text:00001010 var_10          = -0x10
.text:00001010 var_8           = -8
.text:00001010
.text:00001010                 PUSH    {R4-R7,LR}
.text:00001012                 LDR     R4, =(__stack_chk_guard_ptr - 0x101C)
.text:00001014                 SUB     SP, SP, #0x14
.text:00001016                 ADD     R6, SP, #0x14+var_10
.text:00001018                 ADD     R4, PC ; __stack_chk_guard_ptr
.text:0000101A                 LDR     R4, [R4] ; __stack_chk_guard
.text:0000101C                 MOVS    R1, #0xA
.text:0000101E                 LDR     R3, [R4]
.text:00001020                 MOV     R0, SP
.text:00001022                 STR     R3, [SP,#0x14+var_8]
.text:00001024                 ADD     R7, SP, #8
.text:00001026                 BL      _ZN4TestC2Ei    ; Test::Test(int)
.text:0000102A                 MOV     R1, SP
.text:0000102C                 MOVS    R0, R6
.text:0000102E                 BL      _ZN4TestC2ERKS_ ; Test::Test(Test const&)
.text:00001032                 MOVS    R1, R6
.text:00001034                 MOVS    R0, R7
.text:00001036                 BL      _Z7TestFun4Test ; TestFun(Test)
.text:0000103A                 MOVS    R0, R7          ; this
.text:0000103C                 BL      _ZN4TestD2Ev    ; Test::~Test()
.text:00001040                 MOVS    R0, R6          ; this
.text:00001042                 BL      _ZN4TestD2Ev    ; Test::~Test()
.text:00001046                 LDR     R1, =(aLesson5 - 0x1052)
.text:00001048                 LDR     R2, =(a________ - 0x1050)
.text:0000104A                 MOVS    R0, #3
.text:0000104C                 ADD     R2, PC          ; "........\n"
.text:0000104E                 ADD     R1, PC          ; "lesson5"
.text:00001050                 BL      j_j___android_log_print
.text:00001054                 MOV     R0, SP          ; this
.text:00001056                 BL      _ZN4TestD2Ev    ; Test::~Test()
.text:0000105A                 LDR     R2, [SP,#0x14+var_8]
.text:0000105C                 LDR     R3, [R4]
.text:0000105E                 CMP     R2, R3
.text:00001060                 BEQ     loc_1066
.text:00001062                 BL      j_j___stack_chk_fail
.text:00001066 ; ---------------------------------------------------------------------------
.text:00001066
.text:00001066 loc_1066                                ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+50j
.text:00001066                 ADD     SP, SP, #0x14
.text:00001068                 POP     {R4-R7,PC}

一切都一样,唯一不同的是由于没有TestFun(t);没有接管的对象,所以从TestFun函数执行完,这两个对象就析构了。

0x04

如果Lesson5.cpp,Java_com_example_ndkreverse5_Lesson5_main调用的函数变成如下的形式:

#include "com_example_ndkreverse5_Lesson5.h"
#include "Test.h"

Test TestFun(Test t)
{
   t.num_=12;
   return t;
}
Test& TestFun2(Test t)
{
   return t;
}
Test TestFun3(Test& t)
{
   return t;
}
Test& TestFun4(Test& t)
{
   return t;
}

JNIEXPORT void JNICALL Java_com_example_ndkreverse5_Lesson5_main
  (JNIEnv * env, jobject jobject) {
	Test t(10);
	Test t2 = TestFun2(t);
	ALOGD("........\n");
}

那么log输出如下:

D/lesson5 ( 4841): Initializing:10
D/lesson5 ( 4841): Initializing with other:10
D/lesson5 ( 4841): Initializing with other:10
D/lesson5 ( 4841): Destroy:10
D/lesson5 ( 4841): ........
D/lesson5 ( 4841): Destroy:10
D/lesson5 ( 4841): Destroy:10

汇编代码有什么不同呢?

.text:00001010                 EXPORT Java_com_example_ndkreverse5_Lesson5_main
.text:00001010 Java_com_example_ndkreverse5_Lesson5_main
.text:00001010
.text:00001010 var_24          = -0x24
.text:00001010 var_20          = -0x20
.text:00001010 var_1C          = -0x1C
.text:00001010
.text:00001010                 PUSH    {R4-R7,LR}
.text:00001012                 LDR     R4, =(__stack_chk_guard_ptr - 0x101C)
.text:00001014                 SUB     SP, SP, #0x14
.text:00001016                 ADD     R6, SP, #0x28+var_20
.text:00001018                 ADD     R4, PC ; __stack_chk_guard_ptr
.text:0000101A                 LDR     R4, [R4] ; __stack_chk_guard
.text:0000101C                 MOVS    R1, #0xA
.text:0000101E                 LDR     R3, [R4]
.text:00001020                 MOV     R0, SP
.text:00001022                 STR     R3, [SP,#0x28+var_1C]
.text:00001024                 BL      _ZN4TestC2Ei    ; Test::Test(int) ;在堆栈首地址处生成第一个对象
.text:00001028                 MOV     R1, SP
.text:0000102A                 MOVS    R0, R6
.text:0000102C                 BL      _ZN4TestC2ERKS_ ; Test::Test(Test const&) ;在堆栈首地址+8处生成第二个对象
.text:00001030                 MOVS    R0, R6
.text:00001032                 BL      _Z8TestFun24Test ; TestFun2(Test) ; 什么都没做
.text:00001036                 ADD     R7, SP, #0x28+var_24
.text:00001038                 MOVS    R1, R0
.text:0000103A                 MOVS    R0, R7
.text:0000103C                 BL      _ZN4TestC2ERKS_ ; Test::Test(Test const&) ;从TestFun2返回后在堆栈首地址+4处生成第三个对象
.text:00001040                 MOVS    R0, R6          ; this
.text:00001042                 BL      _ZN4TestD2Ev    ; Test::~Test() ;首先析构了第二个对象
.text:00001046                 LDR     R1, =(aLesson5 - 0x1052)
.text:00001048                 LDR     R2, =(a________ - 0x1050)
.text:0000104A                 MOVS    R0, #3
.text:0000104C                 ADD     R2, PC          ; "........\n" ;打印出......
.text:0000104E                 ADD     R1, PC          ; "lesson5"
.text:00001050                 BL      j_j___android_log_print
.text:00001054                 MOVS    R0, R7          ; this
.text:00001056                 BL      _ZN4TestD2Ev    ; Test::~Test() ;然后析构了第三个对象
.text:0000105A                 MOV     R0, SP          ; this
.text:0000105C                 BL      _ZN4TestD2Ev    ; Test::~Test() ;然后析构了第一个对象
.text:00001060                 LDR     R2, [SP,#0x28+var_1C]
.text:00001062                 LDR     R3, [R4]
.text:00001064                 CMP     R2, R3
.text:00001066                 BEQ     loc_106C
.text:00001068                 BL      j_j___stack_chk_fail
.text:0000106C ; ---------------------------------------------------------------------------
.text:0000106C
.text:0000106C loc_106C                                ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+56j
.text:0000106C                 ADD     SP, SP, #0x14
.text:0000106E                 POP     {R4-R7,PC}
.text:00001000 ; TestFun2(Test)
.text:00001000                 EXPORT _Z8TestFun24Test
.text:00001000 _Z8TestFun24Test                        ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+22p
.text:00001000                 BX      LR

在TestFun2中没有做任何操作,函数返回后再调用拷贝构造函数生成第二个对象。

0x05

如果Lesson5.cpp,Java_com_example_ndkreverse5_Lesson5_main调用的函数变成如下的形式:

#include "com_example_ndkreverse5_Lesson5.h"
#include "Test.h"

Test TestFun(Test t)
{
   t.num_=12;
   return t;
}
Test& TestFun2(Test t)
{
   return t;
}
Test TestFun3(Test& t)
{
   return t;
}
Test& TestFun4(Test& t)
{
   return t;
}

JNIEXPORT void JNICALL Java_com_example_ndkreverse5_Lesson5_main
  (JNIEnv * env, jobject jobject) {
	Test t(10);
	Test t2 = TestFun3(t);
	ALOGD("........\n");
}

那么log输出如下:

D/lesson5 ( 5255): Initializing:10
D/lesson5 ( 5255): Initializing with other:10
D/lesson5 ( 5255): ........
D/lesson5 ( 5255): Destroy:10
D/lesson5 ( 5255): Destroy:10

汇编代码有什么不同呢?

.text:00001010                 EXPORT Java_com_example_ndkreverse5_Lesson5_main
.text:00001010 Java_com_example_ndkreverse5_Lesson5_main
.text:00001010
.text:00001010 var_1C          = -0x1C
.text:00001010 var_18          = -0x18
.text:00001010 var_14          = -0x14
.text:00001010
.text:00001010                 PUSH    {R0-R6,LR}
.text:00001012                 LDR     R4, =(__stack_chk_guard_ptr - 0x101C)
.text:00001014                 ADD     R5, SP, #0x20+var_1C
.text:00001016                 MOVS    R1, #0xA
.text:00001018                 ADD     R4, PC ; __stack_chk_guard_ptr
.text:0000101A                 LDR     R4, [R4] ; __stack_chk_guard
.text:0000101C                 ADD     R6, SP, #0x20+var_18
.text:0000101E                 MOVS    R0, R5
.text:00001020                 LDR     R3, [R4]
.text:00001022                 STR     R3, [SP,#0x20+var_14]
.text:00001024                 BL      _ZN4TestC2Ei    ; Test::Test(int) ;构造了第一个对象
.text:00001028                 MOVS    R0, R6          ; Test *
.text:0000102A                 MOVS    R1, R5
.text:0000102C                 BL      _Z8TestFun3R4Test ; TestFun3(Test &) ;调用TestFun3
.text:00001030                 LDR     R1, =(aLesson5 - 0x103C)
.text:00001032                 LDR     R2, =(a________ - 0x103A)
.text:00001034                 MOVS    R0, #3
.text:00001036                 ADD     R2, PC          ; "........\n" ;输出......
.text:00001038                 ADD     R1, PC          ; "lesson5"
.text:0000103A                 BL      j_j___android_log_print
.text:0000103E                 MOVS    R0, R6          ; this
.text:00001040                 BL      _ZN4TestD2Ev    ; Test::~Test()  ;首先析构了第二个对象
.text:00001044                 MOVS    R0, R5          ; this
.text:00001046                 BL      _ZN4TestD2Ev    ; Test::~Test()  ;然后析构了第一个对象
.text:0000104A                 LDR     R2, [SP,#0x20+var_14]
.text:0000104C                 LDR     R3, [R4]
.text:0000104E                 CMP     R2, R3
.text:00001050                 BEQ     locret_1056
.text:00001052                 BL      j_j___stack_chk_fail
.text:00001056 ; ---------------------------------------------------------------------------
.text:00001056
.text:00001056 locret_1056                             ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+40j
.text:00001056                 POP     {R0-R6,PC}
.text:00001002 ; _DWORD __fastcall TestFun3(Test *)
.text:00001002                 EXPORT _Z8TestFun3R4Test
.text:00001002 _Z8TestFun3R4Test                       ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+1Cp
.text:00001002                 PUSH    {R4,LR}
.text:00001004                 MOVS    R4, R0
.text:00001006                 BL      _ZN4TestC2ERKS_ ; Test::Test(Test const&) ;利用拷贝构造函数生成了第二个对象
.text:0000100A                 MOVS    R0, R4
.text:0000100C                 POP     {R4,PC}

0x06

如果Lesson5.cpp,Java_com_example_ndkreverse5_Lesson5_main调用的函数变成如下的形式:

#include "com_example_ndkreverse5_Lesson5.h"
#include "Test.h"

Test TestFun(Test t)
{
   t.num_=12;
   return t;
}
Test& TestFun2(Test t)
{
   return t;
}
Test TestFun3(Test& t)
{
   return t;
}
Test& TestFun4(Test& t)
{
   return t;
}

JNIEXPORT void JNICALL Java_com_example_ndkreverse5_Lesson5_main
  (JNIEnv * env, jobject jobject) {
	Test t(10);
	Test t2 = TestFun4(t);
	ALOGD("........\n");
}

那么,输出的log如下:

D/lesson5 ( 5719): Initializing:10
D/lesson5 ( 5719): Initializing with other:10
D/lesson5 ( 5719): ........
D/lesson5 ( 5719): Destroy:10
D/lesson5 ( 5719): Destroy:10

对应的反汇编有什么不同呢?

.text:00001010                 EXPORT Java_com_example_ndkreverse5_Lesson5_main
.text:00001010 Java_com_example_ndkreverse5_Lesson5_main
.text:00001010
.text:00001010 var_1C          = -0x1C
.text:00001010 var_18          = -0x18
.text:00001010 var_14          = -0x14
.text:00001010
.text:00001010                 PUSH    {R0-R6,LR}
.text:00001012                 LDR     R4, =(__stack_chk_guard_ptr - 0x101C)
.text:00001014                 ADD     R5, SP, #0x20+var_1C
.text:00001016                 MOVS    R1, #0xA
.text:00001018                 ADD     R4, PC ; __stack_chk_guard_ptr
.text:0000101A                 LDR     R4, [R4] ; __stack_chk_guard
.text:0000101C                 MOVS    R0, R5
.text:0000101E                 LDR     R3, [R4]
.text:00001020                 ADD     R6, SP, #0x20+var_18
.text:00001022                 STR     R3, [SP,#0x20+var_14]
.text:00001024                 BL      _ZN4TestC2Ei    ; Test::Test(int) ;生成第一个对象
.text:00001028                 MOVS    R0, R5
.text:0000102A                 BL      _Z8TestFun4R4Test ; TestFun4(Test &)  ;调用TestFun4,是一个空实现
.text:0000102E                 MOVS    R1, R0
.text:00001030                 MOVS    R0, R6
.text:00001032                 BL      _ZN4TestC2ERKS_ ; Test::Test(Test const&) ;从TestFun4返回后,调用拷贝构造函数生成了第二个对象
.text:00001036                 LDR     R1, =(aLesson5 - 0x1042)
.text:00001038                 LDR     R2, =(a________ - 0x1040)
.text:0000103A                 MOVS    R0, #3
.text:0000103C                 ADD     R2, PC          ; "........\n" 输出......
.text:0000103E                 ADD     R1, PC          ; "lesson5"
.text:00001040                 BL      j_j___android_log_print
.text:00001044                 MOVS    R0, R6          ; this
.text:00001046                 BL      _ZN4TestD2Ev    ; Test::~Test() ;析构第二个对象
.text:0000104A                 MOVS    R0, R5          ; this
.text:0000104C                 BL      _ZN4TestD2Ev    ; Test::~Test() ;析构第一个对象
.text:00001050                 LDR     R2, [SP,#0x20+var_14]
.text:00001052                 LDR     R3, [R4]
.text:00001054                 CMP     R2, R3
.text:00001056                 BEQ     locret_105C
.text:00001058                 BL      j_j___stack_chk_fail
.text:0000105C ; ---------------------------------------------------------------------------
.text:0000105C
.text:0000105C locret_105C                             ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+46j
.text:0000105C                 POP     {R0-R6,PC}
.text:0000100E ; TestFun4(Test &)
.text:0000100E                 EXPORT _Z8TestFun4R4Test
.text:0000100E _Z8TestFun4R4Test                       ; CODE XREF: Java_com_example_ndkreverse5_Lesson5_main+1Ap
.text:0000100E                 BX      LR

main函数从TestFun4返回后,才会调用拷贝构造函数生成第二个对象。首选析构第二个对象,然后析构第一个对象。

时间: 2024-10-18 03:37:49

Android SO逆向-对象的拷贝构造函数的相关文章

C++类对象的拷贝构造函数分析

对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=100;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. #include <iostream>using namespace std;class CA{ public: CA(int b) { a=b; } void Show () { cout<<a<<endl; } private: int a;};int main(){

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__))

Item 12:完整地拷贝对象(拷贝构造函数、复制运算符) Effective C++笔记

Item 12: Copy all parts of an object 在一个成熟的面向对象的C++系统中,只有两种拷贝对象的方式:复制构造函数和赋值运算符, 不妨称他们为拷贝函数. 拷贝函数属于编译器默认生成的函数(参考:Item 5:那些被C++默默地声明和调用的函数), 默认的拷贝函数确实会完整地拷贝对象,但有时我们选择重载拷贝函数,问题就出在这里! 一个正确拷贝函数的实现是这样的: class Customer{ string name; public: Customer::Custo

拷贝构造函数和赋值运算符重载的区别

拷贝构造函数是用一个已存在的对象去构造一个不存在的对象(拷贝构造函数毕竟还是构造函数嘛),也就是初始化一个对象.而赋值运算符重载函数是用一个存在的对象去给另一个已存在并初始化过(即已经过构造函数的初始化了)的对象进行赋值. 它们定义上的区别,楼上的已经说过了. 比如:String s1("hello"),s2=s1;//拷贝构造函数Sring s1("hello"),s2;s1=s2;//赋值运算符重载以下情况都会调用拷贝构造函数:1.一个对象以值传递的方式传入函数

从一个例子讲解拷贝构造函数与return

1 #include "iostream" 2 using namespace std; 3 4 5 class Location 6 { 7 public: 8 Location(int xx = 0, int yy = 0) 9 { 10 X = xx; Y = yy; 11 cout << X << "," << Y << " Constructor Object." << end

C++构造函数和拷贝构造函数详解

构造函数.析构函数与赋值函数是每个类最基本的函数.它们太普通以致让人容易麻痹大意,其实这些貌似简单的函数就象没有顶盖的下水道那样危险. 每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数). 对于任意一个类A,如果不想编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如: A(void); // 缺省的无参数构造函数 A(const A &a); // 缺省的拷贝构造函数 ~A(void); // 缺省的析构函数 A & op

构造函数、拷贝构造函数、析构函数

构造函数:在对象被创建时利用特定的初始值构造对象,即初始化对象. 拷贝构造函数:用一个已经存在的对象去初始化另一个对象,这两个对象的类型应该是一样的. 格式: class 类名 { public: 类名(形参);                 //构造函数 类名(类名 &对象名);    //拷贝构造函数 ... }; 类名::类名(形参)            //构造函数的实现 { 函数体 } 类名::类(类名 &对象名)      //拷贝构造函数的实现 { 函数体 } 构造函数在

C++ 拷贝构造函数和重载赋值操作符相互调用分析 [转]

结论: 从面相对象编程的角度考虑,拷贝构造函数调用重载赋值操作符,重载赋值操作符调用拷贝构造函数的写法都是没有意义的.应该避免. Don't try to implement one of the copying functions in terms of the other. Instead, put common functionality in a third function that both call. ——Effective C++ Third Edition By Scott M

构造函数、初始化列表来初始化字段、析构函数、拷贝构造函数

#include <iostream> using namespace std; class Line { public: int getLength( void ); Line( int len ); // 简单的构造函数 Line( const Line &obj); // 拷贝构造函数 ~Line(); // 析构函数 private: int *ptr; }; // 成员函数定义,包括构造函数 Line::Line(int len) { cout << "