obfuskator/crates/noxis-runtime/src/decoder/xtea.rs
loki5512344 a641e69455 Initial commit: noxis JVM bytecode obfuscator
Workspace of 9 crates (noxis, noxis-core, noxis-classfile, noxis-analysis,
noxis-passes, noxis-runtime, noxis-jar, noxis-verify, noxis-structure-check).
AGPL-3.0-only license, Rust 2024 edition, unsafe_code denied.
2026-10-08 22:01:03 +02:00

245 lines
8.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Ветвь `xtea-ctr`: XTEA в режиме счётчика, побайтовый XOR на месте.
//!
//! Зеркало `scheme::xtea_ctr` побитово: ключ — четыре `u32` из key/nonce,
//! счётчик — номер чанка, все правые сдвиги — логические (`u32` в Rust).
use noxis_classfile::{Arith, ArrayKind, BranchCond, Cond, Convert, Insn, Prim, SlotKind};
use super::kdf::{Gen, c_int, load, store};
/// XTEA delta — знаковое представление для `ldc_w`.
const DELTA: i32 = 0x9E37_79B9u32 as i32;
/// Расшифровать `data` на месте; `len` — длина массива.
pub(super) fn emit_xtea(asm: &mut Gen, key: u16, nonce: u16, data: u16, len: u16) {
let slots = Slots::alloc(asm);
emit_key_array(asm, key, nonce, &slots);
asm.emit(c_int(0));
asm.emit(store(slots.chunk, SlotKind::Int));
let outer = asm.label();
let done = asm.label();
asm.mark(outer);
asm.emit(load(slots.chunk, SlotKind::Int));
asm.emit(load(len, SlotKind::Int));
asm.emit(Insn::Branch(BranchCond::Int(Cond::Ge), done));
emit_counter(asm, &slots);
emit_rounds(asm, &slots);
emit_xor_bytes(asm, data, len, &slots);
asm.emit(load(slots.chunk, SlotKind::Int));
asm.emit(c_int(8));
asm.emit(int_op(Arith::Add));
asm.emit(store(slots.chunk, SlotKind::Int));
asm.emit(Insn::Goto(outer));
asm.mark(done);
}
/// Слоты ветви.
struct Slots {
keys: u16,
chunk: u16,
value0: u16,
value1: u16,
sum: u16,
round: u16,
counter: u16,
tmp_a: u16,
tmp_b: u16,
src: u16,
shift: u16,
sub: u16,
index: u16,
}
impl Slots {
fn alloc(asm: &mut Gen) -> Self {
Self {
keys: asm.alloc(false),
chunk: asm.alloc(false),
value0: asm.alloc(false),
value1: asm.alloc(false),
sum: asm.alloc(false),
round: asm.alloc(false),
counter: asm.alloc(false),
tmp_a: asm.alloc(false),
tmp_b: asm.alloc(false),
src: asm.alloc(false),
shift: asm.alloc(false),
sub: asm.alloc(false),
index: asm.alloc(false),
}
}
}
/// `k = [key.lo, key.hi, nonce.lo, nonce.hi]` как `int[4]`.
fn emit_key_array(asm: &mut Gen, key: u16, nonce: u16, slots: &Slots) {
asm.emit(c_int(4));
asm.emit(Insn::NewArray(ArrayKind::Int));
asm.emit(store(slots.keys, SlotKind::Ref));
for (position, (source, high)) in [(key, false), (key, true), (nonce, false), (nonce, true)]
.into_iter()
.enumerate()
{
asm.emit(load(slots.keys, SlotKind::Ref));
asm.emit(c_int(position as i32));
asm.emit(load(source, SlotKind::Long));
if high {
asm.emit(c_int(32));
asm.emit(long_shift(Arith::Ushr));
asm.emit(Insn::Convert(Convert::L2I));
} else {
asm.emit(Insn::Convert(Convert::L2I));
}
asm.emit(Insn::ArrayStore(ArrayKind::Int));
}
}
/// Счётчик чанка: `v0 = 0`, `v1 = chunk`, `sum = round = 0`.
fn emit_counter(asm: &mut Gen, slots: &Slots) {
asm.emit(load(slots.chunk, SlotKind::Int));
asm.emit(c_int(3));
asm.emit(int_op(Arith::Ushr));
asm.emit(store(slots.counter, SlotKind::Int));
asm.emit(c_int(0));
asm.emit(store(slots.value0, SlotKind::Int));
asm.emit(load(slots.counter, SlotKind::Int));
asm.emit(store(slots.value1, SlotKind::Int));
asm.emit(c_int(0));
asm.emit(store(slots.sum, SlotKind::Int));
asm.emit(c_int(0));
asm.emit(store(slots.round, SlotKind::Int));
}
/// 32 раунда XTEA над `(v0, v1, sum)`.
fn emit_rounds(asm: &mut Gen, slots: &Slots) {
let again = asm.label();
let done = asm.label();
asm.mark(again);
asm.emit(load(slots.round, SlotKind::Int));
asm.emit(c_int(32));
asm.emit(Insn::Branch(BranchCond::Int(Cond::Ge), done));
mixing(asm, slots.value0, slots.value1, 0, slots);
asm.emit(load(slots.sum, SlotKind::Int));
asm.emit(c_int(DELTA));
asm.emit(int_op(Arith::Add));
asm.emit(store(slots.sum, SlotKind::Int));
mixing(asm, slots.value1, slots.value0, 11, slots);
asm.emit(Insn::Iinc {
index: slots.round,
delta: 1,
});
asm.emit(Insn::Goto(again));
asm.mark(done);
}
/// Половина раунда: `dst += (((src<<4)^(src>>>5)) + src) ^ (sum + k[(sum>>>s)&3])`.
fn mixing(asm: &mut Gen, dst: u16, src: u16, key_shift: i32, slots: &Slots) {
asm.emit(load(src, SlotKind::Int));
asm.emit(c_int(4));
asm.emit(int_op(Arith::Shl));
asm.emit(store(slots.tmp_a, SlotKind::Int));
asm.emit(load(src, SlotKind::Int));
asm.emit(c_int(5));
asm.emit(int_op(Arith::Ushr));
asm.emit(store(slots.tmp_b, SlotKind::Int));
asm.emit(load(slots.tmp_a, SlotKind::Int));
asm.emit(load(slots.tmp_b, SlotKind::Int));
asm.emit(int_op(Arith::Xor));
asm.emit(load(src, SlotKind::Int));
asm.emit(int_op(Arith::Add));
asm.emit(store(slots.tmp_a, SlotKind::Int));
asm.emit(load(slots.sum, SlotKind::Int));
asm.emit(load(slots.keys, SlotKind::Ref));
asm.emit(load(slots.sum, SlotKind::Int));
// Первая половина: `sum & 3` без сдвига; вторая: `(sum >>> s) & 3`.
if key_shift != 0 {
asm.emit(c_int(key_shift));
asm.emit(int_op(Arith::Ushr));
}
asm.emit(c_int(3));
asm.emit(int_op(Arith::And));
asm.emit(Insn::ArrayLoad(ArrayKind::Int));
asm.emit(int_op(Arith::Add));
asm.emit(store(slots.tmp_b, SlotKind::Int));
asm.emit(load(slots.tmp_a, SlotKind::Int));
asm.emit(load(slots.tmp_b, SlotKind::Int));
asm.emit(int_op(Arith::Xor));
asm.emit(load(dst, SlotKind::Int));
asm.emit(int_op(Arith::Add));
asm.emit(store(dst, SlotKind::Int));
}
/// XOR 8 байт чанка со стримом `(v0, v1)`; хвост короче 8 — частично.
fn emit_xor_bytes(asm: &mut Gen, data: u16, len: u16, slots: &Slots) {
asm.emit(c_int(0));
asm.emit(store(slots.index, SlotKind::Int));
let again = asm.label();
let done = asm.label();
let use_v0 = asm.label();
let have_src = asm.label();
asm.mark(again);
asm.emit(load(slots.index, SlotKind::Int));
asm.emit(c_int(8));
asm.emit(Insn::Branch(BranchCond::Int(Cond::Ge), done));
asm.emit(load(len, SlotKind::Int));
asm.emit(load(slots.chunk, SlotKind::Int));
asm.emit(load(slots.index, SlotKind::Int));
asm.emit(int_op(Arith::Add));
asm.emit(Insn::Branch(BranchCond::Int(Cond::Le), done));
asm.emit(load(slots.index, SlotKind::Int));
asm.emit(c_int(4));
asm.emit(int_op(Arith::And));
asm.emit(Insn::Branch(BranchCond::Zero(Cond::Eq), use_v0));
asm.emit(load(slots.value1, SlotKind::Int));
asm.emit(Insn::Goto(have_src));
asm.mark(use_v0);
asm.emit(load(slots.value0, SlotKind::Int));
asm.mark(have_src);
asm.emit(store(slots.src, SlotKind::Int));
asm.emit(load(slots.index, SlotKind::Int));
asm.emit(c_int(3));
asm.emit(int_op(Arith::And));
asm.emit(store(slots.tmp_a, SlotKind::Int));
asm.emit(c_int(3));
asm.emit(load(slots.tmp_a, SlotKind::Int));
asm.emit(int_op(Arith::Sub));
asm.emit(c_int(3));
asm.emit(int_op(Arith::Shl));
asm.emit(store(slots.shift, SlotKind::Int));
asm.emit(load(slots.src, SlotKind::Int));
asm.emit(load(slots.shift, SlotKind::Int));
asm.emit(int_op(Arith::Ushr));
asm.emit(c_int(0xFF));
asm.emit(int_op(Arith::And));
asm.emit(store(slots.sub, SlotKind::Int));
asm.emit(load(data, SlotKind::Ref));
asm.emit(load(slots.chunk, SlotKind::Int));
asm.emit(load(slots.index, SlotKind::Int));
asm.emit(int_op(Arith::Add));
asm.emit(Insn::Dup2);
asm.emit(Insn::ArrayLoad(ArrayKind::Byte));
asm.emit(load(slots.sub, SlotKind::Int));
asm.emit(int_op(Arith::Xor));
asm.emit(Insn::ArrayStore(ArrayKind::Byte));
asm.emit(Insn::Iinc {
index: slots.index,
delta: 1,
});
asm.emit(Insn::Goto(again));
asm.mark(done);
}
/// Сдвиг `long` на `int` (`lshl`/`lushr`).
fn long_shift(op: Arith) -> Insn {
Insn::Binary {
prim: Prim::Long,
op,
}
}
/// `iadd/isub/...` для `int`.
fn int_op(op: Arith) -> Insn {
Insn::Binary {
prim: Prim::Int,
op,
}
}