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

二进制逆向工程静态分析技能,用于在不执行程序的情况下深入分析二进制文件结构、逻辑和功能。通过radare2和Ghidra等工具进行函数枚举、代码反汇编、反编译、控制流分析和交叉引用追踪,帮助安全研究人员理解恶意软件、固件或闭源软件的内部工作机制。关键词:二进制分析、逆向工程、静态分析、反汇编、反编译、radare2、Ghidra、函数分析、控制流图、交叉引用、恶意软件分析、固件逆向、安全研究

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

name: binary-re-static-analysis description: 用于分析二进制结构、反汇编代码或反编译函数。通过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  # 字节级差异

# 记录发现:
# - 输出首先在哪里出现分歧?
# - 文件大小匹配吗?(逻辑错误与截断)
# - 损坏中出现了什么模式?

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


两阶段方法

阶段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 not installed"

# 如果缺失,使用以下命令安装:
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"
    }
  ]
}

知识记录

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

[BINARY-RE:static] {文件名} (sha256: {哈希值})

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

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

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

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

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

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

示例记录条目

[BINARY-RE:static] 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 如果达到足够的理解