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
136
crates/noxis-classfile/src/insn/kinds.rs
Normal file
136
crates/noxis-classfile/src/insn/kinds.rs
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
//! Типы операндов инструкций: константы, примитивы, условия, вызовы.
|
||||
|
||||
/// Идентификатор метки внутри одного метода.
|
||||
pub type Label = u32;
|
||||
|
||||
/// Константа для `ldc`/`ldc_w`/`ldc2_w`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Const {
|
||||
Int(i32),
|
||||
Long(i64),
|
||||
Float(f32),
|
||||
Double(f64),
|
||||
String(String),
|
||||
/// Внутреннее имя класса (`java/lang/String`).
|
||||
Class(String),
|
||||
MethodType(String),
|
||||
MethodHandle {
|
||||
kind: u8,
|
||||
owner: String,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
itf: bool,
|
||||
},
|
||||
/// `CONSTANT_Dynamic`: имя, дескриптор, индекс bootstrap-метода.
|
||||
Dynamic {
|
||||
name: String,
|
||||
descriptor: String,
|
||||
bootstrap: u16,
|
||||
},
|
||||
}
|
||||
|
||||
/// Размер операнда/результата арифметики.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Prim {
|
||||
Int,
|
||||
Long,
|
||||
Float,
|
||||
Double,
|
||||
}
|
||||
|
||||
/// Арифметическая/битовая операция; тип берётся из [`Prim`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Arith {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Rem,
|
||||
Neg,
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
Shl,
|
||||
Shr,
|
||||
Ushr,
|
||||
}
|
||||
|
||||
/// Условие перехода.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Cond {
|
||||
Eq,
|
||||
Ne,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
}
|
||||
|
||||
/// Вид условного перехода (соответствует семействам `if*`).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BranchCond {
|
||||
/// `if<cond>` относительно нуля.
|
||||
Zero(Cond),
|
||||
/// `if_icmp<cond>`.
|
||||
Int(Cond),
|
||||
/// `if_acmp<eq|ne>`.
|
||||
Ref { eq: bool },
|
||||
/// `ifnull` / `ifnonnull`.
|
||||
Null { is_null: bool },
|
||||
}
|
||||
|
||||
/// Вид вызова.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum InvokeKind {
|
||||
Virtual,
|
||||
Special,
|
||||
Static,
|
||||
Interface,
|
||||
}
|
||||
|
||||
/// Конверсия типов (`i2l`, `f2b`, ... — JVMS §6.5.i2<b|c|s|l|f|d>).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Convert {
|
||||
I2L,
|
||||
I2F,
|
||||
I2D,
|
||||
I2B,
|
||||
I2C,
|
||||
I2S,
|
||||
L2I,
|
||||
L2F,
|
||||
L2D,
|
||||
F2I,
|
||||
F2L,
|
||||
F2D,
|
||||
D2I,
|
||||
D2L,
|
||||
D2F,
|
||||
}
|
||||
|
||||
/// Тип элемента массива для `newarray`/`*aload`/`*astore`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ArrayKind {
|
||||
Boolean,
|
||||
Char,
|
||||
Float,
|
||||
Double,
|
||||
Byte,
|
||||
Short,
|
||||
Int,
|
||||
Long,
|
||||
Reference,
|
||||
}
|
||||
|
||||
/// Тип слота локальной переменной для `*load*`/`*store*`.
|
||||
///
|
||||
/// Хранится явно: выбор опкода (`iload` vs `aload`) — подсказка верификатору,
|
||||
/// потерять её при round-trip нельзя.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SlotKind {
|
||||
Int,
|
||||
Long,
|
||||
Float,
|
||||
Double,
|
||||
Ref,
|
||||
}
|
||||
164
crates/noxis-classfile/src/insn/mod.rs
Normal file
164
crates/noxis-classfile/src/insn/mod.rs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
//! Инструкции IR: смещения отсутствуют, ветвления адресуют [`Label`].
|
||||
//!
|
||||
//! Это принципиально: любой пасс, меняющий порядок/длину инструкций, не обязан
|
||||
//! пересчитывать jump-офсеты — это делает writer один раз в конце пайплайна.
|
||||
|
||||
mod kinds;
|
||||
|
||||
pub use kinds::{
|
||||
Arith, ArrayKind, BranchCond, Cond, Const, Convert, InvokeKind, Label, Prim, SlotKind,
|
||||
};
|
||||
|
||||
/// Мнемоника одной инструкции.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Insn {
|
||||
/// Маркер метки. Ветвления указывают на [`Label`], writer вычисляет смещения.
|
||||
Mark(Label),
|
||||
Nop,
|
||||
AconstNull,
|
||||
Const(Const),
|
||||
/// Загрузка локальной переменной (`iload`, `aload`, ...).
|
||||
Load {
|
||||
slot: u16,
|
||||
kind: SlotKind,
|
||||
},
|
||||
/// Сохранение локальной переменной.
|
||||
Store {
|
||||
slot: u16,
|
||||
kind: SlotKind,
|
||||
},
|
||||
/// `iinc` — увеличение локальной переменной на константу.
|
||||
Iinc {
|
||||
index: u16,
|
||||
delta: i32,
|
||||
},
|
||||
/// Загрузка элемента массива.
|
||||
ArrayLoad(ArrayKind),
|
||||
/// Сохранение элемента массива.
|
||||
ArrayStore(ArrayKind),
|
||||
Pop,
|
||||
Pop2,
|
||||
Dup,
|
||||
DupX1,
|
||||
DupX2,
|
||||
Dup2,
|
||||
Dup2X1,
|
||||
Dup2X2,
|
||||
Swap,
|
||||
/// `iadd`, `lmul`, `lneg`, ... — тип + операция.
|
||||
Binary {
|
||||
prim: Prim,
|
||||
op: Arith,
|
||||
},
|
||||
/// Сравнение длин (`lcmp`) и плавающих (`fcmpl`/`fcmpg`).
|
||||
Cmp {
|
||||
prim: Prim,
|
||||
nan_great: bool,
|
||||
},
|
||||
/// Конверсия типов (`i2l`, `d2f`, ...).
|
||||
Convert(Convert),
|
||||
Branch(BranchCond, Label),
|
||||
Goto(Label),
|
||||
/// `jsr` — вызов подпрограммы (только классы версии ≤ 50).
|
||||
Jsr(Label),
|
||||
/// `ret` — возврат из подпрограммы (`jsr`/`ret` парны).
|
||||
Ret(u16),
|
||||
TableSwitch {
|
||||
default: Label,
|
||||
low: i32,
|
||||
high: i32,
|
||||
targets: Vec<Label>,
|
||||
},
|
||||
LookupSwitch {
|
||||
default: Label,
|
||||
pairs: Vec<(i32, Label)>,
|
||||
},
|
||||
/// `return`/`ireturn`/...; `None` — пустой возврат.
|
||||
Return(Option<Prim>),
|
||||
/// `areturn` — возврат ссылки (у [`Prim`] нет ссылочного типа).
|
||||
AReturn,
|
||||
GetStatic {
|
||||
owner: String,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
},
|
||||
PutStatic {
|
||||
owner: String,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
},
|
||||
GetField {
|
||||
owner: String,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
},
|
||||
PutField {
|
||||
owner: String,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
},
|
||||
Invoke {
|
||||
kind: InvokeKind,
|
||||
owner: String,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
itf: bool,
|
||||
},
|
||||
InvokeDynamic {
|
||||
name: String,
|
||||
descriptor: String,
|
||||
bootstrap: u16,
|
||||
},
|
||||
New(String),
|
||||
NewArray(ArrayKind),
|
||||
ANewArray(String),
|
||||
/// `multianewarray` — массив любого измерения; `dims` — сколько измерений со стека.
|
||||
MultiANewArray {
|
||||
descriptor: String,
|
||||
dims: u8,
|
||||
},
|
||||
ArrayLength,
|
||||
AThrow,
|
||||
CheckCast(String),
|
||||
InstanceOf(String),
|
||||
MonitorEnter,
|
||||
MonitorExit,
|
||||
}
|
||||
|
||||
impl Insn {
|
||||
/// Метки, на которые ссылается инструкция (для построения CFG).
|
||||
pub fn targets(&self) -> Vec<Label> {
|
||||
match self {
|
||||
Insn::Branch(_, label) | Insn::Goto(label) | Insn::Jsr(label) => vec![*label],
|
||||
Insn::TableSwitch {
|
||||
default,
|
||||
targets,
|
||||
low: _,
|
||||
high: _,
|
||||
} => {
|
||||
let mut all = Vec::with_capacity(targets.len() + 1);
|
||||
all.push(*default);
|
||||
all.extend(targets.iter().copied());
|
||||
all
|
||||
}
|
||||
Insn::LookupSwitch { default, pairs } => {
|
||||
let mut all = Vec::with_capacity(pairs.len() + 1);
|
||||
all.push(*default);
|
||||
all.extend(pairs.iter().map(|(k, l)| {
|
||||
let _ = k;
|
||||
*l
|
||||
}));
|
||||
all
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Незакодированный переход (для вставки opaque-предикатов и перестройки CFG).
|
||||
pub fn branch_target_mut(&mut self) -> Option<&mut Label> {
|
||||
match self {
|
||||
Insn::Branch(_, label) | Insn::Goto(label) => Some(label),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue