mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-18 11:13:57 +00:00
separate emulator from executable
# Conflicts: # src/windows_emulator/main.cpp
This commit is contained in:
@@ -2,3 +2,4 @@ add_subdirectory(common)
|
||||
add_subdirectory(emulator)
|
||||
add_subdirectory(unicorn_emulator)
|
||||
add_subdirectory(windows_emulator)
|
||||
add_subdirectory(sample)
|
||||
|
||||
23
src/sample/CMakeLists.txt
Normal file
23
src/sample/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
|
||||
*.cpp
|
||||
*.hpp
|
||||
*.rc
|
||||
)
|
||||
|
||||
list(SORT SRC_FILES)
|
||||
|
||||
add_executable(sample ${SRC_FILES})
|
||||
|
||||
momo_assign_source_group(${SRC_FILES})
|
||||
|
||||
target_precompile_headers(sample PRIVATE std_include.hpp)
|
||||
|
||||
target_link_libraries(sample PRIVATE
|
||||
common
|
||||
reflect
|
||||
windows_emulator
|
||||
)
|
||||
|
||||
set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT sample)
|
||||
|
||||
momo_strip_target(sample)
|
||||
105
src/sample/main.cpp
Normal file
105
src/sample/main.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "std_include.hpp"
|
||||
|
||||
#include <windows_emulator.hpp>
|
||||
#include <debugging/x64_gdb_stub_handler.hpp>
|
||||
|
||||
#include "object_watching.hpp"
|
||||
|
||||
bool use_gdb = false;
|
||||
|
||||
namespace
|
||||
{
|
||||
void watch_system_objects(windows_emulator& win_emu)
|
||||
{
|
||||
watch_object(win_emu, win_emu.process().teb);
|
||||
watch_object(win_emu, win_emu.process().peb);
|
||||
watch_object(win_emu, win_emu.process().kusd);
|
||||
auto* params_hook = watch_object(win_emu, win_emu.process().process_params);
|
||||
|
||||
win_emu.emu().hook_memory_write(win_emu.process().peb.value() + offsetof(PEB, ProcessParameters), 0x8,
|
||||
[&](const uint64_t address, size_t, const uint64_t value)
|
||||
{
|
||||
const auto target_address = win_emu.process().peb.value() + offsetof(
|
||||
PEB, ProcessParameters);
|
||||
|
||||
if (address == target_address)
|
||||
{
|
||||
const emulator_object<RTL_USER_PROCESS_PARAMETERS> obj{
|
||||
win_emu.emu(), value
|
||||
};
|
||||
|
||||
win_emu.emu().delete_hook(params_hook);
|
||||
params_hook = watch_object(win_emu, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void run()
|
||||
{
|
||||
windows_emulator win_emu {
|
||||
R"(C:\Users\mauri\source\repos\ConsoleApplication6\x64\Release\ConsoleApplication6.exe)",
|
||||
{
|
||||
L"Hello",
|
||||
L"World",
|
||||
}
|
||||
};
|
||||
|
||||
watch_system_objects(win_emu);
|
||||
|
||||
try
|
||||
{
|
||||
if (use_gdb)
|
||||
{
|
||||
puts("Launching gdb stub...");
|
||||
|
||||
x64_gdb_stub_handler handler{win_emu.emu()};
|
||||
run_gdb_stub(handler, "i386:x86-64", gdb_registers.size(), "0.0.0.0:28960");
|
||||
}
|
||||
else
|
||||
{
|
||||
win_emu.emu().start_from_ip();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
printf("Emulation failed at: %llX\n", win_emu.emu().read_instruction_pointer());
|
||||
throw;
|
||||
}
|
||||
|
||||
printf("Emulation done.\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
//setvbuf(stdout, nullptr, _IOFBF, 0x10000);
|
||||
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
run();
|
||||
}
|
||||
while (use_gdb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
|
||||
#if defined(_WIN32) && 0
|
||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
|
||||
{
|
||||
return main(__argc, __argv);
|
||||
}
|
||||
#endif
|
||||
20
src/sample/object_watching.hpp
Normal file
20
src/sample/object_watching.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "reflect_type_info.hpp"
|
||||
|
||||
template <typename T>
|
||||
emulator_hook* watch_object(windows_emulator& emu, emulator_object<T> object)
|
||||
{
|
||||
const reflect_type_info<T> info{};
|
||||
|
||||
return emu.emu().hook_memory_read(object.value(), object.size(),
|
||||
[i = std::move(info), object, &emu](const uint64_t address, size_t, uint64_t)
|
||||
{
|
||||
const auto rip = emu.emu().read_instruction_pointer();
|
||||
|
||||
const auto offset = address - object.value();
|
||||
printf("%s: %llX (%s) at %llX (%s)\n", i.get_type_name().c_str(), offset,
|
||||
i.get_member_name(offset).c_str(), rip,
|
||||
emu.process().module_manager.find_name(rip));
|
||||
});
|
||||
}
|
||||
56
src/sample/reflect_type_info.hpp
Normal file
56
src/sample/reflect_type_info.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "reflect_extension.hpp"
|
||||
#include <reflect>
|
||||
|
||||
template <typename T>
|
||||
class reflect_type_info
|
||||
{
|
||||
public:
|
||||
reflect_type_info()
|
||||
{
|
||||
this->type_name_ = reflect::type_name<T>();
|
||||
|
||||
reflect::for_each<T>([this](auto I)
|
||||
{
|
||||
const auto member_name = reflect::member_name<I, T>();
|
||||
const auto member_offset = reflect::offset_of<I, T>();
|
||||
|
||||
this->members_[member_offset] = member_name;
|
||||
});
|
||||
}
|
||||
|
||||
std::string get_member_name(const size_t offset) const
|
||||
{
|
||||
size_t last_offset{};
|
||||
std::string_view last_member{};
|
||||
|
||||
for (const auto& member : this->members_)
|
||||
{
|
||||
if (offset == member.first)
|
||||
{
|
||||
return member.second;
|
||||
}
|
||||
|
||||
if (offset < member.first)
|
||||
{
|
||||
const auto diff = offset - last_offset;
|
||||
return std::string(last_member) + "+" + std::to_string(diff);
|
||||
}
|
||||
|
||||
last_offset = member.first;
|
||||
last_member = member.second;
|
||||
}
|
||||
|
||||
return "<N/A>";
|
||||
}
|
||||
|
||||
const std::string& get_type_name() const
|
||||
{
|
||||
return this->type_name_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string type_name_{};
|
||||
std::map<size_t, std::string> members_{};
|
||||
};
|
||||
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
79
src/sample/std_include.hpp
Normal file
79
src/sample/std_include.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4005)
|
||||
#pragma warning(disable: 4127)
|
||||
#pragma warning(disable: 4201)
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable: 4245)
|
||||
#pragma warning(disable: 4324)
|
||||
#pragma warning(disable: 4458)
|
||||
#pragma warning(disable: 4471)
|
||||
#pragma warning(disable: 4505)
|
||||
#pragma warning(disable: 4702)
|
||||
#pragma warning(disable: 4996)
|
||||
#pragma warning(disable: 5054)
|
||||
#pragma warning(disable: 6011)
|
||||
#pragma warning(disable: 6297)
|
||||
#pragma warning(disable: 6385)
|
||||
#pragma warning(disable: 6386)
|
||||
#pragma warning(disable: 6387)
|
||||
#pragma warning(disable: 26110)
|
||||
#pragma warning(disable: 26451)
|
||||
#pragma warning(disable: 26444)
|
||||
#pragma warning(disable: 26451)
|
||||
#pragma warning(disable: 26489)
|
||||
#pragma warning(disable: 26495)
|
||||
#pragma warning(disable: 26498)
|
||||
#pragma warning(disable: 26812)
|
||||
#pragma warning(disable: 28020)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <ranges>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#define NTDDI_WIN11_GE 0
|
||||
#define PHNT_VERSION PHNT_WIN11
|
||||
#include <phnt_windows.h>
|
||||
#include <phnt.h>
|
||||
#include <ntgdi.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace std::literals;
|
||||
@@ -6,7 +6,7 @@ file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
|
||||
|
||||
list(SORT SRC_FILES)
|
||||
|
||||
add_executable(windows_emulator ${SRC_FILES})
|
||||
add_library(windows_emulator ${SRC_FILES})
|
||||
|
||||
momo_assign_source_group(${SRC_FILES})
|
||||
|
||||
@@ -14,12 +14,17 @@ target_precompile_headers(windows_emulator PRIVATE std_include.hpp)
|
||||
|
||||
target_link_libraries(windows_emulator PRIVATE
|
||||
common
|
||||
phnt::phnt
|
||||
reflect
|
||||
unicorn_emulator
|
||||
mini-gdbstub
|
||||
)
|
||||
|
||||
set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT windows_emulator)
|
||||
target_link_libraries(windows_emulator PUBLIC
|
||||
emulator
|
||||
phnt::phnt
|
||||
)
|
||||
|
||||
target_include_directories(windows_emulator INTERFACE
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
||||
|
||||
momo_strip_target(windows_emulator)
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
#include <gdbstub.h>
|
||||
|
||||
#include "std_include.hpp"
|
||||
|
||||
#include "emulator_utils.hpp"
|
||||
#include "process_context.hpp"
|
||||
#include "syscalls.hpp"
|
||||
|
||||
#include "reflect_extension.hpp"
|
||||
#include <reflect>
|
||||
|
||||
#include "windows_emulator.hpp"
|
||||
|
||||
#include "debugging/x64_gdb_stub_handler.hpp"
|
||||
|
||||
|
||||
bool use_gdb = false;
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
class type_info
|
||||
{
|
||||
public:
|
||||
type_info()
|
||||
{
|
||||
this->type_name_ = reflect::type_name<T>();
|
||||
|
||||
reflect::for_each<T>([this](auto I)
|
||||
{
|
||||
const auto member_name = reflect::member_name<I, T>();
|
||||
const auto member_offset = reflect::offset_of<I, T>();
|
||||
|
||||
this->members_[member_offset] = member_name;
|
||||
});
|
||||
}
|
||||
|
||||
std::string get_member_name(const size_t offset) const
|
||||
{
|
||||
size_t last_offset{};
|
||||
std::string_view last_member{};
|
||||
|
||||
for (const auto& member : this->members_)
|
||||
{
|
||||
if (offset == member.first)
|
||||
{
|
||||
return member.second;
|
||||
}
|
||||
|
||||
if (offset < member.first)
|
||||
{
|
||||
const auto diff = offset - last_offset;
|
||||
return std::string(last_member) + "+" + std::to_string(diff);
|
||||
}
|
||||
|
||||
last_offset = member.first;
|
||||
last_member = member.second;
|
||||
}
|
||||
|
||||
return "<N/A>";
|
||||
}
|
||||
|
||||
const std::string& get_type_name() const
|
||||
{
|
||||
return this->type_name_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string type_name_{};
|
||||
std::map<size_t, std::string> members_{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
emulator_hook* watch_object(windows_emulator& emu, emulator_object<T> object)
|
||||
{
|
||||
const type_info<T> info{};
|
||||
|
||||
return emu.emu().hook_memory_read(object.value(), object.size(),
|
||||
[i = std::move(info), object, &emu](const uint64_t address, size_t, uint64_t)
|
||||
{
|
||||
const auto rip = emu.emu().read_instruction_pointer();
|
||||
|
||||
const auto offset = address - object.value();
|
||||
printf("%s: %llX (%s) at %llX (%s)\n", i.get_type_name().c_str(), offset,
|
||||
i.get_member_name(offset).c_str(), rip,
|
||||
emu.process().module_manager.find_name(rip));
|
||||
});
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
const std::filesystem::path application =
|
||||
//R"(C:\Users\mauri\source\repos\ConsoleApplication6\x64\Release\ConsoleApplication6.exe)";
|
||||
R"(C:\Program Files (x86)\Steam\steamapps\common\Hogwarts Legacy\Phoenix\Binaries\Win64\HogwartsLegacy.exe)";
|
||||
|
||||
windows_emulator win_emu{application, {L"Hello", L"World"}};
|
||||
|
||||
watch_object(win_emu, win_emu.process().teb);
|
||||
watch_object(win_emu, win_emu.process().peb);
|
||||
watch_object(win_emu, win_emu.process().kusd);
|
||||
auto* params_hook = watch_object(win_emu, win_emu.process().process_params);
|
||||
|
||||
win_emu.emu().hook_memory_write(win_emu.process().peb.value() + offsetof(PEB, ProcessParameters), 0x8,
|
||||
[&](const uint64_t address, size_t, const uint64_t value)
|
||||
{
|
||||
const auto target_address = win_emu.process().peb.value() + offsetof(
|
||||
PEB, ProcessParameters);
|
||||
|
||||
if (address == target_address)
|
||||
{
|
||||
const emulator_object<RTL_USER_PROCESS_PARAMETERS> obj{
|
||||
win_emu.emu(), value
|
||||
};
|
||||
|
||||
win_emu.emu().delete_hook(params_hook);
|
||||
params_hook = watch_object(win_emu, obj);
|
||||
}
|
||||
});
|
||||
|
||||
win_emu.set_verbose(false);
|
||||
|
||||
try
|
||||
{
|
||||
if (use_gdb)
|
||||
{
|
||||
puts("Launching gdb stub...");
|
||||
|
||||
x64_gdb_stub_handler handler{win_emu.emu()};
|
||||
run_gdb_stub(handler, "i386:x86-64", gdb_registers.size(), "0.0.0.0:28960");
|
||||
}
|
||||
else
|
||||
{
|
||||
win_emu.emu().start_from_ip();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
printf("Emulation failed at: %llX\n", win_emu.emu().read_instruction_pointer());
|
||||
throw;
|
||||
}
|
||||
|
||||
printf("Emulation done.\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
//setvbuf(stdout, nullptr, _IOFBF, 0x10000);
|
||||
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
run();
|
||||
}
|
||||
while (use_gdb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
|
||||
#if defined(_WIN32) && 0
|
||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
|
||||
{
|
||||
return main(__argc, __argv);
|
||||
}
|
||||
#endif
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "windows_emulator.hpp"
|
||||
#include "context_frame.hpp"
|
||||
|
||||
#include <unicorn_x64_emulator.hpp>
|
||||
|
||||
#define GS_SEGMENT_ADDR 0x6000000ULL
|
||||
#define GS_SEGMENT_SIZE (20 << 20) // 20 MB
|
||||
|
||||
@@ -465,6 +467,11 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<x64_emulator> create_default_x64_emulator()
|
||||
{
|
||||
return unicorn::create_x64_emulator();
|
||||
}
|
||||
|
||||
windows_emulator::windows_emulator(const std::filesystem::path& application, const std::vector<std::wstring>& arguments,
|
||||
std::unique_ptr<x64_emulator> emu)
|
||||
: windows_emulator(std::move(emu))
|
||||
@@ -569,6 +576,16 @@ void windows_emulator::setup_hooks()
|
||||
{
|
||||
++this->process().executed_instructions;
|
||||
|
||||
if (address == 0x180038B65)
|
||||
{
|
||||
puts("!!! DLL init failed");
|
||||
}
|
||||
if (address == 0x180038A20)
|
||||
{
|
||||
const auto* name = this->process().module_manager.find_name(
|
||||
this->emu().reg(x64_register::rcx));
|
||||
printf("!!! DLL init: %s\n", name);
|
||||
}
|
||||
const auto* binary = this->process().module_manager.find_by_address(address);
|
||||
|
||||
if (binary)
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
#pragma once
|
||||
#include <x64_emulator.hpp>
|
||||
#include <unicorn_x64_emulator.hpp>
|
||||
|
||||
#include "syscalls.hpp"
|
||||
#include "process_context.hpp"
|
||||
|
||||
std::unique_ptr<x64_emulator> create_default_x64_emulator();
|
||||
|
||||
class windows_emulator
|
||||
{
|
||||
public:
|
||||
windows_emulator(std::unique_ptr<x64_emulator> emu = unicorn::create_x64_emulator());
|
||||
windows_emulator(std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||
windows_emulator(const std::filesystem::path& application, const std::vector<std::wstring>& arguments = {},
|
||||
std::unique_ptr<x64_emulator> emu = unicorn::create_x64_emulator());
|
||||
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||
|
||||
windows_emulator(windows_emulator&&) = delete;
|
||||
windows_emulator(const windows_emulator&) = delete;
|
||||
|
||||
Reference in New Issue
Block a user