mcpField
A
analyzing-rust-binaries

analyzing-rust-binaries

github.com/trilwu安全17751 次安装12 周前更新
代码仓库

关于 analyzing-rust-binaries

这是一份面向安全研究人员和逆向工程师的 Rust 二进制分析指南,适用于恶意软件、命令行程序和编译后的服务。它利用 panic 字符串、符号和依赖路径识别项目代码,并解释 Rust 反汇编中的常见结构。

主要功能

  1. 01从 panic 字符串提取源码路径
  2. 02识别 Cargo 依赖与构建信息
  3. 03解析旧版和 v0 符号名
  4. 04区分项目代码与标准库代码
  5. 05识别 Result、Option 和泛型编译特征
  6. 06检查 unsafe、FFI 与反序列化风险

适用场景

  1. 01分析剥离符号的 Rust 程序
  2. 02调查 Rust 恶意软件或植入程序
  3. 03审查编译后二进制中的安全问题
  4. 04定位服务中的网络处理与错误路径

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trilwu/secskills --skill analyzing-rust-binaries

安装后在 Claude Code 中输入/analyzing-rust-binaries确认已加载。

详细介绍

分析 Rust 二进制文件

Rust 二进制文件体积庞大、采用静态链接,而且充斥着内联的泛型代码——但它们泄露的信息比大多数人预想的更多。panic 位置会嵌入源文件路径和行号,符号包含 crate 和模块结构,标准库的格式化机制也一眼就能认出。关键在于分清哪些代码属于你,哪些属于占比达 90% 的 core、alloc 和 vendored crate。

适用场景

  • strings 显示 rustc version、core::panicking、/rustc/<hash>/library/
  • 符号以 _ZN...17h<hex>E(旧版)或 _R(v0 名称修饰)开头
  • 剥离符号后的二进制文件大小为 3–30 MB,动态导入项很少
  • 分析 Rust 恶意软件、Rust CLI 或编译后的 Rust 服务

不适用场景

  • Go 二进制文件——使用 analyzing-go-binaries;符号恢复方式完全不同
  • .NET 或 Unity——使用对应的技能
  • 疑似恶意软件,且尚未隔离——先使用 analyzing-malware
  • 有源代码可用——使用 auditing-code-for-vulnerabilities,其中包含 Rust unsafe 检查清单

确认并识别特征

strings -n 8 target | rg -m8 'rustc version|/rustc/[0-9a-f]{40}|core::panicking|cargo/registry'

在反汇编之前,两个信息就能完成大部分工作:

panic 字符串会泄露源代码树结构。 Rust 会嵌入每个 panic!、unwrap() 和边界检查对应的文件路径和行号。这能让你了解 crate 布局、开发者的目录结构,而且通常还能知道项目名称。

strings -n 10 target | rg 'src/[a-z_/]+\.rs' | sort -u | head -40
# → src/main.rs, src/crypto/aes.rs, /home/dev/projects/implant/src/c2.rs

注册表路径会指明依赖项。 编译进去的 vendored crate 会在 panic 位置留下 ~/.cargo/registry/src/.../<crate>-<version>/ 路径,这实际上就是一份依赖项列表。

strings target | rg -o 'cargo/registry/src/[^/]+/([a-z0-9_-]+-[0-9.]+)' -r '$1' | sort -u

模块布局加上依赖项列表的组合,通常能让你在阅读任何指令之前就判断出二进制文件的用途。

名称反修饰

Rust 有两种名称修饰方案,工具必须同时支持。

# Legacy: _ZN4core3fmt5Write9write_fmt17h<16 hex>E
nm -C target | head -30
rustfilt < symbols.txt

# v0 (RFC 2603): starts with _R, encodes generics and paths properly
rustfilt      # handles both
# Ghidra 11+ and IDA 8+ have v0 demanglers; enable Rust demangling in the
# analyzer options rather than reading raw symbols
# Filter to your target's code. Everything under core::, alloc::, std::, and
# a registry crate name is stock.
nm -C target 2>/dev/null | rg -v '^.* (core|alloc|std|hashbrown|serde|tokio)::' | head -40

如果符号已被完全剥离,可以退而查看 panic 字符串:每个字符串都紧邻包含它的函数,因此将 panic 消息与已知源代码路径交叉比对,就能确定其所在函数。

在反汇编器中阅读 Rust 代码

五种模式涵盖了大多数令人困惑之处:

单态化。 泛型函数会针对每种具体类型分别编译,因此 Vec<u8>::push 和 Vec<String>::push 是两个主体相似的独立函数。预期会遇到重复项,不要因为两个函数几乎相同就认定它们是复制粘贴而来。

