文章目录
简介
Flutter的逆向主要集中在如何反编译出源代码,解决这个问题需要先了解snapshots的文件格式,并能解析所有Objects。
旧版本的snapshots是独立存储在assets目录下,新版本的snapshots直接融合在libapp.so中,因此本文的研究重点将集中于libapp.so,而flutter运行引擎代码则位于libflutter.so中.
基础知识
Dart基础
Dart代码运行在isolate中
- isolate
- heap 内存管理,包括分配、垃圾回收等
- objects 运行时对象、代码、数据等
运行方式
- AOT
- release版本,使用snapshot
- JIT
- debug版本,使用源代码
整数编码
编码方式: LEB128
例子:
0x4e46 8301 826d 9900
- 读取bytes,直到读取的值大于0x7F
- 0x4e 46 83
- 反转bytes
- 0x83 46 4e
- 10000011 01000110 01001110
- 删除二进制的最高位
- 0000011 1000110 1001110
- 转换成十进制
- 58190 unsigned int
- 58190 int 最高位决定正负
架构布局
libapp.so包含两个snapshots,分别包含代码段和数据段(heap)
-
VM isolate snapshots
- _kDartVmSnapshotInstructions
- _kDartVmSnapshotData
-
APP isolate snapshots
- _kDartIsolateSnapshotInstructions
- _kDartIsolateSnapshotData
libapp.so
├── ...
│
├── .text
│ └── _kDartVmSnapshotInstructions
├── .rodata
│ └── _kDartVmSnapshotData
├── .text.2
│ └── _kDartIsolateSnapshotInstructions
└── .rodata.2
└── _kDartIsolateSnapshotData
Snapshot文件格式
Header 头部
Offset | Type | Name | Description |
---|---|---|---|
0 | 4 | Magic number | snapshots的标志头,常量 0xf5f5dcdc |
4 | 8 | Size | ELF文件中snapshots的大小,以字节为单位 |
12 | 8 | Kind | 一个数字,用来标识snapshots的种类 |
20 | 32 | Version | 使用的编译器版本,由make_version.py生成 |
52 | string | Features | 以Null结尾的特征字符串,它们用于标识snapshots是生产版本还是调试版本 |
unk | u_int | Base objects | 基本对象的数量,由clustered_snapshot.cc的AddBaseObjects中定义 |
unk | u_int | Objects | 对象的数量 |
unk | u_int | Clusters | Clusters的数量 |
unk | u_int | Field table length | 字段表的数量 |
Object serialization 对象序列化
对象序列化,对于每个Dart对象都可以分解为一组固定的字段,每个字段都有一个确定的值或者是对另一个对象的引用。序列化的过程,每个Dart对象都会为其分配一个ID
例子: 假设Mint的可视化编程为下面伪代码
class Mint{
bool isCanonical; //标志是否是规范化的值
long values; //具体的值
}
--> 值为7的非规范Mint对象的序列化为: 0x00 87
class Array {
int length;
bool isCanonical;
TypeArguments typeArguments; // 对类型为TypeArguments对象的引用
Object[length] data; //对象引用的数组
}
假设:
--> Array[0] = Mint(7)
--> Array[1] = Mint(12)
--> Mint(7) 对象序列化ID=2
--> Mint(12) 对象序列化ID=3
则:
--> Array非规范化序列化为: 0x 82 00 81 82 83
- 怎么理解每一个Dart对象所处的内存布局?
- 怎么理解一段hex数据分别解析出每一个dart对象?
Clusters 对象族
Clusters是snapshot中共享通用Dart类型的一组对象。 在序列化过程中,Clusters按顺序序列化,并且每个簇都负责序列化其中的每个对象。可以理解为按Clusetrs进行序列化和反序列化。
Clusters序列化过程 clustered_snapshot.cc
- Trace
- 基本信息初始化
- alloc
- 遍历所有Clusters,并将reference ID分配给它们包含的每个对象,以及序列化有关的每个Clusters的基本信息。 通常,此基本信息只是Clusters的class ID,以及该Clusters包含的对象数或类似的信息
- fill
- 遍历所有Clusters,序列化其中的每个对象
Code objects 代码对象
Dart对象Code
类型,其主要指向代码的偏移地址。
参考
[译]Android环境下的Flutter逆向工程
Reverse engineering Flutter for Android