mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Compare commits
4 Commits
e0c386abbb
...
32bit-emul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b76880cf1 | ||
|
|
60931da92a | ||
|
|
4b5d82079c | ||
|
|
9c2a0d946e |
715
src/backends/unicorn-emulator/unicorn_x86_32_emulator.cpp
Normal file
715
src/backends/unicorn-emulator/unicorn_x86_32_emulator.cpp
Normal file
@@ -0,0 +1,715 @@
|
||||
#define UNICORN_EMULATOR_IMPL
|
||||
#include "unicorn_x86_32_emulator.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "unicorn_memory_regions.hpp"
|
||||
#include "unicorn_hook.hpp"
|
||||
|
||||
#include "function_wrapper.hpp"
|
||||
#include <ranges>
|
||||
|
||||
namespace unicorn
|
||||
{
|
||||
namespace
|
||||
{
|
||||
static_assert(static_cast<uint32_t>(memory_permission::none) == UC_PROT_NONE);
|
||||
static_assert(static_cast<uint32_t>(memory_permission::read) == UC_PROT_READ);
|
||||
static_assert(static_cast<uint32_t>(memory_permission::exec) == UC_PROT_EXEC);
|
||||
static_assert(static_cast<uint32_t>(memory_permission::all) == UC_PROT_ALL);
|
||||
|
||||
static_assert(static_cast<uint32_t>(x86_register::end) == UC_X86_REG_ENDING);
|
||||
|
||||
uc_x86_insn map_hookable_instruction(const x86_hookable_instructions instruction)
|
||||
{
|
||||
switch (instruction)
|
||||
{
|
||||
case x86_hookable_instructions::syscall:
|
||||
return UC_X86_INS_SYSCALL;
|
||||
case x86_hookable_instructions::cpuid:
|
||||
return UC_X86_INS_CPUID;
|
||||
case x86_hookable_instructions::rdtsc:
|
||||
return UC_X86_INS_RDTSC;
|
||||
case x86_hookable_instructions::rdtscp:
|
||||
return UC_X86_INS_RDTSCP;
|
||||
default:
|
||||
throw std::runtime_error("Bad instruction for mapping");
|
||||
}
|
||||
}
|
||||
|
||||
memory_violation_type map_memory_violation_type(const uc_mem_type mem_type)
|
||||
{
|
||||
switch (mem_type)
|
||||
{
|
||||
case UC_MEM_READ_PROT:
|
||||
case UC_MEM_WRITE_PROT:
|
||||
case UC_MEM_FETCH_PROT:
|
||||
return memory_violation_type::protection;
|
||||
case UC_MEM_READ_UNMAPPED:
|
||||
case UC_MEM_WRITE_UNMAPPED:
|
||||
case UC_MEM_FETCH_UNMAPPED:
|
||||
return memory_violation_type::unmapped;
|
||||
default:
|
||||
throw std::runtime_error("Memory type does not constitute a violation");
|
||||
}
|
||||
}
|
||||
|
||||
memory_operation map_memory_operation(const uc_mem_type mem_type)
|
||||
{
|
||||
switch (mem_type)
|
||||
{
|
||||
case UC_MEM_READ:
|
||||
case UC_MEM_READ_PROT:
|
||||
case UC_MEM_READ_AFTER:
|
||||
case UC_MEM_READ_UNMAPPED:
|
||||
return memory_operation::read;
|
||||
case UC_MEM_WRITE:
|
||||
case UC_MEM_WRITE_PROT:
|
||||
case UC_MEM_WRITE_UNMAPPED:
|
||||
return memory_operation::write;
|
||||
case UC_MEM_FETCH:
|
||||
case UC_MEM_FETCH_PROT:
|
||||
case UC_MEM_FETCH_UNMAPPED:
|
||||
return memory_operation::exec;
|
||||
default:
|
||||
return memory_operation::none;
|
||||
}
|
||||
}
|
||||
|
||||
struct hook_object : utils::object
|
||||
{
|
||||
emulator_hook* as_opaque_hook()
|
||||
{
|
||||
return reinterpret_cast<emulator_hook*>(this);
|
||||
}
|
||||
};
|
||||
|
||||
class hook_container : public hook_object
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
requires(std::is_base_of_v<utils::object, T> && std::is_move_constructible_v<T>)
|
||||
void add(T data, unicorn_hook hook)
|
||||
{
|
||||
hook_entry entry{};
|
||||
|
||||
entry.data = std::make_unique<T>(std::move(data));
|
||||
entry.hook = std::move(hook);
|
||||
|
||||
this->hooks_.emplace_back(std::move(entry));
|
||||
}
|
||||
|
||||
private:
|
||||
struct hook_entry
|
||||
{
|
||||
std::unique_ptr<utils::object> data{};
|
||||
unicorn_hook hook{};
|
||||
};
|
||||
|
||||
std::vector<hook_entry> hooks_;
|
||||
};
|
||||
|
||||
struct mmio_callbacks
|
||||
{
|
||||
using read_wrapper = function_wrapper<uint64_t, uc_engine*, uint64_t, unsigned>;
|
||||
using write_wrapper = function_wrapper<void, uc_engine*, uint64_t, unsigned, uint64_t>;
|
||||
|
||||
read_wrapper read{};
|
||||
write_wrapper write{};
|
||||
};
|
||||
|
||||
class uc_context_serializer
|
||||
{
|
||||
public:
|
||||
uc_context_serializer(uc_engine* uc, const bool in_place)
|
||||
: uc_(uc)
|
||||
{
|
||||
if (in_place)
|
||||
{
|
||||
// Unicorn stores pointers in the struct. The serialization here is broken
|
||||
throw std::runtime_error("Memory saving not supported atm");
|
||||
}
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#endif
|
||||
|
||||
uc_ctl_context_mode(uc, UC_CTL_CONTEXT_CPU | (in_place ? UC_CTL_CONTEXT_MEMORY : 0));
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
this->size_ = uc_context_size(uc);
|
||||
uce(uc_context_alloc(uc, &this->context_));
|
||||
}
|
||||
|
||||
~uc_context_serializer()
|
||||
{
|
||||
if (this->context_)
|
||||
{
|
||||
(void)uc_context_free(this->context_);
|
||||
}
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
uce(uc_context_save(this->uc_, this->context_));
|
||||
buffer.write(this->context_, this->size_);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer) const
|
||||
{
|
||||
buffer.read(this->context_, this->size_);
|
||||
uce(uc_context_restore(this->uc_, this->context_));
|
||||
}
|
||||
|
||||
uc_context_serializer(uc_context_serializer&&) = delete;
|
||||
uc_context_serializer(const uc_context_serializer&) = delete;
|
||||
uc_context_serializer& operator=(uc_context_serializer&&) = delete;
|
||||
uc_context_serializer& operator=(const uc_context_serializer&) = delete;
|
||||
|
||||
private:
|
||||
uc_engine* uc_{};
|
||||
uc_context* context_{};
|
||||
size_t size_{};
|
||||
};
|
||||
|
||||
basic_block map_block(const uc_tb& translation_block)
|
||||
{
|
||||
basic_block block{};
|
||||
|
||||
block.address = translation_block.pc;
|
||||
block.instruction_count = translation_block.icount;
|
||||
block.size = translation_block.size;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
void assert_32bit_limit(const size_t size)
|
||||
{
|
||||
if (size > sizeof(uint32_t))
|
||||
{
|
||||
throw std::runtime_error("Exceeded uint32_t size limit");
|
||||
}
|
||||
}
|
||||
|
||||
class unicorn_x86_32_emulator : public x86_32_emulator
|
||||
{
|
||||
public:
|
||||
unicorn_x86_32_emulator()
|
||||
{
|
||||
uce(uc_open(UC_ARCH_X86, UC_MODE_32, &this->uc_));
|
||||
uce(uc_ctl_set_cpu_model(this->uc_, UC_CPU_X86_EPYC_ROME));
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#endif
|
||||
constexpr auto is_64_bit = sizeof(void*) >= 8;
|
||||
uce(uc_ctl_set_tcg_buffer_size(this->uc_, (is_64_bit ? 2 : 1) << 30 /* 2 gb */));
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
|
||||
~unicorn_x86_32_emulator() override
|
||||
{
|
||||
this->hooks_.clear();
|
||||
uc_close(this->uc_);
|
||||
}
|
||||
|
||||
void start(const size_t count) override
|
||||
{
|
||||
this->has_violation_ = false;
|
||||
const auto start = this->read_instruction_pointer();
|
||||
constexpr auto end = std::numeric_limits<uint64_t>::max();
|
||||
const auto res = uc_emu_start(*this, start, end, 0, count);
|
||||
if (res == UC_ERR_OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto is_violation = //
|
||||
res == UC_ERR_READ_UNMAPPED || //
|
||||
res == UC_ERR_WRITE_UNMAPPED || //
|
||||
res == UC_ERR_FETCH_UNMAPPED || //
|
||||
res == UC_ERR_READ_PROT || //
|
||||
res == UC_ERR_WRITE_PROT || //
|
||||
res == UC_ERR_FETCH_PROT;
|
||||
|
||||
if (!is_violation || !this->has_violation_)
|
||||
{
|
||||
uce(res);
|
||||
}
|
||||
}
|
||||
|
||||
void stop() override
|
||||
{
|
||||
uce(uc_emu_stop(*this));
|
||||
}
|
||||
|
||||
void load_gdt(const pointer_type address, const uint32_t limit) override
|
||||
{
|
||||
const std::array<uint32_t, 2> gdtr = {limit, address};
|
||||
this->write_register(x86_register::gdtr, gdtr.data(), gdtr.size() * sizeof(uint64_t));
|
||||
}
|
||||
|
||||
void set_segment_base(const x86_register base, const pointer_type value) override
|
||||
{
|
||||
constexpr auto IA32_FS_BASE_MSR = 0xC0000100;
|
||||
constexpr auto IA32_GS_BASE_MSR = 0xC0000101;
|
||||
|
||||
struct msr_value
|
||||
{
|
||||
uint64_t id{};
|
||||
uint64_t value{};
|
||||
};
|
||||
|
||||
msr_value msr_val{
|
||||
.id = 0,
|
||||
.value = value,
|
||||
};
|
||||
|
||||
switch (base)
|
||||
{
|
||||
case x86_register::fs:
|
||||
case x86_register::fs_base:
|
||||
msr_val.id = IA32_FS_BASE_MSR;
|
||||
break;
|
||||
case x86_register::gs:
|
||||
case x86_register::gs_base:
|
||||
msr_val.id = IA32_GS_BASE_MSR;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
this->write_register(x86_register::msr, &msr_val, sizeof(msr_val));
|
||||
}
|
||||
|
||||
size_t write_raw_register(const int reg, const void* value, const size_t size) override
|
||||
{
|
||||
auto result_size = size;
|
||||
uce(uc_reg_write2(*this, reg, value, &result_size));
|
||||
|
||||
if (size < result_size)
|
||||
{
|
||||
throw std::runtime_error("Register size mismatch: " + std::to_string(size) +
|
||||
" != " + std::to_string(result_size));
|
||||
}
|
||||
|
||||
return result_size;
|
||||
}
|
||||
|
||||
size_t read_raw_register(const int reg, void* value, const size_t size) override
|
||||
{
|
||||
size_t result_size = size;
|
||||
memset(value, 0, size);
|
||||
uce(uc_reg_read2(*this, reg, value, &result_size));
|
||||
|
||||
if (size < result_size)
|
||||
{
|
||||
throw std::runtime_error("Register size mismatch: " + std::to_string(size) +
|
||||
" != " + std::to_string(result_size));
|
||||
}
|
||||
|
||||
return result_size;
|
||||
}
|
||||
|
||||
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
|
||||
mmio_write_callback write_cb) override
|
||||
{
|
||||
auto read_wrapper = [c = std::move(read_cb)](uc_engine*, const uint64_t addr, const uint32_t s) {
|
||||
assert_32bit_limit(s);
|
||||
uint64_t value{};
|
||||
c(addr, &value, s);
|
||||
return value;
|
||||
};
|
||||
|
||||
auto write_wrapper = [c = std::move(write_cb)](uc_engine*, const uint64_t addr, const uint32_t s,
|
||||
const uint64_t value) {
|
||||
assert_32bit_limit(s);
|
||||
c(addr, &value, s);
|
||||
};
|
||||
|
||||
mmio_callbacks cb{
|
||||
.read = mmio_callbacks::read_wrapper(std::move(read_wrapper)),
|
||||
.write = mmio_callbacks::write_wrapper(std::move(write_wrapper)),
|
||||
};
|
||||
|
||||
uce(uc_mmio_map(*this, address, size, cb.read.get_c_function(), cb.read.get_user_data(),
|
||||
cb.write.get_c_function(), cb.write.get_user_data()));
|
||||
|
||||
this->mmio_[address] = std::move(cb);
|
||||
}
|
||||
|
||||
void map_memory(const uint64_t address, const size_t size, memory_permission permissions) override
|
||||
{
|
||||
uce(uc_mem_map(*this, address, size, static_cast<uint32_t>(permissions)));
|
||||
}
|
||||
|
||||
void unmap_memory(const uint64_t address, const size_t size) override
|
||||
{
|
||||
uce(uc_mem_unmap(*this, address, size));
|
||||
|
||||
const auto mmio_entry = this->mmio_.find(address);
|
||||
if (mmio_entry != this->mmio_.end())
|
||||
{
|
||||
this->mmio_.erase(mmio_entry);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_read_memory(const uint64_t address, void* data, const size_t size) const override
|
||||
{
|
||||
return uc_mem_read(*this, address, data, size) == UC_ERR_OK;
|
||||
}
|
||||
|
||||
void read_memory(const uint64_t address, void* data, const size_t size) const override
|
||||
{
|
||||
uce(uc_mem_read(*this, address, data, size));
|
||||
}
|
||||
|
||||
void write_memory(const uint64_t address, const void* data, const size_t size) override
|
||||
{
|
||||
uce(uc_mem_write(*this, address, data, size));
|
||||
}
|
||||
|
||||
void apply_memory_protection(const uint64_t address, const size_t size,
|
||||
memory_permission permissions) override
|
||||
{
|
||||
uce(uc_mem_protect(*this, address, size, static_cast<uint32_t>(permissions)));
|
||||
}
|
||||
|
||||
emulator_hook* hook_instruction(const int instruction_type, instruction_hook_callback callback) override
|
||||
{
|
||||
unicorn_hook hook{*this};
|
||||
auto container = std::make_unique<hook_container>();
|
||||
|
||||
const auto inst_type = static_cast<x86_hookable_instructions>(instruction_type);
|
||||
|
||||
if (inst_type == x86_hookable_instructions::invalid)
|
||||
{
|
||||
function_wrapper<int, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) {
|
||||
return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0;
|
||||
});
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN_INVALID, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
}
|
||||
else if (inst_type == x86_hookable_instructions::syscall)
|
||||
{
|
||||
function_wrapper<void, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) { c(); });
|
||||
|
||||
const auto uc_instruction = map_hookable_instruction(inst_type);
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max(),
|
||||
uc_instruction));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
}
|
||||
else
|
||||
{
|
||||
function_wrapper<int, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) {
|
||||
return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0;
|
||||
});
|
||||
|
||||
const auto uc_instruction = map_hookable_instruction(inst_type);
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max(),
|
||||
uc_instruction));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
}
|
||||
|
||||
auto* result = container->as_opaque_hook();
|
||||
|
||||
this->hooks_.push_back(std::move(container));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
emulator_hook* hook_basic_block(basic_block_hook_callback callback) override
|
||||
{
|
||||
function_wrapper<void, uc_engine*, uint64_t, size_t> wrapper(
|
||||
[c = std::move(callback)](uc_engine*, const uint64_t address, const size_t size) {
|
||||
basic_block block{};
|
||||
block.address = address;
|
||||
block.size = size;
|
||||
|
||||
c(block);
|
||||
});
|
||||
|
||||
unicorn_hook hook{*this};
|
||||
auto container = std::make_unique<hook_container>();
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_BLOCK, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
|
||||
auto* result = container->as_opaque_hook();
|
||||
this->hooks_.push_back(std::move(container));
|
||||
return result;
|
||||
}
|
||||
|
||||
emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) override
|
||||
{
|
||||
function_wrapper<void, uc_engine*, uc_tb*, uc_tb*> wrapper(
|
||||
[c = std::move(callback)](uc_engine*, const uc_tb* cur_tb, const uc_tb* prev_tb) {
|
||||
const auto current_block = map_block(*cur_tb);
|
||||
const auto previous_block = map_block(*prev_tb);
|
||||
|
||||
c(current_block, previous_block);
|
||||
});
|
||||
|
||||
unicorn_hook hook{*this};
|
||||
auto container = std::make_unique<hook_container>();
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_EDGE_GENERATED, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
|
||||
auto* result = container->as_opaque_hook();
|
||||
this->hooks_.push_back(std::move(container));
|
||||
return result;
|
||||
}
|
||||
|
||||
emulator_hook* hook_interrupt(interrupt_hook_callback callback) override
|
||||
{
|
||||
function_wrapper<void, uc_engine*, int> wrapper(
|
||||
[c = std::move(callback)](uc_engine*, const int interrupt_type) { c(interrupt_type); });
|
||||
|
||||
unicorn_hook hook{*this};
|
||||
auto container = std::make_unique<hook_container>();
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INTR, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
|
||||
auto* result = container->as_opaque_hook();
|
||||
this->hooks_.push_back(std::move(container));
|
||||
return result;
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) override
|
||||
{
|
||||
function_wrapper<bool, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
|
||||
[c = std::move(callback), this](uc_engine*, const uc_mem_type type, const uint64_t address,
|
||||
const int size, const int64_t) {
|
||||
const auto ip = this->read_instruction_pointer();
|
||||
|
||||
assert(size >= 0);
|
||||
const auto operation = map_memory_operation(type);
|
||||
const auto violation = map_memory_violation_type(type);
|
||||
|
||||
const auto resume = c(address, static_cast<uint64_t>(size), operation, violation) ==
|
||||
memory_violation_continuation::resume;
|
||||
|
||||
const auto has_ip_changed = ip != this->read_instruction_pointer();
|
||||
|
||||
if (!resume)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->has_violation_ = resume && has_ip_changed;
|
||||
|
||||
if (has_ip_changed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
unicorn_hook hook{*this};
|
||||
auto container = std::make_unique<hook_container>();
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_INVALID, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<uint64_t>::max()));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
|
||||
auto* result = container->as_opaque_hook();
|
||||
this->hooks_.push_back(std::move(container));
|
||||
return result;
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_execution(const uint64_t address, const uint64_t size,
|
||||
memory_execution_hook_callback callback)
|
||||
{
|
||||
auto exec_wrapper = [c = std::move(callback)](uc_engine*, const uint64_t address,
|
||||
const uint32_t /*size*/) {
|
||||
c(address); //
|
||||
};
|
||||
|
||||
function_wrapper<void, uc_engine*, uint64_t, uint32_t> wrapper(std::move(exec_wrapper));
|
||||
|
||||
unicorn_hook hook{*this};
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_CODE, wrapper.get_function(),
|
||||
wrapper.get_user_data(), address, address + size));
|
||||
|
||||
auto* container = this->create_hook_container();
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
return container->as_opaque_hook();
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) override
|
||||
{
|
||||
return this->hook_memory_execution(0, std::numeric_limits<uint64_t>::max(), std::move(callback));
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_execution(const uint64_t address,
|
||||
memory_execution_hook_callback callback) override
|
||||
{
|
||||
return this->hook_memory_execution(address, 1, std::move(callback));
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size,
|
||||
memory_access_hook_callback callback) override
|
||||
{
|
||||
auto read_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type,
|
||||
const uint64_t address, const int length,
|
||||
const uint64_t value) {
|
||||
const auto operation = map_memory_operation(type);
|
||||
if (operation == memory_operation::read && length > 0)
|
||||
{
|
||||
c(address, &value, std::min(static_cast<size_t>(length), sizeof(value)));
|
||||
}
|
||||
};
|
||||
|
||||
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
|
||||
std::move(read_wrapper));
|
||||
|
||||
unicorn_hook hook{*this};
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_READ_AFTER, wrapper.get_function(),
|
||||
wrapper.get_user_data(), address, address + size));
|
||||
|
||||
auto* container = this->create_hook_container();
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
return container->as_opaque_hook();
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size,
|
||||
memory_access_hook_callback callback) override
|
||||
{
|
||||
auto write_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, const uint64_t addr,
|
||||
const int length, const uint64_t value) {
|
||||
const auto operation = map_memory_operation(type);
|
||||
if (operation == memory_operation::write && length > 0)
|
||||
{
|
||||
c(addr, &value, std::min(static_cast<size_t>(length), sizeof(value)));
|
||||
}
|
||||
};
|
||||
|
||||
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
|
||||
std::move(write_wrapper));
|
||||
|
||||
unicorn_hook hook{*this};
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_WRITE, wrapper.get_function(),
|
||||
wrapper.get_user_data(), address, address + size));
|
||||
|
||||
auto* container = this->create_hook_container();
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
return container->as_opaque_hook();
|
||||
}
|
||||
|
||||
hook_container* create_hook_container()
|
||||
{
|
||||
auto container = std::make_unique<hook_container>();
|
||||
auto* ptr = container.get();
|
||||
this->hooks_.push_back(std::move(container));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void delete_hook(emulator_hook* hook) override
|
||||
{
|
||||
const auto entry =
|
||||
std::ranges::find_if(this->hooks_, [&](const std::unique_ptr<hook_object>& hook_ptr) {
|
||||
return hook_ptr->as_opaque_hook() == hook;
|
||||
});
|
||||
|
||||
if (entry != this->hooks_.end())
|
||||
{
|
||||
this->hooks_.erase(entry);
|
||||
}
|
||||
}
|
||||
|
||||
operator uc_engine*() const
|
||||
{
|
||||
return this->uc_;
|
||||
}
|
||||
|
||||
void serialize_state(utils::buffer_serializer& buffer, const bool is_snapshot) const override
|
||||
{
|
||||
if (this->has_snapshots_ && !is_snapshot)
|
||||
{
|
||||
// TODO: Investigate if this is really necessary
|
||||
throw std::runtime_error("Unable to serialize after snapshot was taken!");
|
||||
}
|
||||
|
||||
this->has_snapshots_ |= is_snapshot;
|
||||
|
||||
const uc_context_serializer serializer(this->uc_, is_snapshot);
|
||||
serializer.serialize(buffer);
|
||||
}
|
||||
|
||||
void deserialize_state(utils::buffer_deserializer& buffer, const bool is_snapshot) override
|
||||
{
|
||||
if (this->has_snapshots_ && !is_snapshot)
|
||||
{
|
||||
// TODO: Investigate if this is really necessary
|
||||
throw std::runtime_error("Unable to deserialize after snapshot was taken!");
|
||||
}
|
||||
|
||||
const uc_context_serializer serializer(this->uc_, is_snapshot);
|
||||
serializer.deserialize(buffer);
|
||||
}
|
||||
|
||||
std::vector<std::byte> save_registers() const override
|
||||
{
|
||||
utils::buffer_serializer buffer{};
|
||||
const uc_context_serializer serializer(this->uc_, false);
|
||||
serializer.serialize(buffer);
|
||||
return buffer.move_buffer();
|
||||
}
|
||||
|
||||
void restore_registers(const std::vector<std::byte>& register_data) override
|
||||
{
|
||||
utils::buffer_deserializer buffer{register_data};
|
||||
const uc_context_serializer serializer(this->uc_, false);
|
||||
serializer.deserialize(buffer);
|
||||
}
|
||||
|
||||
bool has_violation() const override
|
||||
{
|
||||
return this->has_violation_;
|
||||
}
|
||||
|
||||
std::string get_name() const override
|
||||
{
|
||||
return "Unicorn Engine (32bit)";
|
||||
}
|
||||
|
||||
private:
|
||||
mutable bool has_snapshots_{false};
|
||||
uc_engine* uc_{};
|
||||
bool has_violation_{false};
|
||||
std::vector<std::unique_ptr<hook_object>> hooks_{};
|
||||
std::unordered_map<uint64_t, mmio_callbacks> mmio_{};
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<x86_32_emulator> create_x86_32_emulator()
|
||||
{
|
||||
return std::make_unique<unicorn_x86_32_emulator>();
|
||||
}
|
||||
}
|
||||
19
src/backends/unicorn-emulator/unicorn_x86_32_emulator.hpp
Normal file
19
src/backends/unicorn-emulator/unicorn_x86_32_emulator.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <arch_emulator.hpp>
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#ifdef UNICORN_EMULATOR_IMPL
|
||||
#define UNICORN_EMULATOR_DLL_STORAGE EXPORT_SYMBOL
|
||||
#else
|
||||
#define UNICORN_EMULATOR_DLL_STORAGE IMPORT_SYMBOL
|
||||
#endif
|
||||
|
||||
namespace unicorn
|
||||
{
|
||||
#if !MOMO_BUILD_AS_LIBRARY
|
||||
UNICORN_EMULATOR_DLL_STORAGE
|
||||
#endif
|
||||
std::unique_ptr<x86_32_emulator> create_x86_32_emulator();
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <system_error>
|
||||
#include <variant>
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
@@ -342,4 +346,63 @@ struct SECTION_IMAGE_INFORMATION
|
||||
ULONG CheckSum;
|
||||
};
|
||||
|
||||
namespace winpe
|
||||
{
|
||||
|
||||
enum class pe_arch
|
||||
{
|
||||
pe32,
|
||||
pe64
|
||||
};
|
||||
|
||||
inline std::variant<pe_arch, std::error_code> get_pe_arch(const std::filesystem::path& file)
|
||||
{
|
||||
std::ifstream f(file, std::ios::binary);
|
||||
if (!f)
|
||||
{
|
||||
return std::make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
PEDosHeader_t dos{};
|
||||
f.read(reinterpret_cast<char*>(&dos), sizeof(dos));
|
||||
if (!f || dos.e_magic != PEDosHeader_t::k_Magic)
|
||||
{
|
||||
return std::make_error_code(std::errc::executable_format_error);
|
||||
}
|
||||
|
||||
f.seekg(dos.e_lfanew, std::ios::beg);
|
||||
uint32_t nt_signature = 0;
|
||||
f.read(reinterpret_cast<char*>(&nt_signature), sizeof(nt_signature));
|
||||
if (!f || nt_signature != PENTHeaders_t<std::uint32_t>::k_Signature)
|
||||
{
|
||||
return std::make_error_code(std::errc::executable_format_error);
|
||||
}
|
||||
|
||||
PEFileHeader_t file_header{};
|
||||
f.read(reinterpret_cast<char*>(&file_header), sizeof(file_header));
|
||||
if (!f)
|
||||
{
|
||||
return std::make_error_code(std::errc::executable_format_error);
|
||||
}
|
||||
|
||||
uint16_t magic = 0;
|
||||
f.read(reinterpret_cast<char*>(&magic), sizeof(magic));
|
||||
if (!f)
|
||||
{
|
||||
return std::make_error_code(std::errc::executable_format_error);
|
||||
}
|
||||
|
||||
if (magic == PEOptionalHeader_t<std::uint32_t>::k_Magic)
|
||||
{
|
||||
return pe_arch::pe32;
|
||||
}
|
||||
if (magic == PEOptionalHeader_t<std::uint64_t>::k_Magic)
|
||||
{
|
||||
return pe_arch::pe64;
|
||||
}
|
||||
|
||||
return std::make_error_code(std::errc::executable_format_error);
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
@@ -64,3 +64,16 @@ struct x86_64_traits
|
||||
};
|
||||
|
||||
using x86_64_emulator = x86_emulator<x86_64_traits>;
|
||||
|
||||
// --[x86_32]-------------------------------------------------------------------------
|
||||
|
||||
struct x86_32_traits
|
||||
{
|
||||
using pointer_type = uint32_t;
|
||||
using register_type = x86_register;
|
||||
static constexpr register_type instruction_pointer = x86_register::eip;
|
||||
static constexpr register_type stack_pointer = x86_register::esp;
|
||||
using hookable_instructions = x86_hookable_instructions;
|
||||
};
|
||||
|
||||
using x86_32_emulator = x86_emulator<x86_32_traits>;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace
|
||||
win_emu.log.disable_output(true);
|
||||
win_emu.start();
|
||||
|
||||
if (win_emu.process.exception_rip.has_value())
|
||||
if (win_emu.process.exception_ip.has_value())
|
||||
{
|
||||
throw std::runtime_error("Exception!");
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace
|
||||
emu.reg(x86_register::rsp, stack_end);
|
||||
}
|
||||
|
||||
bool is_object_signaled(process_context& c, const handle h, const uint32_t current_thread_id)
|
||||
bool is_object_signaled(process_context64& c, const handle h, const uint32_t current_thread_id)
|
||||
{
|
||||
const auto type = h.value.type;
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
emulator_thread::emulator_thread(memory_manager& memory, const process_context& context, const uint64_t start_address,
|
||||
emulator_thread::emulator_thread(memory_manager& memory, const process_context64& context, const uint64_t start_address,
|
||||
const uint64_t argument, const uint64_t stack_size, const bool suspended,
|
||||
const uint32_t id)
|
||||
: memory_ptr(&memory),
|
||||
@@ -139,7 +139,7 @@ bool emulator_thread::is_terminated() const
|
||||
return this->exit_status.has_value();
|
||||
}
|
||||
|
||||
bool emulator_thread::is_thread_ready(process_context& process, utils::clock& clock)
|
||||
bool emulator_thread::is_thread_ready(process_context64& process, utils::clock& clock)
|
||||
{
|
||||
if (this->is_terminated() || this->suspended > 0)
|
||||
{
|
||||
@@ -208,7 +208,7 @@ bool emulator_thread::is_thread_ready(process_context& process, utils::clock& cl
|
||||
return true;
|
||||
}
|
||||
|
||||
void emulator_thread::setup_registers(x86_64_emulator& emu, const process_context& context) const
|
||||
void emulator_thread::setup_registers(x86_64_emulator& emu, const process_context64& context) const
|
||||
{
|
||||
if (!this->gs_segment)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <utils/moved_marker.hpp>
|
||||
|
||||
struct process_context;
|
||||
struct process_context64;
|
||||
|
||||
struct pending_apc
|
||||
{
|
||||
@@ -48,7 +48,7 @@ class emulator_thread : public ref_counted_object
|
||||
{
|
||||
}
|
||||
|
||||
emulator_thread(memory_manager& memory, const process_context& context, uint64_t start_address, uint64_t argument,
|
||||
emulator_thread(memory_manager& memory, const process_context64& context, uint64_t start_address, uint64_t argument,
|
||||
uint64_t stack_size, bool suspended, uint32_t id);
|
||||
|
||||
emulator_thread(const emulator_thread&) = delete;
|
||||
@@ -103,7 +103,7 @@ class emulator_thread : public ref_counted_object
|
||||
|
||||
bool is_terminated() const;
|
||||
|
||||
bool is_thread_ready(process_context& process, utils::clock& clock);
|
||||
bool is_thread_ready(process_context64& process, utils::clock& clock);
|
||||
|
||||
void save(x86_64_emulator& emu)
|
||||
{
|
||||
@@ -115,7 +115,7 @@ class emulator_thread : public ref_counted_object
|
||||
emu.restore_registers(this->last_registers);
|
||||
}
|
||||
|
||||
void setup_if_necessary(x86_64_emulator& emu, const process_context& context)
|
||||
void setup_if_necessary(x86_64_emulator& emu, const process_context64& context)
|
||||
{
|
||||
if (!this->executed_instructions)
|
||||
{
|
||||
@@ -211,7 +211,7 @@ class emulator_thread : public ref_counted_object
|
||||
}
|
||||
|
||||
private:
|
||||
void setup_registers(x86_64_emulator& emu, const process_context& context) const;
|
||||
void setup_registers(x86_64_emulator& emu, const process_context64& context) const;
|
||||
|
||||
void release()
|
||||
{
|
||||
|
||||
@@ -48,13 +48,11 @@ class object_wrapper
|
||||
|
||||
class windows_emulator;
|
||||
class module_manager;
|
||||
struct process_context;
|
||||
|
||||
using clock_wrapper = object_wrapper<utils::clock>;
|
||||
using x64_emulator_wrapper = object_wrapper<x86_64_emulator>;
|
||||
using memory_manager_wrapper = object_wrapper<memory_manager>;
|
||||
using module_manager_wrapper = object_wrapper<module_manager>;
|
||||
using process_context_wrapper = object_wrapper<process_context>;
|
||||
using windows_emulator_wrapper = object_wrapper<windows_emulator>;
|
||||
using socket_factory_wrapper = object_wrapper<network::socket_factory>;
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch_exception(x86_64_emulator& emu, const process_context& proc, const DWORD status,
|
||||
void dispatch_exception(x86_64_emulator& emu, const process_context64& proc, const DWORD status,
|
||||
const std::vector<EmulatorTraits<Emu64>::ULONG_PTR>& parameters)
|
||||
{
|
||||
CONTEXT64 ctx{};
|
||||
@@ -172,7 +172,7 @@ void dispatch_exception(x86_64_emulator& emu, const process_context& proc, const
|
||||
dispatch_exception_pointers(emu, proc.ki_user_exception_dispatcher, pointers);
|
||||
}
|
||||
|
||||
void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc, const uint64_t address,
|
||||
void dispatch_access_violation(x86_64_emulator& emu, const process_context64& proc, const uint64_t address,
|
||||
const memory_operation operation)
|
||||
{
|
||||
dispatch_exception(emu, proc, STATUS_ACCESS_VIOLATION,
|
||||
@@ -182,22 +182,22 @@ void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc
|
||||
});
|
||||
}
|
||||
|
||||
void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context& proc)
|
||||
void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context64& proc)
|
||||
{
|
||||
dispatch_exception(emu, proc, STATUS_ILLEGAL_INSTRUCTION, {});
|
||||
}
|
||||
|
||||
void dispatch_integer_division_by_zero(x86_64_emulator& emu, const process_context& proc)
|
||||
void dispatch_integer_division_by_zero(x86_64_emulator& emu, const process_context64& proc)
|
||||
{
|
||||
dispatch_exception(emu, proc, STATUS_INTEGER_DIVIDE_BY_ZERO, {});
|
||||
}
|
||||
|
||||
void dispatch_single_step(x86_64_emulator& emu, const process_context& proc)
|
||||
void dispatch_single_step(x86_64_emulator& emu, const process_context64& proc)
|
||||
{
|
||||
dispatch_exception(emu, proc, STATUS_SINGLE_STEP, {});
|
||||
}
|
||||
|
||||
void dispatch_breakpoint(x86_64_emulator& emu, const process_context& proc)
|
||||
void dispatch_breakpoint(x86_64_emulator& emu, const process_context64& proc)
|
||||
{
|
||||
dispatch_exception(emu, proc, STATUS_BREAKPOINT, {});
|
||||
}
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
#include <platform/traits.hpp>
|
||||
#include <platform/primitives.hpp>
|
||||
|
||||
struct process_context;
|
||||
struct process_context64;
|
||||
|
||||
void dispatch_exception(x86_64_emulator& emu, const process_context& proc, DWORD status,
|
||||
void dispatch_exception(x86_64_emulator& emu, const process_context64& proc, DWORD status,
|
||||
const std::vector<EmulatorTraits<Emu64>::ULONG_PTR>& parameters);
|
||||
template <typename T>
|
||||
requires(std::is_integral_v<T> && !std::is_same_v<T, DWORD>)
|
||||
void dispatch_exception(x86_64_emulator& emu, const process_context& proc, const T status,
|
||||
void dispatch_exception(x86_64_emulator& emu, const process_context64& proc, const T status,
|
||||
const std::vector<EmulatorTraits<Emu64>::ULONG_PTR>& parameters)
|
||||
{
|
||||
dispatch_exception(emu, proc, static_cast<DWORD>(status), parameters);
|
||||
}
|
||||
|
||||
void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc, uint64_t address,
|
||||
void dispatch_access_violation(x86_64_emulator& emu, const process_context64& proc, uint64_t address,
|
||||
memory_operation operation);
|
||||
void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context& proc);
|
||||
void dispatch_integer_division_by_zero(x86_64_emulator& emu, const process_context& proc);
|
||||
void dispatch_single_step(x86_64_emulator& emu, const process_context& proc);
|
||||
void dispatch_breakpoint(x86_64_emulator& emu, const process_context& proc);
|
||||
void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context64& proc);
|
||||
void dispatch_integer_division_by_zero(x86_64_emulator& emu, const process_context64& proc);
|
||||
void dispatch_single_step(x86_64_emulator& emu, const process_context64& proc);
|
||||
void dispatch_breakpoint(x86_64_emulator& emu, const process_context64& proc);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "handles.hpp"
|
||||
|
||||
class windows_emulator;
|
||||
struct process_context;
|
||||
|
||||
struct io_device_context
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <utils/time.hpp>
|
||||
|
||||
struct process_context;
|
||||
class windows_emulator;
|
||||
|
||||
class kusd_mmio
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "module_manager.hpp"
|
||||
#include "module_mapping.hpp"
|
||||
#include "platform/win_pefile.hpp"
|
||||
#include "windows-emulator/logger.hpp"
|
||||
|
||||
#include <serialization_helper.hpp>
|
||||
@@ -119,7 +120,28 @@ mapped_module* module_manager::map_local_module(const std::filesystem::path& fil
|
||||
|
||||
try
|
||||
{
|
||||
auto mod = map_module_from_file(*this->memory_, std::move(local_file));
|
||||
mapped_module mod;
|
||||
auto petype_result = winpe::get_pe_arch(file);
|
||||
if (std::holds_alternative<winpe::pe_arch>(petype_result))
|
||||
{
|
||||
if (std::get<winpe::pe_arch>(petype_result) == winpe::pe_arch::pe32)
|
||||
{
|
||||
mod = map_module_from_file<std::uint32_t>(*this->memory_, std::move(local_file));
|
||||
}
|
||||
else if (std::get<winpe::pe_arch>(petype_result) == winpe::pe_arch::pe64)
|
||||
{
|
||||
mod = map_module_from_file<std::uint64_t>(*this->memory_, std::move(local_file));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unknown PE architecture");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unknown PE architecture");
|
||||
}
|
||||
|
||||
mod.is_static = is_static;
|
||||
|
||||
logger.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base);
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
uint64_t get_first_section_offset(const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
||||
template <typename T>
|
||||
uint64_t get_first_section_offset(const PENTHeaders_t<T>& nt_headers, const uint64_t nt_headers_offset)
|
||||
{
|
||||
const auto* nt_headers_addr = reinterpret_cast<const uint8_t*>(&nt_headers);
|
||||
const size_t optional_header_offset =
|
||||
@@ -29,8 +30,9 @@ namespace
|
||||
return mem;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const std::byte> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
const PEOptionalHeader_t<T>& optional_header)
|
||||
{
|
||||
const auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
|
||||
@@ -79,8 +81,9 @@ namespace
|
||||
obj.set(new_value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void apply_relocations(const mapped_module& binary, const utils::safe_buffer_accessor<std::byte> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
const PEOptionalHeader_t<T>& optional_header)
|
||||
{
|
||||
const auto delta = binary.image_base - optional_header.ImageBase;
|
||||
if (delta == 0)
|
||||
@@ -141,9 +144,10 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void map_sections(memory_manager& memory, mapped_module& binary,
|
||||
const utils::safe_buffer_accessor<const std::byte> buffer,
|
||||
const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
||||
const utils::safe_buffer_accessor<const std::byte> buffer, const PENTHeaders_t<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>(static_cast<size_t>(first_section_offset));
|
||||
@@ -196,6 +200,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
mapped_module map_module_from_data(memory_manager& memory, const std::span<const std::byte> data,
|
||||
std::filesystem::path file)
|
||||
{
|
||||
@@ -208,10 +213,10 @@ mapped_module map_module_from_data(memory_manager& memory, const std::span<const
|
||||
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();
|
||||
const auto nt_headers = buffer.as<PENTHeaders_t<T>>(nt_headers_offset).get();
|
||||
const auto& optional_header = nt_headers.OptionalHeader;
|
||||
|
||||
if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
|
||||
if (nt_headers.FileHeader.Machine != PEMachineType::I386 && nt_headers.FileHeader.Machine != PEMachineType::AMD64)
|
||||
{
|
||||
throw std::runtime_error("Unsupported architecture!");
|
||||
}
|
||||
@@ -254,6 +259,7 @@ mapped_module map_module_from_data(memory_manager& memory, const std::span<const
|
||||
return binary;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path file)
|
||||
{
|
||||
const auto data = utils::io::read_file(file);
|
||||
@@ -262,10 +268,19 @@ mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path
|
||||
throw std::runtime_error("Bad file data: " + file.string());
|
||||
}
|
||||
|
||||
return map_module_from_data(memory, data, std::move(file));
|
||||
return map_module_from_data<T>(memory, data, std::move(file));
|
||||
}
|
||||
|
||||
bool unmap_module(memory_manager& memory, const mapped_module& mod)
|
||||
{
|
||||
return memory.release_memory(mod.image_base, static_cast<size_t>(mod.size_of_image));
|
||||
}
|
||||
|
||||
template mapped_module map_module_from_data<std::uint32_t>(memory_manager& memory,
|
||||
const std::span<const std::byte> data,
|
||||
std::filesystem::path file);
|
||||
template mapped_module map_module_from_data<std::uint64_t>(memory_manager& memory,
|
||||
const std::span<const std::byte> data,
|
||||
std::filesystem::path file);
|
||||
template mapped_module map_module_from_file<std::uint32_t>(memory_manager& memory, std::filesystem::path file);
|
||||
template mapped_module map_module_from_file<std::uint64_t>(memory_manager& memory, std::filesystem::path file);
|
||||
@@ -4,6 +4,6 @@
|
||||
#include "../memory_manager.hpp"
|
||||
|
||||
mapped_module map_module_from_data(memory_manager& memory, std::span<const uint8_t> data, std::filesystem::path file);
|
||||
template <typename T>
|
||||
mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path file);
|
||||
|
||||
bool unmap_module(memory_manager& memory, const mapped_module& mod);
|
||||
|
||||
@@ -27,9 +27,9 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void process_context::setup(x86_64_emulator& emu, memory_manager& memory, const application_settings& app_settings,
|
||||
const mapped_module& executable, const mapped_module& ntdll,
|
||||
const apiset::container& apiset_container)
|
||||
void process_context64::setup(x86_64_emulator& emu, memory_manager& memory, const application_settings& app_settings,
|
||||
const mapped_module& executable, const mapped_module& ntdll,
|
||||
const apiset::container& apiset_container)
|
||||
{
|
||||
setup_gdt(emu, memory);
|
||||
|
||||
@@ -125,7 +125,7 @@ void process_context::setup(x86_64_emulator& emu, memory_manager& memory, const
|
||||
this->default_register_set = emu.save_registers();
|
||||
}
|
||||
|
||||
void process_context::serialize(utils::buffer_serializer& buffer) const
|
||||
void process_context64::serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->current_ip);
|
||||
buffer.write(this->previous_ip);
|
||||
@@ -133,7 +133,7 @@ void process_context::serialize(utils::buffer_serializer& buffer) const
|
||||
buffer.write(this->shared_section_size);
|
||||
buffer.write(this->dbwin_buffer);
|
||||
buffer.write(this->dbwin_buffer_size);
|
||||
buffer.write_optional(this->exception_rip);
|
||||
buffer.write_optional(this->exception_ip);
|
||||
buffer.write_optional(this->exit_status);
|
||||
buffer.write(this->base_allocator);
|
||||
buffer.write(this->peb);
|
||||
@@ -163,7 +163,7 @@ void process_context::serialize(utils::buffer_serializer& buffer) const
|
||||
buffer.write(this->threads.find_handle(this->active_thread).bits);
|
||||
}
|
||||
|
||||
void process_context::deserialize(utils::buffer_deserializer& buffer)
|
||||
void process_context64::deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->current_ip);
|
||||
buffer.read(this->previous_ip);
|
||||
@@ -171,7 +171,7 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
|
||||
buffer.read(this->shared_section_size);
|
||||
buffer.read(this->dbwin_buffer);
|
||||
buffer.read(this->dbwin_buffer_size);
|
||||
buffer.read_optional(this->exception_rip);
|
||||
buffer.read_optional(this->exception_ip);
|
||||
buffer.read_optional(this->exit_status);
|
||||
buffer.read(this->base_allocator);
|
||||
buffer.read(this->peb);
|
||||
@@ -207,7 +207,7 @@ void process_context::deserialize(utils::buffer_deserializer& buffer)
|
||||
this->active_thread = this->threads.get(buffer.read<uint64_t>());
|
||||
}
|
||||
|
||||
generic_handle_store* process_context::get_handle_store(const handle handle)
|
||||
generic_handle_store* process_context64::get_handle_store(const handle handle)
|
||||
{
|
||||
switch (handle.value.type)
|
||||
{
|
||||
@@ -234,8 +234,8 @@ generic_handle_store* process_context::get_handle_store(const handle handle)
|
||||
}
|
||||
}
|
||||
|
||||
handle process_context::create_thread(memory_manager& memory, const uint64_t start_address, const uint64_t argument,
|
||||
const uint64_t stack_size, const bool suspended)
|
||||
handle process_context64::create_thread(memory_manager& memory, const uint64_t start_address, const uint64_t argument,
|
||||
const uint64_t stack_size, const bool suspended)
|
||||
{
|
||||
emulator_thread t{memory, *this, start_address, argument, stack_size, suspended, ++this->spawned_thread_count};
|
||||
auto [h, thr] = this->threads.store_and_get(std::move(t));
|
||||
@@ -243,7 +243,9 @@ handle process_context::create_thread(memory_manager& memory, const uint64_t sta
|
||||
return h;
|
||||
}
|
||||
|
||||
uint16_t process_context::add_or_find_atom(std::u16string name)
|
||||
// --[ process_context_common ]-----------------------------------------------------------------------------------------
|
||||
|
||||
uint16_t process_context_common::add_or_find_atom(std::u16string name)
|
||||
{
|
||||
uint16_t index = 0;
|
||||
if (!atoms.empty())
|
||||
@@ -286,7 +288,7 @@ uint16_t process_context::add_or_find_atom(std::u16string name)
|
||||
return index;
|
||||
}
|
||||
|
||||
bool process_context::delete_atom(const std::u16string& name)
|
||||
bool process_context_common::delete_atom(const std::u16string& name)
|
||||
{
|
||||
for (auto it = atoms.begin(); it != atoms.end(); ++it)
|
||||
{
|
||||
@@ -303,7 +305,7 @@ bool process_context::delete_atom(const std::u16string& name)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool process_context::delete_atom(uint16_t atom_id)
|
||||
bool process_context_common::delete_atom(uint16_t atom_id)
|
||||
{
|
||||
const auto it = atoms.find(atom_id);
|
||||
if (it == atoms.end())
|
||||
@@ -319,7 +321,7 @@ bool process_context::delete_atom(uint16_t atom_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::u16string* process_context::get_atom_name(uint16_t atom_id) const
|
||||
const std::u16string* process_context_common::get_atom_name(uint16_t atom_id) const
|
||||
{
|
||||
const auto it = atoms.find(atom_id);
|
||||
if (it == atoms.end())
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
struct emulator_settings;
|
||||
struct application_settings;
|
||||
|
||||
struct process_context
|
||||
struct process_context_common
|
||||
{
|
||||
struct callbacks
|
||||
{
|
||||
@@ -50,7 +50,44 @@ struct process_context
|
||||
atom_entry() = default;
|
||||
};
|
||||
|
||||
process_context(x86_64_emulator& emu, memory_manager& memory, utils::clock& clock, callbacks& cb)
|
||||
std::map<uint16_t, atom_entry> atoms{};
|
||||
uint16_t add_or_find_atom(std::u16string name);
|
||||
bool delete_atom(const std::u16string& name);
|
||||
bool delete_atom(uint16_t atom_id);
|
||||
const std::u16string* get_atom_name(uint16_t atom_id) const;
|
||||
|
||||
uint64_t current_ip{0};
|
||||
uint64_t previous_ip{0};
|
||||
|
||||
uint64_t shared_section_address{0};
|
||||
uint64_t shared_section_size{0};
|
||||
uint64_t dbwin_buffer{0};
|
||||
uint64_t dbwin_buffer_size{0};
|
||||
|
||||
std::optional<NTSTATUS> exit_status{};
|
||||
|
||||
uint64_t ntdll_image_base{};
|
||||
uint64_t ldr_initialize_thunk{};
|
||||
uint64_t rtl_user_thread_start{};
|
||||
uint64_t ki_user_apc_dispatcher{};
|
||||
uint64_t ki_user_exception_dispatcher{};
|
||||
|
||||
std::optional<uint64_t> exception_ip{};
|
||||
|
||||
handle_store<handle_types::event, event> events{};
|
||||
handle_store<handle_types::file, file> files{};
|
||||
handle_store<handle_types::section, section> sections{};
|
||||
handle_store<handle_types::semaphore, semaphore> semaphores{};
|
||||
handle_store<handle_types::port, port> ports{};
|
||||
handle_store<handle_types::mutant, mutant> mutants{};
|
||||
handle_store<handle_types::registry, registry_key, 2> registry_keys{};
|
||||
|
||||
std::vector<std::byte> default_register_set{};
|
||||
};
|
||||
|
||||
struct process_context64 final : process_context_common
|
||||
{
|
||||
process_context64(x86_64_emulator& emu, memory_manager& memory, utils::clock& clock, callbacks& cb)
|
||||
: callbacks_(&cb),
|
||||
base_allocator(emu),
|
||||
peb(emu),
|
||||
@@ -65,11 +102,6 @@ struct process_context
|
||||
handle create_thread(memory_manager& memory, uint64_t start_address, uint64_t argument, uint64_t stack_size,
|
||||
bool suspended);
|
||||
|
||||
uint16_t add_or_find_atom(std::u16string name);
|
||||
bool delete_atom(const std::u16string& name);
|
||||
bool delete_atom(uint16_t atom_id);
|
||||
const std::u16string* get_atom_name(uint16_t atom_id) const;
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
|
||||
@@ -77,40 +109,13 @@ struct process_context
|
||||
|
||||
callbacks* callbacks_{};
|
||||
|
||||
uint64_t current_ip{0};
|
||||
uint64_t previous_ip{0};
|
||||
|
||||
uint64_t shared_section_address{0};
|
||||
uint64_t shared_section_size{0};
|
||||
uint64_t dbwin_buffer{0};
|
||||
uint64_t dbwin_buffer_size{0};
|
||||
|
||||
std::optional<uint64_t> exception_rip{};
|
||||
std::optional<NTSTATUS> exit_status{};
|
||||
|
||||
emulator_allocator base_allocator;
|
||||
|
||||
emulator_object<PEB64> peb;
|
||||
emulator_object<RTL_USER_PROCESS_PARAMETERS64> process_params;
|
||||
kusd_mmio kusd;
|
||||
|
||||
uint64_t ntdll_image_base{};
|
||||
uint64_t ldr_initialize_thunk{};
|
||||
uint64_t rtl_user_thread_start{};
|
||||
uint64_t ki_user_apc_dispatcher{};
|
||||
uint64_t ki_user_exception_dispatcher{};
|
||||
|
||||
handle_store<handle_types::event, event> events{};
|
||||
handle_store<handle_types::file, file> files{};
|
||||
handle_store<handle_types::section, section> sections{};
|
||||
handle_store<handle_types::device, io_device_container> devices{};
|
||||
handle_store<handle_types::semaphore, semaphore> semaphores{};
|
||||
handle_store<handle_types::port, port> ports{};
|
||||
handle_store<handle_types::mutant, mutant> mutants{};
|
||||
handle_store<handle_types::registry, registry_key, 2> registry_keys{};
|
||||
std::map<uint16_t, atom_entry> atoms{};
|
||||
|
||||
std::vector<std::byte> default_register_set{};
|
||||
|
||||
uint32_t spawned_thread_count{0};
|
||||
handle_store<handle_types::thread, emulator_thread> threads{};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "process_context.hpp"
|
||||
#include "module/module_manager.hpp"
|
||||
|
||||
struct syscall_context;
|
||||
using syscall_handler = void (*)(const syscall_context& c);
|
||||
|
||||
@@ -8,7 +8,7 @@ struct syscall_context
|
||||
{
|
||||
windows_emulator& win_emu;
|
||||
x86_64_emulator& emu;
|
||||
process_context& proc;
|
||||
process_context64& proc;
|
||||
mutable bool write_status{true};
|
||||
mutable bool retrigger_syscall{false};
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace syscalls
|
||||
}
|
||||
|
||||
c.proc.exit_status = error_status;
|
||||
c.proc.exception_rip = c.emu.read_instruction_pointer();
|
||||
c.proc.exception_ip = c.emu.read_instruction_pointer();
|
||||
c.emu.stop();
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
@@ -36,7 +36,7 @@ namespace syscalls
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
c.proc.exception_rip = thread_context.read().Rip;
|
||||
c.proc.exception_ip = thread_context.read().Rip;
|
||||
c.emu.stop();
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
emulator_thread* get_thread_by_id(process_context& process, const uint32_t id)
|
||||
emulator_thread* get_thread_by_id(process_context64& process, const uint32_t id)
|
||||
{
|
||||
for (auto& t : process.threads | std::views::values)
|
||||
{
|
||||
@@ -540,7 +540,7 @@ void windows_emulator::setup_hooks()
|
||||
|
||||
if (this->fuzzing || true) // TODO: Fix
|
||||
{
|
||||
this->process.exception_rip = rip;
|
||||
this->process.exception_ip = rip;
|
||||
this->emu().stop();
|
||||
}
|
||||
});
|
||||
@@ -564,7 +564,7 @@ void windows_emulator::setup_hooks()
|
||||
|
||||
if (this->fuzzing)
|
||||
{
|
||||
this->process.exception_rip = ip;
|
||||
this->process.exception_ip = ip;
|
||||
this->emu().stop();
|
||||
return memory_violation_continuation::stop;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
std::unique_ptr<x86_64_emulator> create_default_x86_64_emulator();
|
||||
|
||||
struct emulator_callbacks : module_manager::callbacks, process_context::callbacks
|
||||
struct emulator_callbacks : module_manager::callbacks, process_context64::callbacks
|
||||
{
|
||||
utils::optional_function<instruction_hook_continuation(uint32_t syscall_id, x86_64_emulator::pointer_type address,
|
||||
std::string_view mod_name, std::string_view syscall_name)>
|
||||
@@ -69,7 +69,7 @@ class windows_emulator
|
||||
memory_manager memory;
|
||||
registry_manager registry{};
|
||||
module_manager mod_manager;
|
||||
process_context process;
|
||||
process_context64 process;
|
||||
syscall_dispatcher dispatcher;
|
||||
|
||||
windows_emulator(const emulator_settings& settings = {}, emulator_callbacks callbacks = {},
|
||||
|
||||
Reference in New Issue
Block a user