From 349526a54ac91fdb783893be3002660a15095453 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 5 Apr 2025 11:59:49 +0200 Subject: [PATCH] Simplify hooking interface --- src/analyzer/main.cpp | 12 +- src/analyzer/object_watching.hpp | 2 +- src/emulator/hook_interface.hpp | 43 +---- src/emulator/scoped_hook.hpp | 27 ++- src/fuzzer/main.cpp | 8 +- src/icicle-emulator/icicle_x64_emulator.cpp | 58 +++--- src/unicorn-emulator/unicorn_x64_emulator.cpp | 167 +++++++++--------- src/windows-emulator/windows_emulator.cpp | 6 +- src/windows-gdb-stub/x64_gdb_stub_handler.hpp | 88 ++++++--- 9 files changed, 226 insertions(+), 185 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index e72a3e3d..3c27000b 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -42,13 +42,15 @@ namespace win_emu.emu().hook_memory_write( win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters), 0x8, - [&win_emu, cache_logging, params_hook, modules](const uint64_t address, size_t, - const uint64_t value) mutable { + [&win_emu, cache_logging, params_hook, modules](const uint64_t address, const void*, size_t) mutable { const auto target_address = win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters); if (address == target_address) { - const emulator_object obj{win_emu.emu(), value}; + const emulator_object obj{ + win_emu.emu(), + win_emu.emu().read_memory(address), + }; win_emu.emu().delete_hook(params_hook); params_hook = watch_object(win_emu, modules, obj, cache_logging); @@ -236,7 +238,7 @@ namespace continue; } - auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) { + auto read_handler = [&, section, concise_logging](const uint64_t address, const void*, size_t) { const auto rip = win_emu->emu().read_instruction_pointer(); if (!win_emu->mod_manager.executable->is_within(rip)) { @@ -258,7 +260,7 @@ namespace section.name.c_str(), address, rip); }; - const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) { + const auto write_handler = [&, section, concise_logging](const uint64_t address, const void*, size_t) { const auto rip = win_emu->emu().read_instruction_pointer(); if (!win_emu->mod_manager.executable->is_within(rip)) { diff --git a/src/analyzer/object_watching.hpp b/src/analyzer/object_watching.hpp index 0ee5d3f1..df8ab871 100644 --- a/src/analyzer/object_watching.hpp +++ b/src/analyzer/object_watching.hpp @@ -11,7 +11,7 @@ emulator_hook* watch_object(windows_emulator& emu, const std::setname); diff --git a/src/emulator/hook_interface.hpp b/src/emulator/hook_interface.hpp index 01762b8f..96ae6195 100644 --- a/src/emulator/hook_interface.hpp +++ b/src/emulator/hook_interface.hpp @@ -40,11 +40,11 @@ using edge_generation_hook_callback = using basic_block_hook_callback = std::function; using instruction_hook_callback = std::function; - using interrupt_hook_callback = std::function; -using simple_memory_hook_callback = std::function; -using complex_memory_hook_callback = - std::function; + +using memory_access_hook_callback = std::function; +using memory_execution_hook_callback = std::function; + using memory_violation_hook_callback = std::function; @@ -53,43 +53,18 @@ class hook_interface public: virtual ~hook_interface() = default; - virtual emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) = 0; + virtual emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) = 0; + virtual emulator_hook* hook_memory_execution(uint64_t address, memory_execution_hook_callback callback) = 0; + virtual emulator_hook* hook_memory_read(uint64_t address, size_t size, memory_access_hook_callback callback) = 0; + virtual emulator_hook* hook_memory_write(uint64_t address, size_t size, memory_access_hook_callback callback) = 0; - virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter, - complex_memory_hook_callback callback) = 0; virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0; virtual emulator_hook* hook_interrupt(interrupt_hook_callback callback) = 0; + virtual emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) = 0; virtual emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) = 0; virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0; virtual void delete_hook(emulator_hook* hook) = 0; - - emulator_hook* hook_memory_read(const uint64_t address, const size_t size, simple_memory_hook_callback callback) - { - return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::read); - } - - emulator_hook* hook_memory_write(const uint64_t address, const size_t size, simple_memory_hook_callback callback) - { - return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::write); - } - - emulator_hook* hook_memory_execution(const uint64_t address, const size_t size, - simple_memory_hook_callback callback) - { - return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::exec); - } - - private: - emulator_hook* hook_simple_memory_access(const uint64_t address, const size_t size, - simple_memory_hook_callback callback, const memory_operation operation) - { - assert((static_cast(operation) & (static_cast(operation) - 1)) == 0); - return this->hook_memory_access(address, size, operation, - [c = std::move(callback)](const uint64_t a, const size_t s, - const uint64_t value, - memory_operation) { c(a, s, value); }); - } }; diff --git a/src/emulator/scoped_hook.hpp b/src/emulator/scoped_hook.hpp index 188eeea3..6246b760 100644 --- a/src/emulator/scoped_hook.hpp +++ b/src/emulator/scoped_hook.hpp @@ -7,8 +7,13 @@ class scoped_hook scoped_hook() = default; scoped_hook(emulator& emu, emulator_hook* hook) + : scoped_hook(emu, std::vector{hook}) + { + } + + scoped_hook(emulator& emu, std::vector hooks) : emu_(&emu), - hook_(hook) + hooks_(std::move(hooks)) { } @@ -31,9 +36,9 @@ class scoped_hook { this->remove(); this->emu_ = obj.emu_; - this->hook_ = obj.hook_; + this->hooks_ = std::move(obj.hooks_); - obj.hook_ = {}; + obj.hooks_ = {}; } return *this; @@ -41,14 +46,22 @@ class scoped_hook void remove() { - if (this->hook_) + auto hooks = std::move(this->hooks_); + this->hooks_ = {}; + + for (auto* hook : hooks_) { - this->emu_->delete_hook(this->hook_); - this->hook_ = {}; + try + { + this->emu_->delete_hook(hook); + } + catch (...) + { + } } } private: emulator* emu_{}; - emulator_hook* hook_{}; + std::vector hooks_{}; }; diff --git a/src/fuzzer/main.cpp b/src/fuzzer/main.cpp index 9c10eee1..237d357c 100644 --- a/src/fuzzer/main.cpp +++ b/src/fuzzer/main.cpp @@ -35,7 +35,9 @@ namespace void forward_emulator(windows_emulator& win_emu) { const auto target = win_emu.mod_manager.executable->find_export("vulnerable"); - win_emu.emu().hook_memory_execution(target, 1, [&](uint64_t, size_t, uint64_t) { win_emu.emu().stop(); }); + win_emu.emu().hook_memory_execution(target, [&](uint64_t) { + win_emu.emu().stop(); // + }); run_emulation(win_emu); } @@ -64,7 +66,9 @@ namespace const auto ret = emu.emu().read_stack(0); - emu.emu().hook_memory_execution(ret, 1, [&](uint64_t, size_t, uint64_t) { emu.emu().stop(); }); + emu.emu().hook_memory_execution(ret, [&](uint64_t) { + emu.emu().stop(); // + }); } void restore_emulator() diff --git a/src/icicle-emulator/icicle_x64_emulator.cpp b/src/icicle-emulator/icicle_x64_emulator.cpp index 8de2594e..071543df 100644 --- a/src/icicle-emulator/icicle_x64_emulator.cpp +++ b/src/icicle-emulator/icicle_x64_emulator.cpp @@ -298,36 +298,46 @@ namespace icicle return wrap_hook(id); } - emulator_hook* hook_memory_access(const uint64_t address, const size_t size, const memory_operation filter, - complex_memory_hook_callback callback) override + emulator_hook* hook_memory_execution(const uint64_t address, memory_execution_hook_callback callback) override { - if (filter == memory_permission::none) - { - return nullptr; - } + // TODO + (void)address; + throw std::runtime_error("Not implemented"); + } - const auto shared_callback = std::make_shared(std::move(callback)); + emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) override + { + auto wrapper = make_function_object(std::move(callback)); + auto* ptr = wrapper.get(); + auto* func = +[](void* user, const uint64_t addr) { + auto& func = *static_cast(user); + (func)(addr); + }; - if ((filter & memory_permission::exec) == memory_permission::exec) - { - if (address != 0 || size != std::numeric_limits::max()) - { - throw std::runtime_error("Not supported!"); - } + const auto id = icicle_add_execution_hook(this->emu_, func, ptr); + this->hooks_[id] = std::move(wrapper); - auto* ptr = shared_callback.get(); - auto wrapper = wrap_shared(shared_callback); - auto* func = +[](void* user, const uint64_t ptr) { - (*static_cast(user))(ptr, 0, 0, memory_permission::exec); - }; + return wrap_hook(id); + } - const auto id = icicle_add_execution_hook(this->emu_, func, ptr); - this->hooks_[id] = std::move(wrapper); + emulator_hook* hook_memory_read(const uint64_t address, const size_t size, + memory_access_hook_callback callback) override + { + // TODO + (void)address; + (void)size; + (void)callback; + throw std::runtime_error("Not implemented"); + } - return wrap_hook(id); - } - - return nullptr; + emulator_hook* hook_memory_write(const uint64_t address, const size_t size, + const memory_access_hook_callback callback) override + { + // TODO + (void)address; + (void)size; + (void)callback; + throw std::runtime_error("Not implemented"); } void delete_hook(emulator_hook* hook) override diff --git a/src/unicorn-emulator/unicorn_x64_emulator.cpp b/src/unicorn-emulator/unicorn_x64_emulator.cpp index a91e44e5..1e18be59 100644 --- a/src/unicorn-emulator/unicorn_x64_emulator.cpp +++ b/src/unicorn-emulator/unicorn_x64_emulator.cpp @@ -175,63 +175,6 @@ namespace unicorn size_t size_{}; }; - void add_read_hook(uc_engine* uc, const uint64_t address, const size_t size, hook_container& container, - const std::shared_ptr& callback) - { - function_wrapper wrapper( - [callback](uc_engine*, const uc_mem_type type, const uint64_t address, const int size, const int64_t) { - const auto operation = map_memory_operation(type); - if (operation != memory_permission::none) - { - (*callback)(address, static_cast(size), 0, operation); - } - }); - - unicorn_hook hook{uc}; - - uce(uc_hook_add(uc, hook.make_reference(), UC_HOOK_MEM_READ, wrapper.get_function(), - wrapper.get_user_data(), address, address + size)); - - container.add(std::move(wrapper), std::move(hook)); - } - - void add_write_hook(uc_engine* uc, const uint64_t address, const size_t size, hook_container& container, - const std::shared_ptr& callback) - { - function_wrapper wrapper( - [callback](uc_engine*, const uc_mem_type type, const uint64_t address, const int size, - const uint64_t value) { - const auto operation = map_memory_operation(type); - if (operation != memory_permission::none) - { - (*callback)(address, static_cast(size), value, operation); - } - }); - - unicorn_hook hook{uc}; - - uce(uc_hook_add(uc, hook.make_reference(), UC_HOOK_MEM_WRITE, wrapper.get_function(), - wrapper.get_user_data(), address, address + size)); - - container.add(std::move(wrapper), std::move(hook)); - } - - void add_exec_hook(uc_engine* uc, const uint64_t address, const size_t size, hook_container& container, - const std::shared_ptr& callback) - { - function_wrapper wrapper( - [callback](uc_engine*, const uint64_t address, const uint32_t size) { - (*callback)(address, size, 0, memory_permission::exec); - }); - - unicorn_hook hook{uc}; - - uce(uc_hook_add(uc, hook.make_reference(), UC_HOOK_CODE, wrapper.get_function(), wrapper.get_user_data(), - address, address + size)); - - container.add(std::move(wrapper), std::move(hook)); - } - basic_block map_block(const uc_tb& translation_block) { basic_block block{}; @@ -581,38 +524,94 @@ namespace unicorn return result; } - emulator_hook* hook_memory_access(const uint64_t address, const size_t size, const memory_operation filter, - complex_memory_hook_callback callback) override + emulator_hook* hook_memory_execution(uint64_t address, uint64_t size, + memory_execution_hook_callback callback) { - if (filter == memory_permission::none) - { - return nullptr; - } + auto exec_wrapper = [c = std::move(callback)](uc_engine*, const uint64_t address, + const uint32_t /*size*/) { + c(address); // + }; - const auto shared_callback = std::make_shared(std::move(callback)); + function_wrapper wrapper(std::move(exec_wrapper)); + 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)); + + auto* container = this->create_hook_container(); + container->add(std::move(wrapper), std::move(hook)); + return container->as_opaque_hook(); + } + + emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) override + { + return this->hook_memory_execution(0, std::numeric_limits::max(), std::move(callback)); + } + + 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(uint64_t address, size_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 size, + const uint64_t value) { + const auto operation = map_memory_operation(type); + if (operation != memory_permission::none && size > 0) + { + c(address, &value, std::min(static_cast(size), sizeof(value))); + } + }; + + function_wrapper 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)); + + auto* container = this->create_hook_container(); + container->add(std::move(wrapper), std::move(hook)); + return container->as_opaque_hook(); + } + + emulator_hook* hook_memory_write(uint64_t address, size_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 address, const int size, + const uint64_t value) { + const auto operation = map_memory_operation(type); + if (operation != memory_permission::none && size > 0) + { + c(address, &value, std::min(static_cast(size), sizeof(value))); + } + }; + + function_wrapper 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)); + + auto* container = this->create_hook_container(); + container->add(std::move(wrapper), std::move(hook)); + return container->as_opaque_hook(); + } + + hook_container* create_hook_container() + { auto container = std::make_unique(); - - if ((filter & memory_operation::read) != memory_operation::none) - { - add_read_hook(*this, address, size, *container, shared_callback); - } - - if ((filter & memory_operation::write) != memory_operation::none) - { - add_write_hook(*this, address, size, *container, shared_callback); - } - - if ((filter & memory_operation::exec) != memory_operation::none) - { - add_exec_hook(*this, address, size, *container, shared_callback); - } - - auto* result = container->as_opaque_hook(); - + auto* ptr = container.get(); this->hooks_.push_back(std::move(container)); - - return result; + return ptr; } void delete_hook(emulator_hook* hook) override diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index b9abddab..9b200b63 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -517,9 +517,9 @@ void windows_emulator::setup_hooks() return memory_violation_continuation::resume; }); - this->emu().hook_memory_execution( - 0, std::numeric_limits::max(), - [&](const uint64_t address, const size_t, const uint64_t) { this->on_instruction_execution(address); }); + this->emu().hook_memory_execution([&](const uint64_t address) { + this->on_instruction_execution(address); // + }); } void windows_emulator::start(size_t count) diff --git a/src/windows-gdb-stub/x64_gdb_stub_handler.hpp b/src/windows-gdb-stub/x64_gdb_stub_handler.hpp index 6fedb346..aa519acd 100644 --- a/src/windows-gdb-stub/x64_gdb_stub_handler.hpp +++ b/src/windows-gdb-stub/x64_gdb_stub_handler.hpp @@ -8,26 +8,6 @@ #include "x64_register_mapping.hpp" #include "x64_target_descriptions.hpp" -inline memory_operation map_breakpoint_type(const gdb_stub::breakpoint_type type) -{ - using enum gdb_stub::breakpoint_type; - - switch (type) - { - case software: - case hardware_exec: - return memory_operation::exec; - case hardware_read: - return memory_permission::read; - case hardware_write: - return memory_permission::write; - case hardware_read_write: - return memory_permission::read_write; - default: - throw std::runtime_error("Bad bp type"); - } -} - struct breakpoint_key { size_t addr{}; @@ -196,11 +176,9 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler try { return this->hooks_.access([&](hook_map& hooks) { - hooks[{addr, size, type}] = scoped_hook( - *this->emu_, this->emu_->hook_memory_access(addr, size, map_breakpoint_type(type), - [this](uint64_t, size_t, uint64_t, memory_operation) { - this->on_interrupt(); // - })); + auto hook_vector = this->create_hook(type, addr, size); + + hooks[{addr, size, type}] = scoped_hook(*this->emu_, std::move(hook_vector)); return true; }); @@ -264,4 +242,64 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler using hook_map = std::unordered_map; utils::concurrency::container hooks_{}; + + std::vector create_execute_hook(const uint64_t addr, const size_t size) + { + std::vector hooks{}; + hooks.reserve(size); + + for (size_t i = 0; i < size; ++i) + { + auto* hook = this->emu_->hook_memory_execution(addr + i, [this](const uint64_t) { + this->on_interrupt(); // + }); + + hooks.push_back(hook); + } + + return hooks; + } + + std::vector create_read_hook(const uint64_t addr, const size_t size) + { + auto* hook = this->emu_->hook_memory_read(addr, size, [this](const uint64_t, const void*, const size_t) { + this->on_interrupt(); // + }); + + return {hook}; + } + + std::vector create_write_hook(const uint64_t addr, const size_t size) + { + auto* hook = this->emu_->hook_memory_write(addr, size, [this](const uint64_t, const void*, const size_t) { + this->on_interrupt(); // + }); + + return {hook}; + } + + std::vector create_hook(const gdb_stub::breakpoint_type type, const uint64_t addr, + const size_t size) + { + using enum gdb_stub::breakpoint_type; + + switch (type) + { + case software: + case hardware_exec: + return this->create_execute_hook(addr, size); + case hardware_read: + return this->create_read_hook(addr, size); + case hardware_write: + return this->create_write_hook(addr, size); + case hardware_read_write: { + auto hooks1 = this->create_hook(hardware_read, addr, size); + auto hooks2 = this->create_hook(hardware_write, addr, size); + hooks1.insert(hooks1.end(), hooks2.begin(), hooks2.end()); + return hooks1; + } + default: + throw std::runtime_error("Bad bp type"); + } + } };