mirror of
https://github.com/EgeBalci/deoptimizer
synced 2026-06-08 10:55:50 +00:00
First attempt of deoptimize with source.
This commit is contained in:
Generated
+2
-26
@@ -65,12 +65,6 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
@@ -83,12 +77,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
@@ -207,14 +195,12 @@ dependencies = [
|
||||
name = "deoptimizer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"clap",
|
||||
"colored",
|
||||
"ctrlc",
|
||||
"hexdump",
|
||||
"iced-x86",
|
||||
"keystone",
|
||||
"log",
|
||||
"rand",
|
||||
"thiserror",
|
||||
@@ -321,16 +307,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keystone"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0409d156f77bf2a5fc3a9f78233b0594ca5cf7a6021fa617403e6c7a2dd421fe"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -361,7 +337,7 @@ version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
@@ -441,7 +417,7 @@ version = "0.38.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
|
||||
@@ -6,14 +6,12 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
chrono = "0.4.31"
|
||||
clap = { version = "4.4.7", features = ["derive"] }
|
||||
colored = "2.0.4"
|
||||
ctrlc = "3.4.1"
|
||||
hexdump = "0.1.1"
|
||||
iced-x86 = "1.20.0"
|
||||
keystone = "0.9.0"
|
||||
log = "0.4.20"
|
||||
rand = "0.8.5"
|
||||
thiserror = "1.0.50"
|
||||
|
||||
+15
-14
@@ -1,4 +1,5 @@
|
||||
use log::{error, info, warn, LevelFilter};
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
mod options;
|
||||
@@ -24,25 +25,25 @@ fn main() {
|
||||
warn!("The output size will drasstically increase.")
|
||||
}
|
||||
|
||||
let file = utils::read_file(opts.file.clone()).expect("failed reading target file");
|
||||
let file = utils::read_file(opts.file.clone()).expect("failed reading target file!");
|
||||
let mut out_file = File::create(opts.source).expect("source file creation failed!");
|
||||
info!("File size: {}", file.len());
|
||||
let mut deopt = x86_64::Deoptimizer::new();
|
||||
deopt.set_syntax(opts.syntax).expect("invalid syntax");
|
||||
let out = deopt
|
||||
.disassemble(&file, opts.mode, 0x401000)
|
||||
.expect("disassembly failed!");
|
||||
println!("{}", out);
|
||||
match deopt.assemble(out.clone(), opts.mode, 0x401000) {
|
||||
Ok(b) => {
|
||||
let mut f2 = std::fs::File::create("success.bin").expect("failed opening file!");
|
||||
f2.write_all(&b.bytes).expect("failed writing!");
|
||||
}
|
||||
let acode = deopt
|
||||
.analyze(&file, opts.bitness, 0x401000)
|
||||
.expect("code analysis failed!");
|
||||
// let out = deopt.disassemble(&acode).expect("disassembly failed!");
|
||||
// println!("{}", out);
|
||||
info!("Deoptimizing...");
|
||||
let bin = deopt.deoptimize(&acode);
|
||||
let bytes = match bin {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
let mut f2 = std::fs::File::create("error.asm").expect("failed opening file!");
|
||||
let _ = f2.write_all(b"[BITS 32]\n");
|
||||
f2.write_all(out.as_bytes()).expect("failed writing!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
// hexdump::hexdump(&bin.bytes);
|
||||
let _ = out_file.write_all(bytes.as_bytes());
|
||||
info!("All done!");
|
||||
}
|
||||
|
||||
+12
-4
@@ -18,13 +18,17 @@ pub struct Options {
|
||||
#[arg(long, short = 'f', default_value_t = String::new())]
|
||||
pub file: String,
|
||||
|
||||
/// source assembly file.
|
||||
#[arg(long, short = 's', default_value_t = String::new())]
|
||||
pub source: String,
|
||||
|
||||
/// assembler formatter syntax (nasm/masm/intel/gas).
|
||||
#[arg(long, short = 's', default_value_t = String::from("keystone"))]
|
||||
#[arg(long, default_value_t = String::from("keystone"))]
|
||||
pub syntax: String,
|
||||
|
||||
/// bitness of the binary file (32/64).
|
||||
#[arg(long, short = 'm', default_value_t = 32)]
|
||||
pub mode: u32,
|
||||
/// bitness of the binary file (16/32/64).
|
||||
#[arg(long, short = 'b', default_value_t = 64)]
|
||||
pub bitness: u32,
|
||||
|
||||
/// total number of deoptimization cycles.
|
||||
#[arg(long, short = 'c', default_value_t = 1)]
|
||||
@@ -34,6 +38,10 @@ pub struct Options {
|
||||
#[arg(long, short = 'F', default_value_t = 0.5)]
|
||||
pub freq: f64,
|
||||
|
||||
/// allow processing of invalid instructions.
|
||||
#[arg(long)]
|
||||
pub allow_invalid: bool,
|
||||
|
||||
/// verbose output mode.
|
||||
#[arg(long, short = 'v')]
|
||||
pub verbose: bool,
|
||||
|
||||
+258
-167
@@ -1,14 +1,12 @@
|
||||
extern crate keystone;
|
||||
use iced_x86::*;
|
||||
use keystone::{AsmResult, Keystone};
|
||||
use log::{error, info, warn};
|
||||
use rand::seq::SliceRandom;
|
||||
use std::collections::HashMap;
|
||||
use log::{debug, error, info, warn};
|
||||
use rand::{distributions::uniform::UniformSampler, seq::SliceRandom, Rng};
|
||||
use std::{collections::HashMap, error};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::x86_64::{
|
||||
apply_ap_transform, apply_itr_transform, apply_li_transform, apply_om_transform,
|
||||
apply_rs_transform,
|
||||
apply_rs_transform, set_branch_target, to_db_mnemonic,
|
||||
};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -23,6 +21,14 @@ pub enum DeoptimizerError {
|
||||
InstructionNotFound,
|
||||
#[error("Code analysis results are not found.")]
|
||||
MissingCodeAnalysis,
|
||||
#[error("Near branch value too large.")]
|
||||
NearBranchTooBig,
|
||||
#[error("Far branch value too large.")]
|
||||
FarBranchTooBig,
|
||||
#[error("Found invalid instruction.")]
|
||||
InvalidInstruction,
|
||||
#[error("Instruction encoding failed: {0}")]
|
||||
EncodingFail(#[from] IcedError),
|
||||
}
|
||||
|
||||
enum AssemblySyntax {
|
||||
@@ -33,30 +39,77 @@ enum AssemblySyntax {
|
||||
Gas,
|
||||
}
|
||||
|
||||
pub struct AnalyzedCode {
|
||||
bitness: u32,
|
||||
bytes: Vec<u8>,
|
||||
start_addr: u64,
|
||||
code: Vec<Instruction>,
|
||||
addr_map: HashMap<u64, Instruction>,
|
||||
known_addr_table: Vec<u64>,
|
||||
near_branch_targets: Vec<u64>,
|
||||
far_branch_targets: Vec<u64>,
|
||||
cfe_addr_table: Vec<u64>,
|
||||
}
|
||||
|
||||
impl AnalyzedCode {
|
||||
fn is_known_address(&self, addr: u64) -> bool {
|
||||
self.known_addr_table.contains(&addr)
|
||||
}
|
||||
fn is_near_branch_target(&self, addr: u64) -> bool {
|
||||
self.near_branch_targets.contains(&addr)
|
||||
}
|
||||
fn is_far_branch_target(&self, addr: u64) -> bool {
|
||||
self.far_branch_targets.contains(&addr)
|
||||
}
|
||||
fn get_random_cfe_addr(&self) -> Option<u64> {
|
||||
self.cfe_addr_table.choose(&mut rand::thread_rng()).copied()
|
||||
}
|
||||
pub fn is_affecting_cf(&self, inst: &Instruction) -> Result<bool, DeoptimizerError> {
|
||||
// Check if the instruction exists in self.code
|
||||
if self.addr_map.get(&inst.ip()).is_none() {
|
||||
return Err(DeoptimizerError::InstructionNotFound);
|
||||
}
|
||||
|
||||
let mut rflags = RflagsBits::NONE;
|
||||
let m_rflags = inst.rflags_modified();
|
||||
if m_rflags == RflagsBits::NONE {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut cursor = inst.clone();
|
||||
while self.addr_map.get(&cursor.next_ip()).is_some() {
|
||||
cursor = *self.addr_map.get(&cursor.next_ip()).unwrap();
|
||||
rflags = rflags | cursor.rflags_modified();
|
||||
if (cursor.rflags_read() & m_rflags) > 0 {
|
||||
break;
|
||||
}
|
||||
if (m_rflags & rflags) == m_rflags {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Deoptimizer {
|
||||
/// Total number of deoptimization cycles.
|
||||
pub cycle: u32,
|
||||
/// Deoptimization frequency.
|
||||
pub freq: f64,
|
||||
/// Is the given code analyzed.
|
||||
pub is_analyzed: bool,
|
||||
code_map: Option<HashMap<u64, Instruction>>,
|
||||
disassembly: Option<String>,
|
||||
/// Allow processing of invalid instructions.
|
||||
pub allow_invalid: bool,
|
||||
/// Disassembler syntax.
|
||||
syntax: AssemblySyntax,
|
||||
known_addr_table: Option<Vec<u64>>,
|
||||
known_branch_targets: Option<Vec<u64>>,
|
||||
cfe_addr_table: Option<Vec<u64>>,
|
||||
}
|
||||
|
||||
impl Deoptimizer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
cycle: 1,
|
||||
freq: 0.5,
|
||||
is_analyzed: false,
|
||||
code_map: None,
|
||||
disassembly: None,
|
||||
allow_invalid: false,
|
||||
syntax: AssemblySyntax::Nasm,
|
||||
known_addr_table: None,
|
||||
known_branch_targets: None,
|
||||
cfe_addr_table: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,57 +125,72 @@ impl Deoptimizer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn analyze(&mut self, code: &[u8], mode: u32, start_addr: u64) {
|
||||
info!("Analyzing {} bytes with {} bit mode...", code.len(), mode);
|
||||
let mut decoder = Decoder::with_ip(mode, code, start_addr, DecoderOptions::NONE);
|
||||
let mut instruction = Instruction::default();
|
||||
pub fn analyze(
|
||||
&mut self,
|
||||
bytes: &[u8],
|
||||
bitness: u32,
|
||||
start_addr: u64,
|
||||
) -> Result<AnalyzedCode, DeoptimizerError> {
|
||||
info!(
|
||||
"Analyzing {} bytes with {} bit mode...",
|
||||
bytes.len(),
|
||||
bitness
|
||||
);
|
||||
let mut decoder = Decoder::with_ip(bitness, bytes, start_addr, DecoderOptions::NONE);
|
||||
let mut inst = Instruction::default();
|
||||
let mut known_addr_table = Vec::new();
|
||||
let mut known_branch_targets = Vec::new();
|
||||
let mut near_branch_targets = Vec::new();
|
||||
let mut far_branch_targets = Vec::new();
|
||||
let mut cfe_addr_table = Vec::new();
|
||||
let mut code: HashMap<u64, Instruction> = HashMap::new();
|
||||
let mut code = Vec::new();
|
||||
let mut addr_map: HashMap<u64, Instruction> = HashMap::new();
|
||||
while decoder.can_decode() {
|
||||
decoder.decode_out(&mut instruction);
|
||||
code.insert(instruction.ip(), instruction);
|
||||
decoder.decode_out(&mut inst);
|
||||
if inst.is_invalid() {
|
||||
warn!("Found invalid instruction at: 0x{:016X}", inst.ip());
|
||||
if !self.allow_invalid {
|
||||
return Err(DeoptimizerError::InvalidSyntax);
|
||||
}
|
||||
}
|
||||
code.push(inst);
|
||||
addr_map.insert(inst.ip(), inst);
|
||||
|
||||
if inst.op_count() == 1
|
||||
&& matches!(inst.op0_kind(), OpKind::FarBranch16 | OpKind::FarBranch32)
|
||||
{
|
||||
match inst.op0_kind() {
|
||||
OpKind::FarBranch32 => far_branch_targets.push(inst.far_branch32() as u64),
|
||||
OpKind::FarBranch16 => far_branch_targets.push(inst.far_branch16() as u64),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// Push to known address table
|
||||
known_addr_table.push(instruction.ip());
|
||||
let nbt = instruction.near_branch_target();
|
||||
known_addr_table.push(inst.ip());
|
||||
let nbt = inst.near_branch_target();
|
||||
if nbt != 0 {
|
||||
known_branch_targets.push(nbt);
|
||||
near_branch_targets.push(nbt);
|
||||
}
|
||||
// Push to control flow exit address table if it is a JMP of RET
|
||||
if instruction.mnemonic() == iced_x86::Mnemonic::Ret
|
||||
|| instruction.mnemonic() == iced_x86::Mnemonic::Retf
|
||||
|| instruction.mnemonic() == iced_x86::Mnemonic::Jmp
|
||||
if inst.mnemonic() == Mnemonic::Ret
|
||||
|| inst.mnemonic() == Mnemonic::Retf
|
||||
|| inst.mnemonic() == Mnemonic::Jmp
|
||||
{
|
||||
cfe_addr_table.push(instruction.ip())
|
||||
cfe_addr_table.push(inst.ip())
|
||||
}
|
||||
}
|
||||
self.code_map = Some(code);
|
||||
self.known_addr_table = Some(known_addr_table);
|
||||
self.known_branch_targets = Some(known_branch_targets);
|
||||
self.cfe_addr_table = Some(cfe_addr_table);
|
||||
self.is_analyzed = true;
|
||||
}
|
||||
|
||||
fn is_known_address(&mut self, addr: u64) -> bool {
|
||||
if let Some(table) = &self.known_addr_table {
|
||||
return table.contains(&addr);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_branch_target(&mut self, addr: u64) -> bool {
|
||||
if let Some(table) = &self.known_branch_targets {
|
||||
return table.contains(&addr);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn get_random_cfe_addr(&self) -> Option<u64> {
|
||||
if let Some(table) = &self.cfe_addr_table {
|
||||
return table.choose(&mut rand::thread_rng()).copied();
|
||||
}
|
||||
None
|
||||
Ok(AnalyzedCode {
|
||||
bitness,
|
||||
bytes: bytes.to_vec(),
|
||||
start_addr,
|
||||
code,
|
||||
addr_map,
|
||||
known_addr_table,
|
||||
near_branch_targets,
|
||||
far_branch_targets,
|
||||
cfe_addr_table,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_instruction(&self, inst: &Instruction) -> String {
|
||||
@@ -158,154 +226,177 @@ impl Deoptimizer {
|
||||
result
|
||||
}
|
||||
|
||||
pub fn disassemble(
|
||||
&mut self,
|
||||
code: &[u8],
|
||||
mode: u32,
|
||||
start_addr: u64,
|
||||
) -> Result<String, DeoptimizerError> {
|
||||
let mut decoder = Decoder::with_ip(mode, code, start_addr, DecoderOptions::NONE);
|
||||
pub fn disassemble(&mut self, acode: &AnalyzedCode) -> Result<String, DeoptimizerError> {
|
||||
info!(
|
||||
"Disassembling at -> 0x{:X} (mode={})",
|
||||
acode.start_addr, acode.bitness
|
||||
);
|
||||
let mut result = String::new();
|
||||
// let mut info_factory = InstructionInfoFactory::new();
|
||||
let mut instruction = Instruction::default();
|
||||
// Analyze the given binary and genereate nessesary tables...
|
||||
self.analyze(code, mode, start_addr);
|
||||
info!("Disassembling at -> 0x{:X} (mode={mode})", start_addr);
|
||||
while decoder.can_decode() {
|
||||
decoder.decode_out(&mut instruction);
|
||||
// let is_cfe = self.is_affecting_cf(&instruction)?;
|
||||
if instruction.is_invalid() {
|
||||
warn!("Found invalid instruction at {}", instruction.ip());
|
||||
let start_index = (instruction.ip() - start_addr) as usize;
|
||||
let instr_bytes = &code[start_index..start_index + instruction.len()];
|
||||
result += &format!(
|
||||
"loc_{:016X}: {}\n",
|
||||
instruction.ip(),
|
||||
to_db_mnemonic(instr_bytes)
|
||||
for inst in acode.code.clone() {
|
||||
if inst.is_invalid() {
|
||||
warn!(
|
||||
"Inlining invalid instruction bytes at: 0x{:016X}",
|
||||
inst.ip()
|
||||
);
|
||||
let start_index = (inst.ip() - acode.start_addr) as usize;
|
||||
let instr_bytes = &acode.bytes[start_index..start_index + inst.len()];
|
||||
result += &format!("loc_{:016X}: {}\n", inst.ip(), to_db_mnemonic(instr_bytes));
|
||||
continue;
|
||||
}
|
||||
|
||||
let temp = self.format_instruction(&instruction);
|
||||
let nbt = instruction.near_branch_target();
|
||||
let temp = self.format_instruction(&inst);
|
||||
let nbt = inst.near_branch_target();
|
||||
if nbt != 0 {
|
||||
if self.is_known_address(nbt) {
|
||||
if acode.is_known_address(nbt) {
|
||||
result += &format!(
|
||||
"loc_{:016X}: {} {}\n",
|
||||
instruction.ip(),
|
||||
inst.ip(),
|
||||
temp.split(' ').next().unwrap(),
|
||||
&format!("loc_{:016X}", nbt)
|
||||
);
|
||||
continue;
|
||||
} else {
|
||||
warn!("Misaligned instruction detected at {}", instruction.ip())
|
||||
warn!("Misaligned instruction detected at {}", inst.ip())
|
||||
}
|
||||
}
|
||||
result += &format!("loc_{:016X}: {}\n", instruction.ip(), temp);
|
||||
result += &format!("loc_{:016X}: {}\n", inst.ip(), temp);
|
||||
}
|
||||
self.disassembly = Some(result.clone());
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn is_affecting_cf(&self, inst: &Instruction) -> Result<bool, DeoptimizerError> {
|
||||
// First check if the code is analyzed
|
||||
if !self.is_analyzed || self.code_map.is_none() {
|
||||
return Err(DeoptimizerError::MissingCodeAnalysis);
|
||||
}
|
||||
let code_map = self.code_map.clone().unwrap();
|
||||
|
||||
// Check if the instruction exists in self.code
|
||||
if code_map.get(&inst.ip()).is_none() {
|
||||
return Err(DeoptimizerError::InstructionNotFound);
|
||||
}
|
||||
|
||||
let mut rflags = RflagsBits::NONE;
|
||||
let m_rflags = inst.rflags_modified();
|
||||
if m_rflags == RflagsBits::NONE {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut cursor = inst.clone();
|
||||
while code_map.get(&cursor.next_ip()).is_some() {
|
||||
cursor = *code_map.get(&cursor.next_ip()).unwrap();
|
||||
rflags = rflags | cursor.rflags_modified();
|
||||
if (cursor.rflags_read() & m_rflags) > 0 {
|
||||
break;
|
||||
}
|
||||
if (m_rflags & rflags) == m_rflags {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn apply_transform(
|
||||
bitness: u32,
|
||||
inst: &Instruction,
|
||||
mode: u32,
|
||||
) -> Result<Vec<Instruction>, DeoptimizerError> {
|
||||
if inst.op_count() == 0 {
|
||||
todo!("Perform call proxy...");
|
||||
} else {
|
||||
if inst.op_count() > 1 {
|
||||
// Priority is important, start with immidate obfuscation
|
||||
if let Ok(t) = apply_ap_transform(&mut inst.clone()) {
|
||||
if let Ok(t) = apply_ap_transform(bitness, &mut inst.clone()) {
|
||||
return Ok(t);
|
||||
}
|
||||
if let Ok(t) = apply_li_transform(&mut inst.clone()) {
|
||||
if let Ok(t) = apply_li_transform(bitness, &mut inst.clone()) {
|
||||
return Ok(t);
|
||||
}
|
||||
if let Ok(t) = apply_itr_transform(&mut inst.clone(), mode) {
|
||||
if let Ok(t) = apply_itr_transform(bitness, &mut inst.clone()) {
|
||||
// Clobbers CFLAGS
|
||||
return Ok(t);
|
||||
}
|
||||
// second target memory obfuscation
|
||||
if let Ok(t) = apply_om_transform(&mut inst.clone(), mode) {
|
||||
if let Ok(t) = apply_om_transform(bitness, &mut inst.clone()) {
|
||||
return Ok(t);
|
||||
}
|
||||
// Now swap registers
|
||||
if let Ok(t) = apply_rs_transform(&mut inst.clone(), mode) {
|
||||
if let Ok(t) = apply_rs_transform(bitness, &mut inst.clone()) {
|
||||
return Ok(t);
|
||||
}
|
||||
}
|
||||
|
||||
Err(DeoptimizerError::AllTransformsFailed)
|
||||
}
|
||||
|
||||
pub fn assemble(
|
||||
&self,
|
||||
code: String,
|
||||
mode: u32,
|
||||
addr: u64,
|
||||
) -> Result<AsmResult, keystone::Error> {
|
||||
let m = match mode {
|
||||
16 => keystone::MODE_16,
|
||||
32 => keystone::MODE_32,
|
||||
64 => keystone::MODE_64,
|
||||
_ => return Err(keystone::ERR_MODE),
|
||||
};
|
||||
let engine = Keystone::new(keystone::Arch::X86, m)?;
|
||||
pub fn deoptimize(&self, acode: &AnalyzedCode) -> Result<String, DeoptimizerError> {
|
||||
let mut dcode = Vec::new(); // deoptimized code
|
||||
// let mut nb_fix_table: HashMap<u64, u64> = HashMap::new();
|
||||
// let mut fb_fix_table: HashMap<u64, u64> = HashMap::new();
|
||||
let mut dcode_addr_table = Vec::new();
|
||||
// let mut new_ip: u64 = acode.start_addr;
|
||||
for inst in acode.code.clone() {
|
||||
// if acode.is_near_branch_target(inst.ip()) {
|
||||
// nb_fix_table.insert(inst.ip(), new_ip);
|
||||
// }
|
||||
// if acode.is_far_branch_target(inst.ip()) {
|
||||
// fb_fix_table.insert(inst.ip(), new_ip);
|
||||
// }
|
||||
// inst.set_ip(new_ip);
|
||||
if rand::thread_rng().gen_range(0.0..1.0) < self.freq {
|
||||
match Deoptimizer::apply_transform(acode.bitness, &inst) {
|
||||
Ok(dinst) => {
|
||||
// new_ip = dcode.last().unwrap().next_ip();
|
||||
dcode_addr_table.push(dinst.first().unwrap().ip());
|
||||
dcode = [dcode, dinst.clone()].concat();
|
||||
continue;
|
||||
}
|
||||
Err(e) => debug!("TransformError: {e}"),
|
||||
}
|
||||
}
|
||||
// new_ip = inst.next_ip();
|
||||
dcode_addr_table.push(inst.ip());
|
||||
dcode.push(inst);
|
||||
}
|
||||
|
||||
match self.syntax {
|
||||
AssemblySyntax::Nasm | AssemblySyntax::Keystone => {
|
||||
engine.option(keystone::OptionType::SYNTAX, keystone::OPT_SYNTAX_INTEL)?
|
||||
// let mut dfcode = Vec::new();
|
||||
// for mut inst in dcode.clone() {
|
||||
// let nbt = inst.near_branch_target();
|
||||
// if nbt != 0 {
|
||||
// if let Some(new_nbt) = nb_fix_table.get(&nbt) {
|
||||
// // info!("{} -> {:X}h ({:?})", inst, new_nbt, inst.op0_kind());
|
||||
// set_branch_target(&mut inst, *new_nbt)?;
|
||||
// dfcode.push(inst);
|
||||
// } else {
|
||||
// error!("Could not find near branch fix entry for: {}", inst);
|
||||
// }
|
||||
// continue;
|
||||
// }
|
||||
// match inst.op0_kind() {
|
||||
// OpKind::FarBranch16 => {
|
||||
// let fbt = inst.far_branch16();
|
||||
// if let Some(new_fbt) = fb_fix_table.get(&(fbt as u64)) {
|
||||
// info!("{} -> {:X}h ({:?})", inst, new_fbt, inst.op0_kind());
|
||||
// set_branch_target(&mut inst, *new_fbt)?;
|
||||
// dfcode.push(inst);
|
||||
// } else {
|
||||
// error!("Could not find far branch fix entry for: {}", inst);
|
||||
// }
|
||||
// continue;
|
||||
// }
|
||||
// OpKind::FarBranch32 => {
|
||||
// let fbt = inst.far_branch32();
|
||||
// if let Some(new_fbt) = fb_fix_table.get(&(fbt as u64)) {
|
||||
// info!("{} -> {:X}h ({:?})", inst, new_fbt, inst.op0_kind());
|
||||
// set_branch_target(&mut inst, *new_fbt)?;
|
||||
// dfcode.push(inst);
|
||||
// } else {
|
||||
// error!("Could not find far branch fix entry for: {}", inst);
|
||||
// }
|
||||
// continue;
|
||||
// }
|
||||
// _ => (),
|
||||
// }
|
||||
// dfcode.push(inst);
|
||||
// }
|
||||
//
|
||||
let mut result = String::from(format!("[BITS {}]\n", acode.bitness));
|
||||
for inst in dcode {
|
||||
let temp = self.format_instruction(&inst);
|
||||
let mut i_addr = rand::thread_rng().gen_range(u64::MIN..u64::MAX);
|
||||
let mut label_prefix = "doc";
|
||||
if dcode_addr_table.contains(&inst.ip()) {
|
||||
label_prefix = "loc";
|
||||
i_addr = inst.ip();
|
||||
}
|
||||
AssemblySyntax::Masm => {
|
||||
engine.option(keystone::OptionType::SYNTAX, keystone::OPT_SYNTAX_MASM)?
|
||||
let nbt = inst.near_branch_target();
|
||||
if nbt != 0 {
|
||||
result += &format!(
|
||||
"{}_{:016X}: {} {}\n",
|
||||
label_prefix,
|
||||
i_addr,
|
||||
temp.split(' ').next().unwrap(),
|
||||
&format!("loc_{:016X}", nbt)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
AssemblySyntax::Intel => {
|
||||
engine.option(keystone::OptionType::SYNTAX, keystone::OPT_SYNTAX_INTEL)?
|
||||
}
|
||||
AssemblySyntax::Gas => {
|
||||
engine.option(keystone::OptionType::SYNTAX, keystone::OPT_SYNTAX_GAS)?
|
||||
}
|
||||
};
|
||||
engine.asm(code, addr)
|
||||
result += &format!("{}_{:016X}: {}\n", label_prefix, i_addr, temp);
|
||||
}
|
||||
|
||||
// let mut encoder = Encoder::new(acode.bitness);
|
||||
// let mut buffer = Vec::new();
|
||||
// for inst in dfcode {
|
||||
// match encoder.encode(&inst, inst.ip()) {
|
||||
// Ok(_) => buffer = [buffer, encoder.take_buffer()].concat(),
|
||||
// Err(e) => {
|
||||
// error!("Encoding failed for -> {}", inst);
|
||||
// return Err(DeoptimizerError::EncodingFail(e));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// println!("{}", result);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_db_mnemonic(bytes: &[u8]) -> String {
|
||||
let mut db_inst = String::from("db ");
|
||||
for b in bytes.iter() {
|
||||
db_inst += &format!("0x{:02X}, ", b);
|
||||
}
|
||||
db_inst.trim_end_matches(", ").to_string()
|
||||
}
|
||||
|
||||
+211
-91
@@ -3,6 +3,8 @@ use iced_x86::*;
|
||||
use rand::thread_rng;
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
|
||||
use super::DeoptimizerError;
|
||||
|
||||
pub fn randomize_immediate_value(imm: u64) -> u64 {
|
||||
let mut rng = rand::thread_rng();
|
||||
if imm < u8::MAX as u64 {
|
||||
@@ -16,35 +18,82 @@ pub fn randomize_immediate_value(imm: u64) -> u64 {
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn get_inverse_mnemonic(inst: &Instruction) -> Option<Mnemonic> {
|
||||
// let ret = match inst.mnemonic() {
|
||||
// Mnemonic::Add => Mnemonic::Sub,
|
||||
// Mnemonic::Sub => Mnemonic::Add,
|
||||
// Mnemonic::Sbb => Mnemonic::Adc,
|
||||
// Mnemonic::Adc => Mnemonic::Sbb,
|
||||
// Mnemonic::Rol => Mnemonic::Ror,
|
||||
// Mnemonic::Ror => Mnemonic::Rol,
|
||||
// Mnemonic::Xor => Mnemonic::Xor,
|
||||
// Mnemonic::Inc => Mnemonic::Dec,
|
||||
// Mnemonic::Dec => Mnemonic::Inc,
|
||||
// Mnemonic::Or => Mnemonic::And,
|
||||
// Mnemonic::And => Mnemonic::Or,
|
||||
// Mnemonic::Shr => Mnemonic::Shl,
|
||||
// Mnemonic::Shl => Mnemonic::Shr,
|
||||
// Mnemonic::Not => Mnemonic::Not,
|
||||
// _ => return None,
|
||||
// };
|
||||
// Some(ret)
|
||||
// }
|
||||
pub fn encode(
|
||||
bitness: u32,
|
||||
insts: Vec<Instruction>,
|
||||
rip: u64,
|
||||
) -> Result<Vec<Instruction>, IcedError> {
|
||||
let mut buffer = Vec::new();
|
||||
let mut result = Vec::new();
|
||||
for inst in insts.clone() {
|
||||
let mut encoder = Encoder::new(bitness);
|
||||
match encoder.encode(&inst, inst.ip()) {
|
||||
Ok(_) => buffer = [buffer, encoder.take_buffer()].concat(),
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
}
|
||||
let mut decoder = Decoder::new(bitness, &buffer, DecoderOptions::NONE);
|
||||
decoder.set_ip(rip);
|
||||
let mut inst = Instruction::default();
|
||||
while decoder.can_decode() {
|
||||
decoder.decode_out(&mut inst);
|
||||
result.push(inst);
|
||||
}
|
||||
|
||||
pub fn is_ap_safe_instruction(inst: &Instruction) -> bool {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn is_using_static_register(inst: &Instruction) -> bool {
|
||||
if !matches!(
|
||||
inst.mnemonic(),
|
||||
Mnemonic::In
|
||||
| Mnemonic::Out
|
||||
| Mnemonic::Outsb
|
||||
| Mnemonic::Outsd
|
||||
| Mnemonic::Outsw
|
||||
| Mnemonic::Or
|
||||
| Mnemonic::Adc
|
||||
| Mnemonic::Add
|
||||
| Mnemonic::And
|
||||
| Mnemonic::Cmp
|
||||
| Mnemonic::Sbb
|
||||
| Mnemonic::Sub
|
||||
| Mnemonic::Xor
|
||||
| Mnemonic::Mov
|
||||
| Mnemonic::Test
|
||||
| Mnemonic::Lodsb
|
||||
| Mnemonic::Scasb
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if matches!(
|
||||
inst.op0_register(),
|
||||
Register::AL | Register::AX | Register::EAX
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if inst.op_count() > 0 && inst.op0_kind() == OpKind::Register {
|
||||
if matches!(
|
||||
inst.mnemonic(),
|
||||
Mnemonic::In | Mnemonic::Out | Mnemonic::Outsb | Mnemonic::Outsd | Mnemonic::Outsw
|
||||
) && inst.op0_register() == Register::DX
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_ap_compatible(inst: &Instruction) -> bool {
|
||||
matches!(
|
||||
inst.mnemonic(),
|
||||
Mnemonic::Mov | Mnemonic::Add | Mnemonic::Adc | Mnemonic::Sub | Mnemonic::Sbb
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_li_safe_instruction(inst: &Instruction) -> bool {
|
||||
pub fn is_li_compatible(inst: &Instruction) -> bool {
|
||||
matches!(
|
||||
inst.mnemonic(),
|
||||
Mnemonic::And
|
||||
@@ -61,20 +110,6 @@ pub fn is_li_safe_instruction(inst: &Instruction) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
// pub fn get_random_arithmetic_mnemonic() -> Mnemonic {
|
||||
// let arithmetics = Vec::from([
|
||||
// Mnemonic::Mov,
|
||||
// Mnemonic::Add,
|
||||
// Mnemonic::Sub,
|
||||
// Mnemonic::Cmp,
|
||||
// Mnemonic::Adc,
|
||||
// Mnemonic::Sbb,
|
||||
// Mnemonic::Inc,
|
||||
// Mnemonic::Dec,
|
||||
// ]);
|
||||
// *arithmetics.choose(&mut rand::thread_rng()).unwrap()
|
||||
// }
|
||||
|
||||
pub fn get_aprx_immediate_size(imm: u64) -> OpKind {
|
||||
if imm <= u8::MAX as u64 {
|
||||
return OpKind::Immediate8;
|
||||
@@ -132,9 +167,53 @@ pub fn get_register_save_seq(reg: Register) -> Result<(Instruction, Instruction)
|
||||
pub fn get_random_register_value(reg: Register) -> u64 {
|
||||
let mut rng = rand::thread_rng();
|
||||
if reg.size() > 4 {
|
||||
return rng.gen_range(1..u32::MAX) as u64;
|
||||
return rng.gen_range(1..i32::MAX) as u64;
|
||||
}
|
||||
rng.gen_range(1..u64::pow(2, (reg.size() * 8) as u32)) as u64
|
||||
rng.gen_range(1..i64::pow(2, (reg.size() * 8) as u32)) as u64
|
||||
}
|
||||
|
||||
pub fn set_op1_immediate(inst: &mut Instruction, imm: u64) -> Result<(), TransformError> {
|
||||
match inst.op1_kind() {
|
||||
OpKind::Immediate8 => inst.set_immediate8(imm as u8),
|
||||
OpKind::Immediate8to16 => inst.set_immediate8to16(imm as i16),
|
||||
OpKind::Immediate8to32 => inst.set_immediate8to32(imm as i32),
|
||||
OpKind::Immediate8to64 => inst.set_immediate8to64(imm as i64),
|
||||
OpKind::Immediate8_2nd => inst.set_immediate8_2nd(imm as u8),
|
||||
OpKind::Immediate16 => inst.set_immediate16(imm as u16),
|
||||
OpKind::Immediate32 => inst.set_immediate32(imm as u32),
|
||||
OpKind::Immediate32to64 => inst.set_immediate32to64(imm as i64),
|
||||
OpKind::Immediate64 => inst.set_immediate64(imm),
|
||||
_ => return Err(TransformError::UnexpectedImmediateSize),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_branch_target(inst: &mut Instruction, bt: u64) -> Result<(), DeoptimizerError> {
|
||||
if matches!(inst.op0_kind(), OpKind::FarBranch16 | OpKind::FarBranch32) {
|
||||
if bt < u16::MAX as u64 {
|
||||
inst.set_op0_kind(OpKind::FarBranch16);
|
||||
inst.set_far_branch16(bt as u16);
|
||||
} else if bt >= u16::MAX as u64 && bt < u32::MAX as u64 {
|
||||
inst.set_op0_kind(OpKind::FarBranch32);
|
||||
inst.set_near_branch32(bt as u32);
|
||||
} else {
|
||||
return Err(DeoptimizerError::FarBranchTooBig);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if bt < u16::MAX as u64 {
|
||||
inst.set_op0_kind(OpKind::NearBranch16);
|
||||
inst.set_near_branch16(bt as u16);
|
||||
} else if bt >= u16::MAX as u64 && bt < u32::MAX as u64 {
|
||||
inst.set_op0_kind(OpKind::NearBranch32);
|
||||
inst.set_near_branch32(bt as u32);
|
||||
} else if bt >= u32::MAX as u64 && bt < u64::MAX {
|
||||
inst.set_op0_kind(OpKind::NearBranch64);
|
||||
inst.set_near_branch64(bt);
|
||||
} else {
|
||||
return Err(DeoptimizerError::NearBranchTooBig);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_random_gp_register(
|
||||
@@ -210,63 +289,104 @@ pub fn get_random_gp_register(
|
||||
}
|
||||
Err(TransformError::RegisterNotFound)
|
||||
}
|
||||
// pub fn get_xor_code_with(reg: Register) -> Result<Code, TransformError> {
|
||||
// let c = match reg.size() {
|
||||
// 1 => Code::Xor_rm8_imm8,
|
||||
// 2 => Code::Xor_rm16_imm16,
|
||||
// 4 | 8 => Code::Xor_rm32_imm32,
|
||||
// _ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
// };
|
||||
// Ok(c)
|
||||
// }
|
||||
pub fn get_and_code_with(reg: Register) -> Result<Code, TransformError> {
|
||||
let c = match reg.size() {
|
||||
1 => Code::And_rm8_imm8,
|
||||
2 => Code::And_rm16_imm16,
|
||||
4 | 8 => Code::And_rm32_imm32,
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
|
||||
pub fn get_code_with_template(
|
||||
mnemonic: Mnemonic,
|
||||
inst: &Instruction,
|
||||
) -> Result<Code, TransformError> {
|
||||
if (inst.op_count() != 2 && inst.op_count() != 1) || !is_immediate_operand(inst.op1_kind()) {
|
||||
return Err(TransformError::InvalidTemplate);
|
||||
}
|
||||
let dst_op_size = match inst.op0_kind() {
|
||||
OpKind::Memory => inst.memory_size().element_size(),
|
||||
OpKind::Register => inst.op0_register().size(),
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
};
|
||||
Ok(c)
|
||||
|
||||
Ok(get_code_with_size(mnemonic, dst_op_size)?)
|
||||
}
|
||||
pub fn get_or_code_with(reg: Register) -> Result<Code, TransformError> {
|
||||
let c = match reg.size() {
|
||||
1 => Code::Or_rm8_imm8,
|
||||
2 => Code::Or_rm16_imm16,
|
||||
4 | 8 => Code::Or_rm32_imm32,
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
|
||||
pub fn get_code_with_size(mnemonic: Mnemonic, size: usize) -> Result<Code, TransformError> {
|
||||
let c = match mnemonic {
|
||||
Mnemonic::Add => match size {
|
||||
1 => Code::Add_rm8_imm8,
|
||||
2 => Code::Add_rm16_imm16,
|
||||
4 => Code::Add_rm32_imm32,
|
||||
8 => Code::Add_rm64_imm32,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
},
|
||||
Mnemonic::Adc => match size {
|
||||
1 => Code::Adc_rm8_imm8,
|
||||
2 => Code::Adc_rm16_imm16,
|
||||
4 => Code::Adc_rm32_imm32,
|
||||
8 => Code::Adc_rm64_imm32,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
},
|
||||
Mnemonic::Sub => match size {
|
||||
1 => Code::Sub_rm8_imm8,
|
||||
2 => Code::Sub_rm16_imm16,
|
||||
4 => Code::Sub_rm32_imm32,
|
||||
8 => Code::Sub_rm64_imm32,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
},
|
||||
Mnemonic::Xor => match size {
|
||||
1 => Code::Xor_rm8_imm8,
|
||||
2 => Code::Xor_rm16_imm16,
|
||||
4 => Code::Xor_rm32_imm32,
|
||||
8 => Code::Xor_rm64_imm32,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
},
|
||||
Mnemonic::And => match size {
|
||||
1 => Code::And_rm8_imm8,
|
||||
2 => Code::And_rm16_imm16,
|
||||
4 => Code::And_rm32_imm32,
|
||||
8 => Code::And_rm64_imm32,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
},
|
||||
Mnemonic::Or => match size {
|
||||
1 => Code::Or_rm8_imm8,
|
||||
2 => Code::Or_rm16_imm16,
|
||||
4 => Code::Or_rm32_imm32,
|
||||
8 => Code::Or_rm64_imm32,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
},
|
||||
Mnemonic::Not => match size {
|
||||
1 => Code::Not_rm8,
|
||||
2 => Code::Not_rm16,
|
||||
4 => Code::Not_rm32,
|
||||
8 => Code::Not_rm64,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
},
|
||||
Mnemonic::Mov => match size {
|
||||
1 => Code::Mov_rm8_imm8,
|
||||
2 => Code::Mov_rm16_imm16,
|
||||
4 => Code::Mov_rm32_imm32,
|
||||
8 => Code::Mov_rm64_imm32,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
},
|
||||
Mnemonic::Cmp => match size {
|
||||
1 => Code::Cmp_rm8_imm8,
|
||||
2 => Code::Cmp_rm16_imm16,
|
||||
4 => Code::Cmp_rm32_imm32,
|
||||
8 => Code::Cmp_rm64_imm32,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
},
|
||||
Mnemonic::Test => match size {
|
||||
1 => Code::Test_rm8_imm8,
|
||||
2 => Code::Test_rm16_imm16,
|
||||
4 => Code::Test_rm32_imm32,
|
||||
8 => Code::Test_rm64_imm32,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
},
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
};
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
pub fn get_not_code_with(reg: Register) -> Result<Code, TransformError> {
|
||||
let c = match reg.size() {
|
||||
1 => Code::Not_rm8,
|
||||
2 => Code::Not_rm16,
|
||||
4 => Code::Not_rm32,
|
||||
8 => Code::Not_rm64,
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
};
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
pub fn get_add_code_with(reg: Register) -> Result<Code, TransformError> {
|
||||
let c = match reg.size() {
|
||||
1 => Code::Add_rm8_imm8,
|
||||
2 => Code::Add_rm16_imm16,
|
||||
4 => Code::Add_rm32_imm32,
|
||||
8 => Code::Add_rm64_imm32,
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
};
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
pub fn get_sub_code_with(reg: Register) -> Result<Code, TransformError> {
|
||||
let c = match reg.size() {
|
||||
1 => Code::Sub_rm8_imm8,
|
||||
2 => Code::Sub_rm16_imm16,
|
||||
4 => Code::Sub_rm32_imm32,
|
||||
8 => Code::Sub_rm64_imm32,
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
};
|
||||
Ok(c)
|
||||
pub fn to_db_mnemonic(bytes: &[u8]) -> String {
|
||||
let mut db_inst = String::from("db ");
|
||||
for b in bytes.iter() {
|
||||
db_inst += &format!("0x{:02X}, ", b);
|
||||
}
|
||||
db_inst.trim_end_matches(", ").to_string()
|
||||
}
|
||||
|
||||
+122
-131
@@ -4,41 +4,116 @@ mod tests {
|
||||
use iced_x86::*;
|
||||
|
||||
#[test]
|
||||
fn test_om_transform() {
|
||||
println!("[*] testing offset mutation...");
|
||||
// let inst = Instruction::with2(
|
||||
// Code::Mov_r64_rm64,
|
||||
// Register::RAX,
|
||||
// MemoryOperand::with_base_displ(Register::RBX, 0x10),
|
||||
// )
|
||||
let inst = Instruction::with2(
|
||||
Code::Mov_r64_rm64,
|
||||
Register::RAX,
|
||||
MemoryOperand::new(
|
||||
Register::RBX,
|
||||
Register::AL,
|
||||
1,
|
||||
0x10,
|
||||
1,
|
||||
false,
|
||||
Register::None,
|
||||
),
|
||||
)
|
||||
.expect("Instruction creation failed");
|
||||
let mut formatter = NasmFormatter::new();
|
||||
let mut formatted_inst = String::new();
|
||||
formatter.format(&inst, &mut formatted_inst);
|
||||
println!("[*] instruction: {}", formatted_inst);
|
||||
println!("[*] Memory Base: {:?}", inst.memory_base());
|
||||
// assert_eq!(formatted_inst, "mov eax,[ebx+10h]");
|
||||
for _i in 0..10 {
|
||||
println!("---------------------");
|
||||
match apply_om_transform(&mut inst.clone(), 64) {
|
||||
fn test_ap_transform() {
|
||||
println!("[*] Testing arithmetic partitioning transform...");
|
||||
let code: &[u8] = &[
|
||||
0xbf, 0x8e, 0x00, 0x00, 0xc0, 0x41, 0xb9, 0x02, 0x00, 0x00, 0x00, 0xc6, 0x40, 0x38,
|
||||
0x01, 0x48, 0xb8, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x04, 0x10, 0x81,
|
||||
0xc2, 0x02, 0x10, 0x00, 0x00, 0x48, 0x81, 0xc6, 0x10, 0xff, 0xff, 0xff, 0x81, 0xd6,
|
||||
0x0c, 0x00, 0x88, 0xd6, 0x48, 0x81, 0xd6, 0x0c, 0x00, 0x88, 0x00, 0x14, 0xdc, 0x2c,
|
||||
0xa0, 0x48, 0x81, 0xee, 0xa0, 0x00, 0x00, 0x00, 0x2d, 0x20, 0x05, 0x93, 0x19, 0x1c,
|
||||
0x0c, 0x48, 0x1d, 0x33, 0x11, 0x22, 0x00,
|
||||
];
|
||||
let mut decoder = Decoder::new(64, code, DecoderOptions::NONE);
|
||||
let mut inst = Instruction::default();
|
||||
while decoder.can_decode() {
|
||||
decoder.decode_out(&mut inst);
|
||||
println!("[>] {}", inst);
|
||||
match apply_ap_transform(64, &mut inst.clone()) {
|
||||
Ok(result) => {
|
||||
for i in result {
|
||||
formatted_inst.clear();
|
||||
formatter.format(&mut i.clone(), &mut formatted_inst);
|
||||
println!("[+] {}", formatted_inst);
|
||||
println!("{:016X}:\t{}", i.ip(), i);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[-] {e}");
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_li_transform() {
|
||||
println!("[*] Testing logical inverse transform...");
|
||||
let code: &[u8] = &[
|
||||
0x41, 0x81, 0xf2, 0x69, 0x6e, 0x65, 0x49, 0x34, 0x01, 0x80, 0x74, 0x24, 0x27, 0x01,
|
||||
0x24, 0x01, 0x25, 0x00, 0x00, 0x08, 0x00, 0x48, 0x81, 0x23, 0xff, 0x9f, 0xff, 0xff,
|
||||
0x81, 0x20, 0x1f, 0x00, 0xfe, 0xff, 0x80, 0x64, 0xf8, 0x38, 0xfb, 0x81, 0xca, 0x00,
|
||||
0x03, 0x00, 0x00, 0x81, 0xca, 0x00, 0x00, 0x00, 0x03, 0x48, 0x81, 0x0b, 0x00, 0x60,
|
||||
0x00, 0x00, 0x80, 0x4c, 0xc8, 0x38, 0x20, 0xd1, 0xeb, 0xc1, 0xeb, 0x00, 0x48, 0xc1,
|
||||
0xeb, 0x10, 0xc0, 0xe0, 0x05, 0xc1, 0x21, 0x20, 0xc1, 0xc8, 0x0a, 0xc0, 0x0a, 0x14,
|
||||
0x48, 0xc1, 0xc1, 0x31, 0x40, 0xc0, 0xc6, 0x69,
|
||||
];
|
||||
let mut decoder = Decoder::new(64, code, DecoderOptions::NONE);
|
||||
let mut inst = Instruction::default();
|
||||
while decoder.can_decode() {
|
||||
decoder.decode_out(&mut inst);
|
||||
println!("[>] {}", inst);
|
||||
match apply_li_transform(64, &mut inst.clone()) {
|
||||
Ok(result) => {
|
||||
for i in result {
|
||||
println!("{:016X}:\t{}", i.ip(), i);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[-] {e}");
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_itr_transform() {
|
||||
println!("[*] Testing immediate to register transform...");
|
||||
let code: &[u8] = &[
|
||||
0x41, 0x81, 0xf2, 0x69, 0x6e, 0x65, 0x49, 0x34, 0x01, 0x80, 0x74, 0x24, 0x27, 0x01,
|
||||
0x24, 0x01, 0x25, 0x00, 0x00, 0x08, 0x00, 0x48, 0x81, 0x23, 0xff, 0x9f, 0xff, 0xff,
|
||||
0x81, 0x20, 0x1f, 0x00, 0xfe, 0xff, 0x80, 0x64, 0xf8, 0x38, 0xfb, 0x81, 0xca, 0x00,
|
||||
0x03, 0x00, 0x00, 0x81, 0xca, 0x00, 0x00, 0x00, 0x03, 0x48, 0x81, 0x0b, 0x00, 0x60,
|
||||
0x00, 0x00, 0x80, 0x4c, 0xc8, 0x38, 0x20, 0xd1, 0xeb, 0xc1, 0xeb, 0x00, 0x48, 0xc1,
|
||||
0xeb, 0x10, 0xc0, 0xe0, 0x05, 0xc1, 0x21, 0x20, 0xc1, 0xc8, 0x0a, 0xc0, 0x0a, 0x14,
|
||||
0x48, 0xc1, 0xc1, 0x31, 0x40, 0xc0, 0xc6, 0x69,
|
||||
];
|
||||
let mut decoder = Decoder::new(64, code, DecoderOptions::NONE);
|
||||
let mut inst = Instruction::default();
|
||||
while decoder.can_decode() {
|
||||
decoder.decode_out(&mut inst);
|
||||
println!("[>] {}", inst);
|
||||
match apply_itr_transform(64, &mut inst.clone()) {
|
||||
Ok(result) => {
|
||||
for i in result {
|
||||
println!("{:016X}:\t{}", i.ip(), i);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[-] {e}");
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_om_transform() {
|
||||
println!("[*] testing offset mutation...");
|
||||
let code: &[u8] = &[
|
||||
0x48, 0x8d, 0x09, 0x89, 0x38, 0x48, 0x8b, 0x4b, 0x28, 0x48, 0x89, 0x74, 0x24, 0x10,
|
||||
0xc7, 0x45, 0x40, 0x01, 0x00, 0x00, 0x00, 0x48, 0x89, 0x54, 0xc8, 0x28, 0x48, 0x8d,
|
||||
0x0c, 0xc9, 0x41, 0x0f, 0xb6, 0xb4, 0x80, 0x53, 0xb3, 0x0f, 0x00, 0x48, 0x81, 0x23,
|
||||
0xff, 0x9f, 0xff, 0xff, 0x48, 0x0f, 0xba, 0x2b, 0x0e, 0x48, 0x8b, 0x04, 0x25, 0x88,
|
||||
0x77, 0x66, 0x55,
|
||||
];
|
||||
let mut decoder = Decoder::new(64, code, DecoderOptions::NONE);
|
||||
let mut inst = Instruction::default();
|
||||
while decoder.can_decode() {
|
||||
decoder.decode_out(&mut inst);
|
||||
println!("[>] {}", inst);
|
||||
match apply_om_transform(64, &mut inst.clone()) {
|
||||
Ok(result) => {
|
||||
for i in result {
|
||||
println!("{:016X}:\t{}", inst.ip(), i);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -52,113 +127,29 @@ mod tests {
|
||||
#[test]
|
||||
fn test_rs_transform() {
|
||||
println!("[*] Testing register swap...");
|
||||
let inst = Instruction::with2(Code::Mov_rm64_r64, Register::RAX, Register::RBX)
|
||||
.expect("Instruction creation failed");
|
||||
let mut formatter = NasmFormatter::new();
|
||||
let mut formatted_inst = String::new();
|
||||
formatter.format(&inst, &mut formatted_inst);
|
||||
println!("[*] instruction: {}", formatted_inst);
|
||||
// assert_eq!(formatted_inst, "mov eax,bl");
|
||||
for _i in 0..10 {
|
||||
println!("---------------------");
|
||||
match apply_rs_transform(&mut inst.clone(), 64) {
|
||||
Ok(res) => {
|
||||
for i in res {
|
||||
formatted_inst.clear();
|
||||
formatter.format(&mut i.clone(), &mut formatted_inst);
|
||||
println!("[+] {}", formatted_inst);
|
||||
let code: &[u8] = &[
|
||||
0x34, 0x01, 0x66, 0x83, 0xe0, 0x31, 0x83, 0xc0, 0x69, 0x49, 0x81, 0xf2, 0x69, 0x6e,
|
||||
0x65, 0x49, 0x30, 0x44, 0x24, 0x27, 0x29, 0x5c, 0x78, 0x2a, 0x09, 0xca, 0x87, 0xd6,
|
||||
];
|
||||
let mut decoder = Decoder::new(64, code, DecoderOptions::NONE);
|
||||
let mut inst = Instruction::default();
|
||||
while decoder.can_decode() {
|
||||
decoder.decode_out(&mut inst);
|
||||
println!("[>] {}", inst);
|
||||
match apply_rs_transform(64, &mut inst.clone()) {
|
||||
Ok(result) => {
|
||||
for i in result {
|
||||
println!("{:016X}:\t{}", inst.ip(), i);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[-] {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_itr_transform() {
|
||||
println!("[*] Testing immediate to register transform...");
|
||||
let inst = Instruction::with2(Code::Mov_r32_imm32, Register::EAX, 0x6931)
|
||||
.expect("Instruction creation failed");
|
||||
let mut formatter = NasmFormatter::new();
|
||||
let mut formatted_inst = String::new();
|
||||
formatter.format(&inst, &mut formatted_inst);
|
||||
println!("[*] instruction: {}", formatted_inst);
|
||||
// assert_eq!(formatted_inst, "mov eax,ebx");
|
||||
for _i in 0..10 {
|
||||
println!("---------------------");
|
||||
match apply_itr_transform(&mut inst.clone(), 32) {
|
||||
Ok(res) => {
|
||||
for i in res {
|
||||
formatted_inst.clear();
|
||||
formatter.format(&mut i.clone(), &mut formatted_inst);
|
||||
println!("[+] {}", formatted_inst);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[-] {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ap_transform() {
|
||||
println!("[*] Testing arithmetic partitioning transform...");
|
||||
let inst = Instruction::with2(Code::Mov_rm32_imm32, Register::EAX, 0xDEADBEEF as u32)
|
||||
.expect("Instruction creation failed");
|
||||
let mut formatter = NasmFormatter::new();
|
||||
let mut formatted_inst = String::new();
|
||||
formatter.format(&inst, &mut formatted_inst);
|
||||
println!("[*] instruction: {}", formatted_inst);
|
||||
// assert_eq!(formatted_inst, "mov eax,ebx");
|
||||
for _i in 0..10 {
|
||||
println!("---------------------");
|
||||
match apply_ap_transform(&mut inst.clone()) {
|
||||
Ok(res) => {
|
||||
for i in res {
|
||||
formatted_inst.clear();
|
||||
formatter.format(&mut i.clone(), &mut formatted_inst);
|
||||
println!("[+] {}", formatted_inst);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[-] {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_li_transform() {
|
||||
println!("[*] Testing logical inverse transform...");
|
||||
let inst = Instruction::with2(Code::Ror_rm32_imm8, Register::EAX, 0x31)
|
||||
.expect("Instruction creation failed");
|
||||
let mut formatter = NasmFormatter::new();
|
||||
let mut formatted_inst = String::new();
|
||||
formatter.format(&inst, &mut formatted_inst);
|
||||
println!("[*] instruction: {}", formatted_inst);
|
||||
// assert_eq!(formatted_inst, "mov eax,ebx");
|
||||
println!("---------------------");
|
||||
match apply_li_transform(&mut inst.clone()) {
|
||||
Ok(res) => {
|
||||
for i in res {
|
||||
formatted_inst.clear();
|
||||
formatter.format(&mut i.clone(), &mut formatted_inst);
|
||||
println!("[+] {}", formatted_inst);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[-] {e}");
|
||||
}
|
||||
}
|
||||
println!("---------------------");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_random_gp_register() {
|
||||
match get_random_gp_register(false, 4, None) {
|
||||
|
||||
+252
-238
@@ -7,279 +7,104 @@ use thiserror::Error;
|
||||
pub enum TransformError {
|
||||
#[error("This transform not possible for given instruction.")]
|
||||
TransformNotPossible,
|
||||
#[error("Unexpected memory size given.")]
|
||||
UnexpectedMemorySize,
|
||||
#[error("Unexpected register size given.")]
|
||||
UnexpectedRegisterSize,
|
||||
#[error("Unexpected immediate operand size encountered.")]
|
||||
UnexpectedImmediateSize,
|
||||
#[error("Invalid processor mode given. (16/32/64 accepted)")]
|
||||
InvalidProcessorMode,
|
||||
// #[error("Register collection failed.")]
|
||||
// RegisterCollectFail,
|
||||
#[error("No GP register found with given parameters.")]
|
||||
RegisterNotFound,
|
||||
#[error("Invalid instruction template.")]
|
||||
InvalidTemplate,
|
||||
#[error("IcedError: {0}")]
|
||||
IcedError(#[from] IcedError),
|
||||
}
|
||||
|
||||
// Memory Obfuscation Transforms
|
||||
|
||||
/// Applies offset mutation to given instruction.
|
||||
pub fn apply_om_transform(
|
||||
inst: &mut Instruction,
|
||||
mode: u32,
|
||||
) -> Result<Vec<Instruction>, TransformError> {
|
||||
// First check the operand types.
|
||||
let base_reg = inst.memory_base();
|
||||
if !inst
|
||||
.op_kinds()
|
||||
.collect::<Vec<OpKind>>()
|
||||
.contains(&OpKind::Memory)
|
||||
|| base_reg == Register::None
|
||||
|| base_reg.is_segment_register()
|
||||
|| base_reg.is_vector_register()
|
||||
{
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
|
||||
if base_reg == Register::None {
|
||||
let mut info_factory = InstructionInfoFactory::new();
|
||||
let info = info_factory.info(&inst);
|
||||
let rand_reg =
|
||||
get_random_gp_register(mode == 64, (mode / 8) as usize, Some(info.used_registers()))?;
|
||||
let rand_val = get_random_register_value(rand_reg);
|
||||
let (reg_save_pre, reg_save_suf) = get_register_save_seq(rand_reg)?;
|
||||
inst.set_memory_base(rand_reg);
|
||||
let mut new_disply: i64 = inst.memory_displacement64().abs_diff(rand_val) as i64;
|
||||
if rand_val > inst.memory_displacement64() {
|
||||
new_disply = -new_disply;
|
||||
}
|
||||
let mov = match mode {
|
||||
32 => {
|
||||
inst.set_memory_displacement32(new_disply as u32);
|
||||
Instruction::with2(Code::Mov_rm32_imm32, rand_reg, rand_val)?
|
||||
}
|
||||
64 => {
|
||||
inst.set_memory_displacement64(new_disply as u64);
|
||||
Instruction::with2(Code::Mov_r64_imm64, rand_reg, rand_val)?
|
||||
}
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
};
|
||||
Ok(Vec::from([reg_save_pre, mov, *inst, reg_save_suf]))
|
||||
} else {
|
||||
let rnd_reg_val = get_random_register_value(base_reg);
|
||||
let coin_flip: bool = rand::thread_rng().gen();
|
||||
|
||||
let (c1, c2) = match coin_flip {
|
||||
true => (get_add_code_with(base_reg)?, get_sub_code_with(base_reg)?),
|
||||
false => (get_sub_code_with(base_reg)?, get_add_code_with(base_reg)?),
|
||||
};
|
||||
let pre_inst = Instruction::with2(c1, base_reg, rnd_reg_val)?;
|
||||
let post_inst = Instruction::with2(c2, base_reg, rnd_reg_val)?;
|
||||
let new_disply = inst.memory_displacement64().abs_diff(rnd_reg_val);
|
||||
let mut new_disply_signed = new_disply as i64; // This is not right!!!
|
||||
if coin_flip {
|
||||
new_disply_signed = -new_disply_signed;
|
||||
}
|
||||
match mode {
|
||||
16 | 32 => inst.set_memory_displacement32(new_disply_signed as u32),
|
||||
64 => inst.set_memory_displacement64(new_disply_signed as u64),
|
||||
_ => return Err(TransformError::InvalidProcessorMode),
|
||||
}
|
||||
Ok(Vec::from([pre_inst, inst.clone(), post_inst]))
|
||||
}
|
||||
}
|
||||
|
||||
// Register Obfuscation Transforms
|
||||
/// Applies register swapping transform to given instruction.
|
||||
pub fn apply_rs_transform(
|
||||
inst: &mut Instruction,
|
||||
mode: u32,
|
||||
) -> Result<Vec<Instruction>, TransformError> {
|
||||
if !inst
|
||||
.op_kinds()
|
||||
.collect::<Vec<OpKind>>()
|
||||
.contains(&OpKind::Register)
|
||||
{
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
let mut info_factory = InstructionInfoFactory::new();
|
||||
let info = info_factory.info(&inst);
|
||||
|
||||
let mut reg_idxs = Vec::new();
|
||||
let mut used_regs = Vec::new();
|
||||
for o in 0..inst.op_count() {
|
||||
if inst.op_kind(o) == OpKind::Register {
|
||||
reg_idxs.push(o)
|
||||
}
|
||||
}
|
||||
for i in reg_idxs {
|
||||
if inst.op_register(i).is_gpr() {
|
||||
used_regs.push((i, inst.op_register(i)));
|
||||
}
|
||||
}
|
||||
if used_regs.len() == 0 {
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
|
||||
let (reg_index, swap_reg) = *used_regs.choose(&mut rand::thread_rng()).unwrap();
|
||||
let rand_reg =
|
||||
get_random_gp_register(mode == 64, swap_reg.size(), Some(info.used_registers()))?;
|
||||
let xchg = match swap_reg.size() {
|
||||
1 => Instruction::with2(Code::Xchg_rm8_r8, swap_reg, rand_reg)?,
|
||||
2 => Instruction::with2(Code::Xchg_rm16_r16, swap_reg, rand_reg)?,
|
||||
4 => Instruction::with2(Code::Xchg_rm32_r32, swap_reg, rand_reg)?,
|
||||
8 => Instruction::with2(Code::Xchg_rm64_r64, swap_reg, rand_reg)?,
|
||||
_ => return Err(TransformError::TransformNotPossible),
|
||||
};
|
||||
inst.set_op_register(reg_index, rand_reg);
|
||||
Ok(Vec::from([xchg, inst.clone(), xchg]))
|
||||
}
|
||||
|
||||
// Immidiate Obfuscation Transforms
|
||||
|
||||
/// Applies immidiate-to-register transform to given instruction.
|
||||
pub fn apply_itr_transform(
|
||||
inst: &mut Instruction,
|
||||
mode: u32,
|
||||
) -> Result<Vec<Instruction>, TransformError> {
|
||||
let idxs = match get_immediate_indexes(inst) {
|
||||
Some(i) => i,
|
||||
None => return Err(TransformError::TransformNotPossible),
|
||||
};
|
||||
let mut info_factory = InstructionInfoFactory::new();
|
||||
let info = info_factory.info(&inst);
|
||||
|
||||
let rand_reg =
|
||||
get_random_gp_register(mode == 64, (mode / 8) as usize, Some(info.used_registers()))?;
|
||||
let (reg_save_pre, reg_save_post) = get_register_save_seq(rand_reg)?;
|
||||
|
||||
let mut mov = match rand_reg.size() {
|
||||
4 => Instruction::with2(
|
||||
Code::Mov_rm64_imm32,
|
||||
rand_reg,
|
||||
inst.immediate(*idxs.first().unwrap()),
|
||||
)?,
|
||||
8 => Instruction::with2(
|
||||
Code::Mov_r64_imm64,
|
||||
rand_reg,
|
||||
inst.immediate(*idxs.first().unwrap()),
|
||||
)?,
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
};
|
||||
// Obfuscate mov...
|
||||
let obs_mov = apply_ap_transform(&mut mov)?;
|
||||
inst.set_op_kind(*idxs.first().unwrap(), OpKind::Register);
|
||||
inst.set_op_register(*idxs.first().unwrap(), rand_reg);
|
||||
let mut result = [Vec::from([reg_save_pre]), obs_mov].concat();
|
||||
result.push(*inst);
|
||||
result.push(reg_save_post);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Applies arithmetic partitioning transform to given instruction.
|
||||
pub fn apply_ap_transform(inst: &mut Instruction) -> Result<Vec<Instruction>, TransformError> {
|
||||
if !is_ap_safe_instruction(inst)
|
||||
|| inst.op0_kind() != OpKind::Register
|
||||
|| !is_immediate_operand(inst.op1_kind())
|
||||
|| inst.op_count() != 2
|
||||
|| (inst.mnemonic() == Mnemonic::Mov && inst.op1_kind() == OpKind::Immediate64)
|
||||
{
|
||||
pub fn apply_ap_transform(
|
||||
bitness: u32,
|
||||
inst: &mut Instruction,
|
||||
) -> Result<Vec<Instruction>, TransformError> {
|
||||
if !is_ap_compatible(inst) || !is_immediate_operand(inst.op1_kind()) || inst.op_count() != 2 {
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
// We are looking for MOV (=) ADD/ADC (+) SUB/SBB (-)
|
||||
// let mnemonic = inst.mnemonic();
|
||||
let reg = inst.op0_register();
|
||||
let rip = inst.ip();
|
||||
let imm = inst.immediate(1);
|
||||
let rand_imm_val = randomize_immediate_value(imm);
|
||||
let imm_delta: u64 = rand_imm_val.abs_diff(imm);
|
||||
|
||||
match inst.op1_kind() {
|
||||
OpKind::Immediate8 => inst.set_immediate8(rand_imm_val as u8),
|
||||
OpKind::Immediate8to16 => inst.set_immediate8to16(rand_imm_val as i16),
|
||||
OpKind::Immediate8to32 => inst.set_immediate8to32(rand_imm_val as i32),
|
||||
OpKind::Immediate8to64 => inst.set_immediate8to64(rand_imm_val as i64),
|
||||
OpKind::Immediate8_2nd => inst.set_immediate8_2nd(rand_imm_val as u8),
|
||||
OpKind::Immediate16 => inst.set_immediate16(rand_imm_val as u16),
|
||||
OpKind::Immediate32 => inst.set_immediate32(rand_imm_val as u32),
|
||||
OpKind::Immediate32to64 => inst.set_immediate32(rand_imm_val as u32),
|
||||
OpKind::Immediate64 => inst.set_immediate64(rand_imm_val),
|
||||
_ => return Err(TransformError::UnexpectedImmediateSize),
|
||||
};
|
||||
|
||||
let mut result = Vec::new();
|
||||
if inst.mnemonic() == Mnemonic::Mov {
|
||||
result.push(*inst);
|
||||
}
|
||||
// we always need to make the value adjustment before the original instruction for preserving
|
||||
// the cflags...
|
||||
if imm > rand_imm_val {
|
||||
result.push(Instruction::with2(get_add_code_with(reg)?, reg, imm_delta)?);
|
||||
let mut fix_inst = inst.clone();
|
||||
if inst.mnemonic() == Mnemonic::Mov && inst.op1_kind() == OpKind::Immediate64 {
|
||||
set_op1_immediate(inst, !imm)?;
|
||||
fix_inst = Instruction::with1(get_code_with_size(Mnemonic::Not, 8)?, inst.op0_register())?;
|
||||
} else {
|
||||
result.push(Instruction::with2(get_sub_code_with(reg)?, reg, imm_delta)?);
|
||||
}
|
||||
if inst.mnemonic() != Mnemonic::Mov {
|
||||
result.push(*inst);
|
||||
set_op1_immediate(inst, rand_imm_val)?;
|
||||
if imm > rand_imm_val {
|
||||
fix_inst.set_code(get_code_with_template(Mnemonic::Add, inst)?);
|
||||
set_op1_immediate(&mut fix_inst, imm_delta)?;
|
||||
} else {
|
||||
fix_inst.set_code(get_code_with_template(Mnemonic::Sub, inst)?);
|
||||
set_op1_immediate(&mut fix_inst, imm_delta)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
if inst.mnemonic() == Mnemonic::Mov {
|
||||
Ok(encode(bitness, Vec::from([*inst, fix_inst]), rip)?)
|
||||
} else {
|
||||
Ok(encode(bitness, Vec::from([fix_inst, *inst]), rip)?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies logical inverse transform to given instruction.
|
||||
pub fn apply_li_transform(inst: &mut Instruction) -> Result<Vec<Instruction>, TransformError> {
|
||||
if !is_li_safe_instruction(inst)
|
||||
|| inst.op0_kind() != OpKind::Register
|
||||
|| !is_immediate_operand(inst.op1_kind())
|
||||
|| inst.op_count() != 2
|
||||
{
|
||||
pub fn apply_li_transform(
|
||||
bitness: u32,
|
||||
inst: &mut Instruction,
|
||||
) -> Result<Vec<Instruction>, TransformError> {
|
||||
if !is_li_compatible(inst) || !is_immediate_operand(inst.op1_kind()) || inst.op_count() != 2 {
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
// We are looking for XOR (^) AND (&) OR (|) SHR (>) SHL (<) ROR (>>) ROL (<<)
|
||||
let rip = inst.ip();
|
||||
let mnemonic = inst.mnemonic();
|
||||
let reg = inst.op0_register();
|
||||
let mut imm = inst.immediate(1);
|
||||
// let rand_imm_val = randomize_immediate_value(imm);
|
||||
if imm == 0 {
|
||||
// Unlikely but possible...
|
||||
return Ok(Vec::from([Instruction::with(Code::Nopd)]));
|
||||
}
|
||||
|
||||
let result = match mnemonic {
|
||||
Mnemonic::Xor => {
|
||||
match reg.size() {
|
||||
1 => inst.set_immediate8(!(imm as u8)),
|
||||
2 => inst.set_immediate16(!(imm as u16)),
|
||||
4 => inst.set_immediate32(!(imm as u32)),
|
||||
8 => inst.set_immediate32(!(imm as u32)),
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
};
|
||||
Vec::from([Instruction::with1(get_not_code_with(reg)?, reg)?, *inst])
|
||||
set_op1_immediate(inst, !imm)?;
|
||||
let mut not = inst.clone();
|
||||
not.set_code(get_code_with_template(Mnemonic::Not, inst)?);
|
||||
Vec::from([not, *inst])
|
||||
}
|
||||
Mnemonic::And => {
|
||||
let mut or = Instruction::with2(get_or_code_with(reg)?, reg, 0)?;
|
||||
match reg.size() {
|
||||
1 => or.set_immediate8(!(imm as u8)),
|
||||
2 => or.set_immediate16(!(imm as u16)),
|
||||
4 => or.set_immediate32(!(imm as u32)),
|
||||
8 => or.set_immediate32(!(imm as u32)),
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
};
|
||||
Vec::from([
|
||||
Instruction::with1(get_not_code_with(reg)?, reg)?,
|
||||
or,
|
||||
Instruction::with1(get_not_code_with(reg)?, reg)?,
|
||||
])
|
||||
let mut or = inst.clone();
|
||||
or.set_code(get_code_with_template(Mnemonic::Or, inst)?);
|
||||
set_op1_immediate(&mut or, !imm)?;
|
||||
let mut not = inst.clone();
|
||||
not.set_code(get_code_with_template(Mnemonic::Not, inst)?);
|
||||
Vec::from([not, or, not])
|
||||
}
|
||||
Mnemonic::Or => {
|
||||
let mut or = Instruction::with2(get_and_code_with(reg)?, reg, 0)?;
|
||||
match reg.size() {
|
||||
1 => or.set_immediate8(!(imm as u8)),
|
||||
2 => or.set_immediate16(!(imm as u16)),
|
||||
4 => or.set_immediate32(!(imm as u32)),
|
||||
8 => or.set_immediate32(!(imm as u32)),
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
};
|
||||
Vec::from([
|
||||
Instruction::with1(get_not_code_with(reg)?, reg)?,
|
||||
or,
|
||||
Instruction::with1(get_not_code_with(reg)?, reg)?,
|
||||
])
|
||||
let mut and = inst.clone();
|
||||
and.set_code(get_code_with_template(Mnemonic::And, inst)?);
|
||||
set_op1_immediate(&mut and, !imm)?;
|
||||
let mut not = inst.clone();
|
||||
not.set_code(get_code_with_template(Mnemonic::Not, inst)?);
|
||||
Vec::from([not, and, not])
|
||||
}
|
||||
Mnemonic::Shr | Mnemonic::Sar | Mnemonic::Shl | Mnemonic::Sal => {
|
||||
if imm == 1 {
|
||||
// Need to fix this case...
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
if imm.is_power_of_two() {
|
||||
let mut shift1 = inst.clone();
|
||||
let mut shift2 = inst.clone();
|
||||
@@ -295,15 +120,204 @@ pub fn apply_li_transform(inst: &mut Instruction) -> Result<Vec<Instruction>, Tr
|
||||
}
|
||||
}
|
||||
Mnemonic::Ror | Mnemonic::Rcr | Mnemonic::Rol | Mnemonic::Rcl => {
|
||||
let reg_size = (reg.size() * 8) as u64;
|
||||
imm = imm % reg_size;
|
||||
let pow = rand::thread_rng().gen_range(1..((u8::MAX as u64 / reg_size) - 2) as u8) - 1;
|
||||
inst.set_immediate8((reg_size * pow as u64 + imm) as u8);
|
||||
let dst_op_size = match inst.op0_kind() {
|
||||
OpKind::Memory => (inst.memory_size().element_size() * 8) as u64,
|
||||
OpKind::Register => (inst.op0_register().size() * 8) as u64,
|
||||
_ => return Err(TransformError::InvalidTemplate),
|
||||
};
|
||||
imm = imm % dst_op_size;
|
||||
let pow = rand::thread_rng().gen_range(2..(u8::MAX as u64 / dst_op_size) as u8);
|
||||
inst.set_immediate8((dst_op_size * pow as u64 + imm) as u8);
|
||||
Vec::from([*inst])
|
||||
}
|
||||
_ => return Err(TransformError::TransformNotPossible),
|
||||
};
|
||||
Ok(result)
|
||||
Ok(encode(bitness, result, rip)?)
|
||||
}
|
||||
|
||||
/// Applies immidiate-to-register transform to given instruction.
|
||||
pub fn apply_itr_transform(
|
||||
bitness: u32,
|
||||
inst: &mut Instruction,
|
||||
) -> Result<Vec<Instruction>, TransformError> {
|
||||
if inst.is_stack_instruction() {
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
let idxs = match get_immediate_indexes(inst) {
|
||||
Some(i) => i,
|
||||
None => return Err(TransformError::TransformNotPossible),
|
||||
};
|
||||
|
||||
let rip = inst.ip();
|
||||
let mut info_factory = InstructionInfoFactory::new();
|
||||
let info = info_factory.info(&inst);
|
||||
let rand_reg = get_random_gp_register(
|
||||
bitness == 64,
|
||||
(bitness / 8) as usize,
|
||||
Some(info.used_registers()),
|
||||
)?;
|
||||
let (reg_save_pre, reg_save_post) = get_register_save_seq(rand_reg)?;
|
||||
|
||||
let mut mov = match rand_reg.size() {
|
||||
4 => Instruction::with2(
|
||||
Code::Mov_rm32_imm32,
|
||||
rand_reg,
|
||||
inst.immediate(*idxs.first().unwrap()),
|
||||
)?,
|
||||
8 => Instruction::with2(
|
||||
Code::Mov_r64_imm64,
|
||||
rand_reg,
|
||||
inst.immediate(*idxs.first().unwrap()),
|
||||
)?,
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
};
|
||||
// Obfuscate mov...
|
||||
let obs_mov = apply_ap_transform(bitness, &mut mov)?;
|
||||
inst.set_op_kind(*idxs.first().unwrap(), OpKind::Register);
|
||||
inst.set_op_register(*idxs.first().unwrap(), rand_reg);
|
||||
let mut result = [Vec::from([reg_save_pre]), obs_mov].concat();
|
||||
result.push(*inst);
|
||||
result.push(reg_save_post);
|
||||
Ok(encode(bitness, result, rip)?)
|
||||
}
|
||||
// Memory Obfuscation Transforms
|
||||
|
||||
/// Applies offset mutation to given instruction.
|
||||
/// Note: This transform may clobber the CFLAGS!
|
||||
/// avoid using with CF altering instructions.
|
||||
pub fn apply_om_transform(
|
||||
bitness: u32,
|
||||
inst: &mut Instruction,
|
||||
) -> Result<Vec<Instruction>, TransformError> {
|
||||
// First check the operand types.
|
||||
let base_reg = inst.memory_base();
|
||||
if !inst
|
||||
.op_kinds()
|
||||
.collect::<Vec<OpKind>>()
|
||||
.contains(&OpKind::Memory)
|
||||
|| base_reg.is_segment_register()
|
||||
|| base_reg.is_vector_register()
|
||||
{
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
let rip = inst.ip();
|
||||
let mem_disp = inst.memory_displacement64();
|
||||
if base_reg == Register::None {
|
||||
if inst.is_stack_instruction() {
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
let mut ifac = InstructionInfoFactory::new();
|
||||
let info = ifac.info(inst);
|
||||
let rand_reg = get_random_gp_register(
|
||||
bitness == 64,
|
||||
(bitness / 8) as usize,
|
||||
Some(info.used_registers()),
|
||||
)?;
|
||||
let (reg_save_pre, reg_save_suf) = get_register_save_seq(rand_reg)?;
|
||||
inst.set_memory_base(rand_reg);
|
||||
inst.set_memory_displacement64(0);
|
||||
inst.set_memory_displ_size(0);
|
||||
|
||||
let mut mov = match bitness {
|
||||
16 => Instruction::with2(Code::Mov_rm16_imm16, rand_reg, mem_disp)?,
|
||||
32 => Instruction::with2(Code::Mov_rm32_imm32, rand_reg, mem_disp)?,
|
||||
64 => Instruction::with2(Code::Mov_r64_imm64, rand_reg, mem_disp)?,
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
};
|
||||
|
||||
let movs = apply_ap_transform(bitness, &mut mov)?;
|
||||
let mut result = Vec::from([reg_save_pre]);
|
||||
result = [result, movs].concat();
|
||||
result.push(*inst);
|
||||
result.push(reg_save_suf);
|
||||
Ok(encode(bitness, result, rip)?)
|
||||
} else {
|
||||
let rnd_reg_val = get_random_register_value(base_reg);
|
||||
let coin_flip: bool = rand::thread_rng().gen();
|
||||
let (c1, c2) = match coin_flip {
|
||||
true => (
|
||||
get_code_with_size(Mnemonic::Add, base_reg.size())?,
|
||||
get_code_with_size(Mnemonic::Sub, base_reg.size())?,
|
||||
),
|
||||
false => (
|
||||
get_code_with_size(Mnemonic::Sub, base_reg.size())?,
|
||||
get_code_with_size(Mnemonic::Add, base_reg.size())?,
|
||||
),
|
||||
};
|
||||
let pre_inst = Instruction::with2(c1, base_reg, rnd_reg_val)?;
|
||||
let post_inst = Instruction::with2(c2, base_reg, rnd_reg_val)?;
|
||||
let new_disply = mem_disp.abs_diff(rnd_reg_val);
|
||||
let mut new_disply_signed = new_disply as i64; // This is not right!!!
|
||||
if coin_flip {
|
||||
new_disply_signed = -new_disply_signed;
|
||||
}
|
||||
inst.set_memory_displ_size(1);
|
||||
inst.set_memory_displacement64(new_disply_signed as u64);
|
||||
Ok(encode(
|
||||
bitness,
|
||||
Vec::from([pre_inst, inst.clone(), post_inst]),
|
||||
rip,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
// Register Obfuscation Transforms
|
||||
/// Applies register swapping transform to given instruction.
|
||||
pub fn apply_rs_transform(
|
||||
bitness: u32,
|
||||
inst: &mut Instruction,
|
||||
) -> Result<Vec<Instruction>, TransformError> {
|
||||
if !inst
|
||||
.op_kinds()
|
||||
.collect::<Vec<OpKind>>()
|
||||
.contains(&OpKind::Register)
|
||||
{
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
// We need to fix the code if it is spesific to any register
|
||||
if is_using_static_register(inst) && is_immediate_operand(inst.op1_kind()) {
|
||||
if let Ok(code) = get_code_with_template(inst.mnemonic(), inst) {
|
||||
inst.set_code(code);
|
||||
match inst.op0_register().size() {
|
||||
1 => inst.set_op1_kind(OpKind::Immediate8),
|
||||
2 => inst.set_op1_kind(OpKind::Immediate16),
|
||||
4 => inst.set_op1_kind(OpKind::Immediate32),
|
||||
8 => inst.set_op1_kind(OpKind::Immediate64), // This may actually fail
|
||||
_ => return Err(TransformError::UnexpectedRegisterSize),
|
||||
}
|
||||
} else {
|
||||
return Err(TransformError::TransformNotPossible);
|
||||
}
|
||||
}
|
||||
let rip = inst.ip();
|
||||
let mut info_factory = InstructionInfoFactory::new();
|
||||
let info = info_factory.info(&inst);
|
||||
|
||||
let mut reg_idxs = Vec::new();
|
||||
let mut used_regs = Vec::new();
|
||||
for o in 0..inst.op_count() {
|
||||
if inst.op_kind(o) == OpKind::Register {
|
||||
reg_idxs.push(o)
|
||||
}
|
||||
}
|
||||
for i in reg_idxs {
|
||||
if inst.op_register(i).is_gpr() {
|
||||
used_regs.push((i, inst.op_register(i)));
|
||||
}
|
||||
}
|
||||
|
||||
let (reg_index, swap_reg) = *used_regs.last().unwrap();
|
||||
let rand_reg =
|
||||
get_random_gp_register(bitness == 64, swap_reg.size(), Some(info.used_registers()))?;
|
||||
let xchg = match swap_reg.size() {
|
||||
1 => Instruction::with2(Code::Xchg_rm8_r8, swap_reg, rand_reg)?,
|
||||
2 => Instruction::with2(Code::Xchg_rm16_r16, swap_reg, rand_reg)?,
|
||||
4 => Instruction::with2(Code::Xchg_rm32_r32, swap_reg, rand_reg)?,
|
||||
8 => Instruction::with2(Code::Xchg_rm64_r64, swap_reg, rand_reg)?,
|
||||
_ => return Err(TransformError::TransformNotPossible),
|
||||
};
|
||||
inst.set_op_register(reg_index, rand_reg);
|
||||
Ok(encode(bitness, Vec::from([xchg, inst.clone(), xchg]), rip)?)
|
||||
}
|
||||
|
||||
// Others...
|
||||
|
||||
Reference in New Issue
Block a user