working on adding virtual branch support still, wanna make sure i do it

right...
master
John Doe 3 years ago
parent 8f691a29f1
commit 3251b6e475

2
deps/vmprofiler vendored

@ -1 +1 @@
Subproject commit 4c4bcf8a1836eaa2b9e295b46caf67857b726f03 Subproject commit b018ca7999b7a5bd372f56fad4d11f1f9033f255

@ -1,7 +1,10 @@
#pragma once #pragma once
#include <unicorn/unicorn.h> #include <unicorn/unicorn.h>
#include <atomic> #include <atomic>
#include <functional>
#include <nt/image.hpp> #include <nt/image.hpp>
#include <numeric>
#include <string>
#include <vmctx.hpp> #include <vmctx.hpp>
#include <vmprofiler.hpp> #include <vmprofiler.hpp>
@ -17,20 +20,62 @@ class emu_t {
bool init(); bool init();
void emulate(); void emulate();
/// <summary>
/// emulates a single virtual instruction and returns it... this function is
/// used internally to determine if virtual JCC addresses are legit...
/// </summary>
/// <returns>returns the single virtual instruction that was
/// emulated...</returns>
vm::instrs::vinstr_t step();
private: private:
uc_engine* uc; uc_engine* uc;
const vm::vmctx_t* m_vm; const vm::vmctx_t* m_vm;
zydis_reg_t vip, vsp; zydis_reg_t vip, vsp;
/// <summary>
/// single step structure information...
/// </summary>
struct {
bool m_toggle;
uc_context* cpu_context;
std::uint8_t stack[STACK_SIZE];
} m_single_step;
std::vector<vm::instrs::vinstr_t> vinstrs; std::vector<vm::instrs::vinstr_t> vinstrs;
vm::instrs::hndlr_trace_t cc_trace; vm::instrs::hndlr_trace_t cc_trace;
/// <summary>
/// unicorn engine hook
/// </summary>
uc_hook code_exec_hook, invalid_mem_hook, int_hook; uc_hook code_exec_hook, invalid_mem_hook, int_hook;
/// <summary>
/// code execution callback for executable memory ranges of the vmprotect'ed
/// module... essentially used to single step the processor over virtual
/// handlers...
/// </summary>
/// <param name="uc"></param>
/// <param name="address"></param>
/// <param name="size"></param>
/// <param name="obj"></param>
/// <returns></returns>
static bool code_exec_callback(uc_engine* uc, static bool code_exec_callback(uc_engine* uc,
uint64_t address, uint64_t address,
uint32_t size, uint32_t size,
emu_t* obj); emu_t* obj);
/// <summary>
/// invalid memory access handler. no runtime values can possibly effect the
/// decryption of virtual instructions. thus invalid memory accesses can be
/// ignored entirely...
/// </summary>
/// <param name="uc">uc engine context pointer...</param>
/// <param name="type">type of memory access...</param>
/// <param name="address">address of the memory access...</param>
/// <param name="size">size of the memory access...</param>
/// <param name="value">value being read...</param>
/// <param name="obj">emu_t object pointer...</param>
static void invalid_mem(uc_engine* uc, static void invalid_mem(uc_engine* uc,
uc_mem_type type, uc_mem_type type,
uint64_t address, uint64_t address,
@ -38,6 +83,23 @@ class emu_t {
int64_t value, int64_t value,
emu_t* obj); emu_t* obj);
/// <summary>
/// interrupt callback for unicorn engine. this is used to advance rip over
/// division instructions which div by 0...
/// </summary>
/// <param name="uc">the uc engine pointer...</param>
/// <param name="intno">interrupt number...</param>
/// <param name="obj">emu_t object...</param>
static void int_callback(uc_engine* uc, std::uint32_t intno, emu_t* obj); static void int_callback(uc_engine* uc, std::uint32_t intno, emu_t* obj);
/// <summary>
/// determines if there is a JCC in the virtual instruction stream, if there
/// is returns a pair of image based addresses for both of the branches...
/// </summary>
/// <param name="vinstrs">vector of virtual instructions...</param>
/// <returns>returns a pair of imaged based addresses, one for each branch
/// address... if there is no jcc then it returns nothing...</returns>
std::optional<std::pair<std::uintptr_t, std::uintptr_t>> has_jcc(
std::vector<vm::instrs::vinstr_t>& vinstrs);
}; };
} // namespace vm } // namespace vm

