Module 2: Modern C++ for Shellcode
How Stardust abuses modern C++ features for compile-time evasion.
Wait, C++ for Shellcode?
Most shellcode is written in C or pure assembly. Using C++ sounds insane — classes, templates, virtual tables, exceptions... all that overhead. But Stardust carefully uses only the zero-cost features of modern C++. The compiler does heavy lifting at build time so the runtime binary stays tiny.
The Three Modern C++ Features That Matter
1. constexpr / consteval — Forced Compile-Time Execution
constexpr (C++14+) tells the compiler a function may run at compile time. consteval (C++20) forces it — the result must be computed at compile time. Stardust uses these features so that strings like API names are hashed during compilation and never appear in the binary:
C++ - constexpr.hnamespace expr {
template <typename T = char>
constexpr auto hash_string( // constexpr = CAN run at compile time
const T* string // consteval would FORCE compile time (C++20)
) -> uint32_t {
uint32_t hash = 5381; // DJB2 initial value
uint8_t byte = 0;
while ( *string ) {
byte = static_cast<uint8_t>( *string++ );
if ( byte >= 'a' ) byte -= 0x20; // Case-insensitive (uppercase)
hash = ( ( hash << 5 ) + hash ) + byte; // hash * 33 + byte
}
return hash;
}
}
What Happens at Build Time
What you writeauto hash = expr::hash_string( "NtAllocateVirtualMemory" );
What the compiler produces (pseudocode)auto hash = 0x1A2B3C4D; // Just a constant integer. No string. No function call.
The string "NtAllocateVirtualMemory" exists only in your source code. It never makes it into the binary. Defenders running strings on the shellcode see nothing.
constexpr vs consteval
constexpr functions can be evaluated at compile time but may also run at runtime if called with non-constant arguments. consteval (C++20) must be evaluated at compile time — the compiler errors if it cannot. When used with constant string literals, constexpr is typically sufficient since the compiler can and does resolve it at compile time. Whether Stardust uses constexpr or consteval may depend on the version or fork, but the effect is the same: hashes are baked into the binary as constants.
2. Templates — Type-Safe API Resolution
Stardust uses templates to make API resolution type-safe. Instead of casting void* everywhere:
C++ - resolve.hnamespace resolve {
template <typename T>
inline auto declfn api(
_In_ const uintptr_t module_base,
_In_ const uintptr_t symbol_hash
) -> T* {
return reinterpret_cast<T*>( _api( module_base, symbol_hash ) );
}
}
// Usage: compiler knows the exact function signature
decltype(MessageBoxA)* msgbox = RESOLVE_API(
reinterpret_cast<uintptr_t>(user32), MessageBoxA
);
3. decltype — Automatic Type Deduction
C++ - macros.h#define D_API( x ) decltype( x ) * x;
// Inside the instance class:
struct {
D_API( LoadLibraryA ) // expands to: decltype(LoadLibraryA)* LoadLibraryA;
D_API( GetProcAddress ) // expands to: decltype(GetProcAddress)* GetProcAddress;
};
decltype(LoadLibraryA) gives the compiler the exact function signature of the real Windows API. Your function pointer is automatically the right type. No manual typedef needed.
What C++ Features Stardust AVOIDS
| Feature | Why It's Banned |
|---|---|
Exceptions (try/catch) | Requires runtime support tables, huge overhead |
RTTI (dynamic_cast) | Type info tables bloat the binary |
| STL containers | Depend on the C++ standard library (heap, exceptions) |
| Virtual functions | vtable pointers aren't position-independent |
| Global constructors | Require CRT initialization that doesn't exist |
| SSE instructions | Disabled via -mno-sse for compatibility |
The -nostdlib Flag
Stardust compiles with -nostdlib, meaning there is no C runtime, no C++ standard library. No printf, no malloc, no std::string, no new/delete. Every memory operation is done manually via memory::copy(), memory::zero(), or Windows API calls.
Pop Quiz: Modern C++ Features
Q1: What is the difference between constexpr and consteval?
Q2: Why does Stardust use decltype(LoadLibraryA)* LoadLibraryA;?