如何逆向Flutter应用

目前大多数使用Flutter的应用都是采用add2app的方式,在APP中与Flutter相关的内容主要有FlutterEngine、APP产物、资源文件。我们可以在应用市场上寻找一个接入Flutter的应用做实验。(apk可在各大应用市场下载,ipa下载可以在mac上安装Apple Configurator 2进行),apk和ipa中flutter相关产物目录如下:iOS包文件为ipa,下载后将其后缀重命名为zip进行解压,解压后Payload下即可看到应用文件夹,其中FlutterEngine、APP
摘要由CSDN通过智能技术生成

目前大多数使用Flutter的应用都是采用add2app的方式,在APP中与Flutter相关的内容主要有FlutterEngine、APP产物、资源文件。我们可以在应用市场上寻找一个接入Flutter的应用做实验。(apk可在各大应用市场下载,ipa下载可以在mac上安装Apple Configurator 2进行),apk和ipa中flutter相关产物目录如下:

iOS包文件为ipa,下载后将其后缀重命名为zip进行解压,解压后Payload下即可看到应用文件夹,其中FlutterEngine、APP产物、资源文件分别在如下位置:

xxx.app
└── Frameworks
    ├── App.framework
    │   ├── App(Dart APP产物)
    │   ├── Info.plist
    │   ├── SC_Info
    │   ├── _CodeSignature
    │   └── flutter_assets
    │       ├── flutter_assets
    │       ├── AssetManifest.json
    │       ├── FontManifest.json
    │       ├── LICENSE
    │       ├── fonts
    │       ├── images
    │       ├── mtf_module_info
    │       └── packages
    └── Flutter.framework
        ├── Flutter(FlutterEngine)
        ├── Info.plist
        ├── SC_Info
        ├── _CodeSignature
        └── icudtl.dat

Android包文件为apk,下载后将其后缀重命名为zip进行解压,其中FlutterEngine、APP产物、资源文件分别在如下位置:

xxx.apk
├── assets
│   └── flutter_assets
│       └── flutter_assets
│       ├── AssetManifest.json
│       ├── FontManifest.json
│       ├── LICENSE
│       ├── fonts
│       ├── images
│       ├── mtf_module_info
│       └── packages
└── lib
		└── armeabi
        ├── libapp.o(Dart APP产物)
        └── libflutter.so(FlutterEngine)

FlutterEngine各个app都是使用官方或者在官方基础上进行修改,差别不多,我们暂不关心这部分的逆向。资源文件多是图片,字体等无需逆向即可查看的资源。我们主要关心的是使用Dart编写的业务逻辑或者某些框架代码,这部分代码在APP产物中。即:App.framework/App 或 armeabi/libapp.o这两个文件都是动态库,我们先简单看看里面包含什么?

# 可以安装常用的 bin utils工具,如 brew update && brew install binutils
~/Downloads > objdump -t App
App:     文件格式 mach-o-arm64

SYMBOL TABLE:
0000000001697e60 g       0f SECT   02 0000 [.const] _kDartIsolateSnapshotData
000000000000b000 g       0f SECT   01 0000 [.text] _kDartIsolateSnapshotInstructions
0000000001690440 g       0f SECT   02 0000 [.const] _kDartVmSnapshotData
0000000000006000 g       0f SECT   01 0000 [.text] _kDartVmSnapshotInstructions

~/Downloads > greadelf -s libapp.so
Symbol table '.dynsym' contains 5 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT        UND
     1: 00001000 12992 FUNC    GLOBAL DEFAULT        1 _kDartVmSnapshot[...]
     2: 00005000 0x127df60 FUNC    GLOBAL DEFAULT    2 _kDartIsolateSna[...]
     3: 01283000 22720 OBJECT  GLOBAL DEFAULT        3 _kDartVmSnapshotData
     4: 01289000 0x9fc858 OBJECT  GLOBAL DEFAULT     4 _kDartIsolateSna[...]

可以看到无论是Android还是iOS,Dart App产物中都包含4个程序段。(来自https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode)

  • ‘_kDartVmSnapshotData’: 代表 isolate 之间共享的 Dart 堆 (heap) 的初始状态。有助于更快地启动 Dart isolate,但不包含任何 isolate 专属的信息。

  • ‘_kDartVmSnapshotInstructions’:包含 VM 中所有 Dart isolate 之间共享的通用例程的 AOT 指令。这种快照的体积通常非常小,并且大多会包含程序桩 (stub)。

  • ‘_kDartIsolateSnapshotData’:代表 Dart 堆的初始状态,并包含 isolate 专属的信息。

  • ‘_kDartIsolateSnapshotInstructions’:包含由 Dart isolate 执行的 AOT 代码。

