MCPcopy Index your code
hub / github.com/Irate-Walrus/stardust-rs

github.com/Irate-Walrus/stardust-rs @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
75 symbols 119 edges 21 files 14 documented · 19%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Rust Position Independent Shellcode (PIC) Template for i686 & x86_64 Linux & Windows

[!warning] This is an experiment and I can personally guarantee it is unsafe. I describe below some of the unobvious (to me) issues I ended up facing. I'm keen to hear of any possible workarounds for these issues, just open a PR.

This code is based on the following previous work:

  • https://bruteratel.com/research/feature-update/2021/01/30/OBJEXEC/
  • https://5pider.net/blog/2024/01/27/modern-shellcode-implant-design/
  • https://github.com/wumb0/rust_bof
  • https://kerkour.com/rust-position-independent-shellcode
  • https://github.com/safedv/Rustic64

Some github and rust-lang issues from the journey, thank you friends!:

The following targets are supported.

Target Payload Size
i686-linux 4141B
x86_64-linux 4167B
i686-windows 4141B
x86_64-windows 4120B

[!NOTE] Last tested with rustc 1.97.0-nightly (20de910db 2026-05-02) and cargo 1.97.0-nightly (4f9b52075 2026-05-01)

The following dependencies must be installed:

cargo install --force cargo-make

# Cross-compiler for windows
sudo apt-get install mingw-w64

# Cross-compiler for i686 linux
sudo apt-get install gcc-multilib

# Linux rustup targets
rustup target add i686-unknown-linux-gnu
rustup target add x86_64-unknown-linux-gnu

# Windows rustup targets
rustup target add i686-pc-windows-gnu
rustup target add x86_64-pc-windows-gn

Or you can build in the provided docker environment:

docker run --rm -it -v .:/build stardust-rs bash

To build one of these targets use cargo make -p $target build

Following is the current output of cargo make -p x86_64-linux run:

***     [LOADER]        ***
[*] Allocate RW Memory
[*] Copy Shellcode Into RW Memory
[*] Set Memory RX
[*] Allocation Start Address:   0x700000000000
[*] Allocation End Address:     0x700000001047
[*] Allocation Size:            4167B

***     [STARDUST x86_64]       ***
[*] Hello Stardust!
[*] Stardust Start Address:     0x700000000000
[*] Stardust Length:            4167
[*] Stardust Instance:          0x7f785645f000
[*] Hitting Breakpoint!

Problem #1 - format! macro e.g. &'static &str

Using the alloc::fmt::format! macro will result in a segementation fault due to absolute addresses to reference the pieces in Arguments { pieces, fmt: None, args }.

This results in the if !piece.is_empty() check failing within the following code @ https://github.com/rust-lang/rust/blob/master/library/core/src/fmt/mod.rs:

/* core::fmt::write () at core/src/fmt/mod.rs:1179 */
/* 1172 */ match args.fmt {
/* 1173 */     None => {
/* 1174 */         // We can use default formatting parameters for all arguments.
/* 1175 */         for (i, arg) in args.args.iter().enumerate() {
/* 1176 */             // SAFETY: args.args and args.pieces come from the same Arguments,
/* 1177 */             // which guarantees the indexes are always within bounds.
/* 1178 */             let piece = unsafe { args.pieces.get_unchecked(i) };
/* 1179 */             if !piece.is_empty() { // This is the check currently failing
/* 1180 */                 formatter.buf.write_str(*piece)?;
/* 1181 */             }
/* 1182 */
/* 1183 */             // SAFETY: There are no formatting parameters and hence no
/* 1184 */             // count arguments.
/* 1185 */             unsafe {
/* 1186 */                 arg.fmt(&mut formatter)?;
/* 1187 */             }
/* 1188 */             idx += 1;
/* 1189 */         }
/* 1190 */     }

This leads to a call being made to _gcc_except_table which has been removed by linux.ld resulting in a segmentation fault.

[!note] Patching the GOT appeared to get us a little further along before it crashes. YAY!🥳

Solution: None

(Solved) Problem #2 - Global Offset Table (GOT)

A bunch of stuff uses the GOT including calls to functions with the compiler_builtins crate, e.g. the following functions:

  • memcpy
  • memmove
  • memset
  • memcmp
  • bcmp

This resulted in a segmentation fault due to a call made to a bad/absolute hard-coded memory address stored within the GOT and then referenced by a RIP-relative offset.

Similarly using extern "C" functions directly may result in the use the GOT.

The following code was used to ensure memcpy was required by the binary:

let src = alloc::string::String::from("SSECCUS\t\t:ypcmem gnitseT");
let dst: String = src.chars().rev().collect();
info!(&dst);

Patching the hardcoded addresses with GDB results in a successful execution as seen below:

Patching memcpy address in GOT with GDB

Solution:

  • Patch the GOT dynamically during runtime, this appears to allow the use of compiler_builtins!
  • Don't directly call extern functions before patching, call them within asm! macro

(Solved) Problem #3 - i686 Windows and -fPIC

You're best off reading this (or maybe you're not, won't get that time back) i686-w64-mingw32-gcc and relative data addressing (PIC).

Solution:

  • Compile an i686 elf
  • Patch the GOT dynamically during runtime
  • Specify stdcall where required.
  • "It's all just machine code in the end" - Me while justifying this mess

The short summary is that, in Rust, the compiler emits GOT references implicitly for static data and compiler_builtins — you cannot opt out without either going fully GOT-free (no statics, no compiler_builtins functions). In this case fighting the compiler's codegen is harder than patching the GOT at runtime.

Core symbols most depended-on inside this repo

resolve_function
called by 10
stardust/src/stcore/os/windows/resolve.rs
handle
called by 4
stardust/src/stcore/os/windows/allocator.rs
data_offset
called by 2
stardust/src/stcore/mod.rs
got_offset
called by 2
stardust/src/stcore/mod.rs
epilogue_offset
called by 2
stardust/src/stcore/mod.rs
rip_start
called by 2
stardust/src/stcore/arch/i686/mod.rs
rip_end
called by 2
stardust/src/stcore/arch/i686/mod.rs
resolve_module
called by 2
stardust/src/stcore/os/windows/resolve.rs

Shape

Function 41
Method 24
Class 10

Languages

Rust99%
Python1%

Modules by API surface

stardust/src/stcore/os/windows/ntdll.rs9 symbols
stardust/src/stcore/os/windows/allocator.rs8 symbols
stardust/src/stcore/os/linux/resolve.rs8 symbols
stardust/src/stcore/os/windows/resolve.rs5 symbols
stardust/src/stcore/os/windows/kernel32.rs5 symbols
stardust/src/stcore/os/linux/mod.rs5 symbols
stardust/src/stcore/instance.rs5 symbols
stardust/src/stcore/os/linux/allocator.rs4 symbols
stardust/src/main.rs4 symbols
stardust/src/stcore/os/windows/mod.rs3 symbols
stardust/src/stcore/mod.rs3 symbols
runner/src/main.rs3 symbols

For agents

$ claude mcp add stardust-rs \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact