mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-27 07:11:01 +00:00
Support violation hooks
This commit is contained in:
@@ -9,6 +9,7 @@ extern "C"
|
||||
{
|
||||
using raw_func = void(void*);
|
||||
using ptr_func = void(void*, uint64_t);
|
||||
using violation_func = int32_t(void*, uint64_t address, uint8_t operation, int32_t unmapped);
|
||||
using data_accessor_func = void(void* user, const void* data, size_t length);
|
||||
|
||||
using icicle_mmio_read_func = void(void* user, uint64_t address, size_t length, void* data);
|
||||
@@ -26,6 +27,7 @@ extern "C"
|
||||
int32_t icicle_restore_registers(icicle_emulator*, const void* data, size_t length);
|
||||
uint32_t icicle_add_syscall_hook(icicle_emulator*, raw_func* callback, void* data);
|
||||
uint32_t icicle_add_execution_hook(icicle_emulator*, ptr_func* callback, void* data);
|
||||
uint32_t icicle_add_violation_hook(icicle_emulator*, violation_func* callback, void* data);
|
||||
void icicle_remove_syscall_hook(icicle_emulator*, uint32_t id);
|
||||
size_t icicle_read_register(icicle_emulator*, int reg, void* data, size_t length);
|
||||
size_t icicle_write_register(icicle_emulator*, int reg, const void* data, size_t length);
|
||||
@@ -46,13 +48,36 @@ namespace icicle
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct function_object : std::function<T>, utils::object
|
||||
emulator_hook* wrap_hook(const uint32_t id)
|
||||
{
|
||||
using std::function<T>::function;
|
||||
return reinterpret_cast<emulator_hook*>(static_cast<size_t>(id));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct function_object : utils::object
|
||||
{
|
||||
std::function<T> func{};
|
||||
|
||||
function_object(std::function<T> f = {})
|
||||
: func(std::move(f))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args) const
|
||||
{
|
||||
return this->func.operator()(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
~function_object() override = default;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::unique_ptr<function_object<T>> make_function_object(std::function<T> func)
|
||||
{
|
||||
return std::make_unique<function_object<T>>(std::move(func));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::unique_ptr<utils::object> wrap_shared(std::shared_ptr<T> shared_ptr)
|
||||
{
|
||||
@@ -242,18 +267,18 @@ namespace icicle
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto callback_store = std::make_unique<function_object<void()>>([c = std::move(callback)] {
|
||||
(void)c(); //
|
||||
});
|
||||
auto obj = make_function_object(std::move(callback));
|
||||
auto* ptr = obj.get();
|
||||
|
||||
const auto invoker = +[](void* cb) {
|
||||
(*static_cast<function_object<void()>*>(cb))(); //
|
||||
const auto& func = *static_cast<decltype(ptr)>(cb);
|
||||
(void)func(); //
|
||||
};
|
||||
|
||||
const auto id = icicle_add_syscall_hook(this->emu_, invoker, callback_store.get());
|
||||
this->hooks_[id] = std::move(callback_store);
|
||||
const auto id = icicle_add_syscall_hook(this->emu_, invoker, ptr);
|
||||
this->hooks_[id] = std::move(obj);
|
||||
|
||||
return reinterpret_cast<emulator_hook*>(static_cast<size_t>(id));
|
||||
return wrap_hook(id);
|
||||
}
|
||||
|
||||
emulator_hook* hook_basic_block(basic_block_hook_callback callback) override
|
||||
@@ -278,15 +303,29 @@ namespace icicle
|
||||
// throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_violation(uint64_t address, size_t size,
|
||||
emulator_hook* hook_memory_violation(const uint64_t address, const size_t size,
|
||||
memory_violation_hook_callback callback) override
|
||||
{
|
||||
// TODO
|
||||
(void)address;
|
||||
(void)size;
|
||||
(void)callback;
|
||||
return nullptr;
|
||||
// throw std::runtime_error("Not implemented");
|
||||
|
||||
auto obj = make_function_object(std::move(callback));
|
||||
auto* ptr = obj.get();
|
||||
auto* wrapper =
|
||||
+[](void* user, const uint64_t address, const uint8_t operation, const int32_t unmapped) -> int32_t {
|
||||
const auto violation_type = unmapped //
|
||||
? memory_violation_type::unmapped
|
||||
: memory_violation_type::protection;
|
||||
|
||||
const auto& func = *static_cast<decltype(ptr)>(user);
|
||||
const auto res = func(address, 1, static_cast<memory_operation>(operation), violation_type);
|
||||
return res == memory_violation_continuation::resume ? 1 : 0;
|
||||
};
|
||||
|
||||
const auto id = icicle_add_violation_hook(this->emu_, wrapper, ptr);
|
||||
this->hooks_[id] = std::move(obj);
|
||||
|
||||
return wrap_hook(id);
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_access(const uint64_t address, const size_t size, const memory_operation filter,
|
||||
@@ -297,7 +336,7 @@ namespace icicle
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto shared_callback = std::make_shared<complex_memory_hook_callback>(std::move(callback));
|
||||
const auto shared_callback = std::make_shared<complex_memory_hook_callback>(std::move(callback));
|
||||
|
||||
if ((filter & memory_permission::exec) == memory_permission::exec)
|
||||
{
|
||||
@@ -315,7 +354,7 @@ namespace icicle
|
||||
const auto id = icicle_add_execution_hook(this->emu_, func, ptr);
|
||||
this->hooks_[id] = std::move(wrapper);
|
||||
|
||||
return reinterpret_cast<emulator_hook*>(static_cast<size_t>(id));
|
||||
return wrap_hook(id);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use icicle_cpu::ValueSource;
|
||||
use icicle_cpu::ExceptionCode;
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::registers;
|
||||
@@ -16,11 +17,11 @@ fn create_x64_vm() -> icicle_vm::Vm {
|
||||
return icicle_vm::build(&cpu_config).unwrap();
|
||||
}
|
||||
|
||||
fn map_permissions(foreign_permissions: u8) -> u8 {
|
||||
const FOREIGN_READ: u8 = 1 << 0;
|
||||
const FOREIGN_WRITE: u8 = 1 << 1;
|
||||
const FOREIGN_EXEC: u8 = 1 << 2;
|
||||
const FOREIGN_READ: u8 = 1 << 0;
|
||||
const FOREIGN_WRITE: u8 = 1 << 1;
|
||||
const FOREIGN_EXEC: u8 = 1 << 2;
|
||||
|
||||
fn map_permissions(foreign_permissions: u8) -> u8 {
|
||||
let mut permissions: u8 = 0;
|
||||
|
||||
if (foreign_permissions & FOREIGN_READ) != 0 {
|
||||
@@ -46,6 +47,7 @@ enum HookType {
|
||||
Read,
|
||||
Write,
|
||||
Execute,
|
||||
Violation,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@@ -133,6 +135,7 @@ pub struct IcicleEmulator {
|
||||
vm: icicle_vm::Vm,
|
||||
reg: registers::X64RegisterNodes,
|
||||
syscall_hooks: HookContainer<dyn Fn()>,
|
||||
violation_hooks: HookContainer<dyn Fn(u64, u8, bool) -> bool>,
|
||||
execution_hooks: Rc<RefCell<HookContainer<dyn Fn(u64)>>>,
|
||||
}
|
||||
|
||||
@@ -185,6 +188,7 @@ impl IcicleEmulator {
|
||||
reg: registers::X64RegisterNodes::new(&virtual_machine.cpu.arch),
|
||||
vm: virtual_machine,
|
||||
syscall_hooks: HookContainer::new(),
|
||||
violation_hooks: HookContainer::new(),
|
||||
execution_hooks: exec_hooks,
|
||||
}
|
||||
}
|
||||
@@ -199,34 +203,66 @@ impl IcicleEmulator {
|
||||
loop {
|
||||
let reason = self.vm.run();
|
||||
|
||||
let invoke_syscall = match reason {
|
||||
icicle_vm::VmExit::UnhandledException((code, _)) => {
|
||||
code == icicle_cpu::ExceptionCode::Syscall
|
||||
}
|
||||
_ => false,
|
||||
match reason {
|
||||
icicle_vm::VmExit::InstructionLimit => break,
|
||||
icicle_vm::VmExit::UnhandledException((code, value)) => {
|
||||
let continue_execution = self.handle_exception(code, value);
|
||||
if !continue_execution {
|
||||
break
|
||||
}
|
||||
},
|
||||
_ => break,
|
||||
};
|
||||
|
||||
if !invoke_syscall {
|
||||
if reason == icicle_vm::VmExit::InstructionLimit {
|
||||
break;
|
||||
}
|
||||
|
||||
println!("NO SYSCALL");
|
||||
break;
|
||||
}
|
||||
|
||||
for (_key, func) in self.syscall_hooks.get_hooks() {
|
||||
func();
|
||||
}
|
||||
|
||||
self.vm.cpu.write_pc(self.vm.cpu.read_pc() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_exception(&mut self, code: ExceptionCode, value: u64) -> bool {
|
||||
let continue_execution = match code {
|
||||
ExceptionCode::Syscall => self.handle_syscall(),
|
||||
ExceptionCode::ReadPerm => self.handle_violation(value, FOREIGN_READ, false),
|
||||
ExceptionCode::WritePerm => self.handle_violation(value, FOREIGN_WRITE, false),
|
||||
ExceptionCode::ReadUnmapped => self.handle_violation(value, FOREIGN_READ, true),
|
||||
ExceptionCode::WriteUnmapped => self.handle_violation(value, FOREIGN_WRITE, true),
|
||||
ExceptionCode::ExecViolation => self.handle_violation(value, FOREIGN_EXEC, true),
|
||||
_ => false
|
||||
};
|
||||
|
||||
return continue_execution;
|
||||
}
|
||||
|
||||
fn handle_violation(&mut self, address: u64, permission: u8, unmapped: bool) -> bool {
|
||||
let hooks = &self.violation_hooks.get_hooks();
|
||||
if hooks.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut continue_execution = true;
|
||||
|
||||
for (_key, func) in self.violation_hooks.get_hooks() {
|
||||
continue_execution &= func(address, permission, unmapped );
|
||||
}
|
||||
|
||||
return continue_execution;
|
||||
}
|
||||
|
||||
fn handle_syscall(&mut self) -> bool{
|
||||
for (_key, func) in self.syscall_hooks.get_hooks() {
|
||||
func();
|
||||
}
|
||||
|
||||
self.vm.cpu.write_pc(self.vm.cpu.read_pc() + 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.vm.icount_limit = self.vm.cpu.icount;
|
||||
}
|
||||
|
||||
pub fn add_violation_hook(&mut self, callback: Box<dyn Fn(u64, u8, bool) -> bool>) -> u32 {
|
||||
let hook_id = self.violation_hooks.add_hook(callback);
|
||||
return qualify_hook_id(hook_id, HookType::Violation);
|
||||
}
|
||||
|
||||
pub fn add_execution_hook(&mut self, callback: Box<dyn Fn(u64)>) -> u32 {
|
||||
let hook_id = self.execution_hooks.borrow_mut().add_hook(callback);
|
||||
return qualify_hook_id(hook_id, HookType::Execute);
|
||||
@@ -242,6 +278,7 @@ impl IcicleEmulator {
|
||||
|
||||
match hook_type {
|
||||
HookType::Syscall => self.syscall_hooks.remove_hook(hook_id),
|
||||
HookType::Violation => self.violation_hooks.remove_hook(hook_id),
|
||||
HookType::Execute => self.execution_hooks.borrow_mut().remove_hook(hook_id),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ type PtrFunction = extern "C" fn(*mut c_void, u64);
|
||||
type DataFunction = extern "C" fn(*mut c_void, *const c_void, usize);
|
||||
type MmioReadFunction = extern "C" fn(*mut c_void, u64, usize, *mut c_void);
|
||||
type MmioWriteFunction = extern "C" fn(*mut c_void, u64, usize, *const c_void);
|
||||
type ViolationFunction = extern "C" fn(*mut c_void, u64, u8, i32) -> i32;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn icicle_map_mmio(
|
||||
@@ -116,7 +117,11 @@ pub fn icicle_save_registers(ptr: *mut c_void, accessor: DataFunction, accessor_
|
||||
unsafe {
|
||||
let emulator = &mut *(ptr as *mut IcicleEmulator);
|
||||
let registers = emulator.save_registers();
|
||||
accessor(accessor_data, registers.as_ptr() as *const c_void, registers.len());
|
||||
accessor(
|
||||
accessor_data,
|
||||
registers.as_ptr() as *const c_void,
|
||||
registers.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,18 +145,35 @@ pub fn icicle_read_memory(ptr: *mut c_void, address: u64, data: *mut c_void, siz
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn icicle_add_syscall_hook(ptr: *mut c_void, callback: RawFunction, data: *mut c_void) {
|
||||
pub fn icicle_add_violation_hook(ptr: *mut c_void, callback: ViolationFunction, data: *mut c_void) -> u32 {
|
||||
unsafe {
|
||||
let emulator = &mut *(ptr as *mut IcicleEmulator);
|
||||
emulator.add_syscall_hook(Box::new(move || callback(data)));
|
||||
return emulator.add_violation_hook(Box::new(
|
||||
move |address: u64, permission: u8, unmapped: bool| {
|
||||
let result = callback(data, address, permission, to_cbool(unmapped));
|
||||
if result == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn icicle_add_execution_hook(ptr: *mut c_void, callback: PtrFunction, data: *mut c_void) {
|
||||
pub fn icicle_add_syscall_hook(ptr: *mut c_void, callback: RawFunction, data: *mut c_void) -> u32 {
|
||||
unsafe {
|
||||
let emulator = &mut *(ptr as *mut IcicleEmulator);
|
||||
emulator.add_execution_hook(Box::new(move |ptr: u64| callback(data, ptr)));
|
||||
return emulator.add_syscall_hook(Box::new(move || callback(data)));
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn icicle_add_execution_hook(ptr: *mut c_void, callback: PtrFunction, data: *mut c_void) -> u32 {
|
||||
unsafe {
|
||||
let emulator = &mut *(ptr as *mut IcicleEmulator);
|
||||
return emulator.add_execution_hook(Box::new(move |ptr: u64| callback(data, ptr)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user