Flutter与so文件动调
Flutter与so文件动调
这是2024-CtfNewStar的Week5 Ohn_flutter!!!一个Flutter的安卓题目,这里只讲如何恢复符号表和so文件动调,具体WP请看CtfNewStar
Flutter是谷歌的移动UI框架,Flutter 使用编译型语言 Dart 开发,代码会被编译为原生机器码(而非通过 JavaScript 桥接),运行速度接近原生应用。
工具准备
前置知识
Flutter 应用的结构
Flutter 应用的核心文件通常包括:
libapp.so
(Android)或App.framework
(iOS):包含 Dart 编译后的原生代码。flutter_assets/
:存放资源文件(图片、字体、配置文件等)。isolate_snapshot_data
和vm_snapshot_data
:Dart 虚拟机的快照数据(用于启动应用)。libflutter.so
(Android):Flutter 引擎库。
恢复符号表
配置好blutter环境以后,打开x64 Native Tools Command Prompt(这是visual studio2022 自带的一个终端,它可以在Visual Studio
文件夹中找到)
然后运行blutter.py
并提供libapp.so
和libflutter.so
的文件夹路径以及输出文件夹路径
1 | python .\blutter.py ..\chall\lib\arm64-v8a\ .\output |
以这道题目为例
1 | python blutter.py C:\Users\LENOVO\Desktop\app-release\lib\arm64-v8a C:\Users\LENOVO\Desktop\app-release\lib\arm64-v8a\output |
后面的输出路径如果没有在给出的位置找到相关文件夹的话会自动生成
要注意一下so文件架构是否支持
然后你会得到
在”asm\ohn_flutter”目录下,把这个文件夹在vscode里面打开,我们可以看到汇编形式的源代码,可以尝试用关键的函数地址搜索,找到程序的主逻辑处。
开始恢复符号表
blutter 反编译得到 asm
文件夹和 ida_script
文件夹,ida_script
在 IDA 里先加载 ida_dart_struct.h
头文件再运行 addNames.py
脚本可得到大部分符号
具体步骤
1. 加载二进制文件到 IDA
2. 导入 Dart 结构头文件
在 IDA 菜单栏点击
File → Load file → Parse C header
选择
ida_script/ida_dart_struct.h
勾选
Add to imported types
,点击 Parse
→ 成功后会在Structures
窗口看到Dart
相关结构(如Dart_Isolate
、Dart_Code
等)
3. 运行符号恢复脚本
在 IDA 菜单栏点击
File → Script file
选择
ida_script/addNames.py
等待脚本执行完成
完成后仍然有大量混淆用IDA自带的去goomba插件(右键-De-obfuscate)或D810都可以去除部分混淆
SO文件调试
步骤一:将 android_server 文件 push 到手机中并执行
复制IDA目录下的\dbgsrv
目录下的android_server
和android_server64
到真机中并执行:
1 | //复制到真机中 |
当然你也可以通过指令进行h重命名
1 | adb shell "mv /data/local/tmp/android_server32 /data/local/tmp/新文件名" |
步骤二:端口转发
默认启动端口为23946。
另开一个cmd执行如下指令:
1 | adb forward tcp:23946 tcp:23946 |
第一个端口对应电脑端,第二个端口对应手机端。
执行后,将会通过电脑端的23946端口转发数据到手机端的23946端口。
步骤三:启动要调试的应用程序
你可以直接在手机进行启动,或者输入以下命令进行启动
1 | adb shell am start -D -n 包名/活动名 |
步骤四:使用IDA附加进程
用IDA打开想要调试的so库,调试器选择Remote ARM Linux/Android debugger
。
在菜单栏的Debugger -> Debugger options中设置程序暂停的时机
在菜单栏的Debugger -> Process options中设置主机和端口
默认端口127.0.0.1
在菜单栏的Debugger -> Attach to Process中选择要调试的进程进行附加
需要注意的是,在安装 APK 时,检查对应 apk 的属性,需要满足以下两个属性均为 true
,可以通过使用 apktool
解包后修改重新打包实现,也可以使用面具模块或 xposed
模块实现。
1 | android:debuggable="true" |
果不想这么复杂,直接把 so 复制到 /data/app/包名/lib
里面,就会优先加载这个目录里面的 so,或者直接在 IDA 中的 module 中选择 base.apk
,然后定位到需要调试的地址,然后手动按 P 将其转为 code,然后就可以下断点调试。
在 IDA 中直接 F9 运行,此时 APP 将会运行起来,IDA 将会弹出下列界面,点击 same 就可以了。
为什么要将两个属性设置为True
呢?
当android:extractNativeLibs="true"
时
安装 APK 时系统会把里面的 .so
解压到磁盘,例如:
1 | /data/app/com.xxx.xxx-xxxx/lib/arm64/libtarget.so |
- 进程加载时,linker 会从这个路径用
mmap
映射到内存。 - 因为有实际的文件路径,IDA 动态调试时 attach 到进程,就能看到这个映射关系(
maps
里会有一行带libtarget.so
的路径)。 - 这样 IDA 就可以直接自动关联你的
.so
文件,地址对齐也正确。
当android:extractNativeLibs="false"
时
这种情况下,APK 安装后不会把
.so
文件解压出来,而是 直接在 APK 压缩包里。当程序运行时,
linker
会用一种特殊的方式:- 打开
base.apk
- 定位 zip 里的
.so
mmap
那段压缩包里的数据- 然后根据 ELF 头信息建立
soinfo
结构,把段映射到内存里
- 打开
所以内存里依然有 ELF 格式的
.so
,但是磁盘上没有独立文件,/proc/<pid>/maps
里只会显示类似:
1 | /data/app/com.xxx.xxx-xxxx/base.apk!lib/arm64/libtarget.so |
- 因为没有真实文件,IDA attach 的时候就没法直接知道应该如何和你本地的
libtarget.so
对齐。
android:debuggable="true"
这个属性呢?
这个属性和前面 extractNativeLibs
不一样,它完全是 跟调试器 attach 权限相关的。
它是
AndroidManifest.xml
里的application
节点属性。决定了这个应用能不能被外部调试器(如 IDA、gdb、Android Studio Debugger)attach。