#![allow(dead_code)]
use crate::opcodes::{stack, arithmetic, control, special};
pub struct Substitution {
rng: u64,
enabled: bool,
}
impl Substitution {
pub fn new(seed: u64, enabled: bool) -> Self {
Self {
rng: seed ^ 0xCAFEBABE_DEADBEEF,
enabled,
}
}
pub fn next_rand(&mut self) -> u64 {
self.rng = self.rng
.wrapping_mul(0x5851F42D4C957F2D)
.wrapping_add(0x14057B7EF767814F);
self.rng
}
pub fn should_substitute(&mut self) -> bool {
self.enabled && (self.next_rand() % 100) < 50
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
}
pub enum IncSubstitution {
Original,
PushOneAdd,
PushNegOneSub,
}
impl IncSubstitution {
pub fn choose(subst: &mut Substitution) -> Self {
if !subst.should_substitute() {
return Self::Original;
}
match subst.next_rand() % 3 {
0 => Self::PushOneAdd,
1 => Self::PushNegOneSub,
_ => Self::Original,
}
}
pub fn emit<F: Fn(u8) -> u8>(&self, bytecode: &mut Vec<u8>, encode: &F) -> (bool, bool) {
match self {
Self::Original => {
bytecode.push(encode(arithmetic::INC));
(false, false)
}
Self::PushOneAdd => {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(1);
(true, false) }
Self::PushNegOneSub => {
bytecode.push(encode(stack::PUSH_IMM));
bytecode.extend_from_slice(&u64::MAX.to_le_bytes());
(false, true) }
}
}
}
pub enum DecSubstitution {
Original,
PushOneSub,
PushNegOneAdd,
}
impl DecSubstitution {
pub fn choose(subst: &mut Substitution) -> Self {
if !subst.should_substitute() {
return Self::Original;
}
match subst.next_rand() % 3 {
0 => Self::PushOneSub,
1 => Self::PushNegOneAdd,
_ => Self::Original,
}
}
pub fn emit<F: Fn(u8) -> u8>(&self, bytecode: &mut Vec<u8>, encode: &F) -> (bool, bool) {
match self {
Self::Original => {
bytecode.push(encode(arithmetic::DEC));
(false, false)
}
Self::PushOneSub => {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(1);
(false, true) }
Self::PushNegOneAdd => {
bytecode.push(encode(stack::PUSH_IMM));
bytecode.extend_from_slice(&u64::MAX.to_le_bytes());
(true, false) }
}
}
}
pub enum NotSubstitution {
Original,
XorWithMax,
}
impl NotSubstitution {
pub fn choose(subst: &mut Substitution) -> Self {
if subst.should_substitute() {
Self::XorWithMax
} else {
Self::Original
}
}
pub fn emit<F: Fn(u8) -> u8>(&self, bytecode: &mut Vec<u8>, encode: &F) -> bool {
match self {
Self::Original => {
bytecode.push(encode(arithmetic::NOT));
false
}
Self::XorWithMax => {
bytecode.push(encode(stack::PUSH_IMM));
bytecode.extend_from_slice(&u64::MAX.to_le_bytes());
true }
}
}
}
pub struct AndSubstitution;
impl AndSubstitution {
pub fn should_use(subst: &mut Substitution) -> bool {
subst.should_substitute()
}
pub fn emit_demorgan_prefix<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(stack::SWAP));
}
pub fn emit_demorgan_swap<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(stack::SWAP));
}
pub fn emit_demorgan_or<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(arithmetic::OR));
}
pub fn emit_original<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(arithmetic::AND));
}
}
pub struct OrSubstitution;
impl OrSubstitution {
pub fn should_use(subst: &mut Substitution) -> bool {
subst.should_substitute()
}
pub fn emit_demorgan_prefix<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(stack::SWAP));
}
pub fn emit_demorgan_swap<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(stack::SWAP));
}
pub fn emit_demorgan_and<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(arithmetic::AND));
}
pub fn emit_original<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(arithmetic::OR));
}
}
pub struct ConstantSubstitution;
impl ConstantSubstitution {
pub fn should_split(subst: &mut Substitution, value: u64) -> bool {
subst.should_substitute() && value > 1 && value < u64::MAX - 1000
}
pub fn split(subst: &mut Substitution, value: u64) -> (u64, u64) {
let a = subst.next_rand() % (value / 2 + 1);
let b = value - a;
(a, b)
}
pub fn emit_value<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, value: u64, encode: &F) {
if value <= 255 {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(value as u8);
} else {
bytecode.push(encode(stack::PUSH_IMM));
bytecode.extend_from_slice(&value.to_le_bytes());
}
}
}
pub struct ZeroSubstitution;
impl ZeroSubstitution {
pub fn should_obfuscate(subst: &mut Substitution) -> bool {
subst.should_substitute()
}
pub fn get_xor_value(subst: &mut Substitution) -> u8 {
(subst.next_rand() % 255) as u8 + 1 }
pub fn emit_prefix<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, x: u8, encode: &F) {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::DUP));
}
pub fn emit_original<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(0);
}
}
pub enum AddSubstitution {
Original,
SubNegate,
NotSubNot,
}
impl AddSubstitution {
pub fn choose(subst: &mut Substitution) -> Self {
if !subst.should_substitute() {
return Self::Original;
}
match subst.next_rand() % 5 {
0 => Self::SubNegate,
1 => Self::NotSubNot,
_ => Self::Original,
}
}
pub fn emit<F: Fn(u8) -> u8>(&self, bytecode: &mut Vec<u8>, encode: &F) -> AddEmitResult {
match self {
Self::Original => {
bytecode.push(encode(arithmetic::ADD));
AddEmitResult::Done
}
Self::SubNegate => {
bytecode.push(encode(arithmetic::NOT));
bytecode.push(encode(arithmetic::INC));
bytecode.push(encode(arithmetic::SUB));
AddEmitResult::Done
}
Self::NotSubNot => {
bytecode.push(encode(stack::SWAP));
bytecode.push(encode(arithmetic::NOT));
bytecode.push(encode(stack::SWAP));
bytecode.push(encode(arithmetic::SUB));
bytecode.push(encode(arithmetic::NOT));
AddEmitResult::Done
}
}
}
}
pub enum AddEmitResult {
Done,
}
pub enum SubSubstitution {
Original,
AddNegate,
NotAddNot,
}
impl SubSubstitution {
pub fn choose(subst: &mut Substitution) -> Self {
if !subst.should_substitute() {
return Self::Original;
}
match subst.next_rand() % 5 {
0 => Self::AddNegate,
1 => Self::NotAddNot,
_ => Self::Original,
}
}
pub fn emit<F: Fn(u8) -> u8>(&self, bytecode: &mut Vec<u8>, encode: &F) -> SubEmitResult {
match self {
Self::Original => {
bytecode.push(encode(arithmetic::SUB));
SubEmitResult::Done
}
Self::AddNegate => {
bytecode.push(encode(arithmetic::NOT));
bytecode.push(encode(arithmetic::INC));
bytecode.push(encode(arithmetic::ADD));
SubEmitResult::Done
}
Self::NotAddNot => {
bytecode.push(encode(stack::SWAP));
bytecode.push(encode(arithmetic::NOT));
bytecode.push(encode(stack::SWAP));
bytecode.push(encode(arithmetic::ADD));
bytecode.push(encode(arithmetic::NOT));
SubEmitResult::Done
}
}
}
}
pub enum SubEmitResult {
Done,
}
pub enum MulSubstitution {
Original,
DoubleAdd,
DoubleShift,
TripleShiftAdd,
QuadShift,
}
impl MulSubstitution {
pub fn choose_for_constant(subst: &mut Substitution, multiplier: u64) -> Option<Self> {
if !subst.should_substitute() {
return None;
}
match multiplier {
2 => {
if subst.next_rand().is_multiple_of(2) {
Some(Self::DoubleAdd)
} else {
Some(Self::DoubleShift)
}
}
3 => Some(Self::TripleShiftAdd),
4 => Some(Self::QuadShift),
8 => Some(Self::QuadShift), _ => None,
}
}
pub fn emit_mul2<F: Fn(u8) -> u8>(variant: &Self, bytecode: &mut Vec<u8>, encode: &F) {
match variant {
Self::DoubleAdd => {
bytecode.push(encode(stack::DUP));
bytecode.push(encode(arithmetic::ADD));
}
Self::DoubleShift => {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(1);
bytecode.push(encode(arithmetic::SHL));
}
_ => {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(2);
bytecode.push(encode(arithmetic::MUL));
}
}
}
pub fn emit_mul3<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(stack::DUP));
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(1);
bytecode.push(encode(arithmetic::SHL));
bytecode.push(encode(arithmetic::ADD));
}
pub fn emit_mul_pow2<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, shift: u8, encode: &F) {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(shift);
bytecode.push(encode(arithmetic::SHL));
}
}
pub enum XorSubstitution {
Original,
OrAndNot,
MaskedOr,
}
impl XorSubstitution {
pub fn choose(subst: &mut Substitution) -> Self {
if !subst.should_substitute() {
return Self::Original;
}
match subst.next_rand() % 4 {
0 => Self::OrAndNot,
1 => Self::MaskedOr,
_ => Self::Original, }
}
pub fn emit<F: Fn(u8) -> u8>(&self, bytecode: &mut Vec<u8>, encode: &F) {
match self {
Self::Original => {
bytecode.push(encode(arithmetic::XOR));
}
Self::OrAndNot => {
bytecode.push(encode(arithmetic::XOR));
}
Self::MaskedOr => {
bytecode.push(encode(arithmetic::XOR));
}
}
}
}
pub struct DeadCodeInsertion;
impl DeadCodeInsertion {
pub fn should_insert(subst: &mut Substitution) -> bool {
subst.enabled && (subst.next_rand() % 100) < 15 }
pub fn emit<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
match subst.next_rand() % 3 {
0 => Self::emit_push_drop(subst, bytecode, encode),
1 => Self::emit_xor_zero(subst, bytecode, encode),
_ => Self::emit_push_pop_balanced(subst, bytecode, encode),
}
}
pub fn emit_deterministic<F: Fn(u8) -> u8>(position: usize, bytecode: &mut Vec<u8>, encode: &F) {
let entropy = (position as u64)
.wrapping_mul(0x9E3779B97F4A7C15)
.wrapping_add(0x1234567890ABCDEF);
if (entropy % 100) >= 10 {
return;
}
let pattern = (entropy / 100) % 3;
match pattern {
0 => {
let x = ((entropy >> 8) % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::DROP));
}
1 => {
let x = ((entropy >> 16) % 256) as u8;
let y = ((entropy >> 24) % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(y);
bytecode.push(encode(stack::DROP));
bytecode.push(encode(stack::DROP));
}
_ => {
bytecode.push(encode(crate::opcodes::special::NOP));
}
}
}
pub fn emit_with_stack<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
match subst.next_rand() % 5 {
0 => Self::emit_push_drop(subst, bytecode, encode),
1 => Self::emit_dup_drop(bytecode, encode),
2 => Self::emit_xor_zero(subst, bytecode, encode),
3 => Self::emit_not_not(bytecode, encode),
_ => Self::emit_add_sub_zero(bytecode, encode),
}
}
fn emit_push_drop<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
let x = (subst.next_rand() % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::DROP));
}
fn emit_dup_drop<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(stack::DUP));
bytecode.push(encode(stack::DROP));
}
fn emit_xor_zero<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
let x = (subst.next_rand() % 256) as u8;
let y = (subst.next_rand() % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(y);
bytecode.push(encode(stack::DROP));
bytecode.push(encode(stack::DROP));
}
fn emit_not_not<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(arithmetic::NOT));
bytecode.push(encode(arithmetic::NOT));
}
fn emit_add_sub_zero<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(0);
bytecode.push(encode(arithmetic::ADD));
}
fn emit_push_pop_balanced<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
let x = (subst.next_rand() % 100) as u8;
let y = (subst.next_rand() % 100) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(y);
bytecode.push(encode(arithmetic::ADD));
bytecode.push(encode(stack::DROP));
}
}
pub struct OpaquePredicate;
impl OpaquePredicate {
pub fn emit_always_true<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
match subst.next_rand() % 4 {
0 => Self::emit_xor_self_eq_zero(subst, bytecode, encode),
1 => Self::emit_or_one_ne_zero(subst, bytecode, encode),
2 => Self::emit_square_ge_zero(subst, bytecode, encode),
_ => Self::emit_and_self_eq_self(subst, bytecode, encode),
}
}
pub fn emit_always_false<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
match subst.next_rand() % 3 {
0 => Self::emit_xor_self_ne_zero(subst, bytecode, encode),
1 => Self::emit_and_zero(subst, bytecode, encode),
_ => Self::emit_false_via_sub(subst, bytecode, encode),
}
}
fn emit_xor_self_eq_zero<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
let x = (subst.next_rand() % 255) as u8 + 1;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::DUP));
bytecode.push(encode(arithmetic::XOR));
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(0);
bytecode.push(encode(arithmetic::XOR)); bytecode.push(encode(stack::DROP)); bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(1); }
fn emit_or_one_ne_zero<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
let x = (subst.next_rand() % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(1);
bytecode.push(encode(arithmetic::OR));
bytecode.push(encode(stack::DROP));
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(1);
}
fn emit_square_ge_zero<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
let x = (subst.next_rand() % 10) as u8 + 1; bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::DUP));
bytecode.push(encode(arithmetic::MUL));
bytecode.push(encode(stack::DROP));
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(1);
}
fn emit_and_self_eq_self<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
let x = (subst.next_rand() % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::DUP));
bytecode.push(encode(arithmetic::AND));
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(arithmetic::XOR)); bytecode.push(encode(stack::DROP));
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(1);
}
fn emit_xor_self_ne_zero<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
let x = (subst.next_rand() % 255) as u8 + 1;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::DUP));
bytecode.push(encode(arithmetic::XOR)); bytecode.push(encode(stack::DROP));
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(0);
}
fn emit_and_zero<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
let x = (subst.next_rand() % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(0);
bytecode.push(encode(arithmetic::AND)); }
fn emit_false_via_sub<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
let x = (subst.next_rand() % 255) as u8 + 1;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::DUP));
bytecode.push(encode(arithmetic::SUB)); }
}
pub struct ComparisonSubstitution;
impl ComparisonSubstitution {
pub fn should_use(subst: &mut Substitution) -> bool {
subst.should_substitute()
}
pub fn emit_eq_obfuscated<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(arithmetic::XOR));
}
pub fn emit_ne_obfuscated<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(arithmetic::XOR));
}
pub fn emit_lt_via_sub<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(control::CMP));
}
}
pub struct ControlFlowSubstitution;
impl ControlFlowSubstitution {
pub fn should_use(subst: &mut Substitution) -> bool {
subst.enabled && (subst.next_rand() % 100) < 30 }
pub fn emit_jz_obfuscated<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, encode: &F) {
bytecode.push(encode(stack::DUP));
bytecode.push(encode(stack::DUP));
bytecode.push(encode(arithmetic::XOR)); bytecode.push(encode(arithmetic::XOR)); bytecode.push(encode(stack::DROP)); }
pub fn emit_fake_conditional<F: Fn(u8) -> u8>(subst: &mut Substitution, bytecode: &mut Vec<u8>, encode: &F) {
match subst.next_rand() % 5 {
0 => {
let x = (subst.next_rand() % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::DROP));
}
1 => {
let x = (subst.next_rand() % 256) as u8;
let y = (subst.next_rand() % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(y);
bytecode.push(encode(stack::DROP));
bytecode.push(encode(stack::DROP));
}
2 => {
let x = (subst.next_rand() % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::DUP));
bytecode.push(encode(stack::DROP));
bytecode.push(encode(stack::DROP));
}
3 => {
let x = (subst.next_rand() % 256) as u8;
let y = (subst.next_rand() % 256) as u8;
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(x);
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(y);
bytecode.push(encode(stack::SWAP));
bytecode.push(encode(stack::DROP));
bytecode.push(encode(stack::DROP));
}
_ => {
let nop_count = (subst.next_rand() % 3) as usize + 1;
for _ in 0..nop_count {
bytecode.push(encode(special::NOP));
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_substitution_disabled() {
let mut subst = Substitution::new(12345, false);
assert!(!subst.should_substitute());
}
#[test]
fn test_substitution_enabled() {
let mut subst = Substitution::new(12345, true);
let mut found_true = false;
for _ in 0..100 {
if subst.should_substitute() {
found_true = true;
break;
}
}
assert!(found_true);
}
#[test]
fn test_deterministic() {
let mut s1 = Substitution::new(99999, true);
let mut s2 = Substitution::new(99999, true);
for _ in 0..10 {
assert_eq!(s1.next_rand(), s2.next_rand());
}
}
#[test]
fn test_constant_split() {
let mut subst = Substitution::new(12345, true);
let (a, b) = ConstantSubstitution::split(&mut subst, 100);
assert_eq!(a + b, 100);
}
#[test]
fn test_add_substitution_variants() {
let mut subst = Substitution::new(12345, true);
let mut bytecode = Vec::new();
let encode = |x: u8| x;
for _ in 0..20 {
let variant = AddSubstitution::choose(&mut subst);
let initial_len = bytecode.len();
variant.emit(&mut bytecode, &encode);
assert!(bytecode.len() > initial_len, "ADD variant should emit bytecode");
}
}
#[test]
fn test_sub_substitution_variants() {
let mut subst = Substitution::new(54321, true);
let mut bytecode = Vec::new();
let encode = |x: u8| x;
for _ in 0..20 {
let variant = SubSubstitution::choose(&mut subst);
let initial_len = bytecode.len();
variant.emit(&mut bytecode, &encode);
assert!(bytecode.len() > initial_len, "SUB variant should emit bytecode");
}
}
#[test]
fn test_dead_code_insertion() {
let mut subst = Substitution::new(11111, true);
let mut bytecode = Vec::new();
let encode = |x: u8| x;
DeadCodeInsertion::emit(&mut subst, &mut bytecode, &encode);
assert!(!bytecode.is_empty(), "Dead code should emit something");
}
#[test]
fn test_opaque_predicate_true() {
let mut subst = Substitution::new(22222, true);
let mut bytecode = Vec::new();
let encode = |x: u8| x;
OpaquePredicate::emit_always_true(&mut subst, &mut bytecode, &encode);
assert!(!bytecode.is_empty(), "Opaque true should emit bytecode");
}
#[test]
fn test_opaque_predicate_false() {
let mut subst = Substitution::new(33333, true);
let mut bytecode = Vec::new();
let encode = |x: u8| x;
OpaquePredicate::emit_always_false(&mut subst, &mut bytecode, &encode);
assert!(!bytecode.is_empty(), "Opaque false should emit bytecode");
}
#[test]
fn test_mul_substitution_for_powers() {
let mut subst = Substitution::new(44444, true);
assert!(MulSubstitution::choose_for_constant(&mut subst, 2).is_some() ||
!subst.should_substitute());
let mut subst2 = Substitution::new(44444, true);
let mut found = false;
for _ in 0..10 {
if MulSubstitution::choose_for_constant(&mut subst2, 3).is_some() {
found = true;
break;
}
}
}
#[test]
fn test_xor_substitution() {
let mut subst = Substitution::new(55555, true);
let mut bytecode = Vec::new();
let encode = |x: u8| x;
for _ in 0..10 {
let variant = XorSubstitution::choose(&mut subst);
let initial_len = bytecode.len();
variant.emit(&mut bytecode, &encode);
assert!(bytecode.len() > initial_len, "XOR variant should emit bytecode");
}
}
#[test]
fn test_comparison_substitution() {
let mut bytecode = Vec::new();
let encode = |x: u8| x;
ComparisonSubstitution::emit_eq_obfuscated(&mut bytecode, &encode);
assert!(!bytecode.is_empty());
bytecode.clear();
ComparisonSubstitution::emit_ne_obfuscated(&mut bytecode, &encode);
assert!(!bytecode.is_empty());
}
#[test]
fn test_control_flow_substitution() {
let mut subst = Substitution::new(66666, true);
let mut bytecode = Vec::new();
let encode = |x: u8| x;
ControlFlowSubstitution::emit_jz_obfuscated(&mut bytecode, &encode);
assert!(!bytecode.is_empty());
bytecode.clear();
ControlFlowSubstitution::emit_fake_conditional(&mut subst, &mut bytecode, &encode);
assert!(!bytecode.is_empty());
}
}