Unicorn rust bindings improvements
This commit is contained in:
parent
06f454d513
commit
f8f0d4471f
|
@ -10,7 +10,7 @@ include = [
|
|||
"/Cargo.toml",
|
||||
"/README.md",
|
||||
"/src/*",
|
||||
"build.rs"
|
||||
"build.rs",
|
||||
]
|
||||
license = "GPL-2.0"
|
||||
readme = "README.md"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use bytes::Buf;
|
||||
use flate2::read::GzDecoder;
|
||||
use reqwest::header::USER_AGENT;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{env, process::Command};
|
||||
use tar::Archive;
|
||||
|
||||
fn find_unicorn(unicorn_dir: &PathBuf) -> Option<PathBuf> {
|
||||
fn find_unicorn(unicorn_dir: &Path) -> Option<PathBuf> {
|
||||
for entry in std::fs::read_dir(unicorn_dir).ok()? {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
|
@ -49,6 +49,7 @@ fn download_unicorn() -> Option<String> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::branches_sharing_code)]
|
||||
fn main() {
|
||||
let profile = env::var("PROFILE").unwrap();
|
||||
|
||||
|
|
|
@ -164,3 +164,9 @@ impl RegisterARM {
|
|||
pub const FP: RegisterARM = RegisterARM::R11;
|
||||
pub const IP: RegisterARM = RegisterARM::R12;
|
||||
}
|
||||
|
||||
impl From<RegisterARM> for i32 {
|
||||
fn from(r: RegisterARM) -> Self {
|
||||
r as i32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||
|
||||
// ARM64 registers
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum RegisterARM64 {
|
||||
INVALID = 0,
|
||||
X29 = 1,
|
||||
|
@ -319,3 +317,9 @@ impl RegisterARM64 {
|
|||
pub const FP: RegisterARM64 = RegisterARM64::X29;
|
||||
pub const LR: RegisterARM64 = RegisterARM64::X30;
|
||||
}
|
||||
|
||||
impl From<RegisterARM64> for i32 {
|
||||
fn from(r: RegisterARM64) -> Self {
|
||||
r as i32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::unicorn_const::*;
|
||||
use crate::Unicorn;
|
||||
|
||||
use super::unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Query};
|
||||
use libc::{c_char, c_int};
|
||||
use std::ffi::c_void;
|
||||
use std::pin::Pin;
|
||||
use std::{ffi::c_void, marker::PhantomData};
|
||||
|
||||
pub type uc_handle = *mut c_void;
|
||||
pub type uc_hook = *mut c_void;
|
||||
|
@ -75,156 +76,120 @@ extern "C" {
|
|||
pub fn uc_context_alloc(engine: uc_handle, context: *mut uc_context) -> uc_error;
|
||||
pub fn uc_context_save(engine: uc_handle, context: uc_context) -> uc_error;
|
||||
pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error;
|
||||
pub fn uc_set_data_ptr(engine: uc_handle, ptr: *mut c_void) -> uc_error;
|
||||
pub fn uc_get_data_ptr(engine: uc_handle) -> *mut c_void;
|
||||
}
|
||||
|
||||
pub struct CodeHook {
|
||||
pub unicorn: *mut crate::UnicornInner,
|
||||
pub callback: Box<dyn FnMut(crate::UnicornHandle, u64, u32)>,
|
||||
pub struct UcHook<'a, D: 'a, F: 'a> {
|
||||
pub callback: F,
|
||||
pub phantom: PhantomData<&'a D>,
|
||||
}
|
||||
|
||||
pub struct BlockHook {
|
||||
pub unicorn: *mut crate::UnicornInner,
|
||||
pub callback: Box<dyn FnMut(crate::UnicornHandle, u64, u32)>,
|
||||
pub trait IsUcHook<'a> {}
|
||||
|
||||
impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {}
|
||||
|
||||
fn read_uc_from_uc_handle<'a, D>(uc: uc_handle) -> &'a mut crate::Unicorn<'a, D>
|
||||
where
|
||||
D: 'a,
|
||||
{
|
||||
unsafe {
|
||||
(uc_get_data_ptr(uc) as *mut Unicorn<'a, D>)
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemHook {
|
||||
pub unicorn: *mut crate::UnicornInner,
|
||||
pub callback: Box<dyn FnMut(crate::UnicornHandle, MemType, u64, usize, i64)>,
|
||||
}
|
||||
|
||||
pub struct InterruptHook {
|
||||
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(
|
||||
pub extern "C" fn code_hook_proxy<D, F>(
|
||||
uc: uc_handle,
|
||||
address: u64,
|
||||
size: u32,
|
||||
user_data: *mut CodeHook,
|
||||
) {
|
||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||
user_data: *mut UcHook<D, F>,
|
||||
) where
|
||||
F: FnMut(&mut crate::Unicorn<D>, u64, u32),
|
||||
{
|
||||
let unicorn = read_uc_from_uc_handle(uc);
|
||||
let callback = unsafe { &mut (*user_data).callback };
|
||||
assert_eq!(uc, unicorn.uc);
|
||||
callback(
|
||||
crate::UnicornHandle {
|
||||
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||
},
|
||||
address,
|
||||
size,
|
||||
);
|
||||
callback(unicorn, address, size);
|
||||
}
|
||||
|
||||
pub extern "C" fn block_hook_proxy(
|
||||
pub extern "C" fn block_hook_proxy<D, F>(
|
||||
uc: uc_handle,
|
||||
address: u64,
|
||||
size: u32,
|
||||
user_data: *mut BlockHook,
|
||||
) {
|
||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||
user_data: *mut UcHook<D, F>,
|
||||
) where
|
||||
F: FnMut(&mut crate::Unicorn<D>, u64, u32),
|
||||
{
|
||||
let unicorn = read_uc_from_uc_handle(uc);
|
||||
let callback = unsafe { &mut (*user_data).callback };
|
||||
assert_eq!(uc, unicorn.uc);
|
||||
callback(
|
||||
crate::UnicornHandle {
|
||||
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||
},
|
||||
address,
|
||||
size,
|
||||
);
|
||||
callback(unicorn, address, size);
|
||||
}
|
||||
|
||||
pub extern "C" fn mem_hook_proxy(
|
||||
pub extern "C" fn mem_hook_proxy<D, F>(
|
||||
uc: uc_handle,
|
||||
mem_type: MemType,
|
||||
address: u64,
|
||||
size: u32,
|
||||
value: i64,
|
||||
user_data: *mut MemHook,
|
||||
) {
|
||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||
user_data: *mut UcHook<D, F>,
|
||||
) -> bool
|
||||
where
|
||||
F: FnMut(&mut crate::Unicorn<D>, MemType, u64, usize, i64) -> bool,
|
||||
{
|
||||
let unicorn = read_uc_from_uc_handle(uc);
|
||||
let callback = 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(unicorn, mem_type, address, size as usize, value)
|
||||
}
|
||||
|
||||
pub extern "C" fn intr_hook_proxy(uc: uc_handle, value: u32, user_data: *mut InterruptHook) {
|
||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||
pub extern "C" fn intr_hook_proxy<D, F>(uc: uc_handle, value: u32, user_data: *mut UcHook<D, F>)
|
||||
where
|
||||
F: FnMut(&mut crate::Unicorn<D>, u32),
|
||||
{
|
||||
let unicorn = read_uc_from_uc_handle(uc);
|
||||
let callback = unsafe { &mut (*user_data).callback };
|
||||
assert_eq!(uc, unicorn.uc);
|
||||
callback(
|
||||
crate::UnicornHandle {
|
||||
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||
},
|
||||
value,
|
||||
);
|
||||
callback(unicorn, value);
|
||||
}
|
||||
|
||||
pub extern "C" fn insn_in_hook_proxy(
|
||||
pub extern "C" fn insn_in_hook_proxy<D, F>(
|
||||
uc: uc_handle,
|
||||
port: u32,
|
||||
size: usize,
|
||||
user_data: *mut InstructionInHook,
|
||||
) {
|
||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||
user_data: *mut UcHook<D, F>,
|
||||
) where
|
||||
F: FnMut(&mut crate::Unicorn<D>, u32, usize),
|
||||
{
|
||||
let unicorn = read_uc_from_uc_handle(uc);
|
||||
let callback = unsafe { &mut (*user_data).callback };
|
||||
assert_eq!(uc, unicorn.uc);
|
||||
callback(
|
||||
crate::UnicornHandle {
|
||||
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||
},
|
||||
port,
|
||||
size,
|
||||
);
|
||||
callback(unicorn, port, size);
|
||||
}
|
||||
|
||||
pub extern "C" fn insn_out_hook_proxy(
|
||||
pub extern "C" fn insn_out_hook_proxy<D, F>(
|
||||
uc: uc_handle,
|
||||
port: u32,
|
||||
size: usize,
|
||||
value: u32,
|
||||
user_data: *mut InstructionOutHook,
|
||||
) {
|
||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||
user_data: *mut UcHook<D, F>,
|
||||
) where
|
||||
F: FnMut(&mut crate::Unicorn<D>, u32, usize, u32),
|
||||
{
|
||||
let unicorn = read_uc_from_uc_handle(uc);
|
||||
let callback = unsafe { &mut (*user_data).callback };
|
||||
assert_eq!(uc, unicorn.uc);
|
||||
callback(
|
||||
crate::UnicornHandle {
|
||||
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||
},
|
||||
port,
|
||||
size,
|
||||
value,
|
||||
);
|
||||
callback(unicorn, port, size, value);
|
||||
}
|
||||
|
||||
pub extern "C" fn insn_sys_hook_proxy(uc: uc_handle, user_data: *mut InstructionSysHook) {
|
||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||
pub extern "C" fn insn_sys_hook_proxy<D, F>(uc: uc_handle, user_data: *mut UcHook<D, F>)
|
||||
where
|
||||
F: FnMut(&mut crate::Unicorn<D>),
|
||||
{
|
||||
let unicorn = read_uc_from_uc_handle(uc);
|
||||
assert_eq!(uc, unicorn.uc);
|
||||
callback(crate::UnicornHandle {
|
||||
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||
});
|
||||
let callback = unsafe { &mut (*user_data).callback };
|
||||
callback(unicorn);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
//! use unicorn_engine::RegisterARM;
|
||||
//! 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 mut unicorn = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance");
|
||||
|
@ -35,17 +35,15 @@ mod arm64;
|
|||
mod m68k;
|
||||
mod mips;
|
||||
mod ppc;
|
||||
mod riscv;
|
||||
mod sparc;
|
||||
mod x86;
|
||||
pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, riscv::*, sparc::*, x86::*};
|
||||
use std::{marker::PhantomData, ptr};
|
||||
|
||||
pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, sparc::*, x86::*};
|
||||
|
||||
use ffi::uc_handle;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::marker::PhantomPinned;
|
||||
use std::pin::Pin;
|
||||
use unicorn_const::*;
|
||||
use libc::c_void;
|
||||
use unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Permission, Query};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
|
@ -53,106 +51,127 @@ pub struct Context {
|
|||
}
|
||||
|
||||
impl Context {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Context { context: 0 }
|
||||
}
|
||||
#[must_use]
|
||||
pub fn is_initialized(&self) -> bool {
|
||||
self.context != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Context {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::uc_context_free(self.context) };
|
||||
impl Default for Context {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Context {
|
||||
fn drop(&mut self) {
|
||||
if self.is_initialized() {
|
||||
unsafe {
|
||||
ffi::uc_context_free(self.context);
|
||||
}
|
||||
}
|
||||
self.context = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// 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 struct Unicorn<'a, D: 'a> {
|
||||
pub uc: uc_handle,
|
||||
pub arch: Arch,
|
||||
pub code_hooks: HashMap<*mut libc::c_void, Box<ffi::CodeHook>>,
|
||||
pub block_hooks: HashMap<*mut libc::c_void, Box<ffi::BlockHook>>,
|
||||
pub mem_hooks: HashMap<*mut libc::c_void, Box<ffi::MemHook>>,
|
||||
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,
|
||||
/// to keep ownership over the hook for this uc instance's lifetime
|
||||
pub hooks: Vec<(ffi::uc_hook, Box<dyn ffi::IsUcHook<'a> + 'a>)>,
|
||||
pub data: D,
|
||||
}
|
||||
|
||||
impl Unicorn {
|
||||
impl Unicorn<()> {
|
||||
/// Create a new instance of the unicorn engine for the specified architecture
|
||||
/// 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> {
|
||||
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(arch: Arch, mode: Mode) -> Result<Unicorn<'a, ()>, uc_error> {
|
||||
Self::new_with_data(arch, mode, ())
|
||||
}
|
||||
|
||||
/// 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 = 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(),
|
||||
_pin: std::marker::PhantomPinned,
|
||||
}),
|
||||
uc: handle,
|
||||
arch,
|
||||
data,
|
||||
hooks: vec![],
|
||||
})
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrow<'a>(&'a mut self) -> UnicornHandle<'a> {
|
||||
UnicornHandle {
|
||||
inner: self.inner.as_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Unicorn {
|
||||
/// Drop UC
|
||||
/// TODO: !!! Right now, this leaks the unicorn instance on purpose.
|
||||
/// UC 1 for some platforms, for example aarch64, seems to crash on cleanup.
|
||||
/// After updating to Unicorn 2, we should call `uc_close` again!
|
||||
impl<'a, D> Drop for Unicorn<'a, D> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::uc_close(self.inner.uc) };
|
||||
if !self.uc.is_null() {
|
||||
// TODO: !!!
|
||||
// This is a deliberate leak, get rid of it after updating to UC2!
|
||||
// unsafe { ffi::uc_close(self.uc) };
|
||||
}
|
||||
self.uc = ptr::null_mut();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for UnicornInner {
|
||||
impl<'a, D> std::fmt::Debug for Unicorn<'a, D> {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(formatter, "Unicorn {{ uc: {:p} }}", self.uc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> UnicornHandle<'a> {
|
||||
impl<'a, D> Unicorn<'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 {
|
||||
&self.data
|
||||
}
|
||||
|
||||
/// Return a mutable reference to whatever data was passed during initialization.
|
||||
pub fn get_data_mut(&mut self) -> &mut D {
|
||||
&mut self.data
|
||||
}
|
||||
|
||||
/// Return the architecture of the current emulator.
|
||||
pub fn get_arch(&self) -> Arch {
|
||||
self.inner.arch
|
||||
self.arch
|
||||
}
|
||||
|
||||
/// Returns a vector with the memory regions that are mapped in the emulator.
|
||||
pub fn mem_regions(&self) -> Result<Vec<MemRegion>, uc_error> {
|
||||
let mut nb_regions: u32 = 0;
|
||||
let mut p_regions: *const MemRegion = std::ptr::null_mut();
|
||||
let err = unsafe { ffi::uc_mem_regions(self.inner.uc, &mut p_regions, &mut nb_regions) };
|
||||
let p_regions: *const MemRegion = std::ptr::null_mut();
|
||||
let err = unsafe { ffi::uc_mem_regions(self.uc, &p_regions, &mut nb_regions) };
|
||||
if err == uc_error::OK {
|
||||
let mut regions = Vec::new();
|
||||
for i in 0..nb_regions {
|
||||
regions.push(unsafe { std::mem::transmute_copy(&*p_regions.offset(i as isize)) });
|
||||
regions.push(unsafe { std::mem::transmute_copy(&*p_regions.add(i as usize)) });
|
||||
}
|
||||
unsafe { libc::free(p_regions as _) };
|
||||
Ok(regions)
|
||||
|
@ -163,7 +182,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
|
||||
/// Read a range of bytes from memory at the specified address.
|
||||
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.uc, address, buf.as_mut_ptr(), buf.len()) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -174,7 +193,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
/// 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> {
|
||||
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.uc, address, buf.as_mut_ptr(), size) };
|
||||
if err == uc_error::OK {
|
||||
Ok(buf)
|
||||
} else {
|
||||
|
@ -183,7 +202,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
}
|
||||
|
||||
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.uc, address, bytes.as_ptr(), bytes.len()) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -193,6 +212,8 @@ impl<'a> UnicornHandle<'a> {
|
|||
|
||||
/// 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
|
||||
/// ensure that `size` matches the size of the passed buffer, an invalid `size` value will
|
||||
/// likely cause a crash in unicorn.
|
||||
|
@ -202,14 +223,14 @@ impl<'a> UnicornHandle<'a> {
|
|||
/// `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(
|
||||
pub unsafe 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) };
|
||||
let err = ffi::uc_mem_map_ptr(self.uc, address, size, perms.bits(), ptr);
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -227,7 +248,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
size: libc::size_t,
|
||||
perms: Permission,
|
||||
) -> 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.uc, address, size, perms.bits()) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -240,7 +261,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
/// `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> {
|
||||
let err = unsafe { ffi::uc_mem_unmap(self.inner.uc, address, size) };
|
||||
let err = unsafe { ffi::uc_mem_unmap(self.uc, address, size) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -258,7 +279,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
size: libc::size_t,
|
||||
perms: Permission,
|
||||
) -> 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.uc, address, size, perms.bits()) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -268,8 +289,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
|
||||
/// 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.uc, regid.into(), &value as *const _ as _) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -281,8 +301,8 @@ impl<'a> UnicornHandle<'a> {
|
|||
///
|
||||
/// 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> {
|
||||
let err = unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), value.as_ptr() as _) };
|
||||
pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: &[u8]) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_reg_write(self.uc, regid.into(), value.as_ptr() as _) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -295,8 +315,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
/// 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.uc, regid.into(), &mut value as *mut u64 as _) };
|
||||
if err == uc_error::OK {
|
||||
Ok(value)
|
||||
} else {
|
||||
|
@ -306,7 +325,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
|
||||
/// 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, ST (x86); Q, V (arm64)).
|
||||
/// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
|
||||
pub fn reg_read_long<T: Into<i32>>(&self, regid: T) -> Result<Box<[u8]>, uc_error> {
|
||||
let err: uc_error;
|
||||
let boxed: Box<[u8]>;
|
||||
|
@ -329,8 +348,6 @@ impl<'a> UnicornHandle<'a> {
|
|||
value = vec![0; 64];
|
||||
} else if curr_reg_id == x86::RegisterX86::GDTR as i32
|
||||
|| curr_reg_id == x86::RegisterX86::IDTR as i32
|
||||
|| (curr_reg_id >= x86::RegisterX86::ST0 as i32
|
||||
&& curr_reg_id <= x86::RegisterX86::ST7 as i32)
|
||||
{
|
||||
value = vec![0; 10]; // 64 bit base address in IA-32e mode
|
||||
} else {
|
||||
|
@ -350,7 +367,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
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.uc, curr_reg_id, value.as_mut_ptr() as _) };
|
||||
|
||||
if err == uc_error::OK {
|
||||
boxed = value.into_boxed_slice();
|
||||
|
@ -363,8 +380,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
/// 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.uc, regid.into(), &mut value as *mut i32 as _) };
|
||||
if err == uc_error::OK {
|
||||
Ok(value)
|
||||
} else {
|
||||
|
@ -373,36 +389,34 @@ impl<'a> UnicornHandle<'a> {
|
|||
}
|
||||
|
||||
/// Add a code hook.
|
||||
pub fn add_code_hook<F: 'static>(
|
||||
pub fn add_code_hook<F: 'a>(
|
||||
&mut self,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
callback: F,
|
||||
) -> Result<ffi::uc_hook, uc_error>
|
||||
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 user_data = Box::new(ffi::CodeHook {
|
||||
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||
callback: Box::new(callback),
|
||||
let mut user_data = Box::new(ffi::UcHook {
|
||||
callback,
|
||||
phantom: PhantomData::<&D>,
|
||||
});
|
||||
|
||||
let err = unsafe {
|
||||
ffi::uc_hook_add(
|
||||
self.inner.uc,
|
||||
self.uc,
|
||||
&mut hook_ptr,
|
||||
HookType::CODE,
|
||||
ffi::code_hook_proxy as _,
|
||||
ffi::code_hook_proxy::<D, F> as _,
|
||||
user_data.as_mut() as *mut _ as _,
|
||||
begin,
|
||||
end,
|
||||
)
|
||||
};
|
||||
if err == uc_error::OK {
|
||||
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||
.code_hooks
|
||||
.insert(hook_ptr, user_data);
|
||||
self.hooks.push((hook_ptr, user_data));
|
||||
Ok(hook_ptr)
|
||||
} else {
|
||||
Err(err)
|
||||
|
@ -410,31 +424,30 @@ impl<'a> UnicornHandle<'a> {
|
|||
}
|
||||
|
||||
/// 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
|
||||
F: FnMut(UnicornHandle, u64, u32),
|
||||
F: FnMut(&mut Unicorn<D>, u64, u32),
|
||||
{
|
||||
let mut hook_ptr = std::ptr::null_mut();
|
||||
let mut user_data = Box::new(ffi::BlockHook {
|
||||
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||
callback: Box::new(callback),
|
||||
let mut user_data = Box::new(ffi::UcHook {
|
||||
callback,
|
||||
phantom: PhantomData::<&D>,
|
||||
});
|
||||
|
||||
let err = unsafe {
|
||||
ffi::uc_hook_add(
|
||||
self.inner.uc,
|
||||
self.uc,
|
||||
&mut hook_ptr,
|
||||
HookType::BLOCK,
|
||||
ffi::block_hook_proxy as _,
|
||||
ffi::block_hook_proxy::<D, F> as _,
|
||||
user_data.as_mut() as *mut _ as _,
|
||||
1,
|
||||
0,
|
||||
)
|
||||
};
|
||||
if err == uc_error::OK {
|
||||
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||
.block_hooks
|
||||
.insert(hook_ptr, user_data);
|
||||
self.hooks.push((hook_ptr, user_data));
|
||||
|
||||
Ok(hook_ptr)
|
||||
} else {
|
||||
Err(err)
|
||||
|
@ -442,7 +455,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
}
|
||||
|
||||
/// Add a memory hook.
|
||||
pub fn add_mem_hook<F: 'static>(
|
||||
pub fn add_mem_hook<F: 'a>(
|
||||
&mut self,
|
||||
hook_type: HookType,
|
||||
begin: u64,
|
||||
|
@ -450,33 +463,32 @@ impl<'a> UnicornHandle<'a> {
|
|||
callback: F,
|
||||
) -> Result<ffi::uc_hook, uc_error>
|
||||
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) {
|
||||
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 mut user_data = Box::new(ffi::UcHook {
|
||||
callback,
|
||||
phantom: PhantomData::<&D>,
|
||||
});
|
||||
|
||||
let err = unsafe {
|
||||
ffi::uc_hook_add(
|
||||
self.inner.uc,
|
||||
self.uc,
|
||||
&mut hook_ptr,
|
||||
hook_type,
|
||||
ffi::mem_hook_proxy as _,
|
||||
ffi::mem_hook_proxy::<D, F> as _,
|
||||
user_data.as_mut() as *mut _ as _,
|
||||
begin,
|
||||
end,
|
||||
)
|
||||
};
|
||||
if err == uc_error::OK {
|
||||
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||
.mem_hooks
|
||||
.insert(hook_ptr, user_data);
|
||||
self.hooks.push((hook_ptr, user_data));
|
||||
|
||||
Ok(hook_ptr)
|
||||
} else {
|
||||
Err(err)
|
||||
|
@ -484,31 +496,30 @@ impl<'a> UnicornHandle<'a> {
|
|||
}
|
||||
|
||||
/// 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
|
||||
F: FnMut(UnicornHandle, u32),
|
||||
F: FnMut(&mut Unicorn<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 mut user_data = Box::new(ffi::UcHook {
|
||||
callback,
|
||||
phantom: PhantomData::<&D>,
|
||||
});
|
||||
|
||||
let err = unsafe {
|
||||
ffi::uc_hook_add(
|
||||
self.inner.uc,
|
||||
self.uc,
|
||||
&mut hook_ptr,
|
||||
HookType::INTR,
|
||||
ffi::intr_hook_proxy as _,
|
||||
ffi::intr_hook_proxy::<D, F> as _,
|
||||
user_data.as_mut() as *mut _ as _,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
};
|
||||
if err == uc_error::OK {
|
||||
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||
.intr_hooks
|
||||
.insert(hook_ptr, user_data);
|
||||
self.hooks.push((hook_ptr, user_data));
|
||||
|
||||
Ok(hook_ptr)
|
||||
} else {
|
||||
Err(err)
|
||||
|
@ -516,22 +527,22 @@ impl<'a> UnicornHandle<'a> {
|
|||
}
|
||||
|
||||
/// 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
|
||||
F: FnMut(UnicornHandle, u32, usize),
|
||||
F: FnMut(&mut Unicorn<D>, u32, usize) + 'a,
|
||||
{
|
||||
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 mut user_data = Box::new(ffi::UcHook {
|
||||
callback,
|
||||
phantom: PhantomData::<&D>,
|
||||
});
|
||||
|
||||
let err = unsafe {
|
||||
ffi::uc_hook_add(
|
||||
self.inner.uc,
|
||||
self.uc,
|
||||
&mut hook_ptr,
|
||||
HookType::INSN,
|
||||
ffi::insn_in_hook_proxy as _,
|
||||
ffi::insn_in_hook_proxy::<D, F> as _,
|
||||
user_data.as_mut() as *mut _ as _,
|
||||
0,
|
||||
0,
|
||||
|
@ -539,9 +550,8 @@ impl<'a> UnicornHandle<'a> {
|
|||
)
|
||||
};
|
||||
if err == uc_error::OK {
|
||||
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||
.insn_in_hooks
|
||||
.insert(hook_ptr, user_data);
|
||||
self.hooks.push((hook_ptr, user_data));
|
||||
|
||||
Ok(hook_ptr)
|
||||
} else {
|
||||
Err(err)
|
||||
|
@ -549,22 +559,22 @@ impl<'a> UnicornHandle<'a> {
|
|||
}
|
||||
|
||||
/// 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
|
||||
F: FnMut(UnicornHandle, u32, usize, u32),
|
||||
F: FnMut(&mut Unicorn<D>, u32, usize, u32) + 'a,
|
||||
{
|
||||
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 mut user_data = Box::new(ffi::UcHook {
|
||||
callback,
|
||||
phantom: PhantomData::<&D>,
|
||||
});
|
||||
|
||||
let err = unsafe {
|
||||
ffi::uc_hook_add(
|
||||
self.inner.uc,
|
||||
self.uc,
|
||||
&mut hook_ptr,
|
||||
HookType::INSN,
|
||||
ffi::insn_out_hook_proxy as _,
|
||||
ffi::insn_out_hook_proxy::<D, F> as _,
|
||||
user_data.as_mut() as *mut _ as _,
|
||||
0,
|
||||
0,
|
||||
|
@ -572,9 +582,8 @@ impl<'a> UnicornHandle<'a> {
|
|||
)
|
||||
};
|
||||
if err == uc_error::OK {
|
||||
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||
.insn_out_hooks
|
||||
.insert(hook_ptr, user_data);
|
||||
self.hooks.push((hook_ptr, user_data));
|
||||
|
||||
Ok(hook_ptr)
|
||||
} else {
|
||||
Err(err)
|
||||
|
@ -582,7 +591,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
}
|
||||
|
||||
/// Add hook for x86 SYSCALL or SYSENTER.
|
||||
pub fn add_insn_sys_hook<F: 'static>(
|
||||
pub fn add_insn_sys_hook<F>(
|
||||
&mut self,
|
||||
insn_type: x86::InsnSysX86,
|
||||
begin: u64,
|
||||
|
@ -590,20 +599,20 @@ impl<'a> UnicornHandle<'a> {
|
|||
callback: F,
|
||||
) -> Result<ffi::uc_hook, uc_error>
|
||||
where
|
||||
F: FnMut(UnicornHandle),
|
||||
F: FnMut(&mut Unicorn<D>) + 'a,
|
||||
{
|
||||
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 mut user_data = Box::new(ffi::UcHook {
|
||||
callback,
|
||||
phantom: PhantomData::<&D>,
|
||||
});
|
||||
|
||||
let err = unsafe {
|
||||
ffi::uc_hook_add(
|
||||
self.inner.uc,
|
||||
self.uc,
|
||||
&mut hook_ptr,
|
||||
HookType::INSN,
|
||||
ffi::insn_sys_hook_proxy as _,
|
||||
ffi::insn_sys_hook_proxy::<D, F> as _,
|
||||
user_data.as_mut() as *mut _ as _,
|
||||
begin,
|
||||
end,
|
||||
|
@ -611,9 +620,8 @@ impl<'a> UnicornHandle<'a> {
|
|||
)
|
||||
};
|
||||
if err == uc_error::OK {
|
||||
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||
.insn_sys_hooks
|
||||
.insert(hook_ptr, user_data);
|
||||
self.hooks.push((hook_ptr, user_data));
|
||||
|
||||
Ok(hook_ptr)
|
||||
} else {
|
||||
Err(err)
|
||||
|
@ -624,50 +632,13 @@ impl<'a> UnicornHandle<'a> {
|
|||
///
|
||||
/// `hook` is the value returned by `add_*_hook` functions.
|
||||
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 mut in_one_hashmap = false;
|
||||
|
||||
if handle.code_hooks.contains_key(&hook) {
|
||||
in_one_hashmap = true;
|
||||
handle.code_hooks.remove(&hook);
|
||||
}
|
||||
// drop the hook
|
||||
self.hooks
|
||||
.retain(|(hook_ptr, _hook_impl)| hook_ptr != &hook);
|
||||
|
||||
if handle.mem_hooks.contains_key(&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;
|
||||
}
|
||||
err = unsafe { ffi::uc_hook_del(self.uc, hook) };
|
||||
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
|
@ -678,10 +649,10 @@ impl<'a> UnicornHandle<'a> {
|
|||
|
||||
/// 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> {
|
||||
let mut empty_context: ffi::uc_context = Default::default();
|
||||
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut empty_context) };
|
||||
let err = unsafe { ffi::uc_context_alloc(self.uc, &mut empty_context) };
|
||||
if err == uc_error::OK {
|
||||
Ok(Context {
|
||||
context: empty_context,
|
||||
|
@ -693,7 +664,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
|
||||
/// Save current Unicorn context to previously allocated Context struct.
|
||||
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.uc, context.context) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -703,16 +674,16 @@ impl<'a> UnicornHandle<'a> {
|
|||
|
||||
/// 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
|
||||
/// This can be used for fast rollbacks with `context_restore`.
|
||||
/// 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();
|
||||
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut new_context) };
|
||||
let err = unsafe { ffi::uc_context_alloc(self.uc, &mut new_context) };
|
||||
if err != uc_error::OK {
|
||||
return Err(err);
|
||||
}
|
||||
let err = unsafe { ffi::uc_context_save(self.inner.uc, new_context) };
|
||||
let err = unsafe { ffi::uc_context_save(self.uc, new_context) };
|
||||
if err == uc_error::OK {
|
||||
Ok(Context {
|
||||
context: new_context,
|
||||
|
@ -729,7 +700,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
/// internal metadata. Contexts may not be shared across engine instances with
|
||||
/// differing arches or modes. Memory has to be restored manually, if needed.
|
||||
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.uc, context.context) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -750,11 +721,15 @@ impl<'a> UnicornHandle<'a> {
|
|||
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 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
unsafe {
|
||||
ffi::uc_set_data_ptr(self.uc, self as *mut _ as *mut _);
|
||||
let err = ffi::uc_emu_start(self.uc, begin, until, timeout, count as _);
|
||||
ffi::uc_set_data_ptr(self.uc, ptr::null_mut());
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -763,7 +738,7 @@ impl<'a> UnicornHandle<'a> {
|
|||
/// This is usually called from callback function in hooks.
|
||||
/// NOTE: For now, this will stop the execution only after the current block.
|
||||
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.uc) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -773,14 +748,46 @@ impl<'a> UnicornHandle<'a> {
|
|||
|
||||
/// 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> {
|
||||
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.uc, query, &mut result) };
|
||||
if err == uc_error::OK {
|
||||
Ok(result)
|
||||
} else {
|
||||
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,
|
||||
_ => panic!("Arch pc not yet know to the unicorn rust bindings"),
|
||||
};
|
||||
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,
|
||||
_ => panic!("Arch not yet known to the unicorn rust bindings"),
|
||||
};
|
||||
self.reg_write(reg, value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,5 +21,10 @@ pub enum RegisterM68K {
|
|||
D7,
|
||||
SR,
|
||||
PC,
|
||||
ENDING,
|
||||
}
|
||||
|
||||
impl From<RegisterM68K> for i32 {
|
||||
fn from(r: RegisterM68K) -> Self {
|
||||
r as i32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -245,3 +245,9 @@ impl RegisterMIPS {
|
|||
pub const LO2: RegisterMIPS = RegisterMIPS::AC2;
|
||||
pub const LO3: RegisterMIPS = RegisterMIPS::AC3;
|
||||
}
|
||||
|
||||
impl From<RegisterMIPS> for i32 {
|
||||
fn from(r: RegisterMIPS) -> Self {
|
||||
r as i32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,3 +40,9 @@ pub enum RegisterPPC {
|
|||
GPR30 = 32,
|
||||
GPR31 = 33,
|
||||
}
|
||||
|
||||
impl From<RegisterPPC> for i32 {
|
||||
fn from(r: RegisterPPC) -> Self {
|
||||
r as i32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,3 +211,9 @@ impl RegisterRISCV {
|
|||
pub const FT10: RegisterRISCV = RegisterRISCV::F30;
|
||||
pub const FT11: RegisterRISCV = RegisterRISCV::F31;
|
||||
}
|
||||
|
||||
impl From<RegisterRISCV> for i32 {
|
||||
fn from(r: RegisterRISCV) -> Self {
|
||||
r as i32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||
|
||||
// SPARC registers
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum RegisterSPARC {
|
||||
INVALID = 0,
|
||||
F0 = 1,
|
||||
|
@ -93,13 +92,10 @@ pub enum RegisterSPARC {
|
|||
Y = 86,
|
||||
XCC = 87,
|
||||
PC = 88,
|
||||
ENDING = 89,
|
||||
}
|
||||
|
||||
impl RegisterSPARC {
|
||||
// alias registers
|
||||
// (assoc) O6 = 84,
|
||||
// (assoc) I6 = 67,
|
||||
pub const O6: RegisterSPARC = RegisterSPARC::SP;
|
||||
pub const I6: RegisterSPARC = RegisterSPARC::FP;
|
||||
impl From<RegisterSPARC> for i32 {
|
||||
fn from(r: RegisterSPARC) -> Self {
|
||||
r as i32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ pub const MILISECOND_SCALE: u64 = 1_000;
|
|||
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum uc_error {
|
||||
OK = 0,
|
||||
NOMEM = 1,
|
||||
|
@ -89,6 +90,7 @@ bitflags! {
|
|||
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum Query {
|
||||
MODE = 1,
|
||||
PAGE_SIZE = 2,
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||
|
||||
// X86 registers
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[allow(clippy::upper_case_acronyms, non_camel_case_types)]
|
||||
pub enum RegisterX86 {
|
||||
INVALID = 0,
|
||||
AH = 1,
|
||||
|
@ -242,8 +240,15 @@ pub enum RegisterX86 {
|
|||
ENDING = 234,
|
||||
}
|
||||
|
||||
impl From<RegisterX86> for i32 {
|
||||
fn from(r: RegisterX86) -> Self {
|
||||
r as i32
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum InsnX86 {
|
||||
IN = 218,
|
||||
OUT = 500,
|
||||
|
@ -254,6 +259,7 @@ pub enum InsnX86 {
|
|||
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum InsnSysX86 {
|
||||
SYSCALL = InsnX86::SYSCALL as isize,
|
||||
SYSENTER = InsnX86::SYSENTER as isize,
|
||||
|
|
|
@ -32,6 +32,7 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [
|
|||
RegisterX86::EDX,
|
||||
RegisterX86::EFLAGS,
|
||||
RegisterX86::EIP,
|
||||
RegisterX86::EIZ,
|
||||
RegisterX86::ES,
|
||||
RegisterX86::ESI,
|
||||
RegisterX86::ESP,
|
||||
|
@ -46,6 +47,7 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [
|
|||
RegisterX86::RDI,
|
||||
RegisterX86::RDX,
|
||||
RegisterX86::RIP,
|
||||
RegisterX86::RIZ,
|
||||
RegisterX86::RSI,
|
||||
RegisterX86::RSP,
|
||||
RegisterX86::SI,
|
||||
|
@ -58,7 +60,17 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [
|
|||
RegisterX86::CR2,
|
||||
RegisterX86::CR3,
|
||||
RegisterX86::CR4,
|
||||
RegisterX86::CR5,
|
||||
RegisterX86::CR6,
|
||||
RegisterX86::CR7,
|
||||
RegisterX86::CR8,
|
||||
RegisterX86::CR9,
|
||||
RegisterX86::CR10,
|
||||
RegisterX86::CR11,
|
||||
RegisterX86::CR12,
|
||||
RegisterX86::CR13,
|
||||
RegisterX86::CR14,
|
||||
RegisterX86::CR15,
|
||||
RegisterX86::DR0,
|
||||
RegisterX86::DR1,
|
||||
RegisterX86::DR2,
|
||||
|
@ -67,6 +79,14 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [
|
|||
RegisterX86::DR5,
|
||||
RegisterX86::DR6,
|
||||
RegisterX86::DR7,
|
||||
RegisterX86::DR8,
|
||||
RegisterX86::DR9,
|
||||
RegisterX86::DR10,
|
||||
RegisterX86::DR11,
|
||||
RegisterX86::DR12,
|
||||
RegisterX86::DR13,
|
||||
RegisterX86::DR14,
|
||||
RegisterX86::DR15,
|
||||
RegisterX86::FP0,
|
||||
RegisterX86::FP1,
|
||||
RegisterX86::FP2,
|
||||
|
@ -133,15 +153,12 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [
|
|||
RegisterX86::R15W,
|
||||
];
|
||||
|
||||
type Unicorn<'a> = unicorn_engine::UnicornHandle<'a>;
|
||||
|
||||
#[test]
|
||||
fn emulate_x86() {
|
||||
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_emulator::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));
|
||||
|
||||
|
@ -183,16 +200,15 @@ fn x86_code_callback() {
|
|||
let codes_cell = Rc::new(RefCell::new(codes));
|
||||
|
||||
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();
|
||||
codes.push(CodeExpectation(address, size));
|
||||
};
|
||||
|
||||
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_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ())
|
||||
.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_write(0x1000, &x86_code32), Ok(()));
|
||||
|
||||
|
@ -215,15 +231,14 @@ fn x86_intr_callback() {
|
|||
let intr_cell = Rc::new(RefCell::new(IntrExpectation(0)));
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ())
|
||||
.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_write(0x1000, &x86_code32), Ok(()));
|
||||
|
||||
|
@ -251,22 +266,16 @@ 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 |uc: Unicorn<'_>, mem_type: MemType, address: u64, size: usize, value: i64| {
|
||||
move |_: &mut Unicorn<'_, ()>, mem_type: MemType, address: u64, size: usize, value: i64| {
|
||||
let mut mems = callback_mems.borrow_mut();
|
||||
let mut uc = uc;
|
||||
|
||||
mems.push(MemExpectation(mem_type, address, size, value));
|
||||
|
||||
if mem_type == MemType::READ_UNMAPPED {
|
||||
uc.mem_map(address, 0x1000, Permission::ALL).unwrap();
|
||||
}
|
||||
true
|
||||
};
|
||||
|
||||
// mov eax, 0xdeadbeef;
|
||||
|
@ -276,9 +285,8 @@ fn x86_mem_callback() {
|
|||
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_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ())
|
||||
.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_write(0x1000, &x86_code32), Ok(()));
|
||||
|
||||
|
@ -293,7 +301,7 @@ fn x86_mem_callback() {
|
|||
10 * SECOND_SCALE,
|
||||
0x1000
|
||||
),
|
||||
Ok(())
|
||||
Err(uc_error::READ_UNMAPPED)
|
||||
);
|
||||
|
||||
assert_eq!(expects, *mems_cell.borrow());
|
||||
|
@ -308,15 +316,14 @@ fn x86_insn_in_callback() {
|
|||
let insn_cell = Rc::new(RefCell::new(InsnInExpectation(0, 0)));
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ())
|
||||
.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_write(0x1000, &x86_code32), Ok(()));
|
||||
|
||||
|
@ -345,15 +352,14 @@ fn x86_insn_out_callback() {
|
|||
let insn_cell = Rc::new(RefCell::new(InsnOutExpectation(0, 0, 0)));
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ())
|
||||
.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_write(0x1000, &x86_code32), Ok(()));
|
||||
|
||||
|
@ -382,7 +388,7 @@ fn x86_insn_sys_callback() {
|
|||
let insn_cell = Rc::new(RefCell::new(InsnSysExpectation(0)));
|
||||
|
||||
let callback_insn = insn_cell.clone();
|
||||
let callback = move |uc: Unicorn<'_>| {
|
||||
let callback = move |uc: &mut Unicorn<'_, ()>| {
|
||||
println!("!!!!");
|
||||
let rax = uc.reg_read(RegisterX86::RAX as i32).unwrap();
|
||||
*callback_insn.borrow_mut() = InsnSysExpectation(rax);
|
||||
|
@ -393,9 +399,8 @@ fn x86_insn_sys_callback() {
|
|||
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_emulator::Unicorn::new(Arch::X86, Mode::MODE_64, ())
|
||||
.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_write(0x1000, &x86_code), Ok(()));
|
||||
|
||||
|
@ -420,9 +425,8 @@ fn x86_insn_sys_callback() {
|
|||
fn emulate_arm() {
|
||||
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_emulator::Unicorn::new(Arch::ARM, Mode::THUMB, ())
|
||||
.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));
|
||||
|
||||
|
@ -461,9 +465,8 @@ fn emulate_arm() {
|
|||
fn emulate_mips() {
|
||||
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_emulator::Unicorn::new(Arch::MIPS, Mode::MODE_32, ())
|
||||
.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_write(0x1000, &mips_code32), Ok(()));
|
||||
assert_eq!(
|
||||
|
@ -487,9 +490,8 @@ fn emulate_mips() {
|
|||
fn emulate_ppc() {
|
||||
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_emulator::Unicorn::new(Arch::PPC, Mode::PPC32, ())
|
||||
.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_write(0x1000, &ppc_code32), Ok(()));
|
||||
assert_eq!(
|
||||
|
@ -512,9 +514,8 @@ fn emulate_ppc() {
|
|||
|
||||
#[test]
|
||||
fn mem_unmapping() {
|
||||
let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||
let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ())
|
||||
.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_unmap(0x1000, 0x4000), Ok(()));
|
||||
}
|
||||
|
@ -525,9 +526,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_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||
let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ())
|
||||
.expect("failed to initialize unicorn instance");
|
||||
let mut emu = unicorn.borrow();
|
||||
|
||||
// Attempt to write to memory before mapping it.
|
||||
assert_eq!(
|
||||
|
@ -536,7 +536,7 @@ fn mem_map_ptr() {
|
|||
);
|
||||
|
||||
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(())
|
||||
);
|
||||
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||
|
@ -572,7 +572,7 @@ fn mem_map_ptr() {
|
|||
);
|
||||
|
||||
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(())
|
||||
);
|
||||
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||
|
@ -600,13 +600,12 @@ fn mem_map_ptr() {
|
|||
|
||||
#[test]
|
||||
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![
|
||||
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_emulator::Unicorn::new(Arch::X86, *mode, ())
|
||||
.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_write(0x1000, &x86_code), Ok(()));
|
||||
let _ = emu.emu_start(
|
||||
|
@ -621,9 +620,8 @@ 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_engine::Unicorn::new(Arch::X86, mode)
|
||||
let emu2 = unicorn_emulator::Unicorn::new(Arch::X86, *mode, ())
|
||||
.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);
|
||||
|
@ -644,16 +642,15 @@ fn x86_block_callback() {
|
|||
let blocks_cell = Rc::new(RefCell::new(blocks));
|
||||
|
||||
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();
|
||||
blocks.push(BlockExpectation(address, size));
|
||||
};
|
||||
|
||||
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_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ())
|
||||
.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_write(0x1000, &x86_code32), Ok(()));
|
||||
|
||||
|
|
|
@ -342,6 +342,7 @@ struct uc_struct {
|
|||
bool no_exit_request; // Disable check_exit_request temporarily. A
|
||||
// workaround to treat the IT block as a whole block.
|
||||
bool init_done; // Whether the initialization is done.
|
||||
void *data_ptr; // optional data pointer for bindings
|
||||
};
|
||||
|
||||
// Metadata stub for the variable-size cpu context used with uc_context_*()
|
||||
|
|
21
uc.c
21
uc.c
|
@ -686,6 +686,27 @@ static void clear_deleted_hooks(uc_engine *uc)
|
|||
list_clear(&uc->hooks_to_del);
|
||||
}
|
||||
|
||||
// set a data ptr (for use in bindings)
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_emu_set_data_ptr(uc_engine *uc, void *data)
|
||||
{
|
||||
if (uc == NULL) {
|
||||
return UC_ERR_ARG;
|
||||
}
|
||||
uc->data_ptr = data;
|
||||
}
|
||||
|
||||
// get a data ptr (for use in bindings)
|
||||
UNICORN_EXPORT
|
||||
void *uc_emu_get_data_ptr(uc_engine *uc, void *data)
|
||||
{
|
||||
if (uc == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return uc->data_ptr;
|
||||
}
|
||||
|
||||
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until,
|
||||
uint64_t timeout, size_t count)
|
||||
|
|
Loading…
Reference in New Issue