0x00
这一节主要分析一维数组、二维数组、数组指针和指针数组的汇编实现。
0x01
我们先直接看C++代码:
#include "com_example_ndkreverse3_Lesson3.h" #include <android/log.h> #define LOG_TAG "lesson3" #define ALOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) //参考http://blog.csdn.net/jltxgcy/article/details/17756391 JNIEXPORT void JNICALL Java_com_example_ndkreverse3_Lesson3_main (JNIEnv * env, jobject jobject) { int a[] = {1,2,3,4,5,6}; for(int i = 0; i< 6; i++) { ALOGD("a=%d\n", a[i]); } } JNIEXPORT void JNICALL Java_com_example_ndkreverse3_Lesson3_main1 (JNIEnv * env, jobject jobject) { int b[][3]={{1,2},{3,4,5}}; for(int i = 0; i< 2; i++) { for(int j = 0; j < 3; j++) { ALOGD("b=%d\n", b[i][j]); } } } JNIEXPORT void JNICALL Java_com_example_ndkreverse3_Lesson3_main2 (JNIEnv * env, jobject jobject) { char *d[] = {"12","345","6789"}; for(int i = 0; i< 3; i++) { for(int j = 0; j < 4; j++) { ALOGD("d=%d\n", *(*(d + i) + j)); } } } JNIEXPORT void JNICALL Java_com_example_ndkreverse3_Lesson3_main3 (JNIEnv * env, jobject jobject) { int a[] = {1,2,3,4,5,6}; int *p = a; for(int i = 0; i< 6; i++) { ALOGD("p=%d\n", *(p + i)); } } JNIEXPORT void JNICALL Java_com_example_ndkreverse3_Lesson3_main4 (JNIEnv * env, jobject jobject) { int b[][3]={{1,2},{3,4,5}}; int (*p)[3] = b; for(int i = 0; i< 2; i++) { for(int j = 0; j < 3; j++) { ALOGD("p=%d\n", *(*(p + i) + j)); } } }
0x01
下面我们用ida打开so,分别对不同函数的汇编形式的代码做出解释。
Java_com_example_ndkreverse3_Lesson3_main:
.text:00000EC8 EXPORT Java_com_example_ndkreverse3_Lesson3_main .text:00000EC8 Java_com_example_ndkreverse3_Lesson3_main .text:00000EC8 .text:00000EC8 var_2C = -0x2C .text:00000EC8 var_14 = -0x14 .text:00000EC8 .text:00000EC8 PUSH {R4-R6,LR} .text:00000ECA SUB SP, SP, #0x20 ;开发了0x20个地址空间作为存储堆栈的位置 .text:00000ECC ADD R4, SP, #0x30+var_2C ;R4赋值为SP+4 .text:00000ECE MOVS R1, R4 ;R1和R4的值一样 .text:00000ED0 LDR R2, =(__stack_chk_guard_ptr - 0xED6) ;位于.got段中__stack_chk_guard_ptr相对于下一条PC指令的偏移,取出来赋值给R2 .text:00000ED2 ADD R2, PC ; __stack_chk_guard_ptr ;R2中存放的是位于.got段中__stack_chk_guard_ptr的地址 .text:00000ED4 LDR R2, [R2] ; __stack_chk_guard ;取地址中的内容,我们看下面代码,这个地址是__stack_chk_guard,这个地址运行时分配的 .text:00000ED6 LDR R3, [R2] ;取这个地址中的内容,我在动态调试状态下得到的内容为6BCAA164 .text:00000ED8 STR R3, [SP,#0x30+var_14] ;把这个值6BCAA164存放在SP+0x1C的位置 .text:00000EDA LDR R3, =(unk_226C - 0xEE0) ;位于.rodata,unk_226C相对于下一条PC指令的偏移,取出来赋值给R3 .text:00000EDC ADD R3, PC ; unk_226C ;R3存放的是位于.rodata段中unk_226C的地址 .text:00000EDE LDMIA R3!, {R0,R5,R6} ;将R3地址堆栈中的内容依次赋值给R0,R5,R6 .text:00000EE0 STMIA R1!, {R0,R5,R6} ;将R0,R5,R6的值分配存放到R1所指向的堆栈中,也就是刚才SUB SP,SP,#0x20,然后再加4的堆栈 .text:00000EE2 LDMIA R3!, {R0,R5,R6} ;同理 .text:00000EE4 STMIA R1!, {R0,R5,R6} ;同理,这样堆栈中就初始化好了1,2,3,4,5,6。PS:堆栈中存放6的下一个地址中的数值就是0x6BCAA164 .text:00000EE6 MOVS R6, #0 ;R6初始化为9 .text:00000EE8 ADDS R5, R2, #0 ;R5初始化为R2,也就是__stack_chk_guard的地址 .text:00000EEA .text:00000EEA loc_EEA ; CODE XREF: Java_com_example_ndkreverse3_Lesson3_main+36j .text:00000EEA LDR R1, =(aLesson3 - 0xEF6) .text:00000EEC LDR R2, =(aAD - 0xEF8) .text:00000EEE LDR R3, [R4,R6] ;从堆栈中取数 .text:00000EF0 MOVS R0, #3 .text:00000EF2 ADD R1, PC ; "lesson3" .text:00000EF4 ADD R2, PC ; "a=%d\n" .text:00000EF6 ADDS R6, #4 ;为取下一个数做准备 .text:00000EF8 BL j_j___android_log_print .text:00000EFC CMP R6, #0x18 ;R6和0x18做比较 .text:00000EFE BNE loc_EEA ;如果不相等,就跳转到loc_EEA .text:00000F00 LDR R2, [SP,#0x30+var_14] ;否则取出原来存入堆栈中的内容,为0x6BCAA164 .text:00000F02 LDR R3, [R5] ;从R5地址中取出的值赋值给R3,刚才R5被赋值为__stack_chk_guard的地址 .text:00000F04 CMP R2, R3 ;比较R2和R3的值 .text:00000F06 BEQ loc_F0C ;如果相等,就跳转到loc_F0C .text:00000F08 BL j_j___stack_chk_fail ;否则堆栈检查失败 .text:00000F0C ; --------------------------------------------------------------------------- .text:00000F0C .text:00000F0C loc_F0C ; CODE XREF: Java_com_example_ndkreverse3_Lesson3_main+3Ej .text:00000F0C ADD SP, SP, #0x20 .text:00000F0E POP {R4-R6,PC}
.text:00000F10 off_F10 DCD __stack_chk_guard_ptr - 0xED6 .text:00000F10 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main+8r .text:00000F14 off_F14 DCD unk_226C - 0xEE0 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main+12r .text:00000F18 off_F18 DCD aLesson3 - 0xEF6 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main:loc_EEAr .text:00000F18 ; "lesson3" .text:00000F1C off_F1C DCD aAD - 0xEF8 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main+24r .text:00000F1C ; "a=%d\n"
.got:00003FAC __stack_chk_guard_ptr DCD __stack_chk_guard
.rodata:0000226C unk_226C DCB 1 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main+14o .rodata:0000226C ; Java_com_example_ndkreverse3_Lesson3_main+16o ... .rodata:0000226D DCB 0 .rodata:0000226E DCB 0 .rodata:0000226F DCB 0 .rodata:00002270 DCB 2 .rodata:00002271 DCB 0 .rodata:00002272 DCB 0 .rodata:00002273 DCB 0 .rodata:00002274 DCB 3 .rodata:00002275 DCB 0 .rodata:00002276 DCB 0 .rodata:00002277 DCB 0 .rodata:00002278 DCB 4 .rodata:00002279 DCB 0 .rodata:0000227A DCB 0 .rodata:0000227B DCB 0 .rodata:0000227C DCB 5 .rodata:0000227D DCB 0 .rodata:0000227E DCB 0 .rodata:0000227F DCB 0 .rodata:00002280 DCB 6 .rodata:00002281 DCB 0 .rodata:00002282 DCB 0 .rodata:00002283 DCB 0
我们可以看到首先开辟了堆栈来存储数组元素,数组元素的初始化值被放在了.rodata中。里面还有堆栈保护的代码。详情请参考代码中的注释。
Java_com_example_ndkreverse3_Lesson3_main1:
.text:00000F20 EXPORT Java_com_example_ndkreverse3_Lesson3_main1 .text:00000F20 Java_com_example_ndkreverse3_Lesson3_main1 .text:00000F20 .text:00000F20 s = -0x2C .text:00000F20 var_14 = -0x14 .text:00000F20 .text:00000F20 PUSH {R4-R6,LR} .text:00000F22 LDR R4, =(__stack_chk_guard_ptr - 0xF2C) .text:00000F24 SUB SP, SP, #0x20 ;开辟堆栈 .text:00000F26 ADD R5, SP, #0x30+s .text:00000F28 ADD R4, PC ; __stack_chk_guard_ptr .text:00000F2A LDR R4, [R4] ; __stack_chk_guard .text:00000F2C MOVS R0, R5 ; s .text:00000F2E LDR R3, [R4] .text:00000F30 MOVS R1, #0 ; c .text:00000F32 MOVS R2, #0x18 ; n .text:00000F34 STR R3, [SP,#0x30+var_14] .text:00000F36 BL j_j_memset ;将s中前n个字节用 c(0) 替换并返回 s 。 .text:00000F3A MOVS R3, #1 .text:00000F3C STR R3, [SP,#0x30+s] ;初始化堆栈中的数组 .text:00000F3E MOVS R3, #2 .text:00000F40 STR R3, [R5,#4] ;[R5,#8]直接略过 .text:00000F42 MOVS R3, #3 .text:00000F44 STR R3, [R5,#0xC] .text:00000F46 MOVS R3, #4 .text:00000F48 STR R3, [R5,#0x10] .text:00000F4A MOVS R3, #5 .text:00000F4C MOVS R6, #0 .text:00000F4E STR R3, [R5,#0x14] .text:00000F50 .text:00000F50 loc_F50 ; CODE XREF: Java_com_example_ndkreverse3_Lesson3_main1+44j .text:00000F50 LDR R1, =(aLesson3 - 0xF5C) .text:00000F52 LDR R2, =(aBD - 0xF5E) .text:00000F54 LDR R3, [R5,R6] ;取数组元素,R5是 sp - 0x20 + 0x4,分别取出1,2,0 .text:00000F56 MOVS R0, #3 .text:00000F58 ADD R1, PC ; "lesson3" .text:00000F5A ADD R2, PC ; "b=%d\n" .text:00000F5C ADDS R6, #4 .text:00000F5E BL j_j___android_log_print .text:00000F62 CMP R6, #0xC ;R6的值与0xC的值比较 .text:00000F64 BNE loc_F50 ;如果不相等,就跳转到loc_F50 .text:00000F66 MOVS R6, #0 ;否则,继续运行 .text:00000F68 .text:00000F68 loc_F68 ; CODE XREF: Java_com_example_ndkreverse3_Lesson3_main1+5Ej .text:00000F68 LDR R1, =(aLesson3 - 0xF76) .text:00000F6A LDR R2, =(aBD - 0xF78) .text:00000F6C ADDS R3, R5, R6 .text:00000F6E LDR R3, [R3,#0xC] ;取数组元素,分别取出3,4,5 .text:00000F70 MOVS R0, #3 .text:00000F72 ADD R1, PC ; "lesson3" .text:00000F74 ADD R2, PC ; "b=%d\n" .text:00000F76 ADDS R6, #4 .text:00000F78 BL j_j___android_log_print .text:00000F7C CMP R6, #0xC .text:00000F7E BNE loc_F68 ;和loc_F50同理 .text:00000F80 LDR R2, [SP,#0x30+var_14] .text:00000F82 LDR R3, [R4] .text:00000F84 CMP R2, R3 ;堆栈检查 .text:00000F86 BEQ loc_F8C .text:00000F88 BL j_j___stack_chk_fail .text:00000F8C ; --------------------------------------------------------------------------- .text:00000F8C .text:00000F8C loc_F8C ; CODE XREF: Java_com_example_ndkreverse3_Lesson3_main1+66j .text:00000F8C ADD SP, SP, #0x20 .text:00000F8E POP {R4-R6,PC}
.text:00000F90 off_F90 DCD __stack_chk_guard_ptr - 0xF2C .text:00000F90 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main1+2r .text:00000F94 off_F94 DCD aLesson3 - 0xF5C ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main1:loc_F50r .text:00000F94 ; "lesson3" .text:00000F98 off_F98 DCD aBD - 0xF5E ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main1+32r .text:00000F98 ; "b=%d\n" .text:00000F9C off_F9C DCD aLesson3 - 0xF76 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main1:loc_F68r .text:00000F9C ; "lesson3" .text:00000FA0 off_FA0 DCD aBD - 0xF78 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main1+4Ar .text:00000FA0 ; "b=%d\n"
.rodata:00002284 aLesson3 DCB "lesson3",0 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main+2Ao .rodata:00002284 ; .text:off_F18o ... .rodata:0000228C aAD DCB "a=%d",0xA,0 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main+2Co .rodata:0000228C ; .text:off_F1Co ... .rodata:00002292 aBD DCB "b=%d",0xA,0 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main1+3Ao .rodata:00002292 ; Java_com_example_ndkreverse3_Lesson3_main1+54o ...我们看到二维数组的本质上和一维数组是一致的,这里二维数组初始化的值并不是存放在.rodata段中,而是用寄存器赋值。
.text:00000FA4 EXPORT Java_com_example_ndkreverse3_Lesson3_main2 .text:00000FA4 Java_com_example_ndkreverse3_Lesson3_main2 .text:00000FA4 .text:00000FA4 var_28 = -0x28 .text:00000FA4 var_24 = -0x24 .text:00000FA4 var_20 = -0x20 .text:00000FA4 var_1C = -0x1C .text:00000FA4 .text:00000FA4 LDR R3, =(__stack_chk_guard_ptr - 0xFAC) .text:00000FA6 PUSH {R4-R7,LR} .text:00000FA8 ADD R3, PC ; __stack_chk_guard_ptr .text:00000FAA LDR R3, [R3] ; __stack_chk_guard .text:00000FAC SUB SP, SP, #0x14 ;开辟堆栈 .text:00000FAE MOV R6, SP .text:00000FB0 MOVS R5, #0 .text:00000FB2 MOVS R4, R3 .text:00000FB4 LDR R2, [R3] .text:00000FB6 STR R2, [SP,#0x28+var_1C] ;存入堆栈保护数值 .text:00000FB8 LDR R2, =(a12 - 0xFBE) .text:00000FBA ADD R2, PC ; "12" ;向堆栈中存入"12"的地址 .text:00000FBC STR R2, [SP,#0x28+var_28] .text:00000FBE LDR R2, =(a345 - 0xFC4) .text:00000FC0 ADD R2, PC ; "345" ;向堆栈中存入"345"的地址 .text:00000FC2 STR R2, [SP,#0x28+var_24] .text:00000FC4 LDR R2, =(a6789 - 0xFCA) .text:00000FC6 ADD R2, PC ; "6789" ;“向堆栈中村如6789”的地址 .text:00000FC8 STR R2, [SP,#0x28+var_20] .text:00000FCA .text:00000FCA loc_FCA ; CODE XREF: Java_com_example_ndkreverse3_Lesson3_main2+44j .text:00000FCA MOVS R7, #0 .text:00000FCC .text:00000FCC loc_FCC ; CODE XREF: Java_com_example_ndkreverse3_Lesson3_main2+3Ej .text:00000FCC LDR R3, [R6,R5] ;从堆栈中取出首地址 .text:00000FCE LDR R1, =(aLesson3 - 0xFDA) .text:00000FD0 LDR R2, =(aDD - 0xFDC) .text:00000FD2 LDRB R3, [R3,R7] ;从.rodata段中取出字符 .text:00000FD4 MOVS R0, #3 .text:00000FD6 ADD R1, PC ; "lesson3" .text:00000FD8 ADD R2, PC ; "d=%d\n" .text:00000FDA ADDS R7, #1 ;每次加1,取一个字符 .text:00000FDC BL j_j___android_log_print .text:00000FE0 CMP R7, #4 ; .text:00000FE2 BNE loc_FCC ;第二层循环 .text:00000FE4 ADDS R5, #4 ;取下一个指针,也是下一个字符串的首地址 .text:00000FE6 CMP R5, #0xC .text:00000FE8 BNE loc_FCA ;第一层循环 .text:00000FEA LDR R2, [SP,#0x28+var_1C] .text:00000FEC LDR R3, [R4] .text:00000FEE CMP R2, R3 .text:00000FF0 BEQ loc_FF6 .text:00000FF2 BL j_j___stack_chk_fail .text:00000FF6 ; --------------------------------------------------------------------------- .text:00000FF6 .text:00000FF6 loc_FF6 ; CODE XREF: Java_com_example_ndkreverse3_Lesson3_main2+4Cj .text:00000FF6 ADD SP, SP, #0x14 .text:00000FF8 POP {R4-R7,PC}
.rodata:00002284 a12 DCB "12",0 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main2+16o .rodata:00002284 ; .text:off_1000o .rodata:00002287 a345 DCB "345",0 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main2+1Co .rodata:00002287 ; .text:off_1004o .rodata:0000228B a6789 DCB "6789",0 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main2+22o .rodata:0000228B ; .text:off_1008o .rodata:00002290 aDD DCB "d=%d",0xA,0 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main2+34o .rodata:00002290 ; .text:off_1010o .rodata:00002296 aPD DCB "p=%d",0xA,0 ; DATA XREF: Java_com_example_ndkreverse3_Lesson3_main3+2Co .rodata:00002296 ; .text:off_1068o ...
指针数组的执行请参考代码中的注释,字符串依然存储在.rodata段中。依然是开辟堆栈空间去存储指针数组。
Java_com_example_ndkreverse3_Lesson3_main3(指针)和Java_com_example_ndkreverse3_Lesson3_main汇编代码是一致的。
Java_com_example_ndkreverse3_Lesson3_main4(数组指针)和Java_com_example_ndkreverse3_Lesson3_main1汇编代码是一致的。