Die Oberfläche ist lokalisiert; Skill-Beschreibungen und -Dokumente werden per KI automatisch in Ihre Sprache übersetzt. Code, Befehle, Pfade und Fachbegriffe bleiben im Original, maßgeblich ist das Dokument des Autors.
Skills entdecken
Produktivität · SKILL.md

re-rust

re-rust

Rust 二进制逆向:符号、monomorphization、所有权模式。触发词:Rust、rust、Rust二进制、cargo

Independent GitHub snapshot · Automated classification · Original documentation governs

Ddslsdzc / rev-skillsApache-2.0

Skill-Dokumentation

GitHub

name: re-rust description: Rust 二进制逆向:符号、monomorphization、所有权模式。触发词:Rust、rust、Rust二进制、cargo capabilities: [lang-runtime-analysis]

Rust 二进制逆向(符号解译 / monomorphization / 所有权)

何时使用 / 何时不用

  • 用:确认是 Rust 编译的二进制(符号带 _ZN/_RNv 前缀)后,demangle 还原符号;识别泛型实例化(monomorphization)产生的重复代码;通过所有权/析构(drop)模式理解对象生命周期;分析 async/await 状态机与 tokio/serde 等库模式
  • 用:恶意 Rust 样本([[re-malware]] 静态还原——Rust 重写的木马/勒索增多)
  • 不用:非 Rust 原生程序(C/C++/Go 直接 [[re-binary-core]];Go 走 [[re-go]])
  • 不用:确认带壳先走 [[re-anti-analysis]](Rust 二进制普遍无壳,但体积大)
  • 注意:动态步骤默认沙箱([[re-analyze/platform-tips]] 最高原则);静态优先(大型样本原则)

工具准备

参考 [[re-analyze/platform-tips]]——反编译/符号为静态步骤,免沙箱;Rust 二进制通常 1-10MB+,静态定位先行。

Rust 工具链(rustc/cargo——装符号工具用,可选)

  • 推荐 rustup(官方安装器,全平台最新): curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • Debian/Ubuntu: apt install rustc cargo(官方包,版本可能滞后);Fedora: dnf install rust cargo;Arch: pacman -S rust
  • macOS: brew install rust;Windows: choco install rust 或 rustup
  • 验证: rustc --version && cargo --version

rustfilt(Rust 符号 demangle,v0 新式 _RNv 必用)

  • Debian/Ubuntu: apt install rustfilt(官方包)
  • 其他发行版无官方包 → 官方安装: cargo install rustfilt
  • 验证: echo '_ZN3foo3barE' | rustfilt 输出 foo::bar

c++filt(legacy _ZN 可解;v0 不行)

  • Linux: binutils 自带(apt install binutils / dnf install binutils / pacman -S binutils)
  • macOS: brew install binutils(命令为 g++filt)或 brew install llvm(llvm-cxxfilt)
  • Windows: WSL 内 Linux 版或 MinGW binutils
  • 验证: c++filt _ZN3foo3barE 输出 foo::bar

Ghidra / IDA(反编译底座,装法见对应技能)

  • Ghidra 11+ 内置 Rust demangler(分析选项 "Demangler Rust",见 [[re-ghidra]])
  • IDA 无内置 → 社区 Rust demangler 插件(如 idarustdemangler),或先 rustfilt 预处理符号表
  • 验证: 反编译器函数名呈可读形式(而非 _ZN.../_RNv... 乱码)

nm / readelf(符号表与节,binutils)

  • 装法同 c++filt(binutils 自带)
  • 验证: nm --version

rustc --emit(编译产物理解,可选)

  • cargo rustc -- --emit=asm / --emit=llvm-ir / --emit=mir:用匹配版本对样本重编译,得到带符号的汇编/IR/MIR,对照理解编译器展开(monomorphization 泛型实例、async 状态机、drop glue 插入点)
  • --emit=link 为默认产物;--emit=metadata 不含代码但含类型/依赖信息
  • 验证: rustc --emit=asm hello.rs 产出 .s 文件

