xollvm

July 21, 2026 ยท View on GitHub

xollvm logo

xollvm

๐Ÿ›ก๏ธ Annotation-driven LLVM 22 obfuscator ยท new pass manager ยท zero LLVM source edits

LLVM License Platforms Passes Integration

๐Ÿ“– User Guide ยท ๐Ÿง  Architecture ยท โš™๏ธ VM Reference ยท โœ… Tests


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.so for -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, โ€ฆ).


What you get

  • 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/.

Passes (high level)

Function pipeline (order enforced by the driver via topological sort):

PassIDCategoryDescription
Constant encryptionconstencExpressionEncrypts scalar int/FP constant operands into runtime-opaque materializations.
Mixed Boolean/ArithmeticmbaExpressionRewrites integer expressions as MBA equivalents.
Instruction substitutionsubstitutionExpressionReplaces instructions with equivalent idioms.
Virtual callvcallCall hardeningVirtualises direct calls via synthetic vtables.
Basic block splitsplitCFGSplits basic blocks to increase graph complexity.
Semantic diffusionsdiffExpression / CFGVolatile-slot masking that resists local simplification.
Bogus control flowbcfCFGAdds opaque predicates and fake edges.
CFG flatteningflatteningCFGReplaces structured control flow with a dispatcher.
Anti-optimization shieldshieldPost-hardeningVolatile barriers and opaque identities.
Anti-decompileradecPost-hardeningIndirectbr trampolines, asm junk, pointer aliasing.
Code virtualisationvmVirtualisationCompiles the function body into a private bytecode stream.

Module-only:

PassIDDescription
Function mergingfmergeCollapses annotated functions sharing a group= label into one selector-dispatched super-function; runs first, so the merged bodies feed the function pipeline.
String encryptionstrencEncrypts 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.


How it works

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.

xollvm end-to-end pipeline: source annotations to obfuscated IR


Quick start

Option 1 โ€” Download a prebuilt release

Grab from Releases:

FileWhatOS
xollvm-linux-Release.tar.zstclang/opt with the obfuscator built inLinux x86_64
xollvm-windows-Release.7zclang/opt with the obfuscator built inWindows x64
Obfuscator-linux-x64.soloadable -fpass-pluginLinux x86_64

Backends included: X86;AArch64;ARM;RISCV.

Option 2 โ€” Build the toolchain from stock LLVM (static extension)

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 install

Note

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.

Option 3 โ€” Build the loadable plugin (.so)

# 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

Annotate functions

// 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.


Run it

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.obf

Loadable 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).


Reproducibility

Seeds cascade base โ†’ module โ†’ function โ†’ pass:

Hierarchical deterministic seed derivation

  • -obf-seed=<N> pins the base seed.
  • -obf-deterministic derives the module seed from a stable hash of the module id (when seed is 0).
  • -obf-seed-manifest=seeds.json dumps the full manifest.

Documentation

DocumentPurpose
BUILD.mdFull compilation guide โ€” static-extension toolchain (Linux/Windows), .so plugin, prerequisites, troubleshooting.
USER.mdAnnotation grammar, pass reference, global options, reports, troubleshooting.
DEV.mdArchitecture: registration, annotation cache, pipeline ordering, reporting, adding passes.
VM.mdCode-virtualisation reference โ€” ISA, bytecode format, hardening layers.
FMERGE.mdFunction-merging reference โ€” grouping, memory ABI, dispatch (switch/indirectbr), thunks, selector laundering.
TESTS.mdRuntime test harness, categories, debug workflows.

License

Apache-2.0 WITH LLVM-exception (same as LLVM). See LICENSE.