标签:
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 ...
下面我们使用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}总结:父类的成员位于低地址,子类的成员位于高地址,虚表指针位于对象的首地址,构造对象时,先构造父类,虚表指针指向父类的虚表,再构造子类,虚表指针指向子类的虚表。析构对象时,先析构子类,虚表指针指向子类的虚表,再析构父类,虚表指针指向父类的虚表。
标签:
原文地址:http://blog.csdn.net/jltxgcy/article/details/51217318