Format all the code

This commit is contained in:
momo5502
2025-01-06 17:13:33 +01:00
parent 64c2a79f0f
commit bff8420ffd
100 changed files with 16439 additions and 14509 deletions

View File

@@ -3,149 +3,149 @@
namespace context_frame
{
void restore(x64_emulator& emu, const CONTEXT64& context)
{
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
{
emu.reg(x64_register::dr0, context.Dr0);
emu.reg(x64_register::dr1, context.Dr1);
emu.reg(x64_register::dr2, context.Dr2);
emu.reg(x64_register::dr3, context.Dr3);
emu.reg(x64_register::dr6, context.Dr6);
emu.reg(x64_register::dr7, context.Dr7);
}
void restore(x64_emulator& emu, const CONTEXT64& context)
{
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
{
emu.reg(x64_register::dr0, context.Dr0);
emu.reg(x64_register::dr1, context.Dr1);
emu.reg(x64_register::dr2, context.Dr2);
emu.reg(x64_register::dr3, context.Dr3);
emu.reg(x64_register::dr6, context.Dr6);
emu.reg(x64_register::dr7, context.Dr7);
}
if (context.ContextFlags & CONTEXT_CONTROL_64)
{
emu.reg<uint16_t>(x64_register::ss, context.SegSs);
emu.reg<uint16_t>(x64_register::cs, context.SegCs);
if (context.ContextFlags & CONTEXT_CONTROL_64)
{
emu.reg<uint16_t>(x64_register::ss, context.SegSs);
emu.reg<uint16_t>(x64_register::cs, context.SegCs);
emu.reg(x64_register::rip, context.Rip);
emu.reg(x64_register::rsp, context.Rsp);
emu.reg(x64_register::rip, context.Rip);
emu.reg(x64_register::rsp, context.Rsp);
emu.reg<uint32_t>(x64_register::eflags, context.EFlags);
}
emu.reg<uint32_t>(x64_register::eflags, context.EFlags);
}
if (context.ContextFlags & CONTEXT_INTEGER_64)
{
emu.reg(x64_register::rax, context.Rax);
emu.reg(x64_register::rbx, context.Rbx);
emu.reg(x64_register::rcx, context.Rcx);
emu.reg(x64_register::rdx, context.Rdx);
emu.reg(x64_register::rbp, context.Rbp);
emu.reg(x64_register::rsi, context.Rsi);
emu.reg(x64_register::rdi, context.Rdi);
emu.reg(x64_register::r8, context.R8);
emu.reg(x64_register::r9, context.R9);
emu.reg(x64_register::r10, context.R10);
emu.reg(x64_register::r11, context.R11);
emu.reg(x64_register::r12, context.R12);
emu.reg(x64_register::r13, context.R13);
emu.reg(x64_register::r14, context.R14);
emu.reg(x64_register::r15, context.R15);
}
if (context.ContextFlags & CONTEXT_INTEGER_64)
{
emu.reg(x64_register::rax, context.Rax);
emu.reg(x64_register::rbx, context.Rbx);
emu.reg(x64_register::rcx, context.Rcx);
emu.reg(x64_register::rdx, context.Rdx);
emu.reg(x64_register::rbp, context.Rbp);
emu.reg(x64_register::rsi, context.Rsi);
emu.reg(x64_register::rdi, context.Rdi);
emu.reg(x64_register::r8, context.R8);
emu.reg(x64_register::r9, context.R9);
emu.reg(x64_register::r10, context.R10);
emu.reg(x64_register::r11, context.R11);
emu.reg(x64_register::r12, context.R12);
emu.reg(x64_register::r13, context.R13);
emu.reg(x64_register::r14, context.R14);
emu.reg(x64_register::r15, context.R15);
}
/*if (context.ContextFlags & CONTEXT_SEGMENTS)
{
emu.reg<uint16_t>(x64_register::ds, context.SegDs);
emu.reg<uint16_t>(x64_register::es, context.SegEs);
emu.reg<uint16_t>(x64_register::fs, context.SegFs);
emu.reg<uint16_t>(x64_register::gs, context.SegGs);
}*/
/*if (context.ContextFlags & CONTEXT_SEGMENTS)
{
emu.reg<uint16_t>(x64_register::ds, context.SegDs);
emu.reg<uint16_t>(x64_register::es, context.SegEs);
emu.reg<uint16_t>(x64_register::fs, context.SegFs);
emu.reg<uint16_t>(x64_register::gs, context.SegGs);
}*/
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
{
emu.reg<uint16_t>(x64_register::fpcw, context.FltSave.ControlWord);
emu.reg<uint16_t>(x64_register::fpsw, context.FltSave.StatusWord);
emu.reg<uint16_t>(x64_register::fptag, context.FltSave.TagWord);
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
{
emu.reg<uint16_t>(x64_register::fpcw, context.FltSave.ControlWord);
emu.reg<uint16_t>(x64_register::fpsw, context.FltSave.StatusWord);
emu.reg<uint16_t>(x64_register::fptag, context.FltSave.TagWord);
for (int i = 0; i < 8; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
emu.reg<M128A>(reg, context.FltSave.FloatRegisters[i]);
}
}
for (int i = 0; i < 8; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
emu.reg<M128A>(reg, context.FltSave.FloatRegisters[i]);
}
}
if (context.ContextFlags & CONTEXT_XSTATE_64)
{
emu.reg<uint32_t>(x64_register::mxcsr, context.MxCsr);
if (context.ContextFlags & CONTEXT_XSTATE_64)
{
emu.reg<uint32_t>(x64_register::mxcsr, context.MxCsr);
for (int i = 0; i < 16; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
emu.reg<M128A>(reg, (&context.Xmm0)[i]);
}
}
}
for (int i = 0; i < 16; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
emu.reg<M128A>(reg, (&context.Xmm0)[i]);
}
}
}
void save(x64_emulator& emu, CONTEXT64& context)
{
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
{
context.Dr0 = emu.reg(x64_register::dr0);
context.Dr1 = emu.reg(x64_register::dr1);
context.Dr2 = emu.reg(x64_register::dr2);
context.Dr3 = emu.reg(x64_register::dr3);
context.Dr6 = emu.reg(x64_register::dr6);
context.Dr7 = emu.reg(x64_register::dr7);
}
void save(x64_emulator& emu, CONTEXT64& context)
{
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
{
context.Dr0 = emu.reg(x64_register::dr0);
context.Dr1 = emu.reg(x64_register::dr1);
context.Dr2 = emu.reg(x64_register::dr2);
context.Dr3 = emu.reg(x64_register::dr3);
context.Dr6 = emu.reg(x64_register::dr6);
context.Dr7 = emu.reg(x64_register::dr7);
}
if (context.ContextFlags & CONTEXT_CONTROL_64)
{
context.SegSs = emu.reg<uint16_t>(x64_register::ss);
context.SegCs = emu.reg<uint16_t>(x64_register::cs);
context.Rip = emu.reg(x64_register::rip);
context.Rsp = emu.reg(x64_register::rsp);
context.EFlags = emu.reg<uint32_t>(x64_register::eflags);
}
if (context.ContextFlags & CONTEXT_CONTROL_64)
{
context.SegSs = emu.reg<uint16_t>(x64_register::ss);
context.SegCs = emu.reg<uint16_t>(x64_register::cs);
context.Rip = emu.reg(x64_register::rip);
context.Rsp = emu.reg(x64_register::rsp);
context.EFlags = emu.reg<uint32_t>(x64_register::eflags);
}
if (context.ContextFlags & CONTEXT_INTEGER_64)
{
context.Rax = emu.reg(x64_register::rax);
context.Rbx = emu.reg(x64_register::rbx);
context.Rcx = emu.reg(x64_register::rcx);
context.Rdx = emu.reg(x64_register::rdx);
context.Rbp = emu.reg(x64_register::rbp);
context.Rsi = emu.reg(x64_register::rsi);
context.Rdi = emu.reg(x64_register::rdi);
context.R8 = emu.reg(x64_register::r8);
context.R9 = emu.reg(x64_register::r9);
context.R10 = emu.reg(x64_register::r10);
context.R11 = emu.reg(x64_register::r11);
context.R12 = emu.reg(x64_register::r12);
context.R13 = emu.reg(x64_register::r13);
context.R14 = emu.reg(x64_register::r14);
context.R15 = emu.reg(x64_register::r15);
}
if (context.ContextFlags & CONTEXT_INTEGER_64)
{
context.Rax = emu.reg(x64_register::rax);
context.Rbx = emu.reg(x64_register::rbx);
context.Rcx = emu.reg(x64_register::rcx);
context.Rdx = emu.reg(x64_register::rdx);
context.Rbp = emu.reg(x64_register::rbp);
context.Rsi = emu.reg(x64_register::rsi);
context.Rdi = emu.reg(x64_register::rdi);
context.R8 = emu.reg(x64_register::r8);
context.R9 = emu.reg(x64_register::r9);
context.R10 = emu.reg(x64_register::r10);
context.R11 = emu.reg(x64_register::r11);
context.R12 = emu.reg(x64_register::r12);
context.R13 = emu.reg(x64_register::r13);
context.R14 = emu.reg(x64_register::r14);
context.R15 = emu.reg(x64_register::r15);
}
if (context.ContextFlags & CONTEXT_SEGMENTS_64)
{
context.SegDs = emu.reg<uint16_t>(x64_register::ds);
context.SegEs = emu.reg<uint16_t>(x64_register::es);
context.SegFs = emu.reg<uint16_t>(x64_register::fs);
context.SegGs = emu.reg<uint16_t>(x64_register::gs);
}
if (context.ContextFlags & CONTEXT_SEGMENTS_64)
{
context.SegDs = emu.reg<uint16_t>(x64_register::ds);
context.SegEs = emu.reg<uint16_t>(x64_register::es);
context.SegFs = emu.reg<uint16_t>(x64_register::fs);
context.SegGs = emu.reg<uint16_t>(x64_register::gs);
}
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
{
context.FltSave.ControlWord = emu.reg<uint16_t>(x64_register::fpcw);
context.FltSave.StatusWord = emu.reg<uint16_t>(x64_register::fpsw);
context.FltSave.TagWord = static_cast<BYTE>(emu.reg<uint16_t>(x64_register::fptag));
for (int i = 0; i < 8; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
context.FltSave.FloatRegisters[i] = emu.reg<M128A>(reg);
}
}
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
{
context.FltSave.ControlWord = emu.reg<uint16_t>(x64_register::fpcw);
context.FltSave.StatusWord = emu.reg<uint16_t>(x64_register::fpsw);
context.FltSave.TagWord = static_cast<BYTE>(emu.reg<uint16_t>(x64_register::fptag));
for (int i = 0; i < 8; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
context.FltSave.FloatRegisters[i] = emu.reg<M128A>(reg);
}
}
if (context.ContextFlags & CONTEXT_XSTATE_64)
{
context.MxCsr = emu.reg<uint32_t>(x64_register::mxcsr);
for (int i = 0; i < 16; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
(&context.Xmm0)[i] = emu.reg<M128A>(reg);
}
}
}
if (context.ContextFlags & CONTEXT_XSTATE_64)
{
context.MxCsr = emu.reg<uint32_t>(x64_register::mxcsr);
for (int i = 0; i < 16; i++)
{
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
(&context.Xmm0)[i] = emu.reg<M128A>(reg);
}
}
}
}

View File

@@ -3,6 +3,6 @@
namespace context_frame
{
void save(x64_emulator& emu, CONTEXT64& context);
void restore(x64_emulator& emu, const CONTEXT64& context);
void save(x64_emulator& emu, CONTEXT64& context);
void restore(x64_emulator& emu, const CONTEXT64& context);
}

View File

@@ -3,137 +3,134 @@
#include <utils/finally.hpp>
extern "C" {
extern "C"
{
#include <gdbstub.h>
}
namespace
{
gdb_action_t map_gdb_action(const gdb_action action)
{
switch (action)
{
case gdb_action::none:
return ACT_NONE;
case gdb_action::resume:
return ACT_RESUME;
case gdb_action::shutdown:
return ACT_SHUTDOWN;
}
gdb_action_t map_gdb_action(const gdb_action action)
{
switch (action)
{
case gdb_action::none:
return ACT_NONE;
case gdb_action::resume:
return ACT_RESUME;
case gdb_action::shutdown:
return ACT_SHUTDOWN;
}
throw std::runtime_error("Bad action");
}
throw std::runtime_error("Bad action");
}
breakpoint_type map_breakpoint_type(const bp_type_t type)
{
switch (type)
{
case BP_SOFTWARE:
return breakpoint_type::software;
case BP_HARDWARE_EXEC:
return breakpoint_type::hardware_exec;
case BP_HARDWARE_WRITE:
return breakpoint_type::hardware_write;
case BP_HARDWARE_READ:
return breakpoint_type::hardware_read;
case BP_HARDWARE_READ_WRITE:
return breakpoint_type::hardware_read_write;
}
breakpoint_type map_breakpoint_type(const bp_type_t type)
{
switch (type)
{
case BP_SOFTWARE:
return breakpoint_type::software;
case BP_HARDWARE_EXEC:
return breakpoint_type::hardware_exec;
case BP_HARDWARE_WRITE:
return breakpoint_type::hardware_write;
case BP_HARDWARE_READ:
return breakpoint_type::hardware_read;
case BP_HARDWARE_READ_WRITE:
return breakpoint_type::hardware_read_write;
}
throw std::runtime_error("Bad breakpoint type");
}
throw std::runtime_error("Bad breakpoint type");
}
gdb_stub_handler& get_handler(void* args)
{
return *static_cast<gdb_stub_handler*>(args);
}
gdb_stub_handler& get_handler(void* args)
{
return *static_cast<gdb_stub_handler*>(args);
}
gdb_action_t cont(void* args)
{
return map_gdb_action(get_handler(args).cont());
}
gdb_action_t cont(void* args)
{
return map_gdb_action(get_handler(args).cont());
}
gdb_action_t stepi(void* args)
{
return map_gdb_action(get_handler(args).stepi());
}
gdb_action_t stepi(void* args)
{
return map_gdb_action(get_handler(args).stepi());
}
int read_reg(void* args, const int regno, size_t* value)
{
return get_handler(args).read_reg(regno, value) ? 0 : 1;
}
int read_reg(void* args, const int regno, size_t* value)
{
return get_handler(args).read_reg(regno, value) ? 0 : 1;
}
int write_reg(void* args, const int regno, const size_t value)
{
return get_handler(args).write_reg(regno, value) ? 0 : 1;
}
int write_reg(void* args, const int regno, const size_t value)
{
return get_handler(args).write_reg(regno, value) ? 0 : 1;
}
int read_mem(void* args, const size_t addr, const size_t len, void* val)
{
return get_handler(args).read_mem(addr, len, val) ? 0 : 1;
}
int read_mem(void* args, const size_t addr, const size_t len, void* val)
{
return get_handler(args).read_mem(addr, len, val) ? 0 : 1;
}
int write_mem(void* args, const size_t addr, const size_t len, void* val)
{
return get_handler(args).write_mem(addr, len, val) ? 0 : 1;
}
int write_mem(void* args, const size_t addr, const size_t len, void* val)
{
return get_handler(args).write_mem(addr, len, val) ? 0 : 1;
}
bool set_bp(void* args, const size_t addr, const bp_type_t type, const size_t size)
{
return get_handler(args).set_bp(map_breakpoint_type(type), addr, size);
}
bool set_bp(void* args, const size_t addr, const bp_type_t type, const size_t size)
{
return get_handler(args).set_bp(map_breakpoint_type(type), addr, size);
}
bool del_bp(void* args, const size_t addr, const bp_type_t type, const size_t size)
{
return get_handler(args).del_bp(map_breakpoint_type(type), addr, size);
}
bool del_bp(void* args, const size_t addr, const bp_type_t type, const size_t size)
{
return get_handler(args).del_bp(map_breakpoint_type(type), addr, size);
}
void on_interrupt(void* args)
{
get_handler(args).on_interrupt();
}
void on_interrupt(void* args)
{
get_handler(args).on_interrupt();
}
target_ops get_target_ops()
{
target_ops ops{};
target_ops get_target_ops()
{
target_ops ops{};
ops.cont = cont;
ops.stepi = stepi;
ops.read_reg = read_reg;
ops.write_reg = write_reg;
ops.read_mem = read_mem;
ops.write_mem = write_mem;
ops.set_bp = set_bp;
ops.del_bp = del_bp;
ops.on_interrupt = on_interrupt;
ops.cont = cont;
ops.stepi = stepi;
ops.read_reg = read_reg;
ops.write_reg = write_reg;
ops.read_mem = read_mem;
ops.write_mem = write_mem;
ops.set_bp = set_bp;
ops.del_bp = del_bp;
ops.on_interrupt = on_interrupt;
return ops;
}
return ops;
}
}
bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, const size_t register_count,
std::string bind_address)
{
const arch_info_t info
{
target_description.data(),
static_cast<int>(register_count),
sizeof(uint64_t),
};
const arch_info_t info{
target_description.data(),
static_cast<int>(register_count),
sizeof(uint64_t),
};
auto ops = get_target_ops();
auto ops = get_target_ops();
gdbstub_t stub{};
gdbstub_t stub{};
if (!gdbstub_init(&stub, &ops, info, bind_address.data()))
{
return false;
}
if (!gdbstub_init(&stub, &ops, info, bind_address.data()))
{
return false;
}
const auto _ = utils::finally([&]
{
gdbstub_close(&stub);
});
const auto _ = utils::finally([&] { gdbstub_close(&stub); });
return gdbstub_run(&stub, &handler);
return gdbstub_run(&stub, &handler);
}

View File

@@ -2,37 +2,37 @@
enum class gdb_action : uint8_t
{
none,
resume,
shutdown,
none,
resume,
shutdown,
};
enum class breakpoint_type : uint8_t
{
software,
hardware_exec,
hardware_write,
hardware_read,
hardware_read_write,
software,
hardware_exec,
hardware_write,
hardware_read,
hardware_read_write,
};
struct gdb_stub_handler
{
virtual ~gdb_stub_handler() = default;
virtual ~gdb_stub_handler() = default;
virtual gdb_action cont() = 0;
virtual gdb_action stepi() = 0;
virtual gdb_action cont() = 0;
virtual gdb_action stepi() = 0;
virtual bool read_reg(int regno, size_t* value) = 0;
virtual bool write_reg(int regno, size_t value) = 0;
virtual bool read_reg(int regno, size_t* value) = 0;
virtual bool write_reg(int regno, size_t value) = 0;
virtual bool read_mem(size_t addr, size_t len, void* val) = 0;
virtual bool write_mem(size_t addr, size_t len, void* val) = 0;
virtual bool read_mem(size_t addr, size_t len, void* val) = 0;
virtual bool write_mem(size_t addr, size_t len, void* val) = 0;
virtual bool set_bp(breakpoint_type type, size_t addr, size_t size) = 0;
virtual bool del_bp(breakpoint_type type, size_t addr, size_t size) = 0;
virtual bool set_bp(breakpoint_type type, size_t addr, size_t size) = 0;
virtual bool del_bp(breakpoint_type type, size_t addr, size_t size) = 0;
virtual void on_interrupt() = 0;
virtual void on_interrupt() = 0;
};
bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, size_t register_count,

View File

@@ -5,41 +5,41 @@
class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
{
public:
win_x64_gdb_stub_handler(windows_emulator& win_emu)
: x64_gdb_stub_handler(win_emu.emu())
, win_emu_(&win_emu)
{
}
public:
win_x64_gdb_stub_handler(windows_emulator& win_emu)
: x64_gdb_stub_handler(win_emu.emu()),
win_emu_(&win_emu)
{
}
gdb_action cont() override
{
try
{
this->win_emu_->start();
}
catch (const std::exception& e)
{
puts(e.what());
}
gdb_action cont() override
{
try
{
this->win_emu_->start();
}
catch (const std::exception& e)
{
puts(e.what());
}
return gdb_action::resume;
}
return gdb_action::resume;
}
gdb_action stepi() override
{
try
{
this->win_emu_->start({}, 1);
}
catch (const std::exception& e)
{
puts(e.what());
}
gdb_action stepi() override
{
try
{
this->win_emu_->start({}, 1);
}
catch (const std::exception& e)
{
puts(e.what());
}
return gdb_action::resume;
}
return gdb_action::resume;
}
private:
windows_emulator* win_emu_{};
private:
windows_emulator* win_emu_{};
};

View File

@@ -4,212 +4,194 @@
#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,*/
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");
}
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{};
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;
}
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);
}
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)
{
}
public:
x64_gdb_stub_handler(x64_emulator& emu)
: emu_(&emu)
{
}
~x64_gdb_stub_handler() override = default;
~x64_gdb_stub_handler() override = default;
gdb_action cont() override
{
try
{
this->emu_->start_from_ip();
}
catch (const std::exception& e)
{
puts(e.what());
}
gdb_action cont() override
{
try
{
this->emu_->start_from_ip();
}
catch (const std::exception& e)
{
puts(e.what());
}
return gdb_action::resume;
}
return gdb_action::resume;
}
gdb_action stepi() override
{
try
{
this->emu_->start_from_ip({}, 1);
}
catch (const std::exception& e)
{
puts(e.what());
}
gdb_action stepi() override
{
try
{
this->emu_->start_from_ip({}, 1);
}
catch (const std::exception& e)
{
puts(e.what());
}
return gdb_action::resume;
}
return gdb_action::resume;
}
bool read_reg(const int regno, size_t* value) override
{
*value = 0;
bool read_reg(const int regno, size_t* value) override
{
*value = 0;
try
{
if (static_cast<size_t>(regno) >= gdb_registers.size())
{
return true;
}
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;
}
}
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;
}
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;
}
}
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 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 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, uint64_t, memory_operation)
{
this->on_interrupt();
}));
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, uint64_t, memory_operation) { this->on_interrupt(); }));
return true;
}
catch (...)
{
return false;
}
}
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;
}
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);
this->hooks_.erase(entry);
return true;
}
catch (...)
{
return false;
}
}
return true;
}
catch (...)
{
return false;
}
}
void on_interrupt() override
{
this->emu_->stop();
}
void on_interrupt() override
{
this->emu_->stop();
}
private:
x64_emulator* emu_{};
std::unordered_map<breakpoint_key, scoped_hook> hooks_{};
private:
x64_emulator* emu_{};
std::unordered_map<breakpoint_key, scoped_hook> hooks_{};
};

File diff suppressed because it is too large Load Diff

View File

@@ -7,88 +7,88 @@ typedef LONG TDI_STATUS;
template <typename Traits>
struct TDI_CONNECTION_INFORMATION
{
LONG UserDataLength;
typename Traits::PVOID UserData;
LONG OptionsLength;
typename Traits::PVOID Options;
LONG RemoteAddressLength;
typename Traits::PVOID RemoteAddress;
LONG UserDataLength;
typename Traits::PVOID UserData;
LONG OptionsLength;
typename Traits::PVOID Options;
LONG RemoteAddressLength;
typename Traits::PVOID RemoteAddress;
};
template <typename Traits>
struct TDI_REQUEST
{
union
{
typename Traits::HANDLE AddressHandle;
EMULATOR_CAST(typename Traits::PVOID, CONNECTION_CONTEXT) ConnectionContext;
typename Traits::HANDLE ControlChannel;
} Handle;
union
{
typename Traits::HANDLE AddressHandle;
EMULATOR_CAST(typename Traits::PVOID, CONNECTION_CONTEXT) ConnectionContext;
typename Traits::HANDLE ControlChannel;
} Handle;
typename Traits::PVOID RequestNotifyObject;
typename Traits::PVOID RequestContext;
TDI_STATUS TdiStatus;
typename Traits::PVOID RequestNotifyObject;
typename Traits::PVOID RequestContext;
TDI_STATUS TdiStatus;
};
template <typename Traits>
struct TDI_REQUEST_SEND_DATAGRAM
{
TDI_REQUEST<Traits> Request;
EMULATOR_CAST(typename Traits::PVOID, PTDI_CONNECTION_INFORMATION) SendDatagramInformation;
TDI_REQUEST<Traits> Request;
EMULATOR_CAST(typename Traits::PVOID, PTDI_CONNECTION_INFORMATION) SendDatagramInformation;
};
template <typename Traits>
struct AFD_SEND_INFO
{
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount;
ULONG AfdFlags;
ULONG TdiFlags;
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount;
ULONG AfdFlags;
ULONG TdiFlags;
};
template <typename Traits>
struct AFD_SEND_DATAGRAM_INFO
{
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount;
ULONG AfdFlags;
TDI_REQUEST_SEND_DATAGRAM<Traits> TdiRequest;
TDI_CONNECTION_INFORMATION<Traits> TdiConnInfo;
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount;
ULONG AfdFlags;
TDI_REQUEST_SEND_DATAGRAM<Traits> TdiRequest;
TDI_CONNECTION_INFORMATION<Traits> TdiConnInfo;
};
template <typename Traits>
struct AFD_RECV_INFO
{
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount;
ULONG AfdFlags;
ULONG TdiFlags;
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount;
ULONG AfdFlags;
ULONG TdiFlags;
};
template <typename Traits>
struct AFD_RECV_DATAGRAM_INFO
{
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount;
ULONG AfdFlags;
ULONG TdiFlags;
typename Traits::PVOID Address;
EMULATOR_CAST(typename Traits::PVOID, PULONG) AddressLength;
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount;
ULONG AfdFlags;
ULONG TdiFlags;
typename Traits::PVOID Address;
EMULATOR_CAST(typename Traits::PVOID, PULONG) AddressLength;
};
struct AFD_POLL_HANDLE_INFO64
{
EmulatorTraits<Emu64>::HANDLE Handle;
ULONG PollEvents;
NTSTATUS Status;
EmulatorTraits<Emu64>::HANDLE Handle;
ULONG PollEvents;
NTSTATUS Status;
};
struct AFD_POLL_INFO64
{
LARGE_INTEGER Timeout;
ULONG NumberOfHandles;
BOOLEAN Unique;
AFD_POLL_HANDLE_INFO64 Handles[1];
LARGE_INTEGER Timeout;
ULONG NumberOfHandles;
BOOLEAN Unique;
AFD_POLL_HANDLE_INFO64 Handles[1];
};
#define AFD_POLL_RECEIVE_BIT 0
@@ -117,59 +117,57 @@ struct AFD_POLL_INFO64
#define AFD_NUM_POLL_EVENTS 11
#define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1)
#define _AFD_REQUEST(ioctl) \
((((ULONG)(ioctl)) >> 2) & 0x03FF)
#define _AFD_BASE(ioctl) \
((((ULONG)(ioctl)) >> 12) & 0xFFFFF)
#define _AFD_REQUEST(ioctl) ((((ULONG)(ioctl)) >> 2) & 0x03FF)
#define _AFD_BASE(ioctl) ((((ULONG)(ioctl)) >> 12) & 0xFFFFF)
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
#define AFD_BIND 0
#define AFD_CONNECT 1
#define AFD_START_LISTEN 2
#define AFD_WAIT_FOR_LISTEN 3
#define AFD_ACCEPT 4
#define AFD_RECEIVE 5
#define AFD_RECEIVE_DATAGRAM 6
#define AFD_SEND 7
#define AFD_SEND_DATAGRAM 8
#define AFD_POLL 9
#define AFD_PARTIAL_DISCONNECT 10
#define AFD_BIND 0
#define AFD_CONNECT 1
#define AFD_START_LISTEN 2
#define AFD_WAIT_FOR_LISTEN 3
#define AFD_ACCEPT 4
#define AFD_RECEIVE 5
#define AFD_RECEIVE_DATAGRAM 6
#define AFD_SEND 7
#define AFD_SEND_DATAGRAM 8
#define AFD_POLL 9
#define AFD_PARTIAL_DISCONNECT 10
#define AFD_GET_ADDRESS 11
#define AFD_QUERY_RECEIVE_INFO 12
#define AFD_QUERY_HANDLES 13
#define AFD_SET_INFORMATION 14
#define AFD_GET_CONTEXT_LENGTH 15
#define AFD_GET_CONTEXT 16
#define AFD_SET_CONTEXT 17
#define AFD_GET_ADDRESS 11
#define AFD_QUERY_RECEIVE_INFO 12
#define AFD_QUERY_HANDLES 13
#define AFD_SET_INFORMATION 14
#define AFD_GET_CONTEXT_LENGTH 15
#define AFD_GET_CONTEXT 16
#define AFD_SET_CONTEXT 17
#define AFD_SET_CONNECT_DATA 18
#define AFD_SET_CONNECT_OPTIONS 19
#define AFD_SET_DISCONNECT_DATA 20
#define AFD_SET_DISCONNECT_OPTIONS 21
#define AFD_SET_CONNECT_DATA 18
#define AFD_SET_CONNECT_OPTIONS 19
#define AFD_SET_DISCONNECT_DATA 20
#define AFD_SET_DISCONNECT_OPTIONS 21
#define AFD_GET_CONNECT_DATA 22
#define AFD_GET_CONNECT_OPTIONS 23
#define AFD_GET_DISCONNECT_DATA 24
#define AFD_GET_DISCONNECT_OPTIONS 25
#define AFD_GET_CONNECT_DATA 22
#define AFD_GET_CONNECT_OPTIONS 23
#define AFD_GET_DISCONNECT_DATA 24
#define AFD_GET_DISCONNECT_OPTIONS 25
#define AFD_SIZE_CONNECT_DATA 26
#define AFD_SIZE_CONNECT_OPTIONS 27
#define AFD_SIZE_DISCONNECT_DATA 28
#define AFD_SIZE_DISCONNECT_OPTIONS 29
#define AFD_SIZE_CONNECT_DATA 26
#define AFD_SIZE_CONNECT_OPTIONS 27
#define AFD_SIZE_DISCONNECT_DATA 28
#define AFD_SIZE_DISCONNECT_OPTIONS 29
#define AFD_GET_INFORMATION 30
#define AFD_TRANSMIT_FILE 31
#define AFD_SUPER_ACCEPT 32
#define AFD_GET_INFORMATION 30
#define AFD_TRANSMIT_FILE 31
#define AFD_SUPER_ACCEPT 32
#define AFD_EVENT_SELECT 33
#define AFD_ENUM_NETWORK_EVENTS 34
#define AFD_EVENT_SELECT 33
#define AFD_ENUM_NETWORK_EVENTS 34
#define AFD_DEFER_ACCEPT 35
#define AFD_WAIT_FOR_LISTEN_LIFO 36
#define AFD_SET_QOS 37
#define AFD_GET_QOS 38
#define AFD_NO_OPERATION 39
#define AFD_VALIDATE_GROUP 40
#define AFD_DEFER_ACCEPT 35
#define AFD_WAIT_FOR_LISTEN_LIFO 36
#define AFD_SET_QOS 37
#define AFD_GET_QOS 38
#define AFD_NO_OPERATION 39
#define AFD_VALIDATE_GROUP 40
#define AFD_GET_UNACCEPTED_CONNECT_DATA 41

View File

@@ -8,31 +8,31 @@ using emulator_pointer = uint64_t;
template <typename T>
class object_wrapper
{
T* obj_;
T* obj_;
public:
object_wrapper(T& obj)
: obj_(&obj)
{
}
public:
object_wrapper(T& obj)
: obj_(&obj)
{
}
T& get() const
{
return *this->obj_;
}
T& get() const
{
return *this->obj_;
}
operator T&() const
{
return this->get();
}
operator T&() const
{
return this->get();
}
void serialize(utils::buffer_serializer&) const
{
}
void serialize(utils::buffer_serializer&) const
{
}
void deserialize(utils::buffer_deserializer&)
{
}
void deserialize(utils::buffer_deserializer&)
{
}
};
class windows_emulator;
@@ -45,256 +45,250 @@ using windows_emulator_wrapper = object_wrapper<windows_emulator>;
template <typename T>
class emulator_object
{
public:
using value_type = T;
public:
using value_type = T;
emulator_object(const x64_emulator_wrapper& wrapper, const uint64_t address = 0)
: emulator_object(wrapper.get(), address)
{
}
emulator_object(const x64_emulator_wrapper& wrapper, const uint64_t address = 0)
: emulator_object(wrapper.get(), address)
{
}
emulator_object(emulator& emu, const uint64_t address = 0)
: emu_(&emu)
, address_(address)
{
}
emulator_object(emulator& emu, const uint64_t address = 0)
: emu_(&emu),
address_(address)
{
}
emulator_object(emulator& emu, const void* address)
: emulator_object(emu, reinterpret_cast<uint64_t>(address))
{
}
emulator_object(emulator& emu, const void* address)
: emulator_object(emu, reinterpret_cast<uint64_t>(address))
{
}
uint64_t value() const
{
return this->address_;
}
uint64_t value() const
{
return this->address_;
}
constexpr uint64_t size() const
{
return sizeof(T);
}
constexpr uint64_t size() const
{
return sizeof(T);
}
uint64_t end() const
{
return this->value() + this->size();
}
uint64_t end() const
{
return this->value() + this->size();
}
T* ptr() const
{
return reinterpret_cast<T*>(this->address_);
}
T* ptr() const
{
return reinterpret_cast<T*>(this->address_);
}
operator bool() const
{
return this->address_ != 0;
}
operator bool() const
{
return this->address_ != 0;
}
T read(const size_t index = 0) const
{
T obj{};
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
return obj;
}
T read(const size_t index = 0) const
{
T obj{};
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
return obj;
}
void write(const T& value, const size_t index = 0) const
{
this->emu_->write_memory(this->address_ + index * this->size(), &value, sizeof(value));
}
void write(const T& value, const size_t index = 0) const
{
this->emu_->write_memory(this->address_ + index * this->size(), &value, sizeof(value));
}
void write_if_valid(const T& value, const size_t index = 0) const
{
if (this->operator bool())
{
this->write(value, index);
}
}
void write_if_valid(const T& value, const size_t index = 0) const
{
if (this->operator bool())
{
this->write(value, index);
}
}
template <typename F>
void access(const F& accessor, const size_t index = 0) const
{
T obj{};
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
template <typename F>
void access(const F& accessor, const size_t index = 0) const
{
T obj{};
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
accessor(obj);
accessor(obj);
this->write(obj, index);
}
this->write(obj, index);
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->address_);
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->address_);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->address_);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->address_);
}
void set_address(const uint64_t address)
{
this->address_ = address;
}
void set_address(const uint64_t address)
{
this->address_ = address;
}
private:
emulator* emu_{};
uint64_t address_{};
private:
emulator* emu_{};
uint64_t address_{};
};
// TODO: warning emulator_utils is hardcoded for 64bit unicode_string usage
class emulator_allocator
{
public:
emulator_allocator(emulator& emu)
: emu_(&emu)
{
}
public:
emulator_allocator(emulator& emu)
: emu_(&emu)
{
}
emulator_allocator(emulator& emu, const uint64_t address, const uint64_t size)
: emu_(&emu)
, address_(address)
, size_(size)
, active_address_(address)
{
}
emulator_allocator(emulator& emu, const uint64_t address, const uint64_t size)
: emu_(&emu),
address_(address),
size_(size),
active_address_(address)
{
}
uint64_t reserve(const uint64_t count, const uint64_t alignment = 1)
{
const auto potential_start = align_up(this->active_address_, alignment);
const auto potential_end = potential_start + count;
const auto total_end = this->address_ + this->size_;
uint64_t reserve(const uint64_t count, const uint64_t alignment = 1)
{
const auto potential_start = align_up(this->active_address_, alignment);
const auto potential_end = potential_start + count;
const auto total_end = this->address_ + this->size_;
if (potential_end > total_end)
{
throw std::runtime_error("Out of memory");
}
if (potential_end > total_end)
{
throw std::runtime_error("Out of memory");
}
this->active_address_ = potential_end;
this->active_address_ = potential_end;
return potential_start;
}
return potential_start;
}
template <typename T>
emulator_object<T> reserve(const size_t count = 1)
{
const auto potential_start = this->reserve(sizeof(T) * count, alignof(T));
return emulator_object<T>(*this->emu_, potential_start);
}
template <typename T>
emulator_object<T> reserve(const size_t count = 1)
{
const auto potential_start = this->reserve(sizeof(T) * count, alignof(T));
return emulator_object<T>(*this->emu_, potential_start);
}
char16_t* copy_string(const std::u16string_view str)
{
UNICODE_STRING<EmulatorTraits<Emu64>> uc_str{};
this->make_unicode_string(uc_str, str);
return reinterpret_cast<char16_t*>(uc_str.Buffer);
}
char16_t* copy_string(const std::u16string_view str)
{
UNICODE_STRING<EmulatorTraits<Emu64>> uc_str{};
this->make_unicode_string(uc_str, str);
return reinterpret_cast<char16_t*>(uc_str.Buffer);
}
void make_unicode_string(UNICODE_STRING<EmulatorTraits<Emu64>>& result, const std::u16string_view str)
{
constexpr auto element_size = sizeof(str[0]);
constexpr auto required_alignment = alignof(decltype(str[0]));
const auto total_length = str.size() * element_size;
void make_unicode_string(UNICODE_STRING<EmulatorTraits<Emu64>>& result, const std::u16string_view str)
{
constexpr auto element_size = sizeof(str[0]);
constexpr auto required_alignment = alignof(decltype(str[0]));
const auto total_length = str.size() * element_size;
const auto string_buffer = this->reserve(total_length + element_size, required_alignment);
const auto string_buffer = this->reserve(total_length + element_size, required_alignment);
this->emu_->write_memory(string_buffer, str.data(), total_length);
this->emu_->write_memory(string_buffer, str.data(), total_length);
constexpr std::array<char, element_size> nullbyte{};
this->emu_->write_memory(string_buffer + total_length, nullbyte.data(), nullbyte.size());
constexpr std::array<char, element_size> nullbyte{};
this->emu_->write_memory(string_buffer + total_length, nullbyte.data(), nullbyte.size());
result.Buffer = string_buffer;
result.Length = static_cast<USHORT>(total_length);
result.MaximumLength = static_cast<USHORT>(total_length + element_size);
}
result.Buffer = string_buffer;
result.Length = static_cast<USHORT>(total_length);
result.MaximumLength = static_cast<USHORT>(total_length + element_size);
}
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> make_unicode_string(const std::u16string_view str)
{
const auto unicode_string = this->reserve<UNICODE_STRING<EmulatorTraits<Emu64>>>();
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> make_unicode_string(const std::u16string_view str)
{
const auto unicode_string = this->reserve<UNICODE_STRING<EmulatorTraits<Emu64>>>();
unicode_string.access(
[&](UNICODE_STRING<EmulatorTraits<Emu64>>& unicode_str) { this->make_unicode_string(unicode_str, str); });
unicode_string.access([&](UNICODE_STRING<EmulatorTraits<Emu64>>& unicode_str)
{
this->make_unicode_string(unicode_str, str);
});
return unicode_string;
}
return unicode_string;
}
uint64_t get_base() const
{
return this->address_;
}
uint64_t get_base() const
{
return this->address_;
}
uint64_t get_size() const
{
return this->size_;
}
uint64_t get_size() const
{
return this->size_;
}
uint64_t get_next_address() const
{
return this->active_address_;
}
uint64_t get_next_address() const
{
return this->active_address_;
}
emulator& get_emulator() const
{
return *this->emu_;
}
emulator& get_emulator() const
{
return *this->emu_;
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->address_);
buffer.write(this->size_);
buffer.write(this->active_address_);
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->address_);
buffer.write(this->size_);
buffer.write(this->active_address_);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->address_);
buffer.read(this->size_);
buffer.read(this->active_address_);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->address_);
buffer.read(this->size_);
buffer.read(this->active_address_);
}
void release()
{
if (this->emu_ && this->address_ && this->size_)
{
this->emu_->release_memory(this->address_, this->size_);
this->address_ = 0;
this->size_ = 0;
}
}
void release()
{
if (this->emu_ && this->address_ && this->size_)
{
this->emu_->release_memory(this->address_, this->size_);
this->address_ = 0;
this->size_ = 0;
}
}
private:
emulator* emu_{};
uint64_t address_{};
uint64_t size_{};
uint64_t active_address_{0};
private:
emulator* emu_{};
uint64_t address_{};
uint64_t size_{};
uint64_t active_address_{0};
};
inline std::u16string read_unicode_string(const emulator& emu, const UNICODE_STRING<EmulatorTraits<Emu64>> ucs)
{
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Length) == 0);
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, MaximumLength) == 2);
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Buffer) == 8);
static_assert(sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>) == 16);
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Length) == 0);
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, MaximumLength) == 2);
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Buffer) == 8);
static_assert(sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>) == 16);
std::u16string result{};
result.resize(ucs.Length / 2);
std::u16string result{};
result.resize(ucs.Length / 2);
emu.read_memory(ucs.Buffer, result.data(), ucs.Length);
emu.read_memory(ucs.Buffer, result.data(), ucs.Length);
return result;
return result;
}
inline std::u16string read_unicode_string(const emulator& emu,
const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> uc_string)
{
const auto ucs = uc_string.read();
return read_unicode_string(emu, ucs);
const auto ucs = uc_string.read();
return read_unicode_string(emu, ucs);
}
inline std::u16string read_unicode_string(emulator& emu, const UNICODE_STRING<EmulatorTraits<Emu64>>* uc_string)
{
return read_unicode_string(emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{emu, uc_string});
return read_unicode_string(emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{emu, uc_string});
}

View File

@@ -2,33 +2,33 @@
struct handle_types
{
enum type : uint16_t
{
reserved = 0,
file,
device,
event,
section,
symlink,
directory,
semaphore,
port,
thread,
registry,
mutant,
token,
};
enum type : uint16_t
{
reserved = 0,
file,
device,
event,
section,
symlink,
directory,
semaphore,
port,
thread,
registry,
mutant,
token,
};
};
#pragma pack(push)
#pragma pack(1)
struct handle_value
{
uint64_t id : 32;
uint64_t type : 16;
uint64_t padding : 14;
uint64_t is_system : 1;
uint64_t is_pseudo : 1;
uint64_t id : 32;
uint64_t type : 16;
uint64_t padding : 14;
uint64_t is_system : 1;
uint64_t is_pseudo : 1;
};
#pragma pack(pop)
@@ -37,315 +37,315 @@ static_assert(sizeof(handle_value) == 8);
// TODO: this is a concrete 64bit handle
union handle
{
handle_value value;
uint64_t bits;
std::uint64_t h;
handle_value value;
uint64_t bits;
std::uint64_t h;
};
namespace utils
{
inline void serialize(buffer_serializer& buffer, const handle& h)
{
buffer.write(h.bits);
}
inline void serialize(buffer_serializer& buffer, const handle& h)
{
buffer.write(h.bits);
}
inline void deserialize(buffer_deserializer& buffer, handle& h)
{
buffer.read(h.bits);
}
inline void deserialize(buffer_deserializer& buffer, handle& h)
{
buffer.read(h.bits);
}
}
inline bool operator==(const handle& h1, const handle& h2)
{
return h1.bits == h2.bits;
return h1.bits == h2.bits;
}
inline bool operator==(const handle& h1, const uint64_t& h2)
{
return h1.bits == h2;
return h1.bits == h2;
}
inline handle_value get_handle_value(const uint64_t h)
{
handle hh{};
hh.bits = h;
return hh.value;
handle hh{};
hh.bits = h;
return hh.value;
}
constexpr handle make_handle(const uint32_t id, const handle_types::type type, const bool is_pseudo)
{
handle_value value{};
handle_value value{};
value.padding = 0;
value.id = id;
value.type = type;
value.is_system = false;
value.is_pseudo = is_pseudo;
value.padding = 0;
value.id = id;
value.type = type;
value.is_system = false;
value.is_pseudo = is_pseudo;
return {value};
return {value};
}
constexpr handle make_handle(const uint64_t value)
{
handle h{};
h.bits = value;
return h;
handle h{};
h.bits = value;
return h;
}
constexpr handle make_pseudo_handle(const uint32_t id, const handle_types::type type)
{
return make_handle(id, type, true);
return make_handle(id, type, true);
}
namespace handle_detail
{
template <typename, typename = void>
struct has_deleter_function : std::false_type
{
};
template <typename, typename = void>
struct has_deleter_function : std::false_type
{
};
template <typename T>
struct has_deleter_function<T, std::void_t<decltype(T::deleter(std::declval<T&>()))>>
: std::is_same<decltype(T::deleter(std::declval<T&>())), bool>
{
};
template <typename T>
struct has_deleter_function<T, std::void_t<decltype(T::deleter(std::declval<T&>()))>>
: std::is_same<decltype(T::deleter(std::declval<T&>())), bool>
{
};
}
struct generic_handle_store
{
virtual ~generic_handle_store() = default;
virtual bool erase(const handle h) = 0;
virtual ~generic_handle_store() = default;
virtual bool erase(const handle h) = 0;
};
template <handle_types::type Type, typename T, uint32_t IndexShift = 0>
requires(utils::Serializable<T>)
requires(utils::Serializable<T>)
class handle_store : public generic_handle_store
{
public:
using index_type = uint32_t;
using value_map = std::map<index_type, T>;
public:
using index_type = uint32_t;
using value_map = std::map<index_type, T>;
bool block_mutation(bool blocked)
{
std::swap(this->block_mutation_, blocked);
return blocked;
}
bool block_mutation(bool blocked)
{
std::swap(this->block_mutation_, blocked);
return blocked;
}
handle store(T value)
{
if (this->block_mutation_)
{
throw std::runtime_error("Mutation of handle store is blocked!");
}
handle store(T value)
{
if (this->block_mutation_)
{
throw std::runtime_error("Mutation of handle store is blocked!");
}
auto index = this->find_free_index();
this->store_.emplace(index, std::move(value));
auto index = this->find_free_index();
this->store_.emplace(index, std::move(value));
return make_handle(index);
}
return make_handle(index);
}
handle make_handle(const index_type index) const
{
handle h{};
h.bits = 0;
h.value.is_pseudo = false;
h.value.type = Type;
h.value.id = index << IndexShift;
handle make_handle(const index_type index) const
{
handle h{};
h.bits = 0;
h.value.is_pseudo = false;
h.value.type = Type;
h.value.id = index << IndexShift;
return h;
}
return h;
}
T* get_by_index(const uint32_t index)
{
return this->get(this->make_handle(index));
}
T* get_by_index(const uint32_t index)
{
return this->get(this->make_handle(index));
}
T* get(const handle_value h)
{
const auto entry = this->get_iterator(h);
if (entry == this->store_.end())
{
return nullptr;
}
T* get(const handle_value h)
{
const auto entry = this->get_iterator(h);
if (entry == this->store_.end())
{
return nullptr;
}
return &entry->second;
}
return &entry->second;
}
T* get(const handle h)
{
return this->get(h.value);
}
T* get(const handle h)
{
return this->get(h.value);
}
T* get(const uint64_t h)
{
handle hh{};
hh.bits = h;
T* get(const uint64_t h)
{
handle hh{};
hh.bits = h;
return this->get(hh);
}
return this->get(hh);
}
size_t size() const
{
return this->store_.size();
}
size_t size() const
{
return this->store_.size();
}
bool erase(const typename value_map::iterator& entry)
{
if (this->block_mutation_)
{
throw std::runtime_error("Mutation of handle store is blocked!");
}
bool erase(const typename value_map::iterator& entry)
{
if (this->block_mutation_)
{
throw std::runtime_error("Mutation of handle store is blocked!");
}
if (entry == this->store_.end())
{
return false;
}
if (entry == this->store_.end())
{
return false;
}
if constexpr (handle_detail::has_deleter_function<T>())
{
if (!T::deleter(entry->second))
{
return false;
}
}
if constexpr (handle_detail::has_deleter_function<T>())
{
if (!T::deleter(entry->second))
{
return false;
}
}
this->store_.erase(entry);
return true;
}
this->store_.erase(entry);
return true;
}
bool erase(const handle_value h)
{
const auto entry = this->get_iterator(h);
return this->erase(entry);
}
bool erase(const handle_value h)
{
const auto entry = this->get_iterator(h);
return this->erase(entry);
}
bool erase(const handle h) override
{
return this->erase(h.value);
}
bool erase(const handle h) override
{
return this->erase(h.value);
}
bool erase(const uint64_t h)
{
handle hh{};
hh.bits = h;
bool erase(const uint64_t h)
{
handle hh{};
hh.bits = h;
return this->erase(hh);
}
return this->erase(hh);
}
bool erase(const T& value)
{
const auto entry = this->find(value);
return this->erase(entry);
}
bool erase(const T& value)
{
const auto entry = this->find(value);
return this->erase(entry);
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->block_mutation_);
buffer.write_map(this->store_);
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->block_mutation_);
buffer.write_map(this->store_);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->block_mutation_);
buffer.read_map(this->store_);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->block_mutation_);
buffer.read_map(this->store_);
}
typename value_map::iterator find(const T& value)
{
auto i = this->store_.begin();
for (; i != this->store_.end(); ++i)
{
if (&i->second == &value)
{
break;
}
}
typename value_map::iterator find(const T& value)
{
auto i = this->store_.begin();
for (; i != this->store_.end(); ++i)
{
if (&i->second == &value)
{
break;
}
}
return i;
}
return i;
}
typename value_map::const_iterator find(const T& value) const
{
auto i = this->store_.begin();
for (; i != this->store_.end(); ++i)
{
if (&i->second == &value)
{
break;
}
}
typename value_map::const_iterator find(const T& value) const
{
auto i = this->store_.begin();
for (; i != this->store_.end(); ++i)
{
if (&i->second == &value)
{
break;
}
}
return i;
}
return i;
}
handle find_handle(const T& value) const
{
const auto entry = this->find(value);
if (entry == this->end())
{
return {};
}
handle find_handle(const T& value) const
{
const auto entry = this->find(value);
if (entry == this->end())
{
return {};
}
return this->make_handle(entry->first);
}
return this->make_handle(entry->first);
}
handle find_handle(const T* value) const
{
if (!value)
{
return {};
}
handle find_handle(const T* value) const
{
if (!value)
{
return {};
}
return this->find_handle(*value);
}
return this->find_handle(*value);
}
typename value_map::iterator begin()
{
return this->store_.begin();
}
typename value_map::iterator begin()
{
return this->store_.begin();
}
typename value_map::const_iterator begin() const
{
return this->store_.begin();
}
typename value_map::const_iterator begin() const
{
return this->store_.begin();
}
typename value_map::iterator end()
{
return this->store_.end();
}
typename value_map::iterator end()
{
return this->store_.end();
}
typename value_map::const_iterator end() const
{
return this->store_.end();
}
typename value_map::const_iterator end() const
{
return this->store_.end();
}
private:
typename value_map::iterator get_iterator(const handle_value h)
{
if (h.type != Type || h.is_pseudo)
{
return this->store_.end();
}
private:
typename value_map::iterator get_iterator(const handle_value h)
{
if (h.type != Type || h.is_pseudo)
{
return this->store_.end();
}
return this->store_.find(static_cast<uint32_t>(h.id) >> IndexShift);
}
return this->store_.find(static_cast<uint32_t>(h.id) >> IndexShift);
}
uint32_t find_free_index()
{
uint32_t index = 1;
for (; index > 0; ++index)
{
if (!this->store_.contains(index))
{
break;
}
}
uint32_t find_free_index()
{
uint32_t index = 1;
for (; index > 0; ++index)
{
if (!this->store_.contains(index))
{
break;
}
}
return index;
}
return index;
}
bool block_mutation_{false};
value_map store_{};
bool block_mutation_{false};
value_map store_{};
};
constexpr auto KNOWN_DLLS_DIRECTORY = make_pseudo_handle(0x1, handle_types::directory);

View File

@@ -3,30 +3,27 @@
namespace
{
struct dummy_device : stateless_device
{
NTSTATUS io_control(windows_emulator&, const io_device_context&) override
{
return STATUS_SUCCESS;
}
};
struct dummy_device : stateless_device
{
NTSTATUS io_control(windows_emulator&, const io_device_context&) override
{
return STATUS_SUCCESS;
}
};
}
std::unique_ptr<io_device> create_device(const std::u16string_view device)
{
if (device == u"CNG"
|| device == u"KsecDD"
|| device == u"PcwDrv"
|| device == u"DeviceApi\\CMApi"
|| device == u"ConDrv\\Server")
{
return std::make_unique<dummy_device>();
}
if (device == u"CNG" || device == u"KsecDD" || device == u"PcwDrv" || device == u"DeviceApi\\CMApi" ||
device == u"ConDrv\\Server")
{
return std::make_unique<dummy_device>();
}
if (device == u"Afd\\Endpoint")
{
return create_afd_endpoint();
}
if (device == u"Afd\\Endpoint")
{
return create_afd_endpoint();
}
throw std::runtime_error("Unsupported device: " + u16_to_u8(device));
throw std::runtime_error("Unsupported device: " + u16_to_u8(device));
}

View File

@@ -12,190 +12,188 @@ struct process_context;
struct io_device_context
{
handle event{};
emulator_pointer /*PIO_APC_ROUTINE*/ apc_routine{};
emulator_pointer apc_context{};
emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block;
ULONG io_control_code{};
emulator_pointer input_buffer{};
ULONG input_buffer_length{};
emulator_pointer output_buffer{};
ULONG output_buffer_length{};
handle event{};
emulator_pointer /*PIO_APC_ROUTINE*/ apc_routine{};
emulator_pointer apc_context{};
emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block;
ULONG io_control_code{};
emulator_pointer input_buffer{};
ULONG input_buffer_length{};
emulator_pointer output_buffer{};
ULONG output_buffer_length{};
io_device_context(x64_emulator& emu)
: io_status_block(emu)
{
}
io_device_context(x64_emulator& emu)
: io_status_block(emu)
{
}
io_device_context(utils::buffer_deserializer& buffer)
: io_device_context(buffer.read<x64_emulator_wrapper>().get())
{
}
io_device_context(utils::buffer_deserializer& buffer)
: io_device_context(buffer.read<x64_emulator_wrapper>().get())
{
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(event);
buffer.write(apc_routine);
buffer.write(apc_context);
buffer.write(io_status_block);
buffer.write(io_control_code);
buffer.write(input_buffer);
buffer.write(input_buffer_length);
buffer.write(output_buffer);
buffer.write(output_buffer_length);
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(event);
buffer.write(apc_routine);
buffer.write(apc_context);
buffer.write(io_status_block);
buffer.write(io_control_code);
buffer.write(input_buffer);
buffer.write(input_buffer_length);
buffer.write(output_buffer);
buffer.write(output_buffer_length);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(event);
buffer.read(apc_routine);
buffer.read(apc_context);
buffer.read(io_status_block);
buffer.read(io_control_code);
buffer.read(input_buffer);
buffer.read(input_buffer_length);
buffer.read(output_buffer);
buffer.read(output_buffer_length);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(event);
buffer.read(apc_routine);
buffer.read(apc_context);
buffer.read(io_status_block);
buffer.read(io_control_code);
buffer.read(input_buffer);
buffer.read(input_buffer_length);
buffer.read(output_buffer);
buffer.read(output_buffer_length);
}
};
struct io_device_creation_data
{
uint64_t buffer;
uint32_t length;
uint64_t buffer;
uint32_t length;
};
inline void write_io_status(const emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block,
const NTSTATUS status)
{
if (io_status_block)
{
io_status_block.access([&](IO_STATUS_BLOCK<EmulatorTraits<Emu64>>& status_block)
{
status_block.Status = status;
});
}
if (io_status_block)
{
io_status_block.access(
[&](IO_STATUS_BLOCK<EmulatorTraits<Emu64>>& status_block) { status_block.Status = status; });
}
}
struct io_device
{
io_device() = default;
virtual ~io_device() = default;
io_device() = default;
virtual ~io_device() = default;
io_device(io_device&&) = default;
io_device& operator=(io_device&&) = default;
io_device(io_device&&) = default;
io_device& operator=(io_device&&) = default;
io_device(const io_device&) = delete;
io_device& operator=(const io_device&) = delete;
io_device(const io_device&) = delete;
io_device& operator=(const io_device&) = delete;
virtual NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) = 0;
virtual NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) = 0;
virtual void create(windows_emulator& win_emu, const io_device_creation_data& data)
{
(void)win_emu;
(void)data;
}
virtual void create(windows_emulator& win_emu, const io_device_creation_data& data)
{
(void)win_emu;
(void)data;
}
virtual void work(windows_emulator& win_emu)
{
(void)win_emu;
}
virtual void work(windows_emulator& win_emu)
{
(void)win_emu;
}
virtual void serialize(utils::buffer_serializer& buffer) const = 0;
virtual void deserialize(utils::buffer_deserializer& buffer) = 0;
virtual void serialize(utils::buffer_serializer& buffer) const = 0;
virtual void deserialize(utils::buffer_deserializer& buffer) = 0;
NTSTATUS execute_ioctl(windows_emulator& win_emu, const io_device_context& c)
{
if (c.io_status_block)
{
c.io_status_block.write({});
}
NTSTATUS execute_ioctl(windows_emulator& win_emu, const io_device_context& c)
{
if (c.io_status_block)
{
c.io_status_block.write({});
}
const auto result = this->io_control(win_emu, c);
write_io_status(c.io_status_block, result);
return result;
}
const auto result = this->io_control(win_emu, c);
write_io_status(c.io_status_block, result);
return result;
}
};
struct stateless_device : io_device
{
void create(windows_emulator&, const io_device_creation_data&) final
{
}
void create(windows_emulator&, const io_device_creation_data&) final
{
}
void serialize(utils::buffer_serializer&) const override
{
}
void serialize(utils::buffer_serializer&) const override
{
}
void deserialize(utils::buffer_deserializer&) override
{
}
void deserialize(utils::buffer_deserializer&) override
{
}
};
std::unique_ptr<io_device> create_device(std::u16string_view device);
class io_device_container : public io_device
{
public:
io_device_container() = default;
public:
io_device_container() = default;
io_device_container(std::u16string device, windows_emulator& win_emu, const io_device_creation_data& data)
: device_name_(std::move(device))
{
this->setup();
this->device_->create(win_emu, data);
}
io_device_container(std::u16string device, windows_emulator& win_emu, const io_device_creation_data& data)
: device_name_(std::move(device))
{
this->setup();
this->device_->create(win_emu, data);
}
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) override
{
this->assert_validity();
return this->device_->io_control(win_emu, context);
}
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) override
{
this->assert_validity();
return this->device_->io_control(win_emu, context);
}
void work(windows_emulator& win_emu) override
{
this->assert_validity();
return this->device_->work(win_emu);
}
void work(windows_emulator& win_emu) override
{
this->assert_validity();
return this->device_->work(win_emu);
}
void serialize(utils::buffer_serializer& buffer) const override
{
this->assert_validity();
void serialize(utils::buffer_serializer& buffer) const override
{
this->assert_validity();
buffer.write_string(this->device_name_);
this->device_->serialize(buffer);
}
buffer.write_string(this->device_name_);
this->device_->serialize(buffer);
}
void deserialize(utils::buffer_deserializer& buffer) override
{
buffer.read_string(this->device_name_);
this->setup();
this->device_->deserialize(buffer);
}
void deserialize(utils::buffer_deserializer& buffer) override
{
buffer.read_string(this->device_name_);
this->setup();
this->device_->deserialize(buffer);
}
template <typename T = io_device>
requires(std::is_base_of_v<io_device, T> || std::is_same_v<io_device, T>)
T* get_internal_device()
{
this->assert_validity();
auto* value = this->device_.get();
return dynamic_cast<T*>(value);
}
template <typename T = io_device>
requires(std::is_base_of_v<io_device, T> || std::is_same_v<io_device, T>)
T* get_internal_device()
{
this->assert_validity();
auto* value = this->device_.get();
return dynamic_cast<T*>(value);
}
private:
std::u16string device_name_{};
std::unique_ptr<io_device> device_{};
private:
std::u16string device_name_{};
std::unique_ptr<io_device> device_{};
void setup()
{
this->device_ = create_device(this->device_name_);
}
void setup()
{
this->device_ = create_device(this->device_name_);
}
void assert_validity() const
{
if (!this->device_)
{
throw std::runtime_error("Device not created!");
}
}
void assert_validity() const
{
if (!this->device_)
{
throw std::runtime_error("Device not created!");
}
}
};

