首页
社区
课程
招聘
[原创] Blutter 工具 SIGSEGV 崩溃问题分析与修复 (附代码补丁)
发表于: 2026-4-8 18:11 1060

[原创] Blutter 工具 SIGSEGV 崩溃问题分析与修复 (附代码补丁)

2026-4-8 18:11
1060

在使用 Blutter(一款 Flutter 应用逆向工程工具)分析 Android Flutter 应用时,程序在执行过程中发生 SIGSEGV (段错误) 崩溃。

首先检查了输入文件和 blutter 可执行文件的架构:

初步怀疑是架构不匹配问题,但经过分析发现 blutter 的设计是正确的:

第一次崩溃回溯:

寄存器状态:

关键发现:rax = 0x0,说明函数返回了 空指针,但代码直接解引用导致了崩溃。

通过分析汇编代码和源码,发现问题出在 blutter/src/DartDumper.cppgetPoolObjectDescription 函数:

检查 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 被幻鳕编辑 ,原因: 敏感信息脱敏
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回