Library / SDK
ac3ss0r/obfusheader.h avatar
ac3ss0r/obfusheader.h

obfusheader.h: a header-only C++14 compile-time obfuscator

Obfusheader.h is a portable header file for C++14 compile-time obfuscation.

1,059 stars122 forksC++Apache-2.0

At a glance

What is it?
obfusheader.h is a self-contained C++14 header that obfuscates strings, integers, control flow, calls and imports entirely at compile time, leaving ciphertext in the binary and no runtime configuration. It targets g++, clang and Visual C++ on x86, x64, ARM64 and ARM32.
Who is it for?
Adopt obfusheader.h if you ship native binaries and want compile-time string and control-flow obfuscation without a runtime library or build tool. Skip it if you need protection for managed code or a supported commercial product with guarantees.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What obfusheader.h is built to protect

obfusheader.h is a header-only library for C++14 and above that performs obfuscation through metaprogramming at compile time: string and decimal encryption, control-flow mutation, call hiding, and import hiding. It is self-contained, compatible with Windows and Linux, and supports g++, clang, and Visual C++ on x86, x64, ARM64, and ARM32. Everything is decided while compiling, so the binary carries ciphertext and no plaintext, and there is nothing to run or configure at runtime.

The intended audience is developers shipping native binaries who want to raise the cost of reverse engineering without adding a runtime dependency or a separate build step. Because the library is a single header, it can be dropped into an existing project and controlled through preprocessor macros. The header is organized into 15 numbered sections, one per protection, so each part can be read, ported, or switched off on its own.

How the obfuscation mechanisms work

Constant encryption covers strings, chars, and any integer type through a universal macro that deduces the element type and length from its argument. The logic of the program is not affected: original values are restored at runtime and are never present in the binary. Only compile-time constants can be encrypted, meaning a literal or a constexpr object with static storage.

Three layers make each string hard to find. The bytes are ciphertext produced by a hash of key and index rather than key plus index, so a long string does not walk into the next key byte. The xor is picked per site out of five algebraic identities, so two identical literals do not decrypt through the same code. The payload lives in a table of VAULT_SLOTS same-sized rows where only one is real, and the row is selected through a masked index recovered by a call that cannot be inlined. Neither the instruction nor the static layout points at the string.

The vault adds an anti-tamper check: the compiler records a checksum of the original payload and the decryptor re-verifies it, so a patched binary hands out re-encrypted garbage instead of plaintext. Hidden references reach the payload through an obfuscated index, exactly like the hidden call tables.

Call hiding uses compile-time function pointer array shuffling with decoy stubs. Import hiding avoids GetProcAddress and dlsym: on Windows it walks the PEB, and on Linux it parses ELF program headers and symbol hash tables, with API names resolved as compile-time hashes. IMPORT_CACHE can store resolved imports XOR-masked in .data.

Control-flow mutation rewrites if, else, for, while, switch, and return with opaque predicates, and includes a mode that keeps all of it legal inside constexpr code. A polymorphic inline asm engine emits 12 executed and 6 unreachable trap blocks per architecture, combined randomly per expansion site. Purely compile-time randomness comes from macro constants such as __TIME__, __LINE__, and __COUNTER__ mixed through an avalanche hash, so no seed or timestamp survives in the binary.

Additional features include watermarking messages that survive any optimization and show up in decompilers, fake signatures that trick Detect-It-Easy and other detectors into reporting VMProtect, Themida, Denuvo and similar protectors, and fully inline implementations of common C string routines such as strcpy, strlen, strncat, strcmp, strncmp, strstr, and strtoul, so nothing hookable is imported.

Using obfusheader.h in a project

Add the include directory to your project and include the header. The README is explicit that the header must be included last.

cpp
#include "obfusheader.h"

Section 15 redefines the keywords if, else, for, while, switch, and return. A macro affects every line that comes after it, including any standard library or third-party header included afterwards. Put obfusheader.h below your other includes, or set CFLOW_BRANCHING to 0.

The if constexpr construct is never mutated: the macro is function-like, so it only fires when an opening parenthesis directly follows if.

Every option is guarded with #ifndef, so you never have to edit the file. Set options before the include, or per translation unit on the command line.

cpp
#define CFLOW_BRANCHING 0        // this file is full of constexpr code
#include "obfusheader.h"
bash
g++ -O2 -DCFLOW_ASM_JUNK=0 -DVAULT_SLOTS=6 main.cpp

Compile-time constant encryption is used through the OBF macro, which deduces the element type and length from its argument.

cpp
printf("char*: %s\n"
       "int (dec): %d\n"
       "long long: %lld\n"
       "int (hex): 0x%x\n"
       "boolean: %d\n",
       OBF("test"), OBF(123),
       OBF(9223372036854775807LL),
       OBF(0x123), OBF(true));

The README notes that bools are encrypted correctly on their own. A previous release needed #define true 1 and #define false 0 because the payload was stored in a bool and lost every key bit but the last while the decryptor tested for non-zero, so OBF(true) silently came back as false. The payload is now stored in an unsigned integer of the same width as its type, so no keywords are redefined anywhere in the header.

Configuration options and their defaults

