https://github.com/00nx/c-string-obfuscator
A lightweight, header-only C++ utility for compile-time string obfuscation using XOR encryption.
https://github.com/00nx/c-string-obfuscator
c cpp dynamic encryption header obfuscator runtime-decryption string windefender windows xor xor-encryption
Last synced: 4 days ago
JSON representation
A lightweight, header-only C++ utility for compile-time string obfuscation using XOR encryption.
- Host: GitHub
- URL: https://github.com/00nx/c-string-obfuscator
- Owner: 00nx
- License: mit
- Created: 2025-07-23T20:23:55.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2026-01-29T14:20:42.000Z (5 months ago)
- Last Synced: 2026-01-30T03:45:46.057Z (5 months ago)
- Topics: c, cpp, dynamic, encryption, header, obfuscator, runtime-decryption, string, windefender, windows, xor, xor-encryption
- Language: C++
- Homepage:
- Size: 29.3 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
Awesome Lists containing this project
README
# C++ Compile-Time String Obfuscator (Rolling XOR)
**Lightweight • Header-only • Hard-to-grep • Anti-static-analysis friendly**
[](https://isocpp.org/std/the-standard)
[](#)
[](#)
## Features
- Compile-time XOR obfuscation of string literals (`char[]` & `wchar_t[]`)
- **Rolling XOR key stream** — different key byte per position (16-byte cycle by default)
- Strong compile-time seed mixing (inspired by splitmix64 / wyhash)
- Lazy decryption (happens only when you first access the string)
- **Automatic zeroization** on scope exit (via RAII helper)
- Very simple & clean macro syntax
- Header-only — drop-in single file integration
- No external dependencies, no dynamic allocation
## Why better than plain single-byte XOR?
| Feature | Classic single XOR | This version (rolling) |
|-----------------------------|--------------------|-----------------------------|
| Key per string | 1 byte | 16-byte cycling stream |
| Static analysis resistance | Low | Significantly higher |
| Easy to grep / pattern match| Very easy | Much harder |
| Key derivation | Weak shifts/mul | Strong mixer (wyhash-like) |
| Memory cleanup | Manual | Automatic RAII |
## Quick Start
```cpp
#include "obfuscator.h"
int main()
{
// Most common & safest way — decrypt + auto-zeroize when leaving scope
const char* msg = OBF_AUTO("kernel32.dll");
// One-time decrypt (stays decrypted until program ends or zeroized manually)
const char* api = OBF("CreateRemoteThread");
// Wide string versions
const wchar_t* wpath = OBF_W_AUTO(L"C:\\Windows\\Temp\\payload.bin");
wprintf(L"Path: %s\n", wpath);
printf("API name: %s\n", api);
// You can also use it inside functions, conditions, etc.
if (some_condition) {
MessageBoxA(NULL, OBF_AUTO("Critical error!"), OBF("Error"), MB_ICONERROR);
}
}
```
## Available Macros
| Macro | Behavior | Recommended? | Lifetime of decrypted data |
|--------------------|----------------------------------------------------|--------------|-------------------------------------|
| `OBF("...")` | Lazy decrypt, stays decrypted in memory forever | Sometimes | Until program ends |
| `OBF_AUTO("...")` | Decrypt + **auto zeroize** when leaving scope | **Yes** | Only while in current scope |
| `OBF_W("L...")` | Wide version — lazy decrypt | Sometimes | Until program ends |
| `OBF_W_AUTO("L...")`| Wide version + **auto zeroize** on scope exit | **Yes** | Only while in current scope |
**Recommendation**: Use `OBF_AUTO` / `OBF_W_AUTO` in most cases — it's significantly safer as it minimizes the time sensitive strings remain in plaintext in memory.
## Performance Overview
All numbers are approximate, measured on modern hardware (Zen 4 / Intel 13th–14th gen, 2024–2025 compilers) with `-O3 -march=native`.
| Scenario | Plain literal (ns) | This lib (ns) | Vectorized xorstr-style (ns) | Overhead factor | Notes |
|-----------------------------------|--------------------|-------------------|------------------------------|-----------------|-------|
| Short string (~8–16 chars), cold | 0.5–2 | 10–25 | 4–12 | ~10–20× | First decrypt dominates |
| Short string, hot (cached) | 0.5–2 | ~1–3 | ~1–2 | ~1–3× | After decrypt(), near-zero cost |
| Medium (~32–64 chars), cold | 1–4 | 15–45 | 5–15 | ~5–15× | Modulo can limit auto-vectorization |
| Medium, hot | 1–4 | ~2–6 | ~1–3 | ~1–3× | |
| Long (~128+ chars), cold | 3–10 | 40–120 | 8–30 | ~5–15× | Vectorized wins big here |
| 10,000 calls/sec (typical logging)| negligible | <0.5% total | <0.2% total | — | Real apps: IO / crypto dominates |
| In tight loop (1M iter/sec) | — | 1–5% slowdown | <1% slowdown | — | Only if decrypt every iteration |