🛡️ RustAegis
Next-Generation Virtualization & Obfuscation Framework for Rust
RustAegis is a research-grade software protection system that compiles Rust code into custom, polymorphic virtual machine bytecode. It is designed to protect sensitive logic against reverse engineering and tampering by moving execution from the native CPU to a secure, randomized software interpreter.
🚀 Key Features
- Virtualization: Converts Rust AST directly into a custom stack-based VM instruction set with 100+ opcodes.
- Rich Type Support: Strings, Vectors/Arrays, integers (signed/unsigned), booleans with proper type tracking.
- Heap Memory: Dynamic memory allocation with automatic cleanup on scope exit.
- Variable Shadowing: Full Rust-like scoping with nested block support.
- Polymorphism: The instruction set mapping (Opcode Table) is randomized for every build via a
.build_seedartifact. - Mixed Boolean-Arithmetic (MBA): Transforms simple arithmetic (
+,-,^) into complex, mathematically equivalent boolean expressions. - Compile-Time Encryption: Bytecode is encrypted with a unique key per build and decrypted only at runtime.
- Anti-Tamper: Integrated integrity checks ensure the bytecode has not been modified.
- Junk Code Injection: Inserts dead code and entropy-based instructions to break signature scanning.
- WASM Support: Full WebAssembly compatibility for browser and Node.js environments.
📦 Installation
Add the following to your Cargo.toml:
[]
= "0.1.5"
🛠️ Usage
Apply the #[vm_protect] attribute to any sensitive function you wish to virtualize.
use vm_protect;
// Standard protection (Polymorphism + Encryption)
// Paranoid protection (Heavy MBA + Obfuscation)
// Use this for critical logic like key derivation.
// Advanced: Strings, Arrays, and Control Flow
⚙️ Architecture & The .build_seed
RustAegis uses a split architecture:
- Compiler (
vm-macro): Runs at compile time, generating encrypted bytecode. - Runtime (
vm): Runs inside your application, executing the bytecode.
Synchronization via .anticheat_build_seed
To ensure the compiler uses the exact same encryption keys and opcode mapping that the runtime expects, the system generates a temporary artifact named .anticheat_build_seed in your project root during the build process.
- Local Development: This happens automatically. If you encounter a "Build ID mismatch" error, simply run
cargo cleanto regenerate the seed. - CI/CD: The seed is unique to each build environment. Do not commit
.anticheat_build_seedto version control if you want unique polymorphism for every deployment. - Reproducible Builds: If you need exactly the same VM bytecode across different machines, you can set the
ANTICHEAT_BUILD_KEYenvironment variable. This overrides the random generation.
# For reproducible builds (same opcodes, same keys)
🔍 Analysis & Reverse Engineering
RustAegis significantly complicates static and dynamic analysis by flattening control flow and obfuscating data flow.
Control Flow Flattening
The VM interpreter acts as a massive switch statement (dispatcher). The original control flow (if/else, loops) is flattened into data-driven jumps within the interpreter loop.
Native CFG:
Distinct blocks for if, else, and return, easily readable by decompilers.
Figure 1: Native assembly of the license check function. Logic is linear and easy to follow.
VM CFG: A single "God Node" (the dispatcher) with edges pointing back to itself. The actual logic is hidden in the bytecode data, not the CPU instructions.
Figure 2: The same function protected by the VM. The control flow is flattened into the VM's fetch-decode-execute loop.
Arithmetic Obfuscation (MBA)
Instead of a single ADD instruction, the analyst sees a randomized sequence of stack operations implementing mathematically equivalent formulas like:
x + y = (x ^ y) + 2 * (x & y) or (x | y) + (x & y)
Figure 3: Even a simple arithmetic function explodes into a complex graph due to MBA transformations and the VM dispatcher overhead.
⚡ Performance & Constraints
Virtualization comes with a cost. RustAegis is designed for security, not speed.
- Performance: Expect a 10x-100x slowdown compared to native code. This is standard for software-based virtualization.
- Usage: Apply
#[vm_protect]only to sensitive functions (license checks, key generation, encryption logic). Do not virtualize tight loops in performance-critical rendering or physics code. - Supported Platforms: Works on
x86_64,aarch64,wasm32, and any platform supported by Ruststdoralloc(no_std compatible).
📂 Examples
Check the examples/ directory for complete test cases:
01_arithmetic.rs: Demonstrates MBA transformations.02_control_flow.rs: Demonstrates if/else logic protection.03_loops.rs: Demonstrates loop virtualization.04_wasm.rs: Demonstrates WASM integration.wasm_test/: Complete WASM test project withwasm-pack.
Run them with:
# For WASM tests
🌐 WASM Support
RustAegis fully supports WebAssembly. To use with WASM:
Setup
# Add WASM target
# Install wasm-pack (optional, for building/testing)
Cargo.toml Configuration
[]
= { = "0.1.5", = false }
= "0.2"
Usage Pattern
Since #[vm_protect] and #[wasm_bindgen] cannot be combined directly, use a wrapper:
use vm_protect;
use *;
// VM-protected implementation
// WASM export wrapper
Building WASM
# Build for web
# Run tests with Node.js
# Run tests in headless browser
The compiled .wasm file will be in pkg/ directory.
📋 Changelog
v0.1.5
Major Refactoring:
- Modular Compiler Architecture: Compiler split into specialized modules (
expr.rs,stmt.rs,literal.rs,array.rs,control.rs,method.rs,cast.rs,emit.rs) for better maintainability. - Proper Scope Management: Implemented
Vec<BTreeMap<String, VarInfo>>for nested scope support with correct variable shadowing. - Automatic Memory Cleanup:
HEAP_FREEis now emitted on scope exit for String/Vector variables, preventing memory leaks.
New Features:
- Variable Shadowing: Inner scopes can now shadow outer variables correctly (e.g.,
let x = 10; { let x = 20; }works as expected). - VarType Enum: Reliable type tracking for Integer, String, Vector, and Bool types.
- Signed Division: Added
IDIVandIMODopcodes for signed integer division/modulo. - Bit Counting Methods: Support for
.count_ones(),.count_zeros(),.leading_zeros(),.trailing_zeros().
Improvements:
- 68 tests passing (up from 332)
- Cleaned up unused emit functions and redundant code
- Better type inference from expressions and type annotations
v0.1.4
New Features:
- Heap Memory Support: Bump allocator with 256 dynamic registers for complex data structures.
- String Operations: Full string support with
len(),get(),push(),concat(),eq(),hash(),is_empty(). - Vector Operations: Array/vector support with
len(),get(),push(),pop(),set(). - Type Casts: Support for
as u8,as u16,as u32,as u64,as i8,as i16,as i32,as i64.
v0.1.3
New Features:
- ValueCryptor (VMProtect-style): Constants are now encrypted at compile-time with a chain of 3-7 cryptographic operations (ADD, SUB, XOR, ROL, ROR, NOT, NEG). The decryption chain is emitted as bytecode, preventing constants from appearing in plaintext.
- Region-based Integrity Checking: Bytecode is divided into 64-byte regions, each with a precomputed FNV-1a hash. Tampering is detected at load time with detailed region identification for Paranoid level.
- Integrity Hash Verification: All encrypted bytecode now includes a full integrity hash verified after decryption.
Protection Levels:
| Level | ValueCryptor | Full Hash | Region Hash |
|---|---|---|---|
| debug | No | No | No |
| standard | No | Yes | No |
| paranoid | Yes | Yes | Yes |
Note on Runtime Integrity: The current integrity checking protects against static patching (modifications to bytecode on disk). Runtime memory patching detection (continuous integrity checks during execution) is intentionally not included in this version to avoid performance overhead. This may be added as an optional feature in future releases for users who require protection against debugger-based runtime patching.
Improvements:
- 332 tests passing (up from ~160)
- Better compile-time hash computation using build-specific FNV constants
- Cleaner separation between compile-time and runtime integrity modules
v0.1.2
New Features:
- WASM/WebAssembly Support: Full
no_stdcompatibility forwasm32-unknown-unknowntarget - WASM Example: Added
examples/04_wasm.rsandexamples/wasm_test/project withwasm-packintegration - Industry-Standard Obfuscation: Added new substitution patterns to
substitution.rs:AddSubstitution- Multiple arithmetic identity transformations for ADDSubSubstitution- Multiple arithmetic identity transformations for SUBMulSubstitution- Multiplication obfuscation patternsXorSubstitution- XOR identity transformationsDeadCodeInsertion- Deterministic dead code injectionOpaquePredicate- Always-true/always-false conditionsComparisonSubstitution- Comparison obfuscationControlFlowSubstitution- Control flow helpers
Bug Fixes:
- Fixed
std::hint::black_box→core::hint::black_boxinbuild.rsforno_stdcompatibility - Fixed
SystemTimeusage with proper#[cfg(feature = "std")]guards instate.rsandnative.rs - Refactored
compiler.rsto use centralizedSubstitutionmodule instead of inline implementations
Improvements:
- Deterministic dead code insertion using position-based entropy (no RNG dependency)
- Better separation of concerns between compiler and substitution modules
v0.1.1
- Initial public release
- Core VM engine with 60+ opcodes
- MBA (Mixed Boolean-Arithmetic) transformations
- Compile-time encryption with AES-256-GCM
- Polymorphic opcode shuffling
⚠️ Disclaimer
This project is for educational and research purposes only. It is designed to demonstrate concepts in software protection, obfuscation, and compiler theory.
📄 License
MIT