LLVM Obfuscation Tools
Standalone command-line tools for obfuscating LLVM bitcode.
Tools
ollvm_obf.py - Python port of omill/tools/ollvm-obf
A nanobind-based Python driver that implements the public
omill/tools/ollvm-obf pipeline:
--string-encrypt--code-clone--substitute--if-convert--loop-to-recursion--flatten--opaque-predicates--bogus-control-flow--bmi-mutate--const-unfold--schedule-instructions--outline-functions--arith-encode--stack-randomize--vectorize--reg-pressure
Example:
uv run tools/obfuscation/ollvm_obf.py \
--code-clone --substitute --if-convert --flatten \
--opaque-predicates --bogus-control-flow --const-unfold \
--schedule-instructions --verify-each input.ll -o output.ll
The CLI also supports --vectorize-data, --vectorize-bitwise,
--vectorize-i64, and --vectorize-percent=N for the vectorization stage.
Stored golden-master fixtures live under tests/golden/ollvm_obf/. The test
suite compares Python output against those checked-in public-pass ollvm-obf
reference files, so normal repo tests do not require a local omill
checkout or C++ ollvm-obf binary.
Canonical golden-master coverage includes selected parity cases for instruction scheduling, if-conversion, BMI mutation, loop-to-recursion, and code cloning:
uv run pytest tests/regressions/test_ollvm_obf_golden_master.py -q
A second-tier semantic suite is also available for passes whose IR shape is expected to differ while runtime behavior should remain equivalent (for example substitution, string encryption, flattening, loop-to-recursion, BMI codegen, vectorization, stack/register-liveness transforms, and a broad full-public- pipeline mixed-module case):
uv run pytest tests/regressions/test_ollvm_obf_semantic_parity.py -q
To regenerate the checked-in golden files from an external C++ ollvm-obf
build, use:
uv run tools/obfuscation/generate_ollvm_obf_goldens.py \
--ollvm-obf /path/to/ollvm-obf
mba_sub - Mixed Boolean Arithmetic Substitution
Replaces arithmetic operations (add, sub, mul, xor, or) with
equivalent but more complex expressions using boolean arithmetic identities.
mba_sub [options] <input.bc> <output.bc>
Options:
--iterations N Number of iterations (default: 1)
--seed N Random seed for reproducibility (default: random)
Example transformations:
x + y→~(x + (-x + (-x + ~y)))x - y→(x ^ -y) + 2*(x & -y)x ^ y→(~x & y) | (x & ~y)x * y→((x|y) * (x&y)) + ((x & ~y) * (y & ~x))x | y→~(~x & ~y)
bb_split - Basic Block Splitter
Splits large basic blocks into smaller ones to increase control flow complexity and make analysis harder.
bb_split [options] <input.bc> <output.bc>
Options:
--iterations N Number of iterations (default: 1)
--min-size N Minimum block size to consider for splitting (default: 10)
--max-size N Maximum block size after splitting (default: 20)
--chance N Percent chance to split eligible blocks (default: 40)
--seed N Random seed for reproducibility (default: random)
indirect_branch - Simple Indirect Branch
Replaces direct branches with indirect branches through a block address array, making control flow analysis harder.
indirect_branch [options] <input.bc> <output.bc>
Options:
--iterations N Number of iterations (default: 1)
--chance N Percent chance to replace branches (default: 50)
--seed N Random seed for reproducibility (default: random)
control_flow_flatten - Control Flow Flattening
Flattens control flow by converting all basic blocks into a switch-based dispatcher pattern. Each block gets a random state value, and control flow is routed through a central dispatcher.
control_flow_flatten [options] <input.bc> <output.bc>
Options:
--iterations N Number of iterations (default: 1)
--use-func-resolver N Percent chance to use function call for state check (default: 0)
--use-global-state N Percent chance to use global variables for state (default: 0)
--use-opaque N Percent chance to use opaque predicates (default: 0)
--use-global-opaque N Percent chance to use globals in opaques (default: 0)
--use-siphash N Percent chance to use SipHash state transform (default: 0)
--clone-siphash N Percent chance to clone SipHash function (default: 0)
--seed N Random seed for reproducibility (default: random)
The SipHash option adds cryptographic hashing to the state comparison, making it much harder to recover the original control flow through static analysis.
indirect_branch_enc - Encrypted Indirect Branch
Advanced version of indirect_branch that uses XTEA encryption for branch target addresses. Includes runtime XTEA decryption code.
indirect_branch_enc [options] <input.bc> <output.bc>
Options:
--iterations N Number of iterations (default: 1)
--chance N Percent chance to replace branches (default: 50)
--seed N Random seed for reproducibility (default: random)
string_encrypt - String Encryption
Encrypts string constants using XOR cipher with SplitMix32 PRNG. Supports two modes:
- global: Decryption happens at program startup via
.ctors - stack: Each string is decrypted on the stack at point of use
string_encrypt [options] <input.bc> <output.bc>
Options:
--mode MODE Encryption mode: 'global' or 'stack' (default: global)
--skip-prefix P Skip strings starting with this prefix
--seed N Random seed for reproducibility (default: random)
Building
These tools are built automatically with the main project:
cmake -B build -G Ninja
cmake --build build --target mba_sub bb_split indirect_branch \
control_flow_flatten indirect_branch_enc string_encrypt
Testing
Run the test script to verify the tools produce expected output:
./tools/obfuscation/tests/run_tests.sh
Compare with the golden master output:
./tools/obfuscation/tests/run_tests.sh | diff - tools/obfuscation/tests/expected_output.txt
Usage Example
Chain multiple obfuscation passes:
# Convert source to bitcode
clang -emit-llvm -c input.c -o input.bc
# Apply obfuscation passes (order matters!)
./build/string_encrypt --seed=42 input.bc step1.bc
./build/mba_sub --seed=42 step1.bc step2.bc
./build/bb_split --seed=42 step2.bc step3.bc
./build/control_flow_flatten --seed=42 step3.bc step4.bc
./build/indirect_branch --seed=42 step4.bc output.bc
# Compile to native code
clang output.bc -o output
Reproducibility
All tools accept a --seed option for deterministic output. This is useful for:
- Golden master testing
- Debugging
- Reproducible builds
When --seed is not specified, a random seed is used.
Pass Descriptions
| Pass | Complexity | Description |
|---|---|---|
mba_sub |
Low | Replaces arithmetic with boolean algebra |
bb_split |
Low | Splits large basic blocks |
indirect_branch |
Medium | Simple indirect branches |
control_flow_flatten |
High | Switch-based dispatcher |
indirect_branch_enc |
High | XTEA-encrypted branches |
string_encrypt |
Medium | XOR-encrypted strings |