diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 16c6d6bc..63dd1c78 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -223,6 +223,11 @@ jobs: - platform: macOS x86_64 runner: macos-13 steps: + - name: Checkout Source + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Download Test Config uses: pyTooling/download-artifact@v4 with: diff --git a/.gitmodules b/.gitmodules index 05a79148..9c5a000b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,4 +14,7 @@ path = deps/zlib url = https://github.com/madler/zlib.git branch = develop - ignore = dirty \ No newline at end of file + ignore = dirty +[submodule "deps/gtest-parallel"] + path = deps/gtest-parallel + url = https://github.com/google/gtest-parallel.git diff --git a/cmake/compiler-env.cmake b/cmake/compiler-env.cmake index 7430d2cd..ff756b49 100644 --- a/cmake/compiler-env.cmake +++ b/cmake/compiler-env.cmake @@ -95,7 +95,10 @@ if(MSVC) #/LTCG ) - add_compile_definitions(_CRT_SECURE_NO_WARNINGS) + add_compile_definitions( + _CRT_SECURE_NO_WARNINGS + _CRT_NONSTDC_NO_WARNINGS + ) endif() ########################################## diff --git a/deps/gtest-parallel b/deps/gtest-parallel new file mode 160000 index 00000000..96f4f904 --- /dev/null +++ b/deps/gtest-parallel @@ -0,0 +1 @@ +Subproject commit 96f4f904922f9bf66689e749c40f314845baaac8 diff --git a/src/windows-emulator-test/CMakeLists.txt b/src/windows-emulator-test/CMakeLists.txt index 5f755e01..202db345 100644 --- a/src/windows-emulator-test/CMakeLists.txt +++ b/src/windows-emulator-test/CMakeLists.txt @@ -19,8 +19,13 @@ if(WIN32) add_dependencies(windows-emulator-test test-sample) endif() +set(PYTHON3_EXE "python3") +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + set(PYTHON3_EXE "python") +endif() + add_test(NAME windows-emulator-test - COMMAND windows-emulator-test + COMMAND "${PYTHON3_EXE}" "${PROJECT_SOURCE_DIR}/deps/gtest-parallel/gtest_parallel.py" ./windows-emulator-test WORKING_DIRECTORY "$") -momo_targets_set_folder("tests" windows-emulator-test) \ No newline at end of file +momo_targets_set_folder("tests" windows-emulator-test) diff --git a/src/windows-emulator-test/emulation_test_utils.hpp b/src/windows-emulator-test/emulation_test_utils.hpp index 72028a01..1b280a25 100644 --- a/src/windows-emulator-test/emulation_test_utils.hpp +++ b/src/windows-emulator-test/emulation_test_utils.hpp @@ -56,6 +56,11 @@ namespace test settings.application = "c:/test-sample.exe"; settings.emulation_root = get_emulator_root(); + + settings.port_mappings[28970] = static_cast(getpid()); + settings.path_mappings["C:\\a.txt"] = + std::filesystem::temp_directory_path() / ("emulator-test-file-" + std::to_string(getpid()) + ".txt"); + return windows_emulator{std::move(settings), std::move(callbacks)}; } diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index 0068561b..84622f5d 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -121,13 +121,13 @@ namespace throw std::runtime_error("Unknown socket protocol: " + std::to_string(win_protocol)); } - std::vector convert_to_win_address(const network::address& a) + std::vector convert_to_win_address(const windows_emulator& win_emu, const network::address& a) { if (a.is_ipv4()) { win_sockaddr_in win_addr{}; win_addr.sin_family = translate_host_to_win_address_family(a.get_family()); - win_addr.sin_port = htons(a.get_port()); + win_addr.sin_port = htons(win_emu.get_emulator_port(a.get_port())); memcpy(&win_addr.sin_addr, &a.get_in_addr().sin_addr, sizeof(win_addr.sin_addr)); const auto ptr = reinterpret_cast(&win_addr); @@ -138,7 +138,7 @@ namespace { win_sockaddr_in6 win_addr{}; win_addr.sin6_family = translate_host_to_win_address_family(a.get_family()); - win_addr.sin6_port = htons(a.get_port()); + win_addr.sin6_port = htons(win_emu.get_emulator_port(a.get_port())); auto& addr = a.get_in6_addr(); memcpy(&win_addr.sin6_addr, &addr.sin6_addr, sizeof(win_addr.sin6_addr)); @@ -152,7 +152,7 @@ namespace throw std::runtime_error("Unsupported host address family for conversion: " + std::to_string(a.get_family())); } - network::address convert_to_host_address(const std::span data) + network::address convert_to_host_address(const windows_emulator& win_emu, const std::span data) { if (data.size() < sizeof(win_sockaddr)) { @@ -177,7 +177,7 @@ namespace memcpy(&win_addr4, data.data(), sizeof(win_addr4)); a.set_ipv4(win_addr4.sin_addr); - a.set_port(ntohs(win_addr4.sin_port)); + a.set_port(win_emu.get_host_port(ntohs(win_addr4.sin_port))); return a; } @@ -534,7 +534,7 @@ namespace return STATUS_BUFFER_TOO_SMALL; } - const auto addr = convert_to_host_address(std::span(data).subspan(address_offset)); + const auto addr = convert_to_host_address(win_emu, std::span(data).subspan(address_offset)); if (bind(*this->s_, &addr.get_addr(), addr.get_size()) == SOCKET_ERROR) { @@ -645,7 +645,7 @@ namespace const auto data_size = std::min(data.size(), static_cast(recevied_data)); emu.write_memory(buffer.buf, data.data(), data_size); - const auto win_from = convert_to_win_address(from); + const auto win_from = convert_to_win_address(win_emu, from); if (receive_info.Address && receive_info.AddressLength) { @@ -681,7 +681,7 @@ namespace auto address_buffer = emu.read_memory(send_info.TdiConnInfo.RemoteAddress, static_cast(send_info.TdiConnInfo.RemoteAddressLength)); - const auto target = convert_to_host_address(address_buffer); + const auto target = convert_to_host_address(win_emu, address_buffer); const auto data = emu.read_memory(buffer.buf, buffer.len); const auto sent_data = diff --git a/src/windows-emulator/file_system.hpp b/src/windows-emulator/file_system.hpp index c0b7c90f..4161bf3e 100644 --- a/src/windows-emulator/file_system.hpp +++ b/src/windows-emulator/file_system.hpp @@ -19,6 +19,12 @@ class file_system ? win_path : (this->working_dir_ / win_path); + const auto mapping = this->mappings_.find(full_path); + if (mapping != this->mappings_.end()) + { + return mapping->second; + } + #ifdef OS_WINDOWS if (this->root_.empty()) { @@ -85,7 +91,13 @@ class file_system buffer.read(this->working_dir_); } + void map(windows_path src, std::filesystem::path dest) + { + this->mappings_[std::move(src)] = std::move(dest); + } + private: std::filesystem::path root_{}; windows_path working_dir_{}; + std::unordered_map mappings_{}; }; diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 2f612d9f..2e83cd40 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -838,6 +838,16 @@ windows_emulator::windows_emulator(const emulator_settings& settings, emulator_c this->file_sys().set_working_directory(settings.application.parent()); } + for (const auto& mapping : settings.path_mappings) + { + this->file_sys().map(mapping.first, mapping.second); + } + + for (const auto& mapping : settings.port_mappings) + { + this->map_port(mapping.first, mapping.second); + } + this->verbose_calls = settings.verbose_calls; this->silent_until_main_ = settings.silent_until_main && !settings.disable_logging; this->use_relative_time_ = settings.use_relative_time; diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 0c1c1a51..29b3f5ca 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -36,6 +36,8 @@ struct emulator_settings bool disable_logging{false}; bool silent_until_main{false}; bool use_relative_time{false}; + std::unordered_map port_mappings{}; + std::unordered_map path_mappings{}; }; enum class apiset_location : uint8_t @@ -114,6 +116,45 @@ class windows_emulator this->syscall_hooks_.push_back(std::move(callback)); } + uint16_t get_host_port(const uint16_t emulator_port) const + { + const auto entry = this->port_mappings_.find(emulator_port); + if (entry == this->port_mappings_.end()) + { + return emulator_port; + } + + return entry->second; + } + + uint16_t get_emulator_port(const uint16_t host_port) const + { + for (const auto& mapping : this->port_mappings_) + { + if (mapping.second == host_port) + { + return mapping.first; + } + } + + return host_port; + } + + void map_port(const uint16_t emulator_port, const uint16_t host_port) + { + if (emulator_port != host_port) + { + this->port_mappings_[emulator_port] = host_port; + return; + } + + const auto entry = this->port_mappings_.find(emulator_port); + if (entry != this->port_mappings_.end()) + { + this->port_mappings_.erase(entry); + } + } + logger log{}; bool verbose{false}; bool verbose_calls{false}; @@ -161,6 +202,7 @@ class windows_emulator std::unique_ptr emu_{}; std::vector syscall_hooks_{}; + std::unordered_map port_mappings_{}; process_context process_; syscall_dispatcher dispatcher_; diff --git a/src/windows-emulator/windows_path.hpp b/src/windows-emulator/windows_path.hpp index 94769552..869d5027 100644 --- a/src/windows-emulator/windows_path.hpp +++ b/src/windows-emulator/windows_path.hpp @@ -30,6 +30,8 @@ namespace windows_path_detail class windows_path { public: + friend std::hash; + windows_path() = default; windows_path(const std::filesystem::path& path) @@ -234,3 +236,24 @@ class windows_path } } }; + +template <> +struct std::hash +{ + std::size_t operator()(const windows_path& k) const noexcept + { + auto hash = std::hash()(k.drive_.has_value()); + + if (k.drive_.has_value()) + { + hash ^= std::hash()(*k.drive_); + } + + for (const auto& folder : k.folders_) + { + hash ^= std::hash()(folder); + } + + return hash; + } +};