diff --git a/src/icicle-emulator/icicle_x64_emulator.cpp b/src/icicle-emulator/icicle_x64_emulator.cpp index 5a9dba17..ad9321ad 100644 --- a/src/icicle-emulator/icicle_x64_emulator.cpp +++ b/src/icicle-emulator/icicle_x64_emulator.cpp @@ -1,14 +1,182 @@ #define ICICLE_EMULATOR_IMPL #include "icicle_x64_emulator.hpp" -extern "C" void test_rust(); +using icicle_emulator = struct icicle_emulator_; + +extern "C" +{ + icicle_emulator* icicle_create_emulator(); + int32_t icicle_map_memory(icicle_emulator*, uint64_t address, uint64_t length, uint8_t permissions); + int32_t icicle_unmap_memory(icicle_emulator*, uint64_t address, uint64_t length); + void icicle_destroy_emulator(icicle_emulator*); +} namespace icicle { + class icicle_x64_emulator : public x64_emulator + { + public: + icicle_x64_emulator() + : emu_(icicle_create_emulator()) + { + if (!this->emu_) + { + throw std::runtime_error("Failed to create icicle emulator instance"); + } + } + + ~icicle_x64_emulator() override + { + if (this->emu_) + { + icicle_destroy_emulator(this->emu_); + this->emu_ = nullptr; + } + } + + void start(const uint64_t start, const uint64_t end, std::chrono::nanoseconds timeout, + const size_t count) override + { + if (timeout.count() < 0) + { + timeout = {}; + } + } + + void stop() override + { + } + + size_t write_raw_register(const int reg, const void* value, const size_t size) override + { + throw std::runtime_error("Not implemented"); + } + + size_t read_raw_register(const int reg, void* value, const size_t size) override + { + throw std::runtime_error("Not implemented"); + } + + void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, + mmio_write_callback write_cb) override + { + throw std::runtime_error("Not implemented"); + } + + void map_memory(const uint64_t address, const size_t size, memory_permission permissions) override + { + const auto res = icicle_map_memory(this->emu_, address, size, static_cast(permissions)); + if (!res) + { + throw std::runtime_error("Failed to map memory"); + } + } + + void unmap_memory(const uint64_t address, const size_t size) override + { + const auto res = icicle_unmap_memory(this->emu_, address, size); + if (!res) + { + throw std::runtime_error("Failed to map memory"); + } + } + + bool try_read_memory(const uint64_t address, void* data, const size_t size) const override + { + throw std::runtime_error("Not implemented"); + } + + void read_memory(const uint64_t address, void* data, const size_t size) const override + { + if (!this->try_read_memory(address, data, size)) + { + throw std::runtime_error("Failed to read memory"); + } + } + + void write_memory(const uint64_t address, const void* data, const size_t size) override + { + throw std::runtime_error("Not implemented"); + } + + void apply_memory_protection(const uint64_t address, const size_t size, memory_permission permissions) override + { + throw std::runtime_error("Not implemented"); + } + + emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override + { + throw std::runtime_error("Not implemented"); + } + + emulator_hook* hook_basic_block(basic_block_hook_callback callback) override + { + throw std::runtime_error("Not implemented"); + } + + emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) override + { + throw std::runtime_error("Not implemented"); + } + + emulator_hook* hook_interrupt(interrupt_hook_callback callback) override + { + throw std::runtime_error("Not implemented"); + } + + emulator_hook* hook_memory_violation(uint64_t address, size_t size, + memory_violation_hook_callback callback) override + { + throw std::runtime_error("Not implemented"); + } + + emulator_hook* hook_memory_access(const uint64_t address, const size_t size, const memory_operation filter, + complex_memory_hook_callback callback) override + { + if (filter == memory_permission::none) + { + return nullptr; + } + + throw std::runtime_error("Not implemented"); + } + + void delete_hook(emulator_hook* hook) override + { + throw std::runtime_error("Not implemented"); + } + + void serialize_state(utils::buffer_serializer& buffer, const bool is_snapshot) const override + { + throw std::runtime_error("Not implemented"); + } + + void deserialize_state(utils::buffer_deserializer& buffer, const bool is_snapshot) override + { + throw std::runtime_error("Not implemented"); + } + + std::vector save_registers() override + { + throw std::runtime_error("Not implemented"); + } + + void restore_registers(const std::vector& register_data) override + { + throw std::runtime_error("Not implemented"); + } + + bool has_violation() const override + { + return false; + } + + private: + icicle_emulator* emu_{}; + }; + std::unique_ptr create_x64_emulator() { - test_rust(); - - return {}; + return std::make_unique(); } } diff --git a/src/icicle/src/icicle.rs b/src/icicle/src/icicle.rs new file mode 100644 index 00000000..b9146cce --- /dev/null +++ b/src/icicle/src/icicle.rs @@ -0,0 +1,65 @@ +fn create_x64_vm() -> icicle_vm::Vm { + let cpu_config = icicle_vm::cpu::Config::from_target_triple("x86_64-none"); + let vm = icicle_vm::build(&cpu_config).unwrap(); + return vm; +} + +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; + + let mut permissions: u8 = 0; + + if (foreign_permissions & FOREIGN_READ) != 0 { + permissions |= icicle_vm::cpu::mem::perm::READ; + } + + if (foreign_permissions & FOREIGN_WRITE) != 0 { + permissions |= icicle_vm::cpu::mem::perm::WRITE; + } + + if (foreign_permissions & FOREIGN_EXEC) != 0 { + permissions |= icicle_vm::cpu::mem::perm::EXEC; + } + + return permissions; +} + +pub struct IcicleEmulator { + vm: icicle_vm::Vm, +} + +impl IcicleEmulator { + pub fn new() -> Self { + Self { + vm: create_x64_vm(), + } + } + + pub fn map_memory(&mut self, address: u64, length: u64, permissions: u8) -> bool { + const MAPPING_PERMISSIONS: u8 = icicle_vm::cpu::mem::perm::MAP + | icicle_vm::cpu::mem::perm::INIT + | icicle_vm::cpu::mem::perm::IN_CODE_CACHE; + + let native_permissions = map_permissions(permissions); + + let mapping = icicle_vm::cpu::mem::Mapping { + perm: native_permissions | MAPPING_PERMISSIONS, + value: 0x0, + }; + + let layout = icicle_vm::cpu::mem::AllocLayout { + addr: Some(address), + size: length, + align: 0x1000, + }; + + let res = self.vm.cpu.mem.alloc_memory(layout, mapping); + return res.is_ok(); + } + + pub fn unmap_memory(&mut self, address: u64, length: u64) -> bool { + return self.vm.cpu.mem.unmap_memory_len(address, length); + } +} diff --git a/src/icicle/src/lib.rs b/src/icicle/src/lib.rs index 5c1cc04b..a766834a 100644 --- a/src/icicle/src/lib.rs +++ b/src/icicle/src/lib.rs @@ -1,58 +1,54 @@ +mod icicle; + +use icicle::IcicleEmulator; +use std::os::raw::c_void; + #[unsafe(no_mangle)] -pub fn test_rust() { - // Setup the CPU state for the target triple - let cpu_config = icicle_vm::cpu::Config::from_target_triple("x86_64-none"); - let mut vm = icicle_vm::build(&cpu_config).unwrap(); - - // Setup an environment to run inside of. - //let mut env = icicle_vm::env::build_auto(&mut vm).unwrap(); - // Load a binary into the environment. - //env.load(&mut vm.cpu, b"./test.elf").unwrap(); - //vm.env = env; - - let mapping = icicle_vm::cpu::mem::Mapping { perm: icicle_vm::cpu::mem::perm::ALL, value: 0x0 }; - - let alloc1 = - vm.cpu.mem.alloc_memory(icicle_vm::cpu::mem::AllocLayout { addr: Some(0x10000), size: 0x100, align: 0x100 }, mapping).unwrap(); - - // Add instrumentation - let counter = vm.cpu.trace.register_store(vec![0_u64]); - vm.add_injector(BlockCounter { counter }); - - // Run until the VM exits. - let exit = vm.run(); - println!("{exit:?}\n{}", icicle_vm::debug::current_disasm(&mut vm)); - - - // Read instrumentation data. - let blocks_hit = vm.cpu.trace[counter].as_any().downcast_ref::>().unwrap()[0]; - let blocks_executed = blocks_hit.saturating_sub(1); - println!("{blocks_executed} blocks were executed"); +pub fn icicle_create_emulator() -> *mut c_void { + let emulator = Box::new(IcicleEmulator::new()); + return Box::into_raw(emulator) as *mut c_void; } -struct BlockCounter { - counter: icicle_vm::cpu::StoreRef, +#[unsafe(no_mangle)] +pub fn icicle_map_memory( + ptr: *mut c_void, + address: u64, + length: u64, + permissions: u8, +) -> i32 { + unsafe { + let emulator = &mut *(ptr as *mut IcicleEmulator); + let res = emulator.map_memory(address, length, permissions); + + if res { + return 1; + } + + return 0; + } } -impl icicle_vm::CodeInjector for BlockCounter { - fn inject( - &mut self, - _cpu: &mut icicle_vm::cpu::Cpu, - group: &icicle_vm::cpu::BlockGroup, - code: &mut icicle_vm::BlockTable, - ) { - let store_id = self.counter.get_store_id(); - for block in &mut code.blocks[group.range()] { - // counter += 1 - let counter = block.pcode.alloc_tmp(8); - let instrumentation = [ - (counter, pcode::Op::Load(store_id), 0_u64).into(), - (counter, pcode::Op::IntAdd, (counter, 1_u64)).into(), - (pcode::Op::Store(store_id), (0_u64, counter)).into(), - ]; +#[unsafe(no_mangle)] +pub fn icicle_unmap_memory(ptr: *mut c_void, address: u64, length: u64) -> i32 { + unsafe { + let emulator = &mut *(ptr as *mut IcicleEmulator); + let res = emulator.unmap_memory(address, length); - // Inject the instrumentation at the start of the block. - block.pcode.instructions.splice(..0, instrumentation); - } - } -} \ No newline at end of file + if res { + return 1; + } + + return 0; + } +} + +#[unsafe(no_mangle)] +pub fn icicle_destroy_emulator(ptr: *mut c_void) { + if ptr.is_null() { + return; + } + + unsafe { + let _ = Box::from_raw(ptr as *mut IcicleEmulator); + } +} diff --git a/src/windows-emulator/CMakeLists.txt b/src/windows-emulator/CMakeLists.txt index 0da4f6e6..53ee76c8 100644 --- a/src/windows-emulator/CMakeLists.txt +++ b/src/windows-emulator/CMakeLists.txt @@ -16,6 +16,7 @@ endif() target_link_libraries(windows-emulator PRIVATE unicorn-emulator + icicle-emulator ) target_link_libraries(windows-emulator PUBLIC emulator)