From 5b2d84ad7e21968af344f1ede23922f066284510 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 21 Aug 2024 20:21:13 +0200 Subject: [PATCH] Cleanup unicorn emulator --- src/unicorn_emulator/function_wrapper.hpp | 36 +++ src/unicorn_emulator/object.hpp | 12 + src/unicorn_emulator/unicorn.hpp | 36 +++ src/unicorn_emulator/unicorn_hook.hpp | 76 +++++ .../unicorn_memory_regions.hpp | 67 ++++ src/unicorn_emulator/unicorn_x64_emulator.cpp | 285 ++++-------------- 6 files changed, 294 insertions(+), 218 deletions(-) create mode 100644 src/unicorn_emulator/function_wrapper.hpp create mode 100644 src/unicorn_emulator/object.hpp create mode 100644 src/unicorn_emulator/unicorn.hpp create mode 100644 src/unicorn_emulator/unicorn_hook.hpp create mode 100644 src/unicorn_emulator/unicorn_memory_regions.hpp diff --git a/src/unicorn_emulator/function_wrapper.hpp b/src/unicorn_emulator/function_wrapper.hpp new file mode 100644 index 00000000..daa48a91 --- /dev/null +++ b/src/unicorn_emulator/function_wrapper.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +#include "object.hpp" + +template +class function_wrapper : public object +{ +public: + using user_data_pointer = void*; + using c_function_type = ReturnType(Args..., user_data_pointer); + using functor_type = std::function; + + function_wrapper(functor_type functor) + : functor_(std::make_unique(std::move(functor))) + { + } + + c_function_type* get_function() const + { + return +[](Args... args, user_data_pointer user_data) -> ReturnType + { + return (*static_cast(user_data))(std::forward(args)...); + }; + } + + user_data_pointer get_user_data() const + { + return this->functor_.get(); + } + +private: + std::unique_ptr functor_{}; +}; diff --git a/src/unicorn_emulator/object.hpp b/src/unicorn_emulator/object.hpp new file mode 100644 index 00000000..28cf52b0 --- /dev/null +++ b/src/unicorn_emulator/object.hpp @@ -0,0 +1,12 @@ +#pragma once + +struct object +{ + object() = default; + virtual ~object() = default; + + object(object&&) = default; + object(const object&) = default; + object& operator=(object&&) = default; + object& operator=(const object&) = default; +}; diff --git a/src/unicorn_emulator/unicorn.hpp b/src/unicorn_emulator/unicorn.hpp new file mode 100644 index 00000000..e82ab63d --- /dev/null +++ b/src/unicorn_emulator/unicorn.hpp @@ -0,0 +1,36 @@ +#pragma once + +#pragma warning(push) +#pragma warning(disable: 4505) +#define NOMINMAX +#include +#pragma warning(pop) + +#include + +namespace unicorn +{ + struct unicorn_error : std::runtime_error + { + unicorn_error(const uc_err error_code) + : std::runtime_error(uc_strerror(error_code)) + , code(error_code) + { + } + + uc_err code{}; + }; + + inline void throw_if_unicorn_error(const uc_err error_code) + { + if (error_code != UC_ERR_OK) + { + throw unicorn_error(error_code); + } + } + + inline void uce(const uc_err error_code) + { + throw_if_unicorn_error(error_code); + } +} diff --git a/src/unicorn_emulator/unicorn_hook.hpp b/src/unicorn_emulator/unicorn_hook.hpp new file mode 100644 index 00000000..558eed1d --- /dev/null +++ b/src/unicorn_emulator/unicorn_hook.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include "unicorn.hpp" + +namespace unicorn +{ + class unicorn_hook + { + public: + unicorn_hook() = default; + + unicorn_hook(uc_engine* uc) + : unicorn_hook(uc, {}) + { + } + + unicorn_hook(uc_engine* uc, const uc_hook hook) + : uc_(uc) + , hook_(hook) + { + } + + ~unicorn_hook() + { + release(); + } + + unicorn_hook(const unicorn_hook&) = delete; + unicorn_hook& operator=(const unicorn_hook&) = delete; + + + unicorn_hook(unicorn_hook&& obj) noexcept + { + this->operator=(std::move(obj)); + } + + uc_hook* make_reference() + { + if (!this->uc_) + { + throw std::runtime_error("Cannot make reference on default constructed hook"); + } + + this->release(); + return &this->hook_; + } + + unicorn_hook& operator=(unicorn_hook&& obj) noexcept + { + if (this != &obj) + { + this->release(); + + this->uc_ = obj.uc_; + this->hook_ = obj.hook_; + obj.uc_ = {}; + } + + + return *this; + } + + void release() + { + if (this->hook_ && this->uc_) + { + uc_hook_del(this->uc_, this->hook_); + this->hook_ = {}; + } + } + + private: + uc_engine* uc_{}; + uc_hook hook_{}; + }; +} diff --git a/src/unicorn_emulator/unicorn_memory_regions.hpp b/src/unicorn_emulator/unicorn_memory_regions.hpp new file mode 100644 index 00000000..55bc6a6c --- /dev/null +++ b/src/unicorn_emulator/unicorn_memory_regions.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include "unicorn.hpp" + +namespace unicorn +{ + class unicorn_memory_regions + { + public: + unicorn_memory_regions(uc_engine* uc) + { + uce(uc_mem_regions(uc, &this->regions_, &this->count_)); + } + + + ~unicorn_memory_regions() + { + this->release(); + } + + unicorn_memory_regions(const unicorn_memory_regions&) = delete; + unicorn_memory_regions& operator=(const unicorn_memory_regions&) = delete; + + unicorn_memory_regions(unicorn_memory_regions&& obj) noexcept + { + this->operator=(std::move(obj)); + } + + unicorn_memory_regions& operator=(unicorn_memory_regions&& obj) noexcept + { + if (this != &obj) + { + this->release(); + + this->count_ = obj.count_; + this->regions_ = obj.regions_; + + obj.count_ = {}; + obj.regions_ = nullptr; + } + + return *this; + } + + std::span get_span() const + { + return {this->regions_, this->count_}; + } + + private: + uint32_t count_{}; + uc_mem_region* regions_{}; + + void release() + { + if (this->regions_) + { + uc_free(regions_); + } + + this->count_ = {}; + this->regions_ = nullptr; + } + }; +} diff --git a/src/unicorn_emulator/unicorn_x64_emulator.cpp b/src/unicorn_emulator/unicorn_x64_emulator.cpp index c656aa5b..4cbc98fc 100644 --- a/src/unicorn_emulator/unicorn_x64_emulator.cpp +++ b/src/unicorn_emulator/unicorn_x64_emulator.cpp @@ -1,13 +1,10 @@ #define UNICORN_EMULATOR_IMPL #include "unicorn_x64_emulator.hpp" -#define NOMINMAX -#include +#include "unicorn_memory_regions.hpp" +#include "unicorn_hook.hpp" -#pragma warning(push) -#pragma warning(disable: 4505) -#include -#pragma warning(pop) +#include "function_wrapper.hpp" namespace unicorn { @@ -20,30 +17,6 @@ namespace unicorn static_assert(static_cast(x64_register::end) == UC_X86_REG_ENDING); - struct unicorn_error : std::runtime_error - { - unicorn_error(const uc_err error_code) - : std::runtime_error(uc_strerror(error_code)) - , code(error_code) - { - } - - uc_err code{}; - }; - - void throw_if_unicorn_error(const uc_err error_code) - { - if (error_code != UC_ERR_OK) - { - throw unicorn_error(error_code); - } - } - - void uce(const uc_err error_code) - { - throw_if_unicorn_error(error_code); - } - uc_x86_insn map_hookable_instruction(const x64_hookable_instructions instruction) { switch (instruction) @@ -70,48 +43,6 @@ namespace unicorn } } - class unicorn_memory_regions - { - public: - unicorn_memory_regions(uc_engine* uc) - { - uce(uc_mem_regions(uc, &this->regions_, &this->count_)); - } - - unicorn_memory_regions(unicorn_memory_regions&&) = delete; - unicorn_memory_regions(const unicorn_memory_regions&) = delete; - unicorn_memory_regions& operator=(unicorn_memory_regions&&) = delete; - unicorn_memory_regions& operator=(const unicorn_memory_regions&) = delete; - - ~unicorn_memory_regions() - { - if (regions_) - { - uc_free(regions_); - } - } - - std::span get_span() const - { - return {this->regions_, this->count_}; - } - - private: - uint32_t count_{}; - uc_mem_region* regions_{}; - }; - - struct object - { - object() = default; - virtual ~object() = default; - - object(object&&) = default; - object(const object&) = default; - object& operator=(object&&) = default; - object& operator=(const object&) = default; - }; - struct hook_object : object { emulator_hook* as_opaque_hook() @@ -120,106 +51,6 @@ namespace unicorn } }; - class unicorn_hook - { - public: - unicorn_hook() = default; - - unicorn_hook(uc_engine* uc) - : unicorn_hook(uc, {}) - { - } - - unicorn_hook(uc_engine* uc, const uc_hook hook) - : uc_(uc) - , hook_(hook) - { - } - - ~unicorn_hook() - { - release(); - } - - unicorn_hook(const unicorn_hook&) = delete; - unicorn_hook& operator=(const unicorn_hook&) = delete; - - - unicorn_hook(unicorn_hook&& obj) noexcept - { - this->operator=(std::move(obj)); - } - - uc_hook* make_reference() - { - if (!this->uc_) - { - throw std::runtime_error("Cannot make reference on default constructed hook"); - } - - this->release(); - return &this->hook_; - } - - unicorn_hook& operator=(unicorn_hook&& obj) noexcept - { - if (this != &obj) - { - this->release(); - - this->uc_ = obj.uc_; - this->hook_ = obj.hook_; - obj.uc_ = {}; - } - - - return *this; - } - - void release() - { - if (this->hook_ && this->uc_) - { - uc_hook_del(this->uc_, this->hook_); - this->hook_ = {}; - } - } - - private: - uc_engine* uc_{}; - uc_hook hook_{}; - }; - - template - class function_wrapper : public object - { - public: - using user_data_pointer = void*; - using c_function_type = ReturnType(Args..., user_data_pointer); - using functor_type = std::function; - - function_wrapper(functor_type functor) - : functor_(std::make_unique(std::move(functor))) - { - } - - c_function_type* get_function() - { - return +[](Args... args, user_data_pointer user_data) -> ReturnType - { - return (*static_cast(user_data))(std::forward(args)...); - }; - } - - user_data_pointer get_user_data() const - { - return this->functor_.get(); - } - - private: - std::unique_ptr functor_{}; - }; - class hook_container : public hook_object { public: @@ -246,6 +77,67 @@ namespace unicorn std::vector hooks_; }; + 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), 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 int64_t) + { + const auto operation = map_memory_operation(type); + if (operation != memory_permission::none) + { + (*callback)(address, static_cast(size), 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, 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)); + } + class unicorn_x64_emulator : public x64_emulator { public: @@ -384,60 +276,17 @@ namespace unicorn if ((filter & memory_operation::read) != memory_operation::none) { - function_wrapper wrapper( - [shared_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) - { - (*shared_callback)(address, static_cast(size), operation); - } - }); - - unicorn_hook hook{*this}; - - uce(uc_hook_add(*this, 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)); + add_read_hook(*this, address, size, *container, shared_callback); } if ((filter & memory_operation::write) != memory_operation::none) { - function_wrapper wrapper( - [shared_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) - { - (*shared_callback)(address, static_cast(size), operation); - } - }); - - 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)); - - container->add(std::move(wrapper), std::move(hook)); + add_write_hook(*this, address, size, *container, shared_callback); } if ((filter & memory_operation::exec) != memory_operation::none) { - function_wrapper wrapper( - [shared_callback](uc_engine*, const uint64_t address, const uint32_t size) - { - (*shared_callback)(address, static_cast(size), memory_permission::exec); - }); - - 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)); - - container->add(std::move(wrapper), std::move(hook)); + add_exec_hook(*this, address, size, *container, shared_callback); } auto* result = container->as_opaque_hook();