View File

@@ -10,216 +10,214 @@ constexpr auto KUSD_BUFFER_SIZE = page_align_up(KUSD_SIZE);
namespace
{
void setup_kusd(KUSER_SHARED_DATA64& kusd, const bool use_relative_time)
{
memset(reinterpret_cast<void*>(&kusd), 0, sizeof(kusd));
void setup_kusd(KUSER_SHARED_DATA64& kusd, const bool use_relative_time)
{
memset(reinterpret_cast<void*>(&kusd), 0, sizeof(kusd));
kusd.TickCountMultiplier = 0x0fa00000;
kusd.InterruptTime.LowPart = 0x17bd9547;
kusd.InterruptTime.High1Time = 0x0000004b;
kusd.InterruptTime.High2Time = 0x0000004b;
kusd.SystemTime.LowPart = 0x7af9da99;
kusd.SystemTime.High1Time = 0x01db27b9;
kusd.SystemTime.High2Time = 0x01db27b9;
kusd.TimeZoneBias.LowPart = 0x3c773000;
kusd.TimeZoneBias.High1Time = -17;
kusd.TimeZoneBias.High2Time = -17;
kusd.TimeZoneId = 0x00000002;
kusd.LargePageMinimum = 0x00200000;
kusd.RNGSeedVersion = 0x0000000000000013;
kusd.TimeZoneBiasStamp = 0x00000004;
kusd.NtBuildNumber = 0x00006c51;
kusd.NtProductType = NtProductWinNt;
kusd.ProductTypeIsValid = 0x01;
kusd.NativeProcessorArchitecture = 0x0009;
kusd.NtMajorVersion = 0x0000000a;
kusd.BootId = 0x0000000b;
kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300;
kusd.SuiteMask = 0x00000110;
kusd.MitigationPolicies.MitigationPolicies = 0x0a;
kusd.MitigationPolicies.NXSupportPolicy = 0x02;
kusd.MitigationPolicies.SEHValidationPolicy = 0x02;
kusd.CyclesPerYield = 0x0064;
kusd.DismountCount = 0x00000006;
kusd.ComPlusPackage = 0x00000001;
kusd.LastSystemRITEventTickCount = 0x01ec1fd3;
kusd.NumberOfPhysicalPages = 0x00bf0958;
kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958;
kusd.TickCount.TickCount.LowPart = 0x001f7f05;
kusd.TickCount.TickCountQuad = 0x00000000001f7f05;
kusd.Cookie = 0x1c3471da;
kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4;
kusd.TimeUpdateLock = 0x0000000002b28586;
kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c;
kusd.BaselineInterruptTimeQpc = 0x0000004b17cd596c;
kusd.QpcSystemTimeIncrement = 0x8000000000000000;
kusd.QpcInterruptTimeIncrement = 0x8000000000000000;
kusd.QpcSystemTimeIncrementShift = 0x01;
kusd.QpcInterruptTimeIncrementShift = 0x01;
kusd.UnparkedProcessorCount = 0x000c;
kusd.TelemetryCoverageRound = 0x00000001;
kusd.LangGenerationCount = 0x00000003;
kusd.InterruptTimeBias = 0x00000015a5d56406;
kusd.QpcBias = 0x000000159530c4af;
kusd.ActiveProcessorCount = 0x0000000c;
kusd.ActiveGroupCount = 0x01;
kusd.QpcData.QpcData = 0x0083;
kusd.QpcData.QpcBypassEnabled = 0x83;
kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff;
kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800;
kusd.XState.EnabledFeatures = 0x000000000000001f;
kusd.XState.EnabledVolatileFeatures = 0x000000000000000f;
kusd.XState.Size = 0x000003c0;
kusd.TickCountMultiplier = 0x0fa00000;
kusd.InterruptTime.LowPart = 0x17bd9547;
kusd.InterruptTime.High1Time = 0x0000004b;
kusd.InterruptTime.High2Time = 0x0000004b;
kusd.SystemTime.LowPart = 0x7af9da99;
kusd.SystemTime.High1Time = 0x01db27b9;
kusd.SystemTime.High2Time = 0x01db27b9;
kusd.TimeZoneBias.LowPart = 0x3c773000;
kusd.TimeZoneBias.High1Time = -17;
kusd.TimeZoneBias.High2Time = -17;
kusd.TimeZoneId = 0x00000002;
kusd.LargePageMinimum = 0x00200000;
kusd.RNGSeedVersion = 0x0000000000000013;
kusd.TimeZoneBiasStamp = 0x00000004;
kusd.NtBuildNumber = 0x00006c51;
kusd.NtProductType = NtProductWinNt;
kusd.ProductTypeIsValid = 0x01;
kusd.NativeProcessorArchitecture = 0x0009;
kusd.NtMajorVersion = 0x0000000a;
kusd.BootId = 0x0000000b;
kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300;
kusd.SuiteMask = 0x00000110;
kusd.MitigationPolicies.MitigationPolicies = 0x0a;
kusd.MitigationPolicies.NXSupportPolicy = 0x02;
kusd.MitigationPolicies.SEHValidationPolicy = 0x02;
kusd.CyclesPerYield = 0x0064;
kusd.DismountCount = 0x00000006;
kusd.ComPlusPackage = 0x00000001;
kusd.LastSystemRITEventTickCount = 0x01ec1fd3;
kusd.NumberOfPhysicalPages = 0x00bf0958;
kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958;
kusd.TickCount.TickCount.LowPart = 0x001f7f05;
kusd.TickCount.TickCountQuad = 0x00000000001f7f05;
kusd.Cookie = 0x1c3471da;
kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4;
kusd.TimeUpdateLock = 0x0000000002b28586;
kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c;
kusd.BaselineInterruptTimeQpc = 0x0000004b17cd596c;
kusd.QpcSystemTimeIncrement = 0x8000000000000000;
kusd.QpcInterruptTimeIncrement = 0x8000000000000000;
kusd.QpcSystemTimeIncrementShift = 0x01;
kusd.QpcInterruptTimeIncrementShift = 0x01;
kusd.UnparkedProcessorCount = 0x000c;
kusd.TelemetryCoverageRound = 0x00000001;
kusd.LangGenerationCount = 0x00000003;
kusd.InterruptTimeBias = 0x00000015a5d56406;
kusd.QpcBias = 0x000000159530c4af;
kusd.ActiveProcessorCount = 0x0000000c;
kusd.ActiveGroupCount = 0x01;
kusd.QpcData.QpcData = 0x0083;
kusd.QpcData.QpcBypassEnabled = 0x83;
kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff;
kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800;
kusd.XState.EnabledFeatures = 0x000000000000001f;
kusd.XState.EnabledVolatileFeatures = 0x000000000000000f;
kusd.XState.Size = 0x000003c0;
if (use_relative_time)
{
kusd.QpcFrequency = 1000;
}
else
{
kusd.QpcFrequency = std::chrono::steady_clock::period::den;
}
if (use_relative_time)
{
kusd.QpcFrequency = 1000;
}
else
{
kusd.QpcFrequency = std::chrono::steady_clock::period::den;
}
constexpr std::wstring_view root_dir{L"C:\\WINDOWS"};
memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2);
constexpr std::wstring_view root_dir{L"C:\\WINDOWS"};
memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2);
kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386;
kusd.ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64;
}
kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386;
kusd.ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64;
}
}
namespace utils
{
inline void serialize(buffer_serializer& buffer, const KUSER_SHARED_DATA64& kusd)
{
static_assert(KUSD_SIZE == sizeof(kusd));
buffer.write(&kusd, KUSD_SIZE);
}
inline void serialize(buffer_serializer& buffer, const KUSER_SHARED_DATA64& kusd)
{
static_assert(KUSD_SIZE == sizeof(kusd));
buffer.write(&kusd, KUSD_SIZE);
}
inline void deserialize(buffer_deserializer& buffer, KUSER_SHARED_DATA64& kusd)
{
buffer.read(&kusd, KUSD_SIZE);
}
inline void deserialize(buffer_deserializer& buffer, KUSER_SHARED_DATA64& kusd)
{
buffer.read(&kusd, KUSD_SIZE);
}
}
kusd_mmio::kusd_mmio(x64_emulator& emu, process_context& process)
: emu_(&emu)
, process_(&process)
: emu_(&emu),
process_(&process)
{
}
kusd_mmio::~kusd_mmio()
{
this->deregister_mmio();
this->deregister_mmio();
}
kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer)
: kusd_mmio(buffer.read<x64_emulator_wrapper>(), buffer.read<process_context_wrapper>())
: kusd_mmio(buffer.read<x64_emulator_wrapper>(), buffer.read<process_context_wrapper>())
{
}
void kusd_mmio::setup(const bool use_relative_time)
{
this->use_relative_time_ = use_relative_time;
this->use_relative_time_ = use_relative_time;
setup_kusd(this->kusd_, use_relative_time);
this->start_time_ = convert_from_ksystem_time(this->kusd_.SystemTime);
setup_kusd(this->kusd_, use_relative_time);
this->start_time_ = convert_from_ksystem_time(this->kusd_.SystemTime);
this->register_mmio();
this->register_mmio();
}
void kusd_mmio::serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->use_relative_time_);
buffer.write(this->kusd_);
buffer.write(this->start_time_);
buffer.write(this->use_relative_time_);
buffer.write(this->kusd_);
buffer.write(this->start_time_);
}
void kusd_mmio::deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->use_relative_time_);
buffer.read(this->kusd_);
buffer.read(this->start_time_);
buffer.read(this->use_relative_time_);
buffer.read(this->kusd_);
buffer.read(this->start_time_);
this->deregister_mmio();
this->register_mmio();
this->deregister_mmio();
this->register_mmio();
}
uint64_t kusd_mmio::read(const uint64_t addr, const size_t size)
{
uint64_t result{};
uint64_t result{};
this->update();
this->update();
if (addr >= KUSD_SIZE)
{
return result;
}
if (addr >= KUSD_SIZE)
{
return result;
}
const auto end = addr + size;
const auto valid_end = std::min(end, static_cast<uint64_t>(KUSD_SIZE));
const auto real_size = valid_end - addr;
const auto end = addr + size;
const auto valid_end = std::min(end, static_cast<uint64_t>(KUSD_SIZE));
const auto real_size = valid_end - addr;
if (real_size > sizeof(result))
{
return result;
}
if (real_size > sizeof(result))
{
return result;
}
const auto* kusd_buffer = reinterpret_cast<uint8_t*>(&this->kusd_);
memcpy(&result, kusd_buffer + addr, real_size);
const auto* kusd_buffer = reinterpret_cast<uint8_t*>(&this->kusd_);
memcpy(&result, kusd_buffer + addr, real_size);
return result;
return result;
}
uint64_t kusd_mmio::address()
{
return KUSD_ADDRESS;
return KUSD_ADDRESS;
}
void kusd_mmio::update()
{
auto time = this->start_time_;
auto time = this->start_time_;
if (this->use_relative_time_)
{
const auto passed_time = this->process_->executed_instructions;
const auto clock_frequency = static_cast<uint64_t>(this->kusd_.QpcFrequency);
if (this->use_relative_time_)
{
const auto passed_time = this->process_->executed_instructions;
const auto clock_frequency = static_cast<uint64_t>(this->kusd_.QpcFrequency);
using duration = std::chrono::system_clock::duration;
time += duration(passed_time * duration::period::den / clock_frequency);
}
else
{
time = std::chrono::system_clock::now();
}
using duration = std::chrono::system_clock::duration;
time += duration(passed_time * duration::period::den / clock_frequency);
}
else
{
time = std::chrono::system_clock::now();
}
convert_to_ksystem_time(&this->kusd_.SystemTime, time);
convert_to_ksystem_time(&this->kusd_.SystemTime, time);
}
void kusd_mmio::register_mmio()
{
if (this->registered_)
{
return;
}
if (this->registered_)
{
return;
}
this->registered_ = true;
this->registered_ = true;
this->emu_->allocate_mmio(KUSD_ADDRESS, KUSD_BUFFER_SIZE,
[this](const uint64_t addr, const size_t size)
{
return this->read(addr, size);
}, [](const uint64_t, const size_t, const uint64_t)
{
// Writing not supported!
});
this->emu_->allocate_mmio(
KUSD_ADDRESS, KUSD_BUFFER_SIZE,
[this](const uint64_t addr, const size_t size) { return this->read(addr, size); },
[](const uint64_t, const size_t, const uint64_t) {
// Writing not supported!
});
}
void kusd_mmio::deregister_mmio()
{
if (this->registered_)
{
this->registered_ = false;
this->emu_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE);
}
if (this->registered_)
{
this->registered_ = false;
this->emu_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE);
}
}

View File

@@ -10,48 +10,48 @@ class windows_emulator;
class kusd_mmio
{
public:
kusd_mmio(x64_emulator& emu, process_context& process);
~kusd_mmio();
public:
kusd_mmio(x64_emulator& emu, process_context& process);
~kusd_mmio();
kusd_mmio(utils::buffer_deserializer& buffer);
kusd_mmio(utils::buffer_deserializer& buffer);
kusd_mmio(kusd_mmio&&) = delete;
kusd_mmio(const kusd_mmio&) = delete;
kusd_mmio& operator=(kusd_mmio&& obj) = delete;
kusd_mmio& operator=(const kusd_mmio&) = delete;
kusd_mmio(kusd_mmio&&) = delete;
kusd_mmio(const kusd_mmio&) = delete;
kusd_mmio& operator=(kusd_mmio&& obj) = delete;
kusd_mmio& operator=(const kusd_mmio&) = delete;
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
KUSER_SHARED_DATA64& get()
{
return this->kusd_;
}
KUSER_SHARED_DATA64& get()
{
return this->kusd_;
}
const KUSER_SHARED_DATA64& get() const
{
return this->kusd_;
}
const KUSER_SHARED_DATA64& get() const
{
return this->kusd_;
}
static uint64_t address();
static uint64_t address();
void setup(bool use_relative_time);
void setup(bool use_relative_time);
private:
x64_emulator* emu_{};
process_context* process_{};
private:
x64_emulator* emu_{};
process_context* process_{};
bool registered_{};
bool use_relative_time_{};
bool registered_{};
bool use_relative_time_{};
KUSER_SHARED_DATA64 kusd_{};
std::chrono::system_clock::time_point start_time_{};
KUSER_SHARED_DATA64 kusd_{};
std::chrono::system_clock::time_point start_time_{};
uint64_t read(uint64_t addr, size_t size);
uint64_t read(uint64_t addr, size_t size);
void update();
void update();
void register_mmio();
void deregister_mmio();
void register_mmio();
void deregister_mmio();
};

View File

@@ -7,130 +7,141 @@ namespace
{
#ifdef _WIN32
#define COLOR(win, posix) win
using color_type = WORD;
using color_type = WORD;
#else
#define COLOR(win, posix) posix
using color_type = const char*;
using color_type = const char*;
#endif
color_type get_reset_color()
{
return COLOR(7, "\033[0m");
}
color_type get_reset_color()
{
return COLOR(7, "\033[0m");
}
color_type get_color_type(const color c)
{
using enum color;
color_type get_color_type(const color c)
{
using enum color;
switch (c)
{
case black: return COLOR(0x8, "\033[0;90m");
case red: return COLOR(0xC, "\033[0;91m");
case green: return COLOR(0xA, "\033[0;92m");
case yellow: return COLOR(0xE, "\033[0;93m");
case blue: return COLOR(0x9, "\033[0;94m");
case cyan: return COLOR(0xB, "\033[0;96m");
case pink: return COLOR(0xD, "\033[0;95m");
case white: return COLOR(0xF, "\033[0;97m");
case dark_gray: return COLOR(0x8, "\033[0;97m");
case gray:
default: return get_reset_color();
}
}
switch (c)
{
case black:
return COLOR(0x8, "\033[0;90m");
case red:
return COLOR(0xC, "\033[0;91m");
case green:
return COLOR(0xA, "\033[0;92m");
case yellow:
return COLOR(0xE, "\033[0;93m");
case blue:
return COLOR(0x9, "\033[0;94m");
case cyan:
return COLOR(0xB, "\033[0;96m");
case pink:
return COLOR(0xD, "\033[0;95m");
case white:
return COLOR(0xF, "\033[0;97m");
case dark_gray:
return COLOR(0x8, "\033[0;97m");
case gray:
default:
return get_reset_color();
}
}
#ifdef _WIN32
HANDLE get_console_handle()
{
return GetStdHandle(STD_OUTPUT_HANDLE);
}
HANDLE get_console_handle()
{
return GetStdHandle(STD_OUTPUT_HANDLE);
}
#endif
void set_color(const color_type color)
{
void set_color(const color_type color)
{
#ifdef _WIN32
SetConsoleTextAttribute(get_console_handle(), color);
SetConsoleTextAttribute(get_console_handle(), color);
#else
printf("%s", color);
printf("%s", color);
#endif
}
}
void reset_color()
{
(void)fflush(stdout);
set_color(get_reset_color());
(void)fflush(stdout);
}
void reset_color()
{
(void)fflush(stdout);
set_color(get_reset_color());
(void)fflush(stdout);
}
std::string_view format(va_list* ap, const char* message)
{
thread_local char buffer[0x1000];
std::string_view format(va_list* ap, const char* message)
{
thread_local char buffer[0x1000];
#ifdef _WIN32
const int count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap);
const int count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap);
#else
const int count = vsnprintf(buffer, sizeof(buffer), message, *ap);
const int count = vsnprintf(buffer, sizeof(buffer), message, *ap);
#endif
if (count < 0) return {};
return {buffer, static_cast<size_t>(count)};
}
if (count < 0)
return {};
return {buffer, static_cast<size_t>(count)};
}
#define format_to_string(msg, str)\
va_list ap;\
va_start(ap, msg);\
const auto str = format(&ap, msg);\
va_end(ap);
#define format_to_string(msg, str) \
va_list ap; \
va_start(ap, msg); \
const auto str = format(&ap, msg); \
va_end(ap);
void print_colored(const std::string_view& line, const color_type base_color)
{
const auto _ = utils::finally(&reset_color);
set_color(base_color);
(void)fwrite(line.data(), 1, line.size(), stdout);
}
void print_colored(const std::string_view& line, const color_type base_color)
{
const auto _ = utils::finally(&reset_color);
set_color(base_color);
(void)fwrite(line.data(), 1, line.size(), stdout);
}
}
void logger::print(const color c, const std::string_view message) const
{
if (this->disable_output_)
{
return;
}
if (this->disable_output_)
{
return;
}
print_colored(message, get_color_type(c));
print_colored(message, get_color_type(c));
}
void logger::print(const color c, const char* message, ...) const
{
format_to_string(message, data);
this->print(c, data);
format_to_string(message, data);
this->print(c, data);
}
void logger::info(const char* message, ...) const
{
format_to_string(message, data);
this->print(color::cyan, data);
format_to_string(message, data);
this->print(color::cyan, data);
}
void logger::warn(const char* message, ...) const
{
format_to_string(message, data);
this->print(color::yellow, data);
format_to_string(message, data);
this->print(color::yellow, data);
}
void logger::error(const char* message, ...) const
{
format_to_string(message, data);
this->print(color::red, data);
format_to_string(message, data);
this->print(color::red, data);
}
void logger::success(const char* message, ...) const
{
format_to_string(message, data);
this->print(color::green, data);
format_to_string(message, data);
this->print(color::green, data);
}
void logger::log(const char* message, ...) const
{
format_to_string(message, data);
this->print(color::gray, data);
format_to_string(message, data);
this->print(color::gray, data);
}

