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::{
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");
}

View File

@ -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;
}
}

View File

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

View File

@ -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<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 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 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 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 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 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 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 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<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 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<D>(uc: uc_handle,
mem_type: MemType,
address: u64,
size: u32,
value: i64,
user_data: *mut MemHook<D>)
{
pub extern "C" fn mem_hook_proxy<D>(
uc: uc_handle,
mem_type: MemType,
address: u64,
size: u32,
value: i64,
user_data: *mut MemHook<D>,
) {
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<D>(uc: uc_handle, value: u32, user_data: *mut InterruptHook<D>) {
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<D>(
uc: uc_handle,
port: u32,
size: usize,
user_data: *mut InstructionInHook<D>) {
uc: uc_handle,
port: u32,
size: usize,
user_data: *mut InstructionInHook<D>,
) {
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<D>(
uc: uc_handle,
port: u32,
size: usize,
value: u32,
user_data: *mut InstructionOutHook<D>) {
uc: uc_handle,
port: u32,
size: usize,
value: u32,
user_data: *mut InstructionOutHook<D>,
) {
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<D>(uc: uc_handle, user_data: *mut InstructionSysHook<D>) {
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) },
});
}

View File

@ -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<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 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<D> {
inner: Pin<Box<UnicornInner<D>>>
inner: Pin<Box<UnicornInner<D>>>,
}
#[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<D>>
inner: Pin<&'a mut UnicornInner<D>>,
}
/// 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_sys_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionSysHook<D>>>,
pub data: D,
_pin: PhantomPinned
_pin: PhantomPinned,
}
impl<D> Unicorn<D> {
/// 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<Unicorn<D>, uc_error> {
pub fn new(arch: Arch, mode: Mode, data: D) -> Result<Unicorn<D>, uc_error> {
let mut handle = std::ptr::null_mut();
let err = unsafe { ffi::uc_open(arch, mode, &mut handle) };
if err == uc_error::OK {
Ok(Unicorn {
inner: Box::pin(UnicornInner {
uc: handle,
arch: arch,
code_hooks: HashMap::new(),
block_hooks: HashMap::new(),
mem_hooks: HashMap::new(),
intr_hooks: HashMap::new(),
insn_in_hooks: HashMap::new(),
insn_out_hooks: HashMap::new(),
insn_sys_hooks: HashMap::new(),
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<D> std::fmt::Debug for UnicornInner<D> {
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<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 {
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<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.
///
///
/// Not to be used with registers larger than 64 bit.
pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> {
let mut value: u64 = 0;
let err = unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut u64 as _) };
let err =
unsafe { ffi::uc_reg_read(self.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<T: Into<i32>>(&self, regid: T) -> Result<Box<[u8]>, 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<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> {
let mut value: i32 = 0;
let err = unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut i32 as _) };
let err =
unsafe { ffi::uc_reg_read(self.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<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 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<F: 'static>(
&mut self,
callback: F,
) -> Result<ffi::uc_hook, uc_error>
where F: FnMut(UnicornHandle<D>, u64, u32)
pub fn add_block_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle<D>, 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<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);
}
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<F: 'static>(
&mut self,
callback: F,
) -> Result<ffi::uc_hook, uc_error>
where F: FnMut(UnicornHandle<D>, u32)
{
pub fn add_intr_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle<D>, u32),
{
let mut hook_ptr = std::ptr::null_mut();
let mut user_data = Box::new(ffi::InterruptHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
callback: Box::new(callback),
});
let 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<F: 'static>(
&mut self,
callback: F,
) -> Result<ffi::uc_hook, uc_error>
where F: FnMut(UnicornHandle<D>, u32, usize)
{
pub fn add_insn_in_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle<D>, 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<F: 'static>(
&mut self,
callback: F,
) -> Result<ffi::uc_hook, uc_error>
where F: FnMut(UnicornHandle<D>, u32, usize, u32)
{
pub fn add_insn_out_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where
F: FnMut(UnicornHandle<D>, 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<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 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<Context, uc_error> {
let mut empty_context: ffi::uc_context = Default::default();
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut empty_context) };
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<Context, uc_error> {
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<usize, uc_error> {
let mut result: libc::size_t = Default::default();
@ -782,6 +801,4 @@ impl<'a, D> UnicornHandle<'a, D> {
Err(err)
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
}

View File

@ -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<D>(uc: &mut super::UnicornHandle<D>, 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<D>, 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<D>, 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<super::Unicorn<RefCell<Heap>>, 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<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
if size % 8 != 0 {
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 {
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<RefCell<Heap>>, 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<RefCell<Heap>>, 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<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) {
fn heap_unalloc(
uc: super::UnicornHandle<RefCell<Heap>>,
_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<RefCell<Heap>>, _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<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) {
fn heap_oob(
uc: super::UnicornHandle<RefCell<Heap>>,
_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<RefCell<Heap>>, _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<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) {
fn heap_bo(
uc: super::UnicornHandle<RefCell<Heap>>,
_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<RefCell<Heap>>, _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<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) {
fn heap_uaf(
uc: super::UnicornHandle<RefCell<Heap>>,
_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<RefCell<Heap>>, _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<D>(uc: &mut super::UnicornHandle<D>) {
let regions = uc
.mem_regions()

View File

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

View File

@ -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<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();
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<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();
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<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();
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<MemExpectation> = 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<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();
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<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();
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<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();
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<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();
// Attempt to write to memory before mapping it.
@ -635,12 +618,10 @@ fn x86_context_save_and_restore() {
let x86_code: Vec<u8> = 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<BlockExpectation> = Vec::new();
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 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(()));
}