Rust-re-learning

·

rust逆向思路:

快速定位关键函数 (真正的main函数):观察输出、输入,字符串搜索,断点等方法。

定位关键 加密区 :根据输入的flag,打硬件断点,快速捕获程序中对flag访问的位置(加密区)。

定位错误输出(附近一定有比较功能的程序):定位到比较位置后 提取出正确加密后的结果

调用约定和名称修饰

rust目前还没有一个稳定的ABI(Application Binary Interface),所以,编译的时候需要将所有依赖的crates一起编译。所以,我们会看到很多莫明其妙的东西。另外,为了保证代码链接的唯一性,我们不得不在编译过程中进行很多修饰,最后导致二进制文件反编译出来的玩意每个函数名字都是一坨()。以下将介绍Rust名称修饰规则。

legacy规则:

是基于Itanium IA-64 C++ ABI(https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling)进行了部分的修改,最主要的是在符号最后加了哈希值用来解决部分场景下的符号唯一性的问题。

eg:

1、普通函数

1
2
3
4
5
6
7
8
// crate 名称:legacy_mangling
fn foo() {
println!("foo");
}

fn foo_arg(x: i32) {
println!("x = {}", x);
}

foo组名后:_ZN15legacy_mangling3foo17h7bf46936ec8fddf1E

其中_ZN为legacy规则的组名符号开头,和Itanium IA-64 C++ ABI规则一致,后面紧跟的15是crate的legacy_mangling的字符个数,包括了中间的下划线。3表示函数foo,最后面紧跟着17是hash值h7bf46936ec8fddf1,并以E表示结束。

foo_arg组名后:_ZN15legacy_mangling7foo_arg17h9d3deebd56cd9668E

可以发现参数并不会体现在前面的组名中,而是通过hash值来做区分。

实战中我们可以直接看到函数名,有效定位关键函数,完美。

2、带泛型参数的函数

不会将泛型参数信息体现在组名中,和普通函数差异只体现在hash上。

3、结构体

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
let point = Point{
x: 1,
y: 2,
};
println!("{}", point.add());
println!("{}", point.sub())
}

struct Point {
x: i32,
y: i32,
}

impl Point {
fn add(&self) -> i32 {
self.x + self.y
}

fn sub(&self) -> i32 {
self.x - self.y
}
}

Point结构体方法addsub组名后:

_ZN15legacy_mangling5Point3add17h9b332fc1bb45a67eE
_ZN15legacy_mangling5Point3sub17hf189faef70b4895dE

组名后是crate + Struct + func + hash的组成方式

4、trait方法

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main() {
let point = Point{
x: 1,
y: 2,
};
println!("{}", point.add());
}

pub trait Compute {
fn add(&self) -> i32;
}

struct Point {
x: i32,
y: i32,
}

impl Compute for Point{
fn add(&self) -> i32 {
self.x + self.y
}
}

组名后:_ZN67_$LT$legacy_mangling..Point$u20$as$u20$legacy_mangling..Compute$GT$3add17h9b332fc1bb45a67eE

可以看到组名之后变得相对比较复杂,因为我们在不违反孤儿原则的情况下,可以实现另外crate中的trait,或者Struct在另外的crate中,所以在组名中的trait和Struct包含了crate名称。$.两个符号是保留的特殊符号,这两个符号在编码中用来命名会报错。

(V0 组名规则以_R作为开头,去掉了后面的Hash值,普通函数中也去掉了E函数作为结尾,它主要解决如下问题:

  • 它以可逆的方式编码有关泛型参数的信息。也就是可以通过符号反推出泛型的参数信息。
  • 它有一个一致的定义,不依赖于漂亮地打印某些语言结构。
  • 字符由A-Za-z0-9,和_组成)

Rust-re-learning
http://example.com/2025/07/06/Rust-re-learning/
作者
oxygen
发布于
2025年7月6日
许可协议