View File

@@ -3,44 +3,44 @@
#ifdef OS_WINDOWS
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos)
#else
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos) __attribute__((format( printf, fmt_pos, var_pos)))
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos) __attribute__((format(printf, fmt_pos, var_pos)))
#endif
enum class color
{
black,
red,
green,
yellow,
blue,
cyan,
pink,
white,
gray,
dark_gray,
black,
red,
green,
yellow,
blue,
cyan,
pink,
white,
gray,
dark_gray,
};
class logger
{
public:
void print(color c, std::string_view message) const;
void print(color c, const char* message, ...) const FORMAT_ATTRIBUTE(3, 4);
void info(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void warn(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void error(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void success(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void log(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
public:
void print(color c, std::string_view message) const;
void print(color c, const char* message, ...) const FORMAT_ATTRIBUTE(3, 4);
void info(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void warn(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void error(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void success(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void log(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void disable_output(const bool value)
{
this->disable_output_ = value;
}
void disable_output(const bool value)
{
this->disable_output_ = value;
}
bool is_output_disabled() const
{
return this->disable_output_;
}
bool is_output_disabled() const
{
return this->disable_output_;
}
private:
bool disable_output_{false};
private:
bool disable_output_{false};
};

View File

@@ -5,69 +5,69 @@
inline std::string get_permission_string(const memory_permission permission)
{
const bool has_exec = (permission & memory_permission::exec) != memory_permission::none;
const bool has_read = (permission & memory_permission::read) != memory_permission::none;
const bool has_write = (permission & memory_permission::write) != memory_permission::none;
const bool has_exec = (permission & memory_permission::exec) != memory_permission::none;
const bool has_read = (permission & memory_permission::read) != memory_permission::none;
const bool has_write = (permission & memory_permission::write) != memory_permission::none;
std::string res = {};
res.reserve(3);
std::string res = {};
res.reserve(3);
res.push_back(has_read ? 'r' : '-');
res.push_back(has_write ? 'w' : '-');
res.push_back(has_exec ? 'x' : '-');
res.push_back(has_read ? 'r' : '-');
res.push_back(has_write ? 'w' : '-');
res.push_back(has_exec ? 'x' : '-');
return res;
return res;
}
inline memory_permission map_nt_to_emulator_protection(uint32_t nt_protection)
{
nt_protection &= ~static_cast<uint32_t>(PAGE_GUARD); // TODO: Implement that
nt_protection &= ~static_cast<uint32_t>(PAGE_GUARD); // TODO: Implement that
switch (nt_protection)
{
case PAGE_NOACCESS:
return memory_permission::none;
case PAGE_READONLY:
return memory_permission::read;
case PAGE_READWRITE:
case PAGE_WRITECOPY:
return memory_permission::read | memory_permission::write;
case PAGE_EXECUTE:
case PAGE_EXECUTE_READ:
return memory_permission::read | memory_permission::exec;
case PAGE_EXECUTE_READWRITE:
return memory_permission::all;
case PAGE_EXECUTE_WRITECOPY:
default:
throw std::runtime_error("Failed to map protection");
}
switch (nt_protection)
{
case PAGE_NOACCESS:
return memory_permission::none;
case PAGE_READONLY:
return memory_permission::read;
case PAGE_READWRITE:
case PAGE_WRITECOPY:
return memory_permission::read | memory_permission::write;
case PAGE_EXECUTE:
case PAGE_EXECUTE_READ:
return memory_permission::read | memory_permission::exec;
case PAGE_EXECUTE_READWRITE:
return memory_permission::all;
case PAGE_EXECUTE_WRITECOPY:
default:
throw std::runtime_error("Failed to map protection");
}
}
inline uint32_t map_emulator_to_nt_protection(const memory_permission permission)
{
const bool has_exec = (permission & memory_permission::exec) != memory_permission::none;
const bool has_read = (permission & memory_permission::read) != memory_permission::none;
const bool has_write = (permission & memory_permission::write) != memory_permission::none;
const bool has_exec = (permission & memory_permission::exec) != memory_permission::none;
const bool has_read = (permission & memory_permission::read) != memory_permission::none;
const bool has_write = (permission & memory_permission::write) != memory_permission::none;
if (!has_read)
{
return PAGE_NOACCESS;
}
if (!has_read)
{
return PAGE_NOACCESS;
}
if (has_exec && has_write)
{
return PAGE_EXECUTE_READWRITE;
}
if (has_exec && has_write)
{
return PAGE_EXECUTE_READWRITE;
}
if (has_exec)
{
return PAGE_EXECUTE_READ;
}
if (has_exec)
{
return PAGE_EXECUTE_READ;
}
if (has_write)
{
return PAGE_READWRITE;
}
if (has_write)
{
return PAGE_READWRITE;
}
return PAGE_READONLY;
return PAGE_READONLY;
}

View File

@@ -3,10 +3,10 @@
struct exported_symbol
{
std::string name{};
uint64_t ordinal{};
uint64_t rva{};
uint64_t address{};
std::string name{};
uint64_t ordinal{};
uint64_t rva{};
uint64_t address{};
};
using exported_symbols = std::vector<exported_symbol>;
@@ -14,39 +14,39 @@ using address_name_mapping = std::map<uint64_t, std::string>;
struct mapped_section
{
std::string name{};
basic_memory_region region{};
std::string name{};
basic_memory_region region{};
};
struct mapped_module
{
std::string name{};
std::filesystem::path path{};
std::string name{};
std::filesystem::path path{};
uint64_t image_base{};
uint64_t size_of_image{};
uint64_t entry_point{};
uint64_t image_base{};
uint64_t size_of_image{};
uint64_t entry_point{};
exported_symbols exports{};
address_name_mapping address_names{};
exported_symbols exports{};
address_name_mapping address_names{};
std::vector<mapped_section> sections{};
std::vector<mapped_section> sections{};
bool is_within(const uint64_t address) const
{
return address >= this->image_base && address < (this->image_base + this->size_of_image);
}
bool is_within(const uint64_t address) const
{
return address >= this->image_base && address < (this->image_base + this->size_of_image);
}
uint64_t find_export(const std::string_view export_name) const
{
for (auto& symbol : this->exports)
{
if (symbol.name == export_name)
{
return symbol.address;
}
}
uint64_t find_export(const std::string_view export_name) const
{
for (auto& symbol : this->exports)
{
if (symbol.name == export_name)
{
return symbol.address;
}
}
return 0;
}
return 0;
}
};

View File

@@ -5,124 +5,124 @@
namespace
{
std::filesystem::path canonicalize_module_path(const std::filesystem::path& file)
{
constexpr std::u16string_view nt_prefix = u"\\??\\";
const auto wide_file = file.u16string();
std::filesystem::path canonicalize_module_path(const std::filesystem::path& file)
{
constexpr std::u16string_view nt_prefix = u"\\??\\";
const auto wide_file = file.u16string();
if (!wide_file.starts_with(nt_prefix))
{
return canonical(absolute(file));
}
if (!wide_file.starts_with(nt_prefix))
{
return canonical(absolute(file));
}
return canonicalize_module_path(wide_file.substr(nt_prefix.size()));
}
return canonicalize_module_path(wide_file.substr(nt_prefix.size()));
}
}
namespace utils
{
static void serialize(buffer_serializer& buffer, const exported_symbol& sym)
{
buffer.write(sym.name);
buffer.write(sym.ordinal);
buffer.write(sym.rva);
buffer.write(sym.address);
}
static void serialize(buffer_serializer& buffer, const exported_symbol& sym)
{
buffer.write(sym.name);
buffer.write(sym.ordinal);
buffer.write(sym.rva);
buffer.write(sym.address);
}
static void deserialize(buffer_deserializer& buffer, exported_symbol& sym)
{
buffer.read(sym.name);
buffer.read(sym.ordinal);
buffer.read(sym.rva);
buffer.read(sym.address);
}
static void deserialize(buffer_deserializer& buffer, exported_symbol& sym)
{
buffer.read(sym.name);
buffer.read(sym.ordinal);
buffer.read(sym.rva);
buffer.read(sym.address);
}
static void serialize(buffer_serializer& buffer, const mapped_module& mod)
{
buffer.write_string(mod.name);
buffer.write(mod.path.u16string());
static void serialize(buffer_serializer& buffer, const mapped_module& mod)
{
buffer.write_string(mod.name);
buffer.write(mod.path.u16string());
buffer.write(mod.image_base);
buffer.write(mod.size_of_image);
buffer.write(mod.entry_point);
buffer.write(mod.image_base);
buffer.write(mod.size_of_image);
buffer.write(mod.entry_point);
buffer.write_vector(mod.exports);
buffer.write_map(mod.address_names);
}
buffer.write_vector(mod.exports);
buffer.write_map(mod.address_names);
}
static void deserialize(buffer_deserializer& buffer, mapped_module& mod)
{
mod.name = buffer.read_string();
mod.path = buffer.read_string<std::u16string::value_type>();
static void deserialize(buffer_deserializer& buffer, mapped_module& mod)
{
mod.name = buffer.read_string();
mod.path = buffer.read_string<std::u16string::value_type>();
buffer.read(mod.image_base);
buffer.read(mod.size_of_image);
buffer.read(mod.entry_point);
buffer.read(mod.image_base);
buffer.read(mod.size_of_image);
buffer.read(mod.entry_point);
buffer.read_vector(mod.exports);
buffer.read_map(mod.address_names);
}
buffer.read_vector(mod.exports);
buffer.read_map(mod.address_names);
}
}
module_manager::module_manager(emulator& emu)
: emu_(&emu)
: emu_(&emu)
{
}
mapped_module* module_manager::map_module(const std::filesystem::path& file, logger& logger)
{
auto canonical_file = canonicalize_module_path(file);
auto canonical_file = canonicalize_module_path(file);
for (auto& mod : this->modules_)
{
if (mod.second.path == canonical_file)
{
return &mod.second;
}
}
for (auto& mod : this->modules_)
{
if (mod.second.path == canonical_file)
{
return &mod.second;
}
}
try
{
auto mod = map_module_from_file(*this->emu_, std::move(canonical_file));
try
{
auto mod = map_module_from_file(*this->emu_, std::move(canonical_file));
logger.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base);
logger.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base);
const auto image_base = mod.image_base;
const auto entry = this->modules_.try_emplace(image_base, std::move(mod));
return &entry.first->second;
}
catch (const std::exception& e)
{
logger.error("Failed to map %s: %s\n", file.generic_string().c_str(), e.what());
return nullptr;
}
catch (...)
{
logger.error("Failed to map %s: Unknown error\n", file.generic_string().c_str());
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;
}
catch (const std::exception& e)
{
logger.error("Failed to map %s: %s\n", file.generic_string().c_str(), e.what());
return nullptr;
}
catch (...)
{
logger.error("Failed to map %s: Unknown error\n", file.generic_string().c_str());
return nullptr;
}
}
void module_manager::serialize(utils::buffer_serializer& buffer) const
{
buffer.write_map(this->modules_);
buffer.write_map(this->modules_);
}
void module_manager::deserialize(utils::buffer_deserializer& buffer)
{
buffer.read_map(this->modules_);
buffer.read_map(this->modules_);
}
bool module_manager::unmap(const uint64_t address)
{
const auto mod = this->modules_.find(address);
if (mod == this->modules_.end())
{
return false;
}
const auto mod = this->modules_.find(address);
if (mod == this->modules_.end())
{
return false;
}
unmap_module(*this->emu_, mod->second);
this->modules_.erase(mod);
unmap_module(*this->emu_, mod->second);
this->modules_.erase(mod);
return true;
return true;
}

View File

@@ -6,58 +6,58 @@ class logger;
class module_manager
{
public:
module_manager(emulator& emu);
public:
module_manager(emulator& emu);
mapped_module* map_module(const std::filesystem::path& file, logger& logger);
mapped_module* map_module(const std::filesystem::path& file, logger& logger);
mapped_module* find_by_address(const uint64_t address)
{
const auto entry = this->get_module(address);
if (entry != this->modules_.end())
{
return &entry->second;
}
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;
}
return nullptr;
}
const char* find_name(const uint64_t address)
{
const auto* mod = this->find_by_address(address);
if (!mod)
{
return "<N/A>";
}
const char* find_name(const uint64_t address)
{
const auto* mod = this->find_by_address(address);
if (!mod)
{
return "<N/A>";
}
return mod->name.c_str();
}
return mod->name.c_str();
}
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
bool unmap(const uint64_t address);
bool unmap(const uint64_t address);
private:
emulator* emu_{};
private:
emulator* emu_{};
using module_map = std::map<uint64_t, mapped_module>;
module_map modules_{};
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();
}
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();
}
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;
}
std::advance(upper_bound, -1);
return upper_bound;
}
};

View File

@@ -7,265 +7,259 @@
namespace
{
uint64_t get_first_section_offset(const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
{
const uint8_t* nt_headers_addr = reinterpret_cast<const uint8_t*>(&nt_headers);
size_t optional_header_offset = reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast<
uintptr_t>(&nt_headers);
size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader;
const uint8_t* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size;
uint64_t get_first_section_offset(const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
{
const uint8_t* nt_headers_addr = reinterpret_cast<const uint8_t*>(&nt_headers);
size_t optional_header_offset =
reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast<uintptr_t>(&nt_headers);
size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader;
const uint8_t* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size;
const auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr);
const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers);
return nt_headers_offset + (first_section_absolute - absolute_base);
}
const auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr);
const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers);
return nt_headers_offset + (first_section_absolute - absolute_base);
}
std::vector<uint8_t> read_mapped_memory(const emulator& emu, const mapped_module& binary)
{
std::vector<uint8_t> memory{};
memory.resize(binary.size_of_image);
emu.read_memory(binary.image_base, memory.data(), memory.size());
std::vector<uint8_t> read_mapped_memory(const emulator& emu, const mapped_module& binary)
{
std::vector<uint8_t> memory{};
memory.resize(binary.size_of_image);
emu.read_memory(binary.image_base, memory.data(), memory.size());
return memory;
}
return memory;
}
void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
const PEOptionalHeader_t<std::uint64_t>& optional_header)
{
auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
{
return;
}
void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
const PEOptionalHeader_t<std::uint64_t>& optional_header)
{
auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
{
return;
}
const auto export_directory = buffer.as<IMAGE_EXPORT_DIRECTORY>(export_directory_entry.
VirtualAddress).get();
const auto export_directory = buffer.as<IMAGE_EXPORT_DIRECTORY>(export_directory_entry.VirtualAddress).get();
const auto names_count = export_directory.NumberOfNames;
//const auto function_count = export_directory.NumberOfFunctions;
const auto names_count = export_directory.NumberOfNames;
// const auto function_count = export_directory.NumberOfFunctions;
const auto names = buffer.as<DWORD>(export_directory.AddressOfNames);
const auto ordinals = buffer.as<WORD>(export_directory.AddressOfNameOrdinals);
const auto functions = buffer.as<DWORD>(export_directory.AddressOfFunctions);
const auto names = buffer.as<DWORD>(export_directory.AddressOfNames);
const auto ordinals = buffer.as<WORD>(export_directory.AddressOfNameOrdinals);
const auto functions = buffer.as<DWORD>(export_directory.AddressOfFunctions);
binary.exports.reserve(names_count);
binary.exports.reserve(names_count);
for (DWORD i = 0; i < names_count; i++)
{
const auto ordinal = ordinals.get(i);
for (DWORD i = 0; i < names_count; i++)
{
const auto ordinal = ordinals.get(i);
exported_symbol symbol{};
symbol.ordinal = export_directory.Base + ordinal;
symbol.rva = functions.get(ordinal);
symbol.address = binary.image_base + symbol.rva;
symbol.name = buffer.as_string(names.get(i));
exported_symbol symbol{};
symbol.ordinal = export_directory.Base + ordinal;
symbol.rva = functions.get(ordinal);
symbol.address = binary.image_base + symbol.rva;
symbol.name = buffer.as_string(names.get(i));
binary.exports.push_back(std::move(symbol));
}
binary.exports.push_back(std::move(symbol));
}
for (const auto& symbol : binary.exports)
{
binary.address_names.try_emplace(symbol.address, symbol.name);
}
}
for (const auto& symbol : binary.exports)
{
binary.address_names.try_emplace(symbol.address, symbol.name);
}
}
template <typename T>
requires(std::is_integral_v<T>)
void apply_relocation(const utils::safe_buffer_accessor<uint8_t> buffer, const uint64_t offset,
const uint64_t delta)
{
const auto obj = buffer.as<T>(offset);
const auto value = obj.get();
const auto new_value = value + static_cast<T>(delta);
obj.set(new_value);
}
template <typename T>
requires(std::is_integral_v<T>)
void apply_relocation(const utils::safe_buffer_accessor<uint8_t> buffer, const uint64_t offset,
const uint64_t delta)
{
const auto obj = buffer.as<T>(offset);
const auto value = obj.get();
const auto new_value = value + static_cast<T>(delta);
obj.set(new_value);
}
void apply_relocations(const mapped_module& binary, const utils::safe_buffer_accessor<uint8_t> buffer,
const PEOptionalHeader_t<std::uint64_t>& optional_header)
{
const auto delta = binary.image_base - optional_header.ImageBase;
if (delta == 0)
{
return;
}
void apply_relocations(const mapped_module& binary, const utils::safe_buffer_accessor<uint8_t> buffer,
const PEOptionalHeader_t<std::uint64_t>& optional_header)
{
const auto delta = binary.image_base - optional_header.ImageBase;
if (delta == 0)
{
return;
}
const auto directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (directory->Size == 0)
{
return;
}
const auto directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (directory->Size == 0)
{
return;
}
auto relocation_offset = directory->VirtualAddress;
const auto relocation_end = relocation_offset + directory->Size;
auto relocation_offset = directory->VirtualAddress;
const auto relocation_end = relocation_offset + directory->Size;
while (relocation_offset < relocation_end)
{
const auto relocation = buffer.as<IMAGE_BASE_RELOCATION>(relocation_offset).get();
while (relocation_offset < relocation_end)
{
const auto relocation = buffer.as<IMAGE_BASE_RELOCATION>(relocation_offset).get();
if (relocation.VirtualAddress <= 0 || relocation.SizeOfBlock <= sizeof(IMAGE_BASE_RELOCATION))
{
break;
}
if (relocation.VirtualAddress <= 0 || relocation.SizeOfBlock <= sizeof(IMAGE_BASE_RELOCATION))
{
break;
}
const auto data_size = relocation.SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION);
const auto entry_count = data_size / sizeof(uint16_t);
const auto data_size = relocation.SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION);
const auto entry_count = data_size / sizeof(uint16_t);
const auto entries = buffer.as<uint16_t>(relocation_offset + sizeof(IMAGE_BASE_RELOCATION));
const auto entries = buffer.as<uint16_t>(relocation_offset + sizeof(IMAGE_BASE_RELOCATION));
relocation_offset += relocation.SizeOfBlock;
relocation_offset += relocation.SizeOfBlock;
for (size_t i = 0; i < entry_count; ++i)
{
const auto entry = entries.get(i);
for (size_t i = 0; i < entry_count; ++i)
{
const auto entry = entries.get(i);
const int type = entry >> 12;
const auto offset = static_cast<uint16_t>(entry & 0xfff);
const auto total_offset = relocation.VirtualAddress + offset;
const int type = entry >> 12;
const auto offset = static_cast<uint16_t>(entry & 0xfff);
const auto total_offset = relocation.VirtualAddress + offset;
switch (type)
{
case IMAGE_REL_BASED_ABSOLUTE:
break;
switch (type)
{
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGHLOW:
apply_relocation<DWORD>(buffer, total_offset, delta);
break;
case IMAGE_REL_BASED_HIGHLOW:
apply_relocation<DWORD>(buffer, total_offset, delta);
break;
case IMAGE_REL_BASED_DIR64:
apply_relocation<ULONGLONG>(buffer, total_offset, delta);
break;
case IMAGE_REL_BASED_DIR64:
apply_relocation<ULONGLONG>(buffer, total_offset, delta);
break;
default:
throw std::runtime_error("Unknown relocation type: " + std::to_string(type));
}
}
}
}
default:
throw std::runtime_error("Unknown relocation type: " + std::to_string(type));
}
}
}
}
void map_sections(emulator& emu, mapped_module& binary,
const utils::safe_buffer_accessor<const uint8_t> buffer,
const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
{
const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset);
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(first_section_offset);
void map_sections(emulator& emu, mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
{
const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset);
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(first_section_offset);
for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i)
{
const auto section = sections.get(i);
const auto target_ptr = binary.image_base + section.VirtualAddress;
for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i)
{
const auto section = sections.get(i);
const auto target_ptr = binary.image_base + section.VirtualAddress;
if (section.SizeOfRawData > 0)
{
const auto size_of_data = std::min(section.SizeOfRawData, section.Misc.VirtualSize);
const auto* source_ptr = buffer.get_pointer_for_range(section.PointerToRawData, size_of_data);
emu.write_memory(target_ptr, source_ptr, size_of_data);
}
if (section.SizeOfRawData > 0)
{
const auto size_of_data = std::min(section.SizeOfRawData, section.Misc.VirtualSize);
const auto* source_ptr = buffer.get_pointer_for_range(section.PointerToRawData, size_of_data);
emu.write_memory(target_ptr, source_ptr, size_of_data);
}
auto permissions = memory_permission::none;
auto permissions = memory_permission::none;
if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE)
{
permissions |= memory_permission::exec;
}
if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE)
{
permissions |= memory_permission::exec;
}
if (section.Characteristics & IMAGE_SCN_MEM_READ)
{
permissions |= memory_permission::read;
}
if (section.Characteristics & IMAGE_SCN_MEM_READ)
{
permissions |= memory_permission::read;
}
if (section.Characteristics & IMAGE_SCN_MEM_WRITE)
{
permissions |= memory_permission::write;
}
if (section.Characteristics & IMAGE_SCN_MEM_WRITE)
{
permissions |= memory_permission::write;
}
const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize));
const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize));
emu.protect_memory(target_ptr, size_of_section, permissions, nullptr);
emu.protect_memory(target_ptr, size_of_section, permissions, nullptr);
mapped_section section_info{};
section_info.region.start = target_ptr;
section_info.region.length = size_of_section;
section_info.region.permissions = permissions;
mapped_section section_info{};
section_info.region.start = target_ptr;
section_info.region.length = size_of_section;
section_info.region.permissions = permissions;
for (size_t j = 0; j < sizeof(section.Name) && section.Name[j]; ++j)
{
section_info.name.push_back(static_cast<char>(section.Name[j]));
}
for (size_t j = 0; j < sizeof(section.Name) && section.Name[j]; ++j)
{
section_info.name.push_back(static_cast<char>(section.Name[j]));
}
binary.sections.push_back(std::move(section_info));
}
}
binary.sections.push_back(std::move(section_info));
}
}
}
mapped_module map_module_from_data(emulator& emu, const std::span<const uint8_t> data,
std::filesystem::path file)
mapped_module map_module_from_data(emulator& emu, const std::span<const uint8_t> data, std::filesystem::path file)
{
mapped_module binary{};
binary.path = std::move(file);
binary.name = binary.path.filename().string();
mapped_module binary{};
binary.path = std::move(file);
binary.name = binary.path.filename().string();
utils::safe_buffer_accessor buffer{data};
utils::safe_buffer_accessor buffer{data};
const auto dos_header = buffer.as<PEDosHeader_t>(0).get();
const auto nt_headers_offset = dos_header.e_lfanew;
const auto dos_header = buffer.as<PEDosHeader_t>(0).get();
const auto nt_headers_offset = dos_header.e_lfanew;
const auto nt_headers = buffer.as<PENTHeaders_t<std::uint64_t>>(nt_headers_offset).get();
auto& optional_header = nt_headers.OptionalHeader;
const auto nt_headers = buffer.as<PENTHeaders_t<std::uint64_t>>(nt_headers_offset).get();
auto& optional_header = nt_headers.OptionalHeader;
if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
{
throw std::runtime_error("Unsupported architecture!");
}
if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
{
throw std::runtime_error("Unsupported architecture!");
}
binary.image_base = optional_header.ImageBase;
binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize
binary.image_base = optional_header.ImageBase;
binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize
if (!emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
{
binary.image_base = emu.find_free_allocation_base(binary.size_of_image);
const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL;
const auto has_dynamic_base =
optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
const auto is_relocatable = is_dll || has_dynamic_base;
if (!emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
{
binary.image_base = emu.find_free_allocation_base(binary.size_of_image);
const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL;
const auto has_dynamic_base = optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
const auto is_relocatable = is_dll || has_dynamic_base;
if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image,
memory_permission::read))
{
throw std::runtime_error("Memory range not allocatable");
}
}
if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
{
throw std::runtime_error("Memory range not allocatable");
}
}
binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint;
binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint;
const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders);
emu.write_memory(binary.image_base, header_buffer,
optional_header.SizeOfHeaders);
const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders);
emu.write_memory(binary.image_base, header_buffer, optional_header.SizeOfHeaders);
map_sections(emu, binary, buffer, nt_headers, nt_headers_offset);
map_sections(emu, binary, buffer, nt_headers, nt_headers_offset);
auto mapped_memory = read_mapped_memory(emu, binary);
utils::safe_buffer_accessor<uint8_t> mapped_buffer{mapped_memory};
auto mapped_memory = read_mapped_memory(emu, binary);
utils::safe_buffer_accessor<uint8_t> mapped_buffer{mapped_memory};
apply_relocations(binary, mapped_buffer, optional_header);
collect_exports(binary, mapped_buffer, optional_header);
apply_relocations(binary, mapped_buffer, optional_header);
collect_exports(binary, mapped_buffer, optional_header);
emu.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size());
emu.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size());
return binary;
return binary;
}
mapped_module map_module_from_file(emulator& emu, std::filesystem::path file)
{
const auto data = utils::io::read_file(file);
if (data.empty())
{
throw std::runtime_error("Bad file data");
}
const auto data = utils::io::read_file(file);
if (data.empty())
{
throw std::runtime_error("Bad file data");
}
return map_module_from_data(emu, data, std::move(file));
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);
return emu.release_memory(mod.image_base, mod.size_of_image);
}

