Merge remote-tracking branch 'origin/main' into unicorn-upgrade-2

This commit is contained in:
momo5502
2025-08-23 10:40:48 +02:00
135 changed files with 9199 additions and 7011 deletions

View File

@@ -1,5 +1,6 @@
use icicle_cpu::ExceptionCode;
use icicle_cpu::ValueSource;
use std::collections::HashSet;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use crate::registers;
@@ -73,31 +74,96 @@ fn qualify_hook_id(hook_id: u32, hook_type: HookType) -> u32 {
pub struct HookContainer<Func: ?Sized> {
hook_id: u32,
is_iterating: bool,
hooks: HashMap<u32, Box<Func>>,
hooks_to_add: HashMap<u32, Box<Func>>,
hooks_to_remove: HashSet<u32>,
}
impl<Func: ?Sized> HookContainer<Func> {
pub fn new() -> Self {
Self {
hook_id: 0,
is_iterating: false,
hooks: HashMap::new(),
hooks_to_add: HashMap::new(),
hooks_to_remove: HashSet::new(),
}
}
pub fn add_hook(&mut self, callback: Box<Func>) -> u32 {
self.hook_id += 1;
let id = self.hook_id;
self.hooks.insert(id, callback);
if self.is_iterating {
self.hooks_to_add.insert(id, callback);
} else {
self.hooks.insert(id, callback);
}
return id;
}
pub fn get_hooks(&self) -> &HashMap<u32, Box<Func>> {
return &self.hooks;
pub fn for_each_hook<F>(&mut self, mut callback: F)
where
F: FnMut(&Func),
{
let was_iterating = self.do_pre_access_work();
for (_, func) in &self.hooks {
callback(func.as_ref());
}
self.do_post_access_work(was_iterating);
}
pub fn access_hook<F>(&mut self, id: u32, mut callback: F)
where
F: FnMut(&Func),
{
let was_iterating = self.do_pre_access_work();
let hook = self.hooks.get(&id);
if hook.is_some() {
callback(hook.unwrap().as_ref());
}
self.do_post_access_work(was_iterating);
}
pub fn is_empty(&self) -> bool {
return self.hooks.is_empty();
}
pub fn remove_hook(&mut self, id: u32) {
self.hooks.remove(&id);
if self.is_iterating {
self.hooks_to_remove.insert(id);
} else {
self.hooks.remove(&id);
}
}
fn do_pre_access_work(&mut self) -> bool {
let was_iterating = self.is_iterating;
self.is_iterating = true;
return was_iterating;
}
fn do_post_access_work(&mut self, was_iterating: bool) {
self.is_iterating = was_iterating;
if self.is_iterating {
return;
}
let to_remove = std::mem::take(&mut self.hooks_to_remove);
for id in &to_remove {
self.hooks.remove(&id);
}
let to_add = std::mem::take(&mut self.hooks_to_add);
for (id, func) in to_add {
self.hooks.insert(id, func);
}
}
}
@@ -157,6 +223,7 @@ struct ExecutionHooks {
specific_hooks: HookContainer<dyn Fn(u64)>,
block_hooks: HookContainer<dyn Fn(u64, u64)>,
address_mapping: HashMap<u64, Vec<u32>>,
one_time_callbacks: Vec<Box<dyn Fn()>>,
}
impl ExecutionHooks {
@@ -167,31 +234,38 @@ impl ExecutionHooks {
specific_hooks: HookContainer::new(),
block_hooks: HookContainer::new(),
address_mapping: HashMap::new(),
one_time_callbacks: Vec::new(),
}
}
fn run_hooks(&self, address: u64) {
for (_key, func) in self.generic_hooks.get_hooks() {
func(address);
fn run_hooks(&mut self, address: u64) {
if !self.one_time_callbacks.is_empty() {
let callbacks = std::mem::take(&mut self.one_time_callbacks);
for cb in callbacks {
cb.as_ref()();
}
}
self.generic_hooks.for_each_hook(|func| {
func(address);
});
let mapping = self.address_mapping.get(&address);
if mapping.is_none() {
return;
}
for id in mapping.unwrap() {
let func = self.specific_hooks.get_hooks().get(&id);
if func.is_some() {
func.unwrap()(address);
}
self.specific_hooks.access_hook(*id, |func| {
func(address);
});
}
}
pub fn on_block(&mut self, address: u64, instructions: u64) {
for (_key, func) in self.block_hooks.get_hooks() {
self.block_hooks.for_each_hook(|func| {
func(address, instructions);
}
});
}
pub fn execute(&mut self, cpu: &mut icicle_cpu::Cpu, address: u64) {
@@ -224,6 +298,10 @@ impl ExecutionHooks {
return id;
}
pub fn schedule(&mut self, callback: Box<dyn Fn()>) {
self.one_time_callbacks.push(callback);
}
pub fn remove_generic_hook(&mut self, id: u32) {
self.generic_hooks.remove_hook(id);
}
@@ -298,6 +376,15 @@ impl icicle_cpu::mem::IoMemory for MmioHandler {
impl IcicleEmulator {
pub fn new() -> Self {
let mut virtual_machine = create_x64_vm();
let capacity_400mb = 50_000;
let mut capacity = 8 * 2 * capacity_400mb; // ~8gb
if cfg!(target_pointer_width = "32") {
capacity = 2 * capacity_400mb; // ~1gb
}
virtual_machine.cpu.mem.set_capacity(capacity);
let stop_value = Rc::new(RefCell::new(false));
let exec_hooks = Rc::new(RefCell::new(ExecutionHooks::new(stop_value.clone())));
@@ -368,10 +455,10 @@ impl IcicleEmulator {
}
}
fn handle_interrupt(&self, code: i32) -> bool {
for (_key, func) in self.interrupt_hooks.get_hooks() {
fn handle_interrupt(&mut self, code: i32) -> bool {
self.interrupt_hooks.for_each_hook(|func| {
func(code);
}
});
return true;
}
@@ -393,16 +480,15 @@ impl IcicleEmulator {
}
fn handle_violation(&mut self, address: u64, permission: u8, unmapped: bool) -> bool {
let hooks = &self.violation_hooks.get_hooks();
if hooks.is_empty() {
if self.violation_hooks.is_empty() {
return false;
}
let mut continue_execution = true;
for (_key, func) in self.violation_hooks.get_hooks() {
self.violation_hooks.for_each_hook(|func| {
continue_execution &= func(address, permission, unmapped);
}
});
return continue_execution;
}
@@ -412,9 +498,9 @@ impl IcicleEmulator {
return self.handle_interrupt(value as i32);
}
for (_key, func) in self.syscall_hooks.get_hooks() {
self.syscall_hooks.for_each_hook(|func| {
func();
}
});
self.vm.cpu.write_pc(self.vm.cpu.read_pc() + 2);
return true;
@@ -521,6 +607,10 @@ impl IcicleEmulator {
}
}
pub fn run_on_next_instruction(&mut self, callback: Box<dyn Fn()>) {
self.execution_hooks.borrow_mut().schedule(callback);
}
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
@@ -533,14 +623,7 @@ impl IcicleEmulator {
value: 0x0,
};
let layout = icicle_vm::cpu::mem::AllocLayout {
addr: Some(address),
size: length,
align: 0x1000,
};
let res = self.get_mem().alloc_memory(layout, mapping);
return res.is_ok();
return self.get_mem().map_memory_len(address, length, mapping);
}
pub fn map_mmio(
@@ -555,14 +638,7 @@ impl IcicleEmulator {
let handler = MmioHandler::new(read_function, write_function);
let handler_id = mem.register_io_handler(handler);
let layout = icicle_vm::cpu::mem::AllocLayout {
addr: Some(address),
size: length,
align: 0x1000,
};
let res = mem.alloc_memory(layout, handler_id);
return res.is_ok();
return self.get_mem().map_memory_len(address, length, handler_id);
}
pub fn unmap_memory(&mut self, address: u64, length: u64) -> bool {

View File

@@ -307,6 +307,14 @@ pub fn icicle_remove_hook(ptr: *mut c_void, id: u32) {
}
}
#[unsafe(no_mangle)]
pub fn icicle_run_on_next_instruction(ptr: *mut c_void, callback: RawFunction, data: *mut c_void) {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
emulator.run_on_next_instruction(Box::new(move || callback(data)));
}
}
#[unsafe(no_mangle)]
pub fn icicle_read_register(
ptr: *mut c_void,

View File

@@ -1,7 +1,9 @@
#define ICICLE_EMULATOR_IMPL
#include "icicle_x86_64_emulator.hpp"
#include <unordered_set>
#include <utils/object.hpp>
#include <utils/finally.hpp>
using icicle_emulator = struct icicle_emulator_;
@@ -21,8 +23,8 @@ extern "C"
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);
int32_t icicle_map_mmio(icicle_emulator*, uint64_t address, uint64_t length, icicle_mmio_read_func* read_callback,
void* read_data, icicle_mmio_write_func* write_callback, void* write_data);
int32_t icicle_map_mmio(icicle_emulator*, uint64_t address, uint64_t length, icicle_mmio_read_func* read_callback, void* read_data,
icicle_mmio_write_func* write_callback, void* write_data);
int32_t icicle_unmap_memory(icicle_emulator*, uint64_t address, uint64_t length);
int32_t icicle_read_memory(icicle_emulator*, uint64_t address, void* data, size_t length);
int32_t icicle_write_memory(icicle_emulator*, uint64_t address, const void* data, size_t length);
@@ -44,6 +46,7 @@ extern "C"
void icicle_start(icicle_emulator*, size_t count);
void icicle_stop(icicle_emulator*);
void icicle_destroy_emulator(icicle_emulator*);
void icicle_run_on_next_instruction(icicle_emulator*, raw_func* callback, void* data);
}
namespace icicle
@@ -58,24 +61,35 @@ namespace icicle
}
}
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
{
bool* hook_state{};
std::function<T> func{};
function_object(std::function<T> f = {})
: func(std::move(f))
function_object(std::function<T> f = {}, bool* state = nullptr)
: hook_state(state),
func(std::move(f))
{
}
template <typename... Args>
auto operator()(Args&&... args) const
{
bool old_state{};
if (this->hook_state)
{
old_state = *this->hook_state;
*this->hook_state = true;
}
const auto _ = utils::finally([&] {
if (this->hook_state)
{
*this->hook_state = old_state;
}
});
return this->func.operator()(std::forward<Args>(args)...);
}
@@ -83,10 +97,18 @@ namespace icicle
};
template <typename T>
std::unique_ptr<function_object<T>> make_function_object(std::function<T> func)
std::unique_ptr<function_object<T>> make_function_object(std::function<T> func, bool& hook_state)
{
return std::make_unique<function_object<T>>(std::move(func));
return std::make_unique<function_object<T>>(std::move(func), &hook_state);
}
struct memory_access_hook
{
uint64_t address{};
uint64_t size{};
memory_access_hook_callback callback{};
bool is_read{};
};
}
class icicle_x86_64_emulator : public x86_64_emulator
@@ -103,6 +125,10 @@ namespace icicle
~icicle_x86_64_emulator() override
{
reset_object_with_delayed_destruction(this->hooks_);
reset_object_with_delayed_destruction(this->storage_);
utils::reset_object_with_delayed_destruction(this->hooks_to_install_);
if (this->emu_)
{
icicle_destroy_emulator(this->emu_);
@@ -162,8 +188,7 @@ namespace icicle
return icicle_read_register(this->emu_, reg, value, size);
}
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
mmio_write_callback write_cb) override
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) override
{
struct mmio_wrapper : utils::object
{
@@ -236,12 +261,12 @@ namespace icicle
return nullptr;
}
auto obj = make_function_object(std::move(callback));
auto obj = make_function_object(std::move(callback), this->is_in_hook_);
auto* ptr = obj.get();
const auto invoker = +[](void* cb) {
const auto& func = *static_cast<decltype(ptr)>(cb);
(void)func(); //
(void)func(0); //
};
const auto id = icicle_add_syscall_hook(this->emu_, invoker, ptr);
@@ -252,7 +277,7 @@ namespace icicle
emulator_hook* hook_basic_block(basic_block_hook_callback callback) override
{
auto object = make_function_object(std::move(callback));
auto object = make_function_object(std::move(callback), this->is_in_hook_);
auto* ptr = object.get();
auto* wrapper = +[](void* user, const uint64_t addr, const uint64_t instructions) {
basic_block block{};
@@ -271,7 +296,7 @@ namespace icicle
emulator_hook* hook_interrupt(interrupt_hook_callback callback) override
{
auto obj = make_function_object(std::move(callback));
auto obj = make_function_object(std::move(callback), this->is_in_hook_);
auto* ptr = obj.get();
auto* wrapper = +[](void* user, const int32_t code) {
const auto& func = *static_cast<decltype(ptr)>(user);
@@ -286,10 +311,9 @@ namespace icicle
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) override
{
auto obj = make_function_object(std::move(callback));
auto obj = make_function_object(std::move(callback), this->is_in_hook_);
auto* ptr = obj.get();
auto* wrapper =
+[](void* user, const uint64_t address, const uint8_t operation, const int32_t unmapped) -> int32_t {
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;
@@ -307,7 +331,7 @@ namespace icicle
emulator_hook* hook_memory_execution(const uint64_t address, memory_execution_hook_callback callback) override
{
auto object = make_function_object(std::move(callback));
auto object = make_function_object(std::move(callback), this->is_in_hook_);
auto* ptr = object.get();
auto* wrapper = +[](void* user, const uint64_t addr) {
const auto& func = *static_cast<decltype(ptr)>(user);
@@ -322,7 +346,7 @@ namespace icicle
emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) override
{
auto object = make_function_object(std::move(callback));
auto object = make_function_object(std::move(callback), this->is_in_hook_);
auto* ptr = object.get();
auto* wrapper = +[](void* user, const uint64_t addr) {
const auto& func = *static_cast<decltype(ptr)>(user);
@@ -335,49 +359,37 @@ namespace icicle
return wrap_hook(id);
}
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size,
memory_access_hook_callback callback) override
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override
{
auto obj = make_function_object(std::move(callback));
auto* ptr = obj.get();
auto* wrapper = +[](void* user, const uint64_t address, const void* data, size_t length) {
const auto& func = *static_cast<decltype(ptr)>(user);
func(address, data, length);
};
const auto id = icicle_add_read_hook(this->emu_, address, address + size, wrapper, ptr);
this->hooks_[id] = std::move(obj);
return wrap_hook(id);
return this->try_install_memory_access_hook(memory_access_hook{
.address = address,
.size = size,
.callback = std::move(callback),
.is_read = true,
});
}
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size,
memory_access_hook_callback callback) override
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override
{
auto obj = make_function_object(std::move(callback));
auto* ptr = obj.get();
auto* wrapper = +[](void* user, const uint64_t address, const void* data, size_t length) {
const auto& func = *static_cast<decltype(ptr)>(user);
func(address, data, length);
};
const auto id = icicle_add_write_hook(this->emu_, address, address + size, wrapper, ptr);
this->hooks_[id] = std::move(obj);
return wrap_hook(id);
return this->try_install_memory_access_hook(memory_access_hook{
.address = address,
.size = size,
.callback = std::move(callback),
.is_read = false,
});
}
void delete_hook(emulator_hook* hook) override
{
const auto id = static_cast<uint32_t>(reinterpret_cast<size_t>(hook));
const auto entry = this->hooks_.find(id);
if (entry == this->hooks_.end())
if (this->is_in_hook_)
{
return;
this->hooks_to_delete_.insert(hook);
this->schedule_action_execution();
}
else
{
this->delete_hook_internal(hook);
}
icicle_remove_hook(this->emu_, id);
this->hooks_.erase(entry);
}
void serialize_state(utils::buffer_serializer& buffer, const bool is_snapshot) const override
@@ -437,9 +449,138 @@ namespace icicle
}
private:
bool is_in_hook_{false};
std::list<std::unique_ptr<utils::object>> storage_{};
std::unordered_map<uint32_t, std::unique_ptr<utils::object>> hooks_{};
std::unordered_map<emulator_hook*, std::optional<uint32_t>> id_mapping_{};
icicle_emulator* emu_{};
uint32_t index_{0};
std::unordered_set<emulator_hook*> hooks_to_delete_{};
std::unordered_map<emulator_hook*, memory_access_hook> hooks_to_install_{};
emulator_hook* wrap_hook(const std::optional<uint32_t> icicle_id)
{
const auto id = ++this->index_;
auto* hook = reinterpret_cast<emulator_hook*>(static_cast<size_t>(id));
this->id_mapping_[hook] = icicle_id;
return hook;
}
emulator_hook* hook_memory_access(memory_access_hook hook, emulator_hook* hook_id)
{
auto obj = make_function_object(std::move(hook.callback), this->is_in_hook_);
auto* ptr = obj.get();
auto* wrapper = +[](void* user, const uint64_t address, const void* data, size_t length) {
const auto& func = *static_cast<decltype(ptr)>(user);
func(address, data, length);
};
auto* installer = hook.is_read ? &icicle_add_read_hook : &icicle_add_write_hook;
const auto id = installer(this->emu_, hook.address, hook.address + hook.size, wrapper, ptr);
this->hooks_[id] = std::move(obj);
if (hook_id)
{
this->id_mapping_[hook_id] = id;
return hook_id;
}
return wrap_hook(id);
}
void delete_hook_internal(emulator_hook* hook)
{
auto hook_id = this->id_mapping_.find(hook);
if (hook_id == this->id_mapping_.end())
{
return;
}
if (!hook_id->second.has_value())
{
this->hooks_to_delete_.insert(hook);
return;
}
const auto id = *hook_id->second;
this->id_mapping_.erase(hook_id);
const auto entry = this->hooks_.find(id);
if (entry == this->hooks_.end())
{
return;
}
icicle_remove_hook(this->emu_, id);
const auto obj = std::move(entry->second);
this->hooks_.erase(entry);
(void)obj;
}
void perform_pending_actions()
{
auto hooks_to_install = std::move(this->hooks_to_install_);
const auto hooks_to_delete = std::move(this->hooks_to_delete_);
this->hooks_to_delete_ = {};
this->hooks_to_install_ = {};
for (auto& hook : hooks_to_install)
{
this->hook_memory_access(std::move(hook.second), hook.first);
}
for (auto* hook : hooks_to_delete)
{
this->delete_hook_internal(hook);
}
}
emulator_hook* try_install_memory_access_hook(memory_access_hook hook)
{
if (!this->is_in_hook_)
{
return this->hook_memory_access(std::move(hook), nullptr);
}
auto* hook_id = wrap_hook(std::nullopt);
this->hooks_to_install_[hook_id] = std::move(hook);
this->schedule_action_execution();
return hook_id;
}
void schedule_action_execution()
{
this->run_on_next_instruction([this] {
this->perform_pending_actions(); //
});
}
void run_on_next_instruction(std::function<void()> func) const
{
auto* heap_func = new std::function(std::move(func));
auto* callback = +[](void* data) {
auto* cb = static_cast<std::function<void()>*>(data);
try
{
(*cb)();
}
catch (...)
{
// Ignore
}
delete cb;
};
icicle_run_on_next_instruction(this->emu_, callback, heap_func);
}
};
std::unique_ptr<x86_64_emulator> create_x86_64_emulator()

View File

@@ -33,6 +33,8 @@ namespace unicorn
return UC_X86_INS_RDTSC;
case x86_hookable_instructions::rdtscp:
return UC_X86_INS_RDTSCP;
case x86_hookable_instructions::sgdt:
return UC_X86_INS_SGDT;
default:
throw std::runtime_error("Bad instruction for mapping");
}
@@ -207,7 +209,7 @@ namespace unicorn
~unicorn_x86_64_emulator() override
{
this->hooks_.clear();
reset_object_with_delayed_destruction(this->hooks_);
uc_close(this->uc_);
}
@@ -244,8 +246,11 @@ namespace unicorn
void load_gdt(const pointer_type address, const uint32_t limit) override
{
const std::array<uint64_t, 4> gdtr = {0, address, limit, 0};
this->write_register(x86_register::gdtr, gdtr.data(), gdtr.size() * sizeof(uint64_t));
uc_x86_mmr gdt{};
gdt.base = address;
gdt.limit = limit;
this->write_register(x86_register::gdtr, &gdt, sizeof(gdt));
}
void set_segment_base(const x86_register base, const pointer_type value) override
@@ -288,8 +293,7 @@ namespace unicorn
if (size < result_size)
{
throw std::runtime_error("Register size mismatch: " + std::to_string(size) +
" != " + std::to_string(result_size));
throw std::runtime_error("Register size mismatch: " + std::to_string(size) + " != " + std::to_string(result_size));
}
return result_size;
@@ -303,15 +307,13 @@ namespace unicorn
if (size < result_size)
{
throw std::runtime_error("Register size mismatch: " + std::to_string(size) +
" != " + std::to_string(result_size));
throw std::runtime_error("Register size mismatch: " + std::to_string(size) + " != " + std::to_string(result_size));
}
return result_size;
}
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
mmio_write_callback write_cb) override
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) override
{
auto read_wrapper = [c = std::move(read_cb)](uc_engine*, const uint64_t addr, const uint32_t s) {
assert_64bit_limit(s);
@@ -320,8 +322,7 @@ namespace unicorn
return value;
};
auto write_wrapper = [c = std::move(write_cb)](uc_engine*, const uint64_t addr, const uint32_t s,
const uint64_t value) {
auto write_wrapper = [c = std::move(write_cb)](uc_engine*, const uint64_t addr, const uint32_t s, const uint64_t value) {
assert_64bit_limit(s);
c(addr, &value, s);
};
@@ -331,8 +332,8 @@ namespace unicorn
.write = mmio_callbacks::write_wrapper(std::move(write_wrapper)),
};
uce(uc_mmio_map(*this, address, size, cb.read.get_c_function(), cb.read.get_user_data(),
cb.write.get_c_function(), cb.write.get_user_data()));
uce(uc_mmio_map(*this, address, size, cb.read.get_c_function(), cb.read.get_user_data(), cb.write.get_c_function(),
cb.write.get_user_data()));
this->mmio_[address] = std::move(cb);
}
@@ -368,8 +369,7 @@ namespace unicorn
uce(uc_mem_write(*this, address, data, size));
}
void apply_memory_protection(const uint64_t address, const size_t size,
memory_permission permissions) override
void apply_memory_protection(const uint64_t address, const size_t size, memory_permission permissions) override
{
uce(uc_mem_protect(*this, address, size, static_cast<uint32_t>(permissions)));
}
@@ -384,34 +384,44 @@ namespace unicorn
if (inst_type == x86_hookable_instructions::invalid)
{
function_wrapper<int, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) {
return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0;
return (c(0) == instruction_hook_continuation::skip_instruction) ? 1 : 0;
});
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN_INVALID, wrapper.get_function(),
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN_INVALID, wrapper.get_function(), wrapper.get_user_data(), 0,
std::numeric_limits<pointer_type>::max()));
container->add(std::move(wrapper), std::move(hook));
}
else if (inst_type == x86_hookable_instructions::syscall)
{
function_wrapper<void, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) { c(); });
function_wrapper<void, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) { (void)c(0); });
const auto uc_instruction = map_hookable_instruction(inst_type);
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(),
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max(),
uc_instruction));
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), wrapper.get_user_data(), 0,
std::numeric_limits<pointer_type>::max(), uc_instruction));
container->add(std::move(wrapper), std::move(hook));
}
else if (inst_type == x86_hookable_instructions::sgdt)
{
function_wrapper<int, uc_engine*, uint64_t> wrapper([c = std::move(callback)](uc_engine*, const uint64_t data) {
return (c(data) == instruction_hook_continuation::skip_instruction) ? 1 : 0;
});
const auto uc_instruction = map_hookable_instruction(inst_type);
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), wrapper.get_user_data(), 0,
std::numeric_limits<pointer_type>::max(), uc_instruction));
container->add(std::move(wrapper), std::move(hook));
}
else
{
function_wrapper<int, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) {
return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0;
return (c(0) == instruction_hook_continuation::skip_instruction) ? 1 : 0;
});
const auto uc_instruction = map_hookable_instruction(inst_type);
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(),
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max(),
uc_instruction));
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), wrapper.get_user_data(), 0,
std::numeric_limits<pointer_type>::max(), uc_instruction));
container->add(std::move(wrapper), std::move(hook));
}
@@ -437,8 +447,8 @@ namespace unicorn
unicorn_hook hook{*this};
auto container = std::make_unique<hook_container>();
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_BLOCK, wrapper.get_function(),
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_BLOCK, wrapper.get_function(), wrapper.get_user_data(), 0,
std::numeric_limits<pointer_type>::max()));
container->add(std::move(wrapper), std::move(hook));
@@ -455,8 +465,8 @@ namespace unicorn
unicorn_hook hook{*this};
auto container = std::make_unique<hook_container>();
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INTR, wrapper.get_function(),
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INTR, wrapper.get_function(), wrapper.get_user_data(), 0,
std::numeric_limits<pointer_type>::max()));
container->add(std::move(wrapper), std::move(hook));
@@ -468,16 +478,16 @@ namespace unicorn
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) override
{
function_wrapper<bool, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
[c = std::move(callback), this](uc_engine*, const uc_mem_type type, const uint64_t address,
const int size, const int64_t) {
[c = std::move(callback), this](uc_engine*, const uc_mem_type type, const uint64_t address, const int size,
const int64_t) {
const auto ip = this->read_instruction_pointer();
assert(size >= 0);
const auto operation = map_memory_operation(type);
const auto violation = map_memory_violation_type(type);
const auto resume = c(address, static_cast<uint64_t>(size), operation, violation) ==
memory_violation_continuation::resume;
const auto resume =
c(address, static_cast<uint64_t>(size), operation, violation) == memory_violation_continuation::resume;
const auto new_ip = this->read_instruction_pointer();
const auto has_ip_changed = ip != new_ip;
@@ -507,8 +517,8 @@ namespace unicorn
unicorn_hook hook{*this};
auto container = std::make_unique<hook_container>();
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_INVALID, wrapper.get_function(),
wrapper.get_user_data(), 0, std::numeric_limits<uint64_t>::max()));
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_INVALID, wrapper.get_function(), wrapper.get_user_data(), 0,
std::numeric_limits<uint64_t>::max()));
container->add(std::move(wrapper), std::move(hook));
@@ -517,11 +527,9 @@ namespace unicorn
return result;
}
emulator_hook* hook_memory_execution(const uint64_t address, const uint64_t size,
memory_execution_hook_callback callback)
emulator_hook* hook_memory_execution(const uint64_t address, const uint64_t size, memory_execution_hook_callback callback)
{
auto exec_wrapper = [c = std::move(callback)](uc_engine*, const uint64_t address,
const uint32_t /*size*/) {
auto exec_wrapper = [c = std::move(callback)](uc_engine*, const uint64_t address, const uint32_t /*size*/) {
c(address); //
};
@@ -529,8 +537,8 @@ namespace unicorn
unicorn_hook hook{*this};
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_CODE, wrapper.get_function(),
wrapper.get_user_data(), address, address + size));
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_CODE, wrapper.get_function(), wrapper.get_user_data(), address,
address + size));
auto* container = this->create_hook_container();
container->add(std::move(wrapper), std::move(hook));
@@ -542,17 +550,14 @@ namespace unicorn
return this->hook_memory_execution(0, std::numeric_limits<uint64_t>::max(), std::move(callback));
}
emulator_hook* hook_memory_execution(const uint64_t address,
memory_execution_hook_callback callback) override
emulator_hook* hook_memory_execution(const uint64_t address, memory_execution_hook_callback callback) override
{
return this->hook_memory_execution(address, 1, std::move(callback));
}
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size,
memory_access_hook_callback callback) override
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override
{
auto read_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type,
const uint64_t address, const int length,
auto read_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, const uint64_t address, const int length,
const uint64_t value) {
const auto operation = map_memory_operation(type);
if (operation == memory_operation::read && length > 0)
@@ -561,23 +566,21 @@ namespace unicorn
}
};
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
std::move(read_wrapper));
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(std::move(read_wrapper));
unicorn_hook hook{*this};
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_READ_AFTER, wrapper.get_function(),
wrapper.get_user_data(), address, address + size));
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_READ_AFTER, wrapper.get_function(), wrapper.get_user_data(),
address, address + size));
auto* container = this->create_hook_container();
container->add(std::move(wrapper), std::move(hook));
return container->as_opaque_hook();
}
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size,
memory_access_hook_callback callback) override
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override
{
auto write_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, const uint64_t addr,
const int length, const uint64_t value) {
auto write_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, const uint64_t addr, const int length,
const uint64_t value) {
const auto operation = map_memory_operation(type);
if (operation == memory_operation::write && length > 0)
{
@@ -585,13 +588,12 @@ namespace unicorn
}
};
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
std::move(write_wrapper));
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(std::move(write_wrapper));
unicorn_hook hook{*this};
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_WRITE, wrapper.get_function(),
wrapper.get_user_data(), address, address + size));
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_WRITE, wrapper.get_function(), wrapper.get_user_data(), address,
address + size));
auto* container = this->create_hook_container();
container->add(std::move(wrapper), std::move(hook));
@@ -608,14 +610,14 @@ namespace unicorn
void delete_hook(emulator_hook* hook) override
{
const auto entry =
std::ranges::find_if(this->hooks_, [&](const std::unique_ptr<hook_object>& hook_ptr) {
return hook_ptr->as_opaque_hook() == hook;
});
const auto entry = std::ranges::find_if(
this->hooks_, [&](const std::unique_ptr<hook_object>& hook_ptr) { return hook_ptr->as_opaque_hook() == hook; });
if (entry != this->hooks_.end())
{
const auto obj = std::move(*entry);
this->hooks_.erase(entry);
(void)obj;
}
}