use crate::emu::Emu;
use iced_x86::{Decoder, DecoderOptions, Instruction};
const INSTRUCTION_ARRAY_SIZE: usize = 8192 * 64;
const CACHE_SIZE: usize = 2048 * MAX_CACHE_PER_LINE;
const CACHE_MASK: usize = CACHE_SIZE - 1; const MAX_CACHE_PER_LINE: usize = 32;
pub const INVALID_LPF_ADDR: u64 = 0xffffffffffffffff;
pub const INVALID_KEY: usize = 0xffffffffffffffff;
pub const INVALID_LEN: usize = 0xffffffffffffffff;
pub fn LPF_OF(addr: u64) -> u64 {
addr & 0xfffffffffffff000
}
#[derive(Clone)]
struct CachedInstruction {
pub lpf: u64,
pub instruction_key: usize,
pub instruction_len: usize,
}
impl Default for CachedInstruction {
fn default() -> Self {
CachedInstruction {
lpf: INVALID_LPF_ADDR,
instruction_key: INVALID_KEY,
instruction_len: INVALID_LEN,
}
}
}
impl CachedInstruction {
pub fn is_valid(&self) -> bool {
self.lpf == INVALID_LPF_ADDR
}
}
#[derive(Clone)]
pub struct InstructionCache<T: Copy + Default> {
cache_entries: Vec<CachedInstruction>,
instructions: Vec<T>,
next_instruction_slot: usize,
pub current_instruction_slot: usize,
current_decode_len: usize,
current_decode_idx: usize,
}
impl<T: Copy + Default> InstructionCache<T> {
pub fn new() -> Self {
InstructionCache {
cache_entries: vec![CachedInstruction::default(); CACHE_SIZE],
instructions: vec![T::default(); INSTRUCTION_ARRAY_SIZE],
next_instruction_slot: 0,
current_decode_len: 0,
current_instruction_slot: 0,
current_decode_idx: 0,
}
}
#[inline(always)]
pub fn get_index_of(&self, lpf: u64, len: u64) -> usize {
const TLB_MASK: u32 = ((CACHE_SIZE - 1) << 12) as u32;
(((lpf + len) & (TLB_MASK as u64)) >> 12) as usize
}
#[inline]
pub fn flush_cache_line(&mut self, idx: usize) {
for i in 0..MAX_CACHE_PER_LINE {
self.cache_entries[idx + i].lpf = INVALID_LPF_ADDR;
}
}
pub fn lookup_entry(&mut self, addr: u64, len: u64) -> bool {
let lpf = crate::maps::tlb::LPF_OF(addr);
let idx = self.get_index_of(lpf, len);
for i in 0..MAX_CACHE_PER_LINE {
if self.cache_entries[idx + i].lpf == INVALID_LPF_ADDR {
return false;
}
if self.cache_entries[idx + i].lpf == addr {
let key = self.cache_entries[idx + i].instruction_key;
self.current_instruction_slot = key;
self.current_decode_len = self.cache_entries[idx + i].instruction_len;
self.current_decode_idx = 0;
return true;
}
}
self.flush_cache_line(idx);
false
}
#[inline(always)]
fn flush_cache(&mut self) {
self.cache_entries.iter_mut().for_each(|entry| {
entry.lpf = INVALID_LPF_ADDR;
entry.instruction_key = INVALID_KEY;
entry.instruction_len = INVALID_LEN;
});
self.next_instruction_slot = 0;
}
pub fn decode_out(&mut self, instruction: &mut T) {
*instruction = self.instructions[self.current_instruction_slot + self.current_decode_idx];
self.current_decode_idx += 1;
}
pub fn can_decode(&self) -> bool {
self.current_decode_idx < self.current_decode_len
}
pub fn insert_single(&mut self, addr: u64, instruction: T) {
let lpf = crate::maps::tlb::LPF_OF(addr);
let idx = self.get_index_of(lpf, 0);
let slot = self.next_instruction_slot;
if slot + 1 > INSTRUCTION_ARRAY_SIZE {
self.flush_cache();
}
self.instructions[self.next_instruction_slot] = instruction;
self.next_instruction_slot += 1;
for i in 0..MAX_CACHE_PER_LINE {
if self.cache_entries[idx + i].lpf == INVALID_LPF_ADDR {
self.cache_entries[idx + i].instruction_key = slot;
self.cache_entries[idx + i].lpf = addr;
self.cache_entries[idx + i].instruction_len = 1;
break;
}
}
}
}
impl InstructionCache<iced_x86::Instruction> {
pub fn insert_from_decoder(&mut self, decoder: &mut Decoder, addition: usize, rip_addr: u64) {
let lpf = crate::maps::tlb::LPF_OF(rip_addr);
let idx = self.get_index_of(lpf, 0);
let slot = self.next_instruction_slot;
let mut count: usize = 0;
let max_position = decoder.max_position();
if max_position + self.next_instruction_slot > INSTRUCTION_ARRAY_SIZE {
self.flush_cache();
}
while decoder.can_decode() && decoder.position() + addition <= max_position {
decoder.decode_out(&mut self.instructions[slot + count]);
let temp = self.instructions[slot + count];
if temp.is_jmp_short_or_near()
|| temp.is_jmp_near_indirect()
|| temp.is_jmp_far()
|| temp.is_jmp_far_indirect()
|| temp.is_jcc_short_or_near()
|| temp.is_call_near_indirect()
|| temp.is_call_near()
|| temp.is_call_far_indirect()
|| temp.is_call_far()
{
count += 1;
break;
}
count += 1;
}
self.next_instruction_slot += count;
for i in 0..MAX_CACHE_PER_LINE {
if self.cache_entries[idx + i].lpf == INVALID_LPF_ADDR {
self.cache_entries[idx + i].instruction_key = slot;
self.cache_entries[idx + i].lpf = rip_addr;
self.cache_entries[idx + i].instruction_len = count;
break;
}
}
assert!(self.lookup_entry(rip_addr, 0), "Cache Insertion FAILED: There is support to be entry after insertion using insert_from_decoder");
}
pub fn insert_instruction(&mut self, addr: u64, instrs: Vec<iced_x86::Instruction>) {
let lpf = crate::maps::tlb::LPF_OF(addr);
let idx = self.get_index_of(lpf, 0);
let slot = self.next_instruction_slot;
self.next_instruction_slot += instrs.len();
if self.next_instruction_slot >= INSTRUCTION_ARRAY_SIZE {
self.flush_cache();
}
for i in 0..instrs.len() {
self.instructions[slot + i] = instrs[i];
}
for i in 0..MAX_CACHE_PER_LINE {
if self.cache_entries[idx + i].lpf == INVALID_LPF_ADDR {
self.cache_entries[idx + i].instruction_key = slot;
self.cache_entries[idx + i].lpf = addr;
self.cache_entries[idx + i].instruction_len = instrs.len();
break;
}
}
}
}
impl InstructionCache<yaxpeax_arm::armv8::a64::Instruction> {
pub fn insert_from_block(&mut self, block: &[u8], pc_addr: u64) {
let lpf = crate::maps::tlb::LPF_OF(pc_addr);
let idx = self.get_index_of(lpf, 0);
let slot = self.next_instruction_slot;
let mut count: usize = 0;
let decoder = yaxpeax_arm::armv8::a64::InstDecoder::default();
let mut offset: usize = 0;
while offset + 4 <= block.len() {
if slot + count >= INSTRUCTION_ARRAY_SIZE {
self.flush_cache();
return; }
let chunk = &block[offset..offset + 4];
let mut reader = yaxpeax_arch::U8Reader::new(chunk);
let ins = match yaxpeax_arch::Decoder::decode(&decoder, &mut reader) {
Ok(ins) => ins,
Err(_) => break,
};
self.instructions[slot + count] = ins;
count += 1;
offset += 4;
use yaxpeax_arm::armv8::a64::Opcode;
match ins.opcode {
Opcode::RET
| Opcode::B
| Opcode::BR
| Opcode::BL
| Opcode::BLR
| Opcode::Bcc(_)
| Opcode::CBZ
| Opcode::CBNZ
| Opcode::TBZ
| Opcode::TBNZ => break,
_ => {}
}
}
if count == 0 {
return;
}
self.next_instruction_slot += count;
for i in 0..MAX_CACHE_PER_LINE {
if self.cache_entries[idx + i].lpf == INVALID_LPF_ADDR {
self.cache_entries[idx + i].instruction_key = slot;
self.cache_entries[idx + i].lpf = pc_addr;
self.cache_entries[idx + i].instruction_len = count;
break;
}
}
assert!(
self.lookup_entry(pc_addr, 0),
"aarch64 cache insertion failed"
);
}
}
impl Emu {
pub fn disassemble(&mut self, addr: u64, amount: u32) -> String {
if self.cfg.arch.is_aarch64() {
return self.disassemble_aarch64(addr, amount);
}
let mut out = String::new();
let code = self.maps.get_mem_by_addr(addr).expect("address not mapped");
let block = code.read_from(addr).to_vec();
let bits: u32 = if self.cfg.is_x64() { 64 } else { 32 };
let mut decoder = Decoder::with_ip(bits, &block, addr, DecoderOptions::NONE);
let mut instruction = Instruction::default();
let mut count: u32 = 1;
while decoder.can_decode() {
decoder.decode_out(&mut instruction);
let output = self.x86_format_instruction(&instruction);
if self.cfg.is_x64() {
out.push_str(&format!("0x{:x}: {}\n", instruction.ip(), output));
} else {
out.push_str(&format!("0x{:x}: {}\n", instruction.ip32(), output));
}
count += 1;
if count == amount {
break;
}
}
out
}
fn disassemble_aarch64(&self, addr: u64, amount: u32) -> String {
let mut out = String::new();
let code = self.maps.get_mem_by_addr(addr).expect("address not mapped");
let block = code.read_from(addr).to_vec();
let decoder = yaxpeax_arm::armv8::a64::InstDecoder::default();
let mut pc = addr;
let mut count: u32 = 0;
let mut offset: usize = 0;
while offset + 4 <= block.len() && count < amount {
let chunk = &block[offset..offset + 4];
let mut reader = yaxpeax_arch::U8Reader::new(chunk);
match yaxpeax_arch::Decoder::decode(&decoder, &mut reader) {
Ok(ins) => {
out.push_str(&format!("0x{:x}: {}\n", pc, ins));
}
Err(e) => {
out.push_str(&format!("0x{:x}: <decode error: {:?}>\n", pc, e));
}
}
pc += 4;
offset += 4;
count += 1;
}
out
}
}