#pragma once #include #include #include #include #define ASSERT_NOT_TERMINATED(win_emu) \ do \ { \ ASSERT_FALSE((win_emu).process.exit_status.has_value()); \ } while (false) #define ASSERT_TERMINATED_WITH_STATUS(win_emu, status) \ do \ { \ ASSERT_TRUE((win_emu).process.exit_status.has_value()); \ ASSERT_EQ(*(win_emu).process.exit_status, status); \ } while (false) #define ASSERT_TERMINATED_SUCCESSFULLY(win_emu) ASSERT_TERMINATED_WITH_STATUS(win_emu, STATUS_SUCCESS) namespace test { inline bool enable_verbose_logging() { const auto* env = getenv("EMULATOR_VERBOSE"); return env && (env == "1"sv || env == "true"sv); } inline std::filesystem::path get_emulator_root() { const auto* env = getenv("EMULATOR_ROOT"); if (!env) { throw std::runtime_error("No EMULATOR_ROOT set!"); } return env; } struct sample_configuration { bool print_time{false}; }; inline application_settings get_sample_app_settings(const sample_configuration& config) { application_settings settings{.application = "C:\\test-sample.exe"}; if (config.print_time) { settings.arguments.emplace_back(u"-time"); } return settings; } inline windows_emulator create_emulator(emulator_settings settings, emulator_callbacks callbacks = {}) { const auto is_verbose = enable_verbose_logging(); if (is_verbose) { settings.disable_logging = false; } settings.emulation_root = get_emulator_root(); settings.path_mappings["C:\\a.txt"] = std::filesystem::temp_directory_path() / ("emulator-test-file-" + std::to_string(getpid()) + ".txt"); return windows_emulator{ settings, std::move(callbacks), emulator_interfaces{ .socket_factory = network::create_static_socket_factory(), }, }; } inline windows_emulator create_sample_emulator(emulator_settings settings, const sample_configuration& config = {}, emulator_callbacks callbacks = {}) { const auto is_verbose = enable_verbose_logging(); if (is_verbose) { settings.disable_logging = false; // settings.verbose_calls = true; } settings.emulation_root = get_emulator_root(); settings.path_mappings["C:\\a.txt"] = std::filesystem::temp_directory_path() / ("emulator-test-file-" + std::to_string(getpid()) + ".txt"); return windows_emulator{ get_sample_app_settings(config), settings, std::move(callbacks), emulator_interfaces{ .socket_factory = network::create_static_socket_factory(), }, }; } inline windows_emulator create_sample_emulator(const sample_configuration& config = {}) { emulator_settings settings{ .disable_logging = true, .use_relative_time = true, }; return create_sample_emulator(std::move(settings), config); } inline windows_emulator create_empty_emulator() { emulator_settings settings{ .disable_logging = true, .use_relative_time = true, }; return create_emulator(std::move(settings)); } inline void bisect_emulation(windows_emulator& emu) { utils::buffer_serializer start_state{}; emu.serialize(start_state); emu.start(); const auto limit = emu.get_executed_instructions(); const auto reset_emulator = [&] { utils::buffer_deserializer deserializer{start_state}; emu.deserialize(deserializer); }; const auto get_state_for_count = [&](const size_t count) { reset_emulator(); emu.start(count); utils::buffer_serializer state{}; emu.serialize(state); return state; }; const auto has_diff_after_count = [&](const size_t count) { const auto s1 = get_state_for_count(count); const auto s2 = get_state_for_count(count); return s1.get_diff(s2).has_value(); }; if (!has_diff_after_count(static_cast(limit))) { puts("Emulation has no diff"); } auto upper_bound = limit; decltype(upper_bound) lower_bound = 0; printf("Bounds: %" PRIx64 " - %" PRIx64 "\n", lower_bound, upper_bound); while (lower_bound + 1 < upper_bound) { const auto diff = (upper_bound - lower_bound); const auto pivot = lower_bound + (diff / 2); const auto has_diff = has_diff_after_count(static_cast(pivot)); auto* bound = has_diff ? &upper_bound : &lower_bound; *bound = pivot; printf("Bounds: %" PRIx64 " - %" PRIx64 "\n", lower_bound, upper_bound); } (void)get_state_for_count(static_cast(lower_bound)); const auto rip = emu.emu().read_instruction_pointer(); printf("Diff detected after 0x%" PRIx64 " instructions at 0x%" PRIx64 " (%s)\n", lower_bound, rip, emu.mod_manager.find_name(rip)); } }