llvm-obfus is an out-of-tree LLVM 21+ pass plugin for policy-driven IR obfuscation.
The plugin applies native LLVM IR transforms to selected functions. The main entry point is obf-safe-pipeline. It runs virtualization, structural rewrites, string and constant protection, self-checksumming, zero-comparison lowering, late indirect dispatch, and final artifact cleanup.
The design goal is simple. The passes make static recovery much harder and stay inside normal LLVM semantics. The project does not rely on malformed objects, inline-asm traps, EH spoofing, or target-specific parser breaks.
The left image shows the original function. The right image shows the obfuscated function.
The obfuscated output contains expanded arithmetic and an indirect dispatch path.
Baseline Function (check_license) |
Obfuscated Function (config_process) |
|---|---|
![]() |
![]() |
The second comparison shows a baseline routine and an obfuscated VM dispatcher.
Baseline Routine (main) |
Obfuscated VM Dispatcher (sub_140003E00) |
|---|---|
![]() |
![]() |
- Design Model: Function-selective policy engine driven by YAML configuration or source-level
__attribute__((annotate(...)))tags. The directoptinterface also accepts command-line configuration flags on non-Windows hosts. - Compiler Compatibility: Uses LLVM New Pass Manager (NPM) extension points. Clang/Clang++, LLVM bitcode, Rust, Zig, and TinyGo integrations are supplied by wrappers or bitcode workflows.
- Platform Support: The C/C++ plugin, runtime, Clang wrapper, bitcode wrapper, Rust (
obf-rustc), and Zig workflows support Linux and Windows x86_64. TinyGo workflows are currently Linux-only. - Profiles: Five built-in performance-versus-security profiles:
fast,standard,guarded,fortress, andlab. - Clean Artifacts: Final cleanup strips release markers, annotations, and local SSA names. Security gates verify configured symbol-isolation invariants.
- Protection levels are
none,light,strong,vm, andstrong_vm. vmandstrong_vmlower selected functions into VM-backed execution paths.- Later hardening stages also process
strong_vmimplementation bodies, not just the public wrapper. - Candidate analysis (
lib/vm/candidate_analysis.cpp) skips incompatible constructs (varargs, non-integral pointers, complex EH pads) and gives clear diagnostics if instruction limits are exceeded. - MBA rewriting diversifies arithmetic identities across
add,sub,xor, andmul. It also rewritesudivanduremby power-of-two constant divisors. It works directly and as part of other transforms such as constant reconstruction and opaque predicates. - Shape families include linear identities (
x ^ y = (x | y) - (x & y)), affine wrappers (Encode(x) = a*x + bwith odd modular multiplier), polynomial zero terms (depth 3+), and constant-multiplication decomposition. - A private
BudgetTrackerenforces a per-expression IR-instruction cap derived frommba.depth. When the budget runs out mid-expansion, the engine emits the plain LLVM binary operation instead. instruction_substitutionrewrites logicaland,or, andxoroperations into equivalent identities. Each site selects one of two variants and can pad the result with an MBA opaque zero.zero_comparisonconverts integer and string equality checks (strcmp,memcmp,strncmp,bcmp,icmpeq) into non-branching bitwise XOR reduction ladders and entropy-masked comparisons.
indirect_dispatchis a late pass in the safe pipeline.- It rewrites supported conditional branches and switch dispatch sites into per-site masked
blockaddressplus arithmetic plusindirectbrsequences. - Each dispatch site derives its masking material from the protected function seed and site index.
- The implementation reconstructs targets from same-function deltas in SSA instead of emitting absolute dispatch tables in globals.
- The pass skips unsupported shapes conservatively: EH personalities, EH pads,
invoke,callbr, existingindirectbr,catchswitch,catchreturn,cleanupreturn,resume,musttail, and non-integral program address spaces.
self_checksumselects an eligible target function and records a sample range of 16 to 32 bytes.- The binder calculates the expected checksum from the final linked bytes.
- During compilation, the pass creates an UNBOUND versioned record.
- After the final link,
obf-checksum-bindcalculates the checksum of the final file-backed code bytes. - The binder then changes the record state to BOUND.
- If execution reaches a protected site with a required UNBOUND record, the program traps.
- The program does not continue with the protected calculation at that site.
obf-clangandobf-clang++bind records automatically for supported Linux x86-64 ELF final links.- They also bind records automatically for native Windows x86-64 PE executable final links.
- This behavior also applies to records from object files or static archives.
- At run time,
rt_core_cccalculates the checksum for the loaded code bytes. - A one-byte change in the sampled range changes the v1 checksum.
- A software breakpoint changes the checksum when it replaces a sampled byte with
0xCC. - The pass XORs
actualwithexpectedand injects the resulting value into the protected calculation. - For integer sites below 64 bits, the pass truncates that XOR value to the site width.
- The v1 rolling hash does not provide cryptographic collision resistance.
self_checksumdoes not authenticate the complete binary.- An attacker can change the code and the BOUND record if the attacker can rewrite both.
- See Self-checksum security contract for the security limits.
string_encodinghandles string encryption.- Ephemeral Micro-Decryption Slots (No Transform-Created Plaintext Buffer):
- Evaluates supported single-byte loads and bounded direct comparisons (
str[i],strcmp,strncmp,memcmp) as transient SSA values. Direct compare lowering is limited to at most 64 effective bytes; larger or unsupported comparisons fall back to the normal decode strategies. - The micro-slot path does not allocate a transform-created contiguous plaintext string buffer (
alloca [N x i8]or heap buffer). Compare lowering uses short-circuit control flow so it does not intentionally read past the first mismatch/NUL that terminatesstrcmp/strncmp. - This is not a physical-register erasure guarantee. LLVM may map, copy, or spill transient SSA values during code generation, and debuggers, tracing, or process-memory inspection can still observe plaintext while it is live.
- Evaluates supported single-byte loads and bounded direct comparisons (
authenticated_modeenables the keyed and integrity-checked runtime decode path.- The runtime support lives in
runtime/string_auth_runtime.cand handles keyed string and constant-pool recovery. - The transform handles lazy decode, eager decode, constructor fallback, and forwarded-pointer cases.
- Short compare-only, non-escaping authenticated strings decode through
rt_core_sd3into per-use stack scratch. The decode path volatile-zeroes the scratch after the compare. Escaping, shared, forwarded, or weakly proven uses keep lazy or constructor stable storage.
- Constant encoding modes are
off,mba_inline,keyed_pool,auto, andall. mba_inlinereconstructs constants directly in IR.keyed_poolmoves constants into keyed, integrity-checked pools that the runtime recovers at use sites.autochooses a strategy per use site based on bit-width and target level.
- The top-level
seedis the root build input. Function-selective passes such asindirect_dispatchderive per-site seeds from the top-level seed, the function name, and the site index. The keyed string and keyed-pool runtime uses the top-level seed directly. authenticated_modeandkeyed_pooluse a domain-separated BLAKE2s schedule ininclude/obf/support/auth_encoding.h:build_key(seed)->function_key(module_id, function_id)->site_key->(enc_key, mac_key)- Authentication uses a keyed BLAKE2s tag over descriptor metadata plus ciphertext. Encryption uses a BLAKE2s-derived XOR keystream with a derived nonce. The scheme does not use AES, ChaCha20, HMAC, or SipHash.
- The emitted artifacts store the 32-byte
build_keyin internal globals and reconstruct derived keys at runtime. This is an embedded-key, self-contained runtime. It does not use a hardware token, remote service, white-box key split, or entropy-anchor binding. - Integrity verification is fail-closed. Descriptor mismatches, tag mismatches, and length mismatches trap in the runtime. The runtime does not return tampered plaintext.
runtime/entropy_anchor.csupports opaque arithmetic and MBA-style transforms. It exposes five deterministic accessor variants:direct,stack_roundtrip,split_recombine,xor_neutral, andadd_sub_neutral.
- The build generates public runtime ABI names in
include/obf/support/runtime_abi_generated.h. - The default public prefix is
rt_core_. - Final cleanup strips marker attributes, removes annotation metadata, anonymizes local and internal obfuscation artifacts, and strips local SSA names.
- Security gates can fail the build on leaked public
obfsymbols. Setsecurity.allow_unsafe_config: trueonly if you explicitly want to allow a weakened test config.
graph TD
Config[YAML Config / Profile / Annotations] --> Frontend[lib/frontend - Config Parser & Validator]
Source[LLVM IR / Bitcode] --> Analysis[lib/analysis - Feature Extraction]
Frontend --> Policy[lib/policy - Policy Engine]
Analysis --> Policy
Policy --> Pipeline[lib/plugin - Safe Pipeline Orchestrator]
subgraph Safe Pipeline Execution Flow
Pipeline --> Step1[1. Entropy Init & Dual-Phase VM Lowering]
Step1 --> Step2[2. String Encode, Zero-Comparison & Constant Pooling]
Step2 --> Step3[3. Opaque GEP, Substitution & Control Flattening]
Step3 --> Step4[4. Outlining, Bogus CF, Self-Checksum & Split]
Step4 --> Step5[5. Strong VM Implementation Hardening]
Step5 --> Step6[6. CFG Cleanup & Indirect Dispatch]
Step6 --> Step7[7. Security Gate Enforcement & Artifact Cleanup]
end
Step7 --> Output[Hardened Target Object / Binary]
Runtime[runtime/ - libobf_runtime.a] -. Linked .-> Output
| Frontend | Integration Method | Configuration Requirements | Platform Support |
|---|---|---|---|
| Clang / Clang++ | -fpass-plugin=<plugin> or obf-clang / obf-clang++ wrapper |
Generic configuration, annotations, or YAML overrides | Linux, Windows |
| LLVM Bitcode | obf-bc CLI wrapper or opt pass plugin |
Valid .bc input/output and explicit configuration |
Linux, Windows |
Rust (rustc/Cargo) |
obf-rustc wrapper via RUSTC_WORKSPACE_WRAPPER |
frontend: rust, default_level: none, exact symbol names |
Linux, Windows |
| Zig | Bitcode pipeline via zig build-obj -femit-llvm-bc |
frontend: zig, default_level: none, exact symbol names |
Linux, Windows |
| TinyGo | obf-tinygo wrapper |
frontend: tinygo, default_level: none, string_encoding.max_strings_per_module: 0, exact symbol names |
Linux only |
Functions are classified into five protection levels:
| Level | Allow VM | MBA / Sub | CFG Flatten | Strings | Constants | Outlining | Bogus CF | Indirect | Self-Checksum | Split |
|---|---|---|---|---|---|---|---|---|---|---|
none |
No | No | No | No | No | No | No | No | No | No |
light |
No | No | No | Yes | Yes | No | No | No | No | Yes |
strong |
No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
vm |
Yes | No | No | Yes | Yes | No | No | Yes | Yes | Yes |
strong_vm |
Yes | Yes | Yes | Yes | No* | Yes | No | Yes | Yes | No |
* In strong_vm, constant protection is bypassed during initial function policy to avoid interfering with VM dispatch tables; constants are absorbed directly into bytecode tables and hardened VM handlers.
Built-in profiles configure default heuristic thresholds:
| Profile Setting | fast |
standard |
guarded |
fortress |
lab |
|---|---|---|---|---|---|
mba.depth |
1 | 1 | 2 | 3 | 4 |
mba.enable_polynomial |
unset | unset | unset | unset | true |
mba.enable_multiplication |
unset | unset | unset | unset | true |
mba.max_ir_instructions |
unset | unset | unset | unset | 320 |
block_split.max_splits_per_function |
1 | 1 | 2 | 4 | 8 |
block_split.min_instructions_per_block |
2 | 2 | 2 | 1 | 1 |
string_encoding.min_string_length |
3 | 2 | 2 | 1 | 1 |
string_encoding.max_strings_per_module |
32 | 128 | 256 | 512 | 1024 |
string_encoding.prefer_lazy_decode |
true |
true |
true |
false |
false |
string_encoding.allow_ctor_fallback |
true |
true |
false |
false |
false |
constant_encoding.max_constants_per_function |
2 | 4 | 8 | 16 | 32 |
security.fail_on_public_obf_symbol |
false |
true |
true |
true |
true |
- CMake 3.24 or higher
- C++23 compiler: Clang, GCC, or MSVC 2022. Windows builds use
clang-clwith the Visual Studio toolchain. - LLVM 21 or newer development package. LLVM 22.1.7 is verified.
- Python 3.10 or newer and
lit - LLVM tools:
clang,clang++,opt,llvm-link,llc,llvm-strip,llvm-nm,llvm-objdump, andllvm-ar;ninjawhen using the Ninja generator - Optional frontend workflows: matching-LLVM nightly/development
rustcandcargo(Linux, Windows); Zig 0.16.x (Linux, Windows); TinyGo 0.41.x with Go 1.23–1.26 and LLD 21 (Linux only)
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_DIR="$(llvm-config --cmakedir)"
cmake --build buildOpen an x64 Native Tools Command Prompt for VS 2022 or Visual Studio Developer PowerShell:
cmake -S . -B build -G Ninja `
-DCMAKE_BUILD_TYPE=Release `
-DLLVM_DIR="C:\path\to\llvm\lib\cmake\llvm" `
-DCMAKE_C_COMPILER="clang-cl" `
-DCMAKE_CXX_COMPILER="clang-cl"
cmake --build buildLLVM_DIR: Path to LLVM CMake package.OBF_RUNTIME_ABI_PREFIX: Prefix for runtime symbols (default:rt_core_).OBF_BENCHMARK_SEED: Optional fixed integer seed for benchmark builds.OBF_RUSTC: Custom path torustc.OBF_CARGO: Custom path tocargo.OBF_ZIG: Custom path tozig.OBF_TINYGO: Custom path totinygo.
Compile through the wrapper. It loads the pass plugin and links the matching runtime archive for link actions:
build/obf-clang -O1 -fno-inline src/auth.c -o auth_app \
--obf-config=path/to/protect.yamlThe wrapper sets OBF_CONFIG for the compiler process. Set OBF_SEED to override the YAML seed:
OBF_SEED=20260817 build/obf-clang -O1 -fno-inline src/auth.c -o auth_app \
--obf-config=path/to/protect.yamlFor direct Clang use, load the plugin, set the configuration, and link libobf_runtime yourself.
Use obf_plugin.so on Linux. Use obf_plugin.dll on Windows.
OBF_CONFIG=path/to/protect.yaml \
clang -O1 -fno-inline \
-fpass-plugin=build/obf_plugin.so \
-Iinclude -c src/auth.c -o auth.o
clang auth.o build/libobf_runtime.a -o auth_appOn Windows, replace obf_plugin.so with obf_plugin.dll.
Use the generated Windows wrapper when possible.
obf-clang and obf-clang++ bind self-checksum records automatically for supported final C/C++ links.
When self_checksum is active on a final wrapper link, specify the output with -o <path>.
Object files and static archives can contain UNBOUND records. Bind these records only after you create the final executable.
Supported v1 targets are:
| Final target | Creates record | Wrapper binds | Binder support |
|---|---|---|---|
| Linux x86-64 ELF executable | Yes | Yes | Supported |
| Linux x86-64 PIE | Yes | Yes | Supported |
| Windows x86-64 PE32+ EXE | Yes | Yes on native Windows | Supported |
| Windows x86-64 DLL | Can create a record | No | Not supported in v1 |
| Windows x86 / non-x86-64 PE | No v1 BOUND record | No | Not supported |
| ARM / ARM64 | No v1 BOUND record | No | Not supported |
| Object file / static archive | Can contain an UNBOUND record | At final link only | Not a final executable |
| Windows final link from a Linux host | Raw pass can emit a PE record | No | Cross-host finalization is not supported |
The wrapper rejects an active self_checksum Windows final link on a non-Windows host.
It does not auto-finalize inherited PE records during that unsupported cross-host workflow.
If you do not use the C/C++ wrapper, bind the executable after the final link.
On Linux, disable the GNU build ID before you bind the file:
clang protected.o build/libobf_runtime.a -Wl,--build-id=none -o protected_app
build/obf-checksum-bind protected_appOn Windows, bind the final PE executable before Authenticode signing:
build\obf-checksum-bind.exe protected.exe
# Sign protected.exe after binding.obf-checksum-bind --probe <file> finds self-checksum record slots without modifying the file.
It does not prove that the file can be bound.
0: The active platform binder recognizes the image and finds one or more self-checksum record slots.3: The active platform binder recognizes the image and finds zero self-checksum record slots.1: Input reading, image parsing, or record-section validation failed.2: Command-line usage error.
Normal binding mode returns:
0: Records were bound successfully, or all existing records were already valid and BOUND.1: Binding failed, including when no.obfscrecords exist.2: Command-line usage error.
Important workflow rules:
- ELF: Use
--build-id=nonefor a manual link. - Windows: Follow this sequence:
link -> bind -> sign. - Windows: Bind before embedded Authenticode signing. The binder rejects a nonzero PE Security directory.
- Windows: DLL binding is not supported in v1.
- Frontends:
obf-bc,obf-rustc, Zig, andobf-tinygodo not run the binder automatically. - C++ Selectors: LLVM function names are used for target matching. Use
extern "C"or mangled names for C++ functions.
See docs/self-checksum.md for record layouts, relocation validation, and binary format rules.
See SECURITY.md for security guarantees and defect reporting.
Mark sensitive routines directly in C/C++ source:
#if defined(__clang__)
#define OBF_PROTECT(level) __attribute__((annotate("obf:" level)))
#else
#define OBF_PROTECT(level)
#endif
OBF_PROTECT("strong_vm")
int verify_license_token(const char* user, const char* token) {
return validate_hash(user, token) ^ 0x5A5A;
}Process standalone bitcode modules with obf-bc:
# Emit bitcode
clang -O1 -emit-llvm -c module.c -o module.bc
# Apply safe obfuscation pipeline
build/obf-bc \
--obf-config=config/production.yaml \
--obf-seed=20260817 \
-o module.obf.bc \
module.bc
# Compile and link
clang module.obf.bc build/libobf_runtime.a -o module_binaryobf-bc transforms bitcode. It does not perform the final link.
If the configuration creates supported v1 records, bind the final executable manually.
On Linux, use --build-id=none when you create the final executable.
Protect Rust binaries using obf-rustc or Cargo:
# Direct rustc invocation
build/obf-rustc \
--obf-config=$(pwd)/config/rust_protect.yaml \
--obf-enable \
--crate-type=bin \
src/main.rs -o rust_app
# Cargo build integration
#
# Set these variables to select the crate that the wrapper must protect.
RUSTC_WORKSPACE_WRAPPER=$(pwd)/build/obf-rustc \
OBF_CONFIG=$(pwd)/config/rust_protect.yaml \
OBF_RUST_MANIFEST_DIR=$(pwd) \
OBF_RUST_CRATE_ROOT=$(pwd)/src/main.rs \
OBF_RUST_CRATE_NAME=my_app \
OBF_RUST_CRATE_TYPE=bin \
cargo build --releaseThe Cargo wrapper requires an exact crate name and a crate type of bin or cdylib.
It also uses one code generation unit for the selected crate.
obf-rustc does not bind self-checksum records automatically after the final link.
Keep self_checksum disabled unless you create and bind a supported final executable.
# 1. Compile Zig source to bitcode
zig build-obj -femit-llvm-bc=component.bc component.zig
# 2. Obfuscate via obf-bc
build/obf-bc \
--obf-config=config/zig_protect.yaml \
-o component.obf.bc \
component.bc
# 3. Assemble and link
clang component.obf.bc main.c build/libobf_runtime.a -o zig_appThe Zig bitcode workflow does not bind self-checksum records automatically.
If you enable self_checksum, use the manual procedure in Self-Checksum Binding.
Otherwise, keep self_checksum disabled.
# Native Linux TinyGo protected compilation
build/obf-tinygo \
--obf-config=$(pwd)/config/tinygo_protect.yaml \
build -scheduler=none -gc=conservative \
-o app_go \
main.goobf-tinygo does not bind self-checksum records automatically.
Keep self_checksum disabled unless you bind the final file with a supported platform binder.
Configuration files use standard YAML syntax:
# Target frontend: generic, rust, zig, or tinygo
frontend: generic
# Base profile: fast, standard, guarded, fortress, lab
profile: guarded
# Global PRNG seed (64-bit unsigned integer)
seed: 20260817
# Default fallback protection level: none, light, strong, vm, strong_vm
default_level: none
# Exact function symbol overrides (takes highest precedence)
overrides:
- name: license_verify
level: strong_vm
- name: decrypt_payload
level: strong
# Pattern-matched target rules (wildcard '*' and '?' supported in generic frontend)
targets:
- match: "auth_*"
level: strong
- match: "crypto_*"
level: vm
# String encryption settings
string_encoding:
min_string_length: 2
max_strings_per_module: 256
prefer_lazy_decode: true
allow_ctor_fallback: false
authenticated_mode: true
# Constant protection settings
constant_encoding:
mode: auto # off, mba_inline, keyed_pool, auto, all
max_constants_per_function: 8
min_bit_width: 8
# Mixed Boolean-Arithmetic (MBA)
mba:
depth: 2
enable_polynomial: false
enable_multiplication: false
max_ir_instructions: 128
# Virtual Machine settings
vm:
max_virtual_instructions: 512
max_mba_depth: 2
# Indirect control dispatch
indirect_dispatch:
enabled: true
max_sites_per_function: 8
max_switch_targets: 16
target_vm_dispatchers: true
target_flattened_headers: true
# Basic block splitting
block_split:
max_splits_per_function: 2
min_instructions_per_block: 2
# Zero comparison reduction
zero_comparison:
enabled: true
max_sites_per_function: 16
max_unroll_bytes: 64
transform_string_comparisons: true
transform_integer_comparisons: true
# Code-as-data self-checksumming
self_checksum:
enabled: true
window_size: 32
max_sites: 4
seed: 20260817
# Security gates and sanitization
security:
fail_on_public_obf_symbol: true
strip_release_markers: true
allow_unsafe_config: false
debug_preserve_generated_names: false
emit_progress_warnings: falseThe safe pipeline execution order runs as follows:
obf-entropy-init: Injects module-level entropy seeds and links entropy anchor bindings.obf-vm(Levelvm): Lowersvm-targeted functions into bytecode and replaces callsites with VM wrappers.obf-vm(Levelstrong_vm): Synthesizes bytecode and dispatch wrappers forstrong_vmfunctions.obf-string-encode: Encrypts static global strings across post-VM module state.obf-zero-comparison: Lowers integer/string equality checks to arithmetic XOR ladders.obf-constant-encode: Transforms constants into inline MBA arithmetic or keyed pools.obf-opaque-gep: Encodes global variable access offsets through opaque math.obf-instruction-substitute: Rewrites bitwise operations into compound identities.obf-opaque-preds: Injects invariant opaque predicate branches.obf-control-flatten: Flattens basic-block control flow graphs into switch dispatch loops.obf-function-outline: Outlines selected control-flow blocks into helper shards.obf-bogus-cf: Injects junk basic blocks and opaque branching loops.obf-self-checksum: Injects code-as-data rolling hash verification windows (rt_core_cc).obf-block-split: Splits eligible linear basic blocks.strong_vmImplementation Hardening: Applies secondary hardening passes (opaque_gep,control_flatten,outline,substitute,bogus_cf) directly to VM interpreter implementations.obf-cfg-state-cleanup: Removes dead CFG placeholders and intermediate metadata.obf-indirect-dispatch: Replaces remaining control-flow branches and VM dispatch headers withindirectbrsequences.- Security Gate Validation: Verifies internal symbol isolation and invariants (
enforce_security_gates). obf-artifact-cleanup: Strips release markers, annotations, and internal SSA names.
- Defense in Depth: Combines control-flow obscurity, semantic abstraction (VM), dynamic key derivation, and integrity checks.
- Fail-Closed Integrity: String decoding and constant-pool access use MAC validation at run time. The program aborts if ciphertext or a descriptor does not match. If execution reaches a protected site with a required UNBOUND v1 record, the run-time guard traps.
- Code-Byte Tamper Dependency: A BOUND self-checksum site hashes selected loaded instruction bytes.
A one-byte change in the sample changes the full v1 checksum.
A software breakpoint also changes it when the breakpoint replaces a sampled byte with
0xCC. Narrow integer sites use a truncated checksum delta in the protected calculation. - No Whole-Binary Authentication:
self_checksumdoes not authenticate the complete executable. It does not stop an attacker who can change both the code and the expected-checksum record. Use signed-code verification if you need binary authentication. - Key Storage Model: Master build keys and initialization seeds are embedded directly in compiled binaries as internal read-only constants. This project implements a self-contained obfuscation model. It does not rely on external hardware security modules (HSM), remote attestation services, or white-box cryptographic guarantees.
- Scope: The transforms make static reverse engineering, symbolic analysis, and binary decompilation more difficult. They do not prevent manual analysis, instruction tracing, memory inspection, or arbitrary binary changes.
See SECURITY.md for the threat model and defect reporting.
See docs/self-checksum.md for technical binding specifications and binary format rules.
- Compilation Overhead:
vmandstrong_vmexpansion increases compilation time and memory usage. Target functions should be compiled with-O1 -fno-inlineor targeted selectively. - Exception Handling: Functions containing complex C++ landing pads, cleanups, or Windows SEH constructs cannot be lowered into VM bytecode and are retained in native code.
- Non-Integral Pointers: Pointers in non-integral address spaces or architecture-specific register frames are excluded from indirect dispatch and VM translation.
- Embedded Keys: Because key schedule root materials reside in the compiled binary, an attacker with full memory inspection capabilities can extract decrypted strings once loaded in memory.
- Self-Checksum Scope: Self-checksum v1 supports Linux x86-64 ELF executables and PIE files. It also supports native Windows x86-64 PE32+ executable files. It does not support Windows DLL binding, x86 targets, ARM targets, Authenticode rebinding, or GNU build-ID recomputation.
- Native C++ Selectors: Generic policy target matching uses LLVM function names.
A native C++ function usually has a mangled LLVM name.
Use that mangled name in
targets[].match. You can also use a selector-friendly symbol such asextern "C". This limit applies to policy selection, not to the binder.
The benchmarks/ directory provides baseline versus obfuscated comparison targets:
- C/C++ Benchmarks:
license_demo,config_demo,vm_workflow_demo,wpo_demo - Multi-Language Benchmarks:
rust_demo(Rust),zig_demo(Zig),tinygo_demo(TinyGo)
Build and run benchmark verification:
# Build all benchmark target pairs
cmake --build build --target obf-benchmarks
# Run end-to-end execution and output parity checks
cmake --build build --target obf-benchmarks-e2e
# Run binary recovery scoring harness
cmake --build build --target obf-re-harness-binaryRun unit tests, runtime validation, and Lit integration tests:
# 1. Run C++ transform and policy unit tests
./build/obf-unit-tests
# 2. Run runtime atomic integrity tests
./build/obf-runtime-atomic-tests
# 3. Run full LLVM Lit integration test suite
lit -v build/tests
# Or run all test suites via CTest
ctest --test-dir build --output-on-failurellvm-obfus/
├── benchmarks/ # Multi-language benchmark corpus and sample applications
├── cmake/ # CMake toolchain, target definitions, and build scripts
├── images/ # Documentation media and control-flow comparison graphs
├── include/
│ └── obf/
│ ├── analysis/ # Feature analysis and complexity metrics headers
│ ├── frontend/ # Configuration structures and YAML parser definitions
│ ├── plugin/ # Pass plugin interfaces and stage declarations
│ ├── policy/ # Function protection level and policy engine headers
│ ├── report/ # Function report and audit telemetry headers
│ ├── support/ # Cryptographic schedules, runtime ABI, and atomic helpers
│ ├── transforms/ # IR transformation pass interfaces
│ └── vm/ # Virtual machine compiler and candidate analysis headers
├── lib/
│ ├── analysis/ # Function metrics and feature extraction
│ ├── frontend/ # YAML configuration loader and validation
│ ├── plugin/ # Pass manager integration and pipeline orchestrator
│ ├── policy/ # Function policy selection logic
│ ├── report/ # Function report generation implementation
│ ├── support/ # Runtime ABI, hashing, and configuration builders
│ ├── transforms/ # Core LLVM IR transformation implementations
│ └── vm/ # Bytecode compiler and interpreter emission
├── runtime/ # Static runtime support library (entropy anchor, auth strings)
├── tests/
│ ├── lit/ # End-to-end LLVM Lit regression test suite
│ └── unit/ # C++ unit tests (obf_unit_tests, runtime_atomic_tests)
└── tools/ # Tooling and frontend wrappers (obf-clang, obf-bc, obf-rustc, obf-tinygo, obf-opt, obf-driver)
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
Developed by @90th



