From f2e29dc6650bd771368dfb241c985bdb65dad83e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 18 Aug 2024 18:50:38 +0200 Subject: [PATCH] Implement some syscalls --- src/CMakeLists.txt | 1 + src/common/CMakeLists.txt | 16 + src/common/empty.cpp | 0 src/common/utils/byte_buffer.hpp | 191 ++++++++++++ src/common/utils/concurrency.hpp | 57 ++++ src/common/utils/finally.hpp | 55 ++++ src/common/utils/timer.hpp | 26 ++ src/emulator/CMakeLists.txt | 6 +- src/emulator/main.cpp | 500 ++++++++++++++++++++++++++++--- src/emulator/unicorn.hpp | 28 ++ 10 files changed, 830 insertions(+), 50 deletions(-) create mode 100644 src/common/CMakeLists.txt create mode 100644 src/common/empty.cpp create mode 100644 src/common/utils/byte_buffer.hpp create mode 100644 src/common/utils/concurrency.hpp create mode 100644 src/common/utils/finally.hpp create mode 100644 src/common/utils/timer.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a1e13d1e..daa5563f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(common) add_subdirectory(emulator) \ No newline at end of file diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt new file mode 100644 index 00000000..dd09f43b --- /dev/null +++ b/src/common/CMakeLists.txt @@ -0,0 +1,16 @@ +file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS + *.cpp + *.hpp +) + +add_library(common ${SRC_FILES}) + +momo_assign_source_group(${SRC_FILES}) + +target_include_directories(common INTERFACE "${CMAKE_CURRENT_LIST_DIR}") + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) +target_link_libraries(common PUBLIC + Threads::Threads +) diff --git a/src/common/empty.cpp b/src/common/empty.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/common/utils/byte_buffer.hpp b/src/common/utils/byte_buffer.hpp new file mode 100644 index 00000000..b06a376b --- /dev/null +++ b/src/common/utils/byte_buffer.hpp @@ -0,0 +1,191 @@ +#pragma once + +#include +#include +#include +#include + +namespace utils +{ + class buffer_deserializer + { + public: + template + buffer_deserializer(const std::basic_string_view& buffer) + : buffer_(reinterpret_cast(buffer.data()), buffer.size() * sizeof(T)) + { + static_assert(std::is_trivially_copyable_v, "Type must be trivially copyable"); + } + + template + buffer_deserializer(const std::basic_string& buffer) + : buffer_deserializer(std::basic_string_view(buffer.data(), buffer.size())) + { + } + + template + buffer_deserializer(const std::vector& buffer) + : buffer_deserializer(std::basic_string_view(buffer.data(), buffer.size())) + { + } + + void read(void* data, const size_t length) + { + if (this->offset_ + length > this->buffer_.size()) + { + throw std::runtime_error("Out of bounds read from byte buffer"); + } + + memcpy(data, this->buffer_.data() + this->offset_, length); + this->offset_ += length; + } + + std::string read_data(const size_t length) + { + std::string result{}; + result.resize(length); + + this->read(result.data(), result.size()); + + return result; + } + + template + T read() + { + static_assert(std::is_trivially_copyable_v, "Type must be trivially copyable"); + + T object{}; + this->read(&object, sizeof(object)); + return object; + } + + template + std::vector read_vector() + { + static_assert(std::is_trivially_copyable_v, "Type must be trivially copyable"); + + std::vector result{}; + const auto size = this->read(); + const auto totalSize = size * sizeof(T); + + if (this->offset_ + totalSize > this->buffer_.size()) + { + throw std::runtime_error("Out of bounds read from byte buffer"); + } + + result.resize(size); + this->read(result.data(), totalSize); + + return result; + } + + std::string read_string() + { + std::string result{}; + const auto size = this->read(); + + if (this->offset_ + size > this->buffer_.size()) + { + throw std::runtime_error("Out of bounds read from byte buffer"); + } + + result.resize(size); + this->read(result.data(), size); + + return result; + } + + size_t get_remaining_size() const + { + return this->buffer_.size() - offset_; + } + + std::string get_remaining_data() + { + return this->read_data(this->get_remaining_size()); + } + + size_t get_offset() const + { + return this->offset_; + } + + private: + size_t offset_{0}; + std::basic_string_view buffer_{}; + }; + + class buffer_serializer + { + public: + buffer_serializer() = default; + + void write(const void* buffer, const size_t length) + { + this->buffer_.append(static_cast(buffer), length); + } + + void write(const char* text) + { + this->write(text, strlen(text)); + } + + void write_string(const char* str, const size_t length) + { + this->write(static_cast(length)); + this->write(str, length); + } + + void write_string(const std::string& str) + { + this->write_string(str.data(), str.size()); + } + + void write_string(const char* str) + { + this->write_string(str, strlen(str)); + } + + void write(const buffer_serializer& object) + { + const auto& buffer = object.get_buffer(); + this->write(buffer.data(), buffer.size()); + } + + template + void write(const T& object) + { + static_assert(std::is_trivially_copyable_v, "Type must be trivially copyable"); + this->write(&object, sizeof(object)); + } + + template + void write(const std::vector& vec) + { + static_assert(std::is_trivially_copyable_v, "Type must be trivially copyable"); + this->write(vec.data(), vec.size() * sizeof(T)); + } + + template + void write_vector(const std::vector& vec) + { + static_assert(std::is_trivially_copyable_v, "Type must be trivially copyable"); + this->write(static_cast(vec.size())); + this->write(vec); + } + + const std::string& get_buffer() const + { + return this->buffer_; + } + + std::string move_buffer() + { + return std::move(this->buffer_); + } + + private: + std::string buffer_{}; + }; +} diff --git a/src/common/utils/concurrency.hpp b/src/common/utils/concurrency.hpp new file mode 100644 index 00000000..e8be0db0 --- /dev/null +++ b/src/common/utils/concurrency.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +namespace utils::concurrency +{ + template + class container + { + public: + template + R access(F&& accessor) const + { + std::lock_guard _{mutex_}; + return accessor(object_); + } + + template + R access(F&& accessor) + { + std::lock_guard _{mutex_}; + return accessor(object_); + } + + template + R access_with_lock(F&& accessor) const + { + std::unique_lock lock{mutex_}; + return accessor(object_, lock); + } + + template + R access_with_lock(F&& accessor) + { + std::unique_lock lock{mutex_}; + return accessor(object_, lock); + } + + T& get_raw() { return object_; } + const T& get_raw() const { return object_; } + + T copy() const + { + std::unique_lock lock{mutex_}; + return object_; + } + + std::unique_lock acquire_lock() + { + return std::unique_lock{mutex_}; + } + + private: + mutable MutexType mutex_{}; + T object_{}; + }; +} diff --git a/src/common/utils/finally.hpp b/src/common/utils/finally.hpp new file mode 100644 index 00000000..8d30b150 --- /dev/null +++ b/src/common/utils/finally.hpp @@ -0,0 +1,55 @@ +#pragma once +#include +#include + +namespace utils +{ + /* + * Copied from here: https://github.com/microsoft/GSL/blob/e0880931ae5885eb988d1a8a57acf8bc2b8dacda/include/gsl/util#L57 + */ + + template + class final_action + { + public: + static_assert(!std::is_reference::value && !std::is_const::value && + !std::is_volatile::value, + "Final_action should store its callable by value"); + + explicit final_action(F f) noexcept : f_(std::move(f)) + { + } + + final_action(final_action&& other) noexcept + : f_(std::move(other.f_)), invoke_(std::exchange(other.invoke_, false)) + { + } + + final_action(const final_action&) = delete; + final_action& operator=(const final_action&) = delete; + final_action& operator=(final_action&&) = delete; + + ~final_action() noexcept + { + if (invoke_) f_(); + } + + // Added by momo5502 + void cancel() + { + invoke_ = false; + } + + private: + F f_; + bool invoke_{true}; + }; + + template + final_action::type>::type> + finally(F&& f) noexcept + { + return final_action::type>::type>( + std::forward(f)); + } +} \ No newline at end of file diff --git a/src/common/utils/timer.hpp b/src/common/utils/timer.hpp new file mode 100644 index 00000000..b5a21553 --- /dev/null +++ b/src/common/utils/timer.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace utils +{ + template + class timer + { + public: + void update() + { + this->point_ = Clock::now(); + } + + bool has_elapsed(typename Clock::duration duration) const + { + const auto now = Clock::now(); + const auto diff = now - this->point_; + return diff > duration; + } + + private: + typename Clock::time_point point_{ Clock::now() }; + }; +} diff --git a/src/emulator/CMakeLists.txt b/src/emulator/CMakeLists.txt index 8381ec23..2e84237a 100644 --- a/src/emulator/CMakeLists.txt +++ b/src/emulator/CMakeLists.txt @@ -12,7 +12,11 @@ momo_assign_source_group(${SRC_FILES}) target_precompile_headers(emulator PRIVATE std_include.hpp) -target_link_libraries(emulator PRIVATE unicorn phnt::phnt) +target_link_libraries(emulator PRIVATE + common + unicorn + phnt::phnt +) set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT emulator) diff --git a/src/emulator/main.cpp b/src/emulator/main.cpp index c27ca250..9ad243a5 100644 --- a/src/emulator/main.cpp +++ b/src/emulator/main.cpp @@ -11,9 +11,20 @@ #define KUSD_ADDRESS 0x7ffe0000 #include "unicorn.hpp" +#include 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); @@ -24,6 +35,11 @@ namespace 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); @@ -51,16 +67,34 @@ namespace return reinterpret_cast(this->address_); } - template - void access(const F& accessor) const + 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 + void access(const F& accessor) const + { + T obj{}; + e(uc_mem_read(*this->uc_, this->address_, &obj, sizeof(obj))); + accessor(obj); - e(uc_mem_write(*this->uc_, this->address_, &obj, sizeof(obj))); + this->write(obj); } private: @@ -107,19 +141,36 @@ namespace class unicorn_hook { public: - using function = std::function; + using function = std::function; - unicorn_hook(const unicorn& uc, const int type, const uint64_t begin, const uint64_t end, function callback) + template + unicorn_hook(const unicorn& uc, const int type, const uint64_t begin, const uint64_t end, function callback, + Args... args) : uc_(&uc) - , function_(std::make_unique(std::move(callback))) { - auto* handler = +[](uc_engine*, const uint64_t address, const uint32_t size, + this->function_ = std::make_unique( + [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(user_data))(address, size); + (*static_cast(user_data))(address, size); }; - e(uc_hook_add(*this->uc_, &this->hook_, type, handler, this->function_.get(), begin, end)); + 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(user_data))(rip, 0); + }; + } + + e(uc_hook_add(*this->uc_, &this->hook_, type, handler, this->function_.get(), begin, end, args...)); } unicorn_hook(const unicorn_hook&) = delete; @@ -163,9 +214,11 @@ namespace } private: + using internal_function = std::function; + const unicorn* uc_{}; uc_hook hook_{}; - std::unique_ptr function_{}; + std::unique_ptr function_{}; }; void setup_stack(const unicorn& uc, uint64_t stack_base, size_t stack_size) @@ -209,43 +262,53 @@ namespace }); } - std::unordered_map map_module(const unicorn& uc, const std::vector& module_data, - const std::string& name) + struct mapped_binary { + uint64_t image_base{}; + uint64_t size_of_image{}; + std::unordered_map exports{}; + }; + + mapped_binary map_module(const unicorn& uc, const std::vector& module_data, + const std::string& name) + { + mapped_binary binary{}; + // TODO: Range checks auto* ptr = module_data.data(); auto* dos_header = reinterpret_cast(ptr); auto* nt_headers = reinterpret_cast(ptr + dos_header->e_lfanew); auto& optional_header = nt_headers->OptionalHeader; - auto prefered_base = optional_header.ImageBase; + binary.image_base = optional_header.ImageBase; + binary.size_of_image = optional_header.SizeOfImage; while (true) { - const auto res = uc_mem_map(uc, prefered_base, optional_header.SizeOfImage, UC_PROT_READ); + const auto res = uc_mem_map(uc, binary.image_base, binary.size_of_image, UC_PROT_READ); if (res == UC_ERR_OK) { break; } - prefered_base += 0x10000; + binary.image_base += 0x10000; - if (prefered_base < optional_header.ImageBase || (optional_header.DllCharacteristics & + if (binary.image_base < optional_header.ImageBase || (optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) == 0) { throw std::runtime_error("Failed to map range"); } } - printf("Mapping %s at %llX\n", name.c_str(), prefered_base); + printf("Mapping %s at %llX\n", name.c_str(), binary.image_base); - e(uc_mem_write(uc, prefered_base, ptr, optional_header.SizeOfHeaders)); + e(uc_mem_write(uc, binary.image_base, ptr, optional_header.SizeOfHeaders)); const std::span sections(IMAGE_FIRST_SECTION(nt_headers), nt_headers->FileHeader.NumberOfSections); for (const auto& section : sections) { - const auto target_ptr = prefered_base + section.VirtualAddress; + const auto target_ptr = binary.image_base + section.VirtualAddress; if (section.SizeOfRawData > 0) { @@ -292,54 +355,88 @@ namespace const auto* ordinals = reinterpret_cast(ptr + export_directory->AddressOfNameOrdinals); const auto* functions = reinterpret_cast(ptr + export_directory->AddressOfFunctions); - std::unordered_map exports{}; - for (DWORD i = 0; i < names_count; i++) { const auto* function_name = reinterpret_cast(ptr + names[i]); const auto function_rva = functions[ordinals[i]]; - const auto function_address = prefered_base + function_rva; + const auto function_address = binary.image_base + function_rva; - exports[function_name] = function_address; + binary.exports[function_name] = function_address; } - return exports; + return binary; } - void setup_teb_and_peb(const unicorn& uc) + 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{}; + unicorn_object peb{}; + unicorn_object process_params{}; + + mapped_binary executable{}; + mapped_binary ntdll{}; + + std::vector events{}; + }; + + process_context setup_teb_and_peb(const unicorn& uc) { setup_stack(uc, STACK_ADDRESS, STACK_SIZE); auto gs = setup_gs_segment(uc, GS_SEGMENT_ADDR, GS_SEGMENT_SIZE); - const auto teb_object = gs.reserve(); - const auto peb_object = gs.reserve(); - const auto ldr_object = gs.reserve(); + process_context context{}; - teb_object.access([&](TEB& teb) + context.teb = gs.reserve(); + context.peb = gs.reserve(); + //context.ldr = gs.reserve(); + context.process_params = gs.reserve(); + + context.teb.access([&](TEB& teb) { teb.NtTib.StackLimit = reinterpret_cast(STACK_ADDRESS); teb.NtTib.StackBase = reinterpret_cast((STACK_ADDRESS + STACK_SIZE)); - teb.NtTib.Self = &teb_object.ptr()->NtTib; - teb.ProcessEnvironmentBlock = peb_object.ptr(); + teb.NtTib.Self = &context.teb.ptr()->NtTib; + teb.ProcessEnvironmentBlock = context.peb.ptr(); }); - peb_object.access([&](PEB& peb) + context.peb.access([&](PEB& peb) { peb.ImageBaseAddress = nullptr; - peb.Ldr = ldr_object.ptr(); + //peb.Ldr = context.ldr.ptr(); + peb.ProcessParameters = context.process_params.ptr(); }); - ldr_object.access([&](PEB_LDR_DATA& ldr) + /*context.ldr.access([&](PEB_LDR_DATA& ldr) { - ldr.InLoadOrderModuleList.Flink = &ldr_object.ptr()->InLoadOrderModuleList; + ldr.InLoadOrderModuleList.Flink = &context.ldr.ptr()->InLoadOrderModuleList; ldr.InLoadOrderModuleList.Blink = ldr.InLoadOrderModuleList.Flink; - ldr.InMemoryOrderModuleList.Flink = &ldr_object.ptr()->InMemoryOrderModuleList; + ldr.InMemoryOrderModuleList.Flink = &context.ldr.ptr()->InMemoryOrderModuleList; ldr.InMemoryOrderModuleList.Blink = ldr.InMemoryOrderModuleList.Flink; - ldr.InInitializationOrderModuleList.Flink = &ldr_object.ptr()->InInitializationOrderModuleList; + ldr.InInitializationOrderModuleList.Flink = &context.ldr.ptr()->InInitializationOrderModuleList; ldr.InInitializationOrderModuleList.Blink = ldr.InInitializationOrderModuleList.Flink; - }); + });*/ + + return context; } std::vector load_file(const std::filesystem::path& file) @@ -348,33 +445,338 @@ namespace return {(std::istreambuf_iterator(stream)), std::istreambuf_iterator()}; } - std::unordered_map map_file(const unicorn& uc, const std::filesystem::path& file) + mapped_binary map_file(const unicorn& uc, const std::filesystem::path& file) { const auto data = load_file(file); return map_module(uc, data, file.generic_string()); } + void handle_NtQueryPerformanceCounter(const unicorn& uc) + { + const unicorn_object performance_counter{uc, uc.reg(UC_X86_REG_R10)}; + const unicorn_object 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(UC_X86_REG_RAX, STATUS_SUCCESS); + } + catch (...) + { + uc.reg(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(UC_X86_REG_RAX, STATUS_NOT_SUPPORTED); + } + + void handle_NtCreateEvent(const unicorn& uc, process_context& context) + { + const unicorn_object 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(UC_X86_REG_R9D); + const auto initial_state = static_cast(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(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(UC_X86_REG_R8D); + const auto memory_information = uc.reg(UC_X86_REG_R9); + const auto memory_information_length = static_cast(uc.read_stack(5)); + const unicorn_object return_length{uc, uc.read_stack(6)}; + + if (process_handle != ~0ULL) + { + uc.reg(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(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 info{uc, memory_information}; + + info.access([&](MEMORY_IMAGE_INFORMATION& image_info) + { + image_info.ImageBase = reinterpret_cast(context.ntdll.image_base); + image_info.SizeOfImage = context.ntdll.size_of_image; + }); + + uc.reg(UC_X86_REG_RAX, STATUS_SUCCESS); + } + + void handle_NtQuerySystemInformation(const unicorn& uc, const process_context& context) + { + const auto info_class = uc.reg(UC_X86_REG_R10D); + const auto system_information = uc.reg(UC_X86_REG_RDX); + const auto system_information_length = uc.reg(UC_X86_REG_R8D); + const unicorn_object return_length{uc, uc.reg(UC_X86_REG_R9)}; + + if (info_class != SystemBasicInformation) + { + 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(UC_X86_REG_RAX, STATUS_BUFFER_OVERFLOW); + return; + } + + const unicorn_object 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(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 base_address{uc, uc.reg(UC_X86_REG_RDX)}; + const unicorn_object bytes_to_protect{uc, uc.reg(UC_X86_REG_R8)}; + const auto protection = uc.reg(UC_X86_REG_R9D); + const unicorn_object old_protection{uc, uc.read_stack(5)}; + + if (process_handle != ~0ULL) + { + uc.reg(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(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(UC_X86_REG_RAX, STATUS_SUCCESS); + } + void run() { const unicorn uc{UC_ARCH_X86, UC_MODE_64}; setup_kusd(uc); - setup_teb_and_peb(uc); + auto context = setup_teb_and_peb(uc); - const auto executable_exports = map_file(uc, R"(C:\Users\mauri\Desktop\ConsoleApplication6.exe)"); + context.executable = map_file(uc, R"(C:\Users\mauri\Desktop\ConsoleApplication6.exe)"); - const auto ntdll_exports = map_file(uc, R"(C:\Windows\System32\ntdll.dll)"); + context.peb.access([&](PEB& peb) + { + peb.ImageBaseAddress = reinterpret_cast(context.executable.image_base); + }); - const auto entry1 = ntdll_exports.at("LdrInitializeThunk"); - const auto entry2 = ntdll_exports.at("RtlUserThreadStart"); + context.ntdll = map_file(uc, R"(C:\Windows\System32\ntdll.dll)"); + + const auto entry1 = context.ntdll.exports.at("LdrInitializeThunk"); + const auto entry2 = context.ntdll.exports.at("RtlUserThreadStart"); (void)entry1; (void)entry2; - unicorn_hook hook(uc, UC_HOOK_INTR, 0, 0, [](const uint64_t address, const uint32_t /*size*/) - { - printf("Syscall: %llX\n", address); - }); + unicorn_hook hook(uc, UC_HOOK_INSN, 0, std::numeric_limits::max(), + [&](const unicorn&, const uint64_t address, const uint32_t /*size*/) + { + const auto syscall_id = uc.reg(UC_X86_REG_EAX); + + printf("Handling syscall: %X (%llX)\n", syscall_id, address); + + try + { + switch (syscall_id) + { + 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(UC_X86_REG_RAX, STATUS_NOT_IMPLEMENTED); + uc.stop(); + break; + } + } + catch (...) + { + uc.reg(UC_X86_REG_RAX, STATUS_UNSUCCESSFUL); + } + }, UC_X86_INS_SYSCALL); + + unicorn_hook hook2(uc, UC_HOOK_CODE, 0, std::numeric_limits::max(), + [](const unicorn&, const uint64_t address, const uint32_t /*size*/) + { + printf("Inst: %llX\n", address); + }); const auto err = uc_emu_start(uc, entry1, 0, 0, 0); if (err != UC_ERR_OK) @@ -406,7 +808,7 @@ int main(int /*argc*/, char** /*argv*/) puts(e.what()); #ifdef _WIN32 - MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR); + //MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR); #endif } diff --git a/src/emulator/unicorn.hpp b/src/emulator/unicorn.hpp index 82ea24d2..4ec5efc6 100644 --- a/src/emulator/unicorn.hpp +++ b/src/emulator/unicorn.hpp @@ -42,6 +42,34 @@ public: return this->uc_; } + template + T reg(const int regid) const + { + T value{}; + e(uc_reg_read(this->uc_, regid, &value)); + return value; + } + + template + void reg(const int regid, const T& value) const + { + e(uc_reg_write(this->uc_, regid, &value)); + } + + void stop() const + { + e(uc_emu_stop(this->uc_)); + } + + uint64_t read_stack(const size_t index) const + { + uint64_t result{}; + const auto rsp = this->reg(UC_X86_REG_RSP); + + e(uc_mem_read(this->uc_, rsp + (index * sizeof(result)), &result, sizeof(result))); + return result; + } + private: uc_engine* uc_{}; };