obfus.h is a macro-only library for compile-time obfuscating C applications, designed specifically for the Tiny C (tcc) (you can download it here -> Tiny-C-Compiler/releases). It is tailored for Windows x86 and x64 platforms and supports almost all versions of the compiler. Very reliable armor for your C programs!
- π Function Call Obfuscation: Confuse function calls to make your code less readable to unauthorized eyes.
- π‘οΈ Anti-Debugging Techniques: Built-in mechanisms to prevent code analysis during runtime.
- π Control Flow Code Mutation: Turns code into spaghetti, making it difficult to parse conditions and loops.
- π§Ά Strings Hiding: Hides specified strings in a file and dynamically collects them when executed.
- π« Anti-Decompilation Techniques: Makes many popular decompilers useless visually breaking their output.
- π Fake Signatures Adding: Can add fake signatures of various packers and protectors to confuse reverse engineers.
- π§ Virtualization: Makes math operations very difficult to understand using virtual machine commands.
- πͺ Windows API Protection: Automatically protects common GUI and system calls with no extra setup.
obfus.h is a full-featured obfuscation tool, not a proof of concept. It is a powerful tool when you understand where and how to apply it: use it to protect selected native components containing sensitive logic.
Since obfus.h requires TCC, the recommended approach for larger projects is to isolate that logic in C DLLs compiled with TCC and obfuscated with obfus.h. The main application can remain in C#, C++, another language, or C compiled with GCC, LLVM/Clang or Visual C and call those DLLs through their exported interfaces.
The resulting binaries can also be processed by commercial protectors and packers to add another layer of protection. Check compatibility with your chosen protector and settings before distributing the protected components.
Important
obfus.h significantly raises the barrier to reverse engineering, but it is not a panacea. Protecting trade secrets also requires your own internal protection mechanisms; obfuscation should be one layer of your protection strategy.
Integrating obfus.h into your project is a simple process. Just include the following line in your code:
#include "obfus.h"One header. Powerful protection. Include it, build your application and make your native code much harder to analyze.
Protection varies throughout the application and between builds. Define OBFH_BUILD_SEED before including the header to choose a build seed. Reusing a seed keeps builds reproducible with the same source and compiler.
Control-flow protection turns straightforward conditions and loops into a tangled graph of branches, junk code and fake functions, making the original logic harder to follow in disassemblers and decompilers. The inserted code varies throughout the program and can change between builds. CFLOW_V2 adds another layer of control-flow mutation.
Protection is inserted automatically around if conditions. For explicit insertion, use BREAK_STACK_CFLOW; or STACK_PROXY_FUNCTIONS;. NO_CFLOW disables automatic control-flow protection; NO_OBF disables obfuscation.
On Windows x64, additional fake functions add noise to the function graph without requiring extra setup.
IntelliSense stays responsive while TCC builds the fully protected application. Integration remains a single header, with no extra generation step.
Define NO_PDATA_DECOYS=1 to disable the additional x64 fake functions.
Available options for protection configuring:
// Advanced code protection (see the "Virtualization" part of the documentation!) #define VIRT 1 // Allows you to use the functions of a math VM // Additional options #define CFLOW_V2 1 // More powerful Control Flow obfuscation (slowly!) #define ANTIDEBUG_V2 1 // Add hardware-breakpoint checks to anti-debugging protection #define FAKE_SIGNS 1 // Adds fake signatures of various protectors or packers // Disabling default features #define NO_OBF 1 // Don't obfuscate (for debugging) #define NO_CFLOW 1 // Don't use Control-Flow obfuscation #define NO_ANTIDEBUG 1 // Don't build in debugging protection #define NO_PDATA_DECOYS 1 // Omit x64 unwind-backed native decoysor use it with compiler args:
tcc "app.c" -w -D NO_CFLOW -D ANTIDEBUG_V2 -D FAKE_SIGNS -D VIRT
Warning
When compiling an application with obfuscation, use the -w argument to suppress warnings. Otherwise, the console will display numerous intimidating logs that have no impact on the final result. There's no need to be alarmed by them.
Common Windows GUI and system calls are protected automatically. Keep writing ordinary Windows code; obfus.h adds protection without requiring extra wrappers or setup.
π Debugging protection is triggered by calls to many basic MSVCRT functions.
In critical places in the code you can use the ANTI_DEBUG; construct. For example:
ANTI_DEBUG;
if (!licenseExpired()) {
// ...
}The HIDE_STRING(str) obfuscates and visually hides strings by mutating them, significantly complicating their discovery and patching in the source code. When declared in this manner, the strings are assembled on the stack through mov instructions rather than being loaded all at once. This method ensures that the strings are not statically declared and are instead constructed at runtime, making them less susceptible to static analysis. However, it is important to note that this feature cannot be used for hiding static fields during their declaration, as it involves a function call.
Important
Some decompilers may still reveal them due to static optimizations. In disassembler output, the code will appear complex and cumbersome, which can deter straightforward analysis but may not fully prevent determined reverse engineering efforts.
The returned pointer remains valid until the enclosing block ends. Copy the string if it needs to outlive that block.
An example of calling the printf function from the standard library with static hiding of the message and its decryption on the stack:
char *hidden_message = HIDE_STRING("Hello, world!");
// ...
printf(hidden_message);This is a protection technique in which certain calculations are performed through an embedded virtual machine upon command. Makes analysis of mathematical operations very difficult! It will work with the VIRT option enabled (and only!). Otherwise, all virtual machine commands will be replaced by ordinary mathematical operators.
Enable VIRT and use the VM macros to protect sensitive calculations. No extra initialization is required.
Warning
Virtualization in critical locations can impact optimization. Use with caution only in areas where it is really needed
| Function | Type | Op | Description | Example |
|---|---|---|---|---|
VM_ADD |
long | + |
Adds two numbers | VM_ADD(5, 3) = 8 |
VM_SUB |
long | - |
Subtracts two numbers | VM_SUB(5, 3) = 2 |
VM_MUL |
long | * |
Multiplies two numbers | VM_MUL(5, 3) = 15 |
VM_DIV |
long | / |
Divides two numbers | VM_DIV(6, 3) = 2 |
VM_MOD |
long | % |
Calculates the modulus of two numbers | VM_MOD(5, 3) = 2 |
VM_AND |
unsigned int | & |
Bitwise AND | VM_AND(6, 3) = 2 |
VM_OR |
unsigned int | | |
Bitwise OR | VM_OR(6, 3) = 7 |
VM_XOR |
unsigned int | ^ |
Bitwise XOR | VM_XOR(6, 3) = 5 |
VM_NOT |
unsigned int | ~ |
Bitwise NOT | VM_NOT(0) = 0xFFFFFFFFu |
VM_SHL |
unsigned int | << |
Logical left shift | VM_SHL(1, 3) = 8 |
VM_SHR |
unsigned int | >> |
Logical right shift | VM_SHR(8, 3) = 1 |
VM_EQU |
BOOL | == |
Checks if two numbers are equal | VM_EQU(5, 5) = true |
VM_NEQ |
BOOL | != |
Checks if two numbers are not equal | VM_NEQ(5, 3) = true |
VM_LSS |
BOOL | < |
Checks if the first number is less than the second number | VM_LSS(3, 5) = true |
VM_GTR |
BOOL | > |
Checks if the first number is greater than the second number | VM_GTR(5, 3) = true |
VM_LEQ |
BOOL | <= |
Checks if the first number is less than or equal to the second number | VM_LEQ(3, 5) = true |
VM_GEQ |
BOOL | >= |
Checks if the first number is greater than or equal to the second number | VM_GEQ(5, 3) = true |
VM_ADD_DBL |
long double | + |
Adds two double numbers | VM_ADD_DBL(5.5, 3.2) = β8.7 |
VM_SUB_DBL |
long double | - |
Subtracts two double numbers | VM_SUB_DBL(5.5, 3.2) = β2.3 |
VM_MUL_DBL |
long double | * |
Multiplies two double numbers | VM_MUL_DBL(5.5, 3.2) = β17.6 |
VM_DIV_DBL |
long double | / |
Divides two double numbers | VM_DIV_DBL(6.0, 3.0) = β2.0 |
VM_LSS_DBL |
BOOL | < |
Checks if the first double number is less than the second double number | VM_LSS_DBL(3.5, 5.2) = true |
VM_GTR_DBL |
BOOL | > |
Checks if the first double number is greater than the second double number | VM_GTR_DBL(5.5, 3.2) = true |
The virtual machine does not support some basic
doublecomparison operations.
You can use logical operators that use virtual machine calls to further complicate the understanding of your code:
| Operator | Description |
|---|---|
VM_IF |
Use instead of if |
VM_ELSE_IF |
Use instead of else if |
VM_ELSE |
Use instead of else |
This is not a complete replacement for if/else, but is just a complication of standard operators.
A simple example of using virtualization:
// ...
#define VIRT 1
// ...
// if ((2 + 2) == 4) { ... }
VM_IF (VM_EQU(VM_ADD(2, 2), 4)) {
printf("2 + 2 == 4!");
}
// if (condition1) { ... }
// else if (condition2) { ... }
// else { ... }
VM_IF (condition1) {
// if
} VM_ELSE_IF (condition2) {
// else if
} VM_ELSE {
// else
}You can find examples of using all the functions of a virtual machine in the file tests/vm.c
If you need advanced protection against skilled reversers, use CFLOW_V2 and ANTIDEBUG_V2 options.
// Let's obfuscate your code!
#include <stdio.h>
#define VIRT 1 // [+] Use math virtual machine
#define CFLOW_V2 1 // [+] ControlFlow v2
#define FAKE_SIGNS 1 // [+] Fake signatures
#define ANTIDEBUG_V2 1 // [+] AntiDebug v2
#define NO_OBF 0 // [-] Don't obfuscate (disable all)
#define NO_CFLOW 0 // [-] Disable ControlFlow
#define NO_ANTIDEBUG 0 // [-] Disable AntiDebug
#include "obfus.h"
void _start(void) {
ANTI_DEBUG;
char *out = malloc(256);
STACK_PROXY_FUNCTIONS;
if (out) {
strcpy(out, HIDE_STRING("Hello, world!\n"));
printf("%s", out);
} else {
printf("Error!\n");
}
free(out);
int result = VM_ADD(5, 7); // 5 + 7
BREAK_STACK_CFLOW;
VM_IF (VM_EQU(result, 12)) { // (5 + 7) == 12
printf("5 + 7 == 12");
}
exit(0);
}Tiny C 0.9.27 is recommended for use. Unfortunately, some versions of the compiler do not support the functionality needed to completely obfuscation. Visual C, GCC and Clang is not supported and is unlikely to be supported.
Note
Originally created by Fabrice Bellard, TCC is now maintained and developed by the community. You can build the latest development version yourself from the TCC source mirror.
You can use special script for Windows to get the latest versions of obfus.h by downloading the package from the official repository. This is useful if you need to automate security updates without using git.
For example, you can use it before building your project:
+ C:\...> call obfh-update C:\...> tcc app.c -wThe script will update the contents of the obfus.h file in the current directory (according to the specified configuration)
- Defeating a Heavily Obfuscated Binary - Black-box analysis of internal obfus.h security mechanisms and it deobfuscation. All vulnerabilities have now been patched.
- [CTF] 2024 CISCN x ιΏεζ― εθ΅ιε0θ§£ι’-VT θ§£ι’ε ¨ζ΅η¨ - Reversing CTF with previous generation of obfus.h using the original protection code.
- ObfusHunter - A utility for scanning files protected by obfus.h
The code of a program (and its original original logic) protected using obfus.h is almost impossible to recover (deobfuscate). However, using this obfuscator does not guarantee complete protection against all types of threats. It's important to develop and maintain internal program security systems.
What the diagrammatic code will look like after obfuscation:
The reverser will see something like this if he tries to use a decompiler:
This is what all hidden strings via
HIDE_STRINGfeature look like in the disassembler (x86-64 arch):; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; PROTECTED STRING: ; ORIGINAL STRING: ; mov eax, 48h lea rax, aHelloWorld ; mov [rbp-0Fh], al mov r11, rax ; mov eax, 65h ; . . . ; mov [rbp-0Eh], al ; mov eax, 6Ch ; mov [rbp-0Dh], al ; mov eax, 6Ch ; mov [rbp-0Ch], al ; mov eax, 6Fh ; mov [rbp-0Bh], al ; mov eax, 2Ch ; ; etc . . . ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
Thanks to everyone who helped in the development of this project. I appreciate it! β€οΈ
- π¨πΌβπ» @horsicq (for help with the code and advices)
- πΊ @ac3ss0r (for cool ideas and their solutions)
And thanks to you π€ for paying attention to this project!



