Simplify hooking interface

This commit is contained in:
momo5502
2025-04-05 11:59:49 +02:00
parent a336bdf2af
commit 349526a54a
9 changed files with 226 additions and 185 deletions

View File

@@ -42,13 +42,15 @@ namespace
win_emu.emu().hook_memory_write( win_emu.emu().hook_memory_write(
win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters), 0x8, win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
[&win_emu, cache_logging, params_hook, modules](const uint64_t address, size_t, [&win_emu, cache_logging, params_hook, modules](const uint64_t address, const void*, size_t) mutable {
const uint64_t value) mutable {
const auto target_address = win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters); const auto target_address = win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters);
if (address == target_address) if (address == target_address)
{ {
const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{win_emu.emu(), value}; const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{
win_emu.emu(),
win_emu.emu().read_memory<uint64_t>(address),
};
win_emu.emu().delete_hook(params_hook); win_emu.emu().delete_hook(params_hook);
params_hook = watch_object(win_emu, modules, obj, cache_logging); params_hook = watch_object(win_emu, modules, obj, cache_logging);
@@ -236,7 +238,7 @@ namespace
continue; 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(); const auto rip = win_emu->emu().read_instruction_pointer();
if (!win_emu->mod_manager.executable->is_within(rip)) if (!win_emu->mod_manager.executable->is_within(rip))
{ {
@@ -258,7 +260,7 @@ namespace
section.name.c_str(), address, rip); 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(); const auto rip = win_emu->emu().read_instruction_pointer();
if (!win_emu->mod_manager.executable->is_within(rip)) if (!win_emu->mod_manager.executable->is_within(rip))
{ {

View File

@@ -11,7 +11,7 @@ emulator_hook* watch_object(windows_emulator& emu, const std::set<std::string, s
return emu.emu().hook_memory_read( return emu.emu().hook_memory_read(
object.value(), object.size(), object.value(), object.size(),
[i = std::move(info), object, &emu, cache_logging, modules](const uint64_t address, size_t, uint64_t) { [i = std::move(info), object, &emu, cache_logging, modules](const uint64_t address, const void*, size_t) {
const auto rip = emu.emu().read_instruction_pointer(); const auto rip = emu.emu().read_instruction_pointer();
const auto* mod = emu.mod_manager.find_by_address(rip); const auto* mod = emu.mod_manager.find_by_address(rip);
const auto is_main_access = mod == emu.mod_manager.executable || modules.contains(mod->name); const auto is_main_access = mod == emu.mod_manager.executable || modules.contains(mod->name);

View File

@@ -40,11 +40,11 @@ using edge_generation_hook_callback =
using basic_block_hook_callback = std::function<void(const basic_block& block)>; using basic_block_hook_callback = std::function<void(const basic_block& block)>;
using instruction_hook_callback = std::function<instruction_hook_continuation()>; using instruction_hook_callback = std::function<instruction_hook_continuation()>;
using interrupt_hook_callback = std::function<void(int interrupt)>; using interrupt_hook_callback = std::function<void(int interrupt)>;
using simple_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value)>;
using complex_memory_hook_callback = using memory_access_hook_callback = std::function<void(uint64_t address, const void* data, size_t size)>;
std::function<void(uint64_t address, size_t size, uint64_t value, memory_operation operation)>; using memory_execution_hook_callback = std::function<void(uint64_t address)>;
using memory_violation_hook_callback = std::function<memory_violation_continuation( using memory_violation_hook_callback = std::function<memory_violation_continuation(
uint64_t address, size_t size, memory_operation operation, memory_violation_type type)>; uint64_t address, size_t size, memory_operation operation, memory_violation_type type)>;
@@ -53,43 +53,18 @@ class hook_interface
public: public:
virtual ~hook_interface() = default; 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_instruction(int instruction_type, instruction_hook_callback callback) = 0;
virtual emulator_hook* hook_interrupt(interrupt_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_edge_generation(edge_generation_hook_callback callback) = 0;
virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0; virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0;
virtual void delete_hook(emulator_hook* hook) = 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<uint8_t>(operation) & (static_cast<uint8_t>(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); });
}
}; };

View File

@@ -7,8 +7,13 @@ class scoped_hook
scoped_hook() = default; scoped_hook() = default;
scoped_hook(emulator& emu, emulator_hook* hook) scoped_hook(emulator& emu, emulator_hook* hook)
: scoped_hook(emu, std::vector{hook})
{
}
scoped_hook(emulator& emu, std::vector<emulator_hook*> hooks)
: emu_(&emu), : emu_(&emu),
hook_(hook) hooks_(std::move(hooks))
{ {
} }
@@ -31,9 +36,9 @@ class scoped_hook
{ {
this->remove(); this->remove();
this->emu_ = obj.emu_; this->emu_ = obj.emu_;
this->hook_ = obj.hook_; this->hooks_ = std::move(obj.hooks_);
obj.hook_ = {}; obj.hooks_ = {};
} }
return *this; return *this;
@@ -41,14 +46,22 @@ class scoped_hook
void remove() void remove()
{ {
if (this->hook_) auto hooks = std::move(this->hooks_);
this->hooks_ = {};
for (auto* hook : hooks_)
{ {
this->emu_->delete_hook(this->hook_); try
this->hook_ = {}; {
this->emu_->delete_hook(hook);
}
catch (...)
{
}
} }
} }
private: private:
emulator* emu_{}; emulator* emu_{};
emulator_hook* hook_{}; std::vector<emulator_hook*> hooks_{};
}; };

View File

@@ -35,7 +35,9 @@ namespace
void forward_emulator(windows_emulator& win_emu) void forward_emulator(windows_emulator& win_emu)
{ {
const auto target = win_emu.mod_manager.executable->find_export("vulnerable"); 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); run_emulation(win_emu);
} }
@@ -64,7 +66,9 @@ namespace
const auto ret = emu.emu().read_stack(0); 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() void restore_emulator()

View File

@@ -298,36 +298,46 @@ namespace icicle
return wrap_hook(id); return wrap_hook(id);
} }
emulator_hook* hook_memory_access(const uint64_t address, const size_t size, const memory_operation filter, emulator_hook* hook_memory_execution(const uint64_t address, memory_execution_hook_callback callback) override
complex_memory_hook_callback callback) override
{ {
if (filter == memory_permission::none) // TODO
{ (void)address;
return nullptr; throw std::runtime_error("Not implemented");
} }
const auto shared_callback = std::make_shared<complex_memory_hook_callback>(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<memory_execution_hook_callback*>(user);
(func)(addr);
};
if ((filter & memory_permission::exec) == memory_permission::exec) const auto id = icicle_add_execution_hook(this->emu_, func, ptr);
{ this->hooks_[id] = std::move(wrapper);
if (address != 0 || size != std::numeric_limits<size_t>::max())
{
throw std::runtime_error("Not supported!");
}
auto* ptr = shared_callback.get(); return wrap_hook(id);
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); emulator_hook* hook_memory_read(const uint64_t address, const size_t size,
this->hooks_[id] = std::move(wrapper); memory_access_hook_callback callback) override
{
// TODO
(void)address;
(void)size;
(void)callback;
throw std::runtime_error("Not implemented");
}
return wrap_hook(id); emulator_hook* hook_memory_write(const uint64_t address, const size_t size,
} const memory_access_hook_callback callback) override
{
return nullptr; // TODO
(void)address;
(void)size;
(void)callback;
throw std::runtime_error("Not implemented");
} }
void delete_hook(emulator_hook* hook) override void delete_hook(emulator_hook* hook) override

View File

@@ -175,63 +175,6 @@ namespace unicorn
size_t size_{}; 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<complex_memory_hook_callback>& callback)
{
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> 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<uint64_t>(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<complex_memory_hook_callback>& callback)
{
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> 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<uint64_t>(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<complex_memory_hook_callback>& callback)
{
function_wrapper<void, uc_engine*, uint64_t, uint32_t> 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 map_block(const uc_tb& translation_block)
{ {
basic_block block{}; basic_block block{};
@@ -581,38 +524,94 @@ namespace unicorn
return result; return result;
} }
emulator_hook* hook_memory_access(const uint64_t address, const size_t size, const memory_operation filter, emulator_hook* hook_memory_execution(uint64_t address, uint64_t size,
complex_memory_hook_callback callback) override memory_execution_hook_callback callback)
{ {
if (filter == memory_permission::none) auto exec_wrapper = [c = std::move(callback)](uc_engine*, const uint64_t address,
{ const uint32_t /*size*/) {
return nullptr; c(address); //
} };
const auto shared_callback = std::make_shared<complex_memory_hook_callback>(std::move(callback)); function_wrapper<void, uc_engine*, uint64_t, uint32_t> 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<uint64_t>::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_t>(size), sizeof(value)));
}
};
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));
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_t>(size), sizeof(value)));
}
};
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));
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<hook_container>(); auto container = std::make_unique<hook_container>();
auto* ptr = container.get();
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();
this->hooks_.push_back(std::move(container)); this->hooks_.push_back(std::move(container));
return ptr;
return result;
} }
void delete_hook(emulator_hook* hook) override void delete_hook(emulator_hook* hook) override

