mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Cleanup gdb stub and module mapping
This commit is contained in:
54
src/emulator/scoped_hook.hpp
Normal file
54
src/emulator/scoped_hook.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "emulator.hpp"
|
||||
|
||||
class scoped_hook
|
||||
{
|
||||
public:
|
||||
scoped_hook() = default;
|
||||
|
||||
scoped_hook(emulator& emu, emulator_hook* hook)
|
||||
: emu_(&emu)
|
||||
, hook_(hook)
|
||||
{
|
||||
}
|
||||
|
||||
~scoped_hook()
|
||||
{
|
||||
this->remove();
|
||||
}
|
||||
|
||||
scoped_hook(const scoped_hook&) = delete;
|
||||
scoped_hook& operator=(const scoped_hook&) = delete;
|
||||
|
||||
scoped_hook(scoped_hook&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
scoped_hook& operator=(scoped_hook&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->remove();
|
||||
this->emu_ = obj.emu_;
|
||||
this->hook_ = obj.hook_;
|
||||
|
||||
obj.hook_ = {};
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void remove()
|
||||
{
|
||||
if (this->hook_)
|
||||
{
|
||||
this->emu_->delete_hook(this->hook_);
|
||||
this->hook_ = {};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
emulator* emu_{};
|
||||
emulator_hook* hook_{};
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "std_include.hpp"
|
||||
#include "../std_include.hpp"
|
||||
#include "gdb_stub.hpp"
|
||||
|
||||
#include <utils/finally.hpp>
|
||||
215
src/windows_emulator/debugging/x64_gdb_stub_handler.hpp
Normal file
215
src/windows_emulator/debugging/x64_gdb_stub_handler.hpp
Normal file
@@ -0,0 +1,215 @@
|
||||
#pragma once
|
||||
#include <x64_emulator.hpp>
|
||||
#include "gdb_stub.hpp"
|
||||
#include "scoped_hook.hpp"
|
||||
|
||||
inline std::vector gdb_registers{
|
||||
x64_register::rax,
|
||||
x64_register::rbx,
|
||||
x64_register::rcx,
|
||||
x64_register::rdx,
|
||||
x64_register::rsi,
|
||||
x64_register::rdi,
|
||||
x64_register::rbp,
|
||||
x64_register::rsp,
|
||||
x64_register::r8,
|
||||
x64_register::r9,
|
||||
x64_register::r10,
|
||||
x64_register::r11,
|
||||
x64_register::r12,
|
||||
x64_register::r13,
|
||||
x64_register::r14,
|
||||
x64_register::r15,
|
||||
x64_register::rip,
|
||||
x64_register::rflags,
|
||||
/*x64_register::cs,
|
||||
x64_register::ss,
|
||||
x64_register::ds,
|
||||
x64_register::es,
|
||||
x64_register::fs,
|
||||
x64_register::gs,*/
|
||||
};
|
||||
|
||||
inline memory_operation map_breakpoint_type(const breakpoint_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case breakpoint_type::software:
|
||||
case breakpoint_type::hardware_exec:
|
||||
return memory_operation::exec;
|
||||
case breakpoint_type::hardware_read:
|
||||
return memory_permission::read;
|
||||
case breakpoint_type::hardware_write:
|
||||
return memory_permission::write;
|
||||
case breakpoint_type::hardware_read_write:
|
||||
return memory_permission::read_write;
|
||||
default:
|
||||
throw std::runtime_error("Bad bp type");
|
||||
}
|
||||
}
|
||||
|
||||
struct breakpoint_key
|
||||
{
|
||||
size_t addr{};
|
||||
size_t size{};
|
||||
breakpoint_type type{};
|
||||
|
||||
bool operator==(const breakpoint_key& other) const
|
||||
{
|
||||
return this->addr == other.addr && this->size == other.size && this->type == other.type;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::hash<breakpoint_key>
|
||||
{
|
||||
std::size_t operator()(const breakpoint_key& k) const noexcept
|
||||
{
|
||||
return ((std::hash<size_t>()(k.addr)
|
||||
^ (std::hash<size_t>()(k.size) << 1)) >> 1)
|
||||
^ (std::hash<size_t>()(static_cast<size_t>(k.type)) << 1);
|
||||
}
|
||||
};
|
||||
|
||||
class x64_gdb_stub_handler : public gdb_stub_handler
|
||||
{
|
||||
public:
|
||||
x64_gdb_stub_handler(x64_emulator& emu)
|
||||
: emu_(&emu)
|
||||
{
|
||||
}
|
||||
|
||||
~x64_gdb_stub_handler() override = default;
|
||||
|
||||
gdb_action cont() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->start_from_ip();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
|
||||
return gdb_action::resume;
|
||||
}
|
||||
|
||||
gdb_action stepi() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->start_from_ip({}, 1);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
|
||||
return gdb_action::resume;
|
||||
}
|
||||
|
||||
bool read_reg(const int regno, size_t* value) override
|
||||
{
|
||||
*value = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
this->emu_->read_register(gdb_registers[regno], value, sizeof(*value));
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool write_reg(const int regno, const size_t value) override
|
||||
{
|
||||
try
|
||||
{
|
||||
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
this->emu_->write_register(gdb_registers[regno], &value, sizeof(value));
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool read_mem(const size_t addr, const size_t len, void* val) override
|
||||
{
|
||||
return this->emu_->try_read_memory(addr, val, len);
|
||||
}
|
||||
|
||||
bool write_mem(const size_t addr, const size_t len, void* val) override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->write_memory(addr, val, len);
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool set_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->hooks_[{addr, size, type}] = scoped_hook(*this->emu_, this->emu_->hook_memory_access(
|
||||
addr, size, map_breakpoint_type(type),
|
||||
[this](uint64_t, size_t, memory_operation)
|
||||
{
|
||||
this->on_interrupt();
|
||||
}));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool del_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto entry = this->hooks_.find({addr, size, type});
|
||||
if (entry == this->hooks_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->hooks_.erase(entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void on_interrupt() override
|
||||
{
|
||||
this->emu_->stop();
|
||||
}
|
||||
|
||||
private:
|
||||
x64_emulator* emu_{};
|
||||
std::unordered_map<breakpoint_key, scoped_hook> hooks_{};
|
||||
};
|
||||
@@ -12,10 +12,10 @@
|
||||
#include <address_utils.hpp>
|
||||
#include <unicorn_x64_emulator.hpp>
|
||||
|
||||
#include "gdb_stub.hpp"
|
||||
#include "module_mapper.hpp"
|
||||
#include "context_frame.hpp"
|
||||
|
||||
#include "debugging/x64_gdb_stub_handler.hpp"
|
||||
|
||||
|
||||
#define GS_SEGMENT_ADDR 0x6000000ULL
|
||||
#define GS_SEGMENT_SIZE (20 << 20) // 20 MB
|
||||
@@ -32,30 +32,6 @@
|
||||
|
||||
bool use_gdb = false;
|
||||
|
||||
struct breakpoint_key
|
||||
{
|
||||
size_t addr{};
|
||||
size_t size{};
|
||||
breakpoint_type type{};
|
||||
|
||||
bool operator==(const breakpoint_key& other) const
|
||||
{
|
||||
return this->addr == other.addr && this->size == other.size && this->type == other.type;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::hash<breakpoint_key>
|
||||
{
|
||||
std::size_t operator()(const breakpoint_key& k) const noexcept
|
||||
{
|
||||
return ((std::hash<size_t>()(k.addr)
|
||||
^ (std::hash<size_t>()(k.size) << 1)) >> 1)
|
||||
^ (std::hash<size_t>()(static_cast<size_t>(k.type)) << 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
@@ -366,245 +342,6 @@ namespace
|
||||
return context;
|
||||
}
|
||||
|
||||
std::vector gdb_registers{
|
||||
x64_register::rax,
|
||||
x64_register::rbx,
|
||||
x64_register::rcx,
|
||||
x64_register::rdx,
|
||||
x64_register::rsi,
|
||||
x64_register::rdi,
|
||||
x64_register::rbp,
|
||||
x64_register::rsp,
|
||||
x64_register::r8,
|
||||
x64_register::r9,
|
||||
x64_register::r10,
|
||||
x64_register::r11,
|
||||
x64_register::r12,
|
||||
x64_register::r13,
|
||||
x64_register::r14,
|
||||
x64_register::r15,
|
||||
x64_register::rip,
|
||||
x64_register::rflags,
|
||||
/*x64_register::cs,
|
||||
x64_register::ss,
|
||||
x64_register::ds,
|
||||
x64_register::es,
|
||||
x64_register::fs,
|
||||
x64_register::gs,*/
|
||||
};
|
||||
|
||||
memory_operation map_breakpoint_type(const breakpoint_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case breakpoint_type::software:
|
||||
case breakpoint_type::hardware_exec:
|
||||
return memory_operation::exec;
|
||||
case breakpoint_type::hardware_read:
|
||||
return memory_permission::read;
|
||||
case breakpoint_type::hardware_write:
|
||||
return memory_permission::write;
|
||||
case breakpoint_type::hardware_read_write:
|
||||
return memory_permission::read_write;
|
||||
default:
|
||||
throw std::runtime_error("Bad bp type");
|
||||
}
|
||||
}
|
||||
|
||||
class scoped_emulator_hook
|
||||
{
|
||||
public:
|
||||
scoped_emulator_hook() = default;
|
||||
|
||||
scoped_emulator_hook(emulator& emu, emulator_hook* hook)
|
||||
: emu_(&emu)
|
||||
, hook_(hook)
|
||||
{
|
||||
}
|
||||
|
||||
~scoped_emulator_hook()
|
||||
{
|
||||
this->remove();
|
||||
}
|
||||
|
||||
scoped_emulator_hook(const scoped_emulator_hook&) = delete;
|
||||
scoped_emulator_hook& operator=(const scoped_emulator_hook&) = delete;
|
||||
|
||||
scoped_emulator_hook(scoped_emulator_hook&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
scoped_emulator_hook& operator=(scoped_emulator_hook&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->remove();
|
||||
this->emu_ = obj.emu_;
|
||||
this->hook_ = obj.hook_;
|
||||
|
||||
obj.hook_ = {};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void remove()
|
||||
{
|
||||
if (this->hook_)
|
||||
{
|
||||
this->emu_->delete_hook(this->hook_);
|
||||
this->hook_ = {};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
emulator* emu_{};
|
||||
emulator_hook* hook_{};
|
||||
};
|
||||
|
||||
class x64_gdb_stub_handler : public gdb_stub_handler
|
||||
{
|
||||
public:
|
||||
x64_gdb_stub_handler(x64_emulator& emu)
|
||||
: emu_(&emu)
|
||||
{
|
||||
}
|
||||
|
||||
~x64_gdb_stub_handler() override = default;
|
||||
|
||||
gdb_action cont() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->start_from_ip();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
|
||||
return gdb_action::resume;
|
||||
}
|
||||
|
||||
gdb_action stepi() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->start_from_ip({}, 1);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
|
||||
return gdb_action::resume;
|
||||
}
|
||||
|
||||
bool read_reg(const int regno, size_t* value) override
|
||||
{
|
||||
*value = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
this->emu_->read_register(gdb_registers[regno], value, sizeof(*value));
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool write_reg(const int regno, const size_t value) override
|
||||
{
|
||||
try
|
||||
{
|
||||
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
this->emu_->write_register(gdb_registers[regno], &value, sizeof(value));
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool read_mem(const size_t addr, const size_t len, void* val) override
|
||||
{
|
||||
return this->emu_->try_read_memory(addr, val, len);
|
||||
}
|
||||
|
||||
bool write_mem(const size_t addr, const size_t len, void* val) override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->write_memory(addr, val, len);
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool set_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->hooks_[{addr, size, type}] = scoped_emulator_hook(*this->emu_, this->emu_->hook_memory_access(
|
||||
addr, size, map_breakpoint_type(type),
|
||||
[this](uint64_t, size_t, memory_operation)
|
||||
{
|
||||
this->on_interrupt();
|
||||
}));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool del_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto entry = this->hooks_.find({addr, size, type});
|
||||
if (entry == this->hooks_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->hooks_.erase(entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void on_interrupt() override
|
||||
{
|
||||
this->emu_->stop();
|
||||
}
|
||||
|
||||
private:
|
||||
x64_emulator* emu_{};
|
||||
std::unordered_map<breakpoint_key, scoped_emulator_hook> hooks_{};
|
||||
};
|
||||
|
||||
uint64_t find_exported_function(const std::vector<exported_symbol>& exports, const std::string_view name)
|
||||
{
|
||||
for (auto& symbol : exports)
|
||||
@@ -782,15 +519,16 @@ namespace
|
||||
const auto emu = unicorn::create_x64_emulator();
|
||||
|
||||
auto context = setup_context(*emu);
|
||||
context.module_manager = module_manager(*emu);
|
||||
|
||||
context.executable = map_file(context, *emu, R"(C:\Users\mauri\Desktop\boiii.exe)");
|
||||
context.executable = context.module_manager.map_module(R"(C:\Users\mauri\Desktop\ConsoleApplication6.exe)");
|
||||
|
||||
context.peb.access([&](PEB& peb)
|
||||
{
|
||||
peb.ImageBaseAddress = reinterpret_cast<void*>(context.executable->image_base);
|
||||
});
|
||||
|
||||
context.ntdll = map_file(context, *emu, R"(C:\Windows\System32\ntdll.dll)");
|
||||
context.ntdll = context.module_manager.map_module(R"(C:\Windows\System32\ntdll.dll)");
|
||||
|
||||
const auto ldr_initialize_thunk = find_exported_function(context.ntdll->exports, "LdrInitializeThunk");
|
||||
const auto rtl_user_thread_start = find_exported_function(context.ntdll->exports, "RtlUserThreadStart");
|
||||
@@ -805,12 +543,6 @@ namespace
|
||||
return instruction_hook_continuation::skip_instruction;
|
||||
});
|
||||
|
||||
emu->hook_instruction(x64_hookable_instructions::rdtsc, [&]
|
||||
{
|
||||
emu->reg(x64_register::rax, 0x0011223344556677);
|
||||
return instruction_hook_continuation::skip_instruction;
|
||||
});
|
||||
|
||||
emu->hook_instruction(x64_hookable_instructions::invalid, [&]
|
||||
{
|
||||
const auto ip = emu->read_instruction_pointer();
|
||||
@@ -842,37 +574,23 @@ namespace
|
||||
return memory_violation_continuation::resume;
|
||||
});
|
||||
|
||||
/*
|
||||
watch_object(*emu, context.teb);
|
||||
watch_object(*emu, context.peb);
|
||||
watch_object(*emu, context.process_params);
|
||||
watch_object(*emu, context.kusd);
|
||||
*/
|
||||
|
||||
context.verbose = false;
|
||||
|
||||
emu->hook_memory_execution(0, std::numeric_limits<size_t>::max(), [&](const uint64_t address, const size_t)
|
||||
{
|
||||
++context.executed_instructions;
|
||||
|
||||
const mapped_binary* binary{nullptr};
|
||||
for (const auto& entry : context.mapped_binaries)
|
||||
{
|
||||
const auto& mod = entry.second;
|
||||
if (is_within_start_and_length(address, mod->image_base, mod->size_of_image))
|
||||
{
|
||||
binary = mod.get();
|
||||
break;
|
||||
}
|
||||
|
||||
if (address < mod->image_base)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto* binary = context.module_manager.find_by_address(address);
|
||||
|
||||
if (binary)
|
||||
{
|
||||
const auto export_entry = binary->export_remap.find(address);
|
||||
if (export_entry != binary->export_remap.end())
|
||||
const auto export_entry = binary->address_names.find(address);
|
||||
if (export_entry != binary->address_names.end())
|
||||
{
|
||||
printf("Executing function: %s - %s (%llX)\n", binary->name.c_str(), export_entry->second.c_str(),
|
||||
address);
|
||||
@@ -937,6 +655,8 @@ namespace
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
//setvbuf(stdout, nullptr, _IOFBF, 0x10000);
|
||||
|
||||
try
|
||||
{
|
||||
do
|
||||
|
||||
25
src/windows_emulator/module/mapped_module.hpp
Normal file
25
src/windows_emulator/module/mapped_module.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
struct exported_symbol
|
||||
{
|
||||
std::string name{};
|
||||
uint64_t ordinal{};
|
||||
uint64_t rva{};
|
||||
uint64_t address{};
|
||||
};
|
||||
|
||||
using exported_symbols = std::vector<exported_symbol>;
|
||||
using address_name_mapping = std::unordered_map<uint64_t, std::string>;
|
||||
|
||||
struct mapped_module
|
||||
{
|
||||
std::string name{};
|
||||
std::filesystem::path path{};
|
||||
|
||||
uint64_t image_base{};
|
||||
uint64_t size_of_image{};
|
||||
uint64_t entry_point{};
|
||||
|
||||
exported_symbols exports{};
|
||||
address_name_mapping address_names{};
|
||||
};
|
||||
21
src/windows_emulator/module/module_manager.cpp
Normal file
21
src/windows_emulator/module/module_manager.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "module_manager.hpp"
|
||||
#include "module_mapping.hpp"
|
||||
|
||||
module_manager::module_manager(emulator& emu)
|
||||
: emu_(&emu)
|
||||
{
|
||||
}
|
||||
|
||||
mapped_module* module_manager::map_module(std::filesystem::path file)
|
||||
{
|
||||
auto mod = map_module_from_file(*this->emu_, std::move(file));
|
||||
if (!mod)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto image_base = mod->image_base;
|
||||
const auto entry = this->modules_.try_emplace(image_base, std::move(*mod));
|
||||
return &entry.first->second;
|
||||
}
|
||||
46
src/windows_emulator/module/module_manager.hpp
Normal file
46
src/windows_emulator/module/module_manager.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "mapped_module.hpp"
|
||||
#include <emulator.hpp>
|
||||
|
||||
class module_manager
|
||||
{
|
||||
public:
|
||||
module_manager() = default; // TODO: Get rid of that
|
||||
module_manager(emulator& emu);
|
||||
|
||||
mapped_module* map_module(std::filesystem::path file);
|
||||
|
||||
mapped_module* find_by_address(const uint64_t address)
|
||||
{
|
||||
const auto entry = this->get_module(address);
|
||||
if(entry != this->modules_.end())
|
||||
{
|
||||
return &entry->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
emulator* emu_{};
|
||||
|
||||
using module_map = std::map<uint64_t, mapped_module>;
|
||||
module_map modules_{};
|
||||
|
||||
module_map::iterator get_module(const uint64_t address)
|
||||
{
|
||||
if (this->modules_.empty())
|
||||
{
|
||||
return this->modules_.end();
|
||||
}
|
||||
|
||||
auto upper_bound = this->modules_.upper_bound(address);
|
||||
if (upper_bound == this->modules_.begin())
|
||||
{
|
||||
return this->modules_.end();
|
||||
}
|
||||
|
||||
std::advance(upper_bound, -1);
|
||||
return upper_bound;
|
||||
}
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "std_include.hpp"
|
||||
#include "module_mapper.hpp"
|
||||
#include "../std_include.hpp"
|
||||
#include "module_mapping.hpp"
|
||||
#include <address_utils.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
void collect_exports(emulator& emu, mapped_binary& binary, const IMAGE_OPTIONAL_HEADER& optional_header)
|
||||
void collect_exports(emulator& emu, mapped_module& binary, const IMAGE_OPTIONAL_HEADER& optional_header)
|
||||
{
|
||||
auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
|
||||
@@ -41,11 +41,11 @@ namespace
|
||||
|
||||
for (const auto& symbol : binary.exports)
|
||||
{
|
||||
binary.export_remap.try_emplace(symbol.address, symbol.name);
|
||||
binary.address_names.try_emplace(symbol.address, symbol.name);
|
||||
}
|
||||
}
|
||||
|
||||
void apply_relocations(x64_emulator& emu, const mapped_binary& binary,
|
||||
void apply_relocations(emulator& emu, const mapped_module& binary,
|
||||
const IMAGE_OPTIONAL_HEADER& optional_header)
|
||||
{
|
||||
const auto delta = binary.image_base - optional_header.ImageBase;
|
||||
@@ -113,7 +113,7 @@ namespace
|
||||
emu.write_memory(binary.image_base, memory.data(), memory.size());
|
||||
}
|
||||
|
||||
void map_sections(x64_emulator& emu, const mapped_binary& binary, const unsigned char* ptr,
|
||||
void map_sections(emulator& emu, const mapped_module& binary, const unsigned char* ptr,
|
||||
const IMAGE_NT_HEADERS& nt_headers)
|
||||
{
|
||||
const std::span sections(IMAGE_FIRST_SECTION(&nt_headers), nt_headers.FileHeader.NumberOfSections);
|
||||
@@ -153,15 +153,22 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
mapped_binary map_module(x64_emulator& emu, const std::vector<uint8_t>& module_data,
|
||||
std::filesystem::path file)
|
||||
std::vector<uint8_t> load_file(const std::filesystem::path& file)
|
||||
{
|
||||
mapped_binary binary{};
|
||||
std::ifstream stream(file, std::ios::in | std::ios::binary);
|
||||
return {(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()};
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<mapped_module> map_module_from_data(emulator& emu, const std::vector<uint8_t>& data,
|
||||
std::filesystem::path file)
|
||||
{
|
||||
mapped_module binary{};
|
||||
binary.path = std::move(file);
|
||||
binary.name = binary.path.filename().string();
|
||||
|
||||
// TODO: Range checks
|
||||
auto* ptr = module_data.data();
|
||||
auto* ptr = data.data();
|
||||
auto* dos_header = reinterpret_cast<const IMAGE_DOS_HEADER*>(ptr);
|
||||
auto* nt_headers = reinterpret_cast<const IMAGE_NT_HEADERS*>(ptr + dos_header->e_lfanew);
|
||||
auto& optional_header = nt_headers->OptionalHeader;
|
||||
@@ -177,7 +184,7 @@ namespace
|
||||
!emu.allocate_memory(
|
||||
binary.image_base, binary.size_of_image, memory_permission::read))
|
||||
{
|
||||
throw std::runtime_error("Failed to map binary");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,29 +199,20 @@ namespace
|
||||
collect_exports(emu, binary, optional_header);
|
||||
|
||||
return binary;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> load_file(const std::filesystem::path& file)
|
||||
{
|
||||
std::ifstream stream(file, std::ios::in | std::ios::binary);
|
||||
return {(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()};
|
||||
}
|
||||
}
|
||||
|
||||
mapped_binary* map_file(process_context& context, x64_emulator& emu, std::filesystem::path file)
|
||||
std::optional<mapped_module> map_module_from_file(emulator& emu, std::filesystem::path file)
|
||||
{
|
||||
const auto data = load_file(file);
|
||||
if (data.empty())
|
||||
{
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto binary = map_module(emu, data, std::move(file));
|
||||
auto binary_ptr = std::make_unique<mapped_binary>(std::move(binary));
|
||||
auto* res = binary_ptr.get();
|
||||
|
||||
const auto image_base = binary_ptr->image_base;
|
||||
context.mapped_binaries[image_base] = std::move(binary_ptr);
|
||||
|
||||
return res;
|
||||
return map_module_from_data(emu, data, std::move(file));
|
||||
}
|
||||
|
||||
bool unmap_module(emulator& emu, const mapped_module& mod)
|
||||
{
|
||||
return emu.release_memory(mod.image_base, mod.size_of_image);
|
||||
}
|
||||
10
src/windows_emulator/module/module_mapping.hpp
Normal file
10
src/windows_emulator/module/module_mapping.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <x64_emulator.hpp>
|
||||
#include "mapped_module.hpp"
|
||||
|
||||
std::optional<mapped_module> map_module_from_data(emulator& emu, const std::vector<uint8_t>& data,
|
||||
std::filesystem::path file);
|
||||
std::optional<mapped_module> map_module_from_file(emulator& emu, std::filesystem::path file);
|
||||
|
||||
bool unmap_module(emulator& emu, const mapped_module& mod);
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "process_context.hpp"
|
||||
#include <x64_emulator.hpp>
|
||||
|
||||
mapped_binary* map_file(process_context& context, x64_emulator& emu, std::filesystem::path file);
|
||||
@@ -2,26 +2,7 @@
|
||||
#include "emulator_utils.hpp"
|
||||
#include "handles.hpp"
|
||||
|
||||
struct exported_symbol
|
||||
{
|
||||
std::string name{};
|
||||
uint64_t ordinal{};
|
||||
uint64_t rva{};
|
||||
uint64_t address{};
|
||||
};
|
||||
|
||||
using exported_symbols = std::vector<exported_symbol>;
|
||||
|
||||
struct mapped_binary
|
||||
{
|
||||
std::filesystem::path path{};
|
||||
std::string name{};
|
||||
uint64_t image_base{};
|
||||
uint64_t size_of_image{};
|
||||
uint64_t entry_point{};
|
||||
exported_symbols exports{};
|
||||
std::unordered_map<uint64_t, std::string> export_remap{};
|
||||
};
|
||||
#include "module/module_manager.hpp"
|
||||
|
||||
struct event
|
||||
{
|
||||
@@ -54,10 +35,10 @@ struct process_context
|
||||
emulator_object<RTL_USER_PROCESS_PARAMETERS> process_params{};
|
||||
emulator_object<KUSER_SHARED_DATA> kusd{};
|
||||
|
||||
std::map<uint64_t, std::unique_ptr<mapped_binary>> mapped_binaries{};
|
||||
module_manager module_manager{};
|
||||
|
||||
mapped_binary* executable{};
|
||||
mapped_binary* ntdll{};
|
||||
mapped_module* executable{};
|
||||
mapped_module* ntdll{};
|
||||
|
||||
handle_store<handle_types::event, event> events{};
|
||||
handle_store<handle_types::file, file> files{};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "std_include.hpp"
|
||||
#include "syscalls.hpp"
|
||||
#include "module_mapper.hpp"
|
||||
#include "context_frame.hpp"
|
||||
|
||||
struct syscall_context
|
||||
@@ -396,7 +395,7 @@ namespace
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
const auto binary = map_file(c.proc, c.emu, section_entry->name);
|
||||
const auto binary = c.proc.module_manager.map_module(section_entry->name);
|
||||
if (!binary)
|
||||
{
|
||||
return STATUS_FILE_INVALID;
|
||||
|
||||
Reference in New Issue
Block a user