diff --git a/src/icicle-emulator/icicle_x64_emulator.cpp b/src/icicle-emulator/icicle_x64_emulator.cpp index b202472d..a06b804c 100644 --- a/src/icicle-emulator/icicle_x64_emulator.cpp +++ b/src/icicle-emulator/icicle_x64_emulator.cpp @@ -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 - struct function_object : std::function, utils::object + emulator_hook* wrap_hook(const uint32_t id) { - using std::function::function; + return reinterpret_cast(static_cast(id)); + } + + template + struct function_object : utils::object + { + std::function func{}; + + function_object(std::function f = {}) + : func(std::move(f)) + { + } + + template + auto operator()(Args&&... args) const + { + return this->func.operator()(std::forward(args)...); + } + ~function_object() override = default; }; + template + std::unique_ptr> make_function_object(std::function func) + { + return std::make_unique>(std::move(func)); + } + template std::unique_ptr wrap_shared(std::shared_ptr shared_ptr) { @@ -242,18 +267,18 @@ namespace icicle return nullptr; } - auto callback_store = std::make_unique>([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*>(cb))(); // + const auto& func = *static_cast(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(static_cast(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(user); + const auto res = func(address, 1, static_cast(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(std::move(callback)); + const auto shared_callback = std::make_shared(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(static_cast(id)); + return wrap_hook(id); } return nullptr; diff --git a/src/icicle/src/icicle.rs b/src/icicle/src/icicle.rs index b52038a2..dd604da2 100644 --- a/src/icicle/src/icicle.rs +++ b/src/icicle/src/icicle.rs @@ -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, + violation_hooks: HookContainer bool>, execution_hooks: Rc>>, } @@ -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 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) -> 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), _ => {} } diff --git a/src/icicle/src/lib.rs b/src/icicle/src/lib.rs index 183c2a45..1b510a81 100644 --- a/src/icicle/src/lib.rs +++ b/src/icicle/src/lib.rs @@ -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))); } }