From 04ca0981a10bab3735d4ca2d747d7e9dc322750f Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 10 Feb 2025 17:21:34 +0100 Subject: [PATCH] Extract API set data --- src/windows-emulator/apiset/apiset.cpp | 171 ++++++++++++++++++ src/windows-emulator/apiset/apiset.hpp | 37 ++++ .../{apiset.hpp => apiset/default_apiset.hpp} | 0 src/windows-emulator/windows_emulator.cpp | 161 +---------------- src/windows-emulator/windows_emulator.hpp | 8 - 5 files changed, 215 insertions(+), 162 deletions(-) create mode 100644 src/windows-emulator/apiset/apiset.cpp create mode 100644 src/windows-emulator/apiset/apiset.hpp rename src/windows-emulator/{apiset.hpp => apiset/default_apiset.hpp} (100%) diff --git a/src/windows-emulator/apiset/apiset.cpp b/src/windows-emulator/apiset/apiset.cpp new file mode 100644 index 00000000..2031acae --- /dev/null +++ b/src/windows-emulator/apiset/apiset.cpp @@ -0,0 +1,171 @@ +#include "apiset.hpp" +#include "default_apiset.hpp" + +#include "../emulator_utils.hpp" + +#include + +#include +#include + +namespace apiset +{ + namespace + { + uint64_t copy_string(x64_emulator& emu, emulator_allocator& allocator, const void* base_ptr, + const uint64_t offset, const size_t length) + { + if (!length) + { + return 0; + } + + const auto length_to_allocate = length + 2; + const auto str_obj = allocator.reserve(length_to_allocate); + emu.write_memory(str_obj, static_cast(base_ptr) + offset, length); + + return str_obj; + } + + ULONG copy_string_as_relative(x64_emulator& emu, emulator_allocator& allocator, const uint64_t result_base, + const void* base_ptr, const uint64_t offset, const size_t length) + { + const auto address = copy_string(emu, allocator, base_ptr, offset, length); + if (!address) + { + return 0; + } + + assert(address > result_base); + return static_cast(address - result_base); + } + + std::vector decompress_apiset(const std::vector& apiset) + { + auto buffer = utils::compression::zlib::decompress(apiset); + if (buffer.empty()) + throw std::runtime_error("Failed to decompress API-SET"); + return buffer; + } + + std::vector obtain_data(const location location, const std::filesystem::path& root) + { + switch (location) + { +#ifdef OS_WINDOWS + case location::host: { + const auto apiSetMap = + reinterpret_cast(NtCurrentTeb64()->ProcessEnvironmentBlock->ApiSetMap); + const auto* dataPtr = reinterpret_cast(apiSetMap); + return {dataPtr, dataPtr + apiSetMap->Size}; + } +#else + case location::host: + throw std::runtime_error("The APISET host location is not supported on this platform"); +#endif + case location::file: { + const auto apiset = utils::io::read_file(root / "api-set.bin"); + if (apiset.empty()) + throw std::runtime_error("Failed to read file api-set.bin"); + return decompress_apiset(apiset); + } + case location::default_windows_10: { + const std::vector apiset{apiset_w10, apiset_w10 + sizeof(apiset_w10)}; + return decompress_apiset(apiset); + } + case location::default_windows_11: { + const std::vector apiset{apiset_w11, apiset_w11 + sizeof(apiset_w11)}; + return decompress_apiset(apiset); + } + default: + throw std::runtime_error("Bad API set location"); + } + } + } + + container obtain(const location location, const std::filesystem::path& root) + { + return {.data = obtain_data(location, root)}; + } + + container obtain(const std::filesystem::path& root) + { + auto apiset_loc = location::file; + + if (root.empty()) + { +#ifdef OS_WINDOWS + apiset_loc = location::host; +#else + apiset_loc = location::default_windows_11; +#endif + } + + return obtain(apiset_loc, root); + } + + emulator_object clone(x64_emulator& emu, emulator_allocator& allocator, + const container& container) + { + return clone(emu, allocator, container.get()); + } + + emulator_object clone(x64_emulator& emu, emulator_allocator& allocator, + const API_SET_NAMESPACE& orig_api_set_map) + { + const auto api_set_map_obj = allocator.reserve(); + const auto ns_entries_obj = allocator.reserve(orig_api_set_map.Count); + const auto hash_entries_obj = allocator.reserve(orig_api_set_map.Count); + + api_set_map_obj.access([&](API_SET_NAMESPACE& api_set) { + api_set = orig_api_set_map; + api_set.EntryOffset = static_cast(ns_entries_obj.value() - api_set_map_obj.value()); + api_set.HashOffset = static_cast(hash_entries_obj.value() - api_set_map_obj.value()); + }); + + const auto orig_ns_entries = + offset_pointer(&orig_api_set_map, orig_api_set_map.EntryOffset); + const auto orig_hash_entries = + offset_pointer(&orig_api_set_map, orig_api_set_map.HashOffset); + + for (ULONG i = 0; i < orig_api_set_map.Count; ++i) + { + auto ns_entry = orig_ns_entries[i]; + const auto hash_entry = orig_hash_entries[i]; + + ns_entry.NameOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), &orig_api_set_map, + ns_entry.NameOffset, ns_entry.NameLength); + + if (!ns_entry.ValueCount) + { + continue; + } + + const auto values_obj = allocator.reserve(ns_entry.ValueCount); + const auto orig_values = offset_pointer(&orig_api_set_map, ns_entry.ValueOffset); + + ns_entry.ValueOffset = static_cast(values_obj.value() - api_set_map_obj.value()); + + for (ULONG j = 0; j < ns_entry.ValueCount; ++j) + { + auto value = orig_values[j]; + + value.ValueOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), &orig_api_set_map, + value.ValueOffset, value.ValueLength); + + if (value.NameLength) + { + value.NameOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), + &orig_api_set_map, value.NameOffset, value.NameLength); + } + + values_obj.write(value, j); + } + + ns_entries_obj.write(ns_entry, i); + hash_entries_obj.write(hash_entry, i); + } + + return api_set_map_obj; + } +} diff --git a/src/windows-emulator/apiset/apiset.hpp b/src/windows-emulator/apiset/apiset.hpp new file mode 100644 index 00000000..d0097da1 --- /dev/null +++ b/src/windows-emulator/apiset/apiset.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +#include "../emulator_utils.hpp" + +namespace apiset +{ + enum class location : uint8_t + { + host, + file, + default_windows_10, + default_windows_11 + }; + + struct container + { + std::vector data{}; + + const API_SET_NAMESPACE& get() const + { + return *reinterpret_cast(data.data()); + } + }; + + container obtain(location location, const std::filesystem::path& root); + container obtain(const std::filesystem::path& root); + + emulator_object clone(x64_emulator& emu, emulator_allocator& allocator, + const API_SET_NAMESPACE& orig_api_set_map); + + emulator_object clone(x64_emulator& emu, emulator_allocator& allocator, + const container& container); +} diff --git a/src/windows-emulator/apiset.hpp b/src/windows-emulator/apiset/default_apiset.hpp similarity index 100% rename from src/windows-emulator/apiset.hpp rename to src/windows-emulator/apiset/default_apiset.hpp diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 3ed9bd45..64c63cca 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -1,7 +1,6 @@ #include "std_include.hpp" #include "windows_emulator.hpp" -#include "address_utils.hpp" #include "cpu_context.hpp" #include @@ -11,8 +10,8 @@ #include #include -#include "apiset.hpp" #include "exception_dispatch.hpp" +#include "apiset/apiset.hpp" constexpr auto MAX_INSTRUCTIONS_PER_TIME_SLICE = 100000; @@ -50,144 +49,6 @@ namespace adjust_application(app_settings); } - uint64_t copy_string(x64_emulator& emu, emulator_allocator& allocator, const void* base_ptr, const uint64_t offset, - const size_t length) - { - if (!length) - { - return 0; - } - - const auto length_to_allocate = length + 2; - const auto str_obj = allocator.reserve(length_to_allocate); - emu.write_memory(str_obj, static_cast(base_ptr) + offset, length); - - return str_obj; - } - - ULONG copy_string_as_relative(x64_emulator& emu, emulator_allocator& allocator, const uint64_t result_base, - const void* base_ptr, const uint64_t offset, const size_t length) - { - const auto address = copy_string(emu, allocator, base_ptr, offset, length); - if (!address) - { - return 0; - } - - assert(address > result_base); - return static_cast(address - result_base); - } - - emulator_object clone_api_set_map(x64_emulator& emu, emulator_allocator& allocator, - const API_SET_NAMESPACE& orig_api_set_map) - { - const auto api_set_map_obj = allocator.reserve(); - const auto ns_entries_obj = allocator.reserve(orig_api_set_map.Count); - const auto hash_entries_obj = allocator.reserve(orig_api_set_map.Count); - - api_set_map_obj.access([&](API_SET_NAMESPACE& api_set) { - api_set = orig_api_set_map; - api_set.EntryOffset = static_cast(ns_entries_obj.value() - api_set_map_obj.value()); - api_set.HashOffset = static_cast(hash_entries_obj.value() - api_set_map_obj.value()); - }); - - const auto orig_ns_entries = - offset_pointer(&orig_api_set_map, orig_api_set_map.EntryOffset); - const auto orig_hash_entries = - offset_pointer(&orig_api_set_map, orig_api_set_map.HashOffset); - - for (ULONG i = 0; i < orig_api_set_map.Count; ++i) - { - auto ns_entry = orig_ns_entries[i]; - const auto hash_entry = orig_hash_entries[i]; - - ns_entry.NameOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), &orig_api_set_map, - ns_entry.NameOffset, ns_entry.NameLength); - - if (!ns_entry.ValueCount) - { - continue; - } - - const auto values_obj = allocator.reserve(ns_entry.ValueCount); - const auto orig_values = offset_pointer(&orig_api_set_map, ns_entry.ValueOffset); - - ns_entry.ValueOffset = static_cast(values_obj.value() - api_set_map_obj.value()); - - for (ULONG j = 0; j < ns_entry.ValueCount; ++j) - { - auto value = orig_values[j]; - - value.ValueOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), &orig_api_set_map, - value.ValueOffset, value.ValueLength); - - if (value.NameLength) - { - value.NameOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), - &orig_api_set_map, value.NameOffset, value.NameLength); - } - - values_obj.write(value, j); - } - - ns_entries_obj.write(ns_entry, i); - hash_entries_obj.write(hash_entry, i); - } - - return api_set_map_obj; - } - - std::vector decompress_apiset(const std::vector& apiset) - { - auto buffer = utils::compression::zlib::decompress(apiset); - if (buffer.empty()) - throw std::runtime_error("Failed to decompress API-SET"); - return buffer; - } - - std::vector obtain_api_set(const apiset_location location, const std::filesystem::path& root) - { - switch (location) - { -#ifdef OS_WINDOWS - case apiset_location::host: { - const auto apiSetMap = - reinterpret_cast(NtCurrentTeb64()->ProcessEnvironmentBlock->ApiSetMap); - const auto* dataPtr = reinterpret_cast(apiSetMap); - std::vector buffer(dataPtr, dataPtr + apiSetMap->Size); - return buffer; - } -#else - case apiset_location::host: - throw std::runtime_error("The APISET host location is not supported on this platform"); -#endif - case apiset_location::file: { - const auto apiset = utils::io::read_file(root / "api-set.bin"); - if (apiset.empty()) - throw std::runtime_error("Failed to read file api-set.bin"); - return decompress_apiset(apiset); - } - case apiset_location::default_windows_10: { - const std::vector apiset{apiset_w10, apiset_w10 + sizeof(apiset_w10)}; - return decompress_apiset(apiset); - } - case apiset_location::default_windows_11: { - const std::vector apiset{apiset_w11, apiset_w11 + sizeof(apiset_w11)}; - return decompress_apiset(apiset); - } - default: - throw std::runtime_error("Bad API set location"); - } - } - - emulator_object build_api_set_map(x64_emulator& emu, emulator_allocator& allocator, - const apiset_location location = apiset_location::host, - const std::filesystem::path& root = {}) - { - return clone_api_set_map(emu, allocator, - reinterpret_cast(*obtain_api_set(location, root).data())); - } - emulator_allocator create_allocator(memory_manager& memory, const size_t size) { const auto base = memory.find_free_allocation_base(size); @@ -210,7 +71,8 @@ namespace } void setup_context(windows_emulator& win_emu, const application_settings& app_settings, - const emulator_settings& emu_settings, const uint64_t process_image_base) + const emulator_settings& emu_settings, const uint64_t process_image_base, + const apiset::container& apiset_container) { auto& emu = win_emu.emu(); auto& context = win_emu.process; @@ -278,21 +140,10 @@ namespace proc_params.MaximumLength = proc_params.Length; }); - apiset_location apiset_loc = apiset_location::file; - - if (win_emu.emulation_root.empty()) - { -#ifdef OS_WINDOWS - apiset_loc = apiset_location::host; -#else - apiset_loc = apiset_location::default_windows_11; -#endif - } - context.peb.access([&](PEB64& peb) { peb.ImageBaseAddress = process_image_base; peb.ProcessParameters = context.process_params.ptr(); - peb.ApiSetMap = build_api_set_map(emu, allocator, apiset_loc, win_emu.emulation_root).ptr(); + peb.ApiSetMap = apiset::clone(emu, allocator, apiset_container).ptr(); peb.ProcessHeap = nullptr; peb.ProcessHeaps = nullptr; @@ -487,7 +338,9 @@ void windows_emulator::setup_process(const application_settings& app_settings, c auto* exe = this->mod_manager.map_module(app_settings.application, this->log, true); - setup_context(*this, app_settings, emu_settings, exe->image_base); + const auto apiset_data = apiset::obtain(this->emulation_root); + + setup_context(*this, app_settings, emu_settings, exe->image_base, apiset_data); context.executable = exe; context.ntdll = this->mod_manager.map_module(R"(C:\Windows\System32\ntdll.dll)", this->log, true); diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 8e663858..6665dc55 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -47,14 +47,6 @@ struct emulator_settings std::set> modules{}; }; -enum class apiset_location : uint8_t -{ - host, - file, - default_windows_10, - default_windows_11 -}; - class windows_emulator { std::unique_ptr emu_{};