Android SO逆向1-ARM介绍
来源:互联网 发布:一淘跟淘宝有关系吗 编辑:程序博客网 时间:2024/05/22 15:30
原文: http://drops.wooyun.org/mobile/10009
0x00 概述
把之前学习SO逆向的笔记分享出来,内容比较简单,大牛就可以略过了。
0x01 ARM寄存器
1.1.通用寄存器
1.未分组寄存器:R0~R72.分组寄存器:R8~812R13: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。
N、Z、C、V均为条件码标志位,他们的内容可被运算的结果所改变。
N:正负标志,N=1表示运算的结果为负,N=0表示运算的结果为正或0Z:零标志,Z=1表示运算的结果为0,Z=0表示运算的结果为非0C:进位标志,加法运算产生了进位时则C=1,否则C=0 借位标志,减肥运算产生了借位则C=0,否则C=1V:溢出标志,V=1表示有溢出,V=0表示无溢出
1.3.地址空间
程序正常执行时,每执行一条ARM指令,当前指令计数器增加4个字节。
0x02 汇编语言
2.1.汇编指令格式
<opcode>{<cond>}{S}<Rd>,<Rn>{,<OP2>}格式中<>的内容必不可少,{}中的内容可省略<opcode>:表示操作码,如ADD表示算术加法{<cond>}:表示指令执行的条件域,如EQ、NE等。{S}:决定指令的执行结果是否影响CPSR的值,使用该后缀则指令执行的结果影响CPSR的值,否则不影响<Rd>:表示目的寄存器<Rn>:表示第一个操作数,为寄存器<op2>:表示第二个操作数,可以是立即数、寄存器或寄存器移位操作数例子: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.指令的条件执行
指令的条件后缀只是影响指令是否执行,不影响指令的内容
例子:ADDEQ R4,R3,#1 相等则相加,即CPSR中Z置位时该指令执行,否则不执行。
2.4.ARM指令分类
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+4LDR R0,[R1],#4 R0 = [R1],R1 = R1 +4LDR 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 {<cond>}{S} Rd,op2 将op2传给RdMOV R1, R0 将寄存器R0的值传到寄存器R1MOV PC,R14 将寄存器R14的值传到PC,常用于子程序返回MOV R1,R0,LSL #3 将寄存器R0的值左移3位后传给R1MOV R0,#5 将立即数5传给R0
2.6.2. MVN指令
MVN {<cond>}{S}Rd, op2 将op2取反传给RdMVN R0,#0 将0取反后传给R0,R0 = -1MVN R1,R2 将R2取反,结果保存到R1
2.6.3. 移位指令
LSL 逻辑左移LSR 逻辑右移ASR 算术右移ROR 循环右移RRX 带扩展的循环右移
2.6.4. ADD加法指令
ADD{<cond>}{S}Rd, Rn, op2ADD R0,R1,R2 R0 = R1 + R2ADD R0,R1,#5 R0 = R1 + 5ADD R0,R1,R2,LSL #2 R0 = R1 + (R2左移2位)
2.6.5. ADC带进位加法指令
ADC{<cond>}{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{<cond>}{S} Rd,Rn,op2 Rd = Rn - op2SUB R0,R1,R2 R0 = R1 - R2SUB R0,R1,#6 R0 = R1 -6SUB R0,R2,R3,LSL #1 R0 = R2 - (R3左移1位)
2.6.7. SBC带借位减法指令
SBC{<cond>}{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影响CPSRSBC R1,R3,R5 高32位相减,去除C的反码
2.6.8. RSC带借位的逆向减法指令
RSC{<cond>}{S} Rd,Rn,op2 把操作数op2减去Rn,再减去CPSR中的C标志位的反码,并将结果保存到Rd中,Rd = op2 - Rn - !C
2.6.9. 逻辑运算指令
AND{<cond>}{S} Rd,Rn,op2 按位与,Rd = Rn AND op2ORR{<cond>}{S} Rd,Rn,op2 按位或,Rd = Rn OR op2EOR{<cond>}{S} Rd,Rn,op2 按位异或,Rd = Rn EOR op2
2.6.10. CMP比较指令
CMP{<cond>}{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{<cond>}{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{<cond>}{S} Rd,Rm,Rs Rd = Rm * RsMULS R0,R1,R2MLA{<cond>}{S} Rd,Rm,Rs,Rn Rd = (Rm * Rs) + RnMLAS R0,R1,R2,R3
2.7.数据加载与存储指令
2.7.1. LDR/STR字数据加载/存储指令
LDR/STR{<cond>}{T}Rd,addr LDR指令用于从存储器中将一个32位的字数据加载到目的寄存器Rd中,当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当做目的地址,从而可以实现程序流程的跳转。STR指令用于从源寄存器中将一个32位的字数据存储到存储器中,和LDR相反。后缀T可选。LDR R4,START 将存储地址为START的字数据读入R4STR R5,DATA1 将R5存入存储地址为DATA1中LDR R0,[R1] 将存储器地址为R1的字数据读入存储器R0LDR R0,[R1,R2] 将存储器地址为R1+R2的字数据读入存储器R0LDR R0,[R1,#8] 将存储器地址为R1+8的字数据读入存储器R0LDR R0,[R1,R2,LSL #2] 将存储器地址为R1+R2*4的字数据读入存储区R0STR R0,[R1,R2]! 将R0字数据存入存储器地址R1+R2的存储单元中,并将新地址R2+R2写入R2STR R0,[R1,#8]! 将R0字数据存入存储器地址R1+8的存储单元中,并将新地址R2+8写入R2STR R0,[R1,R2,LSL #2] 将R0字数据存入存储器地址R1+R2*4的存储单元中,并将新地址R2+R2*4写入R1LDR R0,[R1],#8 将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+8写入R1 LDR R0,[R1],R2 将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2写入R1LDR R0,[R1],R2,LSL #2 将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2*4写入R1
2.7.2. LDRB/STRB字节数据加载/存储指令
LDRB/STRB{<cond>}{T}Rd,addr LDRB指令用于从存储器中将一个8位的字节数据加载到目的寄存器中,同时将寄存器的高24位清零,当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当做目的地址,从而可以实现程序流程的跳转。STRB指令用于从源寄存器中将一个8位的字节数据存储到存储器中,和LDRB相反。后缀T可选。
2.7.3. LDRH/STRH半字数据加载/存储指令
LDRH/STRH{<cond>}{T}Rd,addr LDRH指令用于从存储器中将一个16位的半字数据加载到目的寄存器中,同时将寄存器的高16位清零,当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当做目的地址,从而可以实现程序流程的跳转。STRH指令用于从源寄存器中将一个16位的半字数据存储到存储器中,和LDRH相反。后缀T可选。
2.7.4. LDM/STM批量数据加载/存储指令
LDM/STM{<cond>}{<type>}Rn{!},<regs>{^} LDM用于从基址寄存器所指示的一片连续存储器中读取数据到寄存器列表所指向的多个寄存器中,内存单元的起始地址为基址寄存器Rn的值,各个寄存器由寄存器列表regs表示,该指令一般用于多个寄存器数据的出栈操作STM用于将寄存器列表所指向的多个寄存器中的值存入由基址寄存器所指向的一片连续存储器中,内存单元的起始地址为基址寄存器Rn的值,各个寄存器又寄存器列表regs表示。该指令一般用于多个寄存器数据的进栈操作。type表示类型,用于数据的存储与读取有以下几种情况:IA:每次传送后地址值加。IB:每次传送前地址值加。DA:每次传送后地址值减。DB:每次传送前地址值减。用于堆栈操作时有如下几种情况:FD:满递减堆栈ED:空递减堆栈FA:满递增堆栈EA:空递增堆栈
2.7.5. SWP字数据交换指令
SWP{<cond>}<Rd>,<Rm>,[<Rn>] Rd = [Rn],[Rn] = Rm,当寄存器Rm和目的寄存器Rd为同一个寄存器时,指令交换该急促亲和存储器的内容SWP R0,R1,[R2] R0 = [R2],[R2] = R1SWP R0,R0,[R1] R0 = [R1],[R1] = R0SWPB指令用于将寄存器Rn指向的存储器中的字节数据加载到目的寄存器Rd中,目的寄存器的高24位清零,同时将Rm中的字数据存储到Rn指向的存储器中。
2.8.分支语句
2.8.1. 分支指令B
B{<cond>}label 跳转到label处执行,PC=label例子:backword SUB R1,R1,#1 CMP R1,#0 比较R1和0 BEQ forward 如果R1=0,跳转到forware处执行 SUB R1,R2,#3 SUB R1,R1,#1forward ADD R1,R2,#4 ADD R2,R3,#2 B backword 无条件跳转到backword处执行
2.8.2. 带返回的分支指令BL
BL{<cond>}label 在跳转之前,将PC的当前内容保存在R14(LR)中保存,因此,可以通过将R14的内容重新加载到PC中,返回到跳转指令之后的指令处执行。该指令用于实现子程序的调用,程序的返回可通过把LR寄存器的值复制到PC寄存器中来实现。例子:BL func 跳转到子程序ADD R1,R2,#2 子程序调用完返回后执行的语句,返回地址....func 子程序...MOV R15,R14 复制返回地址到PC,实现子程序的返回
2.8.3. 带状态切换的分支指令BX
BX{<cond>} Rm 当执行BX指令时,如果条件cond满足,则处理器会判断Rm的位[0]是否为1,如果为1则跳转时自动将CPSR寄存器的标志T置位,并将目标地址的代码解释为Thumb代码来执行,则处理器会切换到Thumb状态,反之,若Rm的位[0]为0,则跳转时自动将CPSR寄存器的标志T复位,并将目标地址处的代码解释为ARM代码来执行,即处理器会切换到ARM状态。
注意:bx lr的作用等同于mov pc,lr。即跳转到lr中存放的地址处。 非零值存储在R0中返回。
那么lr存放的是什么地址呢?lr就是连接寄存器(Link Register, LR),在ARM体系结构中LR的特殊用途有两种:一是用来保存子程序返回地址;二是当异常发生时,LR中保存的值等于异常发生时PC的值减4(或者减2),因此在各种异常模式下可以根据LR的值返回到异常发生前的相应位置继续执行。
当通过BL或BLX指令调用子程序时,硬件自动将子程序返回地址保存在R14寄存器中。在子程序返回时,把LR的值复制到程序计数器PC即可实现子程序返回。
2.9堆栈
2.9.1. 进栈出栈
出栈使用LDM指令,进栈使用STM指令。LDM和STM指令往往结合下面一些参数实现堆栈的操作。FD:满递减堆栈。ED:空递减堆栈。FA:满递增堆栈。EA:空递增堆栈。满堆栈是指SP(R13)指向堆栈的最后一个已使用地址或满位置(也就是SP指向堆栈的最后一个数据项的位置);相反,空堆栈是指SP指向堆栈的第一个没有使用的地址或空位置。LDMFD和STMFD分别指POP出栈和PUSH入栈
2.9.2. PUSH指令
PUSH{cond} reglist PUSH将寄存器推入满递减堆栈PUSH {r0,r4-r7} 将R0,R4-R7寄存器内容压入堆栈
2.9.3. POP指令
POP{cond} reglist POP从满递减堆栈中弹出数据到寄存器POP {r0,r4-r7} 将R0,R4-R7寄存器从堆栈中弹出
0x03 创建Android NDK程序
3.1. NDK程序创建过程
1.创建一个android程序。2.在程序右键-->Android Tools-->Add 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目录下的文件。
3.2. 编写程序
3.2.1. CLASS文件
在com.example.hellojni包中创建class文件,本文一次创建多个实例,共参考。
GetInt.java代码为
package com.example.hellojni;public class GetInt { public static native int getInt();}
GetString.java代码为
package com.example.hellojni; public class GetString { public static native String getStr(); public native String getString(); public native int add(int a, int b); }
GetFor.java代码为
package com.example.hellojni; public class GetFor { public static native int getFor1(int n); public static native int getFor2(int n);}
GetIfElse.java代码为
package com.example.hellojni; public class GetIfElse { public static native String getIfElse(int n);}
GetWhile.java代码为
package com.example.hellojni; public class GetWhile { public static native int getWhile(int n);}
GetSwitch.java代码为
package com.example.hellojni; public class GetSwitch { public static native int getSwitch(int a,int b,int i);}
MainActivity.java代码为
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"); }}
build_headers.xml代码为
<?xml version="1.0" encoding="UTF-8"?> <!-- ====================================================================== 2014-10-28 上午8:05:50 HelloJNI description 0xExploit ====================================================================== --><project name="HelloJNI" default="BuildAllHeaders"> <description> description </description> <!-- ================================= target: BuildAllHeaders ================================= --> <target name="BuildAllHeaders"> <antcall target="BuildGetStringHeader"></antcall> <antcall target="BuildGetIntHeader"></antcall> <antcall target="BuildGetForHeader"></antcall> <antcall target="BuildGetWhileHeader"></antcall> <antcall target="BuildGetIfElseHeader"></antcall> <antcall target="BuildGetStringHeader"></antcall> </target> <!-- - - - - - - - - - - - - - - - - - target: depends - - - - - - - - - - - - - - - - - --> <target name="BuildGetStringHeader"> <javah destdir="./jni" classpath="./bin/classes/" class="com.example.hellojni.GetString"></javah> </target> <target name="BuildGetIntHeader"> <javah destdir="./jni" classpath="./bin/classes/" class="com.example.hellojni.GetInt"></javah> </target> <target name="BuildGetForHeader"> <javah destdir="./jni" classpath="./bin/classes/" class="com.example.hellojni.GetFor"></javah> </target> <target name="BuildGetWhileHeader"> <javah destdir="./jni" classpath="./bin/classes/" class="com.example.hellojni.GetWhile"></javah> </target> <target name="BuildGetIfElseHeader"> <javah destdir="./jni" classpath="./bin/classes/" class="com.example.hellojni.GetIfElse"></javah> </target> <target name="BuildGetSwitchHeader"> <javah destdir="./jni" classpath="./bin/classes/" class="com.example.hellojni.GetSwitch"></javah> </target> </project>
然后双击ANT中的HelloJNI,然后F5刷新工程项目,可以看到jni目录下,多出6个文件,com_example_hellojni_GetFor.h等,此文件里面就是函数.h接口文件,是没有具体代码的,需要把里面的函数复制到jni目录下的HelloJNI.cpp文件中,然后去实现函数的具体部分。
HelloJNI.cpp的代码为
#include <jni.h>#include <com_example_hellojni_GetInt.h>#include <com_example_hellojni_GetString.h>#include <com_example_hellojni_GetFor.h>#include <com_example_hellojni_GetIfElse.h>#include <com_example_hellojni_GetWhile.h>#include <com_example_hellojni_GetSwitch.h> 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 <= n){ s += i++; } return s;}JNIEXPORT jstring JNICALL Java_com_example_hellojni_GetIfElse_getIfElse (JNIEnv *env, 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_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; }}
以上就是一些实例的代码,下面就来分析逆向后的ARM代码。以下反汇编代码都是通过IDA得到的,至于IDA的使用方法,大家可以看看书。
3.2.2. getInt()方法
getInt()的方法代码如下:
JNIEXPORT jint JNICALL Java_com_example_hellojni_GetInt_getInt (JNIEnv *, jclass){ return 8;}
反编译后的代码为:
EXPORT Java_com_example_hellojni_GetInt_getIntJava_com_example_hellojni_GetInt_getIntMOVS R0, #8 ;R0 = 8BX LR ;子程序返回R0
3.2.3. getStr()方法
getStr()的方法代码如下:
JNIEXPORT jstring JNICALL Java_com_example_hellojni_GetString_getStr (JNIEnv *env, jclass){ return env->NewStringUTF("static method call");}
反编译后的代码为:
EXPORT Java_com_example_hellojni_GetString_getStrJava_com_example_hellojni_GetString_getStrPUSH {R3,LR} ;将R3和LR入栈LDR R2, [R0] ;[R0]是JNIEnv,R2=*env,RO一般是放返回值的,调用函数后会被覆盖的,所以要复制出去LDR R1, =(aStaticMethodCa - 0xF7A) MOVS R3, #0x29C ;R3=0x29CADD 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
3.2.3. getFor1()方法
getFor1()的方法代码如下:
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;}
反编译后的代码为:
代码解释如下:
EXPORT Java_com_example_hellojni_GetFor_getFor1Java_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+1loc_FB0 ; CODE XREF: Java_com_example_hellojni_GetFor_getFor1+4j CMP R3, R2 ;比较R3和R2,R2是第一个参数,即n BLT loc_FAA ;如果R3<R2,跳到loc_FAA BX LR ;否则,子程序返回R0;End of function Java_com_example_hellojni_GetFor_getFor1
3.2.4. getWhile()方法
getWhile()的函数代码如下:
JNIEXPORT jint JNICALL Java_com_example_hellojni_GetWhile_getWhile (JNIEnv *, jclass, jint n){ int i = 1; int s = 0; while(i <= n){ s += i++; } return s;}
反编译后的结果为:
代码解释如下:
EXPORT Java_com_example_hellojni_GetWhile_getWhile Java_com_example_hellojni_GetWhile_getWhile MOVS R0, #0 ;R0 = 0 MOVS R3, #1 ;R3 = 1 B loc_FEA ;跳转到loc_FEA ; ------------------------------------------------------------- loc_FE6 ; CODE XREF: le_hellojni_GetWhile_getWhile+Cj ADDS R0, R0, R3 ;R0=R0+R3 ADDS R3, #1 ;R3=R3+1 loc_FEA ; CODE XREF: le_hellojni_GetWhile_getWhile+4j CMP R3, R2 ;比较R3和R2,R2为第一个参数,即n BLE loc_FE6 ;如果R3<R2,跳转到loc_FE6 BX LR ;否则返回结果R0 ; End of function Java_com_example_hellojni_GetWhile_getWhile
3.2.5. getIfElse()方法
getIfElse()的代码如下
JNIEXPORT jstring JNICALL Java_com_example_hellojni_GetIfElse_getIfElse (JNIEnv *env, 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"); }}
反编译后的结果为:
代码解释如下:
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
3.2.6. getSwitch()方法
getSwitch()的代码如下:
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; }}
反编译后的结果为:
代码解释如下:
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
- Android SO逆向1-ARM介绍
- Android SO逆向1-ARM介绍
- Android SO逆向1-ARM介绍
- Android逆向分析--so文件
- Android SO逆向-多重继承
- Android so逆向基本知识总结
- Android逆向so文件,调试加解读
- Android逆向so文件,调试加解读
- Android SO逆向2-实例分析
- Android SO逆向-数组与指针
- Android SO逆向2-实例分析
- Android逆向so文件,调试加解读
- Android 逆向调用so(一)
- Android 逆向破解之so加载时机
- Android 逆向apk的.so动态库
- Android逆向分析基础-ARM 汇编语言基础
- Porting snappy/libspicec.so onto Android-ARM
- Android SO ARM及Thumb2指令集
- Android菜鸟认知——Intent和Intent filter初印象
- 原生态JAVAEE酒店管理系统系列五
- myeclipse加载项目,出现"大量红叉"
- windows下的C:\Windows\System32\drivers\etc\hosts
- HDU2852 KiKi's K-Number(二分+树状数组)
- Android SO逆向1-ARM介绍
- PAT_Basic 1011
- 11.smarty 对象
- 大学毕业4年-回顾和总结(2)-钱,收入和支出
- JVM之对象生死
- Node.js模块引入
- Mathematica字符串处理之-mywife.cc
- 严格模式
- 障碍滑雪比赛(Slalom,UVa 11627)