阅读:2876回复:0
Android SO逆向1-ARM介绍
◆0 概述
把之前学习SO逆向的笔记分享出来,内容比较简单,大牛就可以略过了。 ◆1 ARM寄存器 1.1.通用寄存器 1.未分组寄存器:R0~R7 2.分组寄存器:R8~812 R13:SP,常用作堆栈指针,始终指向堆栈的顶部,当一个数据(32位)推入堆栈时,SP(R13的值减4)向下浮动指向下一个地址,即新的栈顶,当数据从堆栈中弹出时,SP(R13的值加4)向上浮动指向新的栈顶。 R14:连接寄存器(LR),当执行BL子程序调用指令时,R14中得到R15(程序计数器PC)的备份,其他情况下,R14用作通用寄存器。 R15:程序计数器(PC):用于控制程序中指令的执行顺序。正常运行时,PC指向CPU运行的下一条指令。每次取值后PC的值会自动修改以指向下一条指令,从而保证了指令按一定的顺序执行。当程序的执行顺序发生改变(如转移)时,需要修改PC的值。 1.2.状态寄存器 CPSR(R16):当前程序状态寄存器,用来保存ALU中的当前操作信息,控制允许和禁止中断、设置处理器的工作模式等。 SPSRs:五个备份的程序状态寄存器,用来进行异常处理。当异常发生时,SPSR用于保存CPSR的当前值,从异常退出时可由SPSR来恢复CPSR。 图片:2015102908554547624cpsr.jpg N、Z、C、V均为条件码标志位,他们的内容可被运算的结果所改变。 N:正负标志,N=1表示运算的结果为负,N=0表示运算的结果为正或0 Z:零标志,Z=1表示运算的结果为0,Z=0表示运算的结果为非0 C:进位标志,加法运算产生了进位时则C=1,否则C=0 借位标志,减肥运算产生了借位则C=0,否则C=1 V:溢出标志,V=1表示有溢出,V=0表示无溢出 1.3.地址空间 程序正常执行时,每执行一条ARM指令,当前指令计数器增加4个字节。 ◆2 汇编语言 2.1.汇编指令格式 {}{S},{,} 格式中的内容必不可少,{}中的内容可省略 :表示操作码,如ADD表示算术加法 {}:表示指令执行的条件域,如EQ、NE等。 {S}:决定指令的执行结果是否影响CPSR的值,使用该后缀则指令执行的结果影响CPSR的值,否则不影响 :表示目的寄存器 :表示第一个操作数,为寄存器 :表示第二个操作数,可以是立即数、寄存器或寄存器移位操作数 例子:ADDEQS R0,R1,#8;其中操作码为ADD,条件域cond为EQ,S表示该指令的执行影响CPSR寄存器的值,目的寄存器Rd为R0,第一个操作数寄存器Rd为R1,第二个操作数OP2为立即数#8 2.2.指令的可选后缀 S:指令执行后程序状态寄存器的条件标志位将被刷新 ADDS R1,R0,#2 !:指令中的地址表达式中含有!后缀时,指令执行后,基址寄存器中的地址值将发生变化,变化的结果是:基址寄存器中的值(指令执行后)=指令执行前的值 + 地址偏移量 LDR R3,[R0,#2]! 指令执行后,R0 = R0 + 2 2.3.指令的条件执行 指令的条件后缀只是影响指令是否执行,不影响指令的内容 条件码 助记符后缀 标志 含义 0000 EQ Z置位 相等 0001 NE Z清零 不相等 0010 CS C指令 无符号数大于或等于 0011 CC C清零 无符号数小于 0100 MI N置位 负数 0101 PL N清零 正数或零 0110 VS V置位 溢出 0111 VC V清零 未溢出 1000 HI C置位Z清零 无符号数大于 1001 LS C清零Z置位 无符号数小于或等于 1010 GE N等于V 带符号数大于或等于 1011 LT N不等于V 带符号数小于 1100 GT Z清零且(N等于V) 带符号数大于 1101 LE Z置位或(N不等于V) 带符号数小于或等于 1110 AL 忽略 无条件执行 例子:ADDEQ R4,R3,#1 相等则相加,即CPSR中Z置位时该指令执行,否则不执行。 2.4.ARM指令分类 助记符 指令功能描述 助记符 指令功能描述 ADC 带进位加法指令 MRC 从协处理器寄存器到ARM寄存器的数据传输指令 ADD 加法指令 MRS 传送CPSR或SPSR的内容到通用寄存器指令 AND 逻辑与指令 MSR 传送通用寄存器到CPSR或SPSR的指令 B 分支指令 MUL 32位乘法指令 BIC 位清零指令 MLA 32位乘加指令 BL 带返回的分支指令 MVN 数据取反传送指令 BLX 带返回和状态切换的分支指令 ORR 逻辑或指令 BX 带状态切换的分支指令 RSB 逆向减法指令 CDP 协处理器数据操作指令 RSC 带错位的逆向减法指令 CMN 比较反值指令 SBC 带错位减法指令 CMP 比较指令 STC 协处理器寄存器写入存储器指令 EOR 异或指令 STM 批量内存字写入指令 LDC 存储器到协处理器的数据传输指令 STR 寄存器到存储器的数据存储指令 LDM 加载多个寄存器指令 SUB 减法指令 LDR 存储器到寄存器的数据加载指令 SWI 软件中断指令 MCR 从ARM寄存器到协处理器寄存器的数据传输指令 TEQ 相等测试指令 MOV 数据传送指令 TST 位测试指令 2.5.ARM寻址方式 寻址方式就是根据指令中操作数的信息来寻找操作数实际物理地址的方式 2.5.1立即数寻址 MOV R0,#15 #15就是立即数 2.5.2寄存器寻址 ADD R0, R1, R2 将R1和R2的内容相加,其结果存放在寄存器R0中 2.5.3寄存器间接寻址 LDR R0, [R4] 以寄存器R4的值作为操作数的地址,在存储器中取得一个操作数存入寄存器R0中 2.5.4寄存器移位寻址 ADD R0,R1,R2,LSL #1 将R2的值左移一位,所得值与R1相加,存放到R0中 MOV R0,R1,LSL R3 将R1的值左移R3位,然后将结果存放到R0中 2.5.5基址变址寻址 LDR R0,[R1,#4] 将R1的值加4作为操作数的地址,在存储器中取得操作数放入R0中 LDR R0,[R1,#4]! 将R1的值加4作为操作数的地址,在存储器中取得操作数放入R0中,然后R1 = R1+4 LDR R0,[R1],#4 R0 = [R1],R1 = R1 +4 LDR R0,[R1,R2] R0 = [R1+R2] 2.5.6.多寄存器寻址 一条指令可以完成多个寄存器值的传送(最多可传送16个通用寄存器),连续的寄存器用“-”,否则用“,” LDMIA R0!,{R1 - R4} R1 = [R0],R2=[R0+4],R3=[R0+8],R4=[R0+12] 后缀IA表示在每次执行玩加载/存储操作后,R0按自长度增加。 2.5.7.相对寻址 以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址,如下图的BL分支跳转 BL proc 跳转到子程序proc处执行 ... proc MOV R0,#1 ... 2.5.8.堆栈寻址 按先进先出的方式工作,堆栈指针用R13表示,总是指向栈顶,LDMFD和STMFD分别表示POP出栈和PUSH进栈 STMFD R13!,{R0 - R4}; LDMFD R13!,{R0 - R4}; 2.6.数据处理指令 2.6.1. MOV指令 MOV {}{S} Rd,op2 将op2传给Rd MOV R1, R0 将寄存器R0的值传到寄存器R1 MOV PC,R14 将寄存器R14的值传到PC,常用于子程序返回 MOV R1,R0,LSL #3 将寄存器R0的值左移3位后传给R1 MOV R0,#5 将立即数5传给R0 2.6.2. MVN指令 MVN {}{S}Rd, op2 将op2取反传给Rd MVN R0,#0 将0取反后传给R0,R0 = -1 MVN R1,R2 将R2取反,结果保存到R1 2.6.3. 移位指令 LSL 逻辑左移 LSR 逻辑右移 ASR 算术右移 ROR 循环右移 RRX 带扩展的循环右移 2.6.4. ADD加法指令 ADD{}{S}Rd, Rn, op2 ADD R0,R1,R2 R0 = R1 + R2 ADD R0,R1,#5 R0 = R1 + 5 ADD R0,R1,R2,LSL #2 R0 = R1 + (R2左移2位) 2.6.5. ADC带进位加法指令 ADC{}{S} Rd,Rn,op2 将Rn的值和操作数op2相加,再加上CPSR中C条件标志位的值,并将结果保存到Rd中 例:用ADC完成64位加法,设第一个64位操作数保存在R2,R3中,第二个64位操作数放在R4,R5中,结果保存在R0,R1中 ADDS R0,R2,R4 低32位相加,产生进位 ADC R1,R3,R5 高32位相加,加上进位 2.6.6. SUB减法指令 SUB{}{S} Rd,Rn,op2 Rd = Rn - op2 SUB R0,R1,R2 R0 = R1 - R2 SUB R0,R1,#6 R0 = R1 -6 SUB R0,R2,R3,LSL #1 R0 = R2 - (R3左移1位) 2.6.7. SBC带借位减法指令 SBC{}{S} Rd,Rn,op2 把Rn的值减去操作数op2,再减去CPSR中的C标志位的反码,并将结果保存到Rd中,Rd = Rn - op2 - !C 例:用SBC完成64位减法,设第一个64位操作数保存在R2,R3中,第二个64位操作数放在R4,R5中,结果保存在R0,R1中 SUBS R0,R2,R4 低32位相减,S影响CPSR SBC R1,R3,R5 高32位相减,去除C的反码 2.6.8. RSC带借位的逆向减法指令 RSC{}{S} Rd,Rn,op2 把操作数op2减去Rn,再减去CPSR中的C标志位的反码,并将结果保存到Rd中,Rd = op2 - Rn - !C 2.6.9. 逻辑运算指令 AND{}{S} Rd,Rn,op2 按位与,Rd = Rn AND op2 ORR{}{S} Rd,Rn,op2 按位或,Rd = Rn OR op2 EOR{}{S} Rd,Rn,op2 按位异或,Rd = Rn EOR op2 2.6.10. CMP比较指令 CMP{}{S} Rd,Rn,op2 将Rn的值和op2进行比较,同时更新CPSR中条件标志位的值(实际上是执行一次减法,但不存储结果),当操作数Rn大于op2时,则此后带有GT后缀的指令将可以执行(根据相应的指令判断是否执行,如GT,LT等)。 CMP R1,#10 比较R1和10,并设置CPSR的标志位 ADDGT R0,R0,#5 如果R1>10,则执行ADDGT指令,将R0加5 2.6.11. CMN反值比较指令 CMN{}{S} Rd,Rn,op2 将Rn的值和op2取反后进行比较,同时更新CPSR中条件标志位的值(实际上将Rn和op2相加),后面的指令就可以根据条件标志位决定是否执行。 CMN R0,R1 将R0和R1相加,并设置CPSR的值 2.6.12. MUL/MLA/SMULL/SMLAL/UMULL/UMLAL乘法指令 MUL 32位乘法指令 MLA 32位乘加指令 SMULL 64位有符号数乘法指令 SMLAL 64位有符号数乘加指令 UMULL 64位无符号数乘法指令 UMLAL 64位无符号数乘加指令 MUL{}{S} Rd,Rm,Rs Rd = Rm * Rs MULS R0,R1,R2 MLA{}{S} Rd,Rm,Rs,Rn Rd = (Rm * Rs) + Rn MLAS R0,R1,R2,R3 2.7.数据加载与存储指令 助记符 说明 操作 LDR{}Rd,addr 加载字数据 Rd = [addr] LDRB{}Rd,addr 加载无符号字节数据 Rd = [addr] LDRT{}Rd,addr 以用户模式加载字数据 Rd = [addr] LDRBT{}Rd,addr 以用户模式加载无符号字节数据 Rd = [addr] LDRH{}Rd,addr 加载无符号半字数据 Rd = [addr] LDRSB{}Rd,addr 加载有符号字节数据 Rd = [addr] LDRSH{}Rd,addr 加载有符号半字数据 Rd = [addr] STR{}Rd,addr 存储字数据 [addr] = Rd STRB{}Rd,addr 存储字节数据 [addr] = Rd STRT{}Rd,addr 以用户模式存储字数据 [addr] = Rd STRBT{}Rd,addr 以用户模式存储字节数据 [addr] = Rd STRH{}Rd,addr 存储半字数据 [addr] = Rd LDM{}{type}Rn{!},regs 多寄存器加载 reglist = [Rn...] STM{}{type}Rn{!},regs 多寄存器存储 [Rn...] = reglist SWP{}Rd,Rm,[Rn] 寄存器和存储器字数据交换 Rd=[Rn],[Rn]=Rm(Rn!=Rd或Rm) SWP{}B Rd,Rm,[Rn] 寄存器和存储器字节数据交换 Rd = [Rn],[Rn] = Rm(Rn!=Rd或Rm) 2.7.1. LDR/STR字数据加载/存储指令 LDR/STR{}{T}Rd,addr LDR指令用于从存储器中将一个32位的字数据加载到目的寄存器Rd中,当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当做目的地址,从而可以实现程序流程的跳转。 STR指令用于从源寄存器中将一个32位的字数据存储到存储器中,和LDR相反。后缀T可选。 LDR R4,START 将存储地址为START的字数据读入R4 STR R5,DATA1 将R5存入存储地址为DATA1中 LDR R0,[R1] 将存储器地址为R1的字数据读入存储器R0 LDR R0,[R1,R2] 将存储器地址为R1+R2的字数据读入存储器R0 LDR R0,[R1,#8] 将存储器地址为R1+8的字数据读入存储器R0 LDR R0,[R1,R2,LSL #2] 将存储器地址为R1+R2*4的字数据读入存储区R0 STR R0,[R1,R2]! 将R0字数据存入存储器地址R1+R2的存储单元中,并将新地址R2+R2写入R2 STR R0,[R1,#8]! 将R0字数据存入存储器地址R1+8的存储单元中,并将新地址R2+8写入R2 STR R0,[R1,R2,LSL #2] 将R0字数据存入存储器地址R1+R2*4的存储单元中,并将新地址R2+R2*4写入R1 LDR R0,[R1],#8 将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+8写入R1 LDR R0,[R1],R2 将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2写入R1 LDR R0,[R1],R2,LSL #2 将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2*4写入R1 2.7.2. LDRB/STRB字节数据加载/存储指令 LDRB/STRB{}{T}Rd,addr LDRB指令用于从存储器中将一个8位的字节数据加载到目的寄存器中,同时将寄存器的高24位清零,当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当做目的地址,从而可以实现程序流程的跳转。 STRB指令用于从源寄存器中将一个8位的字节数据存储到存储器中,和LDRB相反。后缀T可选。 2.7.3. LDRH/STRH半字数据加载/存储指令 [code]LDRH/STRH{}{T}Rd,addr LDRH指令用于从存储器中将一个16位的半字数据加载到目的寄存器中,同时将寄存器的高16位清零,当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当做目的地址,从而可以实现程序流程的跳转。 STRH指令用于从源寄存器中将一个16位的半字数据存储到存储器中,和LDRH相反。后缀T可选。 [/code] 2.7.4. LDM/STM批量数据加载/存储指令 [code]LDM/STM{}{}Rn{!},{^} LDM用于从基址寄存器所指示的一片连续存储器中读取数据到寄存器列表所指向的多个寄存器中,内存单元的起始地址为基址寄存器Rn的值,各个寄存器由寄存器列表regs表示,该指令一般用于多个寄存器数据的出栈操作 STM用于将寄存器列表所指向的多个寄存器中的值存入由基址寄存器所指向的一片连续存储器中,内存单元的起始地址为基址寄存器Rn的值,各个寄存器又寄存器列表regs表示。该指令一般用于多个寄存器数据的进栈操作。 type表示类型,用于数据的存储与读取有以下几种情况: IA:每次传送后地址值加。 IB:每次传送前地址值加。 DA:每次传送后地址值减。 DB:每次传送前地址值减。 用于堆栈操作时有如下几种情况: FD:满递减堆栈 ED:空递减堆栈 FA:满递增堆栈 EA:空递增堆栈 [/code] 2.7.5. SWP字数据交换指令 [code]SWP{},,[] Rd = [Rn],[Rn] = Rm,当寄存器Rm和目的寄存器Rd为同一个寄存器时,指令交换该急促亲和存储器的内容 SWP R0,R1,[R2] R0 = [R2],[R2] = R1 SWP R0,R0,[R1] R0 = [R1],[R1] = R0 SWPB指令用于将寄存器Rn指向的存储器中的字节数据加载到目的寄存器Rd中,目的寄存器的高24位清零,同时将Rm中的字数据存储到Rn指向的存储器中。 [/code] 2.8.分支语句 助记符 说明 操作 B{cond}label 分支指令 PCAdd Native Support(需要ADT配置好NDK的路径,新版的ADT没有配置NDK的地方,需要安装NDK的jar包)-->命名so文件(比如叫HelloJNI)。然后就会在程序中创建jni目录,包含了我们要写的NDK程序文件。 3.在src的包中(比如叫com.example.hellojni)中创建java文件,后面会演示几个实例。 4.在程序根目录下创建文件build_headers.xml,使用ANT editor打开(ADT需要安装ANT),使用alt+/键调出自动提示,选择Bulidfile template创建模板文件。后面会给出代码实例。 5.打开ANT工具,选择第一个"增加"按钮(一个小的加号),然后将build_headers.xml添加进来,ANT中就会增加HelloJNI,每次修改源码后,双击HelloJNI,就会自动修改/jni目录下的文件。 </pre> 3.2. 编写程序 3.2.1. CLASS文件 在com.example.hellojni包中创建class文件,本文一次创建多个实例,共参考。 GetInt.java代码为 [code]package com.example.hellojni; public class GetInt { public static native int getInt(); } [/code] GetString.java代码为 [code]package com.example.hellojni; public class GetString { public static native String getStr(); public native String getString(); public native int add(int a, int b); } [/code] GetFor.java代码为 [code]package com.example.hellojni; public class GetFor { public static native int getFor1(int n); public static native int getFor2(int n); } [/code] GetIfElse.java代码为 [code]package com.example.hellojni; public class GetIfElse { public static native String getIfElse(int n); } [/code] GetWhile.java代码为 [code]package com.example.hellojni; public class GetWhile { public static native int getWhile(int n); } [/code] GetSwitch.java代码为 [code]package com.example.hellojni; public class GetSwitch { public static native int getSwitch(int a,int b,int i); } [/code] MainActivity.java代码为 [code]package com.example.hellojni; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; public class MainActivity extends ActionBarActivity { private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); //tv.setText(String.valueOf(GetInt.getInt())); //tv.setText(GetString.getStr()); //tv.setText(String.valueOf(GetFor.getFor1(4))); //tv.setText(String.valueOf(GetFor.getFor2(4))); //tv.setText(String.valueOf(GetWhile.getWhile(5))); //tv.setText(GetIfElse.getIfElse(20)); tv.setText(String.valueOf(GetSwitch.getSwitch(4,2,2))); } static{ System.loadLibrary("HelloJNI"); } } [/code] build_headers.xml代码为 [code] description [/code] 然后双击ANT中的HelloJNI,然后F5刷新工程项目,可以看到jni目录下,多出6个文件,com_example_hellojni_GetFor.h等,此文件里面就是函数.h接口文件,是没有具体代码的,需要把里面的函数复制到jni目录下的HelloJNI.cpp文件中,然后去实现函数的具体部分。 HelloJNI.cpp的代码为 [code]#include #include #include #include #include #include #include int nums[5] = {1, 2, 3, 4, 5}; JNIEXPORT jstring JNICALL Java_com_example_hellojni_GetString_getStr (JNIEnv *env, jclass){ return env->NewStringUTF("static method call"); } /* * Class: com_example_hellojni_GetString * Method: getString * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_hellojni_GetString_getString (JNIEnv *env, jobject){ return env->NewStringUTF("method call"); } /* * Class: com_example_hellojni_GetString * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_example_hellojni_GetString_add (JNIEnv *, jobject, jint a, jint b){ return a+b; } JNIEXPORT jint JNICALL Java_com_example_hellojni_GetInt_getInt (JNIEnv *, jclass){ return 8; } JNIEXPORT jint JNICALL Java_com_example_hellojni_GetFor_getFor1 (JNIEnv *, jclass, jint n){ int i = 0; int s = 0; for (i = 0; i < n; i++){ s += i * 2; } return s; } JNIEXPORT jint JNICALL Java_com_example_hellojni_GetFor_getFor2 (JNIEnv *, jclass, jint n){ int i = 0; int s = 0; for (i = 0; i < n; i++){ s += i * i + nums[n-1]; } return s; } JNIEXPORT jint JNICALL Java_com_example_hellojni_GetWhile_getWhile (JNIEnv *, jclass, jint n){ int i = 1; int s = 0; while(i 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_hellojni_GetSwitch_getSwitch (JNIEnv *, 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; } } [/code] 以上就是一些实例的代码,下面就来分析逆向后的ARM代码。以下反汇编代码都是通过IDA得到的,至于IDA的使用方法,大家可以看看书。 3.2.2. getInt()方法 getInt()的方法代码如下: [code]JNIEXPORT jint JNICALL Java_com_example_hellojni_GetInt_getInt (JNIEnv *, jclass){ return 8; } [/code] 反编译后的代码为: [code]EXPORT Java_com_example_hellojni_GetInt_getInt Java_com_example_hellojni_GetInt_getInt MOVS R0, #8 ;R0 = 8 BX LR ;子程序返回R0 [/code] 3.2.3. getStr()方法 getStr()的方法代码如下: [code]JNIEXPORT jstring JNICALL Java_com_example_hellojni_GetString_getStr (JNIEnv *env, jclass){ return env->NewStringUTF("static method call"); } [/code] 反编译后的代码为: [code]EXPORT Java_com_example_hellojni_GetString_getStr Java_com_example_hellojni_GetString_getStr PUSH {R3,LR} ;将R3和LR入栈 LDR R2, [R0] ;[R0]是JNIEnv,R2=*env,RO一般是放返回值的,调用函数后会被覆盖的,所以要复制出去 LDR R1, =(aStaticMethodCa - 0xF7A) MOVS R3, #0x29C ;R3=0x29C ADD R1, PC ; "static method call" ;R1="static method call" LDR R3, [R2,R3] ;R2偏移R3,是NewStringUTF,可以查看JNI API(Android软件安全与逆向分析7.6节也有介绍),如下图所示,所有的函数在附件中。 BLX R3 ;调用NewStringUTF函数,第一个参数R0,是JNIEnv,子程序返回,第二个参数是R1 [/code] 图片:2015102908554547624cpsr.jpg 3.2.3. getFor1()方法 getFor1()的方法代码如下: [code]JNIEXPORT jint JNICALL Java_com_example_hellojni_GetFor_getFor1 (JNIEnv *, jclass, jint n){ int i = 0; int s = 0; for (i = 0; i < n; i++){ s += i * 2; } return s; } [/code] 反编译后的代码为: 图片:2015102908554547624cpsr.jpg 代码解释如下: [code]EXPORT Java_com_example_hellojni_GetFor_getFor1 Java_com_example_hellojni_GetFor_getFor1 MOVS R0, #0 ;R0 = 0 MOVS R3, R0 ;R3 = 0 B loc_FB0 ;跳转到loc_FB0 ; --------------------------------------------------------------------------- loc_FAA ; CODE XREF: Java_com_example_hellojni_GetFor_getFor1+Ej LSLS R1, R3, #1 ;R1=R3左移一位(即R1=R3*2) ADDS R0, R0, R1 ;R0=R0+R1 ADDS R3, #1 ;R3=R3+1 loc_FB0 ; CODE XREF: Java_com_example_hellojni_GetFor_getFor1+4j CMP R3, R2 ;比较R3和R2,R2是第一个参数,即n BLT loc_FAA ;如果R3NewStringUTF("he is a strong man"); } else{ return env->NewStringUTF("he is an old man"); } } [/code] 反编译后的结果为: 图片:2015102908554547624cpsr.jpg 代码解释如下: [code] EXPORT Java_com_example_hellojni_GetIfElse_getIfEls Java_com_example_hellojni_GetIfElse_getIfElse PUSH {R4,LR} ;R4,LR入栈。 MOVS R3, #0xA7 ;R3=167 LDR R4, [R0] ;[R0]是JNIEnv,此处是R4=*env LSLS R3, R3, #2 ;R3=R3左移2位 CMP R2, #0xF ;比较R2(即n)和16 BGT loc_1002 ;如果R2>16,跳转到loc_1002 LDR R1, =(aHeIsABoy - 0x1002) ;和下一条指令一起,将R1="he is a boy" ADD R1, PC ; "he is a boy" B loc_101A ;跳转到loc_101A ; ------------------------------------------------------------- loc_1002 ; CODE XREF: le_hellojni_GetIfElse_getIfElse+Aj CMP R2, #0x1D BGT loc_100C LDR R1, =(aHeIsAYoungMan - 0x100C) ADD R1, PC ; "he is a young man" B loc_101A ; ------------------------------------------------------------- loc_100C ; CODE XREF: le_hellojni_GetIfElse_getIfElse+14j CMP R2, #0x2C BGT loc_1016 LDR R1, =(aHeIsAStrongMan - 0x1016) ADD R1, PC ; "he is a strong man" B loc_101A ; ------------------------------------------------------------- loc_1016 ; CODE XREF: le_hellojni_GetIfElse_getIfElse+1Ej LDR R1, =(aHeIsAnOldMan - 0x101C) ADD R1, PC ; "he is an old man" loc_101A ; CODE XREF: le_hellojni_GetIfElse_getIfElse+10j ; le_hellojni_GetIfElse_getIfElse+1Aj ... LDR R3, [R4,R3] ;R4的偏移R3*4,是NewStringUTF BLX R3 ;子程序返回,第一个参数是R0,第二个参数是R1 POP {R4,PC} ;一般是和第一行执行相反的出栈动作.将LR放入到PC,PC是下一条命令的地址,改变它的值也就相当跳转. ; End of function Java_com_example_hellojni_GetIfElse_getIfElse [/code] 3.2.6. getSwitch()方法 getSwitch()的代码如下: [code]JNIEXPORT jint JNICALL Java_com_example_hellojni_GetSwitch_getSwitch (JNIEnv *, 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; } } [/code] 反编译后的结果为: 图片:2015102908554547624cpsr.jpg 代码解释如下: [code] EXPORT Java_com_example_hellojni_GetSwitch_getSwitch Java_com_example_hellojni_GetSwitch_getSwitch arg_0 = 0 PUSH {R3,LR} LDR R1, [SP,#8+arg_0] ADDS R0, R2, R3 SUBS R1, #1 CMP R1, #3 ; switch 4 cases BHI locret_105C ; jumptable 0000103E default case,跳转到default,此时返回R0,R0=R2+R3 MOVS R0, R1 BL __gnu_thumb1_case_uqi ; switch jump ; ------------------------------------------------------------- jpt_103E DCB 2 ; jump table for switch statement DCB 4 DCB 6 DCB 9 ; ------------------------------------------------------------- loc_1046 ; CODE XREF: le_hellojni_GetSwitch_getSwitch+Ej ADDS R0, R2, R3 ; jumptable 0000103E case 0 B locret_105C ; jumptable 0000103E default case ; ------------------------------------------------------------- loc_104A ; CODE XREF: le_hellojni_GetSwitch_getSwitch+Ej SUBS R0, R2, R3 ; jumptable 0000103E case 1 B locret_105C ; jumptable 0000103E default case ; ------------------------------------------------------------- loc_104E ; CODE XREF: le_hellojni_GetSwitch_getSwitch+Ej MOVS R0, R3 ; jumptable 0000103E case 2 MULS R0, R2 B locret_105C ; jumptable 0000103E default case ; ------------------------------------------------------------- loc_1054 ; CODE XREF: le_hellojni_GetSwitch_getSwitch+Ej MOVS R0, R2 ; jumptable 0000103E case 3 MOVS R1, R3 BLX __divsi3 locret_105C ; CODE XREF: le_hellojni_GetSwitch_getSwitch+Aj ; le_hellojni_GetSwitch_getSwitch+18j ... POP {R3,PC} ; jumptable 0000103E default case ; End of function Java_com_example_hellojni_GetSwitch_getSwitch [/code] |
|