Rust静态分析工具

来源: https://github.com/pxiaoer/static-analysis#rust

  • C2Rust – C2Rust helps you migrate C99-compliant code to Rust. The translator (or transpiler) produces unsafe Rust code that closely mirrors the input C code.
  • C2Rust-C2Rust 帮助您将 c99兼容的代码迁移到 Rust。转换器(或 transpiler)生成不安全的 Rust 代码,这些代码紧密地映射了输入的 c 代码。

  • cargo udeps – Find unused dependencies in Cargo.toml. It either prints out a “unused crates” line listing the crates, or it prints out a line saying that no crates were unused.
  • Cargo.toml 中查找未使用的依赖项。它要么打印出一行“未使用的板条箱”列出板条箱,要么打印出一行说明没有板条箱未使用。

  • cargo-audit – Audit Cargo.lock for crates with security vulnerabilities reported to the RustSec Advisory Database.
  • cargo审计-审计 Cargo.lock 的板条箱与安全漏洞报告给 RustSec 咨询数据库。

  • cargo-bloat – Find out what takes most of the space in your executable. supports ELF (Linux, BSD), Mach-O (macOS) and PE (Windows) binaries.
  • Cargo-bloat-找出在您的可执行文件中占用大部分空间的内容。支持 ELF (Linux,BSD)、 Mach-O (macOS)和 PE (Windows)二进制文件。

  • cargo-deny – A cargo plugin for linting your dependencies. It can be used either as a command line too, a Rust crate, or a Github action for CI. It checks for valid license information, duplicate crates, security vulnerabilities, and more.
  • Cargo-deny-一个货物插件,用于连接您的依赖关系。它也可以用作命令行、 Rust crate 或者用于 CI 的 Github 动作。它检查有效的许可证信息、重复的板条箱、安全漏洞等。

  • cargo-expand – Cargo subcommand to show result of macro expansion and #[derive] expansion applied to the current crate. This is a wrapper around a more verbose compiler command.
  • Cargo-expand-Cargo 子命令,以显示应用于当前板条箱的宏展开和 # [ derive ]展开的结果。这是一个更详细的编译器命令的包装器。

  • cargo-inspect – Inspect Rust code without syntactic sugar to see what the compiler does behind the curtains.
  • Cargo-Inspect ——检查没有语法糖的 Rust 代码,看看编译器在窗帘后面做了什么。

  • cargo-spellcheck – Checks all your documentation for spelling and grammar mistakes with hunspell (ready) and languagetool (preview)
  • Cargo-spellcheck-使用 hunspell (就绪)和 languagetool (预览)检查所有文档的拼写和语法错误

  • clippy – A code linter to catch common mistakes and improve your Rust code.
  • Clippy-一个代码链接捕捉常见错误,并改善您的锈代码。

  • dylint – A tool for running Rust lints from dynamic libraries. Dylint makes it easy for developers to maintain their own personal lint collections.
  • Dylint-从动态库运行锈线的工具。Dylint 使得开发人员可以轻松地维护他们自己的个人棉绒集合。

  • electrolysis ⚠️ – A tool for formally verifying Rust programs by transpiling them into definitions in the Lean theorem prover.
  • 电解 something-通过将锈病程序转换成 Lean 定理证明器中的定义,从而正式验证锈病程序的工具。

  • herbie ⚠️ – Adds warnings or errors to your crate when using a numerically unstable floating point expression.
  • 当使用数值不稳定的浮点表达式时,在箱子中添加警告或错误。

  • linter-rust ⚠️ – Linting your Rust-files in Atom, using rustc and cargo.
  • Linter-rust something-Linting your Rust-files in Atom,using rustc and cargo.

  • MIRAI – And abstract interpreter operating on Rust’s mid-level intermediate language, and providing warnings based on taint analysis.
  • 和抽象的解释器操作在 Rust 的中级中间语言上,并提供基于污染分析的警告。

  • prae – Provides a convenient macro that allows you to generate type wrappers that promise to always uphold arbitrary invariants that you specified.
  • Prae-提供了一个方便的宏,允许您生成类型包装器,承诺始终支持您指定的任意不变量。

  • Prusti – A static verifier for Rust, based on the Viper verification infrastructure. By default Prusti verifies absence of panics by proving that statements such as unreachable!() and panic!() are unreachable.
  • Prusti-基于 Viper 验证基础设施的锈蚀静态验证器。默认情况下,Prusti 验证没有恐慌通过证明声明,如不可及!()和恐慌!()无法联络。

  • Rudra ©️ – Rust Memory Safety & Undefined Behavior Detection. It is capable of analyzing single Rust packages as well as all the packages on crates.io.
  • 锈蚀记忆安全与未定义行为检测。它能够分析单个锈包以及在 crates.io 上的所有包。

  • Rust Language Server – Supports functionality such as ‘goto definition’, symbol search, reformatting, and code completion, and enables renaming and refactorings.
  • Rust Language Server ——支持诸如“ goto definition”、符号搜索、重新格式化和代码完成等功能,并支持重命名和重构。

  • rust-analyzer – Supports functionality such as ‘goto definition’, type inference, symbol search, reformatting, and code completion, and enables renaming and refactorings.
  • Rust-analyzer-支持诸如“ goto definition”、类型推断、符号搜索、重新格式化和代码完成等功能,并支持重命名和重构。

  • rust-audit – Audit Rust binaries for known bugs or security vulnerabilities. This works by embedding data about the dependency tree (Cargo.lock) in JSON format into a dedicated linker section of the compiled executable.
  • 锈蚀审计-审计锈蚀二进制已知的错误或安全漏洞。这是通过将关于依赖关系树(Cargo.lock)的 JSON 格式数据嵌入到编译的可执行文件的专用链接器部分来实现的。

  • rustfix – Read and apply the suggestions made by rustc (and third-party lints, like those offered by clippy).
  • Rustfix ——阅读和应用 rustc 提供的建议(以及第三方 lints,比如 clippy 提供的建议)。

  • rustfmt – A tool for formatting Rust code according to style guidelines.
  • 一个根据样式指南格式化锈病代码的工具。

  • RustViz – RustViz is a tool that generates visualizations from simple Rust programs to assist users in better understanding the Rust Lifetime and Borrowing mechanism. It generates SVG files with graphical indicators that integrate with mdbook to render visualizations of data-flow in Rust programs.
  • RustViz-RustViz 是一个工具,可以从简单的 Rust 程序生成可视化,以帮助用户更好地理解 Rust Lifetime 和 boring 机制。它生成具有图形指示器的 SVG 文件,这些指示器与 mdbook 集成,以便在 Rust 程序中呈现数据流的可视化。

  • warnalyzer – Show unused code from multi-crate Rust projects
  • 警报解除器-显示未使用的代码从多个板条箱锈项目

