Shroudware
Shroudware is a compile-time obfuscation library implemented entirely in the C preprocessor.
Presentation
When a compiled binary is loaded into a disassembler (IDA Pro, Ghidra, Binary Ninja), the analyst’s first actions are predictable:
- Open the strings window (Shift+F12 in IDA)
- Review the import table
- Search for known constants (0x40, 0x3000, etc.)
These three data points provide immediate context. String references
reveal file paths, registry keys, error messages, and API names. The
import table shows every DLL and function the binary calls. Known
constants like PAGE_EXECUTE_READWRITE (0x40) or MEM_COMMIT (0x1000)
betray the program’s intent without reading a single instruction.
Most obfuscation tooling addresses this at the compiler level (LLVM passes like OLLVM, Hikari) or through post-build transformation (packers, crypters, VMProtect). These work, but they introduce toolchain dependencies, increase build complexity, and often require specific compiler versions or commercial licenses.
Shroudware operates entirely within the C preprocessor. The compiler’s own constant folding pass performs the encryption. No plugins, no post-build steps, no runtime unpacking stubs.
How To Use
Extensive usage is documented in demo.c.
// Include the header
#include "shroudware.h"
// Example usage also provided in the repository
char* secret = OBFSTR("This is a secret message!"); // This string will not appear in the .rdata section
char* unencrypted = "This string is not obfuscated."; // This string will appear in the .rdata section
// How to import modules and functions
typedef LONG (NTAPI *NtQIP_t)(HANDLE, ULONG, PVOID, ULONG, PULONG); // type defintion
NtQIP_t pNtQIP = (NtQIP_t)OBF_IMPORT("ntdll.dll", "NtQueryInformationProcess"); // Long function names are handled automatically
// The value of PAGE_EXECUTE_READWRITE (0x40) will never appear in the .rdata section
unsigned int page_rwx = OBF_CONST(PAGE_EXECUTE_READWRITE); Features
- String Encryption — Encrypts strings at compile time
- Compile Time Hashing — Hashes function names at compile time
- IAT Hiding — Hides IAT entries by walking the PEB manually
- Value Obfuscation - Masks integer values (ex. MEM_COMMIT)