Unicorn rust bindings improvements

This commit is contained in:
Dominik Maier
2021-11-08 19:34:53 +01:00
parent 06f454d513
commit f8f0d4471f
16 changed files with 417 additions and 388 deletions

View File

@ -9,7 +9,7 @@
//! use unicorn_engine::RegisterARM;
//! use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE};
//!
//! fn main() {
//! fn emulate() {
//! let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23
//!
//! let mut unicorn = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance");
@ -35,17 +35,15 @@ mod arm64;
mod m68k;
mod mips;
mod ppc;
mod riscv;
mod sparc;
mod x86;
pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, riscv::*, sparc::*, x86::*};
use std::{marker::PhantomData, ptr};
pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, sparc::*, x86::*};
use ffi::uc_handle;
use std::collections::HashMap;
use std::ffi::c_void;
use std::marker::PhantomPinned;
use std::pin::Pin;
use unicorn_const::*;
use libc::c_void;
use unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Permission, Query};
#[derive(Debug)]
pub struct Context {
@ -53,106 +51,127 @@ pub struct Context {
}
impl Context {
#[must_use]
pub fn new() -> Self {
Context { context: 0 }
}
#[must_use]
pub fn is_initialized(&self) -> bool {
self.context != 0
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe { ffi::uc_context_free(self.context) };
impl Default for Context {
fn default() -> Self {
Self::new()
}
}
impl Drop for Context {
fn drop(&mut self) {
if self.is_initialized() {
unsafe {
ffi::uc_context_free(self.context);
}
}
self.context = 0;
}
}
#[derive(Debug)]
/// A Unicorn emulator instance.
pub struct Unicorn {
inner: Pin<Box<UnicornInner>>,
}
#[derive(Debug)]
/// Handle used to safely access exposed functions and data of a Unicorn instance.
pub struct UnicornHandle<'a> {
inner: Pin<&'a mut UnicornInner>,
}
/// Internal Management struct
pub struct UnicornInner {
pub struct Unicorn<'a, D: 'a> {
pub uc: uc_handle,
pub arch: Arch,
pub code_hooks: HashMap<*mut libc::c_void, Box<ffi::CodeHook>>,
pub block_hooks: HashMap<*mut libc::c_void, Box<ffi::BlockHook>>,
pub mem_hooks: HashMap<*mut libc::c_void, Box<ffi::MemHook>>,
pub intr_hooks: HashMap<*mut libc::c_void, Box<ffi::InterruptHook>>,
pub insn_in_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionInHook>>,
pub insn_out_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionOutHook>>,
pub insn_sys_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionSysHook>>,
_pin: PhantomPinned,
/// to keep ownership over the hook for this uc instance's lifetime
pub hooks: Vec<(ffi::uc_hook, Box<dyn ffi::IsUcHook<'a> + 'a>)>,
pub data: D,
}
impl Unicorn {
impl Unicorn<()> {
/// Create a new instance of the unicorn engine for the specified architecture
/// and hardware mode.
pub fn new(arch: Arch, mode: Mode) -> Result<Unicorn, uc_error> {
pub fn new(arch: Arch, mode: Mode) -> Result<Unicorn<'a, ()>, uc_error> {
Self::new_with_data(arch, mode, ())
}
}
impl<'a, D> Unicorn<'a, D>
where
D: 'a,
{
/// Create a new instance of the unicorn engine for the specified architecture
/// and hardware mode.
pub fn new(arch: Arch, mode: Mode) -> Result<Unicorn<'a, ()>, uc_error> {
Self::new_with_data(arch, mode, ())
}
/// Create a new instance of the unicorn engine for the specified architecture
/// and hardware mode.
pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result<Unicorn<'a, D>, uc_error> {
let mut handle = std::ptr::null_mut();
let err = unsafe { ffi::uc_open(arch, mode, &mut handle) };
if err == uc_error::OK {
Ok(Unicorn {
inner: Box::pin(UnicornInner {
uc: handle,
arch: arch,
code_hooks: HashMap::new(),
block_hooks: HashMap::new(),
mem_hooks: HashMap::new(),
intr_hooks: HashMap::new(),
insn_in_hooks: HashMap::new(),
insn_out_hooks: HashMap::new(),
insn_sys_hooks: HashMap::new(),
_pin: std::marker::PhantomPinned,
}),
uc: handle,
arch,
data,
hooks: vec![],
})
} else {
Err(err)
}
}
pub fn borrow<'a>(&'a mut self) -> UnicornHandle<'a> {
UnicornHandle {
inner: self.inner.as_mut(),
}
}
}
impl Drop for Unicorn {
/// Drop UC
/// TODO: !!! Right now, this leaks the unicorn instance on purpose.
/// UC 1 for some platforms, for example aarch64, seems to crash on cleanup.
/// After updating to Unicorn 2, we should call `uc_close` again!
impl<'a, D> Drop for Unicorn<'a, D> {
fn drop(&mut self) {
unsafe { ffi::uc_close(self.inner.uc) };
if !self.uc.is_null() {
// TODO: !!!
// This is a deliberate leak, get rid of it after updating to UC2!
// unsafe { ffi::uc_close(self.uc) };
}
self.uc = ptr::null_mut();
}
}
impl std::fmt::Debug for UnicornInner {
impl<'a, D> std::fmt::Debug for Unicorn<'a, D> {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "Unicorn {{ uc: {:p} }}", self.uc)
}
}
impl<'a> UnicornHandle<'a> {
impl<'a, D> Unicorn<'a, D> {
/// Return whatever data was passed during initialization.
///
/// For an example, have a look at `utils::init_emu_with_heap` where
/// a struct is passed which is used for a custom allocator.
pub fn get_data(&self) -> &D {
&self.data
}
/// Return a mutable reference to whatever data was passed during initialization.
pub fn get_data_mut(&mut self) -> &mut D {
&mut self.data
}
/// Return the architecture of the current emulator.
pub fn get_arch(&self) -> Arch {
self.inner.arch
self.arch
}
/// Returns a vector with the memory regions that are mapped in the emulator.
pub fn mem_regions(&self) -> Result<Vec<MemRegion>, uc_error> {
let mut nb_regions: u32 = 0;
let mut p_regions: *const MemRegion = std::ptr::null_mut();
let err = unsafe { ffi::uc_mem_regions(self.inner.uc, &mut p_regions, &mut nb_regions) };
let p_regions: *const MemRegion = std::ptr::null_mut();
let err = unsafe { ffi::uc_mem_regions(self.uc, &p_regions, &mut nb_regions) };
if err == uc_error::OK {
let mut regions = Vec::new();
for i in 0..nb_regions {
regions.push(unsafe { std::mem::transmute_copy(&*p_regions.offset(i as isize)) });
regions.push(unsafe { std::mem::transmute_copy(&*p_regions.add(i as usize)) });
}
unsafe { libc::free(p_regions as _) };
Ok(regions)
@ -163,7 +182,7 @@ impl<'a> UnicornHandle<'a> {
/// Read a range of bytes from memory at the specified address.
pub fn mem_read(&self, address: u64, buf: &mut [u8]) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_read(self.inner.uc, address, buf.as_mut_ptr(), buf.len()) };
let err = unsafe { ffi::uc_mem_read(self.uc, address, buf.as_mut_ptr(), buf.len()) };
if err == uc_error::OK {
Ok(())
} else {
@ -174,7 +193,7 @@ impl<'a> UnicornHandle<'a> {
/// Return a range of bytes from memory at the specified address as vector.
pub fn mem_read_as_vec(&self, address: u64, size: usize) -> Result<Vec<u8>, uc_error> {
let mut buf = vec![0; size];
let err = unsafe { ffi::uc_mem_read(self.inner.uc, address, buf.as_mut_ptr(), size) };
let err = unsafe { ffi::uc_mem_read(self.uc, address, buf.as_mut_ptr(), size) };
if err == uc_error::OK {
Ok(buf)
} else {
@ -183,7 +202,7 @@ impl<'a> UnicornHandle<'a> {
}
pub fn mem_write(&mut self, address: u64, bytes: &[u8]) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_write(self.inner.uc, address, bytes.as_ptr(), bytes.len()) };
let err = unsafe { ffi::uc_mem_write(self.uc, address, bytes.as_ptr(), bytes.len()) };
if err == uc_error::OK {
Ok(())
} else {
@ -193,6 +212,8 @@ impl<'a> UnicornHandle<'a> {
/// Map an existing memory region in the emulator at the specified address.
///
/// # Safety
///
/// This function is marked unsafe because it is the responsibility of the caller to
/// ensure that `size` matches the size of the passed buffer, an invalid `size` value will
/// likely cause a crash in unicorn.
@ -202,14 +223,14 @@ impl<'a> UnicornHandle<'a> {
/// `size` must be a multiple of 4kb or this will return `Error::ARG`.
///
/// `ptr` is a pointer to the provided memory region that will be used by the emulator.
pub fn mem_map_ptr(
pub unsafe fn mem_map_ptr(
&mut self,
address: u64,
size: usize,
perms: Permission,
ptr: *mut c_void,
) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_map_ptr(self.inner.uc, address, size, perms.bits(), ptr) };
let err = ffi::uc_mem_map_ptr(self.uc, address, size, perms.bits(), ptr);
if err == uc_error::OK {
Ok(())
} else {
@ -227,7 +248,7 @@ impl<'a> UnicornHandle<'a> {
size: libc::size_t,
perms: Permission,
) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_map(self.inner.uc, address, size, perms.bits()) };
let err = unsafe { ffi::uc_mem_map(self.uc, address, size, perms.bits()) };
if err == uc_error::OK {
Ok(())
} else {
@ -240,7 +261,7 @@ impl<'a> UnicornHandle<'a> {
/// `address` must be aligned to 4kb or this will return `Error::ARG`.
/// `size` must be a multiple of 4kb or this will return `Error::ARG`.
pub fn mem_unmap(&mut self, address: u64, size: libc::size_t) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_unmap(self.inner.uc, address, size) };
let err = unsafe { ffi::uc_mem_unmap(self.uc, address, size) };
if err == uc_error::OK {
Ok(())
} else {
@ -258,7 +279,7 @@ impl<'a> UnicornHandle<'a> {
size: libc::size_t,
perms: Permission,
) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_protect(self.inner.uc, address, size, perms.bits()) };
let err = unsafe { ffi::uc_mem_protect(self.uc, address, size, perms.bits()) };
if err == uc_error::OK {
Ok(())
} else {
@ -268,8 +289,7 @@ impl<'a> UnicornHandle<'a> {
/// Write an unsigned value from a register.
pub fn reg_write<T: Into<i32>>(&mut self, regid: T, value: u64) -> Result<(), uc_error> {
let err =
unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), &value as *const _ as _) };
let err = unsafe { ffi::uc_reg_write(self.uc, regid.into(), &value as *const _ as _) };
if err == uc_error::OK {
Ok(())
} else {
@ -281,8 +301,8 @@ impl<'a> UnicornHandle<'a> {
///
/// The user has to make sure that the buffer length matches the register size.
/// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: Box<[u8]>) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), value.as_ptr() as _) };
pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: &[u8]) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_reg_write(self.uc, regid.into(), value.as_ptr() as _) };
if err == uc_error::OK {
Ok(())
} else {
@ -295,8 +315,7 @@ impl<'a> UnicornHandle<'a> {
/// Not to be used with registers larger than 64 bit.
pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> {
let mut value: u64 = 0;
let err =
unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut u64 as _) };
let err = unsafe { ffi::uc_reg_read(self.uc, regid.into(), &mut value as *mut u64 as _) };
if err == uc_error::OK {
Ok(value)
} else {
@ -306,7 +325,7 @@ impl<'a> UnicornHandle<'a> {
/// Read 128, 256 or 512 bit register value into heap allocated byte array.
///
/// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM, ST (x86); Q, V (arm64)).
/// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
pub fn reg_read_long<T: Into<i32>>(&self, regid: T) -> Result<Box<[u8]>, uc_error> {
let err: uc_error;
let boxed: Box<[u8]>;
@ -329,8 +348,6 @@ impl<'a> UnicornHandle<'a> {
value = vec![0; 64];
} else if curr_reg_id == x86::RegisterX86::GDTR as i32
|| curr_reg_id == x86::RegisterX86::IDTR as i32
|| (curr_reg_id >= x86::RegisterX86::ST0 as i32
&& curr_reg_id <= x86::RegisterX86::ST7 as i32)
{
value = vec![0; 10]; // 64 bit base address in IA-32e mode
} else {
@ -350,7 +367,7 @@ impl<'a> UnicornHandle<'a> {
return Err(uc_error::ARCH);
}
err = unsafe { ffi::uc_reg_read(self.inner.uc, curr_reg_id, value.as_mut_ptr() as _) };
err = unsafe { ffi::uc_reg_read(self.uc, curr_reg_id, value.as_mut_ptr() as _) };
if err == uc_error::OK {
boxed = value.into_boxed_slice();
@ -363,8 +380,7 @@ impl<'a> UnicornHandle<'a> {
/// Read a signed 32-bit value from a register.
pub fn reg_read_i32<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> {
let mut value: i32 = 0;
let err =
unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut i32 as _) };
let err = unsafe { ffi::uc_reg_read(self.uc, regid.into(), &mut value as *mut i32 as _) };
if err == uc_error::OK {
Ok(value)
} else {
@ -373,36 +389,34 @@ impl<'a> UnicornHandle<'a> {
}
/// Add a code hook.
pub fn add_code_hook<F: 'static>(
pub fn add_code_hook<F: 'a>(
&mut self,
begin: u64,
end: u64,
callback: F,
) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle, u64, u32),
F: FnMut(&mut crate::Unicorn<D>, u64, u32) + 'a,
{
let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::CodeHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback),
let mut user_data = Box::new(ffi::UcHook {
callback,
phantom: PhantomData::<&D>,
});
let err = unsafe {
ffi::uc_hook_add(
self.inner.uc,
self.uc,
&mut hook_ptr,
HookType::CODE,
ffi::code_hook_proxy as _,
ffi::code_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _,
begin,
end,
)
};
if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() }
.code_hooks
.insert(hook_ptr, user_data);
self.hooks.push((hook_ptr, user_data));
Ok(hook_ptr)
} else {
Err(err)
@ -410,31 +424,30 @@ impl<'a> UnicornHandle<'a> {
}
/// Add a block hook.
pub fn add_block_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
pub fn add_block_hook<F: 'a>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle, u64, u32),
F: FnMut(&mut Unicorn<D>, u64, u32),
{
let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::BlockHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback),
let mut user_data = Box::new(ffi::UcHook {
callback,
phantom: PhantomData::<&D>,
});
let err = unsafe {
ffi::uc_hook_add(
self.inner.uc,
self.uc,
&mut hook_ptr,
HookType::BLOCK,
ffi::block_hook_proxy as _,
ffi::block_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _,
1,
0,
)
};
if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() }
.block_hooks
.insert(hook_ptr, user_data);
self.hooks.push((hook_ptr, user_data));
Ok(hook_ptr)
} else {
Err(err)
@ -442,7 +455,7 @@ impl<'a> UnicornHandle<'a> {
}
/// Add a memory hook.
pub fn add_mem_hook<F: 'static>(
pub fn add_mem_hook<F: 'a>(
&mut self,
hook_type: HookType,
begin: u64,
@ -450,33 +463,32 @@ impl<'a> UnicornHandle<'a> {
callback: F,
) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle, MemType, u64, usize, i64),
F: FnMut(&mut Unicorn<D>, MemType, u64, usize, i64) -> bool,
{
if !(HookType::MEM_ALL | HookType::MEM_READ_AFTER).contains(hook_type) {
return Err(uc_error::ARG);
}
let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::MemHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback),
let mut user_data = Box::new(ffi::UcHook {
callback,
phantom: PhantomData::<&D>,
});
let err = unsafe {
ffi::uc_hook_add(
self.inner.uc,
self.uc,
&mut hook_ptr,
hook_type,
ffi::mem_hook_proxy as _,
ffi::mem_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _,
begin,
end,
)
};
if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() }
.mem_hooks
.insert(hook_ptr, user_data);
self.hooks.push((hook_ptr, user_data));
Ok(hook_ptr)
} else {
Err(err)
@ -484,31 +496,30 @@ impl<'a> UnicornHandle<'a> {
}
/// Add an interrupt hook.
pub fn add_intr_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
pub fn add_intr_hook<F: 'a>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle, u32),
F: FnMut(&mut Unicorn<D>, u32),
{
let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::InterruptHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback),
let mut user_data = Box::new(ffi::UcHook {
callback,
phantom: PhantomData::<&D>,
});
let err = unsafe {
ffi::uc_hook_add(
self.inner.uc,
self.uc,
&mut hook_ptr,
HookType::INTR,
ffi::intr_hook_proxy as _,
ffi::intr_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _,
0,
0,
)
};
if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() }
.intr_hooks
.insert(hook_ptr, user_data);
self.hooks.push((hook_ptr, user_data));
Ok(hook_ptr)
} else {
Err(err)
@ -516,22 +527,22 @@ impl<'a> UnicornHandle<'a> {
}
/// Add hook for x86 IN instruction.
pub fn add_insn_in_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
pub fn add_insn_in_hook<F: 'a>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle, u32, usize),
F: FnMut(&mut Unicorn<D>, u32, usize) + 'a,
{
let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::InstructionInHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback),
let mut user_data = Box::new(ffi::UcHook {
callback,
phantom: PhantomData::<&D>,
});
let err = unsafe {
ffi::uc_hook_add(
self.inner.uc,
self.uc,
&mut hook_ptr,
HookType::INSN,
ffi::insn_in_hook_proxy as _,
ffi::insn_in_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _,
0,
0,
@ -539,9 +550,8 @@ impl<'a> UnicornHandle<'a> {
)
};
if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() }
.insn_in_hooks
.insert(hook_ptr, user_data);
self.hooks.push((hook_ptr, user_data));
Ok(hook_ptr)
} else {
Err(err)
@ -549,22 +559,22 @@ impl<'a> UnicornHandle<'a> {
}
/// Add hook for x86 OUT instruction.
pub fn add_insn_out_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
pub fn add_insn_out_hook<F: 'a>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle, u32, usize, u32),
F: FnMut(&mut Unicorn<D>, u32, usize, u32) + 'a,
{
let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::InstructionOutHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback),
let mut user_data = Box::new(ffi::UcHook {
callback,
phantom: PhantomData::<&D>,
});
let err = unsafe {
ffi::uc_hook_add(
self.inner.uc,
self.uc,
&mut hook_ptr,
HookType::INSN,
ffi::insn_out_hook_proxy as _,
ffi::insn_out_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _,
0,
0,
@ -572,9 +582,8 @@ impl<'a> UnicornHandle<'a> {
)
};
if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() }
.insn_out_hooks
.insert(hook_ptr, user_data);
self.hooks.push((hook_ptr, user_data));
Ok(hook_ptr)
} else {
Err(err)
@ -582,7 +591,7 @@ impl<'a> UnicornHandle<'a> {
}
/// Add hook for x86 SYSCALL or SYSENTER.
pub fn add_insn_sys_hook<F: 'static>(
pub fn add_insn_sys_hook<F>(
&mut self,
insn_type: x86::InsnSysX86,
begin: u64,
@ -590,20 +599,20 @@ impl<'a> UnicornHandle<'a> {
callback: F,
) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle),
F: FnMut(&mut Unicorn<D>) + 'a,
{
let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::InstructionSysHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback),
let mut user_data = Box::new(ffi::UcHook {
callback,
phantom: PhantomData::<&D>,
});
let err = unsafe {
ffi::uc_hook_add(
self.inner.uc,
self.uc,
&mut hook_ptr,
HookType::INSN,
ffi::insn_sys_hook_proxy as _,
ffi::insn_sys_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _,
begin,
end,
@ -611,9 +620,8 @@ impl<'a> UnicornHandle<'a> {
)
};
if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() }
.insn_sys_hooks
.insert(hook_ptr, user_data);
self.hooks.push((hook_ptr, user_data));
Ok(hook_ptr)
} else {
Err(err)
@ -624,50 +632,13 @@ impl<'a> UnicornHandle<'a> {
///
/// `hook` is the value returned by `add_*_hook` functions.
pub fn remove_hook(&mut self, hook: ffi::uc_hook) -> Result<(), uc_error> {
let handle = unsafe { self.inner.as_mut().get_unchecked_mut() };
let err: uc_error;
let mut in_one_hashmap = false;
if handle.code_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.code_hooks.remove(&hook);
}
// drop the hook
self.hooks
.retain(|(hook_ptr, _hook_impl)| hook_ptr != &hook);
if handle.mem_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.mem_hooks.remove(&hook);
}
if handle.block_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.block_hooks.remove(&hook);
}
if handle.intr_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.intr_hooks.remove(&hook);
}
if handle.insn_in_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.insn_in_hooks.remove(&hook);
}
if handle.insn_out_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.insn_out_hooks.remove(&hook);
}
if handle.insn_sys_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.insn_sys_hooks.remove(&hook);
}
if in_one_hashmap {
err = unsafe { ffi::uc_hook_del(handle.uc, hook) };
} else {
err = uc_error::HOOK;
}
err = unsafe { ffi::uc_hook_del(self.uc, hook) };
if err == uc_error::OK {
Ok(())
@ -678,10 +649,10 @@ impl<'a> UnicornHandle<'a> {
/// Allocate and return an empty Unicorn context.
///
/// To be populated via context_save.
/// To be populated via `context_save`.
pub fn context_alloc(&self) -> Result<Context, uc_error> {
let mut empty_context: ffi::uc_context = Default::default();
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut empty_context) };
let err = unsafe { ffi::uc_context_alloc(self.uc, &mut empty_context) };
if err == uc_error::OK {
Ok(Context {
context: empty_context,
@ -693,7 +664,7 @@ impl<'a> UnicornHandle<'a> {
/// Save current Unicorn context to previously allocated Context struct.
pub fn context_save(&self, context: &mut Context) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_context_save(self.inner.uc, context.context) };
let err = unsafe { ffi::uc_context_save(self.uc, context.context) };
if err == uc_error::OK {
Ok(())
} else {
@ -703,16 +674,16 @@ impl<'a> UnicornHandle<'a> {
/// Allocate and return a Context struct initialized with the current CPU context.
///
/// This can be used for fast rollbacks with context_restore.
/// In case of many non-concurrent context saves, use context_alloc and *_save
/// This can be used for fast rollbacks with `context_restore`.
/// In case of many non-concurrent context saves, use `context_alloc` and *_save
/// individually to avoid unnecessary allocations.
pub fn context_init(&self) -> Result<Context, uc_error> {
let mut new_context: ffi::uc_context = Default::default();
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut new_context) };
let err = unsafe { ffi::uc_context_alloc(self.uc, &mut new_context) };
if err != uc_error::OK {
return Err(err);
}
let err = unsafe { ffi::uc_context_save(self.inner.uc, new_context) };
let err = unsafe { ffi::uc_context_save(self.uc, new_context) };
if err == uc_error::OK {
Ok(Context {
context: new_context,
@ -729,7 +700,7 @@ impl<'a> UnicornHandle<'a> {
/// internal metadata. Contexts may not be shared across engine instances with
/// differing arches or modes. Memory has to be restored manually, if needed.
pub fn context_restore(&self, context: &Context) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_context_restore(self.inner.uc, context.context) };
let err = unsafe { ffi::uc_context_restore(self.uc, context.context) };
if err == uc_error::OK {
Ok(())
} else {
@ -750,11 +721,15 @@ impl<'a> UnicornHandle<'a> {
timeout: u64,
count: usize,
) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_emu_start(self.inner.uc, begin, until, timeout, count as _) };
if err == uc_error::OK {
Ok(())
} else {
Err(err)
unsafe {
ffi::uc_set_data_ptr(self.uc, self as *mut _ as *mut _);
let err = ffi::uc_emu_start(self.uc, begin, until, timeout, count as _);
ffi::uc_set_data_ptr(self.uc, ptr::null_mut());
if err == uc_error::OK {
Ok(())
} else {
Err(err)
}
}
}
@ -763,7 +738,7 @@ impl<'a> UnicornHandle<'a> {
/// This is usually called from callback function in hooks.
/// NOTE: For now, this will stop the execution only after the current block.
pub fn emu_stop(&mut self) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_emu_stop(self.inner.uc) };
let err = unsafe { ffi::uc_emu_stop(self.uc) };
if err == uc_error::OK {
Ok(())
} else {
@ -773,14 +748,46 @@ impl<'a> UnicornHandle<'a> {
/// Query the internal status of the engine.
///
/// supported: MODE, PAGE_SIZE, ARCH
/// supported: `MODE`, `PAGE_SIZE`, `ARCH`
pub fn query(&self, query: Query) -> Result<usize, uc_error> {
let mut result: libc::size_t = Default::default();
let err = unsafe { ffi::uc_query(self.inner.uc, query, &mut result) };
let err = unsafe { ffi::uc_query(self.uc, query, &mut result) };
if err == uc_error::OK {
Ok(result)
} else {
Err(err)
}
}
/// Gets the current program counter for this `unicorn` instance.
#[inline]
pub fn pc_read(&self) -> Result<u64, uc_error> {
let arch = self.get_arch();
let reg = match arch {
Arch::X86 => RegisterX86::RIP as i32,
Arch::ARM => RegisterARM::PC as i32,
Arch::ARM64 => RegisterARM64::PC as i32,
Arch::MIPS => RegisterMIPS::PC as i32,
Arch::SPARC => RegisterSPARC::PC as i32,
Arch::M68K => RegisterM68K::PC as i32,
_ => panic!("Arch pc not yet know to the unicorn rust bindings"),
};
self.reg_read(reg)
}
/// Sets the program counter for this `unicorn` instance.
#[inline]
pub fn set_pc(&mut self, value: u64) -> Result<(), uc_error> {
let arch = self.get_arch();
let reg = match arch {
Arch::X86 => RegisterX86::RIP as i32,
Arch::ARM => RegisterARM::PC as i32,
Arch::ARM64 => RegisterARM64::PC as i32,
Arch::MIPS => RegisterMIPS::PC as i32,
Arch::SPARC => RegisterSPARC::PC as i32,
Arch::M68K => RegisterM68K::PC as i32,
_ => panic!("Arch not yet known to the unicorn rust bindings"),
};
self.reg_write(reg, value)
}
}