diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 9c020538a7..7aa9ca73b4 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -3,14 +3,14 @@ name = "unicorn-engine" version = "2.0.0-rc3" authors = ["Ziqiao Kong", "Lukas Seidel"] documentation = "https://github.com/unicorn-engine/unicorn/wiki" -edition = "2018" +edition = "2021" include = [ "/.gitmodules", "/COPYING", "/Cargo.toml", "/README.md", "/src/*", - "build.rs" + "build.rs", ] license = "GPL-2.0" readme = "README.md" diff --git a/bindings/rust/README.md b/bindings/rust/README.md index dba1d53c3d..d855cea3a8 100644 --- a/bindings/rust/README.md +++ b/bindings/rust/README.md @@ -5,23 +5,23 @@ Rust bindings for the [Unicorn](http://www.unicorn-engine.org/) emulator with ut Checkout Unicorn2 source code at [dev branch](https://github.com/unicorn-engine/unicorn/tree/dev). ```rust -use unicorn_engine::RegisterARM; +use unicorn_engine::{Unicorn, RegisterARM}; use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE}; fn main() { 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"); + let mut unicorn = Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); let mut emu = unicorn.borrow(); emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page"); emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions"); - emu.reg_write(RegisterARM::R0 as i32, 123).expect("failed write R0"); - emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5"); + emu.reg_write(RegisterARM::R0, 123).expect("failed write R0"); + emu.reg_write(RegisterARM::R5, 1337).expect("failed write R5"); let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000); - assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(100)); - assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337)); + assert_eq!(emu.reg_read(RegisterARM::R0, Ok(100)); + assert_eq!(emu.reg_read(RegisterARM::R5, Ok(1337)); } ``` Further sample code can be found in ```tests/unicorn.rs```. diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index 9900870d31..195e9e0d0c 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 6d72cefea0..ac56b7f85f 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 299b1bbf14..eb0a264734 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 3252a3bda8..300e12206b 100644 --- a/bindings/rust/src/ffi.rs +++ b/bindings/rust/src/ffi.rs @@ -1,14 +1,15 @@ #![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 core::ffi::c_void; use libc::{c_char, c_int}; -use std::ffi::c_void; -use std::pin::Pin; pub type uc_handle = *mut c_void; pub type uc_hook = *mut c_void; -pub type uc_context = libc::size_t; +pub type uc_context = *mut c_void; extern "C" { pub fn uc_version(major: *mut u32, minor: *mut u32) -> u32; @@ -77,154 +78,98 @@ extern "C" { pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error; } -pub struct CodeHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} - -pub struct BlockHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, +pub struct UcHook<'a, D: 'a, F: 'a> { + pub callback: F, + pub uc: Unicorn<'a, D>, } -pub struct MemHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} - -pub struct InterruptHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} +pub trait IsUcHook<'a> {} -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, -} +impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {} -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 }; - assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - address, - size, - ); + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u64, u32), +{ + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, 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 }; - assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - address, - size, - ); + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u64, u32), +{ + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, 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 }; - assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - mem_type, - address, - size as usize, - value, - ); + user_data: *mut UcHook, +) -> bool +where + F: FnMut(&mut crate::Unicorn, MemType, u64, usize, i64) -> bool, +{ + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, 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 }; - assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - value, - ); +pub extern "C" fn intr_hook_proxy(uc: uc_handle, value: u32, user_data: *mut UcHook) +where + F: FnMut(&mut crate::Unicorn, u32), +{ + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, 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 }; - assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - port, - size, - ); + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u32, usize), +{ + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, 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 }; - assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - port, - size, - value, - ); + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u32, usize, u32), +{ + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, 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 }; - assert_eq!(uc, unicorn.uc); - callback(crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }); +pub extern "C" fn insn_sys_hook_proxy(uc: uc_handle, user_data: *mut UcHook) +where + F: FnMut(&mut crate::Unicorn), +{ + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc); } diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 99e1531c65..b2a8225256 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -9,43 +9,49 @@ //! use unicorn_engine::RegisterARM; //! use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE}; //! -//! fn main() { -//! let arm_code32: Vec = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 +//! fn emulate() { +//! let arm_code32 = [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"); -//! let mut emu = unicorn.borrow(); +//! let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); //! emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page"); //! emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions"); //! -//! emu.reg_write(RegisterARM::R0 as i32, 123).expect("failed write R0"); -//! emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5"); +//! emu.reg_write(RegisterARM::R0, 123).expect("failed write R0"); +//! emu.reg_write(RegisterARM::R5, 1337).expect("failed write R5"); //! -//! let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000); -//! assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(100)); -//! assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337)); +//! emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000).unwrap(); +//! assert_eq!(emu.reg_read(RegisterARM::R0), Ok(100)); +//! assert_eq!(emu.reg_read(RegisterARM::R5), Ok(1337)); //! } //! ``` //! -mod ffi; +#![no_std] + +#[macro_use] +extern crate alloc; + pub mod unicorn_const; mod arm; mod arm64; +mod ffi; mod m68k; mod mips; mod ppc; mod riscv; mod sparc; mod x86; -pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, riscv::*, sparc::*, x86::*}; +pub use crate::{ + arm::*, arm64::*, m68k::*, mips::*, ppc::*, riscv::*, sparc::*, unicorn_const::*, x86::*, +}; + +use alloc::{boxed::Box, rc::Rc, vec::Vec}; +use core::{cell::UnsafeCell, ptr}; 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 +59,123 @@ pub struct Context { } impl Context { - pub fn new() -> Self { - Context { context: 0 } - } + #[must_use] pub fn is_initialized(&self) -> bool { - self.context != 0 + !self.context.is_null() } } impl Drop for Context { fn drop(&mut self) { - unsafe { ffi::uc_context_free(self.context) }; + if self.is_initialized() { + unsafe { + ffi::uc_context_free(self.context); + } + } + self.context = ptr::null_mut(); } } -#[derive(Debug)] -/// A Unicorn emulator instance. -pub struct Unicorn { - inner: Pin>, +pub struct UnicornInner<'a, D> { + pub uc: uc_handle, + pub arch: Arch, + /// to keep ownership over the hook for this uc instance's lifetime + pub hooks: Vec<(ffi::uc_hook, Box + 'a>)>, + pub data: D, } -#[derive(Debug)] -/// Handle used to safely access exposed functions and data of a Unicorn instance. -pub struct UnicornHandle<'a> { - inner: Pin<&'a mut UnicornInner>, +/// Drop UC +impl<'a, D> Drop for UnicornInner<'a, D> { + fn drop(&mut self) { + if !self.uc.is_null() { + unsafe { ffi::uc_close(self.uc) }; + } + self.uc = ptr::null_mut(); + } } -/// Internal Management struct -pub struct UnicornInner { - 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, +/// A Unicorn emulator instance. +pub struct Unicorn<'a, D: 'a> { + inner: Rc>>, } -impl Unicorn { +impl<'a> Unicorn<'a, ()> { /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. - pub fn new(arch: Arch, mode: Mode) -> Result { - let mut handle = std::ptr::null_mut(); + 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_with_data(arch: Arch, mode: Mode, data: D) -> Result, uc_error> { + let mut handle = core::ptr::null_mut(); let err = unsafe { ffi::uc_open(arch, mode, &mut handle) }; if err == uc_error::OK { Ok(Unicorn { - inner: Box::pin(UnicornInner { + inner: Rc::new(UnsafeCell::from(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, - }), + arch, + data, + hooks: vec![], + })), }) } else { Err(err) } } +} - pub fn borrow<'a>(&'a mut self) -> UnicornHandle<'a> { - UnicornHandle { - inner: self.inner.as_mut(), - } +impl<'a, D> core::fmt::Debug for Unicorn<'a, D> { + fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(formatter, "Unicorn {{ uc: {:p} }}", self.inner().uc) } } -impl Drop for Unicorn { - fn drop(&mut self) { - unsafe { ffi::uc_close(self.inner.uc) }; +impl<'a, D> Unicorn<'a, D> { + fn inner(&self) -> &UnicornInner<'a, D> { + unsafe { self.inner.get().as_ref().unwrap() } } -} -impl std::fmt::Debug for UnicornInner { - fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, "Unicorn {{ uc: {:p} }}", self.uc) + fn inner_mut(&mut self) -> &mut UnicornInner<'a, D> { + unsafe { self.inner.get().as_mut().unwrap() } + } + + /// 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. + #[must_use] + pub fn get_data(&self) -> &D { + &self.inner().data + } + + /// Return a mutable reference to whatever data was passed during initialization. + #[must_use] + pub fn get_data_mut(&mut self) -> &mut D { + &mut self.inner_mut().data } -} -impl<'a> UnicornHandle<'a> { /// Return the architecture of the current emulator. + #[must_use] pub fn get_arch(&self) -> Arch { - self.inner.arch + self.inner().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 = core::ptr::null_mut(); + let err = unsafe { ffi::uc_mem_regions(self.inner().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 { core::mem::transmute_copy(&*p_regions.add(i as usize)) }); } unsafe { libc::free(p_regions as _) }; Ok(regions) @@ -163,7 +186,8 @@ 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.inner().uc, address, buf.as_mut_ptr(), buf.len()) }; if err == uc_error::OK { Ok(()) } else { @@ -174,7 +198,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.inner().uc, address, buf.as_mut_ptr(), size) }; if err == uc_error::OK { Ok(buf) } else { @@ -183,7 +207,8 @@ 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.inner().uc, address, bytes.as_ptr(), bytes.len()) }; if err == uc_error::OK { Ok(()) } else { @@ -193,6 +218,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 +229,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.inner().uc, address, size, perms.bits(), ptr); if err == uc_error::OK { Ok(()) } else { @@ -227,7 +254,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.inner().uc, address, size, perms.bits()) }; if err == uc_error::OK { Ok(()) } else { @@ -240,7 +267,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.inner().uc, address, size) }; if err == uc_error::OK { Ok(()) } else { @@ -258,7 +285,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.inner().uc, address, size, perms.bits()) }; if err == uc_error::OK { Ok(()) } else { @@ -269,7 +296,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 _) }; + unsafe { ffi::uc_reg_write(self.inner().uc, regid.into(), &value as *const _ as _) }; if err == uc_error::OK { Ok(()) } else { @@ -281,8 +308,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.inner().uc, regid.into(), value.as_ptr() as _) }; if err == uc_error::OK { Ok(()) } else { @@ -296,7 +323,7 @@ impl<'a> UnicornHandle<'a> { 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 _) }; + unsafe { ffi::uc_reg_read(self.inner().uc, regid.into(), &mut value as *mut u64 as _) }; if err == uc_error::OK { Ok(value) } else { @@ -350,7 +377,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.inner().uc, curr_reg_id, value.as_mut_ptr() as _) }; if err == uc_error::OK { boxed = value.into_boxed_slice(); @@ -364,7 +391,7 @@ impl<'a> UnicornHandle<'a> { 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 _) }; + unsafe { ffi::uc_reg_read(self.inner().uc, regid.into(), &mut value as *mut i32 as _) }; if err == uc_error::OK { Ok(value) } else { @@ -373,36 +400,36 @@ 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 hook_ptr = core::ptr::null_mut(); + let mut user_data = Box::new(ffi::UcHook { + callback, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.inner().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.inner_mut().hooks.push((hook_ptr, user_data)); Ok(hook_ptr) } else { Err(err) @@ -410,31 +437,32 @@ 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 hook_ptr = core::ptr::null_mut(); + let mut user_data = Box::new(ffi::UcHook { + callback, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.inner().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.inner_mut().hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -442,7 +470,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 +478,34 @@ 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 hook_ptr = core::ptr::null_mut(); + let mut user_data = Box::new(ffi::UcHook { + callback, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.inner().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.inner_mut().hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -484,31 +513,32 @@ 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 hook_ptr = core::ptr::null_mut(); + let mut user_data = Box::new(ffi::UcHook { + callback, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.inner().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.inner_mut().hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -516,22 +546,24 @@ 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), { - 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 hook_ptr = core::ptr::null_mut(); + let mut user_data = Box::new(ffi::UcHook { + callback, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.inner().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 +571,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.inner_mut().hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -549,22 +580,24 @@ 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), { - 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 hook_ptr = core::ptr::null_mut(); + let mut user_data = Box::new(ffi::UcHook { + callback, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.inner().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 +605,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.inner_mut().hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -582,7 +614,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 +622,22 @@ 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 hook_ptr = core::ptr::null_mut(); + let mut user_data = Box::new(ffi::UcHook { + callback, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.inner().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 +645,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.inner_mut().hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -624,50 +657,14 @@ 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); - } - - 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); - } + // drop the hook + self.inner_mut() + .hooks + .retain(|(hook_ptr, _hook_impl)| hook_ptr != &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.inner().uc, hook) }; if err == uc_error::OK { Ok(()) @@ -678,10 +675,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 mut empty_context: ffi::uc_context = ptr::null_mut(); + let err = unsafe { ffi::uc_context_alloc(self.inner().uc, &mut empty_context) }; if err == uc_error::OK { Ok(Context { context: empty_context, @@ -693,7 +690,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.inner().uc, context.context) }; if err == uc_error::OK { Ok(()) } else { @@ -703,16 +700,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 mut new_context: ffi::uc_context = ptr::null_mut(); + let err = unsafe { ffi::uc_context_alloc(self.inner().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.inner().uc, new_context) }; if err == uc_error::OK { Ok(Context { context: new_context, @@ -729,7 +726,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.inner().uc, context.context) }; if err == uc_error::OK { Ok(()) } else { @@ -750,11 +747,13 @@ 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 { + let err = ffi::uc_emu_start(self.inner().uc, begin, until, timeout, count as _); + if err == uc_error::OK { + Ok(()) + } else { + Err(err) + } } } @@ -763,7 +762,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.inner().uc) }; if err == uc_error::OK { Ok(()) } else { @@ -773,14 +772,50 @@ 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.inner().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, + Arch::PPC => RegisterPPC::PC as i32, + Arch::RISCV => RegisterRISCV::PC as i32, + Arch::MAX => panic!("Illegal Arch specified"), + }; + 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, + Arch::PPC => RegisterPPC::PC as i32, + Arch::RISCV => RegisterRISCV::PC as i32, + Arch::MAX => panic!("Illegal Arch specified"), + }; + self.reg_write(reg, value) + } } diff --git a/bindings/rust/src/m68k.rs b/bindings/rust/src/m68k.rs index 54c6292052..097cd4ec83 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 5f462af03e..77002f8253 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 a28827d3b2..ba63356547 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 ca91c3505e..1d7e0db1f1 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 6c6892e62f..e65b35892a 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, @@ -103,3 +102,9 @@ impl RegisterSPARC { 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 9cb14da739..01975bdf32 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 7ed652308c..944ec250e1 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 aaf208b36d..98ca78e1fb 100644 --- a/bindings/rust/tests/unicorn.rs +++ b/bindings/rust/tests/unicorn.rs @@ -1,9 +1,11 @@ -use std::cell::RefCell; -use std::rc::Rc; +extern crate alloc; + +use alloc::rc::Rc; +use core::cell::RefCell; use unicorn_engine::unicorn_const::{ uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE, }; -use unicorn_engine::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86}; +use unicorn_engine::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86, Unicorn}; pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::AH, @@ -133,17 +135,14 @@ 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_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .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)); + assert_eq!(emu.reg_write(RegisterX86::EAX, 123), Ok(())); + assert_eq!(emu.reg_read(RegisterX86::EAX), Ok(123)); // Attempt to write to memory before mapping it. assert_eq!( @@ -158,8 +157,8 @@ fn emulate_x86() { Ok(x86_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(())); - assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(())); assert_eq!( emu.emu_start( @@ -170,8 +169,8 @@ fn emulate_x86() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11)); - assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49)); + assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11)); + assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49)); } #[test] @@ -183,16 +182,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_engine::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 +213,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_engine::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(())); @@ -257,17 +254,20 @@ fn x86_mem_callback() { 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| { - let mut mems = callback_mems.borrow_mut(); - let mut uc = uc; + let callback = move |uc: &mut Unicorn<'_, ()>, + mem_type: MemType, + address: u64, + size: usize, + value: i64| { + let mut mems = callback_mems.borrow_mut(); - mems.push(MemExpectation(mem_type, address, size, value)); + mems.push(MemExpectation(mem_type, address, size, value)); - if mem_type == MemType::READ_UNMAPPED { - uc.mem_map(address, 0x1000, Permission::ALL).unwrap(); - } - }; + if mem_type == MemType::READ_UNMAPPED { + uc.mem_map(address, 0x1000, Permission::ALL).unwrap(); + } + true + }; // mov eax, 0xdeadbeef; // mov [0x2000], eax; @@ -276,16 +276,15 @@ 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_engine::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(())); let hook = emu - .add_mem_hook(HookType::MEM_ALL, 0, std::u64::MAX, callback) + .add_mem_hook(HookType::MEM_ALL, 0, u64::MAX, callback) .expect("failed to add memory hook"); - assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 0x123), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::EAX, 0x123), Ok(())); assert_eq!( emu.emu_start( 0x1000, @@ -308,15 +307,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_engine::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 +343,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_engine::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,9 +379,9 @@ 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(); + let rax = uc.reg_read(RegisterX86::RAX).unwrap(); *callback_insn.borrow_mut() = InsnSysExpectation(rax); }; @@ -393,9 +390,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_engine::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,11 +416,10 @@ 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_engine::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)); + assert_eq!(emu.reg_write(RegisterARM::R1, 123), Ok(())); + assert_eq!(emu.reg_read(RegisterARM::R1), Ok(123)); // Attempt to write to memory before mapping it. assert_eq!( @@ -439,8 +434,8 @@ fn emulate_arm() { Ok(arm_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterARM::SP as i32, 12), Ok(())); - assert_eq!(emu.reg_write(RegisterARM::R0 as i32, 10), Ok(())); + assert_eq!(emu.reg_write(RegisterARM::SP, 12), Ok(())); + assert_eq!(emu.reg_write(RegisterARM::R0, 10), Ok(())); // ARM checks the least significant bit of the address to know // if the code is in Thumb mode. @@ -453,24 +448,23 @@ fn emulate_arm() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterARM::SP as i32), Ok(0)); - assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(10)); + assert_eq!(emu.reg_read(RegisterARM::SP), Ok(0)); + assert_eq!(emu.reg_read(RegisterARM::R0), Ok(10)); } #[test] 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_engine::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!( emu.mem_read_as_vec(0x1000, mips_code32.len()), Ok(mips_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterMIPS::AT as i32, 0), Ok(())); + assert_eq!(emu.reg_write(RegisterMIPS::AT, 0), Ok(())); assert_eq!( emu.emu_start( 0x1000, @@ -480,24 +474,23 @@ fn emulate_mips() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterMIPS::AT as i32), Ok(0x3456)); + assert_eq!(emu.reg_read(RegisterMIPS::AT), Ok(0x3456)); } #[test] 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_engine::Unicorn::new(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN) .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!( emu.mem_read_as_vec(0x1000, ppc_code32.len()), Ok(ppc_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterPPC::GPR3 as i32, 42), Ok(())); - assert_eq!(emu.reg_write(RegisterPPC::GPR6 as i32, 1337), Ok(())); + assert_eq!(emu.reg_write(RegisterPPC::GPR3, 42), Ok(())); + assert_eq!(emu.reg_write(RegisterPPC::GPR6, 1337), Ok(())); assert_eq!( emu.emu_start( 0x1000, @@ -507,14 +500,13 @@ fn emulate_ppc() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterPPC::GPR26 as i32), Ok(1379)); + assert_eq!(emu.reg_read(RegisterPPC::GPR26), Ok(1379)); } #[test] fn mem_unmapping() { - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_engine::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 +517,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_engine::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 +527,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(())); @@ -545,8 +536,8 @@ fn mem_map_ptr() { Ok(x86_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(())); - assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(())); assert_eq!( emu.emu_start( @@ -557,8 +548,8 @@ fn mem_map_ptr() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11)); - assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49)); + assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11)); + assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49)); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(())); // Use a Vec for the emulator memory. @@ -572,7 +563,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(())); @@ -581,8 +572,8 @@ fn mem_map_ptr() { Ok(x86_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(())); - assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(())); assert_eq!( emu.emu_start( @@ -593,20 +584,19 @@ fn mem_map_ptr() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11)); - assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49)); + assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11)); + assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49)); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(())); } #[test] fn x86_context_save_and_restore() { - for mode in vec![Mode::MODE_32, Mode::MODE_64] { - let x86_code: Vec = vec![ + for mode in [Mode::MODE_32, Mode::MODE_64] { + let x86_code = [ 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_engine::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,16 +611,12 @@ 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_engine::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); - assert_eq!( - emu2.reg_read(*register as i32), - emu.reg_read(*register as i32) - ); + assert_eq!(emu2.reg_read(*register), emu.reg_read(*register)); } } } @@ -644,16 +630,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_engine::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(()));