#pragma once #include #include #define VIRTUAL_REGISTER_COUNT 24 #define VIRTUAL_SEH_REGISTER 24 namespace vm::instrs { /// /// mnemonic representation of supported virtual instructions... /// enum class mnemonic_t { unknown, sreg, lreg, lconst, add, div, idiv, mul, imul, nand, nop, nor, read, write, shl, shld, shr, shrd, lvsp, svsp, writecr3, readcr3, writecr8, readcr8, cpuid, rdtsc, call, jmp, vmexit }; /// /// the main virtual instruction structure which is returned by profilers... /// struct vinstr_t { /// /// mnemonic of the virtual instruction... /// mnemonic_t mnemonic; /// /// size varient of the virtual instruction... I.E SREGQ would have a value of /// "64" here...where the SREGDW varient would have a "32" here... this is the /// stack disposition essentially, or the value on the stack... /// u8 stack_size; struct { /// /// true if the virtual instruction has an imm false if not... /// bool has_imm; /// /// size in bits of the imm... 8, 16, 32, 64... /// u8 size; /// /// imm value... /// u64 val; } imm; }; /// /// virtual branch type... /// enum class vbranch_type { /// /// vmexit /// none, /// /// virtual jcc /// jcc, /// /// absolute jmp... /// absolute, /// /// jmp table, either indirect or direct... /// table }; /// /// virtual code block /// struct vblk_t { /// /// start address VIP of this basic block... /// struct { /// /// relative virtual address... /// std::uint32_t rva; /// /// image based relative virtual address... /// std::uintptr_t img_base; } m_vip; /// /// virtual instruction pointer and virtual stack pointer used for this basic /// block... /// struct { zydis_reg_t vip; zydis_reg_t vsp; } m_vm; struct { /// /// unicorn-engine cpu context of the first instruction of the jmp /// handler... /// uc_context* ctx; /// /// unicorn-engine stack of the first instruction of the jmp handler... /// std::uint8_t* stack; struct { zydis_reg_t vip; zydis_reg_t vsp; } m_vm; /// /// first instruction of the virtual jmp handler... /// std::uintptr_t rip; } m_jmp; /// /// vector of virtual instructions for this basic block... /// std::vector m_vinstrs; /// /// virtual branch type... /// vbranch_type branch_type; /// /// vector of virtual instruction pointers. one for each branch... /// std::vector branches; }; /// /// virtual routine structure /// struct vrtn_t { /// /// relative virtual address to the first instruction of the vm enter... /// std::uint32_t m_rva; /// /// vector of virtual code blocks... these virtual code blocks contain virtual /// instructions... /// std::vector m_blks; }; /// /// emu instruction containing current cpu register values and such... /// struct emu_instr_t { /// /// decoded instruction... /// zydis_decoded_instr_t m_instr; /// /// cpu context before execution of this instruction... /// uc_context* m_cpu; }; /// /// handler trace containing information about a stream of instructions... also /// contains some information about the virtual machine such as vip and vsp... /// struct hndlr_trace_t { /// /// pointer to the unicorn-engine... used by profilers... /// uc_engine* m_uc; /// /// copy of the stack at the very first instruction of the virtual machine /// handler... /// std::uint8_t* m_stack; /// /// rip at the beginning of the trace... /// std::uintptr_t m_begin; /// /// native register used for virtual instruction pointer... /// zydis_reg_t m_vip; /// /// native register used for the virtual stack pointer... /// zydis_reg_t m_vsp; /// /// vector of emulated, diassembled instructions... /// std::vector m_instrs; }; /// /// matcher function which returns true if an instruction matches a desired /// one... /// using matcher_t = std::function; /// /// virtual instruction structure generator... this can update the vip and vsp /// argument... it cannot update the instruction stream (hndlr)... /// using vinstr_gen_t = std::function( zydis_reg_t& vip, zydis_reg_t& vsp, hndlr_trace_t& hndlr)>; /// /// each virtual instruction has its own profiler_t structure which can generate /// all varients of the virtual instruction for each size... /// struct profiler_t { /// /// string name of the virtual instruction that this profile generates for... /// std::string name; /// /// mnemonic representation of the virtual instruction... /// mnemonic_t mnemonic; /// /// vector of matcher lambda's which return true if a given instruction /// matches... /// std::vector matchers; /// /// generates a virtual instruction structure... /// vinstr_gen_t generate; }; /// /// list of all profiles here... /// extern profiler_t jmp; extern profiler_t sreg; extern profiler_t lreg; extern profiler_t lconst; extern profiler_t add; extern profiler_t lvsp; extern profiler_t svsp; extern profiler_t nand; extern profiler_t nop; extern profiler_t nor; extern profiler_t read; extern profiler_t write; extern profiler_t imul; extern profiler_t shr; 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}; /// /// 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 /// same algorithm as the one in vm::utils::deobfuscate... /// /// void deobfuscate(hndlr_trace_t& trace); /// /// sorts the profiles by descending order of matchers... this will prevent a /// smaller profiler with less matchers from being used when it should not be... /// /// this function can be called multiple times... /// void init(); /// /// determines the virtual instruction for the vm handler given vsp and vip... /// /// vip native register... /// vsp native register... /// /// returns vinstr_t structure... vinstr_t determine(hndlr_trace_t& hndlr); /// /// get profile from mnemonic... /// /// mnemonic of the profile to get... /// pointer to the profile... profiler_t* get_profile(mnemonic_t mnemonic); } // namespace vm::instrs // MOV REG, [VIP] #define IMM_FETCH \ [](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 [VSP], REG #define STR_VALUE \ [](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; \ } // MOV REG, [VSP] #define LOAD_VALUE \ [](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; \ } // SUB VSP, OFFSET #define SUB_VSP \ [](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; \ }