π‘οΈ Annotation-driven LLVM 22 obfuscator Β· new pass manager Β· zero LLVM source edits
π Getting Started Β· π§© Passes Β· βοΈ Virtualization Β· π§ Development Β· β Testing
Docs source lives in docs/ and is published to the site via MkDocs.
An annotation-driven LLVM obfuscation framework for the new pass manager (NPM).
Configuration comes from source-level annotations (llvm.global.annotations) and is resolved
once per module into a cached, deterministic configuration map.
xollvm plugs into stock LLVM with no LLVM source edits β it is compiled in as an LLVM
static extension (LLVM_EXTERNAL_PROJECTS), so the same code ships three ways:
- a clang/opt toolchain with the obfuscator built in (Linux and Windows),
- a loadable pass plugin
Obfuscator.sofor-fpass-plugin(Linux/macOS).
Disclaimer / Legal: Intended for legitimate software-protection use cases (IP protection, anti-tamper research, academic evaluation, CTFs with permission). Do not use it for malware, unauthorized access, or to violate laws / terms of service. You are responsible for compliance with all applicable laws.
Important
Obfuscation is not a security boundary. Treat it as one layer in a broader defensive strategy (hardening, anti-tamper, secure update, key management, β¦).
- Module entry pass:
-passes=obfuscationβ module-only work (fmerge,strenc) then an ordered per-function pipeline. - Annotation-driven config with canonical pass IDs + aliases.
- Deterministic seeding (module β function β pass) with an optional seed manifest.
- Safety rails: instruction/block/loop-depth gating + IR-growth budgeting.
- Diagnostics:
-passes=obf-dump-config,-passes=obf-metrics. - Reports: JSON obfuscation map + per-pass CFG snapshots; HTML viewer (Python).
- Runtime test suite (Python) under
utils/.
Function pipeline (order enforced by the driver via topological sort):
| Pass | ID | Category | Description |
|---|---|---|---|
| Constant encryption | constenc |
Expression | Encrypts scalar int/FP constant operands into runtime-opaque materializations. |
| Mixed Boolean/Arithmetic | mba |
Expression | Rewrites integer expressions as MBA equivalents. |
| Instruction substitution | substitution |
Expression | Replaces instructions with equivalent idioms. |
| Virtual call | vcall |
Call hardening | Virtualises direct calls via synthetic vtables. |
| Basic block split | split |
CFG | Splits basic blocks to increase graph complexity. |
| Semantic diffusion | sdiff |
Expression / CFG | Volatile-slot masking that resists local simplification. |
| Bogus control flow | bcf |
CFG | Adds opaque predicates and fake edges. |
| CFG flattening | flattening |
CFG | Replaces structured control flow with a dispatcher. |
| Anti-optimization shield | shield |
Post-hardening | Volatile barriers and opaque identities. |
| Anti-decompiler | adec |
Post-hardening | Indirectbr trampolines, asm junk, pointer aliasing. |
| Code virtualisation | vm |
Virtualisation | Compiles the function body into a private bytecode stream. |
Module-only:
| Pass | ID | Description |
|---|---|---|
| Function merging | fmerge |
Collapses annotated functions sharing a group= label into one selector-dispatched super-function; runs first, so the merged bodies feed the function pipeline. |
| String encryption | strenc |
Encrypts string literals; enabled when any annotated function includes strenc(...). |
Note
vm conflicts with flattening (both restructure the CFG). Use one or the other per function.
Note
fmerge is opt-in per function and runs before everything else β the resulting super-functions
are then obfuscated by the function pipeline. See FMERGE.md.
Annotations drive everything. A module analysis parses llvm.global.annotations once into a
cached Function β Config map; the module entry pass then runs module-only fmerge and strenc
and a deterministic, budget-gated per-function pipeline.
Grab from Releases:
| File | What | OS |
|---|---|---|
xollvm-linux-Release.tar.zst |
clang/opt with the obfuscator built in |
Linux x86_64 |
xollvm-windows-Release.7z |
clang/opt with the obfuscator built in |
Windows x64 |
Obfuscator-linux-x64.so |
loadable -fpass-plugin |
Linux x86_64 |
Backends included: X86;AArch64;ARM;RISCV.
No fork, no patch β point LLVM's build at this repo:
git clone --depth 1 --branch release/22.x https://github.com/llvm/llvm-project
git clone https://github.com/und3ath/xollvm
cmake -S llvm-project/llvm -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_PROJECTS="llvm;clang" \
-DLLVM_ENABLE_RTTI=ON -DLLVM_ENABLE_EH=ON \
-DLLVM_TARGETS_TO_BUILD="X86;AArch64;ARM;RISCV" \
-DLLVM_EXTERNAL_PROJECTS=Obfuscator \
-DLLVM_EXTERNAL_OBFUSCATOR_SOURCE_DIR="$PWD/xollvm" \
-DLLVM_OBFUSCATOR_LINK_INTO_TOOLS=ON
cmake --build build --target installNote
Under LINK_INTO_TOOLS the AES stub is compiled by an external clang (the in-tree
clang can't be used β it would form a build cycle). Make sure a clang is on PATH.
# needs an installed LLVM 22 (e.g. apt llvm-22-dev)
cmake -S xollvm -B build -G Ninja -DLLVM_DIR=/usr/lib/llvm-22/lib/cmake/llvm
ninja -C build Obfuscator # -> build/Obfuscator.so// Light: expression-level only
__attribute__((annotate("obf: mba(prob=70,maxDepth=3), substitution(loop=2)")))
int light(int x) { return x * 3 + 7; }
// Heavy: structural + post-hardening
__attribute__((annotate("obf: mba(prob=70), bcf(prob=30), flattening(minBlocks=3), shield, adec")))
int heavy(int x, int y) { return x ^ y; }
// Maximum: VM virtualisation (replaces the entire function body)
__attribute__((annotate("obf: vm(hardened=1,useAES=1,regEncrypt=1)")))
int secret(int key, int data) { return key ^ (data + 0xDEAD); }
// Function merging: fold same-group functions into one super-function
__attribute__((annotate("obf: fmerge(group=core,thunk=1,launder=1)")))
int parse_hdr(const char *p, int n) { /* ... */ return n; }
__attribute__((annotate("obf: fmerge(group=core,thunk=1,launder=1)")))
long crc_step(long acc, int b) { /* ... */ return acc; }C++: [[clang::annotate("obf: mba(prob=60), bcf(prob=25)")]]. See obf_annotations.h for the full cheat-sheet.
Prebuilt / static-extension toolchain (obfuscator is built in):
clang -S -emit-llvm -O0 test.c -o test.ll
opt -passes=obfuscation test.ll -S -o test.obf.ll -obf-seed=1 -obf-deterministic
clang test.obf.ll -O2 -o test.obfLoadable plugin (.so):
opt -load-pass-plugin=./Obfuscator-linux-x64.so -passes=obfuscation test.ll -S -o test.obf.ll
# or with clang: clang -fpass-plugin=./Obfuscator-linux-x64.so ...Diagnostics: -passes=obf-dump-config (resolved config), -passes=obf-metrics (JSONL).
Seeds cascade base β module β function β pass:
-obf-seed=<N>pins the base seed.-obf-deterministicderives the module seed from a stable hash of the module id (when seed is 0).-obf-seed-manifest=seeds.jsondumps the full manifest.
| Document | Purpose |
|---|---|
| BUILD.md | Full compilation guide β static-extension toolchain (Linux/Windows), .so plugin, prerequisites, troubleshooting. |
| USER.md | Annotation grammar, pass reference, global options, reports, troubleshooting. |
| DEV.md | Architecture: registration, annotation cache, pipeline ordering, reporting, adding passes. |
| VM.md | Code-virtualisation reference β ISA, bytecode format, hardening layers. |
| FMERGE.md | Function-merging reference β grouping, memory ABI, dispatch (switch/indirectbr), thunks, selector laundering. |
| TESTS.md | Runtime test harness, categories, debug workflows. |
Apache-2.0 WITH LLVM-exception (same as LLVM). See LICENSE.