Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VelaOS 固件函数名恢复工具

从无符号 VelaOS 固件(小米基于 NuttX 的可穿戴设备操作系统)中自动恢复函数名称。组合多种分析技术,从纯二进制固件中尽可能多地识别函数。

当前最佳结果:小米手表 S3 v4.8.0 — 19,010 / 22,172 个函数已命名(85.7%)

工作原理

工具使用五种互补技术识别函数名:

  1. __func__ 字符串交叉引用 — C 编译器在使用 __func__ 时会将函数名存为字符串常量。找到这些字符串并追踪引用,即可 100% 确定函数名。

  2. 跨版本字节签名匹配 — 不同固件版本中许多函数的机器码完全相同。从已命名版本提取字节签名,在目标版本中匹配。支持多版本迭代交叉传播。

  3. 跨架构特征匹配 — 模拟器(ARM-A 架构,有完整符号)与真机固件(Cortex-M 架构,无符号)共享架构无关特征。通过字符串指纹(Jaccard 相似度)和调用图拓扑进行匹配。

  4. 反向调用图投票 — 对于无字符串引用的函数(如 mallocmemcpy):如果一个未知函数的大部分调用者都映射到模拟器中调用某个已知函数的调用者,则它们很可能是同一函数。

  5. 高扇入函数排名匹配 — 对仍未命名的高价值函数,按真机/模拟器 fan-in 排名对齐,并结合 fan-out 与函数大小相似度进行筛选。该方法通常提供中等置信度候选,适合人工复核后确认(例如 memsetabortmalloc/free 相关函数)。

环境要求

  • Python 3.8+
  • Ghidra 11.x+(用于步骤 1-2)
  • Python 依赖:
    pip install pyelftools capstone

安装配置

1. 克隆项目

git clone <repo-url>
cd ReverseVela

2. 安装 Python 依赖

pip install pyelftools capstone

3. 安装 Ghidra

https://ghidra-sre.org/ 下载并解压到以下任意位置(自动检测):

  • ~/Documents/Dev/Tools/ghidra_*(Windows)
  • ~/ghidra
  • C:\ghidra/opt/ghidra

4. 放置固件文件

firmware/
  S3/
    4.8.0/
      vela_ap.bin     # 主 AP 固件二进制文件
    4.6.10/
      vela_ap.bin
    ...

5. 放置模拟器 ELF(带完整符号)

simulator/
  vela-pre-4.0/
    nuttx             # 带完整调试符号的 ELF
  vela-release-4.0/
    nuttx
  vela-watch-5.0/
    nuttx
  ...

系统会根据固件中的 NuttX 版本字符串自动选择匹配的模拟器。

6. 验证配置

python pipeline.py --info

显示已发现的设备、固件版本、模拟器和 Ghidra 路径。

使用方法

运行完整分析(推荐)

python pipeline.py --device S3 --version 4.8.0

默认运行多版本迭代增强模式(质量最高):

  1. 运行步骤 1-3(Ghidra 导入/导出 + 模拟器特征提取)处理所有版本
  2. 运行步骤 4-7(匹配 + 编译)处理每个固件版本
  3. 用增强后的符号表重新进行跨版本匹配(各版本互相交叉传播函数名)
  4. 生成最终输出 output/S3/4.8.0/symbols.csv

快速单版本运行

python pipeline.py --device S3 --version 4.8.0 --no-enrich

更快但结果更少(不进行多版本交叉传播)。

只运行指定步骤

# 只运行步骤 4 到 7
python pipeline.py --device S3 --version 4.8.0 --steps 4-7

# 只运行步骤 5 和 7
python pipeline.py --device S3 --version 4.8.0 --steps 5,7

强制重新生成

python pipeline.py --device S3 --version 4.8.0 --force

忽略所有缓存,重新运行全部步骤。

流水线步骤

步骤 工具 脚本 说明
1 Ghidra RenameByStringXref.java 导入固件 + __func__ 交叉引用重命名
2 Ghidra Export*.java 导出函数特征和字节签名
3 Python extract_sim_features.py 提取模拟器 ELF 特征 + 签名
4 Python cross_version_match.py 跨版本字节签名匹配
5 Python cross_arch_match.py 跨架构特征匹配
6 Python reverse_cg_match.py + high_fanin_match.py 反向调用图投票 + 高扇入函数排名匹配
7 Python compile_results.py 合并所有结果 → 最终符号表

