ARM 汇编的一些知识
寄存器数量
不同模式下访问的寄存器
寄存器用途
条件执行后缀
ARM 的指令流水线
IDA 中的一些设置
so 文件加载断点
android studio 只生成 ARM 的 so 文件
IDA 中添加自动注释
Dump 内存
使用 IDA 分析 so 文件-导入 Jni.h 识别类型
ARM 汇编的一些知识
寄存器数量
ARM 处理器一共有 37 个 32 位寄存器。
30 个为“通用“寄存器: r0-r14
未分组:r0-r7,即只有一个寄存器
分 组:r8-r14,即有多个同名寄存器
r8-r12 :两个
r13-r14:6 个 r13(sp),R14(lr)
1 个固定的程序计数器 : pc (又称 r15)
6 个为状态寄存器。 : cpsr spsr
不能被同时访问,一种模式下最多同时访问 18 个寄存器
不同模式下访问的寄存器
寄存器用途
sp(r13) - 堆栈指针
lr(r14) - 连接寄存器
调用子程序时存放调用地址,存放返回地址
pc(r15) - 程序计数器,相当于 windows 的 EIP
(1)跳转到指定地址
mov pc,lr // 直接修改 pc ,完成跳转
bx lr // 跳转到 lr 保存的地址
(2)在函数入口保存寄存器信息
stmfd sp!, {r11,lr} // 保存大括号中的寄存器到栈中,从右往左
(3)使用 ldm 指令修改 pc,完成函数返回
ldmfd sp!, {r11,pc} // 将栈中数据依次加载到寄存器中,从左往右
cpsr – 当前程序状态寄存器
spsr–备份的程序状态寄存器
条件执行后缀
无条件跳转指令: B
带条件跳转指令:BXX,BNE, BEQ
ARM 的指令流水线
ARM 指令 当前执行的 PC 和与看到的 PC 相差 8
即 看汇编时,需要 pc+8 才是真正的 pc
Thumb 指令 当前执行的 PC 和与看到的 PC 相差 4
即 看汇编时,需要 pc+4 才是真正的 pc
0001000 mov r0, #8 ; r0 = 8
0001004 add r0,pc,r0 r0 =pc+r0 = r0 + 当前指令+8+r0
IDA 中的一些设置
在打开的 IDA 数据库文件中,直接附加调试 so 文件时会有警告提示
选择指令地址,快捷键 Alt+G,可以切换指令集
在 IDA 动态调试时,使用插件 KeyPatch 可以汇编 ARM 指令或者 thumb 指令
Keypatch 是一个 python 插件,其要求是 python2.7 ,依赖一个库 keystone
so 文件加载断点
① Init 段的 init_proc 函数(linker, so 文件加载的解释器模块)
② Init_array 段的构造数组函数(linker)
③ Jni_Onload(libdvm.so)
第一步:分析 android 源码,找到调用的代码
第二步:根据代码,定位到模块
可以看到源码是 linker.cpp, 模块就是在 linker 模块
第三步:根据源码特征,在模块中定位到反汇编代码
从 android 设备中下载 linker 模块,模块是/system/bin 目录下
根据字符串定位: Calling %s @ %p for '%s'
查找数据引用的代码,找到调用代码
274C
第四步:下断点
② 找 Jni_Onload 函数
函数指针调用,说明也是寄存器调用
("[Calling JNI_OnLoad for "%s"]"
Jni_Onload 调用的模块是在 system/lib/libdvm.so
503cc
41464000+503cc=414B43CC
系统:android 4.4.4_r1
linker: 274C 调用 init 段以及 init_array 段的函数的偏移
libdvm.so: 503cc, 调用 JNI_Onload 的偏移
android studio 只生成 ARM 的 so 文件
IDA 中添加自动注释
效果如下:
Dump 内存
输入脚本命令的窗口
使用 Ctrl+S,查看模块内存,找到需要 dump 的模块,找到首地址和结束地址
static main(void)
{
auto fp, begin, end, dexbyte;
fp = fopen("F:\dump.so", "wb");
begin = 0x75394000;
end = 0x7539a000;
for ( dexbyte = begin; dexbyte < end; dexbyte ++ )
fputc(Byte(dexbyte), fp);
}
使用 IDA 分析 so 文件-导入 Jni.h 识别类型
导入 jni.h 之后,可以修改参数类型