use crate::opcodes::{arithmetic, stack};
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub enum AddVariant {
Direct,
XorAndDouble,
OrAnd,
DoubleOrMinusXor,
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub enum SubVariant {
Direct,
XorMinusNotAndDouble,
TwosComplement,
MaskedDifference,
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub enum XorVariant {
Direct,
OrAndNotAnd,
MaskedOr,
OrMinusAnd,
}
pub struct MbaTransformer {
seed: u64,
}
impl MbaTransformer {
pub fn new(seed: u64) -> Self {
Self { seed }
}
fn next_random(&mut self) -> u64 {
self.seed = self.seed.wrapping_mul(0x5DEECE66D).wrapping_add(0xB);
self.seed
}
fn select_add_variant(&mut self) -> AddVariant {
match self.next_random() % 8 {
0 => AddVariant::XorAndDouble,
1 => AddVariant::OrAnd,
_ => AddVariant::Direct, }
}
fn select_sub_variant(&mut self) -> SubVariant {
match self.next_random() % 8 {
0 => SubVariant::TwosComplement, 1 => SubVariant::XorMinusNotAndDouble,
_ => SubVariant::Direct, }
}
fn select_xor_variant(&mut self) -> XorVariant {
match self.next_random() % 8 {
0 => XorVariant::OrMinusAnd, 1 => XorVariant::OrAndNotAnd,
_ => XorVariant::Direct, }
}
pub fn emit_add(&mut self, output: &mut Vec<u8>, opcode_encoder: impl Fn(u8) -> u8) {
let variant = self.select_add_variant();
match variant {
AddVariant::Direct => {
output.push(opcode_encoder(arithmetic::ADD));
}
AddVariant::XorAndDouble => {
output.push(opcode_encoder(stack::DUP)); output.push(opcode_encoder(stack::POP_REG));
output.push(7);
output.push(opcode_encoder(stack::SWAP)); output.push(opcode_encoder(stack::DUP)); output.push(opcode_encoder(stack::POP_REG));
output.push(6);
output.push(opcode_encoder(arithmetic::AND));
output.push(opcode_encoder(stack::PUSH_IMM8));
output.push(1);
output.push(opcode_encoder(arithmetic::SHL));
output.push(opcode_encoder(stack::PUSH_REG));
output.push(7); output.push(opcode_encoder(stack::PUSH_REG));
output.push(6); output.push(opcode_encoder(arithmetic::XOR));
output.push(opcode_encoder(arithmetic::ADD)); }
AddVariant::OrAnd => {
output.push(opcode_encoder(stack::DUP)); output.push(opcode_encoder(stack::POP_REG));
output.push(7);
output.push(opcode_encoder(stack::SWAP)); output.push(opcode_encoder(stack::DUP)); output.push(opcode_encoder(stack::POP_REG));
output.push(6);
output.push(opcode_encoder(arithmetic::OR));
output.push(opcode_encoder(stack::PUSH_REG));
output.push(7);
output.push(opcode_encoder(stack::PUSH_REG));
output.push(6);
output.push(opcode_encoder(arithmetic::AND));
output.push(opcode_encoder(arithmetic::ADD)); }
AddVariant::DoubleOrMinusXor => {
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(7);
output.push(opcode_encoder(stack::SWAP));
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(6);
output.push(opcode_encoder(arithmetic::OR));
output.push(opcode_encoder(stack::PUSH_IMM8));
output.push(1);
output.push(opcode_encoder(arithmetic::SHL));
output.push(opcode_encoder(stack::PUSH_REG));
output.push(7);
output.push(opcode_encoder(stack::PUSH_REG));
output.push(6);
output.push(opcode_encoder(arithmetic::XOR));
output.push(opcode_encoder(arithmetic::SUB)); }
}
}
pub fn emit_sub(&mut self, output: &mut Vec<u8>, opcode_encoder: impl Fn(u8) -> u8) {
let variant = self.select_sub_variant();
match variant {
SubVariant::Direct => {
output.push(opcode_encoder(arithmetic::SUB));
}
SubVariant::TwosComplement => {
output.push(opcode_encoder(arithmetic::NOT)); output.push(opcode_encoder(arithmetic::INC)); output.push(opcode_encoder(arithmetic::ADD)); }
SubVariant::XorMinusNotAndDouble => {
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(7);
output.push(opcode_encoder(stack::SWAP));
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(6);
output.push(opcode_encoder(stack::SWAP));
output.push(opcode_encoder(arithmetic::XOR));
output.push(opcode_encoder(stack::PUSH_REG));
output.push(6); output.push(opcode_encoder(arithmetic::NOT)); output.push(opcode_encoder(stack::PUSH_REG));
output.push(7); output.push(opcode_encoder(arithmetic::AND));
output.push(opcode_encoder(stack::PUSH_IMM8));
output.push(1);
output.push(opcode_encoder(arithmetic::SHL));
output.push(opcode_encoder(arithmetic::SUB)); }
SubVariant::MaskedDifference => {
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(7);
output.push(opcode_encoder(stack::SWAP));
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(6);
output.push(opcode_encoder(stack::DROP)); output.push(opcode_encoder(stack::DROP));
output.push(opcode_encoder(stack::PUSH_REG));
output.push(6); output.push(opcode_encoder(stack::PUSH_REG));
output.push(7); output.push(opcode_encoder(arithmetic::NOT)); output.push(opcode_encoder(arithmetic::AND));
output.push(opcode_encoder(stack::PUSH_REG));
output.push(6); output.push(opcode_encoder(arithmetic::NOT)); output.push(opcode_encoder(stack::PUSH_REG));
output.push(7); output.push(opcode_encoder(arithmetic::AND));
output.push(opcode_encoder(arithmetic::SUB)); }
}
}
pub fn emit_xor(&mut self, output: &mut Vec<u8>, opcode_encoder: impl Fn(u8) -> u8) {
let variant = self.select_xor_variant();
match variant {
XorVariant::Direct => {
output.push(opcode_encoder(arithmetic::XOR));
}
XorVariant::OrAndNotAnd => {
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(7);
output.push(opcode_encoder(stack::SWAP));
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(6);
output.push(opcode_encoder(arithmetic::OR));
output.push(opcode_encoder(stack::PUSH_REG));
output.push(6);
output.push(opcode_encoder(stack::PUSH_REG));
output.push(7);
output.push(opcode_encoder(arithmetic::AND));
output.push(opcode_encoder(arithmetic::NOT)); output.push(opcode_encoder(arithmetic::AND)); }
XorVariant::MaskedOr => {
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(7);
output.push(opcode_encoder(stack::SWAP));
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(6);
output.push(opcode_encoder(stack::DROP));
output.push(opcode_encoder(stack::DROP));
output.push(opcode_encoder(stack::PUSH_REG));
output.push(6); output.push(opcode_encoder(stack::PUSH_REG));
output.push(7); output.push(opcode_encoder(arithmetic::NOT)); output.push(opcode_encoder(arithmetic::AND));
output.push(opcode_encoder(stack::PUSH_REG));
output.push(6); output.push(opcode_encoder(arithmetic::NOT)); output.push(opcode_encoder(stack::PUSH_REG));
output.push(7); output.push(opcode_encoder(arithmetic::AND));
output.push(opcode_encoder(arithmetic::OR)); }
XorVariant::OrMinusAnd => {
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(7);
output.push(opcode_encoder(stack::SWAP));
output.push(opcode_encoder(stack::DUP));
output.push(opcode_encoder(stack::POP_REG));
output.push(6);
output.push(opcode_encoder(arithmetic::OR));
output.push(opcode_encoder(stack::PUSH_REG));
output.push(6);
output.push(opcode_encoder(stack::PUSH_REG));
output.push(7);
output.push(opcode_encoder(arithmetic::AND));
output.push(opcode_encoder(arithmetic::SUB)); }
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_variants() {
let mut mba = MbaTransformer::new(12345);
let mut output = Vec::new();
mba.emit_add(&mut output, |x| x);
assert!(!output.is_empty());
let first_output = output.clone();
output.clear();
let mut mba2 = MbaTransformer::new(12345);
mba2.emit_add(&mut output, |x| x);
assert_eq!(first_output, output);
}
#[test]
fn test_variant_selection() {
let _mba = MbaTransformer::new(0);
let mut variants_seen = std::collections::HashSet::new();
for seed in 0..100 {
let mut m = MbaTransformer::new(seed);
let v = m.select_add_variant();
variants_seen.insert(std::mem::discriminant(&v));
}
assert!(variants_seen.len() > 1);
}
}