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:
parent
1e457ecc66
commit
66df39ebc9
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -265,4 +265,4 @@ pub enum RegisterARM64 {
|
||||
|
||||
// pseudo registers
|
||||
PC = 260,
|
||||
}
|
||||
}
|
||||
|
@ -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) },
|
||||
});
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -21,4 +21,4 @@ pub enum RegisterM68K {
|
||||
D7,
|
||||
SR,
|
||||
PC,
|
||||
}
|
||||
}
|
||||
|
@ -153,4 +153,4 @@ pub enum RegisterMIPS {
|
||||
MPL0 = 134,
|
||||
MPL1 = 135,
|
||||
MPL2 = 136,
|
||||
}
|
||||
}
|
||||
|
@ -38,5 +38,5 @@ pub enum RegisterPPC {
|
||||
GPR28 = 30,
|
||||
GPR29 = 31,
|
||||
GPR30 = 32,
|
||||
GPR31 = 33
|
||||
}
|
||||
GPR31 = 33,
|
||||
}
|
||||
|
@ -91,4 +91,4 @@ pub enum RegisterSPARC {
|
||||
Y = 86,
|
||||
XCC = 87,
|
||||
PC = 88,
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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()
|
||||
|
@ -278,4 +278,4 @@ pub struct X86Mmr {
|
||||
pub base: u64,
|
||||
pub limit: u32,
|
||||
pub flags: u32,
|
||||
}
|
||||
}
|
||||
|
@ -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(()));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user