-
-
[原创] Blutter 工具 SIGSEGV 崩溃问题分析与修复 (附代码补丁)
-
发表于: 4天前 619
-
一、问题描述
在使用 Blutter(一款 Flutter 应用逆向工程工具)分析 Android Flutter 应用时,程序在执行过程中发生 SIGSEGV (段错误) 崩溃。
环境信息
- 工具: Blutter (最新版本)
- 目标应用: Flutter 3.11.4 编译的 Android ARM64 应用
- 操作系统: Kali Linux (x86_64)
- 执行命令:
python3 blutter.py ~/xxx/xxx/lib/arm64-v8a ~/xxx/blutter_out_put
错误信息
Dumping Object Pool
Traceback (most recent call last):
File "blutter.py", line 248, in <module>
main(args.indir, args.outdir, args.rebuild, args.vs_sln, args.no_analysis)
File "blutter.py", line 230, in main
main2(libapp_file, libflutter_file, outdir, rebuild_blutter, create_vs_sln, no_analysis)
File "blutter.py", line 221, in main2
build_and_run(input)
File "blutter.py", line 210, in build_and_run
subprocess.run([input.blutter_file, '-i', input.libapp_path, '-o', input.outdir], check=True)
subprocess.CallProcessError: Command '[...]' died with <Signals.SIGSEGV: 11>.
二、分析过程
1. 初步分析
首先检查了输入文件和 blutter 可执行文件的架构:
# 检查 libapp.so 架构
file libapp.so
# ELF 64-bit LSB shared object, ARM aarch64
# 检查 blutter 可执行文件架构
file blutter_dartvm3.11.4_android_arm64
# ELF 64-bit LSB pie executable, x86-64
初步怀疑是架构不匹配问题,但经过分析发现 blutter 的设计是正确的:
- Blutter 可执行文件运行在 x86-64 主机上
- Dart VM 库编译为 x86-64,但配置为解析 ARM64 快照数据
2. 使用 GDB 定位崩溃点
gdb -batch -ex "run ..." -ex "bt" --args blutter_dartvm3.11.4_android_arm64 ...
第一次崩溃回溯:
#0 0x00005555555c25c7 in DartDumper::getPoolObjectDescription[abi:cxx11](long, bool)
#1 0x00005555555c2aae in DartDumper::DumpObjectPool(char const*)
#2 0x00005555555827b2 in main
寄存器状态:
rax 0x0 0 ← NULL 指针!
rbx 0x7fffffffc9d0 140737488341456
rcx 0x408ec0000be00000 4651866571552063488
rdx 0x6d7138 7172408
关键发现:rax = 0x0,说明函数返回了 空指针,但代码直接解引用导致了崩溃。
3. 定位源代码问题
通过分析汇编代码和源码,发现问题出在 blutter/src/DartDumper.cpp 的 getPoolObjectDescription 函数:
// 第 803-806 行
const auto imm = pool.RawValueAt(idx + 1);
auto dartFn = app.GetFunction(imm - app.base()); // 可能返回 nullptr!
return std::format("[pp+{:#x}] UnlinkedCall: {:#x} - {}",
offset, dartFn->Address(), dartFn->FullName().c_str()); // 空指针解引用!
检查 GetFunction 函数实现:
// blutter/src/DartApp.cpp
DartFnBase* DartApp::GetFunction(uint64_t addr)
{
auto fn = functions.find(addr);
if (fn != functions.end()) {
return fn->second;
}
auto stub = stubs.find(addr);
if (stub != stubs.end()) {
return stub->second;
}
// ... 其他逻辑 ...
return nullptr; // 找不到时返回 nullptr!
}
4. 发现多处相同问题
通过代码搜索,发现共有 3 处存在相同的空指针解引用问题:
| 位置 | 函数 | 行号 |
|---|---|---|
| 1 | getPoolObjectDescription |
~806 |
| 2 | DumpStructHeaderFile |
~194 |
| 3 | ObjectToString (kFunctionCid case) |
~493 |
三、根本原因
Blutter 在处理某些 Flutter 应用时,DartApp::GetFunction() 可能返回 nullptr,但调用方没有检查返回值就直接解引用,导致段错误。
这种情况可能发生在:
- 应用使用了混淆技术
- 某些函数未在标准位置注册
- UnlinkedCall 引用的函数地址无法解析
四、修复方案
修复代码
文件: blutter/src/DartDumper.cpp
修复 1: getPoolObjectDescription 函数
// 第 803-809 行
auto unlinkTargetType = pool.TypeAt(idx + 1);
if (unlinkTargetType == dart::ObjectPool::EntryType::kImmediate) {
const auto imm = pool.RawValueAt(idx + 1);
auto dartFn = app.GetFunction(imm - app.base());
+ if (dartFn == nullptr) {
+ return std::format("[pp+{:#x}] UnlinkedCall: {:#x} - [unknown function]", offset, imm);
+ }
return std::format("[pp+{:#x}] UnlinkedCall: {:#x} - {}",
offset, dartFn->Address(), dartFn->FullName().c_str());
}
修复 2: DumpStructHeaderFile 函数
// 第 192-196 行
if (unlinkTargetType == dart::ObjectPool::EntryType::kImmediate) {
const auto imm = pool.RawValueAt(i + 1);
auto dartFn = app.GetFunction(imm - app.base());
+ if (dartFn == nullptr) {
+ name = std::format("UnlinkedCall_{:#x}_unknown_{:#x}", offset, imm);
+ } else {
name = std::format("UnlinkedCall_{:#x}_{:#x}", offset, dartFn->Address(), offset);
+ }
}
修复 3: ObjectToString 函数 (kFunctionCid case)
// 第 491-496 行
case dart::kFunctionCid: {
// stub never be in Object Pool
- auto dartFn = app.GetFunction(dart::Function::Cast(obj).entry_point() - app.base())->AsFunction();
+ auto dartFnBase = app.GetFunction(dart::Function::Cast(obj).entry_point() - app.base());
+ if (dartFnBase == nullptr) {
+ return std::format("Function: [unknown] ({:#x})", dart::Function::Cast(obj).entry_point() - app.base());
+ }
+ auto dartFn = dartFnBase->AsFunction();
if (dartFn->IsClosure()) {
重新编译
cd /root/apps/blutter
python3 blutter.py ~/xxx/xxx/lib/arm64-v8a ~/xxx/blutter_out_put --rebuild
五、修复验证
执行结果
libapp is loaded at 0x7effda200000
Dart heap at 0x7efe00000000
Analyzing the application
Dumping Object Pool
Generating application assemblies
Generating Frida script
输出文件
blutter_out_put/
├── asm/ # 反汇编的 Dart 代码
├── blutter_frida.js # Frida 动态分析脚本
├── ida_script/ # IDA Pro 辅助脚本
├── objs.txt # 对象完整转储
└── pp.txt # 对象池转储
输出样例
pp.txt (对象池转储):
pool heap offset: 0x580080
[pp+0x10] Stub: Subtype7TestCache (0x1f248c)
[pp+0x18] Stub: Subtype6TestCache (0x1f2768)
[pp+0x40] List(5) [0x1, 0x2, 0x2, 0x2, Null]
[pp+0x50] String: "file:///Users/flora/Work/Flutter/xxx/.dart_tool/flutter_build/dart_plugin_registrant.dart"
[pp+0x168] Obj!MethodChannel@40b431 : {
off_8: "plugins.flutter.io/path_provider",
off_c_Obj!tqa@40b811
}
asm/ABi.dart (反汇编代码):
// class id: 2108, size: 0x30, field offset: 0x8
class SM extends Object {
[closure] Future<void> <anonymous closure>(dynamic, tNa) async {
// ** addr: 0x6fdd80, size: 0x18c
// 0x6fdd80: EnterFrame
// 0x6fdd80: stp fp, lr, [SP, #-0x10]!
// 0x6fdd84: mov fp, SP
// ...
}
}
六、总结
问题本质
这是一个典型的 空指针解引用 漏洞,由以下因素导致:
GetFunction()函数设计上可能返回nullptr- 调用方未进行防御性检查
- 只在特定应用场景下触发(函数无法解析时)
修复效果
- ✅ 程序不再崩溃
- ✅ 成功生成所有逆向分析文件
- ✅ 对于无法解析的函数,输出
[unknown function]标记
建议
- 代码审查建议: Blutter 项目应检查所有
GetFunction()调用点,确保都有空指针检查 - 用户体验: 对于无法解析的函数,可以考虑增加日志输出,便于调试
- 防御性编程: 建议在
GetFunction()内部处理边界情况,或返回一个默认的空函数对象
七、相关资源
- Blutter 项目: ee8K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6%4L8%4u0S2N6$3W2@1i4K6u0r3j5X3I4#2N6s2c8W2M7R3`.`.
- Flutter 反向工程: Blutter 通过编译 Dart AOT Runtime 来实现 Flutter 应用的逆向分析
- 问题修复: 3 处空指针检查,共 13 行代码修改
最后于 4天前
被幻鳕编辑
,原因: 敏感信息脱敏
赞赏
赞赏
雪币:
留言: