mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Compare commits
61 Commits
72a5bc0a42
...
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 | ||
|
|
dd56acc2b4 | ||
|
|
3e7813b269 | ||
|
|
cf2dacb304 | ||
|
|
7112d619b4 | ||
|
|
ce73ef2b6c | ||
|
|
5302900a9d | ||
|
|
e17204552f | ||
|
|
f218ec5d1d | ||
|
|
a3d019c759 | ||
|
|
2b0277cf6b | ||
|
|
ec0d626a96 | ||
|
|
3b68260921 | ||
|
|
f524a8cc38 | ||
|
|
497a4e98cb | ||
|
|
e396192fbb | ||
|
|
0688a2cb98 | ||
|
|
600d12f983 | ||
|
|
ea1bbb8b82 | ||
|
|
6943b4369a | ||
|
|
29b8ec4072 | ||
|
|
b00f40aba7 | ||
|
|
de491ade0e | ||
|
|
0de53515ed | ||
|
|
818a4a4b88 | ||
|
|
f6f87bf23d | ||
|
|
7724b0d416 | ||
|
|
9bdb5c9562 | ||
|
|
b9df0682ad | ||
|
|
254475e308 |
@@ -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
|
||||
@@ -586,10 +592,10 @@ typedef struct _PEB64
|
||||
ULONG NtGlobalFlag;
|
||||
|
||||
ULARGE_INTEGER CriticalSectionTimeout;
|
||||
EMULATOR_CAST(std::int64_t, SIZE_T) HeapSegmentReserve;
|
||||
EMULATOR_CAST(std::int64_t, SIZE_T) HeapSegmentCommit;
|
||||
EMULATOR_CAST(std::int64_t, SIZE_T) HeapDeCommitTotalFreeThreshold;
|
||||
EMULATOR_CAST(std::int64_t, SIZE_T) HeapDeCommitFreeBlockThreshold;
|
||||
EMULATOR_CAST(std::uint64_t, SIZE_T) HeapSegmentReserve;
|
||||
EMULATOR_CAST(std::uint64_t, SIZE_T) HeapSegmentCommit;
|
||||
EMULATOR_CAST(std::uint64_t, SIZE_T) HeapDeCommitTotalFreeThreshold;
|
||||
EMULATOR_CAST(std::uint64_t, SIZE_T) HeapDeCommitFreeBlockThreshold;
|
||||
|
||||
ULONG NumberOfHeaps;
|
||||
ULONG MaximumNumberOfHeaps;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#define MEM_PRIVATE 0x00020000
|
||||
#define MEM_MAPPED 0x00040000
|
||||
#define MEM_RESET 0x00080000
|
||||
#define MEM_IMAGE 0x01000000
|
||||
#define MEM_TOP_DOWN 0x00100000
|
||||
#define MEM_WRITE_WATCH 0x00200000
|
||||
#define MEM_PHYSICAL 0x00400000
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -11,6 +11,7 @@ using NTSTATUS = std::uint32_t;
|
||||
#define STATUS_PENDING ((NTSTATUS)0x00000103L)
|
||||
|
||||
#define STATUS_GUARD_PAGE_VIOLATION ((NTSTATUS)0x80000001L)
|
||||
#define STATUS_DATATYPE_MISALIGNMENT ((NTSTATUS)0x80000002L)
|
||||
#define STATUS_BREAKPOINT ((NTSTATUS)0x80000003L)
|
||||
#define STATUS_SINGLE_STEP ((NTSTATUS)0x80000004L)
|
||||
|
||||
@@ -23,16 +24,22 @@ using NTSTATUS = std::uint32_t;
|
||||
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||
#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
|
||||
|
||||
#define STATUS_ALERTED ((NTSTATUS)0x00000101L)
|
||||
#define STATUS_PIPE_LISTENING ((NTSTATUS)0x00000105L)
|
||||
#define STATUS_PIPE_CONNECTED ((NTSTATUS)0x00000106L)
|
||||
|
||||
#define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L)
|
||||
#define STATUS_SERVICE_NOTIFICATION ((NTSTATUS)0x40000018L)
|
||||
|
||||
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
|
||||
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
|
||||
#define STATUS_NO_MORE_ENTRIES ((NTSTATUS)0x8000001AL)
|
||||
|
||||
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
|
||||
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
|
||||
#define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018L)
|
||||
#define STATUS_NOT_MAPPED_VIEW ((NTSTATUS)0xC0000019L)
|
||||
#define STATUS_UNABLE_TO_DELETE_SECTION ((NTSTATUS)0xC000001BL)
|
||||
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
|
||||
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
|
||||
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
|
||||
@@ -40,35 +47,44 @@ using NTSTATUS = std::uint32_t;
|
||||
#define STATUS_MUTANT_NOT_OWNED ((NTSTATUS)0xC0000046L)
|
||||
#define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS)0xC0000047L)
|
||||
#define STATUS_SECTION_NOT_IMAGE ((NTSTATUS)0xC0000049L)
|
||||
#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)
|
||||
#define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS)0xC00000ACL)
|
||||
#define STATUS_INVALID_PIPE_STATE ((NTSTATUS)0xC00000ADL)
|
||||
#define STATUS_PIPE_DISCONNECTED ((NTSTATUS)0xC00000B0L)
|
||||
#define STATUS_PIPE_CLOSING ((NTSTATUS)0xC00000B1L)
|
||||
#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)
|
||||
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
|
||||
#define STATUS_PIPE_NOT_CONNECTED ((NTSTATUS)0xC00000BEL)
|
||||
#define STATUS_PIPE_EMPTY ((NTSTATUS)0xC00000D9L)
|
||||
#define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5L)
|
||||
#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
|
||||
#define STATUS_INVALID_PARAMETER_1 ((NTSTATUS)0xC00000EFL)
|
||||
#define STATUS_INVALID_PARAMETER_2 ((NTSTATUS)0xC00000F0L)
|
||||
#define STATUS_INVALID_PARAMETER_3 ((NTSTATUS)0xC00000F1L)
|
||||
#define STATUS_INVALID_PARAMETER_4 ((NTSTATUS)0xC00000F2L)
|
||||
#define STATUS_INVALID_PARAMETER_5 ((NTSTATUS)0xC00000F3L)
|
||||
#define STATUS_INVALID_PARAMETER_6 ((NTSTATUS)0xC00000F4L)
|
||||
#define STATUS_INVALID_PARAMETER_7 ((NTSTATUS)0xC00000F5L)
|
||||
#define STATUS_INVALID_PARAMETER_8 ((NTSTATUS)0xC00000F6L)
|
||||
#define STATUS_INVALID_PARAMETER_9 ((NTSTATUS)0xC00000F7L)
|
||||
#define STATUS_INVALID_PARAMETER_10 ((NTSTATUS)0xC00000F8L)
|
||||
#define STATUS_INVALID_PARAMETER_11 ((NTSTATUS)0xC00000F9L)
|
||||
#define STATUS_INVALID_PARAMETER_12 ((NTSTATUS)0xC00000FAL)
|
||||
#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L)
|
||||
#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)
|
||||
#define STATUS_PORT_NOT_SET ((NTSTATUS)0xC0000353L)
|
||||
#define STATUS_DEBUGGER_INACTIVE ((NTSTATUS)0xC0000354L)
|
||||
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL)
|
||||
#define STATUS_PIPE_EMPTY ((NTSTATUS)0xC00000D9L)
|
||||
#define STATUS_PIPE_BUSY ((NTSTATUS)0xC00000AAL)
|
||||
#define STATUS_PIPE_DISCONNECTED ((NTSTATUS)0xC00000B0L)
|
||||
#define STATUS_PIPE_LISTENING ((NTSTATUS)0x00000105L)
|
||||
#define STATUS_PIPE_CONNECTED ((NTSTATUS)0x00000106L)
|
||||
#define STATUS_PIPE_CLOSING ((NTSTATUS)0xC00000B1L)
|
||||
#define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS)0xC00000ACL)
|
||||
#define STATUS_INVALID_PIPE_STATE ((NTSTATUS)0xC00000ADL)
|
||||
#define STATUS_PIPE_NOT_CONNECTED ((NTSTATUS)0xC00000BEL)
|
||||
|
||||
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
|
||||
|
||||
#define STATUS_SERVICE_NOTIFICATION ((NTSTATUS)0x40000018L)
|
||||
|
||||
#define FILE_DEVICE_NETWORK 0x00000012
|
||||
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
|
||||
|
||||
@@ -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
|
||||
@@ -94,6 +96,7 @@
|
||||
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
|
||||
#endif
|
||||
|
||||
#define PROCESSOR_ARCHITECTURE_INTEL 0
|
||||
#define PROCESSOR_ARCHITECTURE_AMD64 9
|
||||
|
||||
enum class PEMachineType : std::uint16_t
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,8 +3,14 @@
|
||||
|
||||
namespace utils
|
||||
{
|
||||
std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(clock& c, const LARGE_INTEGER delay_interval)
|
||||
std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(clock& c, const LARGE_INTEGER delay_interval,
|
||||
const LARGE_INTEGER infinite_value)
|
||||
{
|
||||
if (delay_interval.QuadPart == infinite_value.QuadPart)
|
||||
{
|
||||
return std::chrono::steady_clock::time_point::min();
|
||||
}
|
||||
|
||||
if (delay_interval.QuadPart <= 0)
|
||||
{
|
||||
const auto relative_time = -delay_interval.QuadPart;
|
||||
|
||||
@@ -102,7 +102,9 @@ namespace utils
|
||||
}
|
||||
};
|
||||
|
||||
std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(clock& c, LARGE_INTEGER delay_interval);
|
||||
std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(clock& c, LARGE_INTEGER delay_interval,
|
||||
LARGE_INTEGER infinite_value = {.LowPart = 0,
|
||||
.HighPart = -2147483648});
|
||||
|
||||
KSYSTEM_TIME convert_to_ksystem_time(const std::chrono::system_clock::time_point& tp);
|
||||
void convert_to_ksystem_time(volatile KSYSTEM_TIME* dest, const std::chrono::system_clock::time_point& tp);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1006,7 +1006,8 @@ namespace
|
||||
std::optional<std::chrono::steady_clock::time_point> timeout{};
|
||||
if (info.Timeout.QuadPart != std::numeric_limits<int64_t>::max())
|
||||
{
|
||||
timeout = utils::convert_delay_interval_to_time_point(win_emu.clock(), info.Timeout);
|
||||
timeout = utils::convert_delay_interval_to_time_point(win_emu.clock(), info.Timeout,
|
||||
{.QuadPart = std::numeric_limits<int64_t>::max()});
|
||||
}
|
||||
|
||||
this->delay_ioctrl(c, {}, timeout, timeout_callback);
|
||||
|
||||
@@ -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,11 +147,14 @@ 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
|
||||
{
|
||||
return this->await_time.has_value() && this->await_time.value() < clock.steady_now();
|
||||
constexpr auto infinite = std::chrono::steady_clock::time_point::min();
|
||||
return this->await_time.has_value() && this->await_time.value() != infinite && this->await_time.value() < clock.steady_now();
|
||||
}
|
||||
|
||||
bool is_terminated() const;
|
||||
@@ -187,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
|
||||
@@ -235,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()
|
||||
|
||||
@@ -66,8 +66,6 @@ class emulator_object
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
emulator_object() = default;
|
||||
|
||||
emulator_object(const x64_emulator_wrapper& wrapper, const uint64_t address = 0)
|
||||
: emulator_object(wrapper.get(), address)
|
||||
{
|
||||
@@ -84,6 +82,11 @@ class emulator_object
|
||||
{
|
||||
}
|
||||
|
||||
emulator_object(utils::buffer_deserializer& buffer)
|
||||
: emulator_object(buffer.read<memory_manager_wrapper>().get())
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t value() const
|
||||
{
|
||||
return this->address_;
|
||||
@@ -175,11 +178,6 @@ class emulator_object
|
||||
this->address_ = address;
|
||||
}
|
||||
|
||||
void set_memory_interface(memory_interface& memory)
|
||||
{
|
||||
this->memory_ = &memory;
|
||||
}
|
||||
|
||||
emulator_object<T> shift(const int64_t offset) const
|
||||
{
|
||||
return emulator_object<T>(*this->memory_, this->address_ + offset);
|
||||
@@ -410,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;
|
||||
@@ -442,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,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace utils
|
||||
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.write(region.is_mmio);
|
||||
buffer.write(region.kind);
|
||||
buffer.write(region.initial_permission);
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write_map(region.committed_regions);
|
||||
@@ -86,7 +86,7 @@ namespace utils
|
||||
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.read(region.is_mmio);
|
||||
buffer.read(region.kind);
|
||||
buffer.read(region.initial_permission);
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
buffer.read_map(region.committed_regions);
|
||||
@@ -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)
|
||||
@@ -133,7 +134,7 @@ void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, co
|
||||
|
||||
for (const auto& reserved_region : this->reserved_regions_)
|
||||
{
|
||||
if (reserved_region.second.is_mmio)
|
||||
if (reserved_region.second.kind == memory_region_kind::mmio)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -169,7 +171,7 @@ void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer
|
||||
for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
|
||||
{
|
||||
auto& reserved_region = i->second;
|
||||
if (reserved_region.is_mmio)
|
||||
if (reserved_region.kind == memory_region_kind::mmio)
|
||||
{
|
||||
i = this->reserved_regions_.erase(i);
|
||||
continue;
|
||||
@@ -183,7 +185,9 @@ void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer
|
||||
|
||||
buffer.read(data.data(), region.second.length);
|
||||
|
||||
this->map_memory(region.first, region.second.length, region.second.permissions);
|
||||
const auto effective_permission =
|
||||
region.second.permissions.is_guarded() ? memory_permission::none : region.second.permissions.common;
|
||||
this->map_memory(region.first, region.second.length, effective_permission);
|
||||
this->write_memory(region.first, data.data(), region.second.length);
|
||||
}
|
||||
}
|
||||
@@ -211,6 +215,8 @@ bool memory_manager::protect_memory(const uint64_t address, const size_t size, c
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
split_regions(committed_regions, {address, end});
|
||||
|
||||
const auto effective_permission = permissions.is_guarded() ? memory_permission::none : permissions.common;
|
||||
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
@@ -226,7 +232,7 @@ bool memory_manager::protect_memory(const uint64_t address, const size_t size, c
|
||||
old_first_permissions = sub_region.second.permissions;
|
||||
}
|
||||
|
||||
this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
|
||||
this->apply_memory_protection(sub_region.first, sub_region.second.length, effective_permission);
|
||||
sub_region.second.permissions = permissions;
|
||||
}
|
||||
}
|
||||
@@ -256,7 +262,7 @@ bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mm
|
||||
.try_emplace(address,
|
||||
reserved_region{
|
||||
.length = size,
|
||||
.is_mmio = true,
|
||||
.kind = memory_region_kind::mmio,
|
||||
})
|
||||
.first;
|
||||
|
||||
@@ -268,7 +274,7 @@ bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mm
|
||||
}
|
||||
|
||||
bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions,
|
||||
const bool reserve_only)
|
||||
const bool reserve_only, const memory_region_kind kind)
|
||||
{
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
@@ -280,6 +286,7 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size,
|
||||
reserved_region{
|
||||
.length = size,
|
||||
.initial_permission = permissions,
|
||||
.kind = kind,
|
||||
})
|
||||
.first;
|
||||
|
||||
@@ -316,6 +323,8 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co
|
||||
uint64_t last_region_start{};
|
||||
const committed_region* last_region{nullptr};
|
||||
|
||||
const auto effective_permission = permissions.is_guarded() ? memory_permission::none : permissions.common;
|
||||
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
@@ -331,10 +340,14 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co
|
||||
|
||||
if (map_length > 0)
|
||||
{
|
||||
this->map_memory(map_start, static_cast<size_t>(map_length), permissions);
|
||||
this->map_memory(map_start, static_cast<size_t>(map_length), effective_permission);
|
||||
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;
|
||||
}
|
||||
@@ -345,7 +358,7 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co
|
||||
const auto map_start = last_region ? (last_region_start + last_region->length) : address;
|
||||
const auto map_length = end - map_start;
|
||||
|
||||
this->map_memory(map_start, static_cast<size_t>(map_length), permissions);
|
||||
this->map_memory(map_start, static_cast<size_t>(map_length), effective_permission);
|
||||
committed_regions[map_start] = committed_region{static_cast<size_t>(map_length), permissions};
|
||||
}
|
||||
|
||||
@@ -364,7 +377,7 @@ bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry->second.is_mmio)
|
||||
if (entry->second.kind == memory_region_kind::mmio)
|
||||
{
|
||||
throw std::runtime_error("Not allowed to decommit MMIO!");
|
||||
}
|
||||
@@ -406,38 +419,59 @@ bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
|
||||
|
||||
bool memory_manager::release_memory(const uint64_t address, size_t size)
|
||||
{
|
||||
const auto entry = this->reserved_regions_.find(address);
|
||||
if (!size)
|
||||
{
|
||||
const auto entry = this->reserved_regions_.find(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
}
|
||||
|
||||
this->reserved_regions_.erase(entry);
|
||||
this->update_layout_version();
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto aligned_start = page_align_down(address);
|
||||
const auto aligned_end = page_align_up(address + size);
|
||||
size = static_cast<size_t>(aligned_end - aligned_start);
|
||||
|
||||
const auto entry = this->find_reserved_region(aligned_start);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!size)
|
||||
{
|
||||
size = entry->second.length;
|
||||
}
|
||||
const auto reserved_start = entry->first;
|
||||
const auto reserved_end = entry->first + entry->second.length;
|
||||
|
||||
size = static_cast<size_t>(page_align_up(size));
|
||||
|
||||
if (size > entry->second.length)
|
||||
if (reserved_end < aligned_end)
|
||||
{
|
||||
throw std::runtime_error("Cross region release not supported yet!");
|
||||
}
|
||||
|
||||
const auto end = address + size;
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
reserved_region region = std::move(entry->second);
|
||||
this->reserved_regions_.erase(entry);
|
||||
|
||||
split_regions(committed_regions, {end});
|
||||
auto& committed_regions = region.committed_regions;
|
||||
split_regions(committed_regions, {aligned_start, aligned_end});
|
||||
|
||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||
{
|
||||
if (i->first >= end)
|
||||
if (i->first >= aligned_end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto sub_region_end = i->first + i->second.length;
|
||||
if (i->first >= address && sub_region_end <= end)
|
||||
if (i->first >= aligned_start && sub_region_end <= aligned_end)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
@@ -448,13 +482,41 @@ bool memory_manager::release_memory(const uint64_t address, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
entry->second.length -= size;
|
||||
if (entry->second.length > 0)
|
||||
committed_region_map left_committed{};
|
||||
committed_region_map right_committed{};
|
||||
|
||||
for (const auto& sub_region : committed_regions)
|
||||
{
|
||||
this->reserved_regions_[address + size] = std::move(entry->second);
|
||||
if (sub_region.first < aligned_start)
|
||||
{
|
||||
left_committed.emplace(sub_region.first, sub_region.second);
|
||||
}
|
||||
else if (sub_region.first >= aligned_end)
|
||||
{
|
||||
right_committed.emplace(sub_region.first, sub_region.second);
|
||||
}
|
||||
}
|
||||
|
||||
if (reserved_start < aligned_start)
|
||||
{
|
||||
reserved_region left_region{};
|
||||
left_region.length = static_cast<size_t>(aligned_start - reserved_start);
|
||||
left_region.initial_permission = region.initial_permission;
|
||||
left_region.kind = region.kind;
|
||||
left_region.committed_regions = std::move(left_committed);
|
||||
this->reserved_regions_.try_emplace(reserved_start, std::move(left_region));
|
||||
}
|
||||
|
||||
if (aligned_end < reserved_end)
|
||||
{
|
||||
reserved_region right_region{};
|
||||
right_region.length = static_cast<size_t>(reserved_end - aligned_end);
|
||||
right_region.initial_permission = region.initial_permission;
|
||||
right_region.kind = region.kind;
|
||||
right_region.committed_regions = std::move(right_committed);
|
||||
this->reserved_regions_.try_emplace(aligned_end, std::move(right_region));
|
||||
}
|
||||
|
||||
this->reserved_regions_.erase(entry);
|
||||
this->update_layout_version();
|
||||
return true;
|
||||
}
|
||||
@@ -472,10 +534,11 @@ void memory_manager::unmap_all_memory()
|
||||
this->reserved_regions_.clear();
|
||||
}
|
||||
|
||||
uint64_t memory_manager::allocate_memory(const size_t size, const nt_memory_permission permissions, const bool reserve_only, uint64_t start)
|
||||
uint64_t memory_manager::allocate_memory(const size_t size, const nt_memory_permission permissions, const bool reserve_only, uint64_t start,
|
||||
const memory_region_kind kind)
|
||||
{
|
||||
const auto allocation_base = this->find_free_allocation_base(size, start);
|
||||
if (!allocate_memory(allocation_base, size, permissions, reserve_only))
|
||||
if (!allocate_memory(allocation_base, size, permissions, reserve_only, kind))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -569,6 +632,7 @@ region_info memory_manager::get_region_info(const uint64_t address)
|
||||
result.start = result.allocation_base;
|
||||
result.length = result.allocation_length;
|
||||
result.initial_permissions = entry->second.initial_permission;
|
||||
result.kind = reserved_region.kind;
|
||||
|
||||
if (committed_regions.empty())
|
||||
{
|
||||
@@ -634,6 +698,28 @@ bool memory_manager::overlaps_reserved_region(const uint64_t address, const size
|
||||
return false;
|
||||
}
|
||||
|
||||
memory_region_kind memory_manager::get_region_kind(const uint64_t address) const
|
||||
{
|
||||
if (this->reserved_regions_.empty())
|
||||
{
|
||||
return memory_region_kind::free;
|
||||
}
|
||||
|
||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||
if (upper_bound == this->reserved_regions_.begin())
|
||||
{
|
||||
return memory_region_kind::free;
|
||||
}
|
||||
|
||||
const auto entry = --upper_bound;
|
||||
if (entry->first + entry->second.length <= address)
|
||||
{
|
||||
return memory_region_kind::free;
|
||||
}
|
||||
|
||||
return entry->second.kind;
|
||||
}
|
||||
|
||||
void memory_manager::read_memory(const uint64_t address, void* data, const size_t size) const
|
||||
{
|
||||
this->memory_->read_memory(address, data, size);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "std_include.hpp"
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
@@ -16,6 +17,15 @@ constexpr auto MAX_ALLOCATION_END_EXCL = MAX_ALLOCATION_ADDRESS + 1ULL;
|
||||
constexpr auto DEFAULT_ALLOCATION_ADDRESS_64BIT = 0x100000000ULL;
|
||||
constexpr auto DEFAULT_ALLOCATION_ADDRESS_32BIT = 0x10000ULL;
|
||||
|
||||
enum class memory_region_kind : uint8_t
|
||||
{
|
||||
free = 0,
|
||||
private_allocation,
|
||||
section_view,
|
||||
section_image,
|
||||
mmio,
|
||||
};
|
||||
|
||||
// This maps to the `basic_memory_region` struct defined in
|
||||
// emulator\memory_region.hpp
|
||||
struct region_info : basic_memory_region<nt_memory_permission>
|
||||
@@ -25,6 +35,7 @@ struct region_info : basic_memory_region<nt_memory_permission>
|
||||
bool is_reserved{};
|
||||
bool is_committed{};
|
||||
nt_memory_permission initial_permissions{};
|
||||
memory_region_kind kind{memory_region_kind::free};
|
||||
};
|
||||
|
||||
using mmio_read_callback = std::function<void(uint64_t addr, void* data, size_t size)>;
|
||||
@@ -57,7 +68,7 @@ class memory_manager : public memory_interface
|
||||
size_t length{};
|
||||
memory_permission initial_permission{};
|
||||
committed_region_map committed_regions{};
|
||||
bool is_mmio{false};
|
||||
memory_region_kind kind{memory_region_kind::private_allocation};
|
||||
};
|
||||
|
||||
using reserved_region_map = std::map<uint64_t, reserved_region>;
|
||||
@@ -70,7 +81,8 @@ class memory_manager : public memory_interface
|
||||
bool protect_memory(uint64_t address, size_t size, nt_memory_permission permissions, nt_memory_permission* old_permissions = nullptr);
|
||||
|
||||
bool allocate_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb);
|
||||
bool allocate_memory(uint64_t address, size_t size, nt_memory_permission permissions, bool reserve_only = false);
|
||||
bool allocate_memory(uint64_t address, size_t size, nt_memory_permission permissions, bool reserve_only = false,
|
||||
memory_region_kind kind = memory_region_kind::private_allocation);
|
||||
|
||||
bool commit_memory(uint64_t address, size_t size, nt_memory_permission permissions);
|
||||
bool decommit_memory(uint64_t address, size_t size);
|
||||
@@ -79,7 +91,8 @@ class memory_manager : public memory_interface
|
||||
|
||||
void unmap_all_memory();
|
||||
|
||||
uint64_t allocate_memory(size_t size, nt_memory_permission permissions, bool reserve_only = false, uint64_t start = 0);
|
||||
uint64_t allocate_memory(size_t size, nt_memory_permission permissions, bool reserve_only = false, uint64_t start = 0,
|
||||
memory_region_kind kind = memory_region_kind::private_allocation);
|
||||
|
||||
uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const;
|
||||
|
||||
@@ -89,6 +102,8 @@ class memory_manager : public memory_interface
|
||||
|
||||
bool overlaps_reserved_region(uint64_t address, size_t size) const;
|
||||
|
||||
memory_region_kind get_region_kind(uint64_t address) const;
|
||||
|
||||
const reserved_region_map& get_reserved_regions() const
|
||||
{
|
||||
return this->reserved_regions_;
|
||||
@@ -127,3 +142,46 @@ class memory_manager : public memory_interface
|
||||
|
||||
void update_layout_version();
|
||||
};
|
||||
|
||||
namespace memory_region_policy
|
||||
{
|
||||
constexpr bool is_section_kind(const memory_region_kind kind)
|
||||
{
|
||||
return kind == memory_region_kind::section_view || kind == memory_region_kind::section_image;
|
||||
}
|
||||
|
||||
constexpr bool is_mapped_memory_kind(const memory_region_kind kind)
|
||||
{
|
||||
return is_section_kind(kind) || kind == memory_region_kind::mmio;
|
||||
}
|
||||
|
||||
constexpr uint32_t to_memory_basic_information_type(const memory_region_kind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case memory_region_kind::section_image:
|
||||
return MEM_IMAGE;
|
||||
case memory_region_kind::section_view:
|
||||
return MEM_MAPPED;
|
||||
case memory_region_kind::mmio:
|
||||
return MEM_MAPPED;
|
||||
default:
|
||||
return MEM_PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr NTSTATUS nt_free_virtual_memory_denied_status(const memory_region_kind kind)
|
||||
{
|
||||
if (is_section_kind(kind))
|
||||
{
|
||||
return STATUS_UNABLE_TO_DELETE_SECTION;
|
||||
}
|
||||
|
||||
if (kind == memory_region_kind::mmio)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ inline std::optional<nt_memory_permission> try_map_nt_to_emulator_protection(uin
|
||||
ext = memory_permission_ext::guard;
|
||||
}
|
||||
|
||||
// Remove the highest nibble since we are not currently handling those memory protection constants
|
||||
// https://github.com/winsiderss/phnt/blob/master/ntmmapi.h#L26
|
||||
nt_protection &= ~0xF0000000;
|
||||
|
||||
memory_permission common = memory_permission::none;
|
||||
switch (nt_protection)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
@@ -266,8 +294,8 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, regist
|
||||
|
||||
p.ProcessHeap = 0;
|
||||
p.ProcessHeaps = 0;
|
||||
p.HeapSegmentReserve = 0x0000000000100000; // TODO: Read from executable
|
||||
p.HeapSegmentCommit = 0x0000000000002000;
|
||||
p.HeapSegmentReserve = executable.size_of_heap_reserve;
|
||||
p.HeapSegmentCommit = executable.size_of_heap_commit;
|
||||
p.HeapDeCommitTotalFreeThreshold = 0x0000000000010000;
|
||||
p.HeapDeCommitFreeBlockThreshold = 0x0000000000001000;
|
||||
p.NumberOfHeaps = 0x00000000;
|
||||
@@ -357,8 +385,8 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, regist
|
||||
// Copy similar settings from PEB64
|
||||
p32.ProcessHeap = 0;
|
||||
p32.ProcessHeaps = 0;
|
||||
p32.HeapSegmentReserve = 0x00100000;
|
||||
p32.HeapSegmentCommit = 0x00002000;
|
||||
p32.HeapSegmentReserve = static_cast<uint32_t>(executable.size_of_heap_reserve);
|
||||
p32.HeapSegmentCommit = static_cast<uint32_t>(executable.size_of_heap_commit);
|
||||
p32.HeapDeCommitTotalFreeThreshold = 0x00010000;
|
||||
p32.HeapDeCommitFreeBlockThreshold = 0x00001000;
|
||||
p32.NumberOfHeaps = 0;
|
||||
@@ -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)
|
||||
@@ -453,16 +574,8 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
|
||||
buffer.read_optional(this->process_params32);
|
||||
buffer.read(this->kusd);
|
||||
|
||||
if (this->peb32.has_value())
|
||||
{
|
||||
this->peb32->set_memory_interface(*this->peb64.get_memory_interface());
|
||||
}
|
||||
if (this->process_params32.has_value())
|
||||
{
|
||||
this->process_params32->set_memory_interface(*this->peb64.get_memory_interface());
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -471,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);
|
||||
@@ -498,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>
|
||||
@@ -83,6 +84,9 @@ namespace syscalls
|
||||
NTSTATUS handle_NtOpenDirectoryObject(const syscall_context& c, emulator_object<handle> directory_handle,
|
||||
ACCESS_MASK /*desired_access*/,
|
||||
emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes);
|
||||
NTSTATUS handle_NtCreateDirectoryObject(const syscall_context& /*c*/, emulator_object<handle> /*directory_handle*/,
|
||||
ACCESS_MASK /*desired_access*/,
|
||||
emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes);
|
||||
NTSTATUS handle_NtOpenSymbolicLinkObject(const syscall_context& c, emulator_object<handle> link_handle, ACCESS_MASK /*desired_access*/,
|
||||
emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes);
|
||||
NTSTATUS handle_NtQuerySymbolicLinkObject(const syscall_context& c, handle link_handle,
|
||||
@@ -152,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*/,
|
||||
@@ -227,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*/,
|
||||
@@ -463,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;
|
||||
}
|
||||
@@ -831,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;
|
||||
@@ -840,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)
|
||||
@@ -998,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;
|
||||
@@ -1027,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)
|
||||
@@ -1056,6 +1133,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
|
||||
add_handler(NtProtectVirtualMemory);
|
||||
add_handler(NtLockVirtualMemory);
|
||||
add_handler(NtOpenDirectoryObject);
|
||||
add_handler(NtCreateDirectoryObject);
|
||||
add_handler(NtTraceEvent);
|
||||
add_handler(NtAllocateVirtualMemoryEx);
|
||||
add_handler(NtCreateIoCompletion);
|
||||
@@ -1139,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);
|
||||
@@ -1147,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);
|
||||
@@ -1212,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);
|
||||
@@ -1239,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
|
||||
}
|
||||
|
||||
@@ -1220,6 +1220,20 @@ namespace syscalls
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtCreateDirectoryObject(const syscall_context& /*c*/, const emulator_object<handle> /*directory_handle*/,
|
||||
const ACCESS_MASK /*desired_access*/,
|
||||
const emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes)
|
||||
{
|
||||
const auto attributes = object_attributes.read();
|
||||
|
||||
if (attributes.ObjectName == 0)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtOpenSymbolicLinkObject(const syscall_context& c, const emulator_object<handle> link_handle,
|
||||
ACCESS_MASK /*desired_access*/,
|
||||
const emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../cpu_context.hpp"
|
||||
#include "../emulator_utils.hpp"
|
||||
#include "../syscall_utils.hpp"
|
||||
#include "../memory_manager.hpp"
|
||||
|
||||
namespace syscalls
|
||||
{
|
||||
@@ -58,7 +59,15 @@ namespace syscalls
|
||||
|
||||
image_info.Protect = map_emulator_to_nt_protection(region_info.permissions);
|
||||
image_info.AllocationProtect = map_emulator_to_nt_protection(region_info.initial_permissions);
|
||||
image_info.Type = MEM_PRIVATE;
|
||||
|
||||
if (!region_info.is_reserved)
|
||||
{
|
||||
image_info.Type = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
image_info.Type = memory_region_policy::to_memory_basic_information_type(region_info.kind);
|
||||
}
|
||||
});
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
@@ -199,6 +208,12 @@ namespace syscalls
|
||||
allocation_bytes = page_align_up(allocation_bytes);
|
||||
bytes_to_allocate.write(allocation_bytes);
|
||||
|
||||
const auto base_protection = page_protection & ~static_cast<uint32_t>(PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE);
|
||||
if (base_protection == PAGE_WRITECOPY || base_protection == PAGE_EXECUTE_WRITECOPY)
|
||||
{
|
||||
return STATUS_INVALID_PAGE_PROTECTION;
|
||||
}
|
||||
|
||||
const auto protection = try_map_nt_to_emulator_protection(page_protection);
|
||||
if (!protection.has_value())
|
||||
{
|
||||
@@ -218,7 +233,7 @@ namespace syscalls
|
||||
// initial value of BaseAddress is non-NULL, the region is allocated starting at the specified virtual
|
||||
// address rounded down to the next host page size address boundary. If the initial value of BaseAddress
|
||||
// is NULL, the operating system will determine where to allocate the region.
|
||||
potential_base = page_align_up(potential_base);
|
||||
potential_base = page_align_down(potential_base);
|
||||
}
|
||||
|
||||
if (!potential_base)
|
||||
@@ -267,7 +282,7 @@ namespace syscalls
|
||||
|
||||
if (free_type == 0)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
return STATUS_INVALID_PARAMETER_4;
|
||||
}
|
||||
|
||||
const auto allocation_base = base_address.read();
|
||||
@@ -275,14 +290,70 @@ namespace syscalls
|
||||
|
||||
if (free_type & MEM_RELEASE)
|
||||
{
|
||||
return c.win_emu.memory.release_memory(allocation_base, static_cast<size_t>(allocation_size)) ? STATUS_SUCCESS
|
||||
: STATUS_MEMORY_NOT_ALLOCATED;
|
||||
if (!allocation_size)
|
||||
{
|
||||
const auto region_info = c.win_emu.memory.get_region_info(allocation_base);
|
||||
if (!region_info.is_reserved)
|
||||
{
|
||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
if (region_info.allocation_base != allocation_base)
|
||||
{
|
||||
return STATUS_FREE_VM_NOT_AT_BASE;
|
||||
}
|
||||
}
|
||||
|
||||
const auto region_kind = c.win_emu.memory.get_region_kind(allocation_base);
|
||||
const auto denied_status = memory_region_policy::nt_free_virtual_memory_denied_status(region_kind);
|
||||
if (denied_status != STATUS_SUCCESS)
|
||||
{
|
||||
return denied_status;
|
||||
}
|
||||
|
||||
const bool success = c.win_emu.memory.release_memory(allocation_base, static_cast<size_t>(allocation_size));
|
||||
if (success)
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
const auto region_info = c.win_emu.memory.get_region_info(allocation_base);
|
||||
if (!region_info.is_reserved)
|
||||
{
|
||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
return STATUS_FREE_VM_NOT_AT_BASE;
|
||||
}
|
||||
|
||||
if (free_type & MEM_DECOMMIT)
|
||||
{
|
||||
return c.win_emu.memory.decommit_memory(allocation_base, static_cast<size_t>(allocation_size)) ? STATUS_SUCCESS
|
||||
: STATUS_MEMORY_NOT_ALLOCATED;
|
||||
const auto region_kind = c.win_emu.memory.get_region_kind(allocation_base);
|
||||
const auto denied_status = memory_region_policy::nt_free_virtual_memory_denied_status(region_kind);
|
||||
if (denied_status != STATUS_SUCCESS)
|
||||
{
|
||||
return denied_status;
|
||||
}
|
||||
|
||||
auto decommit_size = static_cast<size_t>(allocation_size);
|
||||
if (!decommit_size)
|
||||
{
|
||||
const auto region_info = c.win_emu.memory.get_region_info(allocation_base);
|
||||
if (!region_info.is_reserved)
|
||||
{
|
||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
if (region_info.allocation_base != allocation_base)
|
||||
{
|
||||
return STATUS_FREE_VM_NOT_AT_BASE;
|
||||
}
|
||||
|
||||
decommit_size = region_info.allocation_length;
|
||||
}
|
||||
|
||||
const bool success = c.win_emu.memory.decommit_memory(allocation_base, decommit_size);
|
||||
return success ? STATUS_SUCCESS : STATUS_MEMORY_NOT_ALLOCATED;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Bad free type");
|
||||
|
||||
@@ -8,8 +8,10 @@ namespace syscalls
|
||||
{
|
||||
const auto value = h.value;
|
||||
|
||||
if (h.h == 0xDEADC0DE)
|
||||
if (h.h == 0xDEADC0DE || h.h == 0xDEADBEEF)
|
||||
{
|
||||
c.win_emu.callbacks.on_suspicious_activity("Anti-debug check with invalid handle");
|
||||
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
@@ -265,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)
|
||||
@@ -302,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)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace syscalls
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
const auto return_length_info = c.win_emu.memory.get_region_info(return_length.value());
|
||||
|
||||
switch (info_class)
|
||||
{
|
||||
case ProcessExecuteFlags:
|
||||
@@ -62,6 +64,24 @@ namespace syscalls
|
||||
});
|
||||
|
||||
case ProcessDebugObjectHandle:
|
||||
|
||||
c.win_emu.callbacks.on_suspicious_activity("Anti-debug check with ProcessDebugObjectHandle");
|
||||
|
||||
if ((process_information & 3) != 0)
|
||||
{
|
||||
return STATUS_DATATYPE_MISALIGNMENT;
|
||||
}
|
||||
|
||||
if (return_length.value() == 0)
|
||||
{
|
||||
return STATUS_PORT_NOT_SET;
|
||||
}
|
||||
|
||||
if (!return_length_info.is_reserved)
|
||||
{
|
||||
return STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
|
||||
return handle_query<handle>(c.emu, process_information, process_information_length, return_length, [](handle& h) {
|
||||
h = NULL_HANDLE;
|
||||
return STATUS_PORT_NOT_SET;
|
||||
@@ -75,6 +95,13 @@ namespace syscalls
|
||||
});
|
||||
|
||||
case ProcessDebugPort:
|
||||
c.win_emu.callbacks.on_suspicious_activity("Anti-debug check with ProcessDebugPort");
|
||||
|
||||
return handle_query<EmulatorTraits<Emu64>::PVOID>(c.emu, process_information, process_information_length, return_length,
|
||||
[](EmulatorTraits<Emu64>::PVOID& ptr) {
|
||||
ptr = 0; //
|
||||
});
|
||||
|
||||
case ProcessDeviceMap:
|
||||
return handle_query<EmulatorTraits<Emu64>::PVOID>(c.emu, process_information, process_information_length, return_length,
|
||||
[](EmulatorTraits<Emu64>::PVOID& ptr) {
|
||||
@@ -202,7 +229,7 @@ namespace syscalls
|
||||
|| info_class == ProcessDynamicFunctionTableInformation //
|
||||
|| info_class == ProcessPriorityBoost //
|
||||
|| info_class == ProcessPriorityClassEx //
|
||||
|| info_class == ProcessPriorityClass)
|
||||
|| info_class == ProcessPriorityClass || info_class == ProcessAffinityMask)
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -333,6 +360,7 @@ namespace syscalls
|
||||
PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info;
|
||||
|
||||
c.emu.read_memory(process_information, &info, sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION));
|
||||
c.win_emu.callbacks.on_suspicious_activity("Setting ProcessInstrumentationCallback");
|
||||
|
||||
c.proc.instrumentation_callback = info.Callback;
|
||||
|
||||
|
||||
@@ -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*/,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "../emulator_utils.hpp"
|
||||
#include "../syscall_utils.hpp"
|
||||
#include "../memory_manager.hpp"
|
||||
|
||||
#include <utils/io.hpp>
|
||||
|
||||
@@ -193,7 +194,8 @@ namespace syscalls
|
||||
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);
|
||||
c.win_emu.memory.allocate_memory(address, shared_section_size, memory_permission::read_write, false,
|
||||
memory_region_kind::section_view);
|
||||
c.proc.shared_section_address = address;
|
||||
c.proc.shared_section_size = shared_section_size;
|
||||
|
||||
@@ -206,7 +208,8 @@ namespace syscalls
|
||||
constexpr auto dbwin_buffer_section_size = 0x1000;
|
||||
|
||||
const auto address = c.win_emu.memory.find_free_allocation_base(dbwin_buffer_section_size);
|
||||
c.win_emu.memory.allocate_memory(address, dbwin_buffer_section_size, memory_permission::read_write);
|
||||
c.win_emu.memory.allocate_memory(address, dbwin_buffer_section_size, memory_permission::read_write, false,
|
||||
memory_region_kind::section_view);
|
||||
c.proc.dbwin_buffer = address;
|
||||
c.proc.dbwin_buffer_size = dbwin_buffer_section_size;
|
||||
|
||||
@@ -373,7 +376,7 @@ namespace syscalls
|
||||
const auto aligned_size = static_cast<size_t>(page_align_up(size));
|
||||
const auto reserve_only = section_entry->allocation_attributes == SEC_RESERVE;
|
||||
const auto protection = map_nt_to_emulator_protection(section_entry->section_page_protection);
|
||||
const auto address = c.win_emu.memory.allocate_memory(aligned_size, protection, reserve_only);
|
||||
const auto address = c.win_emu.memory.allocate_memory(aligned_size, protection, reserve_only, 0, memory_region_kind::section_view);
|
||||
|
||||
if (!reserve_only && !file_data.empty())
|
||||
{
|
||||
@@ -577,24 +580,27 @@ namespace syscalls
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (base_address == c.proc.shared_section_address)
|
||||
if (c.proc.shared_section_address && base_address >= c.proc.shared_section_address &&
|
||||
base_address < c.proc.shared_section_address + c.proc.shared_section_size)
|
||||
{
|
||||
const auto address = c.proc.shared_section_address;
|
||||
c.proc.shared_section_address = 0;
|
||||
c.win_emu.memory.release_memory(base_address, static_cast<size_t>(c.proc.shared_section_size));
|
||||
c.win_emu.memory.release_memory(address, static_cast<size_t>(c.proc.shared_section_size));
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (base_address == c.proc.dbwin_buffer)
|
||||
if (c.proc.dbwin_buffer && is_within_start_and_length(base_address, c.proc.dbwin_buffer, c.proc.dbwin_buffer_size))
|
||||
{
|
||||
const auto address = c.proc.dbwin_buffer;
|
||||
c.proc.dbwin_buffer = 0;
|
||||
c.win_emu.memory.release_memory(base_address, static_cast<size_t>(c.proc.dbwin_buffer_size));
|
||||
c.win_emu.memory.release_memory(address, static_cast<size_t>(c.proc.dbwin_buffer_size));
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
const auto* mod = c.win_emu.mod_manager.find_by_address(base_address);
|
||||
if (mod != nullptr)
|
||||
{
|
||||
if (c.win_emu.mod_manager.unmap(base_address))
|
||||
if (c.win_emu.mod_manager.unmap(mod->image_base))
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -602,14 +608,14 @@ namespace syscalls
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (c.win_emu.memory.release_memory(base_address, 0))
|
||||
const auto region_info = c.win_emu.memory.get_region_info(base_address);
|
||||
if (region_info.is_reserved && memory_region_policy::is_section_kind(region_info.kind) &&
|
||||
c.win_emu.memory.release_memory(region_info.allocation_base, 0))
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
c.win_emu.log.error("Unmapping non-module/non-memory section not supported!\n");
|
||||
c.emu.stop();
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
return STATUS_NOT_MAPPED_VIEW;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtUnmapViewOfSectionEx(const syscall_context& c, const handle process_handle, const uint64_t base_address,
|
||||
|
||||
@@ -199,12 +199,14 @@ namespace syscalls
|
||||
});
|
||||
|
||||
case SystemProcessorInformation:
|
||||
return handle_query<SYSTEM_PROCESSOR_INFORMATION64>(c.emu, system_information, system_information_length, return_length,
|
||||
[&](SYSTEM_PROCESSOR_INFORMATION64& info) {
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.MaximumProcessors = 2;
|
||||
info.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
|
||||
});
|
||||
case SystemEmulationProcessorInformation:
|
||||
return handle_query<SYSTEM_PROCESSOR_INFORMATION64>(
|
||||
c.emu, system_information, system_information_length, return_length, [&](SYSTEM_PROCESSOR_INFORMATION64& info) {
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.MaximumProcessors = 2;
|
||||
info.ProcessorArchitecture =
|
||||
(info_class == SystemProcessorInformation ? PROCESSOR_ARCHITECTURE_AMD64 : PROCESSOR_ARCHITECTURE_INTEL);
|
||||
});
|
||||
|
||||
case SystemNumaProcessorMap:
|
||||
return handle_query<SYSTEM_NUMA_INFORMATION64>(c.emu, system_information, system_information_length, return_length,
|
||||
|
||||
@@ -309,6 +309,8 @@ namespace syscalls
|
||||
const emulator_object<BOOLEAN> info{c.emu, thread_information};
|
||||
info.write(cur_emulator_thread.debugger_hide);
|
||||
|
||||
c.win_emu.callbacks.on_suspicious_activity("Checking if the thread is hidden from the debugger");
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -403,7 +405,10 @@ namespace syscalls
|
||||
NTSTATUS handle_NtDelayExecution(const syscall_context& c, const BOOLEAN alertable, const emulator_object<LARGE_INTEGER> delay_interval)
|
||||
{
|
||||
auto& t = c.win_emu.current_thread();
|
||||
t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), delay_interval.read());
|
||||
if (delay_interval.value())
|
||||
{
|
||||
t.await_time = utils::convert_delay_interval_to_time_point(c.win_emu.clock(), delay_interval.read());
|
||||
}
|
||||
c.win_emu.yield_thread(alertable);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
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