diff --git a/CMakeLists.txt b/CMakeLists.txt index 6669c8a..e7dd2b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,11 +66,20 @@ list(APPEND vmprofiler_SOURCES "src/vmprofiles/nor.cpp" "src/vmprofiles/read.cpp" "src/vmprofiles/shr.cpp" + "src/vmprofiles/shrd.cpp" + "src/vmprofiles/shl.cpp" + "src/vmprofiles/shld.cpp" "src/vmprofiles/sreg.cpp" "src/vmprofiles/svsp.cpp" "src/vmprofiles/vmexit.cpp" "src/vmprofiles/write.cpp" + "src/vmprofiles/lcr0.cpp" + "src/vmprofiles/and.cpp" + "src/vmprofiles/or.cpp" + "src/vmprofiles/writedr7.cpp" "src/vmutils.cpp" + "include/uc_allocation_tracker.hpp" + "src/uc_allocation_tracker.cpp" "include/vmctx.hpp" "include/vminstrs.hpp" "include/vmlocate.hpp" diff --git a/deps/linux-pe b/deps/linux-pe index 9b87640..4f83eae 160000 --- a/deps/linux-pe +++ b/deps/linux-pe @@ -1 +1 @@ -Subproject commit 9b8764004a284fefe599c407f4ebbdf5a8aa4b0a +Subproject commit 4f83eae434696201f5075d65b11bf8329c6d218a diff --git a/deps/unicorn b/deps/unicorn index 63a445c..6c1cbef 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit 63a445cbba18bf1313ac3699b5d25462b5d529f4 +Subproject commit 6c1cbef6ac505d355033aef1176b684d02e1eb3a diff --git a/include/uc_allocation_tracker.hpp b/include/uc_allocation_tracker.hpp new file mode 100644 index 0000000..00093a4 --- /dev/null +++ b/include/uc_allocation_tracker.hpp @@ -0,0 +1,8 @@ +#pragma once +#include + +extern int g_allocation_tracker; + +uc_err uct_context_alloc(uc_engine *uc, uc_context **context); +uc_err uct_context_free(uc_context *context); +void print_allocation_number(); \ No newline at end of file diff --git a/include/vmctx.hpp b/include/vmctx.hpp index 0941aaf..42d2870 100644 --- a/include/vmctx.hpp +++ b/include/vmctx.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace vm { class vmctx_t { @@ -11,12 +12,13 @@ class vmctx_t { std::uintptr_t vm_entry_rva); bool init(); const std::uintptr_t m_module_base, m_image_base, m_vm_entry_rva, - m_image_size; + m_image_size, m_image_load_delta; zydis_reg_t get_vip() const { return m_vip; } zydis_reg_t get_vsp() const { return m_vsp; } zydis_rtn_t get_vm_enter() const { return m_vm_entry; } + const std::array& get_vmentry_push_order() const; private: /// /// m_vip and m_vsp are set to the native registers used for them by the vm @@ -29,5 +31,8 @@ class vmctx_t { /// the virtual machine enter flattened and deobfuscated... /// zydis_rtn_t m_vm_entry; + + //using array container instead of c-style array in order to pass by reference. + std::array vmentry_push_order; }; } // namespace vm \ No newline at end of file diff --git a/include/vminstrs.hpp b/include/vminstrs.hpp index 8eaa05c..f5cdfa2 100644 --- a/include/vminstrs.hpp +++ b/include/vminstrs.hpp @@ -2,6 +2,7 @@ #include #include +#include #define VIRTUAL_REGISTER_COUNT 24 #define VIRTUAL_SEH_REGISTER 24 @@ -10,12 +11,14 @@ namespace vm::instrs { /// /// mnemonic representation of supported virtual instructions... /// -enum class mnemonic_t { +enum class mnemonic_t : uint8_t { unknown, sreg, lreg, lconst, add, + _or, + _and, //The fucking idiots who wrote the standard thought reserving the word "and" was appropriate div, idiv, mul, @@ -31,6 +34,7 @@ enum class mnemonic_t { shrd, lvsp, svsp, + lcr0, writecr3, readcr3, writecr8, @@ -39,7 +43,8 @@ enum class mnemonic_t { rdtsc, call, jmp, - vmexit + vmexit, + writedr7 }; /// @@ -105,6 +110,8 @@ enum class vbranch_type { /// virtual code block /// struct vblk_t { + bool is_branch; + /// /// start address VIP of this basic block... /// @@ -117,7 +124,7 @@ struct vblk_t { /// /// image based relative virtual address... /// - std::uintptr_t img_base; + std::uintptr_t img_based; } m_vip; /// @@ -152,6 +159,8 @@ struct vblk_t { std::uintptr_t rip; } m_jmp; + std::array vmexit_pop_order; + /// /// vector of virtual instructions for this basic block... /// @@ -286,6 +295,8 @@ extern profiler_t sreg; extern profiler_t lreg; extern profiler_t lconst; extern profiler_t add; +extern profiler_t _or; +extern profiler_t _and; extern profiler_t lvsp; extern profiler_t svsp; extern profiler_t nand; @@ -293,18 +304,24 @@ extern profiler_t nop; extern profiler_t nor; extern profiler_t read; extern profiler_t write; +extern profiler_t lcr0; +extern profiler_t writedr7; extern profiler_t imul; +extern profiler_t shl; +extern profiler_t shld; extern profiler_t shr; +extern profiler_t shrd; +extern profiler_t shrd; extern profiler_t vmexit; /// /// unsorted vector of profiles... they get sorted once at runtime... /// inline std::vector profiles = { - &vmexit, &shr, &imul, &nor, &write, &svsp, &read, &nand, - &lvsp, &add, &jmp, &sreg, &lreg, &lconst, &nop}; + &vmexit, &shl, &shld, &shr, &shrd, &imul, &nor, &write, &svsp, &read, + &nand, &lvsp, &add, &jmp, &_or, &_and, &sreg, &lreg, &lcr0, &lconst, &nop, &writedr7}; -/// +/// /// no i did not make this by hand, you cannot clown upon me! /// inline std::map reg_map = { diff --git a/include/vmprofiler.hpp b/include/vmprofiler.hpp index 9ec68aa..bf854ef 100644 --- a/include/vmprofiler.hpp +++ b/include/vmprofiler.hpp @@ -4,4 +4,5 @@ #include #include #include -#include \ No newline at end of file +#include +#include \ No newline at end of file diff --git a/include/vmutils.hpp b/include/vmutils.hpp index 14af5ab..e0bd3bd 100644 --- a/include/vmutils.hpp +++ b/include/vmutils.hpp @@ -70,6 +70,10 @@ inline bool open_binary_file(const std::string& file, /// bool is_jmp(const zydis_decoded_instr_t& instr); +bool is_32_bit_gp(const ZydisRegister reg); + +bool is_64_bit_gp(const ZydisRegister reg); + /// /// used by profiles to see if an instruction is a MOV/SX/ZX... /// diff --git a/src/uc_allocation_tracker.cpp b/src/uc_allocation_tracker.cpp new file mode 100644 index 0000000..16b8d31 --- /dev/null +++ b/src/uc_allocation_tracker.cpp @@ -0,0 +1,22 @@ +#include +#include + +int g_allocation_tracker; + +uc_err uct_context_alloc(uc_engine *uc, uc_context **context) +{ + ++g_allocation_tracker; + //std::printf("Allocations: %p\n", g_allocation_tracker); + return uc_context_alloc(uc, context); +} +uc_err uct_context_free(uc_context *context) +{ + --g_allocation_tracker; + //std::printf("Allocations: %p\n", g_allocation_tracker); + return uc_context_free(context); +} + +void print_allocation_number() +{ + std::printf("uc_context allocations: %p\n", g_allocation_tracker); +} \ No newline at end of file diff --git a/src/vmctx.cpp b/src/vmctx.cpp index fd5eaaa..a8aa73e 100644 --- a/src/vmctx.cpp +++ b/src/vmctx.cpp @@ -8,7 +8,8 @@ vmctx_t::vmctx_t(std::uintptr_t module_base, : m_module_base(module_base), m_image_base(image_base), m_vm_entry_rva(vm_entry_rva), - m_image_size(image_size) {} + m_image_size(image_size), + m_image_load_delta(m_module_base - m_image_base) {} bool vmctx_t::init() { vm::utils::init(); @@ -20,6 +21,40 @@ bool vmctx_t::init() { vm::utils::deobfuscate(m_vm_entry); + //Get the order in which native registers are pushed + int push_index = 0; + for (const auto& instr : m_vm_entry) + { + if (instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSH && + instr.instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + vm::utils::is_64_bit_gp(instr.instr.operands[0].reg.value)) + { + if (std::find(vmentry_push_order.begin(), vmentry_push_order.begin() + push_index, + instr.instr.operands[0].reg.value) != vmentry_push_order.begin() + push_index) + { + //Every register should only be pushed once + std::printf("Error initializing vmctx_t: vmenter pushes could not be parsed.\n"); + vm::utils::print(m_vm_entry); + return false; + } + vmentry_push_order[push_index++] = instr.instr.operands[0].reg.value; + } + else if (instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ) + { + if (std::find(vmentry_push_order.begin(), vmentry_push_order.begin() + push_index, + instr.instr.operands[0].reg.value) != vmentry_push_order.begin() + push_index) + { + // Same shit + std::printf("Error initializing vmctx_t: vmenter pushes could not be parsed.\n"); + vm::utils::print(m_vm_entry); + return false; + } + vmentry_push_order[push_index++] = ZYDIS_REGISTER_RFLAGS; + } + if (push_index == 16) + break; + } + // find mov reg, [rsp+0x90]. this register will be VIP... const auto vip_fetch = std::find_if( m_vm_entry.begin(), m_vm_entry.end(), @@ -53,4 +88,8 @@ bool vmctx_t::init() { m_vsp = vsp_fetch->instr.operands[0].reg.value; return true; } +const std::array& vm::vmctx_t::get_vmentry_push_order() const +{ + return vmentry_push_order; +} } // namespace vm \ No newline at end of file diff --git a/src/vminstrs.cpp b/src/vminstrs.cpp index 4b25806..53db713 100644 --- a/src/vminstrs.cpp +++ b/src/vminstrs.cpp @@ -1,5 +1,5 @@ #include - +#include namespace vm::instrs { void deobfuscate(hndlr_trace_t& trace) { static const auto _uses_reg = [](zydis_decoded_operand_t& op, @@ -64,11 +64,13 @@ void deobfuscate(hndlr_trace_t& trace) { if (std::find(blacklist.begin(), blacklist.end(), itr->m_instr.mnemonic) != blacklist.end()) { + uct_context_free(itr->m_cpu); trace.m_instrs.erase(itr); break; } - if (vm::utils::is_jmp(itr->m_instr)) { + if (vm::utils::is_jmp(itr->m_instr) && itr->m_instr.operands[0].type != ZYDIS_OPERAND_TYPE_REGISTER) { + uct_context_free(itr->m_cpu); trace.m_instrs.erase(itr); break; } @@ -111,6 +113,7 @@ void deobfuscate(hndlr_trace_t& trace) { _writes(read_result->m_instr, reg)) continue; + uct_context_free(itr->m_cpu); trace.m_instrs.erase(itr); break; } @@ -128,8 +131,42 @@ void init() { } vinstr_t determine(hndlr_trace_t& hndlr) { - const auto& instrs = hndlr.m_instrs; - const auto profile = std::find_if( + std::vector trimmed_instrs = hndlr.m_instrs; + // find the last MOV REG, DWORD PTR [VIP] in the instruction stream, then + // remove any instructions from this instruction to the JMP/RET... + const auto rva_fetch = std::find_if( + trimmed_instrs.rbegin(), trimmed_instrs.rend(), + [& vip = hndlr.m_vip]( + const vm::instrs::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 == vip && i.operands[1].size == 32; + }); + + if (rva_fetch != trimmed_instrs.rend()) + trimmed_instrs.erase((rva_fetch + 1).base(), trimmed_instrs.end()); + auto profile = std::find_if( + profiles.begin(), profiles.end(), [&](profiler_t* profile) -> bool { + for (auto& matcher : profile->matchers) { + const auto matched = + std::find_if(trimmed_instrs.begin(), trimmed_instrs.end(), + [&](const emu_instr_t& instr) -> bool { + const auto& i = instr.m_instr; + return matcher(hndlr.m_vip, hndlr.m_vsp, i); + }); + if (matched == trimmed_instrs.end()) + return false; + } + return true; + }); + + if (profile == profiles.end()) + { + const auto& instrs = hndlr.m_instrs; + // Try again with original instruction stream including those after the last MOV REG, DWORD PTR [VIP] just to be sure + profile = std::find_if( profiles.begin(), profiles.end(), [&](profiler_t* profile) -> bool { for (auto& matcher : profile->matchers) { const auto matched = @@ -141,8 +178,9 @@ vinstr_t determine(hndlr_trace_t& hndlr) { if (matched == instrs.end()) return false; } - return true; - }); + return true; + }); + } if (profile == profiles.end()) return vinstr_t{mnemonic_t::unknown}; diff --git a/src/vmlocate.cpp b/src/vmlocate.cpp index 49e3ff8..a6c0700 100644 --- a/src/vmlocate.cpp +++ b/src/vmlocate.cpp @@ -49,6 +49,18 @@ std::vector get_vm_entries(std::uintptr_t module_base, zydis_rtn_t rtn; if (!vm::utils::scn::executable(module_base, result)) continue; + // Make sure that the form of the vmenter is a jmp immediately followed by a call imm + ZydisDecodedInstruction after_push; + if (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(vm::utils::g_decoder.get(), + (void*)(result + 5), 5, &after_push))) + { + if (after_push.mnemonic != ZYDIS_MNEMONIC_CALL || + after_push.operands[0].type != ZYDIS_OPERAND_TYPE_IMMEDIATE) + continue; + } + else + continue; + if (!vm::utils::flatten(rtn, result, false, 500, module_base)) continue; // the last instruction in the stream should be a JMP to a register or a diff --git a/src/vmprofiles/add.cpp b/src/vmprofiles/add.cpp index e02e66c..56b4dcc 100644 --- a/src/vmprofiles/add.cpp +++ b/src/vmprofiles/add.cpp @@ -46,6 +46,7 @@ profiler_t add = { [](zydis_reg_t& vip, zydis_reg_t& vsp, hndlr_trace_t& hndlr) -> std::optional { vinstr_t res{mnemonic_t::add}; + res.imm.has_imm = false; // MOV REG, [VSP] const auto mov_reg_vsp = std::find_if( @@ -71,7 +72,6 @@ profiler_t add = { }); res.stack_size = mov_vsp_offset->m_instr.operands[1].size; - res.imm.size = mov_reg_vsp->m_instr.operands[1].size; return res; }}; } \ No newline at end of file diff --git a/src/vmprofiles/and.cpp b/src/vmprofiles/and.cpp new file mode 100644 index 0000000..fed4203 --- /dev/null +++ b/src/vmprofiles/and.cpp @@ -0,0 +1,63 @@ +#include + +// Loads an address and value from the stack, ands the derefed address with the value +namespace vm::instrs { +profiler_t _and = { + "AND", + mnemonic_t::_and, + {{// MOV REG, [VSP] This is the address + LOAD_VALUE, + // MOV REG, [VSP+8] + [](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 == vsp && + instr.operands[1].mem.disp.has_displacement, + instr.operands[1].mem.disp.value == 8; + }, + // AND [REG], REG + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_AND && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[0].mem.base != ZYDIS_REGISTER_NONE && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }, + // PUSHFQ + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ; + }, + // POP [VSP] + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_POP && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[0].mem.base == vsp; + }}}, + [](zydis_reg_t& vip, zydis_reg_t& vsp, + hndlr_trace_t& hndlr) -> std::optional { + vinstr_t res{mnemonic_t::_and}; + res.imm.has_imm = false; + + // MOV REG [VSP+OFFSET] + const auto mov_vsp_offset = 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 == vsp && + i.operands[1].mem.disp.has_displacement; + }); + if (mov_vsp_offset == hndlr.m_instrs.end()) + return std::nullopt; + + res.stack_size = mov_vsp_offset->m_instr.operands[0].size; + return res; + }}; +} \ No newline at end of file diff --git a/src/vmprofiles/jmp.cpp b/src/vmprofiles/jmp.cpp index e5385fb..3e15e36 100644 --- a/src/vmprofiles/jmp.cpp +++ b/src/vmprofiles/jmp.cpp @@ -72,9 +72,10 @@ profiler_t jmp = { i.operands[1].reg.value == write_dep; }); - if (mov_reg_write_dep == instrs.end()) return {}; - - vsp = mov_reg_write_dep->m_instr.operands[0].reg.value; + if (mov_reg_write_dep == instrs.end()) + vsp = write_dep; + else + vsp = mov_reg_write_dep->m_instr.operands[0].reg.value; } else { // find the MOV REG, [VSP] instruction... const auto mov_reg_deref_vsp = std::find_if( @@ -87,7 +88,8 @@ profiler_t jmp = { i.operands[1].mem.base == vsp; }); - if (mov_reg_deref_vsp == instrs.end()) return {}; + if (mov_reg_deref_vsp == instrs.end()) + return {}; // find the MOV REG, mov_reg_deref_vsp->operands[0].reg.value const auto mov_vip_reg = std::find_if( @@ -100,10 +102,27 @@ profiler_t jmp = { i.operands[1].reg.value == mov_reg_deref_vsp->m_instr.operands[0].reg.value; }); + //It is possible that mov_vip_reg is actually updating the rolling key, if so use original vip + const auto load_handler_rva = std::find_if( + mov_vip_reg, instrs.end(), + [&](const 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 && + vm::utils::is_32_bit_gp(i.operands[0].reg.value) && + i.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && + i.operands[1].mem.base == + mov_vip_reg->m_instr.operands[0].reg.value; + }); - if (mov_vip_reg == instrs.end()) return {}; + if (mov_vip_reg == instrs.end()) + return {}; - vip = mov_vip_reg->m_instr.operands[0].reg.value; + vip = (load_handler_rva != instrs.end()) ? + mov_vip_reg->m_instr.operands[0].reg.value : + mov_vip_reg->m_instr.operands[1].reg.value; + //Ok so basically mov_vip_reg, despite its name, isn't guaranteed to be + //mov vip, reg, and can in fact be mov rkey, vip. // see if VSP gets updated as well... const auto mov_reg_vsp = std::find_if( @@ -123,6 +142,7 @@ profiler_t jmp = { vinstr_t res; res.mnemonic = mnemonic_t::jmp; res.imm.has_imm = false; + res.stack_size = 64; return res; }}; } \ No newline at end of file diff --git a/src/vmprofiles/lcr0.cpp b/src/vmprofiles/lcr0.cpp new file mode 100644 index 0000000..d3964a2 --- /dev/null +++ b/src/vmprofiles/lcr0.cpp @@ -0,0 +1,44 @@ +#include + +//Loads CR0 onto the stack +namespace vm::instrs { +profiler_t lcr0 = { + "LCR0", + mnemonic_t::lcr0, + { + // MOV REG, CR0 + [](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_REGISTER && + instr.operands[1].reg.value == ZYDIS_REGISTER_CR0; + }, + // 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[0].mem.index == ZYDIS_REGISTER_NONE && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[1].reg.value != vsp; + } + }, + [](zydis_reg_t& vip, zydis_reg_t& vsp, + hndlr_trace_t& hndlr) -> std::optional { + vinstr_t res{mnemonic_t::lcr0}; + res.imm.has_imm = false; + res.stack_size = 64; + return res; + } +}; +} diff --git a/src/vmprofiles/lvsp.cpp b/src/vmprofiles/lvsp.cpp index 5beece4..6eff169 100644 --- a/src/vmprofiles/lvsp.cpp +++ b/src/vmprofiles/lvsp.cpp @@ -15,7 +15,7 @@ profiler_t lvsp = { }}}, [](zydis_reg_t& vip, zydis_reg_t& vsp, hndlr_trace_t& hndlr) -> std::optional { - vinstr_t res{mnemonic_t::svsp}; + vinstr_t res{mnemonic_t::lvsp}; res.imm.has_imm = false; const auto load_vsp = std::find_if( diff --git a/src/vmprofiles/nand.cpp b/src/vmprofiles/nand.cpp index 0638a5a..16553f5 100644 --- a/src/vmprofiles/nand.cpp +++ b/src/vmprofiles/nand.cpp @@ -21,10 +21,10 @@ profiler_t nand = { return instr.mnemonic == ZYDIS_MNEMONIC_NOT && instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER; }, - // AND REG, REG + // OR REG, REG [](const zydis_reg_t vip, const zydis_reg_t vsp, const zydis_decoded_instr_t& instr) -> bool { - return instr.mnemonic == ZYDIS_MNEMONIC_AND && + return instr.mnemonic == ZYDIS_MNEMONIC_OR && instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; }, diff --git a/src/vmprofiles/nor.cpp b/src/vmprofiles/nor.cpp index 53bedbb..fd04544 100644 --- a/src/vmprofiles/nor.cpp +++ b/src/vmprofiles/nor.cpp @@ -21,10 +21,10 @@ profiler_t nor = { return instr.mnemonic == ZYDIS_MNEMONIC_NOT && instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER; }, - // OR REG, REG + // AND REG, REG [](const zydis_reg_t vip, const zydis_reg_t vsp, const zydis_decoded_instr_t& instr) -> bool { - return instr.mnemonic == ZYDIS_MNEMONIC_OR && + return instr.mnemonic == ZYDIS_MNEMONIC_AND && instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; }, @@ -51,7 +51,7 @@ profiler_t nor = { }}}, [](zydis_reg_t& vip, zydis_reg_t& vsp, hndlr_trace_t& hndlr) -> std::optional { - vinstr_t res{mnemonic_t::nand}; + vinstr_t res{mnemonic_t::nor}; res.imm.has_imm = false; // MOV [VSP+OFFSET], REG diff --git a/src/vmprofiles/or.cpp b/src/vmprofiles/or.cpp new file mode 100644 index 0000000..95fbb5c --- /dev/null +++ b/src/vmprofiles/or.cpp @@ -0,0 +1,60 @@ +#include + +namespace vm::instrs { +profiler_t _or = { + "OR", + mnemonic_t::_or, + {{// MOV REG, [VSP] + LOAD_VALUE, + // MOV REG, [VSP+OFFSET] + [](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 == vsp && + instr.operands[1].mem.disp.has_displacement; + }, + // OR [REG], REG + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_OR && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[0].mem.base != ZYDIS_REGISTER_NONE && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }, + // PUSHFQ + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ; + }, + // POP [VSP] + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_POP && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[0].mem.base == vsp; + }}}, + [](zydis_reg_t& vip, zydis_reg_t& vsp, + hndlr_trace_t& hndlr) -> std::optional { + vinstr_t res{mnemonic_t::_or}; + res.imm.has_imm = false; + + // MOV REG [VSP+OFFSET] + const auto reg_vsp_offset = 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 == vsp && + i.operands[1].mem.disp.has_displacement; + }); + if (reg_vsp_offset == hndlr.m_instrs.end()) + return std::nullopt; + res.stack_size = reg_vsp_offset->m_instr.operands[0].size; + return res; + }}; +} \ No newline at end of file diff --git a/src/vmprofiles/read.cpp b/src/vmprofiles/read.cpp index 3bf26dc..8c61eca 100644 --- a/src/vmprofiles/read.cpp +++ b/src/vmprofiles/read.cpp @@ -26,13 +26,13 @@ profiler_t read = { 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 && + return i.mnemonic == ZYDIS_MNEMONIC_MOV || i.mnemonic == ZYDIS_MNEMONIC_MOVZX && i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && i.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && i.operands[1].mem.base != vsp; }); - res.stack_size = mov_reg_reg->m_instr.operands[0].size; + res.stack_size = mov_reg_reg->m_instr.operands[1].size; return res; }}; } \ No newline at end of file diff --git a/src/vmprofiles/shl.cpp b/src/vmprofiles/shl.cpp new file mode 100644 index 0000000..bb8cc87 --- /dev/null +++ b/src/vmprofiles/shl.cpp @@ -0,0 +1,63 @@ +#include + +namespace vm::instrs { +profiler_t shl = { + "SHL", + mnemonic_t::shl, + {{// MOV REG, [VSP] + LOAD_VALUE, + // MOV REG, [VSP+OFFSET] + [](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 == vsp && + instr.operands[1].mem.disp.has_displacement; + }, + // SHL REG, REG + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_SHL && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }, + // MOV [VSP+OFFSET], 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[0].mem.disp.has_displacement && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }, + // PUSHFQ + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ; + }, + // POP [VSP] + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_POP && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[0].mem.base == vsp; + }}}, + [](zydis_reg_t& vip, zydis_reg_t& vsp, + hndlr_trace_t& hndlr) -> std::optional { + vinstr_t res{mnemonic_t::shl}; + res.imm.has_imm = false; + + const auto shl_reg = 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_SHL && + i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + i.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }); + + res.stack_size = shl_reg->m_instr.operands[0].size; + return res; + }}; +} \ No newline at end of file diff --git a/src/vmprofiles/shld.cpp b/src/vmprofiles/shld.cpp new file mode 100644 index 0000000..edbc98b --- /dev/null +++ b/src/vmprofiles/shld.cpp @@ -0,0 +1,73 @@ +#include + +namespace vm::instrs { +profiler_t shld = { + "SHLD", + mnemonic_t::shld, + {{// MOV REG, [VSP] + LOAD_VALUE, + // MOV REG, [VSP+OFFSET] + [](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 == vsp && + instr.operands[1].mem.disp.has_displacement; + }, + // MOV REG, [VSP+OFFSET] + [](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 == vsp && + instr.operands[1].mem.disp.has_displacement; + }, + // SHLD REG, REG + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_SHLD && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }, + // MOV [VSP+OFFSET], 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[0].mem.disp.has_displacement && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }, + // PUSHFQ + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ; + }, + // POP [VSP] + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_POP && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[0].mem.base == vsp; + }}}, + [](zydis_reg_t& vip, zydis_reg_t& vsp, + hndlr_trace_t& hndlr) -> std::optional { + vinstr_t res{mnemonic_t::shld}; + res.imm.has_imm = false; + + const auto shld_reg = 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_SHLD && + i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + i.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }); + + res.stack_size = shld_reg->m_instr.operands[0].size; + return res; + }}; + +} \ No newline at end of file diff --git a/src/vmprofiles/shrd.cpp b/src/vmprofiles/shrd.cpp new file mode 100644 index 0000000..77268b4 --- /dev/null +++ b/src/vmprofiles/shrd.cpp @@ -0,0 +1,73 @@ +#include + +namespace vm::instrs { +profiler_t shrd = { + "SHRD", + mnemonic_t::shrd, + {{// MOV REG, [VSP] + LOAD_VALUE, + // MOV REG, [VSP+OFFSET] + [](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 == vsp && + instr.operands[1].mem.disp.has_displacement; + }, + // MOV REG, [VSP+OFFSET] + [](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 == vsp && + instr.operands[1].mem.disp.has_displacement; + }, + // SHRD REG, REG + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_SHRD && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }, + // MOV [VSP+OFFSET], 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[0].mem.disp.has_displacement && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }, + // PUSHFQ + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ; + }, + // POP [VSP] + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_POP && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + instr.operands[0].mem.base == vsp; + }}}, + [](zydis_reg_t& vip, zydis_reg_t& vsp, + hndlr_trace_t& hndlr) -> std::optional { + vinstr_t res{mnemonic_t::shrd}; + res.imm.has_imm = false; + + const auto shrd_reg = 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_SHRD && + i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + i.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; + }); + + res.stack_size = shrd_reg->m_instr.operands[0].size; + return res; + }}; + +} \ No newline at end of file diff --git a/src/vmprofiles/svsp.cpp b/src/vmprofiles/svsp.cpp index 7175fcf..bd565d0 100644 --- a/src/vmprofiles/svsp.cpp +++ b/src/vmprofiles/svsp.cpp @@ -31,19 +31,20 @@ profiler_t svsp = { }}}, [](zydis_reg_t& vip, zydis_reg_t& vsp, hndlr_trace_t& hndlr) -> std::optional { - vinstr_t res{mnemonic_t::lvsp}; - const auto sub_vsp = std::find_if( + vinstr_t res{mnemonic_t::svsp}; + const auto mov_vsp_reg = 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; + return i.mnemonic == ZYDIS_MNEMONIC_MOV && + i.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && + i.operands[0].mem.base == vsp && + i.operands[0].mem.disp.has_displacement == false && + i.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER; }); res.imm.has_imm = false; - res.stack_size = sub_vsp->m_instr.operands[1].imm.value.u; + res.stack_size = mov_vsp_reg->m_instr.operands[1].size; return res; }}; } \ No newline at end of file diff --git a/src/vmprofiles/write.cpp b/src/vmprofiles/write.cpp index 8a6a686..e12f411 100644 --- a/src/vmprofiles/write.cpp +++ b/src/vmprofiles/write.cpp @@ -29,6 +29,8 @@ profiler_t write = { return instr.mnemonic == ZYDIS_MNEMONIC_MOV && instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY && instr.operands[0].mem.base != vsp && + instr.operands[0].mem.base != ZYDIS_REGISTER_RSP && + //!instr.operands[0].mem.disp.has_displacement && instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER && instr.operands[1].reg.value != vsp; }}}, diff --git a/src/vmprofiles/writedr7.cpp b/src/vmprofiles/writedr7.cpp new file mode 100644 index 0000000..6218abf --- /dev/null +++ b/src/vmprofiles/writedr7.cpp @@ -0,0 +1,37 @@ +#include + +//Write value on top of stack to dr7 +namespace vm::instrs { +profiler_t writedr7 = { + "WRITEDR7", + mnemonic_t::writedr7, + { + // MOV REG, [VSP+OFFSET] + LOAD_VALUE, + // ADD VSP, OFFSET + [](const zydis_reg_t vip, const zydis_reg_t vsp, + const zydis_decoded_instr_t& instr) -> bool { + return instr.mnemonic == ZYDIS_MNEMONIC_ADD && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[0].reg.value == vsp && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; + }, + // MOV DR7, 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[0].reg.value == ZYDIS_REGISTER_DR7 && + instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[1].reg.value != vsp; + } + }, + [](zydis_reg_t& vip, zydis_reg_t& vsp, + hndlr_trace_t& hndlr) -> std::optional { + vinstr_t res{mnemonic_t::writedr7}; + res.stack_size == 64; + res.imm.has_imm = false; + return res; + } +}; +} diff --git a/src/vmutils.cpp b/src/vmutils.cpp index 73e917c..06cfff7 100644 --- a/src/vmutils.cpp +++ b/src/vmutils.cpp @@ -28,6 +28,16 @@ bool is_mov(const zydis_decoded_instr_t& instr) { instr.mnemonic == ZYDIS_MNEMONIC_MOVZX; } +bool is_32_bit_gp(const ZydisRegister reg) +{ + return reg >= ZYDIS_REGISTER_EAX && reg <= ZYDIS_REGISTER_R15D; +} + +bool is_64_bit_gp(const ZydisRegister reg) +{ + return reg >= ZYDIS_REGISTER_RAX && reg <= ZYDIS_REGISTER_R15; +} + bool flatten(zydis_rtn_t& routine, std::uintptr_t routine_addr, bool keep_jmps,