Remove capstone from frida [aarch64] (#1723)
* Partially finish ASAN and CmpLog changes * Fix handle_trap, report_error, and remove capstone * Fix a few bugs. Can now detect UAFs properly * Some small changes * Make API more consistent with x86 * Fix printing * Remove unneeded inputs, final changes * formatting * Fix x86 build * Formatting
This commit is contained in:
parent
a0a4dd60bb
commit
fce5fd9a2b
@ -29,11 +29,10 @@ serdeany_autoreg = ["libafl_bolts/serdeany_autoreg"]
|
|||||||
cc = { version = "1.0", features = ["parallel"] }
|
cc = { version = "1.0", features = ["parallel"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||||
capstone = "0.11.0"
|
yaxpeax-arm = "0.2.4"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||||
yaxpeax-x86 = { git = "https://github.com/tokatoka/yaxpeax-x86/" } # replace this with origin later
|
yaxpeax-x86 = { git = "https://github.com/tokatoka/yaxpeax-x86/" } # replace this with origin later
|
||||||
yaxpeax-arch = "0.2.7"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../libafl", default-features = false, version = "0.11.1", features = [
|
libafl = { path = "../libafl", default-features = false, version = "0.11.1", features = [
|
||||||
@ -67,6 +66,7 @@ frida-gum = { version = "0.13.2", features = [
|
|||||||
"module-names",
|
"module-names",
|
||||||
] }
|
] }
|
||||||
dynasmrt = "2"
|
dynasmrt = "2"
|
||||||
|
|
||||||
color-backtrace = { version = "0.6", features = ["resolve-modules"] }
|
color-backtrace = { version = "0.6", features = ["resolve-modules"] }
|
||||||
termcolor = "1.1.3"
|
termcolor = "1.1.3"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
@ -80,5 +80,7 @@ paste = "1.0"
|
|||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
mmap-rs = "0.6.0"
|
mmap-rs = "0.6.0"
|
||||||
|
|
||||||
|
yaxpeax-arch = "0.2.7"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = { version = "2", default-features = false, features = ["logging"] }
|
serial_test = { version = "2", default-features = false, features = ["logging"] }
|
||||||
|
@ -13,15 +13,6 @@ use core::{
|
|||||||
use std::{ffi::c_void, num::NonZeroUsize, ptr::write_volatile, rc::Rc};
|
use std::{ffi::c_void, num::NonZeroUsize, ptr::write_volatile, rc::Rc};
|
||||||
|
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
use capstone::{
|
|
||||||
arch::{
|
|
||||||
arm64::{Arm64Extender, Arm64OperandType, Arm64Shift},
|
|
||||||
ArchOperand::Arm64Operand,
|
|
||||||
BuildsCapstone,
|
|
||||||
},
|
|
||||||
Capstone,
|
|
||||||
};
|
|
||||||
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
|
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use frida_gum::instruction_writer::X86Register;
|
use frida_gum::instruction_writer::X86Register;
|
||||||
@ -31,7 +22,6 @@ use frida_gum::{
|
|||||||
instruction_writer::InstructionWriter, interceptor::Interceptor, stalker::StalkerOutput, Gum,
|
instruction_writer::InstructionWriter, interceptor::Interceptor, stalker::StalkerOutput, Gum,
|
||||||
Module, ModuleDetails, ModuleMap, NativePointer, RangeDetails,
|
Module, ModuleDetails, ModuleMap, NativePointer, RangeDetails,
|
||||||
};
|
};
|
||||||
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
|
|
||||||
use frida_gum_sys::Insn;
|
use frida_gum_sys::Insn;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use libafl_bolts::{cli::FuzzerOptions, AsSlice};
|
use libafl_bolts::{cli::FuzzerOptions, AsSlice};
|
||||||
@ -44,20 +34,24 @@ use libc::{getrlimit, rlimit};
|
|||||||
use libc::{getrlimit64, rlimit64};
|
use libc::{getrlimit64, rlimit64};
|
||||||
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
|
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
|
||||||
use rangemap::RangeMap;
|
use rangemap::RangeMap;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use yaxpeax_arch::Arch;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use yaxpeax_arm::armv8::a64::{ARMv8, InstDecoder, Opcode, Operand, ShiftStyle, SizeCode};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use yaxpeax_x86::amd64::{InstDecoder, Instruction, Opcode};
|
use yaxpeax_x86::amd64::{InstDecoder, Instruction, Opcode};
|
||||||
|
|
||||||
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
|
#[cfg(any(target_arch = "x86_64"))]
|
||||||
use crate::utils::frida_to_cs;
|
use crate::utils::frida_to_cs;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use crate::utils::instruction_width;
|
use crate::utils::{instruction_width, writer_register};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::utils::operand_details;
|
use crate::utils::{operand_details, AccessType};
|
||||||
use crate::{
|
use crate::{
|
||||||
alloc::Allocator,
|
alloc::Allocator,
|
||||||
asan::errors::{AsanError, AsanErrors, AsanReadWriteError, ASAN_ERRORS},
|
asan::errors::{AsanError, AsanErrors, AsanReadWriteError, ASAN_ERRORS},
|
||||||
helper::{FridaRuntime, SkipRange},
|
helper::{FridaRuntime, SkipRange},
|
||||||
utils::{disas_count, AccessType},
|
utils::disas_count,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -193,15 +187,9 @@ impl FridaRuntime for AsanRuntime {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
self.hook_functions(gum);
|
self.hook_functions(gum);
|
||||||
/*
|
|
||||||
unsafe {
|
/* unsafe {
|
||||||
let mem = self.allocator.alloc(0xac + 2, 8);
|
let mem = self.allocator.alloc(0xac + 2, 8);
|
||||||
mprotect(
|
|
||||||
(self.shadow_check_func.unwrap() as usize & 0xffffffffffff000) as *mut c_void,
|
|
||||||
0x1000,
|
|
||||||
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE | ProtFlags::PROT_EXEC,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
log::info!("Test0");
|
log::info!("Test0");
|
||||||
/*
|
/*
|
||||||
0x555555916ce9 <libafl_frida::asan_rt::AsanRuntime::init+13033> je libafl_frida::asan_rt::AsanRuntime::init+14852 <libafl_frida::asan_rt::AsanRuntime::init+14852>
|
0x555555916ce9 <libafl_frida::asan_rt::AsanRuntime::init+13033> je libafl_frida::asan_rt::AsanRuntime::init+14852 <libafl_frida::asan_rt::AsanRuntime::init+14852>
|
||||||
@ -265,8 +253,8 @@ impl FridaRuntime for AsanRuntime {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
// assert!((self.shadow_check_func.unwrap())(((mem2 as usize) + 8875) as *const c_void, 4));
|
// assert!((self.shadow_check_func.unwrap())(((mem2 as usize) + 8875) as *const c_void, 4));
|
||||||
}
|
}*/
|
||||||
*/
|
|
||||||
self.register_thread();
|
self.register_thread();
|
||||||
}
|
}
|
||||||
fn pre_exec<I: libafl::inputs::Input + libafl::inputs::HasTargetBytes>(
|
fn pre_exec<I: libafl::inputs::Input + libafl::inputs::HasTargetBytes>(
|
||||||
@ -1074,113 +1062,61 @@ impl AsanRuntime {
|
|||||||
extern "C" fn handle_trap(&mut self) {
|
extern "C" fn handle_trap(&mut self) {
|
||||||
let mut actual_pc = self.regs[31];
|
let mut actual_pc = self.regs[31];
|
||||||
actual_pc = match self.stalked_addresses.get(&actual_pc) {
|
actual_pc = match self.stalked_addresses.get(&actual_pc) {
|
||||||
|
//get the pc associated with the trapped insn
|
||||||
Some(addr) => *addr,
|
Some(addr) => *addr,
|
||||||
None => actual_pc,
|
None => actual_pc,
|
||||||
};
|
};
|
||||||
|
|
||||||
let cs = Capstone::new()
|
let decoder = <ARMv8 as Arch>::Decoder::default();
|
||||||
.arm64()
|
|
||||||
.mode(capstone::arch::arm64::ArchMode::Arm)
|
|
||||||
.detail(true)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let instructions = cs
|
let insn = disas_count(
|
||||||
.disasm_count(
|
&decoder,
|
||||||
unsafe { std::slice::from_raw_parts(actual_pc as *mut u8, 24) },
|
unsafe { std::slice::from_raw_parts(actual_pc as *mut u8, 4) },
|
||||||
actual_pc as u64,
|
1,
|
||||||
3,
|
)[0];
|
||||||
)
|
|
||||||
.unwrap();
|
if insn.opcode == Opcode::MSR && insn.operands[0] == Operand::SystemReg(23056) { //the first operand is nzcv
|
||||||
let instructions = instructions.iter().collect::<Vec<&capstone::Insn>>();
|
//What case is this for??
|
||||||
let mut insn = instructions.first().unwrap();
|
/*insn = instructions.get(2).unwrap();
|
||||||
if insn.mnemonic().unwrap() == "msr" && insn.op_str().unwrap() == "nzcv, x0" {
|
actual_pc = insn.address() as usize;*/
|
||||||
insn = instructions.get(2).unwrap();
|
|
||||||
actual_pc = insn.address() as usize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let detail = cs.insn_detail(insn).unwrap();
|
let operands_len = insn
|
||||||
let arch_detail = detail.arch_detail();
|
.operands
|
||||||
let (mut base_reg, mut index_reg, displacement) =
|
.iter()
|
||||||
if let Arm64Operand(arm64operand) = arch_detail.operands().last().unwrap() {
|
.position(|item| *item == Operand::Nothing)
|
||||||
if let Arm64OperandType::Mem(opmem) = arm64operand.op_type {
|
.unwrap_or_else(|| 4);
|
||||||
(opmem.base().0, opmem.index().0, opmem.disp())
|
|
||||||
} else {
|
//the memory operand is always the last operand in aarch64
|
||||||
(0, 0, 0)
|
let (base_reg, index_reg, displacement) = match insn.operands[operands_len - 1] {
|
||||||
|
Operand::RegRegOffset(reg1, reg2, _, _, _) => (reg1, Some(reg2), 0),
|
||||||
|
Operand::RegPreIndex(reg, disp, _) => (reg, None, disp),
|
||||||
|
Operand::RegPostIndex(reg, _) => {
|
||||||
|
//in post index the disp is applied after so it doesn't matter for this memory access
|
||||||
|
(reg, None, 0)
|
||||||
|
}
|
||||||
|
Operand::RegPostIndexReg(reg, _) => (reg, None, 0),
|
||||||
|
_ => {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
(0, 0, 0)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if capstone::arch::arm64::Arm64Reg::ARM64_REG_X0 as u16 <= base_reg
|
|
||||||
&& base_reg <= capstone::arch::arm64::Arm64Reg::ARM64_REG_X28 as u16
|
|
||||||
{
|
|
||||||
base_reg -= capstone::arch::arm64::Arm64Reg::ARM64_REG_X0 as u16;
|
|
||||||
} else if base_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_X29 as u16 {
|
|
||||||
base_reg = 29u16;
|
|
||||||
} else if base_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_X30 as u16 {
|
|
||||||
base_reg = 30u16;
|
|
||||||
} else if base_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_SP as u16
|
|
||||||
|| base_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_WSP as u16
|
|
||||||
|| base_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_XZR as u16
|
|
||||||
|| base_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_WZR as u16
|
|
||||||
{
|
|
||||||
base_reg = 31u16;
|
|
||||||
} else if capstone::arch::arm64::Arm64Reg::ARM64_REG_W0 as u16 <= base_reg
|
|
||||||
&& base_reg <= capstone::arch::arm64::Arm64Reg::ARM64_REG_W30 as u16
|
|
||||||
{
|
|
||||||
base_reg -= capstone::arch::arm64::Arm64Reg::ARM64_REG_W0 as u16;
|
|
||||||
} else if capstone::arch::arm64::Arm64Reg::ARM64_REG_S0 as u16 <= base_reg
|
|
||||||
&& base_reg <= capstone::arch::arm64::Arm64Reg::ARM64_REG_S31 as u16
|
|
||||||
{
|
|
||||||
base_reg -= capstone::arch::arm64::Arm64Reg::ARM64_REG_S0 as u16;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
let mut fault_address =
|
let fault_address =
|
||||||
(self.regs[base_reg as usize] as isize + displacement as isize) as usize;
|
(self.regs[base_reg as usize] as isize + displacement as isize) as usize;
|
||||||
|
|
||||||
if index_reg == 0 {
|
|
||||||
index_reg = 0xffff;
|
|
||||||
} else {
|
|
||||||
if capstone::arch::arm64::Arm64Reg::ARM64_REG_X0 as u16 <= index_reg
|
|
||||||
&& index_reg <= capstone::arch::arm64::Arm64Reg::ARM64_REG_X28 as u16
|
|
||||||
{
|
|
||||||
index_reg -= capstone::arch::arm64::Arm64Reg::ARM64_REG_X0 as u16;
|
|
||||||
} else if index_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_X29 as u16 {
|
|
||||||
index_reg = 29u16;
|
|
||||||
} else if index_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_X30 as u16 {
|
|
||||||
index_reg = 30u16;
|
|
||||||
} else if index_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_SP as u16
|
|
||||||
|| index_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_WSP as u16
|
|
||||||
|| index_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_XZR as u16
|
|
||||||
|| index_reg == capstone::arch::arm64::Arm64Reg::ARM64_REG_WZR as u16
|
|
||||||
{
|
|
||||||
index_reg = 31u16;
|
|
||||||
} else if capstone::arch::arm64::Arm64Reg::ARM64_REG_W0 as u16 <= index_reg
|
|
||||||
&& index_reg <= capstone::arch::arm64::Arm64Reg::ARM64_REG_W30 as u16
|
|
||||||
{
|
|
||||||
index_reg -= capstone::arch::arm64::Arm64Reg::ARM64_REG_W0 as u16;
|
|
||||||
} else if capstone::arch::arm64::Arm64Reg::ARM64_REG_S0 as u16 <= index_reg
|
|
||||||
&& index_reg <= capstone::arch::arm64::Arm64Reg::ARM64_REG_S31 as u16
|
|
||||||
{
|
|
||||||
index_reg -= capstone::arch::arm64::Arm64Reg::ARM64_REG_S0 as u16;
|
|
||||||
}
|
|
||||||
fault_address += self.regs[index_reg as usize];
|
|
||||||
}
|
|
||||||
|
|
||||||
let backtrace = Backtrace::new();
|
let backtrace = Backtrace::new();
|
||||||
|
|
||||||
let (stack_start, stack_end) = Self::current_stack();
|
let (stack_start, stack_end) = Self::current_stack();
|
||||||
#[allow(clippy::option_if_let_else)]
|
#[allow(clippy::option_if_let_else)]
|
||||||
let error = if fault_address >= stack_start && fault_address < stack_end {
|
let error = if fault_address >= stack_start && fault_address < stack_end {
|
||||||
if insn.mnemonic().unwrap().starts_with('l') {
|
if insn.opcode.to_string().starts_with('l') {
|
||||||
AsanError::StackOobRead((
|
AsanError::StackOobRead((
|
||||||
self.regs,
|
self.regs,
|
||||||
actual_pc,
|
actual_pc,
|
||||||
(
|
(
|
||||||
Some(base_reg),
|
Some(base_reg),
|
||||||
Some(index_reg),
|
Some(index_reg.unwrap_or_else(|| 0xffff)),
|
||||||
displacement as usize,
|
displacement as usize,
|
||||||
fault_address,
|
fault_address,
|
||||||
),
|
),
|
||||||
@ -1192,7 +1128,7 @@ impl AsanRuntime {
|
|||||||
actual_pc,
|
actual_pc,
|
||||||
(
|
(
|
||||||
Some(base_reg),
|
Some(base_reg),
|
||||||
Some(index_reg),
|
Some(index_reg.unwrap_or_else(|| 0xffff)),
|
||||||
displacement as usize,
|
displacement as usize,
|
||||||
fault_address,
|
fault_address,
|
||||||
),
|
),
|
||||||
@ -1208,14 +1144,14 @@ impl AsanRuntime {
|
|||||||
pc: actual_pc,
|
pc: actual_pc,
|
||||||
fault: (
|
fault: (
|
||||||
Some(base_reg),
|
Some(base_reg),
|
||||||
Some(index_reg),
|
Some(index_reg.unwrap_or_else(|| 0xffff)),
|
||||||
displacement as usize,
|
displacement as usize,
|
||||||
fault_address,
|
fault_address,
|
||||||
),
|
),
|
||||||
metadata: metadata.clone(),
|
metadata: metadata.clone(),
|
||||||
backtrace,
|
backtrace,
|
||||||
};
|
};
|
||||||
if insn.mnemonic().unwrap().starts_with('l') {
|
if insn.opcode.to_string().starts_with('l') {
|
||||||
if metadata.freed {
|
if metadata.freed {
|
||||||
AsanError::ReadAfterFree(asan_readwrite_error)
|
AsanError::ReadAfterFree(asan_readwrite_error)
|
||||||
} else {
|
} else {
|
||||||
@ -1232,7 +1168,7 @@ impl AsanRuntime {
|
|||||||
actual_pc,
|
actual_pc,
|
||||||
(
|
(
|
||||||
Some(base_reg),
|
Some(base_reg),
|
||||||
Some(index_reg),
|
Some(index_reg.unwrap_or_else(|| 0xffff)),
|
||||||
displacement as usize,
|
displacement as usize,
|
||||||
fault_address,
|
fault_address,
|
||||||
),
|
),
|
||||||
@ -2192,53 +2128,101 @@ impl AsanRuntime {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn asan_is_interesting_instruction(
|
pub fn asan_is_interesting_instruction(
|
||||||
capstone: &Capstone,
|
decoder: InstDecoder,
|
||||||
_address: u64,
|
_address: u64,
|
||||||
instr: &Insn,
|
instr: &Insn,
|
||||||
) -> Option<(
|
) -> Option<(
|
||||||
capstone::RegId,
|
u16, //reg1
|
||||||
capstone::RegId,
|
Option<(u16, SizeCode)>, //size of reg2. This needs to be an option in the case that we don't have one
|
||||||
i32,
|
i32, //displacement.
|
||||||
u32,
|
u32, //load/store size
|
||||||
Arm64Shift,
|
Option<(ShiftStyle, u8)>, //(shift type, shift size)
|
||||||
Arm64Extender,
|
|
||||||
)> {
|
)> {
|
||||||
// We need to re-decode frida-internal capstone values to upstream capstone
|
// We need to re-decode frida-internal capstone values to upstream capstone
|
||||||
let cs_instr = frida_to_cs(capstone, instr);
|
|
||||||
let cs_instr = cs_instr.first().unwrap();
|
|
||||||
|
|
||||||
|
let instr = disas_count(&decoder, instr.bytes(), 1)[0];
|
||||||
// We have to ignore these instructions. Simulating them with their side effects is
|
// We have to ignore these instructions. Simulating them with their side effects is
|
||||||
// complex, to say the least.
|
// complex, to say the least.
|
||||||
match cs_instr.mnemonic().unwrap() {
|
match instr.opcode {
|
||||||
"ldaxr" | "stlxr" | "ldxr" | "stxr" | "ldar" | "stlr" | "ldarb" | "ldarh" | "ldaxp"
|
Opcode::LDAXR
|
||||||
| "ldaxrb" | "ldaxrh" | "stlrb" | "stlrh" | "stlxp" | "stlxrb" | "stlxrh" | "ldxrb"
|
| Opcode::STLXR
|
||||||
| "ldxrh" | "stxrb" | "stxrh" => return None,
|
| Opcode::LDXR
|
||||||
|
| Opcode::LDAR
|
||||||
|
| Opcode::STLR
|
||||||
|
| Opcode::LDARB
|
||||||
|
| Opcode::LDAXP
|
||||||
|
| Opcode::LDAXRB
|
||||||
|
| Opcode::LDAXRH
|
||||||
|
| Opcode::STLRB
|
||||||
|
| Opcode::STLRH
|
||||||
|
| Opcode::STLXP
|
||||||
|
| Opcode::STLXRB
|
||||||
|
| Opcode::STLXRH
|
||||||
|
| Opcode::LDXRB
|
||||||
|
| Opcode::LDXRH
|
||||||
|
| Opcode::STXRB
|
||||||
|
| Opcode::STXRH => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
//we need to do this convuluted operation because operands in yaxpeax are in a constant slice of size 4,
|
||||||
let operands = capstone
|
//and any unused operands are Operand::Nothing
|
||||||
.insn_detail(cs_instr)
|
let operands_len = instr
|
||||||
.unwrap()
|
.operands
|
||||||
.arch_detail()
|
.iter()
|
||||||
.operands();
|
.position(|item| *item == Operand::Nothing)
|
||||||
if operands.len() < 2 {
|
.unwrap_or_else(|| 4);
|
||||||
|
if operands_len < 2 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Arm64Operand(arm64operand) = operands.last().unwrap() {
|
/*if instr.opcode == Opcode::LDRSW || instr.opcode == Opcode::LDR {
|
||||||
if let Arm64OperandType::Mem(opmem) = arm64operand.op_type {
|
//this is a special case for pc-relative loads. The only two opcodes capable of this are LDR and LDRSW
|
||||||
return Some((
|
// For more information on this, look up "literal" loads in the ARM docs.
|
||||||
opmem.base(),
|
match instr.operands[1] {
|
||||||
opmem.index(),
|
//this is safe because an ldr is guranteed to have at least 3 operands
|
||||||
opmem.disp(),
|
Operand::PCOffset(off) => {
|
||||||
instruction_width(cs_instr, &operands),
|
return Some((32, None, off, memory_access_size, None));
|
||||||
arm64operand.shift,
|
|
||||||
arm64operand.ext,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
None
|
// println!("{:?} {}", instr, memory_access_size);
|
||||||
|
//abuse the fact that the last operand is always the mem operand
|
||||||
|
match instr.operands[operands_len - 1] {
|
||||||
|
Operand::RegRegOffset(reg1, reg2, size, shift, shift_size) => {
|
||||||
|
let ret = Some((
|
||||||
|
reg1,
|
||||||
|
Some((reg2, size)),
|
||||||
|
0,
|
||||||
|
instruction_width(&instr),
|
||||||
|
Some((shift, shift_size)),
|
||||||
|
));
|
||||||
|
// log::trace!("Interesting instruction: {}, {:?}", instr.to_string(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Operand::RegPreIndex(reg, disp, _) => {
|
||||||
|
let ret = Some((reg, None, disp, instruction_width(&instr), None));
|
||||||
|
// log::trace!("Interesting instruction: {}, {:?}", instr.to_string(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Operand::RegPostIndex(reg, _) => {
|
||||||
|
//in post index the disp is applied after so it doesn't matter for this memory access
|
||||||
|
let ret = Some((reg, None, 0, instruction_width(&instr), None));
|
||||||
|
// log::trace!("Interesting instruction: {}, {:?}", instr.to_string(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Operand::RegPostIndexReg(reg, _) => {
|
||||||
|
let ret = Some((reg, None, 0, instruction_width(&instr), None));
|
||||||
|
// log::trace!("Interesting instruction: {}, {:?}", instr.to_string(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the current instruction is interesting for address sanitization.
|
/// Checks if the current instruction is interesting for address sanitization.
|
||||||
@ -2474,12 +2458,11 @@ impl AsanRuntime {
|
|||||||
&mut self,
|
&mut self,
|
||||||
_address: u64,
|
_address: u64,
|
||||||
output: &StalkerOutput,
|
output: &StalkerOutput,
|
||||||
basereg: capstone::RegId,
|
basereg: u16,
|
||||||
indexreg: capstone::RegId,
|
indexreg: Option<(u16, SizeCode)>,
|
||||||
displacement: i32,
|
displacement: i32,
|
||||||
width: u32,
|
width: u32,
|
||||||
shift: Arm64Shift,
|
shift: Option<(ShiftStyle, u8)>,
|
||||||
extender: Arm64Extender,
|
|
||||||
) {
|
) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
i32::try_from(frida_gum_sys::GUM_RED_ZONE_SIZE).is_ok(),
|
i32::try_from(frida_gum_sys::GUM_RED_ZONE_SIZE).is_ok(),
|
||||||
@ -2489,11 +2472,11 @@ impl AsanRuntime {
|
|||||||
let redzone_size = frida_gum_sys::GUM_RED_ZONE_SIZE as i32;
|
let redzone_size = frida_gum_sys::GUM_RED_ZONE_SIZE as i32;
|
||||||
let writer = output.writer();
|
let writer = output.writer();
|
||||||
|
|
||||||
let basereg = writer_register(basereg);
|
let basereg = writer_register(basereg, SizeCode::X, false); //the writer register can never be zr and is always 64 bit
|
||||||
let indexreg = if indexreg.0 == 0 {
|
let indexreg = if let Some((reg, sizecode)) = indexreg {
|
||||||
None
|
Some(writer_register(reg, sizecode, true)) //the index register can be zr
|
||||||
} else {
|
} else {
|
||||||
Some(writer_register(indexreg))
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.current_report_impl == 0
|
if self.current_report_impl == 0
|
||||||
@ -2502,8 +2485,6 @@ impl AsanRuntime {
|
|||||||
{
|
{
|
||||||
let after_report_impl = writer.code_offset() + 2;
|
let after_report_impl = writer.code_offset() + 2;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
writer.put_jmp_near_label(after_report_impl);
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
writer.put_b_label(after_report_impl);
|
writer.put_b_label(after_report_impl);
|
||||||
|
|
||||||
@ -2558,28 +2539,22 @@ impl AsanRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Arm64Extender::ARM64_EXT_INVALID, Arm64Shift::Invalid) = (extender, shift) {
|
if let Some((shift_type, amount)) = shift {
|
||||||
writer.put_add_reg_reg_reg(
|
let extender_encoding: i32 = match shift_type {
|
||||||
Aarch64Register::X0,
|
ShiftStyle::UXTB => 0b000,
|
||||||
Aarch64Register::X0,
|
ShiftStyle::UXTH => 0b001,
|
||||||
Aarch64Register::X1,
|
ShiftStyle::UXTW => 0b010,
|
||||||
);
|
ShiftStyle::UXTX => 0b011,
|
||||||
} else {
|
ShiftStyle::SXTB => 0b100,
|
||||||
let extender_encoding: i32 = match extender {
|
ShiftStyle::SXTH => 0b101,
|
||||||
Arm64Extender::ARM64_EXT_UXTB => 0b000,
|
ShiftStyle::SXTW => 0b110,
|
||||||
Arm64Extender::ARM64_EXT_UXTH => 0b001,
|
ShiftStyle::SXTX => 0b111,
|
||||||
Arm64Extender::ARM64_EXT_UXTW => 0b010,
|
_ => -1,
|
||||||
Arm64Extender::ARM64_EXT_UXTX => 0b011,
|
|
||||||
Arm64Extender::ARM64_EXT_SXTB => 0b100,
|
|
||||||
Arm64Extender::ARM64_EXT_SXTH => 0b101,
|
|
||||||
Arm64Extender::ARM64_EXT_SXTW => 0b110,
|
|
||||||
Arm64Extender::ARM64_EXT_SXTX => 0b111,
|
|
||||||
Arm64Extender::ARM64_EXT_INVALID => -1,
|
|
||||||
};
|
};
|
||||||
let (shift_encoding, shift_amount): (i32, u32) = match shift {
|
let (shift_encoding, shift_amount): (i32, u32) = match shift_type {
|
||||||
Arm64Shift::Lsl(amount) => (0b00, amount),
|
ShiftStyle::LSL => (0b00, amount as u32),
|
||||||
Arm64Shift::Lsr(amount) => (0b01, amount),
|
ShiftStyle::LSR => (0b01, amount as u32),
|
||||||
Arm64Shift::Asr(amount) => (0b10, amount),
|
ShiftStyle::ASR => (0b10, amount as u32),
|
||||||
_ => (-1, 0),
|
_ => (-1, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2589,16 +2564,22 @@ impl AsanRuntime {
|
|||||||
writer.put_bytes(
|
writer.put_bytes(
|
||||||
&(0x8b210000 | ((extender_encoding as u32) << 13) | (shift_amount << 10))
|
&(0x8b210000 | ((extender_encoding as u32) << 13) | (shift_amount << 10))
|
||||||
.to_le_bytes(),
|
.to_le_bytes(),
|
||||||
);
|
); //add x0, x0, w1, [shift] #[amount]
|
||||||
} else if shift_encoding != -1 {
|
} else if shift_encoding != -1 {
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
writer.put_bytes(
|
writer.put_bytes(
|
||||||
&(0x8b010000 | ((shift_encoding as u32) << 22) | (shift_amount << 10))
|
&(0x8b010000 | ((shift_encoding as u32) << 22) | (shift_amount << 10))
|
||||||
.to_le_bytes(),
|
.to_le_bytes(),
|
||||||
);
|
); //add x0, x0, x1, [shift] #[amount]
|
||||||
} else {
|
} else {
|
||||||
panic!("extender: {extender:?}, shift: {shift:?}");
|
panic!("shift_type: {shift_type:?}, shift: {shift:?}");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
writer.put_add_reg_reg_reg(
|
||||||
|
Aarch64Register::X0,
|
||||||
|
Aarch64Register::X0,
|
||||||
|
Aarch64Register::X1,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2625,12 +2606,12 @@ impl AsanRuntime {
|
|||||||
let displacement = displacement.unsigned_abs();
|
let displacement = displacement.unsigned_abs();
|
||||||
let displacement_hi = displacement / 4096;
|
let displacement_hi = displacement / 4096;
|
||||||
let displacement_lo = displacement % 4096;
|
let displacement_lo = displacement % 4096;
|
||||||
writer.put_bytes(&(0xd1400000u32 | (displacement_hi << 10)).to_le_bytes());
|
writer.put_bytes(&(0xd1400000u32 | (displacement_hi << 10)).to_le_bytes()); //sub x0, x0, #[displacement / 4096] LSL#12
|
||||||
writer.put_sub_reg_reg_imm(
|
writer.put_sub_reg_reg_imm(
|
||||||
Aarch64Register::X0,
|
Aarch64Register::X0,
|
||||||
Aarch64Register::X0,
|
Aarch64Register::X0,
|
||||||
u64::from(displacement_lo),
|
u64::from(displacement_lo),
|
||||||
);
|
); //sub x0, x0, #[displacement & 4095]
|
||||||
}
|
}
|
||||||
} else if displacement > 0 {
|
} else if displacement > 0 {
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
@ -2670,7 +2651,7 @@ impl AsanRuntime {
|
|||||||
64 => writer.put_bytes(self.blob_check_mem_64bytes()),
|
64 => writer.put_bytes(self.blob_check_mem_64bytes()),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
//Shouldn't there be some manipulation of the code_offset here?
|
||||||
// Add the branch to report
|
// Add the branch to report
|
||||||
//writer.put_brk_imm(0x12);
|
//writer.put_brk_imm(0x12);
|
||||||
writer.put_branch_address(self.current_report_impl);
|
writer.put_branch_address(self.current_report_impl);
|
||||||
|
@ -19,8 +19,11 @@ use libafl::{
|
|||||||
use libafl_bolts::{ownedref::OwnedPtr, Named, SerdeAny};
|
use libafl_bolts::{ownedref::OwnedPtr, Named, SerdeAny};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use termcolor::{Color, ColorSpec, WriteColor};
|
use termcolor::{Color, ColorSpec, WriteColor};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use yaxpeax_arch::Arch;
|
||||||
use yaxpeax_arch::LengthedInstruction;
|
use yaxpeax_arch::LengthedInstruction;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use yaxpeax_arm::armv8::a64::ARMv8;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use yaxpeax_x86::amd64::InstDecoder;
|
use yaxpeax_x86::amd64::InstDecoder;
|
||||||
|
|
||||||
@ -239,21 +242,26 @@ impl AsanErrors {
|
|||||||
writeln!(output, "{:━^100}", " CODE ").unwrap();
|
writeln!(output, "{:━^100}", " CODE ").unwrap();
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
let mut cs = Capstone::new()
|
let decoder = <ARMv8 as Arch>::Decoder::default();
|
||||||
.arm64()
|
|
||||||
.mode(capstone::arch::arm64::ArchMode::Arm)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
let decoder = InstDecoder::minimal();
|
let decoder = InstDecoder::minimal();
|
||||||
|
|
||||||
let start_pc = error.pc - 4 * 5;
|
let start_pc = error.pc - 4 * 5;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
let insts = disas_count(
|
let insts = disas_count(
|
||||||
&decoder,
|
&decoder,
|
||||||
unsafe { std::slice::from_raw_parts(start_pc as *mut u8, 15 * 11) },
|
unsafe { std::slice::from_raw_parts(start_pc as *mut u8, 15 * 11) },
|
||||||
11,
|
11,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
let insts = disas_count(
|
||||||
|
&decoder,
|
||||||
|
unsafe { std::slice::from_raw_parts(start_pc as *mut u8, 4 * 11) },
|
||||||
|
11,
|
||||||
|
);
|
||||||
|
|
||||||
let mut inst_address = start_pc;
|
let mut inst_address = start_pc;
|
||||||
|
|
||||||
for insn in insts {
|
for insn in insts {
|
||||||
@ -489,22 +497,27 @@ impl AsanErrors {
|
|||||||
writeln!(output, "{:━^100}", " CODE ").unwrap();
|
writeln!(output, "{:━^100}", " CODE ").unwrap();
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
let mut cs = Capstone::new()
|
let decoder = <ARMv8 as Arch>::Decoder::default();
|
||||||
.arm64()
|
|
||||||
.mode(capstone::arch::arm64::ArchMode::Arm)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
let decoder = InstDecoder::minimal();
|
let decoder = InstDecoder::minimal();
|
||||||
|
|
||||||
let start_pc = pc;
|
let start_pc = pc;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
let insts = disas_count(
|
let insts = disas_count(
|
||||||
&decoder,
|
&decoder,
|
||||||
unsafe { std::slice::from_raw_parts(start_pc as *mut u8, 15 * 11) },
|
unsafe { std::slice::from_raw_parts(start_pc as *mut u8, 15 * 11) },
|
||||||
11,
|
11,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
let insts = disas_count(
|
||||||
|
&decoder,
|
||||||
|
unsafe { std::slice::from_raw_parts(start_pc as *mut u8, 4 * 11) },
|
||||||
|
11,
|
||||||
|
);
|
||||||
|
|
||||||
let mut inst_address = start_pc;
|
let mut inst_address = start_pc;
|
||||||
for insn in insts {
|
for insn in insts {
|
||||||
if inst_address == pc {
|
if inst_address == pc {
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
|
|
||||||
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
|
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use frida_gum_sys::Insn;
|
||||||
use libafl::{
|
use libafl::{
|
||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
Error,
|
Error,
|
||||||
@ -27,11 +29,9 @@ use frida_gum::{
|
|||||||
instruction_writer::{Aarch64Register, IndexMode, InstructionWriter},
|
instruction_writer::{Aarch64Register, IndexMode, InstructionWriter},
|
||||||
stalker::StalkerOutput,
|
stalker::StalkerOutput,
|
||||||
};
|
};
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
use frida_gum_sys::Insn;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||||
use crate::utils::{frida_to_cs, instruction_width, writer_register};
|
use crate::utils::{disas_count, writer_register};
|
||||||
|
|
||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||||
/// Speciial `CmpLog` Cases for `aarch64`
|
/// Speciial `CmpLog` Cases for `aarch64`
|
||||||
@ -44,10 +44,7 @@ pub enum SpecialCmpLogCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use capstone::{
|
use yaxpeax_arm::armv8::a64::{InstDecoder, Opcode, Operand, ShiftStyle};
|
||||||
arch::{arm64::Arm64OperandType, ArchOperand::Arm64Operand},
|
|
||||||
Capstone,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The [`frida_gum_sys::GUM_RED_ZONE_SIZE`] casted to [`i32`]
|
/// The [`frida_gum_sys::GUM_RED_ZONE_SIZE`] casted to [`i32`]
|
||||||
///
|
///
|
||||||
@ -68,13 +65,12 @@ fn gum_red_zone_size_i32() -> i32 {
|
|||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||||
pub enum CmplogOperandType {
|
pub enum CmplogOperandType {
|
||||||
/// A Register
|
/// A Register
|
||||||
Regid(capstone::RegId),
|
Regid(Aarch64Register),
|
||||||
/// An immediate value
|
/// An immediate value
|
||||||
Imm(u64),
|
Imm(u64),
|
||||||
/// A constant immediate value
|
/// A constant immediate value
|
||||||
Cimm(u64),
|
Cimm(u64),
|
||||||
/// A memory operand
|
// We don't need a memory type because you cannot directly compare with memory
|
||||||
Mem(capstone::RegId, capstone::RegId, i32, u32),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Frida`-based binary-only innstrumentation that logs compares to the fuzzer
|
/// `Frida`-based binary-only innstrumentation that logs compares to the fuzzer
|
||||||
@ -276,8 +272,9 @@ impl CmpLogRuntime {
|
|||||||
&self,
|
&self,
|
||||||
_address: u64,
|
_address: u64,
|
||||||
output: &StalkerOutput,
|
output: &StalkerOutput,
|
||||||
op1: &CmplogOperandType,
|
op1: &CmplogOperandType, //first operand of the comparsion
|
||||||
op2: &CmplogOperandType,
|
op2: &CmplogOperandType, //second operand of the comparsion
|
||||||
|
_shift: Option<(ShiftStyle, u8)>,
|
||||||
special_case: Option<SpecialCmpLogCase>,
|
special_case: Option<SpecialCmpLogCase>,
|
||||||
) {
|
) {
|
||||||
let writer = output.writer();
|
let writer = output.writer();
|
||||||
@ -296,67 +293,17 @@ impl CmpLogRuntime {
|
|||||||
CmplogOperandType::Imm(value) | CmplogOperandType::Cimm(value) => {
|
CmplogOperandType::Imm(value) | CmplogOperandType::Cimm(value) => {
|
||||||
writer.put_ldr_reg_u64(Aarch64Register::X0, *value);
|
writer.put_ldr_reg_u64(Aarch64Register::X0, *value);
|
||||||
}
|
}
|
||||||
CmplogOperandType::Regid(reg) => {
|
CmplogOperandType::Regid(reg) => match *reg {
|
||||||
let reg = writer_register(*reg);
|
|
||||||
match reg {
|
|
||||||
Aarch64Register::X0 | Aarch64Register::W0 => {}
|
Aarch64Register::X0 | Aarch64Register::W0 => {}
|
||||||
Aarch64Register::X1 | Aarch64Register::W1 => {
|
Aarch64Register::X1 | Aarch64Register::W1 => {
|
||||||
writer.put_mov_reg_reg(Aarch64Register::X0, Aarch64Register::X1);
|
writer.put_mov_reg_reg(Aarch64Register::X0, Aarch64Register::X1);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if !writer.put_mov_reg_reg(Aarch64Register::X0, reg) {
|
if !writer.put_mov_reg_reg(Aarch64Register::X0, *reg) {
|
||||||
writer.put_mov_reg_reg(Aarch64Register::W0, reg);
|
writer.put_mov_reg_reg(Aarch64Register::W0, *reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
CmplogOperandType::Mem(basereg, indexreg, displacement, _width) => {
|
|
||||||
let basereg = writer_register(*basereg);
|
|
||||||
let indexreg = if indexreg.0 == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(writer_register(*indexreg))
|
|
||||||
};
|
|
||||||
|
|
||||||
// calculate base+index+displacment into x0
|
|
||||||
let displacement = displacement
|
|
||||||
+ if basereg == Aarch64Register::Sp {
|
|
||||||
16 + gum_red_zone_size_i32()
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
if indexreg.is_some() {
|
|
||||||
if let Some(indexreg) = indexreg {
|
|
||||||
writer.put_add_reg_reg_reg(Aarch64Register::X0, basereg, indexreg);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match basereg {
|
|
||||||
Aarch64Register::X0 | Aarch64Register::W0 => {}
|
|
||||||
Aarch64Register::X1 | Aarch64Register::W1 => {
|
|
||||||
writer.put_mov_reg_reg(Aarch64Register::X0, Aarch64Register::X1);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if !writer.put_mov_reg_reg(Aarch64Register::X0, basereg) {
|
|
||||||
writer.put_mov_reg_reg(Aarch64Register::W0, basereg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_assert!(displacement >= 0);
|
|
||||||
|
|
||||||
//add displacement
|
|
||||||
#[allow(clippy::cast_sign_loss)]
|
|
||||||
writer.put_add_reg_reg_imm(
|
|
||||||
Aarch64Register::X0,
|
|
||||||
Aarch64Register::X0,
|
|
||||||
displacement as u64,
|
|
||||||
);
|
|
||||||
|
|
||||||
//deref into x0 to get the real value
|
|
||||||
writer.put_ldr_reg_reg_offset(Aarch64Register::X0, Aarch64Register::X0, 0u64);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure operand2 value is saved into x1
|
// make sure operand2 value is saved into x1
|
||||||
@ -374,207 +321,17 @@ impl CmpLogRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CmplogOperandType::Regid(reg) => {
|
CmplogOperandType::Regid(reg) => match *reg {
|
||||||
let reg = writer_register(*reg);
|
|
||||||
match reg {
|
|
||||||
Aarch64Register::X1 | Aarch64Register::W1 => {}
|
Aarch64Register::X1 | Aarch64Register::W1 => {}
|
||||||
Aarch64Register::X0 | Aarch64Register::W0 => {
|
Aarch64Register::X0 | Aarch64Register::W0 => {
|
||||||
writer.put_ldr_reg_reg_offset(
|
writer.put_ldr_reg_reg_offset(Aarch64Register::X1, Aarch64Register::Sp, 0u64);
|
||||||
Aarch64Register::X1,
|
|
||||||
Aarch64Register::Sp,
|
|
||||||
0u64,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if !writer.put_mov_reg_reg(Aarch64Register::X1, reg) {
|
if !writer.put_mov_reg_reg(Aarch64Register::X1, *reg) {
|
||||||
writer.put_mov_reg_reg(Aarch64Register::W1, reg);
|
writer.put_mov_reg_reg(Aarch64Register::W1, *reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
CmplogOperandType::Mem(basereg, indexreg, displacement, _width) => {
|
|
||||||
let basereg = writer_register(*basereg);
|
|
||||||
let indexreg = if indexreg.0 == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(writer_register(*indexreg))
|
|
||||||
};
|
|
||||||
|
|
||||||
// calculate base+index+displacement into x1
|
|
||||||
let displacement = displacement
|
|
||||||
+ if basereg == Aarch64Register::Sp {
|
|
||||||
16 + gum_red_zone_size_i32()
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
if indexreg.is_some() {
|
|
||||||
if let Some(indexreg) = indexreg {
|
|
||||||
match indexreg {
|
|
||||||
Aarch64Register::X0 | Aarch64Register::W0 => {
|
|
||||||
match basereg {
|
|
||||||
Aarch64Register::X1 | Aarch64Register::W1 => {
|
|
||||||
// x0 is overwritten indexreg by op1 value.
|
|
||||||
// x1 is basereg
|
|
||||||
|
|
||||||
// Preserve x2, x3:
|
|
||||||
writer.put_stp_reg_reg_reg_offset(
|
|
||||||
Aarch64Register::X2,
|
|
||||||
Aarch64Register::X3,
|
|
||||||
Aarch64Register::Sp,
|
|
||||||
i64::from(-(16 + gum_red_zone_size_i32())),
|
|
||||||
IndexMode::PreAdjust,
|
|
||||||
);
|
|
||||||
|
|
||||||
//reload indexreg to x2
|
|
||||||
writer.put_ldr_reg_reg_offset(
|
|
||||||
Aarch64Register::X2,
|
|
||||||
Aarch64Register::Sp,
|
|
||||||
0u64,
|
|
||||||
);
|
|
||||||
//add them into basereg==x1
|
|
||||||
writer.put_add_reg_reg_reg(
|
|
||||||
basereg,
|
|
||||||
basereg,
|
|
||||||
Aarch64Register::X2,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Restore x2, x3
|
|
||||||
assert!(writer.put_ldp_reg_reg_reg_offset(
|
|
||||||
Aarch64Register::X2,
|
|
||||||
Aarch64Register::X3,
|
|
||||||
Aarch64Register::Sp,
|
|
||||||
16 + i64::from(frida_gum_sys::GUM_RED_ZONE_SIZE),
|
|
||||||
IndexMode::PostAdjust,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// x0 is overwrittern indexreg by op1 value.
|
|
||||||
// basereg is not x1 nor x0
|
|
||||||
|
|
||||||
//reload indexreg to x1
|
|
||||||
writer.put_ldr_reg_reg_offset(
|
|
||||||
Aarch64Register::X1,
|
|
||||||
Aarch64Register::Sp,
|
|
||||||
0u64,
|
|
||||||
);
|
|
||||||
//add basereg into indexreg==x1
|
|
||||||
writer.put_add_reg_reg_reg(
|
|
||||||
Aarch64Register::X1,
|
|
||||||
basereg,
|
|
||||||
Aarch64Register::X1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Aarch64Register::X1 | Aarch64Register::W1 => {
|
|
||||||
match basereg {
|
|
||||||
Aarch64Register::X0 | Aarch64Register::W0 => {
|
|
||||||
// x0 is overwritten basereg by op1 value.
|
|
||||||
// x1 is indexreg
|
|
||||||
|
|
||||||
// Preserve x2, x3:
|
|
||||||
writer.put_stp_reg_reg_reg_offset(
|
|
||||||
Aarch64Register::X2,
|
|
||||||
Aarch64Register::X3,
|
|
||||||
Aarch64Register::Sp,
|
|
||||||
i64::from(-(16 + gum_red_zone_size_i32())),
|
|
||||||
IndexMode::PreAdjust,
|
|
||||||
);
|
|
||||||
|
|
||||||
//reload basereg to x2
|
|
||||||
writer.put_ldr_reg_reg_offset(
|
|
||||||
Aarch64Register::X2,
|
|
||||||
Aarch64Register::Sp,
|
|
||||||
0u64,
|
|
||||||
);
|
|
||||||
//add basereg into indexreg==x1
|
|
||||||
writer.put_add_reg_reg_reg(
|
|
||||||
indexreg,
|
|
||||||
Aarch64Register::X2,
|
|
||||||
indexreg,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Restore x2, x3
|
|
||||||
assert!(writer.put_ldp_reg_reg_reg_offset(
|
|
||||||
Aarch64Register::X2,
|
|
||||||
Aarch64Register::X3,
|
|
||||||
Aarch64Register::Sp,
|
|
||||||
16 + i64::from(frida_gum_sys::GUM_RED_ZONE_SIZE),
|
|
||||||
IndexMode::PostAdjust,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// indexreg is x1
|
|
||||||
// basereg is not x0 and not x1
|
|
||||||
|
|
||||||
//add them into x1
|
|
||||||
writer.put_add_reg_reg_reg(indexreg, basereg, indexreg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
match basereg {
|
|
||||||
Aarch64Register::X0 | Aarch64Register::W0 => {
|
|
||||||
//basereg is overwritten by op1 value
|
|
||||||
//index reg is not x0 nor x1
|
|
||||||
|
|
||||||
//reload basereg to x1
|
|
||||||
writer.put_ldr_reg_reg_offset(
|
|
||||||
Aarch64Register::X1,
|
|
||||||
Aarch64Register::Sp,
|
|
||||||
0u64,
|
|
||||||
);
|
|
||||||
//add indexreg to basereg==x1
|
|
||||||
writer.put_add_reg_reg_reg(
|
|
||||||
Aarch64Register::X1,
|
|
||||||
Aarch64Register::X1,
|
|
||||||
indexreg,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
//basereg is not x0, can be x1
|
|
||||||
//index reg is not x0 nor x1
|
|
||||||
|
|
||||||
//add them into x1
|
|
||||||
writer.put_add_reg_reg_reg(
|
|
||||||
Aarch64Register::X1,
|
|
||||||
basereg,
|
|
||||||
indexreg,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match basereg {
|
|
||||||
Aarch64Register::X1 | Aarch64Register::W1 => {}
|
|
||||||
Aarch64Register::X0 | Aarch64Register::W0 => {
|
|
||||||
// x0 is overwrittern basereg by op1 value.
|
|
||||||
//reload basereg to x1
|
|
||||||
writer.put_ldr_reg_reg_offset(
|
|
||||||
Aarch64Register::X1,
|
|
||||||
Aarch64Register::Sp,
|
|
||||||
0u64,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
writer.put_mov_reg_reg(Aarch64Register::W1, basereg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add displacement
|
|
||||||
#[allow(clippy::cast_sign_loss)]
|
|
||||||
writer.put_add_reg_reg_imm(
|
|
||||||
Aarch64Register::X1,
|
|
||||||
Aarch64Register::X1,
|
|
||||||
displacement as u64,
|
|
||||||
);
|
|
||||||
//deref into x1 to get the real value
|
|
||||||
writer.put_ldr_reg_reg_offset(Aarch64Register::X1, Aarch64Register::X1, 0u64);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//call cmplog runtime to populate the values map
|
//call cmplog runtime to populate the values map
|
||||||
@ -596,97 +353,122 @@ impl CmpLogRuntime {
|
|||||||
/// Check if the current instruction is cmplog relevant one(any opcode which sets the flags)
|
/// Check if the current instruction is cmplog relevant one(any opcode which sets the flags)
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn cmplog_is_interesting_instruction(
|
pub fn cmplog_is_interesting_instruction(
|
||||||
capstone: &Capstone,
|
decoder: InstDecoder,
|
||||||
_address: u64,
|
_address: u64,
|
||||||
instr: &Insn,
|
instr: &Insn,
|
||||||
) -> Option<(
|
) -> Option<(
|
||||||
CmplogOperandType,
|
CmplogOperandType,
|
||||||
CmplogOperandType,
|
CmplogOperandType,
|
||||||
|
Option<(ShiftStyle, u8)>, //possible shifts: everything except MSL
|
||||||
Option<SpecialCmpLogCase>,
|
Option<SpecialCmpLogCase>,
|
||||||
)> {
|
)> {
|
||||||
// We need to re-decode frida-internal capstone values to upstream capstone
|
let mut instr = disas_count(&decoder, instr.bytes(), 1)[0];
|
||||||
let cs_instr = frida_to_cs(capstone, instr);
|
let operands_len = instr
|
||||||
let cs_instr = cs_instr.first().unwrap();
|
.operands
|
||||||
|
.iter()
|
||||||
|
.position(|item| *item == Operand::Nothing)
|
||||||
|
.unwrap_or_else(|| 4);
|
||||||
|
// "cmp" | "ands" | "subs" | "adds" | "negs" | "ngcs" | "sbcs" | "bics" | "cbz"
|
||||||
|
// | "cbnz" | "tbz" | "tbnz" | "adcs" - yaxpeax aliases insns (i.e., cmp -> subs)
|
||||||
// We only care for compare instructions - aka instructions which set the flags
|
// We only care for compare instructions - aka instructions which set the flags
|
||||||
match cs_instr.mnemonic().unwrap() {
|
match instr.opcode {
|
||||||
"cmp" | "ands" | "subs" | "adds" | "negs" | "ngcs" | "sbcs" | "bics" | "cbz"
|
Opcode::SUBS
|
||||||
| "cbnz" | "tbz" | "tbnz" | "adcs" => (),
|
| Opcode::ANDS
|
||||||
|
| Opcode::ADDS
|
||||||
|
| Opcode::SBCS
|
||||||
|
| Opcode::BICS
|
||||||
|
| Opcode::CBZ
|
||||||
|
| Opcode::CBNZ
|
||||||
|
| Opcode::TBZ
|
||||||
|
| Opcode::TBNZ
|
||||||
|
| Opcode::ADC => (),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
let mut operands = capstone
|
|
||||||
.insn_detail(cs_instr)
|
|
||||||
.unwrap()
|
|
||||||
.arch_detail()
|
|
||||||
.operands();
|
|
||||||
|
|
||||||
// cbz - 1 operand, tbz - 3 operands
|
// cbz - 1 operand, everything else - 3 operands
|
||||||
let special_case = [
|
let special_case = [
|
||||||
"cbz", "cbnz", "tbz", "tbnz", "subs", "adds", "ands", "sbcs", "bics", "adcs",
|
Opcode::CBZ,
|
||||||
|
Opcode::CBNZ,
|
||||||
|
Opcode::TBZ,
|
||||||
|
Opcode::TBNZ,
|
||||||
|
Opcode::SUBS,
|
||||||
|
Opcode::ADDS,
|
||||||
|
Opcode::ANDS,
|
||||||
|
Opcode::SBCS,
|
||||||
|
Opcode::BICS,
|
||||||
|
Opcode::ADCS,
|
||||||
]
|
]
|
||||||
.contains(&cs_instr.mnemonic().unwrap());
|
.contains(&instr.opcode);
|
||||||
if operands.len() != 2 && !special_case {
|
//this check is to ensure that there are the right number of operands
|
||||||
|
if operands_len != 2 && !special_case {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle special opcodes case which have 3 operands, but the 1st(dest) is not important to us
|
// handle special opcodes case which have 3 operands, but the 1st(dest) is not important to us
|
||||||
if ["subs", "adds", "ands", "sbcs", "bics", "adcs"].contains(&cs_instr.mnemonic().unwrap())
|
////subs", "adds", "ands", "sbcs", "bics", "adcs"
|
||||||
|
if [
|
||||||
|
Opcode::SUBS,
|
||||||
|
Opcode::ADDS,
|
||||||
|
Opcode::ANDS,
|
||||||
|
Opcode::SBCS,
|
||||||
|
Opcode::BICS,
|
||||||
|
Opcode::ADCS,
|
||||||
|
]
|
||||||
|
.contains(&instr.opcode)
|
||||||
{
|
{
|
||||||
//remove the dest operand from the list
|
//remove the dest operand from the list
|
||||||
operands.remove(0);
|
instr.operands.rotate_left(1);
|
||||||
|
instr.operands[3] = Operand::Nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cbz marked as special since there is only 1 operand
|
// cbz marked as special since there is only 1 operand
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
let special_case = matches!(cs_instr.mnemonic().unwrap(), "cbz" | "cbnz");
|
let special_case = matches!(instr.opcode, Opcode::CBZ | Opcode::CBNZ);
|
||||||
|
|
||||||
#[allow(clippy::cast_sign_loss, clippy::similar_names)]
|
#[allow(clippy::cast_sign_loss, clippy::similar_names)]
|
||||||
let operand1 = if let Arm64Operand(arm64operand) = operands.first().unwrap() {
|
let operand1 = match instr.operands[0] {
|
||||||
match arm64operand.op_type {
|
//the only possibilities are registers for the first operand
|
||||||
Arm64OperandType::Reg(regid) => Some(CmplogOperandType::Regid(regid)),
|
//precompute the aarch64 frida register because it is ambiguous if register=31 means xzr or sp in yaxpeax
|
||||||
Arm64OperandType::Imm(val) => Some(CmplogOperandType::Imm(val as u64)),
|
Operand::Register(sizecode, reg) => Some(CmplogOperandType::Regid(writer_register(
|
||||||
Arm64OperandType::Mem(opmem) => Some(CmplogOperandType::Mem(
|
reg, sizecode, true,
|
||||||
opmem.base(),
|
))),
|
||||||
opmem.index(),
|
Operand::RegisterOrSP(sizecode, reg) => Some(CmplogOperandType::Regid(
|
||||||
opmem.disp(),
|
writer_register(reg, sizecode, false),
|
||||||
instruction_width(cs_instr, &operands),
|
|
||||||
)),
|
)),
|
||||||
Arm64OperandType::Cimm(val) => Some(CmplogOperandType::Cimm(val as u64)),
|
_ => panic!("First argument is not a register"), //this should never be possible in arm64
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
let operand2 = if special_case {
|
let operand2 = if special_case {
|
||||||
Some(CmplogOperandType::Imm(0))
|
Some((CmplogOperandType::Imm(0), None))
|
||||||
} else if let Arm64Operand(arm64operand2) = &operands[1] {
|
|
||||||
match arm64operand2.op_type {
|
|
||||||
Arm64OperandType::Reg(regid) => Some(CmplogOperandType::Regid(regid)),
|
|
||||||
Arm64OperandType::Imm(val) => Some(CmplogOperandType::Imm(val as u64)),
|
|
||||||
Arm64OperandType::Mem(opmem) => Some(CmplogOperandType::Mem(
|
|
||||||
opmem.base(),
|
|
||||||
opmem.index(),
|
|
||||||
opmem.disp(),
|
|
||||||
instruction_width(cs_instr, &operands),
|
|
||||||
)),
|
|
||||||
Arm64OperandType::Cimm(val) => Some(CmplogOperandType::Cimm(val as u64)),
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
match instr.operands[1] {
|
||||||
|
Operand::Register(sizecode, reg) => Some((
|
||||||
|
CmplogOperandType::Regid(writer_register(reg, sizecode, true)),
|
||||||
|
None,
|
||||||
|
)),
|
||||||
|
Operand::ImmShift(imm, shift) => {
|
||||||
|
Some((CmplogOperandType::Imm((imm as u64) << shift), None))
|
||||||
|
} //precalculate the shift
|
||||||
|
Operand::RegShift(shiftstyle, amount, regsize, reg) => {
|
||||||
|
let reg = CmplogOperandType::Regid(writer_register(reg, regsize, true));
|
||||||
|
let shift = (shiftstyle, amount);
|
||||||
|
Some((reg, Some(shift)))
|
||||||
|
}
|
||||||
|
Operand::Immediate(imm) => Some((CmplogOperandType::Imm(imm as u64), None)),
|
||||||
|
_ => panic!("Second argument could not be decoded"),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// tbz will need to have special handling at emit time(masking operand1 value with operand2)
|
// tbz will need to have special handling at emit time(masking operand1 value with operand2)
|
||||||
let special_case = match cs_instr.mnemonic().unwrap() {
|
let special_case = match instr.opcode {
|
||||||
"tbz" => Some(SpecialCmpLogCase::Tbz),
|
Opcode::TBZ => Some(SpecialCmpLogCase::Tbz),
|
||||||
"tbnz" => Some(SpecialCmpLogCase::Tbnz),
|
Opcode::TBNZ => Some(SpecialCmpLogCase::Tbnz),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(op1) = operand1 {
|
if let Some(op1) = operand1 {
|
||||||
operand2.map(|op2| (op1, op2, special_case))
|
operand2.map(|op2| (op1, op2.0, op2.1, special_case))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,6 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
use capstone::{
|
|
||||||
arch::{self, BuildsCapstone},
|
|
||||||
Capstone,
|
|
||||||
};
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use frida_gum::instruction_writer::InstructionWriter;
|
use frida_gum::instruction_writer::InstructionWriter;
|
||||||
use frida_gum::{
|
use frida_gum::{
|
||||||
@ -27,6 +22,10 @@ use libafl_targets::drcov::DrCovBasicBlock;
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
|
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
|
||||||
use rangemap::RangeMap;
|
use rangemap::RangeMap;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use yaxpeax_arch::Arch;
|
||||||
|
#[cfg(all(target_arch = "aarch64", unix))]
|
||||||
|
use yaxpeax_arm::armv8::a64::{ARMv8, InstDecoder};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use yaxpeax_x86::amd64::InstDecoder;
|
use yaxpeax_x86::amd64::InstDecoder;
|
||||||
|
|
||||||
@ -445,28 +444,14 @@ where
|
|||||||
let ranges = Rc::clone(ranges);
|
let ranges = Rc::clone(ranges);
|
||||||
let runtimes = Rc::clone(runtimes);
|
let runtimes = Rc::clone(runtimes);
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
let capstone = Capstone::new()
|
|
||||||
.arm64()
|
|
||||||
.mode(arch::arm64::ArchMode::Arm)
|
|
||||||
.detail(true)
|
|
||||||
.build()
|
|
||||||
.expect("Failed to create Capstone object");
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
let decoder = InstDecoder::minimal();
|
let decoder = InstDecoder::minimal();
|
||||||
|
|
||||||
Transformer::from_callback(gum, move |basic_block, output| {
|
|
||||||
Self::transform(
|
|
||||||
basic_block,
|
|
||||||
&output,
|
|
||||||
&ranges,
|
|
||||||
&runtimes,
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
&capstone,
|
let decoder = <ARMv8 as Arch>::Decoder::default();
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
decoder,
|
Transformer::from_callback(gum, move |basic_block, output| {
|
||||||
);
|
Self::transform(basic_block, &output, &ranges, &runtimes, decoder);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,7 +508,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
if let Some((basereg, indexreg, displacement, width, shift, extender)) = res {
|
if let Some((basereg, indexreg, displacement, width, shift)) = res {
|
||||||
if let Some(rt) = runtimes.match_first_type_mut::<AsanRuntime>() {
|
if let Some(rt) = runtimes.match_first_type_mut::<AsanRuntime>() {
|
||||||
rt.emit_shadow_check(
|
rt.emit_shadow_check(
|
||||||
address,
|
address,
|
||||||
@ -533,18 +518,25 @@ where
|
|||||||
displacement,
|
displacement,
|
||||||
width,
|
width,
|
||||||
shift,
|
shift,
|
||||||
extender,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||||
if let Some(rt) = runtimes.match_first_type_mut::<CmpLogRuntime>() {
|
if let Some(rt) = runtimes.match_first_type_mut::<CmpLogRuntime>() {
|
||||||
if let Some((op1, op2, special_case)) =
|
if let Some((op1, op2, shift, special_case)) =
|
||||||
CmpLogRuntime::cmplog_is_interesting_instruction(&capstone, address, instr)
|
CmpLogRuntime::cmplog_is_interesting_instruction(decoder, address, instr)
|
||||||
|
//change this as well
|
||||||
{
|
{
|
||||||
//emit code that saves the relevant data in runtime(passes it to x0, x1)
|
//emit code that saves the relevant data in runtime(passes it to x0, x1)
|
||||||
rt.emit_comparison_handling(address, &output, &op1, &op2, special_case);
|
rt.emit_comparison_handling(
|
||||||
|
address,
|
||||||
|
&output,
|
||||||
|
&op1,
|
||||||
|
&op2,
|
||||||
|
shift,
|
||||||
|
special_case,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,100 +1,128 @@
|
|||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use capstone::Capstone;
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
use capstone::{
|
|
||||||
arch::{self, arm64::Arm64OperandType, ArchOperand::Arm64Operand},
|
|
||||||
Insn,
|
|
||||||
};
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
use frida_gum::instruction_writer::Aarch64Register;
|
use frida_gum::instruction_writer::Aarch64Register;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use frida_gum::instruction_writer::X86Register;
|
use frida_gum::instruction_writer::X86Register;
|
||||||
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
|
#[cfg(any(target_arch = "x86_64"))]
|
||||||
use frida_gum_sys;
|
use frida_gum_sys;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use num_traits::cast::FromPrimitive;
|
use num_traits::cast::FromPrimitive;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use yaxpeax_arch::LengthedInstruction;
|
use yaxpeax_arch::LengthedInstruction;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use yaxpeax_arch::{Decoder, ReaderBuilder};
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use yaxpeax_arm::armv8::a64::{InstDecoder, Instruction, Opcode, Operand, SIMDSizeCode, SizeCode};
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
use yaxpeax_x86::amd64::Operand;
|
use yaxpeax_x86::amd64::Operand;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use yaxpeax_x86::amd64::{InstDecoder, Instruction, RegSpec};
|
use yaxpeax_x86::amd64::{InstDecoder, Instruction, RegSpec};
|
||||||
|
|
||||||
|
/// Determine the size of an SIMD register
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_simd_size(sizecode: SIMDSizeCode) -> u32 {
|
||||||
|
match sizecode {
|
||||||
|
SIMDSizeCode::B => 1,
|
||||||
|
SIMDSizeCode::H => 2,
|
||||||
|
SIMDSizeCode::S => 4,
|
||||||
|
SIMDSizeCode::D => 8,
|
||||||
|
SIMDSizeCode::Q => 16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine the size of a normal register
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_reg_size(sizecode: SizeCode) -> u32 {
|
||||||
|
match sizecode {
|
||||||
|
SizeCode::W => {
|
||||||
|
//this is guaranteed to be 4 because we deal with the strb/ldrb and strh/ldrh should be dealt with in instruction_width
|
||||||
|
4
|
||||||
|
}
|
||||||
|
SizeCode::X => 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine the width of the specified instruction
|
/// Determine the width of the specified instruction
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn instruction_width(instr: &Insn, operands: &[arch::ArchOperand]) -> u32 {
|
pub fn instruction_width(instr: &Instruction) -> u32 {
|
||||||
use capstone::arch::arm64::{Arm64Insn as I, Arm64Reg as R, Arm64Vas as V};
|
let num_registers = match instr.opcode {
|
||||||
|
Opcode::STP
|
||||||
let num_registers = match instr.id().0.into() {
|
| Opcode::STXP
|
||||||
I::ARM64_INS_STP
|
| Opcode::STNP
|
||||||
| I::ARM64_INS_STXP
|
| Opcode::STLXP
|
||||||
| I::ARM64_INS_STNP
|
| Opcode::LDP
|
||||||
| I::ARM64_INS_STLXP
|
| Opcode::LDXP
|
||||||
| I::ARM64_INS_LDP
|
| Opcode::LDNP => 2,
|
||||||
| I::ARM64_INS_LDXP
|
|
||||||
| I::ARM64_INS_LDNP => 2,
|
|
||||||
_ => 1,
|
_ => 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mnemonic = instr.mnemonic().unwrap();
|
// let mnemonic = instr.opcode.to_string().as_bytes();
|
||||||
match mnemonic.as_bytes().last().unwrap() {
|
match instr.opcode.to_string().as_bytes().last().unwrap() {
|
||||||
b'b' => return 1,
|
b'b' => return 1,
|
||||||
b'h' => return 2,
|
b'h' => return 2,
|
||||||
b'w' => return 4 * num_registers,
|
b'w' => return 4 * num_registers,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Arm64Operand(operand) = operands.first().unwrap() {
|
let size = match instr.operands.first().unwrap() {
|
||||||
if operand.vas != V::ARM64_VAS_INVALID {
|
Operand::Register(sizecode, _) => {
|
||||||
let count_byte: u32 = if mnemonic.starts_with("st") || mnemonic.starts_with("ld") {
|
//this is used for standard loads/stores including ldr, ldp, etc.
|
||||||
mnemonic.chars().nth(2).unwrap().to_digit(10).unwrap()
|
get_reg_size(*sizecode)
|
||||||
} else {
|
|
||||||
1
|
|
||||||
};
|
|
||||||
|
|
||||||
return match operand.vas {
|
|
||||||
V::ARM64_VAS_1B => count_byte,
|
|
||||||
V::ARM64_VAS_1H => 2 * count_byte,
|
|
||||||
V::ARM64_VAS_4B | V::ARM64_VAS_1S | V::ARM64_VAS_1D | V::ARM64_VAS_2H => {
|
|
||||||
4 * count_byte
|
|
||||||
}
|
}
|
||||||
V::ARM64_VAS_8B
|
Operand::RegisterPair(sizecode, _) => {
|
||||||
| V::ARM64_VAS_4H
|
//not sure where this is used, but it is possible in yaxpeax
|
||||||
| V::ARM64_VAS_2S
|
get_reg_size(*sizecode)
|
||||||
| V::ARM64_VAS_2D
|
}
|
||||||
| V::ARM64_VAS_1Q => 8 * count_byte,
|
Operand::SIMDRegister(sizecode, _) => {
|
||||||
V::ARM64_VAS_8H | V::ARM64_VAS_4S | V::ARM64_VAS_16B => 16 * count_byte,
|
//this is used in cases like ldr q0, [sp]
|
||||||
V::ARM64_VAS_INVALID => {
|
get_simd_size(*sizecode)
|
||||||
panic!("should not be reached");
|
}
|
||||||
|
Operand::SIMDRegisterGroup(sizecode, _, _, num) => {
|
||||||
|
////This is used for cases such as ld4 {v1.2s, v2.2s, v3.2s, v4.2s}, [x0].
|
||||||
|
//the sizecode is the size of each simd structure (This can only be D or Q), num is the number of them (i.e. ld4 would be 4)
|
||||||
|
get_simd_size(*sizecode) * *num as u32
|
||||||
|
}
|
||||||
|
Operand::SIMDRegisterGroupLane(_, sizecode, num, _) => {
|
||||||
|
//This is used for cases such as ld4 {v0.s, v1.s, v2.s, v3.s}[0], [x0]. In this case sizecode is the size of each lane, num is the number of them
|
||||||
|
get_simd_size(*sizecode) * *num as u32
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if let Arm64OperandType::Reg(operand) = operand.op_type {
|
num_registers * size
|
||||||
match u32::from(operand.0) {
|
|
||||||
R::ARM64_REG_W0..=R::ARM64_REG_W30
|
|
||||||
| R::ARM64_REG_WZR
|
|
||||||
| R::ARM64_REG_WSP
|
|
||||||
| R::ARM64_REG_S0..=R::ARM64_REG_S31 => return 4 * num_registers,
|
|
||||||
R::ARM64_REG_D0..=R::ARM64_REG_D31 => return 8 * num_registers,
|
|
||||||
R::ARM64_REG_Q0..=R::ARM64_REG_Q31 => return 16,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
8 * num_registers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert from a capstone register id to a frida `InstructionWriter` register index
|
/// Convert from a yaxpeax register to frida gum's register state
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn writer_register(reg: capstone::RegId) -> Aarch64Register {
|
pub fn writer_register(reg: u16, sizecode: SizeCode, zr: bool) -> Aarch64Register {
|
||||||
let regint: u16 = reg.0;
|
//yaxpeax and arm both make it so that depending on the opcode reg=31 can be EITHER SP or XZR.
|
||||||
Aarch64Register::from_u32(u32::from(regint)).unwrap()
|
match (reg, sizecode, zr) {
|
||||||
|
(0..=28, SizeCode::X, _) => {
|
||||||
|
Aarch64Register::from_u32(Aarch64Register::X0 as u32 + reg as u32).unwrap()
|
||||||
|
}
|
||||||
|
(0..=30, SizeCode::W, _) => {
|
||||||
|
Aarch64Register::from_u32(Aarch64Register::W0 as u32 + reg as u32).unwrap()
|
||||||
|
}
|
||||||
|
(29, SizeCode::X, _) => Aarch64Register::Fp,
|
||||||
|
(30, SizeCode::X, _) => Aarch64Register::Lr,
|
||||||
|
(31, SizeCode::X, false) => Aarch64Register::Sp,
|
||||||
|
(31, SizeCode::W, false) => Aarch64Register::Wsp,
|
||||||
|
(31, SizeCode::X, true) => Aarch64Register::Xzr,
|
||||||
|
(31, SizeCode::W, true) => Aarch64Register::Wzr,
|
||||||
|
_ => panic!("Failed to get writer register"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translate from `RegSpec` to `X86Register`
|
/// Translate from `RegSpec` to `X86Register`
|
||||||
|
#[cfg(all(target_arch = "x86_64", unix))]
|
||||||
const X86_64_REGS: [(RegSpec, X86Register); 34] = [
|
const X86_64_REGS: [(RegSpec, X86Register); 34] = [
|
||||||
(RegSpec::eax(), X86Register::Eax),
|
(RegSpec::eax(), X86Register::Eax),
|
||||||
(RegSpec::ecx(), X86Register::Ecx),
|
(RegSpec::ecx(), X86Register::Ecx),
|
||||||
@ -151,7 +179,7 @@ pub fn writer_register(reg: RegSpec) -> X86Register {
|
|||||||
|
|
||||||
/// Translates a frida instruction to a capstone instruction.
|
/// Translates a frida instruction to a capstone instruction.
|
||||||
/// Returns a [`capstone::Instructions`] with a single [`capstone::Insn`] inside.
|
/// Returns a [`capstone::Instructions`] with a single [`capstone::Insn`] inside.
|
||||||
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
|
#[cfg(any(target_arch = "x86_64"))]
|
||||||
pub(crate) fn frida_to_cs(decoder: InstDecoder, frida_insn: &frida_gum_sys::Insn) -> Instruction {
|
pub(crate) fn frida_to_cs(decoder: InstDecoder, frida_insn: &frida_gum_sys::Insn) -> Instruction {
|
||||||
decoder.decode_slice(frida_insn.bytes()).unwrap()
|
decoder.decode_slice(frida_insn.bytes()).unwrap()
|
||||||
}
|
}
|
||||||
@ -229,3 +257,19 @@ pub fn disas_count(decoder: &InstDecoder, data: &[u8], count: usize) -> Vec<Inst
|
|||||||
counter -= 1;
|
counter -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
/// Disassemble "count" number of instructions
|
||||||
|
pub fn disas_count(decoder: &InstDecoder, data: &[u8], count: usize) -> Vec<Instruction> {
|
||||||
|
let _counter = count;
|
||||||
|
let mut ret = vec![];
|
||||||
|
let _start = 0;
|
||||||
|
|
||||||
|
let mut reader = ReaderBuilder::<u64, u8>::read_from(data);
|
||||||
|
|
||||||
|
while let Ok(insn) = decoder.decode(&mut reader) {
|
||||||
|
ret.push(insn);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user