Comprehensive WOW64 subsystem implementation

This commit is contained in:
brian
2025-10-13 19:53:14 +08:00
parent 9453123db0
commit 65eecf1cfd
51 changed files with 3772 additions and 283 deletions

View File

@@ -40,7 +40,10 @@ namespace
return {};
}
const auto instructions = d.disassemble(instruction_bytes, 1);
uint16_t reg_cs = 0;
auto& emu_ref = const_cast<emulator&>(emu);
emu_ref.read_raw_register(static_cast<int>(x86_register::cs), &reg_cs, sizeof(reg_cs));
const auto instructions = d.disassemble(emu_ref, reg_cs, instruction_bytes, 1);
if (instructions.empty())
{
return {};
@@ -268,26 +271,32 @@ namespace
return false;
}
const auto instructions = d.disassemble(instruction_bytes, 1);
uint16_t reg_cs = 0;
auto& emu_ref = const_cast<emulator&>(emu);
emu_ref.read_raw_register(static_cast<int>(x86_register::cs), &reg_cs, sizeof(reg_cs));
const auto instructions = d.disassemble(emu_ref, reg_cs, instruction_bytes, 1);
if (instructions.empty())
{
return false;
}
return cs_insn_group(d.get_handle(), instructions.data(), CS_GRP_RET);
const auto handle = d.resolve_handle(emu_ref, reg_cs);
return cs_insn_group(handle, instructions.data(), CS_GRP_RET);
}
void record_instruction(analysis_context& c, const uint64_t address)
{
auto& emu = c.win_emu->emu();
std::array<uint8_t, MAX_INSTRUCTION_BYTES> instruction_bytes{};
const auto result = c.win_emu->emu().try_read_memory(address, instruction_bytes.data(), instruction_bytes.size());
const auto result = emu.try_read_memory(address, instruction_bytes.data(), instruction_bytes.size());
if (!result)
{
return;
}
const auto reg_cs = emu.reg<uint16_t>(x86_register::cs);
disassembler disasm{};
const auto instructions = disasm.disassemble(instruction_bytes, 1);
const auto instructions = disasm.disassemble(emu, reg_cs, instruction_bytes, 1);
if (instructions.empty())
{
return;
@@ -308,8 +317,10 @@ namespace
debugger::handle_events(ec);
}
#endif
const auto& current_thread = c.win_emu->current_thread();
const auto previous_ip = current_thread.previous_ip;
[[maybe_unused]] const auto current_ip = current_thread.current_ip;
const auto is_main_exe = win_emu.mod_manager.executable->contains(address);
const auto is_previous_main_exe = win_emu.mod_manager.executable->contains(previous_ip);

View File

@@ -1,72 +1,125 @@
#include "std_include.hpp"
#include "disassembler.hpp"
#include <utils/finally.hpp>
namespace
{
void cse(const cs_err error)
{
if (error != CS_ERR_OK)
{
throw std::runtime_error(cs_strerror(error));
}
}
}
disassembler::disassembler()
{
auto deleter = utils::finally([&] { this->release(); });
cse(cs_open(CS_ARCH_X86, CS_MODE_64, &this->handle_));
cse(cs_option(this->handle_, CS_OPT_DETAIL, CS_OPT_ON));
deleter.cancel();
}
disassembler::~disassembler()
{
this->release();
}
disassembler::disassembler(disassembler&& obj) noexcept
{
this->operator=(std::move(obj));
}
disassembler& disassembler::operator=(disassembler&& obj) noexcept
{
if (this != &obj)
{
this->release();
this->handle_ = obj.handle_;
obj.handle_ = 0;
}
return *this;
}
void disassembler::release()
{
if (this->handle_)
{
cs_close(&this->handle_);
this->handle_ = 0;
}
}
instructions disassembler::disassemble(const std::span<const uint8_t> data, const size_t count) const
{
cs_insn* insts{};
const auto inst_count = cs_disasm(this->handle_, data.data(), data.size(), count, 0, &insts);
return instructions{std::span(insts, inst_count)};
}
void instructions::release()
{
if (!this->instructions_.empty())
{
cs_free(this->instructions_.data(), this->instructions_.size());
}
this->instructions_ = {};
}
#include "std_include.hpp"
#include "disassembler.hpp"
#include "common/segment_utils.hpp"
#include <utils/finally.hpp>
namespace
{
void cse(const cs_err error)
{
if (error != CS_ERR_OK)
{
throw std::runtime_error(cs_strerror(error));
}
}
}
disassembler::disassembler()
{
auto deleter = utils::finally([&] { this->release(); });
cse(cs_open(CS_ARCH_X86, CS_MODE_64, &this->handle_64_));
cse(cs_option(this->handle_64_, CS_OPT_DETAIL, CS_OPT_ON));
cse(cs_open(CS_ARCH_X86, CS_MODE_32, &this->handle_32_));
cse(cs_option(this->handle_32_, CS_OPT_DETAIL, CS_OPT_ON));
cse(cs_open(CS_ARCH_X86, CS_MODE_16, &this->handle_16_));
cse(cs_option(this->handle_16_, CS_OPT_DETAIL, CS_OPT_ON));
deleter.cancel();
}
disassembler::~disassembler()
{
this->release();
}
disassembler::disassembler(disassembler&& obj) noexcept
{
this->operator=(std::move(obj));
}
disassembler& disassembler::operator=(disassembler&& obj) noexcept
{
if (this != &obj)
{
this->release();
this->handle_64_ = obj.handle_64_;
this->handle_32_ = obj.handle_32_;
this->handle_16_ = obj.handle_16_;
obj.handle_64_ = 0;
obj.handle_32_ = 0;
obj.handle_16_ = 0;
}
return *this;
}
void disassembler::release()
{
if (this->handle_64_)
{
cs_close(&this->handle_64_);
this->handle_64_ = 0;
}
if (this->handle_32_)
{
cs_close(&this->handle_32_);
this->handle_32_ = 0;
}
if (this->handle_16_)
{
cs_close(&this->handle_16_);
this->handle_16_ = 0;
}
}
instructions disassembler::disassemble(emulator& cpu, const uint16_t cs_selector, const std::span<const uint8_t> data,
const size_t count) const
{
// Select the handle by decoding the code segment descriptor as documented in Intel 64 and IA-32 Architectures SDM Vol. 3.
const csh handle_to_use = this->resolve_handle(cpu, cs_selector);
cs_insn* insts{};
const auto inst_count = cs_disasm(handle_to_use, data.data(), data.size(), count, 0, &insts);
return instructions{std::span(insts, inst_count)};
}
std::optional<disassembler::segment_bitness> disassembler::get_segment_bitness(emulator& cpu, const uint16_t cs_selector)
{
return segment_utils::get_segment_bitness(cpu, cs_selector);
}
csh disassembler::resolve_handle(emulator& cpu, const uint16_t cs_selector) const
{
const auto mode = disassembler::get_segment_bitness(cpu, cs_selector);
if (!mode)
{
return this->handle_64_;
}
switch (*mode)
{
case segment_bitness::bit16:
return this->handle_16_;
case segment_bitness::bit32:
return this->handle_32_;
case segment_bitness::bit64:
default:
return this->handle_64_;
}
}
void instructions::release()
{
if (!this->instructions_.empty())
{
cs_free(this->instructions_.data(), this->instructions_.size());
}
this->instructions_ = {};
}

View File

@@ -1,6 +1,12 @@
#pragma once
#include <capstone/capstone.h>
#include <optional>
#include <span>
#include "common/segment_utils.hpp"
class emulator;
class instructions
{
@@ -90,15 +96,31 @@ class disassembler
disassembler(const disassembler& obj) = delete;
disassembler& operator=(const disassembler& obj) = delete;
instructions disassemble(std::span<const uint8_t> data, size_t count) const;
using segment_bitness = segment_utils::segment_bitness;
csh get_handle() const
instructions disassemble(emulator& cpu, uint16_t cs_selector, std::span<const uint8_t> data, size_t count) const;
static std::optional<segment_bitness> get_segment_bitness(emulator& cpu, uint16_t cs_selector);
csh resolve_handle(emulator& cpu, uint16_t cs_selector) const;
csh get_handle_64() const
{
return this->handle_;
return this->handle_64_;
}
csh get_handle_32() const
{
return this->handle_32_;
}
csh get_handle_16() const
{
return this->handle_16_;
}
private:
csh handle_{};
csh handle_64_{};
csh handle_32_{};
csh handle_16_{};
void release();
};

View File

@@ -88,7 +88,7 @@ namespace
emulator_object<RTL_USER_PROCESS_PARAMETERS64> get_process_params(windows_emulator& win_emu)
{
const auto peb = win_emu.process.peb.read();
const auto peb = win_emu.process.peb64.read();
return {win_emu.emu(), peb.ProcessParameters};
}
@@ -189,14 +189,14 @@ namespace
(void)concise;
#if !defined(__GNUC__) || defined(__clang__)
watch_object(win_emu, modules, *win_emu.current_thread().teb, verbose);
watch_object(win_emu, modules, win_emu.process.peb, verbose);
watch_object(win_emu, modules, *win_emu.current_thread().teb64, verbose);
watch_object(win_emu, modules, win_emu.process.peb64, verbose);
watch_object<KUSER_SHARED_DATA64>(win_emu, modules, kusd_mmio::address(), verbose);
auto state = std::make_shared<analysis_state>(win_emu, modules, verbose, concise);
state->params_hook_ = watch_object(win_emu, modules, win_emu.process.process_params, verbose, state->params_state_);
state->ldr_hook_ = watch_object<PEB_LDR_DATA64>(win_emu, modules, win_emu.process.peb.read().Ldr, verbose, state->ldr_state_);
state->params_hook_ = watch_object(win_emu, modules, win_emu.process.process_params64, verbose, state->params_state_);
state->ldr_hook_ = watch_object<PEB_LDR_DATA64>(win_emu, modules, win_emu.process.peb64.read().Ldr, verbose, state->ldr_state_);
const auto update_env_hook = [state] {
state->env_ptr_hook_ = install_env_hook(state); //
@@ -204,17 +204,17 @@ namespace
update_env_hook();
win_emu.emu().hook_memory_write(win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
win_emu.emu().hook_memory_write(win_emu.process.peb64.value() + offsetof(PEB64, ProcessParameters), 0x8,
[state, update_env = std::move(update_env_hook)](const uint64_t, const void*, size_t) {
const auto new_ptr = state->win_emu_.process.peb.read().ProcessParameters;
const auto new_ptr = state->win_emu_.process.peb64.read().ProcessParameters;
state->params_hook_ = watch_object<RTL_USER_PROCESS_PARAMETERS64>(
state->win_emu_, state->modules_, new_ptr, state->verbose_, state->params_state_);
update_env();
});
win_emu.emu().hook_memory_write(
win_emu.process.peb.value() + offsetof(PEB64, Ldr), 0x8, [state](const uint64_t, const void*, size_t) {
const auto new_ptr = state->win_emu_.process.peb.read().Ldr;
win_emu.process.peb64.value() + offsetof(PEB64, Ldr), 0x8, [state](const uint64_t, const void*, size_t) {
const auto new_ptr = state->win_emu_.process.peb64.read().Ldr;
state->ldr_hook_ =
watch_object<PEB_LDR_DATA64>(state->win_emu_, state->modules_, new_ptr, state->verbose_, state->ldr_state_);
});
@@ -253,7 +253,11 @@ namespace
{
for (const auto& instruction : instructions)
{
const auto* mnemonic = cs_insn_name(c.d.get_handle(), instruction);
const auto& e = c.win_emu;
auto& emu = e->emu();
const auto reg_cs = emu.reg<uint16_t>(x86_register::cs);
const auto handle = c.d.resolve_handle(emu, reg_cs);
const auto* mnemonic = cs_insn_name(handle, instruction);
c.win_emu->log.print(color::white, "%s: %" PRIu64 "\n", mnemonic, count);
}
}