Extract API set data

This commit is contained in:
momo5502
2025-02-10 17:21:34 +01:00
parent 2815a74a18
commit 04ca0981a1
5 changed files with 215 additions and 162 deletions

View File

@@ -0,0 +1,171 @@
#include "apiset.hpp"
#include "default_apiset.hpp"
#include "../emulator_utils.hpp"
#include <x64_emulator.hpp>
#include <utils/io.hpp>
#include <utils/compression.hpp>
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<const uint8_t*>(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<ULONG>(address - result_base);
}
std::vector<uint8_t> decompress_apiset(const std::vector<uint8_t>& apiset)
{
auto buffer = utils::compression::zlib::decompress(apiset);
if (buffer.empty())
throw std::runtime_error("Failed to decompress API-SET");
return buffer;
}
std::vector<uint8_t> obtain_data(const location location, const std::filesystem::path& root)
{
switch (location)
{
#ifdef OS_WINDOWS
case location::host: {
const auto apiSetMap =
reinterpret_cast<const API_SET_NAMESPACE*>(NtCurrentTeb64()->ProcessEnvironmentBlock->ApiSetMap);
const auto* dataPtr = reinterpret_cast<const uint8_t*>(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<uint8_t> apiset{apiset_w10, apiset_w10 + sizeof(apiset_w10)};
return decompress_apiset(apiset);
}
case location::default_windows_11: {
const std::vector<uint8_t> 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<API_SET_NAMESPACE> clone(x64_emulator& emu, emulator_allocator& allocator,
const container& container)
{
return clone(emu, allocator, container.get());
}
emulator_object<API_SET_NAMESPACE> clone(x64_emulator& emu, emulator_allocator& allocator,
const API_SET_NAMESPACE& orig_api_set_map)
{
const auto api_set_map_obj = allocator.reserve<API_SET_NAMESPACE>();
const auto ns_entries_obj = allocator.reserve<API_SET_NAMESPACE_ENTRY>(orig_api_set_map.Count);
const auto hash_entries_obj = allocator.reserve<API_SET_HASH_ENTRY>(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<ULONG>(ns_entries_obj.value() - api_set_map_obj.value());
api_set.HashOffset = static_cast<ULONG>(hash_entries_obj.value() - api_set_map_obj.value());
});
const auto orig_ns_entries =
offset_pointer<API_SET_NAMESPACE_ENTRY>(&orig_api_set_map, orig_api_set_map.EntryOffset);
const auto orig_hash_entries =
offset_pointer<API_SET_HASH_ENTRY>(&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<API_SET_VALUE_ENTRY>(ns_entry.ValueCount);
const auto orig_values = offset_pointer<API_SET_VALUE_ENTRY>(&orig_api_set_map, ns_entry.ValueOffset);
ns_entry.ValueOffset = static_cast<ULONG>(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;
}
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <vector>
#include <cstdint>
#include <filesystem>
#include "../emulator_utils.hpp"
namespace apiset
{
enum class location : uint8_t
{
host,
file,
default_windows_10,
default_windows_11
};
struct container
{
std::vector<uint8_t> data{};
const API_SET_NAMESPACE& get() const
{
return *reinterpret_cast<const API_SET_NAMESPACE*>(data.data());
}
};
container obtain(location location, const std::filesystem::path& root);
container obtain(const std::filesystem::path& root);
emulator_object<API_SET_NAMESPACE> clone(x64_emulator& emu, emulator_allocator& allocator,
const API_SET_NAMESPACE& orig_api_set_map);
emulator_object<API_SET_NAMESPACE> clone(x64_emulator& emu, emulator_allocator& allocator,
const container& container);
}

View File

@@ -1,7 +1,6 @@
#include "std_include.hpp"
#include "windows_emulator.hpp"
#include "address_utils.hpp"
#include "cpu_context.hpp"
#include <unicorn_x64_emulator.hpp>
@@ -11,8 +10,8 @@
#include <utils/compression.hpp>
#include <utils/lazy_object.hpp>
#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<const uint8_t*>(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<ULONG>(address - result_base);
}
emulator_object<API_SET_NAMESPACE> 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<API_SET_NAMESPACE>();
const auto ns_entries_obj = allocator.reserve<API_SET_NAMESPACE_ENTRY>(orig_api_set_map.Count);
const auto hash_entries_obj = allocator.reserve<API_SET_HASH_ENTRY>(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<ULONG>(ns_entries_obj.value() - api_set_map_obj.value());
api_set.HashOffset = static_cast<ULONG>(hash_entries_obj.value() - api_set_map_obj.value());
});
const auto orig_ns_entries =
offset_pointer<API_SET_NAMESPACE_ENTRY>(&orig_api_set_map, orig_api_set_map.EntryOffset);
const auto orig_hash_entries =
offset_pointer<API_SET_HASH_ENTRY>(&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<API_SET_VALUE_ENTRY>(ns_entry.ValueCount);
const auto orig_values = offset_pointer<API_SET_VALUE_ENTRY>(&orig_api_set_map, ns_entry.ValueOffset);
ns_entry.ValueOffset = static_cast<ULONG>(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<uint8_t> decompress_apiset(const std::vector<uint8_t>& apiset)
{
auto buffer = utils::compression::zlib::decompress(apiset);
if (buffer.empty())
throw std::runtime_error("Failed to decompress API-SET");
return buffer;
}
std::vector<uint8_t> 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<const API_SET_NAMESPACE*>(NtCurrentTeb64()->ProcessEnvironmentBlock->ApiSetMap);
const auto* dataPtr = reinterpret_cast<const uint8_t*>(apiSetMap);
std::vector<uint8_t> 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<uint8_t> apiset{apiset_w10, apiset_w10 + sizeof(apiset_w10)};
return decompress_apiset(apiset);
}
case apiset_location::default_windows_11: {
const std::vector<uint8_t> apiset{apiset_w11, apiset_w11 + sizeof(apiset_w11)};
return decompress_apiset(apiset);
}
default:
throw std::runtime_error("Bad API set location");
}
}
emulator_object<API_SET_NAMESPACE> 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<const API_SET_NAMESPACE&>(*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);

View File

@@ -47,14 +47,6 @@ struct emulator_settings
std::set<std::string, std::less<>> modules{};
};
enum class apiset_location : uint8_t
{
host,
file,
default_windows_10,
default_windows_11
};
class windows_emulator
{
std::unique_ptr<x64_emulator> emu_{};