The README documents options with defaults, appearing in the order of section 1. CONST_ENCRYPTION defaults to 1 and encrypts literals passed to OBF or MAKEOBF. CONST_ENCRYPT_MODE defaults to OBF_MODE_NORMAL, with OBF_MODE_THREADLOCAL as the alternative; the older NORMAL and THREADLOCAL spellings are still accepted. VAULT_SLOTS defaults to 4 and controls decoy rows stored next to each payload, in the range 1 to 8. VAULT_CHECK defaults to 1 and verifies the decrypted payload against a compile-time checksum. CFLOW_BRANCHING defaults to 1 as the master switch for keyword mutation. CFLOW_CONST_DECRYPTION defaults to 1 and adds fake branches inside the string and decimal decryptor. CFLOW_ASM_JUNK defaults to 1; setting it to 0 removes all inline asm from control flow, making it legal inside constexpr. CFLOW_IF, CFLOW_ELSE, CFLOW_FOR, CFLOW_WHILE, CFLOW_SWITCH, and CFLOW_RETURN each default to CFLOW_BRANCHING and leave individual keywords alone when set to 0.

INDIRECT_BRANCHING defaults to 1 and enables opaque conditional jumps inside the runtime gate. JUNK_ASM defaults to 1 and emits polymorphic asm blocks through ASM_JUNK and STACKBRK. JUNK_TRAP defaults to 1 and emits unreachable int3, ud2, brk, and bkpt bait. CALL_SLOTS defaults to 10 and sets entries in a hidden call table, in the range 2 to 10. IMPORT_RESOLVER defaults to OBF_RESOLVER_AUTO, with _MANUAL and _API as alternatives. IMPORT_CACHE defaults to 1 and caches resolved imports XOR-masked in.data. FAKE_SIGNATURES defaults to 0 and enables protector bait sections from section 13. INLINE_STD defaults to 1 and enables the inline string routines of section 14. KERNEL_MODE defaults to 0 and avoids pulling in windows.h or dlfcn.h. WATERMARKING defaults to 1 and enables the WATERMARK machinery.

Bad combinations are caught at compile time with a pointed #error instead of failing much later inside a template. That is a practical advantage: a misconfiguration fails early with a readable message.

Limitations and when obfusheader.h is the wrong tool

The library is compile-time only, so it cannot protect code that is generated or loaded at runtime, and it cannot encrypt values that are not compile-time constants. Decryption happens in the caller's own frame with no dynamic allocations, so the decrypted value is only valid while the object holding it remains in scope. A value returned or stored carelessly can become dangling.

The README warns that including the header anywhere but last redefines keywords for everything that follows. That is a real integration hazard for projects with many headers or generated code, and the mitigation is to set CFLOW_BRANCHING to 0 or to place the include carefully.

obfusheader.h is the wrong tool if you need protection for managed or interpreted languages, a supported commercial product with guarantees and maintenance contracts, or protection that survives a determined reverse engineer. It raises cost rather than preventing analysis, and the README makes no claims about resistance to specific tools beyond the named detector bait.

There are no GitHub releases, so versioning and upgrade notes are not published. The last push was on 2026-10-13.

Alternatives and how the approach differs

The README does not name competing protectors. A runtime obfuscation library requires linking and initialization code, and its decryption logic is visible in the binary as a recognizable routine. A separate build-time tool rewrites binaries after compilation and adds a step to the pipeline. obfusheader.h instead folds everything into the preprocessor and template machinery of the compiler, so there is no separate tool, no linked library, and no runtime initialization.

The trade-off is portability versus depth. Because the work happens inside the compiler, the protection is tied to the languages and compilers the header supports, and the control-flow mutation is limited to the keywords it redefines. A binary rewriter can operate on any compiled output but requires an extra build stage and is harder to debug. A team should decide based on whether it can accept a header-only dependency and the include-order discipline it demands.

Maintenance, licensing and upgrade cost

The repository is not archived and the last push was on 2026-10-13. The license is Apache-2.0. The top-level entries are LICENSE, README.md, images, include, and tests.

Upgrade cost is low in dependency terms because there are no external dependencies, but it is not zero. The header relies on compiler-specific behavior for inline asm and PEB or ELF parsing, so a compiler or platform upgrade can break a feature silently or loudly. The options and the include-order rule are the surface a maintainer must keep in mind. Because there are no GitHub releases, tracking changes means following commits.

The Apache-2.0 license allows use, modification, and distribution, including in commercial products, with attribution and patent provisions, but this is not legal advice and a team should have counsel review its obligations.

Editorial conclusion

Adopt obfusheader.h if you ship native binaries and want compile-time string and control-flow obfuscation without a runtime library or build tool. Skip it if you need protection for managed code or a supported commercial product with guarantees. Verify the include order and the tuning options against your compiler and constexpr usage before adopting it.

Frequently asked questions

How do I include obfusheader.h correctly?

Add the include directory to your project and include the header last, because section 15 redefines keywords that affect every later line.

Can I disable control-flow mutation in obfusheader.h?

Yes, set CFLOW_BRANCHING to 0 before the include, or disable individual keywords with CFLOW_IF, CFLOW_ELSE, and the related options.

Does obfusheader.h work with constexpr code?

Yes, set CFLOW_ASM_JUNK to 0 to remove inline asm from control flow, and note that if constexpr is never mutated.

What compilers does obfusheader.h support?

It supports g++, clang, and Visual C++ on x86, x64, ARM64, and ARM32 under C++14 and above.

Official sources

  1. ac3ss0r/obfusheader.h on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ac3ss0r-obfusheader-h.svg)](https://hysenlabs.com/projects/ac3ss0r-obfusheader-h)

Was this useful?