Improve Rust bindings (#1367)
* fixed tests * constant readability * HookType as bitflags * Mode as bitflags * improve bitflags * cargo fmt * removed unnecessary "as usize"
This commit is contained in:
parent
1e457ecc66
commit
66df39ebc9
|
@ -1,7 +1,4 @@
|
||||||
use std::{
|
use std::{env, process::Command};
|
||||||
env,
|
|
||||||
process::Command,
|
|
||||||
};
|
|
||||||
|
|
||||||
use build_helper::rustc::{link_lib, link_search};
|
use build_helper::rustc::{link_lib, link_search};
|
||||||
|
|
||||||
|
@ -9,15 +6,15 @@ fn main() {
|
||||||
println!("cargo:rerun-if-changed=unicorn");
|
println!("cargo:rerun-if-changed=unicorn");
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
let unicorn = "libunicorn.a";
|
let unicorn = "libunicorn.a";
|
||||||
let _ = Command::new("cp")
|
let _ = Command::new("cp")
|
||||||
.current_dir("../..")
|
.current_dir("../..")
|
||||||
.arg(&unicorn)
|
.arg(&unicorn)
|
||||||
.arg(&out_dir)
|
.arg(&out_dir)
|
||||||
.status()
|
.status()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
link_search(
|
link_search(
|
||||||
Some(build_helper::SearchKind::Native),
|
Some(build_helper::SearchKind::Native),
|
||||||
build_helper::out_dir());
|
build_helper::out_dir(),
|
||||||
|
);
|
||||||
link_lib(Some(build_helper::LibKind::Static), "unicorn");
|
link_lib(Some(build_helper::LibKind::Static), "unicorn");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,6 @@ pub enum RegisterARM {
|
||||||
CONTROL = 117,
|
CONTROL = 117,
|
||||||
XPSR = 118,
|
XPSR = 118,
|
||||||
ENDING = 119,
|
ENDING = 119,
|
||||||
|
|
||||||
// alias registers
|
// alias registers
|
||||||
// (assoc) R13 = 12,
|
// (assoc) R13 = 12,
|
||||||
// (assoc) R14 = 10,
|
// (assoc) R14 = 10,
|
||||||
|
@ -144,4 +143,4 @@ impl RegisterARM {
|
||||||
pub const SL: RegisterARM = RegisterARM::R10;
|
pub const SL: RegisterARM = RegisterARM::R10;
|
||||||
pub const FP: RegisterARM = RegisterARM::R11;
|
pub const FP: RegisterARM = RegisterARM::R11;
|
||||||
pub const IP: RegisterARM = RegisterARM::R12;
|
pub const IP: RegisterARM = RegisterARM::R12;
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,4 +265,4 @@ pub enum RegisterARM64 {
|
||||||
|
|
||||||
// pseudo registers
|
// pseudo registers
|
||||||
PC = 260,
|
PC = 260,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use super::unicorn_const::*;
|
||||||
|
use libc::{c_char, c_int};
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use libc::{c_char, c_int};
|
|
||||||
use super::unicorn_const::*;
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -42,8 +41,12 @@ extern "C" {
|
||||||
ptr: *mut c_void,
|
ptr: *mut c_void,
|
||||||
) -> uc_error;
|
) -> uc_error;
|
||||||
pub fn uc_mem_unmap(engine: uc_handle, address: u64, size: libc::size_t) -> uc_error;
|
pub fn uc_mem_unmap(engine: uc_handle, address: u64, size: libc::size_t) -> uc_error;
|
||||||
pub fn uc_mem_protect(engine: uc_handle, address: u64, size: libc::size_t, perms: u32)
|
pub fn uc_mem_protect(
|
||||||
-> uc_error;
|
engine: uc_handle,
|
||||||
|
address: u64,
|
||||||
|
size: libc::size_t,
|
||||||
|
perms: u32,
|
||||||
|
) -> uc_error;
|
||||||
pub fn uc_mem_regions(
|
pub fn uc_mem_regions(
|
||||||
engine: uc_handle,
|
engine: uc_handle,
|
||||||
regions: *const *const MemRegion,
|
regions: *const *const MemRegion,
|
||||||
|
@ -74,102 +77,154 @@ extern "C" {
|
||||||
pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error;
|
pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct CodeHook<D> {
|
pub struct CodeHook<D> {
|
||||||
pub unicorn: *mut crate::UnicornInner<D>,
|
pub unicorn: *mut crate::UnicornInner<D>,
|
||||||
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u64, u32)>
|
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u64, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockHook<D> {
|
pub struct BlockHook<D> {
|
||||||
pub unicorn: *mut crate::UnicornInner<D>,
|
pub unicorn: *mut crate::UnicornInner<D>,
|
||||||
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u64, u32)>
|
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u64, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MemHook<D> {
|
pub struct MemHook<D> {
|
||||||
pub unicorn: *mut crate::UnicornInner<D>,
|
pub unicorn: *mut crate::UnicornInner<D>,
|
||||||
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, MemType, u64, usize, i64)>
|
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, MemType, u64, usize, i64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InterruptHook<D> {
|
pub struct InterruptHook<D> {
|
||||||
pub unicorn: *mut crate::UnicornInner<D>,
|
pub unicorn: *mut crate::UnicornInner<D>,
|
||||||
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32)>
|
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InstructionInHook<D> {
|
pub struct InstructionInHook<D> {
|
||||||
pub unicorn: *mut crate::UnicornInner<D>,
|
pub unicorn: *mut crate::UnicornInner<D>,
|
||||||
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32, usize)>
|
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InstructionOutHook<D> {
|
pub struct InstructionOutHook<D> {
|
||||||
pub unicorn: *mut crate::UnicornInner<D>,
|
pub unicorn: *mut crate::UnicornInner<D>,
|
||||||
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32, usize, u32)>
|
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>, u32, usize, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InstructionSysHook<D> {
|
pub struct InstructionSysHook<D> {
|
||||||
pub unicorn: *mut crate::UnicornInner<D>,
|
pub unicorn: *mut crate::UnicornInner<D>,
|
||||||
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>)>
|
pub callback: Box<dyn FnMut(crate::UnicornHandle<D>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn code_hook_proxy<D>(uc: uc_handle, address: u64, size: u32, user_data: *mut CodeHook<D>) {
|
pub extern "C" fn code_hook_proxy<D>(
|
||||||
|
uc: uc_handle,
|
||||||
|
address: u64,
|
||||||
|
size: u32,
|
||||||
|
user_data: *mut CodeHook<D>,
|
||||||
|
) {
|
||||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
assert_eq!(uc, unicorn.uc);
|
assert_eq!(uc, unicorn.uc);
|
||||||
callback(crate::UnicornHandle { inner: unsafe { Pin::new_unchecked(unicorn) } }, address, size);
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn block_hook_proxy<D>(uc: uc_handle, address: u64, size: u32, user_data: *mut BlockHook<D>) {
|
pub extern "C" fn block_hook_proxy<D>(
|
||||||
|
uc: uc_handle,
|
||||||
|
address: u64,
|
||||||
|
size: u32,
|
||||||
|
user_data: *mut BlockHook<D>,
|
||||||
|
) {
|
||||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
assert_eq!(uc, unicorn.uc);
|
assert_eq!(uc, unicorn.uc);
|
||||||
callback(crate::UnicornHandle { inner: unsafe { Pin::new_unchecked(unicorn) } }, address, size);
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn mem_hook_proxy<D>(uc: uc_handle,
|
pub extern "C" fn mem_hook_proxy<D>(
|
||||||
mem_type: MemType,
|
uc: uc_handle,
|
||||||
address: u64,
|
mem_type: MemType,
|
||||||
size: u32,
|
address: u64,
|
||||||
value: i64,
|
size: u32,
|
||||||
user_data: *mut MemHook<D>)
|
value: i64,
|
||||||
{
|
user_data: *mut MemHook<D>,
|
||||||
|
) {
|
||||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
assert_eq!(uc, unicorn.uc);
|
assert_eq!(uc, unicorn.uc);
|
||||||
callback(crate::UnicornHandle { inner: unsafe { Pin::new_unchecked(unicorn) } }, mem_type, address, size as usize, value);
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
mem_type,
|
||||||
|
address,
|
||||||
|
size as usize,
|
||||||
|
value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn intr_hook_proxy<D>(uc: uc_handle, value: u32, user_data: *mut InterruptHook<D>) {
|
pub extern "C" fn intr_hook_proxy<D>(uc: uc_handle, value: u32, user_data: *mut InterruptHook<D>) {
|
||||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
assert_eq!(uc, unicorn.uc);
|
assert_eq!(uc, unicorn.uc);
|
||||||
callback(crate::UnicornHandle { inner: unsafe { Pin::new_unchecked(unicorn) } }, value);
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn insn_in_hook_proxy<D>(
|
pub extern "C" fn insn_in_hook_proxy<D>(
|
||||||
uc: uc_handle,
|
uc: uc_handle,
|
||||||
port: u32,
|
port: u32,
|
||||||
size: usize,
|
size: usize,
|
||||||
user_data: *mut InstructionInHook<D>) {
|
user_data: *mut InstructionInHook<D>,
|
||||||
|
) {
|
||||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
assert_eq!(uc, unicorn.uc);
|
assert_eq!(uc, unicorn.uc);
|
||||||
callback(crate::UnicornHandle { inner: unsafe { Pin::new_unchecked(unicorn) } }, port, size);
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
port,
|
||||||
|
size,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn insn_out_hook_proxy<D>(
|
pub extern "C" fn insn_out_hook_proxy<D>(
|
||||||
uc: uc_handle,
|
uc: uc_handle,
|
||||||
port: u32,
|
port: u32,
|
||||||
size: usize,
|
size: usize,
|
||||||
value: u32,
|
value: u32,
|
||||||
user_data: *mut InstructionOutHook<D>) {
|
user_data: *mut InstructionOutHook<D>,
|
||||||
|
) {
|
||||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
assert_eq!(uc, unicorn.uc);
|
assert_eq!(uc, unicorn.uc);
|
||||||
callback(crate::UnicornHandle { inner: unsafe { Pin::new_unchecked(unicorn) } }, port, size, value);
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
port,
|
||||||
|
size,
|
||||||
|
value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern "C" fn insn_sys_hook_proxy<D>(uc: uc_handle, user_data: *mut InstructionSysHook<D>) {
|
pub extern "C" fn insn_sys_hook_proxy<D>(uc: uc_handle, user_data: *mut InstructionSysHook<D>) {
|
||||||
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
let callback = &mut unsafe { &mut *(*user_data).callback };
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
assert_eq!(uc, unicorn.uc);
|
assert_eq!(uc, unicorn.uc);
|
||||||
callback(crate::UnicornHandle { inner: unsafe { Pin::new_unchecked(unicorn) } });
|
callback(crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
|
||||||
//! Bindings for the Unicorn emulator.
|
//! Bindings for the Unicorn emulator.
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
//! # Example use
|
//! # Example use
|
||||||
//!
|
//!
|
||||||
|
@ -9,18 +8,18 @@
|
||||||
//!
|
//!
|
||||||
//! use unicorn::RegisterARM;
|
//! use unicorn::RegisterARM;
|
||||||
//! use unicorn::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE};
|
//! use unicorn::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE};
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23
|
//! let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23
|
||||||
//!
|
//!
|
||||||
//! let mut unicorn = unicorn::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN, 0).expect("failed to initialize Unicorn instance");
|
//! let mut unicorn = unicorn::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN, 0).expect("failed to initialize Unicorn instance");
|
||||||
//! let mut emu = unicorn.borrow();
|
//! let mut emu = unicorn.borrow();
|
||||||
//! emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page");
|
//! emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page");
|
||||||
//! emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions");
|
//! emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions");
|
||||||
//!
|
//!
|
||||||
//! emu.reg_write(RegisterARM::R0 as i32, 123).expect("failed write R0");
|
//! emu.reg_write(RegisterARM::R0 as i32, 123).expect("failed write R0");
|
||||||
//! emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5");
|
//! emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5");
|
||||||
//!
|
//!
|
||||||
//! let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000);
|
//! let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000);
|
||||||
//! assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(100));
|
//! assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(100));
|
||||||
//! assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337));
|
//! assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337));
|
||||||
|
@ -29,8 +28,8 @@
|
||||||
//!
|
//!
|
||||||
|
|
||||||
mod ffi;
|
mod ffi;
|
||||||
pub mod utils;
|
|
||||||
pub mod unicorn_const;
|
pub mod unicorn_const;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
mod arm;
|
mod arm;
|
||||||
mod arm64;
|
mod arm64;
|
||||||
|
@ -39,21 +38,13 @@ mod mips;
|
||||||
mod ppc;
|
mod ppc;
|
||||||
mod sparc;
|
mod sparc;
|
||||||
mod x86;
|
mod x86;
|
||||||
pub use crate::{
|
pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, sparc::*, x86::*};
|
||||||
arm64::*,
|
|
||||||
arm::*,
|
|
||||||
m68k::*,
|
|
||||||
mips::*,
|
|
||||||
ppc::*,
|
|
||||||
sparc::*,
|
|
||||||
x86::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::ffi::c_void;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use ffi::uc_handle;
|
use ffi::uc_handle;
|
||||||
use std::pin::Pin;
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::c_void;
|
||||||
use std::marker::PhantomPinned;
|
use std::marker::PhantomPinned;
|
||||||
|
use std::pin::Pin;
|
||||||
use unicorn_const::*;
|
use unicorn_const::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -79,13 +70,13 @@ impl Drop for Context {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// A Unicorn emulator instance.
|
/// A Unicorn emulator instance.
|
||||||
pub struct Unicorn<D> {
|
pub struct Unicorn<D> {
|
||||||
inner: Pin<Box<UnicornInner<D>>>
|
inner: Pin<Box<UnicornInner<D>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Handle used to safely access exposed functions and data of a Unicorn instance.
|
/// Handle used to safely access exposed functions and data of a Unicorn instance.
|
||||||
pub struct UnicornHandle<'a, D> {
|
pub struct UnicornHandle<'a, D> {
|
||||||
inner: Pin<&'a mut UnicornInner<D>>
|
inner: Pin<&'a mut UnicornInner<D>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal Management struct
|
/// Internal Management struct
|
||||||
|
@ -100,39 +91,40 @@ pub struct UnicornInner<D> {
|
||||||
pub insn_out_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionOutHook<D>>>,
|
pub insn_out_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionOutHook<D>>>,
|
||||||
pub insn_sys_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionSysHook<D>>>,
|
pub insn_sys_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionSysHook<D>>>,
|
||||||
pub data: D,
|
pub data: D,
|
||||||
_pin: PhantomPinned
|
_pin: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> Unicorn<D> {
|
impl<D> Unicorn<D> {
|
||||||
/// Create a new instance of the unicorn engine for the specified architecture
|
/// Create a new instance of the unicorn engine for the specified architecture
|
||||||
/// and hardware mode.
|
/// and hardware mode.
|
||||||
pub fn new(arch: Arch, mode: Mode, data: D)
|
pub fn new(arch: Arch, mode: Mode, data: D) -> Result<Unicorn<D>, uc_error> {
|
||||||
-> Result<Unicorn<D>, uc_error> {
|
|
||||||
|
|
||||||
let mut handle = std::ptr::null_mut();
|
let mut handle = std::ptr::null_mut();
|
||||||
let err = unsafe { ffi::uc_open(arch, mode, &mut handle) };
|
let err = unsafe { ffi::uc_open(arch, mode, &mut handle) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
Ok(Unicorn {
|
Ok(Unicorn {
|
||||||
inner: Box::pin(UnicornInner {
|
inner: Box::pin(UnicornInner {
|
||||||
uc: handle,
|
uc: handle,
|
||||||
arch: arch,
|
arch: arch,
|
||||||
code_hooks: HashMap::new(),
|
code_hooks: HashMap::new(),
|
||||||
block_hooks: HashMap::new(),
|
block_hooks: HashMap::new(),
|
||||||
mem_hooks: HashMap::new(),
|
mem_hooks: HashMap::new(),
|
||||||
intr_hooks: HashMap::new(),
|
intr_hooks: HashMap::new(),
|
||||||
insn_in_hooks: HashMap::new(),
|
insn_in_hooks: HashMap::new(),
|
||||||
insn_out_hooks: HashMap::new(),
|
insn_out_hooks: HashMap::new(),
|
||||||
insn_sys_hooks: HashMap::new(),
|
insn_sys_hooks: HashMap::new(),
|
||||||
data: data,
|
data: data,
|
||||||
_pin: std::marker::PhantomPinned
|
_pin: std::marker::PhantomPinned,
|
||||||
})})
|
}),
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn borrow<'a>(&'a mut self) -> UnicornHandle<'a, D> {
|
pub fn borrow<'a>(&'a mut self) -> UnicornHandle<'a, D> {
|
||||||
UnicornHandle { inner: self.inner.as_mut() }
|
UnicornHandle {
|
||||||
|
inner: self.inner.as_mut(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +147,7 @@ impl<D> std::fmt::Debug for UnicornInner<D> {
|
||||||
|
|
||||||
impl<'a, D> UnicornHandle<'a, D> {
|
impl<'a, D> UnicornHandle<'a, D> {
|
||||||
/// 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.
|
||||||
pub fn get_data(&self) -> &D {
|
pub fn get_data(&self) -> &D {
|
||||||
|
@ -230,11 +222,12 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
/// `size` must be a multiple of 4kb or this will return `Error::ARG`.
|
/// `size` must be a multiple of 4kb or this will return `Error::ARG`.
|
||||||
///
|
///
|
||||||
/// `ptr` is a pointer to the provided memory region that will be used by the emulator.
|
/// `ptr` is a pointer to the provided memory region that will be used by the emulator.
|
||||||
pub fn mem_map_ptr(&mut self,
|
pub fn mem_map_ptr(
|
||||||
address: u64,
|
&mut self,
|
||||||
size: usize,
|
address: u64,
|
||||||
perms: Permission,
|
size: usize,
|
||||||
ptr: *mut c_void
|
perms: Permission,
|
||||||
|
ptr: *mut c_void,
|
||||||
) -> Result<(), uc_error> {
|
) -> Result<(), uc_error> {
|
||||||
let err = unsafe { ffi::uc_mem_map_ptr(self.inner.uc, address, size, perms.bits(), ptr) };
|
let err = unsafe { ffi::uc_mem_map_ptr(self.inner.uc, address, size, perms.bits(), ptr) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
|
@ -248,10 +241,11 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
///
|
///
|
||||||
/// `address` must be aligned to 4kb or this will return `Error::ARG`.
|
/// `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_map(&mut self,
|
pub fn mem_map(
|
||||||
address: u64,
|
&mut self,
|
||||||
size: libc::size_t,
|
address: u64,
|
||||||
perms: Permission
|
size: libc::size_t,
|
||||||
|
perms: Permission,
|
||||||
) -> Result<(), uc_error> {
|
) -> Result<(), uc_error> {
|
||||||
let err = unsafe { ffi::uc_mem_map(self.inner.uc, address, size, perms.bits()) };
|
let err = unsafe { ffi::uc_mem_map(self.inner.uc, address, size, perms.bits()) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
|
@ -265,10 +259,7 @@ impl<'a, D> UnicornHandle<'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,
|
pub fn mem_unmap(&mut self, address: u64, size: libc::size_t) -> Result<(), uc_error> {
|
||||||
address: u64,
|
|
||||||
size: libc::size_t
|
|
||||||
) -> Result<(), uc_error> {
|
|
||||||
let err = unsafe { ffi::uc_mem_unmap(self.inner.uc, address, size) };
|
let err = unsafe { ffi::uc_mem_unmap(self.inner.uc, address, size) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -281,10 +272,11 @@ impl<'a, D> UnicornHandle<'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_protect(&mut self,
|
pub fn mem_protect(
|
||||||
address: u64,
|
&mut self,
|
||||||
size: libc::size_t,
|
address: u64,
|
||||||
perms: Permission
|
size: libc::size_t,
|
||||||
|
perms: Permission,
|
||||||
) -> Result<(), uc_error> {
|
) -> Result<(), uc_error> {
|
||||||
let err = unsafe { ffi::uc_mem_protect(self.inner.uc, address, size, perms.bits()) };
|
let err = unsafe { ffi::uc_mem_protect(self.inner.uc, address, size, perms.bits()) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
|
@ -296,7 +288,8 @@ impl<'a, D> UnicornHandle<'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.inner.uc, regid.into(), &value as *const _ as _) };
|
let err =
|
||||||
|
unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), &value as *const _ as _) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -305,7 +298,7 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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>>(&self, regid: T, value: Box<[u8]>) -> Result<(), uc_error> {
|
pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: Box<[u8]>) -> Result<(), uc_error> {
|
||||||
|
@ -318,11 +311,12 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read an unsigned value from a register.
|
/// 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>>(&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.inner.uc, regid.into(), &mut value as *mut u64 as _) };
|
let err =
|
||||||
|
unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut u64 as _) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
Ok(value)
|
Ok(value)
|
||||||
} else {
|
} else {
|
||||||
|
@ -331,7 +325,7 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 (x86); Q, V (arm64)).
|
/// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
|
||||||
pub fn reg_read_long<T: Into<i32>>(&self, regid: T) -> Result<Box<[u8]>, uc_error> {
|
pub fn reg_read_long<T: Into<i32>>(&self, regid: T) -> Result<Box<[u8]>, uc_error> {
|
||||||
let err: uc_error;
|
let err: uc_error;
|
||||||
|
@ -341,29 +335,39 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
let curr_arch = self.get_arch();
|
let curr_arch = self.get_arch();
|
||||||
|
|
||||||
if curr_arch == Arch::X86 {
|
if curr_arch == Arch::X86 {
|
||||||
if curr_reg_id >= x86::RegisterX86::XMM0 as i32 && curr_reg_id <= x86::RegisterX86::XMM31 as i32 {
|
if curr_reg_id >= x86::RegisterX86::XMM0 as i32
|
||||||
value = vec![0; 16 as usize];
|
&& curr_reg_id <= x86::RegisterX86::XMM31 as i32
|
||||||
} else if curr_reg_id >= x86::RegisterX86::YMM0 as i32 && curr_reg_id <= x86::RegisterX86::YMM31 as i32 {
|
{
|
||||||
value = vec![0; 32 as usize];
|
value = vec![0; 16];
|
||||||
} else if curr_reg_id >= x86::RegisterX86::ZMM0 as i32 && curr_reg_id <= x86::RegisterX86::ZMM31 as i32 {
|
} else if curr_reg_id >= x86::RegisterX86::YMM0 as i32
|
||||||
value = vec![0; 64 as usize];
|
&& curr_reg_id <= x86::RegisterX86::YMM31 as i32
|
||||||
} else if curr_reg_id == x86::RegisterX86::GDTR as i32 ||
|
{
|
||||||
curr_reg_id == x86::RegisterX86::IDTR as i32 {
|
value = vec![0; 32];
|
||||||
value = vec![0; 10 as usize]; // 64 bit base address in IA-32e mode
|
} else if curr_reg_id >= x86::RegisterX86::ZMM0 as i32
|
||||||
|
&& curr_reg_id <= x86::RegisterX86::ZMM31 as i32
|
||||||
|
{
|
||||||
|
value = vec![0; 64];
|
||||||
|
} else if curr_reg_id == x86::RegisterX86::GDTR as i32
|
||||||
|
|| curr_reg_id == x86::RegisterX86::IDTR as i32
|
||||||
|
{
|
||||||
|
value = vec![0; 10]; // 64 bit base address in IA-32e mode
|
||||||
} else {
|
} else {
|
||||||
return Err(uc_error::ARG)
|
return Err(uc_error::ARG);
|
||||||
}
|
}
|
||||||
} else if curr_arch == Arch::ARM64 {
|
} else if curr_arch == Arch::ARM64 {
|
||||||
if (curr_reg_id >= arm64::RegisterARM64::Q0 as i32 && curr_reg_id <= arm64::RegisterARM64::Q31 as i32) ||
|
if (curr_reg_id >= arm64::RegisterARM64::Q0 as i32
|
||||||
(curr_reg_id >= arm64::RegisterARM64::V0 as i32 && curr_reg_id <= arm64::RegisterARM64::V31 as i32) {
|
&& curr_reg_id <= arm64::RegisterARM64::Q31 as i32)
|
||||||
value = vec![0; 16 as usize];
|
|| (curr_reg_id >= arm64::RegisterARM64::V0 as i32
|
||||||
|
&& curr_reg_id <= arm64::RegisterARM64::V31 as i32)
|
||||||
|
{
|
||||||
|
value = vec![0; 16];
|
||||||
} else {
|
} else {
|
||||||
return Err(uc_error::ARG)
|
return Err(uc_error::ARG);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(uc_error::ARCH)
|
return Err(uc_error::ARCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = unsafe { ffi::uc_reg_read(self.inner.uc, curr_reg_id, value.as_mut_ptr() as _) };
|
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 {
|
||||||
|
@ -377,7 +381,8 @@ impl<'a, D> UnicornHandle<'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.inner.uc, regid.into(), &mut value as *mut i32 as _) };
|
let err =
|
||||||
|
unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut i32 as _) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
Ok(value)
|
Ok(value)
|
||||||
} else {
|
} else {
|
||||||
|
@ -392,14 +397,15 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
end: u64,
|
end: u64,
|
||||||
callback: F,
|
callback: F,
|
||||||
) -> Result<ffi::uc_hook, uc_error>
|
) -> Result<ffi::uc_hook, uc_error>
|
||||||
where F: FnMut(UnicornHandle<D>, u64, u32)
|
where
|
||||||
|
F: FnMut(UnicornHandle<D>, u64, u32),
|
||||||
{
|
{
|
||||||
let mut hook_ptr = std::ptr::null_mut();
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
let mut user_data = Box::new(ffi::CodeHook {
|
let mut user_data = Box::new(ffi::CodeHook {
|
||||||
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
callback: Box::new(callback),
|
callback: Box::new(callback),
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = unsafe {
|
let err = unsafe {
|
||||||
ffi::uc_hook_add(
|
ffi::uc_hook_add(
|
||||||
self.inner.uc,
|
self.inner.uc,
|
||||||
|
@ -412,7 +418,9 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
unsafe { self.inner.as_mut().get_unchecked_mut() }.code_hooks.insert(hook_ptr, user_data);
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.code_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
Ok(hook_ptr)
|
Ok(hook_ptr)
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -420,11 +428,9 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a block hook.
|
/// Add a block hook.
|
||||||
pub fn add_block_hook<F: 'static>(
|
pub fn add_block_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
|
||||||
&mut self,
|
where
|
||||||
callback: F,
|
F: FnMut(UnicornHandle<D>, u64, u32),
|
||||||
) -> Result<ffi::uc_hook, uc_error>
|
|
||||||
where F: FnMut(UnicornHandle<D>, u64, u32)
|
|
||||||
{
|
{
|
||||||
let mut hook_ptr = std::ptr::null_mut();
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
let mut user_data = Box::new(ffi::BlockHook {
|
let mut user_data = Box::new(ffi::BlockHook {
|
||||||
|
@ -444,7 +450,9 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
unsafe { self.inner.as_mut().get_unchecked_mut() }.block_hooks.insert(hook_ptr, user_data);
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.block_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
Ok(hook_ptr)
|
Ok(hook_ptr)
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -459,18 +467,19 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
end: u64,
|
end: u64,
|
||||||
callback: F,
|
callback: F,
|
||||||
) -> Result<ffi::uc_hook, uc_error>
|
) -> Result<ffi::uc_hook, uc_error>
|
||||||
where F: FnMut(UnicornHandle<D>, MemType, u64, usize, i64)
|
where
|
||||||
|
F: FnMut(UnicornHandle<D>, MemType, u64, usize, i64),
|
||||||
{
|
{
|
||||||
if (hook_type as i32) < 16 || hook_type == HookType::INSN_INVALID {
|
if !(HookType::MEM_ALL | HookType::MEM_READ_AFTER).contains(hook_type) {
|
||||||
return Err(uc_error::ARG);
|
return Err(uc_error::ARG);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut hook_ptr = std::ptr::null_mut();
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
let mut user_data = Box::new(ffi::MemHook {
|
let mut user_data = Box::new(ffi::MemHook {
|
||||||
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
callback: Box::new(callback),
|
callback: Box::new(callback),
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = unsafe {
|
let err = unsafe {
|
||||||
ffi::uc_hook_add(
|
ffi::uc_hook_add(
|
||||||
self.inner.uc,
|
self.inner.uc,
|
||||||
|
@ -483,7 +492,9 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
unsafe { self.inner.as_mut().get_unchecked_mut() }.mem_hooks.insert(hook_ptr, user_data);
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.mem_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
Ok(hook_ptr)
|
Ok(hook_ptr)
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -491,18 +502,16 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an interrupt hook.
|
/// Add an interrupt hook.
|
||||||
pub fn add_intr_hook<F: 'static>(
|
pub fn add_intr_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
|
||||||
&mut self,
|
where
|
||||||
callback: F,
|
F: FnMut(UnicornHandle<D>, u32),
|
||||||
) -> Result<ffi::uc_hook, uc_error>
|
{
|
||||||
where F: FnMut(UnicornHandle<D>, u32)
|
|
||||||
{
|
|
||||||
let mut hook_ptr = std::ptr::null_mut();
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
let mut user_data = Box::new(ffi::InterruptHook {
|
let mut user_data = Box::new(ffi::InterruptHook {
|
||||||
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
callback: Box::new(callback),
|
callback: Box::new(callback),
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = unsafe {
|
let err = unsafe {
|
||||||
ffi::uc_hook_add(
|
ffi::uc_hook_add(
|
||||||
self.inner.uc,
|
self.inner.uc,
|
||||||
|
@ -515,7 +524,9 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
unsafe { self.inner.as_mut().get_unchecked_mut() }.intr_hooks.insert(hook_ptr, user_data);
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.intr_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
Ok(hook_ptr)
|
Ok(hook_ptr)
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -523,18 +534,16 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add hook for x86 IN instruction.
|
/// Add hook for x86 IN instruction.
|
||||||
pub fn add_insn_in_hook<F: 'static>(
|
pub fn add_insn_in_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
|
||||||
&mut self,
|
where
|
||||||
callback: F,
|
F: FnMut(UnicornHandle<D>, u32, usize),
|
||||||
) -> Result<ffi::uc_hook, uc_error>
|
{
|
||||||
where F: FnMut(UnicornHandle<D>, u32, usize)
|
|
||||||
{
|
|
||||||
let mut hook_ptr = std::ptr::null_mut();
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
let mut user_data = Box::new(ffi::InstructionInHook {
|
let mut user_data = Box::new(ffi::InstructionInHook {
|
||||||
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
callback: Box::new(callback),
|
callback: Box::new(callback),
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = unsafe {
|
let err = unsafe {
|
||||||
ffi::uc_hook_add(
|
ffi::uc_hook_add(
|
||||||
self.inner.uc,
|
self.inner.uc,
|
||||||
|
@ -548,7 +557,9 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
unsafe { self.inner.as_mut().get_unchecked_mut() }.insn_in_hooks.insert(hook_ptr, user_data);
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.insn_in_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
Ok(hook_ptr)
|
Ok(hook_ptr)
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -556,18 +567,16 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add hook for x86 OUT instruction.
|
/// Add hook for x86 OUT instruction.
|
||||||
pub fn add_insn_out_hook<F: 'static>(
|
pub fn add_insn_out_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
|
||||||
&mut self,
|
where
|
||||||
callback: F,
|
F: FnMut(UnicornHandle<D>, u32, usize, u32),
|
||||||
) -> Result<ffi::uc_hook, uc_error>
|
{
|
||||||
where F: FnMut(UnicornHandle<D>, u32, usize, u32)
|
|
||||||
{
|
|
||||||
let mut hook_ptr = std::ptr::null_mut();
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
let mut user_data = Box::new(ffi::InstructionOutHook {
|
let mut user_data = Box::new(ffi::InstructionOutHook {
|
||||||
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
callback: Box::new(callback),
|
callback: Box::new(callback),
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = unsafe {
|
let err = unsafe {
|
||||||
ffi::uc_hook_add(
|
ffi::uc_hook_add(
|
||||||
self.inner.uc,
|
self.inner.uc,
|
||||||
|
@ -581,7 +590,9 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
unsafe { self.inner.as_mut().get_unchecked_mut() }.insn_out_hooks.insert(hook_ptr, user_data);
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.insn_out_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
Ok(hook_ptr)
|
Ok(hook_ptr)
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -596,14 +607,15 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
end: u64,
|
end: u64,
|
||||||
callback: F,
|
callback: F,
|
||||||
) -> Result<ffi::uc_hook, uc_error>
|
) -> Result<ffi::uc_hook, uc_error>
|
||||||
where F: FnMut(UnicornHandle<D>)
|
where
|
||||||
{
|
F: FnMut(UnicornHandle<D>),
|
||||||
|
{
|
||||||
let mut hook_ptr = std::ptr::null_mut();
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
let mut user_data = Box::new(ffi::InstructionSysHook {
|
let mut user_data = Box::new(ffi::InstructionSysHook {
|
||||||
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
callback: Box::new(callback),
|
callback: Box::new(callback),
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = unsafe {
|
let err = unsafe {
|
||||||
ffi::uc_hook_add(
|
ffi::uc_hook_add(
|
||||||
self.inner.uc,
|
self.inner.uc,
|
||||||
|
@ -617,7 +629,9 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
unsafe { self.inner.as_mut().get_unchecked_mut() }.insn_sys_hooks.insert(hook_ptr, user_data);
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.insn_sys_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
Ok(hook_ptr)
|
Ok(hook_ptr)
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -681,13 +695,15 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate and return an empty Unicorn context.
|
/// Allocate and return an empty Unicorn context.
|
||||||
///
|
///
|
||||||
/// To be populated via context_save.
|
/// To be populated via context_save.
|
||||||
pub fn context_alloc(&self) -> Result<Context, uc_error> {
|
pub fn context_alloc(&self) -> Result<Context, uc_error> {
|
||||||
let mut empty_context: ffi::uc_context = Default::default();
|
let mut empty_context: ffi::uc_context = Default::default();
|
||||||
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut empty_context) };
|
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut empty_context) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
Ok(Context { context: empty_context })
|
Ok(Context {
|
||||||
|
context: empty_context,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
|
@ -704,9 +720,9 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate and return a Context struct initialized with the current CPU context.
|
/// Allocate and return a Context struct initialized with the current CPU context.
|
||||||
///
|
///
|
||||||
/// This can be used for fast rollbacks with context_restore.
|
/// This can be used for fast rollbacks with context_restore.
|
||||||
/// In case of many non-concurrent context saves, use context_alloc and *_save
|
/// In case of many non-concurrent context saves, use context_alloc and *_save
|
||||||
/// individually to avoid unnecessary allocations.
|
/// individually to avoid unnecessary allocations.
|
||||||
pub fn context_init(&self) -> Result<Context, uc_error> {
|
pub fn context_init(&self) -> Result<Context, uc_error> {
|
||||||
let mut new_context: ffi::uc_context = Default::default();
|
let mut new_context: ffi::uc_context = Default::default();
|
||||||
|
@ -716,7 +732,9 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
let err = unsafe { ffi::uc_context_save(self.inner.uc, new_context) };
|
let err = unsafe { ffi::uc_context_save(self.inner.uc, new_context) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
Ok(Context { context: new_context })
|
Ok(Context {
|
||||||
|
context: new_context,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
unsafe { ffi::uc_free(new_context) };
|
unsafe { ffi::uc_free(new_context) };
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -724,7 +742,7 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore a previously saved Unicorn context.
|
/// Restore a previously saved Unicorn context.
|
||||||
///
|
///
|
||||||
/// 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.
|
||||||
|
@ -743,11 +761,12 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
/// is hit. `timeout` specifies a duration in microseconds after which the emulation is
|
/// is hit. `timeout` specifies a duration in microseconds after which the emulation is
|
||||||
/// stopped (infinite execution if set to 0). `count` is the maximum number of instructions
|
/// stopped (infinite execution if set to 0). `count` is the maximum number of instructions
|
||||||
/// to emulate (emulate all the available instructions if set to 0).
|
/// to emulate (emulate all the available instructions if set to 0).
|
||||||
pub fn emu_start(&mut self,
|
pub fn emu_start(
|
||||||
begin: u64,
|
&mut self,
|
||||||
until: u64,
|
begin: u64,
|
||||||
timeout: u64,
|
until: u64,
|
||||||
count: usize
|
timeout: u64,
|
||||||
|
count: usize,
|
||||||
) -> Result<(), uc_error> {
|
) -> Result<(), uc_error> {
|
||||||
let err = unsafe { ffi::uc_emu_start(self.inner.uc, begin, until, timeout, count as _) };
|
let err = unsafe { ffi::uc_emu_start(self.inner.uc, begin, until, timeout, count as _) };
|
||||||
if err == uc_error::OK {
|
if err == uc_error::OK {
|
||||||
|
@ -771,7 +790,7 @@ impl<'a, D> UnicornHandle<'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(&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();
|
||||||
|
@ -782,6 +801,4 @@ impl<'a, D> UnicornHandle<'a, D> {
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,4 +21,4 @@ pub enum RegisterM68K {
|
||||||
D7,
|
D7,
|
||||||
SR,
|
SR,
|
||||||
PC,
|
PC,
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,4 +153,4 @@ pub enum RegisterMIPS {
|
||||||
MPL0 = 134,
|
MPL0 = 134,
|
||||||
MPL1 = 135,
|
MPL1 = 135,
|
||||||
MPL2 = 136,
|
MPL2 = 136,
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,5 +38,5 @@ pub enum RegisterPPC {
|
||||||
GPR28 = 30,
|
GPR28 = 30,
|
||||||
GPR29 = 31,
|
GPR29 = 31,
|
||||||
GPR30 = 32,
|
GPR30 = 32,
|
||||||
GPR31 = 33
|
GPR31 = 33,
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,4 +91,4 @@ pub enum RegisterSPARC {
|
||||||
Y = 86,
|
Y = 86,
|
||||||
XCC = 87,
|
XCC = 87,
|
||||||
PC = 88,
|
PC = 88,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ pub const API_MINOR: u64 = 0;
|
||||||
pub const VERSION_MAJOR: u64 = 1;
|
pub const VERSION_MAJOR: u64 = 1;
|
||||||
pub const VERSION_MINOR: u64 = 0;
|
pub const VERSION_MINOR: u64 = 0;
|
||||||
pub const VERSION_EXTRA: u64 = 2;
|
pub const VERSION_EXTRA: u64 = 2;
|
||||||
pub const SECOND_SCALE: u64 = 1000000;
|
pub const SECOND_SCALE: u64 = 1_000_000;
|
||||||
pub const MILISECOND_SCALE: u64 = 1000;
|
pub const MILISECOND_SCALE: u64 = 1_000;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
@ -51,32 +51,40 @@ pub enum MemType {
|
||||||
READ_AFTER = 25,
|
READ_AFTER = 25,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(i32)]
|
bitflags! {
|
||||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
#[repr(C)]
|
||||||
pub enum HookType {
|
pub struct HookType: i32 {
|
||||||
INTR = 1,
|
const INTR = 1;
|
||||||
INSN = 2,
|
const INSN = 2;
|
||||||
CODE = 4,
|
const CODE = 4;
|
||||||
BLOCK = 8,
|
const BLOCK = 8;
|
||||||
MEM_READ_UNMAPPED = 16,
|
|
||||||
MEM_WRITE_UNMAPPED = 32,
|
const MEM_READ_UNMAPPED = 0x10;
|
||||||
MEM_FETCH_UNMAPPED = 64,
|
const MEM_WRITE_UNMAPPED = 0x20;
|
||||||
MEM_READ_PROT = 128,
|
const MEM_FETCH_UNMAPPED = 0x40;
|
||||||
MEM_WRITE_PROT = 256,
|
const MEM_UNMAPPED = Self::MEM_READ_UNMAPPED.bits | Self::MEM_WRITE_UNMAPPED.bits | Self::MEM_FETCH_UNMAPPED.bits;
|
||||||
MEM_FETCH_PROT = 512,
|
|
||||||
MEM_READ = 1024,
|
const MEM_READ_PROT = 0x80;
|
||||||
MEM_WRITE = 2048,
|
const MEM_WRITE_PROT = 0x100;
|
||||||
MEM_FETCH = 4096,
|
const MEM_FETCH_PROT = 0x200;
|
||||||
MEM_READ_AFTER = 8192,
|
const MEM_PROT = Self::MEM_READ_PROT.bits | Self::MEM_WRITE_PROT.bits | Self::MEM_FETCH_PROT.bits;
|
||||||
INSN_INVALID = 16384,
|
|
||||||
MEM_UNMAPPED = 112,
|
const MEM_READ = 0x400;
|
||||||
MEM_PROT = 896,
|
const MEM_WRITE = 0x800;
|
||||||
MEM_READ_INVALID = 144,
|
const MEM_FETCH = 0x1000;
|
||||||
MEM_WRITE_INVALID = 288,
|
const MEM_VALID = Self::MEM_READ.bits | Self::MEM_WRITE.bits | Self::MEM_FETCH.bits;
|
||||||
MEM_FETCH_INVALID = 576,
|
|
||||||
MEM_INVALID = 1008,
|
const MEM_READ_AFTER = 0x2000;
|
||||||
MEM_VALID = 7168,
|
|
||||||
MEM_ALL = 8176,
|
const INSN_INVALID = 0x4000;
|
||||||
|
|
||||||
|
const MEM_READ_INVALID = Self::MEM_READ_UNMAPPED.bits | Self::MEM_READ_PROT.bits;
|
||||||
|
const MEM_WRITE_INVALID = Self::MEM_WRITE_UNMAPPED.bits | Self::MEM_WRITE_PROT.bits;
|
||||||
|
const MEM_FETCH_INVALID = Self::MEM_FETCH_UNMAPPED.bits | Self::MEM_FETCH_PROT.bits;
|
||||||
|
const MEM_INVALID = Self::MEM_READ_INVALID.bits | Self::MEM_WRITE_INVALID.bits | Self::MEM_FETCH_INVALID.bits;
|
||||||
|
|
||||||
|
const MEM_ALL = Self::MEM_VALID.bits | Self::MEM_INVALID.bits;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -94,7 +102,7 @@ pub struct Permission : u32 {
|
||||||
const READ = 1;
|
const READ = 1;
|
||||||
const WRITE = 2;
|
const WRITE = 2;
|
||||||
const EXEC = 4;
|
const EXEC = 4;
|
||||||
const ALL = 7;
|
const ALL = Self::READ.bits | Self::WRITE.bits | Self::EXEC.bits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,47 +127,32 @@ pub enum Arch {
|
||||||
MAX = 8,
|
MAX = 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
bitflags! {
|
||||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
#[repr(C)]
|
||||||
pub enum Mode {
|
pub struct Mode: i32 {
|
||||||
|
const LITTLE_ENDIAN = 0;
|
||||||
|
const BIG_ENDIAN = 0x4000_0000;
|
||||||
|
|
||||||
LITTLE_ENDIAN = 0,
|
const ARM = 0;
|
||||||
BIG_ENDIAN = 1073741824,
|
const THUMB = 0x10;
|
||||||
|
const MCLASS = 0x20;
|
||||||
// use LITTLE_ENDIAN.
|
const V8 = 0x40;
|
||||||
// MODE_ARM = 0,
|
const ARM926 = 0x80;
|
||||||
THUMB = 16,
|
const ARM946 = 0x100;
|
||||||
MCLASS = 32,
|
const ARM1176 = 0x200;
|
||||||
V8 = 64,
|
const MICRO = Self::THUMB.bits;
|
||||||
ARM926 = 128,
|
const MIPS3 = Self::MCLASS.bits;
|
||||||
ARM946 = 256,
|
const MIPS32R6 = Self::V8.bits;
|
||||||
ARM1176 = 512,
|
const MIPS32 = 4;
|
||||||
// (assoc) MICRO = 16,
|
const MIPS64 = 8;
|
||||||
// (assoc) MIPS3 = 32,
|
const MODE_16 = 2;
|
||||||
// (assoc) MIPS32R6 = 64,
|
const MODE_32 = Self::MIPS32.bits;
|
||||||
MIPS32 = 4,
|
const MODE_64 = Self::MIPS64.bits;
|
||||||
MIPS64 = 8,
|
const PPC32 = Self::MIPS32.bits;
|
||||||
MODE_16 = 2,
|
const PPC64 = Self::MIPS64.bits;
|
||||||
// (assoc) MODE_32 = 4,
|
const QPX = Self::THUMB.bits;
|
||||||
// (assoc) MODE_64 = 8,
|
const SPARC32 = Self::MIPS32.bits;
|
||||||
// (assoc) PPC32 = 4,
|
const SPARC64 = Self::MIPS64.bits;
|
||||||
// (assoc) PPC64 = 8,
|
const V9 = Self::THUMB.bits;
|
||||||
// (assoc) QPX = 16,
|
}
|
||||||
// (assoc) SPARC32 = 4,
|
|
||||||
// (assoc) SPARC64 = 8,
|
|
||||||
// (assoc) V9 = 16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
|
||||||
pub const MICRO: Mode = Mode::THUMB;
|
|
||||||
pub const MIPS3: Mode = Mode::MCLASS;
|
|
||||||
pub const MIPS32R6: Mode = Mode::V8;
|
|
||||||
pub const MODE_32: Mode = Mode::MIPS32;
|
|
||||||
pub const MODE_64: Mode = Mode::MIPS64;
|
|
||||||
pub const PPC32: Mode = Mode::MIPS32;
|
|
||||||
pub const PPC64: Mode = Mode::MIPS64;
|
|
||||||
pub const QPX: Mode = Mode::THUMB;
|
|
||||||
pub const SPARC32: Mode = Mode::MIPS32;
|
|
||||||
pub const SPARC64: Mode = Mode::MIPS64;
|
|
||||||
pub const V9: Mode = Mode::THUMB;
|
|
||||||
}
|
|
|
@ -1,19 +1,18 @@
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
use capstone::prelude::*;
|
|
||||||
use super::arm::RegisterARM;
|
use super::arm::RegisterARM;
|
||||||
use super::arm64::RegisterARM64;
|
use super::arm64::RegisterARM64;
|
||||||
use super::x86::RegisterX86;
|
|
||||||
use super::sparc::RegisterSPARC;
|
|
||||||
use super::mips::RegisterMIPS;
|
|
||||||
use super::m68k::RegisterM68K;
|
use super::m68k::RegisterM68K;
|
||||||
use super::{Permission, Mode, Arch, HookType, MemType, uc_error};
|
use super::mips::RegisterMIPS;
|
||||||
use std::ptr;
|
use super::sparc::RegisterSPARC;
|
||||||
|
use super::x86::RegisterX86;
|
||||||
|
use super::{uc_error, Arch, HookType, MemType, Mode, Permission};
|
||||||
|
use capstone::prelude::*;
|
||||||
|
use libc::{c_void, mmap, size_t, MAP_ANON, MAP_PRIVATE, PROT_READ, PROT_WRITE};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use libc::{mmap, c_void, size_t, MAP_ANON, MAP_PRIVATE,PROT_READ,PROT_WRITE};
|
use std::ptr;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
|
@ -22,7 +21,6 @@ pub struct Chunk {
|
||||||
pub freed: bool,
|
pub freed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Heap {
|
pub struct Heap {
|
||||||
pub real_base: *mut c_void,
|
pub real_base: *mut c_void,
|
||||||
|
@ -34,77 +32,122 @@ pub struct Heap {
|
||||||
pub unalloc_hook: super::ffi::uc_hook,
|
pub unalloc_hook: super::ffi::uc_hook,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Hooks (parts of the) code segment to display register info and the current instruction.
|
/// Hooks (parts of the) code segment to display register info and the current instruction.
|
||||||
pub fn add_debug_prints_ARM<D>(uc: &mut super::UnicornHandle<D>, code_start: u64, code_end: u64) {
|
pub fn add_debug_prints_ARM<D>(uc: &mut super::UnicornHandle<D>, code_start: u64, code_end: u64) {
|
||||||
let cs_arm: Capstone = Capstone::new()
|
let cs_arm: Capstone = Capstone::new()
|
||||||
.arm()
|
.arm()
|
||||||
.mode(arch::arm::ArchMode::Arm)
|
.mode(arch::arm::ArchMode::Arm)
|
||||||
.detail(true)
|
.detail(true)
|
||||||
.build().expect("failed to create capstone for ARM");
|
.build()
|
||||||
|
.expect("failed to create capstone for ARM");
|
||||||
|
|
||||||
let cs_thumb: Capstone = Capstone::new()
|
let cs_thumb: Capstone = Capstone::new()
|
||||||
.arm()
|
.arm()
|
||||||
.mode(arch::arm::ArchMode::Thumb)
|
.mode(arch::arm::ArchMode::Thumb)
|
||||||
.detail(true)
|
.detail(true)
|
||||||
.build().expect("failed to create capstone for thumb");
|
.build()
|
||||||
|
.expect("failed to create capstone for thumb");
|
||||||
|
|
||||||
let callback = Box::new(move |uc: super::UnicornHandle<D>, addr: u64, size: u32| {
|
let callback = Box::new(move |uc: super::UnicornHandle<D>, addr: u64, size: u32| {
|
||||||
let sp = uc.reg_read(RegisterARM::SP as i32).expect("failed to read SP");
|
let sp = uc
|
||||||
let lr = uc.reg_read(RegisterARM::LR as i32).expect("failed to read LR");
|
.reg_read(RegisterARM::SP as i32)
|
||||||
let r0 = uc.reg_read(RegisterARM::R0 as i32).expect("failed to read r0");
|
.expect("failed to read SP");
|
||||||
let r1 = uc.reg_read(RegisterARM::R1 as i32).expect("failed to read r1");
|
let lr = uc
|
||||||
let r2 = uc.reg_read(RegisterARM::R2 as i32).expect("failed to read r2");
|
.reg_read(RegisterARM::LR as i32)
|
||||||
let r3 = uc.reg_read(RegisterARM::R3 as i32).expect("failed to read r3");
|
.expect("failed to read LR");
|
||||||
let r4 = uc.reg_read(RegisterARM::R4 as i32).expect("failed to read r4");
|
let r0 = uc
|
||||||
let r5 = uc.reg_read(RegisterARM::R5 as i32).expect("failed to read r5");
|
.reg_read(RegisterARM::R0 as i32)
|
||||||
let r6 = uc.reg_read(RegisterARM::R6 as i32).expect("failed to read r6");
|
.expect("failed to read r0");
|
||||||
let r7 = uc.reg_read(RegisterARM::R7 as i32).expect("failed to read r7");
|
let r1 = uc
|
||||||
let r8 = uc.reg_read(RegisterARM::R8 as i32).expect("failed to read r8");
|
.reg_read(RegisterARM::R1 as i32)
|
||||||
let r9 = uc.reg_read(RegisterARM::R9 as i32).expect("failed to read r9");
|
.expect("failed to read r1");
|
||||||
let r10 = uc.reg_read(RegisterARM::R10 as i32).expect("failed to read r10");
|
let r2 = uc
|
||||||
let r11 = uc.reg_read(RegisterARM::R11 as i32).expect("failed to read r11");
|
.reg_read(RegisterARM::R2 as i32)
|
||||||
|
.expect("failed to read r2");
|
||||||
|
let r3 = uc
|
||||||
|
.reg_read(RegisterARM::R3 as i32)
|
||||||
|
.expect("failed to read r3");
|
||||||
|
let r4 = uc
|
||||||
|
.reg_read(RegisterARM::R4 as i32)
|
||||||
|
.expect("failed to read r4");
|
||||||
|
let r5 = uc
|
||||||
|
.reg_read(RegisterARM::R5 as i32)
|
||||||
|
.expect("failed to read r5");
|
||||||
|
let r6 = uc
|
||||||
|
.reg_read(RegisterARM::R6 as i32)
|
||||||
|
.expect("failed to read r6");
|
||||||
|
let r7 = uc
|
||||||
|
.reg_read(RegisterARM::R7 as i32)
|
||||||
|
.expect("failed to read r7");
|
||||||
|
let r8 = uc
|
||||||
|
.reg_read(RegisterARM::R8 as i32)
|
||||||
|
.expect("failed to read r8");
|
||||||
|
let r9 = uc
|
||||||
|
.reg_read(RegisterARM::R9 as i32)
|
||||||
|
.expect("failed to read r9");
|
||||||
|
let r10 = uc
|
||||||
|
.reg_read(RegisterARM::R10 as i32)
|
||||||
|
.expect("failed to read r10");
|
||||||
|
let r11 = uc
|
||||||
|
.reg_read(RegisterARM::R11 as i32)
|
||||||
|
.expect("failed to read r11");
|
||||||
println!("________________________________________________________________________\n");
|
println!("________________________________________________________________________\n");
|
||||||
println!("$r0: {:#010x} $r1: {:#010x} $r2: {:#010x} $r3: {:#010x}", r0, r1, r2, r3);
|
println!(
|
||||||
println!("$r4: {:#010x} $r5: {:#010x} $r6: {:#010x} $r7: {:#010x}", r4, r5, r6, r7);
|
"$r0: {:#010x} $r1: {:#010x} $r2: {:#010x} $r3: {:#010x}",
|
||||||
println!("$r8: {:#010x} $r9: {:#010x} $r10: {:#010x} $r11: {:#010x}", r8, r9, r10, r11);
|
r0, r1, r2, r3
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"$r4: {:#010x} $r5: {:#010x} $r6: {:#010x} $r7: {:#010x}",
|
||||||
|
r4, r5, r6, r7
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"$r8: {:#010x} $r9: {:#010x} $r10: {:#010x} $r11: {:#010x}",
|
||||||
|
r8, r9, r10, r11
|
||||||
|
);
|
||||||
println!("$sp: {:#010x} $lr: {:#010x}\n", sp, lr);
|
println!("$sp: {:#010x} $lr: {:#010x}\n", sp, lr);
|
||||||
|
|
||||||
// decide which mode (ARM/Thumb) to use for disasm
|
// decide which mode (ARM/Thumb) to use for disasm
|
||||||
let cpsr = uc.reg_read(RegisterARM::CPSR as i32).expect("failed to read CPSR");
|
let cpsr = uc
|
||||||
|
.reg_read(RegisterARM::CPSR as i32)
|
||||||
|
.expect("failed to read CPSR");
|
||||||
let mut buf = vec![0; size as usize];
|
let mut buf = vec![0; size as usize];
|
||||||
uc.mem_read(addr, &mut buf).expect("failed to read opcode from memory");
|
uc.mem_read(addr, &mut buf)
|
||||||
|
.expect("failed to read opcode from memory");
|
||||||
let ins = if cpsr & 0x20 != 0 {
|
let ins = if cpsr & 0x20 != 0 {
|
||||||
cs_thumb.disasm_all(&buf, size as u64)
|
cs_thumb.disasm_all(&buf, size as u64)
|
||||||
} else {
|
} else {
|
||||||
cs_arm.disasm_all(&buf, size as u64)
|
cs_arm.disasm_all(&buf, size as u64)
|
||||||
}.expect(&format!("failed to disasm at addr {:#010x}", addr));
|
}
|
||||||
|
.expect(&format!("failed to disasm at addr {:#010x}", addr));
|
||||||
println!("$pc: {:#010x}", addr);
|
println!("$pc: {:#010x}", addr);
|
||||||
println!("{}", ins);
|
println!("{}", ins);
|
||||||
});
|
});
|
||||||
|
|
||||||
uc.add_code_hook(code_start, code_end, callback).expect("failed to set debug hook");
|
uc.add_code_hook(code_start, code_end, callback)
|
||||||
|
.expect("failed to set debug hook");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new Unicorn instance with an initialized heap and active sanitizer.
|
||||||
/// Returns a new Unicorn instance with an initialized heap and active sanitizer.
|
///
|
||||||
///
|
|
||||||
/// Introduces an accessible way of dynamic memory allocation for emulation and helps
|
/// Introduces an accessible way of dynamic memory allocation for emulation and helps
|
||||||
/// detecting common memory corruption bugs.
|
/// detecting common memory corruption bugs.
|
||||||
/// The allocator makes heavy use of Unicorn hooks for sanitization/ crash amplification
|
/// The allocator makes heavy use of Unicorn hooks for sanitization/ crash amplification
|
||||||
/// and thus introduces some overhead.
|
/// and thus introduces some overhead.
|
||||||
pub fn init_emu_with_heap(arch: Arch,
|
pub fn init_emu_with_heap(
|
||||||
mut size: u32,
|
arch: Arch,
|
||||||
base_addr: u64,
|
mut size: u32,
|
||||||
grow: bool
|
base_addr: u64,
|
||||||
|
grow: bool,
|
||||||
) -> Result<super::Unicorn<RefCell<Heap>>, uc_error> {
|
) -> Result<super::Unicorn<RefCell<Heap>>, uc_error> {
|
||||||
let heap = RefCell::new(Heap {real_base: 0 as _,
|
let heap = RefCell::new(Heap {
|
||||||
uc_base: 0,
|
real_base: 0 as _,
|
||||||
len: 0,
|
uc_base: 0,
|
||||||
grow_dynamically: false,
|
len: 0,
|
||||||
chunk_map: HashMap::new(),
|
grow_dynamically: false,
|
||||||
top: 0,
|
chunk_map: HashMap::new(),
|
||||||
unalloc_hook: 0 as _ });
|
top: 0,
|
||||||
|
unalloc_hook: 0 as _,
|
||||||
|
});
|
||||||
|
|
||||||
let mut unicorn = super::Unicorn::new(arch, Mode::LITTLE_ENDIAN, heap)?;
|
let mut unicorn = super::Unicorn::new(arch, Mode::LITTLE_ENDIAN, heap)?;
|
||||||
let mut uc = unicorn.borrow(); // get handle
|
let mut uc = unicorn.borrow(); // get handle
|
||||||
|
@ -118,19 +161,36 @@ pub fn init_emu_with_heap(arch: Arch,
|
||||||
let null_ptr = ptr::null_mut();
|
let null_ptr = ptr::null_mut();
|
||||||
unsafe {
|
unsafe {
|
||||||
// manually mmap space for heap to know location
|
// manually mmap space for heap to know location
|
||||||
let arena_ptr = mmap(null_ptr, size as usize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
|
let arena_ptr = mmap(
|
||||||
uc.mem_map_ptr(base_addr, size as usize, Permission::READ | Permission::WRITE, arena_ptr)?;
|
null_ptr,
|
||||||
let h = uc.add_mem_hook(HookType::MEM_VALID, base_addr, base_addr + size as u64, Box::new(heap_unalloc))?;
|
size as usize,
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_ANON | MAP_PRIVATE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
uc.mem_map_ptr(
|
||||||
|
base_addr,
|
||||||
|
size as usize,
|
||||||
|
Permission::READ | Permission::WRITE,
|
||||||
|
arena_ptr,
|
||||||
|
)?;
|
||||||
|
let h = uc.add_mem_hook(
|
||||||
|
HookType::MEM_VALID,
|
||||||
|
base_addr,
|
||||||
|
base_addr + size as u64,
|
||||||
|
Box::new(heap_unalloc),
|
||||||
|
)?;
|
||||||
let chunks = HashMap::new();
|
let chunks = HashMap::new();
|
||||||
let heap: &mut Heap = &mut *uc.get_data().borrow_mut();
|
let heap: &mut Heap = &mut *uc.get_data().borrow_mut();
|
||||||
heap.real_base = arena_ptr; // heap pointer in process mem
|
heap.real_base = arena_ptr; // heap pointer in process mem
|
||||||
heap.uc_base = base_addr;
|
heap.uc_base = base_addr;
|
||||||
heap.len = size as usize;
|
heap.len = size as usize;
|
||||||
/*
|
/*
|
||||||
let the heap grow dynamically
|
let the heap grow dynamically
|
||||||
(ATTENTION: There are no guarantees that the heap segment will be continuous in process mem any more)
|
(ATTENTION: There are no guarantees that the heap segment will be continuous in process mem any more)
|
||||||
*/
|
*/
|
||||||
heap.grow_dynamically = grow;
|
heap.grow_dynamically = grow;
|
||||||
heap.chunk_map = chunks;
|
heap.chunk_map = chunks;
|
||||||
heap.top = base_addr; // pointer to top of heap in unicorn mem, increases on allocations
|
heap.top = base_addr; // pointer to top of heap in unicorn mem, increases on allocations
|
||||||
heap.unalloc_hook = h; // hook ID, needed to rearrange hooks on allocations
|
heap.unalloc_hook = h; // hook ID, needed to rearrange hooks on allocations
|
||||||
|
@ -140,12 +200,15 @@ pub fn init_emu_with_heap(arch: Arch,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `malloc` for the utils allocator.
|
/// `malloc` for the utils allocator.
|
||||||
///
|
///
|
||||||
/// Returns a pointer into memory used as heap and applies
|
/// Returns a pointer into memory used as heap and applies
|
||||||
/// canary hooks to detect out-of-bounds accesses.
|
/// canary hooks to detect out-of-bounds accesses.
|
||||||
/// Grows the heap if necessary and if it is configured to, otherwise
|
/// Grows the heap if necessary and if it is configured to, otherwise
|
||||||
/// return WRITE_UNMAPPED if there is no space left.
|
/// return WRITE_UNMAPPED if there is no space left.
|
||||||
pub fn uc_alloc(uc: &mut super::UnicornHandle<RefCell<Heap>>, mut size: u64) -> Result<u64, uc_error> {
|
pub fn uc_alloc(
|
||||||
|
uc: &mut super::UnicornHandle<RefCell<Heap>>,
|
||||||
|
mut size: u64,
|
||||||
|
) -> Result<u64, uc_error> {
|
||||||
// 8 byte aligned
|
// 8 byte aligned
|
||||||
if size % 8 != 0 {
|
if size % 8 != 0 {
|
||||||
size = ((size / 8) + 1) * 8;
|
size = ((size / 8) + 1) * 8;
|
||||||
|
@ -163,7 +226,11 @@ pub fn uc_alloc(uc: &mut super::UnicornHandle<RefCell<Heap>>, mut size: u64) ->
|
||||||
if increase_by % 8 != 0 {
|
if increase_by % 8 != 0 {
|
||||||
increase_by = ((increase_by / 8) + 1) * 8;
|
increase_by = ((increase_by / 8) + 1) * 8;
|
||||||
}
|
}
|
||||||
uc.mem_map(uc_base + len as u64, increase_by, Permission::READ | Permission::WRITE)?;
|
uc.mem_map(
|
||||||
|
uc_base + len as u64,
|
||||||
|
increase_by,
|
||||||
|
Permission::READ | Permission::WRITE,
|
||||||
|
)?;
|
||||||
uc.get_data().borrow_mut().len += increase_by;
|
uc.get_data().borrow_mut().len += increase_by;
|
||||||
len = uc.get_data().borrow_mut().len;
|
len = uc.get_data().borrow_mut().len;
|
||||||
}
|
}
|
||||||
|
@ -171,31 +238,57 @@ pub fn uc_alloc(uc: &mut super::UnicornHandle<RefCell<Heap>>, mut size: u64) ->
|
||||||
|
|
||||||
// canary hooks
|
// canary hooks
|
||||||
uc.add_mem_hook(HookType::MEM_WRITE, addr, addr + 3, Box::new(heap_bo))?;
|
uc.add_mem_hook(HookType::MEM_WRITE, addr, addr + 3, Box::new(heap_bo))?;
|
||||||
uc.add_mem_hook(HookType::MEM_READ, addr, addr + 3, Box::new(heap_oob))?;
|
uc.add_mem_hook(HookType::MEM_READ, addr, addr + 3, Box::new(heap_oob))?;
|
||||||
uc.add_mem_hook(HookType::MEM_WRITE, addr + 4 + size, addr + 4 + size + 3, Box::new(heap_bo))?;
|
uc.add_mem_hook(
|
||||||
uc.add_mem_hook(HookType::MEM_READ, addr + 4 + size, addr + 4 + size + 3, Box::new(heap_oob))?;
|
HookType::MEM_WRITE,
|
||||||
|
addr + 4 + size,
|
||||||
|
addr + 4 + size + 3,
|
||||||
|
Box::new(heap_bo),
|
||||||
|
)?;
|
||||||
|
uc.add_mem_hook(
|
||||||
|
HookType::MEM_READ,
|
||||||
|
addr + 4 + size,
|
||||||
|
addr + 4 + size + 3,
|
||||||
|
Box::new(heap_oob),
|
||||||
|
)?;
|
||||||
|
|
||||||
// add new chunk
|
// add new chunk
|
||||||
let curr_offset = addr + 4 - uc_base;
|
let curr_offset = addr + 4 - uc_base;
|
||||||
let curr_chunk = Chunk {offset: curr_offset, len: size as size_t, freed: false};
|
let curr_chunk = Chunk {
|
||||||
uc.get_data().borrow_mut().chunk_map.insert(addr + 4, curr_chunk);
|
offset: curr_offset,
|
||||||
|
len: size as size_t,
|
||||||
|
freed: false,
|
||||||
|
};
|
||||||
|
uc.get_data()
|
||||||
|
.borrow_mut()
|
||||||
|
.chunk_map
|
||||||
|
.insert(addr + 4, curr_chunk);
|
||||||
let new_top = uc.get_data().borrow_mut().top + size + 8; // canary*2
|
let new_top = uc.get_data().borrow_mut().top + size + 8; // canary*2
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
println!("[+] New Allocation from {:#010x} to {:#010x} (size: {})",
|
println!(
|
||||||
uc.get_data().borrow().top, uc.get_data().borrow().top + size - 1 + 8, size);
|
"[+] New Allocation from {:#010x} to {:#010x} (size: {})",
|
||||||
uc.get_data().borrow_mut().top = new_top;
|
uc.get_data().borrow().top,
|
||||||
|
uc.get_data().borrow().top + size - 1 + 8,
|
||||||
|
size
|
||||||
|
);
|
||||||
|
uc.get_data().borrow_mut().top = new_top;
|
||||||
|
|
||||||
// adjust oob hooks
|
// adjust oob hooks
|
||||||
let old_h = uc.get_data().borrow_mut().unalloc_hook;
|
let old_h = uc.get_data().borrow_mut().unalloc_hook;
|
||||||
uc.remove_hook(old_h)?;
|
uc.remove_hook(old_h)?;
|
||||||
let new_h = uc.add_mem_hook(HookType::MEM_VALID, new_top, uc_base + len as u64, Box::new(heap_unalloc))?;
|
let new_h = uc.add_mem_hook(
|
||||||
|
HookType::MEM_VALID,
|
||||||
|
new_top,
|
||||||
|
uc_base + len as u64,
|
||||||
|
Box::new(heap_unalloc),
|
||||||
|
)?;
|
||||||
uc.get_data().borrow_mut().unalloc_hook = new_h;
|
uc.get_data().borrow_mut().unalloc_hook = new_h;
|
||||||
|
|
||||||
return Ok(addr + 4);
|
return Ok(addr + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `free` for the utils allocator.
|
/// `free` for the utils allocator.
|
||||||
///
|
///
|
||||||
/// Marks the chunk to be freed to detect double-frees later on
|
/// Marks the chunk to be freed to detect double-frees later on
|
||||||
/// and places sanitization hooks over the freed region to detect
|
/// and places sanitization hooks over the freed region to detect
|
||||||
/// use-after-frees.
|
/// use-after-frees.
|
||||||
|
@ -208,17 +301,30 @@ pub fn uc_free(uc: &mut super::UnicornHandle<RefCell<Heap>>, ptr: u64) -> Result
|
||||||
let mut chunk_size = 0;
|
let mut chunk_size = 0;
|
||||||
{
|
{
|
||||||
let mut heap = uc.get_data().borrow_mut();
|
let mut heap = uc.get_data().borrow_mut();
|
||||||
let curr_chunk = heap.chunk_map.get_mut(&ptr).expect("failed to find requested chunk on heap");
|
let curr_chunk = heap
|
||||||
|
.chunk_map
|
||||||
|
.get_mut(&ptr)
|
||||||
|
.expect("failed to find requested chunk on heap");
|
||||||
chunk_size = curr_chunk.len as u64;
|
chunk_size = curr_chunk.len as u64;
|
||||||
curr_chunk.freed = true;
|
curr_chunk.freed = true;
|
||||||
}
|
}
|
||||||
uc.add_mem_hook(HookType::MEM_VALID, ptr, ptr + chunk_size - 1, Box::new(heap_uaf))?;
|
uc.add_mem_hook(
|
||||||
|
HookType::MEM_VALID,
|
||||||
|
ptr,
|
||||||
|
ptr + chunk_size - 1,
|
||||||
|
Box::new(heap_uaf),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn heap_unalloc(
|
||||||
fn heap_unalloc(uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) {
|
uc: super::UnicornHandle<RefCell<Heap>>,
|
||||||
|
_mem_type: MemType,
|
||||||
|
addr: u64,
|
||||||
|
_size: usize,
|
||||||
|
_val: i64,
|
||||||
|
) {
|
||||||
let arch = uc.get_arch();
|
let arch = uc.get_arch();
|
||||||
let reg = match arch {
|
let reg = match arch {
|
||||||
Arch::X86 => RegisterX86::RIP as i32,
|
Arch::X86 => RegisterX86::RIP as i32,
|
||||||
|
@ -227,15 +333,20 @@ fn heap_unalloc(uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, add
|
||||||
Arch::MIPS => RegisterMIPS::PC as i32,
|
Arch::MIPS => RegisterMIPS::PC as i32,
|
||||||
Arch::SPARC => RegisterSPARC::PC as i32,
|
Arch::SPARC => RegisterSPARC::PC as i32,
|
||||||
Arch::M68K => RegisterM68K::PC as i32,
|
Arch::M68K => RegisterM68K::PC as i32,
|
||||||
_ => panic!("Arch not yet supported by unicorn::utils module")
|
_ => panic!("Arch not yet supported by unicorn::utils module"),
|
||||||
};
|
};
|
||||||
let pc = uc.reg_read(reg).expect("failed to read pc");
|
let pc = uc.reg_read(reg).expect("failed to read pc");
|
||||||
panic!("ERROR: unicorn-rs Sanitizer: Heap out-of-bounds access of unallocated memory on addr {:#0x}, $pc: {:#010x}",
|
panic!("ERROR: unicorn-rs Sanitizer: Heap out-of-bounds access of unallocated memory on addr {:#0x}, $pc: {:#010x}",
|
||||||
addr, pc);
|
addr, pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn heap_oob(
|
||||||
fn heap_oob(uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) {
|
uc: super::UnicornHandle<RefCell<Heap>>,
|
||||||
|
_mem_type: MemType,
|
||||||
|
addr: u64,
|
||||||
|
_size: usize,
|
||||||
|
_val: i64,
|
||||||
|
) {
|
||||||
let arch = uc.get_arch();
|
let arch = uc.get_arch();
|
||||||
let reg = match arch {
|
let reg = match arch {
|
||||||
Arch::X86 => RegisterX86::RIP as i32,
|
Arch::X86 => RegisterX86::RIP as i32,
|
||||||
|
@ -244,14 +355,22 @@ fn heap_oob(uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u
|
||||||
Arch::MIPS => RegisterMIPS::PC as i32,
|
Arch::MIPS => RegisterMIPS::PC as i32,
|
||||||
Arch::SPARC => RegisterSPARC::PC as i32,
|
Arch::SPARC => RegisterSPARC::PC as i32,
|
||||||
Arch::M68K => RegisterM68K::PC as i32,
|
Arch::M68K => RegisterM68K::PC as i32,
|
||||||
_ => panic!("Arch not yet supported by unicorn::utils module")
|
_ => panic!("Arch not yet supported by unicorn::utils module"),
|
||||||
};
|
};
|
||||||
let pc = uc.reg_read(reg).expect("failed to read pc");
|
let pc = uc.reg_read(reg).expect("failed to read pc");
|
||||||
panic!("ERROR: unicorn-rs Sanitizer: Heap out-of-bounds read on addr {:#0x}, $pc: {:#010x}", addr, pc);
|
panic!(
|
||||||
|
"ERROR: unicorn-rs Sanitizer: Heap out-of-bounds read on addr {:#0x}, $pc: {:#010x}",
|
||||||
|
addr, pc
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn heap_bo(
|
||||||
fn heap_bo (uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) {
|
uc: super::UnicornHandle<RefCell<Heap>>,
|
||||||
|
_mem_type: MemType,
|
||||||
|
addr: u64,
|
||||||
|
_size: usize,
|
||||||
|
_val: i64,
|
||||||
|
) {
|
||||||
let arch = uc.get_arch();
|
let arch = uc.get_arch();
|
||||||
let reg = match arch {
|
let reg = match arch {
|
||||||
Arch::X86 => RegisterX86::RIP as i32,
|
Arch::X86 => RegisterX86::RIP as i32,
|
||||||
|
@ -260,14 +379,22 @@ fn heap_bo (uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u
|
||||||
Arch::MIPS => RegisterMIPS::PC as i32,
|
Arch::MIPS => RegisterMIPS::PC as i32,
|
||||||
Arch::SPARC => RegisterSPARC::PC as i32,
|
Arch::SPARC => RegisterSPARC::PC as i32,
|
||||||
Arch::M68K => RegisterM68K::PC as i32,
|
Arch::M68K => RegisterM68K::PC as i32,
|
||||||
_ => panic!("Arch not yet supported by unicorn::utils module")
|
_ => panic!("Arch not yet supported by unicorn::utils module"),
|
||||||
};
|
};
|
||||||
let pc = uc.reg_read(reg).expect("failed to read pc");
|
let pc = uc.reg_read(reg).expect("failed to read pc");
|
||||||
panic!("ERROR: unicorn-rs Sanitizer: Heap buffer-overflow on addr {:#0x}, $pc: {:#010x}", addr, pc);
|
panic!(
|
||||||
|
"ERROR: unicorn-rs Sanitizer: Heap buffer-overflow on addr {:#0x}, $pc: {:#010x}",
|
||||||
|
addr, pc
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn heap_uaf(
|
||||||
fn heap_uaf (uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr: u64, _size: usize, _val: i64) {
|
uc: super::UnicornHandle<RefCell<Heap>>,
|
||||||
|
_mem_type: MemType,
|
||||||
|
addr: u64,
|
||||||
|
_size: usize,
|
||||||
|
_val: i64,
|
||||||
|
) {
|
||||||
let arch = uc.get_arch();
|
let arch = uc.get_arch();
|
||||||
let reg = match arch {
|
let reg = match arch {
|
||||||
Arch::X86 => RegisterX86::RIP as i32,
|
Arch::X86 => RegisterX86::RIP as i32,
|
||||||
|
@ -276,14 +403,15 @@ fn heap_uaf (uc: super::UnicornHandle<RefCell<Heap>>, _mem_type: MemType, addr:
|
||||||
Arch::MIPS => RegisterMIPS::PC as i32,
|
Arch::MIPS => RegisterMIPS::PC as i32,
|
||||||
Arch::SPARC => RegisterSPARC::PC as i32,
|
Arch::SPARC => RegisterSPARC::PC as i32,
|
||||||
Arch::M68K => RegisterM68K::PC as i32,
|
Arch::M68K => RegisterM68K::PC as i32,
|
||||||
_ => panic!("Arch not yet supported by unicorn::utils module")
|
_ => panic!("Arch not yet supported by unicorn::utils module"),
|
||||||
};
|
};
|
||||||
let pc = uc.reg_read(reg).expect("failed to read pc");
|
let pc = uc.reg_read(reg).expect("failed to read pc");
|
||||||
panic!("ERROR: unicorn-rs Sanitizer: Heap use-after-free on addr {:#0x}, $pc: {:#010x}", addr, pc);
|
panic!(
|
||||||
|
"ERROR: unicorn-rs Sanitizer: Heap use-after-free on addr {:#0x}, $pc: {:#010x}",
|
||||||
|
addr, pc
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn vmmap<D>(uc: &mut super::UnicornHandle<D>) {
|
pub fn vmmap<D>(uc: &mut super::UnicornHandle<D>) {
|
||||||
let regions = uc
|
let regions = uc
|
||||||
.mem_regions()
|
.mem_regions()
|
||||||
|
|
|
@ -278,4 +278,4 @@ pub struct X86Mmr {
|
||||||
pub base: u64,
|
pub base: u64,
|
||||||
pub limit: u32,
|
pub limit: u32,
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use unicorn::{RegisterARM, RegisterX86, InsnSysX86, RegisterMIPS, RegisterPPC};
|
use unicorn::unicorn_const::{uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE};
|
||||||
use unicorn::unicorn_const::{Mode, Arch, Permission, MemType, HookType, SECOND_SCALE, uc_error};
|
use unicorn::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86};
|
||||||
|
|
||||||
pub static X86_REGISTERS: [RegisterX86; 145] = [
|
pub static X86_REGISTERS: [RegisterX86; 145] = [
|
||||||
RegisterX86::AH,
|
RegisterX86::AH,
|
||||||
|
@ -159,7 +159,8 @@ type Unicorn<'a> = unicorn::UnicornHandle<'a, u32>;
|
||||||
fn emulate_x86() {
|
fn emulate_x86() {
|
||||||
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 123), Ok(()));
|
assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 123), Ok(()));
|
||||||
assert_eq!(emu.reg_read(RegisterX86::EAX as i32), Ok(123));
|
assert_eq!(emu.reg_read(RegisterX86::EAX as i32), Ok(123));
|
||||||
|
@ -170,10 +171,7 @@ fn emulate_x86() {
|
||||||
(Err(uc_error::WRITE_UNMAPPED))
|
(Err(uc_error::WRITE_UNMAPPED))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
emu.mem_read_as_vec(0x1000, x86_code32.len()),
|
emu.mem_read_as_vec(0x1000, x86_code32.len()),
|
||||||
|
@ -212,12 +210,10 @@ fn x86_code_callback() {
|
||||||
|
|
||||||
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
let hook = emu
|
let hook = emu
|
||||||
|
@ -245,12 +241,10 @@ fn x86_intr_callback() {
|
||||||
|
|
||||||
let x86_code32: Vec<u8> = vec![0xcd, 0x80]; // INT 0x80;
|
let x86_code32: Vec<u8> = vec![0xcd, 0x80]; // INT 0x80;
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
let hook = emu
|
let hook = emu
|
||||||
|
@ -277,19 +271,17 @@ 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 = move |_: Unicorn<'_>,
|
let callback =
|
||||||
mem_type: MemType,
|
move |_: Unicorn<'_>, mem_type: MemType, address: u64, size: usize, value: i64| {
|
||||||
address: u64,
|
let mut mems = callback_mems.borrow_mut();
|
||||||
size: usize,
|
mems.push(MemExpectation(mem_type, address, size, value));
|
||||||
value: i64| {
|
};
|
||||||
let mut mems = callback_mems.borrow_mut();
|
|
||||||
mems.push(MemExpectation(mem_type, address, size, value));
|
|
||||||
};
|
|
||||||
|
|
||||||
// mov eax, 0xdeadbeef;
|
// mov eax, 0xdeadbeef;
|
||||||
// mov [0x2000], eax;
|
// mov [0x2000], eax;
|
||||||
|
@ -298,12 +290,10 @@ fn x86_mem_callback() {
|
||||||
0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xA3, 0x00, 0x20, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x01, 0x00,
|
0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xA3, 0x00, 0x20, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x01, 0x00,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
let hook = emu
|
let hook = emu
|
||||||
|
@ -338,15 +328,15 @@ fn x86_insn_in_callback() {
|
||||||
|
|
||||||
let x86_code32: Vec<u8> = vec![0xe5, 0x10]; // IN eax, 0x10;
|
let x86_code32: Vec<u8> = vec![0xe5, 0x10]; // IN eax, 0x10;
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
let hook = emu.add_insn_in_hook(callback).expect("failed to add in hook");
|
let hook = emu
|
||||||
|
.add_insn_in_hook(callback)
|
||||||
|
.expect("failed to add in hook");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
emu.emu_start(
|
emu.emu_start(
|
||||||
|
@ -375,15 +365,15 @@ fn x86_insn_out_callback() {
|
||||||
|
|
||||||
let x86_code32: Vec<u8> = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al;
|
let x86_code32: Vec<u8> = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al;
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
let hook = emu.add_insn_out_hook(callback).expect("failed to add out hook");
|
let hook = emu
|
||||||
|
.add_insn_out_hook(callback)
|
||||||
|
.expect("failed to add out hook");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
emu.emu_start(
|
emu.emu_start(
|
||||||
|
@ -417,15 +407,15 @@ fn x86_insn_sys_callback() {
|
||||||
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05,
|
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_64, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_64, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
|
||||||
|
|
||||||
let hook = emu.add_insn_sys_hook(InsnSysX86::SYSCALL, 1, 0, callback).expect("failed to add syscall hook");
|
let hook = emu
|
||||||
|
.add_insn_sys_hook(InsnSysX86::SYSCALL, 1, 0, callback)
|
||||||
|
.expect("failed to add syscall hook");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
emu.emu_start(
|
emu.emu_start(
|
||||||
|
@ -444,7 +434,8 @@ fn x86_insn_sys_callback() {
|
||||||
fn emulate_arm() {
|
fn emulate_arm() {
|
||||||
let arm_code32: Vec<u8> = vec![0x83, 0xb0]; // sub sp, #0xc
|
let arm_code32: Vec<u8> = vec![0x83, 0xb0]; // sub sp, #0xc
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::ARM, Mode::THUMB, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::ARM, Mode::THUMB, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(emu.reg_write(RegisterARM::R1 as i32, 123), Ok(()));
|
assert_eq!(emu.reg_write(RegisterARM::R1 as i32, 123), Ok(()));
|
||||||
assert_eq!(emu.reg_read(RegisterARM::R1 as i32), Ok(123));
|
assert_eq!(emu.reg_read(RegisterARM::R1 as i32), Ok(123));
|
||||||
|
@ -455,10 +446,7 @@ fn emulate_arm() {
|
||||||
(Err(uc_error::WRITE_UNMAPPED))
|
(Err(uc_error::WRITE_UNMAPPED))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &arm_code32), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &arm_code32), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
emu.mem_read_as_vec(0x1000, arm_code32.len()),
|
emu.mem_read_as_vec(0x1000, arm_code32.len()),
|
||||||
|
@ -487,12 +475,10 @@ fn emulate_arm() {
|
||||||
fn emulate_mips() {
|
fn emulate_mips() {
|
||||||
let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456;
|
let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456;
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::MIPS, Mode::MODE_32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::MIPS, Mode::MODE_32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &mips_code32), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &mips_code32), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
emu.mem_read_as_vec(0x1000, mips_code32.len()),
|
emu.mem_read_as_vec(0x1000, mips_code32.len()),
|
||||||
|
@ -515,12 +501,10 @@ fn emulate_mips() {
|
||||||
fn emulate_ppc() {
|
fn emulate_ppc() {
|
||||||
let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3
|
let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::PPC, Mode::PPC32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::PPC, Mode::PPC32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &ppc_code32), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &ppc_code32), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
emu.mem_read_as_vec(0x1000, ppc_code32.len()),
|
emu.mem_read_as_vec(0x1000, ppc_code32.len()),
|
||||||
|
@ -542,12 +526,10 @@ fn emulate_ppc() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mem_unmapping() {
|
fn mem_unmapping() {
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
|
assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,7 +539,8 @@ fn mem_map_ptr() {
|
||||||
let mut mem: [u8; 4000] = [0; 4000];
|
let mut mem: [u8; 4000] = [0; 4000];
|
||||||
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
|
|
||||||
// Attempt to write to memory before mapping it.
|
// Attempt to write to memory before mapping it.
|
||||||
|
@ -635,12 +618,10 @@ fn x86_context_save_and_restore() {
|
||||||
let x86_code: Vec<u8> = vec![
|
let x86_code: Vec<u8> = vec![
|
||||||
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05,
|
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05,
|
||||||
];
|
];
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, mode, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, mode, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
|
||||||
let _ = emu.emu_start(
|
let _ = emu.emu_start(
|
||||||
0x1000,
|
0x1000,
|
||||||
|
@ -654,12 +635,16 @@ fn x86_context_save_and_restore() {
|
||||||
let context = context.unwrap();
|
let context = context.unwrap();
|
||||||
|
|
||||||
/* and create a new emulator, into which we will "restore" that context */
|
/* and create a new emulator, into which we will "restore" that context */
|
||||||
let mut unicorn2 = unicorn::Unicorn::new(Arch::X86, mode, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn2 = unicorn::Unicorn::new(Arch::X86, mode, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let emu2 = unicorn2.borrow();
|
let emu2 = unicorn2.borrow();
|
||||||
assert_eq!(emu2.context_restore(&context), Ok(()));
|
assert_eq!(emu2.context_restore(&context), Ok(()));
|
||||||
for register in X86_REGISTERS.iter() {
|
for register in X86_REGISTERS.iter() {
|
||||||
println!("Testing register {:?}", register);
|
println!("Testing register {:?}", register);
|
||||||
assert_eq!(emu2.reg_read(*register as i32), emu.reg_read(*register as i32));
|
assert_eq!(
|
||||||
|
emu2.reg_read(*register as i32),
|
||||||
|
emu.reg_read(*register as i32)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -668,7 +653,7 @@ fn x86_context_save_and_restore() {
|
||||||
fn x86_block_callback() {
|
fn x86_block_callback() {
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
struct BlockExpectation(u64, u32);
|
struct BlockExpectation(u64, u32);
|
||||||
let expects = vec![BlockExpectation(0x1000, 2)];
|
let expects = vec![BlockExpectation(0x1000, 2), BlockExpectation(0x1000, 2)];
|
||||||
let blocks: Vec<BlockExpectation> = Vec::new();
|
let blocks: Vec<BlockExpectation> = Vec::new();
|
||||||
let blocks_cell = Rc::new(RefCell::new(blocks));
|
let blocks_cell = Rc::new(RefCell::new(blocks));
|
||||||
|
|
||||||
|
@ -680,16 +665,19 @@ fn x86_block_callback() {
|
||||||
|
|
||||||
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
||||||
|
|
||||||
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0).expect("failed to initialize unicorn instance");
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32, 0)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
let mut emu = unicorn.borrow();
|
let mut emu = unicorn.borrow();
|
||||||
assert_eq!(
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
emu.mem_map(0x1000, 0x4000, Permission::ALL),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
let hook = emu.add_block_hook(callback).expect("failed to add block hook");
|
let hook = emu
|
||||||
assert_eq!(emu.emu_start(0x1000, 0x1002, 10 * SECOND_SCALE, 1000), Ok(()));
|
.add_block_hook(callback)
|
||||||
|
.expect("failed to add block hook");
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(0x1000, 0x1002, 10 * SECOND_SCALE, 1000),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
assert_eq!(expects, *blocks_cell.borrow());
|
assert_eq!(expects, *blocks_cell.borrow());
|
||||||
assert_eq!(emu.remove_hook(hook), Ok(()));
|
assert_eq!(emu.remove_hook(hook), Ok(()));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue