前言

交互式反汇编器,简称为IDA。是目前最棒的一个静态反编译软件,为众多0day世界的成员和ShellCode安全分析人士不可缺少的利器!此章节让我们熟悉通过IDA修改参数、函数、返回值,同时详细解读标志位的概念,熟悉堆栈及详细解读ARM三级流水线概念和ARM编码编译为二进制的全过程。

一、环境配置

首先配置IDA与安卓联动

IDA动态调用手机apk,请参考:安卓逆向-从环境搭建到动态调试apk IDA部分https://www.freebuf.com/articles/mobile/285861.html

1)加载server

2)端口转发+执行app(javandk1这个测试app)

adb install E:\IDA7.0\test\javandk1.apk    #安装appadb shell am start -D -n com.example.javandk1/.MainActivity    #启动app

adb forward tcp:23946 tcp:23946    #端口转发

3)打开启动DDMS

DDMS

4)打开IDA 32并调试ndk运行

5)并勾选三项调试

6)F9启动+执行jdb调用DDMS

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8600

此时可以看到加载的不是so,而是/arm/base.odex文件,那么此时怎么加载我们需要的so库,分析JNI_onload呢?

问题:解决方法无libjavandk1.so库

1)在Modules查询java:

2)选择libjavacore.so库-在搜索JNI_load选择

那么此时就进入了libjavacore.so的JNI_onload了

3)接下来下一个断点:点击或F2

4)F9运行后跳转到断点截断处,此时回到Modules继续搜索java:

此时就出现需要调试的so库文件:libjavandk1.so库文件

5)那么继续选择进入搜索JNI即可

此时问题就解决了

6)继续来到JNI_onload下断点,开始分析判断传参

二、参数分析

1、第一种方法

这里BLX传入了几个参数?

这里BLX中R3需要跳转,那么R3也是有规定给地址的,所以我们这里的有4个参数R0-R3

R0-R3:4个寄存器->参数寄存器

R0-R3:用于函数参数及返回值的传递R4-R6,R8,R10-R11:普通的通用寄存器R7:栈帧指针(Frame Pointer),指向前一个保存的栈帧(stack frame)和链接寄存器(link register,lr)在栈上的地址。R9:操作系统保留R12:IP(intra-procedure scratch)R13:SP(stack pointer) 是栈顶指针R14:LR(link register) 存放函数的返回地址R15:PC (program counter),指向当前指令地址

如果R3作为一个地址的存放,当你把一个函数地址存放在R3里面,根据规定R3已经作为地址存放了,如果这个函数要传参,只能从R0-R2,这个三个寄存器里面进行传参。

注:如果想判断有几个参数,看这个BLX(指令)后面的值(是不是寄存器,如果是,Rn小于4,参数个数就是当前减1;大于等于4,参数寄存器就是R0-R3)

此时回到图中,BLX R3;三个参数,用了R0-R2后,只会依次使用下一个寄存器存放跳转的地址;

BLX R4(三个参数:R0,R1,R2,R3)

现在打开堆栈看看

在BLX R3处打桩,F9运行到下一个断点处

记住此时的栈顶是00000001,这时候SP:FF9ABBE0指向栈顶

这时候单步F7

这时候查看,SP的值还是FF9ABBE0是没有变化的

2、第二种方法

静态调试SO库,按F5查看伪代码

可以查看到伪c代码(int a1,int a2)就是查看参数个数

二、修改寄存器-返回值

我们随便找的几个BLX指令的地方。

BLX R3此时传入几个参数 ?三个

那么BLX R12传入几个参数?四个

按F5进入伪C代码

静态注册参数怎么修改呢?选择你要修改的参数,按Y

如何修改寄存器的值呢

这时候就可以修改寄存器的值了

例如:cmp R0,0,那么就执行BEN,意思就是修改了条件为0后,就不执行改条件,反调试会更深入演示

Y键修改C代码(退出C代码按ESC)