View File

@@ -3,8 +3,7 @@
#include <x64_emulator.hpp>
#include "mapped_module.hpp"
mapped_module map_module_from_data(emulator& emu, std::span<const uint8_t> data,
std::filesystem::path file);
mapped_module map_module_from_data(emulator& emu, std::span<const uint8_t> data, std::filesystem::path file);
mapped_module map_module_from_file(emulator& emu, std::filesystem::path file);
bool unmap_module(emulator& emu, const mapped_module& mod);

File diff suppressed because it is too large Load Diff

View File

@@ -5,216 +5,216 @@
namespace
{
constexpr uint64_t MAIN_ROOT_OFFSET = 0x1000;
constexpr uint64_t MAIN_KEY_BLOCK_OFFSET = MAIN_ROOT_OFFSET + 0x20;
constexpr uint64_t MAIN_ROOT_OFFSET = 0x1000;
constexpr uint64_t MAIN_KEY_BLOCK_OFFSET = MAIN_ROOT_OFFSET + 0x20;
struct offset_entry_t
{
int32_t offset;
int32_t hash;
};
struct offset_entry_t
{
int32_t offset;
int32_t hash;
};
struct offsets_t
{
int32_t block_size;
char block_type[2];
int16_t count;
offset_entry_t entries[1];
};
struct offsets_t
{
int32_t block_size;
char block_type[2];
int16_t count;
offset_entry_t entries[1];
};
struct key_block_t
{
int32_t block_size;
char block_type[2];
uint8_t dummya[18];
int32_t subkey_count;
uint8_t dummyb[4];
int32_t subkeys;
uint8_t dummyc[4];
int32_t value_count;
int32_t offsets;
uint8_t dummyd[28];
int16_t len;
int16_t du;
char name[255];
};
struct key_block_t
{
int32_t block_size;
char block_type[2];
uint8_t dummya[18];
int32_t subkey_count;
uint8_t dummyb[4];
int32_t subkeys;
uint8_t dummyc[4];
int32_t value_count;
int32_t offsets;
uint8_t dummyd[28];
int16_t len;
int16_t du;
char name[255];
};
struct value_block_t
{
int32_t block_size;
char block_type[2];
int16_t name_len;
int32_t size;
int32_t offset;
int32_t value_type;
int16_t flags;
int16_t dummy;
char name[255];
};
struct value_block_t
{
int32_t block_size;
char block_type[2];
int16_t name_len;
int32_t size;
int32_t offset;
int32_t value_type;
int16_t flags;
int16_t dummy;
char name[255];
};
bool read_file_data_safe(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
{
if (file.bad())
{
return false;
}
bool read_file_data_safe(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
{
if (file.bad())
{
return false;
}
file.clear();
file.clear();
if (!file.good())
{
return false;
}
if (!file.good())
{
return false;
}
file.seekg(static_cast<std::streamoff>(offset));
file.seekg(static_cast<std::streamoff>(offset));
if (!file.good())
{
return false;
}
if (!file.good())
{
return false;
}
file.read(static_cast<char*>(buffer), static_cast<std::streamsize>(size));
file.read(static_cast<char*>(buffer), static_cast<std::streamsize>(size));
return file.good();
}
return file.good();
}
void read_file_data(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
{
if (!read_file_data_safe(file, offset, buffer, size))
{
throw std::runtime_error("Failed to read file data");
}
}
void read_file_data(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
{
if (!read_file_data_safe(file, offset, buffer, size))
{
throw std::runtime_error("Failed to read file data");
}
}
std::vector<std::byte> read_file_data(std::ifstream& file, const uint64_t offset, const size_t size)
{
std::vector<std::byte> result{};
result.resize(size);
std::vector<std::byte> read_file_data(std::ifstream& file, const uint64_t offset, const size_t size)
{
std::vector<std::byte> result{};
result.resize(size);
read_file_data(file, offset, result.data(), size);
return result;
}
read_file_data(file, offset, result.data(), size);
return result;
}
std::string read_file_data_string(std::ifstream& file, const uint64_t offset, const size_t size)
{
std::string result{};
result.resize(size);
std::string read_file_data_string(std::ifstream& file, const uint64_t offset, const size_t size)
{
std::string result{};
result.resize(size);
read_file_data(file, offset, result.data(), size);
return result;
}
read_file_data(file, offset, result.data(), size);
return result;
}
template <typename T>
requires(std::is_trivially_copyable_v<T>)
T read_file_object(std::ifstream& file, const uint64_t offset, const size_t array_index = 0)
{
T obj{};
read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T));
return obj;
}
template <typename T>
requires(std::is_trivially_copyable_v<T>)
T read_file_object(std::ifstream& file, const uint64_t offset, const size_t array_index = 0)
{
T obj{};
read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T));
return obj;
}
hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path)
{
try
{
if (read_file_data_string(file, 0, 4) != "regf")
{
throw std::runtime_error("Invalid signature");
}
hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path)
{
try
{
if (read_file_data_string(file, 0, 4) != "regf")
{
throw std::runtime_error("Invalid signature");
}
const auto key_block = read_file_object<key_block_t>(file, MAIN_KEY_BLOCK_OFFSET);
const auto key_block = read_file_object<key_block_t>(file, MAIN_KEY_BLOCK_OFFSET);
return {key_block.subkeys, key_block.value_count, key_block.offsets};
}
catch (const std::exception& e)
{
throw std::runtime_error("Bad hive file '" + file_path.string() + "': " + e.what());
}
}
return {key_block.subkeys, key_block.value_count, key_block.offsets};
}
catch (const std::exception& e)
{
throw std::runtime_error("Bad hive file '" + file_path.string() + "': " + e.what());
}
}
}
const hive_value* hive_key::get_value(std::ifstream& file, const std::string_view name)
{
this->parse(file);
this->parse(file);
const auto entry = this->values_.find(name);
if (entry == this->values_.end())
{
return nullptr;
}
const auto entry = this->values_.find(name);
if (entry == this->values_.end())
{
return nullptr;
}
auto& value = entry->second;
auto& value = entry->second;
if (!value.parsed)
{
value.data = read_file_data(file, MAIN_ROOT_OFFSET + value.data_offset, value.data_length);
value.parsed = true;
}
if (!value.parsed)
{
value.data = read_file_data(file, MAIN_ROOT_OFFSET + value.data_offset, value.data_length);
value.parsed = true;
}
return &value;
return &value;
}
void hive_key::parse(std::ifstream& file)
{
if (this->parsed_)
{
return;
}
if (this->parsed_)
{
return;
}
this->parsed_ = true;
this->parsed_ = true;
// Values
// Values
for (auto i = 0; i < this->value_count_; i++)
{
const auto offset = read_file_object<int>(file, MAIN_ROOT_OFFSET + this->value_offsets_ + 4, i);
const auto value = read_file_object<value_block_t>(file, MAIN_ROOT_OFFSET + offset);
for (auto i = 0; i < this->value_count_; i++)
{
const auto offset = read_file_object<int>(file, MAIN_ROOT_OFFSET + this->value_offsets_ + 4, i);
const auto value = read_file_object<value_block_t>(file, MAIN_ROOT_OFFSET + offset);
std::string value_name(value.name, std::min(value.name_len, static_cast<short>(sizeof(value.name))));
std::string value_name(value.name, std::min(value.name_len, static_cast<short>(sizeof(value.name))));
raw_hive_value raw_value{};
raw_value.parsed = false;
raw_value.type = value.value_type;
raw_value.name = value_name;
raw_value.data_length = value.size & 0xffff;
raw_value.data_offset = value.offset + 4;
raw_hive_value raw_value{};
raw_value.parsed = false;
raw_value.type = value.value_type;
raw_value.name = value_name;
raw_value.data_length = value.size & 0xffff;
raw_value.data_offset = value.offset + 4;
if (value.size & 1 << 31)
{
raw_value.data_offset = offset + static_cast<int>(offsetof(value_block_t, offset));
}
if (value.size & 1 << 31)
{
raw_value.data_offset = offset + static_cast<int>(offsetof(value_block_t, offset));
}
utils::string::to_lower_inplace(value_name);
this->values_[std::move(value_name)] = std::move(raw_value);
}
utils::string::to_lower_inplace(value_name);
this->values_[std::move(value_name)] = std::move(raw_value);
}
// Subkeys
// Subkeys
const auto item = read_file_object<offsets_t>(file, MAIN_ROOT_OFFSET + this->subkey_block_offset_);
const auto item = read_file_object<offsets_t>(file, MAIN_ROOT_OFFSET + this->subkey_block_offset_);
if (item.block_type[1] != 'f' && item.block_type[1] != 'h')
{
return;
}
if (item.block_type[1] != 'f' && item.block_type[1] != 'h')
{
return;
}
const auto entry_offsets = this->subkey_block_offset_ + offsetof(offsets_t, entries);
const auto entry_offsets = this->subkey_block_offset_ + offsetof(offsets_t, entries);
for (short i = 0; i < item.count; ++i)
{
const auto offset_entry = read_file_object<offset_entry_t>(file, MAIN_ROOT_OFFSET + entry_offsets, i);
for (short i = 0; i < item.count; ++i)
{
const auto offset_entry = read_file_object<offset_entry_t>(file, MAIN_ROOT_OFFSET + entry_offsets, i);
const auto subkey_block_offset = MAIN_ROOT_OFFSET + offset_entry.offset;
const auto subkey = read_file_object<key_block_t>(file, subkey_block_offset);
const auto subkey_block_offset = MAIN_ROOT_OFFSET + offset_entry.offset;
const auto subkey = read_file_object<key_block_t>(file, subkey_block_offset);
std::string subkey_name(subkey.name, std::min(subkey.len, static_cast<int16_t>(sizeof(subkey.name))));
utils::string::to_lower_inplace(subkey_name);
std::string subkey_name(subkey.name, std::min(subkey.len, static_cast<int16_t>(sizeof(subkey.name))));
utils::string::to_lower_inplace(subkey_name);
this->sub_keys_.emplace(std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets});
}
this->sub_keys_.emplace(std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets});
}
}
hive_parser::hive_parser(const std::filesystem::path& file_path)
: file_(file_path, std::ios::binary)
, root_key_(parse_root_block(file_, file_path))
: file_(file_path, std::ios::binary),
root_key_(parse_root_block(file_, file_path))
{
}

