Merge pull request #1480 from domenukk/rust_bindings

Rust bindings improvements
This commit is contained in:
lazymio
2021-11-10 07:52:31 +01:00
committed by GitHub
15 changed files with 459 additions and 447 deletions

View File

@ -3,14 +3,14 @@ name = "unicorn-engine"
version = "2.0.0-rc4" version = "2.0.0-rc4"
authors = ["Ziqiao Kong", "Lukas Seidel"] authors = ["Ziqiao Kong", "Lukas Seidel"]
documentation = "https://github.com/unicorn-engine/unicorn/wiki" documentation = "https://github.com/unicorn-engine/unicorn/wiki"
edition = "2018" edition = "2021"
include = [ include = [
"/.gitmodules", "/.gitmodules",
"/COPYING", "/COPYING",
"/Cargo.toml", "/Cargo.toml",
"/README.md", "/README.md",
"/src/*", "/src/*",
"build.rs" "build.rs",
] ]
license = "GPL-2.0" license = "GPL-2.0"
readme = "README.md" readme = "README.md"

View File

@ -5,23 +5,23 @@ Rust bindings for the [Unicorn](http://www.unicorn-engine.org/) emulator with ut
Checkout Unicorn2 source code at [dev branch](https://github.com/unicorn-engine/unicorn/tree/dev). Checkout Unicorn2 source code at [dev branch](https://github.com/unicorn-engine/unicorn/tree/dev).
```rust ```rust
use unicorn_engine::RegisterARM; use unicorn_engine::{Unicorn, RegisterARM};
use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE}; use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE};
fn main() { fn main() {
let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23
let mut unicorn = unicorn-engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); let mut unicorn = Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance");
let mut emu = unicorn.borrow(); let mut emu = unicorn.borrow();
emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page"); emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page");
emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions"); emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions");
emu.reg_write(RegisterARM::R0 as i32, 123).expect("failed write R0"); emu.reg_write(RegisterARM::R0, 123).expect("failed write R0");
emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5"); emu.reg_write(RegisterARM::R5, 1337).expect("failed write R5");
let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000); let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000);
assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(100)); assert_eq!(emu.reg_read(RegisterARM::R0, Ok(100));
assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337)); assert_eq!(emu.reg_read(RegisterARM::R5, Ok(1337));
} }
``` ```
Further sample code can be found in ```tests/unicorn.rs```. Further sample code can be found in ```tests/unicorn.rs```.

View File

@ -51,6 +51,7 @@ fn download_unicorn() -> PathBuf {
find_unicorn(&out_dir).unwrap() find_unicorn(&out_dir).unwrap()
} }
#[allow(clippy::branches_sharing_code)]
fn main() { fn main() {
let profile = env::var("PROFILE").unwrap(); let profile = env::var("PROFILE").unwrap();

View File

@ -164,3 +164,9 @@ impl RegisterARM {
pub const FP: RegisterARM = RegisterARM::R11; pub const FP: RegisterARM = RegisterARM::R11;
pub const IP: RegisterARM = RegisterARM::R12; pub const IP: RegisterARM = RegisterARM::R12;
} }
impl From<RegisterARM> for i32 {
fn from(r: RegisterARM) -> Self {
r as i32
}
}

View File

@ -1,9 +1,7 @@
#![allow(non_camel_case_types)]
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
// ARM64 registers // ARM64 registers
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum RegisterARM64 { pub enum RegisterARM64 {
INVALID = 0, INVALID = 0,
X29 = 1, X29 = 1,
@ -319,3 +317,9 @@ impl RegisterARM64 {
pub const FP: RegisterARM64 = RegisterARM64::X29; pub const FP: RegisterARM64 = RegisterARM64::X29;
pub const LR: RegisterARM64 = RegisterARM64::X30; pub const LR: RegisterARM64 = RegisterARM64::X30;
} }
impl From<RegisterARM64> for i32 {
fn from(r: RegisterARM64) -> Self {
r as i32
}
}

View File

@ -1,14 +1,15 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(dead_code)] #![allow(dead_code)]
use super::unicorn_const::*; use crate::Unicorn;
use super::unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Query};
use core::ffi::c_void;
use libc::{c_char, c_int}; use libc::{c_char, c_int};
use std::ffi::c_void;
use std::pin::Pin;
pub type uc_handle = *mut c_void; pub type uc_handle = *mut c_void;
pub type uc_hook = *mut c_void; pub type uc_hook = *mut c_void;
pub type uc_context = libc::size_t; pub type uc_context = *mut c_void;
extern "C" { extern "C" {
pub fn uc_version(major: *mut u32, minor: *mut u32) -> u32; pub fn uc_version(major: *mut u32, minor: *mut u32) -> u32;
@ -77,154 +78,98 @@ extern "C" {
pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error; pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error;
} }
pub struct CodeHook { pub struct UcHook<'a, D: 'a, F: 'a> {
pub unicorn: *mut crate::UnicornInner, pub callback: F,
pub callback: Box<dyn FnMut(crate::UnicornHandle, u64, u32)>, pub uc: Unicorn<'a, D>,
} }
pub struct BlockHook { pub trait IsUcHook<'a> {}
pub unicorn: *mut crate::UnicornInner,
pub callback: Box<dyn FnMut(crate::UnicornHandle, u64, u32)>,
}
pub struct MemHook { impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {}
pub unicorn: *mut crate::UnicornInner,
pub callback: Box<dyn FnMut(crate::UnicornHandle, MemType, u64, usize, i64)>,
}
pub struct InterruptHook { pub extern "C" fn code_hook_proxy<D, F>(
pub unicorn: *mut crate::UnicornInner,
pub callback: Box<dyn FnMut(crate::UnicornHandle, u32)>,
}
pub struct InstructionInHook {
pub unicorn: *mut crate::UnicornInner,
pub callback: Box<dyn FnMut(crate::UnicornHandle, u32, usize)>,
}
pub struct InstructionOutHook {
pub unicorn: *mut crate::UnicornInner,
pub callback: Box<dyn FnMut(crate::UnicornHandle, u32, usize, u32)>,
}
pub struct InstructionSysHook {
pub unicorn: *mut crate::UnicornInner,
pub callback: Box<dyn FnMut(crate::UnicornHandle)>,
}
pub extern "C" fn code_hook_proxy(
uc: uc_handle, uc: uc_handle,
address: u64, address: u64,
size: u32, size: u32,
user_data: *mut CodeHook, user_data: *mut UcHook<D, F>,
) { ) where
let unicorn = unsafe { &mut *(*user_data).unicorn }; F: FnMut(&mut crate::Unicorn<D>, u64, u32),
let callback = &mut unsafe { &mut *(*user_data).callback }; {
assert_eq!(uc, unicorn.uc); let user_data = unsafe { &mut *user_data };
callback( debug_assert_eq!(uc, user_data.uc.inner().uc);
crate::UnicornHandle { (user_data.callback)(&mut user_data.uc, address, size);
inner: unsafe { Pin::new_unchecked(unicorn) },
},
address,
size,
);
} }
pub extern "C" fn block_hook_proxy( pub extern "C" fn block_hook_proxy<D, F>(
uc: uc_handle, uc: uc_handle,
address: u64, address: u64,
size: u32, size: u32,
user_data: *mut BlockHook, user_data: *mut UcHook<D, F>,
) { ) where
let unicorn = unsafe { &mut *(*user_data).unicorn }; F: FnMut(&mut crate::Unicorn<D>, u64, u32),
let callback = &mut unsafe { &mut *(*user_data).callback }; {
assert_eq!(uc, unicorn.uc); let user_data = unsafe { &mut *user_data };
callback( debug_assert_eq!(uc, user_data.uc.inner().uc);
crate::UnicornHandle { (user_data.callback)(&mut user_data.uc, address, size);
inner: unsafe { Pin::new_unchecked(unicorn) },
},
address,
size,
);
} }
pub extern "C" fn mem_hook_proxy( pub extern "C" fn mem_hook_proxy<D, F>(
uc: uc_handle, uc: uc_handle,
mem_type: MemType, mem_type: MemType,
address: u64, address: u64,
size: u32, size: u32,
value: i64, value: i64,
user_data: *mut MemHook, user_data: *mut UcHook<D, F>,
) { ) -> bool
let unicorn = unsafe { &mut *(*user_data).unicorn }; where
let callback = &mut unsafe { &mut *(*user_data).callback }; F: FnMut(&mut crate::Unicorn<D>, MemType, u64, usize, i64) -> bool,
assert_eq!(uc, unicorn.uc); {
callback( let user_data = unsafe { &mut *user_data };
crate::UnicornHandle { debug_assert_eq!(uc, user_data.uc.inner().uc);
inner: unsafe { Pin::new_unchecked(unicorn) }, (user_data.callback)(&mut user_data.uc, mem_type, address, size as usize, value)
},
mem_type,
address,
size as usize,
value,
);
} }
pub extern "C" fn intr_hook_proxy(uc: uc_handle, value: u32, user_data: *mut InterruptHook) { pub extern "C" fn intr_hook_proxy<D, F>(uc: uc_handle, value: u32, user_data: *mut UcHook<D, F>)
let unicorn = unsafe { &mut *(*user_data).unicorn }; where
let callback = &mut unsafe { &mut *(*user_data).callback }; F: FnMut(&mut crate::Unicorn<D>, u32),
assert_eq!(uc, unicorn.uc); {
callback( let user_data = unsafe { &mut *user_data };
crate::UnicornHandle { debug_assert_eq!(uc, user_data.uc.inner().uc);
inner: unsafe { Pin::new_unchecked(unicorn) }, (user_data.callback)(&mut user_data.uc, value);
},
value,
);
} }
pub extern "C" fn insn_in_hook_proxy( pub extern "C" fn insn_in_hook_proxy<D, F>(
uc: uc_handle, uc: uc_handle,
port: u32, port: u32,
size: usize, size: usize,
user_data: *mut InstructionInHook, user_data: *mut UcHook<D, F>,
) { ) where
let unicorn = unsafe { &mut *(*user_data).unicorn }; F: FnMut(&mut crate::Unicorn<D>, u32, usize),
let callback = &mut unsafe { &mut *(*user_data).callback }; {
assert_eq!(uc, unicorn.uc); let user_data = unsafe { &mut *user_data };
callback( debug_assert_eq!(uc, user_data.uc.inner().uc);
crate::UnicornHandle { (user_data.callback)(&mut user_data.uc, port, size);
inner: unsafe { Pin::new_unchecked(unicorn) },
},
port,
size,
);
} }
pub extern "C" fn insn_out_hook_proxy( pub extern "C" fn insn_out_hook_proxy<D, F>(
uc: uc_handle, uc: uc_handle,
port: u32, port: u32,
size: usize, size: usize,
value: u32, value: u32,
user_data: *mut InstructionOutHook, user_data: *mut UcHook<D, F>,
) { ) where
let unicorn = unsafe { &mut *(*user_data).unicorn }; F: FnMut(&mut crate::Unicorn<D>, u32, usize, u32),
let callback = &mut unsafe { &mut *(*user_data).callback }; {
assert_eq!(uc, unicorn.uc); let user_data = unsafe { &mut *user_data };
callback( debug_assert_eq!(uc, user_data.uc.inner().uc);
crate::UnicornHandle { (user_data.callback)(&mut user_data.uc, port, size, value);
inner: unsafe { Pin::new_unchecked(unicorn) },
},
port,
size,
value,
);
} }
pub extern "C" fn insn_sys_hook_proxy(uc: uc_handle, user_data: *mut InstructionSysHook) { pub extern "C" fn insn_sys_hook_proxy<D, F>(uc: uc_handle, user_data: *mut UcHook<D, F>)
let unicorn = unsafe { &mut *(*user_data).unicorn }; where
let callback = &mut unsafe { &mut *(*user_data).callback }; F: FnMut(&mut crate::Unicorn<D>),
assert_eq!(uc, unicorn.uc); {
callback(crate::UnicornHandle { let user_data = unsafe { &mut *user_data };
inner: unsafe { Pin::new_unchecked(unicorn) }, debug_assert_eq!(uc, user_data.uc.inner().uc);
}); (user_data.callback)(&mut user_data.uc);
} }