R0-R3:用于函数参数及返回值的传递R4-R6,R8,R10-R11:普通的通用寄存器R7:栈帧指针(Frame Pointer),指向前一个保存的栈帧(stack frame)和链接寄存器(link register,lr)在栈上的地址。R9:操作系统保留R12:IP(intra-procedure scratch)R13:SP(stack pointer) 是栈顶指针R14:LR(link register) 存放函数的返回地址R15:PC (program counter),指向当前指令地址

三、修改寄存器-修改函数

1、方法1-修改Hex

如果我要改这条指令

根据三级流水线,需要在前三个代码断点

不想让程序执行怎么办?直接同步下PC寄存器

现在你想在HEX处找的PC的指令,当鼠标放在PC指令处,hex自动选择

然后在Hex View-1处快捷键F2操作,修改,

修改为00 00 00 00后,然后在快捷键F2保存下

这时候指令就没了

2、方法2-修改General

或者直接设置PC(只适合调试时候测试使用)

例如此刻需求是,跳过大红框内容,直接执行MOV R0, #0x10004这条指令,此刻PC在F42AA08C

只需要在General registers处修改PC值为MOV处的指令值即可,那么此时MOV出指令值为:

F42AA0A4

开始修改,双击General registers-PC处,修改为MOV的值:

直接跳转mov处

四、IDA配置堆栈信息

在PC窗口处配置出堆栈指针和Hex View对应的十六进制的值

五、IDA标志位详解

CPSR标志位详解

一边情况下标志位情况

断点后的情况

用最简单的理解,这些到底有什么用

通过图很好了解,例如BLX进行运算是正数还是负数,经过运算后值是不是为零,可以理解为条件标志位,帮我们记录一些状态!

标志位的结果内容可被算数或逻辑运算的结果所改变,并且可以决定某条指令是否被执行。

最重要的是N、Z、C、V、Q、T,那么T是什么意思?

T标志位︰该为反应处理器的运行状态。当该位为1时,程序运行于THUMB状态,否则运行于ARM状态。该信号反映在外部引脚TBIT上。在程序中不得修改CPSR中的TBIT位,否则处理器工作状态不能确定。

ARM状态

arm处理器工作于32位指令的状态,所有指令均为32位

thumb状态

arm执行16位指令的状态,即16位状态

六、ARM汇编三级流水线详解

1、什么是三级流水线

前缀

ARM7处理器采用3级流水线来增加处理器指令流的速度,能提供0.9MIPS/MHz的指令处理速度。MIPS(Million Instruction Per Second)表示每秒多少百万条指令。比如0.9MIPS,表示每秒九十万条指令。MIPS/MHz表示CPU在每MHz的运行速度下可以执行多少个MIPS,如0.9MIPS/MHz则表示如果CPU运行在1MHz的频率下,每秒可执行90万条指令。

三级流水线使用3个阶段,因此指令分为3个阶段执行

1)取指从存储器装载一条指令
2)译码识别将要被执行的指令
3)执行处理指令并将结果写会寄存器

但是处理实际是这样的:ARM正在执行第一条指令的同时对第二条指令进行译码,并将第三条指令从存储器中取出

所以,ARM7流水线只能在取第4条指令时,第1条指令才算完成执行

无论处理器处于何种状态,程序计数器R15(PC)总是指向”正在取指“的指令,而不是指向”正在执行“的指令或者正在”译码“的指令。

人们一边会习惯性的将正在执行的指令作为参考点,即当前第一条指令,所以,pc总是指向第三条指令

或者说PC总是指向当前正在执行的指令在加2条指令的地址

2、三级流水线详解

处理器处于ARM状态是,每条指令为4个字节,所以PC指令为正在执行的指令地址加8个字节,即是:

PC值=当前程序执行位置+8字节

处理器处于Thumb状态时,每条指令为2字节,所以PC值为正在执行的指令地址加4字节,即是:

PC值=当前程序执行位置+4字节

下面一个列子很好的说明了这个问题

