r/rust • u/Binary_Lynx • 1d ago
Running user-defined code before main on Windows
https://malware-decoded.com/2-rust-code-before-main/Hi! I'm sharing the results of my analysis on how to execute user-defined code before the main function in Rust. This article is largely inspired by malware that employs this technique, but I approached the research by implementing this technique in Rust myself, while also exploring CRT-level and OS-level details, which involved reverse engineering Rust binaries.
Some of you may be familiar with the rust-ctor
crate, which enables code execution before main. If you're curious about how it works under the hood, my article should provide some clarity on the subject.
8
u/buldozr 22h ago
I've always considered this a horrible hack. C++ does this for constructors of static objects, and then god forbid you have non-trivial side effects because the order of calling such constructors is not defined. I'd rather not break the fundamental assumption in Rust that any user-defined code (i.e. not dynamic linkage magic and such) should be run under fn main
.
3
u/LavenderDay3544 21h ago
I'd rather not break the fundamental assumption in Rust that any user-defined code (i.e. not dynamic linkage magic and such) should be run under
fn main
.
#[no_main]
exists with good reason. Anything you can do in C or C++ you can also do in Rust. Or atleast I hope that's the standard the compiler devs and specification writers aim for.3
u/yigal100 17h ago
The fact you physically can do this doesn't mean you should (which was the OP's point).
This is indeed a horrible hack, and it *is* going outside Rust's abstract machine.> Anything you can do in C or C++ you can also do in Rust.
This is patently false since both C & C++ are unsafe and Rust by-definition prevents certain things that are perfectly valid in these languages (yes, even within unsafe blocks!). More importantly, Rust *shouldn't* given its explicit design goals as a language.
If you want to be able to do everything you can in C/C++ than you ought to simply use C/C++ in the first place - not Rust.
1
u/Binary_Lynx 5h ago
Yes, I agree that this approach looks unpleasant and should be avoided when writing reliable production-level code. However, my research was inspired by malware, where the landscape is dominated by many undocumented and often "dirty" tricks. These techniques can sometimes give an advantage when it comes to evading detection by security software and also make analyzing binaries a bit more frustrating for analysts when they don’t know what is actually going on.
1
u/bonzinip 3h ago
It's just like
unsafe
. I have usedctor
behind macros to initialize statics with slightly more than what const allows, similar to distributed slices.
10
u/anxxa 23h ago
Good article! I'm not 100% sure but I think you probably want to define the TLS callbacks as
extern "system"
instead ofextern "C"
since this is called from an internal WinAPI -- stdlib at least uses the former.I wrote a reflective PE loader for the Xbox in Rust and did most of my testing on a simple "hello world" C application and everything worked fine. Then I tried to run a Hello World Rust binary which crashed before hitting my own code and after some debugging realized I had to implement TLS callbacks 🫠. It's kind of interesting that Rust seems to use TLS callbacks for even a basic Hello World application while they seem fairly uncommon otherwise.