View File

@ -9,43 +9,49 @@
//! use unicorn_engine::RegisterARM; //! use unicorn_engine::RegisterARM;
//! use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE}; //! use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE};
//! //!
//! fn main() { //! fn emulate() {
//! let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 //! let arm_code32 = [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23
//! //!
//! let mut unicorn = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); //! let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance");
//! let mut emu = unicorn.borrow();
//! emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page"); //! emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page");
//! emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions"); //! emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions");
//! //!
//! emu.reg_write(RegisterARM::R0 as i32, 123).expect("failed write R0"); //! emu.reg_write(RegisterARM::R0, 123).expect("failed write R0");
//! emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5"); //! emu.reg_write(RegisterARM::R5, 1337).expect("failed write R5");
//! //!
//! let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000); //! emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000).unwrap();
//! assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(100)); //! assert_eq!(emu.reg_read(RegisterARM::R0), Ok(100));
//! assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337)); //! assert_eq!(emu.reg_read(RegisterARM::R5), Ok(1337));
//! } //! }
//! ``` //! ```
//! //!
mod ffi; #![no_std]
#[macro_use]
extern crate alloc;
pub mod unicorn_const; pub mod unicorn_const;
mod arm; mod arm;
mod arm64; mod arm64;
mod ffi;
mod m68k; mod m68k;
mod mips; mod mips;
mod ppc; mod ppc;
mod riscv; mod riscv;
mod sparc; mod sparc;
mod x86; mod x86;
pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, riscv::*, sparc::*, x86::*};
pub use crate::{
arm::*, arm64::*, m68k::*, mips::*, ppc::*, riscv::*, sparc::*, unicorn_const::*, x86::*,
};
use alloc::{boxed::Box, rc::Rc, vec::Vec};
use core::{cell::UnsafeCell, ptr};
use ffi::uc_handle; use ffi::uc_handle;
use std::collections::HashMap; use libc::c_void;
use std::ffi::c_void; use unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Permission, Query};
use std::marker::PhantomPinned;
use std::pin::Pin;
use unicorn_const::*;
#[derive(Debug)] #[derive(Debug)]
pub struct Context { pub struct Context {
@ -53,106 +59,123 @@ pub struct Context {
} }
impl Context { impl Context {
pub fn new() -> Self { #[must_use]
Context { context: 0 }
}
pub fn is_initialized(&self) -> bool { pub fn is_initialized(&self) -> bool {
self.context != 0 !self.context.is_null()
} }
} }
impl Drop for Context { impl Drop for Context {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { ffi::uc_context_free(self.context) }; if self.is_initialized() {
unsafe {
ffi::uc_context_free(self.context);
}
}
self.context = ptr::null_mut();
} }
} }
#[derive(Debug)] pub struct UnicornInner<'a, D> {
/// A Unicorn emulator instance.
pub struct Unicorn {
inner: Pin<Box<UnicornInner>>,
}
#[derive(Debug)]
/// Handle used to safely access exposed functions and data of a Unicorn instance.
pub struct UnicornHandle<'a> {
inner: Pin<&'a mut UnicornInner>,
}
/// Internal Management struct
pub struct UnicornInner {
pub uc: uc_handle, pub uc: uc_handle,
pub arch: Arch, pub arch: Arch,
pub code_hooks: HashMap<*mut libc::c_void, Box<ffi::CodeHook>>, /// to keep ownership over the hook for this uc instance's lifetime
pub block_hooks: HashMap<*mut libc::c_void, Box<ffi::BlockHook>>, pub hooks: Vec<(ffi::uc_hook, Box<dyn ffi::IsUcHook<'a> + 'a>)>,
pub mem_hooks: HashMap<*mut libc::c_void, Box<ffi::MemHook>>, pub data: D,
pub intr_hooks: HashMap<*mut libc::c_void, Box<ffi::InterruptHook>>,
pub insn_in_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionInHook>>,
pub insn_out_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionOutHook>>,
pub insn_sys_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionSysHook>>,
_pin: PhantomPinned,
} }
impl Unicorn { /// Drop UC
impl<'a, D> Drop for UnicornInner<'a, D> {
fn drop(&mut self) {
if !self.uc.is_null() {
unsafe { ffi::uc_close(self.uc) };
}
self.uc = ptr::null_mut();
}
}
/// A Unicorn emulator instance.
pub struct Unicorn<'a, D: 'a> {
inner: Rc<UnsafeCell<UnicornInner<'a, D>>>,
}
impl<'a> Unicorn<'a, ()> {
/// Create a new instance of the unicorn engine for the specified architecture /// Create a new instance of the unicorn engine for the specified architecture
/// and hardware mode. /// and hardware mode.
pub fn new(arch: Arch, mode: Mode) -> Result<Unicorn, uc_error> { pub fn new(arch: Arch, mode: Mode) -> Result<Unicorn<'a, ()>, uc_error> {
let mut handle = std::ptr::null_mut(); Self::new_with_data(arch, mode, ())
}
}
impl<'a, D> Unicorn<'a, D>
where
D: 'a,
{
/// Create a new instance of the unicorn engine for the specified architecture
/// and hardware mode.
pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result<Unicorn<'a, D>, uc_error> {
let mut handle = core::ptr::null_mut();
let err = unsafe { ffi::uc_open(arch, mode, &mut handle) }; let err = unsafe { ffi::uc_open(arch, mode, &mut handle) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(Unicorn { Ok(Unicorn {
inner: Box::pin(UnicornInner { inner: Rc::new(UnsafeCell::from(UnicornInner {
uc: handle, uc: handle,
arch: arch, arch,
code_hooks: HashMap::new(), data,
block_hooks: HashMap::new(), hooks: vec![],
mem_hooks: HashMap::new(), })),
intr_hooks: HashMap::new(),
insn_in_hooks: HashMap::new(),
insn_out_hooks: HashMap::new(),
insn_sys_hooks: HashMap::new(),
_pin: std::marker::PhantomPinned,
}),
}) })
} else { } else {
Err(err) Err(err)
} }
} }
}
pub fn borrow<'a>(&'a mut self) -> UnicornHandle<'a> { impl<'a, D> core::fmt::Debug for Unicorn<'a, D> {
UnicornHandle { fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
inner: self.inner.as_mut(), write!(formatter, "Unicorn {{ uc: {:p} }}", self.inner().uc)
}
} }
} }
impl Drop for Unicorn { impl<'a, D> Unicorn<'a, D> {
fn drop(&mut self) { fn inner(&self) -> &UnicornInner<'a, D> {
unsafe { ffi::uc_close(self.inner.uc) }; unsafe { self.inner.get().as_ref().unwrap() }
} }
}
impl std::fmt::Debug for UnicornInner { fn inner_mut(&mut self) -> &mut UnicornInner<'a, D> {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { unsafe { self.inner.get().as_mut().unwrap() }
write!(formatter, "Unicorn {{ uc: {:p} }}", self.uc) }
/// Return whatever data was passed during initialization.
///
/// For an example, have a look at `utils::init_emu_with_heap` where
/// a struct is passed which is used for a custom allocator.
#[must_use]
pub fn get_data(&self) -> &D {
&self.inner().data
}
/// Return a mutable reference to whatever data was passed during initialization.
#[must_use]
pub fn get_data_mut(&mut self) -> &mut D {
&mut self.inner_mut().data
} }
}
impl<'a> UnicornHandle<'a> {
/// Return the architecture of the current emulator. /// Return the architecture of the current emulator.
#[must_use]
pub fn get_arch(&self) -> Arch { pub fn get_arch(&self) -> Arch {
self.inner.arch self.inner().arch
} }
/// Returns a vector with the memory regions that are mapped in the emulator. /// Returns a vector with the memory regions that are mapped in the emulator.
pub fn mem_regions(&self) -> Result<Vec<MemRegion>, uc_error> { pub fn mem_regions(&self) -> Result<Vec<MemRegion>, uc_error> {
let mut nb_regions: u32 = 0; let mut nb_regions: u32 = 0;
let mut p_regions: *const MemRegion = std::ptr::null_mut(); let p_regions: *const MemRegion = core::ptr::null_mut();
let err = unsafe { ffi::uc_mem_regions(self.inner.uc, &mut p_regions, &mut nb_regions) }; let err = unsafe { ffi::uc_mem_regions(self.inner().uc, &p_regions, &mut nb_regions) };
if err == uc_error::OK { if err == uc_error::OK {
let mut regions = Vec::new(); let mut regions = Vec::new();
for i in 0..nb_regions { for i in 0..nb_regions {
regions.push(unsafe { std::mem::transmute_copy(&*p_regions.offset(i as isize)) }); regions.push(unsafe { core::mem::transmute_copy(&*p_regions.add(i as usize)) });
} }
unsafe { libc::free(p_regions as _) }; unsafe { libc::free(p_regions as _) };
Ok(regions) Ok(regions)
@ -163,7 +186,8 @@ impl<'a> UnicornHandle<'a> {
/// Read a range of bytes from memory at the specified address. /// Read a range of bytes from memory at the specified address.
pub fn mem_read(&self, address: u64, buf: &mut [u8]) -> Result<(), uc_error> { pub fn mem_read(&self, address: u64, buf: &mut [u8]) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_read(self.inner.uc, address, buf.as_mut_ptr(), buf.len()) }; let err =
unsafe { ffi::uc_mem_read(self.inner().uc, address, buf.as_mut_ptr(), buf.len()) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -174,7 +198,7 @@ impl<'a> UnicornHandle<'a> {
/// Return a range of bytes from memory at the specified address as vector. /// Return a range of bytes from memory at the specified address as vector.
pub fn mem_read_as_vec(&self, address: u64, size: usize) -> Result<Vec<u8>, uc_error> { pub fn mem_read_as_vec(&self, address: u64, size: usize) -> Result<Vec<u8>, uc_error> {
let mut buf = vec![0; size]; let mut buf = vec![0; size];
let err = unsafe { ffi::uc_mem_read(self.inner.uc, address, buf.as_mut_ptr(), size) }; let err = unsafe { ffi::uc_mem_read(self.inner().uc, address, buf.as_mut_ptr(), size) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(buf) Ok(buf)
} else { } else {
@ -183,7 +207,8 @@ impl<'a> UnicornHandle<'a> {
} }
pub fn mem_write(&mut self, address: u64, bytes: &[u8]) -> Result<(), uc_error> { pub fn mem_write(&mut self, address: u64, bytes: &[u8]) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_write(self.inner.uc, address, bytes.as_ptr(), bytes.len()) }; let err =
unsafe { ffi::uc_mem_write(self.inner().uc, address, bytes.as_ptr(), bytes.len()) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -193,6 +218,8 @@ impl<'a> UnicornHandle<'a> {
/// Map an existing memory region in the emulator at the specified address. /// Map an existing memory region in the emulator at the specified address.
/// ///
/// # Safety
///
/// This function is marked unsafe because it is the responsibility of the caller to /// This function is marked unsafe because it is the responsibility of the caller to
/// ensure that `size` matches the size of the passed buffer, an invalid `size` value will /// ensure that `size` matches the size of the passed buffer, an invalid `size` value will
/// likely cause a crash in unicorn. /// likely cause a crash in unicorn.
@ -202,14 +229,14 @@ impl<'a> UnicornHandle<'a> {
/// `size` must be a multiple of 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`.
/// ///
/// `ptr` is a pointer to the provided memory region that will be used by the emulator. /// `ptr` is a pointer to the provided memory region that will be used by the emulator.
pub fn mem_map_ptr( pub unsafe fn mem_map_ptr(
&mut self, &mut self,
address: u64, address: u64,
size: usize, size: usize,
perms: Permission, perms: Permission,
ptr: *mut c_void, ptr: *mut c_void,
) -> Result<(), uc_error> { ) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_map_ptr(self.inner.uc, address, size, perms.bits(), ptr) }; let err = ffi::uc_mem_map_ptr(self.inner().uc, address, size, perms.bits(), ptr);
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -227,7 +254,7 @@ impl<'a> UnicornHandle<'a> {
size: libc::size_t, size: libc::size_t,
perms: Permission, perms: Permission,
) -> Result<(), uc_error> { ) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_map(self.inner.uc, address, size, perms.bits()) }; let err = unsafe { ffi::uc_mem_map(self.inner().uc, address, size, perms.bits()) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -240,7 +267,7 @@ impl<'a> UnicornHandle<'a> {
/// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `address` must be aligned to 4kb or this will return `Error::ARG`.
/// `size` must be a multiple of 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`.
pub fn mem_unmap(&mut self, 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) }; let err = unsafe { ffi::uc_mem_unmap(self.inner().uc, address, size) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -258,7 +285,7 @@ impl<'a> UnicornHandle<'a> {
size: libc::size_t, size: libc::size_t,
perms: Permission, perms: Permission,
) -> Result<(), uc_error> { ) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_protect(self.inner.uc, address, size, perms.bits()) }; let err = unsafe { ffi::uc_mem_protect(self.inner().uc, address, size, perms.bits()) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -269,7 +296,7 @@ impl<'a> UnicornHandle<'a> {
/// Write an unsigned value from a register. /// Write an unsigned value from a register.
pub fn reg_write<T: Into<i32>>(&mut self, regid: T, value: u64) -> Result<(), uc_error> { pub fn reg_write<T: Into<i32>>(&mut self, regid: T, value: u64) -> Result<(), uc_error> {
let err = let err =
unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), &value as *const _ as _) }; unsafe { ffi::uc_reg_write(self.inner().uc, regid.into(), &value as *const _ as _) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -281,8 +308,8 @@ impl<'a> UnicornHandle<'a> {
/// ///
/// The user has to make sure that the buffer length matches the register size. /// The user has to make sure that the buffer length matches the register size.
/// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)). /// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: Box<[u8]>) -> Result<(), uc_error> { pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: &[u8]) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), value.as_ptr() as _) }; let err = unsafe { ffi::uc_reg_write(self.inner().uc, regid.into(), value.as_ptr() as _) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -296,7 +323,7 @@ impl<'a> UnicornHandle<'a> {
pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> { pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> {
let mut value: u64 = 0; let mut value: u64 = 0;
let err = let err =
unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut u64 as _) }; unsafe { ffi::uc_reg_read(self.inner().uc, regid.into(), &mut value as *mut u64 as _) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(value) Ok(value)
} else { } else {
@ -350,7 +377,7 @@ impl<'a> UnicornHandle<'a> {
return Err(uc_error::ARCH); return Err(uc_error::ARCH);
} }
err = unsafe { ffi::uc_reg_read(self.inner.uc, curr_reg_id, value.as_mut_ptr() as _) }; err = unsafe { ffi::uc_reg_read(self.inner().uc, curr_reg_id, value.as_mut_ptr() as _) };
if err == uc_error::OK { if err == uc_error::OK {
boxed = value.into_boxed_slice(); boxed = value.into_boxed_slice();
@ -364,7 +391,7 @@ impl<'a> UnicornHandle<'a> {
pub fn reg_read_i32<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> { pub fn reg_read_i32<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> {
let mut value: i32 = 0; let mut value: i32 = 0;
let err = let err =
unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut i32 as _) }; unsafe { ffi::uc_reg_read(self.inner().uc, regid.into(), &mut value as *mut i32 as _) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(value) Ok(value)
} else { } else {
@ -373,36 +400,36 @@ impl<'a> UnicornHandle<'a> {
} }
/// Add a code hook. /// Add a code hook.
pub fn add_code_hook<F: 'static>( pub fn add_code_hook<F: 'a>(
&mut self, &mut self,
begin: u64, begin: u64,
end: u64, end: u64,
callback: F, callback: F,
) -> Result<ffi::uc_hook, uc_error> ) -> Result<ffi::uc_hook, uc_error>
where where
F: FnMut(UnicornHandle, u64, u32), F: FnMut(&mut crate::Unicorn<D>, u64, u32) + 'a,
{ {
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = core::ptr::null_mut();
let mut user_data = Box::new(ffi::CodeHook { let mut user_data = Box::new(ffi::UcHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, callback,
callback: Box::new(callback), uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::CODE, HookType::CODE,
ffi::code_hook_proxy as _, ffi::code_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _, user_data.as_mut() as *mut _ as _,
begin, begin,
end, end,
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() } self.inner_mut().hooks.push((hook_ptr, user_data));
.code_hooks
.insert(hook_ptr, user_data);
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -410,31 +437,32 @@ impl<'a> UnicornHandle<'a> {
} }
/// Add a block hook. /// Add a block hook.
pub fn add_block_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error> pub fn add_block_hook<F: 'a>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where where
F: FnMut(UnicornHandle, u64, u32), F: FnMut(&mut Unicorn<D>, u64, u32),
{ {
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = core::ptr::null_mut();
let mut user_data = Box::new(ffi::BlockHook { let mut user_data = Box::new(ffi::UcHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, callback,
callback: Box::new(callback), uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::BLOCK, HookType::BLOCK,
ffi::block_hook_proxy as _, ffi::block_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _, user_data.as_mut() as *mut _ as _,
1, 1,
0, 0,
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() } self.inner_mut().hooks.push((hook_ptr, user_data));
.block_hooks
.insert(hook_ptr, user_data);
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -442,7 +470,7 @@ impl<'a> UnicornHandle<'a> {
} }
/// Add a memory hook. /// Add a memory hook.
pub fn add_mem_hook<F: 'static>( pub fn add_mem_hook<F: 'a>(
&mut self, &mut self,
hook_type: HookType, hook_type: HookType,
begin: u64, begin: u64,
@ -450,33 +478,34 @@ impl<'a> UnicornHandle<'a> {
callback: F, callback: F,
) -> Result<ffi::uc_hook, uc_error> ) -> Result<ffi::uc_hook, uc_error>
where where
F: FnMut(UnicornHandle, MemType, u64, usize, i64), F: FnMut(&mut Unicorn<D>, MemType, u64, usize, i64) -> bool,
{ {
if !(HookType::MEM_ALL | HookType::MEM_READ_AFTER).contains(hook_type) { if !(HookType::MEM_ALL | HookType::MEM_READ_AFTER).contains(hook_type) {
return Err(uc_error::ARG); return Err(uc_error::ARG);
} }
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = core::ptr::null_mut();
let mut user_data = Box::new(ffi::MemHook { let mut user_data = Box::new(ffi::UcHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, callback,
callback: Box::new(callback), uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
hook_type, hook_type,
ffi::mem_hook_proxy as _, ffi::mem_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _, user_data.as_mut() as *mut _ as _,
begin, begin,
end, end,
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() } self.inner_mut().hooks.push((hook_ptr, user_data));
.mem_hooks
.insert(hook_ptr, user_data);
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -484,31 +513,32 @@ impl<'a> UnicornHandle<'a> {
} }
/// Add an interrupt hook. /// Add an interrupt hook.
pub fn add_intr_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error> pub fn add_intr_hook<F: 'a>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where where
F: FnMut(UnicornHandle, u32), F: FnMut(&mut Unicorn<D>, u32),
{ {
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = core::ptr::null_mut();
let mut user_data = Box::new(ffi::InterruptHook { let mut user_data = Box::new(ffi::UcHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, callback,
callback: Box::new(callback), uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::INTR, HookType::INTR,
ffi::intr_hook_proxy as _, ffi::intr_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _, user_data.as_mut() as *mut _ as _,
0, 0,
0, 0,
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() } self.inner_mut().hooks.push((hook_ptr, user_data));
.intr_hooks
.insert(hook_ptr, user_data);
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -516,22 +546,24 @@ impl<'a> UnicornHandle<'a> {
} }
/// Add hook for x86 IN instruction. /// Add hook for x86 IN instruction.
pub fn add_insn_in_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error> pub fn add_insn_in_hook<F: 'a>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where where
F: FnMut(UnicornHandle, u32, usize), F: FnMut(&mut Unicorn<D>, u32, usize),
{ {
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = core::ptr::null_mut();
let mut user_data = Box::new(ffi::InstructionInHook { let mut user_data = Box::new(ffi::UcHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, callback,
callback: Box::new(callback), uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::INSN, HookType::INSN,
ffi::insn_in_hook_proxy as _, ffi::insn_in_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _, user_data.as_mut() as *mut _ as _,
0, 0,
0, 0,
@ -539,9 +571,8 @@ impl<'a> UnicornHandle<'a> {
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() } self.inner_mut().hooks.push((hook_ptr, user_data));
.insn_in_hooks
.insert(hook_ptr, user_data);
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -549,22 +580,24 @@ impl<'a> UnicornHandle<'a> {
} }
/// Add hook for x86 OUT instruction. /// Add hook for x86 OUT instruction.
pub fn add_insn_out_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error> pub fn add_insn_out_hook<F: 'a>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
where where
F: FnMut(UnicornHandle, u32, usize, u32), F: FnMut(&mut Unicorn<D>, u32, usize, u32),
{ {
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = core::ptr::null_mut();
let mut user_data = Box::new(ffi::InstructionOutHook { let mut user_data = Box::new(ffi::UcHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, callback,
callback: Box::new(callback), uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::INSN, HookType::INSN,
ffi::insn_out_hook_proxy as _, ffi::insn_out_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _, user_data.as_mut() as *mut _ as _,
0, 0,
0, 0,
@ -572,9 +605,8 @@ impl<'a> UnicornHandle<'a> {
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() } self.inner_mut().hooks.push((hook_ptr, user_data));
.insn_out_hooks
.insert(hook_ptr, user_data);
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -582,7 +614,7 @@ impl<'a> UnicornHandle<'a> {
} }
/// Add hook for x86 SYSCALL or SYSENTER. /// Add hook for x86 SYSCALL or SYSENTER.
pub fn add_insn_sys_hook<F: 'static>( pub fn add_insn_sys_hook<F>(
&mut self, &mut self,
insn_type: x86::InsnSysX86, insn_type: x86::InsnSysX86,
begin: u64, begin: u64,
@ -590,20 +622,22 @@ impl<'a> UnicornHandle<'a> {
callback: F, callback: F,
) -> Result<ffi::uc_hook, uc_error> ) -> Result<ffi::uc_hook, uc_error>
where where
F: FnMut(UnicornHandle), F: FnMut(&mut Unicorn<D>) + 'a,
{ {
let mut hook_ptr = std::ptr::null_mut(); let mut hook_ptr = core::ptr::null_mut();
let mut user_data = Box::new(ffi::InstructionSysHook { let mut user_data = Box::new(ffi::UcHook {
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, callback,
callback: Box::new(callback), uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.inner.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::INSN, HookType::INSN,
ffi::insn_sys_hook_proxy as _, ffi::insn_sys_hook_proxy::<D, F> as _,
user_data.as_mut() as *mut _ as _, user_data.as_mut() as *mut _ as _,
begin, begin,
end, end,
@ -611,9 +645,8 @@ impl<'a> UnicornHandle<'a> {
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
unsafe { self.inner.as_mut().get_unchecked_mut() } self.inner_mut().hooks.push((hook_ptr, user_data));
.insn_sys_hooks
.insert(hook_ptr, user_data);
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -624,50 +657,14 @@ impl<'a> UnicornHandle<'a> {
/// ///
/// `hook` is the value returned by `add_*_hook` functions. /// `hook` is the value returned by `add_*_hook` functions.
pub fn remove_hook(&mut self, hook: ffi::uc_hook) -> Result<(), uc_error> { pub fn remove_hook(&mut self, hook: ffi::uc_hook) -> Result<(), uc_error> {
let handle = unsafe { self.inner.as_mut().get_unchecked_mut() };
let err: uc_error; let err: uc_error;
let mut in_one_hashmap = false;
if handle.code_hooks.contains_key(&hook) { // drop the hook
in_one_hashmap = true; self.inner_mut()
handle.code_hooks.remove(&hook); .hooks
} .retain(|(hook_ptr, _hook_impl)| hook_ptr != &hook);
if handle.mem_hooks.contains_key(&hook) { err = unsafe { ffi::uc_hook_del(self.inner().uc, hook) };
in_one_hashmap = true;
handle.mem_hooks.remove(&hook);
}
if handle.block_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.block_hooks.remove(&hook);
}
if handle.intr_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.intr_hooks.remove(&hook);
}
if handle.insn_in_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.insn_in_hooks.remove(&hook);
}
if handle.insn_out_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.insn_out_hooks.remove(&hook);
}
if handle.insn_sys_hooks.contains_key(&hook) {
in_one_hashmap = true;
handle.insn_sys_hooks.remove(&hook);
}
if in_one_hashmap {
err = unsafe { ffi::uc_hook_del(handle.uc, hook) };
} else {
err = uc_error::HOOK;
}
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
@ -678,10 +675,10 @@ impl<'a> UnicornHandle<'a> {
/// Allocate and return an empty Unicorn context. /// Allocate and return an empty Unicorn context.
/// ///
/// To be populated via context_save. /// To be populated via `context_save`.
pub fn context_alloc(&self) -> Result<Context, uc_error> { pub fn context_alloc(&self) -> Result<Context, uc_error> {
let mut empty_context: ffi::uc_context = Default::default(); let mut empty_context: ffi::uc_context = ptr::null_mut();
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut empty_context) }; let err = unsafe { ffi::uc_context_alloc(self.inner().uc, &mut empty_context) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(Context { Ok(Context {
context: empty_context, context: empty_context,
@ -693,7 +690,7 @@ impl<'a> UnicornHandle<'a> {
/// Save current Unicorn context to previously allocated Context struct. /// Save current Unicorn context to previously allocated Context struct.
pub fn context_save(&self, context: &mut Context) -> Result<(), uc_error> { pub fn context_save(&self, context: &mut Context) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_context_save(self.inner.uc, context.context) }; let err = unsafe { ffi::uc_context_save(self.inner().uc, context.context) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -703,16 +700,16 @@ impl<'a> UnicornHandle<'a> {
/// Allocate and return a Context struct initialized with the current CPU context. /// Allocate and return a Context struct initialized with the current CPU context.
/// ///
/// This can be used for fast rollbacks with context_restore. /// This can be used for fast rollbacks with `context_restore`.
/// In case of many non-concurrent context saves, use context_alloc and *_save /// In case of many non-concurrent context saves, use `context_alloc` and *_save
/// individually to avoid unnecessary allocations. /// individually to avoid unnecessary allocations.
pub fn context_init(&self) -> Result<Context, uc_error> { pub fn context_init(&self) -> Result<Context, uc_error> {
let mut new_context: ffi::uc_context = Default::default(); let mut new_context: ffi::uc_context = ptr::null_mut();
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut new_context) }; let err = unsafe { ffi::uc_context_alloc(self.inner().uc, &mut new_context) };
if err != uc_error::OK { if err != uc_error::OK {
return Err(err); return Err(err);
} }
let err = unsafe { ffi::uc_context_save(self.inner.uc, new_context) }; let err = unsafe { ffi::uc_context_save(self.inner().uc, new_context) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(Context { Ok(Context {
context: new_context, context: new_context,
@ -729,7 +726,7 @@ impl<'a> UnicornHandle<'a> {
/// internal metadata. Contexts may not be shared across engine instances with /// internal metadata. Contexts may not be shared across engine instances with
/// differing arches or modes. Memory has to be restored manually, if needed. /// differing arches or modes. Memory has to be restored manually, if needed.
pub fn context_restore(&self, context: &Context) -> Result<(), uc_error> { pub fn context_restore(&self, context: &Context) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_context_restore(self.inner.uc, context.context) }; let err = unsafe { ffi::uc_context_restore(self.inner().uc, context.context) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -750,11 +747,13 @@ impl<'a> UnicornHandle<'a> {
timeout: u64, timeout: u64,
count: usize, count: usize,
) -> Result<(), uc_error> { ) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_emu_start(self.inner.uc, begin, until, timeout, count as _) }; unsafe {
if err == uc_error::OK { let err = ffi::uc_emu_start(self.inner().uc, begin, until, timeout, count as _);
Ok(()) if err == uc_error::OK {
} else { Ok(())
Err(err) } else {
Err(err)
}
} }
} }
@ -763,7 +762,7 @@ impl<'a> UnicornHandle<'a> {
/// This is usually called from callback function in hooks. /// This is usually called from callback function in hooks.
/// NOTE: For now, this will stop the execution only after the current block. /// NOTE: For now, this will stop the execution only after the current block.
pub fn emu_stop(&mut self) -> Result<(), uc_error> { pub fn emu_stop(&mut self) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_emu_stop(self.inner.uc) }; let err = unsafe { ffi::uc_emu_stop(self.inner().uc) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -773,14 +772,50 @@ impl<'a> UnicornHandle<'a> {
/// Query the internal status of the engine. /// Query the internal status of the engine.
/// ///
/// supported: MODE, PAGE_SIZE, ARCH /// supported: `MODE`, `PAGE_SIZE`, `ARCH`
pub fn query(&self, query: Query) -> Result<usize, uc_error> { pub fn query(&self, query: Query) -> Result<usize, uc_error> {
let mut result: libc::size_t = Default::default(); let mut result: libc::size_t = Default::default();
let err = unsafe { ffi::uc_query(self.inner.uc, query, &mut result) }; let err = unsafe { ffi::uc_query(self.inner().uc, query, &mut result) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(result) Ok(result)
} else { } else {
Err(err) Err(err)
} }
} }
/// Gets the current program counter for this `unicorn` instance.
#[inline]
pub fn pc_read(&self) -> Result<u64, uc_error> {
let arch = self.get_arch();
let reg = match arch {
Arch::X86 => RegisterX86::RIP as i32,
Arch::ARM => RegisterARM::PC as i32,
Arch::ARM64 => RegisterARM64::PC as i32,
Arch::MIPS => RegisterMIPS::PC as i32,
Arch::SPARC => RegisterSPARC::PC as i32,
Arch::M68K => RegisterM68K::PC as i32,
Arch::PPC => RegisterPPC::PC as i32,
Arch::RISCV => RegisterRISCV::PC as i32,
Arch::MAX => panic!("Illegal Arch specified"),
};
self.reg_read(reg)
}
/// Sets the program counter for this `unicorn` instance.
#[inline]
pub fn set_pc(&mut self, value: u64) -> Result<(), uc_error> {
let arch = self.get_arch();
let reg = match arch {
Arch::X86 => RegisterX86::RIP as i32,
Arch::ARM => RegisterARM::PC as i32,
Arch::ARM64 => RegisterARM64::PC as i32,
Arch::MIPS => RegisterMIPS::PC as i32,
Arch::SPARC => RegisterSPARC::PC as i32,
Arch::M68K => RegisterM68K::PC as i32,
Arch::PPC => RegisterPPC::PC as i32,
Arch::RISCV => RegisterRISCV::PC as i32,
Arch::MAX => panic!("Illegal Arch specified"),
};
self.reg_write(reg, value)
}
} }

View File

@ -21,5 +21,10 @@ pub enum RegisterM68K {
D7, D7,
SR, SR,
PC, PC,
ENDING, }
impl From<RegisterM68K> for i32 {
fn from(r: RegisterM68K) -> Self {
r as i32
}
} }

View File

@ -245,3 +245,9 @@ impl RegisterMIPS {
pub const LO2: RegisterMIPS = RegisterMIPS::AC2; pub const LO2: RegisterMIPS = RegisterMIPS::AC2;
pub const LO3: RegisterMIPS = RegisterMIPS::AC3; pub const LO3: RegisterMIPS = RegisterMIPS::AC3;
} }
impl From<RegisterMIPS> for i32 {
fn from(r: RegisterMIPS) -> Self {
r as i32
}
}

View File

@ -40,3 +40,9 @@ pub enum RegisterPPC {
GPR30 = 32, GPR30 = 32,
GPR31 = 33, GPR31 = 33,
} }
impl From<RegisterPPC> for i32 {
fn from(r: RegisterPPC) -> Self {
r as i32
}
}

View File

@ -211,3 +211,9 @@ impl RegisterRISCV {
pub const FT10: RegisterRISCV = RegisterRISCV::F30; pub const FT10: RegisterRISCV = RegisterRISCV::F30;
pub const FT11: RegisterRISCV = RegisterRISCV::F31; pub const FT11: RegisterRISCV = RegisterRISCV::F31;
} }
impl From<RegisterRISCV> for i32 {
fn from(r: RegisterRISCV) -> Self {
r as i32
}
}

View File

@ -1,8 +1,7 @@
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
// SPARC registers // SPARC registers
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum RegisterSPARC { pub enum RegisterSPARC {
INVALID = 0, INVALID = 0,
F0 = 1, F0 = 1,
@ -103,3 +102,9 @@ impl RegisterSPARC {
pub const O6: RegisterSPARC = RegisterSPARC::SP; pub const O6: RegisterSPARC = RegisterSPARC::SP;
pub const I6: RegisterSPARC = RegisterSPARC::FP; pub const I6: RegisterSPARC = RegisterSPARC::FP;
} }
impl From<RegisterSPARC> for i32 {
fn from(r: RegisterSPARC) -> Self {
r as i32
}
}

View File

@ -11,6 +11,7 @@ pub const MILISECOND_SCALE: u64 = 1_000;
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum uc_error { pub enum uc_error {
OK = 0, OK = 0,
NOMEM = 1, NOMEM = 1,
@ -89,6 +90,7 @@ bitflags! {
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum Query { pub enum Query {
MODE = 1, MODE = 1,
PAGE_SIZE = 2, PAGE_SIZE = 2,

View File

@ -1,9 +1,7 @@
#![allow(non_camel_case_types)]
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
// X86 registers // X86 registers
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms, non_camel_case_types)]
pub enum RegisterX86 { pub enum RegisterX86 {
INVALID = 0, INVALID = 0,
AH = 1, AH = 1,
@ -242,8 +240,15 @@ pub enum RegisterX86 {
ENDING = 234, ENDING = 234,
} }
impl From<RegisterX86> for i32 {
fn from(r: RegisterX86) -> Self {
r as i32
}
}
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum InsnX86 { pub enum InsnX86 {
IN = 218, IN = 218,
OUT = 500, OUT = 500,
@ -254,6 +259,7 @@ pub enum InsnX86 {
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum InsnSysX86 { pub enum InsnSysX86 {
SYSCALL = InsnX86::SYSCALL as isize, SYSCALL = InsnX86::SYSCALL as isize,
SYSENTER = InsnX86::SYSENTER as isize, SYSENTER = InsnX86::SYSENTER as isize,

View File

@ -1,9 +1,11 @@
use std::cell::RefCell; extern crate alloc;
use std::rc::Rc;
use alloc::rc::Rc;
use core::cell::RefCell;
use unicorn_engine::unicorn_const::{ use unicorn_engine::unicorn_const::{
uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE, uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE,
}; };
use unicorn_engine::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86}; use unicorn_engine::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86, Unicorn};
pub static X86_REGISTERS: [RegisterX86; 125] = [ pub static X86_REGISTERS: [RegisterX86; 125] = [
RegisterX86::AH, RegisterX86::AH,
@ -133,17 +135,14 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [
RegisterX86::R15W, RegisterX86::R15W,
]; ];
type Unicorn<'a> = unicorn_engine::UnicornHandle<'a>;
#[test] #[test]
fn emulate_x86() { fn emulate_x86() {
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance"); .expect("failed to initialize unicorn instance");
let mut emu = unicorn.borrow(); assert_eq!(emu.reg_write(RegisterX86::EAX, 123), Ok(()));
assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 123), Ok(())); assert_eq!(emu.reg_read(RegisterX86::EAX), Ok(123));
assert_eq!(emu.reg_read(RegisterX86::EAX as i32), Ok(123));
// Attempt to write to memory before mapping it. // Attempt to write to memory before mapping it.
assert_eq!( assert_eq!(
@ -158,8 +157,8 @@ fn emulate_x86() {
Ok(x86_code32.clone()) Ok(x86_code32.clone())
); );
assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(())); assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(()));
assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(())); assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(()));
assert_eq!( assert_eq!(
emu.emu_start( emu.emu_start(
@ -170,8 +169,8 @@ fn emulate_x86() {
), ),
Ok(()) Ok(())
); );
assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11)); assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11));
assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49)); assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49));
} }
#[test] #[test]
@ -183,16 +182,15 @@ fn x86_code_callback() {
let codes_cell = Rc::new(RefCell::new(codes)); let codes_cell = Rc::new(RefCell::new(codes));
let callback_codes = codes_cell.clone(); let callback_codes = codes_cell.clone();
let callback = move |_: Unicorn<'_>, address: u64, size: u32| { let callback = move |_: &mut Unicorn<'_, ()>, address: u64, size: u32| {
let mut codes = callback_codes.borrow_mut(); let mut codes = callback_codes.borrow_mut();
codes.push(CodeExpectation(address, size)); codes.push(CodeExpectation(address, size));
}; };
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance"); .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(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
@ -215,15 +213,14 @@ fn x86_intr_callback() {
let intr_cell = Rc::new(RefCell::new(IntrExpectation(0))); let intr_cell = Rc::new(RefCell::new(IntrExpectation(0)));
let callback_intr = intr_cell.clone(); let callback_intr = intr_cell.clone();
let callback = move |_: Unicorn<'_>, intno: u32| { let callback = move |_: &mut Unicorn<'_, ()>, intno: u32| {
*callback_intr.borrow_mut() = IntrExpectation(intno); *callback_intr.borrow_mut() = IntrExpectation(intno);
}; };
let x86_code32: Vec<u8> = vec![0xcd, 0x80]; // INT 0x80; let x86_code32: Vec<u8> = vec![0xcd, 0x80]; // INT 0x80;
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance"); .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(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
@ -257,17 +254,20 @@ fn x86_mem_callback() {
let mems_cell = Rc::new(RefCell::new(mems)); let mems_cell = Rc::new(RefCell::new(mems));
let callback_mems = mems_cell.clone(); let callback_mems = mems_cell.clone();
let callback = let callback = move |uc: &mut Unicorn<'_, ()>,
move |uc: Unicorn<'_>, mem_type: MemType, address: u64, size: usize, value: i64| { mem_type: MemType,
let mut mems = callback_mems.borrow_mut(); address: u64,
let mut uc = uc; size: usize,
value: i64| {
let mut mems = callback_mems.borrow_mut();
mems.push(MemExpectation(mem_type, address, size, value)); mems.push(MemExpectation(mem_type, address, size, value));
if mem_type == MemType::READ_UNMAPPED { if mem_type == MemType::READ_UNMAPPED {
uc.mem_map(address, 0x1000, Permission::ALL).unwrap(); uc.mem_map(address, 0x1000, Permission::ALL).unwrap();
} }
}; true
};
// mov eax, 0xdeadbeef; // mov eax, 0xdeadbeef;
// mov [0x2000], eax; // mov [0x2000], eax;
@ -276,16 +276,15 @@ fn x86_mem_callback() {
0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xA3, 0x00, 0x20, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x01, 0x00, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xA3, 0x00, 0x20, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x01, 0x00,
]; ];
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance"); .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(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu let hook = emu
.add_mem_hook(HookType::MEM_ALL, 0, std::u64::MAX, callback) .add_mem_hook(HookType::MEM_ALL, 0, u64::MAX, callback)
.expect("failed to add memory hook"); .expect("failed to add memory hook");
assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 0x123), Ok(())); assert_eq!(emu.reg_write(RegisterX86::EAX, 0x123), Ok(()));
assert_eq!( assert_eq!(
emu.emu_start( emu.emu_start(
0x1000, 0x1000,
@ -308,15 +307,14 @@ fn x86_insn_in_callback() {
let insn_cell = Rc::new(RefCell::new(InsnInExpectation(0, 0))); let insn_cell = Rc::new(RefCell::new(InsnInExpectation(0, 0)));
let callback_insn = insn_cell.clone(); let callback_insn = insn_cell.clone();
let callback = move |_: Unicorn<'_>, port: u32, size: usize| { let callback = move |_: &mut Unicorn<()>, port: u32, size: usize| {
*callback_insn.borrow_mut() = InsnInExpectation(port, size); *callback_insn.borrow_mut() = InsnInExpectation(port, size);
}; };
let x86_code32: Vec<u8> = vec![0xe5, 0x10]; // IN eax, 0x10; let x86_code32: Vec<u8> = vec![0xe5, 0x10]; // IN eax, 0x10;
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance"); .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(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
@ -345,15 +343,14 @@ fn x86_insn_out_callback() {
let insn_cell = Rc::new(RefCell::new(InsnOutExpectation(0, 0, 0))); let insn_cell = Rc::new(RefCell::new(InsnOutExpectation(0, 0, 0)));
let callback_insn = insn_cell.clone(); let callback_insn = insn_cell.clone();
let callback = move |_: Unicorn<'_>, port: u32, size: usize, value: u32| { let callback = move |_: &mut Unicorn<'_, ()>, port: u32, size: usize, value: u32| {
*callback_insn.borrow_mut() = InsnOutExpectation(port, size, value); *callback_insn.borrow_mut() = InsnOutExpectation(port, size, value);
}; };
let x86_code32: Vec<u8> = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al; let x86_code32: Vec<u8> = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al;
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance"); .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(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
@ -382,9 +379,9 @@ fn x86_insn_sys_callback() {
let insn_cell = Rc::new(RefCell::new(InsnSysExpectation(0))); let insn_cell = Rc::new(RefCell::new(InsnSysExpectation(0)));
let callback_insn = insn_cell.clone(); let callback_insn = insn_cell.clone();
let callback = move |uc: Unicorn<'_>| { let callback = move |uc: &mut Unicorn<'_, ()>| {
println!("!!!!"); println!("!!!!");
let rax = uc.reg_read(RegisterX86::RAX as i32).unwrap(); let rax = uc.reg_read(RegisterX86::RAX).unwrap();
*callback_insn.borrow_mut() = InsnSysExpectation(rax); *callback_insn.borrow_mut() = InsnSysExpectation(rax);
}; };
@ -393,9 +390,8 @@ fn x86_insn_sys_callback() {
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05,
]; ];
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_64) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_64)
.expect("failed to initialize unicorn instance"); .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(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
@ -420,11 +416,10 @@ fn x86_insn_sys_callback() {
fn emulate_arm() { fn emulate_arm() {
let arm_code32: Vec<u8> = vec![0x83, 0xb0]; // sub sp, #0xc let arm_code32: Vec<u8> = vec![0x83, 0xb0]; // sub sp, #0xc
let mut unicorn = unicorn_engine::Unicorn::new(Arch::ARM, Mode::THUMB) let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::THUMB)
.expect("failed to initialize unicorn instance"); .expect("failed to initialize unicorn instance");
let mut emu = unicorn.borrow(); assert_eq!(emu.reg_write(RegisterARM::R1, 123), Ok(()));
assert_eq!(emu.reg_write(RegisterARM::R1 as i32, 123), Ok(())); assert_eq!(emu.reg_read(RegisterARM::R1), Ok(123));
assert_eq!(emu.reg_read(RegisterARM::R1 as i32), Ok(123));
// Attempt to write to memory before mapping it. // Attempt to write to memory before mapping it.
assert_eq!( assert_eq!(
@ -439,8 +434,8 @@ fn emulate_arm() {
Ok(arm_code32.clone()) Ok(arm_code32.clone())
); );
assert_eq!(emu.reg_write(RegisterARM::SP as i32, 12), Ok(())); assert_eq!(emu.reg_write(RegisterARM::SP, 12), Ok(()));
assert_eq!(emu.reg_write(RegisterARM::R0 as i32, 10), Ok(())); assert_eq!(emu.reg_write(RegisterARM::R0, 10), Ok(()));
// ARM checks the least significant bit of the address to know // ARM checks the least significant bit of the address to know
// if the code is in Thumb mode. // if the code is in Thumb mode.
@ -453,24 +448,23 @@ fn emulate_arm() {
), ),
Ok(()) Ok(())
); );
assert_eq!(emu.reg_read(RegisterARM::SP as i32), Ok(0)); assert_eq!(emu.reg_read(RegisterARM::SP), Ok(0));
assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(10)); assert_eq!(emu.reg_read(RegisterARM::R0), Ok(10));
} }
#[test] #[test]
fn emulate_mips() { fn emulate_mips() {
let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456; let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456;
let mut unicorn = unicorn_engine::Unicorn::new(Arch::MIPS, Mode::MODE_32) let mut emu = unicorn_engine::Unicorn::new(Arch::MIPS, Mode::MODE_32)
.expect("failed to initialize unicorn instance"); .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_write(0x1000, &mips_code32), Ok(()));
assert_eq!( assert_eq!(
emu.mem_read_as_vec(0x1000, mips_code32.len()), emu.mem_read_as_vec(0x1000, mips_code32.len()),
Ok(mips_code32.clone()) Ok(mips_code32.clone())
); );
assert_eq!(emu.reg_write(RegisterMIPS::AT as i32, 0), Ok(())); assert_eq!(emu.reg_write(RegisterMIPS::AT, 0), Ok(()));
assert_eq!( assert_eq!(
emu.emu_start( emu.emu_start(
0x1000, 0x1000,
@ -480,24 +474,23 @@ fn emulate_mips() {
), ),
Ok(()) Ok(())
); );
assert_eq!(emu.reg_read(RegisterMIPS::AT as i32), Ok(0x3456)); assert_eq!(emu.reg_read(RegisterMIPS::AT), Ok(0x3456));
} }
#[test] #[test]
fn emulate_ppc() { fn emulate_ppc() {
let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3 let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3
let mut unicorn = unicorn_engine::Unicorn::new(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN) let mut emu = unicorn_engine::Unicorn::new(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN)
.expect("failed to initialize unicorn instance"); .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_write(0x1000, &ppc_code32), Ok(()));
assert_eq!( assert_eq!(
emu.mem_read_as_vec(0x1000, ppc_code32.len()), emu.mem_read_as_vec(0x1000, ppc_code32.len()),
Ok(ppc_code32.clone()) Ok(ppc_code32.clone())
); );
assert_eq!(emu.reg_write(RegisterPPC::GPR3 as i32, 42), Ok(())); assert_eq!(emu.reg_write(RegisterPPC::GPR3, 42), Ok(()));
assert_eq!(emu.reg_write(RegisterPPC::GPR6 as i32, 1337), Ok(())); assert_eq!(emu.reg_write(RegisterPPC::GPR6, 1337), Ok(()));
assert_eq!( assert_eq!(
emu.emu_start( emu.emu_start(
0x1000, 0x1000,
@ -507,14 +500,13 @@ fn emulate_ppc() {
), ),
Ok(()) Ok(())
); );
assert_eq!(emu.reg_read(RegisterPPC::GPR26 as i32), Ok(1379)); assert_eq!(emu.reg_read(RegisterPPC::GPR26), Ok(1379));
} }
#[test] #[test]
fn mem_unmapping() { fn mem_unmapping() {
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance"); .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(())); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
} }
@ -525,9 +517,8 @@ fn mem_map_ptr() {
let mut mem: [u8; 4000] = [0; 4000]; let mut mem: [u8; 4000] = [0; 4000];
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance"); .expect("failed to initialize unicorn instance");
let mut emu = unicorn.borrow();
// Attempt to write to memory before mapping it. // Attempt to write to memory before mapping it.
assert_eq!( assert_eq!(
@ -536,7 +527,7 @@ fn mem_map_ptr() {
); );
assert_eq!( assert_eq!(
emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _), unsafe { emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _) },
Ok(()) Ok(())
); );
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
@ -545,8 +536,8 @@ fn mem_map_ptr() {
Ok(x86_code32.clone()) Ok(x86_code32.clone())
); );
assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(())); assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(()));
assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(())); assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(()));
assert_eq!( assert_eq!(
emu.emu_start( emu.emu_start(
@ -557,8 +548,8 @@ fn mem_map_ptr() {
), ),
Ok(()) Ok(())
); );
assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11)); assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11));
assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49)); assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49));
assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(())); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
// Use a Vec for the emulator memory. // Use a Vec for the emulator memory.
@ -572,7 +563,7 @@ fn mem_map_ptr() {
); );
assert_eq!( assert_eq!(
emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _), unsafe { emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _) },
Ok(()) Ok(())
); );
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
@ -581,8 +572,8 @@ fn mem_map_ptr() {
Ok(x86_code32.clone()) Ok(x86_code32.clone())
); );
assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(())); assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(()));
assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(())); assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(()));
assert_eq!( assert_eq!(
emu.emu_start( emu.emu_start(
@ -593,20 +584,19 @@ fn mem_map_ptr() {
), ),
Ok(()) Ok(())
); );
assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11)); assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11));
assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49)); assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49));
assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(())); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
} }
#[test] #[test]
fn x86_context_save_and_restore() { fn x86_context_save_and_restore() {
for mode in vec![Mode::MODE_32, Mode::MODE_64] { for mode in [Mode::MODE_32, Mode::MODE_64] {
let x86_code: Vec<u8> = vec![ let x86_code = [
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05,
]; ];
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, mode) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, mode)
.expect("failed to initialize unicorn instance"); .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(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
let _ = emu.emu_start( let _ = emu.emu_start(
@ -621,16 +611,12 @@ fn x86_context_save_and_restore() {
let context = context.unwrap(); let context = context.unwrap();
/* and create a new emulator, into which we will "restore" that context */ /* and create a new emulator, into which we will "restore" that context */
let mut unicorn2 = unicorn_engine::Unicorn::new(Arch::X86, mode) let emu2 = unicorn_engine::Unicorn::new(Arch::X86, mode)
.expect("failed to initialize unicorn instance"); .expect("failed to initialize unicorn instance");
let emu2 = unicorn2.borrow();
assert_eq!(emu2.context_restore(&context), Ok(())); assert_eq!(emu2.context_restore(&context), Ok(()));
for register in X86_REGISTERS.iter() { for register in X86_REGISTERS.iter() {
println!("Testing register {:?}", register); println!("Testing register {:?}", register);
assert_eq!( assert_eq!(emu2.reg_read(*register), emu.reg_read(*register));
emu2.reg_read(*register as i32),
emu.reg_read(*register as i32)
);
} }
} }
} }
@ -644,16 +630,15 @@ fn x86_block_callback() {
let blocks_cell = Rc::new(RefCell::new(blocks)); let blocks_cell = Rc::new(RefCell::new(blocks));
let callback_blocks = blocks_cell.clone(); let callback_blocks = blocks_cell.clone();
let callback = move |_: Unicorn<'_>, address: u64, size: u32| { let callback = move |_: &mut Unicorn<'_, ()>, address: u64, size: u32| {
let mut blocks = callback_blocks.borrow_mut(); let mut blocks = callback_blocks.borrow_mut();
blocks.push(BlockExpectation(address, size)); blocks.push(BlockExpectation(address, size));
}; };
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance"); .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(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));