bloaty(体积分析,坑 2 用;安装见 [[re-go]] 工具准备)

  • 用途: bloaty -d symbols sample 按符号统计体积占比(monomorphization 展开体量可观)
  • 验证: bloaty --version

操作步骤

按顺序执行,每步记录证据(路径 + sha256,见 [[re-triage]])。

  1. 识别 Rust:

    file sample
    nm sample | head -20                  # 出现 _ZN...(legacy)或 _RNv...(v0)即 Rust
    strings sample | grep -iE 'rustc|rust_begin_unwind|panicking|__rust_alloc'
    
    • 特征符号: _ZN4core9panicking...E(panic 基建)、_ZN3std2rt...E(std 运行时)、__rust_alloc/__rust_dealloc(替代 malloc/free 的分配器)
    • std 程序入口是 main;no_std(嵌入式)用 _start
    • .comment 节指纹:readelf -p .comment sample 直接给出 rustc version <版本> (<commit>) 与链接器版本(实测 1.95 产物含 rustc version 1.95.0 (59807616e 2026-04-14) 与 Linker: LLD 22.1.2)——版本指纹先看这里,比猜 panic 风格快
    • panic 文案泄漏源码路径:strings sample | grep 'panicked at' 后的路径形如 /rustc/<commit>/library/std/src/...,<commit> 即精确工具链版本;业务代码路径同样在文案里
  2. 符号解译(demangle):

    nm -C sample | grep main              # 内置 demangle(仅 legacy _ZN 可解)
    nm sample | rustfilt > symbols.txt    # 全量 demangle(v0/legacy 通吃)
    grep -iE 'main|tokio|serde' symbols.txt
    
    • v0 新式(_RNv)只有 rustfilt/Ghidra 11+ 能解,c++filt 原样吐出(坑 5)
    • Ghidra 11+ 导入后自动 demangle:函数树直接可读;符号保留确认: 无输出 → 坑 1
    • v0 默认化后产物仍是混合命名:实测 1.95 产物 v0 符号占绝大多数(455 个 vs legacy 30 个),但 main 与部分 std 内部符号(含 .llvm. 后缀实例)仍是 legacy _ZN——识别与批量 demangle 时两种前缀都要覆盖
  3. monomorphization 识别:

    grep -oE '::[a-z_]+<[^>]*>' symbols.txt | sort | uniq -c | sort -rn | head
    
    • 同一泛型名多种类型参数 = 多份实例代码(例:Vec<u8> 与 Vec<i32> 各一份完整展开)
    • 分析策略:选最常见类型参数的代表实例,同组实例之间只比对类型相关分支;重复代码跳过(坑 2)
  4. 所有权/借用模式:

    • drop: core::ptr::drop_in_place::<T> 调用 = 对象析构点(vtable drop glue),沿它定位结构体生命周期与字段
    • 分配: __rust_alloc/__rust_dealloc 对应 malloc/free;alloc::boxed::Box(独占堆)、alloc::sync::Arc(引用计数增减)、alloc::vec::Vec/alloc::string::String(容量变化触发 realloc)
    • core::mem::replace/take/swap 调用是借用检查器催生的常见操作
    • 反编译里大量 memcpy + size 参数 → 结构拷贝/Clone(derive(Clone))
  5. 常见库模式:

    • tokio/async: tokio::runtime::Runtime::block_on、tokio::task/executor 符号;async fn 展开为状态机(坑 4)
    • serde: serde::de::/serde::ser:: 与 deserialize 符号——配置/协议解析入口,沿它找数据格式
    • 网络: std::net::TcpStream、tokio::net::;日志/配置: log::/tracing::、serde_json
    • strings 中的错误文案/URL/配置串辅助定位目标逻辑

