From 66df39ebc978b01f5d1fee8a1bb3b091b27def32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20W=C3=B6rner?= Date: Sat, 8 May 2021 12:48:50 +0200 Subject: [PATCH] Improve Rust bindings (#1367) * fixed tests * constant readability * HookType as bitflags * Mode as bitflags * improve bitflags * cargo fmt * removed unnecessary "as usize" --- bindings/rust/build.rs | 21 +- bindings/rust/src/arm.rs | 3 +- bindings/rust/src/arm64.rs | 2 +- bindings/rust/src/ffi.rs | 131 ++++++++---- bindings/rust/src/lib.rs | 285 ++++++++++++++------------ bindings/rust/src/m68k.rs | 2 +- bindings/rust/src/mips.rs | 2 +- bindings/rust/src/ppc.rs | 4 +- bindings/rust/src/sparc.rs | 2 +- bindings/rust/src/unicorn_const.rs | 135 ++++++------ bindings/rust/src/utils.rs | 318 ++++++++++++++++++++--------- bindings/rust/src/x86.rs | 2 +- bindings/rust/tests/unicorn.rs | 156 +++++++------- 13 files changed, 620 insertions(+), 443 deletions(-) diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index 2dd86a9d..7df3e61c 100644 --- a/bindings/rust/build.rs +++ b/bindings/rust/build.rs @@ -1,7 +1,4 @@ -use std::{ - env, - process::Command, -}; +use std::{env, process::Command}; use build_helper::rustc::{link_lib, link_search}; @@ -9,15 +6,15 @@ fn main() { println!("cargo:rerun-if-changed=unicorn"); let out_dir = env::var("OUT_DIR").unwrap(); let unicorn = "libunicorn.a"; - let _ = Command::new("cp") - .current_dir("../..") - .arg(&unicorn) - .arg(&out_dir) - .status() - .unwrap(); + let _ = Command::new("cp") + .current_dir("../..") + .arg(&unicorn) + .arg(&out_dir) + .status() + .unwrap(); link_search( Some(build_helper::SearchKind::Native), - build_helper::out_dir()); + build_helper::out_dir(), + ); link_lib(Some(build_helper::LibKind::Static), "unicorn"); } - diff --git a/bindings/rust/src/arm.rs b/bindings/rust/src/arm.rs index a4402cf7..69178664 100644 --- a/bindings/rust/src/arm.rs +++ b/bindings/rust/src/arm.rs @@ -125,7 +125,6 @@ pub enum RegisterARM { CONTROL = 117, XPSR = 118, ENDING = 119, - // alias registers // (assoc) R13 = 12, // (assoc) R14 = 10, @@ -144,4 +143,4 @@ impl RegisterARM { pub const SL: RegisterARM = RegisterARM::R10; pub const FP: RegisterARM = RegisterARM::R11; pub const IP: RegisterARM = RegisterARM::R12; -} \ No newline at end of file +} diff --git a/bindings/rust/src/arm64.rs b/bindings/rust/src/arm64.rs index fecf1b7b..7c24368b 100644 --- a/bindings/rust/src/arm64.rs +++ b/bindings/rust/src/arm64.rs @@ -265,4 +265,4 @@ pub enum RegisterARM64 { // pseudo registers PC = 260, -} \ No newline at end of file +} diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs index f848c1a7..a24523bf 100644 --- a/bindings/rust/src/ffi.rs +++ b/bindings/rust/src/ffi.rs @@ -1,11 +1,10 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] - +use super::unicorn_const::*; +use libc::{c_char, c_int}; use std::ffi::c_void; use std::pin::Pin; -use libc::{c_char, c_int}; -use super::unicorn_const::*; pub type uc_handle = *mut c_void; pub type uc_hook = *mut c_void; @@ -42,8 +41,12 @@ extern "C" { ptr: *mut c_void, ) -> uc_error; pub fn uc_mem_unmap(engine: uc_handle, address: u64, size: libc::size_t) -> uc_error; - pub fn uc_mem_protect(engine: uc_handle, address: u64, size: libc::size_t, perms: u32) - -> uc_error; + pub fn uc_mem_protect( + engine: uc_handle, + address: u64, + size: libc::size_t, + perms: u32, + ) -> uc_error; pub fn uc_mem_regions( engine: uc_handle, regions: *const *const MemRegion, @@ -74,102 +77,154 @@ 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, u64, u32)> + pub callback: Box, u64, u32)>, } pub struct BlockHook { pub unicorn: *mut crate::UnicornInner, - pub callback: Box, u64, u32)> + pub callback: Box, u64, u32)>, } pub struct MemHook { pub unicorn: *mut crate::UnicornInner, - pub callback: Box, MemType, u64, usize, i64)> + pub callback: Box, MemType, u64, usize, i64)>, } pub struct InterruptHook { pub unicorn: *mut crate::UnicornInner, - pub callback: Box, u32)> + pub callback: Box, u32)>, } pub struct InstructionInHook { pub unicorn: *mut crate::UnicornInner, - pub callback: Box, u32, usize)> + pub callback: Box, u32, usize)>, } pub struct InstructionOutHook { pub unicorn: *mut crate::UnicornInner, - pub callback: Box, u32, usize, u32)> + pub callback: Box, u32, usize, u32)>, } pub struct InstructionSysHook { pub unicorn: *mut crate::UnicornInner, - pub callback: Box)> + pub callback: Box)>, } -pub extern "C" fn code_hook_proxy(uc: uc_handle, address: u64, size: u32, user_data: *mut CodeHook) { +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); + callback( + crate::UnicornHandle { + inner: unsafe { Pin::new_unchecked(unicorn) }, + }, + address, + size, + ); } -pub extern "C" fn block_hook_proxy(uc: uc_handle, address: u64, size: u32, user_data: *mut BlockHook) { +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); + callback( + crate::UnicornHandle { + inner: unsafe { Pin::new_unchecked(unicorn) }, + }, + address, + size, + ); } -pub extern "C" fn mem_hook_proxy(uc: uc_handle, - mem_type: MemType, - address: u64, - size: u32, - value: i64, - user_data: *mut MemHook) -{ +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); + callback( + crate::UnicornHandle { + inner: unsafe { Pin::new_unchecked(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 }; assert_eq!(uc, unicorn.uc); - callback(crate::UnicornHandle { inner: unsafe { Pin::new_unchecked(unicorn) } }, value); + callback( + crate::UnicornHandle { + inner: unsafe { Pin::new_unchecked(unicorn) }, + }, + value, + ); } pub extern "C" fn insn_in_hook_proxy( - uc: uc_handle, - port: u32, - size: usize, - user_data: *mut InstructionInHook) { + 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); + callback( + crate::UnicornHandle { + inner: unsafe { Pin::new_unchecked(unicorn) }, + }, + port, + size, + ); } pub extern "C" fn insn_out_hook_proxy( - uc: uc_handle, - port: u32, - size: usize, - value: u32, - user_data: *mut InstructionOutHook) { + 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); + callback( + crate::UnicornHandle { + inner: unsafe { Pin::new_unchecked(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 }; assert_eq!(uc, unicorn.uc); - callback(crate::UnicornHandle { inner: unsafe { Pin::new_unchecked(unicorn) } }); + callback(crate::UnicornHandle { + inner: unsafe { Pin::new_unchecked(unicorn) }, + }); } diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 5d7a82f3..c67e4345 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -1,7 +1,6 @@ - //! Bindings for the Unicorn emulator. //! -//! +//! //! //! # Example use //! @@ -9,18 +8,18 @@ //! //! use unicorn::RegisterARM; //! use unicorn::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::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN, 0).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"); -//! +//! //! 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)); @@ -29,8 +28,8 @@ //! mod ffi; -pub mod utils; pub mod unicorn_const; +pub mod utils; mod arm; mod arm64; @@ -39,21 +38,13 @@ mod mips; mod ppc; mod sparc; mod x86; -pub use crate::{ - arm64::*, - arm::*, - m68k::*, - mips::*, - ppc::*, - sparc::*, - x86::*, -}; +pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, sparc::*, x86::*}; -use std::ffi::c_void; -use std::collections::HashMap; use ffi::uc_handle; -use std::pin::Pin; +use std::collections::HashMap; +use std::ffi::c_void; use std::marker::PhantomPinned; +use std::pin::Pin; use unicorn_const::*; #[derive(Debug)] @@ -79,13 +70,13 @@ impl Drop for Context { #[derive(Debug)] /// A Unicorn emulator instance. pub struct Unicorn { - inner: Pin>> + inner: Pin>>, } #[derive(Debug)] /// Handle used to safely access exposed functions and data of a Unicorn instance. pub struct UnicornHandle<'a, D> { - inner: Pin<&'a mut UnicornInner> + inner: Pin<&'a mut UnicornInner>, } /// Internal Management struct @@ -100,39 +91,40 @@ pub struct UnicornInner { pub insn_out_hooks: HashMap<*mut libc::c_void, Box>>, pub insn_sys_hooks: HashMap<*mut libc::c_void, Box>>, pub data: D, - _pin: PhantomPinned + _pin: PhantomPinned, } impl Unicorn { /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. - pub fn new(arch: Arch, mode: Mode, data: D) - -> Result, uc_error> { - + pub fn new(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(), - data: data, - _pin: std::marker::PhantomPinned - })}) + 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(), + data: data, + _pin: std::marker::PhantomPinned, + }), + }) } else { Err(err) } } pub fn borrow<'a>(&'a mut self) -> UnicornHandle<'a, D> { - UnicornHandle { inner: self.inner.as_mut() } + UnicornHandle { + inner: self.inner.as_mut(), + } } } @@ -155,7 +147,7 @@ impl std::fmt::Debug for UnicornInner { impl<'a, D> UnicornHandle<'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 { @@ -230,11 +222,12 @@ impl<'a, D> UnicornHandle<'a, D> { /// `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(&mut self, - address: u64, - size: usize, - perms: Permission, - ptr: *mut c_void + pub 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) }; if err == uc_error::OK { @@ -248,10 +241,11 @@ impl<'a, D> UnicornHandle<'a, D> { /// /// `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_map(&mut self, - address: u64, - size: libc::size_t, - perms: Permission + pub fn mem_map( + &mut self, + address: u64, + size: libc::size_t, + perms: Permission, ) -> Result<(), uc_error> { let err = unsafe { ffi::uc_mem_map(self.inner.uc, address, size, perms.bits()) }; if err == uc_error::OK { @@ -265,10 +259,7 @@ impl<'a, D> UnicornHandle<'a, D> { /// /// `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> { + 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) }; if err == uc_error::OK { Ok(()) @@ -281,10 +272,11 @@ impl<'a, D> UnicornHandle<'a, D> { /// /// `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_protect(&mut self, - address: u64, - size: libc::size_t, - perms: Permission + pub fn mem_protect( + &mut self, + address: u64, + size: libc::size_t, + perms: Permission, ) -> Result<(), uc_error> { let err = unsafe { ffi::uc_mem_protect(self.inner.uc, address, size, perms.bits()) }; if err == uc_error::OK { @@ -296,7 +288,8 @@ impl<'a, D> UnicornHandle<'a, D> { /// 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.inner.uc, regid.into(), &value as *const _ as _) }; if err == uc_error::OK { Ok(()) } else { @@ -305,7 +298,7 @@ impl<'a, D> UnicornHandle<'a, D> { } /// Write variable sized values into registers. - /// + /// /// 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> { @@ -318,11 +311,12 @@ impl<'a, D> UnicornHandle<'a, D> { } /// Read an unsigned value from a register. - /// + /// /// 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.inner.uc, regid.into(), &mut value as *mut u64 as _) }; if err == uc_error::OK { Ok(value) } else { @@ -331,7 +325,7 @@ impl<'a, D> UnicornHandle<'a, D> { } /// 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 (x86); Q, V (arm64)). pub fn reg_read_long>(&self, regid: T) -> Result, uc_error> { let err: uc_error; @@ -341,29 +335,39 @@ impl<'a, D> UnicornHandle<'a, D> { let curr_arch = self.get_arch(); if curr_arch == Arch::X86 { - if curr_reg_id >= x86::RegisterX86::XMM0 as i32 && curr_reg_id <= x86::RegisterX86::XMM31 as i32 { - value = vec![0; 16 as usize]; - } else if curr_reg_id >= x86::RegisterX86::YMM0 as i32 && curr_reg_id <= x86::RegisterX86::YMM31 as i32 { - value = vec![0; 32 as usize]; - } else if curr_reg_id >= x86::RegisterX86::ZMM0 as i32 && curr_reg_id <= x86::RegisterX86::ZMM31 as i32 { - value = vec![0; 64 as usize]; - } else if curr_reg_id == x86::RegisterX86::GDTR as i32 || - curr_reg_id == x86::RegisterX86::IDTR as i32 { - value = vec![0; 10 as usize]; // 64 bit base address in IA-32e mode + if curr_reg_id >= x86::RegisterX86::XMM0 as i32 + && curr_reg_id <= x86::RegisterX86::XMM31 as i32 + { + value = vec![0; 16]; + } else if curr_reg_id >= x86::RegisterX86::YMM0 as i32 + && curr_reg_id <= x86::RegisterX86::YMM31 as i32 + { + value = vec![0; 32]; + } else if curr_reg_id >= x86::RegisterX86::ZMM0 as i32 + && curr_reg_id <= x86::RegisterX86::ZMM31 as i32 + { + value = vec![0; 64]; + } else if curr_reg_id == x86::RegisterX86::GDTR as i32 + || curr_reg_id == x86::RegisterX86::IDTR as i32 + { + value = vec![0; 10]; // 64 bit base address in IA-32e mode } else { - return Err(uc_error::ARG) + return Err(uc_error::ARG); } } else if curr_arch == Arch::ARM64 { - if (curr_reg_id >= arm64::RegisterARM64::Q0 as i32 && curr_reg_id <= arm64::RegisterARM64::Q31 as i32) || - (curr_reg_id >= arm64::RegisterARM64::V0 as i32 && curr_reg_id <= arm64::RegisterARM64::V31 as i32) { - value = vec![0; 16 as usize]; + if (curr_reg_id >= arm64::RegisterARM64::Q0 as i32 + && curr_reg_id <= arm64::RegisterARM64::Q31 as i32) + || (curr_reg_id >= arm64::RegisterARM64::V0 as i32 + && curr_reg_id <= arm64::RegisterARM64::V31 as i32) + { + value = vec![0; 16]; } else { - return Err(uc_error::ARG) + return Err(uc_error::ARG); } } else { - return Err(uc_error::ARCH) + return Err(uc_error::ARCH); } - + err = unsafe { ffi::uc_reg_read(self.inner.uc, curr_reg_id, value.as_mut_ptr() as _) }; if err == uc_error::OK { @@ -377,7 +381,8 @@ impl<'a, D> UnicornHandle<'a, D> { /// 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.inner.uc, regid.into(), &mut value as *mut i32 as _) }; if err == uc_error::OK { Ok(value) } else { @@ -392,14 +397,15 @@ impl<'a, D> UnicornHandle<'a, D> { end: u64, callback: F, ) -> Result - where F: FnMut(UnicornHandle, u64, u32) + where + F: FnMut(UnicornHandle, u64, u32), { 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 err = unsafe { ffi::uc_hook_add( self.inner.uc, @@ -412,7 +418,9 @@ impl<'a, D> UnicornHandle<'a, D> { ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() }.code_hooks.insert(hook_ptr, user_data); + unsafe { self.inner.as_mut().get_unchecked_mut() } + .code_hooks + .insert(hook_ptr, user_data); Ok(hook_ptr) } else { Err(err) @@ -420,11 +428,9 @@ impl<'a, D> UnicornHandle<'a, D> { } /// Add a block hook. - pub fn add_block_hook( - &mut self, - callback: F, - ) -> Result - where F: FnMut(UnicornHandle, u64, u32) + pub fn add_block_hook(&mut self, callback: F) -> Result + where + F: FnMut(UnicornHandle, u64, u32), { let mut hook_ptr = std::ptr::null_mut(); let mut user_data = Box::new(ffi::BlockHook { @@ -444,7 +450,9 @@ impl<'a, D> UnicornHandle<'a, D> { ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() }.block_hooks.insert(hook_ptr, user_data); + unsafe { self.inner.as_mut().get_unchecked_mut() } + .block_hooks + .insert(hook_ptr, user_data); Ok(hook_ptr) } else { Err(err) @@ -459,18 +467,19 @@ impl<'a, D> UnicornHandle<'a, D> { end: u64, callback: F, ) -> Result - where F: FnMut(UnicornHandle, MemType, u64, usize, i64) + where + F: FnMut(UnicornHandle, MemType, u64, usize, i64), { - if (hook_type as i32) < 16 || hook_type == HookType::INSN_INVALID { + 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 err = unsafe { ffi::uc_hook_add( self.inner.uc, @@ -483,7 +492,9 @@ impl<'a, D> UnicornHandle<'a, D> { ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() }.mem_hooks.insert(hook_ptr, user_data); + unsafe { self.inner.as_mut().get_unchecked_mut() } + .mem_hooks + .insert(hook_ptr, user_data); Ok(hook_ptr) } else { Err(err) @@ -491,18 +502,16 @@ impl<'a, D> UnicornHandle<'a, D> { } /// Add an interrupt hook. - pub fn add_intr_hook( - &mut self, - callback: F, - ) -> Result - where F: FnMut(UnicornHandle, u32) - { + pub fn add_intr_hook(&mut self, callback: F) -> Result + where + F: FnMut(UnicornHandle, 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 err = unsafe { ffi::uc_hook_add( self.inner.uc, @@ -515,7 +524,9 @@ impl<'a, D> UnicornHandle<'a, D> { ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() }.intr_hooks.insert(hook_ptr, user_data); + unsafe { self.inner.as_mut().get_unchecked_mut() } + .intr_hooks + .insert(hook_ptr, user_data); Ok(hook_ptr) } else { Err(err) @@ -523,18 +534,16 @@ impl<'a, D> UnicornHandle<'a, D> { } /// Add hook for x86 IN instruction. - pub fn add_insn_in_hook( - &mut self, - callback: F, - ) -> Result - where F: FnMut(UnicornHandle, u32, usize) - { + pub fn add_insn_in_hook(&mut self, callback: F) -> Result + where + F: FnMut(UnicornHandle, 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 err = unsafe { ffi::uc_hook_add( self.inner.uc, @@ -548,7 +557,9 @@ impl<'a, D> UnicornHandle<'a, D> { ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() }.insn_in_hooks.insert(hook_ptr, user_data); + unsafe { self.inner.as_mut().get_unchecked_mut() } + .insn_in_hooks + .insert(hook_ptr, user_data); Ok(hook_ptr) } else { Err(err) @@ -556,18 +567,16 @@ impl<'a, D> UnicornHandle<'a, D> { } /// Add hook for x86 OUT instruction. - pub fn add_insn_out_hook( - &mut self, - callback: F, - ) -> Result - where F: FnMut(UnicornHandle, u32, usize, u32) - { + pub fn add_insn_out_hook(&mut self, callback: F) -> Result + where + F: FnMut(UnicornHandle, 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 err = unsafe { ffi::uc_hook_add( self.inner.uc, @@ -581,7 +590,9 @@ impl<'a, D> UnicornHandle<'a, D> { ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() }.insn_out_hooks.insert(hook_ptr, user_data); + unsafe { self.inner.as_mut().get_unchecked_mut() } + .insn_out_hooks + .insert(hook_ptr, user_data); Ok(hook_ptr) } else { Err(err) @@ -596,14 +607,15 @@ impl<'a, D> UnicornHandle<'a, D> { end: u64, callback: F, ) -> Result - where F: FnMut(UnicornHandle) - { + where + F: FnMut(UnicornHandle), + { 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 err = unsafe { ffi::uc_hook_add( self.inner.uc, @@ -617,7 +629,9 @@ impl<'a, D> UnicornHandle<'a, D> { ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() }.insn_sys_hooks.insert(hook_ptr, user_data); + unsafe { self.inner.as_mut().get_unchecked_mut() } + .insn_sys_hooks + .insert(hook_ptr, user_data); Ok(hook_ptr) } else { Err(err) @@ -681,13 +695,15 @@ impl<'a, D> UnicornHandle<'a, D> { } /// Allocate and return an empty Unicorn context. - /// + /// /// 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) }; if err == uc_error::OK { - Ok(Context { context: empty_context }) + Ok(Context { + context: empty_context, + }) } else { Err(err) } @@ -704,9 +720,9 @@ impl<'a, D> UnicornHandle<'a, D> { } /// 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 + /// 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(); @@ -716,7 +732,9 @@ impl<'a, D> UnicornHandle<'a, D> { } let err = unsafe { ffi::uc_context_save(self.inner.uc, new_context) }; if err == uc_error::OK { - Ok(Context { context: new_context }) + Ok(Context { + context: new_context, + }) } else { unsafe { ffi::uc_free(new_context) }; Err(err) @@ -724,7 +742,7 @@ impl<'a, D> UnicornHandle<'a, D> { } /// Restore a previously saved Unicorn context. - /// + /// /// Perform a quick rollback of the CPU context, including registers and some /// internal metadata. Contexts may not be shared across engine instances with /// differing arches or modes. Memory has to be restored manually, if needed. @@ -743,11 +761,12 @@ impl<'a, D> UnicornHandle<'a, D> { /// is hit. `timeout` specifies a duration in microseconds after which the emulation is /// stopped (infinite execution if set to 0). `count` is the maximum number of instructions /// to emulate (emulate all the available instructions if set to 0). - pub fn emu_start(&mut self, - begin: u64, - until: u64, - timeout: u64, - count: usize + pub fn emu_start( + &mut self, + begin: u64, + until: u64, + 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 { @@ -771,7 +790,7 @@ impl<'a, D> UnicornHandle<'a, D> { } /// Query the internal status of the engine. - /// + /// /// supported: MODE, PAGE_SIZE, ARCH pub fn query(&self, query: Query) -> Result { let mut result: libc::size_t = Default::default(); @@ -782,6 +801,4 @@ impl<'a, D> UnicornHandle<'a, D> { Err(err) } } - } - diff --git a/bindings/rust/src/m68k.rs b/bindings/rust/src/m68k.rs index b56a0aa3..6c04e851 100644 --- a/bindings/rust/src/m68k.rs +++ b/bindings/rust/src/m68k.rs @@ -21,4 +21,4 @@ pub enum RegisterM68K { D7, SR, PC, -} \ No newline at end of file +} diff --git a/bindings/rust/src/mips.rs b/bindings/rust/src/mips.rs index ac26cadc..e6204664 100644 --- a/bindings/rust/src/mips.rs +++ b/bindings/rust/src/mips.rs @@ -153,4 +153,4 @@ pub enum RegisterMIPS { MPL0 = 134, MPL1 = 135, MPL2 = 136, -} \ No newline at end of file +} diff --git a/bindings/rust/src/ppc.rs b/bindings/rust/src/ppc.rs index 19520a93..a28827d3 100644 --- a/bindings/rust/src/ppc.rs +++ b/bindings/rust/src/ppc.rs @@ -38,5 +38,5 @@ pub enum RegisterPPC { GPR28 = 30, GPR29 = 31, GPR30 = 32, - GPR31 = 33 -} \ No newline at end of file + GPR31 = 33, +} diff --git a/bindings/rust/src/sparc.rs b/bindings/rust/src/sparc.rs index 0665ca86..21e09db4 100644 --- a/bindings/rust/src/sparc.rs +++ b/bindings/rust/src/sparc.rs @@ -91,4 +91,4 @@ pub enum RegisterSPARC { Y = 86, XCC = 87, PC = 88, -} \ No newline at end of file +} diff --git a/bindings/rust/src/unicorn_const.rs b/bindings/rust/src/unicorn_const.rs index 5697e1c3..56b3f275 100644 --- a/bindings/rust/src/unicorn_const.rs +++ b/bindings/rust/src/unicorn_const.rs @@ -6,8 +6,8 @@ pub const API_MINOR: u64 = 0; pub const VERSION_MAJOR: u64 = 1; pub const VERSION_MINOR: u64 = 0; pub const VERSION_EXTRA: u64 = 2; -pub const SECOND_SCALE: u64 = 1000000; -pub const MILISECOND_SCALE: u64 = 1000; +pub const SECOND_SCALE: u64 = 1_000_000; +pub const MILISECOND_SCALE: u64 = 1_000; #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] @@ -51,32 +51,40 @@ pub enum MemType { READ_AFTER = 25, } -#[repr(i32)] -#[derive(PartialEq, Debug, Clone, Copy)] -pub enum HookType { - INTR = 1, - INSN = 2, - CODE = 4, - BLOCK = 8, - MEM_READ_UNMAPPED = 16, - MEM_WRITE_UNMAPPED = 32, - MEM_FETCH_UNMAPPED = 64, - MEM_READ_PROT = 128, - MEM_WRITE_PROT = 256, - MEM_FETCH_PROT = 512, - MEM_READ = 1024, - MEM_WRITE = 2048, - MEM_FETCH = 4096, - MEM_READ_AFTER = 8192, - INSN_INVALID = 16384, - MEM_UNMAPPED = 112, - MEM_PROT = 896, - MEM_READ_INVALID = 144, - MEM_WRITE_INVALID = 288, - MEM_FETCH_INVALID = 576, - MEM_INVALID = 1008, - MEM_VALID = 7168, - MEM_ALL = 8176, +bitflags! { + #[repr(C)] + pub struct HookType: i32 { + const INTR = 1; + const INSN = 2; + const CODE = 4; + const BLOCK = 8; + + const MEM_READ_UNMAPPED = 0x10; + const MEM_WRITE_UNMAPPED = 0x20; + const MEM_FETCH_UNMAPPED = 0x40; + const MEM_UNMAPPED = Self::MEM_READ_UNMAPPED.bits | Self::MEM_WRITE_UNMAPPED.bits | Self::MEM_FETCH_UNMAPPED.bits; + + const MEM_READ_PROT = 0x80; + const MEM_WRITE_PROT = 0x100; + const MEM_FETCH_PROT = 0x200; + const MEM_PROT = Self::MEM_READ_PROT.bits | Self::MEM_WRITE_PROT.bits | Self::MEM_FETCH_PROT.bits; + + const MEM_READ = 0x400; + const MEM_WRITE = 0x800; + const MEM_FETCH = 0x1000; + const MEM_VALID = Self::MEM_READ.bits | Self::MEM_WRITE.bits | Self::MEM_FETCH.bits; + + const MEM_READ_AFTER = 0x2000; + + const INSN_INVALID = 0x4000; + + const MEM_READ_INVALID = Self::MEM_READ_UNMAPPED.bits | Self::MEM_READ_PROT.bits; + const MEM_WRITE_INVALID = Self::MEM_WRITE_UNMAPPED.bits | Self::MEM_WRITE_PROT.bits; + const MEM_FETCH_INVALID = Self::MEM_FETCH_UNMAPPED.bits | Self::MEM_FETCH_PROT.bits; + const MEM_INVALID = Self::MEM_READ_INVALID.bits | Self::MEM_WRITE_INVALID.bits | Self::MEM_FETCH_INVALID.bits; + + const MEM_ALL = Self::MEM_VALID.bits | Self::MEM_INVALID.bits; + } } #[repr(C)] @@ -94,7 +102,7 @@ pub struct Permission : u32 { const READ = 1; const WRITE = 2; const EXEC = 4; - const ALL = 7; + const ALL = Self::READ.bits | Self::WRITE.bits | Self::EXEC.bits; } } @@ -119,47 +127,32 @@ pub enum Arch { MAX = 8, } -#[repr(C)] -#[derive(PartialEq, Debug, Clone, Copy)] -pub enum Mode { +bitflags! { + #[repr(C)] + pub struct Mode: i32 { + const LITTLE_ENDIAN = 0; + const BIG_ENDIAN = 0x4000_0000; - LITTLE_ENDIAN = 0, - BIG_ENDIAN = 1073741824, - - // use LITTLE_ENDIAN. - // MODE_ARM = 0, - THUMB = 16, - MCLASS = 32, - V8 = 64, - ARM926 = 128, - ARM946 = 256, - ARM1176 = 512, - // (assoc) MICRO = 16, - // (assoc) MIPS3 = 32, - // (assoc) MIPS32R6 = 64, - MIPS32 = 4, - MIPS64 = 8, - MODE_16 = 2, - // (assoc) MODE_32 = 4, - // (assoc) MODE_64 = 8, - // (assoc) PPC32 = 4, - // (assoc) PPC64 = 8, - // (assoc) QPX = 16, - // (assoc) SPARC32 = 4, - // (assoc) SPARC64 = 8, - // (assoc) V9 = 16, + const ARM = 0; + const THUMB = 0x10; + const MCLASS = 0x20; + const V8 = 0x40; + const ARM926 = 0x80; + const ARM946 = 0x100; + const ARM1176 = 0x200; + const MICRO = Self::THUMB.bits; + const MIPS3 = Self::MCLASS.bits; + const MIPS32R6 = Self::V8.bits; + const MIPS32 = 4; + const MIPS64 = 8; + const MODE_16 = 2; + const MODE_32 = Self::MIPS32.bits; + const MODE_64 = Self::MIPS64.bits; + const PPC32 = Self::MIPS32.bits; + const PPC64 = Self::MIPS64.bits; + const QPX = Self::THUMB.bits; + const SPARC32 = Self::MIPS32.bits; + const SPARC64 = Self::MIPS64.bits; + const V9 = Self::THUMB.bits; + } } - -impl Mode { - pub const MICRO: Mode = Mode::THUMB; - pub const MIPS3: Mode = Mode::MCLASS; - pub const MIPS32R6: Mode = Mode::V8; - pub const MODE_32: Mode = Mode::MIPS32; - pub const MODE_64: Mode = Mode::MIPS64; - pub const PPC32: Mode = Mode::MIPS32; - pub const PPC64: Mode = Mode::MIPS64; - pub const QPX: Mode = Mode::THUMB; - pub const SPARC32: Mode = Mode::MIPS32; - pub const SPARC64: Mode = Mode::MIPS64; - pub const V9: Mode = Mode::THUMB; -} \ No newline at end of file diff --git a/bindings/rust/src/utils.rs b/bindings/rust/src/utils.rs index f0208abe..aad4f003 100644 --- a/bindings/rust/src/utils.rs +++ b/bindings/rust/src/utils.rs @@ -1,19 +1,18 @@ #![allow(non_snake_case)] extern crate libc; -use capstone::prelude::*; use super::arm::RegisterARM; use super::arm64::RegisterARM64; -use super::x86::RegisterX86; -use super::sparc::RegisterSPARC; -use super::mips::RegisterMIPS; use super::m68k::RegisterM68K; -use super::{Permission, Mode, Arch, HookType, MemType, uc_error}; -use std::ptr; +use super::mips::RegisterMIPS; +use super::sparc::RegisterSPARC; +use super::x86::RegisterX86; +use super::{uc_error, Arch, HookType, MemType, Mode, Permission}; +use capstone::prelude::*; +use libc::{c_void, mmap, size_t, MAP_ANON, MAP_PRIVATE, PROT_READ, PROT_WRITE}; use std::cell::RefCell; use std::collections::HashMap; -use libc::{mmap, c_void, size_t, MAP_ANON, MAP_PRIVATE,PROT_READ,PROT_WRITE}; - +use std::ptr; #[derive(Debug)] pub struct Chunk { @@ -22,7 +21,6 @@ pub struct Chunk { pub freed: bool, } - #[derive(Debug)] pub struct Heap { pub real_base: *mut c_void, @@ -34,77 +32,122 @@ pub struct Heap { pub unalloc_hook: super::ffi::uc_hook, } - /// Hooks (parts of the) code segment to display register info and the current instruction. pub fn add_debug_prints_ARM(uc: &mut super::UnicornHandle, code_start: u64, code_end: u64) { let cs_arm: Capstone = Capstone::new() .arm() .mode(arch::arm::ArchMode::Arm) .detail(true) - .build().expect("failed to create capstone for ARM"); + .build() + .expect("failed to create capstone for ARM"); let cs_thumb: Capstone = Capstone::new() .arm() .mode(arch::arm::ArchMode::Thumb) .detail(true) - .build().expect("failed to create capstone for thumb"); + .build() + .expect("failed to create capstone for thumb"); - let callback = Box::new(move |uc: super::UnicornHandle, addr: u64, size: u32| { - let sp = uc.reg_read(RegisterARM::SP as i32).expect("failed to read SP"); - let lr = uc.reg_read(RegisterARM::LR as i32).expect("failed to read LR"); - let r0 = uc.reg_read(RegisterARM::R0 as i32).expect("failed to read r0"); - let r1 = uc.reg_read(RegisterARM::R1 as i32).expect("failed to read r1"); - let r2 = uc.reg_read(RegisterARM::R2 as i32).expect("failed to read r2"); - let r3 = uc.reg_read(RegisterARM::R3 as i32).expect("failed to read r3"); - let r4 = uc.reg_read(RegisterARM::R4 as i32).expect("failed to read r4"); - let r5 = uc.reg_read(RegisterARM::R5 as i32).expect("failed to read r5"); - let r6 = uc.reg_read(RegisterARM::R6 as i32).expect("failed to read r6"); - let r7 = uc.reg_read(RegisterARM::R7 as i32).expect("failed to read r7"); - let r8 = uc.reg_read(RegisterARM::R8 as i32).expect("failed to read r8"); - let r9 = uc.reg_read(RegisterARM::R9 as i32).expect("failed to read r9"); - let r10 = uc.reg_read(RegisterARM::R10 as i32).expect("failed to read r10"); - let r11 = uc.reg_read(RegisterARM::R11 as i32).expect("failed to read r11"); + let callback = Box::new(move |uc: super::UnicornHandle, addr: u64, size: u32| { + let sp = uc + .reg_read(RegisterARM::SP as i32) + .expect("failed to read SP"); + let lr = uc + .reg_read(RegisterARM::LR as i32) + .expect("failed to read LR"); + let r0 = uc + .reg_read(RegisterARM::R0 as i32) + .expect("failed to read r0"); + let r1 = uc + .reg_read(RegisterARM::R1 as i32) + .expect("failed to read r1"); + let r2 = uc + .reg_read(RegisterARM::R2 as i32) + .expect("failed to read r2"); + let r3 = uc + .reg_read(RegisterARM::R3 as i32) + .expect("failed to read r3"); + let r4 = uc + .reg_read(RegisterARM::R4 as i32) + .expect("failed to read r4"); + let r5 = uc + .reg_read(RegisterARM::R5 as i32) + .expect("failed to read r5"); + let r6 = uc + .reg_read(RegisterARM::R6 as i32) + .expect("failed to read r6"); + let r7 = uc + .reg_read(RegisterARM::R7 as i32) + .expect("failed to read r7"); + let r8 = uc + .reg_read(RegisterARM::R8 as i32) + .expect("failed to read r8"); + let r9 = uc + .reg_read(RegisterARM::R9 as i32) + .expect("failed to read r9"); + let r10 = uc + .reg_read(RegisterARM::R10 as i32) + .expect("failed to read r10"); + let r11 = uc + .reg_read(RegisterARM::R11 as i32) + .expect("failed to read r11"); println!("________________________________________________________________________\n"); - println!("$r0: {:#010x} $r1: {:#010x} $r2: {:#010x} $r3: {:#010x}", r0, r1, r2, r3); - println!("$r4: {:#010x} $r5: {:#010x} $r6: {:#010x} $r7: {:#010x}", r4, r5, r6, r7); - println!("$r8: {:#010x} $r9: {:#010x} $r10: {:#010x} $r11: {:#010x}", r8, r9, r10, r11); + println!( + "$r0: {:#010x} $r1: {:#010x} $r2: {:#010x} $r3: {:#010x}", + r0, r1, r2, r3 + ); + println!( + "$r4: {:#010x} $r5: {:#010x} $r6: {:#010x} $r7: {:#010x}", + r4, r5, r6, r7 + ); + println!( + "$r8: {:#010x} $r9: {:#010x} $r10: {:#010x} $r11: {:#010x}", + r8, r9, r10, r11 + ); println!("$sp: {:#010x} $lr: {:#010x}\n", sp, lr); - + // decide which mode (ARM/Thumb) to use for disasm - let cpsr = uc.reg_read(RegisterARM::CPSR as i32).expect("failed to read CPSR"); + let cpsr = uc + .reg_read(RegisterARM::CPSR as i32) + .expect("failed to read CPSR"); let mut buf = vec![0; size as usize]; - uc.mem_read(addr, &mut buf).expect("failed to read opcode from memory"); + uc.mem_read(addr, &mut buf) + .expect("failed to read opcode from memory"); let ins = if cpsr & 0x20 != 0 { cs_thumb.disasm_all(&buf, size as u64) } else { cs_arm.disasm_all(&buf, size as u64) - }.expect(&format!("failed to disasm at addr {:#010x}", addr)); + } + .expect(&format!("failed to disasm at addr {:#010x}", addr)); println!("$pc: {:#010x}", addr); println!("{}", ins); }); - uc.add_code_hook(code_start, code_end, callback).expect("failed to set debug hook"); + uc.add_code_hook(code_start, code_end, callback) + .expect("failed to set debug hook"); } - -/// Returns a new Unicorn instance with an initialized heap and active sanitizer. -/// +/// Returns a new Unicorn instance with an initialized heap and active sanitizer. +/// /// Introduces an accessible way of dynamic memory allocation for emulation and helps -/// detecting common memory corruption bugs. +/// detecting common memory corruption bugs. /// The allocator makes heavy use of Unicorn hooks for sanitization/ crash amplification /// and thus introduces some overhead. -pub fn init_emu_with_heap(arch: Arch, - mut size: u32, - base_addr: u64, - grow: bool +pub fn init_emu_with_heap( + arch: Arch, + mut size: u32, + base_addr: u64, + grow: bool, ) -> Result>, uc_error> { - let heap = RefCell::new(Heap {real_base: 0 as _, - uc_base: 0, - len: 0, - grow_dynamically: false, - chunk_map: HashMap::new(), - top: 0, - unalloc_hook: 0 as _ }); + let heap = RefCell::new(Heap { + real_base: 0 as _, + uc_base: 0, + len: 0, + grow_dynamically: false, + chunk_map: HashMap::new(), + top: 0, + unalloc_hook: 0 as _, + }); let mut unicorn = super::Unicorn::new(arch, Mode::LITTLE_ENDIAN, heap)?; let mut uc = unicorn.borrow(); // get handle @@ -118,19 +161,36 @@ pub fn init_emu_with_heap(arch: Arch, let null_ptr = ptr::null_mut(); unsafe { // manually mmap space for heap to know location - let arena_ptr = mmap(null_ptr, size as usize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0); - uc.mem_map_ptr(base_addr, size as usize, Permission::READ | Permission::WRITE, arena_ptr)?; - let h = uc.add_mem_hook(HookType::MEM_VALID, base_addr, base_addr + size as u64, Box::new(heap_unalloc))?; + let arena_ptr = mmap( + null_ptr, + size as usize, + PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, + 0, + 0, + ); + uc.mem_map_ptr( + base_addr, + size as usize, + Permission::READ | Permission::WRITE, + arena_ptr, + )?; + let h = uc.add_mem_hook( + HookType::MEM_VALID, + base_addr, + base_addr + size as u64, + Box::new(heap_unalloc), + )?; let chunks = HashMap::new(); let heap: &mut Heap = &mut *uc.get_data().borrow_mut(); heap.real_base = arena_ptr; // heap pointer in process mem heap.uc_base = base_addr; heap.len = size as usize; - /* - let the heap grow dynamically + /* + let the heap grow dynamically (ATTENTION: There are no guarantees that the heap segment will be continuous in process mem any more) */ - heap.grow_dynamically = grow; + heap.grow_dynamically = grow; heap.chunk_map = chunks; heap.top = base_addr; // pointer to top of heap in unicorn mem, increases on allocations heap.unalloc_hook = h; // hook ID, needed to rearrange hooks on allocations @@ -140,12 +200,15 @@ pub fn init_emu_with_heap(arch: Arch, } /// `malloc` for the utils allocator. -/// +/// /// Returns a pointer into memory used as heap and applies -/// canary hooks to detect out-of-bounds accesses. +/// canary hooks to detect out-of-bounds accesses. /// Grows the heap if necessary and if it is configured to, otherwise /// return WRITE_UNMAPPED if there is no space left. -pub fn uc_alloc(uc: &mut super::UnicornHandle>, mut size: u64) -> Result { +pub fn uc_alloc( + uc: &mut super::UnicornHandle>, + mut size: u64, +) -> Result { // 8 byte aligned if size % 8 != 0 { size = ((size / 8) + 1) * 8; @@ -163,7 +226,11 @@ pub fn uc_alloc(uc: &mut super::UnicornHandle>, mut size: u64) -> if increase_by % 8 != 0 { increase_by = ((increase_by / 8) + 1) * 8; } - uc.mem_map(uc_base + len as u64, increase_by, Permission::READ | Permission::WRITE)?; + uc.mem_map( + uc_base + len as u64, + increase_by, + Permission::READ | Permission::WRITE, + )?; uc.get_data().borrow_mut().len += increase_by; len = uc.get_data().borrow_mut().len; } @@ -171,31 +238,57 @@ pub fn uc_alloc(uc: &mut super::UnicornHandle>, mut size: u64) -> // canary hooks uc.add_mem_hook(HookType::MEM_WRITE, addr, addr + 3, Box::new(heap_bo))?; - uc.add_mem_hook(HookType::MEM_READ, addr, addr + 3, Box::new(heap_oob))?; - uc.add_mem_hook(HookType::MEM_WRITE, addr + 4 + size, addr + 4 + size + 3, Box::new(heap_bo))?; - uc.add_mem_hook(HookType::MEM_READ, addr + 4 + size, addr + 4 + size + 3, Box::new(heap_oob))?; - + uc.add_mem_hook(HookType::MEM_READ, addr, addr + 3, Box::new(heap_oob))?; + uc.add_mem_hook( + HookType::MEM_WRITE, + addr + 4 + size, + addr + 4 + size + 3, + Box::new(heap_bo), + )?; + uc.add_mem_hook( + HookType::MEM_READ, + addr + 4 + size, + addr + 4 + size + 3, + Box::new(heap_oob), + )?; + // add new chunk let curr_offset = addr + 4 - uc_base; - let curr_chunk = Chunk {offset: curr_offset, len: size as size_t, freed: false}; - uc.get_data().borrow_mut().chunk_map.insert(addr + 4, curr_chunk); + let curr_chunk = Chunk { + offset: curr_offset, + len: size as size_t, + freed: false, + }; + uc.get_data() + .borrow_mut() + .chunk_map + .insert(addr + 4, curr_chunk); let new_top = uc.get_data().borrow_mut().top + size + 8; // canary*2 #[cfg(debug_assertions)] - println!("[+] New Allocation from {:#010x} to {:#010x} (size: {})", - uc.get_data().borrow().top, uc.get_data().borrow().top + size - 1 + 8, size); - uc.get_data().borrow_mut().top = new_top; + println!( + "[+] New Allocation from {:#010x} to {:#010x} (size: {})", + uc.get_data().borrow().top, + uc.get_data().borrow().top + size - 1 + 8, + size + ); + uc.get_data().borrow_mut().top = new_top; // adjust oob hooks let old_h = uc.get_data().borrow_mut().unalloc_hook; uc.remove_hook(old_h)?; - let new_h = uc.add_mem_hook(HookType::MEM_VALID, new_top, uc_base + len as u64, Box::new(heap_unalloc))?; + let new_h = uc.add_mem_hook( + HookType::MEM_VALID, + new_top, + uc_base + len as u64, + Box::new(heap_unalloc), + )?; uc.get_data().borrow_mut().unalloc_hook = new_h; return Ok(addr + 4); } /// `free` for the utils allocator. -/// +/// /// Marks the chunk to be freed to detect double-frees later on /// and places sanitization hooks over the freed region to detect /// use-after-frees. @@ -208,17 +301,30 @@ pub fn uc_free(uc: &mut super::UnicornHandle>, ptr: u64) -> Result let mut chunk_size = 0; { let mut heap = uc.get_data().borrow_mut(); - let curr_chunk = heap.chunk_map.get_mut(&ptr).expect("failed to find requested chunk on heap"); + let curr_chunk = heap + .chunk_map + .get_mut(&ptr) + .expect("failed to find requested chunk on heap"); chunk_size = curr_chunk.len as u64; curr_chunk.freed = true; } - uc.add_mem_hook(HookType::MEM_VALID, ptr, ptr + chunk_size - 1, Box::new(heap_uaf))?; + uc.add_mem_hook( + HookType::MEM_VALID, + ptr, + ptr + chunk_size - 1, + Box::new(heap_uaf), + )?; } return Ok(()); -} +} - -fn heap_unalloc(uc: super::UnicornHandle>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) { +fn heap_unalloc( + uc: super::UnicornHandle>, + _mem_type: MemType, + addr: u64, + _size: usize, + _val: i64, +) { let arch = uc.get_arch(); let reg = match arch { Arch::X86 => RegisterX86::RIP as i32, @@ -227,15 +333,20 @@ fn heap_unalloc(uc: super::UnicornHandle>, _mem_type: MemType, add Arch::MIPS => RegisterMIPS::PC as i32, Arch::SPARC => RegisterSPARC::PC as i32, Arch::M68K => RegisterM68K::PC as i32, - _ => panic!("Arch not yet supported by unicorn::utils module") + _ => panic!("Arch not yet supported by unicorn::utils module"), }; - let pc = uc.reg_read(reg).expect("failed to read pc"); + let pc = uc.reg_read(reg).expect("failed to read pc"); panic!("ERROR: unicorn-rs Sanitizer: Heap out-of-bounds access of unallocated memory on addr {:#0x}, $pc: {:#010x}", addr, pc); } - -fn heap_oob(uc: super::UnicornHandle>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) { +fn heap_oob( + uc: super::UnicornHandle>, + _mem_type: MemType, + addr: u64, + _size: usize, + _val: i64, +) { let arch = uc.get_arch(); let reg = match arch { Arch::X86 => RegisterX86::RIP as i32, @@ -244,14 +355,22 @@ fn heap_oob(uc: super::UnicornHandle>, _mem_type: MemType, addr: u Arch::MIPS => RegisterMIPS::PC as i32, Arch::SPARC => RegisterSPARC::PC as i32, Arch::M68K => RegisterM68K::PC as i32, - _ => panic!("Arch not yet supported by unicorn::utils module") + _ => panic!("Arch not yet supported by unicorn::utils module"), }; - let pc = uc.reg_read(reg).expect("failed to read pc"); - panic!("ERROR: unicorn-rs Sanitizer: Heap out-of-bounds read on addr {:#0x}, $pc: {:#010x}", addr, pc); + let pc = uc.reg_read(reg).expect("failed to read pc"); + panic!( + "ERROR: unicorn-rs Sanitizer: Heap out-of-bounds read on addr {:#0x}, $pc: {:#010x}", + addr, pc + ); } - -fn heap_bo (uc: super::UnicornHandle>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) { +fn heap_bo( + uc: super::UnicornHandle>, + _mem_type: MemType, + addr: u64, + _size: usize, + _val: i64, +) { let arch = uc.get_arch(); let reg = match arch { Arch::X86 => RegisterX86::RIP as i32, @@ -260,14 +379,22 @@ fn heap_bo (uc: super::UnicornHandle>, _mem_type: MemType, addr: u Arch::MIPS => RegisterMIPS::PC as i32, Arch::SPARC => RegisterSPARC::PC as i32, Arch::M68K => RegisterM68K::PC as i32, - _ => panic!("Arch not yet supported by unicorn::utils module") + _ => panic!("Arch not yet supported by unicorn::utils module"), }; - let pc = uc.reg_read(reg).expect("failed to read pc"); - panic!("ERROR: unicorn-rs Sanitizer: Heap buffer-overflow on addr {:#0x}, $pc: {:#010x}", addr, pc); + let pc = uc.reg_read(reg).expect("failed to read pc"); + panic!( + "ERROR: unicorn-rs Sanitizer: Heap buffer-overflow on addr {:#0x}, $pc: {:#010x}", + addr, pc + ); } - -fn heap_uaf (uc: super::UnicornHandle>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) { +fn heap_uaf( + uc: super::UnicornHandle>, + _mem_type: MemType, + addr: u64, + _size: usize, + _val: i64, +) { let arch = uc.get_arch(); let reg = match arch { Arch::X86 => RegisterX86::RIP as i32, @@ -276,14 +403,15 @@ fn heap_uaf (uc: super::UnicornHandle>, _mem_type: MemType, addr: Arch::MIPS => RegisterMIPS::PC as i32, Arch::SPARC => RegisterSPARC::PC as i32, Arch::M68K => RegisterM68K::PC as i32, - _ => panic!("Arch not yet supported by unicorn::utils module") + _ => panic!("Arch not yet supported by unicorn::utils module"), }; - let pc = uc.reg_read(reg).expect("failed to read pc"); - panic!("ERROR: unicorn-rs Sanitizer: Heap use-after-free on addr {:#0x}, $pc: {:#010x}", addr, pc); - + let pc = uc.reg_read(reg).expect("failed to read pc"); + panic!( + "ERROR: unicorn-rs Sanitizer: Heap use-after-free on addr {:#0x}, $pc: {:#010x}", + addr, pc + ); } - pub fn vmmap(uc: &mut super::UnicornHandle) { let regions = uc .mem_regions() diff --git a/bindings/rust/src/x86.rs b/bindings/rust/src/x86.rs index 3fb85946..03c92176 100644 --- a/bindings/rust/src/x86.rs +++ b/bindings/rust/src/x86.rs @@ -278,4 +278,4 @@ pub struct X86Mmr { pub base: u64, pub limit: u32, pub flags: u32, -} \ No newline at end of file +} diff --git a/bindings/rust/tests/unicorn.rs b/bindings/rust/tests/unicorn.rs index 2fbcb5ad..fc307815 100644 --- a/bindings/rust/tests/unicorn.rs +++ b/bindings/rust/tests/unicorn.rs @@ -2,8 +2,8 @@ use std::cell::RefCell; use std::rc::Rc; -use unicorn::{RegisterARM, RegisterX86, InsnSysX86, RegisterMIPS, RegisterPPC}; -use unicorn::unicorn_const::{Mode, Arch, Permission, MemType, HookType, SECOND_SCALE, uc_error}; +use unicorn::unicorn_const::{uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE}; +use unicorn::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86}; pub static X86_REGISTERS: [RegisterX86; 145] = [ RegisterX86::AH, @@ -159,7 +159,8 @@ type Unicorn<'a> = unicorn::UnicornHandle<'a, u32>; fn emulate_x86() { let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::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)); @@ -170,10 +171,7 @@ fn emulate_x86() { (Err(uc_error::WRITE_UNMAPPED)) ); - assert_eq!( - emu.mem_map(0x1000, 0x4000, Permission::ALL), - Ok(()) - ); + assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); assert_eq!( emu.mem_read_as_vec(0x1000, x86_code32.len()), @@ -212,12 +210,10 @@ fn x86_code_callback() { let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0) + .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_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); let hook = emu @@ -245,12 +241,10 @@ fn x86_intr_callback() { let x86_code32: Vec = vec![0xcd, 0x80]; // INT 0x80; - let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0) + .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_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); let hook = emu @@ -277,19 +271,17 @@ 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 |_: 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)); - }; + let callback = + move |_: 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)); + }; // mov eax, 0xdeadbeef; // mov [0x2000], eax; @@ -298,12 +290,10 @@ fn x86_mem_callback() { 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xA3, 0x00, 0x20, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x01, 0x00, ]; - let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0) + .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_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); let hook = emu @@ -338,15 +328,15 @@ fn x86_insn_in_callback() { let x86_code32: Vec = vec![0xe5, 0x10]; // IN eax, 0x10; - let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0) + .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_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); - let hook = emu.add_insn_in_hook(callback).expect("failed to add in hook"); + let hook = emu + .add_insn_in_hook(callback) + .expect("failed to add in hook"); assert_eq!( emu.emu_start( @@ -375,15 +365,15 @@ fn x86_insn_out_callback() { let x86_code32: Vec = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al; - let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0) + .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_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); - let hook = emu.add_insn_out_hook(callback).expect("failed to add out hook"); + let hook = emu + .add_insn_out_hook(callback) + .expect("failed to add out hook"); assert_eq!( emu.emu_start( @@ -417,15 +407,15 @@ fn x86_insn_sys_callback() { 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, ]; - let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_64, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_64, 0) + .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_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); - let hook = emu.add_insn_sys_hook(InsnSysX86::SYSCALL, 1, 0, callback).expect("failed to add syscall hook"); + let hook = emu + .add_insn_sys_hook(InsnSysX86::SYSCALL, 1, 0, callback) + .expect("failed to add syscall hook"); assert_eq!( emu.emu_start( @@ -444,7 +434,8 @@ fn x86_insn_sys_callback() { fn emulate_arm() { let arm_code32: Vec = vec![0x83, 0xb0]; // sub sp, #0xc - let mut unicorn = unicorn::Unicorn::new(Arch::ARM, Mode::THUMB, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::ARM, Mode::THUMB, 0) + .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)); @@ -455,10 +446,7 @@ fn emulate_arm() { (Err(uc_error::WRITE_UNMAPPED)) ); - assert_eq!( - emu.mem_map(0x1000, 0x4000, Permission::ALL), - Ok(()) - ); + assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &arm_code32), Ok(())); assert_eq!( emu.mem_read_as_vec(0x1000, arm_code32.len()), @@ -487,12 +475,10 @@ fn emulate_arm() { fn emulate_mips() { let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456; - let mut unicorn = unicorn::Unicorn::new(Arch::MIPS, Mode::MODE_32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::MIPS, Mode::MODE_32, 0) + .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_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()), @@ -515,12 +501,10 @@ fn emulate_mips() { fn emulate_ppc() { let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3 - let mut unicorn = unicorn::Unicorn::new(Arch::PPC, Mode::PPC32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::PPC, Mode::PPC32, 0) + .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_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()), @@ -542,12 +526,10 @@ fn emulate_ppc() { #[test] fn mem_unmapping() { - let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0) + .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_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(())); } @@ -557,7 +539,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::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0) + .expect("failed to initialize unicorn instance"); let mut emu = unicorn.borrow(); // Attempt to write to memory before mapping it. @@ -635,12 +618,10 @@ fn x86_context_save_and_restore() { let x86_code: Vec = vec![ 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, ]; - let mut unicorn = unicorn::Unicorn::new(Arch::X86, mode, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::X86, mode, 0) + .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_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); let _ = emu.emu_start( 0x1000, @@ -654,12 +635,16 @@ 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::Unicorn::new(Arch::X86, mode, 0).expect("failed to initialize unicorn instance"); + let mut unicorn2 = unicorn::Unicorn::new(Arch::X86, mode, 0) + .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 as i32), + emu.reg_read(*register as i32) + ); } } } @@ -668,7 +653,7 @@ fn x86_context_save_and_restore() { fn x86_block_callback() { #[derive(PartialEq, Debug)] struct BlockExpectation(u64, u32); - let expects = vec![BlockExpectation(0x1000, 2)]; + let expects = vec![BlockExpectation(0x1000, 2), BlockExpectation(0x1000, 2)]; let blocks: Vec = Vec::new(); let blocks_cell = Rc::new(RefCell::new(blocks)); @@ -680,16 +665,19 @@ fn x86_block_callback() { let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance"); + let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0) + .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_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); - let hook = emu.add_block_hook(callback).expect("failed to add block hook"); - assert_eq!(emu.emu_start(0x1000, 0x1002, 10 * SECOND_SCALE, 1000), Ok(())); + let hook = emu + .add_block_hook(callback) + .expect("failed to add block hook"); + assert_eq!( + emu.emu_start(0x1000, 0x1002, 10 * SECOND_SCALE, 1000), + Ok(()) + ); assert_eq!(expects, *blocks_cell.borrow()); assert_eq!(emu.remove_hook(hook), Ok(())); }