View File

@@ -8,95 +8,95 @@
struct hive_value
{
uint32_t type{};
std::string name{};
std::vector<std::byte> data{};
uint32_t type{};
std::string name{};
std::vector<std::byte> data{};
};
class hive_key
{
public:
hive_key(const int subkey_block_offset, const int value_count, const int value_offsets)
: subkey_block_offset_(subkey_block_offset)
, value_count_(value_count)
, value_offsets_(value_offsets)
{
}
public:
hive_key(const int subkey_block_offset, const int value_count, const int value_offsets)
: subkey_block_offset_(subkey_block_offset),
value_count_(value_count),
value_offsets_(value_offsets)
{
}
utils::unordered_string_map<hive_key>& get_sub_keys(std::ifstream& file)
{
this->parse(file);
return this->sub_keys_;
}
utils::unordered_string_map<hive_key>& get_sub_keys(std::ifstream& file)
{
this->parse(file);
return this->sub_keys_;
}
hive_key* get_sub_key(std::ifstream& file, const std::string_view name)
{
auto& sub_keys = this->get_sub_keys(file);
const auto entry = sub_keys.find(name);
hive_key* get_sub_key(std::ifstream& file, const std::string_view name)
{
auto& sub_keys = this->get_sub_keys(file);
const auto entry = sub_keys.find(name);
if (entry == sub_keys.end())
{
return nullptr;
}
if (entry == sub_keys.end())
{
return nullptr;
}
return &entry->second;
}
return &entry->second;
}
const hive_value* get_value(std::ifstream& file, const std::string_view name);
const hive_value* get_value(std::ifstream& file, const std::string_view name);
private:
struct raw_hive_value : hive_value
{
bool parsed{false};
int data_offset{};
size_t data_length{};
};
private:
struct raw_hive_value : hive_value
{
bool parsed{false};
int data_offset{};
size_t data_length{};
};
bool parsed_{false};
utils::unordered_string_map<hive_key> sub_keys_{};
utils::unordered_string_map<raw_hive_value> values_{};
bool parsed_{false};
utils::unordered_string_map<hive_key> sub_keys_{};
utils::unordered_string_map<raw_hive_value> values_{};
const int subkey_block_offset_{};
const int value_count_{};
const int value_offsets_{};
const int subkey_block_offset_{};
const int value_count_{};
const int value_offsets_{};
void parse(std::ifstream& file);
void parse(std::ifstream& file);
};
class hive_parser
{
public:
explicit hive_parser(const std::filesystem::path& file_path);
public:
explicit hive_parser(const std::filesystem::path& file_path);
[[nodiscard]] hive_key* get_sub_key(const std::filesystem::path& key)
{
hive_key* current_key = &this->root_key_;
[[nodiscard]] hive_key* get_sub_key(const std::filesystem::path& key)
{
hive_key* current_key = &this->root_key_;
for (const auto& key_part : key)
{
if (!current_key)
{
return nullptr;
}
for (const auto& key_part : key)
{
if (!current_key)
{
return nullptr;
}
current_key = current_key->get_sub_key(this->file_, key_part.string());
}
current_key = current_key->get_sub_key(this->file_, key_part.string());
}
return current_key;
}
return current_key;
}
[[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, const std::string_view name)
{
auto* sub_key = this->get_sub_key(key);
if (!sub_key)
{
return nullptr;
}
[[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, const std::string_view name)
{
auto* sub_key = this->get_sub_key(key);
if (!sub_key)
{
return nullptr;
}
return sub_key->get_value(this->file_, name);
}
return sub_key->get_value(this->file_, name);
}
private:
std::ifstream file_{};
hive_key root_key_;
private:
std::ifstream file_{};
hive_key root_key_;
};

View File

@@ -7,33 +7,33 @@
namespace
{
std::filesystem::path canonicalize_path(const std::filesystem::path& key)
{
auto path = key.lexically_normal().wstring();
return utils::string::to_lower_consume(path);
}
std::filesystem::path canonicalize_path(const std::filesystem::path& key)
{
auto path = key.lexically_normal().wstring();
return utils::string::to_lower_consume(path);
}
bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p)
{
auto root_it = root.begin();
auto p_it = p.begin();
bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p)
{
auto root_it = root.begin();
auto p_it = p.begin();
for (; root_it != root.end(); ++root_it, ++p_it)
{
if (p_it == p.end() || *root_it != *p_it)
{
return false;
}
}
for (; root_it != root.end(); ++root_it, ++p_it)
{
if (p_it == p.end() || *root_it != *p_it)
{
return false;
}
}
return true;
}
return true;
}
void register_hive(registry_manager::hive_map& hives,
const std::filesystem::path& key, const std::filesystem::path& file)
{
hives[canonicalize_path(key)] = std::make_unique<hive_parser>(file);
}
void register_hive(registry_manager::hive_map& hives, const std::filesystem::path& key,
const std::filesystem::path& file)
{
hives[canonicalize_path(key)] = std::make_unique<hive_parser>(file);
}
}
registry_manager::registry_manager() = default;
@@ -42,130 +42,130 @@ registry_manager::registry_manager(registry_manager&&) noexcept = default;
registry_manager& registry_manager::operator=(registry_manager&&) noexcept = default;
registry_manager::registry_manager(const std::filesystem::path& hive_path)
: hive_path_(absolute(hive_path))
: hive_path_(absolute(hive_path))
{
this->setup();
this->setup();
}
void registry_manager::setup()
{
this->path_mapping_.clear();
this->hives_.clear();
this->path_mapping_.clear();
this->hives_.clear();
const std::filesystem::path root = R"(\registry)";
const std::filesystem::path machine = root / "machine";
const std::filesystem::path root = R"(\registry)";
const std::filesystem::path machine = root / "machine";
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
register_hive(this->hives_, machine / "security", this->hive_path_ / "SECURITY");
register_hive(this->hives_, machine / "sam", this->hive_path_ / "SAM");
register_hive(this->hives_, machine / "software", this->hive_path_ / "SOFTWARE");
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
register_hive(this->hives_, machine / "hardware", this->hive_path_ / "HARDWARE");
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
register_hive(this->hives_, machine / "security", this->hive_path_ / "SECURITY");
register_hive(this->hives_, machine / "sam", this->hive_path_ / "SAM");
register_hive(this->hives_, machine / "software", this->hive_path_ / "SOFTWARE");
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
register_hive(this->hives_, machine / "hardware", this->hive_path_ / "HARDWARE");
register_hive(this->hives_, root / "user", this->hive_path_ / "NTUSER.dat");
register_hive(this->hives_, root / "user", this->hive_path_ / "NTUSER.dat");
this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001");
this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001");
}
void registry_manager::serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->hive_path_);
buffer.write(this->hive_path_);
}
void registry_manager::deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->hive_path_);
this->setup();
buffer.read(this->hive_path_);
this->setup();
}
std::filesystem::path registry_manager::normalize_path(const std::filesystem::path& path) const
{
auto canonical_path = canonicalize_path(path);
auto canonical_path = canonicalize_path(path);
for (const auto& mapping : this->path_mapping_)
{
if (is_subpath(mapping.first, canonical_path))
{
return mapping.second / canonical_path.lexically_relative(mapping.first);
}
}
for (const auto& mapping : this->path_mapping_)
{
if (is_subpath(mapping.first, canonical_path))
{
return mapping.second / canonical_path.lexically_relative(mapping.first);
}
}
return canonical_path;
return canonical_path;
}
void registry_manager::add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value)
{
this->path_mapping_[canonicalize_path(key)] = canonicalize_path(value);
this->path_mapping_[canonicalize_path(key)] = canonicalize_path(value);
}
std::optional<registry_key> registry_manager::get_key(const std::filesystem::path& key)
{
const auto normal_key = this->normalize_path(key);
const auto normal_key = this->normalize_path(key);
if (is_subpath(normal_key, "\\registry\\machine"))
{
registry_key reg_key{};
reg_key.hive = normal_key;
return {std::move(reg_key)};
}
if (is_subpath(normal_key, "\\registry\\machine"))
{
registry_key reg_key{};
reg_key.hive = normal_key;
return {std::move(reg_key)};
}
const auto iterator = this->find_hive(normal_key);
if (iterator == this->hives_.end())
{
return {};
}
const auto iterator = this->find_hive(normal_key);
if (iterator == this->hives_.end())
{
return {};
}
registry_key reg_key{};
reg_key.hive = iterator->first;
reg_key.path = normal_key.lexically_relative(reg_key.hive);
registry_key reg_key{};
reg_key.hive = iterator->first;
reg_key.path = normal_key.lexically_relative(reg_key.hive);
if (reg_key.path.empty())
{
return {std::move(reg_key)};
}
if (reg_key.path.empty())
{
return {std::move(reg_key)};
}
const auto entry = iterator->second->get_sub_key(reg_key.path);
if (!entry)
{
return std::nullopt;
}
const auto entry = iterator->second->get_sub_key(reg_key.path);
if (!entry)
{
return std::nullopt;
}
return {std::move(reg_key)};
return {std::move(reg_key)};
}
std::optional<registry_value> registry_manager::get_value(const registry_key& key, std::string name)
{
utils::string::to_lower_inplace(name);
utils::string::to_lower_inplace(name);
const auto iterator = this->hives_.find(key.hive);
if (iterator == this->hives_.end())
{
return std::nullopt;
}
const auto iterator = this->hives_.find(key.hive);
if (iterator == this->hives_.end())
{
return std::nullopt;
}
auto* entry = iterator->second->get_value(key.path, name);
if (!entry)
{
return std::nullopt;
}
auto* entry = iterator->second->get_value(key.path, name);
if (!entry)
{
return std::nullopt;
}
registry_value v{};
v.type = entry->type;
v.name = entry->name;
v.data = entry->data;
registry_value v{};
v.type = entry->type;
v.name = entry->name;
v.data = entry->data;
return v;
return v;
}
registry_manager::hive_map::iterator registry_manager::find_hive(const std::filesystem::path& key)
{
for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i)
{
if (is_subpath(i->first, key))
{
return i;
}
}
for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i)
{
if (is_subpath(i->first, key))
{
return i;
}
}
return this->hives_.end();
return this->hives_.end();
}

View File

@@ -6,61 +6,60 @@
struct registry_key
{
std::filesystem::path hive{};
std::filesystem::path path{};
std::filesystem::path hive{};
std::filesystem::path path{};
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->hive);
buffer.write(this->path);
}
void serialize(utils::buffer_serializer& buffer) const
{
buffer.write(this->hive);
buffer.write(this->path);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->hive);
buffer.read(this->path);
}
void deserialize(utils::buffer_deserializer& buffer)
{
buffer.read(this->hive);
buffer.read(this->path);
}
};
struct registry_value
{
uint32_t type;
std::string_view name;
std::span<const std::byte> data;
uint32_t type;
std::string_view name;
std::span<const std::byte> data;
};
class registry_manager
{
public:
using hive_ptr = std::unique_ptr<hive_parser>;
using hive_map = std::unordered_map<std::filesystem::path, hive_ptr>;
public:
using hive_ptr = std::unique_ptr<hive_parser>;
using hive_map = std::unordered_map<std::filesystem::path, hive_ptr>;
registry_manager();
registry_manager(const std::filesystem::path& hive_path);
~registry_manager();
registry_manager();
registry_manager(const std::filesystem::path& hive_path);
~registry_manager();
registry_manager(registry_manager&&) noexcept;
registry_manager& operator=(registry_manager&&) noexcept;
registry_manager(registry_manager&&) noexcept;
registry_manager& operator=(registry_manager&&) noexcept;
registry_manager(const registry_manager&) = delete;
registry_manager& operator=(const registry_manager&) = delete;
registry_manager(const registry_manager&) = delete;
registry_manager& operator=(const registry_manager&) = delete;
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
std::optional<registry_key> get_key(const std::filesystem::path& key);
std::optional<registry_value> get_value(const registry_key& key, std::string name);
std::optional<registry_key> get_key(const std::filesystem::path& key);
std::optional<registry_value> get_value(const registry_key& key, std::string name);
private:
std::filesystem::path hive_path_{};
hive_map hives_{};
std::unordered_map<std::filesystem::path, std::filesystem::path> path_mapping_{};
private:
std::filesystem::path hive_path_{};
hive_map hives_{};
std::unordered_map<std::filesystem::path, std::filesystem::path> path_mapping_{};
std::filesystem::path normalize_path(const std::filesystem::path& path) const;
void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value);
std::filesystem::path normalize_path(const std::filesystem::path& path) const;
void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value);
hive_map::iterator find_hive(const std::filesystem::path& key);
hive_map::iterator find_hive(const std::filesystem::path& key);
void setup();
void setup();
};

View File

@@ -2,32 +2,32 @@
#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)
#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)
#pragma warning(pop)
#endif

View File

@@ -3,145 +3,138 @@
static void serialize(utils::buffer_serializer& buffer, const syscall_handler_entry& obj)
{
buffer.write(obj.name);
buffer.write(obj.name);
}
static void deserialize(utils::buffer_deserializer& buffer, syscall_handler_entry& obj)
{
buffer.read(obj.name);
obj.handler = nullptr;
buffer.read(obj.name);
obj.handler = nullptr;
}
void syscall_dispatcher::serialize(utils::buffer_serializer& buffer) const
{
buffer.write_map(this->handlers_);
buffer.write_map(this->handlers_);
}
void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer)
{
buffer.read_map(this->handlers_);
this->add_handlers();
buffer.read_map(this->handlers_);
this->add_handlers();
}
void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data)
{
this->handlers_ = {};
this->handlers_ = {};
const auto ntdll_syscalls = find_syscalls(ntdll_exports, ntdll_data);
const auto win32u_syscalls = find_syscalls(win32u_exports, win32u_data);
const auto ntdll_syscalls = find_syscalls(ntdll_exports, ntdll_data);
const auto win32u_syscalls = find_syscalls(win32u_exports, win32u_data);
map_syscalls(this->handlers_, ntdll_syscalls);
map_syscalls(this->handlers_, win32u_syscalls);
map_syscalls(this->handlers_, ntdll_syscalls);
map_syscalls(this->handlers_, win32u_syscalls);
this->add_handlers();
this->add_handlers();
}
void syscall_dispatcher::add_handlers()
{
std::map<std::string, syscall_handler> handler_mapping{};
std::map<std::string, syscall_handler> handler_mapping{};
syscall_dispatcher::add_handlers(handler_mapping);
syscall_dispatcher::add_handlers(handler_mapping);
for (auto& entry : this->handlers_)
{
const auto handler = handler_mapping.find(entry.second.name);
if (handler == handler_mapping.end())
{
continue;
}
for (auto& entry : this->handlers_)
{
const auto handler = handler_mapping.find(entry.second.name);
if (handler == handler_mapping.end())
{
continue;
}
entry.second.handler = handler->second;
entry.second.handler = handler->second;
#ifndef NDEBUG
handler_mapping.erase(handler);
handler_mapping.erase(handler);
#endif
}
}
}
void syscall_dispatcher::dispatch(windows_emulator& win_emu)
{
auto& emu = win_emu.emu();
auto& context = win_emu.process();
auto& emu = win_emu.emu();
auto& context = win_emu.process();
const auto address = emu.read_instruction_pointer();
const auto syscall_id = emu.reg<uint32_t>(x64_register::eax);
const auto address = emu.read_instruction_pointer();
const auto syscall_id = emu.reg<uint32_t>(x64_register::eax);
const syscall_context c{win_emu, emu, context, true};
const syscall_context c{win_emu, emu, context, true};
try
{
const auto entry = this->handlers_.find(syscall_id);
if (entry == this->handlers_.end())
{
printf("Unknown syscall: 0x%X\n", syscall_id);
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED);
c.emu.stop();
return;
}
try
{
const auto entry = this->handlers_.find(syscall_id);
if (entry == this->handlers_.end())
{
printf("Unknown syscall: 0x%X\n", syscall_id);
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED);
c.emu.stop();
return;
}
if (!entry->second.handler)
{
printf("Unimplemented syscall: %s - 0x%X\n", entry->second.name.c_str(), syscall_id);
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED);
c.emu.stop();
return;
}
if (!entry->second.handler)
{
printf("Unimplemented syscall: %s - 0x%X\n", entry->second.name.c_str(), syscall_id);
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED);
c.emu.stop();
return;
}
const auto* mod = context.mod_manager.find_by_address(address);
if (mod != context.ntdll && mod != context.win32u)
{
win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n",
entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : "<N/A>");
}
else
{
if (mod->is_within(context.previous_ip))
{
const auto rsp = c.emu.read_stack_pointer();
const auto return_address = c.emu.read_memory<uint64_t>(rsp);
const auto* mod_name = context.mod_manager.find_name(return_address);
const auto* mod = context.mod_manager.find_by_address(address);
if (mod != context.ntdll && mod != context.win32u)
{
win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n",
entry->second.name.c_str(),
syscall_id,
address, mod ? mod->name.c_str() : "<N/A>");
}
else
{
if (mod->is_within(context.previous_ip))
{
const auto rsp = c.emu.read_stack_pointer();
const auto return_address = c.emu.read_memory<uint64_t>(rsp);
const auto* mod_name = context.mod_manager.find_name(return_address);
win_emu.log.print(color::dark_gray,
"Executing syscall: %s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n",
entry->second.name.c_str(), syscall_id, address, return_address, mod_name);
}
else
{
const auto* previous_mod = context.mod_manager.find_by_address(context.previous_ip);
win_emu.log.print(color::blue,
"Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64
" (%s)\n",
entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
context.previous_ip, previous_mod ? previous_mod->name.c_str() : "<N/A>");
}
}
win_emu.log.print(color::dark_gray,
"Executing syscall: %s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n",
entry->second.name.c_str(),
syscall_id, address, return_address, mod_name);
}
else
{
const auto* previous_mod = context.mod_manager.find_by_address(context.previous_ip);
win_emu.log.print(color::blue,
"Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64
" (%s)\n",
entry->second.name.c_str(),
syscall_id,
address, mod ? mod->name.c_str() : "<N/A>", context.previous_ip,
previous_mod ? previous_mod->name.c_str() : "<N/A>");
}
}
entry->second.handler(c);
}
catch (std::exception& e)
{
printf("Syscall threw an exception: %X (0x%" PRIx64 ") - %s\n", syscall_id, address, e.what());
emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
emu.stop();
}
catch (...)
{
printf("Syscall threw an unknown exception: %X (0x%" PRIx64 ")\n", syscall_id, address);
emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
emu.stop();
}
entry->second.handler(c);
}
catch (std::exception& e)
{
printf("Syscall threw an exception: %X (0x%" PRIx64 ") - %s\n", syscall_id, address, e.what());
emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
emu.stop();
}
catch (...)
{
printf("Syscall threw an unknown exception: %X (0x%" PRIx64 ")\n", syscall_id, address);
emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
emu.stop();
}
}
syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data)
{
this->setup(ntdll_exports, ntdll_data, win32u_exports, win32u_data);
this->setup(ntdll_exports, ntdll_data, win32u_exports, win32u_data);
}

View File

