Module 3: Project Anatomy
Every file, every folder, and why it exists.
Directory Structure
File TreeStardust/
├── Makefile # Build system (MinGW cross-compilation)
├── CMakeLists.txt # Alt build for development/debugging
├── README.md
├── bin/
│ └── obj/ # Compiled object files
├── include/
│ ├── common.h # Master header, instance class, symbol<T>
│ ├── constexpr.h # Compile-time DJB2 hashing
│ ├── macros.h # D_API, G_SYM, RESOLVE_IMPORT macros
│ ├── memory.h # zero(), copy(), compare()
│ ├── native.h # Windows NT structures (22,000+ lines)
│ └── resolve.h # API/module resolution declarations
├── scripts/
│ └── linker.ld # Custom section ordering
├── src/
│ ├── main.cc # Entry point + implant logic
│ ├── resolve.cc # PEB walking + export table parsing
│ └── asm/
│ ├── entry.x64.asm # x64 entry point + RipStart
│ ├── entry.x86.asm # x86 entry point + RipStart
│ ├── utils.x64.asm # x64 RipData (data section locator)
│ └── utils.x86.asm # x86 RipData
├── test/
│ └── stomper.cc # Module stomping injection tester
└── static/
└── *.png # Demo screenshots
File Dependency Graph
How Files Connect
main.cc — includes common.h, constexpr.h, resolve.h
↓ calls entry() from
entry.x64.asm / entry.x86.asm — stardust() entry, RipStart()
↓ main.cc uses APIs resolved by
resolve.cc — module() walks PEB, _api() walks exports
↓ string addresses found via
utils.x64.asm / utils.x86.asm — RipData()
↓ all section ordering controlled by
scripts/linker.ld — .text$A, .text$B, .rdata, .text$C
Build Pipeline
The Makefile orchestrates a four-step build process using a MinGW cross-compilation toolchain:
Makefile - Build Steps (Simplified)# 1. Assemble entry points and utilities for both architectures
# (The assembler used depends on the toolchain - may be GAS or NASM)
assemble src/asm/entry.x64.asm -> bin/obj/entry.x64.obj
assemble src/asm/utils.x64.asm -> bin/obj/utils.x64.obj
# 2. Compile C++ sources with MinGW cross-compilation toolchain
compile src/main.cc -> bin/obj/main.x64.obj $CFLAGS
compile src/resolve.cc -> bin/obj/resolve.x64.obj $CFLAGS
# 3. Link everything with custom linker script
link bin/obj/*.x64.obj -> bin/stardust.x64.exe -Tscripts/linker.ld
# 4. Extract .text section as raw shellcode
objcopy --dump-section .text=bin/stardust.x64.bin bin/stardust.x64.exe
# 5. Delete the intermediate PE (we only need the .bin)
rm bin/stardust.x64.exe
Key Compiler Flags
| Flag | Purpose |
|---|---|
-target x86_64-w64-mingw32 | Cross-compile for Windows x64 via MinGW |
-Os | Optimize for size |
-nostdlib | No C/C++ standard library |
-fno-exceptions | Disable C++ exceptions |
-fPIC | Position-independent code |
-masm=intel | Use Intel assembly syntax |
-mno-sse | Disable SSE instructions for compatibility |
-fms-extensions | Microsoft extensions (SAL annotations, etc.) |
-Wl,-Tscripts/linker.ld | Custom linker script for section layout |
MinGW Toolchain Note
Both Stardust and AceLdr use MinGW-based cross-compilation toolchains to produce Windows PE binaries from a Linux build environment. AceLdr uses x86_64-w64-mingw32-gcc (GCC-based). Stardust also targets x86_64-w64-mingw32 but the exact compiler frontend (Clang vs GCC) may vary by version or fork. The key difference is the linker script and objcopy extraction step that turns the PE into raw shellcode.
Pop Quiz: Project Structure
Q1: How is the final shellcode extracted from the compiled PE?
Stardust uses
objcopy --dump-section .text=output.bin to extract just the .text section from the compiled PE. This is different from AceLdr which used a Python script with an "ACELDR" end marker. The objcopy approach is cleaner since the linker script already ensures everything needed is in .text.Q2: How many C++ source files does Stardust have?
Stardust is remarkably minimal: just main.cc (entry point + implant logic) and resolve.cc (PEB walking + export parsing). Everything else is in headers or assembly files. This minimalism is key to the tiny binary size.