Android SO逆向-流程控制语句及表达式运算
来源:互联网 发布:ifix组态软件 编辑:程序博客网 时间:2024/05/01 21:30
0x00
在前一篇文章Android SO逆向-基本数据类型及函数的工作原理中,我们介绍了ndk的使用,这篇文章直接列出C++源码及对应的汇编代码。
0x01
在java层主要是调用native方法,现在列出java层的代码:
Lesson1.java
package com.example.ndkreverse1;public class Lesson1 { static { System.loadLibrary("lesson1"); } public static native int getInt(); public native String getString(); public static native int getFor1(int n); public static native String getIfElse(int n); public static native int getWhile(int n); public static native int getSwitch(int a,int b,int i); public static native int getOperation(int a, int b);}MainActivity.java
package com.example.ndkreverse1;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Lesson1 lesson1 = new Lesson1();lesson1.getInt();lesson1.getString();lesson1.getFor1(5);lesson1.getIfElse(20);lesson1.getWhile(5);lesson1.getSwitch(3, 4, 3);lesson1.getOperation(9, 16);}}
在MainActivity中调用的native方法,是在native层实现的。
#include "com_example_ndkreverse1_Lesson1.h"#include <android/log.h>#define LOG_TAG "lesson1"#define ALOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))JNIEXPORT jint JNICALL Java_com_example_ndkreverse1_Lesson1_getInt (JNIEnv * env, jclass jclass) {return 8;}JNIEXPORT jstring JNICALL Java_com_example_ndkreverse1_Lesson1_getString (JNIEnv * env, jobject jobject) {return env->NewStringUTF("method call");}JNIEXPORT jint JNICALL Java_com_example_ndkreverse1_Lesson1_getFor1 (JNIEnv * env, jclass jclass, jint n) {int i = 0;int s = 0;for (i = 0; i < n; i++){s += i * 2;}return s;}JNIEXPORT jstring JNICALL Java_com_example_ndkreverse1_Lesson1_getIfElse (JNIEnv * env, jclass jclass, jint n) {if(n < 16) {return env->NewStringUTF("he is a boy");} else if(n < 30){return env->NewStringUTF("he is a young man");} else if(n < 45){return env->NewStringUTF("he is a strong man");} else{return env->NewStringUTF("he is an old man");}}JNIEXPORT jint JNICALL Java_com_example_ndkreverse1_Lesson1_getWhile (JNIEnv * env, jclass jclass, jint n) {int i = 1;int s = 0;while(i <= n) {s += i++;}return s;}JNIEXPORT jint JNICALL Java_com_example_ndkreverse1_Lesson1_getSwitch (JNIEnv * env, jclass jclass, jint a, jint b, jint i) {switch (i) {case 1:return a + b;break;case 2:return a - b;break;case 3:return a * b;break;case 4:return a / b;break;default:return a + b;break;}}JNIEXPORT jint JNICALL Java_com_example_ndkreverse1_Lesson1_getOperation (JNIEnv * env, jclass jclass, jint a, jint b) {if (a > 10 || !(b <=20 && b != 15)) {return 8;} else {return 9;}}
0x02
使用ida打开so,下面我们来看一下这些native方法对应的汇编代码。
Java_com_example_ndkreverse1_Lesson1_getInt:
.text:00001026 EXPORT Java_com_example_ndkreverse1_Lesson1_getInt.text:00001026 Java_com_example_ndkreverse1_Lesson1_getInt.text:00001026 MOVS R0, #8.text:00001028 BX LR此时仅仅是返回了一个数字8。BX LR返回到调用Java_com_example_ndkreverse1_Lesson1_getInt的函数,动态调试可以看到是libdvm.so里面的一个方法。
Java_com_example_ndkreverse1_Lesson1_getString:
.text:0000102C EXPORT Java_com_example_ndkreverse1_Lesson1_getString.text:0000102C Java_com_example_ndkreverse1_Lesson1_getString.text:0000102C PUSH {R3,LR}.text:0000102E LDR R1, =(aMethodCall - 0x1034).text:00001030 ADD R1, PC ; "method call".text:00001032 BL _ZN7_JNIEnv12NewStringUTFEPKc ; _JNIEnv::NewStringUTF(char const*).text:00001036 POP {R3,PC}.text:00001036 ; End of function Java_com_example_ndkreverse1_Lesson1_getString我们看Java_com_example_ndkreverse1_Lesson1_getString这个函数有两个参数,分别是env,jobject。所以执行这个函数时,R0为env,R1为jobject。经过操作后,R1的值为method call的地址,我们在Android SO逆向-基本数据类型及函数的工作原理已经讲过了这个地址是怎么获取的,R0还是env,然后调用_ZN7_JNIEnv12NewStringUTFEPKc也就是Ne
wStringUTF函数,返回的结果保持在R0中,然后返回到上一层函数中。
Java_com_example_ndkreverse1_Lesson1_getFor1:
.text:0000103C EXPORT Java_com_example_ndkreverse1_Lesson1_getFor1.text:0000103C Java_com_example_ndkreverse1_Lesson1_getFor1.text:0000103C MOVS R0, #0 ;R0 初始化为0.text:0000103E ADDS R3, R0, #0 ;R3初始化为0.text:00001040.text:00001040 loc_1040 ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getFor1+Ej.text:00001040 CMP R3, R2 ;R3和R2比较.text:00001042 BGE locret_104C ;如果R3>=R2,则跳转到locret_104C返回.text:00001044 LSLS R1, R3, #1 ;否则R3左移1位,相当于乘以2.text:00001046 ADDS R0, R0, R1 ;R0累加R1的值,再赋值给R0.text:00001048 ADDS R3, #1 ;R3加1,再赋值R3.text:0000104A B loc_1040 ;继续循环执行.text:0000104C ; ---------------------------------------------------------------------------.text:0000104C.text:0000104C locret_104C ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getFor1+6j.text:0000104C BX LR ;程序的出口传入的参数R2是n的值,其他的操作系统请看代码中注释,然后对比C++代码,读者便可理解for循环在汇编层的实现。
JNICALL Java_com_example_ndkreverse1_Lesson1_getIfElse:
.text:00001050 EXPORT Java_com_example_ndkreverse1_Lesson1_getIfElse.text:00001050 Java_com_example_ndkreverse1_Lesson1_getIfElse.text:00001050 PUSH {R3,LR}.text:00001052 CMP R2, #0xF ;R2的值和16比较.text:00001054 BGT loc_105C ;如果R2比16的值大,则跳转到loc_105C.text:00001056 LDR R1, =(aHeIsABoy - 0x105C) ;否则返回he is a boy.text:00001058 ADD R1, PC ; "he is a boy".text:0000105A B loc_1074 ;跳转到loc_1074,调用NewStringUTF.text:0000105C ; ---------------------------------------------------------------------------.text:0000105C.text:0000105C loc_105C ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getIfElse+4j.text:0000105C CMP R2, #0x1D ;R2的值和30比较.text:0000105E BGT loc_1066 ;如果R2比30大,则跳转到loc_1066.text:00001060 LDR R1, =(aHeIsAYoungMan - 0x1066) ;否则返回he is a young man.text:00001062 ADD R1, PC ; "he is a young man".text:00001064 B loc_1074 ;跳转到loc_1074,调用NewStringUTF.text:00001066 ; ---------------------------------------------------------------------------.text:00001066.text:00001066 loc_1066 ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getIfElse+Ej.text:00001066 CMP R2, #0x2C ;R2的值和45比较.text:00001068 BGT loc_1070 ;如果R2比45大,则跳转到loc_1070.text:0000106A LDR R1, =(aHeIsAStrongMan - 0x1070) ;否则返回he is a strong man.text:0000106C ADD R1, PC ; "he is a strong man".text:0000106E B loc_1074 ;跳转到loc_1074,调用NewStringUTF.text:00001070 ; ---------------------------------------------------------------------------.text:00001070.text:00001070 loc_1070 ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getIfElse+18j.text:00001070 LDR R1, =(aHeIsAnOldMan - 0x1076).text:00001072 ADD R1, PC ; "he is an old man" ;继续执行到loc_1074,调用NewStringUTF.text:00001074.text:00001074 loc_1074 ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getIfElse+Aj.text:00001074 ; Java_com_example_ndkreverse1_Lesson1_getIfElse+14j ....text:00001074 BL _ZN7_JNIEnv12NewStringUTFEPKc ; _JNIEnv::NewStringUTF(char const*).text:00001078 POP {R3,PC}传入的参数R2是n的值,其他的操作系统请看代码中注释,然后对比C++代码,读者便可理解if else循环在汇编层的实现。我们再看下ida对这段汇编的按F5形成的C语言。
int __fastcall Java_com_example_ndkreverse1_Lesson1_getIfElse(_JNIEnv *a1, int a2, signed int a3){ const char *v3; // r1@2 if ( a3 > 15 ) { if ( a3 > 29 ) { if ( a3 > 44 ) v3 = "he is an old man"; else v3 = "he is a strong man"; } else { v3 = "he is a young man"; } } else { v3 = "he is a boy"; } return _JNIEnv::NewStringUTF(a1, v3);}我们能够看到反汇编后的C++语言和最初的C++语言还是有一定差别的。
Java_com_example_ndkreverse1_Lesson1_getWhile:
.text:0000108C EXPORT Java_com_example_ndkreverse1_Lesson1_getWhile.text:0000108C Java_com_example_ndkreverse1_Lesson1_getWhile.text:0000108C MOVS R0, #0.text:0000108E MOVS R3, #1.text:00001090.text:00001090 loc_1090 ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getWhile+Cj.text:00001090 CMP R3, R2.text:00001092 BGT locret_109A ;如果R3大于R2,就跳转到locret_109A.text:00001094 ADDS R0, R0, R3.text:00001096 ADDS R3, #1.text:00001098 B loc_1090.text:0000109A ; ---------------------------------------------------------------------------.text:0000109A.text:0000109A locret_109A ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getWhile+6j.text:0000109A BX LR这个函数和for循环的汇编实现很像。
Java_com_example_ndkreverse1_Lesson1_getSwitch:
.text:0000109C EXPORT Java_com_example_ndkreverse1_Lesson1_getSwitch.text:0000109C Java_com_example_ndkreverse1_Lesson1_getSwitch.text:0000109C.text:0000109C arg_0 = 0.text:0000109C.text:0000109C PUSH {R3,LR}.text:0000109E LDR R1, [SP,#8+arg_0] ;取参数i.text:000010A0 ADDS R0, R2, R3 ;R2和R3分别代表a和b参数,相加得到默认值存放在R0中.text:000010A2 SUBS R1, #1 ;R1值减一.text:000010A4 CMP R1, #3 ; switch 4 cases ;R1的值和3对比.text:000010A6 BHI def_10AA ; jumptable 000010AA default case ;如果R1的值大于3,那么就跳转到def_10AA.text:000010A8 MOVS R0, R1 ;把R1的值赋值给R0.text:000010AA BL __gnu_thumb1_case_uqi ; switch jump ;跳转到_gnu_thumb1_case_uqi.text:000010AA ; ---------------------------------------------------------------------------.text:000010AE jpt_10AA DCB 2 ; jump table for switch statement.text:000010AF DCB 4.text:000010B0 DCB 6.text:000010B1 DCB 9.text:000010B2 ; ---------------------------------------------------------------------------.text:000010B2.text:000010B2 loc_10B2 ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getSwitch+Ej.text:000010B2 ADDS R0, R2, R3 ; jumptable 000010AA case 0.text:000010B6 ; ---------------------------------------------------------------------------.text:000010B6.text:000010B6 loc_10B6 ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getSwitch+Ej.text:000010B6 SUBS R0, R2, R3 ; jumptable 000010AA case 1.text:000010B8 B def_10AA ; jumptable 000010AA default case.text:000010BA ; ---------------------------------------------------------------------------.text:000010BA.text:000010BA loc_10BA ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getSwitch+Ej.text:000010BA MOVS R0, R3 ; jumptable 000010AA case 2.text:000010BC MULS R0, R2.text:000010BE B def_10AA ; jumptable 000010AA default case.text:000010C0 ; ---------------------------------------------------------------------------.text:000010C0.text:000010C0 loc_10C0 ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getSwitch+Ej.text:000010C0 MOVS R1, R3 ; jumptable 000010AA case 3.text:000010C2 MOVS R0, R2.text:000010C4 BL __divsi3.text:000010C8.text:000010C8 def_10AA ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getSwitch+Aj.text:000010C8 ; Java_com_example_ndkreverse1_Lesson1_getSwitch+18j ....text:000010C8 POP {R3,PC} ; jumptable 000010AA default case.text:000010E0 EXPORT __gnu_thumb1_case_uqi.text:000010E0 __gnu_thumb1_case_uqi ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getSwitch+Ep.text:000010E0 ; _Unwind_VRS_Get+Ap ....text:000010E0 PUSH {R1} .text:000010E2 MOV R1, LR ;把返回值LR赋值给R1.text:000010E4 LSRS R1, R1, #1 ;无用.text:000010E6 LSLS R1, R1, #1 ;无用.text:000010E8 LDRB R1, [R1,R0] ;根据R0的值,取出R1(LR)加上R0地址的内容,也就是上面代码中jpt_10AA某个地址中的内容.text:000010EA LSLS R1, R1, #1 ;然后把此内容值再乘以2.text:000010EC ADD LR, R1 ;把LR加上R1的值赋值给LR,此时LR就是保存是要跳转的switch case的语句的地址了。.text:000010EE POP {R1}.text:000010F0 BX LR ;跳转到switch case对应的语句块,在本例中就是loc_10B2,loc_10B6,loc_10BA,loc_10C0
由于getSwitch参数较多,JNIEnv * env, jclass jclass, jint a, jint b, jint i。前四个参数的的值是由R0~R3传递过来,最后一个参数i是由堆栈传递过来的。详细的解释请看代码。
Java_com_example_ndkreverse1_Lesson1_getOperation:
.text:000010CA EXPORT Java_com_example_ndkreverse1_Lesson1_getOperation.text:000010CA Java_com_example_ndkreverse1_Lesson1_getOperation.text:000010CA MOVS R0, #8 ;R0被赋值为8.text:000010CC CMP R2, #0xA ;比较a与10的值.text:000010CE BGT locret_10DE ;如果大于,就去执行locret_10DE,直接把刚赋值的8返回去.text:000010D0 CMP R3, #0x14 ;否则,b和20比较.text:000010D2 BGT locret_10DE ;如果大于20,就去执行locret_10DE,直接把刚赋值的8返回去.text:000010D4 SUBS R3, #0xF ;否则b的值减去15.text:000010D6 NEGS R2, R3 ;取反b的值.text:000010D8 ADCS R3, R2 ;b的值加上b取反的值再加上进位,得到的值再赋值给b。PS:如果b为15,那么此时b的值为1,否则为0.text:000010DA MOVS R0, #9 ;R0被赋值为9.text:000010DC SUBS R0, R0, R3 ;R0减去R3的值,再赋值给R0返回.text:000010DE.text:000010DE locret_10DE ; CODE XREF: Java_com_example_ndkreverse1_Lesson1_getOperation+4j.text:000010DE ; Java_com_example_ndkreverse1_Lesson1_getOperation+8j.text:000010DE BX LRJava_com_example_ndkreverse1_Lesson1_getOperation函数,R2是参数a,R3是参数b。详细的解释请看代码。
我们再看下反汇编之后形成的C++语言和Java_com_example_ndkreverse1_Lesson1_getOperation原始实现的C++语言还是有比较大的差别的:
int __fastcall Java_com_example_ndkreverse1_Lesson1_getOperation(int a1, int a2, signed int a3, signed int a4){ int result; // r0@1 result = 8; if ( a3 <= 10 && a4 <= 20 ) result = 9 - ((unsigned int)(a4 - 15) <= 0); return result;}里面还有有些错误的,比如a4如果为14,原本函数应该返回9,但是这个函数却返回8。
如果大家遇到不明白的地方,动态调试是个非常不错的方式,可能更快的理解程序,详见ida动态调试so,在init_array和JNI_ONLOAD处下断点。
- Android SO逆向-流程控制语句及表达式运算
- 移位运算及流程控制语句
- 基础知识之运算符及流程控制语句
- C语言入门之流程控制语句及运算符号
- 运算符,表达式,流程控制
- 表达式和流程控制语句
- 第7章so逆向流程控制分析
- javascript学习(2)——[基础回顾]运算符、表达式、流程控制语句
- Java复习笔记+经验总结-01 运算符 表达式 数组 流程控制语句
- 第二天-运算符,流程控制语句
- 运算符与流程控制语句
- C#—运算符、控制流程语句
- java位运算,流程控制语句
- 3. 运算符与流程控制语句
- 04-变量+运算符+流程控制语句
- Java 运算符、表达式和流程控制
- 运算符、表达式和流程控制
- 运算符、表达式和流程控制
- sizeof和strlen的一些理解
- 注意事项
- 从头认识多线程-1.9 迫使线程停止的方法-return法
- 《推荐系统学习》之推荐系统那点事
- #1037 : 数字三角形
- Android SO逆向-流程控制语句及表达式运算
- HTML从零开始
- DefaultHttpClient is deprecated
- 全局大喇叭 广播机制
- 110.Find Minimum in Rotated Sorted Array
- Flume的使用问题及解决方案
- Lucene.Net
- 码农的自我修养-对代码注释的理解
- c++入门第一篇:hello,world!