mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Prepare unicorn isolation
This commit is contained in:
@@ -1,2 +1,4 @@
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(emulator)
|
||||
add_subdirectory(emulator)
|
||||
add_subdirectory(unicorn_emulator)
|
||||
add_subdirectory(windows_emulator)
|
||||
|
||||
@@ -6,19 +6,8 @@ file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
|
||||
|
||||
list(SORT SRC_FILES)
|
||||
|
||||
add_executable(emulator ${SRC_FILES})
|
||||
add_library(emulator ${SRC_FILES})
|
||||
|
||||
momo_assign_source_group(${SRC_FILES})
|
||||
|
||||
target_precompile_headers(emulator PRIVATE std_include.hpp)
|
||||
|
||||
target_link_libraries(emulator PRIVATE
|
||||
common
|
||||
unicorn
|
||||
phnt::phnt
|
||||
reflect
|
||||
target_include_directories(emulator INTERFACE
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
||||
|
||||
set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT emulator)
|
||||
|
||||
momo_strip_target(emulator)
|
||||
|
||||
0
src/emulator/empty.cpp
Normal file
0
src/emulator/empty.cpp
Normal file
92
src/emulator/emulator.hpp
Normal file
92
src/emulator/emulator.hpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
|
||||
#include "memory_permission.hpp"
|
||||
|
||||
struct memory_region
|
||||
{
|
||||
uint64_t start;
|
||||
size_t length;
|
||||
memory_permission pemissions;
|
||||
};
|
||||
|
||||
class emulator
|
||||
{
|
||||
public:
|
||||
emulator() = default;
|
||||
|
||||
emulator(const emulator&) = delete;
|
||||
emulator& operator=(const emulator&) = delete;
|
||||
|
||||
emulator(emulator&&) = delete;
|
||||
emulator& operator=(emulator&&) = delete;
|
||||
|
||||
virtual ~emulator() = default;
|
||||
|
||||
virtual void start(uint64_t start, uint64_t end = 0, std::chrono::microseconds timeout = {}, size_t count = 0) = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual void read_raw_register(int reg, void* value, size_t size) = 0;
|
||||
virtual void write_raw_register(int reg, const void* value, size_t size) = 0;
|
||||
|
||||
virtual void map_memory(uint64_t address, size_t size, memory_permission permissions) = 0;
|
||||
virtual void unmap_memory(uint64_t address, size_t size) = 0;
|
||||
|
||||
virtual void read_memory(uint64_t address, void* data, size_t size) = 0;
|
||||
virtual void write_memory(uint64_t address, const void* data, size_t size) = 0;
|
||||
|
||||
virtual void protect_memory(uint64_t address, size_t size, memory_permission permissions) = 0;
|
||||
|
||||
virtual std::vector<memory_region> get_memory_regions() = 0;
|
||||
};
|
||||
|
||||
template <typename PointerType, typename Register, Register StackPointer>
|
||||
class typed_emulator : public emulator
|
||||
{
|
||||
public:
|
||||
using registers = Register;
|
||||
using pointer_type = PointerType;
|
||||
|
||||
static constexpr size_t pointer_size = sizeof(pointer_type);
|
||||
static constexpr registers stack_pointer = StackPointer;
|
||||
|
||||
void write_register(registers reg, const void* value, const size_t size)
|
||||
{
|
||||
this->write_raw_register(static_cast<int>(reg), value, size);
|
||||
}
|
||||
|
||||
void read_register(registers reg, void* value, const size_t size)
|
||||
{
|
||||
this->read_raw_register(static_cast<int>(reg), value, size);
|
||||
}
|
||||
|
||||
template <typename T = uint64_t>
|
||||
T reg(const registers regid) const
|
||||
{
|
||||
T value{};
|
||||
this->read_register(regid, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T = uint64_t, typename S>
|
||||
void reg(const registers regid, const S& maybe_value) const
|
||||
{
|
||||
T value = static_cast<T>(maybe_value);
|
||||
this->write_register(regid, &value, sizeof(value));
|
||||
}
|
||||
|
||||
pointer_type read_stack(const size_t index) const
|
||||
{
|
||||
uint64_t result{};
|
||||
const auto sp = this->reg(stack_pointer);
|
||||
|
||||
this->read_memory(sp + (index * pointer_size), &result, sizeof(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void read_raw_register(int reg, void* value, size_t size) override = 0;
|
||||
void write_raw_register(int reg, const void* value, size_t size) override = 0;
|
||||
};
|
||||
64
src/emulator/memory_permission.hpp
Normal file
64
src/emulator/memory_permission.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
enum class memory_permission : uint8_t
|
||||
{
|
||||
none = 0,
|
||||
read = 1 << 0,
|
||||
write = 1 << 1,
|
||||
exec = 1 << 2,
|
||||
read_write = read | write,
|
||||
all = read | write | exec
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator&(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>
|
||||
(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator|(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>
|
||||
(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator^(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>
|
||||
(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator~(memory_permission x)
|
||||
{
|
||||
return static_cast<memory_permission>(~static_cast<uint8_t>(x));
|
||||
}
|
||||
|
||||
inline memory_permission&
|
||||
operator&=(memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x = x & y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline memory_permission&
|
||||
operator|=(memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x = x | y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline memory_permission&
|
||||
operator^=(memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x = x ^ y;
|
||||
return x;
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <utils/finally.hpp>
|
||||
|
||||
inline bool is_within_start_and_end(const uint64_t value, const uint64_t start, const uint64_t end)
|
||||
{
|
||||
return value >= start && value < end;
|
||||
}
|
||||
|
||||
inline bool is_within_start_and_length(const uint64_t value, const uint64_t start, const uint64_t length)
|
||||
{
|
||||
return is_within_start_and_end(value, start, start + length);
|
||||
}
|
||||
|
||||
inline uint64_t align_down(const uint64_t value, const uint64_t alignment)
|
||||
{
|
||||
return value & ~(alignment - 1);
|
||||
}
|
||||
|
||||
inline uint64_t align_up(const uint64_t value, const uint64_t alignment)
|
||||
{
|
||||
return align_down(value + (alignment - 1), alignment);
|
||||
}
|
||||
|
||||
inline uint64_t page_align_down(const uint64_t value)
|
||||
{
|
||||
return align_down(value, 0x1000);
|
||||
}
|
||||
|
||||
inline uint64_t page_align_up(const uint64_t value)
|
||||
{
|
||||
return align_up(value, 0x1000);
|
||||
}
|
||||
|
||||
template <typename T = void, typename F>
|
||||
T access_memory_regions(const unicorn& uc, const F& accessor)
|
||||
{
|
||||
uint32_t count{};
|
||||
uc_mem_region* regions{};
|
||||
|
||||
uce(uc_mem_regions(uc, ®ions, &count));
|
||||
const auto _ = utils::finally([&]
|
||||
{
|
||||
uc_free(regions);
|
||||
});
|
||||
|
||||
return accessor(std::span(regions, count));
|
||||
}
|
||||
|
||||
inline uint32_t get_memory_protection(const unicorn& uc, const uint64_t address)
|
||||
{
|
||||
return access_memory_regions<uint32_t>(uc, [&](const std::span<uc_mem_region> regions) -> uint32_t
|
||||
{
|
||||
for (const auto& region : regions)
|
||||
{
|
||||
if (is_within_start_and_end(address, region.begin, region.end))
|
||||
{
|
||||
return region.perms;
|
||||
}
|
||||
}
|
||||
|
||||
return UC_PROT_NONE;
|
||||
});
|
||||
}
|
||||
|
||||
inline bool is_memory_allocated(const unicorn& uc, const uint64_t address)
|
||||
{
|
||||
return access_memory_regions<uint32_t>(uc, [&](const std::span<uc_mem_region> regions)
|
||||
{
|
||||
for (const auto& region : regions)
|
||||
{
|
||||
if (is_within_start_and_end(address, region.begin, region.end))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
inline uint32_t map_nt_to_unicorn_protection(const uint32_t nt_protection)
|
||||
{
|
||||
switch (nt_protection)
|
||||
{
|
||||
case PAGE_NOACCESS:
|
||||
return UC_PROT_NONE;
|
||||
case PAGE_READONLY:
|
||||
return UC_PROT_READ;
|
||||
case PAGE_READWRITE:
|
||||
case PAGE_WRITECOPY:
|
||||
return UC_PROT_READ | UC_PROT_WRITE;
|
||||
case PAGE_EXECUTE:
|
||||
case PAGE_EXECUTE_READ:
|
||||
return UC_PROT_READ | UC_PROT_EXEC;
|
||||
case PAGE_EXECUTE_READWRITE:
|
||||
case PAGE_EXECUTE_WRITECOPY:
|
||||
default:
|
||||
return UC_PROT_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t map_unicorn_to_nt_protection(const uint32_t unicorn_protection)
|
||||
{
|
||||
const bool has_exec = unicorn_protection & UC_PROT_EXEC;
|
||||
const bool has_read = unicorn_protection & UC_PROT_READ;
|
||||
const bool has_write = unicorn_protection & UC_PROT_WRITE;
|
||||
|
||||
if (!has_read)
|
||||
{
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
|
||||
if (has_exec && has_write)
|
||||
{
|
||||
return PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
if (has_exec)
|
||||
{
|
||||
return PAGE_EXECUTE_READ;
|
||||
}
|
||||
|
||||
if (has_write)
|
||||
{
|
||||
return PAGE_READWRITE;
|
||||
}
|
||||
|
||||
return PAGE_READONLY;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "unicorn.hpp"
|
||||
#include "process_context.hpp"
|
||||
|
||||
void handle_syscall(const unicorn& uc, process_context& context);
|
||||
@@ -1,40 +0,0 @@
|
||||
#include "std_include.hpp"
|
||||
#include "unicorn.hpp"
|
||||
|
||||
unicorn::unicorn(const uc_arch arch, const uc_mode mode)
|
||||
{
|
||||
const auto error = uc_open(arch, mode, &this->uc_);
|
||||
ThrowIfUnicornError(error);
|
||||
}
|
||||
|
||||
unicorn::unicorn(unicorn&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
unicorn& unicorn::operator=(unicorn&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->close();
|
||||
|
||||
this->uc_ = obj.uc_;
|
||||
obj.uc_ = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
unicorn::~unicorn()
|
||||
{
|
||||
this->close();
|
||||
}
|
||||
|
||||
void unicorn::close()
|
||||
{
|
||||
if (this->uc_)
|
||||
{
|
||||
uc_close(this->uc_);
|
||||
this->uc_ = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
struct unicorn_error : std::runtime_error
|
||||
{
|
||||
unicorn_error(const uc_err error_code)
|
||||
: std::runtime_error(uc_strerror(error_code))
|
||||
, code(error_code)
|
||||
{
|
||||
}
|
||||
|
||||
uc_err code{};
|
||||
};
|
||||
|
||||
inline void ThrowIfUnicornError(const uc_err error_code)
|
||||
{
|
||||
if (error_code != UC_ERR_OK)
|
||||
{
|
||||
throw unicorn_error(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
#define uce ThrowIfUnicornError
|
||||
|
||||
class unicorn
|
||||
{
|
||||
public:
|
||||
unicorn() = default;
|
||||
unicorn(uc_arch arch, uc_mode mode);
|
||||
|
||||
unicorn(const unicorn& obj) = delete;
|
||||
unicorn& operator=(const unicorn& obj) = delete;
|
||||
|
||||
unicorn(unicorn&& obj) noexcept;
|
||||
unicorn& operator=(unicorn&& obj) noexcept;
|
||||
|
||||
~unicorn();
|
||||
|
||||
void close();
|
||||
|
||||
operator uc_engine*() const
|
||||
{
|
||||
return this->uc_;
|
||||
}
|
||||
|
||||
template <typename T = uint64_t>
|
||||
T reg(const int regid) const
|
||||
{
|
||||
T value{};
|
||||
uce(uc_reg_read(this->uc_, regid, &value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T = uint64_t, typename S>
|
||||
void reg(const int regid, const S& maybe_value) const
|
||||
{
|
||||
T value = static_cast<T>(maybe_value);
|
||||
uce(uc_reg_write(this->uc_, regid, &value));
|
||||
}
|
||||
|
||||
void stop() const
|
||||
{
|
||||
uce(uc_emu_stop(this->uc_));
|
||||
}
|
||||
|
||||
uint64_t read_stack(const size_t index) const
|
||||
{
|
||||
uint64_t result{};
|
||||
const auto rsp = this->reg(UC_X86_REG_RSP);
|
||||
|
||||
uce(uc_mem_read(this->uc_, rsp + (index * sizeof(result)), &result, sizeof(result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
uc_engine* uc_{};
|
||||
};
|
||||
248
src/emulator/x64_emulator.hpp
Normal file
248
src/emulator/x64_emulator.hpp
Normal file
@@ -0,0 +1,248 @@
|
||||
#pragma once
|
||||
#include "emulator.hpp"
|
||||
|
||||
enum class x64_register
|
||||
{
|
||||
invalid = 0,
|
||||
ah,
|
||||
al,
|
||||
ax,
|
||||
bh,
|
||||
bl,
|
||||
bp,
|
||||
bpl,
|
||||
bx,
|
||||
ch,
|
||||
cl,
|
||||
cs,
|
||||
cx,
|
||||
dh,
|
||||
di,
|
||||
dil,
|
||||
dl,
|
||||
ds,
|
||||
dx,
|
||||
eax,
|
||||
ebp,
|
||||
ebx,
|
||||
ecx,
|
||||
edi,
|
||||
edx,
|
||||
eflags,
|
||||
eip,
|
||||
es = eip + 2,
|
||||
esi,
|
||||
esp,
|
||||
fpsw,
|
||||
fs,
|
||||
gs,
|
||||
ip,
|
||||
rax,
|
||||
rbp,
|
||||
rbx,
|
||||
rcx,
|
||||
rdi,
|
||||
rdx,
|
||||
rip,
|
||||
rsi = rip + 2,
|
||||
rsp,
|
||||
si,
|
||||
sil,
|
||||
sp,
|
||||
spl,
|
||||
ss,
|
||||
cr0,
|
||||
cr1,
|
||||
cr2,
|
||||
cr3,
|
||||
cr4,
|
||||
cr8 = cr4 + 4,
|
||||
dr0 = cr8 + 8,
|
||||
dr1,
|
||||
dr2,
|
||||
dr3,
|
||||
dr4,
|
||||
dr5,
|
||||
dr6,
|
||||
dr7,
|
||||
fp0 = dr7 + 9,
|
||||
fp1,
|
||||
fp2,
|
||||
fp3,
|
||||
fp4,
|
||||
fp5,
|
||||
fp6,
|
||||
fp7,
|
||||
k0,
|
||||
k1,
|
||||
k2,
|
||||
k3,
|
||||
k4,
|
||||
k5,
|
||||
k6,
|
||||
k7,
|
||||
mm0,
|
||||
mm1,
|
||||
mm2,
|
||||
mm3,
|
||||
mm4,
|
||||
mm5,
|
||||
mm6,
|
||||
mm7,
|
||||
r8,
|
||||
r9,
|
||||
r10,
|
||||
r11,
|
||||
r12,
|
||||
r13,
|
||||
r14,
|
||||
r15,
|
||||
st0,
|
||||
st1,
|
||||
st2,
|
||||
st3,
|
||||
st4,
|
||||
st5,
|
||||
st6,
|
||||
st7,
|
||||
xmm0,
|
||||
xmm1,
|
||||
xmm2,
|
||||
xmm3,
|
||||
xmm4,
|
||||
xmm5,
|
||||
xmm6,
|
||||
xmm7,
|
||||
xmm8,
|
||||
xmm9,
|
||||
xmm10,
|
||||
xmm11,
|
||||
xmm12,
|
||||
xmm13,
|
||||
xmm14,
|
||||
xmm15,
|
||||
xmm16,
|
||||
xmm17,
|
||||
xmm18,
|
||||
xmm19,
|
||||
xmm20,
|
||||
xmm21,
|
||||
xmm22,
|
||||
xmm23,
|
||||
xmm24,
|
||||
xmm25,
|
||||
xmm26,
|
||||
xmm27,
|
||||
xmm28,
|
||||
xmm29,
|
||||
xmm30,
|
||||
xmm31,
|
||||
ymm0,
|
||||
ymm1,
|
||||
ymm2,
|
||||
ymm3,
|
||||
ymm4,
|
||||
ymm5,
|
||||
ymm6,
|
||||
ymm7,
|
||||
ymm8,
|
||||
ymm9,
|
||||
ymm10,
|
||||
ymm11,
|
||||
ymm12,
|
||||
ymm13,
|
||||
ymm14,
|
||||
ymm15,
|
||||
ymm16,
|
||||
ymm17,
|
||||
ymm18,
|
||||
ymm19,
|
||||
ymm20,
|
||||
ymm21,
|
||||
ymm22,
|
||||
ymm23,
|
||||
ymm24,
|
||||
ymm25,
|
||||
ymm26,
|
||||
ymm27,
|
||||
ymm28,
|
||||
ymm29,
|
||||
ymm30,
|
||||
ymm31,
|
||||
zmm0,
|
||||
zmm1,
|
||||
zmm2,
|
||||
zmm3,
|
||||
zmm4,
|
||||
zmm5,
|
||||
zmm6,
|
||||
zmm7,
|
||||
zmm8,
|
||||
zmm9,
|
||||
zmm10,
|
||||
zmm11,
|
||||
zmm12,
|
||||
zmm13,
|
||||
zmm14,
|
||||
zmm15,
|
||||
zmm16,
|
||||
zmm17,
|
||||
zmm18,
|
||||
zmm19,
|
||||
zmm20,
|
||||
zmm21,
|
||||
zmm22,
|
||||
zmm23,
|
||||
zmm24,
|
||||
zmm25,
|
||||
zmm26,
|
||||
zmm27,
|
||||
zmm28,
|
||||
zmm29,
|
||||
zmm30,
|
||||
zmm31,
|
||||
r8b,
|
||||
r9b,
|
||||
r10b,
|
||||
r11b,
|
||||
r12b,
|
||||
r13b,
|
||||
r14b,
|
||||
r15b,
|
||||
r8d,
|
||||
r9d,
|
||||
r10d,
|
||||
r11d,
|
||||
r12d,
|
||||
r13d,
|
||||
r14d,
|
||||
r15d,
|
||||
r8w,
|
||||
r9w,
|
||||
r10w,
|
||||
r11w,
|
||||
r12w,
|
||||
r13w,
|
||||
r14w,
|
||||
r15w,
|
||||
idtr,
|
||||
gdtr,
|
||||
ldtr,
|
||||
tr,
|
||||
fpcw,
|
||||
fptag,
|
||||
msr,
|
||||
mxcsr,
|
||||
fs_base,
|
||||
gs_base,
|
||||
flags,
|
||||
rflags,
|
||||
fip,
|
||||
fcs,
|
||||
fdp,
|
||||
fds,
|
||||
fop,
|
||||
end, // Must be last
|
||||
};
|
||||
|
||||
using x64_emulator = typed_emulator<uint64_t, x64_register, x64_register::rsp>;
|
||||
16
src/unicorn_emulator/CMakeLists.txt
Normal file
16
src/unicorn_emulator/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
|
||||
*.cpp
|
||||
*.hpp
|
||||
*.rc
|
||||
)
|
||||
|
||||
list(SORT SRC_FILES)
|
||||
|
||||
add_library(unicorn_emulator SHARED ${SRC_FILES})
|
||||
|
||||
target_include_directories(unicorn_emulator INTERFACE
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
||||
|
||||
target_link_libraries(unicorn_emulator PUBLIC emulator)
|
||||
target_link_libraries(unicorn_emulator PRIVATE unicorn)
|
||||
186
src/unicorn_emulator/unicorn_x64_emulator.cpp
Normal file
186
src/unicorn_emulator/unicorn_x64_emulator.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#define UNICORN_EMULATOR_IMPL
|
||||
#include "unicorn_x64_emulator.hpp"
|
||||
|
||||
#include <span>
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
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>(x64_register::end) == UC_X86_REG_ENDING);
|
||||
|
||||
struct unicorn_error : std::runtime_error
|
||||
{
|
||||
unicorn_error(const uc_err error_code)
|
||||
: std::runtime_error(uc_strerror(error_code))
|
||||
, code(error_code)
|
||||
{
|
||||
}
|
||||
|
||||
uc_err code{};
|
||||
};
|
||||
|
||||
void throw_if_unicorn_error(const uc_err error_code)
|
||||
{
|
||||
if (error_code != UC_ERR_OK)
|
||||
{
|
||||
throw unicorn_error(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
void uce(const uc_err error_code)
|
||||
{
|
||||
throw_if_unicorn_error(error_code);
|
||||
}
|
||||
|
||||
class unicorn_memory_regions
|
||||
{
|
||||
public:
|
||||
unicorn_memory_regions(uc_engine* uc)
|
||||
{
|
||||
uce(uc_mem_regions(uc, &this->regions_, &this->count_));
|
||||
}
|
||||
|
||||
unicorn_memory_regions(unicorn_memory_regions&&) = delete;
|
||||
unicorn_memory_regions(const unicorn_memory_regions&) = delete;
|
||||
unicorn_memory_regions& operator=(unicorn_memory_regions&&) = delete;
|
||||
unicorn_memory_regions& operator=(const unicorn_memory_regions&) = delete;
|
||||
|
||||
~unicorn_memory_regions()
|
||||
{
|
||||
if (regions_)
|
||||
{
|
||||
uc_free(regions_);
|
||||
}
|
||||
}
|
||||
|
||||
std::span<uc_mem_region> get_span() const
|
||||
{
|
||||
return {this->regions_, this->count_};
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t count_{};
|
||||
uc_mem_region* regions_{};
|
||||
};
|
||||
|
||||
class unicorn_x64_emulator : public x64_emulator
|
||||
{
|
||||
public:
|
||||
unicorn_x64_emulator()
|
||||
{
|
||||
uce(uc_open(UC_ARCH_X86, UC_MODE_64, &this->uc_));
|
||||
}
|
||||
|
||||
~unicorn_x64_emulator() override
|
||||
{
|
||||
uc_close(this->uc_);
|
||||
}
|
||||
|
||||
void start(const uint64_t start, const uint64_t end, std::chrono::microseconds timeout,
|
||||
const size_t count) override
|
||||
{
|
||||
if (timeout.count() < 0)
|
||||
{
|
||||
timeout = {};
|
||||
}
|
||||
|
||||
uce(uc_emu_start(*this, start, end, static_cast<uint64_t>(timeout.count()), count));
|
||||
}
|
||||
|
||||
void stop() override
|
||||
{
|
||||
uce(uc_emu_stop(*this));
|
||||
}
|
||||
|
||||
void write_raw_register(const int reg, const void* value, const size_t size) override
|
||||
{
|
||||
size_t 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));
|
||||
}
|
||||
}
|
||||
|
||||
void read_raw_register(const int reg, void* value, const size_t size) override
|
||||
{
|
||||
size_t result_size = 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));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void read_memory(const uint64_t address, void* data, const size_t size) 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 protect_memory(const uint64_t address, const size_t size, memory_permission permissions) override
|
||||
{
|
||||
uce(uc_mem_protect(*this, address, size, static_cast<uint32_t>(permissions)));
|
||||
}
|
||||
|
||||
std::vector<memory_region> get_memory_regions() override
|
||||
{
|
||||
const unicorn_memory_regions regions{*this};
|
||||
const auto region_span = regions.get_span();
|
||||
|
||||
std::vector<memory_region> result{};
|
||||
result.reserve(region_span.size());
|
||||
|
||||
for (const auto region : region_span)
|
||||
{
|
||||
memory_region reg{};
|
||||
reg.start = region.begin;
|
||||
reg.length = region.end - region.begin;
|
||||
reg.pemissions = static_cast<memory_permission>(region.perms) & memory_permission::all;
|
||||
|
||||
result.push_back(reg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
operator uc_engine*() const
|
||||
{
|
||||
return this->uc_;
|
||||
}
|
||||
|
||||
private:
|
||||
uc_engine* uc_{};
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<x64_emulator> create_x64_emulator()
|
||||
{
|
||||
return std::make_unique<unicorn_x64_emulator>();
|
||||
}
|
||||
}
|
||||
16
src/unicorn_emulator/unicorn_x64_emulator.hpp
Normal file
16
src/unicorn_emulator/unicorn_x64_emulator.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <x64_emulator.hpp>
|
||||
|
||||
#ifdef UNICORN_EMULATOR_IMPL
|
||||
#define UNICORN_EMULATOR_DLL_STORAGE __declspec(dllexport)
|
||||
#else
|
||||
#define UNICORN_EMULATOR_DLL_STORAGE __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
namespace unicorn
|
||||
{
|
||||
UNICORN_EMULATOR_DLL_STORAGE
|
||||
std::unique_ptr<x64_emulator> create_x64_emulator();
|
||||
}
|
||||
24
src/windows_emulator/CMakeLists.txt
Normal file
24
src/windows_emulator/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
|
||||
*.cpp
|
||||
*.hpp
|
||||
*.rc
|
||||
)
|
||||
|
||||
list(SORT SRC_FILES)
|
||||
|
||||
add_executable(windows_emulator ${SRC_FILES})
|
||||
|
||||
momo_assign_source_group(${SRC_FILES})
|
||||
|
||||
target_precompile_headers(windows_emulator PRIVATE std_include.hpp)
|
||||
|
||||
target_link_libraries(windows_emulator PRIVATE
|
||||
common
|
||||
phnt::phnt
|
||||
reflect
|
||||
unicorn_emulator
|
||||
)
|
||||
|
||||
set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT windows_emulator)
|
||||
|
||||
momo_strip_target(windows_emulator)
|
||||
@@ -1,16 +1,17 @@
|
||||
#pragma once
|
||||
#include "unicorn.hpp"
|
||||
#include "memory_utils.hpp"
|
||||
|
||||
#include <emulator.hpp>
|
||||
|
||||
template <typename T>
|
||||
class unicorn_object
|
||||
class emulator_object
|
||||
{
|
||||
public:
|
||||
unicorn_object() = default;
|
||||
emulator_object() = default;
|
||||
|
||||
unicorn_object(const unicorn& uc, uint64_t address)
|
||||
: uc_(&uc)
|
||||
, address_(address)
|
||||
emulator_object(emulator& emu, const uint64_t address)
|
||||
: emu_(&emu)
|
||||
, address_(address)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -42,22 +43,20 @@ public:
|
||||
T read() const
|
||||
{
|
||||
T obj{};
|
||||
|
||||
uce(uc_mem_read(*this->uc_, this->address_, &obj, sizeof(obj)));
|
||||
|
||||
this->emu_->read_memory(this->address_, &obj, sizeof(obj));
|
||||
return obj;
|
||||
}
|
||||
|
||||
void write(const T& value) const
|
||||
{
|
||||
uce(uc_mem_write(*this->uc_, this->address_, &value, sizeof(value)));
|
||||
this->emu_->write_memory(this->address_, &value, sizeof(value));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void access(const F& accessor) const
|
||||
{
|
||||
T obj{};
|
||||
uce(uc_mem_read(*this->uc_, this->address_, &obj, sizeof(obj)));
|
||||
this->emu_->read_memory(this->address_, &obj, sizeof(obj));
|
||||
|
||||
accessor(obj);
|
||||
|
||||
@@ -65,20 +64,20 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
const unicorn* uc_{};
|
||||
emulator* emu_{};
|
||||
uint64_t address_{};
|
||||
};
|
||||
|
||||
class unicorn_allocator
|
||||
class emulator_allocator
|
||||
{
|
||||
public:
|
||||
unicorn_allocator() = default;
|
||||
emulator_allocator() = default;
|
||||
|
||||
unicorn_allocator(const unicorn& uc, const uint64_t address, const uint64_t size)
|
||||
: uc_(&uc)
|
||||
, 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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -99,10 +98,10 @@ public:
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
unicorn_object<T> reserve()
|
||||
emulator_object<T> reserve()
|
||||
{
|
||||
const auto potential_start = this->reserve(sizeof(T), alignof(T));
|
||||
return unicorn_object<T>(*this->uc_, potential_start);
|
||||
return emulator_object<T>(*this->emu_, potential_start);
|
||||
}
|
||||
|
||||
void make_unicode_string(UNICODE_STRING& result, const std::wstring_view str)
|
||||
@@ -113,32 +112,33 @@ public:
|
||||
|
||||
const auto string_buffer = this->reserve(total_length, required_alignment);
|
||||
|
||||
uce(uc_mem_write(*this->uc_, string_buffer, str.data(), total_length));
|
||||
this->emu_->write_memory(string_buffer, str.data(), total_length);
|
||||
|
||||
result.Buffer = reinterpret_cast<PWCH>(string_buffer);
|
||||
result.Length = static_cast<USHORT>(total_length);
|
||||
result.MaximumLength = result.Length;
|
||||
}
|
||||
|
||||
unicorn_object<UNICODE_STRING> make_unicode_string(const std::wstring_view str)
|
||||
emulator_object<UNICODE_STRING> make_unicode_string(const std::wstring_view str)
|
||||
{
|
||||
const auto unicode_string = this->reserve<UNICODE_STRING>();
|
||||
|
||||
unicode_string.access([&](UNICODE_STRING& unicode_str)
|
||||
{
|
||||
this->make_unicode_string(unicode_str, str);
|
||||
});
|
||||
{
|
||||
this->make_unicode_string(unicode_str, str);
|
||||
});
|
||||
|
||||
return unicode_string;
|
||||
}
|
||||
|
||||
private:
|
||||
const unicorn* uc_{};
|
||||
emulator* emu_{};
|
||||
uint64_t address_{};
|
||||
uint64_t size_{};
|
||||
uint64_t active_address_{ 0 };
|
||||
uint64_t active_address_{0};
|
||||
};
|
||||
|
||||
/*
|
||||
class unicorn_hook
|
||||
{
|
||||
public:
|
||||
@@ -146,7 +146,7 @@ public:
|
||||
|
||||
template <typename... Args>
|
||||
unicorn_hook(const unicorn& uc, const int type, const uint64_t begin, const uint64_t end, function callback,
|
||||
Args... args)
|
||||
Args... args)
|
||||
: uc_(&uc)
|
||||
{
|
||||
this->function_ = std::make_unique<internal_function>(
|
||||
@@ -156,28 +156,28 @@ public:
|
||||
});
|
||||
|
||||
void* handler = +[](uc_engine*, const uint64_t address, const uint32_t size,
|
||||
void* user_data)
|
||||
{
|
||||
(*static_cast<internal_function*>(user_data))(address, size);
|
||||
};
|
||||
void* user_data)
|
||||
{
|
||||
(*static_cast<internal_function*>(user_data))(address, size);
|
||||
};
|
||||
|
||||
if (type == UC_HOOK_INSN)
|
||||
{
|
||||
handler = +[](uc_engine* uc, void* user_data)
|
||||
{
|
||||
uint64_t rip{};
|
||||
uc_reg_read(uc, UC_X86_REG_RIP, &rip);
|
||||
(*static_cast<internal_function*>(user_data))(rip, 0);
|
||||
};
|
||||
{
|
||||
uint64_t rip{};
|
||||
uc_reg_read(uc, UC_X86_REG_RIP, &rip);
|
||||
(*static_cast<internal_function*>(user_data))(rip, 0);
|
||||
};
|
||||
}
|
||||
|
||||
if (type == UC_HOOK_MEM_READ)
|
||||
{
|
||||
handler = +[](uc_engine*, const uc_mem_type /*type*/, const uint64_t address, const int size,
|
||||
const int64_t /*value*/, void* user_data)
|
||||
{
|
||||
(*static_cast<internal_function*>(user_data))(address, size);
|
||||
};
|
||||
handler = +[](uc_engine*, const uc_mem_type, const uint64_t address, const int size,
|
||||
const int64_t, void* user_data)
|
||||
{
|
||||
(*static_cast<internal_function*>(user_data))(address, size);
|
||||
};
|
||||
}
|
||||
uce(uc_hook_add(*this->uc_, &this->hook_, type, handler, this->function_.get(), begin, end, args...));
|
||||
}
|
||||
@@ -229,3 +229,4 @@ private:
|
||||
uc_hook hook_{};
|
||||
std::unique_ptr<internal_function> function_{};
|
||||
};
|
||||
*/
|
||||
@@ -1,14 +1,14 @@
|
||||
#include "std_include.hpp"
|
||||
|
||||
#include "unicorn.hpp"
|
||||
#include "memory_utils.hpp"
|
||||
#include "unicorn_utils.hpp"
|
||||
#include "emulator_utils.hpp"
|
||||
#include "process_context.hpp"
|
||||
#include "syscalls.hpp"
|
||||
|
||||
#include "reflect_extension.hpp"
|
||||
#include <reflect>
|
||||
|
||||
#include <unicorn_x64_emulator.hpp>
|
||||
|
||||
#define GS_SEGMENT_ADDR 0x6000000ULL
|
||||
#define GS_SEGMENT_SIZE (20 << 20) // 20 MB
|
||||
|
||||
@@ -21,32 +21,32 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
void setup_stack(const unicorn& uc, uint64_t stack_base, size_t stack_size)
|
||||
void setup_stack(x64_emulator& emu, const uint64_t stack_base, const size_t stack_size)
|
||||
{
|
||||
uce(uc_mem_map(uc, stack_base, stack_size, UC_PROT_READ | UC_PROT_WRITE));
|
||||
emu.map_memory(stack_base, stack_size, memory_permission::read_write);
|
||||
|
||||
const uint64_t stack_end = stack_base + stack_size;
|
||||
uce(uc_reg_write(uc, UC_X86_REG_RSP, &stack_end));
|
||||
emu.reg(x64_register::rsp, stack_end);
|
||||
}
|
||||
|
||||
unicorn_allocator setup_gs_segment(const unicorn& uc, const uint64_t segment_base, const uint64_t size)
|
||||
emulator_allocator setup_gs_segment(x64_emulator& emu, const uint64_t segment_base, const uint64_t size)
|
||||
{
|
||||
const std::array<uint64_t, 2> value = {
|
||||
IA32_GS_BASE_MSR,
|
||||
segment_base
|
||||
};
|
||||
|
||||
uce(uc_reg_write(uc, UC_X86_REG_MSR, value.data()));
|
||||
uce(uc_mem_map(uc, segment_base, size, UC_PROT_READ | UC_PROT_WRITE));
|
||||
emu.write_register(x64_register::msr, value.data(), value.size());
|
||||
emu.map_memory(segment_base, size, memory_permission::read_write);
|
||||
|
||||
return {uc, segment_base, size};
|
||||
return {emu, segment_base, size};
|
||||
}
|
||||
|
||||
unicorn_object<KUSER_SHARED_DATA> setup_kusd(const unicorn& uc)
|
||||
emulator_object<KUSER_SHARED_DATA> setup_kusd(x64_emulator& emu)
|
||||
{
|
||||
uce(uc_mem_map(uc, KUSD_ADDRESS, page_align_up(sizeof(KUSER_SHARED_DATA)), UC_PROT_READ));
|
||||
emu.map_memory(KUSD_ADDRESS, page_align_up(sizeof(KUSER_SHARED_DATA)), memory_permission::read);
|
||||
|
||||
const unicorn_object<KUSER_SHARED_DATA> kusd_object{uc, KUSD_ADDRESS};
|
||||
const emulator_object<KUSER_SHARED_DATA> kusd_object{emu, KUSD_ADDRESS};
|
||||
kusd_object.access([](KUSER_SHARED_DATA& kusd)
|
||||
{
|
||||
const auto& real_kusd = *reinterpret_cast<KUSER_SHARED_DATA*>(KUSD_ADDRESS);
|
||||
@@ -64,7 +64,7 @@ namespace
|
||||
return kusd_object;
|
||||
}
|
||||
|
||||
mapped_binary map_module(const unicorn& uc, const std::vector<uint8_t>& module_data,
|
||||
mapped_binary map_module(x64_emulator& emu, const std::vector<uint8_t>& module_data,
|
||||
const std::string& name)
|
||||
{
|
||||
mapped_binary binary{};
|
||||
@@ -80,24 +80,26 @@ namespace
|
||||
|
||||
while (true)
|
||||
{
|
||||
const auto res = uc_mem_map(uc, binary.image_base, binary.size_of_image, UC_PROT_READ);
|
||||
if (res == UC_ERR_OK)
|
||||
try
|
||||
{
|
||||
emu.map_memory(binary.image_base, binary.size_of_image, memory_permission::read);
|
||||
break;
|
||||
}
|
||||
|
||||
binary.image_base += 0x10000;
|
||||
|
||||
if (binary.image_base < optional_header.ImageBase || (optional_header.DllCharacteristics &
|
||||
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) == 0)
|
||||
catch (...)
|
||||
{
|
||||
throw std::runtime_error("Failed to map range");
|
||||
binary.image_base += 0x10000;
|
||||
|
||||
if (binary.image_base < optional_header.ImageBase || (optional_header.DllCharacteristics &
|
||||
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) == 0)
|
||||
{
|
||||
throw std::runtime_error("Failed to map range");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("Mapping %s at %llX\n", name.c_str(), binary.image_base);
|
||||
|
||||
uce(uc_mem_write(uc, binary.image_base, ptr, optional_header.SizeOfHeaders));
|
||||
emu.write_memory(binary.image_base, ptr, optional_header.SizeOfHeaders);
|
||||
|
||||
const std::span sections(IMAGE_FIRST_SECTION(nt_headers), nt_headers->FileHeader.NumberOfSections);
|
||||
|
||||
@@ -110,28 +112,28 @@ namespace
|
||||
const void* source_ptr = ptr + section.PointerToRawData;
|
||||
|
||||
const auto size_of_data = std::min(section.SizeOfRawData, section.Misc.VirtualSize);
|
||||
uce(uc_mem_write(uc, target_ptr, source_ptr, size_of_data));
|
||||
emu.write_memory(target_ptr, source_ptr, size_of_data);
|
||||
}
|
||||
uint32_t permissions = UC_PROT_NONE;
|
||||
auto permissions = memory_permission::none;
|
||||
|
||||
if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE)
|
||||
{
|
||||
permissions |= UC_PROT_EXEC;
|
||||
permissions |= memory_permission::exec;
|
||||
}
|
||||
|
||||
if (section.Characteristics & IMAGE_SCN_MEM_READ)
|
||||
{
|
||||
permissions |= UC_PROT_READ;
|
||||
permissions |= memory_permission::read;
|
||||
}
|
||||
|
||||
if (section.Characteristics & IMAGE_SCN_MEM_WRITE)
|
||||
{
|
||||
permissions |= UC_PROT_WRITE;
|
||||
permissions |= memory_permission::write;
|
||||
}
|
||||
|
||||
const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize));
|
||||
|
||||
uce(uc_mem_protect(uc, target_ptr, size_of_section, permissions));
|
||||
emu.protect_memory(target_ptr, size_of_section, permissions);
|
||||
}
|
||||
|
||||
auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
@@ -162,14 +164,14 @@ namespace
|
||||
return binary;
|
||||
}
|
||||
|
||||
process_context setup_context(const unicorn& uc)
|
||||
process_context setup_context(x64_emulator& emu)
|
||||
{
|
||||
setup_stack(uc, STACK_ADDRESS, STACK_SIZE);
|
||||
setup_stack(emu, STACK_ADDRESS, STACK_SIZE);
|
||||
process_context context{};
|
||||
|
||||
context.kusd = setup_kusd(uc);
|
||||
context.kusd = setup_kusd(emu);
|
||||
|
||||
context.gs_segment = setup_gs_segment(uc, GS_SEGMENT_ADDR, GS_SEGMENT_SIZE);
|
||||
context.gs_segment = setup_gs_segment(emu, GS_SEGMENT_ADDR, GS_SEGMENT_SIZE);
|
||||
|
||||
auto& gs = context.gs_segment;
|
||||
|
||||
@@ -210,10 +212,10 @@ namespace
|
||||
return {(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()};
|
||||
}
|
||||
|
||||
mapped_binary map_file(const unicorn& uc, const std::filesystem::path& file)
|
||||
mapped_binary map_file(x64_emulator& emu, const std::filesystem::path& file)
|
||||
{
|
||||
const auto data = load_file(file);
|
||||
return map_module(uc, data, file.generic_string());
|
||||
return map_module(emu, data, file.generic_string());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -268,15 +270,16 @@ namespace
|
||||
std::map<size_t, std::string> members_{};
|
||||
};
|
||||
|
||||
/*
|
||||
template <typename T>
|
||||
unicorn_hook watch_object(const unicorn& uc, unicorn_object<T> object)
|
||||
unicorn_hook watch_object(const unicorn& uc, emulator_object<T> object)
|
||||
{
|
||||
type_info<T> info{};
|
||||
|
||||
return {
|
||||
uc, UC_HOOK_MEM_READ, object.value(), object.end(),
|
||||
[i = std::move(info), o = std::move(object)](const unicorn&, const uint64_t address,
|
||||
const uint32_t /*size*/)
|
||||
const uint32_t )
|
||||
{
|
||||
const auto offset = address - o.value();
|
||||
printf("%s: %llX (%s)\n", i.get_type_name().c_str(), offset,
|
||||
@@ -284,21 +287,21 @@ namespace
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
*/
|
||||
void run()
|
||||
{
|
||||
const unicorn uc{UC_ARCH_X86, UC_MODE_64};
|
||||
const auto emu = unicorn::create_x64_emulator();
|
||||
|
||||
auto context = setup_context(uc);
|
||||
auto context = setup_context(*emu);
|
||||
|
||||
context.executable = map_file(uc, R"(C:\Users\mauri\Desktop\ConsoleApplication6.exe)");
|
||||
context.executable = map_file(*emu, R"(C:\Users\mauri\Desktop\ConsoleApplication6.exe)");
|
||||
|
||||
context.peb.access([&](PEB& peb)
|
||||
{
|
||||
peb.ImageBaseAddress = reinterpret_cast<void*>(context.executable.image_base);
|
||||
});
|
||||
|
||||
context.ntdll = map_file(uc, R"(C:\Windows\System32\ntdll.dll)");
|
||||
context.ntdll = map_file(*emu, R"(C:\Windows\System32\ntdll.dll)");
|
||||
|
||||
const auto entry1 = context.ntdll.exports.at("LdrInitializeThunk");
|
||||
const auto entry2 = context.ntdll.exports.at("RtlUserThreadStart");
|
||||
@@ -306,6 +309,7 @@ namespace
|
||||
(void)entry1;
|
||||
(void)entry2;
|
||||
|
||||
/*
|
||||
std::vector<unicorn_hook> export_hooks{};
|
||||
|
||||
|
||||
@@ -338,21 +342,23 @@ namespace
|
||||
handle_syscall(uc, context);
|
||||
}, UC_X86_INS_SYSCALL);
|
||||
|
||||
export_hooks.emplace_back(watch_object(uc, context.teb));
|
||||
export_hooks.emplace_back(watch_object(uc, context.peb));
|
||||
export_hooks.emplace_back(watch_object(uc, context.process_params));
|
||||
export_hooks.emplace_back(watch_object(uc, context.kusd));
|
||||
//export_hooks.emplace_back(watch_object(uc, context.teb));
|
||||
//export_hooks.emplace_back(watch_object(uc, context.peb));
|
||||
//export_hooks.emplace_back(watch_object(uc, context.process_params));
|
||||
//export_hooks.emplace_back(watch_object(uc, context.kusd));
|
||||
|
||||
unicorn_hook hook2(uc, UC_HOOK_CODE, 0, std::numeric_limits<uint64_t>::max(),
|
||||
[](const unicorn& uc, const uint64_t address, const uint32_t /*size*/)
|
||||
[](const unicorn& uc, const uint64_t address, const uint32_t )
|
||||
{
|
||||
//static bool hit = false;
|
||||
/*if (address == 0x01800D46DD)
|
||||
static bool hit = false;
|
||||
// if (address == 0x1800D3C80)
|
||||
if (address == 0x1800D4420)
|
||||
{
|
||||
hit = true;
|
||||
}*/
|
||||
//hit = true;
|
||||
//uc.stop();
|
||||
}
|
||||
|
||||
//if (hit)
|
||||
if (hit)
|
||||
{
|
||||
printf(
|
||||
"Inst: %16llX - RAX: %16llX - RBX: %16llX - RCX: %16llX - RDX: %16llX - R8: %16llX - R9: %16llX - RDI: %16llX - RSI: %16llX\n",
|
||||
@@ -362,27 +368,23 @@ namespace
|
||||
uc.reg(UC_X86_REG_RDI), uc.reg(UC_X86_REG_RSI));
|
||||
}
|
||||
});
|
||||
|
||||
*/
|
||||
const auto execution_context = context.gs_segment.reserve<CONTEXT>();
|
||||
|
||||
uc.reg(UC_X86_REG_RCX, execution_context.value());
|
||||
uc.reg(UC_X86_REG_RDX, context.ntdll.image_base);
|
||||
emu->reg(x64_register::rcx, execution_context.value());
|
||||
emu->reg(x64_register::rdx, context.ntdll.image_base);
|
||||
|
||||
const auto err = uc_emu_start(uc, entry1, 0, 0, 0);
|
||||
if (err != UC_ERR_OK)
|
||||
try
|
||||
{
|
||||
uint64_t rip{};
|
||||
uc_reg_read(uc, UC_X86_REG_RIP, &rip);
|
||||
printf("Emulation failed at: %llX\n", rip);
|
||||
uce(err);
|
||||
emu->start(entry1);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
printf("Emulation failed at: %llX\n", emu->reg(x64_register::rip));
|
||||
throw;
|
||||
}
|
||||
|
||||
printf("Emulation done. Below is the CPU context\n");
|
||||
|
||||
uint64_t rax{};
|
||||
uce(uc_reg_read(uc, UC_X86_REG_RAX, &rax));
|
||||
|
||||
printf(">>> RAX = 0x%llX\n", rax);
|
||||
printf("Emulation done.\n");
|
||||
}
|
||||
}
|
||||
|
||||
110
src/windows_emulator/memory_utils.hpp
Normal file
110
src/windows_emulator/memory_utils.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <emulator.hpp>
|
||||
|
||||
inline bool is_within_start_and_end(const uint64_t value, const uint64_t start, const uint64_t end)
|
||||
{
|
||||
return value >= start && value < end;
|
||||
}
|
||||
|
||||
inline bool is_within_start_and_length(const uint64_t value, const uint64_t start, const uint64_t length)
|
||||
{
|
||||
return is_within_start_and_end(value, start, start + length);
|
||||
}
|
||||
|
||||
inline uint64_t align_down(const uint64_t value, const uint64_t alignment)
|
||||
{
|
||||
return value & ~(alignment - 1);
|
||||
}
|
||||
|
||||
inline uint64_t align_up(const uint64_t value, const uint64_t alignment)
|
||||
{
|
||||
return align_down(value + (alignment - 1), alignment);
|
||||
}
|
||||
|
||||
inline uint64_t page_align_down(const uint64_t value)
|
||||
{
|
||||
return align_down(value, 0x1000);
|
||||
}
|
||||
|
||||
inline uint64_t page_align_up(const uint64_t value)
|
||||
{
|
||||
return align_up(value, 0x1000);
|
||||
}
|
||||
|
||||
|
||||
inline memory_permission get_memory_protection(emulator& emu, const uint64_t address)
|
||||
{
|
||||
for (const auto& region : emu.get_memory_regions())
|
||||
{
|
||||
if (is_within_start_and_length(address, region.start, region.length))
|
||||
{
|
||||
return region.pemissions;
|
||||
}
|
||||
}
|
||||
|
||||
return memory_permission::none;
|
||||
}
|
||||
|
||||
inline bool is_memory_allocated(emulator& emu, const uint64_t address)
|
||||
{
|
||||
for (const auto& region : emu.get_memory_regions())
|
||||
{
|
||||
if (is_within_start_and_length(address, region.start, region.length))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline memory_permission map_nt_to_unicorn_protection(const uint32_t nt_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:
|
||||
case PAGE_EXECUTE_WRITECOPY:
|
||||
default:
|
||||
return memory_permission::all;
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t map_unicorn_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;
|
||||
|
||||
if (!has_read)
|
||||
{
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
|
||||
if (has_exec && has_write)
|
||||
{
|
||||
return PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
if (has_exec)
|
||||
{
|
||||
return PAGE_EXECUTE_READ;
|
||||
}
|
||||
|
||||
if (has_write)
|
||||
{
|
||||
return PAGE_READWRITE;
|
||||
}
|
||||
|
||||
return PAGE_READONLY;
|
||||
}
|
||||
@@ -28,14 +28,14 @@ struct event
|
||||
|
||||
struct process_context
|
||||
{
|
||||
unicorn_object<TEB> teb{};
|
||||
unicorn_object<PEB> peb{};
|
||||
unicorn_object<RTL_USER_PROCESS_PARAMETERS> process_params{};
|
||||
unicorn_object<KUSER_SHARED_DATA> kusd{};
|
||||
emulator_object<TEB> teb{};
|
||||
emulator_object<PEB> peb{};
|
||||
emulator_object<RTL_USER_PROCESS_PARAMETERS> process_params{};
|
||||
emulator_object<KUSER_SHARED_DATA> kusd{};
|
||||
|
||||
mapped_binary executable{};
|
||||
mapped_binary ntdll{};
|
||||
|
||||
std::vector<event> events{};
|
||||
unicorn_allocator gs_segment{};
|
||||
emulator_allocator gs_segment{};
|
||||
};
|
||||
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
@@ -5,8 +5,8 @@ namespace
|
||||
{
|
||||
void handle_NtQueryPerformanceCounter(const unicorn& uc)
|
||||
{
|
||||
const unicorn_object<LARGE_INTEGER> performance_counter{uc, uc.reg(UC_X86_REG_R10)};
|
||||
const unicorn_object<LARGE_INTEGER> performance_frequency{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const emulator_object<LARGE_INTEGER> performance_counter{uc, uc.reg(UC_X86_REG_R10)};
|
||||
const emulator_object<LARGE_INTEGER> performance_frequency{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
|
||||
try
|
||||
{
|
||||
@@ -56,7 +56,7 @@ namespace
|
||||
|
||||
void handle_NtCreateEvent(const unicorn& uc, process_context& context)
|
||||
{
|
||||
const unicorn_object<uint64_t> event_handle{uc, uc.reg(UC_X86_REG_R10)};
|
||||
const emulator_object<uint64_t> event_handle{uc, uc.reg(UC_X86_REG_R10)};
|
||||
const auto object_attributes = uc.reg(UC_X86_REG_R8);
|
||||
const auto event_type = uc.reg<EVENT_TYPE>(UC_X86_REG_R9D);
|
||||
const auto initial_state = static_cast<BOOLEAN>(uc.read_stack(5));
|
||||
@@ -85,7 +85,7 @@ namespace
|
||||
const auto info_class = uc.reg<uint32_t>(UC_X86_REG_R8D);
|
||||
const auto memory_information = uc.reg(UC_X86_REG_R9);
|
||||
const auto memory_information_length = static_cast<uint32_t>(uc.read_stack(5));
|
||||
const unicorn_object<uint32_t> return_length{uc, uc.read_stack(6)};
|
||||
const emulator_object<uint32_t> return_length{uc, uc.read_stack(6)};
|
||||
|
||||
if (process_handle != ~0ULL)
|
||||
{
|
||||
@@ -124,7 +124,7 @@ namespace
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<MEMORY_IMAGE_INFORMATION> info{uc, memory_information};
|
||||
const emulator_object<MEMORY_IMAGE_INFORMATION> info{uc, memory_information};
|
||||
|
||||
info.access([&](MEMORY_IMAGE_INFORMATION& image_info)
|
||||
{
|
||||
@@ -140,7 +140,7 @@ namespace
|
||||
const auto info_class = uc.reg<uint32_t>(UC_X86_REG_R10D);
|
||||
const auto system_information = uc.reg(UC_X86_REG_RDX);
|
||||
const auto system_information_length = uc.reg<uint32_t>(UC_X86_REG_R8D);
|
||||
const unicorn_object<uint32_t> return_length{uc, uc.reg(UC_X86_REG_R9)};
|
||||
const emulator_object<uint32_t> return_length{uc, uc.reg(UC_X86_REG_R9)};
|
||||
|
||||
if (info_class == SystemFlushInformation
|
||||
|| info_class == SystemHypervisorSharedPageInformation)
|
||||
@@ -162,7 +162,7 @@ namespace
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<SYSTEM_NUMA_INFORMATION> info_obj{uc, system_information};
|
||||
const emulator_object<SYSTEM_NUMA_INFORMATION> info_obj{uc, system_information};
|
||||
|
||||
info_obj.access([&](SYSTEM_NUMA_INFORMATION& info)
|
||||
{
|
||||
@@ -194,7 +194,7 @@ namespace
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<SYSTEM_BASIC_INFORMATION> info{uc, system_information};
|
||||
const emulator_object<SYSTEM_BASIC_INFORMATION> info{uc, system_information};
|
||||
|
||||
info.access([&](SYSTEM_BASIC_INFORMATION& basic_info)
|
||||
{
|
||||
@@ -220,7 +220,7 @@ namespace
|
||||
const auto input_buffer_length = uc.reg<uint32_t>(UC_X86_REG_R8D);
|
||||
const auto system_information = uc.reg(UC_X86_REG_R9);
|
||||
const auto system_information_length = static_cast<uint32_t>(uc.read_stack(5));
|
||||
const unicorn_object<uint32_t> return_length{uc, uc.read_stack(6)};
|
||||
const emulator_object<uint32_t> return_length{uc, uc.read_stack(6)};
|
||||
|
||||
if (info_class == SystemFlushInformation
|
||||
|| info_class == SystemFeatureConfigurationInformation
|
||||
@@ -275,7 +275,7 @@ namespace
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<SYSTEM_BASIC_INFORMATION> info{uc, system_information};
|
||||
const emulator_object<SYSTEM_BASIC_INFORMATION> info{uc, system_information};
|
||||
|
||||
info.access([&](SYSTEM_BASIC_INFORMATION& basic_info)
|
||||
{
|
||||
@@ -300,7 +300,7 @@ namespace
|
||||
const auto info_class = uc.reg<uint32_t>(UC_X86_REG_EDX);
|
||||
const auto process_information = uc.reg(UC_X86_REG_R8);
|
||||
const auto process_information_length = uc.reg<uint32_t>(UC_X86_REG_R9D);
|
||||
const unicorn_object<uint32_t> return_length{uc, uc.read_stack(5)};
|
||||
const emulator_object<uint32_t> return_length{uc, uc.read_stack(5)};
|
||||
|
||||
if (process_handle != ~0ULL)
|
||||
{
|
||||
@@ -326,7 +326,7 @@ namespace
|
||||
return;
|
||||
}
|
||||
|
||||
const unicorn_object<uint32_t> info{uc, process_information};
|
||||
const emulator_object<uint32_t> info{uc, process_information};
|
||||
info.write(0x01234567);
|
||||
|
||||
uc.reg<uint64_t>(UC_X86_REG_RAX, STATUS_SUCCESS);
|
||||
@@ -335,10 +335,10 @@ namespace
|
||||
void handle_NtProtectVirtualMemory(const unicorn& uc)
|
||||
{
|
||||
const auto process_handle = uc.reg(UC_X86_REG_R10);
|
||||
const unicorn_object<uint64_t> base_address{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const unicorn_object<uint32_t> bytes_to_protect{uc, uc.reg(UC_X86_REG_R8)};
|
||||
const emulator_object<uint64_t> base_address{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const emulator_object<uint32_t> bytes_to_protect{uc, uc.reg(UC_X86_REG_R8)};
|
||||
const auto protection = uc.reg<uint32_t>(UC_X86_REG_R9D);
|
||||
const unicorn_object<uint32_t> old_protection{uc, uc.read_stack(5)};
|
||||
const emulator_object<uint32_t> old_protection{uc, uc.read_stack(5)};
|
||||
|
||||
if (process_handle != ~0ULL)
|
||||
{
|
||||
@@ -365,8 +365,8 @@ namespace
|
||||
void handle_NtAllocateVirtualMemory(const unicorn& uc)
|
||||
{
|
||||
const auto process_handle = uc.reg(UC_X86_REG_R10);
|
||||
const unicorn_object<uint64_t> base_address{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const unicorn_object<uint64_t> bytes_to_allocate{uc, uc.reg(UC_X86_REG_R9)};
|
||||
const emulator_object<uint64_t> base_address{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const emulator_object<uint64_t> bytes_to_allocate{uc, uc.reg(UC_X86_REG_R9)};
|
||||
//const auto allocation_type = uc.reg<uint32_t>(UC_X86_REG_R9D);
|
||||
const auto page_protection = static_cast<uint32_t>(uc.read_stack(6));
|
||||
|
||||
@@ -420,8 +420,8 @@ namespace
|
||||
void handle_NtAllocateVirtualMemoryEx(const unicorn& uc)
|
||||
{
|
||||
const auto process_handle = uc.reg(UC_X86_REG_R10);
|
||||
const unicorn_object<uint64_t> base_address{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const unicorn_object<uint64_t> bytes_to_allocate{uc, uc.reg(UC_X86_REG_R8)};
|
||||
const emulator_object<uint64_t> base_address{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const emulator_object<uint64_t> bytes_to_allocate{uc, uc.reg(UC_X86_REG_R8)};
|
||||
//const auto allocation_type = uc.reg<uint32_t>(UC_X86_REG_R9D);
|
||||
const auto page_protection = static_cast<uint32_t>(uc.read_stack(5));
|
||||
|
||||
@@ -475,8 +475,8 @@ namespace
|
||||
void handle_NtFreeVirtualMemory(const unicorn& uc)
|
||||
{
|
||||
const auto process_handle = uc.reg(UC_X86_REG_R10);
|
||||
const unicorn_object<uint64_t> base_address{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const unicorn_object<uint64_t> bytes_to_allocate{uc, uc.reg(UC_X86_REG_R8)};
|
||||
const emulator_object<uint64_t> base_address{uc, uc.reg(UC_X86_REG_RDX)};
|
||||
const emulator_object<uint64_t> bytes_to_allocate{uc, uc.reg(UC_X86_REG_R8)};
|
||||
|
||||
if (process_handle != ~0ULL)
|
||||
{
|
||||
@@ -496,7 +496,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void handle_syscall(const unicorn& uc, process_context& context)
|
||||
void handle_syscall(x64_emulator& emu, process_context& context)
|
||||
{
|
||||
const auto address = uc.reg(UC_X86_REG_RIP);
|
||||
const auto syscall_id = uc.reg<uint32_t>(UC_X86_REG_EAX);
|
||||
6
src/windows_emulator/syscalls.hpp
Normal file
6
src/windows_emulator/syscalls.hpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <x64_emulator.hpp>
|
||||
#include "process_context.hpp"
|
||||
|
||||
void handle_syscall(x64_emulator& emu, process_context& context);
|
||||
Reference in New Issue
Block a user