Improve Rust bindings (#1367)

* fixed tests

* constant readability

* HookType as bitflags

* Mode as bitflags

* improve bitflags

* cargo fmt

* removed unnecessary "as usize"
This commit is contained in:
Simon Wörner 2021-05-08 12:48:50 +02:00 committed by GitHub
parent 1e457ecc66
commit 66df39ebc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 620 additions and 443 deletions

View File

@ -1,7 +1,4 @@
use std::{ use std::{env, process::Command};
env,
process::Command,
};
use build_helper::rustc::{link_lib, link_search}; use build_helper::rustc::{link_lib, link_search};
@ -9,15 +6,15 @@ fn main() {
println!("cargo:rerun-if-changed=unicorn"); println!("cargo:rerun-if-changed=unicorn");
let out_dir = env::var("OUT_DIR").unwrap(); let out_dir = env::var("OUT_DIR").unwrap();
let unicorn = "libunicorn.a"; let unicorn = "libunicorn.a";
let _ = Command::new("cp") let _ = Command::new("cp")
.current_dir("../..") .current_dir("../..")
.arg(&unicorn) .arg(&unicorn)
.arg(&out_dir) .arg(&out_dir)
.status() .status()
.unwrap(); .unwrap();
link_search( link_search(
Some(build_helper::SearchKind::Native), Some(build_helper::SearchKind::Native),
build_helper::out_dir()); build_helper::out_dir(),
);
link_lib(Some(build_helper::LibKind::Static), "unicorn"); link_lib(Some(build_helper::LibKind::Static), "unicorn");
} }

View File

@ -125,7 +125,6 @@ pub enum RegisterARM {
CONTROL = 117, CONTROL = 117,
XPSR = 118, XPSR = 118,
ENDING = 119, ENDING = 119,
// alias registers // alias registers
// (assoc) R13 = 12, // (assoc) R13 = 12,
// (assoc) R14 = 10, // (assoc) R14 = 10,
@ -144,4 +143,4 @@ impl RegisterARM {
pub const SL: RegisterARM = RegisterARM::R10; pub const SL: RegisterARM = RegisterARM::R10;
pub const FP: RegisterARM = RegisterARM::R11; pub const FP: RegisterARM = RegisterARM::R11;
pub const IP: RegisterARM = RegisterARM::R12; pub const IP: RegisterARM = RegisterARM::R12;
} }

View File

@ -265,4 +265,4 @@ pub enum RegisterARM64 {
// pseudo registers // pseudo registers
PC = 260, PC = 260,
} }

View File

