removed need for extra api; no_std

This commit is contained in:
Dominik Maier
2021-11-10 04:11:14 +01:00
parent 492779d7d4
commit 177b21c2af
6 changed files with 311 additions and 293 deletions

View File

@ -3,7 +3,7 @@ name = "unicorn-engine"
version = "2.0.0-rc3" version = "2.0.0-rc3"
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",

View File

@ -18,7 +18,12 @@ fn find_unicorn(unicorn_dir: &Path) -> Option<PathBuf> {
None None
} }
fn download_unicorn() -> Option<String> { fn out_dir() -> PathBuf {
let out_dir = env::var("OUT_DIR").unwrap();
Path::new(&out_dir).to_path_buf()
}
fn download_unicorn() -> PathBuf {
// https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-tar // https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-tar
let pkg_version; let pkg_version;
if let Ok(unicorn_version) = env::var("UNICORN_VERSION") { if let Ok(unicorn_version) = env::var("UNICORN_VERSION") {
@ -26,7 +31,7 @@ fn download_unicorn() -> Option<String> {
} else { } else {
pkg_version = env::var("CARGO_PKG_VERSION").unwrap(); pkg_version = env::var("CARGO_PKG_VERSION").unwrap();
} }
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let out_dir = out_dir();
let client = reqwest::blocking::Client::new(); let client = reqwest::blocking::Client::new();
let resp = client let resp = client
.get(format!( .get(format!(
@ -43,85 +48,107 @@ fn download_unicorn() -> Option<String> {
let mut archive = Archive::new(tar); let mut archive = Archive::new(tar);
archive.unpack(&out_dir).unwrap(); archive.unpack(&out_dir).unwrap();
match find_unicorn(&out_dir) { find_unicorn(&out_dir).unwrap()
Some(dir) => Some(String::from(out_dir.join(dir).to_str()?)),
None => None,
}
} }
#[allow(clippy::branches_sharing_code)] #[allow(clippy::too_many_lines)]
fn main() { fn main() {
let profile = env::var("PROFILE").unwrap(); let profile = env::var("PROFILE").unwrap();
let unicorn_dir = download_unicorn().unwrap(); if let Some(unicorn_dir) = find_unicorn(&out_dir()) {
let rust_build_path = unicorn_dir.join("rust_build");
println!("cargo:rerun-if-changed={}", &unicorn_dir);
// We don't use TARGET since we can't cross-build.
if env::consts::OS == "windows" {
// Windows
let mut cmd = Command::new("cmake");
cmd.current_dir(&unicorn_dir)
.arg("-B")
.arg("rust_build")
.arg("-DUNICORN_BUILD_SHARED=off")
.arg("-G")
.arg("Visual Studio 16 2019");
if profile == "debug" {
cmd.arg("-DCMAKE_BUILD_TYPE=Debug");
} else {
cmd.arg("-DCMAKE_BUILD_TYPE=Release");
}
cmd.output()
.expect("Fail to create build directory on Windows.");
let mut platform = "x64";
let mut conf = "Release";
if std::mem::size_of::<usize>() == 4 {
platform = "Win32";
}
if profile == "debug" {
conf = "Debug";
}
Command::new("msbuild")
.current_dir(format!("{}/rust_build", &unicorn_dir))
.arg("unicorn.sln")
.arg("-m")
.arg("-p:Platform=".to_owned() + platform)
.arg("-p:Configuration=".to_owned() + conf)
.output()
.expect("Fail to build unicorn on Win32.");
println!( println!(
"cargo:rustc-link-search={}/rust_build/{}", "cargo:rustc-link-search={}",
unicorn_dir, conf rust_build_path.to_str().unwrap()
);
println!(
"cargo:rustc-link-search={}",
rust_build_path.join("Debug").to_str().unwrap()
);
println!(
"cargo:rustc-link-search={}",
rust_build_path.join("Release").to_str().unwrap()
); );
} else { } else {
// Most Unix-like systems let unicorn_dir = if env::var("CI").is_ok() {
let mut cmd = Command::new("cmake"); Path::new("..").join("..")
cmd.current_dir(&unicorn_dir)
.arg("-B")
.arg("rust_build")
.arg("-DUNICORN_BUILD_SHARED=off");
if profile == "debug" {
cmd.arg("-DCMAKE_BUILD_TYPE=Debug");
} else { } else {
cmd.arg("-DCMAKE_BUILD_TYPE=Release"); println!("cargo:warning=Unicorn not found. Downloading...");
download_unicorn()
};
let rust_build_path = unicorn_dir.join("rust_build");
let mut cmd = Command::new("cmake");
// We don't use TARGET since we can't cross-build.
if env::consts::OS == "windows" {
// Windows
cmd.current_dir(&unicorn_dir)
.arg("-B")
.arg("rust_build")
.arg("-DUNICORN_BUILD_SHARED=off")
.arg("-G")
.arg("Visual Studio 16 2019");
if profile == "debug" {
cmd.arg("-DCMAKE_BUILD_TYPE=Debug");
} else {
cmd.arg("-DCMAKE_BUILD_TYPE=Release");
}
cmd.output()
.expect("Fail to create build directory on Windows.");
let mut platform = "x64";
let mut conf = "Release";
if std::mem::size_of::<usize>() == 4 {
platform = "Win32";
}
if profile == "debug" {
conf = "Debug";
}
Command::new("msbuild")
.current_dir(&rust_build_path)
.arg("unicorn.sln")
.arg("-m")
.arg("-p:Platform=".to_owned() + platform)
.arg("-p:Configuration=".to_owned() + conf)
.output()
.expect("Fail to build unicorn on Win32.");
println!(
"cargo:rustc-link-search={}",
rust_build_path.join(conf).to_str().unwrap()
);
} else {
// Most Unix-like systems
let mut cmd = Command::new("cmake");
cmd.current_dir(&unicorn_dir)
.arg("-B")
.arg("rust_build")
.arg("-DUNICORN_BUILD_SHARED=off");
if profile == "debug" {
cmd.arg("-DCMAKE_BUILD_TYPE=Debug");
} else {
cmd.arg("-DCMAKE_BUILD_TYPE=Release");
}
cmd.output()
.expect("Fail to create build directory on *nix.");
Command::new("make")
.current_dir(&rust_build_path)
.arg("-j6")
.output()
.expect("Fail to build unicorn on *nix.");
println!(
"cargo:rustc-link-search={}",
rust_build_path.to_str().unwrap()
);
} }
cmd.output()
.expect("Fail to create build directory on *nix.");
Command::new("make")
.current_dir(format!("{}/rust_build", &unicorn_dir))
.arg("-j6")
.output()
.expect("Fail to build unicorn on *nix.");
println!("cargo:rustc-link-search={}/rust_build", unicorn_dir);
} }
// This is a workaround for Unicorn static link since libunicorn.a is also linked again lib*-softmmu.a. // This is a workaround for Unicorn static link since libunicorn.a is also linked again lib*-softmmu.a.
@ -130,7 +157,7 @@ fn main() {
// //
// Lazymio(@wtdcode): Why do I stick to static link? See: https://github.com/rust-lang/cargo/issues/5077 // Lazymio(@wtdcode): Why do I stick to static link? See: https://github.com/rust-lang/cargo/issues/5077
println!("cargo:rustc-link-lib=unicorn"); println!("cargo:rustc-link-lib=unicorn");
for arch in [ for arch in &[
"x86_64", "x86_64",
"arm", "arm",
"armeb", "armeb",
@ -147,10 +174,11 @@ fn main() {
"m68k", "m68k",
"ppc", "ppc",
"ppc64", "ppc64",
] ] {
.iter()
{
println!("cargo:rustc-link-lib={}-softmmu", arch); println!("cargo:rustc-link-lib={}-softmmu", arch);
} }
println!("cargo:rustc-link-lib=unicorn-common"); println!("cargo:rustc-link-lib=unicorn-common");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src");
} }

View File

@ -4,8 +4,8 @@
use crate::Unicorn; use crate::Unicorn;
use super::unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Query}; 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, marker::PhantomData};
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;
@ -76,30 +76,17 @@ extern "C" {
pub fn uc_context_alloc(engine: uc_handle, context: *mut uc_context) -> uc_error; 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_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_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 UcHook<'a, D: 'a, F: 'a> { pub struct UcHook<'a, D: 'a, F: 'a> {
pub callback: F, pub callback: F,
pub phantom: PhantomData<&'a D>, pub uc: Unicorn<'a, D>,
} }
pub trait IsUcHook<'a> {} pub trait IsUcHook<'a> {}
impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {} 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 extern "C" fn code_hook_proxy<D, F>( pub extern "C" fn code_hook_proxy<D, F>(
uc: uc_handle, uc: uc_handle,
address: u64, address: u64,
@ -108,10 +95,9 @@ pub extern "C" fn code_hook_proxy<D, F>(
) where ) where
F: FnMut(&mut crate::Unicorn<D>, u64, u32), F: FnMut(&mut crate::Unicorn<D>, u64, u32),
{ {
let unicorn = read_uc_from_uc_handle(uc); let user_data = unsafe { &mut *user_data };
let callback = unsafe { &mut (*user_data).callback }; debug_assert_eq!(uc, user_data.uc.inner().uc);
assert_eq!(uc, unicorn.uc); (user_data.callback)(&mut user_data.uc, address, size);
callback(unicorn, address, size);
} }
pub extern "C" fn block_hook_proxy<D, F>( pub extern "C" fn block_hook_proxy<D, F>(
@ -122,10 +108,9 @@ pub extern "C" fn block_hook_proxy<D, F>(
) where ) where
F: FnMut(&mut crate::Unicorn<D>, u64, u32), F: FnMut(&mut crate::Unicorn<D>, u64, u32),
{ {
let unicorn = read_uc_from_uc_handle(uc); let user_data = unsafe { &mut *user_data };
let callback = unsafe { &mut (*user_data).callback }; debug_assert_eq!(uc, user_data.uc.inner().uc);
assert_eq!(uc, unicorn.uc); (user_data.callback)(&mut user_data.uc, address, size);
callback(unicorn, address, size);
} }
pub extern "C" fn mem_hook_proxy<D, F>( pub extern "C" fn mem_hook_proxy<D, F>(
@ -139,20 +124,18 @@ pub extern "C" fn mem_hook_proxy<D, F>(
where where
F: FnMut(&mut crate::Unicorn<D>, MemType, u64, usize, i64) -> bool, F: FnMut(&mut crate::Unicorn<D>, MemType, u64, usize, i64) -> bool,
{ {
let unicorn = read_uc_from_uc_handle(uc); let user_data = unsafe { &mut *user_data };
let callback = unsafe { &mut (*user_data).callback }; debug_assert_eq!(uc, user_data.uc.inner().uc);
assert_eq!(uc, unicorn.uc); (user_data.callback)(&mut user_data.uc, mem_type, address, size as usize, value)
callback(unicorn, mem_type, address, size as usize, value)
} }
pub extern "C" fn intr_hook_proxy<D, F>(uc: uc_handle, value: u32, user_data: *mut UcHook<D, F>) pub extern "C" fn intr_hook_proxy<D, F>(uc: uc_handle, value: u32, user_data: *mut UcHook<D, F>)
where where
F: FnMut(&mut crate::Unicorn<D>, u32), F: FnMut(&mut crate::Unicorn<D>, u32),
{ {
let unicorn = read_uc_from_uc_handle(uc); let user_data = unsafe { &mut *user_data };
let callback = unsafe { &mut (*user_data).callback }; debug_assert_eq!(uc, user_data.uc.inner().uc);
assert_eq!(uc, unicorn.uc); (user_data.callback)(&mut user_data.uc, value);
callback(unicorn, value);
} }
pub extern "C" fn insn_in_hook_proxy<D, F>( pub extern "C" fn insn_in_hook_proxy<D, F>(
@ -163,10 +146,9 @@ pub extern "C" fn insn_in_hook_proxy<D, F>(
) where ) where
F: FnMut(&mut crate::Unicorn<D>, u32, usize), F: FnMut(&mut crate::Unicorn<D>, u32, usize),
{ {
let unicorn = read_uc_from_uc_handle(uc); let user_data = unsafe { &mut *user_data };
let callback = unsafe { &mut (*user_data).callback }; debug_assert_eq!(uc, user_data.uc.inner().uc);
assert_eq!(uc, unicorn.uc); (user_data.callback)(&mut user_data.uc, port, size);
callback(unicorn, port, size);
} }
pub extern "C" fn insn_out_hook_proxy<D, F>( pub extern "C" fn insn_out_hook_proxy<D, F>(
@ -178,18 +160,16 @@ pub extern "C" fn insn_out_hook_proxy<D, F>(
) where ) where
F: FnMut(&mut crate::Unicorn<D>, u32, usize, u32), F: FnMut(&mut crate::Unicorn<D>, u32, usize, u32),
{ {
let unicorn = read_uc_from_uc_handle(uc); let user_data = unsafe { &mut *user_data };
let callback = unsafe { &mut (*user_data).callback }; debug_assert_eq!(uc, user_data.uc.inner().uc);
assert_eq!(uc, unicorn.uc); (user_data.callback)(&mut user_data.uc, port, size, value);
callback(unicorn, port, size, value);
} }
pub extern "C" fn insn_sys_hook_proxy<D, F>(uc: uc_handle, user_data: *mut UcHook<D, F>) pub extern "C" fn insn_sys_hook_proxy<D, F>(uc: uc_handle, user_data: *mut UcHook<D, F>)
where where
F: FnMut(&mut crate::Unicorn<D>), F: FnMut(&mut crate::Unicorn<D>),
{ {
let unicorn = read_uc_from_uc_handle(uc); let user_data = unsafe { &mut *user_data };
assert_eq!(uc, unicorn.uc); debug_assert_eq!(uc, user_data.uc.inner().uc);
let callback = unsafe { &mut (*user_data).callback }; (user_data.callback)(&mut user_data.uc);
callback(unicorn);
} }

View File

@ -27,21 +27,29 @@
//! ``` //! ```
//! //!
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;
use std::{marker::PhantomData, ptr};
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 libc::c_void; use libc::c_void;
use unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Permission, Query}; use unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Permission, Query};
@ -69,8 +77,7 @@ impl Drop for Context {
} }
} }
/// A Unicorn emulator instance. pub struct UnicornInner<'a, D> {
pub struct Unicorn<'a, D: 'a> {
pub uc: uc_handle, pub uc: uc_handle,
pub arch: Arch, pub arch: Arch,
/// to keep ownership over the hook for this uc instance's lifetime /// to keep ownership over the hook for this uc instance's lifetime
@ -78,6 +85,21 @@ pub struct Unicorn<'a, D: 'a> {
pub data: D, pub data: D,
} }
/// 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, ()> { 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.
@ -93,14 +115,16 @@ where
/// 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_with_data(arch: Arch, mode: Mode, data: D) -> Result<Unicorn<'a, D>, uc_error> { 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 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 {
uc: handle, inner: Rc::new(UnsafeCell::from(UnicornInner {
arch, uc: handle,
data, arch,
hooks: vec![], data,
hooks: vec![],
})),
}) })
} else { } else {
Err(err) Err(err)
@ -108,50 +132,51 @@ where
} }
} }
/// Drop UC impl<'a, D> core::fmt::Debug for Unicorn<'a, D> {
impl<'a, D> Drop for Unicorn<'a, D> { fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
fn drop(&mut self) { write!(formatter, "Unicorn {{ uc: {:p} }}", self.inner().uc)
if !self.uc.is_null() {
unsafe { ffi::uc_close(self.uc) };
}
self.uc = ptr::null_mut();
}
}
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, D> Unicorn<'a, D> { impl<'a, D> Unicorn<'a, D> {
fn inner(&self) -> &UnicornInner<'a, D> {
unsafe { self.inner.get().as_ref().unwrap() }
}
fn inner_mut(&mut self) -> &mut UnicornInner<'a, D> {
unsafe { self.inner.get().as_mut().unwrap() }
}
/// Return whatever data was passed during initialization. /// Return whatever data was passed during initialization.
/// ///
/// For an example, have a look at `utils::init_emu_with_heap` where /// For an example, have a look at `utils::init_emu_with_heap` where
/// a struct is passed which is used for a custom allocator. /// a struct is passed which is used for a custom allocator.
#[must_use]
pub fn get_data(&self) -> &D { pub fn get_data(&self) -> &D {
&self.data &self.inner().data
} }
/// Return a mutable reference to whatever data was passed during initialization. /// Return a mutable reference to whatever data was passed during initialization.
#[must_use]
pub fn get_data_mut(&mut self) -> &mut D { pub fn get_data_mut(&mut self) -> &mut D {
&mut self.data &mut self.inner_mut().data
} }
/// 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.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 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.uc, &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.add(i as usize)) }); 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)
@ -162,7 +187,8 @@ impl<'a, D> Unicorn<'a, D> {
/// 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.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 {
@ -173,7 +199,7 @@ impl<'a, D> Unicorn<'a, D> {
/// 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.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 {
@ -182,7 +208,8 @@ impl<'a, D> Unicorn<'a, D> {
} }
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.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 {
@ -210,7 +237,7 @@ impl<'a, D> Unicorn<'a, D> {
perms: Permission, perms: Permission,
ptr: *mut c_void, ptr: *mut c_void,
) -> Result<(), uc_error> { ) -> Result<(), uc_error> {
let err = ffi::uc_mem_map_ptr(self.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 {
@ -228,7 +255,7 @@ impl<'a, D> Unicorn<'a, D> {
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.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 {
@ -241,7 +268,7 @@ impl<'a, D> Unicorn<'a, D> {
/// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `address` must be aligned to 4kb or this will return `Error::ARG`.
/// `size` must be a multiple of 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`.
pub fn mem_unmap(&mut self, 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.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 {
@ -259,7 +286,7 @@ impl<'a, D> Unicorn<'a, D> {
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.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,8 @@ impl<'a, D> Unicorn<'a, D> {
/// Write an unsigned value from a register. /// Write an unsigned value from a register.
pub fn reg_write<T: Into<i32>>(&mut self, regid: T, value: u64) -> Result<(), uc_error> { pub fn reg_write<T: Into<i32>>(&mut self, regid: T, value: u64) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_reg_write(self.uc, regid.into(), &value as *const _ as _) }; let err =
unsafe { ffi::uc_reg_write(self.inner().uc, regid.into(), &value as *const _ as _) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -282,7 +310,7 @@ impl<'a, D> Unicorn<'a, D> {
/// 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, ST (x86); Q, V (arm64)). /// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM, ST (x86); Q, V (arm64)).
pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: &[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.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 {
@ -295,7 +323,8 @@ impl<'a, D> Unicorn<'a, D> {
/// Not to be used with registers larger than 64 bit. /// Not to be used with registers larger than 64 bit.
pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> { pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> {
let mut value: u64 = 0; let mut value: u64 = 0;
let err = unsafe { ffi::uc_reg_read(self.uc, regid.into(), &mut value as *mut u64 as _) }; let err =
unsafe { ffi::uc_reg_read(self.inner().uc, regid.into(), &mut value as *mut u64 as _) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(value) Ok(value)
} else { } else {
@ -349,7 +378,7 @@ impl<'a, D> Unicorn<'a, D> {
return Err(uc_error::ARCH); return Err(uc_error::ARCH);
} }
err = unsafe { ffi::uc_reg_read(self.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();
@ -362,7 +391,8 @@ impl<'a, D> Unicorn<'a, D> {
/// Read a signed 32-bit value from a register. /// Read a signed 32-bit value from a register.
pub fn reg_read_i32<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> { pub fn reg_read_i32<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> {
let mut value: i32 = 0; let mut value: i32 = 0;
let err = unsafe { ffi::uc_reg_read(self.uc, regid.into(), &mut value as *mut i32 as _) }; let err =
unsafe { ffi::uc_reg_read(self.inner().uc, regid.into(), &mut value as *mut i32 as _) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(value) Ok(value)
} else { } else {
@ -380,15 +410,17 @@ impl<'a, D> Unicorn<'a, D> {
where where
F: FnMut(&mut crate::Unicorn<D>, u64, u32) + 'a, 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::UcHook { let mut user_data = Box::new(ffi::UcHook {
callback, callback,
phantom: PhantomData::<&D>, uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::CODE, HookType::CODE,
ffi::code_hook_proxy::<D, F> as _, ffi::code_hook_proxy::<D, F> as _,
@ -398,7 +430,7 @@ impl<'a, D> Unicorn<'a, D> {
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
self.hooks.push((hook_ptr, user_data)); self.inner_mut().hooks.push((hook_ptr, user_data));
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
Err(err) Err(err)
@ -410,15 +442,17 @@ impl<'a, D> Unicorn<'a, D> {
where where
F: FnMut(&mut Unicorn<D>, 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::UcHook { let mut user_data = Box::new(ffi::UcHook {
callback, callback,
phantom: PhantomData::<&D>, uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::BLOCK, HookType::BLOCK,
ffi::block_hook_proxy::<D, F> as _, ffi::block_hook_proxy::<D, F> as _,
@ -428,7 +462,7 @@ impl<'a, D> Unicorn<'a, D> {
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
self.hooks.push((hook_ptr, user_data)); self.inner_mut().hooks.push((hook_ptr, user_data));
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
@ -451,15 +485,17 @@ impl<'a, D> Unicorn<'a, D> {
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::UcHook { let mut user_data = Box::new(ffi::UcHook {
callback, callback,
phantom: PhantomData::<&D>, uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
hook_type, hook_type,
ffi::mem_hook_proxy::<D, F> as _, ffi::mem_hook_proxy::<D, F> as _,
@ -469,7 +505,7 @@ impl<'a, D> Unicorn<'a, D> {
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
self.hooks.push((hook_ptr, user_data)); self.inner_mut().hooks.push((hook_ptr, user_data));
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
@ -482,15 +518,17 @@ impl<'a, D> Unicorn<'a, D> {
where where
F: FnMut(&mut Unicorn<D>, 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::UcHook { let mut user_data = Box::new(ffi::UcHook {
callback, callback,
phantom: PhantomData::<&D>, uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::INTR, HookType::INTR,
ffi::intr_hook_proxy::<D, F> as _, ffi::intr_hook_proxy::<D, F> as _,
@ -500,7 +538,7 @@ impl<'a, D> Unicorn<'a, D> {
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
self.hooks.push((hook_ptr, user_data)); self.inner_mut().hooks.push((hook_ptr, user_data));
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
@ -511,17 +549,19 @@ impl<'a, D> Unicorn<'a, D> {
/// Add hook for x86 IN instruction. /// Add hook for x86 IN instruction.
pub fn add_insn_in_hook<F: 'a>(&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(&mut Unicorn<D>, u32, usize) + 'a, 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::UcHook { let mut user_data = Box::new(ffi::UcHook {
callback, callback,
phantom: PhantomData::<&D>, uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::INSN, HookType::INSN,
ffi::insn_in_hook_proxy::<D, F> as _, ffi::insn_in_hook_proxy::<D, F> as _,
@ -532,7 +572,7 @@ impl<'a, D> Unicorn<'a, D> {
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
self.hooks.push((hook_ptr, user_data)); self.inner_mut().hooks.push((hook_ptr, user_data));
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
@ -543,17 +583,19 @@ impl<'a, D> Unicorn<'a, D> {
/// Add hook for x86 OUT instruction. /// Add hook for x86 OUT instruction.
pub fn add_insn_out_hook<F: 'a>(&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(&mut Unicorn<D>, u32, usize, u32) + 'a, 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::UcHook { let mut user_data = Box::new(ffi::UcHook {
callback, callback,
phantom: PhantomData::<&D>, uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::INSN, HookType::INSN,
ffi::insn_out_hook_proxy::<D, F> as _, ffi::insn_out_hook_proxy::<D, F> as _,
@ -564,7 +606,7 @@ impl<'a, D> Unicorn<'a, D> {
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
self.hooks.push((hook_ptr, user_data)); self.inner_mut().hooks.push((hook_ptr, user_data));
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
@ -583,15 +625,17 @@ impl<'a, D> Unicorn<'a, D> {
where where
F: FnMut(&mut Unicorn<D>) + 'a, 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::UcHook { let mut user_data = Box::new(ffi::UcHook {
callback, callback,
phantom: PhantomData::<&D>, uc: Unicorn {
inner: self.inner.clone(),
},
}); });
let err = unsafe { let err = unsafe {
ffi::uc_hook_add( ffi::uc_hook_add(
self.uc, self.inner().uc,
&mut hook_ptr, &mut hook_ptr,
HookType::INSN, HookType::INSN,
ffi::insn_sys_hook_proxy::<D, F> as _, ffi::insn_sys_hook_proxy::<D, F> as _,
@ -602,7 +646,7 @@ impl<'a, D> Unicorn<'a, D> {
) )
}; };
if err == uc_error::OK { if err == uc_error::OK {
self.hooks.push((hook_ptr, user_data)); self.inner_mut().hooks.push((hook_ptr, user_data));
Ok(hook_ptr) Ok(hook_ptr)
} else { } else {
@ -617,10 +661,11 @@ impl<'a, D> Unicorn<'a, D> {
let err: uc_error; let err: uc_error;
// drop the hook // drop the hook
self.hooks self.inner_mut()
.hooks
.retain(|(hook_ptr, _hook_impl)| hook_ptr != &hook); .retain(|(hook_ptr, _hook_impl)| hook_ptr != &hook);
err = unsafe { ffi::uc_hook_del(self.uc, hook) }; err = unsafe { ffi::uc_hook_del(self.inner().uc, hook) };
if err == uc_error::OK { if err == uc_error::OK {
Ok(()) Ok(())
@ -634,7 +679,7 @@ impl<'a, D> Unicorn<'a, D> {
/// 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 = ptr::null_mut(); let mut empty_context: ffi::uc_context = ptr::null_mut();
let err = unsafe { ffi::uc_context_alloc(self.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,
@ -646,7 +691,7 @@ impl<'a, D> Unicorn<'a, D> {
/// 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.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 {
@ -661,11 +706,11 @@ impl<'a, D> Unicorn<'a, D> {
/// 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 = ptr::null_mut(); let mut new_context: ffi::uc_context = ptr::null_mut();
let err = unsafe { ffi::uc_context_alloc(self.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.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,
@ -682,7 +727,7 @@ impl<'a, D> Unicorn<'a, D> {
/// 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.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 {
@ -704,9 +749,7 @@ impl<'a, D> Unicorn<'a, D> {
count: usize, count: usize,
) -> Result<(), uc_error> { ) -> Result<(), uc_error> {
unsafe { unsafe {
ffi::uc_set_data_ptr(self.uc, self as *mut _ as *mut _); let err = ffi::uc_emu_start(self.inner().uc, begin, until, timeout, count as _);
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 { if err == uc_error::OK {
Ok(()) Ok(())
} else { } else {
@ -720,7 +763,7 @@ impl<'a, D> Unicorn<'a, D> {
/// 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.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 {
@ -733,7 +776,7 @@ impl<'a, D> Unicorn<'a, D> {
/// 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.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 {
@ -754,7 +797,7 @@ impl<'a, D> Unicorn<'a, D> {
Arch::M68K => RegisterM68K::PC as i32, Arch::M68K => RegisterM68K::PC as i32,
Arch::PPC => RegisterPPC::PC as i32, Arch::PPC => RegisterPPC::PC as i32,
Arch::RISCV => RegisterRISCV::PC as i32, Arch::RISCV => RegisterRISCV::PC as i32,
_ => panic!("Arch pc not yet know to the unicorn rust bindings"), Arch::MAX => panic!("Illegal Arch specified"),
}; };
self.reg_read(reg) self.reg_read(reg)
} }
@ -772,7 +815,7 @@ impl<'a, D> Unicorn<'a, D> {
Arch::M68K => RegisterM68K::PC as i32, Arch::M68K => RegisterM68K::PC as i32,
Arch::PPC => RegisterPPC::PC as i32, Arch::PPC => RegisterPPC::PC as i32,
Arch::RISCV => RegisterRISCV::PC as i32, Arch::RISCV => RegisterRISCV::PC as i32,
_ => panic!("Arch not yet known to the unicorn rust bindings"), Arch::MAX => panic!("Illegal Arch specified"),
}; };
self.reg_write(reg, value) self.reg_write(reg, value)
} }

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,
@ -32,7 +34,6 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [
RegisterX86::EDX, RegisterX86::EDX,
RegisterX86::EFLAGS, RegisterX86::EFLAGS,
RegisterX86::EIP, RegisterX86::EIP,
RegisterX86::EIZ,
RegisterX86::ES, RegisterX86::ES,
RegisterX86::ESI, RegisterX86::ESI,
RegisterX86::ESP, RegisterX86::ESP,
@ -47,7 +48,6 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [
RegisterX86::RDI, RegisterX86::RDI,
RegisterX86::RDX, RegisterX86::RDX,
RegisterX86::RIP, RegisterX86::RIP,
RegisterX86::RIZ,
RegisterX86::RSI, RegisterX86::RSI,
RegisterX86::RSP, RegisterX86::RSP,
RegisterX86::SI, RegisterX86::SI,
@ -60,17 +60,7 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [
RegisterX86::CR2, RegisterX86::CR2,
RegisterX86::CR3, RegisterX86::CR3,
RegisterX86::CR4, RegisterX86::CR4,
RegisterX86::CR5,
RegisterX86::CR6,
RegisterX86::CR7,
RegisterX86::CR8, RegisterX86::CR8,
RegisterX86::CR9,
RegisterX86::CR10,
RegisterX86::CR11,
RegisterX86::CR12,
RegisterX86::CR13,
RegisterX86::CR14,
RegisterX86::CR15,
RegisterX86::DR0, RegisterX86::DR0,
RegisterX86::DR1, RegisterX86::DR1,
RegisterX86::DR2, RegisterX86::DR2,
@ -79,14 +69,6 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [
RegisterX86::DR5, RegisterX86::DR5,
RegisterX86::DR6, RegisterX86::DR6,
RegisterX86::DR7, RegisterX86::DR7,
RegisterX86::DR8,
RegisterX86::DR9,
RegisterX86::DR10,
RegisterX86::DR11,
RegisterX86::DR12,
RegisterX86::DR13,
RegisterX86::DR14,
RegisterX86::DR15,
RegisterX86::FP0, RegisterX86::FP0,
RegisterX86::FP1, RegisterX86::FP1,
RegisterX86::FP2, RegisterX86::FP2,
@ -159,8 +141,8 @@ fn emulate_x86() {
let mut emu = 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");
assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 123), Ok(())); assert_eq!(emu.reg_write(RegisterX86::EAX, 123), Ok(()));
assert_eq!(emu.reg_read(RegisterX86::EAX as i32), Ok(123)); assert_eq!(emu.reg_read(RegisterX86::EAX), Ok(123));
// Attempt to write to memory before mapping it. // Attempt to write to memory before mapping it.
assert_eq!( assert_eq!(
@ -175,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(
@ -187,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]
@ -266,17 +248,26 @@ fn x86_mem_callback() {
let expects = vec![ let expects = vec![
MemExpectation(MemType::WRITE, 0x2000, 4, 0xdeadbeef), MemExpectation(MemType::WRITE, 0x2000, 4, 0xdeadbeef),
MemExpectation(MemType::READ_UNMAPPED, 0x10000, 4, 0), MemExpectation(MemType::READ_UNMAPPED, 0x10000, 4, 0),
MemExpectation(MemType::READ, 0x10000, 4, 0),
]; ];
let mems: Vec<MemExpectation> = Vec::new(); let mems: Vec<MemExpectation> = Vec::new();
let mems_cell = Rc::new(RefCell::new(mems)); let mems_cell = Rc::new(RefCell::new(mems));
let callback_mems = mems_cell.clone(); let callback_mems = mems_cell.clone();
let callback = let callback = move |uc: &mut Unicorn<'_, ()>,
move |_: &mut Unicorn<'_, ()>, mem_type: MemType, address: u64, size: usize, value: i64| { mem_type: MemType,
let mut mems = callback_mems.borrow_mut(); address: u64,
mems.push(MemExpectation(mem_type, address, size, value)); size: usize,
true value: i64| {
}; let mut mems = callback_mems.borrow_mut();
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; // mov eax, 0xdeadbeef;
// mov [0x2000], eax; // mov [0x2000], eax;
@ -291,9 +282,9 @@ fn x86_mem_callback() {
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,
@ -301,7 +292,7 @@ fn x86_mem_callback() {
10 * SECOND_SCALE, 10 * SECOND_SCALE,
0x1000 0x1000
), ),
Err(uc_error::READ_UNMAPPED) Ok(())
); );
assert_eq!(expects, *mems_cell.borrow()); assert_eq!(expects, *mems_cell.borrow());
@ -316,7 +307,7 @@ 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 |_: &mut 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);
}; };
@ -390,7 +381,7 @@ fn x86_insn_sys_callback() {
let callback_insn = insn_cell.clone(); let callback_insn = insn_cell.clone();
let callback = move |uc: &mut 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);
}; };
@ -427,8 +418,8 @@ fn emulate_arm() {
let mut emu = 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");
assert_eq!(emu.reg_write(RegisterARM::R1 as i32, 123), Ok(())); assert_eq!(emu.reg_write(RegisterARM::R1, 123), Ok(()));
assert_eq!(emu.reg_read(RegisterARM::R1 as i32), Ok(123)); assert_eq!(emu.reg_read(RegisterARM::R1), Ok(123));
// Attempt to write to memory before mapping it. // Attempt to write to memory before mapping it.
assert_eq!( assert_eq!(
@ -443,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.
@ -457,8 +448,8 @@ 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]
@ -473,7 +464,7 @@ fn emulate_mips() {
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,
@ -483,14 +474,14 @@ 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 emu = unicorn_engine::Unicorn::new(Arch::PPC, Mode::PPC32) 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");
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(()));
@ -498,8 +489,8 @@ fn emulate_ppc() {
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,
@ -509,7 +500,7 @@ 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]
@ -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.
@ -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,18 +584,18 @@ 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 &[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 emu = 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");
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(()));
@ -620,15 +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 emu2 = 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");
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)
);
} }
} }
} }

21
uc.c
View File

@ -686,27 +686,6 @@ static void clear_deleted_hooks(uc_engine *uc)
list_clear(&uc->hooks_to_del); 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 UNICORN_EXPORT
uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until,
uint64_t timeout, size_t count) uint64_t timeout, size_t count)