(安卓逆向)技巧思路分享

时间:2024-10-25 14:33:12

需要逆向工具资料+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]