libjavandk1.so:F42AA090 010 00 C0 90 E5 LDR    R12, [R0]    #正在被执行的指令libjavandk1.so:F42AA094 010 5C C3 9C E5 LDR    R12, [R12,#0x35C]  #正在被编码的指令libjavandk1.so:F42AA098 010 3C FF 2F E1 BLX    R12   #正在被取指的指令 PC=F42AA098libjavandk1.so:F42AA09C 010 00 00 50 E3 CMP    R0, #0    #PC+4=F42AA09C

另外补充说明就是根据以上描述,流水线只有被指令填满时才能发挥最大的效能,既每时钟周期完成一条指令的指向(仅单周期指令)

如果程序发送跳转,流水线会被清空,这将需要几个时钟才能使流水线被再次填满。因此,尽量地少使用跳转指令可以提高程序的指令效率

PC代表程序计数器,流水线使用三个阶段,因此指令为分为三个阶段执行:

1、取指(从存储器装载一条指令)
2、译码(识别将要被执行的指令)
3、执行(处理指令并将结果写回寄存器)

而R15(PC)总是指向”正在取指“的指令,而不是指向”正在执行“的指令或者正在”译码“的指令。一般来说,人习惯性约定将”正在执行“的指令作为参考点,称之为第一条指令,因此PC总是指向第三条指令,当ARM状态时,每条指令为4字节长,所以PC始终指向改指令地址加8字节的地址,既:PC值=当前程序执行位置+8;

ARM指令是三级流水线,取指、译指、执行是同时执行的,现在PC指向的是正在取值的地址,那么CPU正在译指的指令地址是PC-4(假设ARM状态下,一个指令占4个字节),cpu正在执行的指令地址是cpu-8,也就是说PC所指向的地址和现在所执行的指令地址相差8。

当突然发生中断的时候,保存的是PC的地址

这样你就知道了,如果返回的时候返回PC,那么中间就有一个指令没有执行,所以用SUB pc lr-irq#4。

这个需要参考《ARM指令》来学习

3、案例-ARM指令转换为机器码

下面我们将一个ARM指令转换为机器码试试

00001BD0 BEQ lc04
BEQ lc04;跳转指令,执行条件EQ,即相等跳转到lc04

来计算这条指令

1)首先ARM指令是32位的,因为这里是BEQ的B的跳转指令,32位可以拆分为格式如下

2)前四位:31-28就是cond,这里的意思就是条件码,例如EQ、NE

0000

3)紧接着3位:27-25(这里由于B指令是101固定的)

0000 101

4)在往下24位:(1或者0,具体判断:带有连接的如果是BL指 令对应的二进制操作数就是1 无条件跳转B指令就是0。)

0000 1010

5)0-23位:偏移地址:目标地址与该指令的相对偏移

偏移的计算公式:

(目标跳转的地址-(当前这条指令的地址+8))/4
(1c04-(1bd0+8))/4=1011
1c04:要跳转的地址
1bd0:此指令所在的地址
+8:arm指令有3级流水线的原因,如果执行1c04地址指令,需要加8
/4:因为要做对 其处理

我们拼接一下计算出来的二进制ARM指令机器码,因为0-23位算出来的是1011,

1011对应0-3位置

4-23用0补齐

结果

0000 1010 0000 0000 0000 0000 0000 1011

转换为16进制就是

0A 00 00 0B

因为是小端模式,需要换一下位置结果就是

0B 00 00 0A

我们在IDA中测试一下

将IDA中hex中随便一个地方改为0B 00 00 0A

我们看到指令变为了BEQ loc_F42AA0D4

我们计算利用偏移公式计算F42AA0D4(目标地址)和F42AA0A0(当前地址)结果是不是1011

(A0D4-(A0A0+8))/4=1011

我们这里就简单了解到了ARM指令如何计算为ARM指令机器码

七、总结

此章节我们详细学习了使用IDA在静态注册和动态注册下分析和修改参数,学习修改返回值、函数,对IDA堆栈功能模块和标志位功能模块进行深入解析,同时了解了ARM中三级流水线并利用案例熟悉ARM指令转换为机器码的案例。