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.
This commit is contained in:
commit
a641e69455
140 changed files with 17197 additions and 0 deletions
245
crates/noxis-runtime/src/decoder/xtea.rs
Normal file
245
crates/noxis-runtime/src/decoder/xtea.rs
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
//! Ветвь `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,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue