app安卓逆向之Native层代码静态分析基础
Native层代码静态分析
1.背景分析
在安卓逆向的过程当中会遇到以下场景
- 经过上一阶段的Java层代码静态分析以及动态调试,发现加密参数的生成方法调用了Native层方法
- 经过对Native层代码分析后修改对应代码,替换原so文件发现app运行异常
针对以上的情景,我们需要使用IDA直接对Native层方法进行静态分析,找出关键代码并且完成相应的代码修改及复现,最终的目的是完成加密算法的还原或是本地调用对应的so文件。
2.概述
本文主要介绍以下安卓Native层逆向的基础知识,主要包括以下部分
- ARM指令
- IDA的基本使用方法
- Java层调用Native层方法原理
- Native层代码的修改
- so文件的替换
相关软件下载
本教程中用到的所有软件都保存在该网盘中,其中包括jadx、ida7.0
https://pan.baidu.com/s/1XsMANMxzdxlrQAnEjIedxQ
密码:zzkk
3.开始
3.1 ARM指令
首先,为什么要掌握基本的ARM指令?
在安卓的Native层逆向中,使用IDA工具对so文件进行反编译,得到的是原始的ARM汇编指令,类比之前的Java层逆向需要懂Smali代码一样,因此需要掌握基本的ARM指令知识。
常见的寻址方式
1、立即数寻址
MOV R0,#64 (将立即数64传给寄存器R0)
2、寄存器寻址
ADD R0,R1,R2 (将寄存器R1与寄存器R2的值相加,得到的和传给R0)
3、寄存器间接寻址
LDR R0,[R1] (将寄存器R1中的值作为地址,对这个地址进行寻址获取操作数,取得的操作数传给寄存器R0)
4、寄存器基址变址寻址
LDR R0,[R1,#4] (将寄存器R1的值+4作为地址,对这个地址进行寻址获取操作数,取得的操作数传给寄存器R0)
5、多寄存器寻址
LDMIA R0,{R1,R2,R3,R4} (将R0寄存器的值寻址获得操作数给R1,然后将R0寄存器的值+4寻址获得操作数给R2,后面R0+8,R0+12如此类推)
LDMIB R0,{R1,R2,R3,R4} (将R0寄存器的值+4寻址获得操作数给R1,后面R0+8,R0+12如此类推)
6、堆栈寻址
STMFD sp!,{R1-R7,LR} (将R1~R7,LR寄存器的值压入堆栈,满递减堆栈)
LDMED sp!,{R1-R7,LR} (将堆栈中的数据取回R1~R7,LR寄存器,空递减堆栈)
LD : load 加载,出栈操作 ST : store 存储,入栈操作 M : multi 多次 F: full 满栈,SP指向最后一个数据 E: empty 空栈,SP指向与最后一个数据相邻的下一个可写入存储单元 D: descending 递减,代表栈的增长方向 A: ascending 递增,代表栈的增长方向
E\F详解
D\A详解
常用寄存器
R0-R3:用于参数返回值的传递
R4-R6,R8,R10-R11:普通的常用寄存器
R7:栈帧指针,指向前一个保存的栈帧,以及链接寄存器在栈上的地址
R9:操作系统保留
R12:又叫IP,指令指针寄存器,CS:IP用于指定指令的起始地址
R13:又叫SP,栈顶指针
R14:又叫LR,存放函数的返回地址
R15:又叫PC,指向下一条指令的地址,也即将将要执行的指令代码
常用指令
ADD:加指令
SUB:减指令
STR:寄存器内容入栈
LDR:寄存器内容出栈
.W:表示指令宽度,确保生成32位长度指令
BL\BLX:函数调用,对应ARM与Thumb指令集
CMP:操作数比较
示例代码
#include <stdio.h>
int func(int a,int b,int c,int d,int e,int f){
int g = a + b + c + d + e + f;
return g;
}
add r0,r1; 将参数a+b求和赋给r0
ldr.w r12,[SP]; 将栈顶f的值赋给r12
add r0,r2; 将c的值和r0求和赋给r0
ldr.w r9,[sp,#4]; 将栈顶+4地址的值赋予给r9,即将e赋给r9
add r0,r3; 将d与r0求和赋给r0
add r0,r12; 将r12与r0求和赋给r0
add r0,r9; 将r9与r0求和赋给r0
3.2 IDA基本使用方法
首先,IDA工具是什么,能帮助我们做什么?
IDA是一款交互式的,可编程的,可扩展的,多处理器的,交叉Windows或Linux WinCE MacOS平台主机来分析程序, 被公认为最好的花钱可以买到的逆向工程利器。IDA已经成为事实上的分析敌意代码的标准,让其自身迅速成为攻击研究领域的重要工具。它支持数十种CPU指令集。
使用IDA能帮助我们在逆向工作中完成以下工作:
1、分析so文件,查看该so为文件的汇编代码及C、C++代码
2、通过静态分析可以通过修改十六进制代码来完成源代码的修改
3、支持App程序在so层的动态调试
打开so文件
1、双击打开IDA Pro (32-bit),并且一路选择OK,最后点GO进入IDA主界面
2、直接拖拽SO文件进入IDA界面,选择ARM格式,便能直接打开SO文件
主窗口介绍
1、函数窗口:列出该so文件里每一个函数,常用对函数进行搜索
2、消息窗口:IDA工具的输出控制台,从中可以读到IDA的运行情况
3、工具栏区域:其主要包括六个逆向工作中的常用窗口
- IDA View:操作和分析二进制文件的主要工具,其主要包括两种形式:文本视图(汇编代码)和图形视图(某个函数的调用执行情况)
红色箭头:表示跨函数调用
粗箭头:表示循环
实线箭头:表示无条件分支
点线箭头:表示条件分支
-
Hex View:显示程序内容的标准十六进制代码,每行显示十六个字节,以及对应的ascii字符,假如在文本视图中选中某一地址,则切换至十六进制窗口中该地址代码将会显示高亮
-
结构体窗口:用于显示IDA决定决定在一个二进制文件中使用的复杂数据结构,双击结构体名称可以展开内部结构
-
枚举窗口:类似于结构体窗口,当IDA识别到枚举类型,则在该窗口展现
-
导出窗口:列出文件的入口点,其包含该so文件导出函数的名称及虚拟地址
-
导入窗口:和导出窗口相反,表示该so文件导入的所有函数名及虚拟地址
4、概况导航栏:表示不同类型的文件内容
-
蓝色:.text section
深蓝:用户自己写的函数编译后的代码区 浅蓝:编译器自己添加的函数,像启动函数,异常函数等(我自己猜的,不一定百分百正确)
-
粉红色:.idata section
有关输入表的一些数据信息
-
军绿色:.rdata section
纯数据,只读
-
灰色:为了段对齐而留下的空隙
-
黑色:禁区,不存在任何数据
常用快捷键及设置
1、F5:文本视图指向函数时,按F5能查看该函数段C代码
2、y JNIEnv *:还原C代码后部分方法调用需要还原JNI函数方法名,选中对应指针按y然后输入JNIEnv *还原JNI函数名
3、SPACE:用于在IDA VIEW视图中切换文本视图及图形视图
4、Shift+F12:展示所有字符串内容窗口,能够查看字符串存储地址及调用地址
5、Ctrl+S:查看段地址的快捷键,得到段的起始地址和结束地址
6、G:跳转到指定地址的代码位置
3.3 Java层调用Native层方法原理
背景概述
在安卓逆向的工作当中,我们在对Java层代码进行分析后,通过System.load()以及System.loadLibrary()方法能够让我们准确的知道相应的Native方法在哪个so文件中实现,但是我们该如何在so文件中找到找到对应的实现方法的位置呢,这时就需要去了解安卓开发中Java层调用Native层方法原理。
Native层方法注册方式
在安卓开发中Native层方法的注册分为静态注册以及动态注册两种形式,对于安卓开发而言,静态注册与动态注册在Java层实现逻辑时一模一样的,区别仅在于如何建立Java层函数与Native层函数的映射关系。
静态注册
静态注册流程:
静态注册优点:
- 注册方式比较简单,有规范的注册流程
静态注册的缺点:
-
不利于方法的更新
-
多次调用效率低
动态注册
动态注册流程:
动态注册优点:
-
注册效率高
-
利于方法更新
-
调用效率高
动态注册缺点:
- 需要理解动态注册原理以及关注注册信息规范
对于逆向工作而言,静态注册方法最终在Native层中生成的函数名格式为Java_包名_类名_Native方法名
,如拼多多中:
因此我们能很快速找到Native层中函数实现的位置,对于动态注册方法,我们则需要进入JNI_Onload()方法中查找对应的函数注册数组,然后找到Native层中的对应实现位置,如京东到家:
3.4 Native层代码的修改
背景概述
在对Native层代码进行逆向时,有时候我们需要通过修改Native层代码来屏蔽某些代码或是插入我们的代码,而Native层代码的修改是基于ARM汇编来完成的,因此我们通过修改其十六进制代码便能完成Native层代码的修改。
修改案例
通过阅读此处代码,可以看出用于调用安卓日志类输出指针v27之前共33个字符
此时我们想修改这个v22参数,将其变大,使其输出80个字符,我们首先需要去到IDA VIEW中查看该命令地址以及在IDA HEX中该地址对应的十六进制代码
此时进入https://armconverter.com/中将我们想要替换的汇编代码生成十六进制代码
得到十六进制代码后回到IDA HEX窗口按F2修改对应的十六进制代码,修改完毕后按F12保存
保存完毕后查看查看代码是否修改成功
3.5 So文件替换
概述
经过上一步对Native代码进行修改,我们需要对so文件进行重新打包,并且完成替换原so文件操作
重打包
点击hedaer工具栏Edit-》Path program-》Apply pathces to input file-》ok
替换so
进入安卓模拟器路径/data/app/{对应app文件夹}/lib/替换原有so文件
4.总结
至此简要的介绍了以下在APP逆向工作中Native层静态分析的基础知识,让我们对安卓逆向有了基本的认识。逆向是一门实践学科,基础知识的掌握只是根基,只有通过大量的app逆向经验,逆向技术才能不断进步。