@@ -3,39 +3,39 @@
#include "process_context.hpp"
struct syscall_context;
using syscall_handler = void(*)(const syscall_context& c);
using syscall_handler = void (*)(const syscall_context& c);
struct syscall_handler_entry
{
syscall_handler handler{};
std::string name{};
syscall_handler handler{};
std::string name{};
};
class windows_emulator;
class syscall_dispatcher
{
public:
syscall_dispatcher() = default;
syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
public:
syscall_dispatcher() = default;
syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
void dispatch(windows_emulator& win_emu);
void dispatch(windows_emulator& win_emu);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
void setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
void setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
std::string get_syscall_name(const uint64_t id)
{
return this->handlers_.at(id).name;
}
std::string get_syscall_name(const uint64_t id)
{
return this->handlers_.at(id).name;
}
private:
std::map<uint64_t, syscall_handler_entry> handlers_{};
private:
std::map<uint64_t, syscall_handler_entry> handlers_{};
static void add_handlers(std::map<std::string, syscall_handler>& handler_mapping);
void add_handlers();
static void add_handlers(std::map<std::string, syscall_handler>& handler_mapping);
void add_handlers();
};

View File

@@ -5,202 +5,193 @@
struct syscall_context
{
windows_emulator& win_emu;
x64_emulator& emu;
process_context& proc;
mutable bool write_status{true};
mutable bool retrigger_syscall{false};
windows_emulator& win_emu;
x64_emulator& emu;
process_context& proc;
mutable bool write_status{true};
mutable bool retrigger_syscall{false};
};
inline uint64_t get_syscall_argument(x64_emulator& emu, const size_t index)
{
switch (index)
{
case 0:
return emu.reg(x64_register::r10);
case 1:
return emu.reg(x64_register::rdx);
case 2:
return emu.reg(x64_register::r8);
case 3:
return emu.reg(x64_register::r9);
default:
return emu.read_stack(index + 1);
}
switch (index)
{
case 0:
return emu.reg(x64_register::r10);
case 1:
return emu.reg(x64_register::rdx);
case 2:
return emu.reg(x64_register::r8);
case 3:
return emu.reg(x64_register::r9);
default:
return emu.read_stack(index + 1);
}
}
inline bool is_uppercase(const char character)
{
return toupper(character) == character;
return toupper(character) == character;
}
inline bool is_syscall(const std::string_view name)
{
return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]);
return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]);
}
inline std::optional<uint32_t> extract_syscall_id(const exported_symbol& symbol, std::span<const std::byte> data)
{
if (!is_syscall(symbol.name))
{
return std::nullopt;
}
if (!is_syscall(symbol.name))
{
return std::nullopt;
}
constexpr auto instruction_size = 5;
constexpr auto instruction_offset = 3;
constexpr auto instruction_operand_offset = 1;
constexpr auto instruction_opcode = static_cast<std::byte>(0xB8);
constexpr auto instruction_size = 5;
constexpr auto instruction_offset = 3;
constexpr auto instruction_operand_offset = 1;
constexpr auto instruction_opcode = static_cast<std::byte>(0xB8);
const auto instruction_rva = symbol.rva + instruction_offset;
const auto instruction_rva = symbol.rva + instruction_offset;
if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode)
{
return std::nullopt;
}
if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode)
{
return std::nullopt;
}
uint32_t syscall_id{0};
static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset));
memcpy(&syscall_id, data.data() + instruction_rva + instruction_operand_offset, sizeof(syscall_id));
uint32_t syscall_id{0};
static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset));
memcpy(&syscall_id, data.data() + instruction_rva + instruction_operand_offset, sizeof(syscall_id));
return syscall_id;
return syscall_id;
}
inline std::map<uint64_t, std::string> find_syscalls(const exported_symbols& exports, std::span<const std::byte> data)
{
std::map<uint64_t, std::string> syscalls{};
std::map<uint64_t, std::string> syscalls{};
for (const auto& symbol : exports)
{
const auto id = extract_syscall_id(symbol, data);
if (id)
{
auto& entry = syscalls[*id];
for (const auto& symbol : exports)
{
const auto id = extract_syscall_id(symbol, data);
if (id)
{
auto& entry = syscalls[*id];
if (!entry.empty())
{
throw std::runtime_error(
"Syscall with id " + std::to_string(*id) + ", which is mapping to " + symbol.name +
", was already mapped to " + entry);
}
if (!entry.empty())
{
throw std::runtime_error("Syscall with id " + std::to_string(*id) + ", which is mapping to " +
symbol.name + ", was already mapped to " + entry);
}
entry = symbol.name;
}
}
entry = symbol.name;
}
}
return syscalls;
return syscalls;
}
inline void map_syscalls(std::map<uint64_t, syscall_handler_entry>& handlers,
std::map<uint64_t, std::string> syscalls)
inline void map_syscalls(std::map<uint64_t, syscall_handler_entry>& handlers, std::map<uint64_t, std::string> syscalls)
{
for (auto& [id, name] : syscalls)
{
auto& entry = handlers[id];
for (auto& [id, name] : syscalls)
{
auto& entry = handlers[id];
if (!entry.name.empty())
{
throw std::runtime_error(
"Syscall with id " + std::to_string(id) + ", which is mapping to " + name +
", was previously mapped to " + entry.name);
}
if (!entry.name.empty())
{
throw std::runtime_error("Syscall with id " + std::to_string(id) + ", which is mapping to " + name +
", was previously mapped to " + entry.name);
}
entry.name = std::move(name);
entry.handler = nullptr;
}
entry.name = std::move(name);
entry.handler = nullptr;
}
}
template <typename T>
requires(std::is_integral_v<T> || std::is_enum_v<T>)
requires(std::is_integral_v<T> || std::is_enum_v<T>)
T resolve_argument(x64_emulator& emu, const size_t index)
{
const auto arg = get_syscall_argument(emu, index);
return static_cast<T>(arg);
const auto arg = get_syscall_argument(emu, index);
return static_cast<T>(arg);
}
template <typename T>
requires(std::is_same_v<std::remove_cvref_t<T>, handle>)
requires(std::is_same_v<std::remove_cvref_t<T>, handle>)
handle resolve_argument(x64_emulator& emu, const size_t index)
{
handle h{};
h.bits = resolve_argument<uint64_t>(emu, index);
return h;
handle h{};
h.bits = resolve_argument<uint64_t>(emu, index);
return h;
}
template <typename T>
requires(std::is_same_v<T, emulator_object<typename T::value_type>>)
requires(std::is_same_v<T, emulator_object<typename T::value_type>>)
T resolve_argument(x64_emulator& emu, const size_t index)
{
const auto arg = get_syscall_argument(emu, index);
return T(emu, arg);
const auto arg = get_syscall_argument(emu, index);
return T(emu, arg);
}
template <typename T>
T resolve_indexed_argument(x64_emulator& emu, size_t& index)
{
return resolve_argument<T>(emu, index++);
return resolve_argument<T>(emu, index++);
}
inline void write_status(const syscall_context& c, const NTSTATUS status, const uint64_t initial_ip)
{
if (c.write_status && !c.retrigger_syscall)
{
c.emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
}
if (c.write_status && !c.retrigger_syscall)
{
c.emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
}
const auto new_ip = c.emu.read_instruction_pointer();
if (initial_ip != new_ip || c.retrigger_syscall)
{
c.emu.reg(x64_register::rip, new_ip - 2);
}
const auto new_ip = c.emu.read_instruction_pointer();
if (initial_ip != new_ip || c.retrigger_syscall)
{
c.emu.reg(x64_register::rip, new_ip - 2);
}
}
inline void forward_syscall(const syscall_context& c, NTSTATUS (*handler)())
{
const auto ip = c.emu.read_instruction_pointer();
const auto ip = c.emu.read_instruction_pointer();
const auto ret = handler();
write_status(c, ret, ip);
const auto ret = handler();
write_status(c, ret, ip);
}
template <typename... Args>
void forward_syscall(const syscall_context& c, NTSTATUS (*handler)(const syscall_context&, Args...))
{
const auto ip = c.emu.read_instruction_pointer();
const auto ip = c.emu.read_instruction_pointer();
size_t index = 0;
std::tuple<const syscall_context&, Args...> func_args
{
c,
resolve_indexed_argument<std::remove_cv_t<std::remove_reference_t<Args>>>(c.emu, index)...
};
size_t index = 0;
std::tuple<const syscall_context&, Args...> func_args{
c, resolve_indexed_argument<std::remove_cv_t<std::remove_reference_t<Args>>>(c.emu, index)...};
(void)index;
(void)index;
const auto ret = std::apply(handler, std::move(func_args));
write_status(c, ret, ip);
const auto ret = std::apply(handler, std::move(func_args));
write_status(c, ret, ip);
}
template <auto Handler>
syscall_handler make_syscall_handler()
{
return +[](const syscall_context& c)
{
forward_syscall(c, Handler);
};
return +[](const syscall_context& c) { forward_syscall(c, Handler); };
}
template <typename T, typename Traits>
void write_attribute(emulator& emu, const PS_ATTRIBUTE<Traits>& attribute, const T& value)
{
if (attribute.ReturnLength)
{
emulator_object<typename Traits::SIZE_T>{emu, attribute.ReturnLength}.write(sizeof(T));
}
if (attribute.ReturnLength)
{
emulator_object<typename Traits::SIZE_T>{emu, attribute.ReturnLength}.write(sizeof(T));
}
if (attribute.Size >= sizeof(T))
{
emulator_object<T>{emu, attribute.Value}.write(value);
}
if (attribute.Size >= sizeof(T))
{
emulator_object<T>{emu, attribute.Value}.write(value);
}
}
constexpr auto HUNDRED_NANOSECONDS_IN_ONE_SECOND = 10000000LL;
@@ -209,67 +200,65 @@ constexpr auto WINDOWS_EPOCH_DIFFERENCE = EPOCH_DIFFERENCE_1601_TO_1970_SECONDS
inline std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(const LARGE_INTEGER delay_interval)
{
if (delay_interval.QuadPart <= 0)
{
const auto relative_time = -delay_interval.QuadPart;
const auto relative_ticks_in_ms = relative_time / 10;
const auto relative_fraction_ns = (relative_time % 10) * 100;
const auto relative_duration = std::chrono::microseconds(relative_ticks_in_ms) +
std::chrono::nanoseconds(relative_fraction_ns);
if (delay_interval.QuadPart <= 0)
{
const auto relative_time = -delay_interval.QuadPart;
const auto relative_ticks_in_ms = relative_time / 10;
const auto relative_fraction_ns = (relative_time % 10) * 100;
const auto relative_duration =
std::chrono::microseconds(relative_ticks_in_ms) + std::chrono::nanoseconds(relative_fraction_ns);
return std::chrono::steady_clock::now() + relative_duration;
}
return std::chrono::steady_clock::now() + relative_duration;
}
const auto delay_seconds_since_1601 = delay_interval.QuadPart / HUNDRED_NANOSECONDS_IN_ONE_SECOND;
const auto delay_fraction_ns = (delay_interval.QuadPart % HUNDRED_NANOSECONDS_IN_ONE_SECOND) * 100;
const auto delay_seconds_since_1601 = delay_interval.QuadPart / HUNDRED_NANOSECONDS_IN_ONE_SECOND;
const auto delay_fraction_ns = (delay_interval.QuadPart % HUNDRED_NANOSECONDS_IN_ONE_SECOND) * 100;
const auto delay_seconds_since_1970 = delay_seconds_since_1601 - EPOCH_DIFFERENCE_1601_TO_1970_SECONDS;
const auto delay_seconds_since_1970 = delay_seconds_since_1601 - EPOCH_DIFFERENCE_1601_TO_1970_SECONDS;
const auto target_time =
std::chrono::system_clock::from_time_t(delay_seconds_since_1970) +
std::chrono::nanoseconds(delay_fraction_ns);
const auto target_time =
std::chrono::system_clock::from_time_t(delay_seconds_since_1970) + std::chrono::nanoseconds(delay_fraction_ns);
const auto now_system = std::chrono::system_clock::now();
const auto now_system = std::chrono::system_clock::now();
const auto duration_until_target = std::chrono::duration_cast<
std::chrono::microseconds>(target_time - now_system);
const auto duration_until_target = std::chrono::duration_cast<std::chrono::microseconds>(target_time - now_system);
return std::chrono::steady_clock::now() + duration_until_target;
return std::chrono::steady_clock::now() + duration_until_target;
}
inline KSYSTEM_TIME convert_to_ksystem_time(const std::chrono::system_clock::time_point& tp)
{
const auto duration = tp.time_since_epoch();
const auto ns_duration = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
const auto duration = tp.time_since_epoch();
const auto ns_duration = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
const auto total_ticks = ns_duration.count() / 100 + WINDOWS_EPOCH_DIFFERENCE;
const auto total_ticks = ns_duration.count() / 100 + WINDOWS_EPOCH_DIFFERENCE;
KSYSTEM_TIME time{};
time.LowPart = static_cast<uint32_t>(total_ticks);
time.High1Time = static_cast<int32_t>(total_ticks >> 32);
time.High2Time = time.High1Time;
KSYSTEM_TIME time{};
time.LowPart = static_cast<uint32_t>(total_ticks);
time.High1Time = static_cast<int32_t>(total_ticks >> 32);
time.High2Time = time.High1Time;
return time;
return time;
}
inline void convert_to_ksystem_time(volatile KSYSTEM_TIME* dest, const std::chrono::system_clock::time_point& tp)
{
const auto time = convert_to_ksystem_time(tp);
memcpy(const_cast<KSYSTEM_TIME*>(dest), &time, sizeof(*dest));
const auto time = convert_to_ksystem_time(tp);
memcpy(const_cast<KSYSTEM_TIME*>(dest), &time, sizeof(*dest));
}
inline std::chrono::system_clock::time_point convert_from_ksystem_time(const KSYSTEM_TIME& time)
{
auto totalTicks = (static_cast<int64_t>(time.High1Time) << 32) | time.LowPart;
totalTicks -= WINDOWS_EPOCH_DIFFERENCE;
auto totalTicks = (static_cast<int64_t>(time.High1Time) << 32) | time.LowPart;
totalTicks -= WINDOWS_EPOCH_DIFFERENCE;
const auto duration = std::chrono::system_clock::duration(totalTicks * 100);
return std::chrono::system_clock::time_point(duration);
const auto duration = std::chrono::system_clock::duration(totalTicks * 100);
return std::chrono::system_clock::time_point(duration);
}
inline std::chrono::system_clock::time_point convert_from_ksystem_time(const volatile KSYSTEM_TIME& time)
{
return convert_from_ksystem_time(*const_cast<const KSYSTEM_TIME*>(&time));
return convert_from_ksystem_time(*const_cast<const KSYSTEM_TIME*>(&time));
}
#ifndef OS_WINDOWS
@@ -278,7 +267,7 @@ using __time64_t = int64_t;
inline LARGE_INTEGER convert_unix_to_windows_time(const __time64_t unix_time)
{
LARGE_INTEGER windows_time{};
windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND;
return windows_time;
LARGE_INTEGER windows_time{};
windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND;
return windows_time;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -12,120 +12,119 @@ std::unique_ptr<x64_emulator> create_default_x64_emulator();
// 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"};
std::vector<std::u16string> arguments{};
std::function<void(std::string_view)> stdout_callback{};
bool disable_logging{false};
bool silent_until_main{false};
bool use_relative_time{false};
std::filesystem::path application{};
std::filesystem::path working_directory{};
std::filesystem::path registry_directory{"./registry"};
std::vector<std::u16string> arguments{};
std::function<void(std::string_view)> stdout_callback{};
bool disable_logging{false};
bool silent_until_main{false};
bool use_relative_time{false};
};
class windows_emulator
{
public:
windows_emulator(std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
windows_emulator(emulator_settings settings,
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
public:
windows_emulator(std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
windows_emulator(emulator_settings settings, std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
windows_emulator(windows_emulator&&) = delete;
windows_emulator(const windows_emulator&) = delete;
windows_emulator& operator=(windows_emulator&&) = delete;
windows_emulator& operator=(const windows_emulator&) = delete;
windows_emulator(windows_emulator&&) = delete;
windows_emulator(const windows_emulator&) = delete;
windows_emulator& operator=(windows_emulator&&) = delete;
windows_emulator& operator=(const windows_emulator&) = delete;
~windows_emulator() = default;
~windows_emulator() = default;
x64_emulator& emu()
{
return *this->emu_;
}
x64_emulator& emu()
{
return *this->emu_;
}
const x64_emulator& emu() const
{
return *this->emu_;
}
const x64_emulator& emu() const
{
return *this->emu_;
}
process_context& process()
{
return this->process_;
}
process_context& process()
{
return this->process_;
}
const process_context& process() const
{
return this->process_;
}
const process_context& process() const
{
return this->process_;
}
syscall_dispatcher& dispatcher()
{
return this->dispatcher_;
}
syscall_dispatcher& dispatcher()
{
return this->dispatcher_;
}
const syscall_dispatcher& dispatcher() const
{
return this->dispatcher_;
}
const syscall_dispatcher& dispatcher() const
{
return this->dispatcher_;
}
emulator_thread& current_thread() const
{
if (!this->process_.active_thread)
{
throw std::runtime_error("No active thread!");
}
emulator_thread& current_thread() const
{
if (!this->process_.active_thread)
{
throw std::runtime_error("No active thread!");
}
return *this->process_.active_thread;
}
return *this->process_.active_thread;
}
void start(std::chrono::nanoseconds timeout = {}, size_t count = 0);
void start(std::chrono::nanoseconds timeout = {}, size_t count = 0);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
void save_snapshot();
void restore_snapshot();
void save_snapshot();
void restore_snapshot();
void add_syscall_hook(instruction_hook_callback callback)
{
this->syscall_hooks_.push_back(std::move(callback));
}
void add_syscall_hook(instruction_hook_callback callback)
{
this->syscall_hooks_.push_back(std::move(callback));
}
void on_stdout(const std::string_view data) const
{
if (this->stdout_callback_)
{
this->stdout_callback_(data);
}
}
void on_stdout(const std::string_view data) const
{
if (this->stdout_callback_)
{
this->stdout_callback_(data);
}
}
logger log{};
bool verbose{false};
bool verbose_calls{false};
bool buffer_stdout{false};
bool fuzzing{false};
bool switch_thread{false};
logger log{};
bool verbose{false};
bool verbose_calls{false};
bool buffer_stdout{false};
bool fuzzing{false};
bool switch_thread{false};
void yield_thread();
void perform_thread_switch();
void yield_thread();
void perform_thread_switch();
bool time_is_relative() const
{
return this->use_relative_time_;
}
bool time_is_relative() const
{
return this->use_relative_time_;
}
private:
bool use_relative_time_{false};
bool silent_until_main_{false};
std::unique_ptr<x64_emulator> emu_{};
std::vector<instruction_hook_callback> syscall_hooks_{};
std::function<void(std::string_view)> stdout_callback_{};
private:
bool use_relative_time_{false};
bool silent_until_main_{false};
std::unique_ptr<x64_emulator> emu_{};
std::vector<instruction_hook_callback> syscall_hooks_{};
std::function<void(std::string_view)> stdout_callback_{};
process_context process_;
syscall_dispatcher dispatcher_;
process_context process_;
syscall_dispatcher dispatcher_;
std::vector<std::byte> process_snapshot_{};
//std::optional<process_context> process_snapshot_{};
std::vector<std::byte> process_snapshot_{};
// std::optional<process_context> process_snapshot_{};
void setup_hooks();
void setup_process(const emulator_settings& settings);
void on_instruction_execution(uint64_t address);
void setup_hooks();
void setup_process(const emulator_settings& settings);
void on_instruction_execution(uint64_t address);
};