关于 analyzing-rust-binaries
这是一份面向安全研究人员和逆向工程师的 Rust 二进制分析指南,适用于恶意软件、命令行程序和编译后的服务。它利用 panic 字符串、符号和依赖路径识别项目代码,并解释 Rust 反汇编中的常见结构。
主要功能
- 01从 panic 字符串提取源码路径
- 02识别 Cargo 依赖与构建信息
- 03解析旧版和 v0 符号名
- 04区分项目代码与标准库代码
- 05识别 Result、Option 和泛型编译特征
- 06检查 unsafe、FFI 与反序列化风险
适用场景
- 01分析剥离符号的 Rust 程序
- 02调查 Rust 恶意软件或植入程序
- 03审查编译后二进制中的安全问题
- 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,其中包含 Rustunsafe检查清单
确认并识别特征
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— 使用源代码时的 Rustunsafe检查清单- rustfilt、Ghidra/IDA Rust 反混淆器、
cargo-geiger、Binary Ninja