Merge pull request #1932 from lockbox/rust-conditional-compilation
Rust conditional compilation
This commit is contained in:
@@ -15,30 +15,15 @@ pub type uc_hook = *mut c_void;
|
|||||||
pub type uc_context = *mut c_void;
|
pub type uc_context = *mut c_void;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn uc_version(
|
pub fn uc_version(major: *mut u32, minor: *mut u32) -> u32;
|
||||||
major: *mut u32,
|
|
||||||
minor: *mut u32,
|
|
||||||
) -> u32;
|
|
||||||
pub fn uc_arch_supported(arch: Arch) -> bool;
|
pub fn uc_arch_supported(arch: Arch) -> bool;
|
||||||
pub fn uc_open(
|
pub fn uc_open(arch: Arch, mode: Mode, engine: *mut uc_handle) -> uc_error;
|
||||||
arch: Arch,
|
|
||||||
mode: Mode,
|
|
||||||
engine: *mut uc_handle,
|
|
||||||
) -> uc_error;
|
|
||||||
pub fn uc_close(engine: uc_handle) -> uc_error;
|
pub fn uc_close(engine: uc_handle) -> uc_error;
|
||||||
pub fn uc_context_free(mem: uc_context) -> uc_error;
|
pub fn uc_context_free(mem: uc_context) -> uc_error;
|
||||||
pub fn uc_errno(engine: uc_handle) -> uc_error;
|
pub fn uc_errno(engine: uc_handle) -> uc_error;
|
||||||
pub fn uc_strerror(error_code: uc_error) -> *const c_char;
|
pub fn uc_strerror(error_code: uc_error) -> *const c_char;
|
||||||
pub fn uc_reg_write(
|
pub fn uc_reg_write(engine: uc_handle, regid: c_int, value: *const c_void) -> uc_error;
|
||||||
engine: uc_handle,
|
pub fn uc_reg_read(engine: uc_handle, regid: c_int, value: *mut c_void) -> uc_error;
|
||||||
regid: c_int,
|
|
||||||
value: *const c_void,
|
|
||||||
) -> uc_error;
|
|
||||||
pub fn uc_reg_read(
|
|
||||||
engine: uc_handle,
|
|
||||||
regid: c_int,
|
|
||||||
value: *mut c_void,
|
|
||||||
) -> uc_error;
|
|
||||||
pub fn uc_mem_write(
|
pub fn uc_mem_write(
|
||||||
engine: uc_handle,
|
engine: uc_handle,
|
||||||
address: u64,
|
address: u64,
|
||||||
@@ -51,12 +36,7 @@ extern "C" {
|
|||||||
bytes: *mut u8,
|
bytes: *mut u8,
|
||||||
size: libc::size_t,
|
size: libc::size_t,
|
||||||
) -> uc_error;
|
) -> uc_error;
|
||||||
pub fn uc_mem_map(
|
pub fn uc_mem_map(engine: uc_handle, address: u64, size: libc::size_t, perms: u32) -> uc_error;
|
||||||
engine: uc_handle,
|
|
||||||
address: u64,
|
|
||||||
size: libc::size_t,
|
|
||||||
perms: u32,
|
|
||||||
) -> uc_error;
|
|
||||||
pub fn uc_mem_map_ptr(
|
pub fn uc_mem_map_ptr(
|
||||||
engine: uc_handle,
|
engine: uc_handle,
|
||||||
address: u64,
|
address: u64,
|
||||||
@@ -73,11 +53,7 @@ extern "C" {
|
|||||||
write_cb: *mut c_void,
|
write_cb: *mut c_void,
|
||||||
user_data_write: *mut c_void,
|
user_data_write: *mut c_void,
|
||||||
) -> uc_error;
|
) -> uc_error;
|
||||||
pub fn uc_mem_unmap(
|
pub fn uc_mem_unmap(engine: uc_handle, address: u64, size: libc::size_t) -> uc_error;
|
||||||
engine: uc_handle,
|
|
||||||
address: u64,
|
|
||||||
size: libc::size_t,
|
|
||||||
) -> uc_error;
|
|
||||||
pub fn uc_mem_protect(
|
pub fn uc_mem_protect(
|
||||||
engine: uc_handle,
|
engine: uc_handle,
|
||||||
address: u64,
|
address: u64,
|
||||||
@@ -107,32 +83,12 @@ extern "C" {
|
|||||||
end: u64,
|
end: u64,
|
||||||
...
|
...
|
||||||
) -> uc_error;
|
) -> uc_error;
|
||||||
pub fn uc_hook_del(
|
pub fn uc_hook_del(engine: uc_handle, hook: uc_hook) -> uc_error;
|
||||||
engine: uc_handle,
|
pub fn uc_query(engine: uc_handle, query_type: Query, result: *mut libc::size_t) -> uc_error;
|
||||||
hook: uc_hook,
|
pub fn uc_context_alloc(engine: uc_handle, context: *mut uc_context) -> uc_error;
|
||||||
) -> uc_error;
|
pub fn uc_context_save(engine: uc_handle, context: uc_context) -> uc_error;
|
||||||
pub fn uc_query(
|
pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error;
|
||||||
engine: uc_handle,
|
pub fn uc_ctl(engine: uc_handle, control: u32, ...) -> uc_error;
|
||||||
query_type: Query,
|
|
||||||
result: *mut libc::size_t,
|
|
||||||
) -> 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_restore(
|
|
||||||
engine: uc_handle,
|
|
||||||
context: uc_context,
|
|
||||||
) -> uc_error;
|
|
||||||
pub fn uc_ctl(
|
|
||||||
engine: uc_handle,
|
|
||||||
control: u32,
|
|
||||||
...
|
|
||||||
) -> uc_error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UcHook<'a, D: 'a, F: 'a> {
|
pub struct UcHook<'a, D: 'a, F: 'a> {
|
||||||
@@ -293,10 +249,8 @@ pub unsafe extern "C" fn insn_out_hook_proxy<D, F>(
|
|||||||
(user_data.callback)(&mut user_data_uc, port, size, value);
|
(user_data.callback)(&mut user_data_uc, port, size, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn insn_sys_hook_proxy<D, F>(
|
pub unsafe extern "C" fn insn_sys_hook_proxy<D, F>(uc: uc_handle, user_data: *mut UcHook<D, F>)
|
||||||
uc: uc_handle,
|
where
|
||||||
user_data: *mut UcHook<D, F>,
|
|
||||||
) where
|
|
||||||
F: FnMut(&mut crate::Unicorn<D>),
|
F: FnMut(&mut crate::Unicorn<D>),
|
||||||
{
|
{
|
||||||
let user_data = &mut *user_data;
|
let user_data = &mut *user_data;
|
||||||
|
|||||||
@@ -42,32 +42,72 @@ use libc::c_void;
|
|||||||
|
|
||||||
use ffi::uc_handle;
|
use ffi::uc_handle;
|
||||||
|
|
||||||
pub use crate::arm::*;
|
|
||||||
pub use crate::arm64::*;
|
|
||||||
pub use crate::m68k::*;
|
|
||||||
pub use crate::mips::*;
|
|
||||||
pub use crate::ppc::*;
|
|
||||||
pub use crate::riscv::*;
|
|
||||||
pub use crate::s390x::*;
|
|
||||||
pub use crate::sparc::*;
|
|
||||||
pub use crate::tricore::*;
|
|
||||||
pub use crate::unicorn_const::*;
|
|
||||||
pub use crate::x86::*;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod unicorn_const;
|
pub mod unicorn_const;
|
||||||
|
pub use unicorn_const::*;
|
||||||
pub mod ffi; // lets consumers call ffi if desired
|
pub mod ffi; // lets consumers call ffi if desired
|
||||||
|
|
||||||
|
// include arm support if conditionally compiled in
|
||||||
|
#[cfg(feature = "arch_arm")]
|
||||||
mod arm;
|
mod arm;
|
||||||
|
#[cfg(feature = "arch_arm")]
|
||||||
|
pub use crate::arm::*;
|
||||||
|
|
||||||
|
// include arm64 support if conditionally compiled in
|
||||||
|
// NOTE: unicorn-c only separates on top-level arch name,
|
||||||
|
// not on the bit-length, so we include both
|
||||||
|
#[cfg(feature = "arch_arm")]
|
||||||
mod arm64;
|
mod arm64;
|
||||||
|
#[cfg(feature = "arch_arm")]
|
||||||
|
pub use crate::arm64::*;
|
||||||
|
|
||||||
|
// include m68k support if conditionally compiled in
|
||||||
|
#[cfg(feature = "arch_m68k")]
|
||||||
mod m68k;
|
mod m68k;
|
||||||
|
#[cfg(feature = "arch_m68k")]
|
||||||
|
pub use crate::m68k::*;
|
||||||
|
|
||||||
|
// include mips support if conditionally compiled in
|
||||||
|
#[cfg(feature = "arch_mips")]
|
||||||
mod mips;
|
mod mips;
|
||||||
|
#[cfg(feature = "arch_mips")]
|
||||||
|
pub use crate::mips::*;
|
||||||
|
|
||||||
|
// include ppc support if conditionally compiled in
|
||||||
|
#[cfg(feature = "arch_ppc")]
|
||||||
mod ppc;
|
mod ppc;
|
||||||
|
#[cfg(feature = "arch_ppc")]
|
||||||
|
pub use crate::ppc::*;
|
||||||
|
|
||||||
|
// include riscv support if conditionally compiled in
|
||||||
|
#[cfg(feature = "arch_riscv")]
|
||||||
mod riscv;
|
mod riscv;
|
||||||
|
#[cfg(feature = "arch_riscv")]
|
||||||
|
pub use crate::riscv::*;
|
||||||
|
|
||||||
|
// include s390x support if conditionally compiled in
|
||||||
|
#[cfg(feature = "arch_s390x")]
|
||||||
mod s390x;
|
mod s390x;
|
||||||
|
#[cfg(feature = "arch_s390x")]
|
||||||
|
pub use crate::s390x::*;
|
||||||
|
|
||||||
|
// include sparc support if conditionally compiled in
|
||||||
|
#[cfg(feature = "arch_sparc")]
|
||||||
mod sparc;
|
mod sparc;
|
||||||
|
#[cfg(feature = "arch_sparc")]
|
||||||
|
pub use crate::sparc::*;
|
||||||
|
|
||||||
|
// include tricore support if conditionally compiled in
|
||||||
|
#[cfg(feature = "arch_tricore")]
|
||||||
mod tricore;
|
mod tricore;
|
||||||
|
#[cfg(feature = "arch_tricore")]
|
||||||
|
pub use crate::tricore::*;
|
||||||
|
|
||||||
|
// include x86 support if conditionally compiled in
|
||||||
|
#[cfg(feature = "arch_x86")]
|
||||||
mod x86;
|
mod x86;
|
||||||
|
#[cfg(feature = "arch_x86")]
|
||||||
|
pub use crate::x86::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
@@ -103,11 +143,7 @@ impl<'a> MmioCallbackScope<'a> {
|
|||||||
!self.regions.is_empty()
|
!self.regions.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unmap(
|
fn unmap(&mut self, begin: u64, size: usize) {
|
||||||
&mut self,
|
|
||||||
begin: u64,
|
|
||||||
size: usize,
|
|
||||||
) {
|
|
||||||
let end: u64 = begin + size as u64;
|
let end: u64 = begin + size as u64;
|
||||||
self.regions = self
|
self.regions = self
|
||||||
.regions
|
.regions
|
||||||
@@ -124,7 +160,10 @@ impl<'a> MmioCallbackScope<'a> {
|
|||||||
} else {
|
} else {
|
||||||
// The unmapped region is in the middle of this region
|
// The unmapped region is in the middle of this region
|
||||||
let second_b = end + 1;
|
let second_b = end + 1;
|
||||||
vec![(*b, (begin - *b) as usize), (second_b, (e - second_b) as usize)]
|
vec![
|
||||||
|
(*b, (begin - *b) as usize),
|
||||||
|
(second_b, (e - second_b) as usize),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
} else if end > *b {
|
} else if end > *b {
|
||||||
if end >= e {
|
if end >= e {
|
||||||
@@ -175,10 +214,7 @@ pub struct Unicorn<'a, D: 'a> {
|
|||||||
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.
|
||||||
pub fn new(
|
pub fn new(arch: Arch, mode: Mode) -> Result<Unicorn<'a, ()>, uc_error> {
|
||||||
arch: Arch,
|
|
||||||
mode: Mode,
|
|
||||||
) -> Result<Unicorn<'a, ()>, uc_error> {
|
|
||||||
Self::new_with_data(arch, mode, ())
|
Self::new_with_data(arch, mode, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,11 +252,7 @@ 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(
|
pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result<Unicorn<'a, D>, uc_error> {
|
||||||
arch: Arch,
|
|
||||||
mode: Mode,
|
|
||||||
data: D,
|
|
||||||
) -> Result<Unicorn<'a, D>, uc_error> {
|
|
||||||
let mut handle = ptr::null_mut();
|
let mut handle = ptr::null_mut();
|
||||||
unsafe { ffi::uc_open(arch, mode, &mut handle) }.and_then(|| {
|
unsafe { ffi::uc_open(arch, mode, &mut handle) }.and_then(|| {
|
||||||
Ok(Unicorn {
|
Ok(Unicorn {
|
||||||
@@ -238,10 +270,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, D> core::fmt::Debug for Unicorn<'a, D> {
|
impl<'a, D> core::fmt::Debug for Unicorn<'a, D> {
|
||||||
fn fmt(
|
fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
&self,
|
|
||||||
formatter: &mut core::fmt::Formatter,
|
|
||||||
) -> core::fmt::Result {
|
|
||||||
write!(formatter, "Unicorn {{ uc: {:p} }}", self.get_handle())
|
write!(formatter, "Unicorn {{ uc: {:p} }}", self.get_handle())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -286,41 +315,31 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
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 = ptr::null_mut();
|
let p_regions: *const MemRegion = ptr::null_mut();
|
||||||
unsafe { ffi::uc_mem_regions(self.get_handle(), &p_regions, &mut nb_regions) }.and_then(|| {
|
unsafe { ffi::uc_mem_regions(self.get_handle(), &p_regions, &mut nb_regions) }.and_then(
|
||||||
let mut regions = Vec::new();
|
|| {
|
||||||
for i in 0..nb_regions {
|
let mut regions = Vec::new();
|
||||||
regions.push(unsafe { core::mem::transmute_copy(&*p_regions.add(i as usize)) });
|
for i in 0..nb_regions {
|
||||||
}
|
regions.push(unsafe { core::mem::transmute_copy(&*p_regions.add(i as usize)) });
|
||||||
unsafe { libc::free(p_regions as _) };
|
}
|
||||||
Ok(regions)
|
unsafe { libc::free(p_regions as _) };
|
||||||
})
|
Ok(regions)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a range of bytes from memory at the specified emulated physical address.
|
/// Read a range of bytes from memory at the specified emulated physical address.
|
||||||
pub fn mem_read(
|
pub fn mem_read(&self, address: u64, buf: &mut [u8]) -> Result<(), uc_error> {
|
||||||
&self,
|
|
||||||
address: u64,
|
|
||||||
buf: &mut [u8],
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
unsafe { ffi::uc_mem_read(self.get_handle(), address, buf.as_mut_ptr(), buf.len()) }.into()
|
unsafe { ffi::uc_mem_read(self.get_handle(), address, buf.as_mut_ptr(), buf.len()) }.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a range of bytes from memory at the specified emulated physical address as vector.
|
/// Return a range of bytes from memory at the specified emulated physical address as vector.
|
||||||
pub fn mem_read_as_vec(
|
pub fn mem_read_as_vec(&self, address: u64, size: usize) -> Result<Vec<u8>, uc_error> {
|
||||||
&self,
|
|
||||||
address: u64,
|
|
||||||
size: usize,
|
|
||||||
) -> Result<Vec<u8>, uc_error> {
|
|
||||||
let mut buf = vec![0; size];
|
let mut buf = vec![0; size];
|
||||||
unsafe { ffi::uc_mem_read(self.get_handle(), address, buf.as_mut_ptr(), size) }.and(Ok(buf))
|
unsafe { ffi::uc_mem_read(self.get_handle(), address, buf.as_mut_ptr(), size) }.and(Ok(buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the data in `bytes` to the emulated physical address `address`
|
/// Write the data in `bytes` to the emulated physical address `address`
|
||||||
pub fn mem_write(
|
pub fn mem_write(&mut self, address: u64, bytes: &[u8]) -> Result<(), uc_error> {
|
||||||
&mut self,
|
|
||||||
address: u64,
|
|
||||||
bytes: &[u8],
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
unsafe { ffi::uc_mem_write(self.get_handle(), address, bytes.as_ptr(), bytes.len()) }.into()
|
unsafe { ffi::uc_mem_write(self.get_handle(), address, bytes.as_ptr(), bytes.len()) }.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +456,12 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
where
|
where
|
||||||
F: FnMut(&mut Unicorn<D>, u64, usize) -> u64,
|
F: FnMut(&mut Unicorn<D>, u64, usize) -> u64,
|
||||||
{
|
{
|
||||||
self.mmio_map(address, size, Some(callback), None::<fn(&mut Unicorn<D>, u64, usize, u64)>)
|
self.mmio_map(
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
Some(callback),
|
||||||
|
None::<fn(&mut Unicorn<D>, u64, usize, u64)>,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map in a write-only MMIO region backed by a callback.
|
/// Map in a write-only MMIO region backed by a callback.
|
||||||
@@ -453,28 +477,25 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
where
|
where
|
||||||
F: FnMut(&mut Unicorn<D>, u64, usize, u64),
|
F: FnMut(&mut Unicorn<D>, u64, usize, u64),
|
||||||
{
|
{
|
||||||
self.mmio_map(address, size, None::<fn(&mut Unicorn<D>, u64, usize) -> u64>, Some(callback))
|
self.mmio_map(
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
None::<fn(&mut Unicorn<D>, u64, usize) -> u64>,
|
||||||
|
Some(callback),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unmap a memory region.
|
/// Unmap a memory region.
|
||||||
///
|
///
|
||||||
/// `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(
|
pub fn mem_unmap(&mut self, address: u64, size: libc::size_t) -> Result<(), uc_error> {
|
||||||
&mut self,
|
|
||||||
address: u64,
|
|
||||||
size: libc::size_t,
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
let err = unsafe { ffi::uc_mem_unmap(self.get_handle(), address, size) };
|
let err = unsafe { ffi::uc_mem_unmap(self.get_handle(), address, size) };
|
||||||
self.mmio_unmap(address, size);
|
self.mmio_unmap(address, size);
|
||||||
err.into()
|
err.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mmio_unmap(
|
fn mmio_unmap(&mut self, address: u64, size: libc::size_t) {
|
||||||
&mut self,
|
|
||||||
address: u64,
|
|
||||||
size: libc::size_t,
|
|
||||||
) {
|
|
||||||
for scope in self.inner_mut().mmio_callbacks.iter_mut() {
|
for scope in self.inner_mut().mmio_callbacks.iter_mut() {
|
||||||
scope.unmap(address, size);
|
scope.unmap(address, size);
|
||||||
}
|
}
|
||||||
@@ -497,49 +518,39 @@ 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>>(
|
pub fn reg_write<T: Into<i32>>(&mut self, regid: T, value: u64) -> Result<(), uc_error> {
|
||||||
&mut self,
|
unsafe { ffi::uc_reg_write(self.get_handle(), regid.into(), &value as *const _ as _) }
|
||||||
regid: T,
|
.into()
|
||||||
value: u64,
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
unsafe { ffi::uc_reg_write(self.get_handle(), regid.into(), &value as *const _ as _) }.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write variable sized values into registers.
|
/// Write variable sized values into registers.
|
||||||
///
|
///
|
||||||
/// The user has to make sure that the buffer length matches the register size.
|
/// The user has to make sure that the buffer length matches the register size.
|
||||||
/// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
|
/// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
|
||||||
pub fn reg_write_long<T: Into<i32>>(
|
pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: &[u8]) -> Result<(), uc_error> {
|
||||||
&self,
|
|
||||||
regid: T,
|
|
||||||
value: &[u8],
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
unsafe { ffi::uc_reg_write(self.get_handle(), regid.into(), value.as_ptr() as _) }.into()
|
unsafe { ffi::uc_reg_write(self.get_handle(), regid.into(), value.as_ptr() as _) }.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read an unsigned value from a register.
|
/// Read an unsigned value from a register.
|
||||||
///
|
///
|
||||||
/// 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>>(
|
pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> {
|
||||||
&self,
|
|
||||||
regid: T,
|
|
||||||
) -> Result<u64, uc_error> {
|
|
||||||
let mut value: u64 = 0;
|
let mut value: u64 = 0;
|
||||||
unsafe { ffi::uc_reg_read(self.get_handle(), regid.into(), &mut value as *mut u64 as _) }.and(Ok(value))
|
unsafe { ffi::uc_reg_read(self.get_handle(), regid.into(), &mut value as *mut u64 as _) }
|
||||||
|
.and(Ok(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read 128, 256 or 512 bit register value into heap allocated byte array.
|
/// 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, ST (x86); Q, V (arm64)).
|
||||||
pub fn reg_read_long<T: Into<i32>>(
|
pub fn reg_read_long<T: Into<i32>>(&self, regid: T) -> Result<Box<[u8]>, uc_error> {
|
||||||
&self,
|
|
||||||
regid: T,
|
|
||||||
) -> Result<Box<[u8]>, uc_error> {
|
|
||||||
let curr_reg_id = regid.into();
|
let curr_reg_id = regid.into();
|
||||||
let curr_arch = self.get_arch();
|
let curr_arch = self.get_arch();
|
||||||
|
|
||||||
let value_size = match curr_arch {
|
let value_size = match curr_arch {
|
||||||
|
#[cfg(feature = "arch_x86")]
|
||||||
Arch::X86 => Self::value_size_x86(curr_reg_id)?,
|
Arch::X86 => Self::value_size_x86(curr_reg_id)?,
|
||||||
|
#[cfg(feature = "arch_arm")]
|
||||||
Arch::ARM64 => Self::value_size_arm64(curr_reg_id)?,
|
Arch::ARM64 => Self::value_size_arm64(curr_reg_id)?,
|
||||||
_ => Err(uc_error::ARCH)?,
|
_ => Err(uc_error::ARCH)?,
|
||||||
};
|
};
|
||||||
@@ -548,6 +559,7 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
.and_then(|| Ok(value.into_boxed_slice()))
|
.and_then(|| Ok(value.into_boxed_slice()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "arch_arm")]
|
||||||
fn value_size_arm64(curr_reg_id: i32) -> Result<usize, uc_error> {
|
fn value_size_arm64(curr_reg_id: i32) -> Result<usize, uc_error> {
|
||||||
match curr_reg_id {
|
match curr_reg_id {
|
||||||
r if (RegisterARM64::Q0 as i32..=RegisterARM64::Q31 as i32).contains(&r)
|
r if (RegisterARM64::Q0 as i32..=RegisterARM64::Q31 as i32).contains(&r)
|
||||||
@@ -559,6 +571,7 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "arch_x86")]
|
||||||
fn value_size_x86(curr_reg_id: i32) -> Result<usize, uc_error> {
|
fn value_size_x86(curr_reg_id: i32) -> Result<usize, uc_error> {
|
||||||
match curr_reg_id {
|
match curr_reg_id {
|
||||||
r if (RegisterX86::XMM0 as i32..=RegisterX86::XMM31 as i32).contains(&r) => Ok(16),
|
r if (RegisterX86::XMM0 as i32..=RegisterX86::XMM31 as i32).contains(&r) => Ok(16),
|
||||||
@@ -575,12 +588,10 @@ 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>>(
|
pub fn reg_read_i32<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> {
|
||||||
&self,
|
|
||||||
regid: T,
|
|
||||||
) -> Result<i32, uc_error> {
|
|
||||||
let mut value: i32 = 0;
|
let mut value: i32 = 0;
|
||||||
unsafe { ffi::uc_reg_read(self.get_handle(), regid.into(), &mut value as *mut i32 as _) }.and(Ok(value))
|
unsafe { ffi::uc_reg_read(self.get_handle(), regid.into(), &mut value as *mut i32 as _) }
|
||||||
|
.and(Ok(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a code hook.
|
/// Add a code hook.
|
||||||
@@ -691,10 +702,7 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add an interrupt hook.
|
/// Add an interrupt hook.
|
||||||
pub fn add_intr_hook<F: 'a>(
|
pub fn add_intr_hook<F: 'a>(&mut self, callback: F) -> Result<UcHookId, uc_error>
|
||||||
&mut self,
|
|
||||||
callback: F,
|
|
||||||
) -> Result<UcHookId, uc_error>
|
|
||||||
where
|
where
|
||||||
F: FnMut(&mut Unicorn<D>, u32),
|
F: FnMut(&mut Unicorn<D>, u32),
|
||||||
{
|
{
|
||||||
@@ -723,10 +731,7 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add hook for invalid instructions
|
/// Add hook for invalid instructions
|
||||||
pub fn add_insn_invalid_hook<F: 'a>(
|
pub fn add_insn_invalid_hook<F: 'a>(&mut self, callback: F) -> Result<UcHookId, uc_error>
|
||||||
&mut self,
|
|
||||||
callback: F,
|
|
||||||
) -> Result<UcHookId, uc_error>
|
|
||||||
where
|
where
|
||||||
F: FnMut(&mut Unicorn<D>) -> bool,
|
F: FnMut(&mut Unicorn<D>) -> bool,
|
||||||
{
|
{
|
||||||
@@ -755,10 +760,8 @@ 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>(
|
#[cfg(feature = "arch_x86")]
|
||||||
&mut self,
|
pub fn add_insn_in_hook<F: 'a>(&mut self, callback: F) -> Result<UcHookId, uc_error>
|
||||||
callback: F,
|
|
||||||
) -> Result<UcHookId, uc_error>
|
|
||||||
where
|
where
|
||||||
F: FnMut(&mut Unicorn<D>, u32, usize) -> u32,
|
F: FnMut(&mut Unicorn<D>, u32, usize) -> u32,
|
||||||
{
|
{
|
||||||
@@ -788,10 +791,8 @@ 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>(
|
#[cfg(feature = "arch_x86")]
|
||||||
&mut self,
|
pub fn add_insn_out_hook<F: 'a>(&mut self, callback: F) -> Result<UcHookId, uc_error>
|
||||||
callback: F,
|
|
||||||
) -> Result<UcHookId, uc_error>
|
|
||||||
where
|
where
|
||||||
F: FnMut(&mut Unicorn<D>, u32, usize, u32),
|
F: FnMut(&mut Unicorn<D>, u32, usize, u32),
|
||||||
{
|
{
|
||||||
@@ -821,6 +822,7 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add hook for x86 SYSCALL or SYSENTER.
|
/// Add hook for x86 SYSCALL or SYSENTER.
|
||||||
|
#[cfg(feature = "arch_x86")]
|
||||||
pub fn add_insn_sys_hook<F>(
|
pub fn add_insn_sys_hook<F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
insn_type: InsnSysX86,
|
insn_type: InsnSysX86,
|
||||||
@@ -892,10 +894,7 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
/// Remove a hook.
|
/// Remove a hook.
|
||||||
///
|
///
|
||||||
/// `hook_id` is the value returned by `add_*_hook` functions.
|
/// `hook_id` is the value returned by `add_*_hook` functions.
|
||||||
pub fn remove_hook(
|
pub fn remove_hook(&mut self, hook_id: UcHookId) -> Result<(), uc_error> {
|
||||||
&mut self,
|
|
||||||
hook_id: UcHookId,
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
// drop the hook
|
// drop the hook
|
||||||
let inner = self.inner_mut();
|
let inner = self.inner_mut();
|
||||||
inner.hooks.retain(|(id, _)| id != &hook_id);
|
inner.hooks.retain(|(id, _)| id != &hook_id);
|
||||||
@@ -908,15 +907,13 @@ 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();
|
||||||
unsafe { ffi::uc_context_alloc(self.get_handle(), &mut empty_context) }
|
unsafe { ffi::uc_context_alloc(self.get_handle(), &mut empty_context) }.and(Ok(Context {
|
||||||
.and(Ok(Context { context: empty_context }))
|
context: empty_context,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save current Unicorn context to previously allocated Context struct.
|
/// Save current Unicorn context to previously allocated Context struct.
|
||||||
pub fn context_save(
|
pub fn context_save(&self, context: &mut Context) -> Result<(), uc_error> {
|
||||||
&self,
|
|
||||||
context: &mut Context,
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
unsafe { ffi::uc_context_save(self.get_handle(), context.context) }.into()
|
unsafe { ffi::uc_context_save(self.get_handle(), context.context) }.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,7 +927,9 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
ffi::uc_context_alloc(self.get_handle(), &mut new_context).and_then(|| {
|
ffi::uc_context_alloc(self.get_handle(), &mut new_context).and_then(|| {
|
||||||
ffi::uc_context_save(self.get_handle(), new_context)
|
ffi::uc_context_save(self.get_handle(), new_context)
|
||||||
.and(Ok(Context { context: new_context }))
|
.and(Ok(Context {
|
||||||
|
context: new_context,
|
||||||
|
}))
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ffi::uc_context_free(new_context);
|
ffi::uc_context_free(new_context);
|
||||||
e
|
e
|
||||||
@@ -944,10 +943,7 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
/// Perform a quick rollback of the CPU context, including registers and some
|
/// Perform a quick rollback of the CPU context, including registers and some
|
||||||
/// 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(
|
pub fn context_restore(&self, context: &Context) -> Result<(), uc_error> {
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
unsafe { ffi::uc_context_restore(self.get_handle(), context.context) }.into()
|
unsafe { ffi::uc_context_restore(self.get_handle(), context.context) }.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -978,107 +974,162 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
/// Query the internal status of the engine.
|
/// Query the internal status of the engine.
|
||||||
///
|
///
|
||||||
/// supported: `MODE`, `PAGE_SIZE`, `ARCH`
|
/// supported: `MODE`, `PAGE_SIZE`, `ARCH`
|
||||||
pub fn query(
|
pub fn query(&self, query: Query) -> Result<usize, uc_error> {
|
||||||
&self,
|
|
||||||
query: Query,
|
|
||||||
) -> Result<usize, uc_error> {
|
|
||||||
let mut result: libc::size_t = Default::default();
|
let mut result: libc::size_t = Default::default();
|
||||||
unsafe { ffi::uc_query(self.get_handle(), query, &mut result) }.and(Ok(result))
|
unsafe { ffi::uc_query(self.get_handle(), query, &mut result) }.and(Ok(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the `i32` register value for the program counter for the specified architecture.
|
||||||
|
///
|
||||||
|
/// If an architecture is not compiled in, this function will return `uc_error::ARCH`.
|
||||||
|
#[inline]
|
||||||
|
fn arch_to_pc_register(arch: Arch) -> Result<i32, uc_error> {
|
||||||
|
match arch {
|
||||||
|
#[cfg(feature = "arch_x86")]
|
||||||
|
Arch::X86 => Ok(RegisterX86::RIP as i32),
|
||||||
|
#[cfg(feature = "arch_arm")]
|
||||||
|
Arch::ARM => Ok(RegisterARM::PC as i32),
|
||||||
|
#[cfg(feature = "arch_arm")]
|
||||||
|
Arch::ARM64 => Ok(RegisterARM64::PC as i32),
|
||||||
|
#[cfg(feature = "arch_mips")]
|
||||||
|
Arch::MIPS => Ok(RegisterMIPS::PC as i32),
|
||||||
|
#[cfg(feature = "arch_sparc")]
|
||||||
|
Arch::SPARC => Ok(RegisterSPARC::PC as i32),
|
||||||
|
#[cfg(feature = "arch_m68k")]
|
||||||
|
Arch::M68K => Ok(RegisterM68K::PC as i32),
|
||||||
|
#[cfg(feature = "arch_ppc")]
|
||||||
|
Arch::PPC => Ok(RegisterPPC::PC as i32),
|
||||||
|
#[cfg(feature = "arch_riscv")]
|
||||||
|
Arch::RISCV => Ok(RegisterRISCV::PC as i32),
|
||||||
|
#[cfg(feature = "arch_s390x")]
|
||||||
|
Arch::S390X => Ok(RegisterS390X::PC as i32),
|
||||||
|
#[cfg(feature = "arch_tricore")]
|
||||||
|
Arch::TRICORE => Ok(RegisterTRICORE::PC as i32),
|
||||||
|
// returns `uc_error::ARCH` for `Arch::MAX`, and any
|
||||||
|
// other architecture that are not compiled in
|
||||||
|
_ => Err(uc_error::ARCH),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the current program counter for this `unicorn` instance.
|
/// Gets the current program counter for this `unicorn` instance.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pc_read(&self) -> Result<u64, uc_error> {
|
pub fn pc_read(&self) -> Result<u64, uc_error> {
|
||||||
let arch = self.get_arch();
|
let arch = self.get_arch();
|
||||||
let reg = match arch {
|
|
||||||
Arch::X86 => RegisterX86::RIP as i32,
|
self.reg_read(Self::arch_to_pc_register(arch)?)
|
||||||
Arch::ARM => RegisterARM::PC as i32,
|
|
||||||
Arch::ARM64 => RegisterARM64::PC as i32,
|
|
||||||
Arch::MIPS => RegisterMIPS::PC as i32,
|
|
||||||
Arch::SPARC => RegisterSPARC::PC as i32,
|
|
||||||
Arch::M68K => RegisterM68K::PC as i32,
|
|
||||||
Arch::PPC => RegisterPPC::PC as i32,
|
|
||||||
Arch::RISCV => RegisterRISCV::PC as i32,
|
|
||||||
Arch::S390X => RegisterS390X::PC as i32,
|
|
||||||
Arch::TRICORE => RegisterTRICORE::PC as i32,
|
|
||||||
Arch::MAX => panic!("Illegal Arch specified"),
|
|
||||||
};
|
|
||||||
self.reg_read(reg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the program counter for this `unicorn` instance.
|
/// Sets the program counter for this `unicorn` instance.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_pc(
|
pub fn set_pc(&mut self, value: u64) -> Result<(), uc_error> {
|
||||||
&mut self,
|
|
||||||
value: u64,
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
let arch = self.get_arch();
|
let arch = self.get_arch();
|
||||||
let reg = match arch {
|
|
||||||
Arch::X86 => RegisterX86::RIP as i32,
|
self.reg_write(Self::arch_to_pc_register(arch)?, value)
|
||||||
Arch::ARM => RegisterARM::PC as i32,
|
|
||||||
Arch::ARM64 => RegisterARM64::PC as i32,
|
|
||||||
Arch::MIPS => RegisterMIPS::PC as i32,
|
|
||||||
Arch::SPARC => RegisterSPARC::PC as i32,
|
|
||||||
Arch::M68K => RegisterM68K::PC as i32,
|
|
||||||
Arch::PPC => RegisterPPC::PC as i32,
|
|
||||||
Arch::RISCV => RegisterRISCV::PC as i32,
|
|
||||||
Arch::S390X => RegisterS390X::PC as i32,
|
|
||||||
Arch::TRICORE => RegisterTRICORE::PC as i32,
|
|
||||||
Arch::MAX => panic!("Illegal Arch specified"),
|
|
||||||
};
|
|
||||||
self.reg_write(reg, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_get_mode(&self) -> Result<Mode, uc_error> {
|
pub fn ctl_get_mode(&self) -> Result<Mode, uc_error> {
|
||||||
let mut result: i32 = Default::default();
|
let mut result: i32 = Default::default();
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_MODE), &mut result) }
|
unsafe {
|
||||||
.and_then(|| Ok(Mode::from_bits_truncate(result)))
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_READ!(ControlType::UC_CTL_UC_MODE),
|
||||||
|
&mut result,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.and_then(|| Ok(Mode::from_bits_truncate(result)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_get_page_size(&self) -> Result<u32, uc_error> {
|
pub fn ctl_get_page_size(&self) -> Result<u32, uc_error> {
|
||||||
let mut result: u32 = Default::default();
|
let mut result: u32 = Default::default();
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_PAGE_SIZE), &mut result) }
|
unsafe {
|
||||||
.and_then(|| Ok(result))
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_READ!(ControlType::UC_CTL_UC_PAGE_SIZE),
|
||||||
|
&mut result,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.and_then(|| Ok(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_set_page_size(
|
pub fn ctl_set_page_size(&self, page_size: u32) -> Result<(), uc_error> {
|
||||||
&self,
|
unsafe {
|
||||||
page_size: u32,
|
ffi::uc_ctl(
|
||||||
) -> Result<(), uc_error> {
|
self.get_handle(),
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_UC_PAGE_SIZE), page_size) }.into()
|
UC_CTL_WRITE!(ControlType::UC_CTL_UC_PAGE_SIZE),
|
||||||
|
page_size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_get_arch(&self) -> Result<Arch, uc_error> {
|
pub fn ctl_get_arch(&self) -> Result<Arch, uc_error> {
|
||||||
let mut result: i32 = Default::default();
|
let mut result: i32 = Default::default();
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_ARCH), &mut result) }
|
unsafe {
|
||||||
.and_then(|| Arch::try_from(result as usize))
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_READ!(ControlType::UC_CTL_UC_ARCH),
|
||||||
|
&mut result,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.and_then(|| Arch::try_from(result as usize))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_get_timeout(&self) -> Result<u64, uc_error> {
|
pub fn ctl_get_timeout(&self) -> Result<u64, uc_error> {
|
||||||
let mut result: u64 = Default::default();
|
let mut result: u64 = Default::default();
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_TIMEOUT), &mut result) }
|
unsafe {
|
||||||
.and(Ok(result))
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_READ!(ControlType::UC_CTL_UC_TIMEOUT),
|
||||||
|
&mut result,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.and(Ok(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_exits_enable(&self) -> Result<(), uc_error> {
|
pub fn ctl_exits_enable(&self) -> Result<(), uc_error> {
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS), 1) }.into()
|
unsafe {
|
||||||
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_exits_disable(&self) -> Result<(), uc_error> {
|
pub fn ctl_exits_disable(&self) -> Result<(), uc_error> {
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS), 0) }.into()
|
unsafe {
|
||||||
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_get_exits_count(&self) -> Result<usize, uc_error> {
|
pub fn ctl_get_exits_count(&self) -> Result<usize, uc_error> {
|
||||||
let mut result: libc::size_t = 0usize;
|
let mut result: libc::size_t = 0usize;
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS_CNT), &mut result) }
|
unsafe {
|
||||||
.and(Ok(result))
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS_CNT),
|
||||||
|
&mut result,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.and(Ok(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_get_exits(&self) -> Result<Vec<u64>, uc_error> {
|
pub fn ctl_get_exits(&self) -> Result<Vec<u64>, uc_error> {
|
||||||
let exits_count: libc::size_t = self.ctl_get_exits_count()?;
|
let exits_count: libc::size_t = self.ctl_get_exits_count()?;
|
||||||
let mut exits: Vec<u64> = Vec::with_capacity(exits_count);
|
let mut exits: Vec<u64> = Vec::with_capacity(exits_count);
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS), exits.as_mut_ptr(), exits_count)
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS),
|
||||||
|
exits.as_mut_ptr(),
|
||||||
|
exits_count,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.and_then(|| unsafe {
|
.and_then(|| unsafe {
|
||||||
exits.set_len(exits_count);
|
exits.set_len(exits_count);
|
||||||
@@ -1086,10 +1137,7 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_set_exits(
|
pub fn ctl_set_exits(&self, exits: &[u64]) -> Result<(), uc_error> {
|
||||||
&self,
|
|
||||||
exits: &[u64],
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::uc_ctl(
|
ffi::uc_ctl(
|
||||||
self.get_handle(),
|
self.get_handle(),
|
||||||
@@ -1103,24 +1151,37 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
|
|
||||||
pub fn ctl_get_cpu_model(&self) -> Result<i32, uc_error> {
|
pub fn ctl_get_cpu_model(&self) -> Result<i32, uc_error> {
|
||||||
let mut result: i32 = Default::default();
|
let mut result: i32 = Default::default();
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_CPU_MODEL), &mut result) }
|
unsafe {
|
||||||
.and(Ok(result))
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_READ!(ControlType::UC_CTL_CPU_MODEL),
|
||||||
|
&mut result,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.and(Ok(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_set_cpu_model(
|
pub fn ctl_set_cpu_model(&self, cpu_model: i32) -> Result<(), uc_error> {
|
||||||
&self,
|
unsafe {
|
||||||
cpu_model: i32,
|
ffi::uc_ctl(
|
||||||
) -> Result<(), uc_error> {
|
self.get_handle(),
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_CPU_MODEL), cpu_model) }.into()
|
UC_CTL_WRITE!(ControlType::UC_CTL_CPU_MODEL),
|
||||||
|
cpu_model,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_remove_cache(
|
pub fn ctl_remove_cache(&self, address: u64, end: u64) -> Result<(), uc_error> {
|
||||||
&self,
|
unsafe {
|
||||||
address: u64,
|
ffi::uc_ctl(
|
||||||
end: u64,
|
self.get_handle(),
|
||||||
) -> Result<(), uc_error> {
|
UC_CTL_WRITE!(ControlType::UC_CTL_TB_REMOVE_CACHE),
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TB_REMOVE_CACHE), address, end) }
|
address,
|
||||||
.into()
|
end,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_request_cache(
|
pub fn ctl_request_cache(
|
||||||
@@ -1128,29 +1189,56 @@ impl<'a, D> Unicorn<'a, D> {
|
|||||||
address: u64,
|
address: u64,
|
||||||
tb: &mut TranslationBlock,
|
tb: &mut TranslationBlock,
|
||||||
) -> Result<(), uc_error> {
|
) -> Result<(), uc_error> {
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ_WRITE!(ControlType::UC_CTL_TB_REQUEST_CACHE), address, tb) }
|
unsafe {
|
||||||
.into()
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_READ_WRITE!(ControlType::UC_CTL_TB_REQUEST_CACHE),
|
||||||
|
address,
|
||||||
|
tb,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_flush_tb(&self) -> Result<(), uc_error> {
|
pub fn ctl_flush_tb(&self) -> Result<(), uc_error> {
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TB_FLUSH)) }.into()
|
unsafe {
|
||||||
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_WRITE!(ControlType::UC_CTL_TB_FLUSH),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_flush_tlb(&self) -> Result<(), uc_error> {
|
pub fn ctl_flush_tlb(&self) -> Result<(), uc_error> {
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TLB_FLUSH)) }.into()
|
unsafe {
|
||||||
|
ffi::uc_ctl(
|
||||||
|
self.get_handle(),
|
||||||
|
UC_CTL_WRITE!(ControlType::UC_CTL_TLB_FLUSH),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_context_mode(
|
pub fn ctl_context_mode(&self, mode: ContextMode) -> Result<(), uc_error> {
|
||||||
&self,
|
unsafe {
|
||||||
mode: ContextMode,
|
ffi::uc_ctl(
|
||||||
) -> Result<(), uc_error> {
|
self.get_handle(),
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_CONTEXT_MODE), mode) }.into()
|
UC_CTL_WRITE!(ControlType::UC_CTL_CONTEXT_MODE),
|
||||||
|
mode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ctl_tlb_type(
|
pub fn ctl_tlb_type(&self, t: TlbType) -> Result<(), uc_error> {
|
||||||
&self,
|
unsafe {
|
||||||
t: TlbType,
|
ffi::uc_ctl(
|
||||||
) -> Result<(), uc_error> {
|
self.get_handle(),
|
||||||
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TLB_TYPE), t as i32) }.into()
|
UC_CTL_WRITE!(ControlType::UC_CTL_TLB_TYPE),
|
||||||
|
t as i32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,37 +14,34 @@ pub const MILISECOND_SCALE: u64 = 1_000;
|
|||||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
pub enum uc_error {
|
pub enum uc_error {
|
||||||
OK = 0,
|
OK = 0,
|
||||||
NOMEM = 1,
|
NOMEM = 1,
|
||||||
ARCH = 2,
|
ARCH = 2,
|
||||||
HANDLE = 3,
|
HANDLE = 3,
|
||||||
MODE = 4,
|
MODE = 4,
|
||||||
VERSION = 5,
|
VERSION = 5,
|
||||||
READ_UNMAPPED = 6,
|
READ_UNMAPPED = 6,
|
||||||
WRITE_UNMAPPED = 7,
|
WRITE_UNMAPPED = 7,
|
||||||
FETCH_UNMAPPED = 8,
|
FETCH_UNMAPPED = 8,
|
||||||
HOOK = 9,
|
HOOK = 9,
|
||||||
INSN_INVALID = 10,
|
INSN_INVALID = 10,
|
||||||
MAP = 11,
|
MAP = 11,
|
||||||
WRITE_PROT = 12,
|
WRITE_PROT = 12,
|
||||||
READ_PROT = 13,
|
READ_PROT = 13,
|
||||||
FETCH_PROT = 14,
|
FETCH_PROT = 14,
|
||||||
ARG = 15,
|
ARG = 15,
|
||||||
READ_UNALIGNED = 16,
|
READ_UNALIGNED = 16,
|
||||||
WRITE_UNALIGNED = 17,
|
WRITE_UNALIGNED = 17,
|
||||||
FETCH_UNALIGNED = 18,
|
FETCH_UNALIGNED = 18,
|
||||||
HOOK_EXIST = 19,
|
HOOK_EXIST = 19,
|
||||||
RESOURCE = 20,
|
RESOURCE = 20,
|
||||||
EXCEPTION = 21,
|
EXCEPTION = 21,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl uc_error {
|
impl uc_error {
|
||||||
/// Calls op if the result is Ok, otherwise returns the Err value of self.
|
/// Calls op if the result is Ok, otherwise returns the Err value of self.
|
||||||
/// This function can be used for control flow based on Result values.
|
/// This function can be used for control flow based on Result values.
|
||||||
pub fn and_then<U, F: FnOnce() -> Result<U, uc_error>>(
|
pub fn and_then<U, F: FnOnce() -> Result<U, uc_error>>(self, op: F) -> Result<U, uc_error> {
|
||||||
self,
|
|
||||||
op: F,
|
|
||||||
) -> Result<U, uc_error> {
|
|
||||||
if let Self::OK = self {
|
if let Self::OK = self {
|
||||||
op()
|
op()
|
||||||
} else {
|
} else {
|
||||||
@@ -55,10 +52,7 @@ impl uc_error {
|
|||||||
/// Returns res if the result is Ok, otherwise returns the Err value of self.
|
/// Returns res if the result is Ok, otherwise returns the Err value of self.
|
||||||
/// Arguments passed to and are eagerly evaluated; if you are passing the result
|
/// Arguments passed to and are eagerly evaluated; if you are passing the result
|
||||||
/// of a function call, it is recommended to use and_then, which is lazily evaluated.
|
/// of a function call, it is recommended to use and_then, which is lazily evaluated.
|
||||||
pub fn and<U>(
|
pub fn and<U>(self, res: Result<U, uc_error>) -> Result<U, uc_error> {
|
||||||
self,
|
|
||||||
res: Result<U, uc_error>,
|
|
||||||
) -> Result<U, uc_error> {
|
|
||||||
if let Self::OK = self {
|
if let Self::OK = self {
|
||||||
res
|
res
|
||||||
} else {
|
} else {
|
||||||
@@ -80,22 +74,22 @@ impl From<uc_error> for Result<(), uc_error> {
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
pub enum MemType {
|
pub enum MemType {
|
||||||
READ = 16,
|
READ = 16,
|
||||||
WRITE = 17,
|
WRITE = 17,
|
||||||
FETCH = 18,
|
FETCH = 18,
|
||||||
READ_UNMAPPED = 19,
|
READ_UNMAPPED = 19,
|
||||||
WRITE_UNMAPPED = 20,
|
WRITE_UNMAPPED = 20,
|
||||||
FETCH_UNMAPPED = 21,
|
FETCH_UNMAPPED = 21,
|
||||||
WRITE_PROT = 22,
|
WRITE_PROT = 22,
|
||||||
READ_PROT = 23,
|
READ_PROT = 23,
|
||||||
FETCH_PROT = 24,
|
FETCH_PROT = 24,
|
||||||
READ_AFTER = 25,
|
READ_AFTER = 25,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
pub enum TlbType {
|
pub enum TlbType {
|
||||||
CPU = 0,
|
CPU = 0,
|
||||||
VIRTUAL = 1,
|
VIRTUAL = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,10 +136,10 @@ bitflags! {
|
|||||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
pub enum Query {
|
pub enum Query {
|
||||||
MODE = 1,
|
MODE = 1,
|
||||||
PAGE_SIZE = 2,
|
PAGE_SIZE = 2,
|
||||||
ARCH = 3,
|
ARCH = 3,
|
||||||
TIMEOUT = 4,
|
TIMEOUT = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
@@ -171,17 +165,17 @@ pub struct MemRegion {
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
pub enum Arch {
|
pub enum Arch {
|
||||||
ARM = 1,
|
ARM = 1,
|
||||||
ARM64 = 2,
|
ARM64 = 2,
|
||||||
MIPS = 3,
|
MIPS = 3,
|
||||||
X86 = 4,
|
X86 = 4,
|
||||||
PPC = 5,
|
PPC = 5,
|
||||||
SPARC = 6,
|
SPARC = 6,
|
||||||
M68K = 7,
|
M68K = 7,
|
||||||
RISCV = 8,
|
RISCV = 8,
|
||||||
S390X = 9,
|
S390X = 9,
|
||||||
TRICORE = 10,
|
TRICORE = 10,
|
||||||
MAX = 11,
|
MAX = 11,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<usize> for Arch {
|
impl TryFrom<usize> for Arch {
|
||||||
@@ -268,23 +262,23 @@ macro_rules! UC_CTL_READ_WRITE {
|
|||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
pub enum ControlType {
|
pub enum ControlType {
|
||||||
UC_CTL_UC_MODE = 0,
|
UC_CTL_UC_MODE = 0,
|
||||||
UC_CTL_UC_PAGE_SIZE = 1,
|
UC_CTL_UC_PAGE_SIZE = 1,
|
||||||
UC_CTL_UC_ARCH = 2,
|
UC_CTL_UC_ARCH = 2,
|
||||||
UC_CTL_UC_TIMEOUT = 3,
|
UC_CTL_UC_TIMEOUT = 3,
|
||||||
UC_CTL_UC_USE_EXITS = 4,
|
UC_CTL_UC_USE_EXITS = 4,
|
||||||
UC_CTL_UC_EXITS_CNT = 5,
|
UC_CTL_UC_EXITS_CNT = 5,
|
||||||
UC_CTL_UC_EXITS = 6,
|
UC_CTL_UC_EXITS = 6,
|
||||||
UC_CTL_CPU_MODEL = 7,
|
UC_CTL_CPU_MODEL = 7,
|
||||||
UC_CTL_TB_REQUEST_CACHE = 8,
|
UC_CTL_TB_REQUEST_CACHE = 8,
|
||||||
UC_CTL_TB_REMOVE_CACHE = 9,
|
UC_CTL_TB_REMOVE_CACHE = 9,
|
||||||
UC_CTL_TB_FLUSH = 10,
|
UC_CTL_TB_FLUSH = 10,
|
||||||
UC_CTL_TLB_FLUSH = 11,
|
UC_CTL_TLB_FLUSH = 11,
|
||||||
UC_CTL_TLB_TYPE = 12,
|
UC_CTL_TLB_TYPE = 12,
|
||||||
UC_CTL_TCG_BUFFER_SIZE = 13,
|
UC_CTL_TCG_BUFFER_SIZE = 13,
|
||||||
UC_CTL_CONTEXT_MODE = 14,
|
UC_CTL_CONTEXT_MODE = 14,
|
||||||
UC_CTL_IO_READ = 1 << 31,
|
UC_CTL_IO_READ = 1 << 31,
|
||||||
UC_CTL_IO_WRITE = 1 << 30,
|
UC_CTL_IO_WRITE = 1 << 30,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
|||||||
Reference in New Issue
Block a user