mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-19 19:53:56 +00:00
Extract API set data
This commit is contained in:
171
src/windows-emulator/apiset/apiset.cpp
Normal file
171
src/windows-emulator/apiset/apiset.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
37
src/windows-emulator/apiset/apiset.hpp
Normal file
37
src/windows-emulator/apiset/apiset.hpp
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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_{};
|
||||
|
||||
Reference in New Issue
Block a user