diff --git a/src/common/platform/kernel_mapped.hpp b/src/common/platform/kernel_mapped.hpp index e58e3b3b..9a082611 100644 --- a/src/common/platform/kernel_mapped.hpp +++ b/src/common/platform/kernel_mapped.hpp @@ -863,28 +863,26 @@ typedef struct _SYSTEM_TIMEOFDAY_INFORMATION64 ULONGLONG SleepTimeBias; } SYSTEM_TIMEOFDAY_INFORMATION64, *PSYSTEM_TIMEOFDAY_INFORMATION64; -#ifndef OS_WINDOWS -typedef struct _SYSTEMTIME +typedef struct _SYSTEMTIME64 { WORD wYear; WORD wMonth; - WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; -} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME; -#endif + WORD wDayOfWeek; +} SYSTEMTIME64, *PSYSTEMTIME64, *LPSYSTEMTIME64; typedef struct _SYSTEM_TIMEZONE_INFORMATION { LONG Bias; ARRAY_CONTAINER StandardName; - SYSTEMTIME StandardDate; + SYSTEMTIME64 StandardDate; LONG StandardBias; ARRAY_CONTAINER DaylightName; - SYSTEMTIME DaylightDate; + SYSTEMTIME64 DaylightDate; LONG DaylightBias; } SYSTEM_TIMEZONE_INFORMATION, *PSYSTEM_TIMEZONE_INFORMATION; @@ -892,10 +890,10 @@ typedef struct _SYSTEM_DYNAMIC_TIMEZONE_INFORMATION { LONG Bias; ARRAY_CONTAINER StandardName; - SYSTEMTIME StandardDate; + SYSTEMTIME64 StandardDate; LONG StandardBias; ARRAY_CONTAINER DaylightName; - SYSTEMTIME DaylightDate; + SYSTEMTIME64 DaylightDate; LONG DaylightBias; ARRAY_CONTAINER TimeZoneKeyName; BOOLEAN DynamicDaylightTimeDisabled; diff --git a/src/common/utils/container.hpp b/src/common/utils/container.hpp index 1dee02e5..0bdb889a 100644 --- a/src/common/utils/container.hpp +++ b/src/common/utils/container.hpp @@ -8,26 +8,28 @@ namespace utils { - struct string_hash + template + struct basic_string_hash { using is_transparent = void; - size_t operator()(const std::string_view str) const + size_t operator()(const std::basic_string_view str) const { - constexpr std::hash hasher{}; + constexpr std::hash> hasher{}; return hasher(str); } }; - struct insensitive_string_hash + template + struct basic_insensitive_string_hash { using is_transparent = void; - size_t operator()(const std::string_view str) const + size_t operator()(const std::basic_string_view str) const { size_t hash = 0; constexpr std::hash hasher{}; - for (const char c : str) + for (const auto c : str) { hash ^= hasher(string::char_to_lower(c)) + 0x9e3779b9 + (hash << 6) + (hash >> 2); } @@ -35,22 +37,39 @@ namespace utils } }; - struct insensitive_string_equal + template + struct basic_insensitive_string_equal { using is_transparent = void; - bool operator()(const std::string_view lhs, const std::string_view rhs) const + bool operator()(const std::basic_string_view lhs, + const std::basic_string_view rhs) const { return string::equals_ignore_case(lhs, rhs); } }; + using string_hash = basic_string_hash>; + using u16string_hash = basic_string_hash>; + + using insensitive_string_hash = basic_insensitive_string_hash>; + using insensitive_u16string_hash = basic_insensitive_string_hash>; + + using insensitive_string_equal = basic_insensitive_string_equal>; + using insensitive_u16string_equal = basic_insensitive_string_equal>; + template using unordered_string_map = std::unordered_map>; + template + using unordered_u16string_map = std::unordered_map>; template using unordered_insensitive_string_map = std::unordered_map; + template + using unordered_insensitive_u16string_map = + std::unordered_map; using unordered_string_set = std::unordered_set>; + using unordered_u16string_set = std::unordered_set>; } diff --git a/src/samples/test-sample/test.cpp b/src/samples/test-sample/test.cpp index 7923e6d1..520ceab3 100644 --- a/src/samples/test-sample/test.cpp +++ b/src/samples/test-sample/test.cpp @@ -11,6 +11,7 @@ #define NOMINMAX #define WIN32_LEAN_AND_MEAN +#include #include #include #include @@ -458,6 +459,48 @@ namespace return true; } + bool test_time_zone() + { + DYNAMIC_TIME_ZONE_INFORMATION current_dtzi = {}; + DWORD result = GetDynamicTimeZoneInformation(¤t_dtzi); + + if (result == TIME_ZONE_ID_INVALID) + { + return false; + } + + if (current_dtzi.Bias != -60 || current_dtzi.StandardBias != 0 || current_dtzi.DaylightBias != -60 || + current_dtzi.DynamicDaylightTimeDisabled != FALSE) + { + return false; + } + + if (wcscmp(current_dtzi.StandardName, L"W. Europe Standard Time") != 0 || + wcscmp(current_dtzi.DaylightName, L"W. Europe Daylight Time") != 0 || + wcscmp(current_dtzi.TimeZoneKeyName, L"W. Europe Standard Time") != 0) + { + return false; + } + + if (current_dtzi.StandardDate.wYear != 0 || current_dtzi.StandardDate.wMonth != 10 || + current_dtzi.StandardDate.wDayOfWeek != 0 || current_dtzi.StandardDate.wDay != 5 || + current_dtzi.StandardDate.wHour != 3 || current_dtzi.StandardDate.wMinute != 0 || + current_dtzi.StandardDate.wSecond != 0 || current_dtzi.StandardDate.wMilliseconds != 0) + { + return false; + } + + if (current_dtzi.DaylightDate.wYear != 0 || current_dtzi.DaylightDate.wMonth != 3 || + current_dtzi.DaylightDate.wDayOfWeek != 0 || current_dtzi.DaylightDate.wDay != 5 || + current_dtzi.DaylightDate.wHour != 2 || current_dtzi.DaylightDate.wMinute != 0 || + current_dtzi.DaylightDate.wSecond != 0 || current_dtzi.DaylightDate.wMilliseconds != 0) + { + return false; + } + + return true; + } + void throw_exception() { if (do_the_task) @@ -599,6 +642,36 @@ namespace return test_access_violation_exception() && test_illegal_instruction_exception(); } + bool trap_flag_cleared = false; + constexpr DWORD TRAP_FLAG_MASK = 0x100; + + LONG NTAPI single_step_handler(PEXCEPTION_POINTERS exception_info) + { + if (exception_info->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) + { + PCONTEXT context = exception_info->ContextRecord; + trap_flag_cleared = (context->EFlags & TRAP_FLAG_MASK) == 0; + return EXCEPTION_CONTINUE_EXECUTION; + } + + return EXCEPTION_CONTINUE_SEARCH; + } + + bool test_interrupts() + { + PVOID veh_handle = AddVectoredExceptionHandler(1, single_step_handler); + if (!veh_handle) + return false; + + __writeeflags(__readeflags() | TRAP_FLAG_MASK); + + __nop(); + + RemoveVectoredExceptionHandler(veh_handle); + + return trap_flag_cleared; + } + void print_time() { const auto epoch_time = std::chrono::system_clock::now().time_since_epoch(); @@ -651,10 +724,15 @@ int main(const int argc, const char* argv[]) RUN_TEST(test_working_directory, "Working Directory") RUN_TEST(test_registry, "Registry") RUN_TEST(test_system_info, "System Info") + RUN_TEST(test_time_zone, "Time Zone") RUN_TEST(test_threads, "Threads") RUN_TEST(test_env, "Environment") RUN_TEST(test_exceptions, "Exceptions") RUN_TEST(test_native_exceptions, "Native Exceptions") + if (!getenv("EMULATOR_ICICLE")) + { + RUN_TEST(test_interrupts, "Interrupts") + } RUN_TEST(test_tls, "TLS") RUN_TEST(test_socket, "Socket") RUN_TEST(test_apc, "APC") diff --git a/src/windows-emulator/process_context.cpp b/src/windows-emulator/process_context.cpp index bb0dd610..a41abcf1 100644 --- a/src/windows-emulator/process_context.cpp +++ b/src/windows-emulator/process_context.cpp @@ -25,11 +25,121 @@ namespace emu.write_memory(GDT_ADDR + 5 * (sizeof(uint64_t)), 0xEFF6000000FFFF); emu.reg(x86_register::ss, 0x2B); } + + std::u16string expand_environment_string(const std::u16string& input, + const utils::unordered_insensitive_u16string_map& env_map) + { + std::u16string result; + result.reserve(input.length()); + size_t pos = 0; + + while (pos < input.length()) + { + size_t start = input.find(u'%', pos); + if (start == std::u16string::npos) + { + result.append(input.substr(pos)); + break; + } + + result.append(input.substr(pos, start - pos)); + + size_t end = input.find(u'%', start + 1); + if (end == std::u16string::npos) + { + result.append(input.substr(start)); + break; + } + + std::u16string var_name = input.substr(start + 1, end - start - 1); + + if (var_name.empty()) + { + result.append(u"%%"); + } + else + { + auto it = env_map.find(var_name); + result.append(it != env_map.end() ? it->second : input.substr(start, end - start + 1)); + } + + pos = end + 1; + } + return result; + } + + utils::unordered_insensitive_u16string_map get_environment_variables(registry_manager& registry) + { + utils::unordered_insensitive_u16string_map env_map; + std::unordered_set keys_to_expand; + + const auto env_key = + registry.get_key({R"(\Registry\Machine\System\CurrentControlSet\Control\Session Manager\Environment)"}); + if (env_key) + { + for (size_t i = 0; const auto value_opt = registry.get_value(*env_key, i); i++) + { + const auto& value = *value_opt; + + if (value.type != REG_SZ && value.type != REG_EXPAND_SZ) + { + continue; + } + + if (value.data.empty() || value.data.size() % 2 != 0) + { + continue; + } + + const auto char_count = value.data.size() / sizeof(char16_t); + const auto* data_ptr = reinterpret_cast(value.data.data()); + if (data_ptr[char_count - 1] != u'\0') + { + continue; + } + + const auto [it, inserted] = + env_map.emplace(u8_to_u16(value.name), std::u16string(data_ptr, char_count - 1)); + if (inserted && value.type == REG_EXPAND_SZ) + { + keys_to_expand.insert(it->first); + } + } + } + + env_map[u"EMULATOR"] = u"1"; + + const auto* env = getenv("EMULATOR_ICICLE"); + if (env && (env == "1"sv || env == "true"sv)) + { + env_map[u"EMULATOR_ICICLE"] = u"1"; + } + + env_map[u"COMPUTERNAME"] = u"momo"; + env_map[u"USERNAME"] = u"momo"; + env_map[u"SystemDrive"] = u"C:"; + env_map[u"SystemRoot"] = u"C:\\WINDOWS"; + + for (const auto& key : keys_to_expand) + { + auto it = env_map.find(key); + if (it != env_map.end()) + { + std::u16string expanded = expand_environment_string(it->second, env_map); + if (expanded != it->second) + { + it->second = expanded; + } + } + } + + return env_map; + } } -void process_context::setup(x86_64_emulator& emu, memory_manager& memory, const application_settings& app_settings, - const mapped_module& executable, const mapped_module& ntdll, - const apiset::container& apiset_container) +void process_context::setup(x86_64_emulator& emu, memory_manager& memory, registry_manager& registry, + const application_settings& app_settings, const mapped_module& executable, + const mapped_module& ntdll, const apiset::container& apiset_container) { setup_gdt(emu, memory); @@ -66,9 +176,17 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, const proc_params.StandardError = proc_params.StandardOutput; proc_params.Environment = allocator.copy_string(u"=::=::\\"); - allocator.copy_string(u"EMULATOR=1"); - allocator.copy_string(u"COMPUTERNAME=momo"); - allocator.copy_string(u"SystemRoot=C:\\WINDOWS"); + + const auto env_map = get_environment_variables(registry); + for (const auto& [name, value] : env_map) + { + std::u16string entry; + entry += name; + entry += u"="; + entry += value; + allocator.copy_string(entry); + } + allocator.copy_string(u""); const auto application_str = app_settings.application.u16string(); diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 293b7d03..98245922 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -59,8 +59,9 @@ struct process_context { } - void setup(x86_64_emulator& emu, memory_manager& memory, const application_settings& app_settings, - const mapped_module& executable, const mapped_module& ntdll, const apiset::container& apiset_container); + void setup(x86_64_emulator& emu, memory_manager& memory, registry_manager& registry, + const application_settings& app_settings, const mapped_module& executable, const mapped_module& ntdll, + const apiset::container& apiset_container); handle create_thread(memory_manager& memory, uint64_t start_address, uint64_t argument, uint64_t stack_size, bool suspended); diff --git a/src/windows-emulator/registry/registry_manager.hpp b/src/windows-emulator/registry/registry_manager.hpp index bf601559..4107f97a 100644 --- a/src/windows-emulator/registry/registry_manager.hpp +++ b/src/windows-emulator/registry/registry_manager.hpp @@ -5,6 +5,25 @@ #include "serialization_helper.hpp" #include "../handles.hpp" +#ifndef OS_WINDOWS +#define REG_NONE (0ul) // No value type +#define REG_SZ (1ul) // Unicode nul terminated string +#define REG_EXPAND_SZ \ + (2ul) // Unicode nul terminated string + // (with environment variable references) +#define REG_BINARY (3ul) // Free form binary +#define REG_DWORD (4ul) // 32-bit number +#define REG_DWORD_LITTLE_ENDIAN (4ul) // 32-bit number (same as REG_DWORD) +#define REG_DWORD_BIG_ENDIAN (5ul) // 32-bit number +#define REG_LINK (6ul) // Symbolic Link (unicode) +#define REG_MULTI_SZ (7ul) // Multiple Unicode strings +#define REG_RESOURCE_LIST (8ul) // Resource list in the resource map +#define REG_FULL_RESOURCE_DESCRIPTOR (9ul) // Resource list in the hardware description +#define REG_RESOURCE_REQUIREMENTS_LIST (10ul) +#define REG_QWORD (11ul) // 64-bit number +#define REG_QWORD_LITTLE_ENDIAN (11ul) // 64-bit number (same as REG_QWORD) +#endif + struct registry_key : ref_counted_object { utils::path_key hive{}; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 610361ca..0090997f 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -215,6 +215,7 @@ namespace syscalls emulator_object return_length); NTSTATUS handle_NtSetInformationProcess(const syscall_context& c, handle process_handle, uint32_t info_class, uint64_t process_information, uint32_t process_information_length); + NTSTATUS handle_NtOpenProcess(); NTSTATUS handle_NtOpenProcessToken(const syscall_context&, handle process_handle, ACCESS_MASK /*desired_access*/, emulator_object token_handle); NTSTATUS handle_NtOpenProcessTokenEx(const syscall_context& c, handle process_handle, ACCESS_MASK desired_access, @@ -769,6 +770,7 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtCreateFile); add_handler(NtDeviceIoControlFile); add_handler(NtQueryWnfStateData); + add_handler(NtOpenProcess); add_handler(NtOpenProcessToken); add_handler(NtOpenProcessTokenEx); add_handler(NtQuerySecurityAttributesToken); diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index 4d58646d..6c3dcd41 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -140,9 +140,20 @@ namespace syscalls { if (!f->enumeration_state || query_flags & SL_RESTART_SCAN) { + const auto mask = file_mask ? read_unicode_string(c.emu, file_mask) : u""; + + if (!mask.empty()) + { + c.win_emu.log.print(color::dark_gray, "--> Enumerating directory: %s (Mask: \"%s\")\n", + u16_to_u8(f->name).c_str(), u16_to_u8(mask).c_str()); + } + else + { + c.win_emu.log.print(color::dark_gray, "--> Enumerating directory: %s\n", u16_to_u8(f->name).c_str()); + } + f->enumeration_state.emplace(file_enumeration_state{}); - f->enumeration_state->files = scan_directory(c.win_emu.file_sys.translate(f->name), - file_mask ? read_unicode_string(c.emu, file_mask) : u""); + f->enumeration_state->files = scan_directory(c.win_emu.file_sys.translate(f->name), mask); } auto& enum_state = *f->enumeration_state; @@ -154,6 +165,10 @@ namespace syscalls if (current_index >= enum_state.files.size()) { + IO_STATUS_BLOCK> block{}; + block.Information = 0; + io_status_block.write(block); + return STATUS_NO_MORE_FILES; } @@ -191,11 +206,7 @@ namespace syscalls T info{}; info.NextEntryOffset = 0; info.FileIndex = static_cast(current_index); - info.FileAttributes = FILE_ATTRIBUTE_NORMAL; - if (current_file.is_directory) - { - info.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; - } + info.FileAttributes = current_file.is_directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL; info.FileNameLength = static_cast(file_name.size() * 2); info.EndOfFile.QuadPart = current_file.file_size; @@ -722,9 +733,21 @@ namespace syscalls return STATUS_INVALID_PARAMETER; } - const auto filename = read_unicode_string( + auto filename = read_unicode_string( c.emu, emulator_object>>{c.emu, attributes.ObjectName}); + if (attributes.RootDirectory) + { + const auto* root = c.proc.files.get(attributes.RootDirectory); + if (!root) + { + return STATUS_INVALID_HANDLE; + } + + const auto has_separator = root->name.ends_with(u"\\") || root->name.ends_with(u"/"); + filename = root->name + (has_separator ? u"" : u"\\") + filename; + } + c.win_emu.log.print(color::dark_gray, "--> Querying file attributes: %s\n", u16_to_u8(filename).c_str()); const auto local_filename = c.win_emu.file_sys.translate(filename).string(); diff --git a/src/windows-emulator/syscalls/process.cpp b/src/windows-emulator/syscalls/process.cpp index 7b000840..46c58f0c 100644 --- a/src/windows-emulator/syscalls/process.cpp +++ b/src/windows-emulator/syscalls/process.cpp @@ -348,6 +348,11 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } + NTSTATUS handle_NtOpenProcess() + { + return STATUS_NOT_SUPPORTED; + } + NTSTATUS handle_NtOpenProcessToken(const syscall_context&, const handle process_handle, const ACCESS_MASK /*desired_access*/, const emulator_object token_handle) { diff --git a/src/windows-emulator/syscalls/section.cpp b/src/windows-emulator/syscalls/section.cpp index d15e770a..27f3cb2e 100644 --- a/src/windows-emulator/syscalls/section.cpp +++ b/src/windows-emulator/syscalls/section.cpp @@ -171,6 +171,9 @@ namespace syscalls c.emu.write_memory(obj_address + 0x9C8, 0xFFFFFFFF); // TIME_ZONE_ID_INVALID + // Windows 2019 offset! + c.emu.write_memory(obj_address + 0xA70, 0xFFFFFFFF); // TIME_ZONE_ID_INVALID + if (view_size) { view_size.write(shared_section_size); diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index c5f90080..9824a9b3 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -345,7 +345,7 @@ void windows_emulator::setup_process(const application_settings& app_settings) const auto apiset_data = apiset::obtain(this->emulation_root); - this->process.setup(this->emu(), this->memory, app_settings, *executable, *ntdll, apiset_data); + this->process.setup(this->emu(), this->memory, this->registry, app_settings, *executable, *ntdll, apiset_data); const auto ntdll_data = emu.read_memory(ntdll->image_base, static_cast(ntdll->size_of_image)); const auto win32u_data = emu.read_memory(win32u->image_base, static_cast(win32u->size_of_image)); @@ -515,6 +515,7 @@ void windows_emulator::setup_hooks() this->emu().hook_interrupt([&](const int interrupt) { const auto rip = this->emu().read_instruction_pointer(); + const auto eflags = this->emu().reg(x86_register::eflags); switch (interrupt) { @@ -522,7 +523,15 @@ void windows_emulator::setup_hooks() dispatch_integer_division_by_zero(this->emu(), this->process); return; case 1: - this->log.print(color::pink, "Singlestep: 0x%" PRIx64 "\n", rip); + if ((eflags & 0x100) != 0) + { + this->log.print(color::pink, "Singlestep (Trap Flag): 0x%" PRIx64 "\n", rip); + this->emu().reg(x86_register::eflags, eflags & ~0x100); + } + else + { + this->log.print(color::pink, "Singlestep: 0x%" PRIx64 "\n", rip); + } dispatch_single_step(this->emu(), this->process); return; case 3: @@ -532,6 +541,10 @@ void windows_emulator::setup_hooks() case 6: dispatch_illegal_instruction_violation(this->emu(), this->process); return; + case 45: + this->log.print(color::pink, "DbgPrint: 0x%" PRIx64 "\n", rip); + dispatch_breakpoint(this->emu(), this->process); + return; default: break; }