需要逆向工具资料+v:13140310004
一、首先刚开始学逆向的话
java基础+Android开发:
到什么程度证明我们的Java基础打牢?
1.首先我们需要看懂反编译的Java代码
2.最好能动手写一个apk (不需要太注重布局文件,侧重点在于代码)
二、Java逆向分析
1.当我们需要修改一个apk的dex文件(Java代码) 不能够直接修改Java代码 只能修改smali代码
需要掌握 smali代码 Android studio动态调试(调试是基于我们导入的smali文件) JEB动态调试 ddms工具使用(方法剖析)
2…NDK开发部分
掌握怎样编写so库文件 -> jni接口(动态注册、静态注册)
3…ARM汇编
掌握常用的指令 (ARM机器码运算) 跳转 B BL BLX
调试
掌握一些必备的技能
固定的流程
一些IDA技巧(函数不能F5解决办法)
一些快捷键(用着用着就自然而然记住了)
了解一些窗口
保护策略
偏向于概念 很重要
资源文件的混淆
签名校验
Java代码混淆
框架检测(xposed frida substrate(受限于5.0以下真机)) frida偏向于测试 xposed偏向于插件化
三、xposed框架用法(重启)
安装及知道如何使用
用法:
hook 普通方法 构造方法 匿名内部类
进阶用法:
hook 自加载的dex-> 一些dex本来是加密的 那么在分析 看不到它的dex 这就是自加载
这张状况:ddms工具-> 进行方法剖析 -> 我们在ddms方法剖析中能够找到这个方法,但是在反编译apk类里面找不到这个类
怎么去hook? 两种方法 1.我们hook它的父类 -> 拿到这个classloader对象 loadclass方法 -> 拿到这个classloader对象
反射查找类
要hook的函数的参数比较复杂 -> 反射拿到这个参数
frida框架(基于通信的)
比较高级功能 -> 拿到地址来进行hook (内存泄漏)
主要是基础阶段,准备阶段 储备知识 -> 协议分析 + 脱壳
frida框架:
hook RegisterNative函数
要hook的函数在哪里? /system/lib/
_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi(每台设备函数名不尽相同) 不是导出函数
命令:
frida -U --no-pause -f -l C:\Users\Administrator\Desktop\
frida -U
–no-pause 直接启动
-f 重新启动app 然后attach
包名
-l 指定js脚本
C:\Users\Administrator\Desktop\ js脚本路径
打印示例:
[RegisterNatives] java_class:
name: registerDID sig: ([B)Z fnPtr: 0x99fb432d module_name: libnet_crypto.so
module_base: 0x99fa2000 offset: 0x1232d
.init_array so被加载进来首先执行的地方
JNI_OnLoad init_array节区函数执行完后执行
.fini_array so最后执行的函数
app可以动态调试 用frida脚本去hook 打印出函数地址 == 在中 RegisterNatives函数下断获取想要的信息
Java层分析某个协议 -> 校验字段 -> 加载so 调用native函数生成 -> IDA分析对应so 找到RegisterNative的第三参数地址
情况1: 动态注册函数的name sig被加密 函数地址可以正常得到
用frida hook RegisterNative函数 打印出name sig address
情况2: JNI_OnLoad函数被处理了(混淆 指令) -> RegisterNative 找不到
IDA快捷键 ctrl+s 跳到 .data区域 看看有没有动态注册函数地址
情况3: 动态注册偏移处 三个字段都被处理 能看到的之后很多函数体
用frida脚本去hook 打印出函数地址
一般不会在反调试函数中做一些重要的运算
某一个函数中有反调试并且函数体积不是很大 -> 尝试把整个函数的调用掉给NOP掉
有些协议包抓不到:
抓包工具: 电脑抓包工具 手机抓包工具 fiddler Charles wireshark 黄鸟 http canary
支持通信协议不同 : fidder (https) http2 websocket 这两个不支持
抓包工具是支持通信协议(可以代理)还是抓不到-> 防抓包保护
可以用frida、xposed来过防抓包(hook框架)
灵光一现
四、协议分析 -> 抓包 -> 分析出数据包的拼接过程 -> 分析字段(改变的字段 校验字段)-> 定位到so算法
使用这个算法:
还原算法 -> 函数混淆(是否要ida/gdb动态调试) 反调试 进行hook来分析重要函数参数 (frida) ,xposed在技术上可以实现so层hook 如果frida被ban了 inlinehook(修改二进制代码实现地址跳转),调用so来调用算法 -> 调用检验(校验签名、检测线程数量等)
五、脱壳
加固:
第一代壳:落地加载 (dex可以在apk目录下看到)
第二代壳:不落地加载 (apk目录下不能看到原始dex)
把加密的dex文件加载到内存中 然后解密 从始至终不会以文件形式存在
第三代壳:指令抽取 (修复指令)
三代壳:
指令抽取然后一次性还原 还原到原始dex文件
指令只有被需要时才还原
还原到原始dex文件 在被抽取的函数中架一座桥 跳转到其他地方运行
分析加固逻辑 先dump dex文件
dump出解密后的item
dex重建
脱壳工具:dexHunter youpk fart fpk3
基于jni反调 主动调用
四代壳:
vmp 跟java vm虚拟机有关联 一部分指令实现需要靠这个虚拟机
java2c
pc andorid -> 自定义解释器© 指令虚拟化
opcode 和 指令 一一对应
把opcode 映射关系改了
vmp
java层
java中的函数被关键词 native修饰 -> 静态注册 动态注册
public native void onCreate(Bundle bundle); 此函数两种都不是
StubApp.interface11(1347); 统一接口 1347 class_id public static native void interface11(int i);
so层
so层执行 interface11(int i);
interface11统一处理的函数 分发器
registernative函数来注册函数地址 统一注册到一个地址
不会交由javavm虚拟机来执行 -> 自己实现了switch分发器 (指令解析器)
java2c dex2c
把java代码转成c代码
顶象加固 两者相结合
六、Android killer技巧:
1.反编译问题:
apk中添加了无用或者加密的dex文件-> apktool无法识别dex文件(正常的dex文件 magic字段格式是一定的)
解决办法 删除这个dex文件 反编译之后把dex文件添加回去
2.回编译问题:
apk添加了资源混淆(大部分都是xml文件找不到某种资源)-> 根据报错信息删除对应的xml文件行数 如果一个xml中
报了很多错误->直接删除整个xml文件
3.更新apktoo能解决大部分问题
4.签名检验问题:
找签名校验函数
java层 native层 -> 搜关键词signature -> 定位到签名校验函数
Unicorn AndroidNative unidbg
七、ARM汇编汇总
1.跳转指令
B 无条件跳转
BL 带链接的无条件跳转
BX 带状态切换的无条件跳转 根据目标地址最低位切换状态(arm/thumb)
BLX 带链接和状态切换的无条件跳转
B loc_地址
BEQ,BNE
2.存储器与寄存器交互数据指令 核心
存储器(主存,内存)
寄存器中放的数据:可以是字符串,可以是数,也可以是一个地址,它可以放各种类型的数据
存储地址单元:地址(如0x00004000)与地址中存在的值
LDR:从存储器中加载数据到寄存器 ← Load
LDR R8,[R9,#4] R8为待加载数据的寄存器,加载值为R9+0x4所指向的存储单元 R8=*(R9+4)
STR:将寄存器的数据存储到存储器 → Store
STR R8,[R9,#4] 将R8寄存器的数据存储到R9+0x4指向的存储单元 *(R9+4)=R8
LDM:将存储器的数据加载到一个寄存器列表 →
LDM R0,{R1-R3}将R0指向的存储单元的数据依次加载到R1,R2,R3寄存器
STM:将一个寄存器列表的数据存储到指定的存储器 ←
PUSH:将寄存器值推入堆栈 压栈–》
POP:将堆栈值推出到寄存器 出栈 《–
SWP:将寄存器与存储器之间的数据进行交换
SWP R1, R1 [R0] 将R1寄存器与R0指向的存储单元的内容进行交换
堆,队列:数据结构,栈是竖的,后进先出,且只能从栈顶依次填入数据
3.数据传送指令
MOV:将立即数或寄存器的数据传送到目标寄存器 ←
MOV R0, #8 R0=8
4.数据算术运算指令
←
ADD,SUB,MUL,DIV
有符号,无符号运算;带进位运算
5.数据逻辑运算指令
与:AND
或:ORR
异或:EOR
移位:实质是乘,除,类似于小数点移位,但相反。小数点左移,数变小;右移变大。
但逻辑移位,左移变大,右移变小,且按2的倍数进行,因为是2进制。
LSL:逻辑左移←
LSR:逻辑右移←
LSL R0,R1,#2 R0=R1*4
LSR R0,R1,#2 R0=R1*2
6.比较指令
CMP:比较
CMP R0 #0 R0寄存器中的值与0比较
标志位:如z位,这个都可以在动态调试时,寄存器窗口看到
7.其他指令
协处理器指令:SWT (切换用户模式)
伪指令:DCB
8.寄存器寻址方式
立即寻址:MOV R0,#1234 R0=0X1234
寄存器寻址:MOV R0,R1 R0=R1
寄存器移位寻址:MOV R0,R1,LSL #2 R0=R1*4
寄存器间接寻址:LDR R0,[R1] 将R1寄存器中的值作为地址,取出地址中的值赋予R0
寄存器间接基址偏移寻址:LDR R0,[R1,#-4]将R1寄存器的值-0x4的值作为地址,取出地址中的值给R0
对int a=0;这一句简单高级语言的汇编理解,首先会开辟一个内存存储单元,然后把0x0这个数放入R0寄存器,
然后再把R0寄存器的数据放入内存存储单元。所以:
MOV R0,#0
STR R0,[R11,#0x14+var_20]