mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Compare commits
35 Commits
dd56acc2b4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d70ab2607e | ||
|
|
f7eb8a2b2d | ||
|
|
c49226d7c1 | ||
|
|
83a6b65add | ||
|
|
dd31b6344f | ||
|
|
d33ef2d93f | ||
|
|
9090e29e21 | ||
|
|
7ba5a7b2e4 | ||
|
|
d5d73f42c9 | ||
|
|
01851ad571 | ||
|
|
e7abe50f00 | ||
|
|
4e5ba450c1 | ||
|
|
ff99a1cb56 | ||
|
|
9fdc2a4ce6 | ||
|
|
7c912146fb | ||
|
|
45ac1fc32f | ||
|
|
5d81d8bda9 | ||
|
|
e46e8dcf7a | ||
|
|
1a613be667 | ||
|
|
d3af3c781c | ||
|
|
17d1e23b60 | ||
|
|
1be74c4cde | ||
|
|
51034297bf | ||
|
|
a2a119aec2 | ||
|
|
6f8dca6614 | ||
|
|
cafa0ebd0b | ||
|
|
90b38b3bff | ||
|
|
5d59700ec7 | ||
|
|
37c2184bfc | ||
|
|
e0c386abbb | ||
|
|
565b499350 | ||
|
|
d51f890197 | ||
|
|
7112d619b4 | ||
|
|
5302900a9d | ||
|
|
de491ade0e |
@@ -91,7 +91,7 @@ export function LandingPage() {
|
||||
icon: <Shield className="h-6 w-6" />,
|
||||
title: "Security Research",
|
||||
description:
|
||||
"Analyze malware and security vulnerabilities in a controlled environment",
|
||||
"Analyze security vulnerabilities in a controlled environment",
|
||||
},
|
||||
{
|
||||
icon: <Lock className="h-6 w-6" />,
|
||||
|
||||
@@ -167,8 +167,15 @@ namespace
|
||||
const auto var_ptr = get_function_argument(win_emu.emu(), index);
|
||||
if (var_ptr && !is_int_resource(var_ptr))
|
||||
{
|
||||
const auto str = read_string<CharType>(win_emu.memory, var_ptr);
|
||||
print_string(win_emu.log, str);
|
||||
try
|
||||
{
|
||||
const auto str = read_string<CharType>(win_emu.memory, var_ptr);
|
||||
print_string(win_emu.log, str);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
print_string(win_emu.log, "[failed to read]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,6 +539,29 @@ namespace
|
||||
max = std::max(import_thunk, max);
|
||||
}
|
||||
|
||||
c.win_emu->emu().hook_memory_write(min, max - min, [&c](const uint64_t address, const void*, size_t) {
|
||||
const auto& watched_module = *c.win_emu->mod_manager.executable;
|
||||
const auto& accessor_module = *c.win_emu->mod_manager.executable;
|
||||
|
||||
const auto rip = c.win_emu->emu().read_instruction_pointer();
|
||||
|
||||
if (!accessor_module.contains(rip))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto sym = watched_module.imports.find(address);
|
||||
if (sym == watched_module.imports.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto import_module = watched_module.imported_modules.at(sym->second.module_index);
|
||||
|
||||
c.win_emu->log.print(color::blue, "Import write access: %s (%s) at 0x%" PRIx64 " (%s)\n", sym->second.name.c_str(),
|
||||
import_module.c_str(), rip, accessor_module.name.c_str());
|
||||
});
|
||||
|
||||
c.win_emu->emu().hook_memory_read(min, max - min, [&c](const uint64_t address, const void*, size_t) {
|
||||
const auto& watched_module = *c.win_emu->mod_manager.executable;
|
||||
const auto& accessor_module = *c.win_emu->mod_manager.executable;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "kernel_mapped.hpp"
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
#define ACCESS_MASK DWORD
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "primitives.hpp"
|
||||
#include "traits.hpp"
|
||||
#include "unicode.hpp"
|
||||
#include "status.hpp"
|
||||
#include "process.hpp"
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
#ifndef NT_SUCCESS
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "traits.hpp"
|
||||
#include "primitives.hpp"
|
||||
|
||||
template <typename Traits>
|
||||
struct EMU_WSABUF
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "unicode.hpp"
|
||||
#include "status.hpp"
|
||||
#include "process.hpp"
|
||||
#include "user.hpp"
|
||||
#include "kernel_mapped.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "file_management.hpp"
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "compiler.hpp"
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
|
||||
#include "../utils/win.hpp"
|
||||
#include "winnt.h"
|
||||
#include <winnt.h>
|
||||
|
||||
#else
|
||||
|
||||
@@ -50,6 +51,14 @@ typedef union _LARGE_INTEGER
|
||||
|
||||
using BYTE = std::uint8_t;
|
||||
#define CHAR BYTE
|
||||
|
||||
typedef struct _RECT
|
||||
{
|
||||
LONG left;
|
||||
LONG top;
|
||||
LONG right;
|
||||
LONG bottom;
|
||||
} RECT;
|
||||
#endif
|
||||
|
||||
using WORD = std::uint16_t;
|
||||
|
||||
@@ -110,4 +110,12 @@ struct KEY_VALUE_FULL_INFORMATION
|
||||
char16_t Name[1];
|
||||
};
|
||||
|
||||
struct KEY_VALUE_ENTRY
|
||||
{
|
||||
EmulatorTraits<Emu64>::PVOID ValueName;
|
||||
ULONG DataLength;
|
||||
ULONG DataOffset;
|
||||
ULONG Type;
|
||||
};
|
||||
|
||||
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
@@ -50,6 +50,7 @@ using NTSTATUS = std::uint32_t;
|
||||
#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
|
||||
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
|
||||
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
|
||||
#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL)
|
||||
#define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS)0xC000009FL)
|
||||
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
|
||||
#define STATUS_PIPE_BUSY ((NTSTATUS)0xC00000AAL)
|
||||
@@ -78,6 +79,7 @@ using NTSTATUS = std::uint32_t;
|
||||
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL)
|
||||
#define STATUS_CONNECTION_RESET ((NTSTATUS)0xC000020DL)
|
||||
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
|
||||
#define STATUS_NOT_SAME_OBJECT ((NTSTATUS)0xC00001ACL)
|
||||
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
|
||||
#define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS)0xC0000245L)
|
||||
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "kernel_mapped.hpp"
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
typedef enum _THREADINFOCLASS
|
||||
|
||||
117
src/common/platform/user.hpp
Normal file
117
src/common/platform/user.hpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
struct USER_SERVERINFO
|
||||
{
|
||||
DWORD dwSRVIFlags;
|
||||
uint64_t cHandleEntries;
|
||||
uint8_t unknown[0x1000];
|
||||
};
|
||||
|
||||
struct USER_DISPINFO
|
||||
{
|
||||
DWORD dwMonitorCount;
|
||||
EMULATOR_CAST(uint64_t, USER_MONITOR*) pPrimaryMonitor;
|
||||
uint8_t unknown[0xFF];
|
||||
};
|
||||
|
||||
struct USER_HANDLEENTRY
|
||||
{
|
||||
uint64_t pHead;
|
||||
uint64_t pOwner;
|
||||
uint64_t unknown;
|
||||
uint8_t bType;
|
||||
uint8_t bFlags;
|
||||
uint16_t wUniq;
|
||||
};
|
||||
static_assert(sizeof(USER_HANDLEENTRY) == 0x20);
|
||||
|
||||
struct USER_SHAREDINFO
|
||||
{
|
||||
uint64_t psi;
|
||||
uint64_t aheList;
|
||||
uint32_t HeEntrySize;
|
||||
uint64_t pDispInfo;
|
||||
uint8_t unknown[0xFF];
|
||||
};
|
||||
|
||||
struct USER_THROBJHEAD
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint64_t h;
|
||||
uint32_t cLockObj;
|
||||
} h;
|
||||
uint64_t pti;
|
||||
};
|
||||
|
||||
struct USER_THRDESKHEAD
|
||||
{
|
||||
USER_THROBJHEAD h;
|
||||
uint64_t rpdesk;
|
||||
uint64_t pSelf;
|
||||
};
|
||||
|
||||
enum USER_HANDLETYPE : uint8_t
|
||||
{
|
||||
TYPE_FREE = 0,
|
||||
TYPE_WINDOW = 1,
|
||||
TYPE_MENU = 2,
|
||||
TYPE_CURSOR = 3,
|
||||
TYPE_SETWINDOWPOS = 4,
|
||||
TYPE_HOOK = 5,
|
||||
TYPE_CLIPDATA = 6,
|
||||
TYPE_CALLPROC = 7,
|
||||
TYPE_ACCELTABLE = 8,
|
||||
TYPE_DDEACCESS = 9,
|
||||
TYPE_DDECONV = 10,
|
||||
TYPE_DDEXACT = 11,
|
||||
TYPE_MONITOR = 12,
|
||||
TYPE_KBDLAYOUT = 13,
|
||||
TYPE_KBDFILE = 14,
|
||||
TYPE_WINEVENTHOOK = 15,
|
||||
TYPE_TIMER = 16,
|
||||
TYPE_INPUTCONTEXT = 17,
|
||||
TYPE_HIDDATA = 18,
|
||||
TYPE_DEVICEINFO = 19,
|
||||
TYPE_TOUCHINPUTINFO = 20,
|
||||
TYPE_GESTUREINFOOBJ = 21,
|
||||
TYPE_CTYPES = 22,
|
||||
TYPE_GENERIC = 255
|
||||
};
|
||||
|
||||
struct USER_MONITOR
|
||||
{
|
||||
EMULATOR_CAST(uint64_t, HMONITOR) hmon;
|
||||
uint8_t unknown1[0x14];
|
||||
RECT rcMonitor;
|
||||
RECT rcWork;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint16_t monitorDpi;
|
||||
uint16_t nativeDpi;
|
||||
} b26;
|
||||
struct
|
||||
{
|
||||
uint32_t unknown1;
|
||||
uint16_t monitorDpi;
|
||||
uint16_t nativeDpi;
|
||||
uint16_t cachedDpi;
|
||||
uint16_t unknown2;
|
||||
RECT rcMonitorDpiAware;
|
||||
} b20;
|
||||
};
|
||||
uint8_t unknown4[0xFF];
|
||||
};
|
||||
|
||||
struct USER_WINDOW
|
||||
{
|
||||
uint8_t unknown[0xFF];
|
||||
};
|
||||
|
||||
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <system_error>
|
||||
#include <variant>
|
||||
|
||||
#include "primitives.hpp"
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "kernel_mapped.hpp"
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
using pointer = uint64_t;
|
||||
|
||||
@@ -160,6 +160,36 @@ namespace utils
|
||||
this->write_span(std::span(vec));
|
||||
}
|
||||
|
||||
void write_vector(const std::vector<bool>& vec)
|
||||
{
|
||||
this->write(static_cast<uint64_t>(vec.size()));
|
||||
|
||||
uint8_t byte = 0;
|
||||
uint8_t bit_index = 0;
|
||||
|
||||
for (const bool b : vec)
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
byte |= (1u << bit_index);
|
||||
}
|
||||
|
||||
++bit_index;
|
||||
|
||||
if (bit_index == 8)
|
||||
{
|
||||
this->write<uint8_t>(byte);
|
||||
byte = 0;
|
||||
bit_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (bit_index != 0)
|
||||
{
|
||||
this->write<uint8_t>(byte);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_list(const std::list<T>& vec)
|
||||
{
|
||||
@@ -385,6 +415,25 @@ namespace utils
|
||||
}
|
||||
}
|
||||
|
||||
void read_vector(std::vector<bool>& result)
|
||||
{
|
||||
const auto bit_count = this->read<uint64_t>();
|
||||
result.clear();
|
||||
result.reserve(static_cast<size_t>(bit_count));
|
||||
|
||||
const auto size = (bit_count + 7) / 8;
|
||||
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
const auto byte = this->read<uint8_t>();
|
||||
|
||||
for (uint8_t bit = 0; bit < 8 && result.size() < bit_count; ++bit)
|
||||
{
|
||||
result.push_back((byte >> bit) & 1u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> read_vector()
|
||||
{
|
||||
|
||||
@@ -59,6 +59,12 @@ class typed_emulator : public emulator
|
||||
return result;
|
||||
}
|
||||
|
||||
void write_stack(const size_t index, const pointer_type& value)
|
||||
{
|
||||
const auto sp = this->read_stack_pointer();
|
||||
this->write_memory(sp + (index * pointer_size), &value, sizeof(value));
|
||||
}
|
||||
|
||||
void push_stack(const pointer_type& value)
|
||||
{
|
||||
const auto sp = this->read_stack_pointer() - pointer_size;
|
||||
|
||||
@@ -509,6 +509,74 @@ namespace
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validate_primary_monitor(MONITORINFOEXA& mi)
|
||||
{
|
||||
if (std::string_view(mi.szDevice) != R"(\\.\DISPLAY1)")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mi.rcMonitor.left != 0 || mi.rcMonitor.top != 0 || mi.rcMonitor.right != 1920 || mi.rcMonitor.bottom != 1080)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(mi.dwFlags & MONITORINFOF_PRIMARY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_monitor_info()
|
||||
{
|
||||
const POINT pt = {0, 0};
|
||||
const auto hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
|
||||
if (!hMonitor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MONITORINFOEXA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
|
||||
if (!GetMonitorInfoA(hMonitor, &mi))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return validate_primary_monitor(mi);
|
||||
}
|
||||
|
||||
BOOL CALLBACK monitor_enum_proc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
|
||||
{
|
||||
auto* valid = reinterpret_cast<bool*>(dwData);
|
||||
|
||||
MONITORINFOEXA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
|
||||
if (!GetMonitorInfoA(hMonitor, &mi))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*valid = validate_primary_monitor(mi);
|
||||
|
||||
return *valid ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
bool test_user_callback()
|
||||
{
|
||||
bool valid = false;
|
||||
if (!EnumDisplayMonitors(nullptr, nullptr, monitor_enum_proc, reinterpret_cast<LPARAM>(&valid)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool test_time_zone()
|
||||
{
|
||||
DYNAMIC_TIME_ZONE_INFORMATION current_dtzi = {};
|
||||
@@ -908,6 +976,7 @@ int main(const int argc, const char* argv[])
|
||||
RUN_TEST(test_working_directory, "Working Directory")
|
||||
RUN_TEST(test_registry, "Registry")
|
||||
RUN_TEST(test_system_info, "System Info")
|
||||
RUN_TEST(test_monitor_info, "Monitor Info")
|
||||
RUN_TEST(test_time_zone, "Time Zone")
|
||||
RUN_TEST(test_threads, "Threads")
|
||||
RUN_TEST(test_threads_winapi, "Threads WinAPI")
|
||||
@@ -921,9 +990,9 @@ int main(const int argc, const char* argv[])
|
||||
RUN_TEST(test_interrupts, "Interrupts")
|
||||
}
|
||||
RUN_TEST(test_tls, "TLS")
|
||||
|
||||
RUN_TEST(test_socket, "Socket")
|
||||
RUN_TEST(test_apc, "APC")
|
||||
RUN_TEST(test_user_callback, "User Callback")
|
||||
|
||||
return valid ? 0 : 1;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,48 @@ struct pending_apc
|
||||
}
|
||||
};
|
||||
|
||||
enum class callback_id : uint32_t
|
||||
{
|
||||
Invalid = 0,
|
||||
NtUserEnumDisplayMonitors,
|
||||
};
|
||||
|
||||
struct callback_frame
|
||||
{
|
||||
callback_id handler_id;
|
||||
uint64_t rip;
|
||||
uint64_t rsp;
|
||||
uint64_t r10;
|
||||
uint64_t rcx;
|
||||
uint64_t rdx;
|
||||
uint64_t r8;
|
||||
uint64_t r9;
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->handler_id);
|
||||
buffer.write(this->rip);
|
||||
buffer.write(this->rsp);
|
||||
buffer.write(this->r10);
|
||||
buffer.write(this->rcx);
|
||||
buffer.write(this->rdx);
|
||||
buffer.write(this->r8);
|
||||
buffer.write(this->r9);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->handler_id);
|
||||
buffer.read(this->rip);
|
||||
buffer.read(this->rsp);
|
||||
buffer.read(this->r10);
|
||||
buffer.read(this->rcx);
|
||||
buffer.read(this->rdx);
|
||||
buffer.read(this->r8);
|
||||
buffer.read(this->r9);
|
||||
}
|
||||
};
|
||||
|
||||
class emulator_thread : public ref_counted_object
|
||||
{
|
||||
public:
|
||||
@@ -105,6 +147,8 @@ class emulator_thread : public ref_counted_object
|
||||
|
||||
bool debugger_hide{false};
|
||||
|
||||
std::vector<callback_frame> callback_stack;
|
||||
|
||||
void mark_as_ready(NTSTATUS status);
|
||||
|
||||
bool is_await_time_over(utils::clock& clock) const
|
||||
@@ -188,6 +232,8 @@ class emulator_thread : public ref_counted_object
|
||||
buffer.write_vector(this->last_registers);
|
||||
|
||||
buffer.write(this->debugger_hide);
|
||||
|
||||
buffer.write_vector(this->callback_stack);
|
||||
}
|
||||
|
||||
void deserialize_object(utils::buffer_deserializer& buffer) override
|
||||
@@ -236,6 +282,8 @@ class emulator_thread : public ref_counted_object
|
||||
buffer.read_vector(this->last_registers);
|
||||
|
||||
buffer.read(this->debugger_hide);
|
||||
|
||||
buffer.read_vector(this->callback_stack);
|
||||
}
|
||||
|
||||
void leak_memory()
|
||||
|
||||
@@ -408,6 +408,8 @@ inline std::u16string read_unicode_string(emulator& emu, const uint64_t uc_strin
|
||||
return read_unicode_string(emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{emu, uc_string});
|
||||
}
|
||||
|
||||
/// Retrieves function arguments from registers or stack memory. This function assumes the stack pointer currently points to the
|
||||
/// return address.
|
||||
inline uint64_t get_function_argument(x86_64_emulator& emu, const size_t index, const bool is_syscall = false)
|
||||
{
|
||||
bool use_32bit_stack = false;
|
||||
@@ -440,3 +442,50 @@ inline uint64_t get_function_argument(x86_64_emulator& emu, const size_t index,
|
||||
return emu.read_stack(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets function arguments in registers or stack memory. This function does not modify RSP and assumes the caller has already allocated
|
||||
/// stack space and that RSP currently points to the return address.
|
||||
inline void set_function_argument(x86_64_emulator& emu, const size_t index, const uint64_t value, const bool is_syscall = false)
|
||||
{
|
||||
bool use_32bit_stack = false;
|
||||
if (!is_syscall)
|
||||
{
|
||||
const auto cs_selector = emu.reg<uint16_t>(x86_register::cs);
|
||||
const auto bitness = segment_utils::get_segment_bitness(emu, cs_selector);
|
||||
use_32bit_stack = bitness && *bitness == segment_utils::segment_bitness::bit32;
|
||||
}
|
||||
|
||||
if (use_32bit_stack)
|
||||
{
|
||||
const auto esp = emu.reg<uint32_t>(x86_register::esp);
|
||||
const auto address = static_cast<uint64_t>(esp) + static_cast<uint64_t>((index + 1) * sizeof(uint32_t));
|
||||
emu.write_memory<uint32_t>(address, static_cast<uint32_t>(value));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
emu.reg(is_syscall ? x86_register::r10 : x86_register::rcx, value);
|
||||
break;
|
||||
case 1:
|
||||
emu.reg(x86_register::rdx, value);
|
||||
break;
|
||||
case 2:
|
||||
emu.reg(x86_register::r8, value);
|
||||
break;
|
||||
case 3:
|
||||
emu.reg(x86_register::r9, value);
|
||||
break;
|
||||
default:
|
||||
emu.write_stack(index + 1, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t aligned_stack_space(const size_t arg_count)
|
||||
{
|
||||
const size_t slots = (arg_count < 4) ? 4 : arg_count;
|
||||
const size_t bytes = slots * sizeof(uint64_t);
|
||||
return (bytes + 15) & ~15;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ struct handle_types
|
||||
token,
|
||||
window,
|
||||
timer,
|
||||
monitor,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@ memory_stats memory_manager::compute_memory_stats() const
|
||||
void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const
|
||||
{
|
||||
buffer.write_atomic(this->layout_version_);
|
||||
buffer.write(this->default_allocation_address_);
|
||||
buffer.write_map(this->reserved_regions_);
|
||||
|
||||
if (is_snapshot)
|
||||
@@ -157,6 +158,7 @@ void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer
|
||||
}
|
||||
|
||||
buffer.read_atomic(this->layout_version_);
|
||||
buffer.read(this->default_allocation_address_);
|
||||
buffer.read_map(this->reserved_regions_);
|
||||
|
||||
if (is_snapshot)
|
||||
@@ -342,6 +344,10 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co
|
||||
committed_regions[map_start] = committed_region{static_cast<size_t>(map_length), permissions};
|
||||
}
|
||||
|
||||
// Update protection for existing committed region when re-committing
|
||||
this->apply_memory_protection(sub_region.first, sub_region.second.length, effective_permission);
|
||||
sub_region.second.permissions = permissions;
|
||||
|
||||
last_region_start = sub_region.first;
|
||||
last_region = &sub_region.second;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include "std_include.hpp"
|
||||
|
||||
#include "minidump_loader.hpp"
|
||||
#include "windows_emulator.hpp"
|
||||
#include "windows_objects.hpp"
|
||||
#include "emulator_thread.hpp"
|
||||
#include "common/platform/unicode.hpp"
|
||||
#include "common/platform/kernel_mapped.hpp"
|
||||
#include "memory_utils.hpp"
|
||||
|
||||
#include <platform/platform.hpp>
|
||||
|
||||
#include <minidump/minidump.hpp>
|
||||
|
||||
namespace minidump_loader
|
||||
|
||||
@@ -15,6 +15,15 @@ namespace
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
};
|
||||
|
||||
struct noop_port : port
|
||||
{
|
||||
NTSTATUS handle_request(windows_emulator& /*win_emu*/, const lpc_request_context& c) override
|
||||
{
|
||||
c.recv_buffer_length = 0;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<port> create_port(const std::u16string_view port)
|
||||
@@ -29,6 +38,11 @@ std::unique_ptr<port> create_port(const std::u16string_view port)
|
||||
return create_dns_resolver();
|
||||
}
|
||||
|
||||
if (port == u"\\WindowsErrorReportingServicePort")
|
||||
{
|
||||
return std::make_unique<noop_port>();
|
||||
}
|
||||
|
||||
return std::make_unique<dummy_port>();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,13 @@ namespace
|
||||
{
|
||||
NTSTATUS handle_request(windows_emulator& win_emu, const lpc_request_context& c) override
|
||||
{
|
||||
// TODO: Fix this. This is broken and wrong.
|
||||
uint32_t server_dll_index{};
|
||||
win_emu.memory.read_memory(c.recv_buffer + 0x18, &server_dll_index, sizeof(server_dll_index));
|
||||
|
||||
if (server_dll_index != 3)
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -17,8 +23,13 @@ namespace
|
||||
const auto dest = data.read();
|
||||
const auto base = dest.Base;
|
||||
|
||||
const auto value = base + 0x10;
|
||||
win_emu.emu().write_memory(base + 8, &value, sizeof(value));
|
||||
const emulator_object<USER_SHAREDINFO> shared_obj{win_emu.emu(), base + 8};
|
||||
shared_obj.access([&](USER_SHAREDINFO& shared) {
|
||||
shared.psi = win_emu.process.user_handles.get_server_info().value();
|
||||
shared.aheList = win_emu.process.user_handles.get_handle_table().value();
|
||||
shared.HeEntrySize = sizeof(USER_HANDLEENTRY);
|
||||
shared.pDispInfo = win_emu.process.user_handles.get_display_info().value();
|
||||
});
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
||||
@@ -176,12 +176,40 @@ namespace
|
||||
|
||||
return env_map;
|
||||
}
|
||||
|
||||
uint32_t read_windows_build(registry_manager& registry)
|
||||
{
|
||||
const auto key = registry.get_key({R"(\Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion)"});
|
||||
|
||||
if (!key)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; const auto value = registry.get_value(*key, i); ++i)
|
||||
{
|
||||
if (value->type != REG_SZ)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value->name == "CurrentBuildNumber" || value->name == "CurrentBuild")
|
||||
{
|
||||
const auto* s = reinterpret_cast<const char16_t*>(value->data.data());
|
||||
return static_cast<uint32_t>(std::strtoul(u16_to_u8(s).c_str(), nullptr, 10));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void process_context::setup(x86_64_emulator& emu, memory_manager& memory, registry_manager& registry,
|
||||
const application_settings& app_settings, const mapped_module& executable, const mapped_module& ntdll,
|
||||
const apiset::container& apiset_container, const mapped_module* ntdll32)
|
||||
{
|
||||
this->windows_build_number = read_windows_build(registry);
|
||||
|
||||
setup_gdt(emu, memory);
|
||||
|
||||
this->kusd.setup();
|
||||
@@ -391,6 +419,94 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, regist
|
||||
this->instrumentation_callback = 0;
|
||||
|
||||
this->default_register_set = emu.save_registers();
|
||||
|
||||
this->user_handles.setup(is_wow64_process);
|
||||
|
||||
auto [h, monitor_obj] = this->user_handles.allocate_object<USER_MONITOR>(handle_types::monitor);
|
||||
this->default_monitor_handle = h;
|
||||
monitor_obj.access([&](USER_MONITOR& monitor) {
|
||||
monitor.hmon = h.bits;
|
||||
monitor.rcMonitor = {.left = 0, .top = 0, .right = 1920, .bottom = 1080};
|
||||
monitor.rcWork = monitor.rcMonitor;
|
||||
if (this->is_older_windows_build())
|
||||
{
|
||||
monitor.b20.monitorDpi = 96;
|
||||
monitor.b20.nativeDpi = monitor.b20.monitorDpi;
|
||||
monitor.b20.cachedDpi = monitor.b20.monitorDpi;
|
||||
monitor.b20.rcMonitorDpiAware = monitor.rcMonitor;
|
||||
}
|
||||
else
|
||||
{
|
||||
monitor.b26.monitorDpi = 96;
|
||||
monitor.b26.nativeDpi = monitor.b26.monitorDpi;
|
||||
}
|
||||
});
|
||||
|
||||
const auto user_display_info = this->user_handles.get_display_info();
|
||||
user_display_info.access([&](USER_DISPINFO& display_info) {
|
||||
display_info.dwMonitorCount = 1;
|
||||
display_info.pPrimaryMonitor = monitor_obj.value();
|
||||
});
|
||||
}
|
||||
|
||||
void process_context::setup_callback_hook(windows_emulator& win_emu, memory_manager& memory)
|
||||
{
|
||||
uint64_t sentinel_addr = this->callback_sentinel_addr;
|
||||
if (!sentinel_addr)
|
||||
{
|
||||
using sentinel_type = std::array<uint8_t, 2>;
|
||||
constexpr sentinel_type sentinel_opcodes{0x90, 0xC3}; // NOP, RET
|
||||
|
||||
auto sentinel_obj = this->base_allocator.reserve_page_aligned<sentinel_type>();
|
||||
sentinel_addr = sentinel_obj.value();
|
||||
this->callback_sentinel_addr = sentinel_addr;
|
||||
|
||||
win_emu.emu().write_memory(sentinel_addr, sentinel_opcodes.data(), sentinel_opcodes.size());
|
||||
|
||||
const auto sentinel_aligned_length = page_align_up(sentinel_addr + sentinel_opcodes.size()) - sentinel_addr;
|
||||
memory.protect_memory(sentinel_addr, static_cast<size_t>(sentinel_aligned_length), memory_permission::all);
|
||||
}
|
||||
|
||||
auto& emu = win_emu.emu();
|
||||
|
||||
emu.hook_memory_execution(sentinel_addr, [&](uint64_t) {
|
||||
auto* t = this->active_thread;
|
||||
|
||||
if (!t || t->callback_stack.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto frame = t->callback_stack.back();
|
||||
t->callback_stack.pop_back();
|
||||
|
||||
const auto callbacks_before = t->callback_stack.size();
|
||||
const uint64_t guest_result = emu.reg(x86_register::rax);
|
||||
|
||||
emu.reg(x86_register::rip, frame.rip);
|
||||
emu.reg(x86_register::rsp, frame.rsp);
|
||||
emu.reg(x86_register::r10, frame.r10);
|
||||
emu.reg(x86_register::rcx, frame.rcx);
|
||||
emu.reg(x86_register::rdx, frame.rdx);
|
||||
emu.reg(x86_register::r8, frame.r8);
|
||||
emu.reg(x86_register::r9, frame.r9);
|
||||
|
||||
win_emu.dispatcher.dispatch_completion(win_emu, frame.handler_id, guest_result);
|
||||
|
||||
uint64_t target_rip = emu.reg(x86_register::rip);
|
||||
emu.reg(x86_register::rip, this->callback_sentinel_addr + 1);
|
||||
|
||||
const bool new_callback_dispatched = t->callback_stack.size() > callbacks_before;
|
||||
if (!new_callback_dispatched)
|
||||
{
|
||||
// Move past the syscall instruction
|
||||
target_rip += 2;
|
||||
}
|
||||
|
||||
const uint64_t ret_stack_ptr = frame.rsp - sizeof(emulator_pointer);
|
||||
emu.write_memory(ret_stack_ptr, &target_rip, sizeof(target_rip));
|
||||
emu.reg(x86_register::rsp, ret_stack_ptr);
|
||||
});
|
||||
}
|
||||
|
||||
void process_context::serialize(utils::buffer_serializer& buffer) const
|
||||
@@ -408,6 +524,7 @@ void process_context::serialize(utils::buffer_serializer& buffer) const
|
||||
buffer.write(this->kusd);
|
||||
|
||||
buffer.write(this->is_wow64_process);
|
||||
buffer.write(this->windows_build_number);
|
||||
buffer.write(this->ntdll_image_base);
|
||||
buffer.write(this->ldr_initialize_thunk);
|
||||
buffer.write(this->rtl_user_thread_start);
|
||||
@@ -416,6 +533,8 @@ void process_context::serialize(utils::buffer_serializer& buffer) const
|
||||
buffer.write(this->ki_user_exception_dispatcher);
|
||||
buffer.write(this->instrumentation_callback);
|
||||
|
||||
buffer.write(this->user_handles);
|
||||
buffer.write(this->default_monitor_handle);
|
||||
buffer.write(this->events);
|
||||
buffer.write(this->files);
|
||||
buffer.write(this->sections);
|
||||
@@ -437,6 +556,8 @@ void process_context::serialize(utils::buffer_serializer& buffer) const
|
||||
buffer.write(this->threads);
|
||||
|
||||
buffer.write(this->threads.find_handle(this->active_thread).bits);
|
||||
|
||||
buffer.write(this->callback_sentinel_addr);
|
||||
}
|
||||
|
||||
void process_context::deserialize(utils::buffer_deserializer& buffer)
|
||||
@@ -454,6 +575,7 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
|
||||
buffer.read(this->kusd);
|
||||
|
||||
buffer.read(this->is_wow64_process);
|
||||
buffer.read(this->windows_build_number);
|
||||
buffer.read(this->ntdll_image_base);
|
||||
buffer.read(this->ldr_initialize_thunk);
|
||||
buffer.read(this->rtl_user_thread_start);
|
||||
@@ -462,6 +584,8 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
|
||||
buffer.read(this->ki_user_exception_dispatcher);
|
||||
buffer.read(this->instrumentation_callback);
|
||||
|
||||
buffer.read(this->user_handles);
|
||||
buffer.read(this->default_monitor_handle);
|
||||
buffer.read(this->events);
|
||||
buffer.read(this->files);
|
||||
buffer.read(this->sections);
|
||||
@@ -489,6 +613,8 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
|
||||
buffer.read(this->threads);
|
||||
|
||||
this->active_thread = this->threads.get(buffer.read<uint64_t>());
|
||||
|
||||
buffer.read(this->callback_sentinel_addr);
|
||||
}
|
||||
|
||||
generic_handle_store* process_context::get_handle_store(const handle handle)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "windows_objects.hpp"
|
||||
#include "emulator_thread.hpp"
|
||||
#include "port.hpp"
|
||||
#include "user_handle_table.hpp"
|
||||
|
||||
#include "apiset/apiset.hpp"
|
||||
|
||||
@@ -66,7 +67,8 @@ struct process_context
|
||||
base_allocator(emu),
|
||||
peb64(emu),
|
||||
process_params64(emu),
|
||||
kusd(memory, clock)
|
||||
kusd(memory, clock),
|
||||
user_handles(memory)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -74,6 +76,8 @@ struct process_context
|
||||
const mapped_module& executable, const mapped_module& ntdll, const apiset::container& apiset_container,
|
||||
const mapped_module* ntdll32 = nullptr);
|
||||
|
||||
void setup_callback_hook(windows_emulator& win_emu, memory_manager& memory);
|
||||
|
||||
handle create_thread(memory_manager& memory, uint64_t start_address, uint64_t argument, uint64_t stack_size, uint32_t create_flags,
|
||||
bool initial_thread = false);
|
||||
|
||||
@@ -86,10 +90,17 @@ struct process_context
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
|
||||
generic_handle_store* get_handle_store(handle handle);
|
||||
|
||||
// WOW64 support flag - set during process setup based on executable architecture
|
||||
bool is_wow64_process{false};
|
||||
|
||||
generic_handle_store* get_handle_store(handle handle);
|
||||
uint32_t windows_build_number{0};
|
||||
|
||||
bool is_older_windows_build() const
|
||||
{
|
||||
return windows_build_number < 26040;
|
||||
}
|
||||
|
||||
callbacks* callbacks_{};
|
||||
|
||||
@@ -118,6 +129,8 @@ struct process_context
|
||||
std::optional<emulator_object<RTL_USER_PROCESS_PARAMETERS32>> process_params32;
|
||||
std::optional<uint64_t> rtl_user_thread_start32{};
|
||||
|
||||
user_handle_table user_handles;
|
||||
handle default_monitor_handle{};
|
||||
handle_store<handle_types::event, event> events{};
|
||||
handle_store<handle_types::file, file> files{};
|
||||
handle_store<handle_types::section, section> sections{};
|
||||
@@ -125,7 +138,7 @@ struct process_context
|
||||
handle_store<handle_types::semaphore, semaphore> semaphores{};
|
||||
handle_store<handle_types::port, port_container> ports{};
|
||||
handle_store<handle_types::mutant, mutant> mutants{};
|
||||
handle_store<handle_types::window, window> windows{};
|
||||
user_handle_store<handle_types::window, window> windows{user_handles};
|
||||
handle_store<handle_types::timer, timer> timers{};
|
||||
handle_store<handle_types::registry, registry_key, 2> registry_keys{};
|
||||
std::map<uint16_t, atom_entry> atoms{};
|
||||
@@ -136,6 +149,8 @@ struct process_context
|
||||
handle_store<handle_types::thread, emulator_thread> threads{};
|
||||
emulator_thread* active_thread{nullptr};
|
||||
|
||||
emulator_pointer callback_sentinel_addr{0};
|
||||
|
||||
// Extended parameters from last NtMapViewOfSectionEx call
|
||||
// These can be used by other syscalls like NtAllocateVirtualMemoryEx
|
||||
uint64_t last_extended_params_numa_node{0};
|
||||
|
||||
@@ -22,6 +22,7 @@ void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read_map(this->handlers_);
|
||||
this->add_handlers();
|
||||
this->add_callbacks();
|
||||
}
|
||||
|
||||
void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const std::span<const std::byte> ntdll_data,
|
||||
@@ -36,6 +37,7 @@ void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const std:
|
||||
map_syscalls(this->handlers_, win32u_syscalls);
|
||||
|
||||
this->add_handlers();
|
||||
this->add_callbacks();
|
||||
}
|
||||
|
||||
void syscall_dispatcher::add_handlers()
|
||||
@@ -140,6 +142,41 @@ void syscall_dispatcher::dispatch_callback(windows_emulator& win_emu, std::strin
|
||||
}
|
||||
}
|
||||
|
||||
void syscall_dispatcher::dispatch_completion(windows_emulator& win_emu, callback_id callback_id, uint64_t guest_result)
|
||||
{
|
||||
auto& emu = win_emu.emu();
|
||||
const syscall_context c{
|
||||
.win_emu = win_emu,
|
||||
.emu = emu,
|
||||
.proc = win_emu.process,
|
||||
.write_status = true,
|
||||
};
|
||||
|
||||
const auto entry = this->callbacks_.find(callback_id);
|
||||
|
||||
if (entry == this->callbacks_.end())
|
||||
{
|
||||
win_emu.log.error("Unknown callback: 0x%X\n", static_cast<uint32_t>(callback_id));
|
||||
c.emu.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
entry->second(c, guest_result);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
win_emu.log.error("Callback 0x%X threw an exception - %s\n", static_cast<int>(callback_id), e.what());
|
||||
emu.stop();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
win_emu.log.error("Callback 0x%X threw an unknown exception\n", static_cast<int>(callback_id));
|
||||
emu.stop();
|
||||
}
|
||||
}
|
||||
|
||||
syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, const std::span<const std::byte> ntdll_data,
|
||||
const exported_symbols& win32u_exports, const std::span<const std::byte> win32u_data)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
struct syscall_context;
|
||||
using syscall_handler = void (*)(const syscall_context& c);
|
||||
using callback_completion_handler = void (*)(const syscall_context& c, uint64_t guest_result);
|
||||
|
||||
struct syscall_handler_entry
|
||||
{
|
||||
@@ -22,6 +23,7 @@ class syscall_dispatcher
|
||||
|
||||
void dispatch(windows_emulator& win_emu);
|
||||
static void dispatch_callback(windows_emulator& win_emu, std::string& syscall_name);
|
||||
void dispatch_completion(windows_emulator& win_emu, callback_id callback_id, uint64_t guest_result);
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
@@ -36,7 +38,9 @@ class syscall_dispatcher
|
||||
|
||||
private:
|
||||
std::map<uint64_t, syscall_handler_entry> handlers_{};
|
||||
std::map<callback_id, callback_completion_handler> callbacks_{};
|
||||
|
||||
static void add_handlers(std::map<std::string, syscall_handler>& handler_mapping);
|
||||
void add_handlers();
|
||||
void add_callbacks();
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ struct syscall_context
|
||||
process_context& proc;
|
||||
mutable bool write_status{true};
|
||||
mutable bool retrigger_syscall{false};
|
||||
mutable bool run_callback{false};
|
||||
};
|
||||
|
||||
inline uint64_t get_syscall_argument(x86_64_emulator& emu, const size_t index)
|
||||
@@ -132,15 +133,16 @@ T resolve_indexed_argument(x86_64_emulator& emu, size_t& index)
|
||||
return resolve_argument<T>(emu, index++);
|
||||
}
|
||||
|
||||
inline void write_syscall_result(const syscall_context& c, const uint64_t result, const uint64_t initial_ip)
|
||||
inline void write_syscall_result(const syscall_context& c, const uint64_t result, const uint64_t initial_ip,
|
||||
const bool is_callback_completion = false)
|
||||
{
|
||||
if (c.write_status && !c.retrigger_syscall)
|
||||
if (c.write_status && !c.retrigger_syscall && !c.run_callback)
|
||||
{
|
||||
c.emu.reg<uint64_t>(x86_register::rax, result);
|
||||
}
|
||||
|
||||
const auto new_ip = c.emu.read_instruction_pointer();
|
||||
if (initial_ip != new_ip || c.retrigger_syscall)
|
||||
if ((initial_ip != new_ip || c.retrigger_syscall || c.run_callback) && !is_callback_completion)
|
||||
{
|
||||
c.emu.reg(x86_register::rip, new_ip - 2);
|
||||
}
|
||||
@@ -176,6 +178,36 @@ syscall_handler make_syscall_handler()
|
||||
return +[](const syscall_context& c) { forward_syscall(c, Handler); };
|
||||
}
|
||||
|
||||
template <typename Result>
|
||||
void forward_callback_completion(const syscall_context& c, const uint64_t /*guest_result*/, Result (*handler)())
|
||||
{
|
||||
const auto ip = c.emu.read_instruction_pointer();
|
||||
|
||||
const auto ret = handler();
|
||||
write_syscall_result(c, static_cast<uint64_t>(ret), ip, true);
|
||||
}
|
||||
|
||||
template <typename Result, typename GuestResult, typename... Args>
|
||||
void forward_callback_completion(const syscall_context& c, const uint64_t guest_result,
|
||||
Result (*handler)(const syscall_context&, GuestResult, Args...))
|
||||
{
|
||||
const auto ip = c.emu.read_instruction_pointer();
|
||||
|
||||
size_t index = 0;
|
||||
std::tuple<const syscall_context&, GuestResult, Args...> func_args{
|
||||
c, static_cast<GuestResult>(guest_result),
|
||||
resolve_indexed_argument<std::remove_cv_t<std::remove_reference_t<Args>>>(c.emu, index)...};
|
||||
|
||||
const auto ret = std::apply(handler, std::move(func_args));
|
||||
write_syscall_result(c, ret, ip, true);
|
||||
}
|
||||
|
||||
template <auto Handler>
|
||||
callback_completion_handler make_callback_completion_handler()
|
||||
{
|
||||
return +[](const syscall_context& c, const uint64_t guest_result) { forward_callback_completion(c, guest_result, Handler); };
|
||||
}
|
||||
|
||||
template <typename T, typename Traits>
|
||||
void write_attribute(emulator& emu, const PS_ATTRIBUTE<Traits>& attribute, const T& value)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "cpu_context.hpp"
|
||||
#include "emulator_utils.hpp"
|
||||
#include "syscall_utils.hpp"
|
||||
#include "user_callback_dispatch.hpp"
|
||||
|
||||
#include <numeric>
|
||||
#include <cwctype>
|
||||
@@ -155,8 +156,11 @@ namespace syscalls
|
||||
NTSTATUS handle_NtQueryObject(const syscall_context& c, handle handle, OBJECT_INFORMATION_CLASS object_information_class,
|
||||
emulator_pointer object_information, ULONG object_information_length,
|
||||
emulator_object<ULONG> return_length);
|
||||
NTSTATUS handle_NtCompareObjects(const syscall_context& c, handle first, handle second);
|
||||
NTSTATUS handle_NtWaitForMultipleObjects(const syscall_context& c, ULONG count, emulator_object<handle> handles, WAIT_TYPE wait_type,
|
||||
BOOLEAN alertable, emulator_object<LARGE_INTEGER> timeout);
|
||||
NTSTATUS handle_NtWaitForMultipleObjects32(const syscall_context& c, ULONG count, emulator_object<uint32_t> handles,
|
||||
WAIT_TYPE wait_type, BOOLEAN alertable, emulator_object<LARGE_INTEGER> timeout);
|
||||
NTSTATUS handle_NtWaitForSingleObject(const syscall_context& c, handle h, BOOLEAN alertable, emulator_object<LARGE_INTEGER> timeout);
|
||||
NTSTATUS handle_NtSetInformationObject();
|
||||
NTSTATUS handle_NtQuerySecurityObject(const syscall_context& c, handle /*h*/, SECURITY_INFORMATION /*security_information*/,
|
||||
@@ -230,6 +234,9 @@ namespace syscalls
|
||||
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> value_name,
|
||||
KEY_VALUE_INFORMATION_CLASS key_value_information_class, uint64_t key_value_information, ULONG length,
|
||||
emulator_object<ULONG> result_length);
|
||||
NTSTATUS handle_NtQueryMultipleValueKey(const syscall_context& c, handle key_handle, emulator_object<KEY_VALUE_ENTRY> value_entries,
|
||||
ULONG entry_count, uint64_t value_buffer, emulator_object<ULONG> buffer_length,
|
||||
emulator_object<ULONG> required_buffer_length);
|
||||
NTSTATUS handle_NtCreateKey(const syscall_context& c, emulator_object<handle> key_handle, ACCESS_MASK desired_access,
|
||||
emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes, ULONG /*title_index*/,
|
||||
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> /*class*/, ULONG /*create_options*/,
|
||||
@@ -466,7 +473,7 @@ namespace syscalls
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (auto* e = c.win_emu.process.events.get(event))
|
||||
if (auto* e = c.proc.events.get(event))
|
||||
{
|
||||
e->signaled = false;
|
||||
}
|
||||
@@ -834,7 +841,7 @@ namespace syscalls
|
||||
const hwnd /*parent*/, const hmenu /*menu*/, const hinstance /*instance*/, const pointer /*l_param*/,
|
||||
const DWORD /*flags*/, const pointer /*acbi_buffer*/)
|
||||
{
|
||||
window win{};
|
||||
auto [handle, win] = c.proc.windows.create(c.win_emu.memory);
|
||||
win.x = x;
|
||||
win.y = y;
|
||||
win.width = width;
|
||||
@@ -843,7 +850,7 @@ namespace syscalls
|
||||
win.class_name = read_large_string(class_name);
|
||||
win.name = read_large_string(window_name);
|
||||
|
||||
return c.proc.windows.store(std::move(win)).bits;
|
||||
return handle.bits;
|
||||
}
|
||||
|
||||
BOOL handle_NtUserDestroyWindow(const syscall_context& c, const hwnd window)
|
||||
@@ -1001,6 +1008,47 @@ namespace syscalls
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
BOOL handle_NtUserEnumDisplayMonitors(const syscall_context& c, const hdc hdc_in, const uint64_t clip_rect_ptr, const uint64_t callback,
|
||||
const uint64_t param)
|
||||
{
|
||||
if (!callback)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const auto hmon = c.win_emu.process.default_monitor_handle.bits;
|
||||
const auto display_info = c.proc.user_handles.get_display_info().read();
|
||||
|
||||
if (clip_rect_ptr)
|
||||
{
|
||||
RECT clip{};
|
||||
c.emu.read_memory(clip_rect_ptr, &clip, sizeof(clip));
|
||||
|
||||
const emulator_object<USER_MONITOR> monitor_obj(c.emu, display_info.pPrimaryMonitor);
|
||||
const auto monitor = monitor_obj.read();
|
||||
|
||||
auto effective_rc{monitor.rcMonitor};
|
||||
effective_rc.left = std::max(effective_rc.left, clip.left);
|
||||
effective_rc.top = std::max(effective_rc.top, clip.top);
|
||||
effective_rc.right = std::min(effective_rc.right, clip.right);
|
||||
effective_rc.bottom = std::min(effective_rc.bottom, clip.bottom);
|
||||
if (effective_rc.right <= effective_rc.left || effective_rc.bottom <= effective_rc.top)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
const uint64_t rect_ptr = display_info.pPrimaryMonitor + offsetof(USER_MONITOR, rcMonitor);
|
||||
dispatch_user_callback(c, callback_id::NtUserEnumDisplayMonitors, callback, hmon, hdc_in, rect_ptr, param);
|
||||
return {};
|
||||
}
|
||||
|
||||
BOOL completion_NtUserEnumDisplayMonitors(const syscall_context&, BOOL guest_result, const hdc /*hdc_in*/,
|
||||
const uint64_t /*clip_rect_ptr*/, const uint64_t /*callback*/, const uint64_t /*param*/)
|
||||
{
|
||||
return guest_result;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtAssociateWaitCompletionPacket()
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
@@ -1030,6 +1078,32 @@ namespace syscalls
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
BOOL handle_NtUserGetHDevName(const syscall_context& c, handle hdev, emulator_pointer device_name)
|
||||
{
|
||||
if (hdev != c.proc.default_monitor_handle)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const std::u16string name = u"\\\\.\\DISPLAY1";
|
||||
c.emu.write_memory(device_name, name.c_str(), (name.size() + 1) * sizeof(char16_t));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
emulator_pointer handle_NtUserMapDesktopObject(const syscall_context& c, handle handle)
|
||||
{
|
||||
const auto index = handle.value.id;
|
||||
|
||||
if (index == 0 || index >= user_handle_table::MAX_HANDLES)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto handle_entry = c.proc.user_handles.get_handle_table().read(static_cast<size_t>(index));
|
||||
return handle_entry.pHead;
|
||||
}
|
||||
}
|
||||
|
||||
void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& handler_mapping)
|
||||
@@ -1143,6 +1217,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
|
||||
add_handler(NtSetInformationFile);
|
||||
add_handler(NtUserRegisterWindowMessage);
|
||||
add_handler(NtQueryValueKey);
|
||||
add_handler(NtQueryMultipleValueKey);
|
||||
add_handler(NtQueryKey);
|
||||
add_handler(NtGetNlsSectionPtr);
|
||||
add_handler(NtAccessCheck);
|
||||
@@ -1151,8 +1226,10 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
|
||||
add_handler(NtGetCurrentProcessorNumberEx);
|
||||
add_handler(NtGetCurrentProcessorNumber);
|
||||
add_handler(NtQueryObject);
|
||||
add_handler(NtCompareObjects);
|
||||
add_handler(NtQueryAttributesFile);
|
||||
add_handler(NtWaitForMultipleObjects);
|
||||
add_handler(NtWaitForMultipleObjects32);
|
||||
add_handler(NtCreateMutant);
|
||||
add_handler(NtReleaseMutant);
|
||||
add_handler(NtDuplicateToken);
|
||||
@@ -1216,6 +1293,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
|
||||
add_handler(NtUserGetKeyboardType);
|
||||
add_handler(NtUserEnumDisplayDevices);
|
||||
add_handler(NtUserEnumDisplaySettings);
|
||||
add_handler(NtUserEnumDisplayMonitors);
|
||||
add_handler(NtUserSetProp);
|
||||
add_handler(NtUserSetProp2);
|
||||
add_handler(NtUserChangeWindowMessageFilterEx);
|
||||
@@ -1243,6 +1321,21 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
|
||||
add_handler(NtSetInformationDebugObject);
|
||||
add_handler(NtRemoveProcessDebug);
|
||||
add_handler(NtNotifyChangeDirectoryFileEx);
|
||||
add_handler(NtUserGetHDevName);
|
||||
add_handler(NtUserMapDesktopObject);
|
||||
|
||||
#undef add_handler
|
||||
}
|
||||
|
||||
void syscall_dispatcher::add_callbacks()
|
||||
{
|
||||
#define add_callback(syscall) \
|
||||
do \
|
||||
{ \
|
||||
this->callbacks_[callback_id::syscall] = make_callback_completion_handler<syscalls::completion_##syscall>(); \
|
||||
} while (0)
|
||||
|
||||
add_callback(NtUserEnumDisplayMonitors);
|
||||
|
||||
#undef add_callback
|
||||
}
|
||||
|
||||
@@ -267,6 +267,11 @@ namespace syscalls
|
||||
|| h.value.type == handle_types::event;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtCompareObjects(const syscall_context&, const handle first, const handle second)
|
||||
{
|
||||
return (first == second) ? STATUS_SUCCESS : STATUS_NOT_SAME_OBJECT;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtWaitForMultipleObjects(const syscall_context& c, const ULONG count, const emulator_object<handle> handles,
|
||||
const WAIT_TYPE wait_type, const BOOLEAN alertable,
|
||||
const emulator_object<LARGE_INTEGER> timeout)
|
||||
@@ -304,6 +309,44 @@ namespace syscalls
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtWaitForMultipleObjects32(const syscall_context& c, const ULONG count, const emulator_object<uint32_t> handles,
|
||||
const WAIT_TYPE wait_type, const BOOLEAN alertable,
|
||||
const emulator_object<LARGE_INTEGER> timeout)
|
||||
{
|
||||
if (wait_type != WaitAny && wait_type != WaitAll)
|
||||
{
|
||||
c.win_emu.log.error("Wait type not supported!\n");
|
||||
c.emu.stop();
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
auto& t = c.win_emu.current_thread();
|
||||
t.await_objects.clear();
|
||||
t.await_any = wait_type == WaitAny;
|
||||
|
||||
for (ULONG i = 0; i < count; ++i)
|
||||
{
|
||||
const auto raw_handle = handles.read(i);
|
||||
const auto h = make_handle(static_cast<uint64_t>(raw_handle));
|
||||
|
||||
if (!is_awaitable_object_type(h))
|
||||
{
|
||||
c.win_emu.log.warn("Unsupported handle type for NtWaitForMultipleObjects32: %d!\n", h.value.type);
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
t.await_objects.push_back(h);
|
||||
}
|
||||
|
||||
if (timeout.value() && !t.await_time.has_value())
|
||||
{
|
||||
t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), timeout.read());
|
||||
}
|
||||
|
||||
c.win_emu.yield_thread(alertable);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtWaitForSingleObject(const syscall_context& c, const handle h, const BOOLEAN alertable,
|
||||
const emulator_object<LARGE_INTEGER> timeout)
|
||||
{
|
||||
|
||||
@@ -261,6 +261,86 @@ namespace syscalls
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtQueryMultipleValueKey(const syscall_context& c, const handle key_handle,
|
||||
const emulator_object<KEY_VALUE_ENTRY> value_entries, const ULONG entry_count,
|
||||
const uint64_t value_buffer, const emulator_object<ULONG> buffer_length,
|
||||
const emulator_object<ULONG> required_buffer_length)
|
||||
{
|
||||
if (entry_count > 0x10000)
|
||||
{
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
const auto* key = c.proc.registry_keys.get(key_handle);
|
||||
if (!key)
|
||||
{
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
auto remaining_length = buffer_length.read();
|
||||
ULONG required_length = 0;
|
||||
ULONG written_bytes = 0;
|
||||
|
||||
for (ULONG i = 0; i < entry_count; i++)
|
||||
{
|
||||
auto entry = value_entries.read(i);
|
||||
if (!entry.ValueName)
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto query_name = read_unicode_string(c.emu, entry.ValueName);
|
||||
|
||||
if (c.win_emu.callbacks.on_generic_access)
|
||||
{
|
||||
// TODO: Find a better way to log this
|
||||
c.win_emu.callbacks.on_generic_access("Querying multiple value key ", query_name + u" (" + key->to_string() + u")");
|
||||
}
|
||||
|
||||
const auto value = c.win_emu.registry.get_value(*key, u16_to_u8(query_name));
|
||||
if (!value)
|
||||
{
|
||||
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto data_length = static_cast<ULONG>(value->data.size());
|
||||
|
||||
if (status == STATUS_SUCCESS)
|
||||
{
|
||||
if (remaining_length >= data_length)
|
||||
{
|
||||
entry.DataOffset = written_bytes;
|
||||
entry.DataLength = data_length;
|
||||
entry.Type = value->type;
|
||||
|
||||
c.emu.write_memory(value_buffer + entry.DataOffset, value->data.data(), entry.DataLength);
|
||||
value_entries.write(entry, i);
|
||||
|
||||
remaining_length -= data_length;
|
||||
written_bytes += data_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
required_length += data_length;
|
||||
}
|
||||
|
||||
buffer_length.write(written_bytes);
|
||||
|
||||
if (required_buffer_length.value())
|
||||
{
|
||||
required_buffer_length.write(required_length);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtCreateKey(const syscall_context& c, const emulator_object<handle> key_handle, const ACCESS_MASK desired_access,
|
||||
const emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes,
|
||||
const ULONG /*title_index*/, const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> /*class*/,
|
||||
|
||||
48
src/windows-emulator/user_callback_dispatch.hpp
Normal file
48
src/windows-emulator/user_callback_dispatch.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "syscall_utils.hpp"
|
||||
|
||||
// TODO: Here we are calling guest functions directly, but this is not how it works in the real Windows kernel.
|
||||
// In the real implementation, the kernel invokes ntdll!KiUserCallbackDispatcher and passes a callback
|
||||
// index that refers to an entry in PEB->KernelCallbackTable. The dispatcher then looks up the function
|
||||
// pointer in that table and invokes the corresponding user-mode callback.
|
||||
|
||||
template <typename... Args>
|
||||
void prepare_call_stack(x86_64_emulator& emu, uint64_t return_address, Args... args)
|
||||
{
|
||||
constexpr size_t arg_count = sizeof...(Args);
|
||||
const size_t stack_args_size = aligned_stack_space(arg_count);
|
||||
|
||||
const uint64_t current_rsp = emu.read_stack_pointer();
|
||||
const uint64_t aligned_rsp = align_down(current_rsp, 16);
|
||||
|
||||
// We subtract the args size (including the shadow space) AND the size of the return address
|
||||
const uint64_t new_rsp = aligned_rsp - stack_args_size - sizeof(emulator_pointer);
|
||||
emu.reg(x86_register::rsp, new_rsp);
|
||||
|
||||
emu.write_memory(new_rsp, &return_address, sizeof(return_address));
|
||||
|
||||
size_t index = 0;
|
||||
(set_function_argument(emu, index++, static_cast<uint64_t>(args)), ...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void dispatch_user_callback(const syscall_context& c, callback_id completion_id, uint64_t func_address, Args... args)
|
||||
{
|
||||
const callback_frame frame{
|
||||
.handler_id = completion_id,
|
||||
.rip = c.emu.read_instruction_pointer(),
|
||||
.rsp = c.emu.read_stack_pointer(),
|
||||
.r10 = c.emu.reg(x86_register::r10),
|
||||
.rcx = c.emu.reg(x86_register::rcx),
|
||||
.rdx = c.emu.reg(x86_register::rdx),
|
||||
.r8 = c.emu.reg(x86_register::r8),
|
||||
.r9 = c.emu.reg(x86_register::r9),
|
||||
};
|
||||
c.proc.active_thread->callback_stack.push_back(frame);
|
||||
|
||||
prepare_call_stack(c.emu, c.proc.callback_sentinel_addr, args...);
|
||||
|
||||
c.emu.reg(x86_register::rip, func_address);
|
||||
c.run_callback = true;
|
||||
}
|
||||
406
src/windows-emulator/user_handle_table.hpp
Normal file
406
src/windows-emulator/user_handle_table.hpp
Normal file
@@ -0,0 +1,406 @@
|
||||
#pragma once
|
||||
#include "emulator_utils.hpp"
|
||||
#include "handles.hpp"
|
||||
|
||||
class user_handle_table
|
||||
{
|
||||
public:
|
||||
static constexpr uint32_t MAX_HANDLES = 0xFFFF;
|
||||
|
||||
user_handle_table(memory_manager& memory)
|
||||
: memory_(&memory)
|
||||
{
|
||||
}
|
||||
|
||||
void setup(const bool is_wow64_process)
|
||||
{
|
||||
this->is_wow64_process_ = is_wow64_process;
|
||||
|
||||
used_indices_.resize(MAX_HANDLES, false);
|
||||
|
||||
const auto server_info_size = static_cast<size_t>(page_align_up(sizeof(USER_SERVERINFO)));
|
||||
server_info_addr_ = this->allocate_memory(server_info_size, memory_permission::read);
|
||||
|
||||
const auto display_info_size = static_cast<size_t>(page_align_up(sizeof(USER_DISPINFO)));
|
||||
display_info_addr_ = this->allocate_memory(display_info_size, memory_permission::read);
|
||||
|
||||
const emulator_object<USER_SERVERINFO> srv_obj(*memory_, server_info_addr_);
|
||||
srv_obj.access([&](USER_SERVERINFO& srv) {
|
||||
srv.cHandleEntries = MAX_HANDLES - 1; //
|
||||
});
|
||||
|
||||
const auto handle_table_size = static_cast<size_t>(page_align_up(sizeof(USER_HANDLEENTRY) * MAX_HANDLES));
|
||||
handle_table_addr_ = this->allocate_memory(handle_table_size, memory_permission::read);
|
||||
}
|
||||
|
||||
emulator_object<USER_SHAREDINFO> get_server_info() const
|
||||
{
|
||||
return {*memory_, server_info_addr_};
|
||||
}
|
||||
|
||||
emulator_object<USER_HANDLEENTRY> get_handle_table() const
|
||||
{
|
||||
return {*memory_, handle_table_addr_};
|
||||
}
|
||||
|
||||
emulator_object<USER_DISPINFO> get_display_info() const
|
||||
{
|
||||
return {*memory_, display_info_addr_};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::pair<handle, emulator_object<T>> allocate_object(handle_types::type type)
|
||||
{
|
||||
const auto index = find_free_index();
|
||||
|
||||
const auto alloc_size = static_cast<size_t>(page_align_up(sizeof(T)));
|
||||
const auto alloc_ptr = this->allocate_memory(alloc_size, memory_permission::read);
|
||||
const emulator_object<T> alloc_obj(*memory_, alloc_ptr);
|
||||
|
||||
const emulator_object<USER_HANDLEENTRY> handle_table_obj(*memory_, handle_table_addr_);
|
||||
handle_table_obj.access(
|
||||
[&](USER_HANDLEENTRY& entry) {
|
||||
entry.pHead = alloc_ptr;
|
||||
entry.bType = get_native_type(type);
|
||||
entry.wUniq = static_cast<uint16_t>(type << 7);
|
||||
},
|
||||
index);
|
||||
|
||||
used_indices_[index] = true;
|
||||
|
||||
return {make_handle(index, type, false), alloc_obj};
|
||||
}
|
||||
|
||||
void free_index(uint32_t index)
|
||||
{
|
||||
if (index >= used_indices_.size() || !used_indices_[index])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
used_indices_[index] = false;
|
||||
|
||||
const emulator_object<USER_HANDLEENTRY> handle_table_obj(*memory_, handle_table_addr_);
|
||||
handle_table_obj.access(
|
||||
[&](USER_HANDLEENTRY& entry) {
|
||||
memory_->release_memory(entry.pHead, 0);
|
||||
entry = {};
|
||||
},
|
||||
index);
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(server_info_addr_);
|
||||
buffer.write(handle_table_addr_);
|
||||
buffer.write(display_info_addr_);
|
||||
buffer.write_vector(used_indices_);
|
||||
buffer.write(is_wow64_process_);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(server_info_addr_);
|
||||
buffer.read(handle_table_addr_);
|
||||
buffer.read(display_info_addr_);
|
||||
buffer.read_vector(used_indices_);
|
||||
buffer.read(is_wow64_process_);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t find_free_index() const
|
||||
{
|
||||
for (uint32_t i = 1; i < used_indices_.size(); ++i)
|
||||
{
|
||||
if (!used_indices_[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("No more user handles available");
|
||||
}
|
||||
|
||||
static uint8_t get_native_type(handle_types::type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case handle_types::type::window:
|
||||
return TYPE_WINDOW;
|
||||
case handle_types::type::monitor:
|
||||
return TYPE_MONITOR;
|
||||
default:
|
||||
throw std::runtime_error("Unhandled handle type!");
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t allocate_memory(const size_t size, const nt_memory_permission permissions)
|
||||
{
|
||||
const auto allocation_base = this->is_wow64_process_ ? DEFAULT_ALLOCATION_ADDRESS_32BIT : DEFAULT_ALLOCATION_ADDRESS_64BIT;
|
||||
const auto base = memory_->find_free_allocation_base(size, allocation_base);
|
||||
return memory_->allocate_memory(size, permissions, false, base);
|
||||
}
|
||||
|
||||
uint64_t server_info_addr_{};
|
||||
uint64_t handle_table_addr_{};
|
||||
uint64_t display_info_addr_{};
|
||||
std::vector<bool> used_indices_{};
|
||||
memory_manager* memory_{};
|
||||
bool is_wow64_process_{};
|
||||
};
|
||||
|
||||
template <handle_types::type Type, typename T>
|
||||
requires(utils::Serializable<T> && std::is_base_of_v<ref_counted_object, T>)
|
||||
class user_handle_store : public generic_handle_store
|
||||
{
|
||||
public:
|
||||
using index_type = uint32_t;
|
||||
using value_map = std::map<index_type, T>;
|
||||
|
||||
explicit user_handle_store(user_handle_table& table)
|
||||
: table_(&table)
|
||||
{
|
||||
}
|
||||
|
||||
std::pair<handle, T&> create(memory_interface& memory)
|
||||
{
|
||||
if (this->block_mutation_)
|
||||
{
|
||||
throw std::runtime_error("Mutation of user object store is blocked!");
|
||||
}
|
||||
|
||||
auto [h, guest_obj] = table_->allocate_object<typename T::guest_type>(Type);
|
||||
|
||||
T new_obj(memory);
|
||||
new_obj.guest = std::move(guest_obj);
|
||||
|
||||
const auto index = static_cast<uint32_t>(h.value.id);
|
||||
const auto it = this->store_.emplace(index, std::move(new_obj)).first;
|
||||
return {h, it->second};
|
||||
}
|
||||
|
||||
bool block_mutation(bool blocked)
|
||||
{
|
||||
std::swap(this->block_mutation_, blocked);
|
||||
return blocked;
|
||||
}
|
||||
|
||||
handle make_handle(const index_type index) const
|
||||
{
|
||||
handle h{};
|
||||
h.bits = 0;
|
||||
h.value.is_pseudo = false;
|
||||
h.value.type = Type;
|
||||
h.value.id = index;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
T* get_by_index(const uint32_t index)
|
||||
{
|
||||
const auto it = this->store_.find(index);
|
||||
if (it == this->store_.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
T* get(const handle_value h)
|
||||
{
|
||||
if (h.type != Type || h.is_pseudo)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return this->get_by_index(static_cast<uint32_t>(h.id));
|
||||
}
|
||||
|
||||
T* get(const handle h)
|
||||
{
|
||||
return this->get(h.value);
|
||||
}
|
||||
|
||||
T* get(const uint64_t h)
|
||||
{
|
||||
handle hh{};
|
||||
hh.bits = h;
|
||||
return this->get(hh);
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return this->store_.size();
|
||||
}
|
||||
|
||||
std::optional<handle> duplicate(const handle h) override
|
||||
{
|
||||
auto* entry = this->get(h);
|
||||
if (!entry)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
++entry->ref_count;
|
||||
return h;
|
||||
}
|
||||
|
||||
std::pair<typename value_map::iterator, bool> erase(const typename value_map::iterator& entry)
|
||||
{
|
||||
if (this->block_mutation_)
|
||||
{
|
||||
throw std::runtime_error("Mutation of handle store is blocked!");
|
||||
}
|
||||
|
||||
if (entry == this->store_.end())
|
||||
{
|
||||
return {entry, false};
|
||||
}
|
||||
|
||||
if constexpr (handle_detail::has_deleter_function<T>())
|
||||
{
|
||||
if (!T::deleter(entry->second))
|
||||
{
|
||||
return {entry, true};
|
||||
}
|
||||
}
|
||||
|
||||
auto new_iter = this->store_.erase(entry);
|
||||
return {new_iter, true};
|
||||
}
|
||||
|
||||
bool erase(const handle_value h)
|
||||
{
|
||||
if (this->block_mutation_)
|
||||
{
|
||||
throw std::runtime_error("Mutation of user object store is blocked!");
|
||||
}
|
||||
|
||||
if (h.type != Type || h.is_pseudo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto index = static_cast<uint32_t>(h.id);
|
||||
const auto entry = this->store_.find(index);
|
||||
|
||||
if (entry == this->store_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (handle_detail::has_deleter_function<T>())
|
||||
{
|
||||
if (!T::deleter(entry->second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
table_->free_index(index);
|
||||
this->store_.erase(entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool erase(const handle h) override
|
||||
{
|
||||
return this->erase(h.value);
|
||||
}
|
||||
|
||||
bool erase(const uint64_t h)
|
||||
{
|
||||
handle hh{};
|
||||
hh.bits = h;
|
||||
return this->erase(hh);
|
||||
}
|
||||
|
||||
bool erase(const T& value)
|
||||
{
|
||||
const auto entry = this->find(value);
|
||||
if (entry == this->store_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->erase(make_handle(entry->first));
|
||||
}
|
||||
|
||||
typename value_map::iterator find(const T& value)
|
||||
{
|
||||
auto i = this->store_.begin();
|
||||
for (; i != this->store_.end(); ++i)
|
||||
{
|
||||
if (&i->second == &value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
typename value_map::const_iterator find(const T& value) const
|
||||
{
|
||||
auto i = this->store_.begin();
|
||||
for (; i != this->store_.end(); ++i)
|
||||
{
|
||||
if (&i->second == &value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
handle find_handle(const T& value) const
|
||||
{
|
||||
const auto entry = this->find(value);
|
||||
if (entry == this->end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return this->make_handle(entry->first);
|
||||
}
|
||||
|
||||
handle find_handle(const T* value) const
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return this->find_handle(*value);
|
||||
}
|
||||
|
||||
typename value_map::iterator begin()
|
||||
{
|
||||
return this->store_.begin();
|
||||
}
|
||||
typename value_map::const_iterator begin() const
|
||||
{
|
||||
return this->store_.begin();
|
||||
}
|
||||
typename value_map::iterator end()
|
||||
{
|
||||
return this->store_.end();
|
||||
}
|
||||
typename value_map::const_iterator end() const
|
||||
{
|
||||
return this->store_.end();
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->block_mutation_);
|
||||
buffer.write_map(this->store_);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->block_mutation_);
|
||||
buffer.read_map(this->store_);
|
||||
}
|
||||
|
||||
private:
|
||||
user_handle_table* table_;
|
||||
bool block_mutation_{false};
|
||||
value_map store_{};
|
||||
};
|
||||
@@ -554,6 +554,7 @@ void windows_emulator::start(size_t count)
|
||||
{
|
||||
this->should_stop = false;
|
||||
this->setup_process_if_necessary();
|
||||
this->process.setup_callback_hook(*this, this->memory);
|
||||
|
||||
const auto use_count = count > 0;
|
||||
const auto start_instructions = this->executed_instructions_;
|
||||
|
||||
@@ -54,19 +54,47 @@ struct event : ref_counted_object
|
||||
}
|
||||
};
|
||||
|
||||
struct window : ref_counted_object
|
||||
template <typename GuestType>
|
||||
struct user_object : ref_counted_object
|
||||
{
|
||||
using guest_type = GuestType;
|
||||
emulator_object<GuestType> guest;
|
||||
|
||||
user_object(memory_interface& memory)
|
||||
: guest(memory)
|
||||
{
|
||||
}
|
||||
|
||||
void serialize_object(utils::buffer_serializer& buffer) const override
|
||||
{
|
||||
buffer.write(this->guest);
|
||||
}
|
||||
|
||||
void deserialize_object(utils::buffer_deserializer& buffer) override
|
||||
{
|
||||
buffer.read(this->guest);
|
||||
}
|
||||
};
|
||||
|
||||
struct window : user_object<USER_WINDOW>
|
||||
{
|
||||
uint32_t thread_id{};
|
||||
std::u16string name{};
|
||||
std::u16string class_name{};
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t width{};
|
||||
int32_t height{};
|
||||
int32_t x{};
|
||||
int32_t y{};
|
||||
std::unordered_map<std::u16string, uint64_t> props;
|
||||
|
||||
window(memory_interface& memory)
|
||||
: user_object(memory)
|
||||
{
|
||||
}
|
||||
|
||||
void serialize_object(utils::buffer_serializer& buffer) const override
|
||||
{
|
||||
user_object::serialize_object(buffer);
|
||||
buffer.write(this->thread_id);
|
||||
buffer.write(this->name);
|
||||
buffer.write(this->class_name);
|
||||
@@ -79,6 +107,7 @@ struct window : ref_counted_object
|
||||
|
||||
void deserialize_object(utils::buffer_deserializer& buffer) override
|
||||
{
|
||||
user_object::deserialize_object(buffer);
|
||||
buffer.read(this->thread_id);
|
||||
buffer.read(this->name);
|
||||
buffer.read(this->class_name);
|
||||
@@ -186,7 +215,6 @@ struct file : ref_counted_object
|
||||
utils::file_handle handle{};
|
||||
std::u16string name{};
|
||||
std::optional<file_enumeration_state> enumeration_state{};
|
||||
std::optional<std::u16string> deferred_rename;
|
||||
|
||||
bool is_file() const
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user