二进制逆向静态分析Skill binary-re-static-analysis

这是一个用于对可执行二进制文件进行深度静态分析的技能,主要功能包括:使用radare2和Ghidra工具进行函数枚举、代码反汇编、反编译、控制流图分析、交叉引用追踪等。该技能帮助安全研究人员在不运行程序的情况下理解二进制文件的结构、逻辑和数据流,常用于漏洞挖掘、恶意软件分析、软件逆向工程等领域。关键词:二进制分析、逆向工程、静态分析、反汇编、反编译、radare2、Ghidra、函数分析、控制流图、交叉引用。

逆向工程 5 次安装 75 次浏览 更新于 3/1/2026

名称: 二进制逆向静态分析 描述: 用于分析二进制结构、反汇编代码或反编译函数。通过radare2 (r2) 和 Ghidra headless 进行深度静态分析 - 函数枚举、交叉引用 (xrefs)、反编译、控制流图。关键词 - “反汇编”, “反编译”, “此函数功能”, “查找函数”, “分析代码”, “r2”, “ghidra”, “pdg”, “afl”

静态分析 (阶段 2-3)

目的

在不执行的情况下理解二进制结构及逻辑。映射函数、追踪数据流、反编译关键代码。

使用时机

  • 在初步分类确定架构和ABI之后
  • 为理解被识别为有趣的具体函数时
  • 当动态分析不切实际或存在风险时
  • 在进行动态验证前建立假设时

预分析:首先比较已知输入/输出

关键: 在深入反汇编之前,检查是否存在已知的输入/输出。

⚠️ 需要人工批准 - 任何执行(即使是用于I/O比较)前都需要获得明确批准。

# 安全:使用模拟器处理跨架构二进制文件(在人工批准后)
# ARM32:
qemu-arm -L /usr/arm-linux-gnueabihf -- ./binary < input.txt > actual.txt

# ARM64:
qemu-aarch64 -L /usr/aarch64-linux-gnu -- ./binary < input.txt > actual.txt

# 基于Docker (macOS/跨架构 - 参见动态分析选项 D):
docker run --rm --platform linux/arm/v7 -v ~/samples:/work:ro \
  arm32v7/debian:bullseye-slim sh -c '/work/binary < /work/input.txt' > actual.txt

# x86-64 原生 (仍需批准):
./binary < input.txt > actual.txt

# 比较输出:
diff expected.txt actual.txt
cmp -l expected.txt actual.txt | head -20  # 字节级差异

# 记录发现:
# - 输出在何处首次出现分歧?
# - 文件大小是否匹配?(逻辑错误 vs 截断)
# - 损坏中出现了什么模式?

此步骤通常在代码分析之前就揭示了漏洞类别。


两阶段方法

阶段 1 (轻量): 函数枚举、字符串、导入 - 快速,覆盖广泛 阶段 2 (深度): 针对性反编译、CFG分析 - 慢速,聚焦

阶段 1:轻量分析 (radare2)

分析深度选择

二进制大小 命令 权衡
< 500KB aaa 完整分析,可能较慢
500KB - 5MB aa; aac 函数 + 所有调用目标
> 5MB aa + 针对性的 af @addr 快速,手动控制深度

会话设置

# 启动 r2 并控制分析
r2 -q0 -e scr.color=false -e anal.timeout=120 -e anal.maxsize=67108864 binary

# 在 r2 内部 (根据二进制大小选择):
aa       # 基础分析
aac      # 同时分析所有调用目标 (推荐用于大多数二进制文件)

关键设置:

  • anal.timeout=120 - 防止分析失控
  • anal.maxsize=67108864 - 最大函数大小 64MB
  • 中型二进制文件使用 aa; aac,小型文件仅使用 aaa

处理未分析的调用目标

如果 axtj 对已知导入返回空结果:

# 导入可能被间接调用或分析深度不足
# 选项 1: 更深层次的分析
aac   # 分析所有调用

# 选项 2: 在调用目标处手动创建函数
af @0x8048abc

# 选项 3: 搜索对导入地址的引用
axtj @sym.imp.connect

函数枚举

# 所有函数 (JSON格式)
aflj

# 按名称模式过滤
aflj~main
aflj~init
aflj~network
aflj~send
aflj~recv

# 函数计数
afl~?

交叉引用分析

# 谁调用了这个函数?
axtj @sym.imp.connect

# 这个函数调用了什么?
axfj @sym.main

# 对地址的数据引用
axtj @0x12345

字符串-函数关联

# 查找哪个函数包含某个字符串
izj~api.vendor.com
# 记下 vaddr,然后查找包含它的函数
afi @0xVADDR

# 或者搜索并映射
"/j api"    # 搜索字符串
axtj @@hit* # 对所有命中结果的交叉引用

导入/导出映射

# 导入及其地址
iij

# 导出及其地址
iEj

# 符号 (如果未剥离)
isj

快速反汇编

# 将函数反汇编为 JSON
pdfj @sym.main

# 从地址开始反汇编 N 条指令
pdj 20 @0x8400

# 打印函数摘要
afi @sym.main

阶段 2:深度分析

r2ghidra 可用性检查

在尝试反编译之前,验证 r2ghidra 是否已安装:

# 检查 r2ghidra 是否可用
r2 -qc 'pdg?' - 2>/dev/null | grep -q Usage && echo "r2ghidra OK" || echo "SKIP: r2ghidra 未安装"

# 如果缺失,使用以下命令安装:
r2pm -ci r2ghidra

如果 r2ghidra 不可用: 依赖反汇编 (pdf) 和交叉引用分析 (axt/axf)。

针对性反编译 (r2ghidra)

# 反编译特定函数
pdgj @sym.target_function

# 或命名函数
pdgj @sym.main

