diff --git a/README.md b/README.md
index 508c4f4b..e69ce6d5 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,8 @@
A high-performance Windows process emulator that operates at syscall level, providing full control over process execution through comprehensive hooking capabilities.
+Perfect for security research, malware analysis, and DRM research where fine-grained control over process execution is required.
+
Built in C++ and powered by the Unicorn Engine.
## Key Features
@@ -28,8 +30,6 @@ Built in C++ and powered by the Unicorn Engine.
* 💻 __Debugging Interface__
* Implements GDB serial protocol for integration with common debugging tools (IDA Pro, GDB, LLDB, VS Code, ...)
-Perfect for security research, malware analysis, and DRM research where fine-grained control over process execution is required.
-
##
> [!NOTE]
> The project is still in a very early, prototypy state. The code still needs a lot of cleanup and many features and syscalls need to be implemented. However, constant progress is being made :)
@@ -83,6 +83,12 @@ Release build:
cmake --workflow --preset=release
```
+## Dumping the Registry
+
+The emulator needs a registry dump to run, otherwise it will print `Bad hive file` errors.
+You can create one by running the src/grab-registry.bat script as administrator.
+This will create a `registry` folder that needs to be placed in the working directory of the emulator.
+
## Running Tests
The project uses CTest for testing. Choose your preferred method:
diff --git a/deps/googletest b/deps/googletest
index 35d0c365..7d76a231 160000
--- a/deps/googletest
+++ b/deps/googletest
@@ -1 +1 @@
-Subproject commit 35d0c365609296fa4730d62057c487e3cfa030ff
+Subproject commit 7d76a231b0e29caf86e68d1df858308cd53b2a66
diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp
index 5c2b53c1..8396bf73 100644
--- a/src/analyzer/main.cpp
+++ b/src/analyzer/main.cpp
@@ -5,19 +5,24 @@
#include "object_watching.hpp"
-bool use_gdb = false;
-
namespace
{
- void watch_system_objects(windows_emulator& win_emu)
+ struct analysis_options
{
- //watch_object(win_emu, *win_emu.current_thread().teb);
- //watch_object(win_emu, win_emu.process().peb);
- //watch_object(win_emu, win_emu.process().kusd);
- auto* params_hook = watch_object(win_emu, win_emu.process().process_params);
+ bool use_gdb{false};
+ bool concise_logging{false};
+ };
+
+ void watch_system_objects(windows_emulator& win_emu, const bool cache_logging)
+ {
+ watch_object(win_emu, *win_emu.current_thread().teb, cache_logging);
+ watch_object(win_emu, win_emu.process().peb, cache_logging);
+ watch_object(win_emu, emulator_object{win_emu.emu(), kusd_mmio::address()}, cache_logging);
+
+ auto* params_hook = watch_object(win_emu, win_emu.process().process_params, cache_logging);
win_emu.emu().hook_memory_write(win_emu.process().peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
- [&](const uint64_t address, size_t, const uint64_t value)
+ [&, cache_logging](const uint64_t address, size_t, const uint64_t value)
{
const auto target_address = win_emu.process().peb.value() + offsetof(
PEB64, ProcessParameters);
@@ -29,18 +34,18 @@ namespace
};
win_emu.emu().delete_hook(params_hook);
- params_hook = watch_object(win_emu, obj);
+ params_hook = watch_object(win_emu, obj, cache_logging);
}
});
}
- void run_emulation(windows_emulator& win_emu)
+ void run_emulation(windows_emulator& win_emu, const analysis_options& options)
{
try
{
- if (use_gdb)
+ if (options.use_gdb)
{
- const auto* address = "0.0.0.0:28960";
+ const auto* address = "127.0.0.1:28960";
win_emu.logger.print(color::pink, "Waiting for GDB connection on %s...\n", address);
win_x64_gdb_stub_handler handler{win_emu};
@@ -51,6 +56,12 @@ namespace
win_emu.start();
}
}
+ catch (const std::exception& e)
+ {
+ win_emu.logger.print(color::red, "Emulation failed at: 0x%llX - %s\n",
+ win_emu.emu().read_instruction_pointer(), e.what());
+ throw;
+ }
catch (...)
{
win_emu.logger.print(color::red, "Emulation failed at: 0x%llX\n", win_emu.emu().read_instruction_pointer());
@@ -68,80 +79,158 @@ namespace
}
}
- std::vector parse_arguments(char* argv[], const size_t argc)
+ std::vector parse_arguments(const std::span args)
{
- std::vector args{};
- args.reserve(argc - 1);
+ std::vector wide_args{};
+ wide_args.reserve(args.size() - 1);
- for (size_t i = 1; i < argc; ++i)
+ for (size_t i = 1; i < args.size(); ++i)
{
- std::string_view arg(argv[i]);
- args.emplace_back(arg.begin(), arg.end());
+ const auto& arg = args[i];
+ wide_args.emplace_back(arg.begin(), arg.end());
}
- return args;
+ return wide_args;
}
- void run(char* argv[], const size_t argc)
+ void run(const analysis_options& options, const std::span args)
{
- if (argc < 1)
+ if (args.empty())
{
return;
}
- const emulator_settings settings{
- .application = argv[0],
- .arguments = parse_arguments(argv, argc),
+ emulator_settings settings{
+ .application = args[0],
+ .arguments = parse_arguments(args),
+ .silent_until_main = options.concise_logging,
};
- windows_emulator win_emu{settings};
+ windows_emulator win_emu{std::move(settings)};
(void)&watch_system_objects;
- //watch_system_objects(win_emu);
+ watch_system_objects(win_emu, options.concise_logging);
win_emu.buffer_stdout = true;
//win_emu.verbose_calls = true;
const auto& exe = *win_emu.process().executable;
- const auto text_start = exe.image_base + 0x1000;
- const auto text_end = exe.image_base + 0x52000;
- constexpr auto scan_size = 0x100;
+ const auto concise_logging = options.concise_logging;
- win_emu.emu().hook_memory_read(text_start, scan_size, [&](const uint64_t address, size_t, uint64_t)
+ for (const auto& section : exe.sections)
{
- const auto rip = win_emu.emu().read_instruction_pointer();
- if (rip >= text_start && rip < text_end)
+ if ((section.region.permissions & memory_permission::exec) != memory_permission::exec)
{
- win_emu.logger.print(color::green, "Reading from executable .text: 0x%llX at 0x%llX\n", address, rip);
+ continue;
}
- });
- run_emulation(win_emu);
+ auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t)
+ {
+ const auto rip = win_emu.emu().read_instruction_pointer();
+ if (win_emu.process().module_manager.find_by_address(rip) != win_emu.process().executable)
+ {
+ return;
+ }
+
+ if (concise_logging)
+ {
+ static uint64_t count{0};
+ ++count;
+ if (count > 100 && count % 10000 != 0) return;
+ }
+
+ win_emu.logger.print(
+ color::green,
+ "Reading from executable section %s at 0x%llX via 0x%llX\n",
+ section.name.c_str(), address, rip);
+ };
+
+ const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t)
+ {
+ const auto rip = win_emu.emu().read_instruction_pointer();
+ if (win_emu.process().module_manager.find_by_address(rip) != win_emu.process().executable)
+ {
+ return;
+ }
+
+ if (concise_logging)
+ {
+ static uint64_t count{0};
+ ++count;
+ if (count > 100 && count % 10000 != 0) return;
+ }
+
+ win_emu.logger.print(
+ color::blue,
+ "Writing to executable section %s at 0x%llX via 0x%llX\n",
+ section.name.c_str(), address, rip);
+ };
+
+ win_emu.emu().hook_memory_read(section.region.start, section.region.length, std::move(read_handler));
+ win_emu.emu().hook_memory_write(section.region.start, section.region.length, std::move(write_handler));
+ }
+
+ run_emulation(win_emu, options);
+ }
+
+ std::vector bundle_arguments(const int argc, char** argv)
+ {
+ std::vector args{};
+
+ for (int i = 1; i < argc; ++i)
+ {
+ args.push_back(argv[i]);
+ }
+
+ return args;
+ }
+
+ analysis_options parse_options(std::vector& args)
+ {
+ analysis_options options{};
+
+ while (!args.empty())
+ {
+ auto arg_it = args.begin();
+ const auto& arg = *arg_it;
+
+ if (arg == "-d")
+ {
+ options.use_gdb = true;
+ }
+ else if (arg == "-c")
+ {
+ options.concise_logging = true;
+ }
+ else
+ {
+ break;
+ }
+
+ args.erase(arg_it);
+ }
+
+ return options;
}
}
int main(const int argc, char** argv)
{
- if (argc <= 1)
- {
- puts("Application not specified!");
- return 1;
- }
-
- //setvbuf(stdout, nullptr, _IOFBF, 0x10000);
- if (argc > 2 && argv[1] == "-d"sv)
- {
- use_gdb = true;
- }
-
try
{
+ auto args = bundle_arguments(argc, argv);
+ const auto options = parse_options(args);
+
+ if (args.empty())
+ {
+ throw std::runtime_error("Application not specified!");
+ }
+
do
{
- const auto offset = use_gdb ? 2 : 1;
- run(argv + offset, static_cast(argc - offset));
+ run(options, args);
}
- while (use_gdb);
+ while (options.use_gdb);
return 0;
}
diff --git a/src/analyzer/object_watching.hpp b/src/analyzer/object_watching.hpp
index a9425668..4ebb62b8 100644
--- a/src/analyzer/object_watching.hpp
+++ b/src/analyzer/object_watching.hpp
@@ -3,19 +3,38 @@
#include "reflect_type_info.hpp"
template
-emulator_hook* watch_object(windows_emulator& emu, emulator_object object)
+emulator_hook* watch_object(windows_emulator& emu, emulator_object object, const bool cache_logging = false)
{
const reflect_type_info info{};
return emu.emu().hook_memory_read(object.value(), object.size(),
- [i = std::move(info), object, &emu](const uint64_t address, size_t, uint64_t)
+ [i = std::move(info), object, &emu, cache_logging](
+ const uint64_t address, size_t, uint64_t)
{
const auto rip = emu.emu().read_instruction_pointer();
+ const auto* mod = emu.process().module_manager.find_by_address(rip);
+ const auto is_main_access = mod == emu.process().executable;
+
+ if (!emu.verbose_calls && !is_main_access)
+ {
+ return;
+ }
+
+ if (cache_logging)
+ {
+ static std::unordered_set logged_addresses{};
+ if (is_main_access && !logged_addresses.insert(address).second)
+ {
+ return;
+ }
+ }
const auto offset = address - object.value();
- emu.logger.log("Object access: %s - 0x%llX (%s) at 0x%llX (%s)\n", i.get_type_name().c_str(),
- offset,
- i.get_member_name(offset).c_str(), rip,
- emu.process().module_manager.find_name(rip));
+ emu.logger.print(is_main_access ? color::green : color::dark_gray,
+ "Object access: %s - 0x%llX (%s) at 0x%llX (%s)\n",
+ i.get_type_name().c_str(),
+ offset,
+ i.get_member_name(offset).c_str(), rip,
+ mod ? mod->name.c_str() : "");
});
}
diff --git a/src/common/platform/file_management.hpp b/src/common/platform/file_management.hpp
index edd18d5f..0d9ead13 100644
--- a/src/common/platform/file_management.hpp
+++ b/src/common/platform/file_management.hpp
@@ -63,6 +63,10 @@
#define PS_ATTRIBUTE_INPUT 0x00020000 // input only
#define PS_ATTRIBUTE_ADDITIVE 0x00040000 // "accumulated" e.g. bitmasks, counters, etc.
+#define SL_RESTART_SCAN 0x01
+#define SL_RETURN_SINGLE_ENTRY 0x02
+#define SL_NO_CURSOR_UPDATE 0x10
+
typedef enum _FSINFOCLASS
{
FileFsVolumeInformation = 1, // q: FILE_FS_VOLUME_INFORMATION
@@ -267,6 +271,70 @@ typedef struct _FILE_STANDARD_INFORMATION
BOOLEAN Directory;
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
+typedef struct _FILE_NAME_INFORMATION
+{
+ ULONG FileNameLength;
+ char16_t FileName[1];
+} FILE_NAME_INFORMATION, * PFILE_NAME_INFORMATION;
+
+typedef struct _FILE_BASIC_INFORMATION
+{
+ LARGE_INTEGER CreationTime; // Specifies the time that the file was created.
+ LARGE_INTEGER LastAccessTime; // Specifies the time that the file was last accessed.
+ LARGE_INTEGER LastWriteTime; // Specifies the time that the file was last written to.
+ LARGE_INTEGER ChangeTime; // Specifies the last time the file was changed.
+ ULONG FileAttributes; // Specifies one or more FILE_ATTRIBUTE_XXX flags.
+} FILE_BASIC_INFORMATION, * PFILE_BASIC_INFORMATION;
+
+typedef struct _FILE_DIRECTORY_INFORMATION
+{
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ char16_t FileName[1];
+} FILE_DIRECTORY_INFORMATION, * PFILE_DIRECTORY_INFORMATION;
+
+typedef struct _FILE_FULL_DIR_INFORMATION
+{
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ ULONG EaSize;
+ char16_t FileName[1];
+} FILE_FULL_DIR_INFORMATION, * PFILE_FULL_DIR_INFORMATION;
+
+typedef struct _FILE_BOTH_DIR_INFORMATION
+{
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ ULONG EaSize;
+ CCHAR ShortNameLength;
+ WCHAR ShortName[12];
+ char16_t FileName[1];
+} FILE_BOTH_DIR_INFORMATION, * PFILE_BOTH_DIR_INFORMATION;
+
#ifndef OS_WINDOWS
typedef BOOLEAN SECURITY_CONTEXT_TRACKING_MODE,
* PSECURITY_CONTEXT_TRACKING_MODE;
diff --git a/src/common/platform/kernel_mapped.hpp b/src/common/platform/kernel_mapped.hpp
index ac3c040f..e01d7ca1 100644
--- a/src/common/platform/kernel_mapped.hpp
+++ b/src/common/platform/kernel_mapped.hpp
@@ -824,4 +824,51 @@ typedef struct _PROCESS_BASIC_INFORMATION64
EMULATOR_CAST(std::uint32_t, KPRIORITY) BasePriority;
EMULATOR_CAST(std::uint64_t, HANDLE) UniqueProcessId;
EMULATOR_CAST(std::uint64_t, HANDLE) InheritedFromUniqueProcessId;
-} PROCESS_BASIC_INFORMATION64, *PPROCESS_BASIC_INFORMATION64;
\ No newline at end of file
+} PROCESS_BASIC_INFORMATION64, *PPROCESS_BASIC_INFORMATION64;
+
+typedef struct _KERNEL_USER_TIMES
+{
+ LARGE_INTEGER CreateTime;
+ LARGE_INTEGER ExitTime;
+ LARGE_INTEGER KernelTime;
+ LARGE_INTEGER UserTime;
+} KERNEL_USER_TIMES, * PKERNEL_USER_TIMES;
+
+struct THREAD_TLS_INFO
+{
+ ULONG Flags;
+
+ union
+ {
+ EmulatorTraits::PVOID* TlsVector;
+ PVOID TlsModulePointer;
+ };
+
+ EMULATOR_CAST(std::uint64_t, ULONG_PTR) ThreadId;
+};
+
+static_assert(sizeof(THREAD_TLS_INFO) == 0x18);
+
+typedef enum _PROCESS_TLS_INFORMATION_TYPE
+{
+ ProcessTlsReplaceIndex,
+ ProcessTlsReplaceVector,
+ MaxProcessTlsOperation
+} PROCESS_TLS_INFORMATION_TYPE, * PPROCESS_TLS_INFORMATION_TYPE;
+
+struct PROCESS_TLS_INFO
+{
+ ULONG Unknown;
+ PROCESS_TLS_INFORMATION_TYPE TlsRequest;
+ ULONG ThreadDataCount;
+
+ union
+ {
+ ULONG TlsIndex;
+ ULONG TlsVectorLength;
+ };
+
+ THREAD_TLS_INFO ThreadData[1];
+};
+
+static_assert(sizeof(PROCESS_TLS_INFO) - sizeof(THREAD_TLS_INFO) == 0x10);
diff --git a/src/common/platform/process.hpp b/src/common/platform/process.hpp
index c8b70e41..f5784778 100644
--- a/src/common/platform/process.hpp
+++ b/src/common/platform/process.hpp
@@ -679,6 +679,17 @@ struct TOKEN_USER64 {
SID_AND_ATTRIBUTES64 User;
};
+typedef struct _TOKEN_SECURITY_ATTRIBUTES_INFORMATION
+{
+ USHORT Version;
+ USHORT Reserved;
+ ULONG AttributeCount;
+ union
+ {
+ EmulatorTraits::PVOID pAttributeV1;
+ } Attribute;
+} TOKEN_SECURITY_ATTRIBUTES_INFORMATION, * PTOKEN_SECURITY_ATTRIBUTES_INFORMATION;
+
struct GDI_HANDLE_ENTRY64
{
union
diff --git a/src/common/platform/status.hpp b/src/common/platform/status.hpp
index f60a6369..af8b8d8c 100644
--- a/src/common/platform/status.hpp
+++ b/src/common/platform/status.hpp
@@ -16,10 +16,15 @@ using NTSTATUS = std::uint32_t;
#endif
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0x00000001L)
#define STATUS_ALERTED ((NTSTATUS)0x00000101L)
+#define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L)
+
+#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
+
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
diff --git a/src/common/platform/synchronisation.hpp b/src/common/platform/synchronisation.hpp
index f65f4de1..c210de6f 100644
--- a/src/common/platform/synchronisation.hpp
+++ b/src/common/platform/synchronisation.hpp
@@ -5,4 +5,13 @@ typedef enum _EVENT_TYPE
{
NotificationEvent,
SynchronizationEvent
-} EVENT_TYPE;
\ No newline at end of file
+} EVENT_TYPE;
+
+typedef enum _WAIT_TYPE
+{
+ WaitAll,
+ WaitAny,
+ WaitNotification,
+ WaitDequeue,
+ WaitDpc,
+} WAIT_TYPE;
diff --git a/src/common/platform/unicode.hpp b/src/common/platform/unicode.hpp
index 3171b9fa..c0f45dc1 100644
--- a/src/common/platform/unicode.hpp
+++ b/src/common/platform/unicode.hpp
@@ -9,7 +9,7 @@ struct UNICODE_STRING {
EMULATOR_CAST(typename Traits::PVOID, char16_t*) Buffer;
};
-inline std::string u16_to_u8(std::u16string_view u16_view) {
+inline std::string u16_to_u8(const std::u16string_view u16_view) {
std::string utf8_str;
utf8_str.reserve(u16_view.size() * 2);
for (char16_t ch : u16_view) {
@@ -27,7 +27,7 @@ inline std::string u16_to_u8(std::u16string_view u16_view) {
return utf8_str;
}
-inline std::string w_to_u8(std::wstring_view w_view) {
+inline std::string w_to_u8(const std::wstring_view w_view) {
std::string utf8_str;
utf8_str.reserve(w_view.size() * 2);
for (char16_t ch : w_view) {
@@ -56,7 +56,7 @@ inline std::string w_to_u8(std::wstring_view w_view) {
return std::wstring(reinterpret_cast(u16str.data()), u16str.size());
}
- inline auto open_unicode(FILE** handle, std::u16string fileName, std::u16string mode)
+ inline auto open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode)
{
return _wfopen_s(handle, u16_to_w(fileName).c_str(), u16_to_w(mode).c_str());
}
diff --git a/src/common/utils/string.hpp b/src/common/utils/string.hpp
new file mode 100644
index 00000000..dac98317
--- /dev/null
+++ b/src/common/utils/string.hpp
@@ -0,0 +1,50 @@
+#pragma once
+#include
+#include
+#include
+#include
+
+namespace utils::string
+{
+ inline char char_to_lower(const char val)
+ {
+ return static_cast(std::tolower(static_cast(val)));
+ }
+
+ inline char16_t char_to_lower(const char16_t val)
+ {
+ if (val >= u'A' && val <= u'Z')
+ {
+ return val + 32;
+ }
+
+ return val;
+ }
+
+ inline wchar_t char_to_lower(const wchar_t val)
+ {
+ return std::towlower(val);
+ }
+
+ template
+ void to_lower_inplace(std::basic_string& str)
+ {
+ std::ranges::transform(str, str.begin(), [](const Elem e)
+ {
+ return char_to_lower(e);
+ });
+ }
+
+ template
+ std::basic_string to_lower(std::basic_string str)
+ {
+ to_lower_inplace(str);
+ return str;
+ }
+
+ template
+ std::basic_string to_lower_consume(std::basic_string& str)
+ {
+ return to_lower(std::move(str));
+ }
+}
diff --git a/src/emulator/memory_manager.cpp b/src/emulator/memory_manager.cpp
index 89719379..1f076c33 100644
--- a/src/emulator/memory_manager.cpp
+++ b/src/emulator/memory_manager.cpp
@@ -1,547 +1,547 @@
-#include "memory_manager.hpp"
-
-#include "memory_region.hpp"
-#include "address_utils.hpp"
-
-#include
-#include
-#include
-#include
-
-namespace
-{
- constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL;
- constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL;
-
- void split_regions(memory_manager::committed_region_map& regions, const std::vector& split_points)
- {
- for (auto i = regions.begin(); i != regions.end(); ++i)
- {
- for (const auto split_point : split_points)
- {
- if (is_within_start_and_length(split_point, i->first, i->second.length) && i->first != split_point)
- {
- const auto first_length = split_point - i->first;
- const auto second_length = i->second.length - first_length;
-
- i->second.length = first_length;
-
- regions[split_point] = memory_manager::committed_region{second_length, i->second.pemissions};
- }
- }
- }
- }
-
- void merge_regions(memory_manager::committed_region_map& regions)
- {
- for (auto i = regions.begin(); i != regions.end();)
- {
- assert(i->second.length > 0);
-
- auto next = i;
- std::advance(next, 1);
-
- if (next == regions.end())
- {
- break;
- }
-
- assert(next->second.length > 0);
-
- const auto end = i->first + i->second.length;
- assert(end <= next->first);
-
- if (end != next->first || i->second.pemissions != next->second.pemissions)
- {
- ++i;
- continue;
- }
-
- i->second.length += next->second.length;
- regions.erase(next);
- }
- }
-}
-
-static void serialize(utils::buffer_serializer& buffer, const memory_manager::committed_region& region)
-{
- buffer.write(region.length);
- buffer.write(region.pemissions);
-}
-
-static void deserialize(utils::buffer_deserializer& buffer, memory_manager::committed_region& region)
-{
- region.length = static_cast(buffer.read());
- region.pemissions = buffer.read();
-}
-
-static void serialize(utils::buffer_serializer& buffer, const memory_manager::reserved_region& region)
-{
- buffer.write(region.is_mmio);
- buffer.write(region.length);
- buffer.write_map(region.committed_regions);
-}
-
-static void deserialize(utils::buffer_deserializer& buffer, memory_manager::reserved_region& region)
-{
- buffer.read(region.is_mmio);
- region.length = static_cast(buffer.read());
- buffer.read_map(region.committed_regions);
-}
-
-void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const
-{
- buffer.write_map(this->reserved_regions_);
-
- if (is_snapshot)
- {
- return;
- }
-
- std::vector data{};
-
- for (const auto& reserved_region : this->reserved_regions_)
- {
- if (reserved_region.second.is_mmio)
- {
- continue;
- }
-
- for (const auto& region : reserved_region.second.committed_regions)
- {
- data.resize(region.second.length);
-
- this->read_memory(region.first, data.data(), region.second.length);
-
- buffer.write(data.data(), region.second.length);
- }
- }
-}
-
-void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer, const bool is_snapshot)
-{
- if (!is_snapshot)
- {
- for (const auto& reserved_region : this->reserved_regions_)
- {
- for (const auto& region : reserved_region.second.committed_regions)
- {
- this->unmap_memory(region.first, region.second.length);
- }
- }
- }
-
- buffer.read_map(this->reserved_regions_);
-
- if (is_snapshot)
- {
- return;
- }
-
- std::vector data{};
-
- for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
- {
- auto& reserved_region = i->second;
- if (reserved_region.is_mmio)
- {
- i = this->reserved_regions_.erase(i);
- continue;
- }
-
- ++i;
-
- for (const auto& region : reserved_region.committed_regions)
- {
- data.resize(region.second.length);
-
- buffer.read(data.data(), region.second.length);
-
- this->map_memory(region.first, region.second.length, region.second.pemissions);
- this->write_memory(region.first, data.data(), region.second.length);
- }
- }
-}
-
-bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions,
- memory_permission* old_permissions)
-{
- const auto entry = this->find_reserved_region(address);
- if (entry == this->reserved_regions_.end())
- {
- return false;
- }
-
- const auto end = address + size;
- const auto region_end = entry->first + entry->second.length;
-
- if (region_end < end)
- {
- throw std::runtime_error("Cross region protect not supported yet!");
- }
-
- std::optional old_first_permissions{};
-
- auto& committed_regions = entry->second.committed_regions;
- split_regions(committed_regions, {address, end});
-
- for (auto& sub_region : committed_regions)
- {
- if (sub_region.first >= end)
- {
- break;
- }
-
- const auto sub_region_end = sub_region.first + sub_region.second.length;
- if (sub_region.first >= address && sub_region_end <= end)
- {
- if (!old_first_permissions.has_value())
- {
- old_first_permissions = sub_region.second.pemissions;
- }
-
- this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
- sub_region.second.pemissions = permissions;
- }
- }
-
- if (old_permissions)
- {
- *old_permissions = old_first_permissions.value_or(memory_permission::none);
- }
-
- merge_regions(committed_regions);
- return true;
-}
-
-bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
- mmio_write_callback write_cb)
-{
- if (this->overlaps_reserved_region(address, size))
- {
- return false;
- }
-
- this->map_mmio(address, size, std::move(read_cb), std::move(write_cb));
-
- const auto entry = this->reserved_regions_.try_emplace(address,
- reserved_region{
- .length = size,
- .is_mmio = true,
- }).first;
-
- entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
-
- return true;
-}
-
-bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions,
- const bool reserve_only)
-{
- if (this->overlaps_reserved_region(address, size))
- {
- return false;
- }
-
- const auto entry = this->reserved_regions_.try_emplace(address, size).first;
-
- if (!reserve_only)
- {
- this->map_memory(address, size, permissions);
- entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
- }
-
- return true;
-}
-
-bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions)
-{
- const auto entry = this->find_reserved_region(address);
- if (entry == this->reserved_regions_.end())
- {
- return false;
- }
-
- const auto end = address + size;
- const auto region_end = entry->first + entry->second.length;
-
- if (region_end < end)
- {
- throw std::runtime_error("Cross region commit not supported yet!");
- }
-
- auto& committed_regions = entry->second.committed_regions;
- split_regions(committed_regions, {address, end});
-
- uint64_t last_region_start{};
- const committed_region* last_region{nullptr};
-
- for (auto& sub_region : committed_regions)
- {
- if (sub_region.first >= end)
- {
- break;
- }
-
- const auto sub_region_end = sub_region.first + sub_region.second.length;
- if (sub_region.first >= address && sub_region_end <= end)
- {
- const auto map_start = last_region ? (last_region_start + last_region->length) : address;
- const auto map_length = sub_region.first - map_start;
-
- if (map_length > 0)
- {
- this->map_memory(map_start, map_length, permissions);
- committed_regions[map_start] = committed_region{map_length, permissions};
- }
-
- last_region_start = sub_region.first;
- last_region = &sub_region.second;
- }
- }
-
- if (!last_region || (last_region_start + last_region->length) < end)
- {
- 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, map_length, permissions);
- committed_regions[map_start] = committed_region{map_length, permissions};
- }
-
- merge_regions(committed_regions);
- return true;
-}
-
-bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
-{
- const auto entry = this->find_reserved_region(address);
- if (entry == this->reserved_regions_.end())
- {
- return false;
- }
-
- if (entry->second.is_mmio)
- {
- throw std::runtime_error("Not allowed to decommit MMIO!");
- }
-
- const auto end = address + size;
- const auto region_end = entry->first + entry->second.length;
-
- if (region_end < end)
- {
- throw std::runtime_error("Cross region decommit not supported yet!");
- }
-
- auto& committed_regions = entry->second.committed_regions;
-
- split_regions(committed_regions, {address, end});
-
- for (auto i = committed_regions.begin(); i != committed_regions.end();)
- {
- if (i->first >= end)
- {
- break;
- }
-
- const auto sub_region_end = i->first + i->second.length;
- if (i->first >= address && sub_region_end <= end)
- {
- this->unmap_memory(i->first, i->second.length);
- i = committed_regions.erase(i);
- continue;
- }
-
- ++i;
- }
-
- return true;
-}
-
-bool memory_manager::release_memory(const uint64_t address, size_t size)
-{
- const auto entry = this->reserved_regions_.find(address);
- if (entry == this->reserved_regions_.end())
- {
- return false;
- }
-
- if (!size)
- {
- size = entry->second.length;
- }
-
- if (size > entry->second.length)
- {
- throw std::runtime_error("Cross region release not supported yet!");
- }
-
- const auto end = address + size;
- auto& committed_regions = entry->second.committed_regions;
-
- split_regions(committed_regions, {end});
-
- for (auto i = committed_regions.begin(); i != committed_regions.end();)
- {
- if (i->first >= end)
- {
- break;
- }
-
- const auto sub_region_end = i->first + i->second.length;
- if (i->first >= address && sub_region_end <= end)
- {
- this->unmap_memory(i->first, i->second.length);
- i = committed_regions.erase(i);
- }
- else
- {
- ++i;
- }
- }
-
- entry->second.length -= size;
- if (entry->second.length > 0)
- {
- this->reserved_regions_[address + size] = std::move(entry->second);
- }
-
- this->reserved_regions_.erase(entry);
- return true;
-}
-
-uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint64_t start) const
-{
- uint64_t start_address =
- std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL);
-
- for (const auto& region : this->reserved_regions_)
- {
- const auto region_end = region.first + region.second.length;
- if (region_end < start_address)
- {
- continue;
- }
-
- if (!regions_with_length_intersect(start_address, size, region.first, region.second.length))
- {
- return start_address;
- }
-
- start_address = page_align_up(region_end);
- }
-
- if (start_address + size <= MAX_ALLOCATION_ADDRESS)
- {
- return start_address;
- }
-
- return 0;
-}
-
-region_info memory_manager::get_region_info(const uint64_t address)
-{
- region_info result{};
- result.start = MIN_ALLOCATION_ADDRESS;
- result.length = MAX_ALLOCATION_ADDRESS - result.start;
- result.pemissions = memory_permission::none;
- result.allocation_base = {};
- result.allocation_length = result.length;
- result.is_committed = false;
- result.is_reserved = false;
-
- if (this->reserved_regions_.empty())
- {
- return result;
- }
-
- auto upper_bound = this->reserved_regions_.upper_bound(address);
- if (upper_bound == this->reserved_regions_.begin())
- {
- result.length = upper_bound->first - result.start;
- return result;
- }
-
- const auto entry = --upper_bound;
- const auto lower_end = entry->first + entry->second.length;
- if (lower_end <= address)
- {
- result.start = lower_end;
- result.length = MAX_ALLOCATION_ADDRESS - result.start;
- return result;
- }
-
- // We have a reserved region
- const auto& reserved_region = entry->second;
- const auto& committed_regions = reserved_region.committed_regions;
-
- result.is_reserved = true;
- result.allocation_base = entry->first;
- result.allocation_length = reserved_region.length;
- result.start = result.allocation_base;
- result.length = result.allocation_length;
-
- if (committed_regions.empty())
- {
- return result;
- }
-
- auto committed_bound = committed_regions.upper_bound(address);
- if (committed_bound == committed_regions.begin())
- {
- result.length = committed_bound->first - result.start;
- return result;
- }
-
- const auto committed_entry = --committed_bound;
- const auto committed_lower_end = committed_entry->first + committed_entry->second.length;
- if (committed_lower_end <= address)
- {
- result.start = committed_lower_end;
- result.length = lower_end - result.start;
- return result;
- }
-
- result.is_committed = true;
- result.start = committed_entry->first;
- result.length = committed_entry->second.length;
- result.pemissions = committed_entry->second.pemissions;
-
- return result;
-}
-
-memory_manager::reserved_region_map::iterator memory_manager::find_reserved_region(const uint64_t address)
-{
- if (this->reserved_regions_.empty())
- {
- return this->reserved_regions_.end();
- }
-
- auto upper_bound = this->reserved_regions_.upper_bound(address);
- if (upper_bound == this->reserved_regions_.begin())
- {
- return this->reserved_regions_.end();
- }
-
- const auto entry = --upper_bound;
- if (entry->first + entry->second.length <= address)
- {
- return this->reserved_regions_.end();
- }
-
- return entry;
-}
-
-bool memory_manager::overlaps_reserved_region(const uint64_t address, const size_t size) const
-{
- for (const auto& region : this->reserved_regions_)
- {
- if (regions_with_length_intersect(address, size, region.first, region.second.length))
- {
- return true;
- }
- }
-
- return false;
-}
+#include "memory_manager.hpp"
+
+#include "memory_region.hpp"
+#include "address_utils.hpp"
+
+#include
+#include
+#include
+#include
+
+namespace
+{
+ constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL;
+ constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL;
+
+ void split_regions(memory_manager::committed_region_map& regions, const std::vector& split_points)
+ {
+ for (auto i = regions.begin(); i != regions.end(); ++i)
+ {
+ for (const auto split_point : split_points)
+ {
+ if (is_within_start_and_length(split_point, i->first, i->second.length) && i->first != split_point)
+ {
+ const auto first_length = split_point - i->first;
+ const auto second_length = i->second.length - first_length;
+
+ i->second.length = first_length;
+
+ regions[split_point] = memory_manager::committed_region{second_length, i->second.pemissions};
+ }
+ }
+ }
+ }
+
+ void merge_regions(memory_manager::committed_region_map& regions)
+ {
+ for (auto i = regions.begin(); i != regions.end();)
+ {
+ assert(i->second.length > 0);
+
+ auto next = i;
+ std::advance(next, 1);
+
+ if (next == regions.end())
+ {
+ break;
+ }
+
+ assert(next->second.length > 0);
+
+ const auto end = i->first + i->second.length;
+ assert(end <= next->first);
+
+ if (end != next->first || i->second.pemissions != next->second.pemissions)
+ {
+ ++i;
+ continue;
+ }
+
+ i->second.length += next->second.length;
+ regions.erase(next);
+ }
+ }
+}
+
+static void serialize(utils::buffer_serializer& buffer, const memory_manager::committed_region& region)
+{
+ buffer.write(region.length);
+ buffer.write(region.pemissions);
+}
+
+static void deserialize(utils::buffer_deserializer& buffer, memory_manager::committed_region& region)
+{
+ region.length = static_cast(buffer.read());
+ region.pemissions = buffer.read();
+}
+
+static void serialize(utils::buffer_serializer& buffer, const memory_manager::reserved_region& region)
+{
+ buffer.write(region.is_mmio);
+ buffer.write(region.length);
+ buffer.write_map(region.committed_regions);
+}
+
+static void deserialize(utils::buffer_deserializer& buffer, memory_manager::reserved_region& region)
+{
+ buffer.read(region.is_mmio);
+ region.length = static_cast(buffer.read());
+ buffer.read_map(region.committed_regions);
+}
+
+void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const
+{
+ buffer.write_map(this->reserved_regions_);
+
+ if (is_snapshot)
+ {
+ return;
+ }
+
+ std::vector data{};
+
+ for (const auto& reserved_region : this->reserved_regions_)
+ {
+ if (reserved_region.second.is_mmio)
+ {
+ continue;
+ }
+
+ for (const auto& region : reserved_region.second.committed_regions)
+ {
+ data.resize(region.second.length);
+
+ this->read_memory(region.first, data.data(), region.second.length);
+
+ buffer.write(data.data(), region.second.length);
+ }
+ }
+}
+
+void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer, const bool is_snapshot)
+{
+ if (!is_snapshot)
+ {
+ for (const auto& reserved_region : this->reserved_regions_)
+ {
+ for (const auto& region : reserved_region.second.committed_regions)
+ {
+ this->unmap_memory(region.first, region.second.length);
+ }
+ }
+ }
+
+ buffer.read_map(this->reserved_regions_);
+
+ if (is_snapshot)
+ {
+ return;
+ }
+
+ std::vector data{};
+
+ for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
+ {
+ auto& reserved_region = i->second;
+ if (reserved_region.is_mmio)
+ {
+ i = this->reserved_regions_.erase(i);
+ continue;
+ }
+
+ ++i;
+
+ for (const auto& region : reserved_region.committed_regions)
+ {
+ data.resize(region.second.length);
+
+ buffer.read(data.data(), region.second.length);
+
+ this->map_memory(region.first, region.second.length, region.second.pemissions);
+ this->write_memory(region.first, data.data(), region.second.length);
+ }
+ }
+}
+
+bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions,
+ memory_permission* old_permissions)
+{
+ const auto entry = this->find_reserved_region(address);
+ if (entry == this->reserved_regions_.end())
+ {
+ return false;
+ }
+
+ const auto end = address + size;
+ const auto region_end = entry->first + entry->second.length;
+
+ if (region_end < end)
+ {
+ throw std::runtime_error("Cross region protect not supported yet!");
+ }
+
+ std::optional old_first_permissions{};
+
+ auto& committed_regions = entry->second.committed_regions;
+ split_regions(committed_regions, {address, end});
+
+ for (auto& sub_region : committed_regions)
+ {
+ if (sub_region.first >= end)
+ {
+ break;
+ }
+
+ const auto sub_region_end = sub_region.first + sub_region.second.length;
+ if (sub_region.first >= address && sub_region_end <= end)
+ {
+ if (!old_first_permissions.has_value())
+ {
+ old_first_permissions = sub_region.second.pemissions;
+ }
+
+ this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
+ sub_region.second.pemissions = permissions;
+ }
+ }
+
+ if (old_permissions)
+ {
+ *old_permissions = old_first_permissions.value_or(memory_permission::none);
+ }
+
+ merge_regions(committed_regions);
+ return true;
+}
+
+bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
+ mmio_write_callback write_cb)
+{
+ if (this->overlaps_reserved_region(address, size))
+ {
+ return false;
+ }
+
+ this->map_mmio(address, size, std::move(read_cb), std::move(write_cb));
+
+ const auto entry = this->reserved_regions_.try_emplace(address,
+ reserved_region{
+ .length = size,
+ .is_mmio = true,
+ }).first;
+
+ entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
+
+ return true;
+}
+
+bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions,
+ const bool reserve_only)
+{
+ if (this->overlaps_reserved_region(address, size))
+ {
+ return false;
+ }
+
+ const auto entry = this->reserved_regions_.try_emplace(address, size).first;
+
+ if (!reserve_only)
+ {
+ this->map_memory(address, size, permissions);
+ entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
+ }
+
+ return true;
+}
+
+bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions)
+{
+ const auto entry = this->find_reserved_region(address);
+ if (entry == this->reserved_regions_.end())
+ {
+ return false;
+ }
+
+ const auto end = address + size;
+ const auto region_end = entry->first + entry->second.length;
+
+ if (region_end < end)
+ {
+ throw std::runtime_error("Cross region commit not supported yet!");
+ }
+
+ auto& committed_regions = entry->second.committed_regions;
+ split_regions(committed_regions, {address, end});
+
+ uint64_t last_region_start{};
+ const committed_region* last_region{nullptr};
+
+ for (auto& sub_region : committed_regions)
+ {
+ if (sub_region.first >= end)
+ {
+ break;
+ }
+
+ const auto sub_region_end = sub_region.first + sub_region.second.length;
+ if (sub_region.first >= address && sub_region_end <= end)
+ {
+ const auto map_start = last_region ? (last_region_start + last_region->length) : address;
+ const auto map_length = sub_region.first - map_start;
+
+ if (map_length > 0)
+ {
+ this->map_memory(map_start, map_length, permissions);
+ committed_regions[map_start] = committed_region{map_length, permissions};
+ }
+
+ last_region_start = sub_region.first;
+ last_region = &sub_region.second;
+ }
+ }
+
+ if (!last_region || (last_region_start + last_region->length) < end)
+ {
+ 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, map_length, permissions);
+ committed_regions[map_start] = committed_region{map_length, permissions};
+ }
+
+ merge_regions(committed_regions);
+ return true;
+}
+
+bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
+{
+ const auto entry = this->find_reserved_region(address);
+ if (entry == this->reserved_regions_.end())
+ {
+ return false;
+ }
+
+ if (entry->second.is_mmio)
+ {
+ throw std::runtime_error("Not allowed to decommit MMIO!");
+ }
+
+ const auto end = address + size;
+ const auto region_end = entry->first + entry->second.length;
+
+ if (region_end < end)
+ {
+ throw std::runtime_error("Cross region decommit not supported yet!");
+ }
+
+ auto& committed_regions = entry->second.committed_regions;
+
+ split_regions(committed_regions, {address, end});
+
+ for (auto i = committed_regions.begin(); i != committed_regions.end();)
+ {
+ if (i->first >= end)
+ {
+ break;
+ }
+
+ const auto sub_region_end = i->first + i->second.length;
+ if (i->first >= address && sub_region_end <= end)
+ {
+ this->unmap_memory(i->first, i->second.length);
+ i = committed_regions.erase(i);
+ continue;
+ }
+
+ ++i;
+ }
+
+ return true;
+}
+
+bool memory_manager::release_memory(const uint64_t address, size_t size)
+{
+ const auto entry = this->reserved_regions_.find(address);
+ if (entry == this->reserved_regions_.end())
+ {
+ return false;
+ }
+
+ if (!size)
+ {
+ size = entry->second.length;
+ }
+
+ if (size > entry->second.length)
+ {
+ throw std::runtime_error("Cross region release not supported yet!");
+ }
+
+ const auto end = address + size;
+ auto& committed_regions = entry->second.committed_regions;
+
+ split_regions(committed_regions, {end});
+
+ for (auto i = committed_regions.begin(); i != committed_regions.end();)
+ {
+ if (i->first >= end)
+ {
+ break;
+ }
+
+ const auto sub_region_end = i->first + i->second.length;
+ if (i->first >= address && sub_region_end <= end)
+ {
+ this->unmap_memory(i->first, i->second.length);
+ i = committed_regions.erase(i);
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ entry->second.length -= size;
+ if (entry->second.length > 0)
+ {
+ this->reserved_regions_[address + size] = std::move(entry->second);
+ }
+
+ this->reserved_regions_.erase(entry);
+ return true;
+}
+
+uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint64_t start) const
+{
+ uint64_t start_address =
+ std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL);
+
+ for (const auto& region : this->reserved_regions_)
+ {
+ const auto region_end = region.first + region.second.length;
+ if (region_end < start_address)
+ {
+ continue;
+ }
+
+ if (!regions_with_length_intersect(start_address, size, region.first, region.second.length))
+ {
+ return start_address;
+ }
+
+ start_address = page_align_up(region_end);
+ }
+
+ if (start_address + size <= MAX_ALLOCATION_ADDRESS)
+ {
+ return start_address;
+ }
+
+ return 0;
+}
+
+region_info memory_manager::get_region_info(const uint64_t address)
+{
+ region_info result{};
+ result.start = MIN_ALLOCATION_ADDRESS;
+ result.length = MAX_ALLOCATION_ADDRESS - result.start;
+ result.permissions = memory_permission::none;
+ result.allocation_base = {};
+ result.allocation_length = result.length;
+ result.is_committed = false;
+ result.is_reserved = false;
+
+ if (this->reserved_regions_.empty())
+ {
+ return result;
+ }
+
+ auto upper_bound = this->reserved_regions_.upper_bound(address);
+ if (upper_bound == this->reserved_regions_.begin())
+ {
+ result.length = upper_bound->first - result.start;
+ return result;
+ }
+
+ const auto entry = --upper_bound;
+ const auto lower_end = entry->first + entry->second.length;
+ if (lower_end <= address)
+ {
+ result.start = lower_end;
+ result.length = MAX_ALLOCATION_ADDRESS - result.start;
+ return result;
+ }
+
+ // We have a reserved region
+ const auto& reserved_region = entry->second;
+ const auto& committed_regions = reserved_region.committed_regions;
+
+ result.is_reserved = true;
+ result.allocation_base = entry->first;
+ result.allocation_length = reserved_region.length;
+ result.start = result.allocation_base;
+ result.length = result.allocation_length;
+
+ if (committed_regions.empty())
+ {
+ return result;
+ }
+
+ auto committed_bound = committed_regions.upper_bound(address);
+ if (committed_bound == committed_regions.begin())
+ {
+ result.length = committed_bound->first - result.start;
+ return result;
+ }
+
+ const auto committed_entry = --committed_bound;
+ const auto committed_lower_end = committed_entry->first + committed_entry->second.length;
+ if (committed_lower_end <= address)
+ {
+ result.start = committed_lower_end;
+ result.length = lower_end - result.start;
+ return result;
+ }
+
+ result.is_committed = true;
+ result.start = committed_entry->first;
+ result.length = committed_entry->second.length;
+ result.permissions = committed_entry->second.pemissions;
+
+ return result;
+}
+
+memory_manager::reserved_region_map::iterator memory_manager::find_reserved_region(const uint64_t address)
+{
+ if (this->reserved_regions_.empty())
+ {
+ return this->reserved_regions_.end();
+ }
+
+ auto upper_bound = this->reserved_regions_.upper_bound(address);
+ if (upper_bound == this->reserved_regions_.begin())
+ {
+ return this->reserved_regions_.end();
+ }
+
+ const auto entry = --upper_bound;
+ if (entry->first + entry->second.length <= address)
+ {
+ return this->reserved_regions_.end();
+ }
+
+ return entry;
+}
+
+bool memory_manager::overlaps_reserved_region(const uint64_t address, const size_t size) const
+{
+ for (const auto& region : this->reserved_regions_)
+ {
+ if (regions_with_length_intersect(address, size, region.first, region.second.length))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/emulator/memory_manager.hpp b/src/emulator/memory_manager.hpp
index d78d43aa..0d7bbb98 100644
--- a/src/emulator/memory_manager.hpp
+++ b/src/emulator/memory_manager.hpp
@@ -71,6 +71,12 @@ public:
this->write_memory(address, &value, sizeof(value));
}
+ template
+ void write_memory(void* address, const T& value)
+ {
+ this->write_memory(reinterpret_cast(address), &value, sizeof(value));
+ }
+
void write_memory(void* address, const void* data, const size_t size)
{
this->write_memory(reinterpret_cast(address), data, size);
diff --git a/src/emulator/memory_region.hpp b/src/emulator/memory_region.hpp
index d3c533f3..880f3666 100644
--- a/src/emulator/memory_region.hpp
+++ b/src/emulator/memory_region.hpp
@@ -6,7 +6,7 @@ struct basic_memory_region
{
uint64_t start{};
size_t length{};
- memory_permission pemissions{};
+ memory_permission permissions{};
};
struct memory_region : basic_memory_region
diff --git a/src/fuzzer/main.cpp b/src/fuzzer/main.cpp
index f075001f..4cc4bc5d 100644
--- a/src/fuzzer/main.cpp
+++ b/src/fuzzer/main.cpp
@@ -144,11 +144,11 @@ namespace
void run(const std::string_view application)
{
- const emulator_settings settings{
+ emulator_settings settings{
.application = application,
};
- windows_emulator win_emu{settings};
+ windows_emulator win_emu{std::move(settings)};
forward_emulator(win_emu);
run_fuzzer(win_emu);
diff --git a/src/test-sample/test.cpp b/src/test-sample/test.cpp
index e8a8460f..6842432c 100644
--- a/src/test-sample/test.cpp
+++ b/src/test-sample/test.cpp
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
#include
#include
@@ -16,6 +17,18 @@ using namespace std::literals;
// to trick compiler optimizations
__declspec(dllexport) bool do_the_task = true;
+struct tls_struct
+{
+ DWORD num = 1337;
+
+ tls_struct()
+ {
+ num = GetCurrentThreadId();
+ }
+};
+
+thread_local tls_struct tls_var{};
+
// getenv is broken right now :(
std::string read_env(const char* env)
{
@@ -58,6 +71,50 @@ bool test_threads()
return counter == (thread_count * 3ULL);
}
+bool test_tls()
+{
+ std::atomic_bool kill{false};
+ std::atomic_uint32_t successes{0};
+ constexpr uint32_t thread_count = 2;
+
+ std::vector ts{};
+ kill = false;
+
+ for (size_t i = 0; i < thread_count; ++i)
+ {
+ ts.emplace_back([&]
+ {
+ while (!kill)
+ {
+ std::this_thread::yield();
+ }
+
+ if (tls_var.num == GetCurrentThreadId())
+ {
+ ++successes;
+ }
+ });
+ }
+
+ LoadLibraryA("d3dcompiler_47.dll");
+ LoadLibraryA("dsound.dll");
+ /*LoadLibraryA("d3d9.dll");
+ LoadLibraryA("dxgi.dll");
+ LoadLibraryA("wlanapi.dll");*/
+
+ kill = true;
+
+ for (auto& t : ts)
+ {
+ if (t.joinable())
+ {
+ t.join();
+ }
+ }
+
+ return successes == thread_count;
+}
+
bool test_env()
{
const auto computername = read_env("COMPUTERNAME");
@@ -97,6 +154,22 @@ bool test_io()
return text == buffer;
}
+bool test_dir_io()
+{
+ size_t count = 0;
+
+ for (auto i : std::filesystem::directory_iterator(R"(C:\Windows\System32\)"))
+ {
+ ++count;
+ if (count > 30)
+ {
+ return true;
+ }
+ }
+
+ return count > 30;
+}
+
std::optional read_registry_string(const HKEY root, const char* path, const char* value)
{
HKEY key{};
@@ -161,7 +234,7 @@ bool test_exceptions()
}
}
-void throw_native_exception()
+void throw_access_violation()
{
if (do_the_task)
{
@@ -169,19 +242,55 @@ void throw_native_exception()
}
}
-bool test_native_exceptions()
+bool test_access_violation_exception()
{
__try
{
- throw_native_exception();
+ throw_access_violation();
return false;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
- return true;
+ return GetExceptionCode() == STATUS_ACCESS_VIOLATION;
}
}
+bool test_ud2_exception(void* address)
+{
+ __try
+ {
+ static_cast(address)();
+ return false;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return GetExceptionCode() == STATUS_ILLEGAL_INSTRUCTION;
+ }
+}
+
+bool test_illegal_instruction_exception()
+{
+ const auto address = VirtualAlloc(nullptr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ if (!address)
+ {
+ return false;
+ }
+
+ memcpy(address, "\x0F\x0B", 2); // ud2
+
+ const auto res = test_ud2_exception(address);
+
+ VirtualFree(address, 0x1000, MEM_RELEASE);
+
+ return res;
+}
+
+bool test_native_exceptions()
+{
+ return test_access_violation_exception()
+ && test_illegal_instruction_exception();
+}
+
void print_time()
{
const auto epoch_time = std::chrono::system_clock::now().time_since_epoch();
@@ -198,7 +307,7 @@ void print_time()
int main(int argc, const char* argv[])
{
- if(argc == 2 && argv[1] == "-time"sv)
+ if (argc == 2 && argv[1] == "-time"sv)
{
print_time();
return 0;
@@ -207,11 +316,13 @@ int main(int argc, const char* argv[])
bool valid = true;
RUN_TEST(test_io, "I/O")
+ RUN_TEST(test_dir_io, "Dir I/O")
RUN_TEST(test_registry, "Registry")
RUN_TEST(test_threads, "Threads")
RUN_TEST(test_env, "Environment")
RUN_TEST(test_exceptions, "Exceptions")
RUN_TEST(test_native_exceptions, "Native Exceptions")
+ RUN_TEST(test_tls, "TLS")
return valid ? 0 : 1;
}
diff --git a/src/unicorn-emulator/unicorn_x64_emulator.cpp b/src/unicorn-emulator/unicorn_x64_emulator.cpp
index dd9caf77..4152e7de 100644
--- a/src/unicorn-emulator/unicorn_x64_emulator.cpp
+++ b/src/unicorn-emulator/unicorn_x64_emulator.cpp
@@ -243,6 +243,7 @@ namespace unicorn
unicorn_x64_emulator()
{
uce(uc_open(UC_ARCH_X86, UC_MODE_64, &this->uc_));
+ uce(uc_ctl_set_tcg_buffer_size(this->uc_, 2 << 30 /* 2 gb */));
}
~unicorn_x64_emulator() override
diff --git a/src/windows-emulator-test/emulation_test_utils.hpp b/src/windows-emulator-test/emulation_test_utils.hpp
index c45db025..cc934220 100644
--- a/src/windows-emulator-test/emulation_test_utils.hpp
+++ b/src/windows-emulator-test/emulation_test_utils.hpp
@@ -20,10 +20,10 @@
namespace test
{
- inline windows_emulator create_sample_emulator(emulator_settings& settings)
+ inline windows_emulator create_sample_emulator(emulator_settings settings)
{
settings.application = "./test-sample.exe";
- return windows_emulator{settings};
+ return windows_emulator{std::move(settings)};
}
inline windows_emulator create_sample_emulator()
@@ -34,6 +34,6 @@ namespace test
.use_relative_time = true,
};
- return create_sample_emulator(settings);
+ return create_sample_emulator(std::move(settings));
}
}
diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp
index 8e561925..2f7d30c1 100644
--- a/src/windows-emulator/emulator_utils.hpp
+++ b/src/windows-emulator/emulator_utils.hpp
@@ -5,7 +5,7 @@
// TODO: Replace with pointer handling structure for future 32 bit support
using emulator_pointer = uint64_t;
-template
+template
class object_wrapper
{
T* obj_;
@@ -101,6 +101,14 @@ public:
this->emu_->write_memory(this->address_ + index * this->size(), &value, sizeof(value));
}
+ void write_if_valid(const T& value, const size_t index = 0) const
+ {
+ if (this->operator bool())
+ {
+ this->write(value, index);
+ }
+ }
+
template
void access(const F& accessor, const size_t index = 0) const
{
@@ -122,6 +130,11 @@ public:
buffer.read(this->address_);
}
+ void set_address(const uint64_t address)
+ {
+ this->address_ = address;
+ }
+
private:
emulator* emu_{};
uint64_t address_{};
diff --git a/src/windows-emulator/handles.hpp b/src/windows-emulator/handles.hpp
index c47b0621..4be1a622 100644
--- a/src/windows-emulator/handles.hpp
+++ b/src/windows-emulator/handles.hpp
@@ -15,6 +15,8 @@ struct handle_types
port,
thread,
registry,
+ mutant,
+ token,
};
};
@@ -24,7 +26,8 @@ struct handle_value
{
uint64_t id : 32;
uint64_t type : 16;
- uint64_t padding : 15;
+ uint64_t padding : 14;
+ uint64_t is_system : 1;
uint64_t is_pseudo : 1;
};
#pragma pack(pop)
@@ -73,11 +76,19 @@ constexpr handle make_handle(const uint32_t id, const handle_types::type type, c
value.padding = 0;
value.id = id;
value.type = type;
+ value.is_system = false;
value.is_pseudo = is_pseudo;
return {value};
}
+constexpr handle make_handle(const uint64_t value)
+{
+ handle h{};
+ h.bits = value;
+ return h;
+}
+
constexpr handle make_pseudo_handle(const uint32_t id, const handle_types::type type)
{
return make_handle(id, type, true);
@@ -97,9 +108,15 @@ namespace handle_detail
};
}
+struct generic_handle_store
+{
+ virtual ~generic_handle_store() = default;
+ virtual bool erase(const handle h) = 0;
+};
+
template
requires(utils::Serializable)
-class handle_store
+class handle_store : public generic_handle_store
{
public:
using index_type = uint32_t;
@@ -199,7 +216,7 @@ public:
return this->erase(entry);
}
- bool erase(const handle h)
+ bool erase(const handle h) override
{
return this->erase(h.value);
}
@@ -328,10 +345,21 @@ private:
value_map store_{};
};
-constexpr auto KNOWN_DLLS_DIRECTORY = make_pseudo_handle(0x1337, handle_types::directory);
-constexpr auto KNOWN_DLLS_SYMLINK = make_pseudo_handle(0x1337, handle_types::symlink);
-constexpr auto SHARED_SECTION = make_pseudo_handle(0x1337, handle_types::section);
+constexpr auto KNOWN_DLLS_DIRECTORY = make_pseudo_handle(0x1, handle_types::directory);
+constexpr auto BASE_NAMED_OBJECTS_DIRECTORY = make_pseudo_handle(0x2, handle_types::directory);
+
+constexpr auto KNOWN_DLLS_SYMLINK = make_pseudo_handle(0x1, handle_types::symlink);
+constexpr auto SHARED_SECTION = make_pseudo_handle(0x1, handle_types::section);
constexpr auto CONSOLE_HANDLE = make_pseudo_handle(0x1, handle_types::file);
constexpr auto STDOUT_HANDLE = make_pseudo_handle(0x2, handle_types::file);
constexpr auto STDIN_HANDLE = make_pseudo_handle(0x3, handle_types::file);
+
+constexpr auto DUMMY_IMPERSONATION_TOKEN = make_pseudo_handle(0x1, handle_types::token);
+
+constexpr auto CURRENT_PROCESS = make_handle(~0ULL);
+constexpr auto CURRENT_THREAD = make_handle(~1ULL);
+
+constexpr auto CURRENT_PROCESS_TOKEN = make_handle(~3ULL);
+constexpr auto CURRENT_THREAD_TOKEN = make_handle(~4ULL);
+constexpr auto CURRENT_THREAD_EFFECTIVE_TOKEN = make_handle(~5ULL);
diff --git a/src/windows-emulator/io_device.cpp b/src/windows-emulator/io_device.cpp
index c619db60..00374b39 100644
--- a/src/windows-emulator/io_device.cpp
+++ b/src/windows-emulator/io_device.cpp
@@ -16,6 +16,7 @@ std::unique_ptr create_device(const std::u16string_view device)
{
if (device == u"CNG"
|| device == u"KsecDD"
+ || device == u"PcwDrv"
|| device == u"DeviceApi\\CMApi"
|| device == u"ConDrv\\Server")
{
diff --git a/src/windows-emulator/logger.hpp b/src/windows-emulator/logger.hpp
index be4ffe91..139d0a1d 100644
--- a/src/windows-emulator/logger.hpp
+++ b/src/windows-emulator/logger.hpp
@@ -54,6 +54,11 @@ public:
this->disable_output_ = value;
}
+ bool is_output_disabled() const
+ {
+ return this->disable_output_;
+ }
+
private:
bool disable_output_{false};
};
diff --git a/src/windows-emulator/module/mapped_module.hpp b/src/windows-emulator/module/mapped_module.hpp
index 5b49a4c7..cf2b91dd 100644
--- a/src/windows-emulator/module/mapped_module.hpp
+++ b/src/windows-emulator/module/mapped_module.hpp
@@ -1,4 +1,5 @@
#pragma once
+#include
struct exported_symbol
{
@@ -11,6 +12,12 @@ struct exported_symbol
using exported_symbols = std::vector;
using address_name_mapping = std::map;
+struct mapped_section
+{
+ std::string name{};
+ basic_memory_region region{};
+};
+
struct mapped_module
{
std::string name{};
@@ -23,6 +30,8 @@ struct mapped_module
exported_symbols exports{};
address_name_mapping address_names{};
+ std::vector sections{};
+
bool is_within(const uint64_t address) const
{
return address >= this->image_base && address < (this->image_base + this->size_of_image);
diff --git a/src/windows-emulator/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp
index ca5f4412..477dcc49 100644
--- a/src/windows-emulator/module/module_manager.cpp
+++ b/src/windows-emulator/module/module_manager.cpp
@@ -3,6 +3,22 @@
#include "module_mapping.hpp"
#include "windows-emulator/logger.hpp"
+namespace
+{
+ std::filesystem::path canonicalize_module_path(const std::filesystem::path& file)
+ {
+ constexpr std::u16string_view nt_prefix = u"\\??\\";
+ const auto wide_file = file.u16string();
+
+ if (!wide_file.starts_with(nt_prefix))
+ {
+ return canonical(absolute(file));
+ }
+
+ return canonicalize_module_path(wide_file.substr(nt_prefix.size()));
+ }
+}
+
static void serialize(utils::buffer_serializer& buffer, const exported_symbol& sym)
{
buffer.write(sym.name);
@@ -22,7 +38,7 @@ static void deserialize(utils::buffer_deserializer& buffer, exported_symbol& sym
static void serialize(utils::buffer_serializer& buffer, const mapped_module& mod)
{
buffer.write_string(mod.name);
- buffer.write_string(mod.path.wstring());
+ buffer.write(mod.path.u16string());
buffer.write(mod.image_base);
buffer.write(mod.size_of_image);
@@ -35,7 +51,7 @@ static void serialize(utils::buffer_serializer& buffer, const mapped_module& mod
static void deserialize(utils::buffer_deserializer& buffer, mapped_module& mod)
{
mod.name = buffer.read_string();
- mod.path = buffer.read_string();
+ mod.path = buffer.read_string();
buffer.read(mod.image_base);
buffer.read(mod.size_of_image);
@@ -52,7 +68,7 @@ module_manager::module_manager(emulator& emu)
mapped_module* module_manager::map_module(const std::filesystem::path& file, logger& logger)
{
- const auto canonical_file = canonical(absolute(file));
+ auto canonical_file = canonicalize_module_path(file);
for (auto& mod : this->modules_)
{
@@ -62,18 +78,26 @@ mapped_module* module_manager::map_module(const std::filesystem::path& file, log
}
}
- auto mod = map_module_from_file(*this->emu_, std::move(canonical_file));
- if (!mod)
+ try
{
- logger.error("Failed to map %s\n", file.generic_string().c_str());
+ auto mod = map_module_from_file(*this->emu_, std::move(canonical_file));
+
+ logger.log("Mapped %s at 0x%llX\n", mod.path.generic_string().c_str(), mod.image_base);
+
+ const auto image_base = mod.image_base;
+ const auto entry = this->modules_.try_emplace(image_base, std::move(mod));
+ return &entry.first->second;
+ }
+ catch (const std::exception& e)
+ {
+ logger.error("Failed to map %s: %s\n", file.generic_string().c_str(), e.what());
+ return nullptr;
+ }
+ catch (...)
+ {
+ logger.error("Failed to map %s: Unknown error\n", file.generic_string().c_str());
return nullptr;
}
-
- logger.log("Mapped %s at 0x%llX\n", mod->path.generic_string().c_str(), mod->image_base);
-
- const auto image_base = mod->image_base;
- const auto entry = this->modules_.try_emplace(image_base, std::move(*mod));
- return &entry.first->second;
}
void module_manager::serialize(utils::buffer_serializer& buffer) const
@@ -85,3 +109,17 @@ void module_manager::deserialize(utils::buffer_deserializer& buffer)
{
buffer.read_map(this->modules_);
}
+
+bool module_manager::unmap(const uint64_t address)
+{
+ const auto mod = this->modules_.find(address);
+ if (mod == this->modules_.end())
+ {
+ return false;
+ }
+
+ unmap_module(*this->emu_, mod->second);
+ this->modules_.erase(mod);
+
+ return true;
+}
diff --git a/src/windows-emulator/module/module_manager.hpp b/src/windows-emulator/module/module_manager.hpp
index 361890e0..3f5fb255 100644
--- a/src/windows-emulator/module/module_manager.hpp
+++ b/src/windows-emulator/module/module_manager.hpp
@@ -36,6 +36,8 @@ public:
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
+ bool unmap(const uint64_t address);
+
private:
emulator* emu_{};
diff --git a/src/windows-emulator/module/module_mapping.cpp b/src/windows-emulator/module/module_mapping.cpp
index b68658b3..1a1ebe54 100644
--- a/src/windows-emulator/module/module_mapping.cpp
+++ b/src/windows-emulator/module/module_mapping.cpp
@@ -19,7 +19,7 @@ namespace
return nt_headers_offset + (first_section_absolute - absolute_base);
}
- std::vector read_mapped_memory(emulator& emu, const mapped_module& binary)
+ std::vector read_mapped_memory(const emulator& emu, const mapped_module& binary)
{
std::vector memory{};
memory.resize(binary.size_of_image);
@@ -40,20 +40,24 @@ namespace
const auto export_directory = buffer.as(export_directory_entry.
VirtualAddress).get();
- //const auto function_count = export_directory->NumberOfFunctions;
const auto names_count = export_directory.NumberOfNames;
+ //const auto function_count = export_directory.NumberOfFunctions;
const auto names = buffer.as(export_directory.AddressOfNames);
const auto ordinals = buffer.as(export_directory.AddressOfNameOrdinals);
const auto functions = buffer.as(export_directory.AddressOfFunctions);
+ binary.exports.reserve(names_count);
+
for (DWORD i = 0; i < names_count; i++)
{
+ const auto ordinal = ordinals.get(i);
+
exported_symbol symbol{};
- symbol.ordinal = ordinals.get(i);
- symbol.name = buffer.as_string(names.get(i));
- symbol.rva = functions.get(symbol.ordinal);
+ symbol.ordinal = export_directory.Base + ordinal;
+ symbol.rva = functions.get(ordinal);
symbol.address = binary.image_base + symbol.rva;
+ symbol.name = buffer.as_string(names.get(i));
binary.exports.push_back(std::move(symbol));
}
@@ -137,7 +141,7 @@ namespace
}
}
- void map_sections(emulator& emu, const mapped_module& binary,
+ void map_sections(emulator& emu, mapped_module& binary,
const utils::safe_buffer_accessor buffer,
const PENTHeaders_t& nt_headers, const uint64_t nt_headers_offset)
{
@@ -176,81 +180,85 @@ namespace
const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize));
emu.protect_memory(target_ptr, size_of_section, permissions, nullptr);
- }
- }
- std::optional map_module(emulator& emu, const std::span data,
- std::filesystem::path file)
- {
- mapped_module binary{};
- binary.path = std::move(file);
- binary.name = binary.path.filename().string();
+ mapped_section section_info{};
+ section_info.region.start = target_ptr;
+ section_info.region.length = size_of_section;
+ section_info.region.permissions = permissions;
- utils::safe_buffer_accessor buffer{data};
-
- const auto dos_header = buffer.as(0).get();
- const auto nt_headers_offset = dos_header.e_lfanew;
-
- const auto nt_headers = buffer.as>(nt_headers_offset).get();
- auto& optional_header = nt_headers.OptionalHeader;
-
- binary.image_base = optional_header.ImageBase;
- binary.size_of_image = optional_header.SizeOfImage; // TODO: Sanitize
-
- if (!emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
- {
- binary.image_base = emu.find_free_allocation_base(binary.size_of_image);
- const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL;
- const auto has_dynamic_base =
- optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
- const auto is_relocatable = is_dll || has_dynamic_base;
-
- if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image,
- memory_permission::read))
+ for (size_t j = 0; j < sizeof(section.Name) && section.Name[j]; ++j)
{
- return {};
+ section_info.name.push_back(static_cast(section.Name[j]));
}
+
+ binary.sections.push_back(std::move(section_info));
}
-
- binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint;
-
- const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders);
- emu.write_memory(binary.image_base, header_buffer,
- optional_header.SizeOfHeaders);
-
- map_sections(emu, binary, buffer, nt_headers, nt_headers_offset);
-
- auto mapped_memory = read_mapped_memory(emu, binary);
- utils::safe_buffer_accessor mapped_buffer{mapped_memory};
-
- apply_relocations(binary, mapped_buffer, optional_header);
- collect_exports(binary, mapped_buffer, optional_header);
-
- emu.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size());
-
- return binary;
}
}
-std::optional map_module_from_data(emulator& emu, const std::span data,
- std::filesystem::path file)
+mapped_module map_module_from_data(emulator& emu, const std::span data,
+ std::filesystem::path file)
{
- try
+ mapped_module binary{};
+ binary.path = std::move(file);
+ binary.name = binary.path.filename().string();
+
+ utils::safe_buffer_accessor buffer{data};
+
+ const auto dos_header = buffer.as(0).get();
+ const auto nt_headers_offset = dos_header.e_lfanew;
+
+ const auto nt_headers = buffer.as>(nt_headers_offset).get();
+ auto& optional_header = nt_headers.OptionalHeader;
+
+ if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
{
- return map_module(emu, data, std::move(file));
+ throw std::runtime_error("Unsupported architecture!");
}
- catch (...)
+
+ binary.image_base = optional_header.ImageBase;
+ binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize
+
+ if (!emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
{
- return {};
+ binary.image_base = emu.find_free_allocation_base(binary.size_of_image);
+ const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL;
+ const auto has_dynamic_base =
+ optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
+ const auto is_relocatable = is_dll || has_dynamic_base;
+
+ if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image,
+ memory_permission::read))
+ {
+ throw std::runtime_error("Memory range not allocatable");
+ }
}
+
+ binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint;
+
+ const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders);
+ emu.write_memory(binary.image_base, header_buffer,
+ optional_header.SizeOfHeaders);
+
+ map_sections(emu, binary, buffer, nt_headers, nt_headers_offset);
+
+ auto mapped_memory = read_mapped_memory(emu, binary);
+ utils::safe_buffer_accessor mapped_buffer{mapped_memory};
+
+ apply_relocations(binary, mapped_buffer, optional_header);
+ collect_exports(binary, mapped_buffer, optional_header);
+
+ emu.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size());
+
+ return binary;
}
-std::optional map_module_from_file(emulator& emu, std::filesystem::path file)
+mapped_module map_module_from_file(emulator& emu, std::filesystem::path file)
{
const auto data = utils::io::read_file(file);
if (data.empty())
{
- return {};
+ throw std::runtime_error("Bad file data");
}
return map_module_from_data(emu, data, std::move(file));
diff --git a/src/windows-emulator/module/module_mapping.hpp b/src/windows-emulator/module/module_mapping.hpp
index a88f285d..8d69db80 100644
--- a/src/windows-emulator/module/module_mapping.hpp
+++ b/src/windows-emulator/module/module_mapping.hpp
@@ -3,8 +3,8 @@
#include
#include "mapped_module.hpp"
-std::optional map_module_from_data(emulator& emu, std::span data,
+mapped_module map_module_from_data(emulator& emu, std::span data,
std::filesystem::path file);
-std::optional map_module_from_file(emulator& emu, std::filesystem::path file);
+mapped_module map_module_from_file(emulator& emu, std::filesystem::path file);
bool unmap_module(emulator& emu, const mapped_module& mod);
diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp
index 23795743..9e553d60 100644
--- a/src/windows-emulator/process_context.hpp
+++ b/src/windows-emulator/process_context.hpp
@@ -84,25 +84,159 @@ struct event : ref_counted_object
}
};
+struct mutant : ref_counted_object
+{
+ uint32_t locked_count{0};
+ uint32_t owning_thread_id{};
+ std::u16string name{};
+
+ bool try_lock(const uint32_t thread_id)
+ {
+ if (this->locked_count == 0)
+ {
+ ++this->locked_count;
+ this->owning_thread_id = thread_id;
+ return true;
+ }
+
+ if (this->owning_thread_id != thread_id)
+ {
+ return false;
+ }
+
+ ++this->locked_count;
+ return true;
+ }
+
+ uint32_t release()
+ {
+ const auto old_count = this->locked_count;
+
+ if (this->locked_count <= 0)
+ {
+ return old_count;
+ }
+
+ --this->locked_count;
+ return old_count;
+ }
+
+ void serialize(utils::buffer_serializer& buffer) const
+ {
+ buffer.write(this->locked_count);
+ buffer.write(this->owning_thread_id);
+ buffer.write(this->name);
+
+ ref_counted_object::serialize(buffer);
+ }
+
+ void deserialize(utils::buffer_deserializer& buffer)
+ {
+ buffer.read(this->locked_count);
+ buffer.read(this->owning_thread_id);
+ buffer.read(this->name);
+
+ ref_counted_object::deserialize(buffer);
+ }
+};
+
+struct file_entry
+{
+ std::filesystem::path file_path{};
+
+ void serialize(utils::buffer_serializer& buffer) const
+ {
+ buffer.write(this->file_path);
+ }
+
+ void deserialize(utils::buffer_deserializer& buffer)
+ {
+ buffer.read(this->file_path);
+ }
+};
+
+struct file_enumeration_state
+{
+ size_t current_index{0};
+ std::vector files{};
+
+ void serialize(utils::buffer_serializer& buffer) const
+ {
+ buffer.write(this->current_index);
+ buffer.write_vector(this->files);
+ }
+
+ void deserialize(utils::buffer_deserializer& buffer)
+ {
+ buffer.read(this->current_index);
+ buffer.read_vector(this->files);
+ }
+};
+
struct file
{
utils::file_handle handle{};
std::u16string name{};
+ std::optional enumeration_state{};
+
+ bool is_file() const
+ {
+ return this->handle;
+ }
+
+ bool is_directory() const
+ {
+ return !this->is_file();
+ }
void serialize(utils::buffer_serializer& buffer) const
{
- buffer.write(this->name);
// TODO: Serialize handle
+ buffer.write(this->name);
+ buffer.write_optional(this->enumeration_state);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->name);
+ buffer.read_optional(this->enumeration_state);
this->handle = {};
}
};
-struct semaphore
+struct section
+{
+ std::u16string name{};
+ std::u16string file_name{};
+ uint64_t maximum_size{};
+ uint32_t section_page_protection{};
+ uint32_t allocation_attributes{};
+
+ bool is_image() const
+ {
+ return this->allocation_attributes & SEC_IMAGE;
+ }
+
+ void serialize(utils::buffer_serializer& buffer) const
+ {
+ buffer.write(this->name);
+ buffer.write(this->file_name);
+ buffer.write(this->maximum_size);
+ buffer.write(this->section_page_protection);
+ buffer.write(this->allocation_attributes);
+ }
+
+ void deserialize(utils::buffer_deserializer& buffer)
+ {
+ buffer.read(this->name);
+ buffer.read(this->file_name);
+ buffer.read(this->maximum_size);
+ buffer.read(this->section_page_protection);
+ buffer.read(this->allocation_attributes);
+ }
+};
+
+struct semaphore : ref_counted_object
{
std::u16string name{};
volatile uint32_t current_count{};
@@ -113,6 +247,8 @@ struct semaphore
buffer.write(this->name);
buffer.write(this->current_count);
buffer.write(this->max_count);
+
+ ref_counted_object::serialize(buffer);
}
void deserialize(utils::buffer_deserializer& buffer)
@@ -120,6 +256,8 @@ struct semaphore
buffer.read(this->name);
buffer.read(this->current_count);
buffer.read(this->max_count);
+
+ ref_counted_object::deserialize(buffer);
}
};
@@ -221,7 +359,8 @@ public:
std::u16string name{};
std::optional exit_status{};
- std::optional await_object{};
+ std::vector await_objects{};
+ bool await_any{false};
bool waiting_for_alert{false};
bool alerted{false};
std::optional await_time{};
@@ -285,7 +424,8 @@ public:
buffer.write_string(this->name);
buffer.write_optional(this->exit_status);
- buffer.write_optional(this->await_object);
+ buffer.write_vector(this->await_objects);
+ buffer.write(this->await_any);
buffer.write(this->waiting_for_alert);
buffer.write(this->alerted);
@@ -317,7 +457,8 @@ public:
buffer.read_string(this->name);
buffer.read_optional(this->exit_status);
- buffer.read_optional(this->await_object);
+ buffer.read_vector(this->await_objects);
+ buffer.read(this->await_any);
buffer.read(this->waiting_for_alert);
buffer.read(this->alerted);
@@ -395,13 +536,13 @@ struct process_context
uint64_t rtl_user_thread_start{};
uint64_t ki_user_exception_dispatcher{};
- uint64_t shared_section_size{};
-
handle_store events{};
handle_store files{};
+ handle_store sections{};
handle_store devices{};
handle_store semaphores{};
handle_store ports{};
+ handle_store mutants{};
handle_store registry_keys{};
std::map atoms{};
@@ -433,12 +574,13 @@ struct process_context
buffer.write(this->rtl_user_thread_start);
buffer.write(this->ki_user_exception_dispatcher);
- buffer.write(this->shared_section_size);
buffer.write(this->events);
buffer.write(this->files);
+ buffer.write(this->sections);
buffer.write(this->devices);
buffer.write(this->semaphores);
buffer.write(this->ports);
+ buffer.write(this->mutants);
buffer.write(this->registry_keys);
buffer.write_map(this->atoms);
@@ -475,12 +617,13 @@ struct process_context
buffer.read(this->rtl_user_thread_start);
buffer.read(this->ki_user_exception_dispatcher);
- buffer.read(this->shared_section_size);
buffer.read(this->events);
buffer.read(this->files);
+ buffer.read(this->sections);
buffer.read(this->devices);
buffer.read(this->semaphores);
buffer.read(this->ports);
+ buffer.read(this->mutants);
buffer.read(this->registry_keys);
buffer.read_map(this->atoms);
diff --git a/src/windows-emulator/registry/hive_parser.cpp b/src/windows-emulator/registry/hive_parser.cpp
index e9825f3e..a71f7b2f 100644
--- a/src/windows-emulator/registry/hive_parser.cpp
+++ b/src/windows-emulator/registry/hive_parser.cpp
@@ -1,4 +1,5 @@
#include "hive_parser.hpp"
+#include
// Based on this implementation: https://github.com/reahly/windows-hive-parser
@@ -130,11 +131,6 @@ namespace
throw std::runtime_error("Bad hive file '" + file_path.string() + "': " + e.what());
}
}
-
- char char_to_lower(const char val)
- {
- return static_cast(std::tolower(static_cast(val)));
- }
}
const hive_value* hive_key::get_value(std::ifstream& file, const std::string_view name)
@@ -188,7 +184,7 @@ void hive_key::parse(std::ifstream& file)
raw_value.data_offset = offset + static_cast(offsetof(value_block_t, offset));
}
- std::ranges::transform(value_name, value_name.begin(), char_to_lower);
+ utils::string::to_lower_inplace(value_name);
this->values_[std::move(value_name)] = std::move(raw_value);
}
@@ -211,7 +207,7 @@ void hive_key::parse(std::ifstream& file)
const auto subkey = read_file_object(file, subkey_block_offset);
std::string subkey_name(subkey.name, std::min(subkey.len, static_cast(sizeof(subkey.name))));
- std::ranges::transform(subkey_name, subkey_name.begin(), char_to_lower);
+ utils::string::to_lower_inplace(subkey_name);
this->sub_keys_.emplace(std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets});
}
diff --git a/src/windows-emulator/registry/registry_manager.cpp b/src/windows-emulator/registry/registry_manager.cpp
index 193bf65a..9947035e 100644
--- a/src/windows-emulator/registry/registry_manager.cpp
+++ b/src/windows-emulator/registry/registry_manager.cpp
@@ -1,25 +1,16 @@
#include "registry_manager.hpp"
-#include
#include
#include "hive_parser.hpp"
+#include
namespace
{
- void string_to_lower(std::string& str)
- {
- std::ranges::transform(str, str.begin(), [](const char val)
- {
- return static_cast(std::tolower(static_cast(val)));
- });
- }
-
std::filesystem::path canonicalize_path(const std::filesystem::path& key)
{
auto path = key.lexically_normal().wstring();
- std::ranges::transform(path, path.begin(), std::towlower);
- return {std::move(path)};
+ return utils::string::to_lower_consume(path);
}
bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p)
@@ -144,7 +135,7 @@ std::optional registry_manager::get_key(const std::filesystem::pat
std::optional registry_manager::get_value(const registry_key& key, std::string name)
{
- string_to_lower(name);
+ utils::string::to_lower_inplace(name);
const auto iterator = this->hives_.find(key.hive);
if (iterator == this->hives_.end())
diff --git a/src/windows-emulator/syscall_dispatcher.cpp b/src/windows-emulator/syscall_dispatcher.cpp
index 8990399a..1cb0a9bf 100644
--- a/src/windows-emulator/syscall_dispatcher.cpp
+++ b/src/windows-emulator/syscall_dispatcher.cpp
@@ -24,15 +24,16 @@ void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer)
}
-void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports)
+void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, std::span ntdll_data,
+ const exported_symbols& win32u_exports, std::span win32u_data)
{
this->handlers_ = {};
- const auto ntdll_syscalls = find_syscalls(ntdll_exports);
- const auto win32u_syscalls = find_syscalls(win32u_exports);
+ const auto ntdll_syscalls = find_syscalls(ntdll_exports, ntdll_data);
+ const auto win32u_syscalls = find_syscalls(win32u_exports, win32u_data);
- map_syscalls(this->handlers_, ntdll_syscalls, 0);
- map_syscalls(this->handlers_, win32u_syscalls, 0x1000);
+ map_syscalls(this->handlers_, ntdll_syscalls);
+ map_syscalls(this->handlers_, win32u_syscalls);
this->add_handlers();
}
@@ -99,10 +100,26 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
}
else
{
- win_emu.logger.print(color::dark_gray, "Executing syscall: %s (0x%X) at 0x%llX\n",
- entry->second.name.c_str(),
- syscall_id,
- address);
+ if (mod->is_within(context.previous_ip))
+ {
+ const auto rsp = c.emu.read_stack_pointer();
+ const auto return_address = c.emu.read_memory(rsp);
+ const auto* mod_name = context.module_manager.find_name(return_address);
+
+ win_emu.logger.print(color::dark_gray, "Executing syscall: %s (0x%X) at 0x%llX via 0x%llX (%s) %lld\n",
+ entry->second.name.c_str(),
+ syscall_id, address, return_address, mod_name, c.proc.executed_instructions);
+ }
+ else
+ {
+ const auto* previous_mod = context.module_manager.find_by_address(context.previous_ip);
+ win_emu.logger.print(color::blue,
+ "Crafted out-of-line syscall: %s (0x%X) at 0x%llX (%s) via 0x%llX (%s)\n",
+ entry->second.name.c_str(),
+ syscall_id,
+ address, mod ? mod->name.c_str() : "", context.previous_ip,
+ previous_mod ? previous_mod->name.c_str() : "");
+ }
}
entry->second.handler(c);
@@ -121,8 +138,8 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
}
}
-syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports,
- const exported_symbols& win32u_exports)
+syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, std::span ntdll_data,
+ const exported_symbols& win32u_exports, std::span win32u_data)
{
- this->setup(ntdll_exports, win32u_exports);
+ this->setup(ntdll_exports, ntdll_data, win32u_exports, win32u_data);
}
diff --git a/src/windows-emulator/syscall_dispatcher.hpp b/src/windows-emulator/syscall_dispatcher.hpp
index ac5fc439..23a5185e 100644
--- a/src/windows-emulator/syscall_dispatcher.hpp
+++ b/src/windows-emulator/syscall_dispatcher.hpp
@@ -17,14 +17,16 @@ class syscall_dispatcher
{
public:
syscall_dispatcher() = default;
- syscall_dispatcher(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports);
+ syscall_dispatcher(const exported_symbols& ntdll_exports, std::span ntdll_data,
+ const exported_symbols& win32u_exports, std::span win32u_data);
void dispatch(windows_emulator& win_emu);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
- void setup(const exported_symbols& ntdll_exports, const exported_symbols& win32u_exports);
+ void setup(const exported_symbols& ntdll_exports, std::span ntdll_data,
+ const exported_symbols& win32u_exports, std::span win32u_data);
std::string get_syscall_name(const uint64_t id)
{
diff --git a/src/windows-emulator/syscall_utils.hpp b/src/windows-emulator/syscall_utils.hpp
index 5f5d5b8e..f9ae5c2a 100644
--- a/src/windows-emulator/syscall_utils.hpp
+++ b/src/windows-emulator/syscall_utils.hpp
@@ -38,32 +38,51 @@ inline bool is_syscall(const std::string_view name)
return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]);
}
-inline std::vector find_syscalls(const exported_symbols& exports)
+inline std::optional extract_syscall_id(const exported_symbol& symbol, std::span data)
{
- // Makes use of the fact that order of Nt* function addresses
- // is equal to the order of syscall IDs.
- // So first Nt* function is the first syscall with ID 0
+ if (!is_syscall(symbol.name))
+ {
+ return std::nullopt;
+ }
- std::map reference_count{};
- std::map ordered_syscalls{};
+ constexpr auto instruction_size = 5;
+ constexpr auto instruction_offset = 3;
+ constexpr auto instruction_operand_offset = 1;
+ constexpr auto instruction_opcode = static_cast(0xB8);
+
+ const auto instruction_rva = symbol.rva + instruction_offset;
+
+ if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode)
+ {
+ return std::nullopt;
+ }
+
+ uint32_t syscall_id{0};
+ static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset));
+ memcpy(&syscall_id, data.data() + instruction_rva + instruction_operand_offset, sizeof(syscall_id));
+
+ return syscall_id;
+}
+
+inline std::map find_syscalls(const exported_symbols& exports, std::span data)
+{
+ std::map syscalls{};
for (const auto& symbol : exports)
{
- if (is_syscall(symbol.name))
+ const auto id = extract_syscall_id(symbol, data);
+ if (id)
{
- ++reference_count[symbol.address];
- ordered_syscalls[symbol.address] = symbol.name;
- }
- }
+ auto& entry = syscalls[*id];
- std::vector syscalls{};
- syscalls.reserve(ordered_syscalls.size());
+ if (!entry.empty())
+ {
+ throw std::runtime_error(
+ "Syscall with id " + std::to_string(*id) + ", which is mapping to " + symbol.name +
+ ", was already mapped to " + entry);
+ }
- for (auto& syscall : ordered_syscalls)
- {
- if (reference_count[syscall.first] == 1)
- {
- syscalls.push_back(std::move(syscall.second));
+ entry = symbol.name;
}
}
@@ -71,14 +90,20 @@ inline std::vector find_syscalls(const exported_symbols& exports)
}
inline void map_syscalls(std::map& handlers,
- const std::vector& syscalls, const uint64_t base_index)
+ std::map syscalls)
{
- for (size_t i = 0; i < syscalls.size(); ++i)
+ for (auto& [id, name] : syscalls)
{
- const auto& syscall = syscalls[i];
+ auto& entry = handlers[id];
- auto& entry = handlers[base_index + i];
- entry.name = syscall;
+ if (!entry.name.empty())
+ {
+ throw std::runtime_error(
+ "Syscall with id " + std::to_string(id) + ", which is mapping to " + name +
+ ", was previously mapped to " + entry.name);
+ }
+
+ entry.name = std::move(name);
entry.handler = nullptr;
}
}
@@ -243,3 +268,10 @@ inline std::chrono::system_clock::time_point convert_from_ksystem_time(const vol
{
return convert_from_ksystem_time(*const_cast(&time));
}
+
+inline LARGE_INTEGER convert_unix_to_windows_time(const __time64_t unix_time)
+{
+ LARGE_INTEGER windows_time{};
+ windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND;
+ return windows_time;
+}
diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp
index c43ec421..93a6cb8e 100644
--- a/src/windows-emulator/syscalls.cpp
+++ b/src/windows-emulator/syscalls.cpp
@@ -9,8 +9,8 @@
#include
#include
#include
-
-#include "utils/finally.hpp"
+#include
+#include
namespace
{
@@ -298,7 +298,7 @@ namespace
const uint64_t thread_information,
const uint32_t thread_information_length)
{
- auto* thread = thread_handle == ~1ULL
+ auto* thread = thread_handle == CURRENT_THREAD
? c.proc.active_thread
: c.proc.threads.get(thread_handle);
@@ -312,6 +312,12 @@ namespace
return STATUS_SUCCESS;
}
+ if (info_class == ThreadHideFromDebugger)
+ {
+ c.win_emu.logger.print(color::pink, "--> Hiding thread %X from debugger!\n", thread->id);
+ return STATUS_SUCCESS;
+ }
+
if (info_class == ThreadNameInformation)
{
if (thread_information_length != sizeof(THREAD_NAME_INFORMATION>))
@@ -328,7 +334,36 @@ namespace
return STATUS_SUCCESS;
}
- printf("Unsupported thread info class: %X\n", info_class);
+ if (info_class == ThreadImpersonationToken)
+ {
+ if (thread_information_length != sizeof(handle))
+ {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ const emulator_object info{c.emu, thread_information};
+ info.write(DUMMY_IMPERSONATION_TOKEN);
+
+ return STATUS_SUCCESS;
+ }
+
+ if (info_class == ThreadZeroTlsCell)
+ {
+ if (thread_information_length != sizeof(ULONG))
+ {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ const auto tls_index = c.emu.read_memory(thread_information);
+ const auto teb = thread->teb->read();
+
+ auto* tls_vector = teb.ThreadLocalStoragePointer;
+ c.emu.write_memory(tls_vector + tls_index, nullptr);
+
+ return STATUS_SUCCESS;
+ }
+
+ printf("Unsupported thread set info class: %X\n", info_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
@@ -351,6 +386,33 @@ namespace
return STATUS_SUCCESS;
}
+ generic_handle_store* get_handle_store(process_context& proc, const handle h)
+ {
+ switch (h.value.type)
+ {
+ case handle_types::thread:
+ return &proc.threads;
+ case handle_types::event:
+ return &proc.events;
+ case handle_types::file:
+ return &proc.files;
+ case handle_types::device:
+ return &proc.devices;
+ case handle_types::semaphore:
+ return &proc.semaphores;
+ case handle_types::registry:
+ return &proc.registry_keys;
+ case handle_types::mutant:
+ return &proc.mutants;
+ case handle_types::port:
+ return &proc.ports;
+ case handle_types::section:
+ return &proc.sections;
+ default:
+ return nullptr;
+ }
+ }
+
NTSTATUS handle_NtClose(const syscall_context& c, const handle h)
{
const auto value = h.value;
@@ -359,32 +421,8 @@ namespace
return STATUS_SUCCESS;
}
- if (value.type == handle_types::thread && c.proc.threads.erase(h))
- {
- return STATUS_SUCCESS;
- }
-
- if (value.type == handle_types::event && c.proc.events.erase(h))
- {
- return STATUS_SUCCESS;
- }
-
- if (value.type == handle_types::file && c.proc.files.erase(h))
- {
- return STATUS_SUCCESS;
- }
-
- if (value.type == handle_types::device && c.proc.devices.erase(h))
- {
- return STATUS_SUCCESS;
- }
-
- if (value.type == handle_types::semaphore && c.proc.semaphores.erase(h))
- {
- return STATUS_SUCCESS;
- }
-
- if (value.type == handle_types::registry && c.proc.registry_keys.erase(h))
+ auto* handle_store = get_handle_store(c.proc, h);
+ if (handle_store && handle_store->erase(h))
{
return STATUS_SUCCESS;
}
@@ -397,9 +435,70 @@ namespace
return STATUS_SUCCESS;
}
- NTSTATUS handle_NtOpenThreadToken()
+ NTSTATUS handle_NtReleaseMutant(const syscall_context& c, const handle mutant_handle,
+ const emulator_object previous_count)
{
- return STATUS_NO_TOKEN;
+ if (mutant_handle.value.type != handle_types::mutant)
+ {
+ c.win_emu.logger.error("Bad handle type for NtReleaseMutant\n");
+ c.emu.stop();
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ auto* mutant = c.proc.mutants.get(mutant_handle);
+ if (!mutant)
+ {
+ return STATUS_INVALID_HANDLE;
+ }
+
+ const auto old_count = mutant->release();
+
+ if (previous_count)
+ {
+ previous_count.write(static_cast(old_count));
+ }
+
+ return STATUS_SUCCESS;
+ }
+
+ NTSTATUS handle_NtCreateMutant(const syscall_context& c, const emulator_object mutant_handle,
+ const ACCESS_MASK /*desired_access*/,
+ const emulator_object>> object_attributes,
+ const BOOLEAN initial_owner)
+ {
+ std::u16string name{};
+ if (object_attributes)
+ {
+ const auto attributes = object_attributes.read();
+ if (attributes.ObjectName)
+ {
+ name = read_unicode_string(c.emu, emulator_object>>{c.emu, attributes.ObjectName});
+ }
+ }
+
+ if (!name.empty())
+ {
+ for (const auto& mutant : c.proc.mutants)
+ {
+ if (mutant.second.name == name)
+ {
+ return STATUS_OBJECT_NAME_EXISTS;
+ }
+ }
+ }
+
+ mutant e{};
+ e.name = std::move(name);
+
+ if (initial_owner)
+ {
+ e.try_lock(c.win_emu.current_thread().id);
+ }
+
+ const auto handle = c.proc.mutants.store(std::move(e));
+ mutant_handle.write(handle);
+
+ return STATUS_SUCCESS;
}
NTSTATUS handle_NtCreateEvent(const syscall_context& c, const emulator_object event_handle,
@@ -417,6 +516,17 @@ namespace
}
}
+ if (!name.empty())
+ {
+ for (const auto& event : c.proc.events)
+ {
+ if (event.second.name == name)
+ {
+ return STATUS_OBJECT_NAME_EXISTS;
+ }
+ }
+ }
+
event e{};
e.type = event_type;
e.signaled = initial_state != FALSE;
@@ -488,7 +598,7 @@ namespace
const auto attributes = object_attributes.read();
auto filename = read_unicode_string(c.emu, reinterpret_cast>*>(attributes.ObjectName));
- c.win_emu.logger.print(color::gray, "Opening section: %S\n", filename.c_str());
+ c.win_emu.logger.print(color::dark_gray, "--> Opening section: %S\n", filename.c_str());
if (filename == u"\\Windows\\SharedSection")
{
@@ -503,19 +613,18 @@ namespace
return STATUS_NOT_SUPPORTED;
}
- filename = u"C:\\WINDOWS\\System32\\" + filename;
- if (!std::filesystem::exists(filename))
+ utils::string::to_lower_inplace(filename);
+
+ for (auto& section_entry : c.proc.sections)
{
- return STATUS_FILE_INVALID;
+ if (section_entry.second.is_image() && section_entry.second.name == filename)
+ {
+ section_handle.write(c.proc.sections.make_handle(section_entry.first));
+ return STATUS_SUCCESS;
+ }
}
- file f{};
- f.name = std::move(filename);
-
- const auto handle = c.proc.files.store(std::move(f));
- section_handle.write(handle);
-
- return STATUS_SUCCESS;
+ return STATUS_OBJECT_NAME_NOT_FOUND;
}
NTSTATUS handle_NtMapViewOfSection(const syscall_context& c, const handle section_handle,
@@ -526,16 +635,17 @@ namespace
const SECTION_INHERIT /*inherit_disposition*/, const ULONG /*allocation_type*/,
const ULONG /*win32_protect*/)
{
- if (process_handle != ~0ULL)
+ if (process_handle != CURRENT_PROCESS)
{
return STATUS_INVALID_HANDLE;
}
if (section_handle == SHARED_SECTION)
{
- const auto address = c.emu.find_free_allocation_base(c.proc.shared_section_size);
- c.emu.allocate_memory(address,
- c.proc.shared_section_size, memory_permission::read_write);
+ constexpr auto shared_section_size = 0x10000;
+
+ const auto address = c.emu.find_free_allocation_base(shared_section_size);
+ c.emu.allocate_memory(address, shared_section_size, memory_permission::read_write);
const std::u16string_view windows_dir = c.proc.kusd.get().NtSystemRoot.arr;
const auto windows_dir_size = windows_dir.size() * 2;
@@ -556,16 +666,24 @@ namespace
});
- const emulator_object>> sysdir_obj{c.emu, obj_address + windir_obj.size()};
+ const emulator_object>> sysdir_obj{c.emu, windir_obj.value() + windir_obj.size()};
sysdir_obj.access([&](UNICODE_STRING>& ucs)
+
{
c.proc.base_allocator.make_unicode_string(ucs, u"C:\\WINDOWS\\System32");
ucs.Buffer = ucs.Buffer - obj_address;
});
- if (view_size.value())
+ const emulator_object>> base_dir_obj{c.emu, sysdir_obj.value() + sysdir_obj.size()};
+ base_dir_obj.access([&](UNICODE_STRING>& ucs)
{
- view_size.write(c.proc.shared_section_size);
+ c.proc.base_allocator.make_unicode_string(ucs, u"\\Sessions\\1\\BaseNamedObjects");
+ ucs.Buffer = ucs.Buffer - obj_address;
+ });
+
+ if (view_size)
+ {
+ view_size.write(shared_section_size);
}
base_address.write(address);
@@ -573,25 +691,60 @@ namespace
return STATUS_SUCCESS;
}
- const auto section_entry = c.proc.files.get(section_handle);
+ const auto section_entry = c.proc.sections.get(section_handle);
if (!section_entry)
{
return STATUS_INVALID_HANDLE;
}
- const auto binary = c.proc.module_manager.map_module(section_entry->name, c.win_emu.logger);
- if (!binary)
+ if (section_entry->is_image())
{
- return STATUS_FILE_INVALID;
+ const auto binary = c.proc.module_manager.map_module(section_entry->file_name, c.win_emu.logger);
+ if (!binary)
+ {
+ return STATUS_FILE_INVALID;
+ }
+
+ std::u16string wide_name(binary->name.begin(), binary->name.end());
+ section_entry->name = utils::string::to_lower_consume(wide_name);
+
+ if (view_size.value())
+ {
+ view_size.write(binary->size_of_image);
+ }
+
+ base_address.write(binary->image_base);
+
+ return STATUS_SUCCESS;
}
- if (view_size.value())
+ uint64_t size = section_entry->maximum_size;
+ std::vector file_data{};
+
+ if (!section_entry->file_name.empty())
{
- view_size.write(binary->size_of_image);
+ if (!utils::io::read_file(section_entry->file_name, &file_data))
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ size = page_align_up(file_data.size());
}
- base_address.write(binary->image_base);
+ const auto protection = map_nt_to_emulator_protection(section_entry->section_page_protection);
+ const auto address = c.emu.allocate_memory(size, protection);
+ if (!file_data.empty())
+ {
+ c.emu.write_memory(address, file_data.data(), file_data.size());
+ }
+
+ if (view_size)
+ {
+ view_size.write(size);
+ }
+
+ base_address.write(address);
return STATUS_SUCCESS;
}
@@ -616,7 +769,7 @@ namespace
const uint64_t memory_information, const uint32_t memory_information_length,
const emulator_object return_length)
{
- if (process_handle != ~0ULL)
+ if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
@@ -657,7 +810,7 @@ namespace
: (region_info.is_reserved
? MEM_RESERVE
: MEM_FREE);
- image_info.Protect = map_emulator_to_nt_protection(region_info.pemissions);
+ image_info.Protect = map_emulator_to_nt_protection(region_info.permissions);
image_info.Type = MEM_PRIVATE;
});
@@ -689,6 +842,7 @@ namespace
{
image_info.ImageBase = reinterpret_cast(mod->image_base);
image_info.SizeOfImage = mod->size_of_image;
+ image_info.ImageFlags = 0;
});
return STATUS_SUCCESS;
@@ -904,7 +1058,7 @@ namespace
const emulator_object target_handle, const ACCESS_MASK /*desired_access*/,
const ULONG /*handle_attributes*/, const ULONG /*options*/)
{
- if (source_process_handle != ~0ULL || target_process_handle != ~0ULL)
+ if (source_process_handle != CURRENT_PROCESS || target_process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
@@ -1008,7 +1162,7 @@ namespace
const uint32_t process_information_length,
const emulator_object return_length)
{
- if (process_handle != ~0ULL)
+ if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
@@ -1096,7 +1250,7 @@ namespace
return STATUS_SUCCESS;
}
- if (info_class == ProcessDefaultHardErrorMode)
+ if (info_class == ProcessDefaultHardErrorMode || info_class == ProcessWx86Information)
{
if (return_length)
{
@@ -1120,6 +1274,24 @@ namespace
return STATUS_NOT_SUPPORTED;
}
+ if (info_class == ProcessTimes)
+ {
+ if (return_length)
+ {
+ return_length.write(sizeof(KERNEL_USER_TIMES));
+ }
+
+ if (process_information_length != sizeof(KERNEL_USER_TIMES))
+ {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ const emulator_object info{c.emu, process_information};
+ info.write(KERNEL_USER_TIMES{});
+
+ return STATUS_SUCCESS;
+ }
+
if (info_class == ProcessBasicInformation)
{
if (return_length)
@@ -1184,9 +1356,13 @@ namespace
const uint32_t thread_information_length,
const emulator_object return_length)
{
- if (thread_handle != ~1ULL)
+ const auto* thread = thread_handle == CURRENT_THREAD
+ ? c.proc.active_thread
+ : c.proc.threads.get(thread_handle);
+
+ if (!thread)
{
- return STATUS_NOT_SUPPORTED;
+ return STATUS_INVALID_HANDLE;
}
if (info_class == ThreadBasicInformation)
@@ -1204,8 +1380,8 @@ namespace
const emulator_object info{c.emu, thread_information};
info.access([&](THREAD_BASIC_INFORMATION64& i)
{
- i.TebBaseAddress = c.win_emu.current_thread().teb->ptr();
- i.ClientId = c.win_emu.current_thread().teb->read().ClientId;
+ i.TebBaseAddress = thread->teb->ptr();
+ i.ClientId = thread->teb->read().ClientId;
});
return STATUS_SUCCESS;
@@ -1229,7 +1405,25 @@ namespace
return STATUS_SUCCESS;
}
- printf("Unsupported thread info class: %X\n", info_class);
+ if (info_class == ThreadQuerySetWin32StartAddress)
+ {
+ if (return_length)
+ {
+ return_length.write(sizeof(ULONG_PTR));
+ }
+
+ if (thread_information_length != sizeof(ULONG_PTR))
+ {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ const emulator_object info{c.emu, thread_information};
+ info.write(thread->start_address);
+
+ return STATUS_SUCCESS;
+ }
+
+ printf("Unsupported thread query info class: %X\n", info_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
@@ -1282,6 +1476,144 @@ namespace
return STATUS_NOT_SUPPORTED;
}
+ std::vector scan_directory(const std::filesystem::path& dir)
+ {
+ std::vector files{
+ {"."},
+ {".."},
+ };
+
+ for (const auto& file : std::filesystem::directory_iterator(dir))
+ {
+ files.emplace_back(file.path().filename());
+ }
+
+ return files;
+ }
+
+ template
+ NTSTATUS handle_file_enumeration(const syscall_context& c, const emulator_object>> io_status_block,
+ const uint64_t file_information, const uint32_t length, const ULONG query_flags,
+ file* f)
+ {
+ if (!f->enumeration_state || query_flags & SL_RESTART_SCAN)
+ {
+ f->enumeration_state.emplace(file_enumeration_state{});
+ f->enumeration_state->files = scan_directory(f->name);
+ }
+
+ auto& enum_state = *f->enumeration_state;
+
+ size_t current_offset{0};
+ emulator_object object{c.emu};
+
+ size_t current_index = enum_state.current_index;
+
+ do
+ {
+ if (current_index >= enum_state.files.size())
+ {
+ break;
+ }
+
+ const auto new_offset = align_up(current_offset, 8);
+ const auto& current_file = enum_state.files[current_index];
+ const auto file_name = current_file.file_path.u16string();
+ const auto required_size = sizeof(T) + (file_name.size() * 2) - 2;
+ const auto end_offset = new_offset + required_size;
+
+ if (end_offset > length)
+ {
+ if (current_offset == 0)
+ {
+ IO_STATUS_BLOCK> block{};
+ block.Information = end_offset;
+ io_status_block.write(block);
+
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ break;
+ }
+
+ if (object)
+ {
+ const auto object_offset = object.value() - file_information;
+
+ object.access([&](T& dir_info)
+ {
+ dir_info.NextEntryOffset = static_cast(new_offset - object_offset);
+ });
+ }
+
+ T info{};
+ info.NextEntryOffset = 0;
+ info.FileIndex = static_cast(current_index);
+ info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ info.FileNameLength = static_cast(file_name.size() * 2);
+
+ object.set_address(file_information + new_offset);
+ object.write(info);
+
+ c.emu.write_memory(object.value() + offsetof(T, FileName), file_name.data(),
+ info.FileNameLength);
+
+ ++current_index;
+ current_offset = end_offset;
+ }
+ while ((query_flags & SL_RETURN_SINGLE_ENTRY) == 0);
+
+ if ((query_flags & SL_NO_CURSOR_UPDATE) == 0)
+ {
+ enum_state.current_index = current_index;
+ }
+
+ IO_STATUS_BLOCK> block{};
+ block.Information = current_offset;
+ io_status_block.write(block);
+
+ return current_index < enum_state.files.size() ? STATUS_SUCCESS : STATUS_NO_MORE_FILES;
+ }
+
+ NTSTATUS handle_NtQueryDirectoryFileEx(const syscall_context& c, const handle file_handle,
+ const handle /*event_handle*/,
+ const emulator_pointer /*PIO_APC_ROUTINE*/ /*apc_routine*/,
+ const emulator_pointer /*apc_context*/,
+ const emulator_object>> io_status_block,
+ const uint64_t file_information, const uint32_t length,
+ const uint32_t info_class, const ULONG query_flags,
+ const emulator_object>> /*file_name*/)
+ {
+ auto* f = c.proc.files.get(file_handle);
+ if (!f || !f->is_directory())
+ {
+ return STATUS_INVALID_HANDLE;
+ }
+
+ if (info_class == FileDirectoryInformation)
+ {
+ return handle_file_enumeration(c, io_status_block, file_information, length,
+ query_flags, f);
+ }
+
+ if (info_class == FileFullDirectoryInformation)
+ {
+ return handle_file_enumeration(c, io_status_block, file_information, length,
+ query_flags, f);
+ }
+
+ if (info_class == FileBothDirectoryInformation)
+ {
+ return handle_file_enumeration(c, io_status_block, file_information, length,
+ query_flags, f);
+ }
+
+ printf("Unsupported query directory file info class: %X\n", info_class);
+ c.emu.stop();
+
+ return STATUS_NOT_SUPPORTED;
+ }
+
NTSTATUS handle_NtQueryInformationFile(const syscall_context& c, const handle file_handle,
const emulator_object>> io_status_block,
const uint64_t file_information, const uint32_t length,
@@ -1293,6 +1625,32 @@ namespace
return STATUS_INVALID_HANDLE;
}
+ if (info_class == FileNameInformation)
+ {
+ const auto required_length = sizeof(FILE_NAME_INFORMATION) + (f->name.size() * 2);
+
+ if (io_status_block)
+ {
+ IO_STATUS_BLOCK> block{};
+ block.Information = sizeof(FILE_NAME_INFORMATION) + required_length;
+ io_status_block.write(block);
+ }
+
+ if (length != required_length)
+ {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ c.emu.write_memory(file_information, FILE_NAME_INFORMATION{
+ .FileNameLength = static_cast(f->name.size() * 2),
+ });
+
+ c.emu.write_memory(file_information + offsetof(FILE_NAME_INFORMATION, FileName), f->name.c_str(),
+ (f->name.size() + 1) * 2);
+
+ return STATUS_SUCCESS;
+ }
+
if (info_class == FileStandardInformation)
{
if (io_status_block)
@@ -1309,7 +1667,7 @@ namespace
const emulator_object info{c.emu, file_information};
FILE_STANDARD_INFORMATION i{};
- i.Directory = f->handle ? FALSE : TRUE;
+ i.Directory = f->is_directory() ? TRUE : FALSE;
if (f->handle)
{
@@ -1357,16 +1715,15 @@ namespace
}
NTSTATUS handle_NtSetInformationProcess(const syscall_context& c, const handle process_handle,
- const uint32_t info_class, const uint64_t /*process_information*/,
- const uint32_t /*process_information_length*/)
+ const uint32_t info_class, const uint64_t process_information,
+ const uint32_t process_information_length)
{
- if (process_handle != ~0ULL)
+ if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
if (info_class == ProcessSchedulerSharedData
- || info_class == ProcessTlsInformation
|| info_class == ProcessConsoleHostProcess
|| info_class == ProcessFaultInformation
|| info_class == ProcessDefaultHardErrorMode
@@ -1375,12 +1732,84 @@ namespace
return STATUS_SUCCESS;
}
+ if (info_class == ProcessTlsInformation)
+ {
+ constexpr auto thread_data_offset = offsetof(PROCESS_TLS_INFO, ThreadData);
+ if (process_information_length < thread_data_offset)
+ {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ const emulator_object data{c.emu, process_information + thread_data_offset};
+
+ PROCESS_TLS_INFO tls_info{};
+ c.emu.read_memory(process_information, &tls_info, thread_data_offset);
+
+ for (uint32_t i = 0; i < tls_info.ThreadDataCount; ++i)
+ {
+ auto entry = data.read(i);
+
+ const auto _ = utils::finally([&]
+ {
+ data.write(entry, i);
+ });
+
+ if (i >= c.proc.threads.size())
+ {
+ entry.Flags = 0;
+ continue;
+ }
+
+ auto thread_iterator = c.proc.threads.begin();
+ std::advance(thread_iterator, i);
+
+ entry.Flags = 2;
+
+ thread_iterator->second.teb->access([&](TEB64& teb)
+ {
+ entry.ThreadId = teb.ClientId.UniqueThread;
+
+ const auto tls_vector = teb.ThreadLocalStoragePointer;
+
+ if (tls_info.TlsRequest == ProcessTlsReplaceIndex)
+ {
+ const auto tls_entry_ptr = tls_vector + tls_info.TlsIndex;
+
+ const auto old_entry = c.emu.read_memory(tls_entry_ptr);
+ c.emu.write_memory(tls_entry_ptr, entry.TlsModulePointer);
+
+ entry.TlsModulePointer = old_entry;
+ }
+ else if (tls_info.TlsRequest == ProcessTlsReplaceVector)
+ {
+ const auto new_tls_vector = entry.TlsVector;
+
+ for (uint32_t index = 0; index < tls_info.TlsVectorLength; ++index)
+ {
+ const auto old_entry = c.emu.read_memory(tls_vector + index);
+ c.emu.write_memory(new_tls_vector + index, old_entry);
+ }
+
+ teb.ThreadLocalStoragePointer =new_tls_vector;
+ entry.TlsVector = tls_vector;
+ }
+ });
+ }
+
+ return STATUS_SUCCESS;
+ }
+
printf("Unsupported info process class: %X\n", info_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}
+ NTSTATUS handle_NtSetInformationKey()
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
NTSTATUS handle_NtApphelpCacheControl()
{
return STATUS_NOT_SUPPORTED;
@@ -1392,7 +1821,7 @@ namespace
const uint32_t protection,
const emulator_object old_protection)
{
- if (process_handle != ~0ULL)
+ if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
@@ -1442,6 +1871,12 @@ namespace
return STATUS_SUCCESS;
}
+ if (object_name == u"\\Sessions\\1\\BaseNamedObjects")
+ {
+ directory_handle.write(BASE_NAMED_OBJECTS_DIRECTORY);
+ return STATUS_SUCCESS;
+ }
+
return STATUS_NOT_SUPPORTED;
}
@@ -1501,12 +1936,14 @@ namespace
const uint32_t allocation_type,
const uint32_t page_protection)
{
- if (process_handle != ~0ULL)
+ if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
- const auto allocation_bytes = bytes_to_allocate.read();
+ auto allocation_bytes = bytes_to_allocate.read();
+ allocation_bytes = page_align_up(allocation_bytes);
+ bytes_to_allocate.write(allocation_bytes);
const auto protection = map_nt_to_emulator_protection(page_protection);
@@ -1531,11 +1968,9 @@ namespace
throw std::runtime_error("Unsupported allocation type!");
}
- if (commit && !reserve)
+ if (commit && !reserve && c.emu.commit_memory(potential_base, allocation_bytes, protection))
{
- return c.emu.commit_memory(potential_base, allocation_bytes, protection)
- ? STATUS_SUCCESS
- : STATUS_MEMORY_NOT_ALLOCATED;
+ return STATUS_SUCCESS;
}
return c.emu.allocate_memory(potential_base, allocation_bytes, protection, !commit)
@@ -1556,7 +1991,7 @@ namespace
const emulator_object base_address,
const emulator_object bytes_to_allocate, const uint32_t free_type)
{
- if (process_handle != ~0ULL)
+ if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
@@ -1583,19 +2018,48 @@ namespace
NTSTATUS handle_NtCreateSection(const syscall_context& c, const emulator_object section_handle,
const ACCESS_MASK /*desired_access*/,
- const emulator_object>> /*object_attributes*/,
+ const emulator_object>> object_attributes,
const emulator_object maximum_size,
- const ULONG /*section_page_protection*/, const ULONG /*allocation_attributes*/,
- const handle /*file_handle*/)
+ const ULONG section_page_protection, const ULONG allocation_attributes,
+ const handle file_handle)
{
- //puts("NtCreateSection not supported");
- section_handle.write(SHARED_SECTION);
+ section s{};
+ s.section_page_protection = section_page_protection;
+ s.allocation_attributes = allocation_attributes;
- maximum_size.access([&c](ULARGE_INTEGER& large_int)
+ const auto* file = c.proc.files.get(file_handle);
+ if (file)
{
- large_int.QuadPart = page_align_up(large_int.QuadPart);
- c.proc.shared_section_size = large_int.QuadPart;
- });
+ c.win_emu.logger.print(color::dark_gray, "--> Section for file %S\n", file->name.c_str());
+ s.file_name = file->name;
+ }
+
+ if (object_attributes)
+ {
+ const auto attributes = object_attributes.read();
+ if (attributes.ObjectName)
+ {
+ const auto name = read_unicode_string(c.emu, reinterpret_cast>*>(attributes.ObjectName));
+ c.win_emu.logger.print(color::dark_gray, "--> Section with name %S\n", name.c_str());
+ s.name = std::move(name);
+ }
+ }
+
+ if (maximum_size)
+ {
+ maximum_size.access([&](ULARGE_INTEGER& large_int)
+ {
+ large_int.QuadPart = page_align_up(large_int.QuadPart);
+ s.maximum_size = large_int.QuadPart;
+ });
+ }
+ else if (!file)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ const auto h = c.proc.sections.store(std::move(s));
+ section_handle.write(h);
return STATUS_SUCCESS;
}
@@ -1642,7 +2106,7 @@ namespace
{
number_of_bytes_read.write(0);
- if (process_handle != ~0ULL)
+ if (process_handle != CURRENT_PROCESS)
{
return STATUS_NOT_SUPPORTED;
}
@@ -1703,10 +2167,46 @@ namespace
return STATUS_SUCCESS;
}
- NTSTATUS handle_NtOpenProcessToken()
+ NTSTATUS handle_NtOpenThreadToken(const syscall_context&, const handle thread_handle,
+ const ACCESS_MASK /*desired_access*/, const BOOLEAN /*open_as_self*/,
+ const emulator_object token_handle)
{
- //puts("NtOpenProcessToken not supported");
- return STATUS_NOT_SUPPORTED;
+ if (thread_handle != CURRENT_THREAD)
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ token_handle.write(CURRENT_THREAD_TOKEN);
+
+ return STATUS_SUCCESS;
+ }
+
+ NTSTATUS handle_NtOpenThreadTokenEx(const syscall_context& c, const handle thread_handle,
+ const ACCESS_MASK desired_access, const BOOLEAN open_as_self,
+ const ULONG /*handle_attributes*/,
+ const emulator_object token_handle)
+ {
+ return handle_NtOpenThreadToken(c, thread_handle, desired_access, open_as_self, token_handle);
+ }
+
+ NTSTATUS handle_NtOpenProcessToken(const syscall_context&, const handle process_handle,
+ const ACCESS_MASK /*desired_access*/, const emulator_object token_handle)
+ {
+ if (process_handle != CURRENT_PROCESS)
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ token_handle.write(CURRENT_PROCESS_TOKEN);
+
+ return STATUS_SUCCESS;
+ }
+
+ NTSTATUS handle_NtOpenProcessTokenEx(const syscall_context& c, const handle process_handle,
+ const ACCESS_MASK desired_access, const ULONG /*handle_attributes*/,
+ const emulator_object token_handle)
+ {
+ return handle_NtOpenProcessToken(c, process_handle, desired_access, token_handle);
}
NTSTATUS handle_NtQuerySecurityAttributesToken()
@@ -1727,29 +2227,74 @@ namespace
return STATUS_NOT_SUPPORTED;
}
+ NTSTATUS handle_NtUserSystemParametersInfo()
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ TOKEN_TYPE get_token_type(const handle token_handle)
+ {
+ return token_handle == DUMMY_IMPERSONATION_TOKEN //
+ ? TokenImpersonation
+ : TokenPrimary;
+ }
+
+ NTSTATUS handle_NtDuplicateToken(const syscall_context&, const handle existing_token_handle,
+ ACCESS_MASK /*desired_access*/,
+ const emulator_object>> /*object_attributes*/,
+ const BOOLEAN /*effective_only*/, const TOKEN_TYPE type,
+ const emulator_object new_token_handle)
+ {
+ if (get_token_type(existing_token_handle) == type)
+ {
+ new_token_handle.write(existing_token_handle);
+ }
+ else if (type == TokenPrimary)
+ {
+ new_token_handle.write(CURRENT_PROCESS_TOKEN);
+ }
+ else
+ {
+ new_token_handle.write(DUMMY_IMPERSONATION_TOKEN);
+ }
+
+ return STATUS_SUCCESS;
+ }
+
+ NTSTATUS handle_NtQueryTimerResolution(const syscall_context&, const emulator_object maximum_time,
+ const emulator_object minimum_time,
+ const emulator_object current_time)
+ {
+ maximum_time.write_if_valid(0x0002625a);
+ minimum_time.write_if_valid(0x00001388);
+ current_time.write_if_valid(0x00002710);
+ return STATUS_SUCCESS;
+ }
+
NTSTATUS handle_NtQueryInformationToken(const syscall_context& c, const handle token_handle,
const TOKEN_INFORMATION_CLASS token_information_class,
const uint64_t token_information, const ULONG token_information_length,
const emulator_object return_length)
{
- if (token_handle != ~3ULL // NtCurrentProcessToken
- && token_handle != ~4ULL // NtCurrentThreadToken
- && token_handle != ~5ULL // NtCurrentThreadEffectiveToken
+ if (token_handle != CURRENT_PROCESS_TOKEN
+ && token_handle != CURRENT_THREAD_TOKEN
+ && token_handle != CURRENT_THREAD_EFFECTIVE_TOKEN
+ && token_handle != DUMMY_IMPERSONATION_TOKEN
)
{
return STATUS_NOT_SUPPORTED;
}
+ const uint8_t sid[] =
+ {
+ 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x15, 0x00, 0x00, 0x00, 0x84, 0x94,
+ 0xD4, 0x04, 0x4B, 0x68, 0x42, 0x34, 0x23,
+ 0xBE, 0x69, 0x4E, 0xE9, 0x03, 0x00, 0x00,
+ };
+
if (token_information_class == TokenUser)
{
- const uint8_t sid[] =
- {
- 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x05, 0x15, 0x00, 0x00, 0x00, 0x84, 0x94,
- 0xD4, 0x04, 0x4B, 0x68, 0x42, 0x34, 0x23,
- 0xBE, 0x69, 0x4E, 0xE9, 0x03, 0x00, 0x00,
- };
-
constexpr auto required_size = sizeof(sid) + 0x10;
return_length.write(required_size);
@@ -1767,6 +2312,78 @@ namespace
return STATUS_SUCCESS;
}
+ if (token_information_class == TokenType)
+ {
+ constexpr auto required_size = sizeof(TOKEN_TYPE);
+ return_length.write(required_size);
+
+ if (required_size > token_information_length)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ emulator_object{c.emu, token_information}.write(get_token_type(token_handle));
+ return STATUS_SUCCESS;
+ }
+
+ if (token_information_class == TokenSessionId)
+ {
+ constexpr auto required_size = sizeof(ULONG);
+ return_length.write(required_size);
+
+ if (required_size > token_information_length)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ emulator_object{c.emu, token_information}.write(1);
+ return STATUS_SUCCESS;
+ }
+
+ if (token_information_class == TokenPrivateNameSpace)
+ {
+ constexpr auto required_size = sizeof(ULONG);
+ return_length.write(required_size);
+
+ if (required_size > token_information_length)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ emulator_object{c.emu, token_information}.write(0);
+ return STATUS_SUCCESS;
+ }
+
+ if (token_information_class == TokenUIAccess)
+ {
+ constexpr auto required_size = sizeof(ULONG);
+ return_length.write(required_size);
+
+ if (required_size > token_information_length)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ emulator_object{c.emu, token_information}.write(1);
+ return STATUS_SUCCESS;
+ }
+
+ if (token_information_class == TokenElevation)
+ {
+ constexpr auto required_size = sizeof(TOKEN_ELEVATION);
+ return_length.write(required_size);
+
+ if (required_size > token_information_length)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ c.emu.write_memory(token_information, TOKEN_ELEVATION{
+ .TokenIsElevated = 0,
+ });
+ return STATUS_SUCCESS;
+ }
+
if (token_information_class == TokenIsAppContainer)
{
constexpr auto required_size = sizeof(ULONG);
@@ -1781,6 +2398,76 @@ namespace
return STATUS_SUCCESS;
}
+ if (token_information_class == TokenStatistics)
+ {
+ constexpr auto required_size = sizeof(TOKEN_STATISTICS);
+ return_length.write(required_size);
+
+ if (required_size > token_information_length)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ c.emu.write_memory(token_information, TOKEN_STATISTICS{});
+
+ return STATUS_SUCCESS;
+ }
+
+ if (token_information_class == TokenSecurityAttributes)
+ {
+ constexpr auto required_size = sizeof(TOKEN_SECURITY_ATTRIBUTES_INFORMATION);
+ return_length.write(required_size);
+
+ if (required_size > token_information_length)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ c.emu.write_memory(token_information, TOKEN_SECURITY_ATTRIBUTES_INFORMATION{
+ .Version = 0,
+ .AttributeCount = 0,
+ });
+
+ return STATUS_SUCCESS;
+ }
+
+ if (token_information_class == TokenIntegrityLevel)
+ {
+ constexpr auto required_size = sizeof(sid) + sizeof(TOKEN_MANDATORY_LABEL);
+ return_length.write(required_size);
+
+ if (required_size > token_information_length)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ TOKEN_MANDATORY_LABEL label{};
+ label.Label.Attributes = 0;
+ label.Label.Sid = reinterpret_cast(token_information + sizeof(TOKEN_MANDATORY_LABEL));
+
+ emulator_object{c.emu, token_information}.write(label);
+ c.emu.write_memory(token_information + sizeof(TOKEN_MANDATORY_LABEL), sid, sizeof(sid));
+ return STATUS_SUCCESS;
+ }
+
+ if (token_information_class == TokenBnoIsolation)
+ {
+ constexpr auto required_size = sizeof(TOKEN_BNO_ISOLATION_INFORMATION);
+ return_length.write(required_size);
+
+ if (required_size > token_information_length)
+ {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ c.emu.write_memory(token_information, TOKEN_BNO_ISOLATION_INFORMATION{
+ .IsolationPrefix = nullptr,
+ .IsolationEnabled = 0,
+ });
+
+ return STATUS_SUCCESS;
+ }
+
printf("Unsupported token info class: %X\n", token_information_class);
c.emu.stop();
return STATUS_NOT_SUPPORTED;
@@ -1803,7 +2490,7 @@ namespace
return STATUS_NOT_SUPPORTED;
}
- NTSTATUS handle_NtGdiInit2(const syscall_context& c)
+ NTSTATUS handle_NtGdiInit(const syscall_context& c)
{
c.proc.peb.access([&](PEB64& peb)
{
@@ -1813,6 +2500,12 @@ namespace
}
});
+ return STATUS_WAIT_1;
+ }
+
+ NTSTATUS handle_NtGdiInit2(const syscall_context& c)
+ {
+ handle_NtGdiInit(c);
return STATUS_NOT_SUPPORTED;
}
@@ -1870,6 +2563,8 @@ namespace
return STATUS_NOT_SUPPORTED;
}
+ // TODO: Fix this. This is broken and wrong.
+
const emulator_object>> data{c.emu, receive_message.value() + 0x48};
const auto dest = data.read();
const auto base = dest.Base;
@@ -1927,7 +2622,7 @@ namespace
return STATUS_SUCCESS;
}
- if (process_handle == ~0ULL)
+ if (process_handle == CURRENT_PROCESS)
{
c.proc.exit_status = exit_status;
c.emu.stop();
@@ -2038,7 +2733,7 @@ namespace
{
mode = u"r+b";
}
- else if (desired_access & GENERIC_READ)
+ else if (desired_access & GENERIC_READ || desired_access & SYNCHRONIZE)
{
mode = u"rb";
}
@@ -2152,14 +2847,14 @@ namespace
std::u16string mode = map_mode(desired_access, create_disposition);
- if (mode.length() == 0 || mode == u"")
+ if (mode.empty())
{
return STATUS_NOT_SUPPORTED;
}
FILE* file{};
- const auto error = open_unicode(&file, f.name.c_str(), mode.c_str());
+ const auto error = open_unicode(&file, f.name, mode);
if (!file)
{
@@ -2184,6 +2879,46 @@ namespace
return STATUS_SUCCESS;
}
+ NTSTATUS handle_NtQueryAttributesFile(const syscall_context& c,
+ const emulator_object>> object_attributes,
+ const emulator_object file_information)
+ {
+ if (!object_attributes)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ const auto attributes = object_attributes.read();
+ if (!attributes.ObjectName)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ const auto filename = read_unicode_string(c.emu, emulator_object>>{c.emu, attributes.ObjectName});
+ const auto u8_filename = u16_to_u8(filename);
+
+ struct _stat64 file_stat{};
+#ifdef OS_WINDOWS
+ if (_stat64(u8_filename.c_str(), &file_stat) != 0)
+#else
+ if (stat64(u8_filename.c_str(), &file_stat) != 0)
+#endif
+ {
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ file_information.access([&](FILE_BASIC_INFORMATION& info)
+ {
+ info.CreationTime = convert_unix_to_windows_time(file_stat.st_atime);
+ info.LastAccessTime = convert_unix_to_windows_time(file_stat.st_atime);
+ info.LastWriteTime = convert_unix_to_windows_time(file_stat.st_mtime);
+ info.ChangeTime = info.LastWriteTime;
+ info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ });
+
+ return STATUS_SUCCESS;
+ }
+
NTSTATUS handle_NtOpenFile(const syscall_context& c,
const emulator_object file_handle,
const ACCESS_MASK desired_access,
@@ -2220,6 +2955,11 @@ namespace
return STATUS_NOT_SUPPORTED;
}
+ NTSTATUS handle_NtUserGetKeyboardLayout()
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
NTSTATUS handle_NtRaiseHardError(const syscall_context& c, const NTSTATUS error_status,
const ULONG /*number_of_parameters*/,
const emulator_object>> /*unicode_string_parameter_mask*/,
@@ -2232,7 +2972,7 @@ namespace
response.write(ResponseAbort);
}
- printf("Hard error: %X\n", static_cast(error_status));
+ c.proc.exit_status = error_status;
c.proc.exception_rip = c.emu.read_instruction_pointer();
c.emu.stop();
@@ -2256,6 +2996,39 @@ namespace
return STATUS_SUCCESS;
}
+ NTSTATUS handle_NtOpenSemaphore(const syscall_context& c, const emulator_object