diff --git a/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp b/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp index d240dc4a..856dc743 100644 --- a/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp +++ b/src/backends/icicle-emulator/icicle_x86_64_emulator.cpp @@ -264,9 +264,14 @@ namespace icicle ice(res, "Failed to read memory"); } + bool try_write_memory(const uint64_t address, const void* data, const size_t size) override + { + return icicle_write_memory(this->emu_, address, data, size); + } + void write_memory(const uint64_t address, const void* data, const size_t size) override { - const auto res = icicle_write_memory(this->emu_, address, data, size); + const auto res = try_write_memory(address, data, size); ice(res, "Failed to write memory"); } diff --git a/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp index 31928ae7..e2aaf56a 100644 --- a/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x86_64_emulator.cpp @@ -380,6 +380,11 @@ namespace unicorn uce(uc_mem_read(*this, address, data, size)); } + bool try_write_memory(const uint64_t address, const void* data, const size_t size) override + { + return uc_mem_write(*this, address, data, size) == UC_ERR_OK; + } + void write_memory(const uint64_t address, const void* data, const size_t size) override { uce(uc_mem_write(*this, address, data, size)); diff --git a/src/common/platform/process.hpp b/src/common/platform/process.hpp index a77be9b9..2b8d2c8d 100644 --- a/src/common/platform/process.hpp +++ b/src/common/platform/process.hpp @@ -1124,4 +1124,10 @@ struct PROCESS_PRIORITY_CLASS UCHAR PriorityClass; }; +struct PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION +{ + ULONG Version; + ULONG Reserved; + uint64_t Callback; +}; // NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) diff --git a/src/common/platform/status.hpp b/src/common/platform/status.hpp index 042d9754..23b55e7a 100644 --- a/src/common/platform/status.hpp +++ b/src/common/platform/status.hpp @@ -68,5 +68,7 @@ using NTSTATUS = std::uint32_t; #define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) +#define STATUS_SERVICE_NOTIFICATION ((NTSTATUS)0x40000018L) + #define FILE_DEVICE_NETWORK 0x00000012 #define FSCTL_AFD_BASE FILE_DEVICE_NETWORK diff --git a/src/emulator/memory_interface.hpp b/src/emulator/memory_interface.hpp index 2f735e9a..41145b24 100644 --- a/src/emulator/memory_interface.hpp +++ b/src/emulator/memory_interface.hpp @@ -19,6 +19,7 @@ class memory_interface virtual void read_memory(uint64_t address, void* data, size_t size) const = 0; virtual bool try_read_memory(uint64_t address, void* data, size_t size) const = 0; virtual void write_memory(uint64_t address, const void* data, size_t size) = 0; + virtual bool try_write_memory(uint64_t address, const void* data, size_t size) = 0; private: virtual void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) = 0; diff --git a/src/windows-emulator/emulator_thread.hpp b/src/windows-emulator/emulator_thread.hpp index 4f14aa7a..d80f2556 100644 --- a/src/windows-emulator/emulator_thread.hpp +++ b/src/windows-emulator/emulator_thread.hpp @@ -102,6 +102,8 @@ class emulator_thread : public ref_counted_object std::vector last_registers{}; + bool debugger_hide{false}; + void mark_as_ready(NTSTATUS status); bool is_await_time_over(utils::clock& clock) const @@ -180,6 +182,8 @@ class emulator_thread : public ref_counted_object buffer.write_optional(this->wow64_cpu_reserved); buffer.write_vector(this->last_registers); + + buffer.write(this->debugger_hide); } void deserialize_object(utils::buffer_deserializer& buffer) override @@ -225,6 +229,8 @@ class emulator_thread : public ref_counted_object buffer.read_optional(this->wow64_cpu_reserved, [this] { return emulator_object(*this->memory_ptr); }); buffer.read_vector(this->last_registers); + + buffer.read(this->debugger_hide); } void leak_memory() diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index f8737ff6..c1f992fc 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -104,6 +104,16 @@ class emulator_object return this->address_ != 0; } + std::optional try_read(const size_t index = 0) const + { + T obj{}; + if (this->memory_->try_read_memory(this->address_ + index * this->size(), &obj, sizeof(obj))) + { + return obj; + } + return std::nullopt; + } + T read(const size_t index = 0) const { T obj{}; @@ -111,6 +121,11 @@ class emulator_object return obj; } + bool try_write(const T& value, const size_t index = 0) const + { + return this->memory_->try_write_memory(this->address_ + index * this->size(), &value, sizeof(value)); + } + void write(const T& value, const size_t index = 0) const { this->memory_->write_memory(this->address_ + index * this->size(), &value, sizeof(value)); diff --git a/src/windows-emulator/memory_manager.cpp b/src/windows-emulator/memory_manager.cpp index 2b91105f..125a40b9 100644 --- a/src/windows-emulator/memory_manager.cpp +++ b/src/windows-emulator/memory_manager.cpp @@ -641,7 +641,14 @@ void memory_manager::read_memory(const uint64_t address, void* data, const size_ bool memory_manager::try_read_memory(const uint64_t address, void* data, const size_t size) const { - return this->memory_->try_read_memory(address, data, size); + try + { + return this->memory_->try_read_memory(address, data, size); + } + catch (...) + { + return false; + } } void memory_manager::write_memory(const uint64_t address, const void* data, const size_t size) @@ -649,6 +656,18 @@ void memory_manager::write_memory(const uint64_t address, const void* data, cons this->memory_->write_memory(address, data, size); } +bool memory_manager::try_write_memory(const uint64_t address, const void* data, const size_t size) +{ + try + { + return this->memory_->try_write_memory(address, data, size); + } + catch (...) + { + return false; + } +} + void memory_manager::map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) { this->memory_->map_mmio(address, size, std::move(read_cb), std::move(write_cb)); diff --git a/src/windows-emulator/memory_manager.hpp b/src/windows-emulator/memory_manager.hpp index 44900aba..c7c3ea8f 100644 --- a/src/windows-emulator/memory_manager.hpp +++ b/src/windows-emulator/memory_manager.hpp @@ -65,6 +65,7 @@ class memory_manager : public memory_interface void read_memory(uint64_t address, void* data, size_t size) const final; bool try_read_memory(uint64_t address, void* data, size_t size) const final; void write_memory(uint64_t address, const void* data, size_t size) final; + bool try_write_memory(uint64_t address, const void* data, size_t size) final; bool protect_memory(uint64_t address, size_t size, nt_memory_permission permissions, nt_memory_permission* old_permissions = nullptr); diff --git a/src/windows-emulator/process_context.cpp b/src/windows-emulator/process_context.cpp index c48c155c..ea8cfee3 100644 --- a/src/windows-emulator/process_context.cpp +++ b/src/windows-emulator/process_context.cpp @@ -388,6 +388,7 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, regist this->rtl_user_thread_start = ntdll.find_export("RtlUserThreadStart"); this->ki_user_apc_dispatcher = ntdll.find_export("KiUserApcDispatcher"); this->ki_user_exception_dispatcher = ntdll.find_export("KiUserExceptionDispatcher"); + this->instrumentation_callback = 0; this->default_register_set = emu.save_registers(); } @@ -413,6 +414,7 @@ void process_context::serialize(utils::buffer_serializer& buffer) const buffer.write_optional(this->rtl_user_thread_start32); buffer.write(this->ki_user_apc_dispatcher); buffer.write(this->ki_user_exception_dispatcher); + buffer.write(this->instrumentation_callback); buffer.write(this->events); buffer.write(this->files); @@ -467,6 +469,7 @@ void process_context::deserialize(utils::buffer_deserializer& buffer) buffer.read_optional(this->rtl_user_thread_start32); buffer.read(this->ki_user_apc_dispatcher); buffer.read(this->ki_user_exception_dispatcher); + buffer.read(this->instrumentation_callback); buffer.read(this->events); buffer.read(this->files); diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 634fe3c4..01070a06 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -110,6 +110,7 @@ struct process_context uint64_t rtl_user_thread_start{}; uint64_t ki_user_apc_dispatcher{}; uint64_t ki_user_exception_dispatcher{}; + uint64_t instrumentation_callback{}; // For WOW64 processes std::optional> peb32; diff --git a/src/windows-emulator/syscall_dispatcher.cpp b/src/windows-emulator/syscall_dispatcher.cpp index 370da9af..31489191 100644 --- a/src/windows-emulator/syscall_dispatcher.cpp +++ b/src/windows-emulator/syscall_dispatcher.cpp @@ -101,6 +101,8 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu) } entry->second.handler(c); + + dispatch_callback(win_emu, entry->second.name); } catch (std::exception& e) { @@ -116,6 +118,24 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu) } } +void syscall_dispatcher::dispatch_callback(windows_emulator& win_emu, std::string& syscall_name) +{ + auto& emu = win_emu.emu(); + auto& context = win_emu.process; + + if (context.instrumentation_callback != 0 && syscall_name != "NtContinue") + { + auto rip_old = emu.reg(x86_register::rip); + + // The increase in RIP caused by executing the syscall here has not yet occurred. + // If RIP is set directly, it will lead to an incorrect address, so the length of + // the syscall instruction needs to be subtracted. + emu.reg(x86_register::rip, context.instrumentation_callback - 2); + + emu.reg(x86_register::r10, rip_old); + } +} + syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, const std::span ntdll_data, const exported_symbols& win32u_exports, const std::span win32u_data) { diff --git a/src/windows-emulator/syscall_dispatcher.hpp b/src/windows-emulator/syscall_dispatcher.hpp index d8c80374..3078eb34 100644 --- a/src/windows-emulator/syscall_dispatcher.hpp +++ b/src/windows-emulator/syscall_dispatcher.hpp @@ -21,6 +21,7 @@ class syscall_dispatcher std::span win32u_data); void dispatch(windows_emulator& win_emu); + static void dispatch_callback(windows_emulator& win_emu, std::string& syscall_name); void serialize(utils::buffer_serializer& buffer) const; void deserialize(utils::buffer_deserializer& buffer); diff --git a/src/windows-emulator/syscall_utils.hpp b/src/windows-emulator/syscall_utils.hpp index ad7ce564..564bea14 100644 --- a/src/windows-emulator/syscall_utils.hpp +++ b/src/windows-emulator/syscall_utils.hpp @@ -232,7 +232,7 @@ NTSTATUS handle_query(x86_64_emulator& emu, const uint64_t buffer, const uint32_ const auto length_setter = [&](const size_t required_size) { if (return_length) { - return_length.write(static_cast(required_size)); + return_length.try_write(static_cast(required_size)); } }; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 15cd7d67..9ee433c0 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -27,7 +27,7 @@ namespace syscalls // syscalls/exception.cpp NTSTATUS handle_NtRaiseHardError(const syscall_context& c, NTSTATUS error_status, ULONG number_of_parameters, emulator_object>> unicode_string_parameter_mask, - emulator_object parameters, HARDERROR_RESPONSE_OPTION valid_response_option, + uint64_t parameters, HARDERROR_RESPONSE_OPTION valid_response_option, emulator_object response); NTSTATUS handle_NtRaiseException(const syscall_context& c, emulator_object>> exception_record, @@ -131,6 +131,9 @@ namespace syscalls NTSTATUS handle_NtReadVirtualMemory(const syscall_context& c, handle process_handle, emulator_pointer base_address, emulator_pointer buffer, ULONG number_of_bytes_to_read, emulator_object number_of_bytes_read); + NTSTATUS handle_NtWriteVirtualMemory(const syscall_context& c, handle process_handle, emulator_pointer base_address, + emulator_pointer buffer, ULONG number_of_bytes_to_write, + emulator_object number_of_bytes_write); NTSTATUS handle_NtSetInformationVirtualMemory(); BOOL handle_NtLockVirtualMemory(); @@ -1094,6 +1097,7 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtCreateSemaphore); add_handler(NtOpenSemaphore); add_handler(NtReadVirtualMemory); + add_handler(NtWriteVirtualMemory); add_handler(NtQueryInformationToken); add_handler(NtDxgkIsFeatureEnabled); add_handler(NtAddAtomEx); diff --git a/src/windows-emulator/syscalls/exception.cpp b/src/windows-emulator/syscalls/exception.cpp index b18edbf0..e2f2847d 100644 --- a/src/windows-emulator/syscalls/exception.cpp +++ b/src/windows-emulator/syscalls/exception.cpp @@ -4,10 +4,9 @@ namespace syscalls { - NTSTATUS handle_NtRaiseHardError(const syscall_context& c, const NTSTATUS error_status, const ULONG /*number_of_parameters*/, - const emulator_object>> - /*unicode_string_parameter_mask*/, - const emulator_object /*parameters*/, const HARDERROR_RESPONSE_OPTION /*valid_response_option*/, + NTSTATUS handle_NtRaiseHardError(const syscall_context& c, const NTSTATUS error_status, const ULONG number_of_parameters, + const emulator_object>> /*unicode_string_parameter_mask*/, + const uint64_t parameters, const HARDERROR_RESPONSE_OPTION /*valid_response_option*/, const emulator_object response) { if (response) @@ -15,6 +14,18 @@ namespace syscalls response.write(ResponseAbort); } + if (error_status & STATUS_SERVICE_NOTIFICATION && number_of_parameters >= 3) + { + std::array params = {0, 0, 0}; + + if (c.emu.try_read_memory(parameters, ¶ms, sizeof(params))) + { + std::u16string message = + read_unicode_string(c.emu, emulator_object>>{c.emu, params[0]}); + c.win_emu.log.error("Error Message: %s\n", u16_to_u8(message).c_str()); + } + } + c.proc.exit_status = error_status; c.win_emu.callbacks.on_exception(); c.emu.stop(); diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index 25bfb3c8..43fe8497 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -1014,6 +1014,12 @@ namespace syscalls std::error_code ec{}; const windows_path path = f.name; + + if (!path.is_absolute()) + { + return STATUS_OBJECT_NAME_NOT_FOUND; + } + const bool is_directory = std::filesystem::is_directory(c.win_emu.file_sys.translate(path), ec); if (is_directory || create_options & FILE_DIRECTORY_FILE) diff --git a/src/windows-emulator/syscalls/memory.cpp b/src/windows-emulator/syscalls/memory.cpp index 0b8e45c3..705c2efd 100644 --- a/src/windows-emulator/syscalls/memory.cpp +++ b/src/windows-emulator/syscalls/memory.cpp @@ -29,7 +29,9 @@ namespace syscalls return STATUS_INVALID_PARAMETER; } - if (info_class == MemoryBasicInformation) + // https://www.exploit-db.com/exploits/44464 + // Both information classes appear to return the same output structure, MEMORY_BASIC_INFORMATION + if (info_class == MemoryBasicInformation || info_class == MemoryPrivilegedBasicInformation) { if (return_length) { @@ -208,6 +210,16 @@ namespace syscalls { potential_base = c.win_emu.memory.find_free_allocation_base(static_cast(allocation_bytes)); } + else + { + // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntallocatevirtualmemory + // BaseAddress + // A pointer to a variable that will receive the base address of the allocated region of pages. If the + // initial value of BaseAddress is non-NULL, the region is allocated starting at the specified virtual + // address rounded down to the next host page size address boundary. If the initial value of BaseAddress + // is NULL, the operating system will determine where to allocate the region. + potential_base = page_align_up(potential_base); + } if (!potential_base) { @@ -280,23 +292,53 @@ namespace syscalls const emulator_pointer buffer, const ULONG number_of_bytes_to_read, const emulator_object number_of_bytes_read) { - number_of_bytes_read.write(0); + number_of_bytes_read.try_write(0); if (process_handle != CURRENT_PROCESS) { return STATUS_NOT_SUPPORTED; } - std::vector memory{}; - memory.resize(number_of_bytes_to_read); + std::vector memory(number_of_bytes_to_read, 0); - if (!c.emu.try_read_memory(base_address, memory.data(), memory.size())) + if (!c.emu.try_read_memory(base_address, memory.data(), number_of_bytes_to_read)) { return STATUS_INVALID_ADDRESS; } - c.emu.write_memory(buffer, memory.data(), memory.size()); - number_of_bytes_read.write(number_of_bytes_to_read); + if (!c.emu.try_write_memory(buffer, memory.data(), number_of_bytes_to_read)) + { + return STATUS_INVALID_ADDRESS; + } + + number_of_bytes_read.try_write(number_of_bytes_to_read); + return STATUS_SUCCESS; + } + + NTSTATUS handle_NtWriteVirtualMemory(const syscall_context& c, const handle process_handle, const emulator_pointer base_address, + const emulator_pointer buffer, const ULONG number_of_bytes_to_write, + const emulator_object number_of_bytes_write) + { + number_of_bytes_write.try_write(0); + + if (process_handle != CURRENT_PROCESS) + { + return STATUS_NOT_SUPPORTED; + } + + std::vector memory(number_of_bytes_to_write, 0); + + if (!c.emu.try_read_memory(buffer, memory.data(), number_of_bytes_to_write)) + { + return STATUS_INVALID_ADDRESS; + } + + if (!c.emu.try_write_memory(base_address, memory.data(), number_of_bytes_to_write)) + { + return STATUS_INVALID_ADDRESS; + } + + number_of_bytes_write.try_write(number_of_bytes_to_write); return STATUS_SUCCESS; } diff --git a/src/windows-emulator/syscalls/object.cpp b/src/windows-emulator/syscalls/object.cpp index 99b77746..1d474ffe 100644 --- a/src/windows-emulator/syscalls/object.cpp +++ b/src/windows-emulator/syscalls/object.cpp @@ -7,6 +7,12 @@ namespace syscalls NTSTATUS handle_NtClose(const syscall_context& c, const handle h) { const auto value = h.value; + + if (h.h == 0xDEADC0DE) + { + return STATUS_INVALID_HANDLE; + } + if (value.is_pseudo) { return STATUS_SUCCESS; diff --git a/src/windows-emulator/syscalls/process.cpp b/src/windows-emulator/syscalls/process.cpp index 0d9105e6..edb18355 100644 --- a/src/windows-emulator/syscalls/process.cpp +++ b/src/windows-emulator/syscalls/process.cpp @@ -281,6 +281,22 @@ namespace syscalls return STATUS_SUCCESS; } + if (info_class == ProcessInstrumentationCallback) + { + if (process_information_length != sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION)) + { + return STATUS_BUFFER_OVERFLOW; + } + + PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info; + + c.emu.read_memory(process_information, &info, sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION)); + + c.proc.instrumentation_callback = info.Callback; + + return STATUS_SUCCESS; + } + c.win_emu.log.error("Unsupported info process class: %X\n", info_class); c.emu.stop(); diff --git a/src/windows-emulator/syscalls/section.cpp b/src/windows-emulator/syscalls/section.cpp index d54d3298..327f937f 100644 --- a/src/windows-emulator/syscalls/section.cpp +++ b/src/windows-emulator/syscalls/section.cpp @@ -223,7 +223,7 @@ namespace syscalls } if (attributes.RootDirectory != KNOWN_DLLS_DIRECTORY && attributes.RootDirectory != KNOWN_DLLS32_DIRECTORY && - attributes.RootDirectory != BASE_NAMED_OBJECTS_DIRECTORY) + attributes.RootDirectory != BASE_NAMED_OBJECTS_DIRECTORY && !filename.starts_with(u"\\KnownDlls")) { c.win_emu.log.error("Unsupported section\n"); c.emu.stop(); diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index ffd9120b..49d8c4dc 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -58,6 +58,7 @@ namespace syscalls if (info_class == ThreadHideFromDebugger) { + c.win_emu.current_thread().debugger_hide = true; c.win_emu.callbacks.on_suspicious_activity("Hiding thread from debugger"); return STATUS_SUCCESS; } @@ -134,6 +135,8 @@ namespace syscalls return STATUS_INVALID_HANDLE; } + emulator_thread& cur_emulator_thread = c.win_emu.current_thread(); + if (info_class == ThreadWow64Context) { // ThreadWow64Context is only valid for WOW64 processes @@ -277,7 +280,7 @@ namespace syscalls } const emulator_object info{c.emu, thread_information}; - info.write(0); + info.write(cur_emulator_thread.debugger_hide); return STATUS_SUCCESS; }