Ghidra Headless (大型二进制文件)

对于复杂函数或当 r2ghidra 处理困难时:

# 创建分析项目并运行脚本
analyzeHeadless /tmp/ghidra_proj proj \
  -import binary \
  -overwrite \
  -processor ARM:LE:32:v7 \
  -postScript ExportDecompilation.java sym.target_function \
  -deleteProject

处理器字符串:

  • ARM 32位: ARM:LE:32:v7ARM:LE:32:Cortex
  • ARM 64位: AARCH64:LE:64:v8A
  • x86_64: x86:LE:64:default
  • MIPS LE: MIPS:LE:32:default
  • MIPS BE: MIPS:BE:32:default

控制流分析

# 函数中的基本块
afbj @sym.main

# 函数调用图 (dot 格式)
agCd @sym.main > callgraph.dot

# 控制流图
agfd @sym.main > cfg.dot

数据结构恢复

# 分析局部变量
afvj @sym.main

# 栈帧布局
afvd @sym.main

# 全局数据引用
adrj

分析模式

模式:网络函数追踪

# 查找所有网络相关调用
axtj @sym.imp.socket
axtj @sym.imp.connect
axtj @sym.imp.send
axtj @sym.imp.recv
axtj @sym.imp.SSL_read
axtj @sym.imp.SSL_write

# 追踪调用者链
for func in $(aflj | jq -r '.[].name'); do
  axfj @$func | grep -q "socket\|connect" && echo $func
done

模式:配置文件分析

# 查找文件操作
axtj @sym.imp.open
axtj @sym.imp.fopen

# 追踪字符串参数
"/j /etc"
"/j .conf"
"/j .json"

# 检查哪些函数引用了这些路径

模式:加密算法识别

# 常见加密导入
axtj @sym.imp.EVP_EncryptInit
axtj @sym.imp.AES_encrypt
axtj @sym.imp.SHA256

# 硬编码密钥 (检查加密调用附近的字符串)
izj | jq '.strings[] | select(.length == 16 or .length == 32)'

r2 JSON 命令参考

命令 输出 使用场景
aflj 函数列表 映射代码结构
axtj @addr 对地址的引用 谁使用了这个?
axfj @addr 从地址出发的引用 它调用了什么?
pdfj @addr 反汇编 理解指令
pdgj @addr 反编译 伪C代码输出
afbj @addr 基本块 控制流
izj 数据字符串 配置、URL
iij 导入 外部依赖
iEj 导出 公共接口
afvj @addr 局部变量 栈分析

输出格式

将分析发现记录为结构化事实:

{
  "functions_analyzed": [
    {
      "name": "sub_8400",
      "address": "0x8400",
      "size": 256,
      "calls": ["socket", "connect", "send"],
      "called_by": ["main", "init_network"],
      "strings_referenced": ["api.vendor.com"],
      "hypothesis": "network_initialization"
    }
  ],
  "call_graph": {
    "main": ["init_config", "init_network", "main_loop"],
    "init_network": ["sub_8400", "SSL_CTX_new"]
  },
  "data_flow": [
    {
      "source": "config_file_read",
      "through": ["parse_config", "extract_url"],
      "sink": "connect_to_server"
    }
  ]
}

知识记录

静态分析后,为情景记忆记录发现:

[二进制逆向:静态] {文件名} (sha256: {哈希值})

已分析函数: {数量}
已执行反编译: {是|否}

关键函数:
  事实: 地址 {addr} 处的函数调用了 {imports} (来源: r2 axfj)
  事实: 地址 {addr} 处的函数引用了字符串 "{string}" (来源: r2 axtj)
  事实: 函数 {name} 似乎用于 {purpose} (来源: 反编译)

交叉引用:
  事实: {caller} 调用了 {callee} (来源: r2 axtj)

假设更新: {精炼后的理论} (置信度: {新值})
  支持: {事实ID}
  矛盾: {事实ID}

新问题:
  问题: {发现的未知项}

已回答问题:
  已解决: {问题} → {答案}

示例记录条目

[二进制逆向:静态] thermostat_daemon (sha256: a1b2c3d4...)

已分析函数: 47
已执行反编译: 是 (函数 0x8400)

关键函数:
  事实: 函数 0x8400 调用 curl_easy_perform, curl_easy_setopt (来源: r2 axfj)
  事实: 函数 0x8400 引用字符串 "api.thermco.com/telemetry" (来源: r2 axtj)
  事实: 函数 0x9200 使用 jsmn 库解析 JSON (来源: 反编译)
  事实: 函数 0x10800 是主循环,在 sleep(30) 后调用 0x8400 (来源: r2 pdf)

交叉引用:
  事实: main 调用 init_config (0x9000) 然后 main_loop (0x10800) (来源: r2 axtj)
  事实: main_loop 在循环中调用 send_telemetry (0x8400) (来源: r2 pdf)

假设更新: 遥测客户端每30秒向 api.thermco.com 发送数据 (置信度: 0.85)
  支持: URL 字符串, curl 导入, 循环中的 sleep(30)
  矛盾: 无

新问题:
  问题: 遥测负载中包含哪些数据字段?
  问题: 是否存在任何身份验证/API密钥?

已回答问题:
  已解决: "什么端点?" → 通过 HTTPS 访问 api.thermco.com/telemetry

决策点

静态分析后:

  1. 识别出关键函数? → 准备进行动态验证
  2. 行为不明确? → 尝试动态分析以进行运行时观察
  3. 检测到加密? → 记录密钥处理方式,供安全审查参考
  4. 存在反分析模式? → 考虑使用 Unicorn 片段模拟

后续步骤

binary-re-dynamic-analysis 通过运行时观察验证假设 → binary-re-synthesis 如果达到足够理解