From 3251b6e475321e929c10bd7f184626b7070e6f9e Mon Sep 17 00:00:00 2001 From: John Doe Date: Wed, 29 Dec 2021 23:29:36 -0800 Subject: [PATCH] working on adding virtual branch support still, wanna make sure i do it right... --- deps/vmprofiler | 2 +- include/vmemu_t.hpp | 62 +++++++++++++++++++++++++++++++++++++ src/vmemu_t.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 135 insertions(+), 4 deletions(-) diff --git a/deps/vmprofiler b/deps/vmprofiler index 4c4bcf8..b018ca7 160000 --- a/deps/vmprofiler +++ b/deps/vmprofiler @@ -1 +1 @@ -Subproject commit 4c4bcf8a1836eaa2b9e295b46caf67857b726f03 +Subproject commit b018ca7999b7a5bd372f56fad4d11f1f9033f255 diff --git a/include/vmemu_t.hpp b/include/vmemu_t.hpp index 0a5caa2..b865683 100644 --- a/include/vmemu_t.hpp +++ b/include/vmemu_t.hpp @@ -1,7 +1,10 @@ #pragma once #include #include +#include #include +#include +#include #include #include @@ -17,20 +20,62 @@ class emu_t { bool init(); void emulate(); + /// + /// emulates a single virtual instruction and returns it... this function is + /// used internally to determine if virtual JCC addresses are legit... + /// + /// returns the single virtual instruction that was + /// emulated... + vm::instrs::vinstr_t step(); + private: uc_engine* uc; const vm::vmctx_t* m_vm; zydis_reg_t vip, vsp; + /// + /// single step structure information... + /// + struct { + bool m_toggle; + uc_context* cpu_context; + std::uint8_t stack[STACK_SIZE]; + } m_single_step; + std::vector vinstrs; vm::instrs::hndlr_trace_t cc_trace; + + /// + /// unicorn engine hook + /// uc_hook code_exec_hook, invalid_mem_hook, int_hook; + /// + /// code execution callback for executable memory ranges of the vmprotect'ed + /// module... essentially used to single step the processor over virtual + /// handlers... + /// + /// + /// + /// + /// + /// static bool code_exec_callback(uc_engine* uc, uint64_t address, uint32_t size, emu_t* obj); + /// + /// invalid memory access handler. no runtime values can possibly effect the + /// decryption of virtual instructions. thus invalid memory accesses can be + /// ignored entirely... + /// + /// uc engine context pointer... + /// type of memory access... + /// address of the memory access... + /// size of the memory access... + /// value being read... + /// emu_t object pointer... static void invalid_mem(uc_engine* uc, uc_mem_type type, uint64_t address, @@ -38,6 +83,23 @@ class emu_t { int64_t value, emu_t* obj); + /// + /// interrupt callback for unicorn engine. this is used to advance rip over + /// division instructions which div by 0... + /// + /// the uc engine pointer... + /// interrupt number... + /// emu_t object... static void int_callback(uc_engine* uc, std::uint32_t intno, emu_t* obj); + + /// + /// 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... + /// + /// vector of virtual instructions... + /// returns a pair of imaged based addresses, one for each branch + /// address... if there is no jcc then it returns nothing... + std::optional> has_jcc( + std::vector& vinstrs); }; } // namespace vm diff --git a/src/vmemu_t.cpp b/src/vmemu_t.cpp index a39bdb7..32c4e78 100644 --- a/src/vmemu_t.cpp +++ b/src/vmemu_t.cpp @@ -59,6 +59,54 @@ bool emu_t::init() { 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() { uc_err err; 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); 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) { @@ -196,16 +247,16 @@ bool emu_t::code_exec_callback(uc_engine* uc, obj->cc_trace.m_vsp = obj->vsp; 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(), [&](const vm::instrs::emu_instr_t& instr) { uc_context_free(instr.m_cpu); }); obj->cc_trace.m_instrs.clear(); - 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); } return true; @@ -249,4 +300,22 @@ void emu_t::invalid_mem(uc_engine* uc, break; } } + +std::optional> emu_t::has_jcc( + std::vector& 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 \ No newline at end of file