use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH};
fn main() {
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
let dest_path = Path::new(&out_dir).join("build_config.rs");
let mut f = File::create(&dest_path).expect("Could not create build_config.rs");
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
let build_seed = generate_build_seed();
let build_id = derive_build_id(&build_seed);
let customer_id = env::var("ANTICHEAT_CUSTOMER_ID")
.unwrap_or_else(|_| "dev-customer".to_string());
let watermark = generate_watermark(&customer_id, &build_seed, timestamp);
writeln!(f, "/// Build timestamp (Unix epoch seconds)").unwrap();
writeln!(f, "pub const BUILD_TIMESTAMP: u64 = {};", timestamp).unwrap();
writeln!(f).unwrap();
let mut entropy_pool = [0u8; 1024];
let pool_seed = generate_random_seed(); let mut rng_state = pool_seed;
for (i, byte) in entropy_pool.iter_mut().enumerate() {
let mac = hmac_sha256(&rng_state, &(i as u32).to_le_bytes());
*byte = mac[0];
if i % 32 == 0 {
rng_state = mac;
}
}
let rnd = generate_random_seed();
let start_offset = (u64::from_le_bytes(rnd[0..8].try_into().unwrap()) % 800) as usize;
let step = (u64::from_le_bytes(rnd[8..16].try_into().unwrap()) % 20 + 1) as usize;
let mut deltas = [0u8; 32];
for i in 0..32 {
let pool_idx = (start_offset + i * step) % 1024;
deltas[i] = build_seed[i] ^ entropy_pool[pool_idx];
}
writeln!(f, "/// Entropy pool for seed reconstruction").unwrap();
writeln!(f, "const ENTROPY_POOL: [u8; 1024] = [").unwrap();
for (i, byte) in entropy_pool.iter().enumerate() {
if i % 16 == 0 { write!(f, "\n ").unwrap(); }
write!(f, "0x{:02x}, ", byte).unwrap();
}
writeln!(f, "\n];").unwrap();
writeln!(f).unwrap();
writeln!(f, "/// Delta values for seed reconstruction").unwrap();
writeln!(f, "const SEED_DELTAS: [u8; 32] = [").unwrap();
for (i, byte) in deltas.iter().enumerate() {
if i > 0 { write!(f, ", ").unwrap(); }
write!(f, "0x{:02x}", byte).unwrap();
}
writeln!(f, "];").unwrap();
writeln!(f).unwrap();
writeln!(f, "/// Reconstruct the build seed at runtime").unwrap();
writeln!(f, "/// This prevents the seed from existing plainly in binary").unwrap();
writeln!(f, "#[inline(always)]").unwrap();
writeln!(f, "pub fn get_build_seed() -> [u8; 32] {{").unwrap();
writeln!(f, " let mut seed = [0u8; 32];").unwrap();
writeln!(f, " let start = core::hint::black_box({});", start_offset).unwrap();
writeln!(f, " let step = core::hint::black_box({});", step).unwrap();
writeln!(f, " ").unwrap();
writeln!(f, " for i in 0..32 {{").unwrap();
writeln!(f, " let idx = (start + i * step) % 1024;").unwrap();
writeln!(f, " // The XOR operation combined with table lookups").unwrap();
writeln!(f, " seed[i] = ENTROPY_POOL[idx] ^ SEED_DELTAS[i];").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " seed").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
writeln!(f, "/// Build ID derived from BUILD_SEED (for watermarking)").unwrap();
writeln!(f, "pub const BUILD_ID: u64 = 0x{:016x};", build_id).unwrap();
writeln!(f).unwrap();
writeln!(f, "/// Customer ID for build tracking (from ANTICHEAT_CUSTOMER_ID env)").unwrap();
writeln!(f, "pub const CUSTOMER_ID: &str = \"{}\";", customer_id).unwrap();
writeln!(f).unwrap();
writeln!(f, "/// 128-bit steganographic watermark derived from customer + build").unwrap();
writeln!(f, "/// Used for identifying leaked builds").unwrap();
write!(f, "pub const WATERMARK: [u8; 16] = [").unwrap();
for (i, byte) in watermark.iter().enumerate() {
if i > 0 {
write!(f, ", ").unwrap();
}
write!(f, "0x{:02x}", byte).unwrap();
}
writeln!(f, "];").unwrap();
writeln!(f).unwrap();
let watermark_hi = u64::from_le_bytes([
watermark[0], watermark[1], watermark[2], watermark[3],
watermark[4], watermark[5], watermark[6], watermark[7],
]);
let watermark_lo = u64::from_le_bytes([
watermark[8], watermark[9], watermark[10], watermark[11],
watermark[12], watermark[13], watermark[14], watermark[15],
]);
writeln!(f, "/// Watermark high 64 bits").unwrap();
writeln!(f, "pub const WATERMARK_HI: u64 = 0x{:016x};", watermark_hi).unwrap();
writeln!(f, "/// Watermark low 64 bits").unwrap();
writeln!(f, "pub const WATERMARK_LO: u64 = 0x{:016x};", watermark_lo).unwrap();
writeln!(f).unwrap();
if let Some(git_hash) = get_git_hash() {
writeln!(f, "/// Git commit hash (first 16 hex chars)").unwrap();
writeln!(f, "pub const GIT_COMMIT: &str = \"{}\";", git_hash).unwrap();
} else {
writeln!(f, "/// Git commit hash (not available)").unwrap();
writeln!(f, "pub const GIT_COMMIT: &str = \"unknown\";").unwrap();
}
writeln!(f).unwrap();
let protection_level = env::var("ANTICHEAT_PROTECTION_LEVEL")
.unwrap_or_else(|_| "medium".to_string());
writeln!(f, "/// Protection level (from ANTICHEAT_PROTECTION_LEVEL env)").unwrap();
writeln!(f, "pub const PROTECTION_LEVEL: &str = \"{}\";", protection_level).unwrap();
writeln!(f).unwrap();
let build_seq = env::var("ANTICHEAT_BUILD_SEQ")
.unwrap_or_else(|_| "0".to_string())
.parse::<u32>()
.unwrap_or(0);
writeln!(f, "/// Build sequence number (from ANTICHEAT_BUILD_SEQ env)").unwrap();
writeln!(f, "pub const BUILD_SEQ: u32 = {};", build_seq).unwrap();
writeln!(f).unwrap();
let yield_mask = generate_yield_mask(&build_seed);
writeln!(f, "/// Async VM yield mask (controls yield frequency)").unwrap();
writeln!(f, "/// Lower value = more frequent yields = more obfuscation").unwrap();
writeln!(f, "pub const YIELD_MASK: u64 = 0x{:02x};", yield_mask).unwrap();
writeln!(f).unwrap();
let opcode_table = generate_opcode_table(&build_seed);
write_opcode_table(&mut f, &opcode_table);
write_shared_opcode_table(&opcode_table);
let magic_bytes = generate_magic_bytes(&build_seed);
write_magic_bytes(&mut f, &magic_bytes);
let native_ids = generate_native_ids(&build_seed);
write_native_ids(&mut f, &native_ids);
let register_map = generate_register_map(&build_seed);
write_register_map(&mut f, ®ister_map);
let fnv_constants = generate_fnv_constants(&build_seed);
write_fnv_constants(&mut f, &fnv_constants);
let xor_key = generate_xor_key(&build_seed);
write_xor_key(&mut f, xor_key);
let flag_bits = generate_flag_bits(&build_seed);
write_flag_bits(&mut f, &flag_bits);
let mutated_handlers_path = Path::new(&out_dir).join("mutated_handlers.rs");
generate_mutated_handlers(&build_seed, &mutated_handlers_path);
generate_whitebox_config(&mut f, &build_seed);
write_build_history(
&build_seed, build_id, timestamp, &customer_id, &opcode_table,
&magic_bytes, &native_ids, ®ister_map, &fnv_constants, xor_key, &flag_bits
);
println!("cargo:rerun-if-env-changed=ANTICHEAT_BUILD_KEY");
println!("cargo:rerun-if-env-changed=ANTICHEAT_PROTECTION_LEVEL");
println!("cargo:rerun-if-env-changed=ANTICHEAT_CUSTOMER_ID");
println!("cargo:rerun-if-env-changed=ANTICHEAT_BUILD_SEQ");
println!("cargo:rerun-if-changed=build.rs");
if let Ok(out_dir) = env::var("OUT_DIR") {
if let Some(target_dir) = Path::new(&out_dir)
.ancestors()
.find(|p| p.file_name().is_some_and(|n| n == "target"))
{
let seed_file = target_dir.join(".anticheat_build_seed");
println!("cargo:rerun-if-changed={}", seed_file.display());
}
}
}
fn generate_watermark(customer_id: &str, build_seed: &[u8; 32], timestamp: u64) -> [u8; 16] {
let mut input = Vec::new();
input.extend_from_slice(customer_id.as_bytes());
input.extend_from_slice(build_seed);
input.extend_from_slice(×tamp.to_le_bytes());
input.extend_from_slice(b"watermark-v1");
let hash = sha256(&input);
let mut watermark = [0u8; 16];
watermark.copy_from_slice(&hash[..16]);
watermark
}
fn generate_yield_mask(build_seed: &[u8; 32]) -> u64 {
let base = (build_seed[16] ^ build_seed[31]) as u64;
0x3F | (base & 0xC0)
}
fn generate_build_seed() -> [u8; 32] {
if let Ok(key) = env::var("ANTICHEAT_BUILD_KEY") {
let seed = hmac_sha256(key.as_bytes(), b"anticheat-vm-seed-v1");
write_shared_seed(&seed);
return seed;
}
let seed = generate_random_seed();
write_shared_seed(&seed);
seed
}
fn write_shared_seed(seed: &[u8; 32]) {
if let Ok(out_dir) = env::var("OUT_DIR") {
let target_dir = Path::new(&out_dir)
.ancestors()
.find(|p| p.file_name().is_some_and(|n| n == "target"));
if let Some(target) = target_dir {
let seed_file = target.join(".anticheat_build_seed");
if let Ok(mut f) = File::create(&seed_file) {
for byte in seed {
let _ = write!(f, "{:02x}", byte);
}
}
}
}
}
fn write_shared_opcode_table(table: &OpcodeTable) {
if let Ok(out_dir) = env::var("OUT_DIR") {
let target_dir = Path::new(&out_dir)
.ancestors()
.find(|p| p.file_name().is_some_and(|n| n == "target"));
if let Some(target) = target_dir {
let table_file = target.join(".anticheat_opcode_table");
if let Ok(mut f) = File::create(&table_file) {
for byte in &table.encode {
let _ = write!(f, "{:02x}", byte);
}
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn write_build_history(
seed: &[u8; 32],
build_id: u64,
timestamp: u64,
customer_id: &str,
opcode_table: &OpcodeTable,
magic_bytes: &[u8; 4],
native_ids: &NativeIdMap,
register_map: &RegisterMap,
fnv_constants: &FnvConstants,
xor_key: u8,
flag_bits: &FlagBits,
) {
use std::fs::OpenOptions;
let history_path = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
Some(Path::new(&manifest_dir).join("build_history.txt"))
} else {
Some(Path::new("build_history.txt").to_path_buf())
};
let Some(history_path) = history_path else { return };
let Ok(mut file) = OpenOptions::new()
.create(true)
.append(true)
.open(&history_path) else { return };
let datetime = format_timestamp(timestamp);
writeln!(file, "================================================================================").ok();
writeln!(file, "BUILD: {}", datetime).ok();
writeln!(file, "================================================================================").ok();
writeln!(file, "Timestamp: {} ({})", timestamp, datetime).ok();
writeln!(file, "Customer ID: {}", customer_id).ok();
writeln!(file, "Build ID: 0x{:016x}", build_id).ok();
writeln!(file).ok();
write!(file, "Seed: ").ok();
for byte in seed {
write!(file, "{:02x}", byte).ok();
}
writeln!(file).ok();
writeln!(file).ok();
writeln!(file, "MAGIC Bytes: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}",
magic_bytes[0], magic_bytes[1], magic_bytes[2], magic_bytes[3]).ok();
writeln!(file).ok();
writeln!(file, "Shuffled Opcodes (base -> shuffled):").ok();
writeln!(file, " PUSH_IMM8: 0x02 -> 0x{:02x}", opcode_table.encode[0x02]).ok();
writeln!(file, " PUSH_IMM: 0x01 -> 0x{:02x}", opcode_table.encode[0x01]).ok();
writeln!(file, " DROP: 0x07 -> 0x{:02x}", opcode_table.encode[0x07]).ok();
writeln!(file, " ADD: 0x20 -> 0x{:02x}", opcode_table.encode[0x20]).ok();
writeln!(file, " SUB: 0x21 -> 0x{:02x}", opcode_table.encode[0x21]).ok();
writeln!(file, " MUL: 0x22 -> 0x{:02x}", opcode_table.encode[0x22]).ok();
writeln!(file, " XOR: 0x23 -> 0x{:02x}", opcode_table.encode[0x23]).ok();
writeln!(file, " CMP: 0x30 -> 0x{:02x}", opcode_table.encode[0x30]).ok();
writeln!(file, " JMP: 0x31 -> 0x{:02x}", opcode_table.encode[0x31]).ok();
writeln!(file, " JZ: 0x32 -> 0x{:02x}", opcode_table.encode[0x32]).ok();
writeln!(file, " JNZ: 0x33 -> 0x{:02x}", opcode_table.encode[0x33]).ok();
writeln!(file, " NOP: 0x40 -> 0x{:02x}", opcode_table.encode[0x40]).ok();
writeln!(file, " HALT: 0xFF -> 0xff (fixed)").ok();
writeln!(file).ok();
writeln!(file, "Handler Duplication (aliases that decode to same base):").ok();
for &base in DUPLICATED_OPCODES {
let base_name = match base {
0x20 => "ADD",
0x21 => "SUB",
0x23 => "XOR",
0x24 => "AND",
0x25 => "OR",
0x30 => "CMP",
_ => "???",
};
let primary = opcode_table.encode[base as usize];
if let Some(aliases) = opcode_table.aliases.get(&base) {
let alias_str: Vec<String> = aliases.iter().map(|a| format!("0x{:02x}", a)).collect();
writeln!(file, " {}: primary=0x{:02x}, aliases=[{}]",
base_name, primary, alias_str.join(", ")).ok();
}
}
writeln!(file).ok();
writeln!(file, "Native Function IDs:").ok();
writeln!(file, " CHECK_ROOT: {}", native_ids.check_root).ok();
writeln!(file, " CHECK_EMULATOR: {}", native_ids.check_emulator).ok();
writeln!(file, " CHECK_HOOKS: {}", native_ids.check_hooks).ok();
writeln!(file, " CHECK_DEBUGGER: {}", native_ids.check_debugger).ok();
writeln!(file, " CHECK_TAMPER: {}", native_ids.check_tamper).ok();
writeln!(file, " GET_TIMESTAMP: {}", native_ids.get_timestamp).ok();
writeln!(file, " HASH_FNV1A: {}", native_ids.hash_fnv1a).ok();
writeln!(file, " READ_MEMORY: {}", native_ids.read_memory).ok();
writeln!(file, " GET_DEVICE_HASH: {}", native_ids.get_device_hash).ok();
writeln!(file).ok();
writeln!(file, "Register Mapping (logical -> physical):").ok();
for i in 0..8 {
writeln!(file, " R{} -> physical {}", i, register_map.map[i]).ok();
}
writeln!(file).ok();
writeln!(file, "FNV Hash Constants:").ok();
writeln!(file, " BASIS_64: 0x{:016x}", fnv_constants.basis_64).ok();
writeln!(file, " PRIME_64: 0x{:016x}", fnv_constants.prime_64).ok();
writeln!(file, " BASIS_32: 0x{:08x}", fnv_constants.basis_32).ok();
writeln!(file, " PRIME_32: 0x{:08x}", fnv_constants.prime_32).ok();
writeln!(file).ok();
writeln!(file, "XOR Obfuscation Key: 0x{:02x}", xor_key).ok();
writeln!(file).ok();
writeln!(file, "Flag Bit Positions:").ok();
writeln!(file, " ZERO: 0b{:08b}", flag_bits.zero).ok();
writeln!(file, " CARRY: 0b{:08b}", flag_bits.carry).ok();
writeln!(file, " OVERFLOW: 0b{:08b}", flag_bits.overflow).ok();
writeln!(file, " SIGN: 0b{:08b}", flag_bits.sign).ok();
writeln!(file).ok();
writeln!(file).ok();
}
fn format_timestamp(timestamp: u64) -> String {
let secs = timestamp;
let days_since_epoch = secs / 86400;
let time_of_day = secs % 86400;
let hours = time_of_day / 3600;
let minutes = (time_of_day % 3600) / 60;
let seconds = time_of_day % 60;
let mut year = 1970;
let mut remaining_days = days_since_epoch;
loop {
let days_in_year = if is_leap_year(year) { 366 } else { 365 };
if remaining_days < days_in_year {
break;
}
remaining_days -= days_in_year;
year += 1;
}
let days_in_months: [u64; 12] = if is_leap_year(year) {
[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
} else {
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
};
let mut month = 1;
for days in days_in_months.iter() {
if remaining_days < *days {
break;
}
remaining_days -= days;
month += 1;
}
let day = remaining_days + 1;
format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02} UTC",
year, month, day, hours, minutes, seconds)
}
fn is_leap_year(year: u64) -> bool {
(year.is_multiple_of(4) && !year.is_multiple_of(100)) || year.is_multiple_of(400)
}
fn generate_random_seed() -> [u8; 32] {
use std::io::Read;
let mut seed = [0u8; 32];
if let Ok(mut file) = File::open("/dev/urandom") {
if file.read_exact(&mut seed).is_ok() {
return seed;
}
}
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default();
let mut entropy = Vec::new();
entropy.extend_from_slice(×tamp.as_nanos().to_le_bytes());
entropy.extend_from_slice(&std::process::id().to_le_bytes());
if let Ok(pwd) = env::var("PWD") {
entropy.extend_from_slice(pwd.as_bytes());
}
if let Ok(user) = env::var("USER") {
entropy.extend_from_slice(user.as_bytes());
}
sha256(&entropy)
}
fn derive_build_id(seed: &[u8; 32]) -> u64 {
const BUILDID_DOMAIN: &[u8] = b"anticheat-vm-build-id-v1";
let hmac_result = hmac_sha256(seed, BUILDID_DOMAIN);
u64::from_le_bytes([
hmac_result[0], hmac_result[1], hmac_result[2], hmac_result[3],
hmac_result[4], hmac_result[5], hmac_result[6], hmac_result[7],
])
}
fn get_git_hash() -> Option<String> {
use std::process::Command;
let output = Command::new("git")
.args(["rev-parse", "--short=16", "HEAD"])
.output()
.ok()?;
if output.status.success() {
let hash = String::from_utf8_lossy(&output.stdout);
Some(hash.trim().to_string())
} else {
None
}
}
fn sha256(data: &[u8]) -> [u8; 32] {
let mut h: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];
const K: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];
let ml = (data.len() as u64) * 8;
let mut padded = data.to_vec();
padded.push(0x80);
while (padded.len() % 64) != 56 {
padded.push(0x00);
}
padded.extend_from_slice(&ml.to_be_bytes());
for chunk in padded.chunks(64) {
let mut w = [0u32; 64];
for i in 0..16 {
w[i] = u32::from_be_bytes([
chunk[i * 4],
chunk[i * 4 + 1],
chunk[i * 4 + 2],
chunk[i * 4 + 3],
]);
}
for i in 16..64 {
let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16].wrapping_add(s0).wrapping_add(w[i - 7]).wrapping_add(s1);
}
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh) =
(h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
for i in 0..64 {
let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
let ch = (e & f) ^ ((!e) & g);
let temp1 = hh.wrapping_add(s1).wrapping_add(ch).wrapping_add(K[i]).wrapping_add(w[i]);
let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
let maj = (a & b) ^ (a & c) ^ (b & c);
let temp2 = s0.wrapping_add(maj);
hh = g;
g = f;
f = e;
e = d.wrapping_add(temp1);
d = c;
c = b;
b = a;
a = temp1.wrapping_add(temp2);
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
h[5] = h[5].wrapping_add(f);
h[6] = h[6].wrapping_add(g);
h[7] = h[7].wrapping_add(hh);
}
let mut result = [0u8; 32];
for (i, &word) in h.iter().enumerate() {
result[i * 4..(i + 1) * 4].copy_from_slice(&word.to_be_bytes());
}
result
}
#[rustfmt::skip]
const BASE_OPCODES: &[(& str, &str, u8)] = &[
("stack", "PUSH_IMM", 0x01),
("stack", "PUSH_IMM8", 0x02),
("stack", "PUSH_REG", 0x03),
("stack", "POP_REG", 0x04),
("stack", "DUP", 0x05),
("stack", "SWAP", 0x06),
("stack", "DROP", 0x07),
("stack", "PUSH_IMM16", 0x08),
("stack", "PUSH_IMM32", 0x09),
("register", "MOV_IMM", 0x10),
("register", "MOV_REG", 0x11),
("register", "LOAD_MEM", 0x12),
("register", "STORE_MEM", 0x13),
("arithmetic", "ADD", 0x20),
("arithmetic", "SUB", 0x21),
("arithmetic", "MUL", 0x22),
("arithmetic", "XOR", 0x23),
("arithmetic", "AND", 0x24),
("arithmetic", "OR", 0x25),
("arithmetic", "SHL", 0x26),
("arithmetic", "SHR", 0x27),
("arithmetic", "NOT", 0x28),
("arithmetic", "ROL", 0x29),
("arithmetic", "ROR", 0x2A),
("arithmetic", "INC", 0x2B),
("arithmetic", "DEC", 0x2C),
("arithmetic", "DIV", 0x46),
("arithmetic", "MOD", 0x47),
("arithmetic", "IDIV", 0x48),
("arithmetic", "IMOD", 0x49),
("control", "CMP", 0x30),
("control", "JMP", 0x31),
("control", "JZ", 0x32),
("control", "JNZ", 0x33),
("control", "JGT", 0x34),
("control", "JLT", 0x35),
("control", "JGE", 0x36),
("control", "JLE", 0x37),
("control", "CALL", 0x38),
("control", "RET", 0x39),
("special", "NOP", 0x40),
("special", "NOP_N", 0x41),
("special", "OPAQUE_TRUE", 0x42),
("special", "OPAQUE_FALSE", 0x43),
("special", "HASH_CHECK", 0x44),
("special", "TIMING_CHECK", 0x45),
("convert", "SEXT8", 0x50),
("convert", "SEXT16", 0x51),
("convert", "SEXT32", 0x52),
("convert", "TRUNC8", 0x53),
("convert", "TRUNC16", 0x54),
("convert", "TRUNC32", 0x55),
("memory", "LOAD8", 0x60),
("memory", "LOAD16", 0x61),
("memory", "LOAD32", 0x62),
("memory", "LOAD64", 0x63),
("memory", "STORE8", 0x64),
("memory", "STORE16", 0x65),
("memory", "STORE32", 0x66),
("memory", "STORE64", 0x67),
("heap", "HEAP_ALLOC", 0x70),
("heap", "HEAP_FREE", 0x71),
("heap", "HEAP_LOAD8", 0x72),
("heap", "HEAP_LOAD16", 0x73),
("heap", "HEAP_LOAD32", 0x74),
("heap", "HEAP_LOAD64", 0x75),
("heap", "HEAP_STORE8", 0x76),
("heap", "HEAP_STORE16", 0x77),
("heap", "HEAP_STORE32", 0x78),
("heap", "HEAP_STORE64", 0x79),
("heap", "HEAP_SIZE", 0x7A),
("vector", "VEC_NEW", 0x80),
("vector", "VEC_LEN", 0x81),
("vector", "VEC_CAP", 0x82),
("vector", "VEC_PUSH", 0x83),
("vector", "VEC_POP", 0x84),
("vector", "VEC_GET", 0x85),
("vector", "VEC_SET", 0x86),
("vector", "VEC_REPEAT", 0x87),
("vector", "VEC_CLEAR", 0x88),
("vector", "VEC_RESERVE", 0x89),
("string", "STR_NEW", 0x90),
("string", "STR_LEN", 0x91),
("string", "STR_PUSH", 0x92),
("string", "STR_GET", 0x93),
("string", "STR_SET", 0x94),
("string", "STR_CMP", 0x95),
("string", "STR_EQ", 0x96),
("string", "STR_HASH", 0x97),
("string", "STR_CONCAT", 0x98),
("native", "NATIVE_CALL", 0xF0),
("native", "NATIVE_READ", 0xF1),
("native", "NATIVE_WRITE", 0xF2),
("native", "INPUT_LEN", 0xF3),
("exec", "HALT", 0xFF),
("exec", "HALT_ERR", 0xFE),
];
const DUPLICATED_OPCODES: &[u8] = &[
0x20, 0x21, 0x23, 0x24, 0x25, 0x30, ];
fn generate_opcode_table(seed: &[u8; 32]) -> OpcodeTable {
let shuffle_key = hmac_sha256(seed, b"opcode-shuffle-v1");
let mut available: Vec<u8> = (0x00..0xFE).collect();
let mut rng_state = shuffle_key;
for i in (1..available.len()).rev() {
let rand_bytes = hmac_sha256(&rng_state, &(i as u32).to_le_bytes());
rng_state = rand_bytes;
let j = (u64::from_le_bytes([
rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7],
]) as usize) % (i + 1);
available.swap(i, j);
}
let mut encode = [0u8; 256]; let mut decode = [0u8; 256];
for i in 0..256 {
encode[i] = i as u8;
decode[i] = i as u8;
}
let mut aliases: std::collections::HashMap<u8, Vec<u8>> = std::collections::HashMap::new();
let mut available_idx = 0;
for (_, _, base_val) in BASE_OPCODES.iter() {
if *base_val == 0xFF || *base_val == 0xFE {
continue;
}
let shuffled_val = available[available_idx];
available_idx += 1;
encode[*base_val as usize] = shuffled_val;
decode[shuffled_val as usize] = *base_val;
}
for &base_val in DUPLICATED_OPCODES {
let mut op_aliases = Vec::new();
for _ in 0..2 {
if available_idx < available.len() {
let alias_shuffled = available[available_idx];
available_idx += 1;
decode[alias_shuffled as usize] = base_val;
op_aliases.push(alias_shuffled);
}
}
if !op_aliases.is_empty() {
aliases.insert(base_val, op_aliases);
}
}
OpcodeTable { encode, decode, aliases }
}
struct OpcodeTable {
encode: [u8; 256], decode: [u8; 256], aliases: std::collections::HashMap<u8, Vec<u8>>, }
fn write_opcode_table(f: &mut File, table: &OpcodeTable) {
writeln!(f, "/// Opcode encoding table (base -> shuffled)").unwrap();
writeln!(f, "/// Used by vm-macro at compile time").unwrap();
write!(f, "pub const OPCODE_ENCODE: [u8; 256] = [").unwrap();
for (i, &val) in table.encode.iter().enumerate() {
if i % 16 == 0 {
write!(f, "\n ").unwrap();
}
write!(f, "0x{:02x}, ", val).unwrap();
}
writeln!(f, "\n];").unwrap();
writeln!(f).unwrap();
writeln!(f, "/// Opcode decoding table (shuffled -> base)").unwrap();
writeln!(f, "/// Used by VM engine at runtime").unwrap();
writeln!(f, "/// Note: Multiple shuffled values may decode to the same base (handler duplication)").unwrap();
write!(f, "pub const OPCODE_DECODE: [u8; 256] = [").unwrap();
for (i, &val) in table.decode.iter().enumerate() {
if i % 16 == 0 {
write!(f, "\n ").unwrap();
}
write!(f, "0x{:02x}, ", val).unwrap();
}
writeln!(f, "\n];").unwrap();
writeln!(f).unwrap();
writeln!(f, "/// Handler duplication aliases (base opcode -> additional shuffled values)").unwrap();
writeln!(f, "/// These decode to the same base opcode, confusing reverse engineers").unwrap();
writeln!(f, "pub mod opcode_aliases {{").unwrap();
let get_name = |base: u8| -> &'static str {
for (_, name, val) in BASE_OPCODES.iter() {
if *val == base { return name; }
}
"UNKNOWN"
};
for &base in DUPLICATED_OPCODES {
if let Some(aliases) = table.aliases.get(&base) {
let name = get_name(base);
write!(f, " pub const {}_ALIASES: &[u8] = &[", name).unwrap();
for (i, &alias) in aliases.iter().enumerate() {
if i > 0 { write!(f, ", ").unwrap(); }
write!(f, "0x{:02x}", alias).unwrap();
}
writeln!(f, "];").unwrap();
}
}
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
writeln!(f, "/// Shuffled opcode values for this build").unwrap();
writeln!(f, "pub mod opcodes {{").unwrap();
let mut current_mod = "";
for (module, name, base_val) in BASE_OPCODES.iter() {
if *module != current_mod {
if !current_mod.is_empty() {
writeln!(f, " }}").unwrap();
}
writeln!(f, " pub mod {} {{", module).unwrap();
current_mod = module;
}
let shuffled = table.encode[*base_val as usize];
writeln!(f, " pub const {}: u8 = 0x{:02x};", name, shuffled).unwrap();
}
if !current_mod.is_empty() {
writeln!(f, " }}").unwrap();
}
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] {
const BLOCK_SIZE: usize = 64;
let mut k = [0u8; BLOCK_SIZE];
if key.len() > BLOCK_SIZE {
let hashed = sha256(key);
k[..32].copy_from_slice(&hashed);
} else {
k[..key.len()].copy_from_slice(key);
}
let mut ipad = [0x36u8; BLOCK_SIZE];
let mut opad = [0x5cu8; BLOCK_SIZE];
for i in 0..BLOCK_SIZE {
ipad[i] ^= k[i];
opad[i] ^= k[i];
}
let mut inner_data = ipad.to_vec();
inner_data.extend_from_slice(data);
let inner_hash = sha256(&inner_data);
let mut outer_data = opad.to_vec();
outer_data.extend_from_slice(&inner_hash);
sha256(&outer_data)
}
fn generate_magic_bytes(seed: &[u8; 32]) -> [u8; 4] {
let hash = hmac_sha256(seed, b"magic-bytes-v1");
[hash[0], hash[1], hash[2], hash[3]]
}
fn write_magic_bytes(f: &mut File, magic: &[u8; 4]) {
writeln!(f, "/// Randomized MAGIC bytes for bytecode header").unwrap();
writeln!(f, "pub const MAGIC: [u8; 4] = [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}];",
magic[0], magic[1], magic[2], magic[3]).unwrap();
writeln!(f).unwrap();
}
struct NativeIdMap {
check_root: u8,
check_emulator: u8,
check_hooks: u8,
check_debugger: u8,
check_tamper: u8,
get_timestamp: u8,
hash_fnv1a: u8,
read_memory: u8,
get_device_hash: u8,
custom_start: u8,
}
fn generate_native_ids(seed: &[u8; 32]) -> NativeIdMap {
let hash = hmac_sha256(seed, b"native-ids-v1");
let mut ids: Vec<u8> = (0..9).collect();
for i in (1..9).rev() {
let j = (hash[i] as usize) % (i + 1);
ids.swap(i, j);
}
NativeIdMap {
check_root: ids[0],
check_emulator: ids[1],
check_hooks: ids[2],
check_debugger: ids[3],
check_tamper: ids[4],
get_timestamp: ids[5],
hash_fnv1a: ids[6],
read_memory: ids[7],
get_device_hash: ids[8],
custom_start: 128, }
}
fn write_native_ids(f: &mut File, ids: &NativeIdMap) {
writeln!(f, "/// Shuffled native function IDs").unwrap();
writeln!(f, "pub mod native_ids {{").unwrap();
writeln!(f, " pub const CHECK_ROOT: u8 = {};", ids.check_root).unwrap();
writeln!(f, " pub const CHECK_EMULATOR: u8 = {};", ids.check_emulator).unwrap();
writeln!(f, " pub const CHECK_HOOKS: u8 = {};", ids.check_hooks).unwrap();
writeln!(f, " pub const CHECK_DEBUGGER: u8 = {};", ids.check_debugger).unwrap();
writeln!(f, " pub const CHECK_TAMPER: u8 = {};", ids.check_tamper).unwrap();
writeln!(f, " pub const GET_TIMESTAMP: u8 = {};", ids.get_timestamp).unwrap();
writeln!(f, " pub const HASH_FNV1A: u8 = {};", ids.hash_fnv1a).unwrap();
writeln!(f, " pub const READ_MEMORY: u8 = {};", ids.read_memory).unwrap();
writeln!(f, " pub const GET_DEVICE_HASH: u8 = {};", ids.get_device_hash).unwrap();
writeln!(f, " pub const CUSTOM_START: u8 = {};", ids.custom_start).unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
struct RegisterMap {
map: [u8; 8], reverse: [u8; 8], }
fn generate_register_map(seed: &[u8; 32]) -> RegisterMap {
let hash = hmac_sha256(seed, b"register-map-v1");
let mut map: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
for i in (1..8).rev() {
let j = (hash[i] as usize) % (i + 1);
map.swap(i, j);
}
let mut reverse = [0u8; 8];
for (logical, &physical) in map.iter().enumerate() {
reverse[physical as usize] = logical as u8;
}
RegisterMap { map, reverse }
}
fn write_register_map(f: &mut File, reg_map: &RegisterMap) {
writeln!(f, "/// Shuffled register mapping (logical -> physical)").unwrap();
write!(f, "pub const REGISTER_MAP: [u8; 8] = [").unwrap();
for (i, &val) in reg_map.map.iter().enumerate() {
if i > 0 { write!(f, ", ").unwrap(); }
write!(f, "{}", val).unwrap();
}
writeln!(f, "];").unwrap();
writeln!(f, "/// Reverse register mapping (physical -> logical)").unwrap();
write!(f, "pub const REGISTER_REVERSE: [u8; 8] = [").unwrap();
for (i, &val) in reg_map.reverse.iter().enumerate() {
if i > 0 { write!(f, ", ").unwrap(); }
write!(f, "{}", val).unwrap();
}
writeln!(f, "];").unwrap();
writeln!(f).unwrap();
}
struct FnvConstants {
basis_64: u64,
prime_64: u64,
basis_32: u32,
prime_32: u32,
}
fn generate_fnv_constants(seed: &[u8; 32]) -> FnvConstants {
let hash = hmac_sha256(seed, b"fnv-constants-v1");
let basis_64 = u64::from_le_bytes([
hash[0], hash[1], hash[2], hash[3],
hash[4], hash[5], hash[6], hash[7],
]) | 1;
let basis_32 = u32::from_le_bytes([
hash[8], hash[9], hash[10], hash[11],
]) | 1;
let prime_modifier = u64::from_le_bytes([
hash[16], hash[17], hash[18], hash[19],
hash[20], hash[21], hash[22], hash[23],
]);
let prime_64 = 0x100000001b3 ^ ((prime_modifier & 0xFFFF_0000_0000_0000) >> 8);
let prime_32 = 0x01000193 ^ ((hash[24] as u32) << 24);
FnvConstants {
basis_64,
prime_64,
basis_32,
prime_32,
}
}
fn write_fnv_constants(f: &mut File, fnv: &FnvConstants) {
writeln!(f, "/// Randomized FNV-1a hash constants").unwrap();
writeln!(f, "pub const FNV_BASIS_64: u64 = 0x{:016x};", fnv.basis_64).unwrap();
writeln!(f, "pub const FNV_PRIME_64: u64 = 0x{:016x};", fnv.prime_64).unwrap();
writeln!(f, "pub const FNV_BASIS_32: u32 = 0x{:08x};", fnv.basis_32).unwrap();
writeln!(f, "pub const FNV_PRIME_32: u32 = 0x{:08x};", fnv.prime_32).unwrap();
writeln!(f).unwrap();
}
fn generate_xor_key(seed: &[u8; 32]) -> u8 {
let hash = hmac_sha256(seed, b"xor-key-v1");
if hash[0] == 0 { hash[1] | 1 } else { hash[0] }
}
fn write_xor_key(f: &mut File, key: u8) {
writeln!(f, "/// Randomized XOR key for string obfuscation").unwrap();
writeln!(f, "pub const XOR_KEY: u8 = 0x{:02x};", key).unwrap();
writeln!(f).unwrap();
}
struct FlagBits {
zero: u8,
carry: u8,
overflow: u8,
sign: u8,
}
fn generate_flag_bits(seed: &[u8; 32]) -> FlagBits {
let hash = hmac_sha256(seed, b"flag-bits-v1");
let mut positions: [u8; 4] = [0, 1, 2, 3];
for i in (1..4).rev() {
let j = (hash[i] as usize) % (i + 1);
positions.swap(i, j);
}
FlagBits {
zero: 1 << positions[0],
carry: 1 << positions[1],
overflow: 1 << positions[2],
sign: 1 << positions[3],
}
}
fn write_flag_bits(f: &mut File, flags: &FlagBits) {
writeln!(f, "/// Shuffled flag bit positions").unwrap();
writeln!(f, "pub mod flags {{").unwrap();
writeln!(f, " pub const ZERO: u8 = 0b{:08b};", flags.zero).unwrap();
writeln!(f, " pub const CARRY: u8 = 0b{:08b};", flags.carry).unwrap();
writeln!(f, " pub const OVERFLOW: u8 = 0b{:08b};", flags.overflow).unwrap();
writeln!(f, " pub const SIGN: u8 = 0b{:08b};", flags.sign).unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
fn generate_mutated_handlers(seed: &[u8; 32], path: &Path) {
let mut f = File::create(path).expect("Could not create mutated_handlers.rs");
let mutation_key = hmac_sha256(seed, b"handler-mutation-v1");
writeln!(f, "// Build-time generated mutated handlers").unwrap();
writeln!(f, "// DO NOT EDIT - Generated by build.rs").unwrap();
writeln!(f, "//").unwrap();
writeln!(f, "// Each build produces different handler implementations").unwrap();
writeln!(f, "// to confuse static analysis and pattern matching.").unwrap();
writeln!(f).unwrap();
writeln!(f, "use crate::error::VmResult;").unwrap();
writeln!(f, "use crate::state::VmState;").unwrap();
writeln!(f).unwrap();
let junk_consts = generate_junk_constants(&mutation_key);
write_junk_constants(&mut f, &junk_consts);
let mut rng_state = mutation_key;
let add_variant = next_rand(&mut rng_state) % 3;
generate_add_handler(&mut f, add_variant, &mut rng_state);
let sub_variant = next_rand(&mut rng_state) % 3;
generate_sub_handler(&mut f, sub_variant, &mut rng_state);
let mul_variant = next_rand(&mut rng_state) % 2;
generate_mul_handler(&mut f, mul_variant, &mut rng_state);
let xor_variant = next_rand(&mut rng_state) % 3;
generate_xor_handler(&mut f, xor_variant, &mut rng_state);
let and_variant = next_rand(&mut rng_state) % 2;
generate_and_handler(&mut f, and_variant, &mut rng_state);
let or_variant = next_rand(&mut rng_state) % 2;
generate_or_handler(&mut f, or_variant, &mut rng_state);
let not_variant = next_rand(&mut rng_state) % 3;
generate_not_handler(&mut f, not_variant, &mut rng_state);
let inc_variant = next_rand(&mut rng_state) % 2;
generate_inc_handler(&mut f, inc_variant, &mut rng_state);
let dec_variant = next_rand(&mut rng_state) % 2;
generate_dec_handler(&mut f, dec_variant, &mut rng_state);
}
fn next_rand(state: &mut [u8; 32]) -> u64 {
*state = hmac_sha256(state, b"next");
u64::from_le_bytes([
state[0], state[1], state[2], state[3],
state[4], state[5], state[6], state[7],
])
}
fn generate_junk_constants(key: &[u8; 32]) -> [u64; 8] {
let hash = hmac_sha256(key, b"junk-constants");
[
u64::from_le_bytes(hash[0..8].try_into().unwrap()),
u64::from_le_bytes(hash[8..16].try_into().unwrap()),
u64::from_le_bytes(hash[16..24].try_into().unwrap()),
u64::from_le_bytes(hash[24..32].try_into().unwrap()),
{
let h2 = hmac_sha256(key, b"junk-constants-2");
u64::from_le_bytes(h2[0..8].try_into().unwrap())
},
{
let h2 = hmac_sha256(key, b"junk-constants-2");
u64::from_le_bytes(h2[8..16].try_into().unwrap())
},
{
let h2 = hmac_sha256(key, b"junk-constants-2");
u64::from_le_bytes(h2[16..24].try_into().unwrap())
},
{
let h2 = hmac_sha256(key, b"junk-constants-2");
u64::from_le_bytes(h2[24..32].try_into().unwrap())
},
]
}
fn write_junk_constants(f: &mut File, consts: &[u64; 8]) {
writeln!(f, "// Junk constants for dead code insertion").unwrap();
writeln!(f, "#[allow(dead_code)]").unwrap();
writeln!(f, "const JUNK: [u64; 8] = [").unwrap();
for c in consts {
writeln!(f, " 0x{:016x},", c).unwrap();
}
writeln!(f, "];").unwrap();
writeln!(f).unwrap();
}
fn generate_junk_line(rng: &mut [u8; 32], var_idx: usize) -> String {
let op = next_rand(rng) % 4;
let const_idx = (next_rand(rng) % 8) as usize;
let const_idx2 = (next_rand(rng) % 8) as usize;
match op {
0 => format!(" let _j{} = JUNK[{}] ^ JUNK[{}];", var_idx, const_idx, const_idx2),
1 => format!(" let _j{} = JUNK[{}].wrapping_add(JUNK[{}]);", var_idx, const_idx, const_idx2),
2 => format!(" let _j{} = JUNK[{}].rotate_left({});", var_idx, const_idx, (next_rand(rng) % 64) as u32),
_ => format!(" let _j{} = JUNK[{}].wrapping_mul(0x{:x});", var_idx, const_idx, next_rand(rng)),
}
}
fn insert_junk(f: &mut File, rng: &mut [u8; 32], count: usize, start_idx: usize) {
for i in 0..count {
writeln!(f, "{}", generate_junk_line(rng, start_idx + i)).unwrap();
}
}
fn generate_add_handler(f: &mut File, variant: u64, rng: &mut [u8; 32]) {
writeln!(f, "/// ADD: Pop 2, push sum (Variant {})", variant).unwrap();
writeln!(f, "#[inline(always)]").unwrap();
writeln!(f, "pub fn mutated_add(state: &mut VmState) -> VmResult<()> {{").unwrap();
let pre_junk = (next_rand(rng) % 3) as usize;
insert_junk(f, rng, pre_junk, 0);
writeln!(f, " let b = state.pop()?;").unwrap();
writeln!(f, " let a = state.pop()?;").unwrap();
let mid_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, mid_junk, 10);
match variant {
0 => {
writeln!(f, " let result = a.wrapping_add(b);").unwrap();
}
1 => {
writeln!(f, " let xor_part = a ^ b;").unwrap();
writeln!(f, " let and_part = a & b;").unwrap();
writeln!(f, " let result = xor_part.wrapping_add(and_part << 1);").unwrap();
}
_ => {
writeln!(f, " let neg_b = (!b).wrapping_add(1);").unwrap();
writeln!(f, " let result = a.wrapping_sub(neg_b);").unwrap();
}
}
let post_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, post_junk, 20);
writeln!(f, " state.set_zero_flag(result);").unwrap();
writeln!(f, " state.push(result)").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
fn generate_sub_handler(f: &mut File, variant: u64, rng: &mut [u8; 32]) {
writeln!(f, "/// SUB: Pop 2, push difference (Variant {})", variant).unwrap();
writeln!(f, "#[inline(always)]").unwrap();
writeln!(f, "pub fn mutated_sub(state: &mut VmState) -> VmResult<()> {{").unwrap();
let pre_junk = (next_rand(rng) % 3) as usize;
insert_junk(f, rng, pre_junk, 0);
writeln!(f, " let b = state.pop()?;").unwrap();
writeln!(f, " let a = state.pop()?;").unwrap();
let mid_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, mid_junk, 10);
match variant {
0 => {
writeln!(f, " let result = a.wrapping_sub(b);").unwrap();
}
1 => {
writeln!(f, " let neg_b = (!b).wrapping_add(1);").unwrap();
writeln!(f, " let result = a.wrapping_add(neg_b);").unwrap();
}
_ => {
writeln!(f, " let not_a = !a;").unwrap();
writeln!(f, " let sum = not_a.wrapping_add(b);").unwrap();
writeln!(f, " let result = !sum;").unwrap();
}
}
let post_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, post_junk, 20);
writeln!(f, " state.set_zero_flag(result);").unwrap();
writeln!(f, " state.push(result)").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
fn generate_mul_handler(f: &mut File, variant: u64, rng: &mut [u8; 32]) {
writeln!(f, "/// MUL: Pop 2, push product (Variant {})", variant).unwrap();
writeln!(f, "#[inline(always)]").unwrap();
writeln!(f, "pub fn mutated_mul(state: &mut VmState) -> VmResult<()> {{").unwrap();
let pre_junk = (next_rand(rng) % 3) as usize;
insert_junk(f, rng, pre_junk, 0);
writeln!(f, " let b = state.pop()?;").unwrap();
writeln!(f, " let a = state.pop()?;").unwrap();
let mid_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, mid_junk, 10);
match variant {
0 => {
writeln!(f, " let result = a.wrapping_mul(b);").unwrap();
}
_ => {
writeln!(f, " let product = a.wrapping_mul(b);").unwrap();
writeln!(f, " let result = product ^ (JUNK[0] ^ JUNK[0]);").unwrap();
}
}
let post_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, post_junk, 20);
writeln!(f, " state.set_zero_flag(result);").unwrap();
writeln!(f, " state.push(result)").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
fn generate_xor_handler(f: &mut File, variant: u64, rng: &mut [u8; 32]) {
writeln!(f, "/// XOR: Pop 2, push XOR (Variant {})", variant).unwrap();
writeln!(f, "#[inline(always)]").unwrap();
writeln!(f, "pub fn mutated_xor(state: &mut VmState) -> VmResult<()> {{").unwrap();
let pre_junk = (next_rand(rng) % 3) as usize;
insert_junk(f, rng, pre_junk, 0);
writeln!(f, " let b = state.pop()?;").unwrap();
writeln!(f, " let a = state.pop()?;").unwrap();
let mid_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, mid_junk, 10);
match variant {
0 => {
writeln!(f, " let result = a ^ b;").unwrap();
}
1 => {
writeln!(f, " let part1 = a & !b;").unwrap();
writeln!(f, " let part2 = !a & b;").unwrap();
writeln!(f, " let result = part1 | part2;").unwrap();
}
_ => {
writeln!(f, " let or_part = a | b;").unwrap();
writeln!(f, " let nand_part = !a | !b;").unwrap();
writeln!(f, " let result = or_part & nand_part;").unwrap();
}
}
let post_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, post_junk, 20);
writeln!(f, " state.set_zero_flag(result);").unwrap();
writeln!(f, " state.push(result)").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
fn generate_and_handler(f: &mut File, variant: u64, rng: &mut [u8; 32]) {
writeln!(f, "/// AND: Pop 2, push AND (Variant {})", variant).unwrap();
writeln!(f, "#[inline(always)]").unwrap();
writeln!(f, "pub fn mutated_and(state: &mut VmState) -> VmResult<()> {{").unwrap();
let pre_junk = (next_rand(rng) % 3) as usize;
insert_junk(f, rng, pre_junk, 0);
writeln!(f, " let b = state.pop()?;").unwrap();
writeln!(f, " let a = state.pop()?;").unwrap();
let mid_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, mid_junk, 10);
match variant {
0 => {
writeln!(f, " let result = a & b;").unwrap();
}
_ => {
writeln!(f, " let not_a = !a;").unwrap();
writeln!(f, " let not_b = !b;").unwrap();
writeln!(f, " let result = !(not_a | not_b);").unwrap();
}
}
let post_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, post_junk, 20);
writeln!(f, " state.set_zero_flag(result);").unwrap();
writeln!(f, " state.push(result)").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
fn generate_or_handler(f: &mut File, variant: u64, rng: &mut [u8; 32]) {
writeln!(f, "/// OR: Pop 2, push OR (Variant {})", variant).unwrap();
writeln!(f, "#[inline(always)]").unwrap();
writeln!(f, "pub fn mutated_or(state: &mut VmState) -> VmResult<()> {{").unwrap();
let pre_junk = (next_rand(rng) % 3) as usize;
insert_junk(f, rng, pre_junk, 0);
writeln!(f, " let b = state.pop()?;").unwrap();
writeln!(f, " let a = state.pop()?;").unwrap();
let mid_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, mid_junk, 10);
match variant {
0 => {
writeln!(f, " let result = a | b;").unwrap();
}
_ => {
writeln!(f, " let not_a = !a;").unwrap();
writeln!(f, " let not_b = !b;").unwrap();
writeln!(f, " let result = !(not_a & not_b);").unwrap();
}
}
let post_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, post_junk, 20);
writeln!(f, " state.set_zero_flag(result);").unwrap();
writeln!(f, " state.push(result)").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
fn generate_not_handler(f: &mut File, variant: u64, rng: &mut [u8; 32]) {
writeln!(f, "/// NOT: Pop 1, push NOT (Variant {})", variant).unwrap();
writeln!(f, "#[inline(always)]").unwrap();
writeln!(f, "pub fn mutated_not(state: &mut VmState) -> VmResult<()> {{").unwrap();
let pre_junk = (next_rand(rng) % 3) as usize;
insert_junk(f, rng, pre_junk, 0);
writeln!(f, " let a = state.pop()?;").unwrap();
let mid_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, mid_junk, 10);
match variant {
0 => {
writeln!(f, " let result = !a;").unwrap();
}
1 => {
writeln!(f, " let result = a ^ u64::MAX;").unwrap();
}
_ => {
writeln!(f, " let result = u64::MAX.wrapping_sub(a);").unwrap();
}
}
let post_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, post_junk, 20);
writeln!(f, " state.set_zero_flag(result);").unwrap();
writeln!(f, " state.push(result)").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
fn generate_inc_handler(f: &mut File, variant: u64, rng: &mut [u8; 32]) {
writeln!(f, "/// INC: Increment top of stack (Variant {})", variant).unwrap();
writeln!(f, "#[inline(always)]").unwrap();
writeln!(f, "pub fn mutated_inc(state: &mut VmState) -> VmResult<()> {{").unwrap();
let pre_junk = (next_rand(rng) % 3) as usize;
insert_junk(f, rng, pre_junk, 0);
writeln!(f, " let a = state.pop()?;").unwrap();
let mid_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, mid_junk, 10);
match variant {
0 => {
writeln!(f, " let result = a.wrapping_add(1);").unwrap();
}
_ => {
writeln!(f, " let result = a.wrapping_sub(u64::MAX);").unwrap();
}
}
let post_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, post_junk, 20);
writeln!(f, " state.set_zero_flag(result);").unwrap();
writeln!(f, " state.push(result)").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
fn generate_dec_handler(f: &mut File, variant: u64, rng: &mut [u8; 32]) {
writeln!(f, "/// DEC: Decrement top of stack (Variant {})", variant).unwrap();
writeln!(f, "#[inline(always)]").unwrap();
writeln!(f, "pub fn mutated_dec(state: &mut VmState) -> VmResult<()> {{").unwrap();
let pre_junk = (next_rand(rng) % 3) as usize;
insert_junk(f, rng, pre_junk, 0);
writeln!(f, " let a = state.pop()?;").unwrap();
let mid_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, mid_junk, 10);
match variant {
0 => {
writeln!(f, " let result = a.wrapping_sub(1);").unwrap();
}
_ => {
writeln!(f, " let result = a.wrapping_add(u64::MAX);").unwrap();
}
}
let post_junk = (next_rand(rng) % 2) as usize;
insert_junk(f, rng, post_junk, 20);
writeln!(f, " state.set_zero_flag(result);").unwrap();
writeln!(f, " state.push(result)").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}
const AES_SBOX: [u8; 256] = [
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
];
const AES_RCON: [u8; 10] = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
const AES_MIX_COLS: [[u8; 4]; 4] = [[2, 3, 1, 1], [1, 2, 3, 1], [1, 1, 2, 3], [3, 1, 1, 2]];
const AES_SHIFT_ROWS: [usize; 16] = [0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11];
const AES_ROUNDS: usize = 10;
const AES_BLOCK_SIZE: usize = 16;
const TBOX_SIZE: usize = AES_ROUNDS * AES_BLOCK_SIZE * 256; const TYBOX_SIZE: usize = 9 * AES_BLOCK_SIZE * 256 * 4; const XOR_TABLE_SIZE: usize = 9 * 96 * 16 * 16; const MBL_SIZE: usize = 9 * AES_BLOCK_SIZE * 256 * 4; const TBOX_LAST_SIZE: usize = AES_BLOCK_SIZE * 256;
fn gf_mul(a: u8, b: u8) -> u8 {
let mut result = 0u8;
let mut aa = a;
let mut bb = b;
for _ in 0..8 {
if bb & 1 != 0 { result ^= aa; }
let hi_bit = aa & 0x80;
aa <<= 1;
if hi_bit != 0 { aa ^= 0x1b; }
bb >>= 1;
}
result
}
fn aes_key_expansion(key: &[u8; 16]) -> [[u8; 16]; 11] {
let mut round_keys = [[0u8; 16]; 11];
round_keys[0].copy_from_slice(key);
for i in 1..11 {
let prev = &round_keys[i - 1];
let mut next = [0u8; 16];
let rot = [prev[13], prev[14], prev[15], prev[12]];
next[0] = prev[0] ^ AES_SBOX[rot[0] as usize] ^ AES_RCON[i - 1];
next[1] = prev[1] ^ AES_SBOX[rot[1] as usize];
next[2] = prev[2] ^ AES_SBOX[rot[2] as usize];
next[3] = prev[3] ^ AES_SBOX[rot[3] as usize];
for j in 4..16 {
next[j] = prev[j] ^ next[j - 4];
}
round_keys[i] = next;
}
round_keys
}
struct BuildRng { state: u64 }
impl BuildRng {
fn new(seed: &[u8]) -> Self {
let mut state = 0x853c49e6748fea9bu64;
for (i, &byte) in seed.iter().enumerate() {
state ^= (byte as u64) << ((i % 8) * 8);
state = state.wrapping_mul(0x5851f42d4c957f2d);
state ^= state >> 33;
}
Self { state }
}
fn next_u64(&mut self) -> u64 {
self.state ^= self.state >> 12;
self.state ^= self.state << 25;
self.state ^= self.state >> 27;
self.state.wrapping_mul(0x2545f4914f6cdd1d)
}
fn random_permutation(&mut self, n: usize) -> Vec<u8> {
let mut perm: Vec<u8> = Vec::with_capacity(n);
for i in 0..n {
perm.push(i as u8);
}
for i in (1..n).rev() {
let j = (self.next_u64() as usize) % (i + 1);
perm.swap(i, j);
}
perm
}
}
#[derive(Clone, Copy)]
struct Bijection8 { forward: [u8; 256], inverse: [u8; 256] }
impl Bijection8 {
fn identity() -> Self {
let mut forward = [0u8; 256];
let mut inverse = [0u8; 256];
for i in 0..256 { forward[i] = i as u8; inverse[i] = i as u8; }
Self { forward, inverse }
}
fn encode(&self, x: u8) -> u8 { self.forward[x as usize] }
fn decode(&self, x: u8) -> u8 { self.inverse[x as usize] }
}
#[derive(Clone, Copy)]
struct Bijection4 { forward: [u8; 16], inverse: [u8; 16] }
impl Bijection4 {
fn identity() -> Self {
let mut forward = [0u8; 16];
let mut inverse = [0u8; 16];
for i in 0..16 { forward[i] = i as u8; inverse[i] = i as u8; }
Self { forward, inverse }
}
fn encode(&self, x: u8) -> u8 { self.forward[(x & 0x0f) as usize] }
fn decode(&self, x: u8) -> u8 { self.inverse[(x & 0x0f) as usize] }
}
struct MixingBijection32 { matrix: [[u8; 32]; 32], inverse: [[u8; 32]; 32] }
impl MixingBijection32 {
fn identity() -> Self {
let mut matrix = [[0u8; 32]; 32];
let mut inverse = [[0u8; 32]; 32];
for i in 0..32 { matrix[i][i] = 1; inverse[i][i] = 1; }
Self { matrix, inverse }
}
fn apply(&self, input: u32) -> u32 {
let mut result = 0u32;
for i in 0..32 {
let mut bit = 0u8;
for j in 0..32 {
if self.matrix[i][j] != 0 && (input >> j) & 1 != 0 { bit ^= 1; }
}
result |= (bit as u32) << i;
}
result
}
fn apply_inverse(&self, input: u32) -> u32 {
let mut result = 0u32;
for i in 0..32 {
let mut bit = 0u8;
for j in 0..32 {
if self.inverse[i][j] != 0 && (input >> j) & 1 != 0 { bit ^= 1; }
}
result |= (bit as u32) << i;
}
result
}
}
struct InternalEncodings {
round_output: [[Bijection8; AES_BLOCK_SIZE]; AES_ROUNDS],
nibble_encodings: [[[Bijection4; 2]; 96]; 9],
}
struct WbcTables {
tbox: Vec<u8>, tybox: Vec<u8>, xor_tables: Vec<u8>, mbl: Vec<u8>, tbox_last: Vec<u8>, }
#[allow(clippy::needless_range_loop)]
fn generate_wbc_tables(key: &[u8; 16], seed: &[u8]) -> WbcTables {
let mut rng = BuildRng::new(seed);
let round_keys = aes_key_expansion(key);
let encodings = generate_encodings(&mut rng);
let mixing_bijections = generate_mixing_bijections(&mut rng);
let mut tbox = vec![0u8; TBOX_SIZE];
let mut tybox = vec![0u8; TYBOX_SIZE];
let mut xor_tables = vec![0u8; XOR_TABLE_SIZE];
let mut mbl = vec![0u8; MBL_SIZE];
let mut tbox_last = vec![0u8; TBOX_LAST_SIZE];
for round in 0..9 {
for col in 0..4 {
for row in 0..4 {
let pos = col * 4 + row;
let shifted_pos = AES_SHIFT_ROWS[pos];
for x in 0..256 {
let decoded = if round == 0 {
x as u8
} else {
encodings.round_output[round - 1][pos].decode(x as u8)
};
let after_key = decoded ^ round_keys[round][shifted_pos];
let after_sbox = AES_SBOX[after_key as usize];
let mut mc_out = [0u8; 4];
for out_row in 0..4 {
mc_out[out_row] = gf_mul(AES_MIX_COLS[out_row][row], after_sbox);
}
let packed = (mc_out[0] as u32)
| ((mc_out[1] as u32) << 8)
| ((mc_out[2] as u32) << 16)
| ((mc_out[3] as u32) << 24);
let mixed = mixing_bijections[round].apply(packed);
let tybox_idx = (round * AES_BLOCK_SIZE * 256 + pos * 256 + x) * 4;
tybox[tybox_idx..tybox_idx + 4].copy_from_slice(&mixed.to_le_bytes());
let tbox_idx = round * AES_BLOCK_SIZE * 256 + pos * 256 + x;
tbox[tbox_idx] = after_sbox;
}
}
}
}
for round in 0..9 {
for table_idx in 0..96 {
for a in 0..16u8 {
for b in 0..16u8 {
let a_decoded = encodings.nibble_encodings[round][table_idx][0].decode(a);
let b_decoded = encodings.nibble_encodings[round][table_idx][1].decode(b);
let result = a_decoded ^ b_decoded;
let encoded_result = if table_idx + 1 < 96 {
encodings.nibble_encodings[round][(table_idx + 1) % 96][0].encode(result)
} else {
result
};
let idx = round * 96 * 16 * 16 + table_idx * 16 * 16 + (a as usize) * 16 + (b as usize);
xor_tables[idx] = encoded_result;
}
}
}
}
for (round, mb) in mixing_bijections.iter().enumerate().take(9) {
for pos in 0..AES_BLOCK_SIZE {
for x in 0..256 {
let l_encoded = (x as u32) << ((pos % 4) * 8);
let unmixed = mb.apply_inverse(l_encoded);
let out_bytes = [
unmixed as u8,
(unmixed >> 8) as u8,
(unmixed >> 16) as u8,
(unmixed >> 24) as u8,
];
let col_base = (pos / 4) * 4;
let encoded_bytes = [
encodings.round_output[round][col_base].encode(out_bytes[0]),
encodings.round_output[round][col_base + 1].encode(out_bytes[1]),
encodings.round_output[round][col_base + 2].encode(out_bytes[2]),
encodings.round_output[round][col_base + 3].encode(out_bytes[3]),
];
let packed = (encoded_bytes[0] as u32)
| ((encoded_bytes[1] as u32) << 8)
| ((encoded_bytes[2] as u32) << 16)
| ((encoded_bytes[3] as u32) << 24);
let mbl_idx = (round * AES_BLOCK_SIZE * 256 + pos * 256 + x) * 4;
mbl[mbl_idx..mbl_idx + 4].copy_from_slice(&packed.to_le_bytes());
}
}
}
let round = AES_ROUNDS - 1;
for pos in 0..AES_BLOCK_SIZE {
let shifted_pos = AES_SHIFT_ROWS[pos];
for x in 0..256 {
let decoded = encodings.round_output[round - 1][pos].decode(x as u8);
let after_key = decoded ^ round_keys[round][shifted_pos];
let after_sbox = AES_SBOX[after_key as usize];
let result = after_sbox ^ round_keys[AES_ROUNDS][shifted_pos];
tbox_last[pos * 256 + x] = result;
tbox[round * AES_BLOCK_SIZE * 256 + pos * 256 + x] = result;
}
}
WbcTables { tbox, tybox, xor_tables, mbl, tbox_last }
}
fn generate_encodings(rng: &mut BuildRng) -> InternalEncodings {
let mut encodings = InternalEncodings {
round_output: [[Bijection8::identity(); AES_BLOCK_SIZE]; AES_ROUNDS],
nibble_encodings: [[[Bijection4::identity(); 2]; 96]; 9],
};
for round in 0..AES_ROUNDS {
for pos in 0..AES_BLOCK_SIZE {
let perm = rng.random_permutation(256);
let mut bij = Bijection8::identity();
for (i, &p) in perm.iter().enumerate() {
bij.forward[i] = p;
bij.inverse[p as usize] = i as u8;
}
encodings.round_output[round][pos] = bij;
}
}
for round in 0..9 {
for table in 0..96 {
for nibble in 0..2 {
let perm = rng.random_permutation(16);
let mut bij = Bijection4::identity();
for (i, &p) in perm.iter().enumerate() {
bij.forward[i] = p;
bij.inverse[p as usize] = i as u8;
}
encodings.nibble_encodings[round][table][nibble] = bij;
}
}
}
encodings
}
fn generate_mixing_bijections(rng: &mut BuildRng) -> [MixingBijection32; 9] {
let mut mbs: [MixingBijection32; 9] = core::array::from_fn(|_| MixingBijection32::identity());
for mb in &mut mbs {
for _ in 0..64 {
let i = (rng.next_u64() as usize) % 32;
let j = (rng.next_u64() as usize) % 32;
if i != j {
for k in 0..32 { mb.matrix[i][k] ^= mb.matrix[j][k]; }
for k in 0..32 { mb.inverse[k][j] ^= mb.inverse[k][i]; }
}
}
}
mbs
}
fn generate_whitebox_config(f: &mut File, build_seed: &[u8; 32]) {
let wbc_key_full = hmac_sha256(build_seed, b"whitebox-aes-key-v1");
let table_seed = hmac_sha256(build_seed, b"whitebox-table-seed-v1");
let mut wbc_key = [0u8; 16];
wbc_key.copy_from_slice(&wbc_key_full[..16]);
let tables = generate_wbc_tables(&wbc_key, &table_seed);
const POOL_SIZE: usize = 65536;
let mut entropy_pool = vec![0u8; POOL_SIZE];
let pool_seed = hmac_sha256(build_seed, b"wbc-shared-pool-v2");
let mut rng_state = pool_seed;
for (i, byte) in entropy_pool.iter_mut().enumerate() {
let mac = hmac_sha256(&rng_state, &(i as u32).to_le_bytes());
*byte = mac[0];
if i % 64 == 0 { rng_state = mac; }
}
let params_seed = hmac_sha256(build_seed, b"wbc-table-params-v2");
writeln!(f, "// ============================================================================").unwrap();
writeln!(f, "// WHITE-BOX CRYPTO TABLES - BUILD-TIME GENERATED").unwrap();
writeln!(f, "// ============================================================================").unwrap();
writeln!(f, "// AES key was used ONLY during compilation to generate these tables.").unwrap();
writeln!(f, "// The key does NOT exist anywhere at runtime - true white-box security!").unwrap();
writeln!(f, "// Tables are protected with entropy pool + delta XOR reconstruction.").unwrap();
writeln!(f, "pub mod whitebox_config {{").unwrap();
writeln!(f, " #![allow(clippy::all)]").unwrap();
writeln!(f, " extern crate alloc;").unwrap();
writeln!(f).unwrap();
writeln!(f, " /// Shared entropy pool for all table reconstruction (64KB)").unwrap();
writeln!(f, " const ENTROPY_POOL: [u8; {}] = [", POOL_SIZE).unwrap();
for (i, byte) in entropy_pool.iter().enumerate() {
if i % 32 == 0 { write!(f, "\n ").unwrap(); }
write!(f, "0x{:02x},", byte).unwrap();
}
writeln!(f, "\n ];").unwrap();
writeln!(f).unwrap();
fn write_table_deltas(
f: &mut File,
name: &str,
table_data: &[u8],
entropy_pool: &[u8],
params_seed: &[u8; 32],
domain: &[u8],
) -> (usize, usize) {
let params = hmac_sha256(params_seed, domain);
let start = (u64::from_le_bytes(params[0..8].try_into().unwrap()) as usize) % entropy_pool.len();
let step = (u64::from_le_bytes(params[8..16].try_into().unwrap()) as usize) % 31 + 1;
let mut deltas = vec![0u8; table_data.len()];
for (i, (&data_byte, delta_byte)) in table_data.iter().zip(deltas.iter_mut()).enumerate() {
let pool_idx = (start + i * step) % entropy_pool.len();
*delta_byte = data_byte ^ entropy_pool[pool_idx];
}
writeln!(f, " /// Delta values for {} reconstruction ({} bytes)", name, deltas.len()).unwrap();
writeln!(f, " const {}_DELTAS: [u8; {}] = [", name, deltas.len()).unwrap();
for (i, byte) in deltas.iter().enumerate() {
if i % 32 == 0 { write!(f, "\n ").unwrap(); }
write!(f, "0x{:02x},", byte).unwrap();
}
writeln!(f, "\n ];").unwrap();
writeln!(f).unwrap();
(start, step)
}
let (tbox_start, tbox_step) = write_table_deltas(
f, "TBOX", &tables.tbox, &entropy_pool, ¶ms_seed, b"tbox-params"
);
let (tybox_start, tybox_step) = write_table_deltas(
f, "TYBOX", &tables.tybox, &entropy_pool, ¶ms_seed, b"tybox-params"
);
let (xor_start, xor_step) = write_table_deltas(
f, "XOR_TABLES", &tables.xor_tables, &entropy_pool, ¶ms_seed, b"xor-params"
);
let (mbl_start, mbl_step) = write_table_deltas(
f, "MBL", &tables.mbl, &entropy_pool, ¶ms_seed, b"mbl-params"
);
let (tbox_last_start, tbox_last_step) = write_table_deltas(
f, "TBOX_LAST", &tables.tbox_last, &entropy_pool, ¶ms_seed, b"tbox-last-params"
);
writeln!(f, " // Table sizes").unwrap();
writeln!(f, " pub const TBOX_SIZE: usize = {};", TBOX_SIZE).unwrap();
writeln!(f, " pub const TYBOX_SIZE: usize = {};", TYBOX_SIZE).unwrap();
writeln!(f, " pub const XOR_TABLE_SIZE: usize = {};", XOR_TABLE_SIZE).unwrap();
writeln!(f, " pub const MBL_SIZE: usize = {};", MBL_SIZE).unwrap();
writeln!(f, " pub const TBOX_LAST_SIZE: usize = {};", TBOX_LAST_SIZE).unwrap();
writeln!(f, " pub const POOL_SIZE: usize = {};", POOL_SIZE).unwrap();
writeln!(f).unwrap();
writeln!(f, " /// Reconstruct T-boxes from entropy pool + deltas").unwrap();
writeln!(f, " #[inline(never)]").unwrap();
writeln!(f, " pub fn reconstruct_tbox() -> alloc::boxed::Box<[[[u8; 256]; 16]; 10]> {{").unwrap();
writeln!(f, " let mut tbox = alloc::boxed::Box::new([[[0u8; 256]; 16]; 10]);").unwrap();
writeln!(f, " let start = core::hint::black_box({});", tbox_start).unwrap();
writeln!(f, " let step = core::hint::black_box({});", tbox_step).unwrap();
writeln!(f, " for i in 0..TBOX_SIZE {{").unwrap();
writeln!(f, " let round = i / (16 * 256);").unwrap();
writeln!(f, " let pos = (i / 256) % 16;").unwrap();
writeln!(f, " let x = i % 256;").unwrap();
writeln!(f, " let pool_idx = (start + i * step) % POOL_SIZE;").unwrap();
writeln!(f, " tbox[round][pos][x] = ENTROPY_POOL[pool_idx] ^ TBOX_DELTAS[i];").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " tbox").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f).unwrap();
writeln!(f, " /// Reconstruct Ty-boxes from entropy pool + deltas").unwrap();
writeln!(f, " #[inline(never)]").unwrap();
writeln!(f, " pub fn reconstruct_tybox() -> alloc::boxed::Box<[[[u32; 256]; 16]; 9]> {{").unwrap();
writeln!(f, " let mut tybox = alloc::boxed::Box::new([[[0u32; 256]; 16]; 9]);").unwrap();
writeln!(f, " let start = core::hint::black_box({});", tybox_start).unwrap();
writeln!(f, " let step = core::hint::black_box({});", tybox_step).unwrap();
writeln!(f, " for i in 0..(TYBOX_SIZE / 4) {{").unwrap();
writeln!(f, " let round = i / (16 * 256);").unwrap();
writeln!(f, " let pos = (i / 256) % 16;").unwrap();
writeln!(f, " let x = i % 256;").unwrap();
writeln!(f, " let base = i * 4;").unwrap();
writeln!(f, " let mut bytes = [0u8; 4];").unwrap();
writeln!(f, " for j in 0..4 {{").unwrap();
writeln!(f, " let pool_idx = (start + (base + j) * step) % POOL_SIZE;").unwrap();
writeln!(f, " bytes[j] = ENTROPY_POOL[pool_idx] ^ TYBOX_DELTAS[base + j];").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " tybox[round][pos][x] = u32::from_le_bytes(bytes);").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " tybox").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f).unwrap();
writeln!(f, " /// Reconstruct XOR tables from entropy pool + deltas").unwrap();
writeln!(f, " #[inline(never)]").unwrap();
writeln!(f, " pub fn reconstruct_xor_tables() -> alloc::boxed::Box<[[[[u8; 16]; 16]; 96]; 9]> {{").unwrap();
writeln!(f, " let mut xor_tables = alloc::boxed::Box::new([[[[0u8; 16]; 16]; 96]; 9]);").unwrap();
writeln!(f, " let start = core::hint::black_box({});", xor_start).unwrap();
writeln!(f, " let step = core::hint::black_box({});", xor_step).unwrap();
writeln!(f, " for i in 0..XOR_TABLE_SIZE {{").unwrap();
writeln!(f, " let round = i / (96 * 16 * 16);").unwrap();
writeln!(f, " let table = (i / (16 * 16)) % 96;").unwrap();
writeln!(f, " let a = (i / 16) % 16;").unwrap();
writeln!(f, " let b = i % 16;").unwrap();
writeln!(f, " let pool_idx = (start + i * step) % POOL_SIZE;").unwrap();
writeln!(f, " xor_tables[round][table][a][b] = ENTROPY_POOL[pool_idx] ^ XOR_TABLES_DELTAS[i];").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " xor_tables").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f).unwrap();
writeln!(f, " /// Reconstruct MBL tables from entropy pool + deltas").unwrap();
writeln!(f, " #[inline(never)]").unwrap();
writeln!(f, " pub fn reconstruct_mbl() -> alloc::boxed::Box<[[[u32; 256]; 16]; 9]> {{").unwrap();
writeln!(f, " let mut mbl = alloc::boxed::Box::new([[[0u32; 256]; 16]; 9]);").unwrap();
writeln!(f, " let start = core::hint::black_box({});", mbl_start).unwrap();
writeln!(f, " let step = core::hint::black_box({});", mbl_step).unwrap();
writeln!(f, " for i in 0..(MBL_SIZE / 4) {{").unwrap();
writeln!(f, " let round = i / (16 * 256);").unwrap();
writeln!(f, " let pos = (i / 256) % 16;").unwrap();
writeln!(f, " let x = i % 256;").unwrap();
writeln!(f, " let base = i * 4;").unwrap();
writeln!(f, " let mut bytes = [0u8; 4];").unwrap();
writeln!(f, " for j in 0..4 {{").unwrap();
writeln!(f, " let pool_idx = (start + (base + j) * step) % POOL_SIZE;").unwrap();
writeln!(f, " bytes[j] = ENTROPY_POOL[pool_idx] ^ MBL_DELTAS[base + j];").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " mbl[round][pos][x] = u32::from_le_bytes(bytes);").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " mbl").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f).unwrap();
writeln!(f, " /// Reconstruct last round T-boxes from entropy pool + deltas").unwrap();
writeln!(f, " #[inline(never)]").unwrap();
writeln!(f, " pub fn reconstruct_tbox_last() -> [[u8; 256]; 16] {{").unwrap();
writeln!(f, " let mut tbox_last = [[0u8; 256]; 16];").unwrap();
writeln!(f, " let start = core::hint::black_box({});", tbox_last_start).unwrap();
writeln!(f, " let step = core::hint::black_box({});", tbox_last_step).unwrap();
writeln!(f, " for i in 0..TBOX_LAST_SIZE {{").unwrap();
writeln!(f, " let pos = i / 256;").unwrap();
writeln!(f, " let x = i % 256;").unwrap();
writeln!(f, " let pool_idx = (start + i * step) % POOL_SIZE;").unwrap();
writeln!(f, " tbox_last[pos][x] = ENTROPY_POOL[pool_idx] ^ TBOX_LAST_DELTAS[i];").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " tbox_last").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f).unwrap();
fn fnv_hash_domain(domain: &[u8]) -> [u8; 32] {
let mut hash1 = 0xcbf29ce484222325u64;
let mut hash2 = 0x84222325cbf29ce4u64;
for &byte in domain {
hash1 ^= byte as u64;
hash1 = hash1.wrapping_mul(0x100000001b3);
hash2 = hash2.wrapping_mul(0x100000001b3);
hash2 ^= byte as u64;
}
let hash3 = hash1.rotate_left(13) ^ hash2;
let hash4 = hash2.rotate_right(17) ^ hash1;
let mut result = [0u8; 32];
result[0..8].copy_from_slice(&hash1.to_le_bytes());
result[8..16].copy_from_slice(&hash2.to_le_bytes());
result[16..24].copy_from_slice(&hash3.to_le_bytes());
result[24..32].copy_from_slice(&hash4.to_le_bytes());
result
}
let bytecode_domain_hash = fnv_hash_domain(b"aegis-bytecode-encryption-v1");
let smc_domain_hash = fnv_hash_domain(b"aegis-smc-key-v1");
let nonce_domain_hash = fnv_hash_domain(b"wbc-nonce\x00\x00\x00\x00\x00\x00\x00");
writeln!(f, " // ========================================================================").unwrap();
writeln!(f, " // PRE-COMPUTED DOMAIN HASHES (strings eliminated from binary!)").unwrap();
writeln!(f, " // ========================================================================").unwrap();
writeln!(f).unwrap();
let (bc_hash_start, bc_hash_step) = {
let params = hmac_sha256(¶ms_seed, b"bytecode-hash-params");
let start = (u64::from_le_bytes(params[0..8].try_into().unwrap()) as usize) % POOL_SIZE;
let step = (u64::from_le_bytes(params[8..16].try_into().unwrap()) as usize) % 31 + 1;
let mut deltas = [0u8; 32];
for i in 0..32 {
let pool_idx = (start + i * step) % POOL_SIZE;
deltas[i] = bytecode_domain_hash[i] ^ entropy_pool[pool_idx];
}
writeln!(f, " /// Bytecode domain hash deltas (32 bytes)").unwrap();
writeln!(f, " const BYTECODE_DOMAIN_HASH_DELTAS: [u8; 32] = [").unwrap();
write!(f, " ").unwrap();
for (i, byte) in deltas.iter().enumerate() {
write!(f, "0x{:02x},", byte).unwrap();
if i == 15 { write!(f, "\n ").unwrap(); }
}
writeln!(f, "\n ];").unwrap();
(start, step)
};
let (smc_hash_start, smc_hash_step) = {
let params = hmac_sha256(¶ms_seed, b"smc-hash-params");
let start = (u64::from_le_bytes(params[0..8].try_into().unwrap()) as usize) % POOL_SIZE;
let step = (u64::from_le_bytes(params[8..16].try_into().unwrap()) as usize) % 31 + 1;
let mut deltas = [0u8; 32];
for i in 0..32 {
let pool_idx = (start + i * step) % POOL_SIZE;
deltas[i] = smc_domain_hash[i] ^ entropy_pool[pool_idx];
}
writeln!(f, " /// SMC domain hash deltas (32 bytes)").unwrap();
writeln!(f, " const SMC_DOMAIN_HASH_DELTAS: [u8; 32] = [").unwrap();
write!(f, " ").unwrap();
for (i, byte) in deltas.iter().enumerate() {
write!(f, "0x{:02x},", byte).unwrap();
if i == 15 { write!(f, "\n ").unwrap(); }
}
writeln!(f, "\n ];").unwrap();
(start, step)
};
let (nonce_hash_start, nonce_hash_step) = {
let params = hmac_sha256(¶ms_seed, b"nonce-hash-params");
let start = (u64::from_le_bytes(params[0..8].try_into().unwrap()) as usize) % POOL_SIZE;
let step = (u64::from_le_bytes(params[8..16].try_into().unwrap()) as usize) % 31 + 1;
let mut deltas = [0u8; 32];
for i in 0..32 {
let pool_idx = (start + i * step) % POOL_SIZE;
deltas[i] = nonce_domain_hash[i] ^ entropy_pool[pool_idx];
}
writeln!(f, " /// Nonce domain hash deltas (32 bytes)").unwrap();
writeln!(f, " const NONCE_DOMAIN_HASH_DELTAS: [u8; 32] = [").unwrap();
write!(f, " ").unwrap();
for (i, byte) in deltas.iter().enumerate() {
write!(f, "0x{:02x},", byte).unwrap();
if i == 15 { write!(f, "\n ").unwrap(); }
}
writeln!(f, "\n ];").unwrap();
(start, step)
};
writeln!(f).unwrap();
writeln!(f, " /// Reconstruct bytecode domain hash from entropy pool + deltas").unwrap();
writeln!(f, " #[inline(never)]").unwrap();
writeln!(f, " pub fn get_bytecode_domain_hash() -> [u8; 32] {{").unwrap();
writeln!(f, " let mut hash = [0u8; 32];").unwrap();
writeln!(f, " let start = core::hint::black_box({});", bc_hash_start).unwrap();
writeln!(f, " let step = core::hint::black_box({});", bc_hash_step).unwrap();
writeln!(f, " for i in 0..32 {{").unwrap();
writeln!(f, " let pool_idx = (start + i * step) % POOL_SIZE;").unwrap();
writeln!(f, " hash[i] = ENTROPY_POOL[pool_idx] ^ BYTECODE_DOMAIN_HASH_DELTAS[i];").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " hash").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f).unwrap();
writeln!(f, " /// Reconstruct SMC domain hash from entropy pool + deltas").unwrap();
writeln!(f, " #[inline(never)]").unwrap();
writeln!(f, " pub fn get_smc_domain_hash() -> [u8; 32] {{").unwrap();
writeln!(f, " let mut hash = [0u8; 32];").unwrap();
writeln!(f, " let start = core::hint::black_box({});", smc_hash_start).unwrap();
writeln!(f, " let step = core::hint::black_box({});", smc_hash_step).unwrap();
writeln!(f, " for i in 0..32 {{").unwrap();
writeln!(f, " let pool_idx = (start + i * step) % POOL_SIZE;").unwrap();
writeln!(f, " hash[i] = ENTROPY_POOL[pool_idx] ^ SMC_DOMAIN_HASH_DELTAS[i];").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " hash").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f).unwrap();
writeln!(f, " /// Reconstruct nonce domain hash from entropy pool + deltas").unwrap();
writeln!(f, " #[inline(never)]").unwrap();
writeln!(f, " pub fn get_nonce_domain_hash() -> [u8; 32] {{").unwrap();
writeln!(f, " let mut hash = [0u8; 32];").unwrap();
writeln!(f, " let start = core::hint::black_box({});", nonce_hash_start).unwrap();
writeln!(f, " let step = core::hint::black_box({});", nonce_hash_step).unwrap();
writeln!(f, " for i in 0..32 {{").unwrap();
writeln!(f, " let pool_idx = (start + i * step) % POOL_SIZE;").unwrap();
writeln!(f, " hash[i] = ENTROPY_POOL[pool_idx] ^ NONCE_DOMAIN_HASH_DELTAS[i];").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, " hash").unwrap();
writeln!(f, " }}").unwrap();
writeln!(f, "}}").unwrap();
writeln!(f).unwrap();
}