Fix syscalls and tls vector updates (#40)

This commit is contained in:
Maurice Heumann
2025-01-02 13:55:03 +01:00
committed by GitHub
5 changed files with 82 additions and 42 deletions

View File

@@ -24,15 +24,16 @@ void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer)
}
void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports)
void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data)
{
this->handlers_ = {};
const auto ntdll_syscalls = find_syscalls(ntdll_exports);
const auto win32u_syscalls = find_syscalls(win32u_exports);
const auto ntdll_syscalls = find_syscalls(ntdll_exports, ntdll_data);
const auto win32u_syscalls = find_syscalls(win32u_exports, win32u_data);
map_syscalls(this->handlers_, ntdll_syscalls, 0);
map_syscalls(this->handlers_, win32u_syscalls, 0x1000);
map_syscalls(this->handlers_, ntdll_syscalls);
map_syscalls(this->handlers_, win32u_syscalls);
this->add_handlers();
}
@@ -99,10 +100,13 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
}
else
{
win_emu.logger.print(color::dark_gray, "Executing syscall: %s (0x%X) at 0x%llX\n",
const auto rsp = c.emu.read_stack_pointer();
const auto return_address = c.emu.read_memory<uint64_t>(rsp);
const auto* mod_name = context.module_manager.find_name(return_address);
win_emu.logger.print(color::dark_gray, "Executing syscall: %s (0x%X) at 0x%llX via %llX (%s)\n",
entry->second.name.c_str(),
syscall_id,
address);
syscall_id, address, return_address, mod_name);
}
entry->second.handler(c);
@@ -121,8 +125,8 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
}
}
syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports,
const exported_symbols& win32u_exports)
syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data)
{
this->setup(ntdll_exports, win32u_exports);
this->setup(ntdll_exports, ntdll_data, win32u_exports, win32u_data);
}

View File

@@ -17,14 +17,16 @@ class syscall_dispatcher
{
public:
syscall_dispatcher() = default;
syscall_dispatcher(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports);
syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
void dispatch(windows_emulator& win_emu);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
void setup(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports);
void setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
std::string get_syscall_name(const uint64_t id)
{

View File

@@ -38,32 +38,51 @@ inline bool is_syscall(const std::string_view name)
return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]);
}
inline std::vector<std::string> find_syscalls(const exported_symbols& exports)
inline std::optional<uint32_t> extract_syscall_id(const exported_symbol& symbol, std::span<const std::byte> data)
{
// Makes use of the fact that order of Nt* function addresses
// is equal to the order of syscall IDs.
// So first Nt* function is the first syscall with ID 0
if (!is_syscall(symbol.name))
{
return std::nullopt;
}
std::map<uint64_t, size_t> reference_count{};
std::map<uint64_t, std::string> ordered_syscalls{};
constexpr auto instruction_size = 5;
constexpr auto instruction_offset = 3;
constexpr auto instruction_operand_offset = 1;
constexpr auto instruction_opcode = static_cast<std::byte>(0xB8);
const auto instruction_rva = symbol.rva + instruction_offset;
if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode)
{
return std::nullopt;
}
uint32_t syscall_id{0};
static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset));
memcpy(&syscall_id, data.data() + instruction_rva + instruction_operand_offset, sizeof(syscall_id));
return syscall_id;
}
inline std::map<uint64_t, std::string> find_syscalls(const exported_symbols& exports, std::span<const std::byte> data)
{
std::map<uint64_t, std::string> syscalls{};
for (const auto& symbol : exports)
{
if (is_syscall(symbol.name))
const auto id = extract_syscall_id(symbol, data);
if (id)
{
++reference_count[symbol.address];
ordered_syscalls[symbol.address] = symbol.name;
}
}
auto& entry = syscalls[*id];
std::vector<std::string> syscalls{};
syscalls.reserve(ordered_syscalls.size());
if (!entry.empty())
{
throw std::runtime_error(
"Syscall with id " + std::to_string(*id) + ", which is mapping to " + symbol.name +
", was already mapped to " + entry);
}
for (auto& syscall : ordered_syscalls)
{
if (reference_count[syscall.first] == 1)
{
syscalls.push_back(std::move(syscall.second));
entry = symbol.name;
}
}
@@ -71,14 +90,20 @@ inline std::vector<std::string> find_syscalls(const exported_symbols& exports)
}
inline void map_syscalls(std::map<uint64_t, syscall_handler_entry>& handlers,
const std::vector<std::string>& syscalls, const uint64_t base_index)
std::map<uint64_t, std::string> syscalls)
{
for (size_t i = 0; i < syscalls.size(); ++i)
for (auto& [id, name] : syscalls)
{
const auto& syscall = syscalls[i];
auto& entry = handlers[id];
auto& entry = handlers[base_index + i];
entry.name = syscall;
if (!entry.name.empty())
{
throw std::runtime_error(
"Syscall with id " + std::to_string(id) + ", which is mapping to " + name +
", was previously mapped to " + entry.name);
}
entry.name = std::move(name);
entry.handler = nullptr;
}
}
@@ -249,4 +274,4 @@ inline LARGE_INTEGER convert_unix_to_windows_time(const __time64_t unix_time)
LARGE_INTEGER windows_time{};
windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND;
return windows_time;
}
}

View File

@@ -1761,9 +1761,8 @@ namespace
{
const auto new_tls_vector = entry.TlsVector;
for (uint32_t j = 1; j < tls_info.TlsVectorLength; ++j)
for (uint32_t index = 0; index < tls_info.TlsVectorLength; ++index)
{
const auto index = j - 1;
const auto old_entry = c.emu.read_memory<void*>(tls_vector + index);
c.emu.write_memory<void*>(new_tls_vector + index, old_entry);
}
@@ -2462,7 +2461,7 @@ namespace
return STATUS_NOT_SUPPORTED;
}
NTSTATUS handle_NtGdiInit2(const syscall_context& c)
NTSTATUS handle_NtGdiInit(const syscall_context& c)
{
c.proc.peb.access([&](PEB& peb)
{
@@ -2472,6 +2471,12 @@ namespace
}
});
return STATUS_WAIT_1;
}
NTSTATUS handle_NtGdiInit2(const syscall_context& c)
{
handle_NtGdiInit(c);
return STATUS_NOT_SUPPORTED;
}
@@ -2932,7 +2937,7 @@ namespace
response.write(ResponseAbort);
}
printf("Hard error: %X\n", static_cast<uint32_t>(error_status));
c.proc.exit_status = error_status;
c.proc.exception_rip = c.emu.read_instruction_pointer();
c.emu.stop();
@@ -3402,6 +3407,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtQueryInformationThread);
add_handler(NtQueryWnfStateNameInformation);
add_handler(NtAlpcSendWaitReceivePort);
add_handler(NtGdiInit);
add_handler(NtGdiInit2);
add_handler(NtUserGetThreadState);
add_handler(NtOpenKeyEx);

View File

@@ -753,7 +753,10 @@ void windows_emulator::setup_process(const emulator_settings& settings)
context.ntdll = context.module_manager.map_module(R"(C:\Windows\System32\ntdll.dll)", this->logger);
context.win32u = context.module_manager.map_module(R"(C:\Windows\System32\win32u.dll)", this->logger);
this->dispatcher_.setup(context.ntdll->exports, context.win32u->exports);
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");