diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 9c020538..23482b93 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -10,7 +10,7 @@ include = [ "/Cargo.toml", "/README.md", "/src/*", - "build.rs" + "build.rs", ] license = "GPL-2.0" readme = "README.md" diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index 9900870d..195e9e0d 100644 --- a/bindings/rust/build.rs +++ b/bindings/rust/build.rs @@ -1,11 +1,11 @@ use bytes::Buf; use flate2::read::GzDecoder; use reqwest::header::USER_AGENT; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::{env, process::Command}; use tar::Archive; -fn find_unicorn(unicorn_dir: &PathBuf) -> Option { +fn find_unicorn(unicorn_dir: &Path) -> Option { for entry in std::fs::read_dir(unicorn_dir).ok()? { let entry = entry.unwrap(); let path = entry.path(); @@ -49,6 +49,7 @@ fn download_unicorn() -> Option { } } +#[allow(clippy::branches_sharing_code)] fn main() { let profile = env::var("PROFILE").unwrap(); diff --git a/bindings/rust/src/arm.rs b/bindings/rust/src/arm.rs index 6d72cefe..ac56b7f8 100644 --- a/bindings/rust/src/arm.rs +++ b/bindings/rust/src/arm.rs @@ -164,3 +164,9 @@ impl RegisterARM { pub const FP: RegisterARM = RegisterARM::R11; pub const IP: RegisterARM = RegisterARM::R12; } + +impl From for i32 { + fn from(r: RegisterARM) -> Self { + r as i32 + } +} diff --git a/bindings/rust/src/arm64.rs b/bindings/rust/src/arm64.rs index 299b1bbf..eb0a2647 100644 --- a/bindings/rust/src/arm64.rs +++ b/bindings/rust/src/arm64.rs @@ -1,9 +1,7 @@ -#![allow(non_camel_case_types)] -// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - // ARM64 registers #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(non_camel_case_types)] pub enum RegisterARM64 { INVALID = 0, X29 = 1, @@ -319,3 +317,9 @@ impl RegisterARM64 { pub const FP: RegisterARM64 = RegisterARM64::X29; pub const LR: RegisterARM64 = RegisterARM64::X30; } + +impl From for i32 { + fn from(r: RegisterARM64) -> Self { + r as i32 + } +} diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs index 3252a3bd..a309c519 100644 --- a/bindings/rust/src/ffi.rs +++ b/bindings/rust/src/ffi.rs @@ -1,10 +1,11 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] -use super::unicorn_const::*; +use crate::Unicorn; + +use super::unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Query}; use libc::{c_char, c_int}; -use std::ffi::c_void; -use std::pin::Pin; +use std::{ffi::c_void, marker::PhantomData}; pub type uc_handle = *mut c_void; pub type uc_hook = *mut c_void; @@ -75,156 +76,120 @@ extern "C" { pub fn uc_context_alloc(engine: uc_handle, context: *mut uc_context) -> uc_error; pub fn uc_context_save(engine: uc_handle, context: uc_context) -> uc_error; pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error; + pub fn uc_set_data_ptr(engine: uc_handle, ptr: *mut c_void) -> uc_error; + pub fn uc_get_data_ptr(engine: uc_handle) -> *mut c_void; } -pub struct CodeHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, +pub struct UcHook<'a, D: 'a, F: 'a> { + pub callback: F, + pub phantom: PhantomData<&'a D>, } -pub struct BlockHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, +pub trait IsUcHook<'a> {} + +impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {} + +fn read_uc_from_uc_handle<'a, D>(uc: uc_handle) -> &'a mut crate::Unicorn<'a, D> +where + D: 'a, +{ + unsafe { + (uc_get_data_ptr(uc) as *mut Unicorn<'a, D>) + .as_mut() + .unwrap() + } } -pub struct MemHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} - -pub struct InterruptHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} - -pub struct InstructionInHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} - -pub struct InstructionOutHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} - -pub struct InstructionSysHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} - -pub extern "C" fn code_hook_proxy( +pub extern "C" fn code_hook_proxy( uc: uc_handle, address: u64, size: u32, - user_data: *mut CodeHook, -) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u64, u32), +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - address, - size, - ); + callback(unicorn, address, size); } -pub extern "C" fn block_hook_proxy( +pub extern "C" fn block_hook_proxy( uc: uc_handle, address: u64, size: u32, - user_data: *mut BlockHook, -) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u64, u32), +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - address, - size, - ); + callback(unicorn, address, size); } -pub extern "C" fn mem_hook_proxy( +pub extern "C" fn mem_hook_proxy( uc: uc_handle, mem_type: MemType, address: u64, size: u32, value: i64, - user_data: *mut MemHook, -) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; + user_data: *mut UcHook, +) -> bool +where + F: FnMut(&mut crate::Unicorn, MemType, u64, usize, i64) -> bool, +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - mem_type, - address, - size as usize, - value, - ); + callback(unicorn, mem_type, address, size as usize, value) } -pub extern "C" fn intr_hook_proxy(uc: uc_handle, value: u32, user_data: *mut InterruptHook) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; +pub extern "C" fn intr_hook_proxy(uc: uc_handle, value: u32, user_data: *mut UcHook) +where + F: FnMut(&mut crate::Unicorn, u32), +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - value, - ); + callback(unicorn, value); } -pub extern "C" fn insn_in_hook_proxy( +pub extern "C" fn insn_in_hook_proxy( uc: uc_handle, port: u32, size: usize, - user_data: *mut InstructionInHook, -) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u32, usize), +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - port, - size, - ); + callback(unicorn, port, size); } -pub extern "C" fn insn_out_hook_proxy( +pub extern "C" fn insn_out_hook_proxy( uc: uc_handle, port: u32, size: usize, value: u32, - user_data: *mut InstructionOutHook, -) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u32, usize, u32), +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - port, - size, - value, - ); + callback(unicorn, port, size, value); } -pub extern "C" fn insn_sys_hook_proxy(uc: uc_handle, user_data: *mut InstructionSysHook) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; +pub extern "C" fn insn_sys_hook_proxy(uc: uc_handle, user_data: *mut UcHook) +where + F: FnMut(&mut crate::Unicorn), +{ + let unicorn = read_uc_from_uc_handle(uc); assert_eq!(uc, unicorn.uc); - callback(crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }); + let callback = unsafe { &mut (*user_data).callback }; + callback(unicorn); } diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 99e1531c..6d007edc 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -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 = 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>, -} - -#[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>, - pub block_hooks: HashMap<*mut libc::c_void, Box>, - pub mem_hooks: HashMap<*mut libc::c_void, Box>, - pub intr_hooks: HashMap<*mut libc::c_void, Box>, - pub insn_in_hooks: HashMap<*mut libc::c_void, Box>, - pub insn_out_hooks: HashMap<*mut libc::c_void, Box>, - pub insn_sys_hooks: HashMap<*mut libc::c_void, Box>, - _pin: PhantomPinned, + /// to keep ownership over the hook for this uc instance's lifetime + pub hooks: Vec<(ffi::uc_hook, Box + '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 { + pub fn new(arch: Arch, mode: Mode) -> Result, 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, 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, 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, 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, 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>(&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>(&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>(&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>(&self, regid: T) -> Result { 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>(&self, regid: T) -> Result, 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>(&self, regid: T) -> Result { 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( + pub fn add_code_hook( &mut self, begin: u64, end: u64, callback: F, ) -> Result where - F: FnMut(UnicornHandle, u64, u32), + F: FnMut(&mut crate::Unicorn, 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:: 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(&mut self, callback: F) -> Result + pub fn add_block_hook(&mut self, callback: F) -> Result where - F: FnMut(UnicornHandle, u64, u32), + F: FnMut(&mut Unicorn, 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:: 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( + pub fn add_mem_hook( &mut self, hook_type: HookType, begin: u64, @@ -450,33 +463,32 @@ impl<'a> UnicornHandle<'a> { callback: F, ) -> Result where - F: FnMut(UnicornHandle, MemType, u64, usize, i64), + F: FnMut(&mut Unicorn, 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:: 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(&mut self, callback: F) -> Result + pub fn add_intr_hook(&mut self, callback: F) -> Result where - F: FnMut(UnicornHandle, u32), + F: FnMut(&mut Unicorn, 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:: 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(&mut self, callback: F) -> Result + pub fn add_insn_in_hook(&mut self, callback: F) -> Result where - F: FnMut(UnicornHandle, u32, usize), + F: FnMut(&mut Unicorn, 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:: 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(&mut self, callback: F) -> Result + pub fn add_insn_out_hook(&mut self, callback: F) -> Result where - F: FnMut(UnicornHandle, u32, usize, u32), + F: FnMut(&mut Unicorn, 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:: 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( + pub fn add_insn_sys_hook( &mut self, insn_type: x86::InsnSysX86, begin: u64, @@ -590,20 +599,20 @@ impl<'a> UnicornHandle<'a> { callback: F, ) -> Result where - F: FnMut(UnicornHandle), + F: FnMut(&mut Unicorn) + '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:: 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 { 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 { 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 { 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 { + 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) + } } diff --git a/bindings/rust/src/m68k.rs b/bindings/rust/src/m68k.rs index 54c62920..097cd4ec 100644 --- a/bindings/rust/src/m68k.rs +++ b/bindings/rust/src/m68k.rs @@ -21,5 +21,10 @@ pub enum RegisterM68K { D7, SR, PC, - ENDING, +} + +impl From for i32 { + fn from(r: RegisterM68K) -> Self { + r as i32 + } } diff --git a/bindings/rust/src/mips.rs b/bindings/rust/src/mips.rs index 5f462af0..77002f82 100644 --- a/bindings/rust/src/mips.rs +++ b/bindings/rust/src/mips.rs @@ -245,3 +245,9 @@ impl RegisterMIPS { pub const LO2: RegisterMIPS = RegisterMIPS::AC2; pub const LO3: RegisterMIPS = RegisterMIPS::AC3; } + +impl From for i32 { + fn from(r: RegisterMIPS) -> Self { + r as i32 + } +} diff --git a/bindings/rust/src/ppc.rs b/bindings/rust/src/ppc.rs index a28827d3..ba633565 100644 --- a/bindings/rust/src/ppc.rs +++ b/bindings/rust/src/ppc.rs @@ -40,3 +40,9 @@ pub enum RegisterPPC { GPR30 = 32, GPR31 = 33, } + +impl From for i32 { + fn from(r: RegisterPPC) -> Self { + r as i32 + } +} diff --git a/bindings/rust/src/riscv.rs b/bindings/rust/src/riscv.rs index ca91c350..1d7e0db1 100644 --- a/bindings/rust/src/riscv.rs +++ b/bindings/rust/src/riscv.rs @@ -211,3 +211,9 @@ impl RegisterRISCV { pub const FT10: RegisterRISCV = RegisterRISCV::F30; pub const FT11: RegisterRISCV = RegisterRISCV::F31; } + +impl From for i32 { + fn from(r: RegisterRISCV) -> Self { + r as i32 + } +} diff --git a/bindings/rust/src/sparc.rs b/bindings/rust/src/sparc.rs index 6c6892e6..f7295750 100644 --- a/bindings/rust/src/sparc.rs +++ b/bindings/rust/src/sparc.rs @@ -1,8 +1,7 @@ -// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - // SPARC registers #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub enum RegisterSPARC { INVALID = 0, F0 = 1, @@ -93,13 +92,10 @@ pub enum RegisterSPARC { Y = 86, XCC = 87, PC = 88, - ENDING = 89, } -impl RegisterSPARC { - // alias registers - // (assoc) O6 = 84, - // (assoc) I6 = 67, - pub const O6: RegisterSPARC = RegisterSPARC::SP; - pub const I6: RegisterSPARC = RegisterSPARC::FP; +impl From for i32 { + fn from(r: RegisterSPARC) -> Self { + r as i32 + } } diff --git a/bindings/rust/src/unicorn_const.rs b/bindings/rust/src/unicorn_const.rs index 9cb14da7..01975bdf 100644 --- a/bindings/rust/src/unicorn_const.rs +++ b/bindings/rust/src/unicorn_const.rs @@ -11,6 +11,7 @@ pub const MILISECOND_SCALE: u64 = 1_000; #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub enum uc_error { OK = 0, NOMEM = 1, @@ -89,6 +90,7 @@ bitflags! { #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub enum Query { MODE = 1, PAGE_SIZE = 2, diff --git a/bindings/rust/src/x86.rs b/bindings/rust/src/x86.rs index 7ed65230..944ec250 100644 --- a/bindings/rust/src/x86.rs +++ b/bindings/rust/src/x86.rs @@ -1,9 +1,7 @@ -#![allow(non_camel_case_types)] -// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - // X86 registers #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms, non_camel_case_types)] pub enum RegisterX86 { INVALID = 0, AH = 1, @@ -242,8 +240,15 @@ pub enum RegisterX86 { ENDING = 234, } +impl From for i32 { + fn from(r: RegisterX86) -> Self { + r as i32 + } +} + #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub enum InsnX86 { IN = 218, OUT = 500, @@ -254,6 +259,7 @@ pub enum InsnX86 { #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub enum InsnSysX86 { SYSCALL = InsnX86::SYSCALL as isize, SYSENTER = InsnX86::SYSENTER as isize, diff --git a/bindings/rust/tests/unicorn.rs b/bindings/rust/tests/unicorn.rs index aaf208b3..80dc68c3 100644 --- a/bindings/rust/tests/unicorn.rs +++ b/bindings/rust/tests/unicorn.rs @@ -32,6 +32,7 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::EDX, RegisterX86::EFLAGS, RegisterX86::EIP, + RegisterX86::EIZ, RegisterX86::ES, RegisterX86::ESI, RegisterX86::ESP, @@ -46,6 +47,7 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::RDI, RegisterX86::RDX, RegisterX86::RIP, + RegisterX86::RIZ, RegisterX86::RSI, RegisterX86::RSP, RegisterX86::SI, @@ -58,7 +60,17 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::CR2, RegisterX86::CR3, RegisterX86::CR4, + RegisterX86::CR5, + RegisterX86::CR6, + RegisterX86::CR7, RegisterX86::CR8, + RegisterX86::CR9, + RegisterX86::CR10, + RegisterX86::CR11, + RegisterX86::CR12, + RegisterX86::CR13, + RegisterX86::CR14, + RegisterX86::CR15, RegisterX86::DR0, RegisterX86::DR1, RegisterX86::DR2, @@ -67,6 +79,14 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::DR5, RegisterX86::DR6, RegisterX86::DR7, + RegisterX86::DR8, + RegisterX86::DR9, + RegisterX86::DR10, + RegisterX86::DR11, + RegisterX86::DR12, + RegisterX86::DR13, + RegisterX86::DR14, + RegisterX86::DR15, RegisterX86::FP0, RegisterX86::FP1, RegisterX86::FP2, @@ -133,15 +153,12 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::R15W, ]; -type Unicorn<'a> = unicorn_engine::UnicornHandle<'a>; - #[test] fn emulate_x86() { let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, 0) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 123), Ok(())); assert_eq!(emu.reg_read(RegisterX86::EAX as i32), Ok(123)); @@ -183,16 +200,15 @@ fn x86_code_callback() { let codes_cell = Rc::new(RefCell::new(codes)); let callback_codes = codes_cell.clone(); - let callback = move |_: Unicorn<'_>, address: u64, size: u32| { + let callback = move |_: &mut Unicorn<'_, ()>, address: u64, size: u32| { let mut codes = callback_codes.borrow_mut(); codes.push(CodeExpectation(address, size)); }; let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -215,15 +231,14 @@ fn x86_intr_callback() { let intr_cell = Rc::new(RefCell::new(IntrExpectation(0))); let callback_intr = intr_cell.clone(); - let callback = move |_: Unicorn<'_>, intno: u32| { + let callback = move |_: &mut Unicorn<'_, ()>, intno: u32| { *callback_intr.borrow_mut() = IntrExpectation(intno); }; let x86_code32: Vec = vec![0xcd, 0x80]; // INT 0x80; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -251,22 +266,16 @@ fn x86_mem_callback() { let expects = vec![ MemExpectation(MemType::WRITE, 0x2000, 4, 0xdeadbeef), MemExpectation(MemType::READ_UNMAPPED, 0x10000, 4, 0), - MemExpectation(MemType::READ, 0x10000, 4, 0), ]; let mems: Vec = Vec::new(); let mems_cell = Rc::new(RefCell::new(mems)); let callback_mems = mems_cell.clone(); let callback = - move |uc: Unicorn<'_>, mem_type: MemType, address: u64, size: usize, value: i64| { + move |_: &mut Unicorn<'_, ()>, mem_type: MemType, address: u64, size: usize, value: i64| { let mut mems = callback_mems.borrow_mut(); - let mut uc = uc; - mems.push(MemExpectation(mem_type, address, size, value)); - - if mem_type == MemType::READ_UNMAPPED { - uc.mem_map(address, 0x1000, Permission::ALL).unwrap(); - } + true }; // mov eax, 0xdeadbeef; @@ -276,9 +285,8 @@ fn x86_mem_callback() { 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xA3, 0x00, 0x20, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x01, 0x00, ]; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -293,7 +301,7 @@ fn x86_mem_callback() { 10 * SECOND_SCALE, 0x1000 ), - Ok(()) + Err(uc_error::READ_UNMAPPED) ); assert_eq!(expects, *mems_cell.borrow()); @@ -308,15 +316,14 @@ fn x86_insn_in_callback() { let insn_cell = Rc::new(RefCell::new(InsnInExpectation(0, 0))); let callback_insn = insn_cell.clone(); - let callback = move |_: Unicorn<'_>, port: u32, size: usize| { + let callback = move |_: &mut Unicorn<'_, ()>, port: u32, size: usize| { *callback_insn.borrow_mut() = InsnInExpectation(port, size); }; let x86_code32: Vec = vec![0xe5, 0x10]; // IN eax, 0x10; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -345,15 +352,14 @@ fn x86_insn_out_callback() { let insn_cell = Rc::new(RefCell::new(InsnOutExpectation(0, 0, 0))); let callback_insn = insn_cell.clone(); - let callback = move |_: Unicorn<'_>, port: u32, size: usize, value: u32| { + let callback = move |_: &mut Unicorn<'_, ()>, port: u32, size: usize, value: u32| { *callback_insn.borrow_mut() = InsnOutExpectation(port, size, value); }; let x86_code32: Vec = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -382,7 +388,7 @@ fn x86_insn_sys_callback() { let insn_cell = Rc::new(RefCell::new(InsnSysExpectation(0))); let callback_insn = insn_cell.clone(); - let callback = move |uc: Unicorn<'_>| { + let callback = move |uc: &mut Unicorn<'_, ()>| { println!("!!!!"); let rax = uc.reg_read(RegisterX86::RAX as i32).unwrap(); *callback_insn.borrow_mut() = InsnSysExpectation(rax); @@ -393,9 +399,8 @@ fn x86_insn_sys_callback() { 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, ]; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_64) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_64, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); @@ -420,9 +425,8 @@ fn x86_insn_sys_callback() { fn emulate_arm() { let arm_code32: Vec = vec![0x83, 0xb0]; // sub sp, #0xc - let mut unicorn = unicorn_engine::Unicorn::new(Arch::ARM, Mode::THUMB) + let mut emu = unicorn_emulator::Unicorn::new(Arch::ARM, Mode::THUMB, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.reg_write(RegisterARM::R1 as i32, 123), Ok(())); assert_eq!(emu.reg_read(RegisterARM::R1 as i32), Ok(123)); @@ -461,9 +465,8 @@ fn emulate_arm() { fn emulate_mips() { let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::MIPS, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::MIPS, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &mips_code32), Ok(())); assert_eq!( @@ -487,9 +490,8 @@ fn emulate_mips() { fn emulate_ppc() { let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3 - let mut unicorn = unicorn_engine::Unicorn::new(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN) + let mut emu = unicorn_emulator::Unicorn::new(Arch::PPC, Mode::PPC32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &ppc_code32), Ok(())); assert_eq!( @@ -512,9 +514,8 @@ fn emulate_ppc() { #[test] fn mem_unmapping() { - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(())); } @@ -525,9 +526,8 @@ fn mem_map_ptr() { let mut mem: [u8; 4000] = [0; 4000]; let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); // Attempt to write to memory before mapping it. assert_eq!( @@ -536,7 +536,7 @@ fn mem_map_ptr() { ); assert_eq!( - emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _), + unsafe { emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _) }, Ok(()) ); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -572,7 +572,7 @@ fn mem_map_ptr() { ); assert_eq!( - emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _), + unsafe { emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _) }, Ok(()) ); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -600,13 +600,12 @@ fn mem_map_ptr() { #[test] fn x86_context_save_and_restore() { - for mode in vec![Mode::MODE_32, Mode::MODE_64] { + for mode in &[Mode::MODE_32, Mode::MODE_64] { let x86_code: Vec = vec![ 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, ]; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, mode) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, *mode, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); let _ = emu.emu_start( @@ -621,9 +620,8 @@ fn x86_context_save_and_restore() { let context = context.unwrap(); /* and create a new emulator, into which we will "restore" that context */ - let mut unicorn2 = unicorn_engine::Unicorn::new(Arch::X86, mode) + let emu2 = unicorn_emulator::Unicorn::new(Arch::X86, *mode, ()) .expect("failed to initialize unicorn instance"); - let emu2 = unicorn2.borrow(); assert_eq!(emu2.context_restore(&context), Ok(())); for register in X86_REGISTERS.iter() { println!("Testing register {:?}", register); @@ -644,16 +642,15 @@ fn x86_block_callback() { let blocks_cell = Rc::new(RefCell::new(blocks)); let callback_blocks = blocks_cell.clone(); - let callback = move |_: Unicorn<'_>, address: u64, size: u32| { + let callback = move |_: &mut Unicorn<'_, ()>, address: u64, size: u32| { let mut blocks = callback_blocks.borrow_mut(); blocks.push(BlockExpectation(address, size)); }; let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); diff --git a/include/uc_priv.h b/include/uc_priv.h index 9d88c233..debd85f8 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -342,6 +342,7 @@ struct uc_struct { bool no_exit_request; // Disable check_exit_request temporarily. A // workaround to treat the IT block as a whole block. bool init_done; // Whether the initialization is done. + void *data_ptr; // optional data pointer for bindings }; // Metadata stub for the variable-size cpu context used with uc_context_*() diff --git a/uc.c b/uc.c index ecb954cc..e40901c2 100644 --- a/uc.c +++ b/uc.c @@ -686,6 +686,27 @@ static void clear_deleted_hooks(uc_engine *uc) list_clear(&uc->hooks_to_del); } +// set a data ptr (for use in bindings) +UNICORN_EXPORT +uc_err uc_emu_set_data_ptr(uc_engine *uc, void *data) +{ + if (uc == NULL) { + return UC_ERR_ARG; + } + uc->data_ptr = data; +} + +// get a data ptr (for use in bindings) +UNICORN_EXPORT +void *uc_emu_get_data_ptr(uc_engine *uc, void *data) +{ + if (uc == NULL) { + return NULL; + } + return uc->data_ptr; +} + + UNICORN_EXPORT uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count)