@ -1,11 +1,10 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(dead_code)] #![allow(dead_code)]
use super::unicorn_const::*;
use libc::{c_char, c_int};
use std::ffi::c_void; use std::ffi::c_void;
use std::pin::Pin; use std::pin::Pin;
use libc::{c_char, c_int};
use super::unicorn_const::*;
pub type uc_handle = *mut c_void; pub type uc_handle = *mut c_void;
pub type uc_hook = *mut c_void; pub type uc_hook = *mut c_void;
@ -42,8 +41,12 @@ extern "C" {
ptr: *mut c_void, ptr: *mut c_void,
) -> uc_error; ) -> uc_error;
pub fn uc_mem_unmap(engine: uc_handle, address: u64, size: libc::size_t) -> 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) pub fn uc_mem_protect(
-> uc_error; engine: uc_handle,
address: u64,
size: libc::size_t,
perms: u32,
) -> uc_error;
pub fn uc_mem_regions( pub fn uc_mem_regions(
engine: uc_handle, engine: uc_handle,
regions: *const *const MemRegion, regions: *const *const MemRegion,
@ -74,102 +77,154 @@ extern "C" {
pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error; pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error;
} }
pub struct CodeHook<D> { pub struct CodeHook<D> {
pub unicorn: *mut crate::UnicornInner<D>, pub unicorn: *mut crate::UnicornInner<D>,
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u64, u32)> pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u64, u32)>,
} }
pub struct BlockHook<D> { pub struct BlockHook<D> {
pub unicorn: *mut crate::UnicornInner<D>, pub unicorn: *mut crate::UnicornInner<D>,
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u64, u32)> pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u64, u32)>,
} }
pub struct MemHook<D> { pub struct MemHook<D> {
pub unicorn: *mut crate::UnicornInner<D>, pub unicorn: *mut crate::UnicornInner<D>,
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, MemType, u64, usize, i64)> pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, MemType, u64, usize, i64)>,
} }
pub struct InterruptHook<D> { pub struct InterruptHook<D> {
pub unicorn: *mut crate::UnicornInner<D>, pub unicorn: *mut crate::UnicornInner<D>,
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32)> pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32)>,
} }
pub struct InstructionInHook<D> { pub struct InstructionInHook<D> {
pub unicorn: *mut crate::UnicornInner<D>, pub unicorn: *mut crate::UnicornInner<D>,
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32, usize)> pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32, usize)>,
} }
pub struct InstructionOutHook<D> { pub struct InstructionOutHook<D> {
pub unicorn: *mut crate::UnicornInner<D>, pub unicorn: *mut crate::UnicornInner<D>,
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32, usize, u32)> pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32, usize, u32)>,
} }
pub struct InstructionSysHook<D> { pub struct InstructionSysHook<D> {
pub unicorn: *mut crate::UnicornInner<D>, pub unicorn: *mut crate::UnicornInner<D>,
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>)> pub callback: Box<dyn FnMut(crate::UnicornHandle<D>)>,
} }
pub extern "C" fn code_hook_proxy<D>(uc: uc_handle, address: u64, size: u32, user_data: *mut CodeHook<D>) { pub extern "C" fn code_hook_proxy<D>(
uc: uc_handle,
address: u64,
size: u32,
user_data: *mut CodeHook<D>,
) {
let unicorn = unsafe { &mut *(*user_data).unicorn }; let unicorn = unsafe { &mut *(*user_data).unicorn };
let callback = &mut unsafe { &mut *(*user_data).callback }; let callback = &mut unsafe { &mut *(*user_data).callback };
assert_eq!(uc, unicorn.uc); 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<D>(uc: uc_handle, address: u64, size: u32, user_data: *mut BlockHook<D>) { pub extern "C" fn block_hook_proxy<D>(
uc: uc_handle,
address: u64,
size: u32,
user_data: *mut BlockHook<D>,
) {
let unicorn = unsafe { &mut *(*user_data).unicorn }; let unicorn = unsafe { &mut *(*user_data).unicorn };
let callback = &mut unsafe { &mut *(*user_data).callback }; let callback = &mut unsafe { &mut *(*user_data).callback };
assert_eq!(uc, unicorn.uc); 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<D>(uc: uc_handle, pub extern "C" fn mem_hook_proxy<D>(
mem_type: MemType, uc: uc_handle,
address: u64, mem_type: MemType,
size: u32, address: u64,
value: i64, size: u32,
user_data: *mut MemHook<D>) value: i64,
{ user_data: *mut MemHook<D>,
) {
let unicorn = unsafe { &mut *(*user_data).unicorn }; let unicorn = unsafe { &mut *(*user_data).unicorn };
let callback = &mut unsafe { &mut *(*user_data).callback }; let callback = &mut unsafe { &mut *(*user_data).callback };
assert_eq!(uc, unicorn.uc); 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<D>(uc: uc_handle, value: u32, user_data: *mut InterruptHook<D>) { pub extern "C" fn intr_hook_proxy<D>(uc: uc_handle, value: u32, user_data: *mut InterruptHook<D>) {
let unicorn = unsafe { &mut *(*user_data).unicorn }; let unicorn = unsafe { &mut *(*user_data).unicorn };
let callback = &mut unsafe { &mut *(*user_data).callback }; let callback = &mut unsafe { &mut *(*user_data).callback };
assert_eq!(uc, unicorn.uc); 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<D>( pub extern "C" fn insn_in_hook_proxy<D>(
uc: uc_handle, uc: uc_handle,
port: u32, port: u32,
size: usize, size: usize,
user_data: *mut InstructionInHook<D>) { user_data: *mut InstructionInHook<D>,
) {
let unicorn = unsafe { &mut *(*user_data).unicorn }; let unicorn = unsafe { &mut *(*user_data).unicorn };
let callback = &mut unsafe { &mut *(*user_data).callback }; let callback = &mut unsafe { &mut *(*user_data).callback };
assert_eq!(uc, unicorn.uc); 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<D>( pub extern "C" fn insn_out_hook_proxy<D>(
uc: uc_handle, uc: uc_handle,
port: u32, port: u32,
size: usize, size: usize,
value: u32, value: u32,
user_data: *mut InstructionOutHook<D>) { user_data: *mut InstructionOutHook<D>,
) {
let unicorn = unsafe { &mut *(*user_data).unicorn }; let unicorn = unsafe { &mut *(*user_data).unicorn };
let callback = &mut unsafe { &mut *(*user_data).callback }; let callback = &mut unsafe { &mut *(*user_data).callback };
assert_eq!(uc, unicorn.uc); 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<D>(uc: uc_handle, user_data: *mut InstructionSysHook<D>) { pub extern "C" fn insn_sys_hook_proxy<D>(uc: uc_handle, user_data: *mut InstructionSysHook<D>) {
let unicorn = unsafe { &mut *(*user_data).unicorn }; let unicorn = unsafe { &mut *(*user_data).unicorn };
let callback = &mut unsafe { &mut *(*user_data).callback }; let callback = &mut unsafe { &mut *(*user_data).callback };
assert_eq!(uc, unicorn.uc); assert_eq!(uc, unicorn.uc);
callback(crate::UnicornHandle { inner: unsafe { Pin::new_unchecked(unicorn) } }); callback(crate::UnicornHandle {
inner: unsafe { Pin::new_unchecked(unicorn) },
});
} }

View File

@ -1,7 +1,6 @@
//! Bindings for the Unicorn emulator. //! Bindings for the Unicorn emulator.
//! //!
//! //!
//! //!
//! # Example use //! # Example use
//! //!
@ -9,18 +8,18 @@
//! //!
//! use unicorn::RegisterARM; //! use unicorn::RegisterARM;
//! use unicorn::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE}; //! use unicorn::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE};
//! //!
//! fn main() { //! fn main() {
//! let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 //! let arm_code32: Vec<u8> = 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 unicorn = unicorn::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN, 0).expect("failed to initialize Unicorn instance");
//! let mut emu = unicorn.borrow(); //! let mut emu = unicorn.borrow();
//! emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page"); //! 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.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::R0 as i32, 123).expect("failed write R0");
//! emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5"); //! 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); //! 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::R0 as i32), Ok(100));
//! assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337)); //! assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337));
@ -29,8 +28,8 @@
//! //!
mod ffi; mod ffi;
pub mod utils;
pub mod unicorn_const; pub mod unicorn_const;
pub mod utils;
mod arm; mod arm;
mod arm64; mod arm64;
@ -39,21 +38,13 @@ mod mips;
mod ppc; mod ppc;
mod sparc; mod sparc;
mod x86; mod x86;
pub use crate::{ pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, sparc::*, x86::*};
arm64::*,
arm::*,
m68k::*,
mips::*,
ppc::*,
sparc::*,
x86::*,
};
use std::ffi::c_void;
use std::collections::HashMap;
use ffi::uc_handle; use ffi::uc_handle;
use std::pin::Pin; use std::collections::HashMap;
use std::ffi::c_void;
use std::marker::PhantomPinned; use std::marker::PhantomPinned;
use std::pin::Pin;
use unicorn_const::*; use unicorn_const::*;
#[derive(Debug)] #[derive(Debug)]
@ -79,13 +70,13 @@ impl Drop for Context {
#[derive(Debug)] #[derive(Debug)]
/// A Unicorn emulator instance. /// A Unicorn emulator instance.
pub struct Unicorn<D> { pub struct Unicorn<D> {
inner: Pin<Box<UnicornInner<D>>> inner: Pin<Box<UnicornInner<D>>>,
} }
#[derive(Debug)] #[derive(Debug)]
/// Handle used to safely access exposed functions and data of a Unicorn instance. /// Handle used to safely access exposed functions and data of a Unicorn instance.
pub struct UnicornHandle<'a, D> { pub struct UnicornHandle<'a, D> {
inner: Pin<&'a mut UnicornInner<D>> inner: Pin<&'a mut UnicornInner<D>>,
} }
/// Internal Management struct /// Internal Management struct
@ -100,39 +91,40 @@ pub struct UnicornInner<D> {
pub insn_out_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionOutHook<D>>>, pub insn_out_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionOutHook<D>>>,
pub insn_sys_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionSysHook<D>>>, pub insn_sys_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionSysHook<D>>>,
pub data: D, pub data: D,
_pin: PhantomPinned _pin: PhantomPinned,
} }
impl<D> Unicorn<D> { impl<D> Unicorn<D> {
/// Create a new instance of the unicorn engine for the specified architecture /// Create a new instance of the unicorn engine for the specified architecture
/// and hardware mode. /// and hardware mode.
pub fn new(arch: Arch, mode: Mode, data: D) pub fn new(arch: Arch, mode: Mode, data: D) -> Result<Unicorn<D>, uc_error> {
-> Result<Unicorn<D>, uc_error> {
let mut handle = std::ptr::null_mut(); let mut handle = std::ptr::null_mut();
let err = unsafe { ffi::uc_open(arch, mode, &mut handle) }; let err = unsafe { ffi::uc_open(arch, mode, &mut handle) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(Unicorn { Ok(Unicorn {
inner: Box::pin(UnicornInner { inner: Box::pin(UnicornInner {
uc: handle, uc: handle,
arch: arch, arch: arch,
code_hooks: HashMap::new(), code_hooks: HashMap::new(),
block_hooks: HashMap::new(), block_hooks: HashMap::new(),
mem_hooks: HashMap::new(), mem_hooks: HashMap::new(),
intr_hooks: HashMap::new(), intr_hooks: HashMap::new(),
insn_in_hooks: HashMap::new(), insn_in_hooks: HashMap::new(),
insn_out_hooks: HashMap::new(), insn_out_hooks: HashMap::new(),
insn_sys_hooks: HashMap::new(), insn_sys_hooks: HashMap::new(),
data: data, data: data,
_pin: std::marker::PhantomPinned _pin: std::marker::PhantomPinned,
})}) }),
})
} else { } else {
Err(err) Err(err)
} }
} }
pub fn borrow<'a>(&'a mut self) -> UnicornHandle<'a, D> { 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<D> std::fmt::Debug for UnicornInner<D> {
impl<'a, D> UnicornHandle<'a, D> { impl<'a, D> UnicornHandle<'a, D> {
/// Return whatever data was passed during initialization. /// Return whatever data was passed during initialization.
/// ///
/// For an example, have a look at utils::init_emu_with_heap where /// For an example, have a look at utils::init_emu_with_heap where
/// a struct is passed which is used for a custom allocator. /// a struct is passed which is used for a custom allocator.
pub fn get_data(&self) -> &D { 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`. /// `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. /// `ptr` is a pointer to the provided memory region that will be used by the emulator.
pub fn mem_map_ptr(&mut self, pub fn mem_map_ptr(
address: u64, &mut self,
size: usize, address: u64,
perms: Permission, size: usize,
ptr: *mut c_void perms: Permission,
ptr: *mut c_void,
) -> Result<(), uc_error> { ) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_map_ptr(self.inner.uc, address, size, perms.bits(), ptr) }; let err = unsafe { ffi::uc_mem_map_ptr(self.inner.uc, address, size, perms.bits(), ptr) };
if err == uc_error::OK { 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`. /// `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`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`.
pub fn mem_map(&mut self, pub fn mem_map(
address: u64, &mut self,
size: libc::size_t, address: u64,
perms: Permission size: libc::size_t,
perms: Permission,
) -> Result<(), uc_error> { ) -> 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 { 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`. /// `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`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`.
pub fn mem_unmap(&mut self, pub fn mem_unmap(&mut self, address: u64, size: libc::size_t) -> Result<(), uc_error> {
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 { if err == uc_error::OK {
Ok(()) Ok(())
@ -281,10 +272,11 @@ impl<'a, D> UnicornHandle<'a, D> {
/// ///
/// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `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`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`.
pub fn mem_protect(&mut self, pub fn mem_protect(
address: u64, &mut self,
size: libc::size_t, address: u64,
perms: Permission size: libc::size_t,
perms: Permission,
) -> Result<(), uc_error> { ) -> 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 { if err == uc_error::OK {
@ -296,7 +288,8 @@ impl<'a, D> UnicornHandle<'a, D> {
/// Write an unsigned value from a register. /// Write an unsigned value from a register.
pub fn reg_write<T: Into<i32>>(&mut self, regid: T, value: u64) -> Result<(), uc_error> { pub fn reg_write<T: Into<i32>>(&mut self, regid: T, value: u64) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), &value as *const _ as _) }; let err =
unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), &value as *const _ as _) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -305,7 +298,7 @@ impl<'a, D> UnicornHandle<'a, D> {
} }
/// Write variable sized values into registers. /// Write variable sized values into registers.
/// ///
/// The user has to make sure that the buffer length matches the register size. /// 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)). /// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: Box<[u8]>) -> Result<(), uc_error> { pub fn reg_write_long<T: Into<i32>>(&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. /// Read an unsigned value from a register.
/// ///
/// Not to be used with registers larger than 64 bit. /// Not to be used with registers larger than 64 bit.
pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> { pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> {
let mut value: u64 = 0; 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 { if err == uc_error::OK {
Ok(value) Ok(value)
} else { } else {
@ -331,7 +325,7 @@ impl<'a, D> UnicornHandle<'a, D> {
} }
/// Read 128, 256 or 512 bit register value into heap allocated byte array. /// 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)). /// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
pub fn reg_read_long<T: Into<i32>>(&self, regid: T) -> Result<Box<[u8]>, uc_error> { pub fn reg_read_long<T: Into<i32>>(&self, regid: T) -> Result<Box<[u8]>, uc_error> {
let err: uc_error; let err: uc_error;
@ -341,29 +335,39 @@ impl<'a, D> UnicornHandle<'a, D> {
let curr_arch = self.get_arch(); let curr_arch = self.get_arch();
if curr_arch == Arch::X86 { if curr_arch == Arch::X86 {
if curr_reg_id >= x86::RegisterX86::XMM0 as i32 && curr_reg_id <= x86::RegisterX86::XMM31 as i32 { if curr_reg_id >= x86::RegisterX86::XMM0 as i32
value = vec![0; 16 as usize]; && curr_reg_id <= x86::RegisterX86::XMM31 as i32
} else if curr_reg_id >= x86::RegisterX86::YMM0 as i32 && curr_reg_id <= x86::RegisterX86::YMM31 as i32 { {
value = vec![0; 32 as usize]; value = vec![0; 16];
} else if curr_reg_id >= x86::RegisterX86::ZMM0 as i32 && curr_reg_id <= x86::RegisterX86::ZMM31 as i32 { } else if curr_reg_id >= x86::RegisterX86::YMM0 as i32
value = vec![0; 64 as usize]; && curr_reg_id <= x86::RegisterX86::YMM31 as i32
} else if curr_reg_id == x86::RegisterX86::GDTR as i32 || {
curr_reg_id == x86::RegisterX86::IDTR as i32 { value = vec![0; 32];
value = vec![0; 10 as usize]; // 64 bit base address in IA-32e mode } 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 { } else {
return Err(uc_error::ARG) return Err(uc_error::ARG);
} }
} else if curr_arch == Arch::ARM64 { } else if curr_arch == Arch::ARM64 {
if (curr_reg_id >= arm64::RegisterARM64::Q0 as i32 && curr_reg_id <= arm64::RegisterARM64::Q31 as i32) || if (curr_reg_id >= arm64::RegisterARM64::Q0 as i32
(curr_reg_id >= arm64::RegisterARM64::V0 as i32 && curr_reg_id <= arm64::RegisterARM64::V31 as i32) { && curr_reg_id <= arm64::RegisterARM64::Q31 as i32)
value = vec![0; 16 as usize]; || (curr_reg_id >= arm64::RegisterARM64::V0 as i32
&& curr_reg_id <= arm64::RegisterARM64::V31 as i32)
{
value = vec![0; 16];
} else { } else {
return Err(uc_error::ARG) return Err(uc_error::ARG);
} }
} else { } 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 _) }; err = unsafe { ffi::uc_reg_read(self.inner.uc, curr_reg_id, value.as_mut_ptr() as _) };
if err == uc_error::OK { if err == uc_error::OK {
@ -377,7 +381,8 @@ impl<'a, D> UnicornHandle<'a, D> {
/// Read a signed 32-bit value from a register. /// Read a signed 32-bit value from a register.
pub fn reg_read_i32<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> { pub fn reg_read_i32<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> {
let mut value: i32 = 0; 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 { if err == uc_error::OK {
Ok(value) Ok(value)
} else { } else {
@ -392,14 +397,15 @@ impl<'a, D> UnicornHandle<'a, D> {
end: u64, end: u64,
callback: F, callback: F,
) -> Result<ffi::uc_hook, uc_error> ) -> Result<ffi::uc_hook, uc_error>
where F: FnMut(UnicornHandle<D>, u64, u32) where
F: FnMut(UnicornHandle<D>, u64, u32),
{ {
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::CodeHook { let mut user_data = Box::new(ffi::CodeHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback), callback: Box::new(callback),
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner.uc,
@ -412,7 +418,9 @@ impl<'a, D> UnicornHandle<'a, D> {
) )
}; };
if err == uc_error::OK { 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) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -420,11 +428,9 @@ impl<'a, D> UnicornHandle<'a, D> {
} }
/// Add a block hook. /// Add a block hook.
pub fn add_block_hook<F: 'static>( pub fn add_block_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
&mut self, where
callback: F, F: FnMut(UnicornHandle<D>, u64, u32),
) -> Result<ffi::uc_hook, uc_error>
where F: FnMut(UnicornHandle<D>, u64, u32)
{ {
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::BlockHook { let mut user_data = Box::new(ffi::BlockHook {
@ -444,7 +450,9 @@ impl<'a, D> UnicornHandle<'a, D> {
) )
}; };
if err == uc_error::OK { 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) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -459,18 +467,19 @@ impl<'a, D> UnicornHandle<'a, D> {
end: u64, end: u64,
callback: F, callback: F,
) -> Result<ffi::uc_hook, uc_error> ) -> Result<ffi::uc_hook, uc_error>
where F: FnMut(UnicornHandle<D>, MemType, u64, usize, i64) where
F: FnMut(UnicornHandle<D>, 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); return Err(uc_error::ARG);
} }
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::MemHook { let mut user_data = Box::new(ffi::MemHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback), callback: Box::new(callback),
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner.uc,
@ -483,7 +492,9 @@ impl<'a, D> UnicornHandle<'a, D> {
) )
}; };
if err == uc_error::OK { 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) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -491,18 +502,16 @@ impl<'a, D> UnicornHandle<'a, D> {
} }
/// Add an interrupt hook. /// Add an interrupt hook.
pub fn add_intr_hook<F: 'static>( pub fn add_intr_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
&mut self, where
callback: F, F: FnMut(UnicornHandle<D>, u32),
) -> Result<ffi::uc_hook, uc_error> {
where F: FnMut(UnicornHandle<D>, u32)
{
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::InterruptHook { let mut user_data = Box::new(ffi::InterruptHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback), callback: Box::new(callback),
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner.uc,
@ -515,7 +524,9 @@ impl<'a, D> UnicornHandle<'a, D> {
) )
}; };
if err == uc_error::OK { 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) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -523,18 +534,16 @@ impl<'a, D> UnicornHandle<'a, D> {
} }
/// Add hook for x86 IN instruction. /// Add hook for x86 IN instruction.
pub fn add_insn_in_hook<F: 'static>( pub fn add_insn_in_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
&mut self, where
callback: F, F: FnMut(UnicornHandle<D>, u32, usize),
) -> Result<ffi::uc_hook, uc_error> {
where F: FnMut(UnicornHandle<D>, u32, usize)
{
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::InstructionInHook { let mut user_data = Box::new(ffi::InstructionInHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback), callback: Box::new(callback),
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner.uc,
@ -548,7 +557,9 @@ impl<'a, D> UnicornHandle<'a, D> {
) )
}; };
if err == uc_error::OK { 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) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -556,18 +567,16 @@ impl<'a, D> UnicornHandle<'a, D> {
} }
/// Add hook for x86 OUT instruction. /// Add hook for x86 OUT instruction.
pub fn add_insn_out_hook<F: 'static>( pub fn add_insn_out_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
&mut self, where
callback: F, F: FnMut(UnicornHandle<D>, u32, usize, u32),
) -> Result<ffi::uc_hook, uc_error> {
where F: FnMut(UnicornHandle<D>, u32, usize, u32)
{
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::InstructionOutHook { let mut user_data = Box::new(ffi::InstructionOutHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback), callback: Box::new(callback),
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner.uc,
@ -581,7 +590,9 @@ impl<'a, D> UnicornHandle<'a, D> {
) )
}; };
if err == uc_error::OK { 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) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -596,14 +607,15 @@ impl<'a, D> UnicornHandle<'a, D> {
end: u64, end: u64,
callback: F, callback: F,
) -> Result<ffi::uc_hook, uc_error> ) -> Result<ffi::uc_hook, uc_error>
where F: FnMut(UnicornHandle<D>) where
{ F: FnMut(UnicornHandle<D>),
{
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::InstructionSysHook { let mut user_data = Box::new(ffi::InstructionSysHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback), callback: Box::new(callback),
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner.uc,
@ -617,7 +629,9 @@ impl<'a, D> UnicornHandle<'a, D> {
) )
}; };
if err == uc_error::OK { 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) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -681,13 +695,15 @@ impl<'a, D> UnicornHandle<'a, D> {
} }
/// Allocate and return an empty Unicorn context. /// Allocate and return an empty Unicorn context.
/// ///
/// To be populated via context_save. /// To be populated via context_save.
pub fn context_alloc(&self) -> Result<Context, uc_error> { pub fn context_alloc(&self) -> Result<Context, uc_error> {
let mut empty_context: ffi::uc_context = Default::default(); let mut empty_context: ffi::uc_context = Default::default();
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut empty_context) }; let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut empty_context) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(Context { context: empty_context }) Ok(Context {
context: empty_context,
})
} else { } else {
Err(err) Err(err)
} }
@ -704,9 +720,9 @@ impl<'a, D> UnicornHandle<'a, D> {
} }
/// Allocate and return a Context struct initialized with the current CPU context. /// Allocate and return a Context struct initialized with the current CPU context.
/// ///
/// This can be used for fast rollbacks with context_restore. /// 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. /// individually to avoid unnecessary allocations.
pub fn context_init(&self) -> Result<Context, uc_error> { pub fn context_init(&self) -> Result<Context, uc_error> {
let mut new_context: ffi::uc_context = Default::default(); 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) }; let err = unsafe { ffi::uc_context_save(self.inner.uc, new_context) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(Context { context: new_context }) Ok(Context {
context: new_context,
})
} else { } else {
unsafe { ffi::uc_free(new_context) }; unsafe { ffi::uc_free(new_context) };
Err(err) Err(err)
@ -724,7 +742,7 @@ impl<'a, D> UnicornHandle<'a, D> {
} }
/// Restore a previously saved Unicorn context. /// Restore a previously saved Unicorn context.
/// ///
/// Perform a quick rollback of the CPU context, including registers and some /// Perform a quick rollback of the CPU context, including registers and some
/// internal metadata. Contexts may not be shared across engine instances with /// internal metadata. Contexts may not be shared across engine instances with
/// differing arches or modes. Memory has to be restored manually, if needed. /// 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 /// 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 /// 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). /// to emulate (emulate all the available instructions if set to 0).
pub fn emu_start(&mut self, pub fn emu_start(
begin: u64, &mut self,
until: u64, begin: u64,
timeout: u64, until: u64,
count: usize timeout: u64,
count: usize,
) -> Result<(), uc_error> { ) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_emu_start(self.inner.uc, begin, until, timeout, count as _) }; let err = unsafe { ffi::uc_emu_start(self.inner.uc, begin, until, timeout, count as _) };
if err == uc_error::OK { if err == uc_error::OK {
@ -771,7 +790,7 @@ impl<'a, D> UnicornHandle<'a, D> {
} }
/// Query the internal status of the engine. /// Query the internal status of the engine.
/// ///
/// supported: MODE, PAGE_SIZE, ARCH /// supported: MODE, PAGE_SIZE, ARCH
pub fn query(&self, query: Query) -> Result<usize, uc_error> { pub fn query(&self, query: Query) -> Result<usize, uc_error> {
let mut result: libc::size_t = Default::default(); let mut result: libc::size_t = Default::default();
@ -782,6 +801,4 @@ impl<'a, D> UnicornHandle<'a, D> {
Err(err) Err(err)
} }
} }
} }

View File

@ -21,4 +21,4 @@ pub enum RegisterM68K {
D7, D7,
SR, SR,
PC, PC,
} }

View File

@ -153,4 +153,4 @@ pub enum RegisterMIPS {
MPL0 = 134, MPL0 = 134,
MPL1 = 135, MPL1 = 135,
MPL2 = 136, MPL2 = 136,
} }

View File

@ -38,5 +38,5 @@ pub enum RegisterPPC {
GPR28 = 30, GPR28 = 30,
GPR29 = 31, GPR29 = 31,
GPR30 = 32, GPR30 = 32,
GPR31 = 33 GPR31 = 33,
} }

View File

@ -91,4 +91,4 @@ pub enum RegisterSPARC {
Y = 86, Y = 86,
XCC = 87, XCC = 87,
PC = 88, PC = 88,
} }

View File

@ -6,8 +6,8 @@ pub const API_MINOR: u64 = 0;
pub const VERSION_MAJOR: u64 = 1; pub const VERSION_MAJOR: u64 = 1;
pub const VERSION_MINOR: u64 = 0; pub const VERSION_MINOR: u64 = 0;
pub const VERSION_EXTRA: u64 = 2; pub const VERSION_EXTRA: u64 = 2;
pub const SECOND_SCALE: u64 = 1000000; pub const SECOND_SCALE: u64 = 1_000_000;
pub const MILISECOND_SCALE: u64 = 1000; pub const MILISECOND_SCALE: u64 = 1_000;
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
@ -51,32 +51,40 @@ pub enum MemType {
READ_AFTER = 25, READ_AFTER = 25,
} }
#[repr(i32)] bitflags! {
#[derive(PartialEq, Debug, Clone, Copy)] #[repr(C)]
pub enum HookType { pub struct HookType: i32 {
INTR = 1, const INTR = 1;
INSN = 2, const INSN = 2;
CODE = 4, const CODE = 4;
BLOCK = 8, const BLOCK = 8;
MEM_READ_UNMAPPED = 16,
MEM_WRITE_UNMAPPED = 32, const MEM_READ_UNMAPPED = 0x10;
MEM_FETCH_UNMAPPED = 64, const MEM_WRITE_UNMAPPED = 0x20;
MEM_READ_PROT = 128, const MEM_FETCH_UNMAPPED = 0x40;
MEM_WRITE_PROT = 256, const MEM_UNMAPPED = Self::MEM_READ_UNMAPPED.bits | Self::MEM_WRITE_UNMAPPED.bits | Self::MEM_FETCH_UNMAPPED.bits;
MEM_FETCH_PROT = 512,
MEM_READ = 1024, const MEM_READ_PROT = 0x80;
MEM_WRITE = 2048, const MEM_WRITE_PROT = 0x100;
MEM_FETCH = 4096, const MEM_FETCH_PROT = 0x200;
MEM_READ_AFTER = 8192, const MEM_PROT = Self::MEM_READ_PROT.bits | Self::MEM_WRITE_PROT.bits | Self::MEM_FETCH_PROT.bits;
INSN_INVALID = 16384,
MEM_UNMAPPED = 112, const MEM_READ = 0x400;
MEM_PROT = 896, const MEM_WRITE = 0x800;
MEM_READ_INVALID = 144, const MEM_FETCH = 0x1000;
MEM_WRITE_INVALID = 288, const MEM_VALID = Self::MEM_READ.bits | Self::MEM_WRITE.bits | Self::MEM_FETCH.bits;
MEM_FETCH_INVALID = 576,
MEM_INVALID = 1008, const MEM_READ_AFTER = 0x2000;
MEM_VALID = 7168,
MEM_ALL = 8176, 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)] #[repr(C)]
@ -94,7 +102,7 @@ pub struct Permission : u32 {
const READ = 1; const READ = 1;
const WRITE = 2; const WRITE = 2;
const EXEC = 4; 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, MAX = 8,
} }
#[repr(C)] bitflags! {
#[derive(PartialEq, Debug, Clone, Copy)] #[repr(C)]
pub enum Mode { pub struct Mode: i32 {
const LITTLE_ENDIAN = 0;
const BIG_ENDIAN = 0x4000_0000;
LITTLE_ENDIAN = 0, const ARM = 0;
BIG_ENDIAN = 1073741824, const THUMB = 0x10;
const MCLASS = 0x20;
// use LITTLE_ENDIAN. const V8 = 0x40;
// MODE_ARM = 0, const ARM926 = 0x80;
THUMB = 16, const ARM946 = 0x100;
MCLASS = 32, const ARM1176 = 0x200;
V8 = 64, const MICRO = Self::THUMB.bits;
ARM926 = 128, const MIPS3 = Self::MCLASS.bits;
ARM946 = 256, const MIPS32R6 = Self::V8.bits;
ARM1176 = 512, const MIPS32 = 4;
// (assoc) MICRO = 16, const MIPS64 = 8;
// (assoc) MIPS3 = 32, const MODE_16 = 2;
// (assoc) MIPS32R6 = 64, const MODE_32 = Self::MIPS32.bits;
MIPS32 = 4, const MODE_64 = Self::MIPS64.bits;
MIPS64 = 8, const PPC32 = Self::MIPS32.bits;
MODE_16 = 2, const PPC64 = Self::MIPS64.bits;
// (assoc) MODE_32 = 4, const QPX = Self::THUMB.bits;
// (assoc) MODE_64 = 8, const SPARC32 = Self::MIPS32.bits;
// (assoc) PPC32 = 4, const SPARC64 = Self::MIPS64.bits;
// (assoc) PPC64 = 8, const V9 = Self::THUMB.bits;
// (assoc) QPX = 16, }
// (assoc) SPARC32 = 4,
// (assoc) SPARC64 = 8,
// (assoc) V9 = 16,
} }
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;
}

View File

@ -1,19 +1,18 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
extern crate libc; extern crate libc;
use capstone::prelude::*;
use super::arm::RegisterARM; use super::arm::RegisterARM;
use super::arm64::RegisterARM64; use super::arm64::RegisterARM64;
use super::x86::RegisterX86;
use super::sparc::RegisterSPARC;
use super::mips::RegisterMIPS;
use super::m68k::RegisterM68K; use super::m68k::RegisterM68K;
use super::{Permission, Mode, Arch, HookType, MemType, uc_error}; use super::mips::RegisterMIPS;
use std::ptr; 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::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use libc::{mmap, c_void, size_t, MAP_ANON, MAP_PRIVATE,PROT_READ,PROT_WRITE}; use std::ptr;
#[derive(Debug)] #[derive(Debug)]
pub struct Chunk { pub struct Chunk {
@ -22,7 +21,6 @@ pub struct Chunk {
pub freed: bool, pub freed: bool,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Heap { pub struct Heap {
pub real_base: *mut c_void, pub real_base: *mut c_void,
@ -34,77 +32,122 @@ pub struct Heap {
pub unalloc_hook: super::ffi::uc_hook, pub unalloc_hook: super::ffi::uc_hook,
} }
/// Hooks (parts of the) code segment to display register info and the current instruction. /// Hooks (parts of the) code segment to display register info and the current instruction.
pub fn add_debug_prints_ARM<D>(uc: &mut super::UnicornHandle<D>, code_start: u64, code_end: u64) { pub fn add_debug_prints_ARM<D>(uc: &mut super::UnicornHandle<D>, code_start: u64, code_end: u64) {
let cs_arm: Capstone = Capstone::new() let cs_arm: Capstone = Capstone::new()
.arm() .arm()
.mode(arch::arm::ArchMode::Arm) .mode(arch::arm::ArchMode::Arm)
.detail(true) .detail(true)
.build().expect("failed to create capstone for ARM"); .build()
.expect("failed to create capstone for ARM");
let cs_thumb: Capstone = Capstone::new() let cs_thumb: Capstone = Capstone::new()
.arm() .arm()
.mode(arch::arm::ArchMode::Thumb) .mode(arch::arm::ArchMode::Thumb)
.detail(true) .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<D>, addr: u64, size: u32| { let callback = Box::new(move |uc: super::UnicornHandle<D>, addr: u64, size: u32| {
let sp = uc.reg_read(RegisterARM::SP as i32).expect("failed to read SP"); let sp = uc
let lr = uc.reg_read(RegisterARM::LR as i32).expect("failed to read LR"); .reg_read(RegisterARM::SP as i32)
let r0 = uc.reg_read(RegisterARM::R0 as i32).expect("failed to read r0"); .expect("failed to read SP");
let r1 = uc.reg_read(RegisterARM::R1 as i32).expect("failed to read r1"); let lr = uc
let r2 = uc.reg_read(RegisterARM::R2 as i32).expect("failed to read r2"); .reg_read(RegisterARM::LR as i32)
let r3 = uc.reg_read(RegisterARM::R3 as i32).expect("failed to read r3"); .expect("failed to read LR");
let r4 = uc.reg_read(RegisterARM::R4 as i32).expect("failed to read r4"); let r0 = uc
let r5 = uc.reg_read(RegisterARM::R5 as i32).expect("failed to read r5"); .reg_read(RegisterARM::R0 as i32)
let r6 = uc.reg_read(RegisterARM::R6 as i32).expect("failed to read r6"); .expect("failed to read r0");
let r7 = uc.reg_read(RegisterARM::R7 as i32).expect("failed to read r7"); let r1 = uc
let r8 = uc.reg_read(RegisterARM::R8 as i32).expect("failed to read r8"); .reg_read(RegisterARM::R1 as i32)
let r9 = uc.reg_read(RegisterARM::R9 as i32).expect("failed to read r9"); .expect("failed to read r1");
let r10 = uc.reg_read(RegisterARM::R10 as i32).expect("failed to read r10"); let r2 = uc
let r11 = uc.reg_read(RegisterARM::R11 as i32).expect("failed to read r11"); .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!("________________________________________________________________________\n");
println!("$r0: {:#010x} $r1: {:#010x} $r2: {:#010x} $r3: {:#010x}", r0, r1, r2, r3); println!(
println!("$r4: {:#010x} $r5: {:#010x} $r6: {:#010x} $r7: {:#010x}", r4, r5, r6, r7); "$r0: {:#010x} $r1: {:#010x} $r2: {:#010x} $r3: {:#010x}",
println!("$r8: {:#010x} $r9: {:#010x} $r10: {:#010x} $r11: {:#010x}", r8, r9, r10, r11); 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); println!("$sp: {:#010x} $lr: {:#010x}\n", sp, lr);
// decide which mode (ARM/Thumb) to use for disasm // 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]; 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 { let ins = if cpsr & 0x20 != 0 {
cs_thumb.disasm_all(&buf, size as u64) cs_thumb.disasm_all(&buf, size as u64)
} else { } else {
cs_arm.disasm_all(&buf, size as u64) 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!("$pc: {:#010x}", addr);
println!("{}", ins); 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 /// 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 /// The allocator makes heavy use of Unicorn hooks for sanitization/ crash amplification
/// and thus introduces some overhead. /// and thus introduces some overhead.
pub fn init_emu_with_heap(arch: Arch, pub fn init_emu_with_heap(
mut size: u32, arch: Arch,
base_addr: u64, mut size: u32,
grow: bool base_addr: u64,
grow: bool,
) -> Result<super::Unicorn<RefCell<Heap>>, uc_error> { ) -> Result<super::Unicorn<RefCell<Heap>>, uc_error> {
let heap = RefCell::new(Heap {real_base: 0 as _, let heap = RefCell::new(Heap {
uc_base: 0, real_base: 0 as _,
len: 0, uc_base: 0,
grow_dynamically: false, len: 0,
chunk_map: HashMap::new(), grow_dynamically: false,
top: 0, chunk_map: HashMap::new(),
unalloc_hook: 0 as _ }); top: 0,
unalloc_hook: 0 as _,
});
let mut unicorn = super::Unicorn::new(arch, Mode::LITTLE_ENDIAN, heap)?; let mut unicorn = super::Unicorn::new(arch, Mode::LITTLE_ENDIAN, heap)?;
let mut uc = unicorn.borrow(); // get handle 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(); let null_ptr = ptr::null_mut();
unsafe { unsafe {
// manually mmap space for heap to know location // 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); let arena_ptr = mmap(
uc.mem_map_ptr(base_addr, size as usize, Permission::READ | Permission::WRITE, arena_ptr)?; null_ptr,
let h = uc.add_mem_hook(HookType::MEM_VALID, base_addr, base_addr + size as u64, Box::new(heap_unalloc))?; 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 chunks = HashMap::new();
let heap: &mut Heap = &mut *uc.get_data().borrow_mut(); let heap: &mut Heap = &mut *uc.get_data().borrow_mut();
heap.real_base = arena_ptr; // heap pointer in process mem heap.real_base = arena_ptr; // heap pointer in process mem
heap.uc_base = base_addr; heap.uc_base = base_addr;
heap.len = size as usize; 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) (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.chunk_map = chunks;
heap.top = base_addr; // pointer to top of heap in unicorn mem, increases on allocations 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 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. /// `malloc` for the utils allocator.
/// ///
/// Returns a pointer into memory used as heap and applies /// 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 /// Grows the heap if necessary and if it is configured to, otherwise
/// return WRITE_UNMAPPED if there is no space left. /// return WRITE_UNMAPPED if there is no space left.
pub fn uc_alloc(uc: &mut super::UnicornHandle<RefCell<Heap>>, mut size: u64) -> Result<u64, uc_error> { pub fn uc_alloc(
uc: &mut super::UnicornHandle<RefCell<Heap>>,
mut size: u64,
) -> Result<u64, uc_error> {
// 8 byte aligned // 8 byte aligned
if size % 8 != 0 { if size % 8 != 0 {
size = ((size / 8) + 1) * 8; size = ((size / 8) + 1) * 8;
@ -163,7 +226,11 @@ pub fn uc_alloc(uc: &mut super::UnicornHandle<RefCell<Heap>>, mut size: u64) ->
if increase_by % 8 != 0 { if increase_by % 8 != 0 {
increase_by = ((increase_by / 8) + 1) * 8; 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; uc.get_data().borrow_mut().len += increase_by;
len = uc.get_data().borrow_mut().len; len = uc.get_data().borrow_mut().len;
} }
@ -171,31 +238,57 @@ pub fn uc_alloc(uc: &mut super::UnicornHandle<RefCell<Heap>>, mut size: u64) ->
// canary hooks // canary hooks
uc.add_mem_hook(HookType::MEM_WRITE, addr, addr + 3, Box::new(heap_bo))?; 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_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(
uc.add_mem_hook(HookType::MEM_READ, addr + 4 + size, addr + 4 + size + 3, Box::new(heap_oob))?; 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 // add new chunk
let curr_offset = addr + 4 - uc_base; let curr_offset = addr + 4 - uc_base;
let curr_chunk = Chunk {offset: curr_offset, len: size as size_t, freed: false}; let curr_chunk = Chunk {
uc.get_data().borrow_mut().chunk_map.insert(addr + 4, curr_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 let new_top = uc.get_data().borrow_mut().top + size + 8; // canary*2
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
println!("[+] New Allocation from {:#010x} to {:#010x} (size: {})", println!(
uc.get_data().borrow().top, uc.get_data().borrow().top + size - 1 + 8, size); "[+] New Allocation from {:#010x} to {:#010x} (size: {})",
uc.get_data().borrow_mut().top = new_top; 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 // adjust oob hooks
let old_h = uc.get_data().borrow_mut().unalloc_hook; let old_h = uc.get_data().borrow_mut().unalloc_hook;
uc.remove_hook(old_h)?; 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; uc.get_data().borrow_mut().unalloc_hook = new_h;
return Ok(addr + 4); return Ok(addr + 4);
} }
/// `free` for the utils allocator. /// `free` for the utils allocator.
/// ///
/// Marks the chunk to be freed to detect double-frees later on /// Marks the chunk to be freed to detect double-frees later on
/// and places sanitization hooks over the freed region to detect /// and places sanitization hooks over the freed region to detect
/// use-after-frees. /// use-after-frees.
@ -208,17 +301,30 @@ pub fn uc_free(uc: &mut super::UnicornHandle<RefCell<Heap>>, ptr: u64) -> Result
let mut chunk_size = 0; let mut chunk_size = 0;
{ {
let mut heap = uc.get_data().borrow_mut(); 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; chunk_size = curr_chunk.len as u64;
curr_chunk.freed = true; 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(()); return Ok(());
} }
fn heap_unalloc(
fn heap_unalloc(uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) { uc: super::UnicornHandle<RefCell<Heap>>,
_mem_type: MemType,
addr: u64,
_size: usize,
_val: i64,
) {
let arch = uc.get_arch(); let arch = uc.get_arch();
let reg = match arch { let reg = match arch {
Arch::X86 => RegisterX86::RIP as i32, Arch::X86 => RegisterX86::RIP as i32,
@ -227,15 +333,20 @@ fn heap_unalloc(uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, add
Arch::MIPS => RegisterMIPS::PC as i32, Arch::MIPS => RegisterMIPS::PC as i32,
Arch::SPARC => RegisterSPARC::PC as i32, Arch::SPARC => RegisterSPARC::PC as i32,
Arch::M68K => RegisterM68K::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}", panic!("ERROR: unicorn-rs Sanitizer: Heap out-of-bounds access of unallocated memory on addr {:#0x}, $pc: {:#010x}",
addr, pc); addr, pc);
} }
fn heap_oob(
fn heap_oob(uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) { uc: super::UnicornHandle<RefCell<Heap>>,
_mem_type: MemType,
addr: u64,
_size: usize,
_val: i64,
) {
let arch = uc.get_arch(); let arch = uc.get_arch();
let reg = match arch { let reg = match arch {
Arch::X86 => RegisterX86::RIP as i32, Arch::X86 => RegisterX86::RIP as i32,
@ -244,14 +355,22 @@ fn heap_oob(uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u
Arch::MIPS => RegisterMIPS::PC as i32, Arch::MIPS => RegisterMIPS::PC as i32,
Arch::SPARC => RegisterSPARC::PC as i32, Arch::SPARC => RegisterSPARC::PC as i32,
Arch::M68K => RegisterM68K::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 read on addr {:#0x}, $pc: {:#010x}", addr, pc); panic!(
"ERROR: unicorn-rs Sanitizer: Heap out-of-bounds read on addr {:#0x}, $pc: {:#010x}",
addr, pc
);
} }
fn heap_bo(
fn heap_bo (uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) { uc: super::UnicornHandle<RefCell<Heap>>,
_mem_type: MemType,
addr: u64,
_size: usize,
_val: i64,
) {
let arch = uc.get_arch(); let arch = uc.get_arch();
let reg = match arch { let reg = match arch {
Arch::X86 => RegisterX86::RIP as i32, Arch::X86 => RegisterX86::RIP as i32,
@ -260,14 +379,22 @@ fn heap_bo (uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u
Arch::MIPS => RegisterMIPS::PC as i32, Arch::MIPS => RegisterMIPS::PC as i32,
Arch::SPARC => RegisterSPARC::PC as i32, Arch::SPARC => RegisterSPARC::PC as i32,
Arch::M68K => RegisterM68K::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 buffer-overflow on addr {:#0x}, $pc: {:#010x}", addr, pc); panic!(
"ERROR: unicorn-rs Sanitizer: Heap buffer-overflow on addr {:#0x}, $pc: {:#010x}",
addr, pc
);
} }
fn heap_uaf(
fn heap_uaf (uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) { uc: super::UnicornHandle<RefCell<Heap>>,
_mem_type: MemType,
addr: u64,
_size: usize,
_val: i64,
) {
let arch = uc.get_arch(); let arch = uc.get_arch();
let reg = match arch { let reg = match arch {
Arch::X86 => RegisterX86::RIP as i32, Arch::X86 => RegisterX86::RIP as i32,
@ -276,14 +403,15 @@ fn heap_uaf (uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr:
Arch::MIPS => RegisterMIPS::PC as i32, Arch::MIPS => RegisterMIPS::PC as i32,
Arch::SPARC => RegisterSPARC::PC as i32, Arch::SPARC => RegisterSPARC::PC as i32,
Arch::M68K => RegisterM68K::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 use-after-free on addr {:#0x}, $pc: {:#010x}", addr, pc); panic!(
"ERROR: unicorn-rs Sanitizer: Heap use-after-free on addr {:#0x}, $pc: {:#010x}",
addr, pc
);
} }
pub fn vmmap<D>(uc: &mut super::UnicornHandle<D>) { pub fn vmmap<D>(uc: &mut super::UnicornHandle<D>) {
let regions = uc let regions = uc
.mem_regions() .mem_regions()

View File

@ -278,4 +278,4 @@ pub struct X86Mmr {
pub base: u64, pub base: u64,
pub limit: u32, pub limit: u32,
pub flags: u32, pub flags: u32,
} }

View File

@ -2,8 +2,8 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use unicorn::{RegisterARM, RegisterX86, InsnSysX86, RegisterMIPS, RegisterPPC}; use unicorn::unicorn_const::{uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE};
use unicorn::unicorn_const::{Mode, Arch, Permission, MemType, HookType, SECOND_SCALE, uc_error}; use unicorn::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86};
pub static X86_REGISTERS: [RegisterX86; 145] = [ pub static X86_REGISTERS: [RegisterX86; 145] = [
RegisterX86::AH, RegisterX86::AH,
@ -159,7 +159,8 @@ type Unicorn<'a> = unicorn::UnicornHandle<'a, u32>;
fn emulate_x86() { fn emulate_x86() {
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx let x86_code32: Vec<u8> = 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(); let mut emu = unicorn.borrow();
assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 123), Ok(())); 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_read(RegisterX86::EAX as i32), Ok(123));
@ -170,10 +171,7 @@ fn emulate_x86() {
(Err(uc_error::WRITE_UNMAPPED)) (Err(uc_error::WRITE_UNMAPPED))
); );
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
assert_eq!( assert_eq!(
emu.mem_read_as_vec(0x1000, x86_code32.len()), emu.mem_read_as_vec(0x1000, x86_code32.len()),
@ -212,12 +210,10 @@ fn x86_code_callback() {
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx let x86_code32: Vec<u8> = 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu let hook = emu
@ -245,12 +241,10 @@ fn x86_intr_callback() {
let x86_code32: Vec<u8> = vec![0xcd, 0x80]; // INT 0x80; let x86_code32: Vec<u8> = 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu let hook = emu
@ -277,19 +271,17 @@ fn x86_mem_callback() {
let expects = vec![ let expects = vec![
MemExpectation(MemType::WRITE, 0x2000, 4, 0xdeadbeef), MemExpectation(MemType::WRITE, 0x2000, 4, 0xdeadbeef),
MemExpectation(MemType::READ_UNMAPPED, 0x10000, 4, 0), MemExpectation(MemType::READ_UNMAPPED, 0x10000, 4, 0),
MemExpectation(MemType::READ, 0x10000, 4, 0),
]; ];
let mems: Vec<MemExpectation> = Vec::new(); let mems: Vec<MemExpectation> = Vec::new();
let mems_cell = Rc::new(RefCell::new(mems)); let mems_cell = Rc::new(RefCell::new(mems));
let callback_mems = mems_cell.clone(); let callback_mems = mems_cell.clone();
let callback = move |_: Unicorn<'_>, let callback =
mem_type: MemType, move |_: Unicorn<'_>, mem_type: MemType, address: u64, size: usize, value: i64| {
address: u64, let mut mems = callback_mems.borrow_mut();
size: usize, mems.push(MemExpectation(mem_type, address, size, value));
value: i64| { };
let mut mems = callback_mems.borrow_mut();
mems.push(MemExpectation(mem_type, address, size, value));
};
// mov eax, 0xdeadbeef; // mov eax, 0xdeadbeef;
// mov [0x2000], eax; // 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, 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu let hook = emu
@ -338,15 +328,15 @@ fn x86_insn_in_callback() {
let x86_code32: Vec<u8> = vec![0xe5, 0x10]; // IN eax, 0x10; let x86_code32: Vec<u8> = 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code32), 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!( assert_eq!(
emu.emu_start( emu.emu_start(
@ -375,15 +365,15 @@ fn x86_insn_out_callback() {
let x86_code32: Vec<u8> = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al; let x86_code32: Vec<u8> = 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code32), 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!( assert_eq!(
emu.emu_start( emu.emu_start(
@ -417,15 +407,15 @@ fn x86_insn_sys_callback() {
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code), 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!( assert_eq!(
emu.emu_start( emu.emu_start(
@ -444,7 +434,8 @@ fn x86_insn_sys_callback() {
fn emulate_arm() { fn emulate_arm() {
let arm_code32: Vec<u8> = vec![0x83, 0xb0]; // sub sp, #0xc let arm_code32: Vec<u8> = 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(); let mut emu = unicorn.borrow();
assert_eq!(emu.reg_write(RegisterARM::R1 as i32, 123), Ok(())); 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_read(RegisterARM::R1 as i32), Ok(123));
@ -455,10 +446,7 @@ fn emulate_arm() {
(Err(uc_error::WRITE_UNMAPPED)) (Err(uc_error::WRITE_UNMAPPED))
); );
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &arm_code32), Ok(())); assert_eq!(emu.mem_write(0x1000, &arm_code32), Ok(()));
assert_eq!( assert_eq!(
emu.mem_read_as_vec(0x1000, arm_code32.len()), emu.mem_read_as_vec(0x1000, arm_code32.len()),
@ -487,12 +475,10 @@ fn emulate_arm() {
fn emulate_mips() { fn emulate_mips() {
let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456; 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &mips_code32), Ok(())); assert_eq!(emu.mem_write(0x1000, &mips_code32), Ok(()));
assert_eq!( assert_eq!(
emu.mem_read_as_vec(0x1000, mips_code32.len()), emu.mem_read_as_vec(0x1000, mips_code32.len()),
@ -515,12 +501,10 @@ fn emulate_mips() {
fn emulate_ppc() { fn emulate_ppc() {
let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &ppc_code32), Ok(())); assert_eq!(emu.mem_write(0x1000, &ppc_code32), Ok(()));
assert_eq!( assert_eq!(
emu.mem_read_as_vec(0x1000, ppc_code32.len()), emu.mem_read_as_vec(0x1000, ppc_code32.len()),
@ -542,12 +526,10 @@ fn emulate_ppc() {
#[test] #[test]
fn mem_unmapping() { 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_unmap(0x1000, 0x4000), 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 mut mem: [u8; 4000] = [0; 4000];
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx let x86_code32: Vec<u8> = 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(); let mut emu = unicorn.borrow();
// Attempt to write to memory before mapping it. // Attempt to write to memory before mapping it.
@ -635,12 +618,10 @@ fn x86_context_save_and_restore() {
let x86_code: Vec<u8> = vec![ let x86_code: Vec<u8> = vec![
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
let _ = emu.emu_start( let _ = emu.emu_start(
0x1000, 0x1000,
@ -654,12 +635,16 @@ fn x86_context_save_and_restore() {
let context = context.unwrap(); let context = context.unwrap();
/* and create a new emulator, into which we will "restore" that context */ /* 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(); let emu2 = unicorn2.borrow();
assert_eq!(emu2.context_restore(&context), Ok(())); assert_eq!(emu2.context_restore(&context), Ok(()));
for register in X86_REGISTERS.iter() { for register in X86_REGISTERS.iter() {
println!("Testing register {:?}", register); 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() { fn x86_block_callback() {
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
struct BlockExpectation(u64, u32); struct BlockExpectation(u64, u32);
let expects = vec![BlockExpectation(0x1000, 2)]; let expects = vec![BlockExpectation(0x1000, 2), BlockExpectation(0x1000, 2)];
let blocks: Vec<BlockExpectation> = Vec::new(); let blocks: Vec<BlockExpectation> = Vec::new();
let blocks_cell = Rc::new(RefCell::new(blocks)); let blocks_cell = Rc::new(RefCell::new(blocks));
@ -680,16 +665,19 @@ fn x86_block_callback() {
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx let x86_code32: Vec<u8> = 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(); let mut emu = unicorn.borrow();
assert_eq!( assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
emu.mem_map(0x1000, 0x4000, Permission::ALL),
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu.add_block_hook(callback).expect("failed to add block hook"); let hook = emu
assert_eq!(emu.emu_start(0x1000, 0x1002, 10 * SECOND_SCALE, 1000), Ok(())); .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!(expects, *blocks_cell.borrow());
assert_eq!(emu.remove_hook(hook), Ok(())); assert_eq!(emu.remove_hook(hook), Ok(()));
} }