-
-
[原创] Blutter 工具 SIGSEGV 崩溃问题分析与修复 (附代码补丁)
-
发表于: 2026-4-8 18:11 1060
-
在使用 Blutter(一款 Flutter 应用逆向工程工具)分析 Android Flutter 应用时,程序在执行过程中发生 SIGSEGV (段错误) 崩溃。
首先检查了输入文件和 blutter 可执行文件的架构:
初步怀疑是架构不匹配问题,但经过分析发现 blutter 的设计是正确的:
第一次崩溃回溯:
寄存器状态:
关键发现:rax = 0x0,说明函数返回了 空指针,但代码直接解引用导致了崩溃。
通过分析汇编代码和源码,发现问题出在 blutter/src/DartDumper.cpp 的 getPoolObjectDescription 函数:
检查 GetFunction 函数实现:
通过代码搜索,发现共有 3 处存在相同的空指针解引用问题:
Blutter 在处理某些 Flutter 应用时,DartApp::GetFunction() 可能返回 nullptr,但调用方没有检查返回值就直接解引用,导致段错误。
这种情况可能发生在:
文件: blutter/src/DartDumper.cpp
pp.txt (对象池转储):
asm/ABi.dart (反汇编代码):
这是一个典型的 空指针解引用 漏洞,由以下因素导致:
| 位置 | 函数 | 行号 |
|---|---|---|
| 1 | getPoolObjectDescription |
~806 |
| 2 | DumpStructHeaderFile |
~194 |
| 3 | ObjectToString (kFunctionCid case) |
~493 |
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>.
# 检查 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
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
// 第 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()); // 空指针解引用!
// 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!
}
// 第 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());
}
// 第 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);
+ }
}
// 第 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 # 对象池转储
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
}
最后于 2026-4-8 19:09
被幻鳕编辑
,原因: 敏感信息脱敏
赞赏
赞赏
雪币:
留言: