<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>LostAquilae&apos;s Blog</title>
    <link>https://lostaquilae.github.io/LostAquilae_blog/</link>
    <description>Cybersecurity research, projects, and technical writing by LostAquilae.</description>
    <language>en</language>
    <item>
      <title>RustyShell Part 3: Leveraging LLVM IR for relative virtual tables</title>
      <link>https://lostaquilae.github.io/LostAquilae_blog/writing/RustyShell-part-3/</link>
      <guid isPermaLink="true">https://lostaquilae.github.io/LostAquilae_blog/writing/RustyShell-part-3/</guid>
      <description>Using an LLVM Pass to  modify Rust code at the LLVM IR level to make vtables relative and avoid relocations.</description>
      <pubDate>Wed, 30 Sep 2026 00:00:00 GMT</pubDate>
      <category>Rust</category>
      <category>LLVM</category>
      <category>Shellcode</category>
      <category>CodeGeneration</category>
      <content:encoded><![CDATA[<h1>RustyShell Part 3: Leveraging LLVM IR for relative virtual tables</h1>
<p>Using an LLVM Pass to  modify Rust code at the LLVM IR level to make vtables relative and avoid relocations.</p>
<p>As I said in the previous blog posts, trait object are going to be a problem. But first, we have to understand what they are.</p>
<h2 id="generics-and-trait-objects">Generics and Trait Objects</h2><p>Rust has some features for generic types. Generic allows you to write one piece of code, and use it with different types and structures. But we have to differentiate between static generic behavior and dynamic generic behavior.</p>
<h3 id="static-generic-behavior">Static generic behavior</h3><p>Let&#39;s take the example of the Rust book for <a href="https://doc.rust-lang.org/book/ch10-01-syntax.html">generic behavior</a>:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">largest</span>&lt;T: std::cmp::<span class="hljs-built_in">PartialOrd</span>&gt;(list: &amp;[T]) <span class="hljs-punctuation">-&gt;</span> &amp;T {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">largest</span> = &amp;list[<span class="hljs-number">0</span>];

    <span class="hljs-keyword">for</span> <span class="hljs-variable">item</span> <span class="hljs-keyword">in</span> list {
        <span class="hljs-keyword">if</span> item &gt; largest {
            largest = item;
        }
    }

    largest
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">number_list</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">34</span>, <span class="hljs-number">50</span>, <span class="hljs-number">25</span>, <span class="hljs-number">100</span>, <span class="hljs-number">65</span>];

    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span> = <span class="hljs-title function_ invoke__">largest</span>(&amp;number_list);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;The largest number is {result}&quot;</span>);

    <span class="hljs-keyword">let</span> <span class="hljs-variable">char_list</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-string">&#x27;y&#x27;</span>, <span class="hljs-string">&#x27;m&#x27;</span>, <span class="hljs-string">&#x27;a&#x27;</span>, <span class="hljs-string">&#x27;q&#x27;</span>];

    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span> = <span class="hljs-title function_ invoke__">largest</span>(&amp;char_list);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;The largest char is {result}&quot;</span>);
}</code></pre><p>Here, we can see the <strong>largest</strong> function is generic over some type T, which have to implement the <a href="https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html">std::cmp::PartialOrd</a> trait. This trait allows you to redefine the <strong>&gt;</strong> operator so that it calls the <strong>gt</strong> function of the trait. So the type you will use the largest function with have to implement this trait, otherwise you will face a compiler error, telling you it doesn&#39;t.</p>
<p>But that&#39;s not the point. What&#39;s interesting to understand here is that we face static generic behavior. This means that when, in the main function, we use the largest function with i32 and then with chars, there is actually two different functions that end up in your binary. One for i32 and one for chars, and call to the largest function is replaced to a call to the right function with the right arguments. Basically, this means that your code would now look like this:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">largest_i32</span>(list: &amp;[<span class="hljs-type">i32</span>]) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-type">i32</span> {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">largest</span> = &amp;list[<span class="hljs-number">0</span>];

    <span class="hljs-keyword">for</span> <span class="hljs-variable">item</span> <span class="hljs-keyword">in</span> list {
        <span class="hljs-keyword">if</span> item &gt; largest {
            largest = item;
        }
    }

    largest
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">largest_char</span>(list: &amp;[<span class="hljs-type">char</span>]) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-type">char</span> {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">largest</span> = &amp;list[<span class="hljs-number">0</span>];

    <span class="hljs-keyword">for</span> <span class="hljs-variable">item</span> <span class="hljs-keyword">in</span> list {
        <span class="hljs-keyword">if</span> item &gt; largest {
            largest = item;
        }
    }

    largest
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">number_list</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">34</span>, <span class="hljs-number">50</span>, <span class="hljs-number">25</span>, <span class="hljs-number">100</span>, <span class="hljs-number">65</span>];

    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span> = <span class="hljs-title function_ invoke__">largest_i32</span>(&amp;number_list);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;The largest number is {result}&quot;</span>);

    <span class="hljs-keyword">let</span> <span class="hljs-variable">char_list</span> = <span class="hljs-built_in">vec!</span>[<span class="hljs-string">&#x27;y&#x27;</span>, <span class="hljs-string">&#x27;m&#x27;</span>, <span class="hljs-string">&#x27;a&#x27;</span>, <span class="hljs-string">&#x27;q&#x27;</span>];

    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span> = <span class="hljs-title function_ invoke__">largest_char</span>(&amp;char_list);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;The largest char is {result}&quot;</span>);
}</code></pre><p>So, as you can see, after the compiler analyses your code, no generic behavior is being executed at runtime, everything is handled at compile time for you. This kind of generic behavior is not a problem for shellcode compatibility, since the above code would be totally fine inside our shellcode template. Problem arises from dynamic generic behavior.</p>
<h3 id="dynamic-generic-behavior-or-trait-object">Dynamic generic behavior or trait object</h3><p>Now what happens when using trait objects. Trait objects are another kind of generic behavior that resolves at runtime. Instead of creating as many instances as needed for a specific code that uses generic, that specific code actually handles every case possible in the same function, at runtime. This is called dynamic dispatch.</p>
<p>It&#39;s the same concept with virtual method in C++. When having parent and child classes having their own implementation of a virtual function, when you hold a reference or a pointer to the Base class, but that could actually be a pointer to the base or child class, then the call to the virtual function goes through what is called a vtable to call the right function on the actual object we are holding a pointer to. For those who know about this behavior in C++, trait objects are exactly the same. But for those who don&#39;t, let&#39;s take an example in Rust:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-keyword">trait</span> <span class="hljs-title class_">SharedBehavior</span> {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">name</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-type">str</span>;
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Struct1</span> {
    name: <span class="hljs-type">String</span>,
}

<span class="hljs-keyword">impl</span> <span class="hljs-title class_">SharedBehavior</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">Struct1</span> {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">name</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-type">str</span> {
        &amp;<span class="hljs-keyword">self</span>.name
    }
}

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Struct2</span> {
    name: <span class="hljs-type">String</span>,
}

<span class="hljs-keyword">impl</span> <span class="hljs-title class_">SharedBehavior</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">Struct2</span> {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">name</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-type">str</span> {
        &amp;<span class="hljs-keyword">self</span>.name
    }
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">print_name</span>(structure: &amp;<span class="hljs-keyword">dyn</span> SharedBehavior) {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Name: {}&quot;</span>, structure.<span class="hljs-title function_ invoke__">name</span>());
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">struct1</span> = Struct1 { name: <span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;Struct1&quot;</span>) };
    <span class="hljs-keyword">let</span> <span class="hljs-variable">struct2</span> = Struct2 { name: <span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;Struct2&quot;</span>) };

    <span class="hljs-title function_ invoke__">print_name</span>(&amp;struct1);
    <span class="hljs-title function_ invoke__">print_name</span>(&amp;struct2);
}</code></pre><p>Okay, so we have a trait called <strong>SharedBehavior</strong> and two structures, <strong>Struct1</strong> and <strong>Struct2</strong>, that just have a name field that represents the structure&#39;s name and that implements the SharedBehavior trait. Then, we have a function <strong>print_name</strong>, that takes a <strong>&amp;dyn SharedBehavior</strong>. This is where all the magic happens. Remember what we said earlier, if we had put this code:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">print_name</span>(structure: &amp;<span class="hljs-keyword">impl</span> <span class="hljs-title class_">SharedBehavior</span>) {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Name: {}&quot;</span>, structure.<span class="hljs-title function_ invoke__">name</span>());
}</code></pre><p>The <strong>&amp;impl SharedBehavior</strong> is just syntactic sugar to no have to declare specifically a generic type T, but it does exactly the same as declaring the T generic type and defining it like the first example of this post. So with this code, we would have ended up with two different functions, one that takes a <strong>Struct1</strong> and one that takes a <strong>Struct2</strong>. But here, we used the <strong>dyn</strong> keyword, which specifically says we want dynamic dispatch, meaning we only have one function that takes care of resolving the right name function according to the actual object passed to it as argument. But how does it do that? Thanks to virtual tables.</p>
<h2 id="virtual-tables">Virtual Tables</h2><p>A virtual table is a table holding pointers to the implementation of functions. For example here, we specifically asked for dynamic dispatch over the <strong>SharedBehavior</strong> trait. So the <strong>print_name</strong> function will receive a specific value as argument.</p>
<p>Normally, a function that takes a structure as argument receives a reference to this structure directly, so that it can work on it. But the <strong>print_name</strong> function does not take a structure directly as argument, it takes what we call a <strong>trait object</strong>, here meaning any object that actually implements this trait. So what does it receive? It receives what we call a <strong>fat pointer</strong>. A <strong>fat pointer</strong> in Rust, is a structure holding two pointers. One that points to the object itself and the other that points to the virtual table of some trait that this object implements, here <strong>SharedBehavior</strong>. Here is a drawing which will help understand:</p>
<figure>
  <img src="https://lostaquilae.github.io/LostAquilae_blog/assets/fat_pointer_vtable_rust-Dy8d_MZN.svg" alt="fat pointers vtable explanation" style="width:100%; margin: 0 auto" />
  <figcaption style="text-align: center">Fat pointers vtables explanation</figcaption>
</figure><p>Note how the vtable is constructed, it will be useful later:</p>
<ul>
<li><p><strong>Drop function</strong>: The first field is a pointer toward the drop function for this structure. Sometimes, on certain object, you will have a specific drop function that will correctly free the memory used by the object. This function may not exist, and if that&#39;s the case, this pointer will be null</p>
</li>
<li><p><strong>Size</strong>: This field represents the size in memory that the object takes. It might be needed for example by the drop function to properly deallocate the right amount of memory when destroying the object</p>
</li>
<li><p><strong>Alignment</strong>: This field represents the alignment in memory of the object. Also needed by operations dealing on memory (allocation, deallocation, ...)</p>
</li>
</ul>
<p>Note that every field are 8 byte long. </p>
<p>After that, you have pointers to the functions of the trait this vtable refers to. So, our <strong>print_name</strong> function will actually do the following to call the right <strong>name</strong> function: Get the second pointer of the fat pointer and follow it. Offset this pointer by the number of bytes necessary to hit the pointer to the name function inside the vtable. Get the function pointer and call it to call the right <strong>name</strong> function. The offset is actually decided by the compiler: It decides that the name function will be at offset 24, the first function pointer after the alignment field. Then for each call to the <strong>name</strong> function on a <strong>trait object</strong> for <strong>SharedBehavior</strong>, it knows it has to offset inside the vtable by 24.</p>
<p>What you need to understand is that one vtable will be generated per structure implementing the SharedBehavior trait. So here, you will have one vtable for Struct1 structure and one vtable for Struct2 structure, each one with different pointers and values for each of its field. But every Struct1 object will share the same vtable though, because they all need the exact same values since the drop function, the size, the alignment and the name function pointer will all be the same for every object of the Struct1 structure.</p>
<p>Okay so now you understand how <strong>VTables</strong> work. You might see where the problem is then. VTables holds pointer i.e. absolute addresses towards functions... It generates relocations of course! That&#39;s our problem. Remember what we said about relocations, if we have an absolute address inside our binary, it will be computed from the preferred loaded address put by the compiler. But if the OS can&#39;t load our binary to this address, then all absolute addresses becomes obsolete and needs to be fixed. Therefore, it breaks shellcode, because we can&#39;t know in advance where we are going to be loaded.</p>
<p>And the problem is: the core and alloc libraries may sometimes use, on certain structures, trait objects, which will resolve into vtables. The closest example of this is the <a href="https://doc.rust-lang.org/alloc/macro.format.html">format!</a> macro. At some point, it uses a trait object on the <a href="https://doc.rust-lang.org/alloc/fmt/trait.Write.html">Write</a> Trait. And it&#39;s quite bothering, because it would mean that not all parts the core and alloc libraries can be used. We would have to test every import inside these crates and verify it doesn&#39;t generate vtables.</p>
<p>But, I saw on the clang compiler for C++, a flag that might be interesting: <strong>-fexperimental-relative-c++-abi-vtables</strong>. It specifically tells the compiler to generate vtables with relative offset to function instead of absolute pointers. This might be exactly what we need.</p>
<p>Okay, so relative vtables do exists... but only in C++, with an experimental flag of the Clang compiler frontend. Clang uses LLVM as backend for the code generation part. Turns out, Rust also uses LLVM as part of its backend. I tried to see if there was any way to enable the same behavior, at the LLVM level though. I didn&#39;t find any. However, LLVM allows you to write scripts for optimizations. The solution might reside in these scripts But first, what is LLVM really?</p>
<h2 id="llvm">LLVM</h2><p>When it comes to compiler, there are different parts of it. Usually you have what we call a frontend and a backend. The frontend takes care of parsing your code and turn it into an <em>intermediate representation</em>. The backend actually turns this <em>intermediate representation</em> into assembly that targets a specific <strong>processor architecture</strong>: x86, ARM, etc...</p>
<p>For example, Clang is the C++ frontend that uses LLVM as a backend. LLVM provides what is called <strong>LLVM IR</strong>, LLVM&#39;s own Intermediate representation. It is a language that sits between your code and assembly. It is hardware-agnostic, and also language agnostic. So Clang turns C++ code into this LLVM IR, and then uses LLVM to optimize the IR and turn it into binary code for a specific <strong>processor architecture</strong>. Sadly, the special flag for relative vtables in C++ only works at the frontend level, not on LLVM.</p>
<p>The Rust compiler roughly works the same way. It&#39;s only a frontend that turn your Rust code into LLVM IR and then uses LLVM as the backend. LLVM performs optimizations at this stage. This allows to benefits from the same optimizations, whatever languages you use or processor architecture you target. It gives you consistency across each of your projects and targets. Of course, you can also have optimizations at the assembly level later on.</p>
<p>Let&#39;s take a look at the LLVM IR now.</p>
<h3 id="llvm-ir">LLVM IR</h3><p>LLVM IR is a <a href="https://en.wikipedia.org/wiki/Static_single-assignment_form">Static Single Assignment</a> language, meaning each variable is assigned exactly once. Let&#39;s take a rust code for example and see its corresponding IR:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-keyword">fn</span> <span class="hljs-title function_">add</span>(i: <span class="hljs-type">i32</span>, j: <span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span> {
    i + j
}</code></pre><pre class="code-block"><code class="hljs language-llvm"><span class="hljs-comment">; Test_Rust::add</span>
<span class="hljs-comment">; Function Attrs: nonlazybind uwtable</span>
<span class="hljs-keyword">define</span> <span class="hljs-keyword">internal</span> <span class="hljs-type">i32</span> <span class="hljs-title">@_RNvCsivtQJMP6Nh_9Test_Rust3add</span>(<span class="hljs-type">i32</span> <span class="hljs-variable">%i</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> <span class="hljs-variable">%j</span>) <span class="hljs-keyword">unnamed_addr</span> <span class="hljs-variable">#0</span> <span class="hljs-title">!dbg</span> <span class="hljs-title">!147</span> <span class="hljs-title">!guid</span> <span class="hljs-title">!155</span> {
<span class="hljs-symbol">start:</span>
  <span class="hljs-variable">%j.dbg.spill</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">alloca</span> [<span class="hljs-number">4</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">4</span>
  <span class="hljs-variable">%i.dbg.spill</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">alloca</span> [<span class="hljs-number">4</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">4</span>
  <span class="hljs-keyword">store</span> <span class="hljs-type">i32</span> <span class="hljs-variable">%i</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-variable">%i.dbg.spill</span><span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">4</span>
  <span class="hljs-keyword">store</span> <span class="hljs-type">i32</span> <span class="hljs-variable">%j</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-variable">%j.dbg.spill</span><span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">4</span>
  <span class="hljs-variable">%0</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">call</span> { <span class="hljs-type">i32</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i1</span> } <span class="hljs-title">@llvm.sadd.with.overflow.i32</span>(<span class="hljs-type">i32</span> <span class="hljs-variable">%i</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> <span class="hljs-variable">%j</span>)<span class="hljs-punctuation">,</span> <span class="hljs-title">!dbg</span> <span class="hljs-title">!158</span>
  <span class="hljs-variable">%_3.0</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">extractvalue</span> { <span class="hljs-type">i32</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i1</span> } <span class="hljs-variable">%0</span><span class="hljs-punctuation">,</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!dbg</span> <span class="hljs-title">!158</span>
  <span class="hljs-variable">%_3.1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">extractvalue</span> { <span class="hljs-type">i32</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i1</span> } <span class="hljs-variable">%0</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!dbg</span> <span class="hljs-title">!158</span>
  <span class="hljs-keyword">br</span> <span class="hljs-type">i1</span> <span class="hljs-variable">%_3.1</span><span class="hljs-punctuation">,</span> <span class="hljs-type">label</span> <span class="hljs-variable">%panic</span><span class="hljs-punctuation">,</span> <span class="hljs-type">label</span> <span class="hljs-variable">%bb1</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!dbg</span> <span class="hljs-title">!158</span>

bb<span class="hljs-number">1</span>:                                              <span class="hljs-comment">; preds = %start</span>
  <span class="hljs-keyword">ret</span> <span class="hljs-type">i32</span> <span class="hljs-variable">%_3.0</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!dbg</span> <span class="hljs-title">!159</span>
<span class="hljs-symbol">
panic:</span>                                            <span class="hljs-comment">; preds = %start</span>
<span class="hljs-comment">; call core::panicking::panic_const::panic_const_add_overflow</span>
  <span class="hljs-keyword">call</span> <span class="hljs-type">void</span> <span class="hljs-title">@_RNvNtNtCsj07cU7DEYcL_4core9panicking11panic_const24panic_const_add_overflow</span>(<span class="hljs-type">ptr</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span> <span class="hljs-title">@alloc_f50689592e4950fb14ebe24334607aab</span>) <span class="hljs-variable">#9</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!dbg</span> <span class="hljs-title">!158</span>
  <span class="hljs-keyword">unreachable</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!dbg</span> <span class="hljs-title">!158</span>
}</code></pre><p>Okay, so we have a simple <em>add</em> function that just adds two number and return the result. First, you can see the function&#39;s identifier, where the name <em>add</em> that we put ends up at the end of the string name. Then you have both parameters, preceded with their type: <strong>i32 %i, i32 %j</strong>. Let&#39;s see how this function turned out in terms of IR.</p>
<p>First, you have two new variables: <strong>%j.dbg.spill</strong> and <strong>%i.dbg.spill</strong>. I call these variables, but LLVM refers to them as registers. It&#39;s like a register in assembly, except you can have and infinite number of them, they are like <em>virtual registers</em>. Let&#39;s just call them variables. So they are both produced with <strong>alloca</strong>, which is a small procedure to allocate on the stack. They allocate <strong>[4 x i8]</strong> meaning 4 bytes each, which is logic since parameters are i32, and their alignment is 4.</p>
<p>Then, the store operation is used, to store the value of <strong>%i</strong> and <strong>%j</strong> into their corresponding newly created variable.</p>
<p>After that, you have a call instruction, which calls the <strong>@llvm.sadd.with.overflow.i32</strong> procedure. We can see that it returns <strong>{ i32, i1 }</strong>, meaning it returns an <strong>i32</strong> and an <strong>i1</strong>. Type in LLVM can have any number of bits you want. In Rust, you would have for example i8, i16, i32 and i64. In LLVM, you can have i1 but also i12 or i23, it doesn&#39;t matter. So this procedure returns the result as the first return value, and then it returns a boolean essentially, since it&#39;s only one bit. This boolean indicates if the add provoked an overflow or not.</p>
<p>Since <strong>%0</strong> now holds a tuple, you need to extract every value to work with them. This is why you have two <strong>extractvalue</strong> instructions, one for extracting the result and the other for extracting the overflow flag.</p>
<p>Then, you have a <strong>br</strong> instruction, which is a branch instruction. It checks if an overflow happened, and if that&#39;s the case, jumps directly to a branch where your rust code panics. But if no overflow happened, then it returns the result thanks to the ret instruction.</p>
<p>So as you can see, LLVM IR is a language that sits between your code and the final assembly code. This is where lots of optimizations are going to take place, done by LLVM. But you have to understand that the final backend will also do optimizations. For example, here we allocate on the stack to store the values, but in the final assembly, for such a short code, everything could happen inside registers, without ever allocating in the stack.</p>
<p>If you want more information on LLVM or LLVM IR, here are some resources: <a href="https://www.compilersutra.com/docs/llvm/">LLVM Tutorial</a>, <a href="https://mcyoung.xyz/2023/08/01/llvm-ir/">A gentle introduction to LLVM IR</a>, <a href="https://llvm.org/docs/">LLVM Project</a>.</p>
<h2 id="how-calls-work-in-modern-operating-system-at-the-assembly-level">How calls work in modern operating system, at the assembly level</h2><p>Before diving into how vtables calls work in LLVM IR, we first need to understand how calls actually work at the assembly level for modern operating systems. </p>
<p>So calls can either be:</p>
<ul>
<li><p><strong>Direct</strong>: This means the address is hardcoded inside the call instruction. You call this function, the compiler resolves the address of the call and directly embed it inside the call instruction. You will always call the same location with a direct call.</p>
</li>
<li><p><strong>Indirect</strong>: Here, the address is retrieved from somewhere, either a memory location or a register for example. Function pointers in C or virtual function table in C++ should behave like this in assembly (minus compiler optimizations). Several executions of this instruction might end up calling different locations, depending on your code behavior. For example, if I define a C function that takes a function pointer as argument and call this function pointer, then the resulting assembly for this function will have a call instruction like this: <strong>call reg</strong>. And the value inside the register will change depending on which function pointer I pass as argument when I call the function. What I want you to understand here is that the same call instruction could resolve into different calls locations.</p>
</li>
</ul>
<p>But calls can also either be:</p>
<ul>
<li><p><strong>Absolute</strong>: The call operand is the absolute, non-relative address toward the memory where the function resides.</p>
</li>
<li><p><strong>Relative</strong>: This is not an address, but rather on offset to the function from the current instruction pointer, pointing at the call instruction (RIP register).</p>
</li>
</ul>
<p>Let&#39;s take some examples to really understand how every function call resolves in assembly:</p>
<pre class="code-block"><code class="hljs language-c"><span class="hljs-type">int</span> <span class="hljs-title function_">test</span><span class="hljs-params">(<span class="hljs-type">int</span> i)</span> {
    <span class="hljs-keyword">return</span> i + <span class="hljs-number">10</span>;
}

<span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> {
    <span class="hljs-type">int</span> i = <span class="hljs-number">10</span>;
    <span class="hljs-type">int</span> j = test(i);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}</code></pre><p>The call to the test function will be of the form: <strong>call rip+offset</strong>. Because it is a <strong>direct call</strong> inside your own code, so the compiler can, at compile time, computes the offset directly and do a <strong>RIP relative call</strong>.</p>
<p>But if you do this:</p>
<pre class="code-block"><code class="hljs language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>

<span class="hljs-type">int</span> <span class="hljs-title function_">test</span><span class="hljs-params">(<span class="hljs-type">int</span> i)</span> {
    <span class="hljs-keyword">return</span> i + <span class="hljs-number">10</span>;
}

<span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> {
    <span class="hljs-type">int</span> i = <span class="hljs-number">10</span>;
    <span class="hljs-type">int</span> j = test(i);
    
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;The value of j is: %d\n&quot;</span>, j);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}</code></pre><p>Then, the call to the <strong>printf</strong> function will also be a <strong>direct call</strong>, but it will be an absolute direct call, because the printf function is not inside your code. When loading your executable, the operating system will give you the absolute address to the printf function, and will insert it inside the call instruction.</p>
<p>So that was the two possibilities for <strong>direct calls</strong>, what about Indirect calls:</p>
<pre class="code-block"><code class="hljs language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>

<span class="hljs-type">int</span> <span class="hljs-title function_">test</span><span class="hljs-params">(<span class="hljs-type">int</span> i)</span> {
    <span class="hljs-keyword">return</span> i + <span class="hljs-number">10</span>;
}

<span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> {
    <span class="hljs-comment">// Getting a function pointer to the test function</span>
    <span class="hljs-type">int</span> (*test_pointer)(<span class="hljs-type">int</span>) = test;
    <span class="hljs-type">int</span> i = <span class="hljs-number">10</span>;

    <span class="hljs-comment">// Calling the pointer to the test function</span>
    <span class="hljs-type">int</span> j = test_pointer(i);
    
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;The value of j is: %d\n&quot;</span>, j);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}</code></pre><p>The call to test_pointer is actually a call to a function pointer on the test function. This means the call will resolve into a call instruction of the form: <strong>call reg</strong> or <strong>call mem_location</strong>, mem_location being somewhere on the stack or the heap or any other memory location. The pointer will have an absolute address to the function. Indirect calls cannot be made relative, they are always made on absolute addresses.</p>
<p>So as we saw, a call can either be <strong>direct and relative</strong> or <strong>direct and absolute</strong> or <strong>indirect and absolute</strong>. VTables are just function pointers inside a structure that resides in your own binary. Some memory jumps and structure parsing is needed, but in the end, it&#39;s just an <strong>indirect and absolute</strong> call.</p>
<h2 id="making-vtables-relative">Making VTables relative</h2><p>I first started to imagine a solution at the assembly level. But that was not the right solution for several reasons:</p>
<ul>
<li><p><strong>No API to edit assembly code</strong>: Compared to LLVM IR, which has LLVM passes that you can custom write, assembly patching would have been done relying only on string matching and weird tricks to make it work. LLVM passes seemed a lot better for this use case.</p>
</li>
<li><p><strong>A lot harder to fix</strong>: At the assembly level, we work on a lower level than LLVM IR. The fix would have taken a lot of assembly instructions to work properly. Plus, vtables pointers couldn&#39;t have been made relative in assembly, so a calculation and a fix would have been only possible at the binary level, which increase complexity of a stable, reliable fix.</p>
</li>
<li><p><strong>Only working for x86</strong>: Working on the assembly level provides a solution for only x86 code, not ARM or other processor architecture. Working on the LLVM IR directly, you can then compile for every processor architecture that LLVM supports</p>
</li>
</ul>
<p>For all these reasons, I started to look at how VTables calls are managed in LLVM IR.</p>
<h3 id="vtables-in-llvm-ir">VTables in LLVM IR</h3><h4 id="vtables-definition">VTables definition</h4><p>First, let&#39;s take a look at the vtable definition in LLVM IR:</p>
<pre class="code-block"><code class="hljs language-llvm"><span class="hljs-title">@vtable.0</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">unnamed_addr</span> <span class="hljs-keyword">constant</span> &lt;{ [<span class="hljs-number">24</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> }&gt; &lt;{ [<span class="hljs-number">24</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>] <span class="hljs-keyword">c</span><span class="hljs-string">&quot;<span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\10</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\08</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span>&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvXs1g_NtCshoIjhMyZzUf_4core3fmtReNtB6_5Debug3fmtB8_</span> }&gt;<span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span>
<span class="hljs-title">@vtable.1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">unnamed_addr</span> <span class="hljs-keyword">constant</span> &lt;{ <span class="hljs-type">ptr</span><span class="hljs-punctuation">,</span> [<span class="hljs-number">16</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> }&gt; &lt;{ <span class="hljs-type">ptr</span> <span class="hljs-title">@_RINvNtCshoIjhMyZzUf_4core3ptr9drop_glueNtNtCshodfiC9HsYt_5alloc6string6StringEBF_</span><span class="hljs-punctuation">,</span> [<span class="hljs-number">16</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>] <span class="hljs-keyword">c</span><span class="hljs-string">&quot;<span class="hljs-char escape_">\18</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\08</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span>&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvXsZ_NtCshodfiC9HsYt_5alloc6stringNtB5_6StringNtNtCshoIjhMyZzUf_4core3fmt5Write9write_str</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvXsZ_NtCshodfiC9HsYt_5alloc6stringNtB5_6StringNtNtCshoIjhMyZzUf_4core3fmt5Write10write_char</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvYNtNtCshodfiC9HsYt_5alloc6string6StringNtNtCshoIjhMyZzUf_4core3fmt5Write9write_fmtB6_</span> }&gt;<span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span></code></pre><p>Here we have two vtables definition. The first one, we can see the <strong>drop</strong> function is zeroed out, meaning the trait object doesn&#39;t have one. The size is <strong>0x10</strong> and the alignment is <strong>0x8</strong>. Then you have a ptr to ****@<em>RNvXs1g_NtCshoIjhMyZzUf_4core3fmtReNtB6_5Debug3fmtB8</em>** function. We can see that there is also the types&#39; definition at the beginning of the line. The ptr keyword type for the function tells us it&#39;s going to be an absolute address toward the function.</p>
<h4 id="vtables-calls">VTables calls</h4><p>To better understand how vtables calls are made, let&#39;s first look at a <em>normal</em> call instruction:</p>
<pre class="code-block"><code class="hljs language-llvm">  <span class="hljs-keyword">call</span> <span class="hljs-keyword">fastcc</span> <span class="hljs-type">void</span> <span class="hljs-title">@_RNvNtCs7UalrjukX49_20RustyShell_shellcode15runtime_resolve18get_module_address</span>(<span class="hljs-type">ptr</span> <span class="hljs-keyword">noalias</span> nofree noundef nonnull <span class="hljs-keyword">align</span> <span class="hljs-number">8</span> captures(none) dereferenceable(<span class="hljs-number">24</span>) <span class="hljs-variable">%_1</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-keyword">noalias</span> nofree noundef nonnull <span class="hljs-keyword">readonly</span> captures(address<span class="hljs-punctuation">,</span> read_provenance) <span class="hljs-title">@alloc_51761afdde5661790743ff42719270e3.5</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i64</span> noundef <span class="hljs-number">12</span>) <span class="hljs-variable">#21</span></code></pre><p>This is a call instruction to call into the get_module_address method of the <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/runtime_resolve.rs">runtime_resolve.rs</a> module. We can see here that the call operand, i.e. the value that we actually call is a function definition: <strong>@_RNvNtCs7UalrjukX49_20RustyShell_shellcode15runtime_resolve18get_module_address</strong>. We can find this function definition somewhere in the same file:</p>
<pre class="code-block"><code class="hljs language-llvm"><span class="hljs-comment">; RustyShell_shellcode::runtime_resolve::get_module_address</span>
<span class="hljs-comment">; Function Attrs: nounwind optsize uwtable</span>
<span class="hljs-keyword">define</span> <span class="hljs-keyword">internal</span> <span class="hljs-keyword">fastcc</span> <span class="hljs-type">void</span> <span class="hljs-title">@_RNvNtCs7UalrjukX49_20RustyShell_shellcode15runtime_resolve18get_module_address</span>(<span class="hljs-type">ptr</span> dead_on_unwind <span class="hljs-keyword">noalias</span> nofree noundef nonnull writable writeonly <span class="hljs-keyword">align</span> <span class="hljs-number">8</span> captures(none) dereferenceable(<span class="hljs-number">24</span>) <span class="hljs-variable">%_0</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-keyword">noalias</span> nofree noundef nonnull <span class="hljs-keyword">readonly</span> captures(address<span class="hljs-punctuation">,</span> read_provenance) <span class="hljs-variable">%module_name.0</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i64</span> noundef <span class="hljs-variable">%module_name.1</span>) <span class="hljs-keyword">unnamed_addr</span> <span class="hljs-variable">#0</span> {
<span class="hljs-symbol">start:</span>
  <span class="hljs-variable">%self_iter.i</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">alloca</span> [<span class="hljs-number">24</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span>
  <span class="hljs-variable">%_4.i</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">alloca</span> [<span class="hljs-number">24</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span></code></pre><p>I&#39;ve put only the first instruction of the function, because what I wanted to show you is that you have here the <strong>define internal fastcc void @_RNvNtCs7UalrjukX49_20RustyShell_shellcode15runtime_resolve18get_module_address</strong> function definition, which is the exact same name than the one in the call instruction.</p>
<p>This kind of call is a <strong>direct call</strong>, whether it be inside your own code <strong>(relative)</strong> or not <strong>(absolute)</strong>. They will never be absolute in our shellcode template, because we make sure that we don&#39;t call functions outside our code, at least not statically.</p>
<p>Now let&#39;s take an example of indirect function call:</p>
<pre class="code-block"><code class="hljs language-llvm">  <span class="hljs-variable">%f.i.i</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">tail</span> <span class="hljs-keyword">call</span> noundef nonnull <span class="hljs-keyword">align</span> <span class="hljs-number">8</span> <span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvNtNtNtCshoIjhMyZzUf_4core2io5error12os_functions16get_os_functions</span>() <span class="hljs-variable">#9</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!noalias</span> <span class="hljs-title">!369</span>
  <span class="hljs-variable">%_4.i.i</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">load</span> <span class="hljs-type">ptr</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-variable">%f.i.i</span><span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!noalias</span> <span class="hljs-title">!369</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!nonnull</span> <span class="hljs-title">!4</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!noundef</span> <span class="hljs-title">!4</span>
  <span class="hljs-variable">%_0.i.i</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">tail</span> <span class="hljs-keyword">call</span> noundef <span class="hljs-keyword">zeroext</span> <span class="hljs-type">i1</span> <span class="hljs-variable">%_4.i.i</span>(<span class="hljs-type">i32</span> noundef <span class="hljs-variable">%_3.i</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-keyword">noalias</span> nofree noundef nonnull <span class="hljs-keyword">align</span> <span class="hljs-number">8</span> dereferenceable(<span class="hljs-number">24</span>) <span class="hljs-variable">%f</span>) <span class="hljs-variable">#9</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!noalias</span> <span class="hljs-title">!363</span></code></pre><p>Here we first have a <strong>call</strong> to <strong>get_os_functions</strong>, which is a function inside the core library, that returns a pointer to a structure holding function pointers. Then, the <strong>load ptr</strong> is here to dereference the pointer returned by <strong>get_os_function</strong> and take the pointer that is at this address, which is a <strong>function pointer</strong>. Finally, you have a call to this <strong>function pointer</strong>.</p>
<p>Remember what I said earlier, <strong>indirect calls</strong> like this are always <strong>absolute</strong>, and that&#39;s true. The call instruction will end up calling a pointer to an <strong>absolute address</strong> toward an os function (os function here being a function inside the core library. It&#39;s just how it has been named inside the core library, it&#39;s not actually a WINAPI function or any other system call on other OS). And yet, this works inside our shellcode. But why? It&#39;s an absolute address, it should involve relocations and break shellcode!</p>
<p>Well the magic happens at the moment where the <strong>function pointer</strong> is initialized. The structure returned by the <strong>get_os_function</strong> is initialized at some point. It is not statically initialized at compile time. The pointers to the <em>os functions</em> is retrieved at runtime. And when they are initialized, it can be done <strong>relatively</strong>. Just like a <strong>direct call</strong> inside you own code, the compiler can compute the offset between your <strong>function pointer</strong> initialization and the actual <strong>function location</strong>. So the resulting instruction in assembly will be in the form: <strong>mov reg, rip+offset</strong>, where <em>reg</em> end up inside your function pointer to initialize it properly.</p>
<p>Therefore, function pointers inside our code is not a problem, because they are also initialized relatively, as long as they are not declared as a global or static variable. Awesome!</p>
<p>Okay, but what about <strong>VTables calls</strong> then? That&#39;s a problem because function pointers inside vtables are actually statically initialized. Inside the <em>.rdata</em> section of the binary, you will find the vtable, with the function pointers computed based on the preferred base address of the binary. Therefore, it will generate <strong>relocations</strong>. Let&#39;s see first how vtables calls are emitted with <strong>LLVM IR</strong>:</p>
<pre class="code-block"><code class="hljs language-llvm">  <span class="hljs-variable">%4</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">getelementptr</span> <span class="hljs-keyword">inbounds</span> <span class="hljs-keyword">nuw</span> <span class="hljs-type">i8</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-variable">%f.8.val</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i64</span> <span class="hljs-number">32</span>
  <span class="hljs-variable">%5</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">load</span> <span class="hljs-type">ptr</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-variable">%4</span><span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!invariant.load</span> <span class="hljs-title">!5</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!nonnull</span> <span class="hljs-title">!5</span>
  <span class="hljs-variable">%_7</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">tail</span> <span class="hljs-keyword">call</span> noundef <span class="hljs-keyword">zeroext</span> <span class="hljs-type">i1</span> <span class="hljs-variable">%5</span>(<span class="hljs-type">ptr</span> noundef nonnull <span class="hljs-variable">%f.0.val</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> noundef <span class="hljs-variable">%0</span>) <span class="hljs-variable">#23</span></code></pre><p>So here we have pretty much the same instruction as the example on <strong>get_os_functions</strong>. You have <strong>%f.8.val</strong>, which is the pointer toward the <strong>vtable</strong>. Then, the <strong>getelementptr</strong> offsets this pointer by 32 bytes, to get into the second function pointer after the <strong>drop</strong>, <strong>size</strong> and <strong>align</strong> fields. The <strong>load ptr</strong> is here to dereference the pointer to get the actual function pointer and then the <strong>call instruction</strong> calls it.</p>
<p>The first question I asked myself was: This looks a lot like when calling a function pointer. How can I differentiate the two? Because in the function pointer example, it is obvious that it comes from a function, and not a vtable. But on the vtable example, the <strong>%f.8.val</strong> could actually be the return value of the get_os_function, that was called earlier. How can I be sure we are talking about a vtable here?</p>
<p>Well the answer lies in the details. You might have noticed that at the end of the line on a <strong>call instruction</strong>, you have a number with <em>#</em> before it. For this vtable call, it is <strong>#23</strong>. This number corresponds to some metadata that is defined at the end of the current file. And here is its value:</p>
<pre class="code-block"><code class="hljs language-llvm"><span class="hljs-keyword">attributes</span> <span class="hljs-variable">#23</span> <span class="hljs-operator">=</span> { <span class="hljs-keyword">inlinehint</span> <span class="hljs-keyword">nounwind</span> }</code></pre><p>And the attribute <strong>inlinehint</strong> especially is only added by Rust on vtables calls. It is inside the Rust compiler code. Now I didn&#39;t dig enough to understand why that is the case, but I think I can still use this correlation to correctly identify <strong>function pointers calls</strong> that actually are <strong>vtables calls</strong>. Until Rust changes this behavior of course. So it works for now, but it might change in the future.</p>
<h3 id="relative-vtables-in-llvm-ir">Relative VTables in LLVM IR</h3><p>When I was trying to find a specific flag that would make vtables relatives, I stumbled upon a GitHub issue from developers of the fuchsia OS about <a href="https://github.com/rust-lang/compiler-team/issues/903">relative vtable in rust</a>. They want to add a flag similar to the <strong>-fexperimental-relative-c++-abi-vtables</strong> C++ Clang flag, so that it would allow vtables to be PC-relative. And in the issue, they explain how such ABI would work. I actually had everything I needed to make my vtables relative. I tried to imagine a solution and when I found this issue, I didn&#39;t want to reinvent the wheel, especially when I was sure it wouldn&#39;t be an improvement on the matter. So thank you to <a href="https://github.com/PiJoules">PiJoules</a> and <a href="https://github.com/erickt">Erick Tryzelaar</a> for their solution. I only did a minor changes to their solution.</p>
<h4 id="vtables-definition-modification">VTables definition modification</h4><p>So here are the changes we need to do to the LLVM IR, so that vtable calls become relatives:</p>
<pre class="code-block"><code class="hljs language-llvm"><span class="hljs-comment">; Original form of vtables</span>
<span class="hljs-title">@vtable.0</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">unnamed_addr</span> <span class="hljs-keyword">constant</span> &lt;{ [<span class="hljs-number">24</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> }&gt; &lt;{ [<span class="hljs-number">24</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>] <span class="hljs-keyword">c</span><span class="hljs-string">&quot;<span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\10</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\08</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span>&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvXs1g_NtCshoIjhMyZzUf_4core3fmtReNtB6_5Debug3fmtB8_</span> }&gt;<span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span>

<span class="hljs-comment">; New version after LLVM Pass</span>
<span class="hljs-title">@vtable.0</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">unnamed_addr</span> <span class="hljs-keyword">constant</span> { [<span class="hljs-number">24</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> } { [<span class="hljs-number">24</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>] <span class="hljs-keyword">c</span><span class="hljs-string">&quot;<span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\10</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\08</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span>&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> <span class="hljs-keyword">trunc</span> (<span class="hljs-type">i64</span> <span class="hljs-keyword">sub</span> (<span class="hljs-type">i64</span> <span class="hljs-keyword">ptrtoint</span> (<span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvXs1g_NtCshoIjhMyZzUf_4core3fmtReNtB6_5Debug3fmtB8_</span> <span class="hljs-keyword">to</span> <span class="hljs-type">i64</span>)<span class="hljs-punctuation">,</span> <span class="hljs-type">i64</span> <span class="hljs-keyword">ptrtoint</span> (<span class="hljs-type">ptr</span> <span class="hljs-title">@vtable.0</span> <span class="hljs-keyword">to</span> <span class="hljs-type">i64</span>)) <span class="hljs-keyword">to</span> <span class="hljs-type">i32</span>) }<span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span></code></pre><p>Okay so here, we see that we don&#39;t modify the first array of i8, since the drop function is zeroed out, nothing to do here, we leave this unchanged. The first pointer becomes i32, because now the pointer becomes an offset toward the function from the start of the vtable. We will see why this design was chosen by analyzing the transformation on vtable calls. So the last part, <strong>i32 trunc (i64 sub (i64 ptrtoint (ptr @<em>RNvXs1g_NtCshoIjhMyZzUf_4core3fmtReNtB6_5Debug3fmtB8</em> to i64), i64 ptrtoint (ptr @vtable.0 to i64)) to i32)</strong> is here to say we take the pointer to the function, we substract the pointer to the vtable to it, and we take the result into an i32. i32 is enough here as long as your binary size is smaller than <strong>2 GB</strong>, which should be fine for now. I could imagine a solution with i64 if this issue ever comes up.</p>
<p>So the value that is going to be loaded from the vtable is the offset to the function from the start of the vtable. This is important, because when fixing the vtables calls, we are going to use a specific <strong>LLVM procedure</strong> that works this way.</p>
<p>Another example with the drop function defined:</p>
<pre class="code-block"><code class="hljs language-llvm"><span class="hljs-comment">; Original form of vtables</span>
<span class="hljs-title">@vtable.1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">unnamed_addr</span> <span class="hljs-keyword">constant</span> &lt;{ <span class="hljs-type">ptr</span><span class="hljs-punctuation">,</span> [<span class="hljs-number">16</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> }&gt; &lt;{ <span class="hljs-type">ptr</span> <span class="hljs-title">@_RINvNtCshoIjhMyZzUf_4core3ptr9drop_glueNtNtCshodfiC9HsYt_5alloc6string6StringEBF_</span><span class="hljs-punctuation">,</span> [<span class="hljs-number">16</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>] <span class="hljs-keyword">c</span><span class="hljs-string">&quot;<span class="hljs-char escape_">\18</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\08</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span>&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvXsZ_NtCshodfiC9HsYt_5alloc6stringNtB5_6StringNtNtCshoIjhMyZzUf_4core3fmt5Write9write_str</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvXsZ_NtCshodfiC9HsYt_5alloc6stringNtB5_6StringNtNtCshoIjhMyZzUf_4core3fmt5Write10write_char</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvYNtNtCshodfiC9HsYt_5alloc6string6StringNtNtCshoIjhMyZzUf_4core3fmt5Write9write_fmtB6_</span> }&gt;<span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span>

<span class="hljs-comment">; New version after LLVM Pass</span>
<span class="hljs-title">@vtable.1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">unnamed_addr</span> <span class="hljs-keyword">constant</span> { <span class="hljs-type">i32</span><span class="hljs-punctuation">,</span> [<span class="hljs-number">4</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> [<span class="hljs-number">16</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>]<span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> } { <span class="hljs-type">i32</span> <span class="hljs-keyword">trunc</span> (<span class="hljs-type">i64</span> <span class="hljs-keyword">sub</span> (<span class="hljs-type">i64</span> <span class="hljs-keyword">ptrtoint</span> (<span class="hljs-type">ptr</span> <span class="hljs-title">@_RINvNtCshoIjhMyZzUf_4core3ptr9drop_glueNtNtCshodfiC9HsYt_5alloc6string6StringEBF_</span> <span class="hljs-keyword">to</span> <span class="hljs-type">i64</span>)<span class="hljs-punctuation">,</span> <span class="hljs-type">i64</span> <span class="hljs-keyword">ptrtoint</span> (<span class="hljs-type">ptr</span> <span class="hljs-title">@vtable.1</span> <span class="hljs-keyword">to</span> <span class="hljs-type">i64</span>)) <span class="hljs-keyword">to</span> <span class="hljs-type">i32</span>)<span class="hljs-punctuation">,</span> [<span class="hljs-number">4</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>] <span class="hljs-keyword">zeroinitializer</span><span class="hljs-punctuation">,</span> [<span class="hljs-number">16</span> <span class="hljs-keyword">x</span> <span class="hljs-type">i8</span>] <span class="hljs-keyword">c</span><span class="hljs-string">&quot;<span class="hljs-char escape_">\18</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\08</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span><span class="hljs-char escape_">\00</span>&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> <span class="hljs-keyword">trunc</span> (<span class="hljs-type">i64</span> <span class="hljs-keyword">sub</span> (<span class="hljs-type">i64</span> <span class="hljs-keyword">ptrtoint</span> (<span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvXsZ_NtCshodfiC9HsYt_5alloc6stringNtB5_6StringNtNtCshoIjhMyZzUf_4core3fmt5Write9write_str</span> <span class="hljs-keyword">to</span> <span class="hljs-type">i64</span>)<span class="hljs-punctuation">,</span> <span class="hljs-type">i64</span> <span class="hljs-keyword">ptrtoint</span> (<span class="hljs-type">ptr</span> <span class="hljs-title">@vtable.1</span> <span class="hljs-keyword">to</span> <span class="hljs-type">i64</span>)) <span class="hljs-keyword">to</span> <span class="hljs-type">i32</span>)<span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> <span class="hljs-keyword">trunc</span> (<span class="hljs-type">i64</span> <span class="hljs-keyword">sub</span> (<span class="hljs-type">i64</span> <span class="hljs-keyword">ptrtoint</span> (<span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvXsZ_NtCshodfiC9HsYt_5alloc6stringNtB5_6StringNtNtCshoIjhMyZzUf_4core3fmt5Write10write_char</span> <span class="hljs-keyword">to</span> <span class="hljs-type">i64</span>)<span class="hljs-punctuation">,</span> <span class="hljs-type">i64</span> <span class="hljs-keyword">ptrtoint</span> (<span class="hljs-type">ptr</span> <span class="hljs-title">@vtable.1</span> <span class="hljs-keyword">to</span> <span class="hljs-type">i64</span>)) <span class="hljs-keyword">to</span> <span class="hljs-type">i32</span>)<span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> <span class="hljs-keyword">trunc</span> (<span class="hljs-type">i64</span> <span class="hljs-keyword">sub</span> (<span class="hljs-type">i64</span> <span class="hljs-keyword">ptrtoint</span> (<span class="hljs-type">ptr</span> <span class="hljs-title">@_RNvYNtNtCshodfiC9HsYt_5alloc6string6StringNtNtCshoIjhMyZzUf_4core3fmt5Write9write_fmtB6_</span> <span class="hljs-keyword">to</span> <span class="hljs-type">i64</span>)<span class="hljs-punctuation">,</span> <span class="hljs-type">i64</span> <span class="hljs-keyword">ptrtoint</span> (<span class="hljs-type">ptr</span> <span class="hljs-title">@vtable.1</span> <span class="hljs-keyword">to</span> <span class="hljs-type">i64</span>)) <span class="hljs-keyword">to</span> <span class="hljs-type">i32</span>) }<span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span></code></pre><p>Basically, every function pointer works the same here. We do the same transformation to the drop function. The interesting part here is about the 4 bytes array of i8, inserted after the i32 representing the offset to the drop table. I chose to add padding here, because yes it is just padding, so that the size and align fields are still at the same offset inside the vtable. In the GitHub issue <a href="https://github.com/rust-lang/compiler-team/issues/903">relative vtable in rust</a>, fuchsia developers didn&#39;t add this padding, because they also changed the size_of_val and align_of_val compiler intrinsics to correspond to the new offsets. As I am not modifying these functions, I need to not modify the size and align offsets inside the vtable to no break them.</p>
<p>Okay, let&#39;s see now how a vtable call is modified</p>
<h4 id="vtables-call-modification">VTables call modification</h4><pre class="code-block"><code class="hljs language-llvm">  <span class="hljs-comment">; Previous vtable call</span>
  <span class="hljs-variable">%4</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">getelementptr</span> <span class="hljs-keyword">inbounds</span> <span class="hljs-keyword">nuw</span> <span class="hljs-type">i8</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-variable">%f.8.val</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i64</span> <span class="hljs-number">32</span>
  <span class="hljs-variable">%5</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">load</span> <span class="hljs-type">ptr</span><span class="hljs-punctuation">,</span> <span class="hljs-type">ptr</span> <span class="hljs-variable">%4</span><span class="hljs-punctuation">,</span> <span class="hljs-keyword">align</span> <span class="hljs-number">8</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!invariant.load</span> <span class="hljs-title">!5</span><span class="hljs-punctuation">,</span> <span class="hljs-title">!nonnull</span> <span class="hljs-title">!5</span>
  <span class="hljs-variable">%_7</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">tail</span> <span class="hljs-keyword">call</span> noundef <span class="hljs-keyword">zeroext</span> <span class="hljs-type">i1</span> <span class="hljs-variable">%5</span>(<span class="hljs-type">ptr</span> noundef nonnull <span class="hljs-variable">%f.0.val</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> noundef <span class="hljs-variable">%0</span>) <span class="hljs-variable">#23</span>
  
  <span class="hljs-comment">; New version after LLVM Pass</span>
  <span class="hljs-variable">%6</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">call</span> <span class="hljs-type">ptr</span> <span class="hljs-title">@llvm.load.relative.i32</span>(<span class="hljs-type">ptr</span> <span class="hljs-variable">%f.8.val</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> <span class="hljs-number">28</span>)
  <span class="hljs-variable">%_7</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">tail</span> <span class="hljs-keyword">call</span> noundef <span class="hljs-keyword">zeroext</span> <span class="hljs-type">i1</span> <span class="hljs-variable">%6</span>(<span class="hljs-type">ptr</span> noundef nonnull <span class="hljs-variable">%f.0.val</span><span class="hljs-punctuation">,</span> <span class="hljs-type">i32</span> noundef <span class="hljs-variable">%0</span>) <span class="hljs-variable">#23</span></code></pre><p>Okay, in the original instructions, we had a <strong>getelementptr</strong> instruction to offset of 32 bytes inside the vtable, which would be the second function pointer. Then a <strong>load ptr</strong> to load the ptr at that offset, and finally call it. The first two instructions are now replaced with a single one. A call to the <strong>@llvm.load.relative.i32</strong>, which takes a pointer and an offset. This specific LLVM function retrieves the i32 value at the address <strong>ptr + offset</strong>, and add the ptr value to it. You can find information about this intrinsic here: <a href="https://reviews.llvm.org/D18367">Introduce llvm.load.relative intrinsic.</a>. It does exactly what is needed to compute the absolute address to the function. Moreover, the offset is changed based on this equation: <strong>new_offset = prev_offset - (((prev_offset / 8) -3) * 4)</strong>. This will compute the new offset, taking into account that every function pointer is now 4 bytes long instead of 8. That&#39;s why <strong>32</strong> becomes <strong>28</strong> in the <strong>new version</strong>. And that&#39;s it, this way, the vtables and vtable call are all done relatively.</p>
<p>Again, thank you to fuchsia dev for their solution. Interesting to say that their goal is actually to make process startup faster by removing relocations that the OS has to handle and reduce memory usage by reducing the size of vtables. I have a totally different goal, and yet, we end up with the same problematic and their solution fits perfectly to the problem I was facing. They used the <strong>Clang</strong> compiler front-end specific flag, now they are trying to modify the Rust compiler front-end. Maybe implementing their solution to the LLVM IR level directly, just like I did, would allow them to have a solution that is language agnostic. I have not looked at how vtables in C++ by Clang are handled, maybe such a solution is impossible, but that would be an interesting lead to follow.</p>
<h2 id="compiler-flags-to-enable-llvm-pass">Compiler flags to enable LLVM Pass</h2><p>Like I said before, LLVM proposes an API to do all this programmatically, through what is called <a href="https://llvm.org/docs/WritingAnLLVMNewPMPass.html">LLVM Pass</a>. It&#39;s like a script, in C++, that allows you to run optimizations on the <strong>LLVM IR</strong>. All these modifications are done via the <strong>LLVM Pass</strong> script. I won&#39;t go over the code here, but if you want to check it out, here it is: <a href="https://github.com/LostAquilae/RustyShell/blob/main/PC-relative_vtable_llvm_pass/PC_relative_vtable.cpp">PC_relative_vtable.cpp</a>.</p>
<p>At first, I thought it wasn&#39;t possible to integrate this inside the Rust workflow. So I started to see how I could, given the LLVM IR of my code, run the pass and then assemble and link thanks to <strong>LLVM assembler</strong> and <strong>mingw linker</strong>, outside the Rust workflow. I managed to do it, but then I discovered, you can actually enable your own <strong>LLVM pass</strong> inside the <strong>Rust compiler workflow</strong>, thanks to several compilation flags:</p>
<ul>
<li><p><strong>-Z llvm-plugins=${PWD}/PC-relative_vtable_llvm_pass/bin/PC-relative-vtable.so</strong>: This flag is here to load the LLVM plugin inside the Rust compiler, so that we can call the optimization.</p>
</li>
<li><p><strong>-C passes=relative-vtable</strong>: Inside the <strong>LLVM Pass</strong>, I named the optimization <strong>relative-vtable</strong>. <strong>-C passes=name</strong> is here to specifically request optimization from LLVM, because LLVM comes with lots of optimization already bundled. But thanks to the previous flag, we have loaded our own LLVM Pass, so we call optimization from it right away.</p>
</li>
<li><p><strong>--emit=llvm-ir</strong>: This flag actually tells the rust compiler to emit LLVM IR files with the <strong>LLVM IR</strong> generated from your code so that you can take a look at it. I couldn&#39;t figure out why, but when it is not enabled, it&#39;s like my pass doesn&#39;t do anything. It runs, I made sure of that, but the modifications done inside it seems to only be in the final binary when this flag is enabled. If anyone wants to look into this topic, I would love to hear what you find out.</p>
</li>
</ul>
<p>And there you have it, the full shellcode template, with the main entry point, <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/Shellcode_example/src/shellcode.rs">shellcode.rs</a>, showing an example of <strong>format!</strong> macro usage, which generates <strong>vtables</strong>, but is still shellcode compatible, thanks to the custom <strong>LLVM Pass</strong> that makes them relative.</p>
<h2 id="conclusion">Conclusion</h2><p>This concludes this series about RustyShell, a new shellcode template that empowers you with even more of the Rust programming language available right out of the box. For now, this template works for small use cases, but I would love people to spot problems with bigger codebases. So don&#39;t hesitate to publish issues or submit pull request if you find use cases that the template doesn&#39;t handle properly, and you have a solution for it. Feedbacks from the community is really important to me, I mean it!</p>
<h2 id="credits">Credits</h2><p><a href="https://github.com/PiJoules">PiJoules</a> and <a href="https://github.com/erickt">Erick Tryzelaar</a> for their work on <a href="https://github.com/rust-lang/compiler-team/issues/903">relative vtable in rust</a></p>
<h2 id="sources">Sources</h2><p><a href="https://doc.rust-lang.org/book/ch10-01-syntax.html">Generic Data Types</a></p>
<p><a href="https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html">PartialOrd Trait</a></p>
<p><a href="https://doc.rust-lang.org/alloc/macro.format.html">format! macro</a></p>
<p><a href="https://doc.rust-lang.org/alloc/fmt/trait.Write.html">Write Trait</a></p>
<p><a href="https://en.wikipedia.org/wiki/Static_single-assignment_form">Static Single Assignment</a></p>
<p><a href="https://www.compilersutra.com/docs/llvm/">LLVM Tutorial</a></p>
<p><a href="https://mcyoung.xyz/2023/08/01/llvm-ir/">A gentle introduction to LLVM IR</a></p>
<p><a href="https://llvm.org/docs/">LLVM Project</a></p>
<p><a href="https://reviews.llvm.org/D18367">Introduce llvm.load.relative intrinsic.</a></p>
<p><a href="https://llvm.org/docs/WritingAnLLVMNewPMPass.html">LLVM Pass</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>RustyShell Part 2: The syntactic sugar</title>
      <link>https://lostaquilae.github.io/LostAquilae_blog/writing/RustyShell-part-2/</link>
      <guid isPermaLink="true">https://lostaquilae.github.io/LostAquilae_blog/writing/RustyShell-part-2/</guid>
      <description>Making writing shellcode easier and more fluid.</description>
      <pubDate>Mon, 28 Sep 2026 00:00:00 GMT</pubDate>
      <category>Rust</category>
      <category>Shellcode</category>
      <category>Syntax</category>
      <content:encoded><![CDATA[<h1>RustyShell Part 2: The syntactic sugar</h1>
<p>Making writing shellcode easier and more fluid.</p>
<p>Writing shellcode means writing code with a lot of constraints.</p>
<p>The simple fact that you cannot use any standard library already forces you to use OS API to do anything. Add to that the fact that you have to resolve dynamically these function pointers, and you need ten lines of code to do what would have taken only one usually.</p>
<p>So how do we make this easier?</p>
<h2 id="using-windows-sys-crate-for-windows-types-and-structures">Using windows-sys crate for Windows types and structures</h2><p>We can include the <a href="https://crates.io/crates/windows-sys">windows-sys</a> crate that already includes a lot of types and structures definition in pure Rust. This is useful to not having to rewrite every definition by hand. It is always possible to rewrite our own implementation of a structure, because sometimes some fields are not quite represented, because Windows consider these fields as private fields that we should not use.</p>
<p>Let&#39;s take an example, the <strong>LDR_DATA_TABLE_ENTRY</strong>, which is the structure representing a loaded module in the current process, found in the doubly linked list of loaded module inside the <strong>PEB</strong>. The windows-sys crate is implementing this structure like this:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-meta">#[repr(C)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">LDR_DATA_TABLE_ENTRY</span> {
    <span class="hljs-keyword">pub</span> Reserved1: [*<span class="hljs-keyword">mut</span> c_void; <span class="hljs-number">2</span>],
    <span class="hljs-keyword">pub</span> InMemoryOrderLinks: LIST_ENTRY,
    <span class="hljs-keyword">pub</span> Reserved2: [*<span class="hljs-keyword">mut</span> c_void; <span class="hljs-number">2</span>],
    <span class="hljs-keyword">pub</span> DllBase: *<span class="hljs-keyword">mut</span> c_void,
    <span class="hljs-keyword">pub</span> Reserved3: [*<span class="hljs-keyword">mut</span> c_void; <span class="hljs-number">2</span>],
    <span class="hljs-keyword">pub</span> FullDllName: UNICODE_STRING,
    <span class="hljs-keyword">pub</span> Reserved4: [<span class="hljs-type">u8</span>; <span class="hljs-number">8</span>],
    <span class="hljs-keyword">pub</span> Reserved5: [*<span class="hljs-keyword">mut</span> c_void; <span class="hljs-number">3</span>],
    <span class="hljs-keyword">pub</span> Anonymous: LDR_DATA_TABLE_ENTRY_0,
    <span class="hljs-keyword">pub</span> TimeDateStamp: <span class="hljs-type">u32</span>,
}</code></pre><p>We see here that, following <em>FullDllName</em> is a field named <em>Reserved4</em>, and is composed of 8 bytes. But it is commonly known that this field is the <em>BaseDllName</em>, which is only the module filename, without the full path. That&#39;s why I redefined this structure to replace this <em>Reserved4</em> field into <em>BaseDllName</em> as a <strong>UNICODE_STRING</strong>, because it is the actual type behind this field.</p>
<p>This allows me to directly access this field in the <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/runtime_resolve.rs">runtime_resolve.rs</a>, without having to do a cast at the moment I retrieve the data behind this field. You can find the redefinitions of this structure in the <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/peb_types.rs">peb_types.rs</a> module.</p>
<p>Apart from that, I can just include every structure I need from this crate directly, which is a great way to get pure Rust definitions of Windows structures.</p>
<h2 id="using-windows-bindgen-crate-for-winapi-function-types">Using windows-bindgen crate for WINAPI function types</h2><p>The <a href="https://crates.io/crates/windows-bindgen">windows-bindgen</a> crate allows to automatically generate bindings for Windows structures and functions. The goal here is to automatically retrieve function pointers for the Windows API. We just need to add a <a href="https://github.com/LostAquilae/RustyShell/blob/main/build.rs">build.rs</a> script:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-keyword">use</span> regex::Regex;
<span class="hljs-keyword">use</span> std::fs::{read_to_string, write};
<span class="hljs-keyword">use</span> std::{env, path::PathBuf};

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-comment">// Only rerun the build script if it has changed</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;cargo:rerun-if-changed=build.rs&quot;</span>);

    <span class="hljs-comment">// Retrieving the OUT_DIR environment variable to get the output dir which we are allowed to use</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">bindings_path</span> = PathBuf::<span class="hljs-title function_ invoke__">from</span>(env::<span class="hljs-title function_ invoke__">var</span>(<span class="hljs-string">&quot;OUT_DIR&quot;</span>).<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;Couldn&#x27;t retrieve OUT_DIR environment variable to put generated bindings inside it&quot;</span>));
    bindings_path.<span class="hljs-title function_ invoke__">push</span>(<span class="hljs-string">&quot;winapi_bindings.rs&quot;</span>);

    <span class="hljs-comment">// Using windows bindgen to generate bindings for WINAPI we use</span>
    windows_bindgen::Bindgen::<span class="hljs-title function_ invoke__">new</span>()
        .<span class="hljs-title function_ invoke__">output</span>(&amp;bindings_path)
        .<span class="hljs-title function_ invoke__">flat</span>()
        .<span class="hljs-title function_ invoke__">sys</span>()
        .<span class="hljs-title function_ invoke__">filters</span>([<span class="hljs-string">&quot;LoadLibraryA&quot;</span>, <span class="hljs-string">&quot;MessageBoxA&quot;</span>])
        .<span class="hljs-title function_ invoke__">extern_fns</span>()
        .<span class="hljs-title function_ invoke__">write</span>();

    <span class="hljs-comment">// The windows bindgen crate generates 2 things for a WINAPI:</span>
    <span class="hljs-comment">// - The direct function you can call directly into your code</span>
    <span class="hljs-comment">// - The function pointer type that can be used as a function pointer</span>
    <span class="hljs-comment">//</span>
    <span class="hljs-comment">// Only the latter is of interest for us, since we are resolving WINAPI calls dynamically at runtime for shellcode purposes.</span>
    <span class="hljs-comment">// This small code only keep the type definitions and remove the functions</span>

    <span class="hljs-comment">// Reading the generated bindings from file into a String</span>
    <span class="hljs-keyword">let</span> <span class="hljs-variable">bindings_content</span> =
        <span class="hljs-title function_ invoke__">read_to_string</span>(&amp;bindings_path).<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;Error: Couldn&#x27;t read bindings file&quot;</span>);

    <span class="hljs-comment">// Applying a regular expression to it to remove the function definitions</span>
    <span class="hljs-keyword">let</span> <span class="hljs-variable">regex</span> = Regex::<span class="hljs-title function_ invoke__">new</span>(<span class="hljs-string">r#&quot;unsafe extern &quot;system&quot; \{[^}]*\}&quot;#</span>)
        .<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;Couldn&#x27;t construct regex object from regex string&quot;</span>);
    <span class="hljs-keyword">let</span> <span class="hljs-variable">filtered_bindings</span> = regex.<span class="hljs-title function_ invoke__">replace_all</span>(&amp;bindings_content, <span class="hljs-string">&quot;&quot;</span>).<span class="hljs-title function_ invoke__">to_string</span>();

    <span class="hljs-comment">// Overwriting the winapi_bindings.rs content with the new filtered one</span>
    <span class="hljs-title function_ invoke__">write</span>(&amp;bindings_path, filtered_bindings)
        .<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;Error: Couldn&#x27;t replace bindings content with filtered one&quot;</span>);
}</code></pre><p>Inside the <strong>filters</strong> function, we can simply add the name of the WINAPI we want to use inside our code. This generates two things in the generated file: The function directly that you can simply use and a function pointer for that exact same function. The small piece of code at the end of the build script is here to remove the function definition, because we don&#39;t want to use that directly, since it will induce imports in the final binary, thus breaking shellcode.</p>
<p>Here is the generated winapi bindings:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">LoadLibraryA</span> = <span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">extern</span> <span class="hljs-string">&quot;system&quot;</span> <span class="hljs-title function_ invoke__">fn</span>(lplibfilename: PCSTR) <span class="hljs-punctuation">-&gt;</span> HMODULE;

<span class="hljs-keyword">pub</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">MessageBoxA</span> =
    <span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">extern</span> <span class="hljs-string">&quot;system&quot;</span> <span class="hljs-title function_ invoke__">fn</span>(hwnd: HWND, lptext: PCSTR, lpcaption: PCSTR, utype: <span class="hljs-type">u32</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span>;

<span class="hljs-keyword">pub</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">HINSTANCE</span> = *<span class="hljs-keyword">mut</span> core::ffi::c_void;
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">HMODULE</span> = HINSTANCE;
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">HWND</span> = *<span class="hljs-keyword">mut</span> core::ffi::c_void;
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">PCSTR</span> = *<span class="hljs-keyword">const</span> <span class="hljs-type">u8</span>;</code></pre><p>We can see the two function pointers at the start. We can simply use these anywhere we need to cast a function pointer to call the corresponding function. Is also defines the types used inside the function pointer definition.</p>
<p>We just simply need to add this to our entry point, or in any module of the project:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-keyword">mod</span> winapi_bindings {
    include!(<span class="hljs-built_in">concat!</span>(<span class="hljs-built_in">env!</span>(<span class="hljs-string">&quot;OUT_DIR&quot;</span>), <span class="hljs-string">&quot;/winapi_bindings.rs&quot;</span>));
}</code></pre><p>This macro invocation will expand to copy everything from the winapi_bindings generated by the build script. You can then import function pointers you need from this module directly. Even though the generation will also define types used in the function pointer, if you ever need one of these types, I would advise you to simply import it from the <a href="https://crates.io/crates/windows-sys">windows-sys</a> crate directly. It will make your code clearer, since the import will be completely written inside the file and not hidden behind the <strong>include!</strong> macro invocation.</p>
<p>If you ever need to inspect the content of this file, note that it is available here: <strong>target/x86_64-pc-windows-gnu/release/build/yourcrate/id/out/winapi_bindings.rs</strong>. Of course, you need to adapt this path if you have a different target or if you are in debug mode.</p>
<h2 id="macros-to-make-dynamically-calling-winapi-function-easier">Macros to make dynamically calling WINAPI function easier</h2><p>I added two macros to the template : <strong>resolve_call_winapi!</strong> and <strong>call_winapi!</strong>. Let&#39;s explore how each one can be used.</p>
<h3 id="resolvecallwinapi">Resolve_call_winapi!</h3><p>It can be used to automatically resolve the module address and the function address and call the function directly. Here&#39;s an example on how to use it for LoadLibraryA:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-comment">// Calling LoadLibraryA to load user32.dll in memory</span>
<span class="hljs-keyword">let</span> <span class="hljs-variable">user32_address</span> = resolve_call_winapi!(
    kernel32,
    LoadLibraryA,
    c<span class="hljs-string">&quot;user32.dll&quot;</span>.<span class="hljs-title function_ invoke__">as_ptr</span>().<span class="hljs-title function_ invoke__">cast</span>()
);</code></pre><p>At first, you have to declare and initialize the variable into which you want to receive the return value of the WINAPI. Then, you can start to call the macro. Here is the argument list:</p>
<ul>
<li><p><strong>Module Name</strong>: The first argument is the name of the module into which the WINAPI is exported. You have to put the name of the file, without the extension <em>.dll</em> which is automatically added inside the macro.</p>
</li>
<li><p><strong>The WINAPI name</strong>: The name of the WINAPI you want to use. You need to write it exactly as it is defined inside the <em>winapi_bindings.rs</em> module, because this argument will also be used to define the type of the function pointer retrieved.</p>
</li>
<li><p><strong>The arguments of the call</strong>: Lastly, you simply put the argument lists, in the order they are defined on the MSDN documentation, just like you would have put them inside a call to the function directly.</p>
</li>
</ul>
<p>So this is how you can quickly resolve the module address and function address and call the WINAPI function.</p>
<h3 id="callwinapi">call_winapi!</h3><p>This one, on the other hand, is useful if you already have a pointer to the right module and don&#39;t want to resolve it again. It can be used like this:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-comment">// Example calling MessageBoxA</span>
<span class="hljs-keyword">let</span> <span class="hljs-variable">result_message_box</span> = call_winapi!(
    user32_address,
    MessageBoxA,
    <span class="hljs-title function_ invoke__">null_mut</span>(),
    c<span class="hljs-string">&quot;Hello World!&quot;</span>.<span class="hljs-title function_ invoke__">as_ptr</span>().<span class="hljs-title function_ invoke__">cast</span>(),
    c<span class="hljs-string">&quot;Example&quot;</span>.<span class="hljs-title function_ invoke__">as_ptr</span>().<span class="hljs-title function_ invoke__">cast</span>(),
    <span class="hljs-number">0</span>
);</code></pre><p>This is exactly like the first macro, except that <em>user32_address</em> refers directly to a pointer to user32.dll that has already been resolved.</p>
<p>These macros return a Result with the Ok variant being the return value of the <strong>WINAPI</strong> you called and the Err variant being a RuntimeResolveErrors of the <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/runtime_resolve.rs">runtime_resolve.rs</a> module.</p>
<p>The code for these macros can be found in the <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/utils.rs">utils.rs</a> module.</p>
<h2 id="several-compilation-mode-for-easy-debugging">Several compilation mode for easy debugging</h2><p>The repo contains three different crates:</p>
<ul>
<li><p><strong>rusty_shell</strong>: This is the actual RustyShell crate, containing <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/allocator.rs">allocator.rs</a> module, the <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/runtime_resolve.rs">runtime_resolve.rs</a> module and the <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/utils.rs">utils.rs</a> module, that you can depend on to automatically enable all this to use in your project.</p>
</li>
<li><p><strong>shellcode_example</strong>: This crate showcases how you can depend on rusty_shell to create your own shellcode crate. It shows you an example of entry point for shellcode and how you can use features of the rusty_shell crate.</p>
</li>
<li><p><strong>no_shellcode_example</strong>: This crate actually depends on the rusty_shell crate but not compiling as shellcode, for debug purposes mainly. It allows you to use the full std, and enables printing with println! inside the <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/runtime_resolve.rs">runtime_resolve.rs</a> module, since this is the only way to print inside this module.</p>
</li>
</ul>
<p>The rusty_shell crate comes with two features:</p>
<ul>
<li><p><strong>shellcode</strong>: this is basically to gate every std related code behind this feature not being enabled. Again, the println! inside <strong>runtime_resolve.rs</strong> are gated behind it. This is a default feature.</p>
</li>
<li><p><strong>debug</strong>: This enables the printf! macro. In <strong>utils.rs</strong>, you can also find a printf! macro, which is gated behind the debug feature. This macro allows you to pass a literal string or a formatted string, thanks to the format! macro, to print something to the console.</p>
</li>
</ul>
<p>I used the cargo make project to compile the different binaries. Here they are:</p>
<ul>
<li><p><strong>cargo make windows_gnu_shellcode</strong>: Compiles the shellcode_example as shellcode, without <strong>debug</strong> feature enabled.</p>
</li>
<li><p><strong>cargo make windows_gnu_shellcode_debug</strong>: Compiles the shellcode_example as shellcode, with <strong>debug</strong> feature enabled, enabling printing through printf! macro.</p>
</li>
<li><p><strong>cargo make windows_gnu_debug_no_shellcode</strong>: Compiles the no_shellcode_example crate, allowing you to get prints inside the <strong>runtime_resolve.rs</strong> module.</p>
</li>
</ul>
<p>So as you can see, the repo offers a lot of different way to compile the code, mostly for debug purposes.</p>
<h2 id="conclusion">Conclusion</h2><p>You can find the shellcode entry point in the <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/Shellcode_example/src/shellcode.rs">shellcode.rs</a> file. This example simply showcases what you can do with the template, like using Vec and Strings thanks to the Global Allocator. It also shows how to use macros to call MessageBoxA for the example.</p>
<p>At this point, you have a well-designed template that lets you code in Rust as position-independent code in a simple and easy way. You can also use a great part of the std, the core and alloc components at least.</p>
<p>But one problem remains: Trait object. They are used in some parts of the core and alloc crate, and they break shellcode compatibility, meaning we cannot use every part of these crates. We&#39;ll see in the third part of this blog how we can tackle this problem.</p>
<h2 id="sources">Sources</h2><p><a href="https://crates.io/crates/windows-sys">windows-sys</a></p>
<p><a href="https://crates.io/crates/windows-bindgen">windows-bindgen</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>RustyShell Part 1: The shellcode template</title>
      <link>https://lostaquilae.github.io/LostAquilae_blog/writing/RustyShell-part-1/</link>
      <guid isPermaLink="true">https://lostaquilae.github.io/LostAquilae_blog/writing/RustyShell-part-1/</guid>
      <description>A basic shellcode template in Rust to compile simply as position-independent code.</description>
      <pubDate>Fri, 25 Sep 2026 00:00:00 GMT</pubDate>
      <category>Rust</category>
      <category>Shellcode</category>
      <category>Template</category>
      <content:encoded><![CDATA[<h1>RustyShell Part 1: The shellcode template</h1>
<p>A basic shellcode template in Rust to compile simply as position-independent code.</p>
<p>This blog is the first part of a series of blog posts about a tool I developed using the Rust programming language.</p>
<p>A while back, I wanted to investigate what was possible with the Rust programming language when it comes to develop <strong>position-independent code</strong>, aka <strong>shellcode</strong>. I&#39;ll first explain what a <strong>position-independent binary</strong> means, then show you how you can achieve shellcode using Rust. Finally, I&#39;ll present a new, innovative way to use more of the Rust programming language for your shellcode.</p>
<p>I just want to be clear that this blog will focus on shellcode for the Windows operating system. Shellcode for other operating systems is out of the scope of this blog.</p>
<p>You can find the code here: <a href="https://github.com/LostAquilae/RustyShell">RustyShell</a>. You can also directly import this inside your project thanks to the <a href="https://crates.io/crates/rusty_shell">rusty_shell</a> crate publish on crates.io.</p>
<h2 id="whats-a-shellcode">What&#39;s a shellcode?</h2><p>Originally, a shellcode meant an attack payload that would open up a reverse shell on a victim&#39;s computer. It had to be what is called <em>position-independent code</em>, because most of the time, it relied on code injection where your code would be directly injected in memory, without ever touching the disk.</p>
<p>But to really comprehend what <em>position-independent code</em> means, you have to know: None of the code you write is position-independent. When you run your binary on an operating system, several steps of loading will be executed to ensure that your binary can execute correctly. But first, let&#39;s open a simple binary to see what&#39;s inside.</p>
<h3 id="hello-world-tell-me-what-you-are">Hello world!, tell me what you are!</h3><p>We&#39;ll take the simplest example possible: a <em>Hello World!</em> executable written in C:</p>
<pre class="code-block"><code class="hljs language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>

<span class="hljs-comment">// This is just for the purpose of the example</span>
<span class="hljs-type">int</span> global = <span class="hljs-number">0</span>;

<span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> {
    <span class="hljs-comment">// This contains a literal string: &#x27;Hello World!\n&#x27;</span>
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;Hello World!\n&quot;</span>);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}</code></pre><p>This code will turn into an executable with the extension <em>.exe</em> on Windows. This type of file on Windows uses the <em>Portable Executable</em> format, which is the main form of executable file on Windows.</p>
<h4 id="pe-format">PE format?</h4><p>Let&#39;s start with a simple diagram that outlines the PE format:</p>
<figure>
  <img src="https://lostaquilae.github.io/LostAquilae_blog/assets/PE_Format-C10a3P-K.svg" alt="The PE format" style="width:30%; margin: 0 auto" />
  <figcaption style="text-align: center">The PE format</figcaption>
</figure><p>This is what the PE format looks like broadly. You first have different headers:</p>
<ul>
<li><p><strong>DOS Header</strong>: This header comes from MS-DOS operating system, the predecessor of Windows.</p>
</li>
<li><p><strong>DOS Stub</strong>: This is a small MS-DOS executable, here for retro-compatibility. It contains a simple string that is displayed if you try to run you executable on MS-DOS. The default value is <em>This program cannot be run in DOS mode</em>.</p>
</li>
<li><p><strong>NT Headers</strong>: This is actually several headers. The important one is the Optional Header, it contains a lot of information about the executable, a lot of it useful for the loading process that we will study later.</p>
</li>
<li><p><strong>Section Table</strong>: This header lists the different sections of your executable with their size, location, etc...</p>
</li>
</ul>
<p>Headers are mostly here for Windows to properly load the executable in memory. Then, you have the different <strong>sections</strong> of your binary that contains the actual payload of your executable. Let&#39;s look at the most important one, especially regarding shellcode compatibility:</p>
<ul>
<li><p><strong>.text</strong>: The section that contains your executable code. All assembly instructions needed to execute your code will be stored in this section. <em>Memory protections: executable and readable</em>.</p>
</li>
<li><p><strong>.rdata</strong>: This section will contain data that only needs to be read. For example, the string <em>Hello World!</em> here is stored inside this section, because we only need to read the string, we never modify it. <em>Memory protections: readable</em>.</p>
</li>
<li><p><strong>.data</strong>: Unlike rdata, this section contains data that can be read and written to. For example, the global variable <em>global</em> in the C code will be stored in this section. <em>Memory protections: readable and writable</em>.</p>
</li>
<li><p><strong>.idata</strong>: This section is a bit peculiar. Note that you use the printf function, but it is not defined anywhere inside the code you compiled. In simple terms, you defer this work to the OS, by calling a function exported by a DLL on Windows. The printf function is actually implemented inside these files. The <em>.idata</em> section is here to store information that tell Windows <em>I need this function from this DLL</em>, and Windows will load the corresponding DLL and give you the address of the function you need inside it, basically. <em>Memory protections: readable</em>.</p>
</li>
<li><p><strong>.edata</strong>: This section is here to tell the OS where your functions are inside your binary. Most compile code does not export their functions, if this is not needed. But DLLs do, in order for you to import their function thanks to the <em>.idata</em> section. <em>Memory protections: readable</em>.</p>
</li>
<li><p><strong>.reloc</strong>: This section is an important one for our position-independent code issue. Relocations are location in your code where you rely on absolute addresses. They might need fixing, and that&#39;s the job of the OS when it is loading you. <em>Memory Protection: No memory protection since this is only needed for the loading process, the OS discards it when loading the executable</em>.</p>
</li>
</ul>
<p>This last section needs a bit more explanation I would say: Imagine the <strong>Hello World!</strong> string is at offset 1000 (I will take simple addresses, that don&#39;t reflect reality at all, but help explain things clearly) in your binary. Somewhere in your code, you need to read that string, in order to send it as argument to the printf function. That&#39;s where the compiler will compute an absolute address. But the compiler cannot know the address you are going to be loaded at. Therefore, it will choose a <em>preferred base address</em>, let&#39;s say 10 000, and compute all absolute addresses from this address. The <strong>Hello World!</strong> string is then at address 10 000 + 1 000 = 11 000, so in your code, you will have a reference to this specific address.</p>
<p>The compiler also put 10 000 inside the <strong>optional header</strong> of your binary, in a field know as <strong>Image Base</strong>. Windows, when loading your binary, will try to load your code at this address first. If it succeeds, then relocations do not need to be fixed, since all absolute addresses correspond to the real address in memory where to find the data, like your <strong>Hello World!</strong> string. But if it can&#39;t load the binary at this address, and loads it at address 20 000 for example, then your <strong>Hello World!</strong> string ends up at address 20 000 + 1 000 = 21 000. As a result of this, you need to fix relocations, to make absolute addresses inside your code point to the actual location of the data in memory. Windows will then add the difference between the <em>preferred base address</em> and the actual address the binary is loaded at, to fix it. So for example, the <strong>Hello World!</strong> string will become: 11 000 + (20 000 - 10 000) = 11 000 + 10 000 = 21 000.</p>
<p>Now that you have a better grasp of the PE format, we can look at the loading process.</p>
<h4 id="loading-process">Loading process</h4><p>Most of what is explained here has been partly explained when analyzing every section of the PE format. It&#39;s just to explicitly enumerate all steps done by the OS for the loading process. This will help us see clearly what we have to avoid in order to get shellcode compatible code.</p>
<ul>
<li><p><strong>Copy all sections</strong>: First, the OS will copy all the sections at their respective offsets. In your RAM, there is what we call a paging system. On Windows for example, it allocates a minimum of 4 Ko at a time, which is a <em>page</em>. The sections of your binary will always be copied at the start of a page, which means you will have a certain offset between the end of the previous section and the start of a new page, where the following section will be copied.</p>
</li>
<li><p><strong>Resolve all imports</strong>: Just like we saw earlier, your binary will actually need to call functions of the OS. So the OS will read the <em>.idata</em> section to load every DLL you need and give you the address of the function you need inside these DLLs.</p>
</li>
<li><p><strong>Handles every relocation</strong>: The OS, if it couldn&#39;t load your binary at its <em>preferred address</em>, will fix every absolute addresses you use so that you don&#39;t try to write or read into non allocated memory.</p>
</li>
</ul>
<p>So this is basically the steps that the OS provides every time you run your executable. But what happens when you can&#39;t have them? Say for process injection? When you inject yourself inside a process, the OS is not here to do all these steps for you. Then you need a code that doesn&#39;t rely on all these steps, hence, position-independent code.</p>
<h3 id="hello-world-but-shellcode">Hello World!, but shellcode</h3><p>So, to get a position-independent code, now we know what we need. Let&#39;s explore each constraint to see how can we respect it.</p>
<h4 id="only-the-text-section">Only the .text section</h4><p>To avoid having to copy every section in memory, the goal is then to have only one section, the .text section containing our code. Now remember, this section is defined as <em>executable</em> and <em>readable</em>. Historically, strings were made into stack based strings so that they live only inside the .text section. But a new solution has emerged in recent years with linker script.</p>
<p>I believe it was <a href="https://kerkour.com/">Sylvain Kerkour</a> who first presented this method in his article <a href="https://kerkour.com/shellcode-in-rust">How to Write and Compile a Shellcode in Rust</a>, for writing shellcode in Rust for Linux. Later on, <a href="https://5pider.net/">C5pider</a> with its <a href="https://5pider.net/blog/2024/01/27/modern-shellcode-implant-design/">Stardust</a> also used the same method. The goal is to merge, thanks to a linker script, the <em>.rdata</em> section, where string literals resides, into the <em>.text</em> section, where your code resides. Since <em>.rdata</em> section is read-only, it poses no problem to include it inside the <em>.text</em> section, which is <em>readable</em> and <em>executable</em>. This allows you to have literal strings inside your executable, and with PC-relative access to it, it remains fully shellcode compatible.</p>
<p>Apart from that, we can&#39;t have .data section, this means no global or static variable is allowed in your code.</p>
<h4 id="no-standard-library">No standard library</h4><p>When you compile your code normally, either using C or Rust, a lot of extra code gets compiled into your binary. For example, your main function is not the real entry point of your binary. The real entry point is another function, automatically added that initialize tons of stuff before calling your own code.</p>
<p>This means you don&#39;t have any access to function you normally use right out of the box. We&#39;ll see how this translates in the next paragraph.</p>
<h4 id="no-imports-whatsoever">No imports whatsoever</h4><p>Because we can&#39;t use the standard library, we rely only on WINAPI calls. But we can&#39;t just call these functions directly, otherwise it will also end up in the import table inside <em>.idata</em> section.</p>
<p>We need to resolve dynamically each function we want to call. We can find the addresses of DLLs inside the process thanks to the PEB structure on Windows. Then, we can read export table from <em>.edata</em> section in the DLL to find the addresses of the function we need, and then call these functions with function pointer.</p>
<h4 id="no-relocations">No relocations</h4><p>With the two first constraint respected, you already avoided most of the use cases that would spin up relocations in your binary.</p>
<h2 id="the-position-independent-template">The position-independent template</h2><p>Let&#39;s check out the template now. What do you need to use Rust and compile your code in position-independent code? A proposition was already published in Rust by <a href="https://github.com/safedv">safedv</a> in <a href="https://github.com/safedv/Rustic64">Rustic64</a>. This template is mostly inspired by his proposition, with a few adjustments to new features of the Rust programming language.</p>
<h3 id="no-main-function">No main function</h3><p>The first thing is to remove the main function and the use of the standard library. We can do this in Rust with these two lines:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-meta">#![no_std]</span>
<span class="hljs-meta">#![no_main]</span></code></pre><p>The first tells the language we won&#39;t use the standard library. Therefore, we cannot import anything from std library. The second tells the language we don&#39;t have a main function per se. Of course, we will have an entry point, but it won&#39;t be called <em>main</em>.</p>
<p>The entry point looks like this:</p>
<pre class="code-block"><code class="hljs language-rust"><span class="hljs-meta">#[unsafe(no_mangle)]</span>
<span class="hljs-meta">#[unsafe(link_section = <span class="hljs-string">&quot;.text.entry&quot;</span>)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">align_stack</span>() <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span> {
    <span class="hljs-keyword">unsafe</span> {
        asm!(
            <span class="hljs-string">&quot;push rsi&quot;</span>,
            <span class="hljs-string">&quot;mov rsi, rsp&quot;</span>,
            <span class="hljs-string">&quot;and rsp, 0x0FFFFFFFFFFFFFFF0&quot;</span>,
            <span class="hljs-string">&quot;sub rsp, 0x020&quot;</span>,
            <span class="hljs-string">&quot;call ExecutePayload&quot;</span>,
            <span class="hljs-string">&quot;mov rsp, rsi&quot;</span>,
            <span class="hljs-string">&quot;pop rsi&quot;</span>
        );
    }
    <span class="hljs-number">0</span>
}</code></pre><p>There is a lot to say here.</p>
<h4 id="unsafenomangle">#[unsafe(no_mangle)]</h4><p>This line tells the compiler to not mangle the function name. Name mangling is a concept used in some programming languages. It does not exist in C for example, that&#39;s why you can&#39;t have two functions that have the same name. But in C++ or Rust, you can name two functions the same way, on two different classes or structures. The name mangling will <em>mangle</em> names to integrate the class or structure name inside the symbol name for the linker, to dissociate the function that have the same name.</p>
<p>But for our template, we need to tell the linker which function name to use for the entry point, therefore, we have to disable name mangling to ensure the function name will stay <em>align_stack</em> for the linker.</p>
<h4 id="unsafelinksection-textentry">#[unsafe(link_section = &quot;.text.entry&quot;)]</h4><p>This attribute tells the compiler to put this function into its own section, separated from all the code. When executing the shellcode in memory, we will just copy the content of the <em>.text</em> section and begin execution at the start of this section. Unfortunately, the Rust compiler might just choose to put the entry point in the middle of the <em>.text</em> section, which would break execution at its very beginning.</p>
<p>Later, in the linker script that we will also check out, we&#39;ll tell the linker to put this section first in the final <em>.text</em> section of the binary to ensure this function we&#39;ll be the starting bytes of the final <em>.text</em> section.</p>
<h4 id="inline-asm">Inline asm</h4><p>Then we use the <strong>asm!</strong> macro to inline a small asm procedure inside the function. This procedure was originally proposed by <a href="https://mattifestation.medium.com/">@mattitestation</a> in his <a href="https://github.com/mattifestation/PIC_Bindshell/blob/master/PIC_Bindshell/AdjustStack.asm">PIC_Bindshell</a> project. It just here to align the stack to a 16 bytes alignment. This is needed for x64 code on Windows because the OS assumes that the stack is 16 bytes aligned, and some WINAPI function might use XMM register, which needs the stack to be 16 bytes aligned. Without this small procedure, execution might crash upon calling such WINAPI function.</p>
<p>After aligning the stack, this entry point calls into a function I called <em>ExecutePayload</em>, which is the <em>real</em> entry point of the code, where the user&#39;s code will start.</p>
<h3 id="dynamic-resolution-of-winapi-function">Dynamic resolution of WINAPI function</h3><p>Like I said earlier, you cannot have any import inside your binary. So when you want to call a function, you have to get its address at runtime and call it thanks to a function pointer.</p>
<p>This is done through two functions: <strong>get_module_address</strong> and <strong>get_exported_symbol</strong>.</p>
<p>The first function, <strong>get_module_address</strong>, will try to retrieve the address of a DLL inside the RAM. On Windows, every process as its own virtual <em>RAM</em>. <strong>DLLs</strong> are loaded inside that virtual memory. The <em>Process Environment Block (PEB)</em> is a structure, available inside that virtual memory as well, that holds lots of information about the current process and notably a doubly linked-list of structures representing each DLL loaded inside the process.</p>
<p>In x64 Windows, the <em>gs</em> register holds a pointer toward the <em>Thread Environment Block (TEB)</em>, which is the same thing as <strong>PEB</strong> but for one thread specifically. At offset <strong>0x60</strong> of this <strong>TEB</strong>, there is a pointer towards the <strong>PEB</strong>. Then, it is simply about looping through the linked list until you find the DLL you want. In each structure representing a DLL, you have its name and its address inside the process, everything you need to find every DLL you might need.</p>
<p>If you can&#39;t find the DLL you&#39;re looking for, it means it is not loaded inside the process. You can specifically require its loading thanks to LoadLibrary function.</p>
<p>Then, <strong>get_exported_symbol</strong> simply reads through the <strong>export table</strong> of the DLL. The <strong>PE</strong> format tells you where this table is located. You just have to loop through it, find the function you seek, and get its address in this export table.</p>
<p>So, when you want to call a function, you first need to retrieve the DLL&#39;s address that contains your function, then find the function you&#39;re looking for in the export table of this DLL to find its address.</p>
<p>All the code for this can be found here: <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/runtime_resolve.rs">runtime_resolve.rs</a>.</p>
<h3 id="global-allocator">Global Allocator</h3><p>When I said you couldn&#39;t use any code from standard libraries, I lied. In Rust, the standard library is actually composed of the core library and the alloc library plus several modules unique to the std. The core library you can use right out of the box for shellcode compatibility, but the alloc crate is where structures allocated on the heap reside. And it needs a way to allocate on the heap.</p>
<p>Normally, this should be done with the std crate, but as we can&#39;t use it for our use case, we need to redefine it. Since Rust comes with great modularity, we can redefine the global allocator that will be used for every heap-allocated structure inside the alloc crate, and we can make it shellcode compatible. Therefore, we can use things like Vec, String, etc... Inside our position-independent code.</p>
<p>All the code can be found here: <a href="https://github.com/LostAquilae/RustyShell/blob/main/crates/RustyShell/src/allocator.rs">allocator.rs</a>. We simply use the <em>get_module_address</em> and <em>get_exported_symbol</em> function to dynamically call into <strong>HeapAlloc</strong>, <strong>HeapFree</strong> and <strong>HeapReAlloc</strong>.</p>
<h3 id="compilation-flags">Compilation flags</h3><p>For this template to compile as position-independent code, we also need to pass some specific compilation flag. Important to note that you need Rust nightly to compile properly this template, since it relies on features and compilation flags that are only available on nightly. Let&#39;s understand each of these flags.</p>
<ul>
<li><p><strong>-Z unstable-options</strong>: This flag is to enable unstable flags that exist in the Rust compiler. It simply allows us to use other flags.</p>
</li>
<li><p><strong>-C panic=immediate-abort</strong>: This is actually a new panic method, where you can simply say you want to interrupt execution upon panicking. Before, you had to propose your own function to define the panicking behavior. With this flag, you don&#39;t have to anymore if you just want to abort execution.</p>
</li>
<li><p><strong>-C link-arg=-nostdlib</strong>: This flag tells the linker to not include any standard library. Like we said before, when compiling your program, a lot of code you didn&#39;t write gets included in the final binary, serving several purposes. But we don&#39;t want any of this for shellcode compatibility, so we ask the linker to not include any of this.</p>
</li>
<li><p><strong>-C codegen-units=512</strong>: This flag is a bit more complicated. When your code is transformed by the compiler, it will be transformed into object files. This flag controls how many <em>units</em> the code will be split into. For example, when compiling the core library, setting this to 1 will make one big object file with everything from the core library in it. Depending on this static library means that all the code from the core library will end up in your binary. Putting a great number, like 512, means splitting the core library into 512 different <em>units</em>, meaning you will end up with 512 object files. Depending on this library, the linker will only include the object files containing the symbols you really need. So why do we put a great number here? Because it allows us to have a small binary in the end. The more <em>units</em> you have, the more fine-grained control the linker have to only include what is of interest to you, reducing the size of your binary. When we are talking about payloads, we want them as small as possible. For this template, we are recompiling the core, alloc, and compiler_builtins crate. We put 512 to have the maximum codegen <em>units</em> on each of these crates. This depends on the crate size, a crate has a maximum number of splitting it can take.</p>
</li>
<li><p><strong>-C link-arg=-Wl,-T./Linker.ld</strong>: This flag just tells the Rust compiler to include a linker script at the linking stage. We are using the target triple <em>x86_64-pc-windows-gnu</em>, which relies on mingw for linking. We will take a look at the linker script later in this blog.</p>
</li>
<li><p><strong>-C strip=symbols</strong>: This is equivalent to -s for the gnu linker <em>ld</em>. It tells the linker to remove debugging info and the symbol table from the executable. These are not needed for correct execution of the binary, it&#39;s just metadata we can remove.</p>
</li>
<li><p><strong>-C lto=yes</strong>: This enables <em>link time optimization</em>. You might think this is about the mingw linker, but it is actually for LLVM. LLVM is a compiler backend for many languages. It provides an <em>intermediate language</em> frontend compiler will turn your code into. Then, LLVM takes care of optimizations on this intermediate representation. This flag tells LLVM to enable lto for your build. If you want to learn more about lto: <a href="https://convolv.es/guides/lto/">Link-time optimization (LTO)</a>. For now, you just need to understand that it allows us to better optimize the binary in terms of size and execution speed.</p>
</li>
<li><p><strong>-C embed-bitcode=yes</strong>: It tells the rust compiler to embedded LLVM bitcode inside the object files. It is needed for <strong>fat lto</strong>, which is enabled with the previous flag.</p>
</li>
<li><p><strong>-C opt-level=s</strong>: This flag controls the optimization to be used. -s value specifically enable optimizations for binary size.</p>
</li>
<li><p><strong>-Z build-std=core,alloc,compiler_builtins</strong>: This flag tells the compiler to rebuild the core, alloc and compiler_builtins crate. This is needed, since we enabled the panic immediate-abort panicking behavior, which is not the already precompiled behavior standards crate are shipped with, hence, the need to recompile them.</p>
</li>
<li><p><strong>-Z build-std-features=compiler-builtins-mem</strong>: This allows the compiler_builtins crate to propose standard builtins memory function, so that we don&#39;t have to redefine them. <a href="https://github.com/safedv/Rustic64">Rustic64</a> for example, has to define these function <a href="https://github.com/safedv/Rustic64/blob/master/src/nocrt.rs">here</a>. They are needed for all kind of things, and your linker might tell you it misses a definition for it. When working with C style null terminated string, your rust code might try to call strlen for example, and you would have to define it yourself, since we ask for no std lib. But enabling this flag allows the compiler_builtins crate to propose out of the box an implementation for these kinds of functions, so you don&#39;t have to write your own implementation.</p>
</li>
</ul>
<p>These are all the compiler flags we need to get a position-independent executable. All these compilation flag documentation is available <a href="https://doc.rust-lang.org/rustc/codegen-options/index.html">here</a>, if you want to go deeper into it.</p>
<h3 id="linker-script">Linker Script</h3><p>As we have seen earlier, we pass a linker script to the linker. Here it is:</p>
<pre class="code-block"><code class="hljs language-ld"><span class="hljs-comment">/* Simple ENTRY statement to put the entry point toward the align_stack routine */</span>
<span class="hljs-function">ENTRY</span>(align_stack);

<span class="hljs-keyword">SECTIONS</span>
{
    <span class="hljs-comment">/* Grouping code and read only data, starting with .text.entry which contains the small align_stack procedure, which is the real entry point of the shellcode */</span>
    <span class="hljs-title">.text</span>  <span class="hljs-variable">__image_base__</span> <span class="hljs-operator">+</span> ( <span class="hljs-variable">__section_alignment__</span> <span class="hljs-operator">&lt;</span> <span class="hljs-number">0x1000</span> <span class="hljs-operator">?</span> . <span class="hljs-operator">:</span> <span class="hljs-variable">__section_alignment__</span> ) <span class="hljs-operator">:</span>
    {
        *(<span class="hljs-title">.text.entry</span>)
        *(<span class="hljs-title">.text</span>)
        *(<span class="hljs-title">.rdata</span>*)
    }

    <span class="hljs-comment">/* Discarding the pdata and xdata sections, which are not needed in the final binary */</span>
    <span class="hljs-meta">/DISCARD/</span> <span class="hljs-operator">:</span>
    {
        *(<span class="hljs-title">.pdata</span>)
        *(<span class="hljs-title">.xdata</span>)
    }
}</code></pre><p>The first line <strong>ENTRY(align_stack)</strong> is here to define the function named align_stack to be the entry point of the binary. This is why we had to disable name mangling on the function, so that the linker can find it later with this directive.</p>
<p>Then, we define the .text section in the final binary to be:</p>
<ul>
<li><p><strong>*(.text.entry)</strong>: First, the section containing only the align_stack function. This is here to ensure the first bytes of the .text section are in fact the align_stack function, to make sure the entry point is at the start of the section, because when injecting the shellcode, execution will start at the beginning of the binary.</p>
</li>
<li><p><strong>*(.text)</strong>: Then, simply the rest of the code.</p>
</li>
<li><p><strong>*(.rdata*)</strong>: Finally, we put the read only section which contains our read only data. This won&#39;t generate relocations since the code and the rdata will be in the same section, the code will use relative addresses to interact with the read only data.</p>
</li>
</ul>
<p>To finish, we just discard sections that are not essential, here <strong>.pdata</strong> and <strong>.xdata</strong>, which contains metadata about exceptions, to correctly handle errors for every function of your code. Since we want panic immediate abort behavior anyway to leave the least traces possible, we don&#39;t really need these sections.</p>
<h2 id="conclusion">Conclusion</h2><p>So this here is everything we need to make a position-independent Rust code. This template is not really new, as we have seen that <a href="https://github.com/safedv/Rustic64">Rustic64</a> already proposes the same features. There are minor improvement to what <a href="https://github.com/safedv">safedv</a> did like the panic immediate abort behavior or the standard function directly proposed by the compiler_builtins crate. In the two blog posts that will follow, we will focus on what RustyShell really brings to the table.</p>
<h2 id="credits">Credits</h2><p><a href="https://kerkour.com/shellcode-in-rust">How to Write and Compile a Shellcode in Rust</a> by <a href="https://kerkour.com/">Sylvain Kerkour</a></p>
<p><a href="https://github.com/Cracked5pider/Stardust">Stardust</a> by <a href="https://5pider.net/">C5pider</a></p>
<p><a href="https://github.com/safedv/Rustic64/tree/master">Rustic64</a> by <a href="https://github.com/safedv">safedv</a></p>
<p><a href="https://github.com/mattifestation/PIC_Bindshell/blob/master/PIC_Bindshell/AdjustStack.asm">PIC_Bindshell</a> by <a href="https://mattifestation.medium.com/">@mattitestation</a></p>
<h2 id="sources">Sources</h2><p><a href="https://convolv.es/guides/lto/">Link-time optimization (LTO)</a></p>
<p><a href="https://doc.rust-lang.org/rustc/codegen-options/index.html">Rust Codegen Options</a></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
