计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
re-rust【效率与协作】Rust 二进制逆向:符号、monomorphization、所有权模式。触发词:Rust、rust、Rust二进制、cargo
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
_ZN/_RNv 前缀)后,demangle 还原符号;识别泛型实例化(monomorphization)产生的重复代码;通过所有权/析构(drop)模式理解对象生命周期;分析 async/await 状态机与 tokio/serde 等库模式参考 [[re-analyze/platform-tips]]——反编译/符号为静态步骤,免沙箱;Rust 二进制通常 1-10MB+,静态定位先行。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shapt install rustc cargo(官方包,版本可能滞后);Fedora: dnf install rust cargo;Arch: pacman -S rustbrew install rust;Windows: choco install rust 或 rustuprustc --version && cargo --version_RNv 必用)apt install rustfilt(官方包)cargo install rustfiltecho '_ZN3foo3barE' | rustfilt 输出 foo::bar_ZN 可解;v0 不行)apt install binutils / dnf install binutils / pacman -S binutils)brew install binutils(命令为 g++filt)或 brew install llvm(llvm-cxxfilt)c++filt _ZN3foo3barE 输出 foo::bar_ZN.../_RNv... 乱码)nm --versioncargo 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 -d symbols sample 按符号统计体积占比(monomorphization 展开体量可观)bloaty --version按顺序执行,每步记录证据(路径 + sha256,见 [[re-triage]])。
识别 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 的分配器)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 风格快strings sample | grep 'panicked at' 后的路径形如 /rustc/<commit>/library/std/src/...,<commit> 即精确工具链版本;业务代码路径同样在文案里符号解译(demangle):
nm -C sample | grep main # 内置 demangle(仅 legacy _ZN 可解)
nm sample | rustfilt > symbols.txt # 全量 demangle(v0/legacy 通吃)
grep -iE 'main|tokio|serde' symbols.txt
_RNv)只有 rustfilt/Ghidra 11+ 能解,c++filt 原样吐出(坑 5)main 与部分 std 内部符号(含 .llvm. 后缀实例)仍是 legacy _ZN——识别与批量 demangle 时两种前缀都要覆盖monomorphization 识别:
grep -oE '::[a-z_]+<[^>]*>' symbols.txt | sort | uniq -c | sort -rn | head
Vec<u8> 与 Vec<i32> 各一份完整展开)所有权/借用模式:
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 调用是借用检查器催生的常见操作常见库模式:
tokio::runtime::Runtime::block_on、tokio::task/executor 符号;async fn 展开为状态机(坑 4)serde::de::/serde::ser:: 与 deserialize 符号——配置/协议解析入口,沿它找数据格式std::net::TcpStream、tokio::net::;日志/配置: log::/tracing::、serde_jsonnm 无输出、函数全 sub_*;原因——release profile 设 strip = true 或发布流程 strip(Rust 没有类似 Go pclntab 的恢复结构);对策——先确认 nm 是否真的空(多数发布版默认保留符号,操作步骤 2 排查);strip 后用 strings 找 panic 文案/错误信息(含文件路径与类型线索),.eh_frame/.gcc_except_table 的 FDE 可还原函数边界(Ghidra 分析选项开启 unwind 解析)bloaty -d symbols)确认;分析时忽略其余实例unwrap/断言都带分支与 panic 调用,可读性被淹没;原因——Rust 在越界索引、unwrap()、assert! 处插入 panic_bounds_check/core::panicking::panic 检查(release 也保留索引检查),加上 unreachable! 死路径;对策——识别并跳过 core::panicking::/std::panicking::/panic_impl 相关调用与异常分支;panic 文案在 strings 里可见,可反向辅助定位调用点match 状态循环,函数名带 {{closure}}/{{opaque}},找不到线性流程;原因——async fn 编译为 Future 状态机,每个 await 点是一个状态与保存点,poll 内按状态转移;对策——按状态号(0..N)追踪转移逻辑,重点读 poll 方法;识别 core::future/std::future::poll_with_tls_context 调用;tokio 程序找 tokio::runtime 符号与 block_on 定位入口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 直接自动解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)两种策略都在_ZN3std2rt...E、__rust_alloc 也时有时无;原因——no_std(嵌入式/内核)不链接 std,仅 core + 自定义分配器;对策——识别改看 core 特征(_ZN4core...)+ .comment 版本指纹;入口是 _start 而非 main,分配器符号(__rust_alloc 或自定义 malloc)按需确认;这类固件常配 [[re-emulation]] 做无环境模拟