Code virtualizer for compiled 64-bit portable executables.
- Disassembly: input is parsed with exe, and the section containing the entry point is disassembled into basic blocks via iced-x86.
- Mutation: instructions are substituted with algebraically equivalent sequences using dead-flag analysis to preserve correctness.
- Lifting: instructions are translated into a stack-machine bytecode the runtime interprets.
- Mutation: operations are rewritten into logically equivalent forms whose structure varies between builds.
- Permutation: operations are reordered through their data dependencies into a semantically equivalent sequence.
- Encryption: immediate values in the bytecode are masked against a rolling key advanced between immediates, so the runtime decrypts each one against the matching key state.
- Chaining: each block is chained to the tail of the previous so any tamper to an earlier block breaks the decryption of every block that follows.
- Patching: virtualized blocks are replaced with dispatch stubs that transfer control to the VM.
- TLS: VM state lives in Thread Local Storage, so each thread runs the interpreter against its own registers and stacks.
- Dispatch: a stub transfers CPU state to the VM, which interprets the corresponding bytecode through indirect dispatch to handler functions.
- Exceptions: a vectored exception handler catches faults inside the VM region and reconstructs the CPU context for external handlers.
- Attestation: a leading sequence of VM-blocks runs anti-debug and integrity checks whose results feed into the decryption chain.
The tests crate executes assembled bytecode through the VM against allocated executable memory.
- Instructions: each lifted instruction is run through the VM and the resulting register state is compared against the expected value.
- Permutation: each test block is exhausted through every valid scheduler ordering and compared against a reference run, so any divergence surfaces deterministically.
cargo run --release --bin obfuscator -- <filename> --virtualize --mutate
- Fork it
- Create your branch (
git checkout -b my-change) - Commit your changes (
git commit -m "changed something") - Push to the branch (
git push origin my-change) - Create new pull request