步骤 1-3 处理所有固件版本和模拟器(已有缓存自动跳过)。步骤 4-7 处理目标版本。 其中步骤 6 会同时产出 matches/reverse_cg.csvmatches/high_fanin.csv,后者默认作为“中置信候选池”参与最终合并。

输出说明

运行成功后,生成以下文件:

output/S3/4.8.0/
  symbols.csv      # 最终符号表(地址、名称、置信度、来源、证据)
  for_ghidra.csv   # 简化版 CSV,用于导入 Ghidra
  report.txt       # 人类可读的分析报告

symbols.csv 格式

address,name,confidence,source,evidence
0x2c301234,nx_start,100,xref_confirmed,__func__ unique xref
0x2c305678,sched_lock,95,binary_crossver,4 versions agree (24-byte sig)
0x2c30abcd,some_func,75,cross_arch,string_match: 3 shared strings jaccard=0.67

置信度等级:

  • >=80%(高)— 非常可能正确,可直接使用
  • 60-79%(中)— 大概率正确,关键函数建议人工验证
  • <60%(低)— 最佳猜测,谨慎使用

导入 Ghidra

# 方式一:在 Ghidra Script Manager 中运行 ApplyNames.java,参数为 CSV 路径
# 方式二:使用 headless 模式:
analyzeHeadless ghidra_projects S3_4.8.0 -process -noanalysis \
  -scriptPath ghidra_scripts -postScript ApplyNames.java output/S3/4.8.0/for_ghidra.csv

缓存机制

每个步骤检查输出文件是否已存在,已有则 [CACHED] 跳过:

  • 重新运行流水线很快(仅执行未缓存的步骤)
  • 使用 --force 强制重新生成全部
  • 步骤 7(compile)总是重新执行(毫秒级合并操作)

可安全删除的目录: output/sim_features/ghidra_projects/ 均可完全删除,流水线会自动重建。

添加新设备

  1. config.pyDEVICES 字典中添加设备参数:

    DEVICES = {
        "NewDevice": {
            "name": "设备显示名称",
            "chip": "芯片型号",
            "arch": "ARM:LE:32:v8-m",       # Ghidra 处理器 ID
            "base_addr": 0x2C300000,          # Flash 基地址
            "header_size": 16,
            "firmware_binary": "vela_ap.bin",
        },
    }
  2. 将固件放入 firmware/NewDevice/{version}/

  3. 验证:python pipeline.py --info

  4. 运行:python pipeline.py --device NewDevice --version 1.0.0

添加新模拟器

将模拟器目录放入 simulator/,确保其中包含 nuttx ELF 文件:

simulator/
  my-new-sim/
    nuttx          # 带调试符号的 ELF

系统会根据固件中的 NuttX 版本字符串自动匹配模拟器。如需添加新的版本检测规则,修改 config.py 中的 NUTTX_VERSION_SIGNATURES

项目结构

config.py                          # 设备配置、路径发现、NuttX 版本检测
pipeline.py                        # 主流水线调度器

scripts/
  extract_sim_features.py          # 模拟器 ELF → 函数特征 + 字节签名
  cross_version_match.py           # 跨版本字节签名匹配
  cross_arch_match.py              # 跨架构(模拟器 → 真机)匹配
  reverse_cg_match.py              # 反向调用图投票
  high_fanin_match.py              # 高扇入函数排名匹配(libc/高价值候选)
  compile_results.py               # 合并所有结果 → 最终符号表
  utils.py                         # 共用工具函数

ghidra_scripts/
  RenameByStringXref.java          # __func__ 字符串交叉引用自动重命名
  ExportFunctionFeatures.java      # 导出 device_functions/calls/strings.csv
  ExportNamedFunctions.java        # 导出 named_functions.csv + stripped_functions.csv
  ExportCFG.java                   # 导出控制流图特征
  ApplyNames.java                  # 从 CSV 将函数名应用到 Ghidra 项目

技术细节

  • 目标芯片:BES2700BP(ARM Cortex-M55,ARMv8-M Mainline,Thumb-2 指令集)
  • Flash 基地址0x2C300000,16 字节固件头
  • 模拟器:ARM-A 架构 ELF,带完整符号表
  • 跨架构大小比例:Thumb-2 与 ARM-A 函数大小通常在 0.2x-5.0x 范围内(匹配时验证)
  • 字节签名:每个函数的前 N 字节(12 或 24 字节),hex 编码
  • NuttX 版本检测:扫描固件二进制中的版本字符串(NuttX-12.3.0curl 7.86.0-DEV 等)

许可

本项目仅用于研究和教育目的。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages