Component Structure
Stardust is built around a centralinstance class that serves as the main execution context for the shellcode. This class encapsulates all module handles and API function pointers needed during execution.
Instance Class
Theinstance class (defined in include/common.h:40-79) contains:
base: Stores the shellcode’s runtime address and total sizekernel32: Module handle and function pointers for kernel32.dll APIsntdll: Module handle and function pointers for ntdll.dll APIs- Each module structure contains a
handle(base address) followed by nested structs for API pointers
D_API Macro
TheD_API macro (from include/macros.h:4) simplifies API pointer declarations:
D_API(LoadLibraryA) expands to:
Entry Point Flow
The execution flow follows a carefully orchestrated chain from assembly to C++:1. Assembly Entry Point (.text$A)
For x64 (src/asm/entry.x64.asm:10-18):
src/asm/entry.x86.asm:10-16):
- Preserve the calling convention
- Align the stack (x64 requires 16-byte alignment for Windows ABI)
- Allocate shadow space on x64 (required by Windows x64 calling convention)
- Call the C++
entryfunction
2. C++ Entry Function
Theentry function (src/main.cc:7-12) creates and starts the instance:
instance object and immediately calls its start() method.
3. Instance Constructor
The constructor (src/main.cc:14-40) initializes the shellcode:
- Calculate shellcode boundaries using
RipStart()andRipData() - Find module base addresses by walking the PEB
- Resolve all API function pointers from the export tables
4. Start Method
Thestart() method (src/main.cc:42-62) contains the actual shellcode payload:
Section Organization
Stardust uses custom linker sections to control memory layout. The linker script (scripts/linker.ld:1-11) defines the order:
Section Layout
declfn Attribute
Thedeclfn macro (include/macros.h:6) ensures functions are placed in .text$B:
declfn are grouped together, making the shellcode layout predictable and enabling accurate size calculation.
Memory Layout and RIP-Relative Addressing
Why RIP-Relative?
Shellcode runs at unknown memory addresses. Traditional absolute addressing breaks when code is relocated. RIP-relative addressing solves this by computing addresses relative to the instruction pointer.RipStart Function
Returns the base address of the shellcode (src/asm/entry.x64.asm:20-27):
call RipPtrpushes the return address onto the stackmov rax, [rsp]reads that return addresssub rax, 0x1bsubtracts the offset to get the start ofstardust- Returns the shellcode base address
RipData Function
Returns the address of the data section (src/asm/utils.x64.asm:7-15):
.text$C so it returns the address right before the data section begins.
Size Calculation
The shellcode size is computed as:END_OFFSET accounts for the small .text$C section size.
Adding New APIs
To add a new API to your shellcode instance:1. Update the Instance Class
Ininclude/common.h, add the API to the appropriate module structure:
2. The Constructor Auto-Resolves
TheRESOLVE_IMPORT macro (include/macros.h:8-12) automatically resolves all APIs:
- Iterates through each field in the module structure
- Treats the stored hash as a lookup key
- Resolves the actual function address
- Replaces the hash with the function pointer
