mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-19 03:33:56 +00:00
Merge icicle progress (#178)
This commit is contained in:
@@ -9,13 +9,13 @@ struct cpu_interface
|
||||
{
|
||||
virtual ~cpu_interface() = default;
|
||||
|
||||
virtual void start(uint64_t start, uint64_t end = 0, std::chrono::nanoseconds timeout = {}, size_t count = 0) = 0;
|
||||
virtual void start(size_t count = 0) = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual size_t read_raw_register(int reg, void* value, size_t size) = 0;
|
||||
virtual size_t write_raw_register(int reg, const void* value, size_t size) = 0;
|
||||
|
||||
virtual std::vector<std::byte> save_registers() = 0;
|
||||
virtual std::vector<std::byte> save_registers() const = 0;
|
||||
virtual void restore_registers(const std::vector<std::byte>& register_data) = 0;
|
||||
|
||||
// TODO: Remove this
|
||||
|
||||
@@ -15,11 +15,6 @@ class typed_emulator : public emulator
|
||||
static constexpr registers stack_pointer = StackPointer;
|
||||
static constexpr registers instruction_pointer = InstructionPointer;
|
||||
|
||||
void start_from_ip(const std::chrono::nanoseconds timeout = {}, const size_t count = 0)
|
||||
{
|
||||
this->start(this->read_instruction_pointer(), 0, timeout, count);
|
||||
}
|
||||
|
||||
size_t write_register(registers reg, const void* value, const size_t size)
|
||||
{
|
||||
return this->write_raw_register(static_cast<int>(reg), value, size);
|
||||
|
||||
@@ -7,11 +7,14 @@ using icicle_emulator = struct icicle_emulator_;
|
||||
|
||||
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);
|
||||
using icicle_mmio_write_func = void(void* user, uint64_t address, size_t length, const void* data);
|
||||
|
||||
using data_accessor_func = void(void* user, const void* data, size_t length);
|
||||
|
||||
icicle_emulator* icicle_create_emulator();
|
||||
int32_t icicle_protect_memory(icicle_emulator*, uint64_t address, uint64_t length, uint8_t permissions);
|
||||
int32_t icicle_map_memory(icicle_emulator*, uint64_t address, uint64_t length, uint8_t permissions);
|
||||
@@ -22,11 +25,14 @@ extern "C"
|
||||
int32_t icicle_write_memory(icicle_emulator*, uint64_t address, const void* data, size_t length);
|
||||
int32_t icicle_save_registers(icicle_emulator*, data_accessor_func* accessor, void* accessor_data);
|
||||
int32_t icicle_restore_registers(icicle_emulator*, const void* data, size_t length);
|
||||
uint32_t icicle_add_syscall_hook(icicle_emulator*, void (*callback)(void*), void* data);
|
||||
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);
|
||||
void icicle_start(icicle_emulator*);
|
||||
void icicle_start(icicle_emulator*, size_t count);
|
||||
void icicle_stop(icicle_emulator*);
|
||||
void icicle_destroy_emulator(icicle_emulator*);
|
||||
}
|
||||
|
||||
@@ -41,6 +47,51 @@ namespace icicle
|
||||
throw std::runtime_error(std::string(error));
|
||||
}
|
||||
}
|
||||
|
||||
emulator_hook* wrap_hook(const uint32_t id)
|
||||
{
|
||||
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)
|
||||
{
|
||||
struct shard_wrapper : utils::object
|
||||
{
|
||||
std::shared_ptr<T> ptr{};
|
||||
~shard_wrapper() override = default;
|
||||
};
|
||||
|
||||
auto wrapper = std::make_unique<shard_wrapper>();
|
||||
wrapper->ptr = std::move(shared_ptr);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
class icicle_x64_emulator : public x64_emulator
|
||||
@@ -64,22 +115,14 @@ namespace icicle
|
||||
}
|
||||
}
|
||||
|
||||
void start(const uint64_t start, const uint64_t end, std::chrono::nanoseconds timeout,
|
||||
const size_t count) override
|
||||
void start(const size_t count) override
|
||||
{
|
||||
if (timeout.count() < 0)
|
||||
{
|
||||
timeout = {};
|
||||
}
|
||||
|
||||
(void)start;
|
||||
(void)end;
|
||||
(void)count;
|
||||
icicle_start(this->emu_);
|
||||
icicle_start(this->emu_, count);
|
||||
}
|
||||
|
||||
void stop() override
|
||||
{
|
||||
icicle_stop(this->emu_);
|
||||
}
|
||||
|
||||
void load_gdt(const pointer_type address, const uint32_t limit) override
|
||||
@@ -142,28 +185,30 @@ namespace icicle
|
||||
auto* ptr = wrapper.get();
|
||||
this->storage_.push_back(std::move(wrapper));
|
||||
|
||||
auto* read_wrapper = +[](void* user, const uint64_t address, const size_t length, void* data) {
|
||||
auto* read_wrapper = +[](void* user, const uint64_t addr, const size_t length, void* data) {
|
||||
constexpr auto limit = sizeof(uint64_t);
|
||||
const auto* w = static_cast<mmio_wrapper*>(user);
|
||||
|
||||
// TODO: Change interface to get rid of loop
|
||||
for (size_t offset = 0; offset < length; offset += limit)
|
||||
{
|
||||
const auto max_read = std::min(limit, length - offset);
|
||||
const auto value = w->read_cb(address + offset - w->base, max_read);
|
||||
const auto value = w->read_cb(addr + offset - w->base, max_read);
|
||||
memcpy(static_cast<uint8_t*>(data) + offset, &value, max_read);
|
||||
}
|
||||
};
|
||||
|
||||
auto* write_wrapper = +[](void* user, const uint64_t address, const size_t length, const void* data) {
|
||||
auto* write_wrapper = +[](void* user, const uint64_t addr, const size_t length, const void* data) {
|
||||
constexpr auto limit = sizeof(uint64_t);
|
||||
const auto* w = static_cast<mmio_wrapper*>(user);
|
||||
|
||||
// TODO: Change interface to get rid of loop
|
||||
for (size_t offset = 0; offset < length; offset += limit)
|
||||
{
|
||||
uint64_t value{};
|
||||
const auto max_read = std::min(limit, length - offset);
|
||||
memcpy(&value, static_cast<const uint8_t*>(data) + offset, max_read);
|
||||
w->write_cb(address + offset - w->base, max_read, value);
|
||||
w->write_cb(addr + offset - w->base, max_read, value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -209,50 +254,69 @@ namespace icicle
|
||||
{
|
||||
if (static_cast<x64_hookable_instructions>(instruction_type) != x64_hookable_instructions::syscall)
|
||||
{
|
||||
// TODO
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto callback_store = std::make_unique<std::function<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<std::function<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->syscall_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
|
||||
{
|
||||
// TODO
|
||||
(void)callback;
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) override
|
||||
{
|
||||
// TODO
|
||||
(void)callback;
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
emulator_hook* hook_interrupt(interrupt_hook_callback callback) override
|
||||
{
|
||||
// TODO
|
||||
(void)callback;
|
||||
return nullptr;
|
||||
// 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
|
||||
{
|
||||
(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,
|
||||
@@ -263,41 +327,65 @@ namespace icicle
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
(void)address;
|
||||
(void)size;
|
||||
(void)callback;
|
||||
const auto shared_callback = std::make_shared<complex_memory_hook_callback>(std::move(callback));
|
||||
|
||||
if ((filter & memory_permission::exec) == memory_permission::exec)
|
||||
{
|
||||
if (address != 0 || size != std::numeric_limits<size_t>::max())
|
||||
{
|
||||
throw std::runtime_error("Not supported!");
|
||||
}
|
||||
|
||||
auto* ptr = shared_callback.get();
|
||||
auto wrapper = wrap_shared(shared_callback);
|
||||
auto* func = +[](void* user, const uint64_t ptr) {
|
||||
(*static_cast<complex_memory_hook_callback*>(user))(ptr, 0, 0, memory_permission::exec);
|
||||
};
|
||||
|
||||
const auto id = icicle_add_execution_hook(this->emu_, func, ptr);
|
||||
this->hooks_[id] = std::move(wrapper);
|
||||
|
||||
return wrap_hook(id);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
// throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
void delete_hook(emulator_hook* hook) override
|
||||
{
|
||||
const auto id = static_cast<uint32_t>(reinterpret_cast<size_t>(hook));
|
||||
const auto entry = this->syscall_hooks_.find(id);
|
||||
if (entry == this->syscall_hooks_.end())
|
||||
const auto entry = this->hooks_.find(id);
|
||||
if (entry == this->hooks_.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
icicle_remove_syscall_hook(this->emu_, id);
|
||||
this->syscall_hooks_.erase(entry);
|
||||
this->hooks_.erase(entry);
|
||||
}
|
||||
|
||||
void serialize_state(utils::buffer_serializer& buffer, const bool is_snapshot) const override
|
||||
{
|
||||
(void)buffer;
|
||||
(void)is_snapshot;
|
||||
throw std::runtime_error("Not implemented");
|
||||
if (is_snapshot)
|
||||
{
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
buffer.write_vector(this->save_registers());
|
||||
}
|
||||
|
||||
void deserialize_state(utils::buffer_deserializer& buffer, const bool is_snapshot) override
|
||||
{
|
||||
(void)buffer;
|
||||
(void)is_snapshot;
|
||||
throw std::runtime_error("Not implemented");
|
||||
if (is_snapshot)
|
||||
{
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
const auto data = buffer.read_vector<std::byte>();
|
||||
this->restore_registers(data);
|
||||
}
|
||||
|
||||
std::vector<std::byte> save_registers() override
|
||||
std::vector<std::byte> save_registers() const override
|
||||
{
|
||||
std::vector<std::byte> data{};
|
||||
auto* accessor = +[](void* user, const void* data, const size_t length) {
|
||||
@@ -323,8 +411,7 @@ namespace icicle
|
||||
|
||||
private:
|
||||
std::list<std::unique_ptr<utils::object>> storage_{};
|
||||
using syscall_hook_storage = std::unique_ptr<std::function<void()>>;
|
||||
std::unordered_map<uint32_t, syscall_hook_storage> syscall_hooks_{};
|
||||
std::unordered_map<uint32_t, std::unique_ptr<utils::object>> hooks_{};
|
||||
icicle_emulator* emu_{};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use icicle_cpu::ValueSource;
|
||||
use std::collections::HashMap;
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -96,10 +98,45 @@ impl<Func: ?Sized> HookContainer<Func> {
|
||||
}
|
||||
}
|
||||
|
||||
struct InstructionHookInjector {
|
||||
hook: pcode::HookId,
|
||||
}
|
||||
|
||||
impl icicle_vm::CodeInjector for InstructionHookInjector {
|
||||
fn inject(
|
||||
&mut self,
|
||||
_cpu: &mut icicle_vm::cpu::Cpu,
|
||||
group: &icicle_vm::cpu::BlockGroup,
|
||||
code: &mut icicle_vm::BlockTable,
|
||||
) {
|
||||
for id in group.range() {
|
||||
let block = &mut code.blocks[id];
|
||||
|
||||
let mut tmp_block = pcode::Block::new();
|
||||
tmp_block.next_tmp = block.pcode.next_tmp;
|
||||
|
||||
for stmt in block.pcode.instructions.drain(..) {
|
||||
tmp_block.push(stmt);
|
||||
if let pcode::Op::InstructionMarker = stmt.op {
|
||||
tmp_block.push(pcode::Op::Hook(self.hook));
|
||||
code.modified.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
std::mem::swap(
|
||||
&mut tmp_block.instructions,
|
||||
&mut block.pcode.instructions,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)>>>,
|
||||
}
|
||||
|
||||
pub struct MmioHandler {
|
||||
@@ -133,11 +170,26 @@ impl icicle_cpu::mem::IoMemory for MmioHandler {
|
||||
|
||||
impl IcicleEmulator {
|
||||
pub fn new() -> Self {
|
||||
let virtual_machine = create_x64_vm();
|
||||
let mut virtual_machine = create_x64_vm();
|
||||
let exec_hooks: Rc<RefCell<HookContainer<dyn Fn(u64)>>> = Rc::new(RefCell::new(HookContainer::new()));
|
||||
|
||||
let exec_hooks_clone = Rc::clone(&exec_hooks);
|
||||
|
||||
let hook = icicle_cpu::InstHook::new(move |_: &mut icicle_cpu::Cpu, addr: u64| {
|
||||
for (_key, func) in exec_hooks_clone.borrow().get_hooks() {
|
||||
func(addr);
|
||||
}
|
||||
});
|
||||
|
||||
let hook = virtual_machine.cpu.add_hook(hook);
|
||||
virtual_machine.add_injector(InstructionHookInjector { hook });
|
||||
|
||||
Self {
|
||||
reg: registers::X64RegisterNodes::new(&virtual_machine.cpu.arch),
|
||||
vm: virtual_machine,
|
||||
syscall_hooks: HookContainer::new(),
|
||||
violation_hooks: HookContainer::new(),
|
||||
execution_hooks: exec_hooks,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,29 +197,80 @@ impl IcicleEmulator {
|
||||
return &mut self.vm.cpu.mem;
|
||||
}
|
||||
|
||||
pub fn start(&mut self) {
|
||||
pub fn start(&mut self, count: u64) {
|
||||
self.vm.icount_limit = match count {
|
||||
0 => u64::MAX,
|
||||
_ => self.vm.cpu.icount + count,
|
||||
};
|
||||
|
||||
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 {
|
||||
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 = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn add_syscall_hook(&mut self, callback: Box<dyn Fn()>) -> u32 {
|
||||
let hook_id = self.syscall_hooks.add_hook(callback);
|
||||
return qualify_hook_id(hook_id, HookType::Syscall);
|
||||
@@ -178,6 +281,8 @@ 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),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,17 +20,27 @@ pub fn icicle_create_emulator() -> *mut c_void {
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn icicle_start(ptr: *mut c_void) {
|
||||
pub fn icicle_start(ptr: *mut c_void, count: usize) {
|
||||
unsafe {
|
||||
let emulator = &mut *(ptr as *mut IcicleEmulator);
|
||||
emulator.start();
|
||||
emulator.start(count as u64);
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn icicle_stop(ptr: *mut c_void) {
|
||||
unsafe {
|
||||
let emulator = &mut *(ptr as *mut IcicleEmulator);
|
||||
emulator.stop();
|
||||
}
|
||||
}
|
||||
|
||||
type RawFunction = extern "C" fn(*mut c_void);
|
||||
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(
|
||||
@@ -107,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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,10 +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_syscall_hook(ptr: *mut c_void, callback: RawFunction, data: *mut c_void) -> u32 {
|
||||
unsafe {
|
||||
let emulator = &mut *(ptr as *mut IcicleEmulator);
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -269,17 +269,12 @@ namespace unicorn
|
||||
uc_close(this->uc_);
|
||||
}
|
||||
|
||||
void start(const uint64_t start, const uint64_t end, std::chrono::nanoseconds timeout,
|
||||
const size_t count) override
|
||||
void start(const size_t count) override
|
||||
{
|
||||
if (timeout.count() < 0)
|
||||
{
|
||||
timeout = {};
|
||||
}
|
||||
|
||||
this->has_violation_ = false;
|
||||
const auto timeoutYs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
|
||||
const auto res = uc_emu_start(*this, start, end, static_cast<uint64_t>(timeoutYs.count()), count);
|
||||
const auto start = this->read_instruction_pointer();
|
||||
constexpr auto end = std::numeric_limits<uint64_t>::max();
|
||||
const auto res = uc_emu_start(*this, start, end, 0, count);
|
||||
if (res == UC_ERR_OK)
|
||||
{
|
||||
return;
|
||||
@@ -647,7 +642,7 @@ namespace unicorn
|
||||
serializer.deserialize(buffer);
|
||||
}
|
||||
|
||||
std::vector<std::byte> save_registers() override
|
||||
std::vector<std::byte> save_registers() const override
|
||||
{
|
||||
utils::buffer_serializer buffer{};
|
||||
const uc_context_serializer serializer(this->uc_, false);
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace test
|
||||
constexpr auto count = 200000;
|
||||
|
||||
auto emu = create_sample_emulator();
|
||||
emu.start({}, count);
|
||||
emu.start(count);
|
||||
|
||||
ASSERT_EQ(emu.get_executed_instructions(), count);
|
||||
}
|
||||
@@ -34,12 +34,12 @@ namespace test
|
||||
constexpr auto offset = 1;
|
||||
const auto instructionsToExecute = executedInstructions - offset;
|
||||
|
||||
new_emu.start({}, instructionsToExecute);
|
||||
new_emu.start(instructionsToExecute);
|
||||
|
||||
ASSERT_EQ(new_emu.get_executed_instructions(), instructionsToExecute);
|
||||
ASSERT_NOT_TERMINATED(new_emu);
|
||||
|
||||
new_emu.start({}, offset);
|
||||
new_emu.start(offset);
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
|
||||
ASSERT_EQ(new_emu.get_executed_instructions(), executedInstructions);
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace test
|
||||
|
||||
const auto get_state_for_count = [&](const size_t count) {
|
||||
reset_emulator();
|
||||
emu.start({}, count);
|
||||
emu.start(count);
|
||||
|
||||
utils::buffer_serializer state{};
|
||||
emu.serialize(state);
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace test
|
||||
TEST(SerializationTest, DeserializedEmulatorBehavesLikeSource)
|
||||
{
|
||||
auto emu = create_sample_emulator();
|
||||
emu.start({}, 100);
|
||||
emu.start(100);
|
||||
|
||||
utils::buffer_serializer serializer{};
|
||||
emu.serialize(serializer);
|
||||
|
||||
@@ -514,15 +514,10 @@ void windows_emulator::setup_hooks()
|
||||
[&](const uint64_t address, const size_t, const uint64_t) { this->on_instruction_execution(address); });
|
||||
}
|
||||
|
||||
void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count)
|
||||
void windows_emulator::start(size_t count)
|
||||
{
|
||||
const auto use_count = count > 0;
|
||||
const auto use_timeout = timeout != std::chrono::nanoseconds{};
|
||||
|
||||
const auto start_time = std::chrono::high_resolution_clock::now();
|
||||
const auto start_instructions = this->executed_instructions_;
|
||||
|
||||
const auto target_time = start_time + timeout;
|
||||
const auto target_instructions = start_instructions + count;
|
||||
|
||||
while (true)
|
||||
@@ -532,25 +527,13 @@ void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count)
|
||||
this->perform_thread_switch();
|
||||
}
|
||||
|
||||
this->emu().start_from_ip(timeout, count);
|
||||
this->emu().start(count);
|
||||
|
||||
if (!this->switch_thread_ && !this->emu().has_violation())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (use_timeout)
|
||||
{
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
|
||||
if (now >= target_time)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
timeout = target_time - now;
|
||||
}
|
||||
|
||||
if (use_count)
|
||||
{
|
||||
const auto current_instructions = this->executed_instructions_;
|
||||
|
||||
@@ -130,7 +130,7 @@ class windows_emulator
|
||||
return this->executed_instructions_;
|
||||
}
|
||||
|
||||
void start(std::chrono::nanoseconds timeout = {}, size_t count = 0);
|
||||
void start(size_t count = 0);
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
|
||||
@@ -37,7 +37,7 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
|
||||
{
|
||||
try
|
||||
{
|
||||
this->win_emu_->start({}, 1);
|
||||
this->win_emu_->start(1);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
||||
@@ -64,7 +64,7 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->start_from_ip();
|
||||
this->emu_->start();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->start_from_ip({}, 1);
|
||||
this->emu_->start(1);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user