Rust逆向——02.函数

例子1:

原题

fn main() {
    call_me();
}

修改后:

fn call_me() {

}

fn main() {
    call_me();
}

这里添加了一个函数定义,我们看下结果

例子2:

fn main() {
    call_me(3);
}

fn call_me(num:) {
    for i in 0..num {
        println!("Ring! Call number {}", i + 1);
    }
}

修改后:

fn main() {
    call_me(3);
}

fn call_me(num:i32) {
    for i in 0..num {
        println!("Ring! Call number {}", i + 1);
    }
}

例子3:

原题:

fn main() {
    call_me();
}

fn call_me(num: u32) {
    for i in 0..num {
        println!("Ring! Call number {}", i + 1);
    }
}

修改后:

fn main() {
    call_me(2);
}

fn call_me(num: u32) {
    for i in 0..num {
        println!("Ring! Call number {}", i + 1);
    }
}

例子4:

原题:

fn main() {
    let original_price = 51;
    println!("Your sale price is {}", sale_price(original_price));
}

fn sale_price(price: i32) -> {
    if is_even(price) {
        price - 10
    } else {
        price - 3
    }
}

fn is_even(num: i32) -> bool {
    num % 2 == 0
}


修改后:

fn main() {
    let original_price = 51;
    println!("Your sale price is {}", sale_price(original_price));
}

fn sale_price(price: i32) -> i32 {
    if is_even(price) {
        price - 10
    } else {
        price - 3
    }
}

fn is_even(num: i32) -> bool {
    num % 2 == 0
}

例子5:

原题:

fn main() {
    let answer = square(3);
    println!("The answer is {}", answer);
}

fn square(num: i32) -> i32 {
    num * num;
}

修改后:

fn main() {
    let answer = square(3);
    println!("The answer is {}", answer);
}

fn square(num: i32) -> i32 {
    num * num
}

Rust逆向——01.变量

我们拿 rust rustling 的练习拿来做例子。如果大家不懂Rust语法,也可以通过这些例子来学下语法。

例子1:

原题:

fn main() {
    x = 5;
    println!("x has the value {}", x);
}

修改后:

fn main() {
    let x = 5;
    println!("x has the value {}", x);
}

Rust的变量定义使用let, let创建的变量为绑定,let x=5指的是把x和5创建了一种关联关系 。

扩展: Rust中常量为什么用let不用const,变量用let mut不用var?

放到r2上自动分析一下。

用Cutter带的Ghidra反编译器试试:

看main函数, let x=5; 是把5赋给了一个dword双字节的地址,lea 加载变量的有效地址,然后直接fmt和调用print,注意这里的println!是一个宏。

例子2:

原题:


fn main() {
    let x: i32;
    if x == 10 {
        println!("Ten!");
    } else {
        println!("Not ten!");
    }
}

修改后:
fn main() {
    let x: i32 = 5;
    if x == 10 {
        println!("Ten!");
    } else {
        println!("Not ten!");
    }
}

Rust出于内存安全的考虑,不允许变量未初始化。如果你写了let x=5; Rust编译器会自动推断,但是最好是自己加一下类型,主要是因为推断的类型不一定对。

例子3:

原题:

fn main() {
    let x = 3;
    println!("Number {}", x);
    x = 5; // don't change this line
    println!("Number {}", x);
}

修改后:


fn main() {
    let mut x = 3;
    println!("Number {}", x);
    x = 5; // don't change this line
    println!("Number {}", x);
}

Rust不允许重复赋值,因为默认变量不可变。 需要加mut来变成可变的变量。

不可变绑定和可变绑定的区别:

问题: 为什么要设计默认变量是不可变的?

例子4:

原题:

fn main() {
    let x: i32;
    println!("Number {}", x);
}

修改后:

fn main() {
    let x: i32 = 0;
    println!("Number {}", x);
}

也是未初始化的问题

例子5:

原题:

fn main() {
    let number = "T-H-R-E-E"; // don't change this line
    println!("Spell a Number : {}", number);
    number = 3;
    println!("Number plus two is : {}", number + 2);
}

修改后:

fn main() {
    let number = "T-H-R-E-E"; // don't change this line
    println!("Spell a Number : {}", number);
    let number = 3;
    println!("Number plus two is : {}", number + 2);
}

第一个number 是不可变的字符串类型,然后下面的赋值 number=3是一个整型,肯定不对。需要重新绑定number,把number改为整型并赋值为3

例子6:

原题:

const NUMBER = 3;

fn main() {
    println!("Number {}", NUMBER);
}

修改后:

const NUMBER: i32 = 3;
fn main() {
    println!("Number {}", NUMBER);
}

Rust的常量必须加类型。那为什么常量不推导呢? 这是因为故意这样设计的,指定类型就不会因为自动推导类型出问题。

扩展: Why is type declaration necessary in constants?

去看看这个0x10003c588是什么内容

Rust的常量是全局的,而是自动内联的。 如果我们把const 换成static,那么它会被存储在静态存储区中,有一个固定的内存地址,而且不会被内联。

我们来试试:

Rust 逆向——00.Hello,World

fn main() {
    println!("Hello, world!");
}

我尝试使用-O 和 -C opt-level=3 来编译,发现编译出来的程序没有任何区别。

如果你使用-o 的话,就会发现生成的程序是没有优化的,会大一些,有461K。

使用GDB来调试

你在编译的时候,需要加-g 来添加调试信息

(gdb) list
1       fn main() {
2           println!("Hello, world!");
3       }
(gdb) 
Line number 4 out of range; helloworld.rs has 3 lines.
(gdb) 
Line number 4 out of range; helloworld.rs has 3 lines.
(gdb) b 2
Breakpoint 1 at 0x100004228: file helloworld.rs, line 2.

使用逆向工具

使用Cutter的jsdec反编译结果。看起来生成的C++/C的代码

使用Ghira的结果