news 2026/4/15 4:00:25

在反汇编器中剖析Rust:揭秘编译器如何优化二进制文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在反汇编器中剖析Rust:揭秘编译器如何优化二进制文件

Rust in your disassembler

今年在Atr2con会议上,我将展示如何破解一个用Rust编写的CrackMe二进制文件。由于会议是在线形式,我选择主要以大型演示的形式录制,仅使用极少的幻灯片。然而,你们中的一些人可能希望了解一些细节或理论。Rust编译器所做的事情非常巧妙且有趣。

字符串是胖指针

这与C语言不同,在C语言中,你的字符串实际上只是一个指向字符的简单指针。在Rust中,你的内联字符串将指向一个包含以下内容的结构:

  • 长度:你字符串的长度。不需要像C语言那样在字符串末尾用\0结尾。
  • 一个指向字符串字符的指针

Radare2将这些字符串显示为"reloc.fixup.xxxx"。例如,下面的字符串显然位于地址0x5e790。如果我们查看该地址的字节,可以清楚地看到指针(6bc0 0400 -> 0x04c06b),然后是长度(0x25)。我们确认字符位于0x04c06b。

;0x5e790;"k\xc0\x04" 0x00008b6a 488d051f5c.. lea rax,reloc.fixup.Space_Station_Airlock_Control_S [0x00008b40]> px10 @ 0x5e790 - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x0005e790 6bc0 0400 0000 0000 2500 k.......%. [0x00008b40]> px0x25 @ 0x04c06b - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x0004c06b 5370 6163 6520 5374 6174 696f 6e20 4169 Space Station Ai 0x0004c07b 726c 6f63 6b20 436f 6e74 726f 6c20 5379 rlock Control Sy 0x0004c08b 7374 656d 0a stem.

单态化

你可能听说过多态。这是指一个给定的函数能够操作不同的类型。

单态化则“相反”:我们确保每种类型严格对应一个函数。

Rust编译器会自动使用单态化——这是出于优化的原因,比如不需要虚函数表。当它遇到泛型函数时,在底层,它实际上会为每种类型生成一个函数的汇编代码。开发者看不到这一点,这是编译器的工作。

// 泛型函数fnsquare<T:std::ops::Mul<Output=T>+Copy>(x:T)->T{x*x}fnmain(){leta=square(3i32);// 生成 square::<i32>letb=square(2.5f64);// 生成 square::<f64>}

编译器也经常为闭包这样做。

闭包

闭包是一个捕获其环境的函数。在下面的例子中,add_x是一个闭包。它捕获了环境,其中x = 5。调用add_x(3)返回 8。

fnmain(){letx=5;letadd_x=|y|x+y;println!("{}",add_x(3));}

Rust编译器的具体行为很大程度上取决于优化级别。假设你用-C opt-level=0编译这段代码。

[0x00007960]> pdi @ sym.main::main::h8f1c9e3495794b54 0x00007b90 sym.main::main::h8f1c9e3495794b54: 0x00007b90 4883ec68 sub rsp, 0x68 0x00007b94 c7442404050. mov dword [rsp + 4], 5 0x00007b9c 488d442404 lea rax, [rsp + 4] 0x00007ba1 4889442408 mov qword [rsp + 8], rax 0x00007ba6 488d7c2408 lea rdi, [rsp + 8] 0x00007bab be03000000 mov esi, 3 0x00007bb0 e85b000000 call sym.main::main::__u7b__u7b_closure_u7d__u7d_::h0ea7a13e6c14ebb2 0x00007bb5 89442464 mov dword [rsp + 0x64], eax

编译器为我们的主函数生成了一个特定的闭包。它被命名为:sym.main::main::__u7b__u7b_closure_u7d__u7d_::h0ea7a13e6c14ebb2。该闭包的参数是:

  • 第一个参数(在rdi中):rsp + 8。这是闭包环境。它包含一个指向rsp + 4的指针,而该地址存放着值 5。
  • 第二个参数(在esi中):3。这是传递给闭包的参数

在闭包的汇编代码中,我们有一些针对整数操作的指令。

0x00007c11 488b07 mov rax, qword [rdi] 0x00007c14 0330 add esi, dword [rax] ... 0x00007c1f 8b442404 mov eax, dword [rsp + 4]

如果闭包被用于浮点数,则会生成另一个闭包,并带有不同的指令。这就是单态化。我们可以通过创建一个同时适用于浮点数整数的闭包来更好地观察这一点:

usestd::ops::Add;fnget_adder<T>(x:T)->implFn(T)->TwhereT:Add<Output=T>+Copy,{move|y|x+y}fnmain(){letadd_int=get_adder(5);letadd_float=get_adder(5.0);println!("{}",add_int(3));println!("{}",add_float(4.5));}

现在,如果我们检查汇编代码,会注意到编译器生成了2个get_adder函数。

[0x00007960]> afl~main 0x00007c00 11 sym.main::get_adder::hba4b3b420a6e866b 0x00007c10 13 sym.main::get_adder::hbe8e2ff9c2a8357d 0x00007c20 122 sym.main::get_adder::__u7b__u7b_closure_u7d__u7d_::h6ff0b01bf90189e9 0x00007c40 117 sym.main::get_adder::__u7b__u7b_closure_u7d__u7d_::h72c4b1cd2fd53136 0x00007c60 1251 sym.main::main::h8f1c9e3495794b54

如果我们检查第一个get_adder的汇编代码,会发现它适用于浮点数:

0x00007c20 sym.main::get_adder::__u7b__u7b_closure_u7d__u7d_::h6ff0b01bf90189e9: 0x00007c20 50 push rax 0x00007c21 0f28c8 movaps xmm1, xmm0 0x00007c24 f20f1007 movsd xmm0, qword [rdi] 0x00007c28 488d3d19ee0. lea rdi, [rip + 0x4ee19] 0x00007c2f e87cfeffff call sym.__f64_as_core::ops::arith::Add_::add::hf4bd57df382c73d6 0x00007c34 58 pop rax 0x00007c35 c3 ret

而第二个get_adder适用于整数:

0x00007c40 sym.main::get_adder::__u7b__u7b_closure_u7d__u7d_::h72c4b1cd2fd53136: 0x00007c40 50 push rax 0x00007c41 8b3f mov edi, dword [rdi] 0x00007c43 488d15feed0. lea rdx, [rip + 0x4edfe] 0x00007c4a e871feffff call sym.__i32_as_core::ops::arith::Add_::add::h471fa892cd8f10a4 0x00007c4f 59 pop rcx 0x00007c50 c3 ret

脱糖

在Rust中,开发者通常调用像obj.blah()这样的方法。Rust编译器在内部将其转换为更明确(但不那么“甜”)的形式:class::blah(&obj)

letobj=MyObject{value:10};obj.blah();// 语法糖

查看下面生成的汇编代码:

; set value = 10 0x00007bc4 c744240c0a00. mov dword [rsp + 0xc], 0xa ; put obj in RDI (1st argument of blah()) 0x00007bcc 488d7c240c lea rdi, [rsp + 0xc] ; call blah() with address of obj as argument 0x00007bd1 e8baffffff call sym.sugar::MyObject::blah::h059bdb1e25fa70c4

在Rust中,用于显示内容的函数println!是一个宏,而不是一个“真正的”函数。因此,在汇编代码中,你不会看到对println!的调用(这个“函数”并不存在),而是看到:

  • 设置格式字符串和参数。
  • 构建 Arguments 结构体。
  • 调用dbg._print,它实际上会调用dbg.write_fmt,dbg.write

我建议你阅读这篇文章以获取更多细节。

下面的汇编代码是由一个 Hello World 程序生成的。

0x00007b20 sym.print::main::h13b5f0e40e4e7865: 0x00007b20 4883ec38 sub rsp, 0x38 ; 在栈上为 Arguments 对象分配空间 0x00007b24 488d7c2408 lea rdi, [rsp + 8] ; 指向字符串 "Hello World" 的胖指针 0x00007b29 488d35d0a30. lea rsi, [rip + 0x4a3d0] 0x00007b30 e83bffffff call sym.core::fmt::rt::__impl_core::fmt::Arguments_::new_const::ha519b55ee7e59acf 0x00007b35 488d7c2408 lea rdi, [rsp + 8] ; 通过 GOT 调用 dbg._print 例程 0x00007b3a ff1550cf0400 call qword [rip + 0x4cf50] 0x00007b40 4883c438 add rsp, 0x38 0x00007b44 c3 ret

注意,对dbg._print的调用是间接的:dbg._print的 GOT(全局偏移表)条目位于rip + 0x4cf50。Radare2 将其显示为一个reloc.fixup

[0x00007b20]> px10 @ reloc.fixup.UHSHxHH_ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x00054a90 c0430200 00000000 0000 .C........ [0x00007b20]> pd10 @ 0x0243c0 ;-- std::io::stdio::_print::h915f3273edec6464: ;DATA XREF from reloc.fixup.UHSHxHH_ @ ┌206: dbg._print (int64_t arg1); │`- args (rdi) vars (13: sp[0x18..0x80]) │ 0x000243c0 55 push rbp ; sync.rs:0:13 ; void _print();

— Cryptax
CSD0tFqvECLokhw9aBeRqtFmyXmRsBISmPE7edMOI2OK8XHtAZcBhDmHp75CbukkcV4OE5l5gwOQpJ1KPqBQB2mY1bHR5IDRIauIsx0/nRk=
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/13 13:01:01

NANOMETRICS 8200-0339

NANOMETRICS 8200-0339 简介NANOMETRICS 8200-0339 是 Nanometrics 公司生产的一款地震监测设备或传感器组件&#xff0c;通常用于地震学、结构健康监测或工业振动分析领域。Nanometrics 专注于高精度地震仪器&#xff0c;其产品广泛应用于科研、灾害预警和工程监测。主要功能与…

作者头像 李华
网站建设 2026/4/14 8:29:13

最新流出8款AI论文神器:知网查重一把过,零AIGC痕迹速藏!

最后72小时&#xff01;这份名单随时可能下架&#xff01; 如果你还在为毕业论文、期末报告、投稿论文的查重率、AI痕迹、导师的连环催命符而彻夜难眠&#xff0c;那么恭喜你&#xff0c;抓住了最后一根救命稻草。本文将为你揭秘8款当前仍在“安全窗口期”的AI论文神器&#xf…

作者头像 李华
网站建设 2026/4/12 18:10:55

2026趋势:AI驱动性能优化

AI正重构性能测试的底层逻辑‌ 到2026年&#xff0c;AI已不再是软件测试中的“辅助工具”&#xff0c;而是‌性能优化的决策中枢‌。传统基于固定脚本、人工调参、静态基线的性能测试模式&#xff0c;正被‌自适应、可解释、低成本的AI驱动体系‌全面取代。测试工程师的角色&a…

作者头像 李华
网站建设 2026/4/14 13:21:23

CFD:针对离散计算部分用OpenMP多线程化,如何选择最优线程数

文章目录一、基本原则二、实用估计方法方法 1&#xff1a;基于经验公式&#xff08;适用于均匀网格&#xff09;方法 2&#xff1a;基于内存带宽瓶颈估计方法 3&#xff1a;运行时自适应调优&#xff08;推荐&#xff09;三、OpenMP 设置建议四、额外建议总结在 OpenMP 并行化基…

作者头像 李华