Result 和 Option 返回值。 这些枚举按值返回,通常占用两个寄存器——一个判别值和一个负载。返回 Result<T, E> 的函数看起来像是返回一个结构体;调用后立即检查第一个寄存器的分支就是错误检查。认出这一点有助于读懂错误路径,而错误路径正是 bug 所在之处。

到处都是边界检查。 每次切片索引都会生成比较操作,并通过条件分支跳转到 panic 位置。这些代码会让反汇编列表变得杂乱;学会跳过它们。它们的缺失则很有参考价值——这意味着代码使用了 unsafe 或 get_unchecked。

Trait 对象分派。 dyn Trait 调用会通过 vtable 完成:一个指针二元组(数据、vtable),然后在固定的 vtable 偏移处进行间接调用。恢复 vtable 就能还原具体类型,与分析 C++ 的方式相同。

字符串处理。 Rust 的 String 和 &str 是指针加长度,没有终止符,与 Go 完全一样。相邻的字面量会在 strings 输出中连在一起;找到指针加载旁边的长度常量。

查找关键代码

# Panic paths point at your code; use them as anchors
strings -t x target | rg 'src/' | head -30      # offsets included
# Cross-reference an offset in the disassembler to land in the owning function

# Crypto and network crates are recognizable by their panic paths
strings target | rg -i 'ring-|rustls|openssl|aes-gcm|chacha20|reqwest|hyper|tokio'

对于服务或植入程序,最快的路径是:识别异步运行时(几乎总是 tokio),通过 panic 路径找到请求处理程序或 C2 循环,然后向外阅读。

Rust 专项安全审查

如果你是在查找漏洞,而不是研究行为:

  • unsafe 代码块是内存安全漏洞的藏身之处。在二进制文件中,留意索引操作是否缺少边界检查,以及符号仍然存在时,查找 from_raw_parts / transmute 调用点。
  • 整数溢出在调试构建中会被检查,在发布构建中则会静默回绕。发布版二进制文件的算术运算不会触发溢出异常。
  • **对攻击者可控输入使用 unwrap() / expect()**会导致远程 panic——这是一种拒绝服务,对于网络服务来说也是一个真实的安全问题。
  • FFI 边界。 接收指针的 extern "C" 函数是 Rust 安全保证失效之处。
  • 反序列化: 使用 serde 将数据反序列化为某些类型时,构造函数会强制执行不变量,但 Deserialize 不会。

分析源代码时,使用 auditing-code-for-vulnerabilities 和 cargo geiger。

Rust 恶意软件须知

Rust 正越来越多地用于勒索软件和加载器,主要是因为它支持交叉编译,且会增加分析人员的工作难度。以下信息仍然对你有帮助:

  • panic 路径会泄露开发者的构建环境——用户名、项目名称和目录结构,这些信息有助于归因。
  • 注册表路径中的依赖项列表可识别网络和加密 crate,从而在你阅读代码之前缩小 C2 协议和加密方案的范围。
  • 二进制文件中的 rustc 版本可用于确定构建时间。
  • 即使是经过激进剥离的样本,仍会输出边界检查 panic 的位置,除非使用 panic=abort 编译并进行了重度 strip;如果这些信息也消失了,请将其记为刻意采用的反分析措施。

将遏制和 IOC 工作交给 analyzing-malware。

应当驳斥的借口

  • “它被剥离了,没什么可恢复的。” Panic 字符串在剥离后仍会保留,并会标明源文件名。
  • “有成千上万个函数。” 大多数都来自 core/alloc/供应商打包的 crate。按反混淆后的路径筛选。
  • “这两个函数完全相同,是复制粘贴的。” 这是同一个泛型的单态化结果。
  • “反编译器显示了一个我看不懂的结构体返回值。” 这是 Result/Option。调用后的判别值测试对应错误分支。
  • “Rust 是内存安全的,所以不存在内存漏洞。” Rust 中存在 unsafe 和 FFI,而且借用检查器无法阻止逻辑漏洞。
  • “这些字符串都损坏了。” Rust 字符串由指针和长度构成,而不是以空字符结尾。

参考资料

  • analyzing-binaries — 通用初步分析、动态分析、反分析
  • analyzing-go-binaries — 另一个静态链接且体积较大的案例
  • analyzing-malware — Rust 样本的遏制和 IOC 分析
  • auditing-code-for-vulnerabilities — 使用源代码时的 Rust unsafe 检查清单
  • rustfilt、Ghidra/IDA Rust 反混淆器、cargo-geiger、Binary Ninja

评论

支持 Markdown