diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2349029f..61f4810a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -224,8 +224,13 @@ jobs: name: Temp ${{ matrix.filesystem }} Root FS path: build/${{matrix.preset}}/artifacts/root + - name: Copy Test Sample + run: cp build/${{matrix.preset}}/artifacts/test-sample.exe build/${{matrix.preset}}/artifacts/root/filesys/c/ + - name: CMake Test run: cd build/${{matrix.preset}} && ctest --verbose + env: + EMULATOR_ROOT: ${{github.workspace}}/build/${{matrix.preset}}/artifacts/root summary: name: Pipeline Summary diff --git a/cmake/compiler-env.cmake b/cmake/compiler-env.cmake index a0eebc1e..6f5b48a7 100644 --- a/cmake/compiler-env.cmake +++ b/cmake/compiler-env.cmake @@ -91,6 +91,8 @@ if(MSVC) momo_add_release_link_options( #/LTCG ) + + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) endif() ########################################## diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 083cbf6b..1c9606c6 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -12,7 +12,7 @@ namespace bool use_gdb{false}; bool concise_logging{false}; bool verbose_logging{false}; - std::string registry_path{"./registry"}; + std::string root_filesystem{"./root"}; }; void watch_system_objects(windows_emulator& win_emu, const bool cache_logging) @@ -107,7 +107,7 @@ namespace emulator_settings settings{ .application = args[0], - .registry_directory = options.registry_path, + .root_filesystem = options.root_filesystem, .arguments = parse_arguments(args), .silent_until_main = options.concise_logging, }; @@ -213,10 +213,10 @@ namespace { if (args.size() < 2) { - throw std::runtime_error("No registry path provided after -r"); + throw std::runtime_error("No root path provided after -r"); } arg_it = args.erase(arg_it); - options.registry_path = args[0]; + options.root_filesystem = args[0]; } else { diff --git a/src/common/network/address.hpp b/src/common/network/address.hpp index d42604b0..52fe8018 100644 --- a/src/common/network/address.hpp +++ b/src/common/network/address.hpp @@ -1,7 +1,9 @@ #pragma once #if _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS +#endif #define _CRT_NO_POSIX_ERROR_CODES #define NOMINMAX #define WIN32_LEAN_AND_MEAN diff --git a/src/emulator/serialization.hpp b/src/emulator/serialization.hpp index 7f6adf37..10da2567 100644 --- a/src/emulator/serialization.hpp +++ b/src/emulator/serialization.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -205,6 +206,26 @@ namespace utils return result; } + template + void read_list(std::list& result) + { + const auto size = this->read(); + result.clear(); + + for (uint64_t i = 0; i < size; ++i) + { + result.emplace_back(this->read()); + } + } + + template + std::list read_list() + { + std::list result{}; + this->read_list(result); + return result; + } + template void read_map(Map& map) { @@ -397,6 +418,17 @@ namespace utils this->write_span(std::span(vec)); } + template + void write_list(const std::list vec) + { + this->write(static_cast(vec.size())); + + for (const auto& v : vec) + { + this->write(v); + } + } + template void write_string(const std::basic_string_view str) { diff --git a/src/fuzzer/main.cpp b/src/fuzzer/main.cpp index 9f8e7d8c..244b75b9 100644 --- a/src/fuzzer/main.cpp +++ b/src/fuzzer/main.cpp @@ -42,12 +42,12 @@ namespace struct fuzzer_executer : fuzzer::executer { - windows_emulator emu{}; + windows_emulator emu{"./"}; // TODO: Fix root directory std::span emulator_data{}; std::unordered_set visited_blocks{}; const std::function* handler{nullptr}; - fuzzer_executer(std::span data) + fuzzer_executer(const std::span data) : emulator_data(data) { emu.fuzzing = true; diff --git a/src/windows-emulator-test/emulation_test_utils.hpp b/src/windows-emulator-test/emulation_test_utils.hpp index 40fec6ea..b183ccad 100644 --- a/src/windows-emulator-test/emulation_test_utils.hpp +++ b/src/windows-emulator-test/emulation_test_utils.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -20,9 +21,21 @@ namespace test { + inline std::filesystem::path get_emulator_root() + { + auto* env = getenv("EMULATOR_ROOT"); + if (!env) + { + throw std::runtime_error("No EMULATOR_ROOT set!"); + } + + return env; + } + inline windows_emulator create_sample_emulator(emulator_settings settings, emulator_callbacks callbacks = {}) { - settings.application = "./test-sample.exe"; + settings.application = "c:/test-sample.exe"; + settings.root_filesystem = get_emulator_root(); return windows_emulator{std::move(settings), std::move(callbacks)}; } diff --git a/src/windows-emulator-test/serialization_test.cpp b/src/windows-emulator-test/serialization_test.cpp index 835e7857..73ca3e05 100644 --- a/src/windows-emulator-test/serialization_test.cpp +++ b/src/windows-emulator-test/serialization_test.cpp @@ -14,7 +14,7 @@ namespace test utils::buffer_deserializer deserializer{serializer1.get_buffer()}; - windows_emulator new_emu{}; + windows_emulator new_emu{get_emulator_root()}; new_emu.deserialize(deserializer); utils::buffer_serializer serializer2{}; @@ -57,7 +57,7 @@ namespace test utils::buffer_deserializer deserializer{serializer.get_buffer()}; - windows_emulator new_emu{}; + windows_emulator new_emu{get_emulator_root()}; new_emu.log.disable_output(true); new_emu.deserialize(deserializer); diff --git a/src/windows-emulator/file_system.cpp b/src/windows-emulator/file_system.cpp deleted file mode 100644 index e9125080..00000000 --- a/src/windows-emulator/file_system.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "std_include.hpp" diff --git a/src/windows-emulator/file_system.hpp b/src/windows-emulator/file_system.hpp index 77d8ba07..89a1f1f6 100644 --- a/src/windows-emulator/file_system.hpp +++ b/src/windows-emulator/file_system.hpp @@ -1,85 +1,67 @@ #pragma once #include "std_include.hpp" -#include +#include "windows_path.hpp" class file_system { public: - static constexpr std::u16string_view nt_prefix = u"\\??\\"; - - file_system(std::filesystem::path root, std::u16string working_dir) + file_system(std::filesystem::path root, windows_path working_dir) : root_(std::move(root)), working_dir_(std::move(working_dir)) { } - static std::filesystem::path canonicalize_windows_path(const std::filesystem::path& file) + std::filesystem::path translate(const windows_path& win_path) const { - const auto wide_file = file.u16string(); - - if (!wide_file.starts_with(nt_prefix)) + if (win_path.is_absolute()) { - return file; + return this->root_ / win_path.to_portable_path(); } - return canonicalize_windows_path(wide_file.substr(nt_prefix.size())); + return this->root_ / (this->working_dir_ / win_path).to_portable_path(); } - static std::filesystem::path canonicalize(const std::filesystem::path& path) - { - return utils::path_key::canonicalize_path(canonicalize_windows_path(path)); - } - - static std::filesystem::path canonicalize_drive_letter(const std::filesystem::path& path) - { - auto canonical_path = canonicalize(path); - if (canonical_path.empty() || !path.has_root_path()) - { - return canonical_path; - } - - return adapt_drive_component(canonical_path); - } - - std::filesystem::path translate(const std::filesystem::path& win_path) const - { - const auto absolute_win_dir = make_absolute_windows_path(win_path.u16string()); - return this->root_ / canonicalize_drive_letter(absolute_win_dir); - } - - void set_working_directory(std::u16string working_dir) + void set_working_directory(windows_path working_dir) { this->working_dir_ = std::move(working_dir); } - const std::u16string& get_working_directory() const + const windows_path& get_working_directory() const { return this->working_dir_; } + windows_path local_to_windows_path(const std::filesystem::path& local_path) const + { + const auto absolute_local_path = absolute(local_path); + const auto relative_path = relative(absolute_local_path, this->root_); + + if (relative_path.empty() || *relative_path.begin() == "..") + { + throw std::runtime_error("Path '" + local_path.string() + "' is not within the root filesystem!"); + } + + char drive{}; + std::list folders{}; + + for (auto i = relative_path.begin(); i != relative_path.end(); ++i) + { + if (i == relative_path.begin()) + { + const auto str = i->string(); + assert(str.size() == 1); + drive = str[0]; + } + else + { + folders.push_back(i->u16string()); + } + } + + return windows_path{drive, std::move(folders)}; + } + private: - std::filesystem::path root_; - std::u16string working_dir_; - - std::u16string make_absolute_windows_path(const std::u16string& path) const - { - if (!path.starts_with(nt_prefix) && (path.size() < 2 || path[1] != ':')) - { - return this->working_dir_ + u'/' + path; - } - - return path; - } - - static std::filesystem::path adapt_drive_component(const std::filesystem::path& original_path) - { - auto root_name = original_path.root_name().u16string(); - - if (!root_name.empty() && root_name.back() == u':') - { - root_name.pop_back(); - } - - return root_name + original_path.root_directory().u16string() + original_path.relative_path().u16string(); - } + std::filesystem::path root_{}; + windows_path working_dir_{}; }; diff --git a/src/windows-emulator/kusd_mmio.cpp b/src/windows-emulator/kusd_mmio.cpp index b6575ed6..5494f247 100644 --- a/src/windows-emulator/kusd_mmio.cpp +++ b/src/windows-emulator/kusd_mmio.cpp @@ -80,7 +80,7 @@ namespace kusd.QpcFrequency = std::chrono::steady_clock::period::den; } - constexpr std::wstring_view root_dir{L"C:\\WINDOWS"}; + constexpr std::u16string_view root_dir{u"C:\\WINDOWS"}; memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2); kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386; diff --git a/src/windows-emulator/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp index 0f73ae5c..0721bec5 100644 --- a/src/windows-emulator/module/module_manager.cpp +++ b/src/windows-emulator/module/module_manager.cpp @@ -28,8 +28,8 @@ namespace utils static void serialize(buffer_serializer& buffer, const mapped_module& mod) { - buffer.write_string(mod.name); - buffer.write(mod.path.u16string()); + buffer.write(mod.name); + buffer.write(mod.path); buffer.write(mod.image_base); buffer.write(mod.size_of_image); @@ -41,8 +41,8 @@ namespace utils static void deserialize(buffer_deserializer& buffer, mapped_module& mod) { - mod.name = buffer.read_string(); - mod.path = buffer.read_string(); + buffer.read(mod.name); + buffer.read(mod.path); buffer.read(mod.image_base); buffer.read(mod.size_of_image); @@ -59,7 +59,7 @@ module_manager::module_manager(emulator& emu, file_system& file_sys) { } -mapped_module* module_manager::map_module(const std::filesystem::path& file, const logger& logger) +mapped_module* module_manager::map_module(const windows_path& file, const logger& logger) { return this->map_local_module(this->file_sys_->translate(file), logger); } @@ -70,7 +70,7 @@ mapped_module* module_manager::map_local_module(const std::filesystem::path& fil for (auto& mod : this->modules_ | std::views::values) { - if (mod.path == file) + if (mod.path == local_file) { return &mod; } diff --git a/src/windows-emulator/module/module_manager.hpp b/src/windows-emulator/module/module_manager.hpp index 2f298954..418c3d10 100644 --- a/src/windows-emulator/module/module_manager.hpp +++ b/src/windows-emulator/module/module_manager.hpp @@ -12,7 +12,7 @@ class module_manager using module_map = std::map; module_manager(emulator& emu, file_system& file_sys); - mapped_module* map_module(const std::filesystem::path& file, const logger& logger); + mapped_module* map_module(const windows_path& file, const logger& logger); mapped_module* map_local_module(const std::filesystem::path& file, const logger& logger); mapped_module* find_by_address(const uint64_t address) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 88b4063d..4f8ecaba 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -192,7 +192,7 @@ namespace return STATUS_OBJECT_NAME_NOT_FOUND; } - const std::wstring original_name(value->name.begin(), value->name.end()); + const std::u16string original_name(value->name.begin(), value->name.end()); if (key_value_information_class == KeyValueBasicInformation) { @@ -2897,10 +2897,10 @@ namespace const auto filename = read_unicode_string( c.emu, emulator_object>>{c.emu, attributes.ObjectName}); - const auto u8_filename = u16_to_u8(filename); + const auto local_filename = c.win_emu.file_sys.translate(filename).string(); struct _stat64 file_stat{}; - if (_stat64(u8_filename.c_str(), &file_stat) != 0) + if (_stat64(local_filename.c_str(), &file_stat) != 0) { return STATUS_OBJECT_NAME_NOT_FOUND; } diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 3a1eb75e..2af740fe 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -208,11 +208,6 @@ namespace emu.reg(x64_register::ss, 0x2B); } - std::filesystem::path canonicalize_path(const std::filesystem::path& path) - { - return canonical(absolute(path)).make_preferred(); - } - void setup_context(windows_emulator& win_emu, const emulator_settings& settings) { auto& emu = win_emu.emu(); @@ -220,7 +215,7 @@ namespace setup_gdt(emu); - context.registry = registry_manager(settings.registry_directory); + context.registry = registry_manager(win_emu.root_directory / "registry"); context.kusd.setup(settings.use_relative_time); @@ -260,7 +255,9 @@ namespace allocator.copy_string(u"SystemRoot=C:\\WINDOWS"); allocator.copy_string(u""); - std::u16string command_line = u"\"" + settings.application.u16string() + u"\""; + const auto application_str = settings.application.u16string(); + + std::u16string command_line = u"\"" + application_str + u"\""; for (const auto& arg : settings.arguments) { @@ -270,9 +267,8 @@ namespace allocator.make_unicode_string(proc_params.CommandLine, command_line); allocator.make_unicode_string(proc_params.CurrentDirectory.DosPath, - win_emu.file_sys.get_working_directory()); - allocator.make_unicode_string(proc_params.ImagePathName, - canonicalize_path(settings.application).u16string()); + win_emu.file_sys.get_working_directory().u16string()); + allocator.make_unicode_string(proc_params.ImagePathName, application_str); const auto total_length = allocator.get_next_address() - context.process_params.value(); @@ -824,11 +820,15 @@ std::unique_ptr create_default_x64_emulator() windows_emulator::windows_emulator(const emulator_settings& settings, emulator_callbacks callbacks, std::unique_ptr emu) - : windows_emulator(std::move(emu)) + : windows_emulator(settings.root_filesystem, std::move(emu)) { if (!settings.working_directory.empty()) { - this->file_sys.set_working_directory(settings.working_directory.u16string()); + this->file_sys.set_working_directory(settings.working_directory); + } + else + { + this->file_sys.set_working_directory(settings.application.parent()); } this->silent_until_main_ = settings.silent_until_main && !settings.disable_logging; @@ -838,8 +838,9 @@ windows_emulator::windows_emulator(const emulator_settings& settings, emulator_c this->setup_process(settings); } -windows_emulator::windows_emulator(std::unique_ptr emu) - : file_sys(R"(C:\Users\mahe.WIBU\Desktop\emulator\src\tools\root)", u"C:\\"), +windows_emulator::windows_emulator(const std::filesystem::path& root_path, std::unique_ptr emu) + : root_directory{absolute(root_path)}, + file_sys(root_directory / "filesys", u"C:\\"), emu_(std::move(emu)), process_(*emu_, file_sys) { @@ -855,7 +856,7 @@ void windows_emulator::setup_process(const emulator_settings& settings) setup_context(*this, settings); - context.executable = context.mod_manager.map_local_module(settings.application, this->log); + context.executable = context.mod_manager.map_module(settings.application, this->log); context.peb.access([&](PEB64& peb) { peb.ImageBaseAddress = reinterpret_cast(context.executable->image_base); // diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 0839db3a..0ece4b6e 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -27,9 +27,9 @@ struct emulator_callbacks // TODO: Split up into application and emulator settings struct emulator_settings { - std::filesystem::path application{}; - std::filesystem::path working_directory{}; - std::filesystem::path registry_directory{"./registry"}; + windows_path application{}; + windows_path working_directory{}; + std::filesystem::path root_filesystem{}; std::vector arguments{}; bool disable_logging{false}; bool silent_until_main{false}; @@ -47,7 +47,8 @@ enum class apiset_location class windows_emulator { public: - windows_emulator(std::unique_ptr emu = create_default_x64_emulator()); + windows_emulator(const std::filesystem::path& root_path, + std::unique_ptr emu = create_default_x64_emulator()); windows_emulator(const emulator_settings& settings, emulator_callbacks callbacks = {}, std::unique_ptr emu = create_default_x64_emulator()); @@ -132,6 +133,7 @@ class windows_emulator return this->callbacks_; } + std::filesystem::path root_directory{}; file_system file_sys; private: diff --git a/src/windows-emulator/windows_path.hpp b/src/windows-emulator/windows_path.hpp new file mode 100644 index 00000000..eaf5a940 --- /dev/null +++ b/src/windows-emulator/windows_path.hpp @@ -0,0 +1,236 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace windows_path_detail +{ + constexpr std::u16string_view unc_prefix = u"\\??\\"; + + inline std::u16string_view strip_unc_prefix(const std::u16string_view path) + { + if (!path.starts_with(unc_prefix)) + { + return path; + } + + return path.substr(unc_prefix.size()); + } + + inline bool is_slash(const char16_t chr) + { + return chr == u'\\' || chr == u'/'; + } +} + +class windows_path +{ + public: + windows_path() = default; + + windows_path(const std::filesystem::path& path) + { + const auto full_path = path.u16string(); + const auto canonical_path = windows_path_detail::strip_unc_prefix(full_path); + + std::u16string folder{}; + + for (const auto chr : canonical_path) + { + if (chr == u':' && this->folders_.empty() && !this->drive_.has_value() && folder.size() == 1) + { + this->drive_ = static_cast(folder[0]); + folder.clear(); + continue; + } + + if (windows_path_detail::is_slash(chr)) + { + if (folder.empty()) + { + continue; + } + + this->folders_.push_back(std::move(folder)); + folder = {}; + continue; + } + + folder.push_back(chr); + } + + if (!folder.empty()) + { + this->folders_.push_back(folder); + } + + this->canonicalize(); + } + + template + requires(!std::is_same_v && !std::is_same_v) + windows_path(T&& path_like) + : windows_path(std::filesystem::path(std::forward(path_like))) + { + } + + windows_path(const std::optional drive, std::list folders) + : drive_(drive), + folders_(std::move(folders)) + { + this->canonicalize(); + } + + bool is_absolute() const + { + return this->drive_.has_value(); + } + + bool is_relative() const + { + return !this->is_absolute(); + } + + std::u16string u16string() const + { + std::u16string path{}; + if (this->drive_) + { + path.push_back(static_cast(*this->drive_)); + path.push_back(u':'); + } + + for (const auto& folder : this->folders_) + { + if (!path.empty()) + { + path.push_back(u'\\'); + } + + path.append(folder); + } + + return path; + } + + std::string string() const + { + return u16_to_u8(this->u16string()); + } + + std::u16string to_unc_path() const + { + if (this->is_relative()) + { + return this->u16string(); + } + + return std::u16string(windows_path_detail::unc_prefix) + this->u16string(); + } + + std::filesystem::path to_portable_path() const + { + std::u16string path{}; + if (this->drive_) + { + path.push_back(static_cast(*this->drive_)); + } + + for (const auto& folder : this->folders_) + { + if (!path.empty()) + { + path.push_back(u'/'); + } + + path.append(folder); + } + + return path; + } + + windows_path operator/(const windows_path& path) const + { + if (path.is_absolute()) + { + return path; + } + + auto folders = this->folders_; + + for (const auto& folder : path.folders_) + { + folders.push_back(folder); + } + + return {this->drive_, std::move(folders)}; + } + + windows_path& operator/=(const windows_path& path) + { + *this = *this / path; + return *this; + } + + windows_path parent() const + { + auto folders = this->folders_; + if (!folders.empty()) + { + folders.pop_back(); + } + + return {this->drive_, std::move(folders)}; + } + + void serialize(utils::buffer_serializer& buffer) const + { + buffer.write_optional(this->drive_); + buffer.write_list(this->folders_); + } + + void deserialize(utils::buffer_deserializer& buffer) + { + buffer.read_optional(this->drive_); + buffer.read_list(this->folders_); + } + + bool operator==(const windows_path& other) const + { + return this->drive_ == other.drive_ && this->folders_ == other.folders_; + } + + bool operator!=(const windows_path& other) const + { + return !this->operator==(other); + } + + bool empty() const + { + return !this->is_relative() && this->folders_.empty(); + } + + private: + std::optional drive_{}; + std::list folders_{}; + + void canonicalize() + { + if (this->drive_.has_value()) + { + this->drive_ = utils::string::char_to_lower(*this->drive_); + } + + for (auto& folder : this->folders_) + { + for (auto& chr : folder) + { + chr = utils::string::char_to_lower(chr); + } + } + } +};