mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-24 14:11:02 +00:00
Split up files
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
#include "std_include.hpp"
|
||||
|
||||
#include "unicorn.hpp"
|
||||
#include "memory_utils.hpp"
|
||||
#include "unicorn_utils.hpp"
|
||||
#include "process_context.hpp"
|
||||
#include "syscalls.hpp"
|
||||
|
||||
#define GS_SEGMENT_ADDR 0x6000000ULL
|
||||
#define GS_SEGMENT_SIZE (20 << 20) // 20 MB
|
||||
|
||||
@@ -10,269 +16,8 @@
|
||||
|
||||
#define KUSD_ADDRESS 0x7ffe0000
|
||||
|
||||
#include "unicorn.hpp"
|
||||
#include <utils/finally.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool is_within_start_and_end(const uint64_t value, const uint64_t start, const uint64_t end)
|
||||
{
|
||||
return value >= start && value < end;
|
||||
}
|
||||
|
||||
bool is_within_start_and_length(const uint64_t value, const uint64_t start, const uint64_t length)
|
||||
{
|
||||
return is_within_start_and_end(value, start, start + length);
|
||||
}
|
||||
|
||||
uint64_t align_down(const uint64_t value, const uint64_t alignment)
|
||||
{
|
||||
return value & ~(alignment - 1);
|
||||
}
|
||||
|
||||
uint64_t align_up(const uint64_t value, const uint64_t alignment)
|
||||
{
|
||||
return align_down(value + (alignment - 1), alignment);
|
||||
}
|
||||
|
||||
uint64_t page_align_down(const uint64_t value)
|
||||
{
|
||||
return align_down(value, 0x1000);
|
||||
}
|
||||
|
||||
uint64_t page_align_up(const uint64_t value)
|
||||
{
|
||||
return align_up(value, 0x1000);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class unicorn_object
|
||||
{
|
||||
public:
|
||||
unicorn_object() = default;
|
||||
|
||||
unicorn_object(const unicorn& uc, uint64_t address)
|
||||
: uc_(&uc)
|
||||
, address_(address)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t value() const
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
|
||||
uint64_t size() const
|
||||
{
|
||||
return sizeof(T);
|
||||
}
|
||||
|
||||
uint64_t end() const
|
||||
{
|
||||
return this->value() + this->size();
|
||||
}
|
||||
|
||||
T* ptr() const
|
||||
{
|
||||
return reinterpret_cast<T*>(this->address_);
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->address_ != 0;
|
||||
}
|
||||
|
||||
T read() const
|
||||
{
|
||||
T obj{};
|
||||
|
||||
e(uc_mem_read(*this->uc_, this->address_, &obj, sizeof(obj)));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void write(const T& value) const
|
||||
{
|
||||
e(uc_mem_write(*this->uc_, this->address_, &value, sizeof(value)));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void access(const F& accessor) const
|
||||
{
|
||||
T obj{};
|
||||
e(uc_mem_read(*this->uc_, this->address_, &obj, sizeof(obj)));
|
||||
|
||||
accessor(obj);
|
||||
|
||||
this->write(obj);
|
||||
}
|
||||
|
||||
private:
|
||||
const unicorn* uc_{};
|
||||
uint64_t address_{};
|
||||
};
|
||||
|
||||
class unicorn_allocator
|
||||
{
|
||||
public:
|
||||
unicorn_allocator() = default;
|
||||
|
||||
unicorn_allocator(const unicorn& uc, const uint64_t address, const uint64_t size)
|
||||
: uc_(&uc)
|
||||
, address_(address)
|
||||
, size_(size)
|
||||
, active_address_(address)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t reserve(const uint64_t count, const uint64_t alignment = 1)
|
||||
{
|
||||
const auto potential_start = align_up(this->active_address_, alignment);
|
||||
const auto potential_end = potential_start + count;
|
||||
const auto total_end = this->address_ + this->size_;
|
||||
|
||||
if (potential_end > total_end)
|
||||
{
|
||||
throw std::runtime_error("Out of memory");
|
||||
}
|
||||
|
||||
this->active_address_ = potential_end;
|
||||
|
||||
return potential_start;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
unicorn_object<T> reserve()
|
||||
{
|
||||
const auto potential_start = this->reserve(sizeof(T), alignof(T));
|
||||
return unicorn_object<T>(*this->uc_, potential_start);
|
||||
}
|
||||
|
||||
void make_unicode_string(UNICODE_STRING& result, const std::wstring_view str)
|
||||
{
|
||||
constexpr auto element_size = sizeof(str[0]);
|
||||
constexpr auto required_alignment = alignof(decltype(str[0]));
|
||||
const auto total_length = str.size() * element_size;
|
||||
|
||||
const auto string_buffer = this->reserve(total_length, required_alignment);
|
||||
|
||||
e(uc_mem_write(*this->uc_, string_buffer, str.data(), total_length));
|
||||
|
||||
result.Buffer = reinterpret_cast<PWCH>(string_buffer);
|
||||
result.Length = static_cast<USHORT>(total_length);
|
||||
result.MaximumLength = result.Length;
|
||||
}
|
||||
|
||||
unicorn_object<UNICODE_STRING> make_unicode_string(const std::wstring_view str)
|
||||
{
|
||||
const auto unicode_string = this->reserve<UNICODE_STRING>();
|
||||
|
||||
unicode_string.access([&](UNICODE_STRING& unicode_str)
|
||||
{
|
||||
this->make_unicode_string(unicode_str, str);
|
||||
});
|
||||
|
||||
return unicode_string;
|
||||
}
|
||||
|
||||
private:
|
||||
const unicorn* uc_{};
|
||||
uint64_t address_{};
|
||||
uint64_t size_{};
|
||||
uint64_t active_address_{0};
|
||||
};
|
||||
|
||||
class unicorn_hook
|
||||
{
|
||||
public:
|
||||
using function = std::function<void(const unicorn& uc, uint64_t address, uint32_t size)>;
|
||||
|
||||
template <typename... Args>
|
||||
unicorn_hook(const unicorn& uc, const int type, const uint64_t begin, const uint64_t end, function callback,
|
||||
Args... args)
|
||||
: uc_(&uc)
|
||||
{
|
||||
this->function_ = std::make_unique<internal_function>(
|
||||
[c = std::move(callback), &uc](const uint64_t address, const uint32_t size)
|
||||
{
|
||||
c(uc, address, size);
|
||||
});
|
||||
|
||||
void* handler = +[](uc_engine*, const uint64_t address, const uint32_t size,
|
||||
void* user_data)
|
||||
{
|
||||
(*static_cast<internal_function*>(user_data))(address, size);
|
||||
};
|
||||
|
||||
if (type == UC_HOOK_INSN)
|
||||
{
|
||||
handler = +[](uc_engine* uc, void* user_data)
|
||||
{
|
||||
uint64_t rip{};
|
||||
uc_reg_read(uc, UC_X86_REG_RIP, &rip);
|
||||
(*static_cast<internal_function*>(user_data))(rip, 0);
|
||||
};
|
||||
}
|
||||
|
||||
if (type == UC_HOOK_MEM_READ)
|
||||
{
|
||||
handler = +[](uc_engine*, const uc_mem_type /*type*/, const uint64_t address, const int size,
|
||||
const int64_t /*value*/, void* user_data)
|
||||
{
|
||||
(*static_cast<internal_function*>(user_data))(address, size);
|
||||
};
|
||||
}
|
||||
e(uc_hook_add(*this->uc_, &this->hook_, type, handler, this->function_.get(), begin, end, args...));
|
||||
}
|
||||
|
||||
unicorn_hook(const unicorn_hook&) = delete;
|
||||
unicorn_hook& operator=(const unicorn_hook&) = delete;
|
||||
|
||||
unicorn_hook(unicorn_hook&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
unicorn_hook& operator=(unicorn_hook&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->remove();
|
||||
|
||||
this->uc_ = obj.uc_;
|
||||
this->hook_ = obj.hook_;
|
||||
this->function_ = std::move(obj.function_);
|
||||
|
||||
obj.hook_ = {};
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~unicorn_hook()
|
||||
{
|
||||
this->remove();
|
||||
}
|
||||
|
||||
void remove()
|
||||
{
|
||||
if (this->hook_)
|
||||
{
|
||||
uc_hook_del(*this->uc_, this->hook_);
|
||||
this->hook_ = {};
|
||||
}
|
||||
|
||||
this->function_ = {};
|
||||
}
|
||||
|
||||
private:
|
||||
using internal_function = std::function<void(uint64_t address, uint32_t size)>;
|
||||
|
||||
const unicorn* uc_{};
|
||||
uc_hook hook_{};
|
||||
std::unique_ptr<internal_function> function_{};
|
||||
};
|
||||
|
||||
void setup_stack(const unicorn& uc, uint64_t stack_base, size_t stack_size)
|
||||
{
|
||||
e(uc_mem_map(uc, stack_base, stack_size, UC_PROT_READ | UC_PROT_WRITE));
|
||||
@@ -314,13 +59,6 @@ namespace
|
||||
});
|
||||
}
|
||||
|
||||
struct mapped_binary
|
||||
{
|
||||
uint64_t image_base{};
|
||||
uint64_t size_of_image{};
|
||||
std::unordered_map<std::string, uint64_t> exports{};
|
||||
};
|
||||
|
||||
mapped_binary map_module(const unicorn& uc, const std::vector<uint8_t>& module_data,
|
||||
const std::string& name)
|
||||
{
|
||||
@@ -419,37 +157,6 @@ namespace
|
||||
return binary;
|
||||
}
|
||||
|
||||
struct event
|
||||
{
|
||||
bool signaled{};
|
||||
EVENT_TYPE type{};
|
||||
|
||||
bool is_signaled()
|
||||
{
|
||||
const auto res = this->signaled;
|
||||
|
||||
if (this->type == SynchronizationEvent)
|
||||
{
|
||||
this->signaled = false;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
struct process_context
|
||||
{
|
||||
unicorn_object<TEB> teb{};
|
||||
unicorn_object<PEB> peb{};
|
||||
unicorn_object<RTL_USER_PROCESS_PARAMETERS> process_params{};
|
||||
|
||||
mapped_binary executable{};
|
||||
mapped_binary ntdll{};
|
||||
|
||||
std::vector<event> events{};
|
||||
unicorn_allocator gs_segment{};
|
||||
};
|
||||
|
||||
process_context setup_teb_and_peb(const unicorn& uc)
|
||||
{
|
||||
setup_stack(uc, STACK_ADDRESS, STACK_SIZE);
|
||||
@@ -515,318 +222,6 @@ namespace
|
||||
return map_module(uc, data, file.generic_string());
|
||||
}
|
||||
|
||||
void handle_NtQueryPerformanceCounter(const unicorn& uc)
|
||||
{
|
||||
const unicorn_object<LARGE_INTEGER> performance_counter{uc, uc.reg(UC_X86_REG_R10)};
|
||||
const unicorn_object<LARGE_INTEGER> performance_frequency{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
|
||||
try
|
||||
{
|
||||
if (performance_counter)
|
||||
{
|
||||
performance_counter.access([](LARGE_INTEGER& value)
|
||||
{
|
||||
QueryPerformanceCounter(&value);
|
||||
});
|
||||
}
|
||||
|
||||
if (performance_frequency)
|
||||
{
|
||||
performance_frequency.access([](LARGE_INTEGER& value)
|
||||
{
|
||||
QueryPerformanceFrequency(&value);
|
||||
});
|
||||
}
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_ACCESS_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t get_memory_protection(const unicorn& uc, uint64_t address)
|
||||
{
|
||||
uint32_t count{};
|
||||
uc_mem_region* regions{};
|
||||
|
||||
e(uc_mem_regions(uc, ®ions, &count));
|
||||
const auto _ = utils::finally([&]
|
||||
{
|
||||
uc_free(regions);
|
||||
});
|
||||
|
||||
for (const auto& region : std::span(regions, count))
|
||||
{
|
||||
if (is_within_start_and_end(address, region.begin, region.end))
|
||||
{
|
||||
return region.perms;
|
||||
}
|
||||
}
|
||||
|
||||
return UC_PROT_NONE;
|
||||
}
|
||||
|
||||
uint32_t map_nt_to_unicorn_protection(const uint32_t nt_protection)
|
||||
{
|
||||
switch (nt_protection)
|
||||
{
|
||||
case PAGE_NOACCESS:
|
||||
return UC_PROT_NONE;
|
||||
case PAGE_READONLY:
|
||||
return UC_PROT_READ;
|
||||
case PAGE_READWRITE:
|
||||
case PAGE_WRITECOPY:
|
||||
return UC_PROT_READ | UC_PROT_WRITE;
|
||||
case PAGE_EXECUTE:
|
||||
case PAGE_EXECUTE_READ:
|
||||
return UC_PROT_READ | UC_PROT_EXEC;
|
||||
case PAGE_EXECUTE_READWRITE:
|
||||
case PAGE_EXECUTE_WRITECOPY:
|
||||
default:
|
||||
return UC_PROT_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t map_unicorn_to_nt_protection(const uint32_t unicorn_protection)
|
||||
{
|
||||
const bool has_exec = unicorn_protection & UC_PROT_EXEC;
|
||||
const bool has_read = unicorn_protection & UC_PROT_READ;
|
||||
const bool has_write = unicorn_protection & UC_PROT_WRITE;
|
||||
|
||||
if (!has_read)
|
||||
{
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
|
||||
if (has_exec && has_write)
|
||||
{
|
||||
return PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
if (has_exec)
|
||||
{
|
||||
return PAGE_EXECUTE_READ;
|
||||
}
|
||||
|
||||
if (has_write)
|
||||
{
|
||||
return PAGE_READWRITE;
|
||||
}
|
||||
|
||||
return PAGE_READONLY;
|
||||
}
|
||||
|
||||
void handle_NtManageHotPatch(const unicorn& uc)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
void handle_NtOpenKey(const unicorn& uc)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
void handle_NtCreateEvent(const unicorn& uc, process_context& context)
|
||||
{
|
||||
const unicorn_object<uint64_t> event_handle{uc, uc.reg(UC_X86_REG_R10)};
|
||||
const auto object_attributes = uc.reg(UC_X86_REG_R8);
|
||||
const auto event_type = uc.reg<EVENT_TYPE>(UC_X86_REG_R9D);
|
||||
const auto initial_state = static_cast<BOOLEAN>(uc.read_stack(5));
|
||||
|
||||
if (object_attributes)
|
||||
{
|
||||
puts("Unsupported object attributes");
|
||||
uc.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t index = context.events.size();
|
||||
event_handle.write(index);
|
||||
|
||||
context.events.emplace_back(initial_state != FALSE, event_type);
|
||||
|
||||
static_assert(sizeof(EVENT_TYPE) == sizeof(uint32_t));
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
void handle_NtQueryVirtualMemory(const unicorn& uc, const process_context& context)
|
||||
{
|
||||
const auto process_handle = uc.reg(UC_X86_REG_R10);
|
||||
const auto base_address = uc.reg(UC_X86_REG_RDX);
|
||||
const auto info_class = uc.reg<uint32_t>(UC_X86_REG_R8D);
|
||||
const auto memory_information = uc.reg(UC_X86_REG_R9);
|
||||
const auto memory_information_length = static_cast<uint32_t>(uc.read_stack(5));
|
||||
const unicorn_object<uint32_t> return_length{uc, uc.read_stack(6)};
|
||||
|
||||
if (process_handle != ~0ULL)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info_class == MemoryWorkingSetExInformation)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info_class != MemoryImageInformation)
|
||||
{
|
||||
printf("Unsupported memory info class: %X\n", info_class);
|
||||
uc.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (return_length)
|
||||
{
|
||||
return_length.write(sizeof(MEMORY_IMAGE_INFORMATION));
|
||||
}
|
||||
|
||||
if (memory_information_length != sizeof(MEMORY_IMAGE_INFORMATION))
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_BUFFER_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_within_start_and_length(base_address, context.ntdll.image_base, context.ntdll.size_of_image))
|
||||
{
|
||||
puts("Bad image request");
|
||||
uc.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<MEMORY_IMAGE_INFORMATION> info{uc, memory_information};
|
||||
|
||||
info.access([&](MEMORY_IMAGE_INFORMATION& image_info)
|
||||
{
|
||||
image_info.ImageBase = reinterpret_cast<void*>(context.ntdll.image_base);
|
||||
image_info.SizeOfImage = context.ntdll.size_of_image;
|
||||
});
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
void handle_NtQuerySystemInformation(const unicorn& uc, const process_context& context)
|
||||
{
|
||||
const auto info_class = uc.reg<uint32_t>(UC_X86_REG_R10D);
|
||||
const auto system_information = uc.reg(UC_X86_REG_RDX);
|
||||
const auto system_information_length = uc.reg<uint32_t>(UC_X86_REG_R8D);
|
||||
const unicorn_object<uint32_t> return_length{uc, uc.reg(UC_X86_REG_R9)};
|
||||
|
||||
if (info_class == SystemFlushInformation)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info_class != SystemBasicInformation && info_class != SystemEmulationBasicInformation)
|
||||
{
|
||||
printf("Unsupported system info class: %X\n", info_class);
|
||||
uc.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (return_length)
|
||||
{
|
||||
return_length.write(sizeof(SYSTEM_BASIC_INFORMATION));
|
||||
}
|
||||
|
||||
if (system_information_length != sizeof(SYSTEM_BASIC_INFORMATION))
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_BUFFER_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<SYSTEM_BASIC_INFORMATION> info{uc, system_information};
|
||||
|
||||
info.access([&](SYSTEM_BASIC_INFORMATION& basic_info)
|
||||
{
|
||||
basic_info.Reserved = 0;
|
||||
basic_info.TimerResolution = 0x0002625a;
|
||||
basic_info.PageSize = 0x1000;
|
||||
basic_info.LowestPhysicalPageNumber = 0x00000001;
|
||||
basic_info.HighestPhysicalPageNumber = 0x00c9c7ff;
|
||||
basic_info.AllocationGranularity = 0x10000;
|
||||
basic_info.MinimumUserModeAddress = 0x0000000000010000;
|
||||
basic_info.MaximumUserModeAddress = 0x00007ffffffeffff;
|
||||
basic_info.ActiveProcessorsAffinityMask = 0x0000000000000fff;
|
||||
basic_info.NumberOfProcessors = 1;
|
||||
});
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
void handle_NtQueryProcessInformation(const unicorn& uc, const process_context& context)
|
||||
{
|
||||
const auto process_handle = uc.reg<uint64_t>(UC_X86_REG_R10);
|
||||
const auto info_class = uc.reg<uint32_t>(UC_X86_REG_EDX);
|
||||
const auto process_information = uc.reg(UC_X86_REG_R8);
|
||||
const auto process_information_length = uc.reg<uint32_t>(UC_X86_REG_R9D);
|
||||
const unicorn_object<uint32_t> return_length{uc, uc.read_stack(5)};
|
||||
|
||||
if (process_handle != ~0ULL)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info_class != ProcessCookie)
|
||||
{
|
||||
printf("Unsupported process info class: %X\n", info_class);
|
||||
uc.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (return_length)
|
||||
{
|
||||
return_length.write(sizeof(uint32_t));
|
||||
}
|
||||
|
||||
if (process_information_length != sizeof(uint32_t))
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_BUFFER_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<uint32_t> info{uc, process_information};
|
||||
info.write(0x01234567);
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
void handle_NtProtectVirtualMemory(const unicorn& uc)
|
||||
{
|
||||
const auto process_handle = uc.reg(UC_X86_REG_R10);
|
||||
const unicorn_object<uint64_t> base_address{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const unicorn_object<uint32_t> bytes_to_protect{uc, uc.reg(UC_X86_REG_R8)};
|
||||
const auto protection = uc.reg<uint32_t>(UC_X86_REG_R9D);
|
||||
const unicorn_object<uint32_t> old_protection{uc, uc.read_stack(5)};
|
||||
|
||||
if (process_handle != ~0ULL)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto address = page_align_down(base_address.read());
|
||||
base_address.write(address);
|
||||
|
||||
const auto size = page_align_up(bytes_to_protect.read());
|
||||
bytes_to_protect.write(static_cast<uint32_t>(size));
|
||||
|
||||
const auto current_uc_protection = get_memory_protection(uc, address);
|
||||
const auto current_protection = map_unicorn_to_nt_protection(current_uc_protection);
|
||||
old_protection.write(current_protection);
|
||||
|
||||
const auto requested_protection = map_nt_to_unicorn_protection(protection);
|
||||
e(uc_mem_protect(uc, address, size, requested_protection));
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
const unicorn uc{UC_ARCH_X86, UC_MODE_64};
|
||||
@@ -876,51 +271,9 @@ namespace
|
||||
}
|
||||
|
||||
unicorn_hook hook(uc, UC_HOOK_INSN, 0, std::numeric_limits<uint64_t>::max(),
|
||||
[&](const unicorn&, const uint64_t address, const uint32_t /*size*/)
|
||||
[&](const unicorn&, const uint64_t, const uint32_t)
|
||||
{
|
||||
const auto syscall_id = uc.reg<uint32_t>(UC_X86_REG_EAX);
|
||||
|
||||
printf("Handling syscall: %X (%llX)\n", syscall_id, address);
|
||||
|
||||
try
|
||||
{
|
||||
switch (syscall_id)
|
||||
{
|
||||
case 0x12:
|
||||
handle_NtOpenKey(uc);
|
||||
break;
|
||||
case 0x19:
|
||||
handle_NtQueryProcessInformation(uc, context);
|
||||
break;
|
||||
case 0x23:
|
||||
handle_NtQueryVirtualMemory(uc, context);
|
||||
break;
|
||||
case 0x31:
|
||||
handle_NtQueryPerformanceCounter(uc);
|
||||
break;
|
||||
case 0x36:
|
||||
handle_NtQuerySystemInformation(uc, context);
|
||||
break;
|
||||
case 0x48:
|
||||
handle_NtCreateEvent(uc, context);
|
||||
break;
|
||||
case 0x50:
|
||||
handle_NtProtectVirtualMemory(uc);
|
||||
break;
|
||||
case 0x11A:
|
||||
handle_NtManageHotPatch(uc);
|
||||
break;
|
||||
default:
|
||||
printf("Unhandled syscall: %X\n", syscall_id);
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED);
|
||||
uc.stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_UNSUCCESSFUL);
|
||||
}
|
||||
handle_syscall(uc, context);
|
||||
}, UC_X86_INS_SYSCALL);
|
||||
|
||||
unicorn_hook hook3(uc, UC_HOOK_MEM_READ, context.peb.value(), context.peb.end(),
|
||||
|
||||
105
src/emulator/memory_utils.hpp
Normal file
105
src/emulator/memory_utils.hpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <utils/finally.hpp>
|
||||
|
||||
inline bool is_within_start_and_end(const uint64_t value, const uint64_t start, const uint64_t end)
|
||||
{
|
||||
return value >= start && value < end;
|
||||
}
|
||||
|
||||
inline bool is_within_start_and_length(const uint64_t value, const uint64_t start, const uint64_t length)
|
||||
{
|
||||
return is_within_start_and_end(value, start, start + length);
|
||||
}
|
||||
|
||||
inline uint64_t align_down(const uint64_t value, const uint64_t alignment)
|
||||
{
|
||||
return value & ~(alignment - 1);
|
||||
}
|
||||
|
||||
inline uint64_t align_up(const uint64_t value, const uint64_t alignment)
|
||||
{
|
||||
return align_down(value + (alignment - 1), alignment);
|
||||
}
|
||||
|
||||
inline uint64_t page_align_down(const uint64_t value)
|
||||
{
|
||||
return align_down(value, 0x1000);
|
||||
}
|
||||
|
||||
inline uint64_t page_align_up(const uint64_t value)
|
||||
{
|
||||
return align_up(value, 0x1000);
|
||||
}
|
||||
|
||||
inline uint32_t get_memory_protection(const unicorn& uc, uint64_t address)
|
||||
{
|
||||
uint32_t count{};
|
||||
uc_mem_region* regions{};
|
||||
|
||||
e(uc_mem_regions(uc, ®ions, &count));
|
||||
const auto _ = utils::finally([&]
|
||||
{
|
||||
uc_free(regions);
|
||||
});
|
||||
|
||||
for (const auto& region : std::span(regions, count))
|
||||
{
|
||||
if (is_within_start_and_end(address, region.begin, region.end))
|
||||
{
|
||||
return region.perms;
|
||||
}
|
||||
}
|
||||
|
||||
return UC_PROT_NONE;
|
||||
}
|
||||
|
||||
inline uint32_t map_nt_to_unicorn_protection(const uint32_t nt_protection)
|
||||
{
|
||||
switch (nt_protection)
|
||||
{
|
||||
case PAGE_NOACCESS:
|
||||
return UC_PROT_NONE;
|
||||
case PAGE_READONLY:
|
||||
return UC_PROT_READ;
|
||||
case PAGE_READWRITE:
|
||||
case PAGE_WRITECOPY:
|
||||
return UC_PROT_READ | UC_PROT_WRITE;
|
||||
case PAGE_EXECUTE:
|
||||
case PAGE_EXECUTE_READ:
|
||||
return UC_PROT_READ | UC_PROT_EXEC;
|
||||
case PAGE_EXECUTE_READWRITE:
|
||||
case PAGE_EXECUTE_WRITECOPY:
|
||||
default:
|
||||
return UC_PROT_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t map_unicorn_to_nt_protection(const uint32_t unicorn_protection)
|
||||
{
|
||||
const bool has_exec = unicorn_protection & UC_PROT_EXEC;
|
||||
const bool has_read = unicorn_protection & UC_PROT_READ;
|
||||
const bool has_write = unicorn_protection & UC_PROT_WRITE;
|
||||
|
||||
if (!has_read)
|
||||
{
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
|
||||
if (has_exec && has_write)
|
||||
{
|
||||
return PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
if (has_exec)
|
||||
{
|
||||
return PAGE_EXECUTE_READ;
|
||||
}
|
||||
|
||||
if (has_write)
|
||||
{
|
||||
return PAGE_READWRITE;
|
||||
}
|
||||
|
||||
return PAGE_READONLY;
|
||||
}
|
||||
40
src/emulator/process_context.hpp
Normal file
40
src/emulator/process_context.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "unicorn_utils.hpp"
|
||||
|
||||
struct mapped_binary
|
||||
{
|
||||
uint64_t image_base{};
|
||||
uint64_t size_of_image{};
|
||||
std::unordered_map<std::string, uint64_t> exports{};
|
||||
};
|
||||
|
||||
struct event
|
||||
{
|
||||
bool signaled{};
|
||||
EVENT_TYPE type{};
|
||||
|
||||
bool is_signaled()
|
||||
{
|
||||
const auto res = this->signaled;
|
||||
|
||||
if (this->type == SynchronizationEvent)
|
||||
{
|
||||
this->signaled = false;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
struct process_context
|
||||
{
|
||||
unicorn_object<TEB> teb{};
|
||||
unicorn_object<PEB> peb{};
|
||||
unicorn_object<RTL_USER_PROCESS_PARAMETERS> process_params{};
|
||||
|
||||
mapped_binary executable{};
|
||||
mapped_binary ntdll{};
|
||||
|
||||
std::vector<event> events{};
|
||||
unicorn_allocator gs_segment{};
|
||||
};
|
||||
293
src/emulator/syscalls.cpp
Normal file
293
src/emulator/syscalls.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
#include "std_include.hpp"
|
||||
#include "syscalls.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
void handle_NtQueryPerformanceCounter(const unicorn& uc)
|
||||
{
|
||||
const unicorn_object<LARGE_INTEGER> performance_counter{uc, uc.reg(UC_X86_REG_R10)};
|
||||
const unicorn_object<LARGE_INTEGER> performance_frequency{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
|
||||
try
|
||||
{
|
||||
if (performance_counter)
|
||||
{
|
||||
performance_counter.access([](LARGE_INTEGER& value)
|
||||
{
|
||||
QueryPerformanceCounter(&value);
|
||||
});
|
||||
}
|
||||
|
||||
if (performance_frequency)
|
||||
{
|
||||
performance_frequency.access([](LARGE_INTEGER& value)
|
||||
{
|
||||
QueryPerformanceFrequency(&value);
|
||||
});
|
||||
}
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_ACCESS_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_NtManageHotPatch(const unicorn& uc)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
void handle_NtOpenKey(const unicorn& uc)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
void handle_NtCreateEvent(const unicorn& uc, process_context& context)
|
||||
{
|
||||
const unicorn_object<uint64_t> event_handle{uc, uc.reg(UC_X86_REG_R10)};
|
||||
const auto object_attributes = uc.reg(UC_X86_REG_R8);
|
||||
const auto event_type = uc.reg<EVENT_TYPE>(UC_X86_REG_R9D);
|
||||
const auto initial_state = static_cast<BOOLEAN>(uc.read_stack(5));
|
||||
|
||||
if (object_attributes)
|
||||
{
|
||||
puts("Unsupported object attributes");
|
||||
uc.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t index = context.events.size();
|
||||
event_handle.write(index);
|
||||
|
||||
context.events.emplace_back(initial_state != FALSE, event_type);
|
||||
|
||||
static_assert(sizeof(EVENT_TYPE) == sizeof(uint32_t));
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
void handle_NtQueryVirtualMemory(const unicorn& uc, const process_context& context)
|
||||
{
|
||||
const auto process_handle = uc.reg(UC_X86_REG_R10);
|
||||
const auto base_address = uc.reg(UC_X86_REG_RDX);
|
||||
const auto info_class = uc.reg<uint32_t>(UC_X86_REG_R8D);
|
||||
const auto memory_information = uc.reg(UC_X86_REG_R9);
|
||||
const auto memory_information_length = static_cast<uint32_t>(uc.read_stack(5));
|
||||
const unicorn_object<uint32_t> return_length{uc, uc.read_stack(6)};
|
||||
|
||||
if (process_handle != ~0ULL)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info_class == MemoryWorkingSetExInformation)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info_class != MemoryImageInformation)
|
||||
{
|
||||
printf("Unsupported memory info class: %X\n", info_class);
|
||||
uc.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (return_length)
|
||||
{
|
||||
return_length.write(sizeof(MEMORY_IMAGE_INFORMATION));
|
||||
}
|
||||
|
||||
if (memory_information_length != sizeof(MEMORY_IMAGE_INFORMATION))
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_BUFFER_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_within_start_and_length(base_address, context.ntdll.image_base, context.ntdll.size_of_image))
|
||||
{
|
||||
puts("Bad image request");
|
||||
uc.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<MEMORY_IMAGE_INFORMATION> info{uc, memory_information};
|
||||
|
||||
info.access([&](MEMORY_IMAGE_INFORMATION& image_info)
|
||||
{
|
||||
image_info.ImageBase = reinterpret_cast<void*>(context.ntdll.image_base);
|
||||
image_info.SizeOfImage = context.ntdll.size_of_image;
|
||||
});
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
void handle_NtQuerySystemInformation(const unicorn& uc, const process_context& context)
|
||||
{
|
||||
const auto info_class = uc.reg<uint32_t>(UC_X86_REG_R10D);
|
||||
const auto system_information = uc.reg(UC_X86_REG_RDX);
|
||||
const auto system_information_length = uc.reg<uint32_t>(UC_X86_REG_R8D);
|
||||
const unicorn_object<uint32_t> return_length{uc, uc.reg(UC_X86_REG_R9)};
|
||||
|
||||
if (info_class == SystemFlushInformation)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info_class != SystemBasicInformation && info_class != SystemEmulationBasicInformation)
|
||||
{
|
||||
printf("Unsupported system info class: %X\n", info_class);
|
||||
uc.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (return_length)
|
||||
{
|
||||
return_length.write(sizeof(SYSTEM_BASIC_INFORMATION));
|
||||
}
|
||||
|
||||
if (system_information_length != sizeof(SYSTEM_BASIC_INFORMATION))
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_BUFFER_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<SYSTEM_BASIC_INFORMATION> info{uc, system_information};
|
||||
|
||||
info.access([&](SYSTEM_BASIC_INFORMATION& basic_info)
|
||||
{
|
||||
basic_info.Reserved = 0;
|
||||
basic_info.TimerResolution = 0x0002625a;
|
||||
basic_info.PageSize = 0x1000;
|
||||
basic_info.LowestPhysicalPageNumber = 0x00000001;
|
||||
basic_info.HighestPhysicalPageNumber = 0x00c9c7ff;
|
||||
basic_info.AllocationGranularity = 0x10000;
|
||||
basic_info.MinimumUserModeAddress = 0x0000000000010000;
|
||||
basic_info.MaximumUserModeAddress = 0x00007ffffffeffff;
|
||||
basic_info.ActiveProcessorsAffinityMask = 0x0000000000000fff;
|
||||
basic_info.NumberOfProcessors = 1;
|
||||
});
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
void handle_NtQueryProcessInformation(const unicorn& uc, const process_context& context)
|
||||
{
|
||||
const auto process_handle = uc.reg<uint64_t>(UC_X86_REG_R10);
|
||||
const auto info_class = uc.reg<uint32_t>(UC_X86_REG_EDX);
|
||||
const auto process_information = uc.reg(UC_X86_REG_R8);
|
||||
const auto process_information_length = uc.reg<uint32_t>(UC_X86_REG_R9D);
|
||||
const unicorn_object<uint32_t> return_length{uc, uc.read_stack(5)};
|
||||
|
||||
if (process_handle != ~0ULL)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info_class != ProcessCookie)
|
||||
{
|
||||
printf("Unsupported process info class: %X\n", info_class);
|
||||
uc.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (return_length)
|
||||
{
|
||||
return_length.write(sizeof(uint32_t));
|
||||
}
|
||||
|
||||
if (process_information_length != sizeof(uint32_t))
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_BUFFER_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<uint32_t> info{uc, process_information};
|
||||
info.write(0x01234567);
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
void handle_NtProtectVirtualMemory(const unicorn& uc)
|
||||
{
|
||||
const auto process_handle = uc.reg(UC_X86_REG_R10);
|
||||
const unicorn_object<uint64_t> base_address{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const unicorn_object<uint32_t> bytes_to_protect{uc, uc.reg(UC_X86_REG_R8)};
|
||||
const auto protection = uc.reg<uint32_t>(UC_X86_REG_R9D);
|
||||
const unicorn_object<uint32_t> old_protection{uc, uc.read_stack(5)};
|
||||
|
||||
if (process_handle != ~0ULL)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto address = page_align_down(base_address.read());
|
||||
base_address.write(address);
|
||||
|
||||
const auto size = page_align_up(bytes_to_protect.read());
|
||||
bytes_to_protect.write(static_cast<uint32_t>(size));
|
||||
|
||||
const auto current_uc_protection = get_memory_protection(uc, address);
|
||||
const auto current_protection = map_unicorn_to_nt_protection(current_uc_protection);
|
||||
old_protection.write(current_protection);
|
||||
|
||||
const auto requested_protection = map_nt_to_unicorn_protection(protection);
|
||||
e(uc_mem_protect(uc, address, size, requested_protection));
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_syscall(const unicorn& uc, process_context& context)
|
||||
{
|
||||
const auto address = uc.reg(UC_X86_REG_RIP);
|
||||
const auto syscall_id = uc.reg<uint32_t>(UC_X86_REG_EAX);
|
||||
|
||||
printf("Handling syscall: %X (%llX)\n", syscall_id, address);
|
||||
|
||||
try
|
||||
{
|
||||
switch (syscall_id)
|
||||
{
|
||||
case 0x12:
|
||||
handle_NtOpenKey(uc);
|
||||
break;
|
||||
case 0x19:
|
||||
handle_NtQueryProcessInformation(uc, context);
|
||||
break;
|
||||
case 0x23:
|
||||
handle_NtQueryVirtualMemory(uc, context);
|
||||
break;
|
||||
case 0x31:
|
||||
handle_NtQueryPerformanceCounter(uc);
|
||||
break;
|
||||
case 0x36:
|
||||
handle_NtQuerySystemInformation(uc, context);
|
||||
break;
|
||||
case 0x48:
|
||||
handle_NtCreateEvent(uc, context);
|
||||
break;
|
||||
case 0x50:
|
||||
handle_NtProtectVirtualMemory(uc);
|
||||
break;
|
||||
case 0x11A:
|
||||
handle_NtManageHotPatch(uc);
|
||||
break;
|
||||
default:
|
||||
printf("Unhandled syscall: %X\n", syscall_id);
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED);
|
||||
uc.stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_UNSUCCESSFUL);
|
||||
}
|
||||
}
|
||||
6
src/emulator/syscalls.hpp
Normal file
6
src/emulator/syscalls.hpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "unicorn.hpp"
|
||||
#include "process_context.hpp"
|
||||
|
||||
void handle_syscall(const unicorn& uc, process_context& context);
|
||||
231
src/emulator/unicorn_utils.hpp
Normal file
231
src/emulator/unicorn_utils.hpp
Normal file
@@ -0,0 +1,231 @@
|
||||
#pragma once
|
||||
#include "unicorn.hpp"
|
||||
#include "memory_utils.hpp"
|
||||
|
||||
template <typename T>
|
||||
class unicorn_object
|
||||
{
|
||||
public:
|
||||
unicorn_object() = default;
|
||||
|
||||
unicorn_object(const unicorn& uc, uint64_t address)
|
||||
: uc_(&uc)
|
||||
, address_(address)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t value() const
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
|
||||
uint64_t size() const
|
||||
{
|
||||
return sizeof(T);
|
||||
}
|
||||
|
||||
uint64_t end() const
|
||||
{
|
||||
return this->value() + this->size();
|
||||
}
|
||||
|
||||
T* ptr() const
|
||||
{
|
||||
return reinterpret_cast<T*>(this->address_);
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->address_ != 0;
|
||||
}
|
||||
|
||||
T read() const
|
||||
{
|
||||
T obj{};
|
||||
|
||||
e(uc_mem_read(*this->uc_, this->address_, &obj, sizeof(obj)));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void write(const T& value) const
|
||||
{
|
||||
e(uc_mem_write(*this->uc_, this->address_, &value, sizeof(value)));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void access(const F& accessor) const
|
||||
{
|
||||
T obj{};
|
||||
e(uc_mem_read(*this->uc_, this->address_, &obj, sizeof(obj)));
|
||||
|
||||
accessor(obj);
|
||||
|
||||
this->write(obj);
|
||||
}
|
||||
|
||||
private:
|
||||
const unicorn* uc_{};
|
||||
uint64_t address_{};
|
||||
};
|
||||
|
||||
class unicorn_allocator
|
||||
{
|
||||
public:
|
||||
unicorn_allocator() = default;
|
||||
|
||||
unicorn_allocator(const unicorn& uc, const uint64_t address, const uint64_t size)
|
||||
: uc_(&uc)
|
||||
, address_(address)
|
||||
, size_(size)
|
||||
, active_address_(address)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t reserve(const uint64_t count, const uint64_t alignment = 1)
|
||||
{
|
||||
const auto potential_start = align_up(this->active_address_, alignment);
|
||||
const auto potential_end = potential_start + count;
|
||||
const auto total_end = this->address_ + this->size_;
|
||||
|
||||
if (potential_end > total_end)
|
||||
{
|
||||
throw std::runtime_error("Out of memory");
|
||||
}
|
||||
|
||||
this->active_address_ = potential_end;
|
||||
|
||||
return potential_start;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
unicorn_object<T> reserve()
|
||||
{
|
||||
const auto potential_start = this->reserve(sizeof(T), alignof(T));
|
||||
return unicorn_object<T>(*this->uc_, potential_start);
|
||||
}
|
||||
|
||||
void make_unicode_string(UNICODE_STRING& result, const std::wstring_view str)
|
||||
{
|
||||
constexpr auto element_size = sizeof(str[0]);
|
||||
constexpr auto required_alignment = alignof(decltype(str[0]));
|
||||
const auto total_length = str.size() * element_size;
|
||||
|
||||
const auto string_buffer = this->reserve(total_length, required_alignment);
|
||||
|
||||
e(uc_mem_write(*this->uc_, string_buffer, str.data(), total_length));
|
||||
|
||||
result.Buffer = reinterpret_cast<PWCH>(string_buffer);
|
||||
result.Length = static_cast<USHORT>(total_length);
|
||||
result.MaximumLength = result.Length;
|
||||
}
|
||||
|
||||
unicorn_object<UNICODE_STRING> make_unicode_string(const std::wstring_view str)
|
||||
{
|
||||
const auto unicode_string = this->reserve<UNICODE_STRING>();
|
||||
|
||||
unicode_string.access([&](UNICODE_STRING& unicode_str)
|
||||
{
|
||||
this->make_unicode_string(unicode_str, str);
|
||||
});
|
||||
|
||||
return unicode_string;
|
||||
}
|
||||
|
||||
private:
|
||||
const unicorn* uc_{};
|
||||
uint64_t address_{};
|
||||
uint64_t size_{};
|
||||
uint64_t active_address_{ 0 };
|
||||
};
|
||||
|
||||
class unicorn_hook
|
||||
{
|
||||
public:
|
||||
using function = std::function<void(const unicorn& uc, uint64_t address, uint32_t size)>;
|
||||
|
||||
template <typename... Args>
|
||||
unicorn_hook(const unicorn& uc, const int type, const uint64_t begin, const uint64_t end, function callback,
|
||||
Args... args)
|
||||
: uc_(&uc)
|
||||
{
|
||||
this->function_ = std::make_unique<internal_function>(
|
||||
[c = std::move(callback), &uc](const uint64_t address, const uint32_t size)
|
||||
{
|
||||
c(uc, address, size);
|
||||
});
|
||||
|
||||
void* handler = +[](uc_engine*, const uint64_t address, const uint32_t size,
|
||||
void* user_data)
|
||||
{
|
||||
(*static_cast<internal_function*>(user_data))(address, size);
|
||||
};
|
||||
|
||||
if (type == UC_HOOK_INSN)
|
||||
{
|
||||
handler = +[](uc_engine* uc, void* user_data)
|
||||
{
|
||||
uint64_t rip{};
|
||||
uc_reg_read(uc, UC_X86_REG_RIP, &rip);
|
||||
(*static_cast<internal_function*>(user_data))(rip, 0);
|
||||
};
|
||||
}
|
||||
|
||||
if (type == UC_HOOK_MEM_READ)
|
||||
{
|
||||
handler = +[](uc_engine*, const uc_mem_type /*type*/, const uint64_t address, const int size,
|
||||
const int64_t /*value*/, void* user_data)
|
||||
{
|
||||
(*static_cast<internal_function*>(user_data))(address, size);
|
||||
};
|
||||
}
|
||||
e(uc_hook_add(*this->uc_, &this->hook_, type, handler, this->function_.get(), begin, end, args...));
|
||||
}
|
||||
|
||||
unicorn_hook(const unicorn_hook&) = delete;
|
||||
unicorn_hook& operator=(const unicorn_hook&) = delete;
|
||||
|
||||
unicorn_hook(unicorn_hook&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
unicorn_hook& operator=(unicorn_hook&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->remove();
|
||||
|
||||
this->uc_ = obj.uc_;
|
||||
this->hook_ = obj.hook_;
|
||||
this->function_ = std::move(obj.function_);
|
||||
|
||||
obj.hook_ = {};
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~unicorn_hook()
|
||||
{
|
||||
this->remove();
|
||||
}
|
||||
|
||||
void remove()
|
||||
{
|
||||
if (this->hook_)
|
||||
{
|
||||
uc_hook_del(*this->uc_, this->hook_);
|
||||
this->hook_ = {};
|
||||
}
|
||||
|
||||
this->function_ = {};
|
||||
}
|
||||
|
||||
private:
|
||||
using internal_function = std::function<void(uint64_t address, uint32_t size)>;
|
||||
|
||||
const unicorn* uc_{};
|
||||
uc_hook hook_{};
|
||||
std::unique_ptr<internal_function> function_{};
|
||||
};
|
||||
Reference in New Issue
Block a user