mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-21 12:43:57 +00:00
Major cleanup and refactoring (#140)
This commit is contained in:
@@ -27,17 +27,17 @@ namespace
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
watch_object(win_emu, modules, *win_emu.current_thread().teb, cache_logging);
|
||||
watch_object(win_emu, modules, win_emu.process().peb, cache_logging);
|
||||
watch_object(win_emu, modules, win_emu.process.peb, cache_logging);
|
||||
watch_object(win_emu, modules, emulator_object<KUSER_SHARED_DATA64>{win_emu.emu(), kusd_mmio::address()},
|
||||
cache_logging);
|
||||
|
||||
auto* params_hook = watch_object(win_emu, modules, win_emu.process().process_params, cache_logging);
|
||||
auto* params_hook = watch_object(win_emu, modules, win_emu.process.process_params, cache_logging);
|
||||
|
||||
win_emu.emu().hook_memory_write(
|
||||
win_emu.process().peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
|
||||
win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
|
||||
[&win_emu, cache_logging, params_hook, modules](const uint64_t address, size_t,
|
||||
const uint64_t value) mutable {
|
||||
const auto target_address = win_emu.process().peb.value() + offsetof(PEB64, ProcessParameters);
|
||||
const auto target_address = win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters);
|
||||
|
||||
if (address == target_address)
|
||||
{
|
||||
@@ -80,7 +80,7 @@ namespace
|
||||
throw;
|
||||
}
|
||||
|
||||
const auto exit_status = win_emu.process().exit_status;
|
||||
const auto exit_status = win_emu.process.exit_status;
|
||||
if (!exit_status.has_value())
|
||||
{
|
||||
win_emu.log.print(color::red, "Emulation terminated without status!\n");
|
||||
@@ -113,18 +113,21 @@ namespace
|
||||
return false;
|
||||
}
|
||||
|
||||
const emulator_settings settings{
|
||||
application_settings app_settings{
|
||||
.application = args[0],
|
||||
.registry_directory = options.registry_path,
|
||||
.emulation_root = options.emulation_root,
|
||||
.arguments = parse_arguments(args),
|
||||
};
|
||||
|
||||
const emulator_settings settings{
|
||||
.emulation_root = options.emulation_root,
|
||||
.registry_directory = options.registry_path,
|
||||
.verbose_calls = options.verbose_logging,
|
||||
.disable_logging = options.silent,
|
||||
.silent_until_main = options.concise_logging,
|
||||
.modules = options.modules,
|
||||
};
|
||||
|
||||
windows_emulator win_emu{settings};
|
||||
windows_emulator win_emu{std::move(app_settings), settings};
|
||||
|
||||
(void)&watch_system_objects;
|
||||
watch_system_objects(win_emu, options.modules, options.concise_logging);
|
||||
@@ -133,12 +136,12 @@ namespace
|
||||
if (options.silent)
|
||||
{
|
||||
win_emu.buffer_stdout = false;
|
||||
win_emu.callbacks().stdout_callback = [](const std::string_view data) {
|
||||
win_emu.callbacks.stdout_callback = [](const std::string_view data) {
|
||||
(void)fwrite(data.data(), 1, data.size(), stdout);
|
||||
};
|
||||
}
|
||||
|
||||
const auto& exe = *win_emu.process().executable;
|
||||
const auto& exe = *win_emu.mod_manager.executable;
|
||||
|
||||
const auto concise_logging = options.concise_logging;
|
||||
|
||||
@@ -151,7 +154,7 @@ namespace
|
||||
|
||||
auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) {
|
||||
const auto rip = win_emu.emu().read_instruction_pointer();
|
||||
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
|
||||
if (win_emu.mod_manager.find_by_address(rip) != win_emu.mod_manager.executable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -171,7 +174,7 @@ namespace
|
||||
|
||||
const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) {
|
||||
const auto rip = win_emu.emu().read_instruction_pointer();
|
||||
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
|
||||
if (win_emu.mod_manager.find_by_address(rip) != win_emu.mod_manager.executable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ emulator_hook* watch_object(windows_emulator& emu, const std::set<std::string, s
|
||||
object.value(), object.size(),
|
||||
[i = std::move(info), object, &emu, cache_logging, modules](const uint64_t address, size_t, uint64_t) {
|
||||
const auto rip = emu.emu().read_instruction_pointer();
|
||||
const auto* mod = emu.process().mod_manager.find_by_address(rip);
|
||||
const auto is_main_access = mod == emu.process().executable || modules.contains(mod->name);
|
||||
const auto* mod = emu.mod_manager.find_by_address(rip);
|
||||
const auto is_main_access = mod == emu.mod_manager.executable || modules.contains(mod->name);
|
||||
|
||||
if (!emu.verbose_calls && !is_main_access)
|
||||
{
|
||||
|
||||
@@ -250,7 +250,7 @@ typedef struct _PEB64
|
||||
|
||||
EmulatorTraits<Emu64>::HANDLE Mutant;
|
||||
|
||||
std::uint64_t* ImageBaseAddress;
|
||||
std::uint64_t ImageBaseAddress;
|
||||
PPEB_LDR_DATA64 Ldr;
|
||||
PRTL_USER_PROCESS_PARAMETERS64 ProcessParameters;
|
||||
std::uint64_t* SubSystemData;
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace
|
||||
win_emu.log.disable_output(true);
|
||||
win_emu.start();
|
||||
|
||||
if (win_emu.process().exception_rip.has_value())
|
||||
if (win_emu.process.exception_rip.has_value())
|
||||
{
|
||||
throw std::runtime_error("Exception!");
|
||||
}
|
||||
@@ -34,7 +34,7 @@ namespace
|
||||
|
||||
void forward_emulator(windows_emulator& win_emu)
|
||||
{
|
||||
const auto target = win_emu.process().executable->find_export("vulnerable");
|
||||
const auto target = win_emu.mod_manager.executable->find_export("vulnerable");
|
||||
win_emu.emu().hook_memory_execution(target, 1, [&](uint64_t, size_t, uint64_t) { win_emu.emu().stop(); });
|
||||
|
||||
run_emulation(win_emu);
|
||||
@@ -42,7 +42,7 @@ namespace
|
||||
|
||||
struct fuzzer_executer : fuzzer::executer
|
||||
{
|
||||
windows_emulator emu{"./"}; // TODO: Fix root directory
|
||||
windows_emulator emu{{.emulation_root = "./"}}; // TODO: Fix root directory
|
||||
std::span<const std::byte> emulator_data{};
|
||||
std::unordered_set<uint64_t> visited_blocks{};
|
||||
const std::function<fuzzer::coverage_functor>* handler{nullptr};
|
||||
@@ -83,8 +83,8 @@ namespace
|
||||
|
||||
restore_emulator();
|
||||
|
||||
const auto memory = emu.memory().allocate_memory(
|
||||
page_align_up(std::max(data.size(), static_cast<size_t>(1))), memory_permission::read_write);
|
||||
const auto memory = emu.memory.allocate_memory(page_align_up(std::max(data.size(), static_cast<size_t>(1))),
|
||||
memory_permission::read_write);
|
||||
emu.emu().write_memory(memory, data.data(), data.size());
|
||||
|
||||
emu.emu().reg(x64_register::rcx, memory);
|
||||
@@ -137,7 +137,7 @@ namespace
|
||||
|
||||
void run(const std::string_view application)
|
||||
{
|
||||
emulator_settings settings{
|
||||
application_settings settings{
|
||||
.application = application,
|
||||
};
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace test
|
||||
auto emu = create_sample_emulator();
|
||||
emu.start({}, count);
|
||||
|
||||
ASSERT_EQ(emu.process().executed_instructions, count);
|
||||
ASSERT_EQ(emu.process.executed_instructions, count);
|
||||
}
|
||||
|
||||
TEST(EmulationTest, CountedEmulationIsAccurate)
|
||||
@@ -27,7 +27,7 @@ namespace test
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||
|
||||
const auto executedInstructions = emu.process().executed_instructions;
|
||||
const auto executedInstructions = emu.process.executed_instructions;
|
||||
|
||||
auto new_emu = create_sample_emulator();
|
||||
|
||||
@@ -36,12 +36,12 @@ namespace test
|
||||
|
||||
new_emu.start({}, instructionsToExecute);
|
||||
|
||||
ASSERT_EQ(new_emu.process().executed_instructions, instructionsToExecute);
|
||||
ASSERT_EQ(new_emu.process.executed_instructions, instructionsToExecute);
|
||||
ASSERT_NOT_TERMINATED(new_emu);
|
||||
|
||||
new_emu.start({}, offset);
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
|
||||
ASSERT_EQ(new_emu.process().executed_instructions, executedInstructions);
|
||||
ASSERT_EQ(new_emu.process.executed_instructions, executedInstructions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <windows_emulator.hpp>
|
||||
|
||||
#define ASSERT_NOT_TERMINATED(win_emu) \
|
||||
do \
|
||||
{ \
|
||||
ASSERT_FALSE((win_emu).process().exit_status.has_value()); \
|
||||
#define ASSERT_NOT_TERMINATED(win_emu) \
|
||||
do \
|
||||
{ \
|
||||
ASSERT_FALSE((win_emu).process.exit_status.has_value()); \
|
||||
} while (false)
|
||||
|
||||
#define ASSERT_TERMINATED_WITH_STATUS(win_emu, status) \
|
||||
do \
|
||||
{ \
|
||||
ASSERT_TRUE((win_emu).process().exit_status.has_value()); \
|
||||
ASSERT_EQ(*(win_emu).process().exit_status, status); \
|
||||
#define ASSERT_TERMINATED_WITH_STATUS(win_emu, status) \
|
||||
do \
|
||||
{ \
|
||||
ASSERT_TRUE((win_emu).process.exit_status.has_value()); \
|
||||
ASSERT_EQ(*(win_emu).process.exit_status, status); \
|
||||
} while (false)
|
||||
|
||||
#define ASSERT_TERMINATED_SUCCESSFULLY(win_emu) ASSERT_TERMINATED_WITH_STATUS(win_emu, STATUS_SUCCESS)
|
||||
@@ -38,7 +38,30 @@ namespace test
|
||||
return env;
|
||||
}
|
||||
|
||||
inline windows_emulator create_sample_emulator(emulator_settings settings, const bool reproducible = false,
|
||||
struct sample_configuration
|
||||
{
|
||||
bool reproducible{false};
|
||||
bool print_time{false};
|
||||
};
|
||||
|
||||
inline application_settings get_sample_app_settings(const sample_configuration& config)
|
||||
{
|
||||
application_settings settings{.application = "C:\\test-sample.exe"};
|
||||
|
||||
if (config.print_time)
|
||||
{
|
||||
settings.arguments.emplace_back(u"-time");
|
||||
}
|
||||
|
||||
if (config.reproducible)
|
||||
{
|
||||
settings.arguments.emplace_back(u"-reproducible");
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
inline windows_emulator create_sample_emulator(emulator_settings settings, const sample_configuration& config = {},
|
||||
emulator_callbacks callbacks = {})
|
||||
{
|
||||
const auto is_verbose = enable_verbose_logging();
|
||||
@@ -49,29 +72,27 @@ namespace test
|
||||
// settings.verbose_calls = true;
|
||||
}
|
||||
|
||||
if (reproducible)
|
||||
{
|
||||
settings.arguments = {u"-reproducible"};
|
||||
}
|
||||
|
||||
settings.application = "c:/test-sample.exe";
|
||||
settings.emulation_root = get_emulator_root();
|
||||
|
||||
settings.port_mappings[28970] = static_cast<uint16_t>(getpid());
|
||||
settings.path_mappings["C:\\a.txt"] =
|
||||
std::filesystem::temp_directory_path() / ("emulator-test-file-" + std::to_string(getpid()) + ".txt");
|
||||
|
||||
return windows_emulator{std::move(settings), std::move(callbacks)};
|
||||
return windows_emulator{
|
||||
get_sample_app_settings(config),
|
||||
settings,
|
||||
std::move(callbacks),
|
||||
};
|
||||
}
|
||||
|
||||
inline windows_emulator create_sample_emulator(const bool reproducible = false)
|
||||
inline windows_emulator create_sample_emulator(const sample_configuration& config = {})
|
||||
{
|
||||
emulator_settings settings{
|
||||
.disable_logging = true,
|
||||
.use_relative_time = true,
|
||||
};
|
||||
|
||||
return create_sample_emulator(std::move(settings), reproducible);
|
||||
return create_sample_emulator(std::move(settings), config);
|
||||
}
|
||||
|
||||
inline void bisect_emulation(windows_emulator& emu)
|
||||
@@ -80,7 +101,7 @@ namespace test
|
||||
emu.serialize(start_state);
|
||||
|
||||
emu.start();
|
||||
const auto limit = emu.process().executed_instructions;
|
||||
const auto limit = emu.process.executed_instructions;
|
||||
|
||||
const auto reset_emulator = [&] {
|
||||
utils::buffer_deserializer deserializer{start_state.get_buffer()};
|
||||
@@ -131,6 +152,6 @@ namespace test
|
||||
const auto rip = emu.emu().read_instruction_pointer();
|
||||
|
||||
printf("Diff detected after 0x%" PRIx64 " instructions at 0x%" PRIx64 " (%s)\n", lower_bound, rip,
|
||||
emu.process().mod_manager.find_name(rip));
|
||||
emu.mod_manager.find_name(rip));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,17 @@
|
||||
|
||||
namespace test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto create_reproducible_sample_emulator()
|
||||
{
|
||||
return create_sample_emulator({.reproducible = true});
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SerializationTest, ResettingEmulatorWorks)
|
||||
{
|
||||
auto emu = create_sample_emulator(true);
|
||||
auto emu = create_reproducible_sample_emulator();
|
||||
|
||||
utils::buffer_serializer start_state{};
|
||||
emu.serialize(start_state);
|
||||
@@ -31,7 +39,7 @@ namespace test
|
||||
|
||||
TEST(SerializationTest, SerializedDataIsReproducible)
|
||||
{
|
||||
auto emu1 = create_sample_emulator(true);
|
||||
auto emu1 = create_reproducible_sample_emulator();
|
||||
emu1.start();
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu1);
|
||||
@@ -41,7 +49,7 @@ namespace test
|
||||
|
||||
utils::buffer_deserializer deserializer{serializer1.get_buffer()};
|
||||
|
||||
windows_emulator new_emu{get_emulator_root()};
|
||||
windows_emulator new_emu{{.emulation_root = get_emulator_root()}};
|
||||
new_emu.deserialize(deserializer);
|
||||
|
||||
utils::buffer_serializer serializer2{};
|
||||
@@ -55,7 +63,7 @@ namespace test
|
||||
|
||||
TEST(SerializationTest, EmulationIsReproducible)
|
||||
{
|
||||
auto emu1 = create_sample_emulator(true);
|
||||
auto emu1 = create_reproducible_sample_emulator();
|
||||
emu1.start();
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu1);
|
||||
@@ -63,7 +71,7 @@ namespace test
|
||||
utils::buffer_serializer serializer1{};
|
||||
emu1.serialize(serializer1);
|
||||
|
||||
auto emu2 = create_sample_emulator(true);
|
||||
auto emu2 = create_reproducible_sample_emulator();
|
||||
emu2.start();
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu2);
|
||||
@@ -76,7 +84,7 @@ namespace test
|
||||
|
||||
TEST(SerializationTest, DeserializedEmulatorBehavesLikeSource)
|
||||
{
|
||||
auto emu = create_sample_emulator(true);
|
||||
auto emu = create_reproducible_sample_emulator();
|
||||
emu.start({}, 100);
|
||||
|
||||
utils::buffer_serializer serializer{};
|
||||
@@ -84,7 +92,7 @@ namespace test
|
||||
|
||||
utils::buffer_deserializer deserializer{serializer.get_buffer()};
|
||||
|
||||
windows_emulator new_emu{get_emulator_root()};
|
||||
windows_emulator new_emu{{.emulation_root = get_emulator_root()}};
|
||||
new_emu.log.disable_output(true);
|
||||
new_emu.deserialize(deserializer);
|
||||
|
||||
|
||||
@@ -7,16 +7,18 @@ namespace test
|
||||
std::string output_buffer{};
|
||||
|
||||
emulator_callbacks callbacks{
|
||||
.stdout_callback = [&output_buffer](const std::string_view data) { output_buffer.append(data); },
|
||||
.stdout_callback =
|
||||
[&output_buffer](const std::string_view data) {
|
||||
output_buffer.append(data); //
|
||||
},
|
||||
};
|
||||
|
||||
const emulator_settings settings{
|
||||
.arguments = {u"-time"},
|
||||
.disable_logging = true,
|
||||
.use_relative_time = false,
|
||||
};
|
||||
|
||||
auto emu = create_sample_emulator(settings, false, callbacks);
|
||||
auto emu = create_sample_emulator(settings, {.print_time = true}, std::move(callbacks));
|
||||
emu.start();
|
||||
|
||||
constexpr auto prefix = "Time: "sv;
|
||||
|
||||
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,7 @@
|
||||
#include "std_include.hpp"
|
||||
#include "context_frame.hpp"
|
||||
#include "cpu_context.hpp"
|
||||
|
||||
namespace context_frame
|
||||
namespace cpu_context
|
||||
{
|
||||
void restore(x64_emulator& emu, const CONTEXT64& context)
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "x64_emulator.hpp"
|
||||
|
||||
namespace context_frame
|
||||
namespace cpu_context
|
||||
{
|
||||
void save(x64_emulator& emu, CONTEXT64& context);
|
||||
void restore(x64_emulator& emu, const CONTEXT64& context);
|
||||
@@ -465,7 +465,7 @@ namespace
|
||||
write_io_status(this->delayed_ioctl_->io_status_block, STATUS_TIMEOUT);
|
||||
}
|
||||
|
||||
auto* e = win_emu.process().events.get(this->delayed_ioctl_->event);
|
||||
auto* e = win_emu.process.events.get(this->delayed_ioctl_->event);
|
||||
if (e)
|
||||
{
|
||||
e->signaled = true;
|
||||
@@ -547,7 +547,7 @@ namespace
|
||||
static std::vector<SOCKET> resolve_endpoints(windows_emulator& win_emu,
|
||||
const std::span<const AFD_POLL_HANDLE_INFO64> handles)
|
||||
{
|
||||
auto& proc = win_emu.process();
|
||||
auto& proc = win_emu.process;
|
||||
|
||||
std::vector<SOCKET> endpoints{};
|
||||
endpoints.reserve(handles.size());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "emulator_thread.hpp"
|
||||
|
||||
#include "context_frame.hpp"
|
||||
#include "cpu_context.hpp"
|
||||
#include "process_context.hpp"
|
||||
|
||||
namespace
|
||||
@@ -226,7 +226,7 @@ void emulator_thread::setup_registers(x64_emulator& emu, const process_context&
|
||||
ctx.ContextFlags = CONTEXT64_ALL;
|
||||
|
||||
unalign_stack(emu);
|
||||
context_frame::save(emu, ctx);
|
||||
cpu_context::save(emu, ctx);
|
||||
|
||||
ctx.Rip = context.rtl_user_thread_start;
|
||||
ctx.Rcx = this->start_address;
|
||||
@@ -238,6 +238,6 @@ void emulator_thread::setup_registers(x64_emulator& emu, const process_context&
|
||||
unalign_stack(emu);
|
||||
|
||||
emu.reg(x64_register::rcx, ctx_obj.value());
|
||||
emu.reg(x64_register::rdx, context.ntdll->image_base);
|
||||
emu.reg(x64_register::rdx, context.ntdll_image_base);
|
||||
emu.reg(x64_register::rip, context.ldr_initialize_thunk);
|
||||
}
|
||||
|
||||
@@ -40,10 +40,12 @@ class object_wrapper
|
||||
};
|
||||
|
||||
class windows_emulator;
|
||||
class module_manager;
|
||||
struct process_context;
|
||||
|
||||
using x64_emulator_wrapper = object_wrapper<x64_emulator>;
|
||||
using memory_manager_wrapper = object_wrapper<memory_manager>;
|
||||
using module_manager_wrapper = object_wrapper<module_manager>;
|
||||
using process_context_wrapper = object_wrapper<process_context>;
|
||||
using windows_emulator_wrapper = object_wrapper<windows_emulator>;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "exception_dispatch.hpp"
|
||||
#include "process_context.hpp"
|
||||
#include "context_frame.hpp"
|
||||
#include "cpu_context.hpp"
|
||||
|
||||
#include <platform/status.hpp>
|
||||
|
||||
@@ -146,7 +146,7 @@ void dispatch_exception(x64_emulator& emu, const process_context& proc, const DW
|
||||
{
|
||||
CONTEXT64 ctx{};
|
||||
ctx.ContextFlags = CONTEXT64_ALL;
|
||||
context_frame::save(emu, ctx);
|
||||
cpu_context::save(emu, ctx);
|
||||
|
||||
exception_record record{};
|
||||
memset(&record, 0, sizeof(record));
|
||||
|
||||
@@ -60,6 +60,14 @@ module_manager::module_manager(memory_manager& memory, file_system& file_sys)
|
||||
{
|
||||
}
|
||||
|
||||
void module_manager::map_main_modules(const windows_path& executable_path, const windows_path& ntdll_path,
|
||||
const windows_path& win32u_path, const logger& logger)
|
||||
{
|
||||
this->executable = this->map_module(executable_path, logger, true);
|
||||
this->ntdll = this->map_module(ntdll_path, logger, true);
|
||||
this->win32u = this->map_module(win32u_path, logger, true);
|
||||
}
|
||||
|
||||
mapped_module* module_manager::map_module(const windows_path& file, const logger& logger, const bool is_static)
|
||||
{
|
||||
return this->map_local_module(this->file_sys_->translate(file), logger, is_static);
|
||||
@@ -104,11 +112,23 @@ mapped_module* module_manager::map_local_module(const std::filesystem::path& fil
|
||||
void module_manager::serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write_map(this->modules_);
|
||||
|
||||
buffer.write(this->executable->image_base);
|
||||
buffer.write(this->ntdll->image_base);
|
||||
buffer.write(this->win32u->image_base);
|
||||
}
|
||||
|
||||
void module_manager::deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read_map(this->modules_);
|
||||
|
||||
const auto executable_base = buffer.read<uint64_t>();
|
||||
const auto ntdll_base = buffer.read<uint64_t>();
|
||||
const auto win32u_base = buffer.read<uint64_t>();
|
||||
|
||||
this->executable = this->find_by_address(executable_base);
|
||||
this->ntdll = this->find_by_address(ntdll_base);
|
||||
this->win32u = this->find_by_address(win32u_base);
|
||||
}
|
||||
|
||||
bool module_manager::unmap(const uint64_t address, const logger& logger)
|
||||
|
||||
@@ -12,6 +12,9 @@ class module_manager
|
||||
using module_map = std::map<uint64_t, mapped_module>;
|
||||
module_manager(memory_manager& memory, file_system& file_sys);
|
||||
|
||||
void map_main_modules(const windows_path& executable_path, const windows_path& ntdll_path,
|
||||
const windows_path& win32u_path, const logger& logger);
|
||||
|
||||
mapped_module* map_module(const windows_path& file, const logger& logger, bool is_static = false);
|
||||
mapped_module* map_local_module(const std::filesystem::path& file, const logger& logger, bool is_static = false);
|
||||
|
||||
@@ -46,6 +49,10 @@ class module_manager
|
||||
return modules_;
|
||||
}
|
||||
|
||||
mapped_module* executable{};
|
||||
mapped_module* ntdll{};
|
||||
mapped_module* win32u{};
|
||||
|
||||
private:
|
||||
memory_manager* memory_{};
|
||||
file_system* file_sys_{};
|
||||
|
||||
205
src/windows-emulator/process_context.cpp
Normal file
205
src/windows-emulator/process_context.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#include "process_context.hpp"
|
||||
|
||||
#include "emulator_utils.hpp"
|
||||
#include "windows_emulator.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
emulator_allocator create_allocator(memory_manager& memory, const size_t size)
|
||||
{
|
||||
const auto base = memory.find_free_allocation_base(size);
|
||||
memory.allocate_memory(base, size, memory_permission::read_write);
|
||||
|
||||
return emulator_allocator{memory, base, size};
|
||||
}
|
||||
|
||||
void setup_gdt(x64_emulator& emu, memory_manager& memory)
|
||||
{
|
||||
constexpr uint64_t gdtr[4] = {0, GDT_ADDR, GDT_LIMIT, 0};
|
||||
emu.write_register(x64_register::gdtr, &gdtr, sizeof(gdtr));
|
||||
memory.allocate_memory(GDT_ADDR, GDT_LIMIT, memory_permission::read);
|
||||
|
||||
emu.write_memory<uint64_t>(GDT_ADDR + 6 * (sizeof(uint64_t)), 0xEFFE000000FFFF);
|
||||
emu.reg<uint16_t>(x64_register::cs, 0x33);
|
||||
|
||||
emu.write_memory<uint64_t>(GDT_ADDR + 5 * (sizeof(uint64_t)), 0xEFF6000000FFFF);
|
||||
emu.reg<uint16_t>(x64_register::ss, 0x2B);
|
||||
}
|
||||
}
|
||||
|
||||
void process_context::setup(x64_emulator& emu, memory_manager& memory, const application_settings& app_settings,
|
||||
const emulator_settings& emu_settings, const mapped_module& executable,
|
||||
const mapped_module& ntdll, const apiset::container& apiset_container)
|
||||
{
|
||||
setup_gdt(emu, memory);
|
||||
|
||||
this->kusd.setup(emu_settings.use_relative_time);
|
||||
|
||||
this->base_allocator = create_allocator(memory, PEB_SEGMENT_SIZE);
|
||||
auto& allocator = this->base_allocator;
|
||||
|
||||
this->peb = allocator.reserve<PEB64>();
|
||||
|
||||
/* Values of the following fields must be
|
||||
* allocated relative to the process_params themselves
|
||||
* and included in the length:
|
||||
*
|
||||
* CurrentDirectory
|
||||
* DllPath
|
||||
* ImagePathName
|
||||
* CommandLine
|
||||
* WindowTitle
|
||||
* DesktopInfo
|
||||
* ShellInfo
|
||||
* RuntimeData
|
||||
* RedirectionDllName
|
||||
*/
|
||||
|
||||
this->process_params = allocator.reserve<RTL_USER_PROCESS_PARAMETERS64>();
|
||||
|
||||
this->process_params.access([&](RTL_USER_PROCESS_PARAMETERS64& proc_params) {
|
||||
proc_params.Flags = 0x6001; //| 0x80000000; // Prevent CsrClientConnectToServer
|
||||
|
||||
proc_params.ConsoleHandle = CONSOLE_HANDLE.h;
|
||||
proc_params.StandardOutput = STDOUT_HANDLE.h;
|
||||
proc_params.StandardInput = STDIN_HANDLE.h;
|
||||
proc_params.StandardError = proc_params.StandardOutput;
|
||||
|
||||
proc_params.Environment = reinterpret_cast<std::uint64_t*>(allocator.copy_string(u"=::=::\\"));
|
||||
allocator.copy_string(u"EMULATOR=1");
|
||||
allocator.copy_string(u"COMPUTERNAME=momo");
|
||||
allocator.copy_string(u"SystemRoot=C:\\WINDOWS");
|
||||
allocator.copy_string(u"");
|
||||
|
||||
const auto application_str = app_settings.application.u16string();
|
||||
|
||||
std::u16string command_line = u"\"" + application_str + u"\"";
|
||||
|
||||
for (const auto& arg : app_settings.arguments)
|
||||
{
|
||||
command_line.push_back(u' ');
|
||||
command_line.append(arg);
|
||||
}
|
||||
|
||||
allocator.make_unicode_string(proc_params.CommandLine, command_line);
|
||||
allocator.make_unicode_string(proc_params.CurrentDirectory.DosPath,
|
||||
app_settings.working_directory.u16string() + u"\\", 1024);
|
||||
allocator.make_unicode_string(proc_params.ImagePathName, application_str);
|
||||
|
||||
const auto total_length = allocator.get_next_address() - this->process_params.value();
|
||||
|
||||
proc_params.Length = static_cast<uint32_t>(std::max(static_cast<uint64_t>(sizeof(proc_params)), total_length));
|
||||
proc_params.MaximumLength = proc_params.Length;
|
||||
});
|
||||
|
||||
this->peb.access([&](PEB64& p) {
|
||||
p.ImageBaseAddress = executable.image_base;
|
||||
p.ProcessParameters = this->process_params.ptr();
|
||||
p.ApiSetMap = apiset::clone(emu, allocator, apiset_container).ptr();
|
||||
|
||||
p.ProcessHeap = nullptr;
|
||||
p.ProcessHeaps = nullptr;
|
||||
p.HeapSegmentReserve = 0x0000000000100000; // TODO: Read from executable
|
||||
p.HeapSegmentCommit = 0x0000000000002000;
|
||||
p.HeapDeCommitTotalFreeThreshold = 0x0000000000010000;
|
||||
p.HeapDeCommitFreeBlockThreshold = 0x0000000000001000;
|
||||
p.NumberOfHeaps = 0x00000000;
|
||||
p.MaximumNumberOfHeaps = 0x00000010;
|
||||
|
||||
p.OSPlatformId = 2;
|
||||
p.OSMajorVersion = 0x0000000a;
|
||||
p.OSBuildNumber = 0x00006c51;
|
||||
|
||||
// p.AnsiCodePageData = allocator.reserve<CPTABLEINFO>().value();
|
||||
// p.OemCodePageData = allocator.reserve<CPTABLEINFO>().value();
|
||||
p.UnicodeCaseTableData = allocator.reserve<NLSTABLEINFO>().value();
|
||||
});
|
||||
|
||||
this->ntdll_image_base = ntdll.image_base;
|
||||
this->ldr_initialize_thunk = ntdll.find_export("LdrInitializeThunk");
|
||||
this->rtl_user_thread_start = ntdll.find_export("RtlUserThreadStart");
|
||||
this->ki_user_exception_dispatcher = ntdll.find_export("KiUserExceptionDispatcher");
|
||||
|
||||
this->default_register_set = emu.save_registers();
|
||||
}
|
||||
|
||||
void process_context::serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->executed_instructions);
|
||||
buffer.write(this->current_ip);
|
||||
buffer.write(this->previous_ip);
|
||||
buffer.write_optional(this->exception_rip);
|
||||
buffer.write_optional(this->exit_status);
|
||||
buffer.write(this->base_allocator);
|
||||
buffer.write(this->peb);
|
||||
buffer.write(this->process_params);
|
||||
buffer.write(this->kusd);
|
||||
|
||||
buffer.write(this->ntdll_image_base);
|
||||
buffer.write(this->ldr_initialize_thunk);
|
||||
buffer.write(this->rtl_user_thread_start);
|
||||
buffer.write(this->ki_user_exception_dispatcher);
|
||||
|
||||
buffer.write(this->events);
|
||||
buffer.write(this->files);
|
||||
buffer.write(this->sections);
|
||||
buffer.write(this->devices);
|
||||
buffer.write(this->semaphores);
|
||||
buffer.write(this->ports);
|
||||
buffer.write(this->mutants);
|
||||
buffer.write(this->registry_keys);
|
||||
buffer.write_map(this->atoms);
|
||||
|
||||
buffer.write_vector(this->default_register_set);
|
||||
buffer.write(this->spawned_thread_count);
|
||||
buffer.write(this->threads);
|
||||
|
||||
buffer.write(this->threads.find_handle(this->active_thread).bits);
|
||||
}
|
||||
|
||||
void process_context::deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->executed_instructions);
|
||||
buffer.read(this->current_ip);
|
||||
buffer.read(this->previous_ip);
|
||||
buffer.read_optional(this->exception_rip);
|
||||
buffer.read_optional(this->exit_status);
|
||||
buffer.read(this->base_allocator);
|
||||
buffer.read(this->peb);
|
||||
buffer.read(this->process_params);
|
||||
buffer.read(this->kusd);
|
||||
|
||||
buffer.read(this->ntdll_image_base);
|
||||
buffer.read(this->ldr_initialize_thunk);
|
||||
buffer.read(this->rtl_user_thread_start);
|
||||
buffer.read(this->ki_user_exception_dispatcher);
|
||||
|
||||
buffer.read(this->events);
|
||||
buffer.read(this->files);
|
||||
buffer.read(this->sections);
|
||||
buffer.read(this->devices);
|
||||
buffer.read(this->semaphores);
|
||||
buffer.read(this->ports);
|
||||
buffer.read(this->mutants);
|
||||
buffer.read(this->registry_keys);
|
||||
buffer.read_map(this->atoms);
|
||||
|
||||
buffer.read_vector(this->default_register_set);
|
||||
buffer.read(this->spawned_thread_count);
|
||||
|
||||
for (auto& thread : this->threads | std::views::values)
|
||||
{
|
||||
thread.leak_memory();
|
||||
}
|
||||
|
||||
buffer.read(this->threads);
|
||||
|
||||
this->active_thread = this->threads.get(buffer.read<uint64_t>());
|
||||
}
|
||||
|
||||
handle process_context::create_thread(memory_manager& memory, const uint64_t start_address, const uint64_t argument,
|
||||
const uint64_t stack_size)
|
||||
{
|
||||
emulator_thread t{memory, *this, start_address, argument, stack_size, ++this->spawned_thread_count};
|
||||
return this->threads.store(std::move(t));
|
||||
}
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "windows_objects.hpp"
|
||||
#include "emulator_thread.hpp"
|
||||
|
||||
#include "apiset/apiset.hpp"
|
||||
|
||||
#define PEB_SEGMENT_SIZE (20 << 20) // 20 MB
|
||||
#define GS_SEGMENT_SIZE (1 << 20) // 1 MB
|
||||
|
||||
@@ -25,18 +27,28 @@
|
||||
#define GDT_LIMIT 0x1000
|
||||
#define GDT_ENTRY_SIZE 0x8
|
||||
|
||||
struct emulator_settings;
|
||||
struct application_settings;
|
||||
|
||||
struct process_context
|
||||
{
|
||||
process_context(x64_emulator& emu, memory_manager& memory, file_system& file_sys)
|
||||
process_context(x64_emulator& emu, memory_manager& memory)
|
||||
: base_allocator(emu),
|
||||
peb(emu),
|
||||
process_params(emu),
|
||||
kusd(memory, *this),
|
||||
mod_manager(memory, file_sys)
|
||||
kusd(memory, *this)
|
||||
{
|
||||
}
|
||||
|
||||
registry_manager registry{};
|
||||
void setup(x64_emulator& emu, memory_manager& memory, const application_settings& app_settings,
|
||||
const emulator_settings& emu_settings, const mapped_module& executable, const mapped_module& ntdll,
|
||||
const apiset::container& apiset_container);
|
||||
|
||||
handle create_thread(memory_manager& memory, const uint64_t start_address, const uint64_t argument,
|
||||
const uint64_t stack_size);
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
|
||||
uint64_t executed_instructions{0};
|
||||
uint64_t current_ip{0};
|
||||
@@ -51,12 +63,7 @@ struct process_context
|
||||
emulator_object<RTL_USER_PROCESS_PARAMETERS64> process_params;
|
||||
kusd_mmio kusd;
|
||||
|
||||
module_manager mod_manager;
|
||||
|
||||
mapped_module* executable{};
|
||||
mapped_module* ntdll{};
|
||||
mapped_module* win32u{};
|
||||
|
||||
uint64_t ntdll_image_base{};
|
||||
uint64_t ldr_initialize_thunk{};
|
||||
uint64_t rtl_user_thread_start{};
|
||||
uint64_t ki_user_exception_dispatcher{};
|
||||
@@ -76,99 +83,4 @@ struct process_context
|
||||
uint32_t spawned_thread_count{0};
|
||||
handle_store<handle_types::thread, emulator_thread> threads{};
|
||||
emulator_thread* active_thread{nullptr};
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->registry);
|
||||
buffer.write(this->executed_instructions);
|
||||
buffer.write(this->current_ip);
|
||||
buffer.write(this->previous_ip);
|
||||
buffer.write_optional(this->exception_rip);
|
||||
buffer.write_optional(this->exit_status);
|
||||
buffer.write(this->base_allocator);
|
||||
buffer.write(this->peb);
|
||||
buffer.write(this->process_params);
|
||||
buffer.write(this->kusd);
|
||||
buffer.write(this->mod_manager);
|
||||
|
||||
buffer.write(this->executable->image_base);
|
||||
buffer.write(this->ntdll->image_base);
|
||||
buffer.write(this->win32u->image_base);
|
||||
|
||||
buffer.write(this->ldr_initialize_thunk);
|
||||
buffer.write(this->rtl_user_thread_start);
|
||||
buffer.write(this->ki_user_exception_dispatcher);
|
||||
|
||||
buffer.write(this->events);
|
||||
buffer.write(this->files);
|
||||
buffer.write(this->sections);
|
||||
buffer.write(this->devices);
|
||||
buffer.write(this->semaphores);
|
||||
buffer.write(this->ports);
|
||||
buffer.write(this->mutants);
|
||||
buffer.write(this->registry_keys);
|
||||
buffer.write_map(this->atoms);
|
||||
|
||||
buffer.write_vector(this->default_register_set);
|
||||
buffer.write(this->spawned_thread_count);
|
||||
buffer.write(this->threads);
|
||||
|
||||
buffer.write(this->threads.find_handle(this->active_thread).bits);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->registry);
|
||||
buffer.read(this->executed_instructions);
|
||||
buffer.read(this->current_ip);
|
||||
buffer.read(this->previous_ip);
|
||||
buffer.read_optional(this->exception_rip);
|
||||
buffer.read_optional(this->exit_status);
|
||||
buffer.read(this->base_allocator);
|
||||
buffer.read(this->peb);
|
||||
buffer.read(this->process_params);
|
||||
buffer.read(this->kusd);
|
||||
buffer.read(this->mod_manager);
|
||||
|
||||
const auto executable_base = buffer.read<uint64_t>();
|
||||
const auto ntdll_base = buffer.read<uint64_t>();
|
||||
const auto win32u_base = buffer.read<uint64_t>();
|
||||
|
||||
this->executable = this->mod_manager.find_by_address(executable_base);
|
||||
this->ntdll = this->mod_manager.find_by_address(ntdll_base);
|
||||
this->win32u = this->mod_manager.find_by_address(win32u_base);
|
||||
|
||||
buffer.read(this->ldr_initialize_thunk);
|
||||
buffer.read(this->rtl_user_thread_start);
|
||||
buffer.read(this->ki_user_exception_dispatcher);
|
||||
|
||||
buffer.read(this->events);
|
||||
buffer.read(this->files);
|
||||
buffer.read(this->sections);
|
||||
buffer.read(this->devices);
|
||||
buffer.read(this->semaphores);
|
||||
buffer.read(this->ports);
|
||||
buffer.read(this->mutants);
|
||||
buffer.read(this->registry_keys);
|
||||
buffer.read_map(this->atoms);
|
||||
|
||||
buffer.read_vector(this->default_register_set);
|
||||
buffer.read(this->spawned_thread_count);
|
||||
|
||||
for (auto& thread : this->threads | std::views::values)
|
||||
{
|
||||
thread.leak_memory();
|
||||
}
|
||||
|
||||
buffer.read(this->threads);
|
||||
|
||||
this->active_thread = this->threads.get(buffer.read<uint64_t>());
|
||||
}
|
||||
|
||||
handle create_thread(memory_manager& memory, const uint64_t start_address, const uint64_t argument,
|
||||
const uint64_t stack_size)
|
||||
{
|
||||
emulator_thread t{memory, *this, start_address, argument, stack_size, ++this->spawned_thread_count};
|
||||
return this->threads.store(std::move(t));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -60,30 +60,17 @@ void registry_manager::setup()
|
||||
this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001");
|
||||
}
|
||||
|
||||
void registry_manager::serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->hive_path_);
|
||||
}
|
||||
|
||||
void registry_manager::deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->hive_path_);
|
||||
this->setup();
|
||||
}
|
||||
|
||||
utils::path_key registry_manager::normalize_path(const utils::path_key& path) const
|
||||
{
|
||||
const utils::path_key canonical_path = path;
|
||||
|
||||
for (const auto& mapping : this->path_mapping_)
|
||||
{
|
||||
if (is_subpath(mapping.first.get(), canonical_path.get()))
|
||||
if (is_subpath(mapping.first.get(), path.get()))
|
||||
{
|
||||
return mapping.second.get() / canonical_path.get().lexically_relative(mapping.first.get());
|
||||
return mapping.second.get() / path.get().lexically_relative(mapping.first.get());
|
||||
}
|
||||
}
|
||||
|
||||
return canonical_path.get();
|
||||
return path;
|
||||
}
|
||||
|
||||
void registry_manager::add_path_mapping(const utils::path_key& key, const utils::path_key& value)
|
||||
|
||||
@@ -46,9 +46,6 @@ class registry_manager
|
||||
registry_manager(const registry_manager&) = delete;
|
||||
registry_manager& operator=(const registry_manager&) = delete;
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
|
||||
std::optional<registry_key> get_key(const utils::path_key& key);
|
||||
std::optional<registry_value> get_value(const registry_key& key, std::string name);
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ void syscall_dispatcher::add_handlers()
|
||||
void syscall_dispatcher::dispatch(windows_emulator& win_emu)
|
||||
{
|
||||
auto& emu = win_emu.emu();
|
||||
auto& context = win_emu.process();
|
||||
auto& context = win_emu.process;
|
||||
|
||||
const auto address = emu.read_instruction_pointer();
|
||||
const auto syscall_id = emu.reg<uint32_t>(x64_register::eax);
|
||||
@@ -88,11 +88,11 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* mod = context.mod_manager.find_by_address(address);
|
||||
if (mod != context.ntdll && mod != context.win32u)
|
||||
const auto* mod = win_emu.mod_manager.find_by_address(address);
|
||||
if (mod != win_emu.mod_manager.ntdll && mod != win_emu.mod_manager.win32u)
|
||||
{
|
||||
win_emu.callbacks().inline_syscall(syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
|
||||
entry->second.name);
|
||||
win_emu.callbacks.inline_syscall(syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
|
||||
entry->second.name);
|
||||
|
||||
win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n",
|
||||
entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : "<N/A>");
|
||||
@@ -106,7 +106,7 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
|
||||
uint64_t return_address{};
|
||||
c.emu.try_read_memory(rsp, &return_address, sizeof(return_address));
|
||||
|
||||
const auto* mod_name = context.mod_manager.find_name(return_address);
|
||||
const auto* mod_name = win_emu.mod_manager.find_name(return_address);
|
||||
|
||||
win_emu.log.print(color::dark_gray,
|
||||
"Executing syscall: %s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n",
|
||||
@@ -114,11 +114,11 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto* previous_mod = context.mod_manager.find_by_address(context.previous_ip);
|
||||
const auto* previous_mod = win_emu.mod_manager.find_by_address(context.previous_ip);
|
||||
|
||||
win_emu.callbacks().outofline_syscall(syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
|
||||
entry->second.name, context.previous_ip,
|
||||
previous_mod ? previous_mod->name.c_str() : "<N/A>");
|
||||
win_emu.callbacks.outofline_syscall(syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
|
||||
entry->second.name, context.previous_ip,
|
||||
previous_mod ? previous_mod->name.c_str() : "<N/A>");
|
||||
|
||||
win_emu.log.print(color::blue,
|
||||
"Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "std_include.hpp"
|
||||
#include "syscall_dispatcher.hpp"
|
||||
#include "context_frame.hpp"
|
||||
#include "cpu_context.hpp"
|
||||
#include "emulator_utils.hpp"
|
||||
#include "syscall_utils.hpp"
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace
|
||||
|
||||
c.win_emu.log.print(color::dark_gray, "--> Registry key: %s\n", u16_to_u8(key).c_str());
|
||||
|
||||
auto entry = c.proc.registry.get_key({key});
|
||||
auto entry = c.win_emu.registry.get_key({key});
|
||||
if (!entry.has_value())
|
||||
{
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
@@ -187,7 +187,7 @@ namespace
|
||||
|
||||
const auto query_name = read_unicode_string(c.emu, value_name);
|
||||
|
||||
const auto value = c.proc.registry.get_value(*key, u16_to_u8(query_name));
|
||||
const auto value = c.win_emu.registry.get_value(*key, u16_to_u8(query_name));
|
||||
if (!value)
|
||||
{
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
@@ -704,8 +704,8 @@ namespace
|
||||
{
|
||||
constexpr auto shared_section_size = 0x10000;
|
||||
|
||||
const auto address = c.win_emu.memory().find_free_allocation_base(shared_section_size);
|
||||
c.win_emu.memory().allocate_memory(address, shared_section_size, memory_permission::read_write);
|
||||
const auto address = c.win_emu.memory.find_free_allocation_base(shared_section_size);
|
||||
c.win_emu.memory.allocate_memory(address, shared_section_size, memory_permission::read_write);
|
||||
|
||||
const std::u16string_view windows_dir = c.proc.kusd.get().NtSystemRoot.arr;
|
||||
const auto windows_dir_size = windows_dir.size() * 2;
|
||||
@@ -756,7 +756,7 @@ namespace
|
||||
|
||||
if (section_entry->is_image())
|
||||
{
|
||||
const auto binary = c.proc.mod_manager.map_module(section_entry->file_name, c.win_emu.log);
|
||||
const auto binary = c.win_emu.mod_manager.map_module(section_entry->file_name, c.win_emu.log);
|
||||
if (!binary)
|
||||
{
|
||||
return STATUS_FILE_INVALID;
|
||||
@@ -789,7 +789,7 @@ namespace
|
||||
}
|
||||
|
||||
const auto protection = map_nt_to_emulator_protection(section_entry->section_page_protection);
|
||||
const auto address = c.win_emu.memory().allocate_memory(size, protection);
|
||||
const auto address = c.win_emu.memory.allocate_memory(size, protection);
|
||||
|
||||
if (!file_data.empty())
|
||||
{
|
||||
@@ -850,7 +850,7 @@ namespace
|
||||
const emulator_object<EMU_MEMORY_BASIC_INFORMATION64> info{c.emu, memory_information};
|
||||
|
||||
info.access([&](EMU_MEMORY_BASIC_INFORMATION64& image_info) {
|
||||
const auto region_info = c.win_emu.memory().get_region_info(base_address);
|
||||
const auto region_info = c.win_emu.memory.get_region_info(base_address);
|
||||
|
||||
assert(!region_info.is_committed || region_info.is_reserved);
|
||||
|
||||
@@ -880,7 +880,7 @@ namespace
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
const auto mod = c.proc.mod_manager.find_by_address(base_address);
|
||||
const auto mod = c.win_emu.mod_manager.find_by_address(base_address);
|
||||
if (!mod)
|
||||
{
|
||||
c.win_emu.log.error("Bad address for memory image request: 0x%" PRIx64 "\n", base_address);
|
||||
@@ -910,7 +910,7 @@ namespace
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
const auto region_info = c.win_emu.memory().get_region_info(base_address);
|
||||
const auto region_info = c.win_emu.memory.get_region_info(base_address);
|
||||
if (!region_info.is_reserved)
|
||||
{
|
||||
return STATUS_INVALID_ADDRESS;
|
||||
@@ -1301,7 +1301,7 @@ namespace
|
||||
|
||||
const emulator_object<SECTION_IMAGE_INFORMATION<EmulatorTraits<Emu64>>> info{c.emu, process_information};
|
||||
info.access([&](SECTION_IMAGE_INFORMATION<EmulatorTraits<Emu64>>& i) {
|
||||
const auto& mod = *c.proc.executable;
|
||||
const auto& mod = *c.win_emu.mod_manager.executable;
|
||||
|
||||
const emulator_object<PEDosHeader_t> dos_header_obj{c.emu, mod.image_base};
|
||||
const auto dos_header = dos_header_obj.read();
|
||||
@@ -1655,7 +1655,7 @@ namespace
|
||||
if (!f->enumeration_state || query_flags & SL_RESTART_SCAN)
|
||||
{
|
||||
f->enumeration_state.emplace(file_enumeration_state{});
|
||||
f->enumeration_state->files = scan_directory(c.win_emu.file_sys().translate(f->name));
|
||||
f->enumeration_state->files = scan_directory(c.win_emu.file_sys.translate(f->name));
|
||||
}
|
||||
|
||||
auto& enum_state = *f->enumeration_state;
|
||||
@@ -2015,8 +2015,7 @@ namespace
|
||||
|
||||
try
|
||||
{
|
||||
c.win_emu.memory().protect_memory(aligned_start, aligned_length, requested_protection,
|
||||
&old_protection_value);
|
||||
c.win_emu.memory.protect_memory(aligned_start, aligned_length, requested_protection, &old_protection_value);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -2118,7 +2117,7 @@ namespace
|
||||
auto potential_base = base_address.read();
|
||||
if (!potential_base)
|
||||
{
|
||||
potential_base = c.win_emu.memory().find_free_allocation_base(allocation_bytes);
|
||||
potential_base = c.win_emu.memory.find_free_allocation_base(allocation_bytes);
|
||||
}
|
||||
|
||||
if (!potential_base)
|
||||
@@ -2138,7 +2137,7 @@ namespace
|
||||
throw std::runtime_error("Unsupported allocation type!");
|
||||
}
|
||||
|
||||
if (commit && !reserve && c.win_emu.memory().commit_memory(potential_base, allocation_bytes, protection))
|
||||
if (commit && !reserve && c.win_emu.memory.commit_memory(potential_base, allocation_bytes, protection))
|
||||
{
|
||||
c.win_emu.log.print(color::dark_gray, "--> Committed 0x%" PRIx64 " - 0x%" PRIx64 "\n", potential_base,
|
||||
potential_base + allocation_bytes);
|
||||
@@ -2149,7 +2148,7 @@ namespace
|
||||
c.win_emu.log.print(color::dark_gray, "--> Allocated 0x%" PRIx64 " - 0x%" PRIx64 "\n", potential_base,
|
||||
potential_base + allocation_bytes);
|
||||
|
||||
return c.win_emu.memory().allocate_memory(potential_base, allocation_bytes, protection, !commit)
|
||||
return c.win_emu.memory.allocate_memory(potential_base, allocation_bytes, protection, !commit)
|
||||
? STATUS_SUCCESS
|
||||
: STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
@@ -2177,14 +2176,14 @@ namespace
|
||||
|
||||
if (free_type & MEM_RELEASE)
|
||||
{
|
||||
return c.win_emu.memory().release_memory(allocation_base, allocation_size) ? STATUS_SUCCESS
|
||||
: STATUS_MEMORY_NOT_ALLOCATED;
|
||||
return c.win_emu.memory.release_memory(allocation_base, allocation_size) ? STATUS_SUCCESS
|
||||
: STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
if (free_type & MEM_DECOMMIT)
|
||||
{
|
||||
return c.win_emu.memory().decommit_memory(allocation_base, allocation_size) ? STATUS_SUCCESS
|
||||
: STATUS_MEMORY_NOT_ALLOCATED;
|
||||
return c.win_emu.memory.decommit_memory(allocation_base, allocation_size) ? STATUS_SUCCESS
|
||||
: STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Bad free type");
|
||||
@@ -2261,7 +2260,7 @@ namespace
|
||||
}
|
||||
|
||||
client_shared_memory.access([&](PORT_VIEW64& view) {
|
||||
p.view_base = c.win_emu.memory().allocate_memory(view.ViewSize, memory_permission::read_write);
|
||||
p.view_base = c.win_emu.memory.allocate_memory(view.ViewSize, memory_permission::read_write);
|
||||
view.ViewBase = p.view_base;
|
||||
view.ViewRemoteBase = view.ViewBase;
|
||||
});
|
||||
@@ -2746,14 +2745,14 @@ namespace
|
||||
const emulator_object<LARGE_INTEGER> /*default_casing_table_size*/)
|
||||
{
|
||||
const auto locale_file =
|
||||
utils::io::read_file(c.win_emu.file_sys().translate(R"(C:\Windows\System32\locale.nls)"));
|
||||
utils::io::read_file(c.win_emu.file_sys.translate(R"(C:\Windows\System32\locale.nls)"));
|
||||
if (locale_file.empty())
|
||||
{
|
||||
return STATUS_FILE_INVALID;
|
||||
}
|
||||
|
||||
const auto size = page_align_up(locale_file.size());
|
||||
const auto base = c.win_emu.memory().allocate_memory(size, memory_permission::read);
|
||||
const auto base = c.win_emu.memory.allocate_memory(size, memory_permission::read);
|
||||
c.emu.write_memory(base, locale_file.data(), locale_file.size());
|
||||
|
||||
base_address.write(base);
|
||||
@@ -2775,7 +2774,7 @@ namespace
|
||||
c.write_status = false;
|
||||
|
||||
const auto context = thread_context.read();
|
||||
context_frame::restore(c.emu, context);
|
||||
cpu_context::restore(c.emu, context);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -2879,7 +2878,7 @@ namespace
|
||||
io_status_block.write(block);
|
||||
}
|
||||
|
||||
c.win_emu.callbacks().stdout_callback(temp_buffer);
|
||||
c.win_emu.callbacks.stdout_callback(temp_buffer);
|
||||
|
||||
if (!temp_buffer.ends_with("\n"))
|
||||
{
|
||||
@@ -3048,14 +3047,14 @@ namespace
|
||||
if (create_disposition & FILE_CREATE)
|
||||
{
|
||||
std::error_code ec{};
|
||||
create_directory(c.win_emu.file_sys().translate(f.name), ec);
|
||||
create_directory(c.win_emu.file_sys.translate(f.name), ec);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
else if (!std::filesystem::is_directory(c.win_emu.file_sys().translate(f.name)))
|
||||
else if (!std::filesystem::is_directory(c.win_emu.file_sys.translate(f.name)))
|
||||
{
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
@@ -3078,7 +3077,7 @@ namespace
|
||||
|
||||
FILE* file{};
|
||||
|
||||
const auto error = open_unicode(&file, c.win_emu.file_sys().translate(path), mode);
|
||||
const auto error = open_unicode(&file, c.win_emu.file_sys.translate(path), mode);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -3123,7 +3122,7 @@ namespace
|
||||
|
||||
c.win_emu.log.print(color::dark_gray, "--> Querying file attributes: %s\n", u16_to_u8(filename).c_str());
|
||||
|
||||
const auto local_filename = c.win_emu.file_sys().translate(filename).string();
|
||||
const auto local_filename = c.win_emu.file_sys.translate(filename).string();
|
||||
|
||||
struct _stat64 file_stat{};
|
||||
if (_stat64(local_filename.c_str(), &file_stat) != 0)
|
||||
@@ -3409,7 +3408,7 @@ namespace
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
const auto* mod = c.proc.mod_manager.find_by_address(base_address);
|
||||
const auto* mod = c.win_emu.mod_manager.find_by_address(base_address);
|
||||
if (!mod)
|
||||
{
|
||||
c.win_emu.log.error("Unmapping non-module section not supported!\n");
|
||||
@@ -3417,7 +3416,7 @@ namespace
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (c.proc.mod_manager.unmap(base_address, c.win_emu.log))
|
||||
if (c.win_emu.mod_manager.unmap(base_address, c.win_emu.log))
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -3446,7 +3445,7 @@ namespace
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
const auto h = c.proc.create_thread(c.win_emu.memory(), start_routine, argument, stack_size);
|
||||
const auto h = c.proc.create_thread(c.win_emu.memory, start_routine, argument, stack_size);
|
||||
thread_handle.write(h);
|
||||
|
||||
if (!attribute_list)
|
||||
@@ -3800,7 +3799,7 @@ namespace
|
||||
c.win_emu.log.print(color::pink, "--> Reading debug registers!\n");
|
||||
}
|
||||
|
||||
context_frame::save(c.emu, context);
|
||||
cpu_context::save(c.emu, context);
|
||||
});
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
@@ -3832,7 +3831,7 @@ namespace
|
||||
});
|
||||
|
||||
const auto context = thread_context.read();
|
||||
context_frame::restore(c.emu, context);
|
||||
cpu_context::restore(c.emu, context);
|
||||
|
||||
if ((context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64) == CONTEXT_DEBUG_REGISTERS_64)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "std_include.hpp"
|
||||
#include "windows_emulator.hpp"
|
||||
|
||||
#include "address_utils.hpp"
|
||||
#include "context_frame.hpp"
|
||||
#include "cpu_context.hpp"
|
||||
|
||||
#include <unicorn_x64_emulator.hpp>
|
||||
|
||||
@@ -11,282 +10,48 @@
|
||||
#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;
|
||||
|
||||
namespace
|
||||
{
|
||||
uint64_t copy_string(x64_emulator& emu, emulator_allocator& allocator, const void* base_ptr, const uint64_t offset,
|
||||
const size_t length)
|
||||
void adjust_working_directory(application_settings& app_settings)
|
||||
{
|
||||
if (!length)
|
||||
if (!app_settings.working_directory.empty())
|
||||
{
|
||||
return 0;
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
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);
|
||||
memory.allocate_memory(base, size, memory_permission::read_write);
|
||||
|
||||
return emulator_allocator{memory, base, size};
|
||||
}
|
||||
|
||||
void setup_gdt(x64_emulator& emu, memory_manager& memory)
|
||||
{
|
||||
constexpr uint64_t gdtr[4] = {0, GDT_ADDR, GDT_LIMIT, 0};
|
||||
emu.write_register(x64_register::gdtr, &gdtr, sizeof(gdtr));
|
||||
memory.allocate_memory(GDT_ADDR, GDT_LIMIT, memory_permission::read);
|
||||
|
||||
emu.write_memory<uint64_t>(GDT_ADDR + 6 * (sizeof(uint64_t)), 0xEFFE000000FFFF);
|
||||
emu.reg<uint16_t>(x64_register::cs, 0x33);
|
||||
|
||||
emu.write_memory<uint64_t>(GDT_ADDR + 5 * (sizeof(uint64_t)), 0xEFF6000000FFFF);
|
||||
emu.reg<uint16_t>(x64_register::ss, 0x2B);
|
||||
}
|
||||
|
||||
void setup_context(windows_emulator& win_emu, const emulator_settings& settings, const windows_path& application,
|
||||
const windows_path& working_dir)
|
||||
{
|
||||
auto& emu = win_emu.emu();
|
||||
auto& context = win_emu.process();
|
||||
auto& memory = win_emu.memory();
|
||||
|
||||
setup_gdt(emu, memory);
|
||||
|
||||
context.registry =
|
||||
registry_manager(win_emu.get_emulation_root().empty() ? settings.registry_directory
|
||||
: win_emu.get_emulation_root() / "registry");
|
||||
|
||||
context.kusd.setup(settings.use_relative_time);
|
||||
|
||||
context.base_allocator = create_allocator(memory, PEB_SEGMENT_SIZE);
|
||||
auto& allocator = context.base_allocator;
|
||||
|
||||
context.peb = allocator.reserve<PEB64>();
|
||||
|
||||
/* Values of the following fields must be
|
||||
* allocated relative to the process_params themselves
|
||||
* and included in the length:
|
||||
*
|
||||
* CurrentDirectory
|
||||
* DllPath
|
||||
* ImagePathName
|
||||
* CommandLine
|
||||
* WindowTitle
|
||||
* DesktopInfo
|
||||
* ShellInfo
|
||||
* RuntimeData
|
||||
* RedirectionDllName
|
||||
*/
|
||||
|
||||
context.process_params = allocator.reserve<RTL_USER_PROCESS_PARAMETERS64>();
|
||||
|
||||
context.process_params.access([&](RTL_USER_PROCESS_PARAMETERS64& proc_params) {
|
||||
proc_params.Flags = 0x6001; //| 0x80000000; // Prevent CsrClientConnectToServer
|
||||
|
||||
proc_params.ConsoleHandle = CONSOLE_HANDLE.h;
|
||||
proc_params.StandardOutput = STDOUT_HANDLE.h;
|
||||
proc_params.StandardInput = STDIN_HANDLE.h;
|
||||
proc_params.StandardError = proc_params.StandardOutput;
|
||||
|
||||
proc_params.Environment = reinterpret_cast<std::uint64_t*>(allocator.copy_string(u"=::=::\\"));
|
||||
allocator.copy_string(u"EMULATOR=1");
|
||||
allocator.copy_string(u"COMPUTERNAME=momo");
|
||||
allocator.copy_string(u"SystemRoot=C:\\WINDOWS");
|
||||
allocator.copy_string(u"");
|
||||
|
||||
const auto application_str = application.u16string();
|
||||
|
||||
std::u16string command_line = u"\"" + application_str + u"\"";
|
||||
|
||||
for (const auto& arg : settings.arguments)
|
||||
{
|
||||
command_line.push_back(u' ');
|
||||
command_line.append(arg);
|
||||
}
|
||||
|
||||
allocator.make_unicode_string(proc_params.CommandLine, command_line);
|
||||
allocator.make_unicode_string(proc_params.CurrentDirectory.DosPath, working_dir.u16string() + u"\\", 1024);
|
||||
allocator.make_unicode_string(proc_params.ImagePathName, application_str);
|
||||
|
||||
const auto total_length = allocator.get_next_address() - context.process_params.value();
|
||||
|
||||
proc_params.Length =
|
||||
static_cast<uint32_t>(std::max(static_cast<uint64_t>(sizeof(proc_params)), total_length));
|
||||
proc_params.MaximumLength = proc_params.Length;
|
||||
});
|
||||
|
||||
apiset_location apiset_loc = apiset_location::file;
|
||||
|
||||
if (win_emu.get_emulation_root().empty())
|
||||
else if (app_settings.application.is_relative())
|
||||
{
|
||||
#ifdef OS_WINDOWS
|
||||
apiset_loc = apiset_location::host;
|
||||
#else
|
||||
apiset_loc = apiset_location::default_windows_11;
|
||||
#endif
|
||||
app_settings.working_directory = std::filesystem::current_path();
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
app_settings.working_directory = app_settings.application.parent();
|
||||
}
|
||||
}
|
||||
|
||||
context.peb.access([&](PEB64& peb) {
|
||||
peb.ImageBaseAddress = nullptr;
|
||||
peb.ProcessParameters = context.process_params.ptr();
|
||||
peb.ApiSetMap = build_api_set_map(emu, allocator, apiset_loc, win_emu.get_emulation_root()).ptr();
|
||||
void adjust_application(application_settings& app_settings)
|
||||
{
|
||||
if (app_settings.application.is_relative())
|
||||
{
|
||||
app_settings.application = app_settings.working_directory / app_settings.application;
|
||||
}
|
||||
}
|
||||
|
||||
peb.ProcessHeap = nullptr;
|
||||
peb.ProcessHeaps = nullptr;
|
||||
peb.HeapSegmentReserve = 0x0000000000100000; // TODO: Read from executable
|
||||
peb.HeapSegmentCommit = 0x0000000000002000;
|
||||
peb.HeapDeCommitTotalFreeThreshold = 0x0000000000010000;
|
||||
peb.HeapDeCommitFreeBlockThreshold = 0x0000000000001000;
|
||||
peb.NumberOfHeaps = 0x00000000;
|
||||
peb.MaximumNumberOfHeaps = 0x00000010;
|
||||
|
||||
peb.OSPlatformId = 2;
|
||||
peb.OSMajorVersion = 0x0000000a;
|
||||
peb.OSBuildNumber = 0x00006c51;
|
||||
|
||||
// peb.AnsiCodePageData = allocator.reserve<CPTABLEINFO>().value();
|
||||
// peb.OemCodePageData = allocator.reserve<CPTABLEINFO>().value();
|
||||
peb.UnicodeCaseTableData = allocator.reserve<NLSTABLEINFO>().value();
|
||||
});
|
||||
void fixup_application_settings(application_settings& app_settings)
|
||||
{
|
||||
adjust_working_directory(app_settings);
|
||||
adjust_application(app_settings);
|
||||
}
|
||||
|
||||
void perform_context_switch_work(windows_emulator& win_emu)
|
||||
{
|
||||
auto& devices = win_emu.process().devices;
|
||||
auto& devices = win_emu.process.devices;
|
||||
|
||||
// Crappy mechanism to prevent mutation while iterating.
|
||||
const auto was_blocked = devices.block_mutation(true);
|
||||
@@ -319,7 +84,7 @@ namespace
|
||||
}
|
||||
|
||||
auto& emu = win_emu.emu();
|
||||
auto& context = win_emu.process();
|
||||
auto& context = win_emu.process;
|
||||
|
||||
const auto is_ready = thread.is_thread_ready(context);
|
||||
|
||||
@@ -351,7 +116,7 @@ namespace
|
||||
|
||||
bool switch_to_thread(windows_emulator& win_emu, const handle thread_handle)
|
||||
{
|
||||
auto* thread = win_emu.process().threads.get(thread_handle);
|
||||
auto* thread = win_emu.process.threads.get(thread_handle);
|
||||
if (!thread)
|
||||
{
|
||||
throw std::runtime_error("Bad thread handle");
|
||||
@@ -364,7 +129,7 @@ namespace
|
||||
{
|
||||
perform_context_switch_work(win_emu);
|
||||
|
||||
auto& context = win_emu.process();
|
||||
auto& context = win_emu.process;
|
||||
|
||||
bool next_thread = false;
|
||||
|
||||
@@ -403,30 +168,35 @@ std::unique_ptr<x64_emulator> create_default_x64_emulator()
|
||||
return unicorn::create_x64_emulator();
|
||||
}
|
||||
|
||||
windows_emulator::windows_emulator(const emulator_settings& settings, emulator_callbacks callbacks,
|
||||
std::unique_ptr<x64_emulator> emu)
|
||||
: windows_emulator(settings.emulation_root, std::move(emu))
|
||||
windows_emulator::windows_emulator(application_settings app_settings, const emulator_settings& settings,
|
||||
emulator_callbacks callbacks, std::unique_ptr<x64_emulator> emu)
|
||||
: windows_emulator(settings, std::move(emu))
|
||||
{
|
||||
windows_path working_dir{};
|
||||
this->callbacks = std::move(callbacks);
|
||||
|
||||
if (!settings.working_directory.empty())
|
||||
fixup_application_settings(app_settings);
|
||||
this->setup_process(app_settings, settings);
|
||||
}
|
||||
|
||||
windows_emulator::windows_emulator(const emulator_settings& settings, std::unique_ptr<x64_emulator> emu)
|
||||
: emu_(std::move(emu)),
|
||||
emulation_root{settings.emulation_root.empty() ? settings.emulation_root : absolute(settings.emulation_root)},
|
||||
file_sys(emulation_root.empty() ? emulation_root : emulation_root / "filesys"),
|
||||
memory(*this->emu_),
|
||||
registry(emulation_root.empty() ? settings.registry_directory : emulation_root / "registry"),
|
||||
mod_manager(memory, file_sys),
|
||||
process(*this->emu_, memory)
|
||||
{
|
||||
#ifndef OS_WINDOWS
|
||||
if (this->emulation_root.empty())
|
||||
{
|
||||
working_dir = settings.working_directory;
|
||||
}
|
||||
#ifdef OS_WINDOWS
|
||||
else if (settings.application.is_relative())
|
||||
{
|
||||
working_dir = std::filesystem::current_path();
|
||||
throw std::runtime_error("Emulation root directory can not be empty!");
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
working_dir = settings.application.parent();
|
||||
}
|
||||
|
||||
for (const auto& mapping : settings.path_mappings)
|
||||
{
|
||||
this->file_sys().map(mapping.first, mapping.second);
|
||||
this->file_sys.map(mapping.first, mapping.second);
|
||||
}
|
||||
|
||||
for (const auto& mapping : settings.port_mappings)
|
||||
@@ -438,65 +208,35 @@ windows_emulator::windows_emulator(const emulator_settings& settings, emulator_c
|
||||
this->silent_until_main_ = settings.silent_until_main && !settings.disable_logging;
|
||||
this->use_relative_time_ = settings.use_relative_time;
|
||||
this->log.disable_output(settings.disable_logging || this->silent_until_main_);
|
||||
this->callbacks_ = std::move(callbacks);
|
||||
this->modules_ = settings.modules;
|
||||
|
||||
this->setup_process(settings, working_dir);
|
||||
}
|
||||
|
||||
windows_emulator::windows_emulator(const std::filesystem::path& emulation_root, std::unique_ptr<x64_emulator> emu)
|
||||
: emulation_root_{emulation_root.empty() ? emulation_root : absolute(emulation_root)},
|
||||
file_sys_(emulation_root_.empty() ? emulation_root_ : emulation_root_ / "filesys"),
|
||||
emu_(std::move(emu)),
|
||||
memory_manager_(*this->emu_),
|
||||
process_(*emu_, memory_manager_, file_sys_)
|
||||
{
|
||||
#ifndef OS_WINDOWS
|
||||
if (this->get_emulation_root().empty())
|
||||
{
|
||||
throw std::runtime_error("Emulation root directory can not be empty!");
|
||||
}
|
||||
#endif
|
||||
|
||||
this->setup_hooks();
|
||||
}
|
||||
|
||||
windows_emulator::~windows_emulator() = default;
|
||||
|
||||
void windows_emulator::setup_process(const emulator_settings& settings, const windows_path& working_directory)
|
||||
void windows_emulator::setup_process(const application_settings& app_settings, const emulator_settings& emu_settings)
|
||||
{
|
||||
auto& emu = this->emu();
|
||||
const auto& emu = this->emu();
|
||||
auto& context = this->process;
|
||||
|
||||
auto& context = this->process();
|
||||
context.mod_manager = module_manager(this->memory(), this->file_sys()); // TODO: Cleanup module manager
|
||||
this->mod_manager.map_main_modules(app_settings.application, R"(C:\Windows\System32\ntdll.dll)",
|
||||
R"(C:\Windows\System32\win32u.dll)", this->log);
|
||||
|
||||
const auto application = settings.application.is_absolute() //
|
||||
? settings.application
|
||||
: (working_directory / settings.application);
|
||||
const auto* executable = this->mod_manager.executable;
|
||||
const auto* ntdll = this->mod_manager.ntdll;
|
||||
const auto* win32u = this->mod_manager.win32u;
|
||||
|
||||
setup_context(*this, settings, application, working_directory);
|
||||
const auto apiset_data = apiset::obtain(this->emulation_root);
|
||||
|
||||
context.executable = context.mod_manager.map_module(application, this->log, true);
|
||||
this->process.setup(this->emu(), this->memory, app_settings, emu_settings, *executable, *ntdll, apiset_data);
|
||||
|
||||
context.peb.access([&](PEB64& peb) {
|
||||
peb.ImageBaseAddress = reinterpret_cast<std::uint64_t*>(context.executable->image_base); //
|
||||
});
|
||||
const auto ntdll_data = emu.read_memory(ntdll->image_base, ntdll->size_of_image);
|
||||
const auto win32u_data = emu.read_memory(win32u->image_base, win32u->size_of_image);
|
||||
|
||||
context.ntdll = context.mod_manager.map_module(R"(C:\Windows\System32\ntdll.dll)", this->log, true);
|
||||
context.win32u = context.mod_manager.map_module(R"(C:\Windows\System32\win32u.dll)", this->log, true);
|
||||
this->dispatcher.setup(ntdll->exports, ntdll_data, win32u->exports, win32u_data);
|
||||
|
||||
const auto ntdll_data = emu.read_memory(context.ntdll->image_base, context.ntdll->size_of_image);
|
||||
const auto win32u_data = emu.read_memory(context.win32u->image_base, context.win32u->size_of_image);
|
||||
|
||||
this->dispatcher_.setup(context.ntdll->exports, ntdll_data, context.win32u->exports, win32u_data);
|
||||
|
||||
context.ldr_initialize_thunk = context.ntdll->find_export("LdrInitializeThunk");
|
||||
context.rtl_user_thread_start = context.ntdll->find_export("RtlUserThreadStart");
|
||||
context.ki_user_exception_dispatcher = context.ntdll->find_export("KiUserExceptionDispatcher");
|
||||
|
||||
context.default_register_set = emu.save_registers();
|
||||
|
||||
const auto main_thread_id = context.create_thread(this->memory(), context.executable->entry_point, 0, 0);
|
||||
const auto main_thread_id = context.create_thread(this->memory, this->mod_manager.executable->entry_point, 0, 0);
|
||||
switch_to_thread(*this, main_thread_id);
|
||||
}
|
||||
|
||||
@@ -518,7 +258,7 @@ void windows_emulator::perform_thread_switch()
|
||||
|
||||
bool windows_emulator::activate_thread(const uint32_t id)
|
||||
{
|
||||
const auto thread = get_thread_by_id(this->process(), id);
|
||||
const auto thread = get_thread_by_id(this->process, id);
|
||||
if (!thread)
|
||||
{
|
||||
return false;
|
||||
@@ -529,25 +269,24 @@ bool windows_emulator::activate_thread(const uint32_t id)
|
||||
|
||||
void windows_emulator::on_instruction_execution(const uint64_t address)
|
||||
{
|
||||
auto& process = this->process();
|
||||
auto& thread = this->current_thread();
|
||||
|
||||
++process.executed_instructions;
|
||||
++this->process.executed_instructions;
|
||||
const auto thread_insts = ++thread.executed_instructions;
|
||||
if (thread_insts % MAX_INSTRUCTIONS_PER_TIME_SLICE == 0)
|
||||
{
|
||||
this->yield_thread();
|
||||
}
|
||||
|
||||
process.previous_ip = process.current_ip;
|
||||
process.current_ip = this->emu().read_instruction_pointer();
|
||||
this->process.previous_ip = this->process.current_ip;
|
||||
this->process.current_ip = this->emu().read_instruction_pointer();
|
||||
|
||||
const auto binary = utils::make_lazy([&] {
|
||||
return this->process().mod_manager.find_by_address(address); //
|
||||
return this->mod_manager.find_by_address(address); //
|
||||
});
|
||||
|
||||
const auto previous_binary = utils::make_lazy([&] {
|
||||
return this->process().mod_manager.find_by_address(process.previous_ip); //
|
||||
return this->mod_manager.find_by_address(this->process.previous_ip); //
|
||||
});
|
||||
|
||||
const auto is_in_interesting_module = [&] {
|
||||
@@ -560,9 +299,9 @@ void windows_emulator::on_instruction_execution(const uint64_t address)
|
||||
(previous_binary && this->modules_.contains(previous_binary->name));
|
||||
};
|
||||
|
||||
const auto is_main_exe = process.executable->is_within(address);
|
||||
const auto is_interesting_call = process.executable->is_within(process.previous_ip) //
|
||||
|| is_main_exe //
|
||||
const auto is_main_exe = this->mod_manager.executable->is_within(address);
|
||||
const auto is_interesting_call = this->mod_manager.executable->is_within(this->process.previous_ip) //
|
||||
|| is_main_exe //
|
||||
|| is_in_interesting_module();
|
||||
|
||||
if (this->silent_until_main_ && is_main_exe)
|
||||
@@ -586,7 +325,7 @@ void windows_emulator::on_instruction_execution(const uint64_t address)
|
||||
uint64_t return_address{};
|
||||
this->emu().try_read_memory(rsp, &return_address, sizeof(return_address));
|
||||
|
||||
const auto* mod_name = this->process().mod_manager.find_name(return_address);
|
||||
const auto* mod_name = this->mod_manager.find_name(return_address);
|
||||
|
||||
log.print(is_interesting_call ? color::yellow : color::dark_gray,
|
||||
"Executing function: %s - %s (0x%" PRIx64 ") via (0x%" PRIx64 ") %s\n", binary->name.c_str(),
|
||||
@@ -606,6 +345,7 @@ void windows_emulator::on_instruction_execution(const uint64_t address)
|
||||
|
||||
auto& emu = this->emu();
|
||||
|
||||
// TODO: Remove or cleanup
|
||||
log.print(color::gray,
|
||||
"Inst: %16" PRIx64 " - RAX: %16" PRIx64 " - RBX: %16" PRIx64 " - RCX: %16" PRIx64 " - RDX: %16" PRIx64
|
||||
" - R8: %16" PRIx64 " - R9: %16" PRIx64 " - RDI: %16" PRIx64 " - RSI: %16" PRIx64 " - %s\n",
|
||||
@@ -625,12 +365,12 @@ void windows_emulator::setup_hooks()
|
||||
}
|
||||
}
|
||||
|
||||
this->dispatcher_.dispatch(*this);
|
||||
this->dispatcher.dispatch(*this);
|
||||
return instruction_hook_continuation::skip_instruction;
|
||||
});
|
||||
|
||||
this->emu().hook_instruction(x64_hookable_instructions::rdtsc, [&] {
|
||||
const auto instructions = this->process().executed_instructions;
|
||||
const auto instructions = this->process.executed_instructions;
|
||||
this->emu().reg(x64_register::rax, instructions & 0xFFFFFFFF);
|
||||
this->emu().reg(x64_register::rdx, (instructions >> 32) & 0xFFFFFFFF);
|
||||
return instruction_hook_continuation::skip_instruction;
|
||||
@@ -650,14 +390,14 @@ void windows_emulator::setup_hooks()
|
||||
switch (interrupt)
|
||||
{
|
||||
case 0:
|
||||
dispatch_integer_division_by_zero(this->emu(), this->process());
|
||||
dispatch_integer_division_by_zero(this->emu(), this->process);
|
||||
return;
|
||||
case 1:
|
||||
this->log.print(color::pink, "Singlestep: 0x%" PRIx64 "\n", rip);
|
||||
dispatch_single_step(this->emu(), this->process());
|
||||
dispatch_single_step(this->emu(), this->process);
|
||||
return;
|
||||
case 6:
|
||||
dispatch_illegal_instruction_violation(this->emu(), this->process());
|
||||
dispatch_illegal_instruction_violation(this->emu(), this->process);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
@@ -667,7 +407,7 @@ void windows_emulator::setup_hooks()
|
||||
|
||||
if (this->fuzzing || true) // TODO: Fix
|
||||
{
|
||||
this->process().exception_rip = rip;
|
||||
this->process.exception_rip = rip;
|
||||
this->emu().stop();
|
||||
}
|
||||
});
|
||||
@@ -676,7 +416,7 @@ void windows_emulator::setup_hooks()
|
||||
const memory_violation_type type) {
|
||||
const auto permission = get_permission_string(operation);
|
||||
const auto ip = this->emu().read_instruction_pointer();
|
||||
const char* name = this->process().mod_manager.find_name(ip);
|
||||
const char* name = this->mod_manager.find_name(ip);
|
||||
|
||||
if (type == memory_violation_type::protection)
|
||||
{
|
||||
@@ -691,12 +431,12 @@ void windows_emulator::setup_hooks()
|
||||
|
||||
if (this->fuzzing)
|
||||
{
|
||||
this->process().exception_rip = ip;
|
||||
this->process.exception_rip = ip;
|
||||
this->emu().stop();
|
||||
return memory_violation_continuation::stop;
|
||||
}
|
||||
|
||||
dispatch_access_violation(this->emu(), this->process(), address, operation);
|
||||
dispatch_access_violation(this->emu(), this->process, address, operation);
|
||||
return memory_violation_continuation::resume;
|
||||
});
|
||||
|
||||
@@ -711,14 +451,14 @@ void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count)
|
||||
const auto use_timeout = timeout != std::chrono::nanoseconds{};
|
||||
|
||||
const auto start_time = std::chrono::high_resolution_clock::now();
|
||||
const auto start_instructions = this->process().executed_instructions;
|
||||
const auto start_instructions = this->process.executed_instructions;
|
||||
|
||||
const auto target_time = start_time + timeout;
|
||||
const auto target_instructions = start_instructions + count;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (this->switch_thread_ || !this->current_thread().is_thread_ready(this->process()))
|
||||
if (this->switch_thread_ || !this->current_thread().is_thread_ready(this->process))
|
||||
{
|
||||
this->perform_thread_switch();
|
||||
}
|
||||
@@ -744,7 +484,7 @@ void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count)
|
||||
|
||||
if (use_count)
|
||||
{
|
||||
const auto current_instructions = this->process().executed_instructions;
|
||||
const auto current_instructions = this->process.executed_instructions;
|
||||
|
||||
if (current_instructions >= target_instructions)
|
||||
{
|
||||
@@ -761,15 +501,20 @@ void windows_emulator::serialize(utils::buffer_serializer& buffer) const
|
||||
buffer.write(this->switch_thread_);
|
||||
buffer.write(this->use_relative_time_);
|
||||
this->emu().serialize_state(buffer, false);
|
||||
this->memory().serialize_memory_state(buffer, false);
|
||||
this->process_.serialize(buffer);
|
||||
this->dispatcher_.serialize(buffer);
|
||||
this->memory.serialize_memory_state(buffer, false);
|
||||
this->mod_manager.serialize(buffer);
|
||||
this->process.serialize(buffer);
|
||||
this->dispatcher.serialize(buffer);
|
||||
}
|
||||
|
||||
void windows_emulator::deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.register_factory<memory_manager_wrapper>([this] {
|
||||
return memory_manager_wrapper{this->memory()}; //
|
||||
return memory_manager_wrapper{this->memory}; //
|
||||
});
|
||||
|
||||
buffer.register_factory<module_manager_wrapper>([this] {
|
||||
return module_manager_wrapper{this->mod_manager}; //
|
||||
});
|
||||
|
||||
buffer.register_factory<x64_emulator_wrapper>([this] {
|
||||
@@ -783,30 +528,34 @@ void windows_emulator::deserialize(utils::buffer_deserializer& buffer)
|
||||
buffer.read(this->switch_thread_);
|
||||
buffer.read(this->use_relative_time_);
|
||||
|
||||
this->memory().unmap_all_memory();
|
||||
this->memory.unmap_all_memory();
|
||||
|
||||
this->emu().deserialize_state(buffer, false);
|
||||
this->memory().deserialize_memory_state(buffer, false);
|
||||
this->process_.deserialize(buffer);
|
||||
this->dispatcher_.deserialize(buffer);
|
||||
this->memory.deserialize_memory_state(buffer, false);
|
||||
this->mod_manager.deserialize(buffer);
|
||||
this->process.deserialize(buffer);
|
||||
this->dispatcher.deserialize(buffer);
|
||||
}
|
||||
|
||||
void windows_emulator::save_snapshot()
|
||||
{
|
||||
utils::buffer_serializer serializer{};
|
||||
throw std::runtime_error("Not supported");
|
||||
/*utils::buffer_serializer serializer{};
|
||||
this->emu().serialize_state(serializer, true);
|
||||
this->memory().serialize_memory_state(serializer, true);
|
||||
this->process_.serialize(serializer);
|
||||
this->memory.serialize_memory_state(serializer, true);
|
||||
this->mod_manager.serialize(serializer);
|
||||
this->process.serialize(serializer);
|
||||
|
||||
this->process_snapshot_ = serializer.move_buffer();
|
||||
|
||||
// TODO: Make process copyable
|
||||
// this->process_snapshot_ = this->process();
|
||||
// this->process_snapshot_ = this->process;*/
|
||||
}
|
||||
|
||||
void windows_emulator::restore_snapshot()
|
||||
{
|
||||
if (this->process_snapshot_.empty())
|
||||
throw std::runtime_error("Not supported");
|
||||
/*if (this->process_snapshot_.empty())
|
||||
{
|
||||
assert(false);
|
||||
return;
|
||||
@@ -814,7 +563,8 @@ void windows_emulator::restore_snapshot()
|
||||
|
||||
utils::buffer_deserializer deserializer{this->process_snapshot_};
|
||||
this->emu().deserialize_state(deserializer, true);
|
||||
this->memory().deserialize_memory_state(deserializer, true);
|
||||
this->process_.deserialize(deserializer);
|
||||
// this->process_ = *this->process_snapshot_;
|
||||
this->memory.deserialize_memory_state(deserializer, true);
|
||||
this->mod_manager.deserialize(deserializer);
|
||||
this->process.deserialize(deserializer);
|
||||
// this->process = *this->process_snapshot_;*/
|
||||
}
|
||||
|
||||
@@ -25,37 +25,47 @@ struct emulator_callbacks
|
||||
outofline_syscall{};
|
||||
};
|
||||
|
||||
// TODO: Split up into application and emulator settings
|
||||
struct emulator_settings
|
||||
struct application_settings
|
||||
{
|
||||
windows_path application{};
|
||||
windows_path working_directory{};
|
||||
std::filesystem::path registry_directory{"./registry"};
|
||||
std::filesystem::path emulation_root{};
|
||||
std::vector<std::u16string> arguments{};
|
||||
};
|
||||
|
||||
struct emulator_settings
|
||||
{
|
||||
std::filesystem::path emulation_root{};
|
||||
std::filesystem::path registry_directory{"./registry"};
|
||||
|
||||
bool verbose_calls{false};
|
||||
bool disable_logging{false};
|
||||
bool silent_until_main{false};
|
||||
bool use_relative_time{false};
|
||||
|
||||
std::unordered_map<uint16_t, uint16_t> port_mappings{};
|
||||
std::unordered_map<windows_path, std::filesystem::path> path_mappings{};
|
||||
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_{};
|
||||
|
||||
public:
|
||||
windows_emulator(const std::filesystem::path& emulation_root,
|
||||
std::filesystem::path emulation_root{};
|
||||
emulator_callbacks callbacks{};
|
||||
logger log{};
|
||||
file_system file_sys;
|
||||
memory_manager memory;
|
||||
registry_manager registry{};
|
||||
module_manager mod_manager;
|
||||
process_context process;
|
||||
syscall_dispatcher dispatcher;
|
||||
|
||||
windows_emulator(const emulator_settings& settings = {},
|
||||
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||
windows_emulator(const emulator_settings& settings, emulator_callbacks callbacks = {},
|
||||
windows_emulator(application_settings app_settings, const emulator_settings& settings = {},
|
||||
emulator_callbacks callbacks = {},
|
||||
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||
|
||||
windows_emulator(windows_emulator&&) = delete;
|
||||
@@ -75,34 +85,14 @@ class windows_emulator
|
||||
return *this->emu_;
|
||||
}
|
||||
|
||||
process_context& process()
|
||||
{
|
||||
return this->process_;
|
||||
}
|
||||
|
||||
const process_context& process() const
|
||||
{
|
||||
return this->process_;
|
||||
}
|
||||
|
||||
syscall_dispatcher& dispatcher()
|
||||
{
|
||||
return this->dispatcher_;
|
||||
}
|
||||
|
||||
const syscall_dispatcher& dispatcher() const
|
||||
{
|
||||
return this->dispatcher_;
|
||||
}
|
||||
|
||||
emulator_thread& current_thread() const
|
||||
{
|
||||
if (!this->process_.active_thread)
|
||||
if (!this->process.active_thread)
|
||||
{
|
||||
throw std::runtime_error("No active thread!");
|
||||
}
|
||||
|
||||
return *this->process_.active_thread;
|
||||
return *this->process.active_thread;
|
||||
}
|
||||
|
||||
void start(std::chrono::nanoseconds timeout = {}, size_t count = 0);
|
||||
@@ -157,7 +147,6 @@ class windows_emulator
|
||||
}
|
||||
}
|
||||
|
||||
logger log{};
|
||||
bool verbose{false};
|
||||
bool verbose_calls{false};
|
||||
bool buffer_stdout{false};
|
||||
@@ -172,61 +161,19 @@ class windows_emulator
|
||||
return this->use_relative_time_;
|
||||
}
|
||||
|
||||
emulator_callbacks& callbacks()
|
||||
{
|
||||
return this->callbacks_;
|
||||
}
|
||||
|
||||
file_system& file_sys()
|
||||
{
|
||||
return this->file_sys_;
|
||||
}
|
||||
|
||||
const file_system& file_sys() const
|
||||
{
|
||||
return this->file_sys_;
|
||||
}
|
||||
|
||||
memory_manager& memory()
|
||||
{
|
||||
return this->memory_manager_;
|
||||
}
|
||||
|
||||
const memory_manager& memory() const
|
||||
{
|
||||
return this->memory_manager_;
|
||||
}
|
||||
|
||||
const std::filesystem::path& get_emulation_root()
|
||||
{
|
||||
return this->emulation_root_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::filesystem::path emulation_root_{};
|
||||
file_system file_sys_;
|
||||
|
||||
emulator_callbacks callbacks_{};
|
||||
|
||||
bool switch_thread_{false};
|
||||
bool use_relative_time_{false};
|
||||
bool silent_until_main_{false};
|
||||
|
||||
std::unique_ptr<x64_emulator> emu_{};
|
||||
std::vector<instruction_hook_callback> syscall_hooks_{};
|
||||
std::unordered_map<uint16_t, uint16_t> port_mappings_{};
|
||||
|
||||
memory_manager memory_manager_;
|
||||
|
||||
std::set<std::string, std::less<>> modules_{};
|
||||
|
||||
process_context process_;
|
||||
syscall_dispatcher dispatcher_;
|
||||
|
||||
std::vector<std::byte> process_snapshot_{};
|
||||
// std::optional<process_context> process_snapshot_{};
|
||||
|
||||
void setup_hooks();
|
||||
void setup_process(const emulator_settings& settings, const windows_path& working_directory);
|
||||
void setup_process(const application_settings& app_settings, const emulator_settings& emu_settings);
|
||||
void on_instruction_execution(uint64_t address);
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
|
||||
|
||||
std::vector<uint32_t> get_thread_ids() override
|
||||
{
|
||||
const auto& threads = this->win_emu_->process().threads;
|
||||
const auto& threads = this->win_emu_->process.threads;
|
||||
|
||||
std::vector<uint32_t> ids{};
|
||||
ids.reserve(threads.size());
|
||||
@@ -70,7 +70,7 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
|
||||
|
||||
std::optional<uint32_t> get_exit_code() override
|
||||
{
|
||||
const auto status = this->win_emu_->process().exit_status;
|
||||
const auto status = this->win_emu_->process.exit_status;
|
||||
if (!status)
|
||||
{
|
||||
return std::nullopt;
|
||||
|
||||
Reference in New Issue
Block a user