OLLVM-Next (Ensia) is a modern, high-resilience LLVM-based compiler obfuscation framework. Continuing the lineage of the Hikari, Hikari-LLVM15, and Hikari-LLVM19 projects, Ensia completely redesigns the core transformation engine for modern LLVM toolchains (LLVM 21+, including LLVM 21, 22, and 23+).
Ensia supports cross-platform code protection across Linux, Windows (MSVC, clang-cl, MinGW), macOS, iOS, and Android, with first-class architecture support for x86_64, AArch64 (ARM64), and i386.
| Tier | Operating Systems | Architectures | Support Status |
|---|---|---|---|
| Tier 0 | Linux, Windows | x86_64, AArch64 | Full support & Release blocker |
| Tier 1 | macOS, FreeBSD | x86_64, AArch64 | Full support & Non-release blocker |
| Tier 2 | OpenBSD, iOS, Android, and others | RISC-V, i386, and others | Partial support & Issue response only |
Traditional VM-based obfuscators (Virtualizers) encapsulate target logic within a custom interpreter loop. While difficult to inspect by hand, they introduce a catastrophic Single Point of Failure (SPOF): once an analyst devirtualizes the bytecode opcode dispatch table or extracts the central interpreter handler loop, all protected routines collapse at once.
Ensia completely rejects single-point interpreter designs. Instead, it enforces Non-Linear SMT / Symbolic Solver State-Space Explosion and Dynamic Taint Analysis (DTA) Neutralization through an interleaved composition of distributed passes.
| Pass ID | CLI Flag | Env Var | Description |
|---|---|---|---|
| ANTIHOOK | -enable-antihook |
ANTIHOOK=1 |
Dual-defense integrity architecture: Entry Prologue Guard (0xE9, 0xEB, 0xCC, 0x68, 0xFF 0x25, 0x48 0xB8) + Scattered In-Flight CFG Auditing, embedded code self-check with data-flow entanglement (T_env / T_exp), direct syscall bypass, and 3-tier anti-taint engine. |
| ACDOBF | -enable-acdobf |
ACDOBF=1 |
Objective-C & Swift metadata scrambling, Fisher-Yates method list shuffling, randomized selector hashing, and dummy selector injection. |
| FCO | -enable-fco |
FCO=1 |
Function Call Obfuscation: replaces direct calls with runtime dlopen/dlsym (POSIX) or GetProcAddress (Windows), completely eliminating external symbol imports from binary headers. |
| ADB | -enable-adb |
ADB=1 |
Zero-dependency debugger detection: direct raw syscall /proc/self/status TracerPid continuous auditing (immune to catch syscall ptrace), kernel anti-attachment via prctl(PR_SET_DUMPABLE, 0), hardware debug registers (DR0-DR7), in-flight scattered checks (local EFLAGS.TF & local RDTSC jitter), and immediate violent termination (SYS_exit_group(137) / inline hardware traps). |
| STRCRY | -enable-strcry |
STRCRY=1 |
Dual-layer Vernam OTP + Rijndael GF(2^8) Galois Field cipher with unordered dynamic inlined decryption stubs and Anti-Dump memory zeroization at function return/resume. |
| CONSTENC | -enable-constenc |
CONSTENC=1 |
Two-phase constant encryption: Scheme A (Bivariate MBA k-share), Scheme B (4-round Feistel non-linear mixing), and Scheme C (Dynamic AntiDebug token %adb.tok entanglement with DominatorTree validation). |
| SUBOBF | -enable-subobf |
SUBOBF=1 |
Instruction Substitution: replaces basic arithmetic and bitwise operations with complex algebraic and rotate-identity trees. |
| MBAOBF | -enable-mbaobf |
MBAOBF=1 |
Mixed Boolean-Arithmetic: transforms expressions into multivariate non-linear boolean-arithmetic over ring Z/(2^n)Z with 42 built-in identities, Point-to-Point (BPP) dataflow tracking, context-dependent non-zero polynomial noise ($P(x, y) \cdot (\text{ctx} \oplus K)$), and stateful contextual barriers. |
| SPLITOBF | -enable-splitobf |
SPLITOBF=1 |
Basic Block Splitting: slices blocks across instruction chains, enforced by mandatory opaque predicate chaining (((seed * (seed + 1)) & 1) == 0) with bogus loops to prevent simplifycfg collapse, and injects inline-ASM stack pointer confusion. |
| BCFOBF | -enable-bcfobf |
BCFOBF=1 |
Bogus Control Flow: injects non-patchable hardware opaque predicates (CPUID SSE bit, RDTSC parity, CNTPCT_EL0, polymorphic barriers), dead-code loops, and entropy chains. |
| CSMOBF | -enable-csmobf |
CSMOBF=1 |
Chaos State Machine: transforms CFGs into a chaotic dynamical system governed by the quadratic logistic map in full Q32 fixed-point arithmetic (diffuseState), modular inverse decoding (modInverse32), dynamic data-flow feedback (DFB), and optional 2-level nested dispatch. |
| CFFOBF | -enable-cffobf |
CFFOBF=1 |
Control Flow Flattening: fallback flattening with Branchless Algebraic State Transitions (mask = 0 - zext(cond), diff = true ^ false, next = false ^ (mask & diff)), eliminating volatile single points of failure. |
| VOBF | -enable-vobf |
VOBF=1 |
Vector Obfuscation: lifts scalar arithmetic and comparisons into 128/256/512-bit SIMD vector space with pseudo-random lane noise, shufflevector bijective permutations, and Vector Taint Diffusion. |
| INDIBRAN | -enable-indibran |
INDIBRAN=1 |
Indirect Branching: encrypts basic block jump targets via Knuth multiplicative golden-ratio hashing, runtime Newton-Raphson modular inverse decode, and encrypted jump table arrays. |
| FUNCWRA | -enable-funcwra |
FUNCWRA=1 |
Function Wrapper: wraps function entry points with polymorphic proxy trampolines (IdentityNoise, ArgShuffle, RetMask), XOR-scrambled function pointers, and enforced noinline optnone. |
Ensia schedules passes to maximize cascading complexity. Each pass treats the obfuscated output of previous passes as input, creating an exponential barrier to symbolic deobfuscation:
1. AntiHooking & AntiClassDump -> Dual-defense AntiHook (Entry Prologue Guard + Scattered In-Flight Auditing + Embedded Integrity Self-Check with Data-Flow Entanglement), direct syscalls, dynamic execution tokens (T_env), ObjC metadata scrambling
2. FunctionWrapper -> Generates polymorphic proxy trampolines around entry points (argument XOR shuffling, frame depth mutation, return masking)
3. FunctionCallObfuscate (FCO) -> Eliminates direct imports via dlopen/dlsym runtime resolution inside callers and proxies
4. AntiDebugging -> Injects direct syscall /proc/self/status TracerPid parser, prctl anti-attach, hardware debug registers (DR0-7), distributed in-flight TF/RDTSC probes, arithmetic data-flow entanglement, violent exit handler
5. StringEncryption -> Encrypts global strings with GF(2^8) stubs & injects volatile exit zeroizers
6. ConstantEncryption (Phase 1) -> Encrypts original programmer literals before CFG transformations
7. Per-Function Transformation Loop:
├── 7a. Substitution (Sub) -> Arithmetic expansions (x + y -> algebraic identities)
├── 7b. MBA Obfuscation -> Multivariate non-linear boolean-arithmetic + polymorphic hardware barriers
├── 7c. Split Basic Blocks -> Slices basic blocks, cutting MBA expressions across blocks + stack confusion
├── 7d. Bogus Control Flow (BCF)-> Injects opaque hardware predicates & clones split blocks into loops
├── 7e. Chaos State Machine (CSM)-> Replaces CFG topology with logistic-map quadratic chaotic dispatch
├── 7f. Classic Flattening (CFF)-> Fallback CFF with branchless algebraic masking for functions skipped by CSM
└── 7g. Vector Obfuscation (Vec)-> Lifts remaining scalar logic & dispatch state into SIMD vector space
8. ConstantEncryption (Phase 2) -> Encrypts state constants & jump keys generated by BCF/CSM/CFF
9. IndirectBranch -> Encrypts jump targets via Knuth multiplicative hashing into jump tables
10. Cleanup Markers -> Erases temporary compiler sentinel declarations (ensia_*) before symbol scrambling
11. FeatureElimination -> Strips DWARF metadata, anonymizes TU path to "a", drops llvm.ident, clears COMDATs, internalizes ODR linkages, scrambles private/internal symbols (_f<hex>, _v<hex>, _a<hex>)
12. LTO Evasion -> Stamps functions with Attribute::OptimizeNone and Attribute::NoInline
# 1. Automatic configuration discovery (loads ./ensia.toml if present in current directory):
clang -fpass-plugin=/path/to/libEnsia.so -O2 main.c -o main
# 2. Explicit configuration via environment variable:
ENSIA_CONFIG=/path/to/ensia.toml \
clang -fpass-plugin=/path/to/libEnsia.so -O2 main.c -o main
# 3. Quick preset selection via environment variable:
ENSIA_PRESET=mid \
clang -fpass-plugin=/path/to/libEnsia.so -O2 main.c -o main
# 4. Frontend plugin invocation with -mllvm option parsing:
clang -Xclang -load -Xclang /path/to/libEnsia.so \
-mllvm -ensia-config=ensia.toml \
-O2 main.c -o main# Build binary crate with Ensia Rust plugin (auto-discovers ./ensia.toml):
ENSIA_CONFIG=ensia.toml RUSTC_BOOTSTRAP=1 \
RUSTFLAGS="-Z llvm-plugins=/path/to/libEnsia_rust.so -C passes=ensia" \
cargo build --release| Preset | Flag / Env | Included Passes & Characteristics | Recommended Use Case |
|---|---|---|---|
low |
-enable-lowobfENSIA_PRESET=low |
Sub + MBA + Split + BCF + StrEnc + ConstEnc. Minimal code bloat, fast compile. | Debugging, rapid testing, performance-critical modules. |
mid (Recommended) |
-enable-medobfENSIA_PRESET=mid |
Sub + MBA + Split + BCF + ConstEnc + StrEnc + Flatten + Vec + IndirBranch. Balanced production protection. | Production releases, commercial SDKs, game protection. |
high |
-enable-highobfENSIA_PRESET=high |
All passes active at high intensity. CSM preferred over Flatten, Feistel tier active, AntiHook, AntiDebug, FCO, FunctionWrapper. | Core financial assets, licensing engines, critical algorithms. |
max |
-enable-maxobfENSIA_PRESET=max |
All passes at maximum intensity: BCF prob=100 loop=3, CSM nested 2-level dispatch, Vec 512-bit, ConstEnc kshare=6 + Feistel, FW 3 rounds, violent exit. | Red-team deliverables, stress-testing toolchains. |
Search order: -mllvm -ensia-config=<path> > ENSIA_CONFIG=<path> > ./ensia.toml.
# ==============================================================================
# ensia.toml — Ensia / OLLVM-Next Comprehensive Configuration Template
# ==============================================================================
# Search order:
# 1. Clang argument: -mllvm -ensia-config=/path/to/ensia.toml
# 2. Environment var: ENSIA_CONFIG=/path/to/ensia.toml
# 3. Current directory: ./ensia.toml
# ==============================================================================
[global]
# Base preset profile: "low" | "mid" (recommended) | "high" | "max" | "csm_vec" | "csm_only" | "vec_only"
preset = "mid"
# Deterministic PRNG seed (hex or integer string, optional).
# When omitted or 0, a cryptographically secure random seed is generated per compile.
# seed = "0xDEADBEEF"
# Emit detailed pass transformation metrics to stderr during compilation
verbose = false
# Enable pass execution trace logging
trace = false
# Demangle C++ / Rust function names in log outputs
demangle_names = true
# ==============================================================================
# PASS CONFIGURATIONS ([passes.<pass_name>])
# Supports canonical names (e.g. string_encryption) and short aliases (e.g. str_enc)
# ==============================================================================
# ── Bogus Control Flow (BCF) ──────────────────────────────────────────────────
[passes.bcf]
enabled = true
probability = 60 # Percentage probability each basic block is selected (0–100)
iterations = 1 # Number of BCF expansion passes per basic block (1–5)
complexity = 4 # Opaque predicate algebraic complexity level (1–10)
entropy_chain = true # Chain hardware predicates (CPUID, RDTSC/CNTPCT) with runtime state
junk_asm = true # Inject polymorphic hardware inline-ASM barriers
junk_asm_min = 2 # Minimum number of polymorphic barrier instructions per dead block
junk_asm_max = 6 # Maximum number of polymorphic barrier instructions per dead block
nested = false # Nest opaque predicate conditions inside cloned branches
create_func = false # Extract dead branches into standalone cold functions
only_junk_asm = false # Only inject polymorphic barriers without altering CFG branches
# ── Instruction Substitution ──────────────────────────────────────────────────
[passes.substitution]
enabled = true
probability = 60 # Probability each arithmetic/bitwise op is expanded (0–100)
iterations = 1 # Number of recursive substitution expansions (1–3)
# ── Mixed Boolean-Arithmetic (MBA) ───────────────────────────────────────────
[passes.mba]
enabled = true
probability = 50 # Probability of applying MBA identities (0–100)
layers = 2 # Recursive non-linear polynomial layers (1–3)
heuristic = true # Zero-noise identity verification against bit-vector solvers
# ── Basic Block Splitting ────────────────────────────────────────────────────
[passes.split_blocks]
enabled = true
splits = 3 # Number of slice points per eligible basic block (1–10)
stack_confusion = true # Inject push/pop (x86) or str/ldr (ARM64) stack-frame desynchronization
# ── String Encryption ────────────────────────────────────────────────────────
[passes.string_encryption]
enabled = true
probability = 100 # Probability of encrypting discovered global string literals (0–100)
anti_dump = true # Strip memory dump markers / wipe decrypted strings from memory
# Always encrypt matching strings (regex patterns)
force_content = [
".*secret.*",
".*token.*",
".*key.*",
".*password.*"
]
# Skip encrypting benign or format strings (regex patterns)
skip_content = [
"^%[0-9]*[a-zA-Z]$",
"^PASS$",
"^FAIL$"
]
# ── Constant Encryption ──────────────────────────────────────────────────────
[passes.constant_encryption]
enabled = true
iterations = 1 # Encryption rounds (1–3)
share_count = 3 # Bivariate MBA additive split shares (2–8)
feistel = true # Apply 4-round non-linear Feistel cipher network
substitute_xor = true # Obfuscate XOR recombination logic with algebraic MBA
substitute_xor_prob = 40 # Probability of substituting recombination XORs (0–100)
globalize = false # Hoist constant share pools to encrypted global memory
globalize_prob = 50 # Probability of globalizing share sets (0–100)
# Force-encrypt specific critical constant literals (case-insensitive hex regex)
force_value = [
"^0x9E3779B9$", # Knuth golden ratio
"^0x5F3759DF$", # Fast inverse square root
"^0xDEADBEEF$"
]
# Skip trivial constants (e.g. 0 and 1)
skip_value = [
"^0x0$",
"^0x1$"
]
# ── Chaos State Machine (CSM) ────────────────────────────────────────────────
[passes.chaos_state_machine]
enabled = true
warmup = 128 # Iterations to discard from Q16 logistic map transient phase
nested_dispatch = false # Enable hierarchical 2-level nested dispatch clusters
max_blocks = 5000 # Maximum CFG block limit before falling back to Classic CFF
# ── Classic Control Flow Flattening (CFF) ────────────────────────────────────
[passes.flattening]
enabled = false # Fallback flattening (auto-active if CSM is disabled)
# ── Vector Obfuscation (SIMD Lifting) ────────────────────────────────────────
[passes.vector_obfuscation]
enabled = true
probability = 40 # Percentage of scalar instructions lifted to SIMD vectors (0–100)
width = 128 # Vector register width in bits: 128 (SSE/NEON), 256 (AVX2), 512 (AVX-512)
shuffle = true # Apply pseudo-random bijective shufflevector permutations
lift_comparisons = true # Lift scalar ICmp comparisons into SIMD mask vectors
# ── Indirect Branching ───────────────────────────────────────────────────────
[passes.indirect_branch]
enabled = true
use_stack = true # Allocate branch jump tables dynamically on the stack frame
enc_jump_target = true # Encrypt jump targets using Knuth multiplicative modular inverse
# ── Function Wrapper ─────────────────────────────────────────────────────────
[passes.function_wrapper]
enabled = false
probability = 50 # Fraction of functions wrapped in proxy trampolines (0–100)
times = 1 # Wrapper recursion depth (1–3)
# ── Function Call Obfuscation (FCO) ──────────────────────────────────────────
[passes.function_call_obfuscate]
enabled = false
flag = 0 # Obfuscation filter mask
symbol_config_path = "" # Path to external symbol import whitelist/blacklist file
# ── Anti-Hooking & Anti-Taint Engine ────────────────────────────────────────
[passes.anti_hooking]
enabled = true
inline_x86 = true # Probe x86_64 prologues for E9 (JMP) / 48 B8 (MOV RAX) patches
inline_aarch64 = true # Probe AArch64 prologues for 0x14000001 (B .+4) inline hooks
inline_win = true # Windows API inline hook detection
objc_runtime = false # Inspect Objective-C method dispatch tables
antirebind = true # Counter dynamic linker symbol rebinding (fishhook / dyld)
direct_syscall = true # Execute direct kernel syscalls (svc #0 / syscall) bypassing libc
check_integrity = true # Verify code segment cryptographic hashes
precompiled_ir_path = "" # Path to external precompiled LLVM IR module for anti-hooking
# ── Anti-Debugging ───────────────────────────────────────────────────────────
[passes.anti_debugging]
enabled = true
probability = 80 # Percentage of function entries receiving anti-debug probes (0–100)
precompiled_ir_path = "" # Path to external precompiled LLVM IR module for anti-debugging
# ── Anti-Class Dump (ObjC / Swift) ───────────────────────────────────────────
[passes.anti_class_dump]
enabled = false # Objective-C / Swift Mach-O metadata scrambling (macOS / iOS only)
use_initialize = true # Defer class initialization to runtime +initialize
rename_methodimp = true # Scramble method implementation names
scramble_methods = true # Permute selector order using Fisher-Yates shuffle
dummy_selectors = false # Inject phantom selector entries into class metadata
dummy_count = 8 # Number of phantom dummy selectors to inject
encrypt_strings = true # Dynamically decrypt selector and class names at runtime
anti_hook = true # Detect Frida/Substrate hooks on class_replaceMethod and sel_registerName
opaque_barriers = true # Insert opaque memory barriers on runtime pointers
# ==============================================================================
# GRANULAR POLICY OVERRIDES ([[policy]])
# Rules are evaluated in order; the first matching rule takes precedence.
# Matches against source module path and function symbol name using ECMAScript regex.
# ==============================================================================
# Core Cryptographic Primitives: Maximum Hardening
[[policy]]
module_regex = ".*(crypto|cipher|signature).*"
function_regex = ".*(encrypt|decrypt|sign|verify|hash).*"
preset = "high"
passes.chaos_state_machine.enabled = true
passes.chaos_state_machine.nested_dispatch = true
passes.constant_encryption.feistel = true
passes.constant_encryption.share_count = 4
passes.mba.layers = 3
passes.vector_obfuscation.width = 256
passes.anti_debugging.probability = 100
passes.anti_hooking.direct_syscall = true
# License Verification & Integrity Checks: Violent Exit on Tamper
[[policy]]
module_regex = ".*(licensing|auth|integrity).*"
function_regex = ".*(validate|check|license).*"
preset = "high"
passes.anti_debugging.enabled = true
passes.anti_debugging.probability = 100
passes.anti_hooking.enabled = true
passes.anti_hooking.direct_syscall = true
passes.string_encryption.probability = 100
# Performance-Critical Loops / Hotspots: Minimal Overhead
[[policy]]
module_regex = ".*"
function_regex = "^(main|fast_path_.*|inner_loop_.*)$"
passes.bcf.enabled = false
passes.chaos_state_machine.enabled = false
passes.flattening.enabled = false
passes.indirect_branch.enabled = false
passes.function_wrapper.enabled = false- CMake 3.20+
- Ninja or Make
- LLVM & Clang (version 21, 22, or 23)
- Rust toolchain (optional, for
libEnsia_rust.soandweb)
git clone https://github.com/Apich-Organization/ensia.git
cd ensia
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)Outputs:
build/obfuscation/libEnsia.so(Clang plugin)build/obfuscation/libEnsia_rust.so(Rustc plugin)
mkdir build && cd build
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_DIR="C:/path/to/llvm/lib/cmake/llvm"
ninja EnsiaTo run the automated verification suite:
bash test/run_obf_tests.shA CTF-style crackme using this obfuscator is available in the ./ctf folder. You can compile it yourself, as the source code is fully open-source, or you can simply download the test binary from our releases page.
Since we do not provide the crack/solution, anyone who successfully solves this challenge with clear, documented steps will win a reward and earn a permanent spot on our security team homepage! If you successfully solve this Crackme, please submit your solution to security@apich.org for verification and rewards.
- Target Binary:
./challenge - Total Score: 1000 pts
- Runtime Environment: Linux x86_64
This challenge consists of 4 tightly coupled cryptographic verification stages. Reverse engineer the program logic to recover the key input for each stage sequentially, and finally decrypt the flag in memory:
| Stage | Objective | Input Format Specification | Score |
|---|---|---|---|
| Stage 1 | Activation Key | XXXX-XXXX-XXXX-XXXX (16 uppercase/lowercase/numeric characters) |
150 pts |
| Stage 2 | Calibration Coordinates | Four 32-bit unsigned integers (space-separated, e.g., a b c d) |
200 pts |
| Stage 3 | Feistel Passphrase | 16-byte ASCII string | 200 pts |
| Stage 4 | Sealing Token | 8-character hexadecimal string | 150 pts |
| Final | Core Enclave Unlocked Flag | Complete ensia{...} string |
300 pts |
| Total | 1000 pts |
- Execute the Program:
./challenge
- Success Criteria:
- The 4 stages utilize non-linear state-coupled vectors. An incorrect input in any stage will cause an avalanche shift in the subsequent keystream and memory zeroization.
- Upon successful completion of all inputs, the program outputs:
[+] Enclave Unlocked! Verification Complete.
[+] Flag: ensia{...}
- Scoring Rules:
- The competition platform supports independent scoring based on the raw keys recovered for Stage 1 through Stage 4.
- Successfully submitting the decrypted
ensia{...}output grants the completion score.
This project is licensed under the AGPL-3.0. It includes code and concepts continuing the lineage of Hikari and LLVM. See LEGAL.md for full details on project history and original authors.
We welcome sponsorships supporting open-source compiler security research. Please review our Sponsorship Policy for details on fund allocation, contribution options via Open Collective, and corporate tiers.
- Open Collective: https://opencollective.com/apich-organization
Please consult CODE_OF_CONDUCT.md and SECURITY.md for vulnerability reporting guidelines and ethical standards.