View File

@@ -517,9 +517,9 @@ void windows_emulator::setup_hooks()
return memory_violation_continuation::resume; return memory_violation_continuation::resume;
}); });
this->emu().hook_memory_execution( this->emu().hook_memory_execution([&](const uint64_t address) {
0, std::numeric_limits<size_t>::max(), this->on_instruction_execution(address); //
[&](const uint64_t address, const size_t, const uint64_t) { this->on_instruction_execution(address); }); });
} }
void windows_emulator::start(size_t count) void windows_emulator::start(size_t count)

View File

@@ -8,26 +8,6 @@
#include "x64_register_mapping.hpp" #include "x64_register_mapping.hpp"
#include "x64_target_descriptions.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 struct breakpoint_key
{ {
size_t addr{}; size_t addr{};
@@ -196,11 +176,9 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
try try
{ {
return this->hooks_.access<bool>([&](hook_map& hooks) { return this->hooks_.access<bool>([&](hook_map& hooks) {
hooks[{addr, size, type}] = scoped_hook( auto hook_vector = this->create_hook(type, addr, size);
*this->emu_, this->emu_->hook_memory_access(addr, size, map_breakpoint_type(type),
[this](uint64_t, size_t, uint64_t, memory_operation) { hooks[{addr, size, type}] = scoped_hook(*this->emu_, std::move(hook_vector));
this->on_interrupt(); //
}));
return true; return true;
}); });
@@ -264,4 +242,64 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
using hook_map = std::unordered_map<breakpoint_key, scoped_hook>; using hook_map = std::unordered_map<breakpoint_key, scoped_hook>;
utils::concurrency::container<hook_map> hooks_{}; utils::concurrency::container<hook_map> hooks_{};
std::vector<emulator_hook*> create_execute_hook(const uint64_t addr, const size_t size)
{
std::vector<emulator_hook*> 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<emulator_hook*> 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<emulator_hook*> 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<emulator_hook*> 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");
}
}
}; };