跨域联合

  • [[re-binary-core]]:本技能由 core 网关引用——初勘 [[re-triage]]、反编译底座 [[re-ghidra]]/[[re-ida]]、库指纹 [[re-imports]]
  • [[re-ghidra]]:11+ 内置 Rust demangler 优先;[[re-ida]]:Rust demangler 插件或 rustfilt 预处理
  • [[re-emulation]]:no_std/嵌入式 Rust 固件无运行环境时可模拟执行辅助
  • 恶意场景:Rust 恶意样本(勒索/木马重写版多)静态按本技能 → 行为分析转 [[re-malware]]
  • [[re-analyze/platform-tips]] 相关分支:静态优先(大型样本)、动态默认沙箱、跨平台样本的 Wine/QEMU 分支

常见坑与陷阱

  • 符号被 strip,恢复极难:现象——nm 无输出、函数全 sub_*;原因——release profile 设 strip = true 或发布流程 strip(Rust 没有类似 Go pclntab 的恢复结构);对策——先确认 nm 是否真的空(多数发布版默认保留符号,操作步骤 2 排查);strip 后用 strings 找 panic 文案/错误信息(含文件路径与类型线索),.eh_frame/.gcc_except_table 的 FDE 可还原函数边界(Ghidra 分析选项开启 unwind 解析)
  • monomorphization 大量重复代码:现象——同一逻辑函数出现几十上百份,仅类型不同,函数列表爆炸;原因——泛型按每个类型参数实例化,每实例一份完整代码(体积也暴涨);对策——demangle 后按泛型名分组统计,选一个代表性实例分析,同组之间 diff 只比对类型相关分支;体积/重复问题用 bloaty(bloaty -d symbols)确认;分析时忽略其余实例
  • panic 路径与正常路径交织:现象——反编译里每个下标/unwrap/断言都带分支与 panic 调用,可读性被淹没;原因——Rust 在越界索引、unwrap()、assert! 处插入 panic_bounds_check/core::panicking::panic 检查(release 也保留索引检查),加上 unreachable! 死路径;对策——识别并跳过 core::panicking::/std::panicking::/panic_impl 相关调用与异常分支;panic 文案在 strings 里可见,可反向辅助定位调用点
  • async/await 状态机展开:现象——一个 async fn 变成结构体 + 巨大 match 状态循环,函数名带 {{closure}}/{{opaque}},找不到线性流程;原因——async fn 编译为 Future 状态机,每个 await 点是一个状态与保存点,poll 内按状态转移;对策——按状态号(0..N)追踪转移逻辑,重点读 poll 方法;识别 core::future/std::future::poll_with_tls_context 调用;tokio 程序找 tokio::runtime 符号与 block_on 定位入口
  • v0 新式命名 c++filt 解不了:现象——c++filt/nm -C 对 _RNv... 原样输出,符号仍是乱码;原因——v0 mangling 自 1.37 起可选(-C symbol-mangling-version=v0),2025 年底起成为默认(此前 2019-2025 年间绝大多数二进制仍是 legacy _ZN,nm -C 可解);对策——统一用 rustfilt(Debian/Ubuntu 官方包 apt install rustfilt,其余 cargo install rustfilt)批处理 nm sample | rustfilt;Ghidra 11+ 内置 demangler 直接自动解
  • panic 策略决定产物形态(unwind vs abort):现象——部分产物找不到 rust_begin_unwind 符号、.eh_frame 很小或缺失,反编译里 core::panicking 调用点直接是终止路径,行为分析时程序整体退出;原因——-C panic=abort(常见于嵌入式/体积敏感/安全硬化构建)去掉 unwind 表与恢复路径,默认 panic=unwind 才保留;对策——用 readelf -S 看 .eh_frame 存在性 + nm 找 rust_begin_unwind/rust_panic 判定策略;abort 产物里不必找 panic 后的恢复逻辑,panic 即终止;panic 文案(panicked at)两种策略都在
  • no_std 产物无 std 运行时符号:现象——找不到 _ZN3std2rt...E、__rust_alloc 也时有时无;原因——no_std(嵌入式/内核)不链接 std,仅 core + 自定义分配器;对策——识别改看 core 特征(_ZN4core...)+ .comment 版本指纹;入口是 _start 而非 main,分配器符号(__rust_alloc 或自定义 malloc)按需确认;这类固件常配 [[re-emulation]] 做无环境模拟
Gespeicherte Skills