Loki is an academic obfuscator prototype developed to showcase VM handler hardening techniques.
It is based on our USENIX Security '22 paper:
@inproceedings{schloegel2022loki,
author = {Moritz Schloegel and Tim Blazytko and Moritz Contag and Cornelius Aschermann and Julius Basler and Thorsten Holz and Ali Abbasi},
title = {{Loki}: Hardening Code Obfuscation Against Automated Attacks},
year = {2022},
booktitle = {USENIX Security Symposium}
}
This repository contains the original research prototype and evaluation tooling, plus branch-local build and virtualization updates for reproducible Linux development and CFG-heavy C/C++ samples. The project remains research code and is not production-ready. Unless otherwise specified, code and data are licensed under AGPL3. Loki received the Artifact Available, Artifact Functional, and Artifact Reproduced badges from the USENIX Artifact Evaluation Committee.
We released an additional artifact on Zenodo containing binaries and data produced during the evaluation.
A technical report with more details is available on arXiv.
- loki: obfuscator prototype, translator, Rust obfuscator, testcases, and scripts to generate obfuscated targets
- lokiattack: attack tooling for binaries obfuscated by Loki
- experiments: documented evaluation experiments and reproduction scripts
- vmsamples: Linux VM sample suite and Loki-virtualized shared-library validation scripts
The easiest supported workflow is the devcontainer image. The Dockerfile can also build the environment locally.
The devcontainer uses:
ghcr.io/llvmparty/loki:20230720-18.04-llvm9
Open the repository in a devcontainer-capable editor, or use the Dev Containers CLI:
devcontainer up --workspace-folder .
devcontainer exec --workspace-folder . bashThe devcontainer mounts the repository at /home/user/loki and runs ./setup.sh after creation.
Run:
./docker_build.shThe build uses BuildKit and compiles LLVM with all available cores while serializing LLVM link jobs by default:
PARALLEL_JOBS=$(nproc)
LLVM_PARALLEL_LINK_JOBS=1Override either variable when needed:
PARALLEL_JOBS=16 LLVM_PARALLEL_LINK_JOBS=1 ./docker_build.shUbuntu 18.04 requires ESM packages for reproducible 2026 builds. If you have an Ubuntu Pro attach config, provide it as a BuildKit secret without committing it:
PRO_ATTACH_CONFIG=pro-attach-config.yaml ./docker_build.shpro-attach-config*.yaml is ignored by git.
The image keeps LLVM installed under /llvm and avoids carrying full LLVM source/build trees in the final image. LLVM remains pinned to Loki's original snapshot rather than an LLVM release tag.
After pulling or building an image, run:
./docker_run.shRun the same command again to attach to the existing container. Stop and remove the container with:
./docker_stop.shInside the container:
./setup.shThe script builds Loki, installs LokiAttack Python dependencies, installs Triton when missing, and installs Intel Pin for Experiment 2. It uses PARALLEL_JOBS from the environment.
This branch adds a reusable shared-library virtualization driver:
loki/translator/loki-virtualize.sh \
--source sample.cpp \
--output libsample_loki.so \
--no-annotation \
--exported-functionsFor LLVM bitcode input:
loki/translator/loki-virtualize.sh \
--input sample.bc \
--output libsample_loki.so \
--function target_functionTarget selection options:
--annotation NAME virtualize annotated functions; default: loki_virtualize
--function NAME virtualize a named function; repeatable
--exported-functions virtualize defined externally visible functions except main
--all-functions virtualize every defined non-intrinsic function
Supported operations currently include scalar integer and pointer-compatible operations up to 64 bits:
add, sub, xor, and, or, mul
shl, lshr, ashr
udiv, sdiv, urem, srem
integer icmp
integer/pointer select
The driver uses a generated Loki uop dispatcher:
loki/translator/generate_uop_dispatch.py
loki/translator/src/uop_dispatch.map
uop_dispatch.map contains stable randomized opcodes and XOR encoding keys. Keep it when repeated protections should use the same uop ABI. Regenerate it when you want a new protected ABI:
loki/translator/loki-virtualize.sh \
--source sample.cpp \
--output libsample_loki.so \
--regenerate-uop-map \
--uop-map-seed project-seedvirtualize-uops batches dependent straight-line expression trees inside basic blocks. The default cap is 12 supported instructions per batch, matching the paper's superoperator depth range.
Relevant options:
--no-batch disable target-specific expression batching
--max-batch-nodes N maximum supported instructions per batch; default: 12
The batching flow is:
- Transform selected target functions to encoded Loki VM calls.
- Emit target-specific batch semantics into
uop_batches.cpp.inc. - Generate a target-specific
uop_dispatch.cppcontaining the base uops and batch semantics. - Obfuscate that dispatcher with Loki.
- Link the transformed target bitcode with the generated Loki VM bitcode.
CFG is preserved by native stitching. Branches, loops, switches, PHIs, memory operations, and external calls remain native. Straight-line arithmetic/predicate expressions inside basic blocks execute through Loki-generated VM code.
--inline-vm exists as an experimental option. The default path links VM bitcode directly because llvm-link + always-inline is not stable for all generated Loki VM instances.
Loki symbols remain visible by default so release packaging can strip them separately. Use --hide-loki-symbols if you want the driver to localize Loki helper symbols during linking.
Build and test the Linux sample suite:
cd vmsamples
./test_loki_vm.shThe script builds:
vmsamples/build/libvm_test_suite.so
vmsamples/build/libvm_test_suite_loki.so
vmsamples/build/check_vm_test_suite
The latest validated Loki path produces:
formed 27 batched VM entries; 94 total VM entries
verified 94 direct Loki VM entries across 23 functions
summary: 892 passed, 0 failed, 0 skipped
The main virtualized shared library is:
vmsamples/build/libvm_test_suite_loki.so
Generated artifacts under vmsamples/build/ are ignored by git.
For the original prototype workflow, use loki/obfuscate.py:
cd loki
python3 obfuscate.py --allow se_analysis /tmp/loki-evalThe original pipeline assumes a selected extern "C" target_function and has limited control-flow support. The branch-local loki-virtualize.sh path adds native-CFG-compatible shared-library virtualization for broader C/C++ targets.
Inside the container, the manual equivalent of setup.sh is:
cd loki
./build.sh
cd ../lokiattack
python3 -m pip install --user -r requirements.txt
./install_triton.sh
cd ../experiments/experiment_02_coverage/tracer
./install_pin.shFor more information about the original Loki project, contact m_u00d8 (@m_u00d8) or mrphrazer (@mr_phrazer).