看了上面可能还是一脸懵o((⊙﹏⊙))o,为什么分四块,Data与Instructions,Vm与Isolate是什么?为什么使用Snapshot(快照)命名。关于这些问题,推荐一篇博客https://mrale.ph/dartvm/ 。Data与Instructions,Vm与Isolate这些概念两两组合,正好对应上面四个段。也就是VmData、VmInstructions、IsolateData、IsolateInstructions。

先说一下Data与Instructions。首先我们知道的是Flutter编译运行在app上分为JIT和AOT模式,线上只能使用AOT模式,也就是Flutter引入的DartVM包含了执行AOT产物的功能。为了与JIT模式兼容,DartVM采用了所谓快照的方式,即JIT运行时编译后的基本结构与AOT编译的基本结构相同。将类信息、全局变量、函数指令直接以序列化的方式存在磁盘中,称为Snapshot(快照)。

1.png

由于快照的序列化格式针对性的为读取速率做了设计,从快照读取也大大提高代码的加载速度(创建所需类信息、全局数据等,可以类比OC Runtime启动加载元类、类信息等)。最开始快照中是不包含机器代码的(即函数体内部的执行逻辑),后来随着AOT模式的开发这部分被加入到快照中了,这些后来者也就是前面说的Instructions。

2.png

这里要补充的是,Instructions指的是可执行汇编指令,在.o文件中必须放在text段里,标记为可执行(否则iOS无法加载并执行它)。类信息、全局变量这些内容可以放在data端作为普通数据被加载。(字节的优化50%包体积也是基于此,有兴趣可以看一下文章:https://juejin.im/post/6844904014170030087)。

接着说DartVmSnapshot 与DartIsolateSnapshot。这就涉及Data虚拟机是如何运行业务代码。虚拟是Data代码运行的载体,VM中运行的逻辑都跑在一个抽象的叫做Isolate(隔离)的实体中。你可以把Isolate当做OC里一个带有Runloop的Thread看待(至于他们之间的关系又是一个令人头疼的面试题,这里不展开了)。简要来说Isolate中维护了堆栈变量,函数调用栈帧,用于GC、JIT等辅助任务的子线程等, 而这里的堆栈变量就是要被序列化到磁盘上的东西,即IsolateSnapshot。此外像dart预置的全局对象,比如null,true,false等等等是由VMIsolate管理的,这些东西需序列化后即VmSnapshot。

3.png

到这里大致了解Flutter APP产物中的结构。那如何读取他们呢?我们可以从clustered_snapshot.cc中的FullSnapshotReader:: 函数看起,看他是如何反序列化的。


void Deserializer::ReadIsolateSnapshot(ObjectStore* object_store) {
  Array& refs = Array::Handle();
  Prepare();
  {
    NoSafepointScope no_safepoint;
    HeapLocker hl(thread(), heap_->old_space());
    // N.B.: Skipping index 0 because ref 0 is illegal.
    const Array& base_objects = Object::vm_isolate_snapshot_object_table();
    for (intptr_t i = 1; i < base_objects.Length(); i++) {
      AddBaseObject(base_objects.At(i));
    }
    Deserialize();
    // Read roots.
    RawObject** from = object_store->from();
    RawObject** to = object_store->to_snapshot(kind_);
    for (RawObject** p = from; p <= to; p++) {
      *p = ReadRef();
    }
#if defined(DEBUG)
    int32_t section_marker = Read<int32_t>();
    ASSERT(section_marker == kSectionMarker);
#endif

    refs = refs_;
    refs_ = NULL;
  }
  thread()->isolate()->class_table()->CopySizesFromClassObjects();
  heap_->old_space()->EvaluateSnapshotLoad();

#if defined(DEBUG)
  Isolate* isolate = thread()->isolate();
  isolate->ValidateClassTable();
  isolate->heap()->Verify();
#endif
  for (intptr_t i = 0; i < num_clusters_; i++) {
    clus
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值