From b560b35284d43a211c67b66d852c950649058b80 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 21 Dec 2021 23:00:08 -0800 Subject: [PATCH] added a few more vmprofiles... --- CMakeLists.txt | 2 + include/vminstrs.hpp | 80 ++++++++++++++++++++++++++++++++++- include/vmutils.hpp | 7 +++ src/vmprofiles/jmp.cpp | 6 ++- src/vmprofiles/lconst.cpp | 86 +++++++++++++++++++++++++++++++++++++ src/vmprofiles/lreg.cpp | 89 +++++++++++++++++++++++++++++++++++++++ src/vmprofiles/sreg.cpp | 23 +++++++--- src/vmutils.cpp | 6 +++ 8 files changed, 289 insertions(+), 10 deletions(-) create mode 100644 src/vmprofiles/lconst.cpp create mode 100644 src/vmprofiles/lreg.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f06b06f..bad5164 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,8 @@ list(APPEND vmprofiler_SOURCES "src/vminstrs.cpp" "src/vmlocate.cpp" "src/vmprofiles/jmp.cpp" + "src/vmprofiles/lconst.cpp" + "src/vmprofiles/lreg.cpp" "src/vmprofiles/sreg.cpp" "src/vmutils.cpp" "include/vmctx.hpp" diff --git a/include/vminstrs.hpp b/include/vminstrs.hpp index 31dc534..30f541f 100644 --- a/include/vminstrs.hpp +++ b/include/vminstrs.hpp @@ -50,7 +50,7 @@ struct vinstr_t { /// "64" here...where the SREGDW varient would have a "32" here... this is the /// stack disposition essentially, or the value on the stack... /// - u8 size; + u8 stack_size; struct { /// @@ -84,6 +84,7 @@ struct emu_instr_t { /// struct hndlr_trace_t { std::uintptr_t m_hndlr_addr; + uc_engine* m_uc; zydis_reg_t m_vip, m_vsp; std::vector m_instrs; }; @@ -137,11 +138,86 @@ struct profiler_t { /// extern profiler_t jmp; extern profiler_t sreg; +extern profiler_t lreg; +extern profiler_t lconst; /// /// unsorted vector of profiles... they get sorted once at runtime... /// -inline std::vector profiles = {&jmp, &sreg}; +inline std::vector profiles = {&jmp, &sreg, &lreg, &lconst}; + +/// +/// no i did not make this by hand, you cannot clown upon me! +/// +inline std::map reg_map = { + {ZYDIS_REGISTER_AL, UC_X86_REG_AL}, + {ZYDIS_REGISTER_CL, UC_X86_REG_CL}, + {ZYDIS_REGISTER_DL, UC_X86_REG_DL}, + {ZYDIS_REGISTER_BL, UC_X86_REG_BL}, + {ZYDIS_REGISTER_AH, UC_X86_REG_AH}, + {ZYDIS_REGISTER_CH, UC_X86_REG_CH}, + {ZYDIS_REGISTER_DH, UC_X86_REG_DH}, + {ZYDIS_REGISTER_BH, UC_X86_REG_BH}, + {ZYDIS_REGISTER_SPL, UC_X86_REG_SPL}, + {ZYDIS_REGISTER_BPL, UC_X86_REG_BPL}, + {ZYDIS_REGISTER_SIL, UC_X86_REG_SIL}, + {ZYDIS_REGISTER_DIL, UC_X86_REG_DIL}, + {ZYDIS_REGISTER_R8B, UC_X86_REG_R8B}, + {ZYDIS_REGISTER_R9B, UC_X86_REG_R9B}, + {ZYDIS_REGISTER_R10B, UC_X86_REG_R10B}, + {ZYDIS_REGISTER_R11B, UC_X86_REG_R11B}, + {ZYDIS_REGISTER_R12B, UC_X86_REG_R12B}, + {ZYDIS_REGISTER_R13B, UC_X86_REG_R13B}, + {ZYDIS_REGISTER_R14B, UC_X86_REG_R14B}, + {ZYDIS_REGISTER_R15B, UC_X86_REG_R15B}, + {ZYDIS_REGISTER_AX, UC_X86_REG_AX}, + {ZYDIS_REGISTER_CX, UC_X86_REG_CX}, + {ZYDIS_REGISTER_DX, UC_X86_REG_DX}, + {ZYDIS_REGISTER_BX, UC_X86_REG_BX}, + {ZYDIS_REGISTER_SP, UC_X86_REG_SP}, + {ZYDIS_REGISTER_BP, UC_X86_REG_BP}, + {ZYDIS_REGISTER_SI, UC_X86_REG_SI}, + {ZYDIS_REGISTER_DI, UC_X86_REG_DI}, + {ZYDIS_REGISTER_R8W, UC_X86_REG_R8W}, + {ZYDIS_REGISTER_R9W, UC_X86_REG_R9W}, + {ZYDIS_REGISTER_R10W, UC_X86_REG_R10W}, + {ZYDIS_REGISTER_R11W, UC_X86_REG_R11W}, + {ZYDIS_REGISTER_R12W, UC_X86_REG_R12W}, + {ZYDIS_REGISTER_R13W, UC_X86_REG_R13W}, + {ZYDIS_REGISTER_R14W, UC_X86_REG_R14W}, + {ZYDIS_REGISTER_R15W, UC_X86_REG_R15W}, + {ZYDIS_REGISTER_EAX, UC_X86_REG_EAX}, + {ZYDIS_REGISTER_ECX, UC_X86_REG_ECX}, + {ZYDIS_REGISTER_EDX, UC_X86_REG_EDX}, + {ZYDIS_REGISTER_EBX, UC_X86_REG_EBX}, + {ZYDIS_REGISTER_ESP, UC_X86_REG_ESP}, + {ZYDIS_REGISTER_EBP, UC_X86_REG_EBP}, + {ZYDIS_REGISTER_ESI, UC_X86_REG_ESI}, + {ZYDIS_REGISTER_EDI, UC_X86_REG_EDI}, + {ZYDIS_REGISTER_R8D, UC_X86_REG_R8D}, + {ZYDIS_REGISTER_R9D, UC_X86_REG_R9D}, + {ZYDIS_REGISTER_R10D, UC_X86_REG_R10D}, + {ZYDIS_REGISTER_R11D, UC_X86_REG_R11D}, + {ZYDIS_REGISTER_R12D, UC_X86_REG_R12D}, + {ZYDIS_REGISTER_R13D, UC_X86_REG_R13D}, + {ZYDIS_REGISTER_R14D, UC_X86_REG_R14D}, + {ZYDIS_REGISTER_R15D, UC_X86_REG_R15D}, + {ZYDIS_REGISTER_RAX, UC_X86_REG_RAX}, + {ZYDIS_REGISTER_RCX, UC_X86_REG_RCX}, + {ZYDIS_REGISTER_RDX, UC_X86_REG_RDX}, + {ZYDIS_REGISTER_RBX, UC_X86_REG_RBX}, + {ZYDIS_REGISTER_RSP, UC_X86_REG_RSP}, + {ZYDIS_REGISTER_RBP, UC_X86_REG_RBP}, + {ZYDIS_REGISTER_RSI, UC_X86_REG_RSI}, + {ZYDIS_REGISTER_RDI, UC_X86_REG_RDI}, + {ZYDIS_REGISTER_R8, UC_X86_REG_R8}, + {ZYDIS_REGISTER_R9, UC_X86_REG_R9}, + {ZYDIS_REGISTER_R10, UC_X86_REG_R10}, + {ZYDIS_REGISTER_R11, UC_X86_REG_R11}, + {ZYDIS_REGISTER_R12, UC_X86_REG_R12}, + {ZYDIS_REGISTER_R13, UC_X86_REG_R13}, + {ZYDIS_REGISTER_R14, UC_X86_REG_R14}, + {ZYDIS_REGISTER_R15, UC_X86_REG_R15}}; /// /// deadstore and opaque branch removal from unicorn engine trace... this is the diff --git a/include/vmutils.hpp b/include/vmutils.hpp index 32c31f8..7c8835f 100644 --- a/include/vmutils.hpp +++ b/include/vmutils.hpp @@ -68,6 +68,13 @@ inline bool open_binary_file(const std::string& file, /// bool is_jmp(const zydis_decoded_instr_t& instr); +/// +/// used by profiles to see if an instruction is a MOV/SX/ZX... +/// +/// +/// +bool is_mov(const zydis_decoded_instr_t& instr); + /// /// prints a disassembly view of a routine... /// diff --git a/src/vmprofiles/jmp.cpp b/src/vmprofiles/jmp.cpp index ef92654..b1d283f 100644 --- a/src/vmprofiles/jmp.cpp +++ b/src/vmprofiles/jmp.cpp @@ -123,6 +123,10 @@ profiler_t jmp = { if (mov_reg_vsp != instrs.end()) vsp = mov_reg_vsp->m_instr.operands[0].reg.value; } - return vinstr_t{mnemonic_t::jmp}; + + vinstr_t res; + memset(&res, NULL, sizeof vinstr_t); + res.mnemonic = mnemonic_t::jmp; + return res; }}; } \ No newline at end of file diff --git a/src/vmprofiles/lconst.cpp b/src/vmprofiles/lconst.cpp new file mode 100644 index 0000000..d8b0acb --- /dev/null +++ b/src/vmprofiles/lconst.cpp @@ -0,0 +1,86 @@ +#include + +namespace vm::instrs { +profiler_t lconst = { + "LCONST", + mnemonic_t::lconst, + {{// MOV REG, [VIP] + [&](const zydis_reg_t vip, + const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return vm::utils::is_mov(instr) && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[1].mem.base == vip; + }, + // SUB VSP, OFFSET + [&](const zydis_reg_t vip, + const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_SUB && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[0].reg.value == vsp && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; + }, + // MOV [VSP], REG + [&](const zydis_reg_t vip, + const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_MOV && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[0].mem.base == vsp && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }}}, + [&](zydis_reg_t& vip, + zydis_reg_t& vsp, + hndlr_trace_t& hndlr) -> std::optional { + vinstr_t res; + res.mnemonic = mnemonic_t::lconst; + res.imm.has_imm = true; + + const auto sub_vsp = std::find_if( + hndlr.m_instrs.begin(), hndlr.m_instrs.end(), + [&](emu_instr_t& instr) -> bool { + const auto& i = instr.m_instr; + return i.mnemonic == ZYDIS_MNEMONIC_SUB && + i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + i.operands[0].reg.value == vsp && + i.operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; + }); + + res.stack_size = sub_vsp->m_instr.operands[1].imm.value.u * 8; + const auto fetch_imm = std::find_if( + hndlr.m_instrs.begin(), hndlr.m_instrs.end(), + [&](emu_instr_t& instr) -> bool { + const auto& i = instr.m_instr; + return vm::utils::is_mov(i) && + i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + i.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && + i.operands[1].mem.base == vip; + }); + + res.imm.size = fetch_imm->m_instr.operands[1].size; + const auto mov_vsp_imm = std::find_if( + hndlr.m_instrs.begin(), hndlr.m_instrs.end(), + [&](emu_instr_t& instr) -> bool { + const auto& i = instr.m_instr; + return i.mnemonic == ZYDIS_MNEMONIC_MOV && + i.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + i.operands[0].mem.base == vsp && + i.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }); + + uc_context* backup; + uc_context_alloc(hndlr.m_uc, &backup); + uc_context_save(hndlr.m_uc, backup); + uc_context_restore(hndlr.m_uc, mov_vsp_imm->m_cpu); + + const uc_x86_reg imm_reg = + vm::instrs::reg_map[mov_vsp_imm->m_instr.operands[1].reg.value]; + + uc_reg_read(hndlr.m_uc, imm_reg, &res.imm.val); + uc_context_restore(hndlr.m_uc, backup); + uc_context_free(backup); + return res; + }}; +} \ No newline at end of file diff --git a/src/vmprofiles/lreg.cpp b/src/vmprofiles/lreg.cpp new file mode 100644 index 0000000..8bf0d63 --- /dev/null +++ b/src/vmprofiles/lreg.cpp @@ -0,0 +1,89 @@ +#include + +namespace vm::instrs { +profiler_t lreg = { + "LREG", + mnemonic_t::lreg, + {{// MOV REG, [VIP] + [&](const zydis_reg_t vip, + const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return vm::utils::is_mov(instr) && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[1].mem.base == vip; + }, + // MOV REG, [RSP+REG] + [&](const zydis_reg_t vip, + const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_MOV && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[1].mem.base == ZYDIS_REGISTER_RSP && + instr.operands[1].mem.index != ZYDIS_REGISTER_NONE; + }, + // SUB VSP, OFFSET + [&](const zydis_reg_t vip, + const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_SUB && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[0].reg.value == vsp && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; + }, + // MOV [VSP], REG + [&](const zydis_reg_t vip, + const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_MOV && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[0].mem.base == vsp && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }}}, + [&](zydis_reg_t& vip, + zydis_reg_t& vsp, + hndlr_trace_t& hndlr) -> std::optional { + vinstr_t res; + memset(&res, NULL, sizeof res); + res.mnemonic = mnemonic_t::lreg; + res.imm.has_imm = true; + res.imm.size = 8; + + const auto sub_vsp = std::find_if( + hndlr.m_instrs.begin(), hndlr.m_instrs.end(), + [&](emu_instr_t& instr) -> bool { + const auto& i = instr.m_instr; + return i.mnemonic == ZYDIS_MNEMONIC_SUB && + i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + i.operands[0].reg.value == vsp && + i.operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; + }); + + res.stack_size = sub_vsp->m_instr.operands[1].imm.value.u * 8; + // // MOV REG, [RSP+REG]... we want the register in [RSP+REG]... + const auto mov_reg_vreg = std::find_if( + hndlr.m_instrs.begin(), hndlr.m_instrs.end(), + [&](emu_instr_t& instr) -> bool { + const auto& i = instr.m_instr; + return i.mnemonic == ZYDIS_MNEMONIC_MOV && + i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + i.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && + i.operands[1].mem.base == ZYDIS_REGISTER_RSP && + i.operands[1].mem.index != ZYDIS_REGISTER_NONE; + }); + + uc_context* backup; + uc_context_alloc(hndlr.m_uc, &backup); + uc_context_save(hndlr.m_uc, backup); + uc_context_restore(hndlr.m_uc, mov_reg_vreg->m_cpu); + + const uc_x86_reg idx_reg = + vm::instrs::reg_map[mov_reg_vreg->m_instr.operands[1].mem.index]; + + uc_reg_read(hndlr.m_uc, idx_reg, &res.imm.val); + uc_context_restore(hndlr.m_uc, backup); + uc_context_free(backup); + return res; + }}; +} \ No newline at end of file diff --git a/src/vmprofiles/sreg.cpp b/src/vmprofiles/sreg.cpp index 0b05d02..0607dce 100644 --- a/src/vmprofiles/sreg.cpp +++ b/src/vmprofiles/sreg.cpp @@ -26,9 +26,7 @@ profiler_t sreg = { [&](const zydis_reg_t vip, const zydis_reg_t vsp, const zydis_decoded_instr_t& instr) -> bool { - return (instr.mnemonic == ZYDIS_MNEMONIC_MOV || - instr.mnemonic == ZYDIS_MNEMONIC_MOVSX || - instr.mnemonic == ZYDIS_MNEMONIC_MOVZX) && + return vm::utils::is_mov(instr) && instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && instr.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && instr.operands[1].mem.base == vip; @@ -47,9 +45,11 @@ profiler_t sreg = { zydis_reg_t& vsp, hndlr_trace_t& hndlr) -> std::optional { vinstr_t res; + memset(&res, NULL, sizeof res); res.mnemonic = mnemonic_t::sreg; + res.imm.has_imm = true; + res.imm.size = 8; - // locates ADD VSP, VALUE... const auto add_vsp = std::find_if( hndlr.m_instrs.begin(), hndlr.m_instrs.end(), [&](emu_instr_t& instr) -> bool { @@ -60,9 +60,7 @@ profiler_t sreg = { i.operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; }); - res.size = add_vsp->m_instr.operands[1].imm.value.u * 8; - - // MOV [RSP+REG], REG... + res.stack_size = add_vsp->m_instr.operands[1].imm.value.u * 8; const auto mov_vreg_value = std::find_if( hndlr.m_instrs.begin(), hndlr.m_instrs.end(), [&](emu_instr_t& instr) -> bool { @@ -74,6 +72,17 @@ profiler_t sreg = { i.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; }); + uc_context* backup; + uc_context_alloc(hndlr.m_uc, &backup); + uc_context_save(hndlr.m_uc, backup); + uc_context_restore(hndlr.m_uc, mov_vreg_value->m_cpu); + + const uc_x86_reg idx_reg = + vm::instrs::reg_map[mov_vreg_value->m_instr.operands[0].mem.index]; + + uc_reg_read(hndlr.m_uc, idx_reg, &res.imm.val); + uc_context_restore(hndlr.m_uc, backup); + uc_context_free(backup); return res; }}; } \ No newline at end of file diff --git a/src/vmutils.cpp b/src/vmutils.cpp index c014666..dfdbd4c 100644 --- a/src/vmutils.cpp +++ b/src/vmutils.cpp @@ -22,6 +22,12 @@ bool is_jmp(const zydis_decoded_instr_t& instr) { instr.mnemonic <= ZYDIS_MNEMONIC_JZ; } +bool is_mov(const zydis_decoded_instr_t& instr) { + return instr.mnemonic == ZYDIS_MNEMONIC_MOV || + instr.mnemonic == ZYDIS_MNEMONIC_MOVSX || + instr.mnemonic == ZYDIS_MNEMONIC_MOVZX; +} + bool flatten(zydis_rtn_t& routine, std::uintptr_t routine_addr, bool keep_jmps,