@ -59,6 +59,54 @@ bool emu_t::init() {
return true; return true;
} }
vm::instrs::vinstr_t emu_t::step() {
m_single_step.m_toggle = true;
uc_err err;
std::uintptr_t rip = m_vm->m_vm_entry_rva + m_vm->m_module_base,
rsp = STACK_BASE + STACK_SIZE - PAGE_4KB;
if ((err = uc_reg_write(uc, UC_X86_REG_RSP, &rsp))) {
std::printf("> uc_reg_write error, reason = %d\n", err);
return;
}
if ((err = uc_reg_write(uc, UC_X86_REG_RIP, &rip))) {
std::printf("> uc_reg_write error, reason = %d\n", err);
return;
}
cc_trace.m_uc = uc;
cc_trace.m_vip = vip;
cc_trace.m_vsp = vsp;
// -- if there is already exists a cpu context back up then restore from it...
if (m_single_step.cpu_context) {
uc_context_restore(uc, m_single_step.cpu_context);
uc_mem_write(uc, STACK_BASE, m_single_step.stack, STACK_SIZE);
}
// -- single step emulate...
std::printf("> beginning execution at = %p\n", rip);
if ((err = uc_emu_start(uc, rip, 0ull, 0ull, 0ull))) {
std::printf("> error starting emu... reason = %d\n", err);
return;
}
// -- allocate new memory context...
if (!m_single_step.cpu_context) {
uc_context* ctx;
uc_context_alloc(uc, &ctx);
m_single_step.cpu_context = ctx;
}
// -- save cpu and stack...
uc_context_save(uc, m_single_step.cpu_context);
uc_mem_read(uc, STACK_BASE, m_single_step.stack, STACK_SIZE);
m_single_step.m_toggle = false;
return vinstrs.back();
}
void emu_t::emulate() { void emu_t::emulate() {
uc_err err; uc_err err;
std::uintptr_t rip = m_vm->m_vm_entry_rva + m_vm->m_module_base, std::uintptr_t rip = m_vm->m_vm_entry_rva + m_vm->m_module_base,
@ -83,6 +131,9 @@ void emu_t::emulate() {
std::printf("> error starting emu... reason = %d\n", err); std::printf("> error starting emu... reason = %d\n", err);
return; return;
} }
const auto jcc_result = has_jcc(vinstrs);
std::printf("> jcc result = %d\n", jcc_result.has_value());
} }
void emu_t::int_callback(uc_engine* uc, std::uint32_t intno, emu_t* obj) { void emu_t::int_callback(uc_engine* uc, std::uint32_t intno, emu_t* obj) {
@ -196,16 +247,16 @@ bool emu_t::code_exec_callback(uc_engine* uc,
obj->cc_trace.m_vsp = obj->vsp; obj->cc_trace.m_vsp = obj->vsp;
obj->vinstrs.push_back(vinstr); obj->vinstrs.push_back(vinstr);
// free the trace since we will start a new one... // -- free the trace since we will start a new one...
std::for_each(obj->cc_trace.m_instrs.begin(), obj->cc_trace.m_instrs.end(), std::for_each(obj->cc_trace.m_instrs.begin(), obj->cc_trace.m_instrs.end(),
[&](const vm::instrs::emu_instr_t& instr) { [&](const vm::instrs::emu_instr_t& instr) {
uc_context_free(instr.m_cpu); uc_context_free(instr.m_cpu);
}); });
obj->cc_trace.m_instrs.clear(); obj->cc_trace.m_instrs.clear();
if (vinstr.mnemonic == vm::instrs::mnemonic_t::jmp || if (vinstr.mnemonic == vm::instrs::mnemonic_t::jmp ||
vinstr.mnemonic == vm::instrs::mnemonic_t::vmexit) vinstr.mnemonic == vm::instrs::mnemonic_t::vmexit ||
obj->m_single_step.m_toggle)
uc_emu_stop(obj->uc); uc_emu_stop(obj->uc);
} }
return true; return true;
@ -249,4 +300,22 @@ void emu_t::invalid_mem(uc_engine* uc,
break; break;
} }
} }
std::optional<std::pair<std::uintptr_t, std::uintptr_t>> emu_t::has_jcc(
std::vector<vm::instrs::vinstr_t>& vinstrs) {
if (vinstrs.back().mnemonic == vm::instrs::mnemonic_t::vmexit)
return {};
// number of LCONST virtual instructions which load 64bit imm's...
const std::uint32_t lconst_num = std::accumulate(
vinstrs.begin(), vinstrs.end(), 0,
[&](std::uint32_t val, vm::instrs::vinstr_t& v) -> std::uint32_t {
return v.mnemonic == vm::instrs::mnemonic_t::lconst && v.imm.size == 64
? ++val
: val;
});
std::printf("> number of LCONST = %d\n", lconst_num);
return {};
}
} // namespace vm } // namespace vm
Loading…
Cancel
Save