查看原文
其他

Flutter应用逆向分析相关讨论

小黄鸭爱学习 看雪学苑 2022-07-01


看雪论坛作者ID:小黄鸭爱学习




前言


声明:本文只代表个人意见,发出来作抛转引玉之用。有不同观点请评论区理性讨论,也欢迎指出错误和不足。
 
市面上flutter的应用数一直是在增加,而且一些恶意涉黄涉赌软件也采用了flutter技术开发。由于市面上没有合适的工具去反编译他们,导致他们的安全性真的很高。



资料搜索阅读


近日我特意去看了看最新的发布的flutter,尝试自行写了一个TestAPK,发现没法像正常的apk一样去分析。这让我没有头绪,好在有万能的谷歌让我找到了几篇非常有用的文章。
 
参考文章:
 
Reverse engineering Flutter apps (Part 1)
(https://blog.tst.sh/reverse-engineering-flutter-apps-part-1/)

Reverse engineering Flutter apps (Part 2)
(https://blog.tst.sh/reverse-engineering-flutter-apps-part-2/)

Introduction to Dart VM
(https://mrale.ph/dartvm/)

虽然文章是一两年前的,但是依然是非常有意义的。
 
通过文章我们知道在发布版本的apk文件里面 libapp.so 保存的是快照,libflutter.so则是引擎,其中包括一个dart的虚拟机。
 
一个正向的生成过程:
flutter工程->虚拟机快照->ELF文件
 
如何要分析这个过程变得呢,需要从dartVM源码来看。
 
一个好消息是,dartVM是开源的。那我们如何通过这个源码分析整个生成过程呢。
 
除了静态读取工程相关文件,文章还提供了一种方式就是通过重编译dartVM并且使用命令自行编译libapp.so的方式来动态分析。




静态分析ELF


我们把生成的apk抽出libapp.so。
 
我们直接拉到010Edit观察一番用ELF文件模板即可。
 
直接拉到dynamic_symbol这个地方。
五个函数(其他文章都只提到四个,这回谷歌怎么加到五个了)
  • _kDartVmSnapshotInstructions
  • _kDartIsolateSnapshotInstructions
  • _kDartVmSnapshotData
  • _kDartIsolateSnapshotData
  • _kDartSnapshotBuildId

两种虚拟机,两种数据快照加方法指令,还有一个新增的不知道是啥。
 
不过我们可以知道,工程的类信息都在 kDartIsolateSnapshotData 这个里面,我们可以尝试静态的读取分析他。




读取并组装回去

一个非常不幸的消息是 发布版的快照文件里面没有能够直接调用的dart语言写的方法。这就说明不存在使用IDA直接F5或者使用fridah ook调用dart层方法测试这种情况。


由上面的文章知道 类 参数 常量 方法指令等信息是分开存放的。所以我们要想组装回去,就要从快照文件把他们一个一个独取出来。
 
首先最核心的也是最难的要知道每个字节代表什么意义,我找了很多资料没有直接说明出来。而且本身dartSDK在非常快速的迭代,每个版本直接的差异会导致没法按照之前的字节来解析。
 
最后还是回到了dartVM本身,我通过资料找到了一个信息。就是每个编译版本会有一个值代表dartVM的版本。
他在第20-52的位置,我们可以把它先读取出来。
 
一开始我以为这个值是那个什么github对应的commit提交的值
所以拿着这个找了半天。但是没有结果。
 
后面我直接不管他,先直接用dart --version命令得出电脑本机的dart编译版本。
FlutterTest>dart --versionDart SDK version: 2.14.4 (stable) (Wed Oct 13 11:11:32 2021 +0200) on "windows_x64"

正好是 2.14.4 版本 ,我们去 github clone 代码切换到这个分支。开始通过阅读这里面的源码来帮助我们解析这个快照文件。



dart SDK 源码阅读


源码阅读是非常枯燥的一件事,幸亏文章中指出了一些核心的类。

我们想办法从头部开始分析。
 
说实话我对C++并不熟悉 导致我在看这些代码的时候非常艰难。
 
好在他们的代码命名非常规范,我连蒙带猜看出了一些东西。
 
dart如何解析这个快照的头部部分代码在
 
snapsshot.h 中 这里有个非常关键的标准位
 
0xdcdcf5f5 在这里被定义为 kMagicValue。
static const int32_t kMagicValue = 0xdcdcf5f5;static const intptr_t kMagicOffset = 0;static const intptr_t kMagicSize = sizeof(int32_t);static const intptr_t kLengthOffset = kMagicOffset + kMagicSize; //0+32static const intptr_t kLengthSize = sizeof(int64_t);static const intptr_t kKindOffset = kLengthOffset + kLengthSize;//0+32+64static const intptr_t kKindSize = sizeof(int64_t);static const intptr_t kHeaderSize = kKindOffset + kKindSize;//0+32+64+64 = 160

分析这段代码可以得出三个东西:
 
一个魔数Size;
 
一个kindSize;
 
而且这个kind是个枚举类 大概就是当前的编译快照类型。
enum Kind { kFull, // Full snapshot of an application. kFullCore, // Full snapshot of core libraries. Agnostic to null safety. kFullJIT, // Full + JIT code kFullAOT, // Full + AOT code kNone, // gen_snapshot kInvalid };

一个LengthSize。
 
这个魔数也就是表示如果以这段开头的二进制就是快照的开始。
 
我们用 elftools 把这里读出来试试。
snapshot_stream = BytesIO(snapshot) snapshot_stream.seek(0) kMagicValue = snapshot_stream.read(4).hex() kLengthValue = snapshot_stream.read(8).hex() kKindValue = snapshot_stream.read(8).hex() print(kMagicValue) print(kLengthValue) print(kKindValue)

输出:
f5f5dcdcfed20b00000000000300000000000000

好像有点对得上了。
 
魔数 f5f5dcdc 读出来了
03 kind 对应 AOT快照
fed20b 应该是镜像的大小
 
我们找到了头来继续往下分析。
 
我找到了一个SnapshotHeaderReader类在clustered_snapshot.h里面。
 
里面正好描写了读出来的几个头部字段。
SnapshotHeaderReader(snapshot->kind(), snapshot->Addr(), snapshot->length())

接下来是两个字段一个是
 
verrsion 在 VerifyVersion()里面说明了他有128的长度
 
features 字段在 VerifyFeatures() 里面长度
 
他的长度是在:
const char* expected_features = Dart::FeaturesString(isolate_group, (isolate_group == NULL), kind_);

推断出来的 做了一大堆的判断拼接,不过我们可以看到最后的一个字符是 safety。
 
显然是不同的版本这个长度是不同的。
 
我们可以盲测 测试出这个string的长度。
 
我们可以依次读出来。
version_hash = snapshot_stream.read(32).decode('UTF-8') features = snapshot_stream.read(64+64+32+11).decode('UTF-8') print(version_hash) print(features)

输出:
9cf77f4405212c45daf608e1cd646852 product no-code_comments no-dwarf_stack_traces_mode lazy_async_stacks no-lazy_dispatchers use_bare_instructions dedup_instructions no-"asserts" arm-eabi softfp null-safety

到这里我先暂时停下了反序列化工作,因为对于C++的不熟悉导致我非常难受。我决定先去学习一波C语言,再回头来翻译这份代码。


vsersion_hash和flutter版本的对照表


https://github.com/ptswarm/reFlutter/blob/main/enginehash.csv
 
version,Engine_commit,Snapshot_Hash
2.8.0-3.1.pre,09f1520e8b9585d133faf1eccced9357670c6d11,adf563436d12ba0d50ea5beb7f3be1bb

2.7.0-3.0.pre,bbba2b2437d739a455ecafb66a22a46d31b233ed,24d9d411c2f90c8fbe8907f99e89d4b0

2.6.0-5.2.pre,1d521d89d8d98f27be4e0ff84d5c6b72dbdc91ca,f10776149bf76be288def3c2ca73bdc1

2.5.0,f0826da7ef2d301eb8f4ead91aaf026aa2b52881,9cf77f4405212c45daf608e1cd646852

2.4.0,844c29f42a614420b2205c178f22d30b43a8b0bb,659a72e41e3276e882709901c27de33d

2.3.0,9d517f475ba1282b619477bde8e708d6a34287cf,7a5b240780941844bae88eca5dbaa7b8

2.2.0,a9d88a4d182bdae23e3a4989abfb7ea25954aad1,e4a09dbf2bb120fe4674e0576617a0dc

2.2.0-10.1.pre,d2a2e93510ad6cfc3d62a90d903b7056e4da8264,34f6eec64e9371856eaaa278ccf56538

2.1.0-12.2.pre,711ab3fda05004ee5f6035f2a0bf099fca39a129,39a9141bbcc3cae43e6f9f6b7fbaafe3

2.0.6,05e680e202af9a92461070cb2d9982acad46c83c,5b97292b25f0a715613b7a28e0734f77

1.25.0-8.3.pre,7a8f8ca02c276dce02f8dd42a44e776ac03fa9bc,9e2165577cef0f0f70f9ff072107920c

1.24.0-10.2.pre,07c1eed46b9d9b58df78566e9b8b2e42e80d3380,a2bdb58c7edf9471da9180bf8185e7f7

1.23.0-18.1.pre,1d12d82d9cb54876f58044aa52198d53ec841c3d,953aa80d78c4d8886e3e4d784fd9d95f

1.22.6,2f0af3715217a0c2ada72c717d4ed9178d68f6ed,8ee4ef7a67df9845fba331734198a953

1.21.0-9.2.pre,20a953183580250aac2e15d36007664118bda5ab,5f40b0a9f04b5018fa08a9b67fd316cd

1.20.4,d1bc06f032f9d6c148ea6b96b48261d6f545004f,04645b6182fad3d68350d84669869ce5

1.20.0-7.2.pre,60b269d898cbe0be27e9b9ba9d21eae97b887ab6,8b2ca977d1d2920b9839d1b60eade6a7

1.19.0-4.3.pre,9a28c3bcf40ce64fee61e807ee3e1395fd6bd954,59da07d9da5a83be4ce75b7913b63dbd

1.18.0-11.1.pre,ef9215ceb2884ddf520d321bcd822d1461330876,b58ead73b2c5dfec69565df469bba387

1.17.5,ee76268252c22f5c11e82a7b87423ca3982e51a7,be7d304ff826e2dfac63538e227c3cc5

1.17.1,6bc433c6b6b5b98dcf4cc11aff31cdee90849f32,74edb834fac3fcea79d7ac2d1d6f1fb2

1.17.0-dev.3.1,c9506cb8e93e5e8879152ff5c948b175abb5b997,9e7cb7c9394c24c2398410b902673e13

v1.15.17,5aff3119480996ca014ec0f8d26d74db617b5852,ee91a9191a5286c31d91a89754ba36af

v1.14.6,c4229bfbbae455ad69c967be19aee3fadd6486e1,e739779cc1d28f0f697a92f2daf5f10f

v1.13.6,bdc9708d235e582483d299642ad8682826ebb90d,81662522448cdd4d02eb060669e5d48b

v1.12.13+hotfix.9,af51afceb8886cc11e25047523c4e0c7e1f5d408,20e5c4f7dc44368ac5a17643b93665f6

v1.11.0,af04338413c3ed73316350f64248a152433073b6,2fb364d659ea53f7892be9ba5e036047

v1.10.7,9e6314d348f9b5521e3c66856324d7a9c4a928c9,c3bbfe8f226120ad0569d7b78ed2d9ef

v1.9.1+hotfix.6,b863200c37df4ed378042de11c4e9ff34e4e58c9,c8562f0ee0ebc38ba217c7955956d1cb

v1.8.3,38ac5f30a7026e870619c2e8e8c99c070d74036f,34948253b59d5a56b2ec161e17975a4e

v1.7.8+hotfix.4,fee001c93f25a1e7258e762781a7361f122d29f5,1d7acad1540192ac459cf60344efb7c1

v1.6.3,8dc3a4cde2075a4f5458fd0eb199627f5124508d,c89592e3e4956c33956c8ba0f691dbd0

v1.5.4-hotfix.2,52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f,eed485c757fba5d731e4054412c99f2e

v1.4.9-hotfix.1,4737fc5cd89b8f0136e927b00f2e159444b95a73,f630ecdf457e27dd24d3b9e0a6bc1c13

v1.3.8,f4951df193a7966f9ed4da43d555eee0913d84d1,9a66dcb2da955dffdbdb0eafa0288784

v1.2.1,3757390fa4b00d2d261bfdf5182d2e87c9113ff9,0c73eb70aa4d30f450273cb424be8c62

v1.1.8,7112b72cc229e05d36716c3d7739885d3ffa72e6,317d4c7e607b1fd7d682c0010aadf1d0

v1.0.0,7375a0f414bde4bc941e623482221db2fc8c4ab5,8343f188ada07642f47c56e518f1307c




 


看雪ID:小黄鸭爱学习

https://bbs.pediy.com/user-home-832784.htm

*本文由看雪论坛 小黄鸭爱学习 原创,转载请注明来自看雪社区




# 往期推荐

1.XX弹幕投票助手分析

2.什么是runC?

3.CTF实战练习:web-Loginme

4.CTF实战练习Cmcc_simplerop

5.python_mmdt:KNN机器学习分类结果测试分析

6.CVE-2021-1732 EXP Win10_1909 KaLendsi 的EXP编写与分析






球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存