├── vmp ├── jit.py ├── __pycache__ │ ├── sem.cpython-39.pyc │ ├── arch.cpython-39.pyc │ ├── regs.cpython-39.pyc │ ├── disasm.cpython-39.pyc │ └── lifter_model_call.cpython-39.pyc ├── disasm.py ├── lifter_model_call.py ├── regs.py ├── sem.py └── arch.py ├── requirements.txt ├── README ├── LICENSE ├── deobfuscate_vmp_handler.py ├── unpack_vmp_handles.py └── vmp_bytecode_extractor.py /vmp/jit.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | dumpulator==0.1.4 2 | triton-library==1.0.0rc2 3 | unicorn==2.0.1.post 4 | -------------------------------------------------------------------------------- /vmp/__pycache__/sem.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh4m2hwz/devirt_vmp/HEAD/vmp/__pycache__/sem.cpython-39.pyc -------------------------------------------------------------------------------- /vmp/__pycache__/arch.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh4m2hwz/devirt_vmp/HEAD/vmp/__pycache__/arch.cpython-39.pyc -------------------------------------------------------------------------------- /vmp/__pycache__/regs.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh4m2hwz/devirt_vmp/HEAD/vmp/__pycache__/regs.cpython-39.pyc -------------------------------------------------------------------------------- /vmp/__pycache__/disasm.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh4m2hwz/devirt_vmp/HEAD/vmp/__pycache__/disasm.cpython-39.pyc -------------------------------------------------------------------------------- /vmp/__pycache__/lifter_model_call.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh4m2hwz/devirt_vmp/HEAD/vmp/__pycache__/lifter_model_call.cpython-39.pyc -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | unpack_vmp_handles.py - automation unpacker vmprotect handles, required dmp file contains EP of vmenter instruction 2 | deobfuscate_vmp_handler.py - automation extractor vmprotect single handler, required dmp file contains ep of handler 3 | -------------------------------------------------------------------------------- /vmp/disasm.py: -------------------------------------------------------------------------------- 1 | from miasm.core.asmblock import disasmEngine 2 | from miasm.arch.vmp.arch import mn_vmp 3 | 4 | 5 | class dis_vmp(disasmEngine): 6 | """MeP miasm disassembly engine - Big Endian 7 | Notes: 8 | - its is mandatory to call the miasm Machine 9 | """ 10 | def __init__(self, bs=None, **kwargs): 11 | super(dis_vmp, self).__init__(mn_vmp, None, bs, **kwargs) -------------------------------------------------------------------------------- /vmp/lifter_model_call.py: -------------------------------------------------------------------------------- 1 | from miasm.ir.analysis import LifterModelCall 2 | from miasm.arch.vmp.sem import Lifter_vmp 3 | 4 | class LifterModelCallVmp(Lifter_vmp, LifterModelCall): 5 | 6 | def __init__(self, loc_db): 7 | Lifter_vmp.__init__(self, loc_db) 8 | def get_out_regs(self, _): 9 | return set([self.sp]) 10 | 11 | def sizeof_char(self): 12 | return 8 13 | 14 | def sizeof_short(self): 15 | return 16 16 | 17 | def sizeof_int(self): 18 | return 32 19 | 20 | def sizeof_long(self): 21 | return 32 22 | 23 | def sizeof_longlong(self): 24 | return 64 25 | 26 | def sizeof_pointer(self): 27 | return 64 28 | -------------------------------------------------------------------------------- /vmp/regs.py: -------------------------------------------------------------------------------- 1 | from miasm.expression.expression import ExprId 2 | from miasm.core.cpu import gen_regs 3 | 4 | exception_flags = ExprId("exception_flags", 32) 5 | exception_flags_init = ExprId("exception_flags_init", 32) 6 | 7 | gpr_names = ["R%d" % r for r in range(255)] # register names 8 | gpr_exprs, gpr_inits, gpr_infos = gen_regs(gpr_names, globals(),sz=64) # sz=32 bits (default) 9 | # PC SP QP 10 | csr_names = ["VIP", "VSP"] 11 | csr_exprs, csr_inits, csr_infos = gen_regs(csr_names, globals(),sz=64) 12 | 13 | PC = csr_exprs[0] 14 | SP = csr_exprs[1] 15 | 16 | PC_init = csr_inits[0] 17 | SP_init = csr_inits[1] 18 | 19 | # Set registers initial values 20 | all_regs_ids = gpr_exprs + csr_exprs + [exception_flags] 21 | all_regs_ids_init = gpr_inits + csr_inits + [exception_flags_init] 22 | all_regs_ids_no_alias = all_regs_ids[:] 23 | all_regs_ids_byname = dict([(x.name, x) for x in all_regs_ids]) 24 | 25 | regs_init = dict() # mandatory name 26 | for i, r in enumerate(all_regs_ids): 27 | regs_init[r] = all_regs_ids_init[i] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Artem Nechaev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vmp/sem.py: -------------------------------------------------------------------------------- 1 | from miasm.arch.aarch64.sem import extend_arg 2 | from miasm.core.cpu import sign_ext 3 | from miasm.expression.expression import * 4 | from miasm.arch.vmp.regs import * 5 | from miasm.arch.vmp.arch import mn_vmp 6 | from miasm.ir.ir import Lifter 7 | 8 | def vpushi64(_, instr, val): 9 | e = [ 10 | ExprAssign(SP,SP - ExprInt(8,64)), 11 | ExprAssign(ExprMem(SP,64),ExprInt(val.arg,64)) 12 | ] 13 | return e, [] 14 | 15 | def vpushrq(_, instr, reg): 16 | e = [ 17 | ExprAssign(SP,SP - ExprInt(8,64)), 18 | ExprAssign(ExprMem(SP,64),reg) 19 | ] 20 | return e, [] 21 | 22 | def vpopq(_, instr, reg): 23 | e = [ 24 | ExprAssign(SP,ExprOp('+',SP,ExprInt(8,64))), 25 | ExprAssign(reg,ExprMem(SP,64)) 26 | ] 27 | return e, [] 28 | 29 | def vpushvsp(_, instr): 30 | e = [ 31 | ExprAssign(SP,SP - ExprInt(8,64)), 32 | ExprAssign(ExprMem(SP,64),SP) 33 | ] 34 | return e, [] 35 | 36 | def vloadtovsp(_, instr): 37 | e = [ 38 | ExprAssign(ExprMem(SP,64),ExprMem(ExprMem(SP,64),64)), 39 | ExprAssign(SP,ExprMem(SP,64)) 40 | ] 41 | return e, [] 42 | 43 | def vloadq(_, instr): 44 | e = [ 45 | ExprAssign(ExprMem(SP,64),ExprMem(ExprMem(SP,64),64)) 46 | ] 47 | return e,[] 48 | 49 | def vloadqstack(_, instr): 50 | e = [ 51 | ExprAssign(ExprMem(SP,64),ExprMem(ExprMem(SP,64),64)) 52 | ] 53 | return e,[] 54 | 55 | def vmenter(_, instr): 56 | e = [ 57 | ExprAssign(SP,SP_init), 58 | ExprAssign(PC,PC_init) 59 | ] 60 | return e,[] 61 | 62 | def vmswitch(_, instr): 63 | return [],[] 64 | 65 | def vaddq(_, instr): 66 | res = ExprMem(SP,64) + ExprMem(ExprOp('+',SP,ExprInt(8,64)),64) 67 | e = [ 68 | ExprAssign(ExprMem(ExprOp('+',SP,ExprInt(8,64)),64),res), 69 | ExprAssign(ExprMem(SP,64),ExprId('VADDQ_FLAGS',64)) 70 | ] 71 | return e, [] 72 | 73 | def vornq(_ ,instr): 74 | res = ~(ExprMem(SP,64) | ExprMem(ExprOp('+',SP,ExprInt(8,64)),64)) 75 | e = [ 76 | ExprAssign(ExprMem(ExprOp('+',SP,ExprInt(8,64)),64),res), 77 | ExprAssign(ExprMem(SP,64),ExprId('VORNQ_FLAGS',64)) 78 | ] 79 | return e, [] 80 | 81 | def vandnq(_ ,instr): 82 | res = ~(ExprMem(SP,64) & ExprMem(ExprOp('+',SP,ExprInt(8,64)),64)) 83 | e = [ 84 | ExprAssign(ExprMem(ExprOp('+',SP,ExprInt(8,64)),64),res), 85 | ExprAssign(ExprMem(SP,64),ExprId('VANDNQ_FLAGS',64)) 86 | ] 87 | return e, [] 88 | 89 | 90 | mnemo_func = { 91 | "VPUSHI64":vpushi64, 92 | "VPUSHVSP":vpushvsp, 93 | "VPUSHRQ": vpushrq, 94 | "VLOADQSTACK": vloadqstack, 95 | "VLOADTOVSP": vloadtovsp, 96 | "VLOADQ": vloadq, 97 | "VPOPQ": vpopq, 98 | "VADDQ": vaddq, 99 | "VANDNQ": vandnq, 100 | "VORNQ": vornq, 101 | "VMENTER": vmenter, 102 | "VMSWITCH": vmswitch 103 | } 104 | 105 | 106 | class Lifter_vmp(Lifter): 107 | """Toshiba MeP miasm IR - Big Endian 108 | It transforms an instructon into an IR. 109 | """ 110 | addrsize = 64 111 | def __init__(self, loc_db=None): 112 | Lifter.__init__(self, mn_vmp, None, loc_db) 113 | self.pc = mn_vmp.getpc() 114 | self.sp = mn_vmp.getsp() 115 | self.IRDst = ExprId("IRDst", 64) 116 | 117 | def get_ir(self, instr): 118 | """Get the IR from a miasm instruction.""" 119 | args = instr.args 120 | instr_ir, extra_ir = mnemo_func[instr.name](self, instr, *args) 121 | return instr_ir, extra_ir 122 | -------------------------------------------------------------------------------- /deobfuscate_vmp_handler.py: -------------------------------------------------------------------------------- 1 | from triton import * 2 | from dumpulator import Dumpulator 3 | from sys import argv 4 | import json 5 | 6 | def load_binary(filename): 7 | dp = Dumpulator(filename,quiet=True) 8 | print("[*] loading dump into triton dse engine") 9 | ctx = TritonContext(ARCH.X86_64) 10 | ctx.setMode(MODE.ALIGNED_MEMORY, True) 11 | mappings = dp.memory.map() 12 | for m in mappings: 13 | base = m.base 14 | size = m.region_size 15 | if m.protect.name == "UNDEFINED": 16 | continue 17 | if len(m.info) > 0: 18 | if m.info[0] == "PEB": 19 | print("[*] loading peb") 20 | ctx.setConcreteMemoryAreaValue(0,bytes(dp.memory.read(base, size))) 21 | continue 22 | print(f"[*] writing data into region: va_range={hex(base)}-{hex(base+size)}, perms: {m.protect.name}") 23 | ctx.setConcreteMemoryAreaValue(base,bytes(dp.memory.read(base, size))) 24 | print("[*] initializing cpu registers") 25 | init_triton_dse_regs(dp, ctx) 26 | print("[+] complete init execution context") 27 | return ctx,dp.regs.rip 28 | 29 | 30 | def init_triton_dse_regs(dp,ctx): 31 | ctx.setConcreteRegisterValue(ctx.registers.rax,dp.regs.rax) 32 | ctx.setConcreteRegisterValue(ctx.registers.rcx,dp.regs.rcx) 33 | ctx.setConcreteRegisterValue(ctx.registers.rdx,dp.regs.rdx) 34 | ctx.setConcreteRegisterValue(ctx.registers.rbx,dp.regs.rbx) 35 | ctx.setConcreteRegisterValue(ctx.registers.rsp,dp.regs.rsp) 36 | ctx.setConcreteRegisterValue(ctx.registers.rbp,dp.regs.rbp) 37 | ctx.setConcreteRegisterValue(ctx.registers.rsi,dp.regs.rsi) 38 | ctx.setConcreteRegisterValue(ctx.registers.rdi,dp.regs.rdi) 39 | ctx.setConcreteRegisterValue(ctx.registers.r8,dp.regs.r8) 40 | ctx.setConcreteRegisterValue(ctx.registers.r9,dp.regs.r9) 41 | ctx.setConcreteRegisterValue(ctx.registers.r10,dp.regs.r10) 42 | ctx.setConcreteRegisterValue(ctx.registers.r11,dp.regs.r11) 43 | ctx.setConcreteRegisterValue(ctx.registers.r12,dp.regs.r12) 44 | ctx.setConcreteRegisterValue(ctx.registers.r13,dp.regs.r13) 45 | ctx.setConcreteRegisterValue(ctx.registers.r14,dp.regs.r14) 46 | ctx.setConcreteRegisterValue(ctx.registers.r15,dp.regs.r15) 47 | 48 | 49 | def symbolizing_vm_context(ctx,config): 50 | print("[+] symbolizing runtime context") 51 | sym_regs = config.get('registers') 52 | sym_mems = config.get("memory") 53 | if sym_regs == None: 54 | print("[-] invalid config: registers array not found!") 55 | exit(-1) 56 | if sym_mems == None: 57 | print("[-] invalid config: memory array not found!") 58 | exit(-1) 59 | for sym_reg in sym_regs: 60 | reg_name = sym_reg.get("reg_name") 61 | var_name = sym_reg.get("var_name") 62 | if reg_name == None or var_name == None: 63 | print("[-] invalid reg line in config") 64 | exit(-1) 65 | print(f"[+] symbolizing register {reg_name} as {var_name}") 66 | ctx.symbolizeRegister(ctx.getRegister(reg_name),var_name) 67 | for sym_mem in sym_mems: 68 | address = sym_mem.get("address") 69 | size = sym_mem.get("size") 70 | if address == None or size == None: 71 | print("[-] invalid mem line in config") 72 | exit(-1) 73 | a = MemoryAccess(int(address,16),int(size)) 74 | print(f"symbolizing memory {a}") 75 | ctx.symbolizeMemory(a) 76 | print("[+] finish symbolize execution context") 77 | 78 | 79 | def emulate(ctx): 80 | print("[*] starting symbolic emulation") 81 | rip = ctx.getConcreteRegisterValue(ctx.registers.rip) 82 | while True: 83 | opcodes = ctx.getConcreteMemoryAreaValue(rip,16) 84 | insn = Instruction(rip,opcodes) 85 | ctx.disassembly(insn) 86 | opcode = insn.getType() 87 | ops = insn.getOperands() 88 | if opcode == OPCODE.X86.TEST or opcode == OPCODE.X86.CMP: 89 | rip+=insn.getSize() 90 | continue 91 | ctx.processing(insn) 92 | if opcode == OPCODE.X86.RET or \ 93 | opcode == OPCODE.X86.JMP and ops[0].getType() == OPERAND.REG: 94 | break 95 | rip = ctx.getConcreteRegisterValue(ctx.registers.rip) 96 | print("[+] done emulation") 97 | return 98 | 99 | #def slice_trace(ctx,expr): 100 | # sliced_reg = ctx.sliceExpressions(expr) 101 | # keys = sorted(sliced_reg) 102 | # trace = {} 103 | # for key in keys: 104 | # disasm = sliced_reg[key].getDisassembly() 105 | # if len(disasm) == 0: 106 | # continue 107 | # address, insn = disasm.split(": ") 108 | # trace[int(address,16)] = insn 109 | # return trace 110 | 111 | 112 | #def slice_mem_trace(ctx,mem): 113 | # return slice_trace(ctx,ctx.getSymbolicMemory(mem)) 114 | 115 | #def slice_reg_trace(ctx,reg): 116 | # return slice_trace(ctx,ctx.getSymbolicRegister(reg)) 117 | 118 | 119 | #def concat_traces(arr_traces): 120 | # trace_addrs = [] 121 | # for trace in arr_traces: 122 | # trace_addrs+=list(trace.keys()) 123 | # concat_trace = {} 124 | # for address in sorted(trace_addrs): 125 | # for trace in arr_traces: 126 | # if trace.get(address) != None: 127 | # concat_trace[address] = trace.get(address) 128 | # return concat_trace 129 | 130 | def deobfuscate(ctx): 131 | dup_insns = [] 132 | sexprs = ctx.getSymbolicExpressions() 133 | keys = sorted(sexprs) 134 | for key in keys: 135 | ty = sexprs[key].getType() 136 | if ty == SYMBOLIC.REGISTER_EXPRESSION or \ 137 | ty == SYMBOLIC.VOLATILE_EXPRESSION or \ 138 | ty == SYMBOLIC.MEMORY_EXPRESSION: 139 | disasm = sexprs[key].getDisassembly() 140 | if len(disasm) > 0: 141 | dup_insns.append(disasm) 142 | insns = [] 143 | for i in range(len(dup_insns)): 144 | if dup_insns[i] not in insns: 145 | insns.append(dup_insns[i]) 146 | return insns 147 | 148 | 149 | def parse_config(filenameconfig): 150 | print("[*] parsing config") 151 | with open(filenameconfig) as f: 152 | j = json.load(f) 153 | return j 154 | 155 | 156 | def drop_hdl_insn(filenamedump,filenameconfig): 157 | ctx, rip = load_binary(filenamedump) 158 | ctx.setConcreteRegisterValue(ctx.registers.rip,rip) 159 | config = parse_config(filenameconfig) 160 | symbolizing_vm_context(ctx,config) 161 | emulate(ctx) 162 | print("[+] clearing obfuscated vmp handler") 163 | trace = deobfuscate(ctx) 164 | print("[+] complete deobfuscate") 165 | trace_s = str() 166 | for insn in trace: 167 | trace_s+=insn.split(": ")[1]+"\n" 168 | return trace_s 169 | 170 | def main(): 171 | if len(argv) != 3: 172 | print(""" 173 | ./vmp_deobfuscate_handler.py 174 | - dump file contains EP vmp handler 175 | - config contains symbolic registers and memory 176 | """) 177 | else: 178 | open("deobfuscated_vmp_hdl.asm","w").write(drop_hdl_insn(argv[1],argv[2])) 179 | print("[+] saved deobfuscated instructions handler trace in file deobfuscated_vmp_hdl.asm") 180 | 181 | if __name__ == "__main__": 182 | main() 183 | -------------------------------------------------------------------------------- /unpack_vmp_handles.py: -------------------------------------------------------------------------------- 1 | from triton import * 2 | from dumpulator import Dumpulator 3 | from unicorn import * 4 | from unicorn.x86_const import * 5 | import os 6 | from sys import argv 7 | 8 | def get_map_prot(name): 9 | if name == "UNDEFINED": 10 | return UC_PROT_NONE 11 | elif name == "PAGE_EXECUTE": 12 | return UC_PROT_EXEC 13 | elif name == "PAGE_EXECUTE_READ": 14 | return UC_PROT_READ | UC_PROT_EXEC 15 | elif name == "PAGE_EXECUTE_READWRITE": 16 | return UC_PROT_ALL 17 | elif name == "PAGE_READWRITE": 18 | return UC_PROT_READ | UC_PROT_WRITE 19 | elif name == "PAGE_READONLY": 20 | return UC_PROT_READ 21 | else: 22 | return UC_PROT_ALL 23 | 24 | def load_binary(filename): 25 | dp = Dumpulator(filename,quiet=True) 26 | mu = Uc(UC_ARCH_X86, UC_MODE_64) 27 | print("[*] loading dump into unicorn and triton dse engine") 28 | mappings = dp.memory.map() 29 | print("[*](unicorn) loading memory regions into unicorn") 30 | for m in mappings: 31 | prot = get_map_prot(m.protect.name) 32 | base = m.base 33 | size = m.region_size 34 | if prot == UC_PROT_NONE: 35 | continue 36 | if len(m.info) > 0: 37 | if m.info[0] == "PEB": 38 | print("[*](unicorn) loading peb into unicorn") 39 | mu.mem_map(0, size, prot) 40 | mu.mem_write(0,bytes(dp.memory.read(base,size))) 41 | continue 42 | print(f"[*](unicorn) mapping memory region: va_range={hex(base)}-{hex(base+size)}, perms: {m.protect.name}") 43 | mu.mem_map(base, size, prot) 44 | print(f"[*](unicorn) writing data into region: va_range={hex(base)}-{hex(base+size)}, perms: {m.protect.name}") 45 | data = bytes(dp.memory.read(base, size)) 46 | mu.mem_write(base, data) 47 | print("[*](unicorn) initializing cpu registers") 48 | mu.reg_write(UC_X86_REG_RAX,dp.regs.rax) 49 | mu.reg_write(UC_X86_REG_RCX,dp.regs.rcx) 50 | mu.reg_write(UC_X86_REG_RDX,dp.regs.rdx) 51 | mu.reg_write(UC_X86_REG_RBX,dp.regs.rbx) 52 | mu.reg_write(UC_X86_REG_RSP,dp.regs.rsp) 53 | mu.reg_write(UC_X86_REG_RBP,dp.regs.rbp) 54 | mu.reg_write(UC_X86_REG_RSI,dp.regs.rsi) 55 | mu.reg_write(UC_X86_REG_RDI,dp.regs.rdi) 56 | mu.reg_write(UC_X86_REG_R8,dp.regs.r8) 57 | mu.reg_write(UC_X86_REG_R9,dp.regs.r9) 58 | mu.reg_write(UC_X86_REG_R10,dp.regs.r10) 59 | mu.reg_write(UC_X86_REG_R11,dp.regs.r11) 60 | mu.reg_write(UC_X86_REG_R12,dp.regs.r12) 61 | mu.reg_write(UC_X86_REG_R13,dp.regs.r13) 62 | mu.reg_write(UC_X86_REG_R14,dp.regs.r14) 63 | mu.reg_write(UC_X86_REG_R15,dp.regs.r15) 64 | print("[+] complete init execution context") 65 | return mu,dp.regs.rip 66 | 67 | pop_regs = [] 68 | pco_reg = None 69 | config = {"registers": [], "memory":[]} 70 | stop = False 71 | mem_modified = {} 72 | 73 | def init_triton_dse(mu): 74 | print("[*](triton dse) init ctx") 75 | ctx = TritonContext(ARCH.X86_64) 76 | ctx.setMode(MODE.ALIGNED_MEMORY, True) 77 | ctx.setConcreteRegisterValue(ctx.registers.rip,mu.reg_read(UC_X86_REG_RIP)) 78 | ctx.setConcreteRegisterValue(ctx.registers.rax,mu.reg_read(UC_X86_REG_RAX)) 79 | ctx.setConcreteRegisterValue(ctx.registers.rcx,mu.reg_read(UC_X86_REG_RCX)) 80 | ctx.setConcreteRegisterValue(ctx.registers.rdx,mu.reg_read(UC_X86_REG_RDX)) 81 | ctx.setConcreteRegisterValue(ctx.registers.rbx,mu.reg_read(UC_X86_REG_RBX)) 82 | ctx.setConcreteRegisterValue(ctx.registers.rsp,mu.reg_read(UC_X86_REG_RSP)) 83 | ctx.setConcreteRegisterValue(ctx.registers.rbp,mu.reg_read(UC_X86_REG_RBP)) 84 | ctx.setConcreteRegisterValue(ctx.registers.rsi,mu.reg_read(UC_X86_REG_RSI)) 85 | ctx.setConcreteRegisterValue(ctx.registers.rdi,mu.reg_read(UC_X86_REG_RDI)) 86 | ctx.setConcreteRegisterValue(ctx.registers.r8,mu.reg_read(UC_X86_REG_R8)) 87 | ctx.setConcreteRegisterValue(ctx.registers.r9,mu.reg_read(UC_X86_REG_R9)) 88 | ctx.setConcreteRegisterValue(ctx.registers.r10,mu.reg_read(UC_X86_REG_R10)) 89 | ctx.setConcreteRegisterValue(ctx.registers.r11,mu.reg_read(UC_X86_REG_R11)) 90 | ctx.setConcreteRegisterValue(ctx.registers.r12,mu.reg_read(UC_X86_REG_R12)) 91 | ctx.setConcreteRegisterValue(ctx.registers.r13,mu.reg_read(UC_X86_REG_R13)) 92 | ctx.setConcreteRegisterValue(ctx.registers.r14,mu.reg_read(UC_X86_REG_R14)) 93 | ctx.setConcreteRegisterValue(ctx.registers.r15,mu.reg_read(UC_X86_REG_R15)) 94 | print("[*](triton dse) mapping and writing datas into regions") 95 | for region in mu.mem_regions(): 96 | print(f"[*](triton dse) mapping and writing data into region: va_range={hex(region[0])}-{hex(region[1])}") 97 | data = mu.mem_read(region[0],region[1]-region[0]) 98 | ctx.setConcreteMemoryAreaValue(region[0], data) 99 | for address,value in mem_modified.items(): 100 | print(f"[*] (triton dse) modifing after unicorn execution memory:",hex(address)) 101 | ctx.setConcreteMemoryAreaValue(address, value) 102 | return ctx 103 | 104 | is_stop_emu = False 105 | ctx = None 106 | 107 | def hook_insn(mu, address, size, _): 108 | global ctx 109 | global pco_reg 110 | global pop_regs 111 | global handle_address 112 | global stop 113 | global is_stop_emu 114 | insn = Instruction(address,bytes(mu.mem_read(address,size+1))) 115 | ctx.disassembly(insn) 116 | if is_stop_emu: 117 | is_stop_emu = False 118 | mu.emu_stop() 119 | return 120 | if len(pop_regs) == 16: 121 | print("[+] found vmexit\n[+] done emulation") 122 | stop = True 123 | is_stop_emu = True 124 | return 125 | opcode = insn.getType() 126 | if opcode == OPCODE.X86.RET: 127 | if not pco_reg: 128 | stop = True 129 | is_stop_emu = True 130 | return 131 | is_stop_emu = True 132 | return 133 | else: 134 | pco_reg = None 135 | try: 136 | op1 = insn.getOperands()[0] 137 | except: 138 | return 139 | if opcode == OPCODE.X86.JMP and op1.getType() == OPERAND.REG: 140 | is_stop_emu = True 141 | return 142 | elif opcode == OPCODE.X86.PUSH and op1.getType() == OPERAND.REG: 143 | pco_reg = op1 144 | for reg in pop_regs: 145 | if reg == op1.getName(): 146 | pop_regs.remove(reg) 147 | elif opcode == OPCODE.X86.POP and op1.getType() == OPERAND.REG: 148 | for reg in pop_regs: 149 | if reg == op1.getName(): 150 | return 151 | pop_regs.append(op1.getName()) 152 | elif opcode == OPCODE.X86.MOV: 153 | op1 = insn.getOperands()[0] 154 | op2 = insn.getOperands()[1] 155 | if op1.getType() == OPERAND.REG and op2.getType() == OPERAND.MEM: 156 | var_name = reg_name = op2.getBaseRegister().getName() 157 | config.get("registers").append({"reg_name":reg_name,"var_name":var_name}) 158 | print("[+] append mem operands to config from insn ->",insn) 159 | elif op1.getType() == OPERAND.MEM and op2.getType() == OPERAND.REG: 160 | var_name = reg_name = op1.getBaseRegister().getName() 161 | config.get("registers").append({"reg_name":reg_name,"var_name":var_name}) 162 | print("[+] append mem operands to config from insn ->",insn) 163 | elif opcode == OPCODE.X86.XOR: 164 | op1 = insn.getOperands()[0] 165 | op2 = insn.getOperands()[1] 166 | if op1.getType() == OPERAND.MEM and op2.getType() == OPERAND.REG: 167 | var_name = reg_name = op1.getBaseRegister().getName() 168 | config.get("registers").append({"reg_name":reg_name,"var_name":var_name}) 169 | print("[+] append mem operands to config from insn ->",insn) 170 | 171 | def hook_mem_access(mu, access, address, size, value, ctx): 172 | if access == UC_MEM_WRITE: 173 | mem_modified[address] = bytes(mu.mem_read(address,size)) 174 | 175 | def symbolizing_vm_context(ctx,config): 176 | print("[+] symbolizing runtime context") 177 | sym_regs = config.get('registers') 178 | sym_mems = config.get("memory") 179 | if sym_regs == None: 180 | print("[-] invalid config: registers array not found!") 181 | exit(-1) 182 | if sym_mems == None: 183 | print("[-] invalid config: memory array not found!") 184 | exit(-1) 185 | for sym_reg in sym_regs: 186 | reg_name = sym_reg.get("reg_name") 187 | var_name = sym_reg.get("var_name") 188 | if reg_name == None or var_name == None: 189 | print("[-] invalid reg line in config") 190 | exit(-1) 191 | print(f"[+] symbolizing register {reg_name} as {var_name}") 192 | ctx.symbolizeRegister(ctx.getRegister(reg_name),var_name) 193 | for sym_mem in sym_mems: 194 | address = sym_mem.get("address") 195 | size = sym_mem.get("size") 196 | if address == None or size == None: 197 | print("[-] invalid mem line in config") 198 | exit(-1) 199 | a = MemoryAccess(int(address,16),int(size)) 200 | print(f"symbolizing memory {a}") 201 | ctx.symbolizeMemory(a) 202 | print("[+] finish symbolize execution context") 203 | 204 | def emulate(ctx): 205 | print("[*] starting symbolic emulation") 206 | rip = ctx.getConcreteRegisterValue(ctx.registers.rip) 207 | while True: 208 | opcodes = ctx.getConcreteMemoryAreaValue(rip,16) 209 | insn = Instruction(rip,opcodes) 210 | ctx.disassembly(insn) 211 | opcode = insn.getType() 212 | ops = insn.getOperands() 213 | if opcode == OPCODE.X86.TEST or opcode == OPCODE.X86.CMP: 214 | rip+=insn.getSize() 215 | continue 216 | ctx.processing(insn) 217 | if opcode == OPCODE.X86.RET or \ 218 | opcode == OPCODE.X86.JMP and ops[0].getType() == OPERAND.REG: 219 | break 220 | rip = ctx.getConcreteRegisterValue(ctx.registers.rip) 221 | print("[+] done emulation") 222 | return 223 | 224 | def deobfuscate(ctx): 225 | dup_insns = [] 226 | sexprs = ctx.getSymbolicExpressions() 227 | keys = sorted(sexprs) 228 | for key in keys: 229 | ty = sexprs[key].getType() 230 | if ty == SYMBOLIC.REGISTER_EXPRESSION or \ 231 | ty == SYMBOLIC.VOLATILE_EXPRESSION or \ 232 | ty == SYMBOLIC.MEMORY_EXPRESSION: 233 | disasm = sexprs[key].getDisassembly() 234 | if len(disasm) > 0: 235 | dup_insns.append(disasm) 236 | insns = [] 237 | for i in range(len(dup_insns)): 238 | if dup_insns[i] not in insns: 239 | insns.append(dup_insns[i]) 240 | return insns 241 | 242 | import hashlib 243 | 244 | def unpack(filenamedump): 245 | global stop 246 | global config 247 | global mem_modified 248 | global ctx 249 | mu, rip = load_binary(filenamedump) 250 | mu.reg_write(UC_X86_REG_RIP,rip) 251 | mu.hook_add(UC_HOOK_CODE, hook_insn, None) 252 | mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_access, None) 253 | count = 0 254 | while not stop: 255 | ctx = init_triton_dse(mu) 256 | mem_modified = {} 257 | print("[+] start unicorn emulation") 258 | mu.emu_start(begin=mu.reg_read(UC_X86_REG_RIP), until=-1) 259 | print("[+] emulation done") 260 | symbolizing_vm_context(ctx,config) 261 | config = {"registers": [], "memory":[]} 262 | emulate(ctx) 263 | trace = deobfuscate(ctx) 264 | trace_s = "" 265 | for insn in trace: 266 | trace_s += insn+"\n" 267 | md5 = hashlib.md5() 268 | md5.update(trace_s.encode()) 269 | is_skip = False 270 | for dir in os.listdir("."): 271 | if "hdl" not in dir: 272 | continue 273 | if not os.path.isdir(dir): 274 | continue 275 | md5_target = hashlib.md5() 276 | md5_target.update(open(f"{dir}/hdl.asm",'rb').read()) 277 | if md5_target.hexdigest() == md5.hexdigest(): 278 | is_skip = True 279 | break 280 | if is_skip: 281 | is_skip=False 282 | print("[+] found already exists handler on disk, skipping") 283 | else: 284 | os.mkdir(f"hdl{count}") 285 | open(f"hdl{count}/hdl.asm","w").write(trace_s) 286 | print(f"dropped: hdl{count}/hdl.asm") 287 | count+=1 288 | print("finish unpacking handlers") 289 | 290 | def main(): 291 | unpack(argv[1]) 292 | 293 | if __name__ == "__main__": 294 | main() 295 | -------------------------------------------------------------------------------- /vmp/arch.py: -------------------------------------------------------------------------------- 1 | from miasm.core.cpu import * 2 | from miasm.core.utils import Disasm_Exception 3 | from miasm.expression.expression import ExprId, ExprInt, ExprLoc, \ 4 | ExprMem, ExprOp, is_expr 5 | from miasm.core.asm_ast import AstId, AstMem 6 | 7 | from miasm.arch.vmp.regs import * 8 | import miasm.arch.vmp.regs as vmp_regs_module 9 | 10 | class instruction_vmp(instruction): 11 | # Default delay slot 12 | # Note: 13 | # - mandatory for the miasm Machine 14 | delayslot = 0 15 | 16 | def __init__(self, name, mode, args, additional_info=None): 17 | self.name = name 18 | self.mode = mode 19 | self.args = args 20 | self.additional_info = additional_info 21 | self.offset = None 22 | self.l = None 23 | self.b = None 24 | 25 | 26 | @staticmethod 27 | def arg2str(expr, pos=None, loc_db=None): 28 | """Convert mnemonics arguments into readable strings according to the 29 | vmp architecture and their internal types 30 | Notes: 31 | - it must be implemented ! However, a simple 'return str(expr)' 32 | could do the trick. 33 | - it is used to mimic objdump output 34 | Args: 35 | expr: argument as a miasm expression 36 | pos: position index in the arguments list 37 | """ 38 | 39 | if isinstance(expr, ExprId) or isinstance(expr, ExprInt): 40 | return str(expr) 41 | 42 | elif isinstance(expr, ExprLoc): 43 | if loc_db is not None: 44 | return loc_db.pretty_str(expr.loc_key) 45 | else: 46 | return str(expr) 47 | # Raise an exception if the expression type was not processed 48 | message = "instruction_vmp.arg2str(): don't know what \ 49 | to do with a '%s' instance." % type(expr) 50 | raise Disasm_Exception(message) 51 | 52 | def __str__(self): 53 | """Return the mnemonic as a string. 54 | Note: 55 | - it is not mandatory as the instruction class already implement 56 | it. It used to get rid of the padding between the opcode and the 57 | arguments. 58 | - most of this code is copied from miasm/core/cpu.py 59 | """ 60 | o = "%s" % self.name 61 | args = [] 62 | if self.args: 63 | o += " " 64 | for i, arg in enumerate(self.args): 65 | if not is_expr(arg): 66 | raise ValueError('zarb arg type') 67 | x = self.arg2str(arg, pos=i) 68 | args.append(x) 69 | o += self.gen_args(args) 70 | return o 71 | 72 | def breakflow(self): 73 | """Instructions that stop a basic block.""" 74 | if self.name in ["VMSWITCH"]: 75 | return True 76 | return False 77 | 78 | def splitflow(self): 79 | """Instructions that splits a basic block, i.e. the CPU can go somewhere else.""" 80 | if self.name in ["VMSWITCH"]: 81 | return True 82 | return False 83 | 84 | def dstflow(self): 85 | """Instructions that explicitly provide the destination.""" 86 | if self.name in ["VMSWITCH"]: 87 | return True 88 | return False 89 | 90 | def dstflow2label(self, loc_db): 91 | """Set the label for the current destination. 92 | Note: it is used at disassembly""" 93 | 94 | def getdstflow(self, loc_db): 95 | """Get the argument that points to the instruction destination.""" 96 | addr = int(self.offset) 97 | if self.name == "VMSWITCH": 98 | return [] 99 | loc_key = loc_db.get_or_create_offset_location(addr) 100 | return [ExprLoc(loc_key, 64)] 101 | 102 | def is_subcall(self): 103 | """Instructions used to call sub functions. 104 | vmp Does not have calls. 105 | """ 106 | return False 107 | 108 | class vmp_additional_info(object): 109 | """Additional vmp instructions information 110 | """ 111 | 112 | def __init__(self): 113 | self.except_on_instr = False 114 | 115 | class mn_vmp(cls_mn): 116 | # Define variables that stores information used to disassemble & assemble 117 | # Notes: - these variables are mandatory 118 | # - they could be moved to the cls_mn class 119 | 120 | num = 0 # holds the number of mnemonics 121 | 122 | all_mn = list() # list of mnenomnics, converted to metamn objects 123 | 124 | all_mn_mode = defaultdict(list) # mneomnics, converted to metamn objects 125 | # Note: 126 | # - the key is the mode # GV: what is it ? 127 | # - the data is a list of 128 | # mnemonics 129 | 130 | all_mn_name = defaultdict(list) # mnenomnics strings 131 | # Note: 132 | # - the key is the mnemonic string 133 | # - the data is the corresponding 134 | # metamn object 135 | 136 | all_mn_inst = defaultdict(list) # mnemonics objects 137 | # Note: 138 | # - the key is the mnemonic Python class 139 | # - the data is an instantiated 140 | # object 141 | bintree = dict() # Variable storing internal values used to guess a 142 | # mnemonic during disassembly 143 | 144 | # Defines the instruction set that will be used 145 | instruction = instruction_vmp 146 | 147 | # Python module that stores registers information 148 | regs = vmp_regs_module 149 | 150 | max_instruction_len = 9 151 | # Default delay slot 152 | # Note: 153 | # - mandatory for the miasm Machine 154 | delayslot = 0 155 | 156 | # Architecture name 157 | name = "vmp" 158 | 159 | # PC name depending on architecture attributes (here, l or b) 160 | # pc = PC 161 | 162 | def additional_info(self): 163 | """Define instruction side effects # GV: not fully understood yet 164 | When used, it must return an object that implements specific 165 | variables, such as except_on_instr. 166 | Notes: 167 | - it must be implemented ! 168 | - it could be moved to the cls_mn class 169 | """ 170 | 171 | return vmp_additional_info() 172 | 173 | @classmethod 174 | def gen_modes(cls, subcls, name, bases, dct, fields): 175 | """Ease populating internal variables used to disassemble & assemble, such 176 | as self.all_mn_mode, self.all_mn_name and self.all_mn_inst 177 | Notes: 178 | - it must be implemented ! 179 | - it could be moved to the cls_mn class. All miasm architectures 180 | use the same code 181 | Args: 182 | cls: ? 183 | sublcs: 184 | name: mnemonic name 185 | bases: ? 186 | dct: ? 187 | fields: ? 188 | Returns: 189 | a list of ? 190 | """ 191 | 192 | dct["mode"] = None 193 | return [(subcls, name, bases, dct, fields)] 194 | 195 | @classmethod 196 | def getmn(cls, name): 197 | """Get the mnemonic name 198 | Notes: 199 | - it must be implemented ! 200 | - it could be moved to the cls_mn class. Most miasm architectures 201 | use the same code 202 | Args: 203 | cls: the mnemonic class 204 | name: the mnemonic string 205 | """ 206 | 207 | return name.upper() 208 | 209 | @classmethod 210 | def getpc(cls, attrib=None): 211 | """"Return the ExprId that represents the Program Counter. 212 | Notes: 213 | - mandatory for the symbolic execution 214 | - PC is defined in regs.py 215 | Args: 216 | attrib: architecture dependent attributes (here, l or b) 217 | """ 218 | 219 | return PC 220 | 221 | @classmethod 222 | def getsp(cls, attrib=None): 223 | """"Return the ExprId that represents the Stack Pointer. 224 | Notes: 225 | - mandatory for the symbolic execution 226 | - SP is defined in regs.py 227 | Args: 228 | attrib: architecture dependent attributes (here, l or b) 229 | """ 230 | 231 | return SP 232 | 233 | @classmethod 234 | def getbits(cls, bitstream, attrib, start, n): 235 | """Return an integer of n bits at the 'start' offset 236 | Note: code from miasm/arch/mips32/arch.py 237 | """ 238 | 239 | # Return zero if zero bits are requested 240 | if not n: 241 | return 0 242 | o = 0 # the returned value 243 | while n: 244 | # Get a byte, the offset is adjusted according to the endianness 245 | offset = start // 8 # the offset in bytes 246 | # n_offset = cls.endian_offset(attrib, offset) # the adjusted offset 247 | c = cls.getbytes(bitstream, offset, 1) 248 | if not c: 249 | raise IOError 250 | 251 | # Extract the bits value 252 | c = ord(c) 253 | r = 8 - start % 8 254 | c &= (1 << r) - 1 255 | l = min(r, n) 256 | c >>= (r - l) 257 | o <<= l 258 | o |= c 259 | n -= l 260 | start += l 261 | 262 | return o 263 | 264 | @classmethod 265 | def endian_offset(cls, attrib, offset): 266 | if attrib == "l": 267 | return offset 268 | else: 269 | raise NotImplementedError("bad attrib") 270 | 271 | def value(self, mode): 272 | v = super(mn_vmp, self).value(mode) 273 | if mode == 'l': 274 | return [x for x in v] 275 | else: 276 | raise NotImplementedError("bad attrib") 277 | 278 | def addop(name, fields, args=None, alias=False): 279 | """ 280 | Dynamically create the "name" object 281 | Notes: 282 | - it could be moved to a generic function such as: 283 | addop(name, fields, cls_mn, args=None, alias=False). 284 | - most architectures use the same code 285 | Args: 286 | name: the mnemonic name 287 | fields: used to fill the object.__dict__'fields' attribute # GV: not understood yet 288 | args: used to fill the object.__dict__'fields' attribute # GV: not understood yet 289 | alias: used to fill the object.__dict__'fields' attribute # GV: not understood yet 290 | """ 291 | 292 | namespace = {"fields": fields, "alias": alias} 293 | 294 | if args is not None: 295 | namespace["args"] = args 296 | 297 | # Dynamically create the "name" object 298 | type(name, (mn_vmp,), namespace) 299 | 300 | class vmp_arg(m_arg): 301 | def asm_ast_to_expr(self, arg, loc_db): 302 | """Convert AST to expressions 303 | Note: - code inspired by miasm/arch/mips32/arch.py""" 304 | 305 | if isinstance(arg, AstId): 306 | if isinstance(arg.name, ExprId): 307 | return arg.name 308 | if isinstance(arg.name, str) and arg.name in gpr_names: 309 | return None # GV: why? 310 | loc_key = loc_db.get_or_create_name_location(arg.name.encode()) 311 | return ExprLoc(loc_key, 64) 312 | 313 | elif isinstance(arg, AstMem): 314 | addr = self.asm_ast_to_expr(arg.ptr, loc_db) 315 | if addr is None: 316 | return None 317 | return ExprMem(addr, 64) 318 | 319 | elif isinstance(arg, AstInt): 320 | return ExprInt(arg.value, 64) 321 | 322 | elif isinstance(arg, AstOp): 323 | args = [self.asm_ast_to_expr(tmp, loc_db) for tmp in arg.args] 324 | if None in args: 325 | return None 326 | return ExprOp(arg.op, *args) 327 | 328 | # Raise an exception if the argument was not processed 329 | message = "mep_arg.asm_ast_to_expr(): don't know what \ 330 | to do with a '%s' instance." % type(arg) 331 | raise Exception(message) 332 | 333 | class vmp_reg(reg_noarg, vmp_arg): 334 | """Generic vmp register 335 | Note: 336 | - the register size will be set using bs() 337 | """ 338 | reg_info = gpr_infos # the list of vmp registers defined in regs.py 339 | parser = reg_info.parser # GV: not understood yet 340 | 341 | class vmp_imm(imm_noarg, vmp_arg): 342 | """Generic vmp immediate 343 | Note: 344 | - the immediate size will be set using bs() 345 | """ 346 | parser = base_expr 347 | def decode(self,v): 348 | v = self.decodeval(v) 349 | self.expr = ExprInt(v, 32) 350 | return True 351 | 352 | reg = bs(l=8, cls=(vmp_reg,),fname="reg") 353 | imm64 = bs(l=64,sz=64,cls=(vmp_imm,vmp_arg),order=1,fname="imm64") 354 | 355 | # mnemonics 356 | addop("VPUSHI64", [bs8(0), imm64]) 357 | addop("VPUSHRQ", [bs8(1), reg]) 358 | addop("VPUSHVSP", [bs8(2), ]) 359 | addop("VLOADTOVSP", [bs8(3), ]) 360 | addop("VLOADQSTACK", [bs8(4), ]) 361 | addop("VLOADQ", [bs8(5), ]) 362 | addop("VPOPQ", [bs8(6), reg]) 363 | addop("VADDQ", [bs8(7), ]) 364 | addop("VANDNQ", [bs8(8),]) 365 | addop("VORNQ", [bs8(9), ]) 366 | addop("VMENTER", [bs8(10), ]) 367 | addop("VMSWITCH", [bs8(11),]) -------------------------------------------------------------------------------- /vmp_bytecode_extractor.py: -------------------------------------------------------------------------------- 1 | from triton import * 2 | from unicorn import * 3 | from unicorn.x86_const import * 4 | from dumpulator import Dumpulator 5 | import os 6 | from sys import argv 7 | 8 | def check_in_region(vmp_range,address): 9 | if vmp_range[0] <= address and address <= vmp_range[1]: 10 | return True 11 | else: 12 | return False 13 | 14 | def get_reg(ctx,reg): 15 | if reg == ctx.registers.rax: 16 | return UC_X86_REG_RAX 17 | elif reg == ctx.registers.rcx: 18 | return UC_X86_REG_RCX 19 | elif reg == ctx.registers.rdx: 20 | return UC_X86_REG_RDX 21 | elif reg == ctx.registers.rbx: 22 | return UC_X86_REG_RBX 23 | elif reg == ctx.registers.rsp: 24 | return UC_X86_REG_RSP 25 | elif reg == ctx.registers.rbp: 26 | return UC_X86_REG_RBP 27 | elif reg == ctx.registers.rsi: 28 | return UC_X86_REG_RSI 29 | elif reg == ctx.registers.rdi: 30 | return UC_X86_REG_RDI 31 | elif reg == ctx.registers.r8: 32 | return UC_X86_REG_R8 33 | elif reg == ctx.registers.r9: 34 | return UC_X86_REG_R9 35 | elif reg == ctx.registers.r10: 36 | return UC_X86_REG_R10 37 | elif reg == ctx.registers.r11: 38 | return UC_X86_REG_R11 39 | elif reg == ctx.registers.r12: 40 | return UC_X86_REG_R12 41 | elif reg == ctx.registers.r13: 42 | return UC_X86_REG_R13 43 | elif reg == ctx.registers.r14: 44 | return UC_X86_REG_R14 45 | elif reg == ctx.registers.r15: 46 | return UC_X86_REG_R15 47 | elif reg == ctx.registers.rip: 48 | return UC_X86_REG_RIP 49 | else: 50 | return UC_X86_REG_INVALID 51 | 52 | is_stop = False 53 | is_call = False 54 | def hook_insn(mu, address, size, hook_ctx): 55 | global is_stop 56 | global is_call 57 | if is_stop: 58 | is_stop = False 59 | mu.emu_stop() 60 | return 61 | ctx = hook_ctx['ctx'] 62 | vmp_range = hook_ctx['vmp_range'] 63 | bb = hook_ctx['bb'] 64 | insn = Instruction(address,bytes(mu.mem_read(address,size+1))) 65 | ctx.disassembly(insn) 66 | if is_call: 67 | is_call = False 68 | if not check_in_region(vmp_range, address): 69 | print("[+] found external call:",insn) 70 | mu.emu_stop() 71 | exit(-1) 72 | if insn.getType() == OPCODE.X86.CALL: 73 | is_call = True 74 | #bb.add(insn) 75 | elif insn.getType() == OPCODE.X86.JMP \ 76 | and insn.getOperands()[0].getType() == OPERAND.REG: 77 | bb.add(insn) 78 | is_stop = True 79 | elif insn.getType() == OPCODE.X86.RET: 80 | bb.add(insn) 81 | is_stop = True 82 | elif not insn.isControlFlow(): 83 | bb.add(insn) 84 | 85 | 86 | class VmpAnalyzerX64: 87 | def __init__(self, filenamedump,vmp_segment_range={"start":0,"end":0}): 88 | self.filenamedump = filenamedump 89 | self.dp = Dumpulator(self.filenamedump,quiet=True) 90 | self.mu = None 91 | self.ctx = None 92 | self.VIP = None 93 | self.VSP = None 94 | self.reg_pco = None 95 | self.RKEY = None 96 | self.VREGISTERS = None 97 | self.is_find_vmenter = False 98 | self.bb_vmenter = None 99 | self.__next_bb_address = 0 100 | self.vmp_segment_range = [vmp_segment_range['start'],vmp_segment_range['end']] 101 | if self.vmp_segment_range[0] == 0 and self.vmp_segment_range[1] == 0: 102 | print("[-] please pass valid vmp segment range") 103 | return None 104 | self.load_binary(self.dp) 105 | self.__init_triton_dse() 106 | self.symbolizeRegisters() 107 | self.hook_ctx = {"vmp_range":self.vmp_segment_range,"ctx":self.ctx,'bb':None} 108 | self.mu.hook_add(UC_HOOK_CODE,hook_insn,self.hook_ctx) 109 | self.entry_point = self.dp.regs.rip 110 | self.fetch_values = { 111 | "VMULD": [], 112 | "VDIVD": [], 113 | "VRDTSC": [], 114 | "VPOPB": [], 115 | "VHADDB": [], 116 | "VPOPW": [], 117 | "VSHRQ": [], 118 | "VPUSHI64" : [], 119 | "VPUSHI32": [], 120 | "VPUSHI16": [], 121 | "VADDQ": [], 122 | "VANDNQ": [], 123 | "VORNQ": [], 124 | "VPUSHRQ": [], 125 | "VPOPQ": [], 126 | "VJMP": [], 127 | "VMENTER": [], 128 | "VLOADQ": [], 129 | "VLOADQSTACK": [], 130 | "VLoadToVSP": [], 131 | "VPUSHVSP": [], 132 | "VSTORED": [], 133 | "VPOPD": [], 134 | "VSTOREQSTACK": [], 135 | "VPUSHRD": [], 136 | "VMEXIT": [] 137 | } 138 | def get_dp(self): 139 | return self.dp 140 | def get_mu(self): 141 | return self.mu 142 | def get_ctx(self): 143 | return self.ctx 144 | # 145 | def analyzeBasicBlock(self, address): 146 | self.hook_ctx['bb'] = BasicBlock([]) 147 | self.mu.emu_start(address,-1) 148 | self.__next_bb_address = self.mu.reg_read(UC_X86_REG_RIP) 149 | return self.hook_ctx['bb'] 150 | # 151 | def getNextEntryBasicBlock(self): 152 | return self.__next_bb_address 153 | # 154 | def is_stack(self,ast_variables): 155 | for var in ast_variables: 156 | if var.getSymbolicVariable().getAlias() == "rsp": 157 | return True 158 | return False 159 | # 160 | def find_vmenter(self, bb): 161 | astctx = self.ctx.getAstContext() 162 | saved_regs = [] 163 | attach_address = 0 164 | for insn in bb.getInstructions(): 165 | if len(saved_regs) == 16: 166 | print("[+] found vmenter at address: ",hex(bb.getFirstAddress())) 167 | attach_address = bb.getInstructions()[0].getAddress() 168 | self.fetch_values['VMENTER'].append(attach_address) 169 | return True 170 | elif insn.isSymbolized() and insn.getType() == OPCODE.X86.PUSHFQ: 171 | if "eflags" in saved_regs: 172 | continue 173 | saved_regs.append('eflags') 174 | elif insn.isSymbolized() and insn.getType() == OPCODE.X86.PUSH and insn.getOperands()[0].getType() == OPERAND.REG: 175 | reg = insn.getOperands()[0] 176 | if reg.getName() in saved_regs: 177 | continue 178 | saved_regs.append(reg.getName()) 179 | if len(saved_regs) == 16: 180 | attach_address = bb.getInstructions()[0].getAddress() 181 | self.fetch_values['VMENTER'].append(attach_address) 182 | print("[+] found vmenter at address: ",hex(bb.getFirstAddress())) 183 | return True 184 | else: 185 | return False 186 | # 187 | def rebuildBasicBlock(self,bb): 188 | pc = bb.getFirstAddress() 189 | rebuilded = BasicBlock() 190 | for e in bb.getInstructions(): 191 | rebuilded.add(Instruction(pc, e.getOpcode())) 192 | pc+=e.getSize() 193 | return rebuilded 194 | # 195 | def analyze_vmenter(self): 196 | for insn in self.bb_vmenter.getInstructions(): 197 | if insn.getType() == OPCODE.X86.MOV: 198 | ops = insn.getOperands() 199 | op1 = ops[0] 200 | op2 = ops[1] 201 | if op1.getType() == OPERAND.REG \ 202 | and op2.getType() == OPERAND.MEM: 203 | base = op2.getBaseRegister() 204 | disp = op2.getDisplacement() 205 | if base != None and disp != None \ 206 | and base == self.ctx.registers.rsp and disp.getValue() == 0x90: 207 | print("[+] found MOV reg, [RSP+90], calc VIP:",insn) 208 | self.VIP=self.ctx.getParentRegister(op1) 209 | print("[*] mapped reg VIP is:",ops[0].getName()) 210 | if insn.getType() == OPCODE.X86.MOV: 211 | ops = insn.getOperands() 212 | if ops[0].getType() == OPERAND.REG \ 213 | and ops[1].getType() == OPERAND.REG \ 214 | and ops[1] == self.ctx.registers.rsp: 215 | print("[+] found MOV contains VSP:",insn) 216 | self.VSP=self.ctx.getParentRegister(ops[0]) 217 | self.VREGISTERS=self.ctx.getParentRegister(ops[1]) 218 | print("[*] mapped reg VSP is:",ops[0].getName()) 219 | print("[*] mapped reg VREGISTERS is:",ops[1].getName()) 220 | elif insn.getType() == OPCODE.X86.POP: 221 | ops = insn.getOperands() 222 | if ops[0].getType() == OPERAND.REG: 223 | print("[+] found POP contains RKEY:",insn) 224 | self.RKEY = self.ctx.getParentRegister(ops[0]) 225 | elif insn.getType() == OPCODE.X86.PUSH and insn.getOperands()[0].getType() == OPERAND.REG: 226 | self.reg_pco = self.ctx.getParentRegister(insn.getOperands()[0]) 227 | elif insn.getType() == OPCODE.X86.RET: 228 | print("[+] found PUSH REG; RET pattern") 229 | print("[+] reg pco (Path Constraint) is:",self.reg_pco.getName()) 230 | break 231 | elif insn.getType() == OPCODE.X86.JMP \ 232 | and insn.getOperands()[0].getType() == OPERAND.REG: 233 | print("[+] found JMP REG pattern") 234 | self.reg_pco = self.ctx.getParentRegister(insn.getOperands()[0]) 235 | print("[+] reg pco (Path Contraint) is:",self.reg_pco.getName()) 236 | break 237 | if not self.reg_pco or not self.RKEY or not self.VSP \ 238 | or not self.VREGISTERS or not self.VIP: 239 | print("[-] this devirtualizer support vmprotect 3.6.x, different version") 240 | exit(1) 241 | print("[+] complete finishing analyzing vmenter handler") 242 | # 243 | def find_VSHRQ(self,bb): 244 | attach_address = bb.getInstructions()[0].getAddress() 245 | is_mov_a = False 246 | is_mov_b = False 247 | is_shift = False 248 | is_mov_res = False 249 | is_pushfq = False 250 | is_pop_rflags = False 251 | val = None 252 | shift_reg = None 253 | for insn in bb.getInstructions(): 254 | opcode = insn.getType() 255 | ops = insn.getOperands() 256 | if opcode == OPCODE.X86.MOV \ 257 | and ops[0].getType() == OPERAND.REG \ 258 | and ops[1].getType() == OPERAND.MEM \ 259 | and ops[0].getBitSize() == 64 \ 260 | and ops[1].getBaseRegister() == self.VSP \ 261 | and ops[1].getDisplacement().getValue() == 0: 262 | val = ops[0] 263 | is_mov_a = True 264 | if opcode == OPCODE.X86.MOV \ 265 | and ops[0].getType() == OPERAND.REG \ 266 | and ops[1].getType() == OPERAND.MEM \ 267 | and ops[0].getBitSize() == 8 \ 268 | and ops[1].getBaseRegister() == self.VSP \ 269 | and ops[1].getDisplacement().getValue() > 0: 270 | shift_reg = ops[0] 271 | is_mov_b = True 272 | if opcode == OPCODE.X86.SHR \ 273 | and ops[0].getType() == OPERAND.REG \ 274 | and ops[1].getType() == OPERAND.REG \ 275 | and ops[0] == val and ops[1] == shift_reg: 276 | is_shift = True 277 | if opcode == OPCODE.X86.MOV \ 278 | and ops[0].getType() == OPERAND.MEM \ 279 | and ops[1].getType() == OPERAND.REG \ 280 | and ops[0].getBaseRegister() == self.VSP \ 281 | and ops[0].getDisplacement().getValue() == 0x08 \ 282 | and ops[1] == val: 283 | is_mov_res = True 284 | if opcode == OPCODE.X86.PUSHFQ: 285 | is_pushfq = True 286 | if opcode == OPCODE.X86.POP \ 287 | and ops[0].getType() == OPERAND.MEM \ 288 | and ops[0].getBaseRegister() == self.VSP: 289 | is_pop_rflags = True 290 | if is_pop_rflags and is_pushfq and is_mov_res \ 291 | and is_mov_b and is_mov_a: 292 | self.fetch_values['VSHRQ'].append(attach_address) 293 | return True 294 | return False 295 | # 296 | def find_VJMP2(self,bb): 297 | is_mov_reg_mem_vsp = False 298 | is_add_vsp_8 = False 299 | is_mov_vip_reg = False 300 | attach_address = bb.getInstructions()[0].getAddress() 301 | inter_vip_val = None 302 | for insn in bb.getInstructions(): 303 | opcode = insn.getType() 304 | ops = insn.getOperands() 305 | if opcode == OPCODE.X86.MOV \ 306 | and ops[1].getType() == OPERAND.MEM \ 307 | and ops[0].getType() == OPERAND.REG \ 308 | and ops[1].getBaseRegister() == self.VSP: 309 | inter_vip_val = ops[0] 310 | is_mov_reg_mem_vsp = True 311 | if opcode == OPCODE.X86.ADD \ 312 | and ops[0] == self.VSP \ 313 | and ops[1].getType() == OPERAND.IMM \ 314 | and ops[1].getValue() == 0x08: 315 | is_add_vsp_8 = True 316 | if opcode == OPCODE.X86.MOV \ 317 | and ops[0] == self.VIP \ 318 | and ops[1] == inter_vip_val: 319 | is_mov_vip_reg = True 320 | if is_add_vsp_8 and is_mov_vip_reg and is_mov_reg_mem_vsp: 321 | self.fetch_values["VJMP"].append(attach_address) 322 | return True 323 | return False 324 | # 325 | def find_VJMP(self,bb): # VJMP VIP=[VSP] 326 | is_mov_reg_vsp = False 327 | is_mov_reg_mem_vsp = False 328 | reg_VIP_inter = None 329 | new_vsp_map_reg = None 330 | new_vip_map_reg = None 331 | is_mov_new_vip = False 332 | attach_address = 0 333 | for insn in bb.getInstructions(): 334 | opcode = insn.getType() 335 | ops = insn.getOperands() 336 | if opcode == OPCODE.X86.MOV \ 337 | and ops[0].getType() == OPERAND.REG \ 338 | and ops[1].getType() == OPERAND.REG \ 339 | and ops[1] == self.VSP: 340 | is_mov_reg_vsp = True 341 | attach_address = insn.getAddress() 342 | new_vsp_map_reg = ops[0] 343 | if opcode == OPCODE.X86.MOV \ 344 | and ops[0].getType() == OPERAND.REG \ 345 | and ops[1].getType() == OPERAND.MEM: 346 | mem = ops[1] 347 | base = mem.getBaseRegister() 348 | if base != 0 and base == self.VSP: 349 | is_mov_reg_mem_vsp = True 350 | reg_VIP_inter = ops[0] 351 | if opcode == OPCODE.X86.MOV \ 352 | and ops[0].getType() == OPERAND.REG \ 353 | and ops[1].getType() == OPERAND.REG: 354 | if reg_VIP_inter != None and ops[1] == reg_VIP_inter: 355 | is_mov_new_vip = True 356 | new_vip_map_reg = ops[0] 357 | if is_mov_new_vip and is_mov_reg_mem_vsp and is_mov_reg_vsp: 358 | self.fetch_values['VJMP'].append(attach_address) 359 | self.VSP = new_vsp_map_reg 360 | self.VIP = new_vip_map_reg 361 | print(f"[+] new mapping regs: VIP is {self.VIP.getName()}, VSP is {self.VSP.getName()}") 362 | return True 363 | return False 364 | # 365 | def find_VPUSHI16(self,bb): 366 | is_sub_vsp_2 = False 367 | is_mov_vsp_rimm16 = False 368 | attach_address = 0 369 | for insn in bb.getInstructions(): 370 | opcode = insn.getType() 371 | ops = insn.getOperands() 372 | if opcode == OPCODE.X86.SUB \ 373 | and ops[0] == self.VSP \ 374 | and ops[1].getType() == OPERAND.IMM \ 375 | and ops[1].getValue() == 0x02: 376 | is_sub_vsp_2 = True 377 | if opcode == OPCODE.X86.MOV \ 378 | and ops[0].getType() == OPERAND.MEM \ 379 | and ops[1].getType() == OPERAND.REG \ 380 | and ops[1].getBitSize() == 16 \ 381 | and ops[0].getBaseRegister() == self.VSP: 382 | is_mov_vsp_rimm16 = True 383 | attach_address = insn.getAddress() 384 | if is_sub_vsp_2 and is_mov_vsp_rimm16: 385 | self.fetch_values['VPUSHI16'].append(attach_address) 386 | return True 387 | return False 388 | # 389 | def analyze_vmexit(self,bb): 390 | pass 391 | # 392 | def find_VPUSHRQ(self,bb): 393 | index_reg = None 394 | val_reg = None 395 | bb_semantic = BasicBlock() 396 | is_mov_idx_reg_vip = False 397 | is_add_vip_1 = False 398 | is_mov_val_vregs_idx = False 399 | is_sub_vsp_8 = False 400 | is_mov_vsp_val = False 401 | attach_address = 0x0 402 | for insn in bb.getInstructions(): 403 | opcode = insn.getType() 404 | ops = insn.getOperands() 405 | if opcode == OPCODE.X86.MOVZX \ 406 | and ops[0].getType() == OPERAND.REG \ 407 | and ops[1].getType() == OPERAND.MEM \ 408 | and self.VIP.getId() in [e[0].getId() for e in insn.getReadRegisters()] \ 409 | and not is_mov_idx_reg_vip: 410 | is_mov_idx_reg_vip = True 411 | index_reg = self.ctx.getParentRegister(ops[0]) 412 | bb_semantic.add(insn) 413 | elif opcode == OPCODE.X86.ADD \ 414 | and ops[0].getType() == OPERAND.REG \ 415 | and ops[1].getType() == OPERAND.IMM \ 416 | and ops[1].getValue() == 0x01 \ 417 | and ops[0] == self.VIP \ 418 | and not is_add_vip_1: 419 | is_add_vip_1 = True 420 | bb_semantic.add(insn) 421 | elif opcode == OPCODE.X86.SUB \ 422 | and ops[0].getType() == OPERAND.REG \ 423 | and ops[1].getType() == OPERAND.IMM \ 424 | and ops[1].getValue() == 0x01 \ 425 | and ops[0] == self.VIP \ 426 | and not is_add_vip_1: 427 | is_add_vip_1 = True 428 | bb_semantic.add(insn) 429 | elif opcode == OPCODE.X86.MOV \ 430 | and ops[0].getType() == OPERAND.REG \ 431 | and ops[1].getType() == OPERAND.MEM \ 432 | and ops[1].getBaseRegister() == self.VREGISTERS \ 433 | and ops[1].getIndexRegister() == index_reg \ 434 | and not is_mov_val_vregs_idx: 435 | val_reg = ops[0] 436 | is_mov_val_vregs_idx = True 437 | attach_address = insn.getAddress() 438 | bb_semantic.add(insn) 439 | elif opcode == OPCODE.X86.SUB \ 440 | and ops[0].getType() == OPERAND.REG \ 441 | and ops[0] == self.VSP \ 442 | and ops[1].getType() == OPERAND.IMM \ 443 | and ops[1].getValue() == 0x08 \ 444 | and not is_sub_vsp_8: 445 | is_sub_vsp_8 = True 446 | bb_semantic.add(insn) 447 | elif opcode == OPCODE.X86.MOV \ 448 | and ops[0].getType() == OPERAND.MEM \ 449 | and ops[1].getType() == OPERAND.REG \ 450 | and ops[0].getBaseRegister() == self.VSP \ 451 | and ops[1] == val_reg and not is_mov_vsp_val: 452 | is_mov_vsp_val = True 453 | bb_semantic.add(insn) 454 | if is_mov_vsp_val and is_sub_vsp_8 \ 455 | and is_mov_val_vregs_idx and is_add_vip_1 \ 456 | and is_mov_idx_reg_vip: 457 | self.fetch_values['VPUSHRQ'].append(attach_address) 458 | #print("[+] found VPUSHRQ handler") 459 | return True 460 | return False 461 | # 462 | def find_VPUSHI64(self,bb): 463 | constant_reg = None 464 | is_mov_const_reg_VIP = False 465 | is_mov_VSP_const_reg = False 466 | is_add_VIP_8 = False 467 | is_sub_VSP_8 = False 468 | attach_address = 0 469 | bb_semantic = BasicBlock() 470 | for insn in bb.getInstructions(): 471 | opcode = insn.getType() 472 | ops = insn.getOperands() 473 | if opcode == OPCODE.X86.MOV \ 474 | and ops[0].getType() == OPERAND.REG \ 475 | and ops[1].getType() == OPERAND.MEM \ 476 | and self.VIP.getId() in [e[0].getId() for e in insn.getReadRegisters()] \ 477 | and not is_mov_const_reg_VIP: 478 | is_mov_const_reg_VIP = True 479 | constant_reg = ops[0] 480 | bb_semantic.add(insn) 481 | elif opcode == OPCODE.X86.MOV \ 482 | and ops[0].getType() == OPERAND.MEM \ 483 | and ops[1].getType() == OPERAND.REG \ 484 | and ops[0].getBaseRegister() == self.VSP \ 485 | and ops[1] == constant_reg \ 486 | and not is_mov_VSP_const_reg: 487 | is_mov_VSP_const_reg = True 488 | attach_address = insn.getAddress() 489 | bb_semantic.add(insn) 490 | elif opcode == OPCODE.X86.ADD \ 491 | and ops[0] == self.VIP and ops[1].getType() == OPERAND.IMM \ 492 | and ops[1].getValue() == 0x08 and not is_add_VIP_8: 493 | is_add_VIP_8 = True 494 | bb_semantic.add(insn) 495 | elif opcode == OPCODE.X86.SUB \ 496 | and ops[0] == self.VIP and ops[1].getType() == OPERAND.IMM \ 497 | and ops[1].getValue() == 0x08 and not is_add_VIP_8: 498 | is_add_VIP_8 = True 499 | bb_semantic.add(insn) 500 | elif opcode == OPCODE.X86.SUB \ 501 | and ops[0] == self.VSP and ops[1].getType() == OPERAND.IMM \ 502 | and ops[1].getValue() == 0x08 and not is_sub_VSP_8: 503 | is_sub_VSP_8 = True 504 | bb_semantic.add(insn) 505 | if is_sub_VSP_8 and is_add_VIP_8 and is_mov_const_reg_VIP and is_mov_VSP_const_reg: 506 | self.fetch_values['VPUSHI64'].append(attach_address) 507 | #print("[+] found VPUSHI64 handler") 508 | return True 509 | return False 510 | # 511 | def find_VPUSHI32(self,bb): 512 | constant_reg = None 513 | is_mov_const_reg_VIP = False 514 | is_mov_VSP_const_reg = False 515 | is_sub_VSP_4 = False 516 | attach_address = 0 517 | bb_semantic = BasicBlock() 518 | for insn in bb.getInstructions(): 519 | opcode = insn.getType() 520 | ops = insn.getOperands() 521 | if opcode == OPCODE.X86.MOV \ 522 | and ops[0].getType() == OPERAND.REG \ 523 | and ops[1].getType() == OPERAND.MEM \ 524 | and self.VIP.getId() in [e[0].getId() for e in insn.getReadRegisters()] \ 525 | and ops[0].getBitSize() == 32 \ 526 | and not is_mov_const_reg_VIP: 527 | is_mov_const_reg_VIP = True 528 | constant_reg = self.ctx.getParentRegister(ops[0]) 529 | bb_semantic.add(insn) 530 | elif opcode == OPCODE.X86.MOV \ 531 | and ops[0].getType() == OPERAND.MEM \ 532 | and ops[1].getType() == OPERAND.REG \ 533 | and ops[0].getBaseRegister() == self.VSP \ 534 | and ops[1].getBitSize() == 32 \ 535 | and self.ctx.getParentRegister(ops[1]) == constant_reg \ 536 | and not is_mov_VSP_const_reg: 537 | is_mov_VSP_const_reg = True 538 | attach_address = insn.getAddress() 539 | bb_semantic.add(insn) 540 | elif opcode == OPCODE.X86.SUB \ 541 | and ops[0] == self.VSP and ops[1].getType() == OPERAND.IMM \ 542 | and ops[1].getValue() == 0x04 and not is_sub_VSP_4: 543 | is_sub_VSP_4 = True 544 | bb_semantic.add(insn) 545 | if is_sub_VSP_4 and is_mov_const_reg_VIP and is_mov_VSP_const_reg: 546 | self.fetch_values['VPUSHI32'].append(attach_address) 547 | #print("[+] found VPUSHI32 handler") 548 | return True 549 | return False 550 | # 551 | def find_VPOPQ(self,bb): 552 | taint_reg = None 553 | index_reg = None 554 | is_mov_reg_vsp_mem = False 555 | is_add_vsp_8 = False 556 | is_mov_vregs_reg = False 557 | is_add_vip_1 = False 558 | is_mov_idx_vip = False 559 | attach_address = 0 560 | bb_semantic = BasicBlock() 561 | for insn in bb.getInstructions(): 562 | opcode = insn.getType() 563 | ops = insn.getOperands() 564 | if opcode == OPCODE.X86.MOV: 565 | if ops[0].getType() == OPERAND.REG \ 566 | and ops[1].getType() == OPERAND.MEM \ 567 | and self.VSP.getId() in [e[0].getId() for e in insn.getReadRegisters()] \ 568 | and not is_mov_reg_vsp_mem: 569 | taint_reg = ops[0] 570 | is_mov_reg_vsp_mem = True 571 | bb_semantic.add(insn) 572 | elif ops[0].getType() == OPERAND.MEM \ 573 | and ops[1].getType() == OPERAND.REG: 574 | base = ops[0].getBaseRegister() 575 | index = ops[0].getIndexRegister() 576 | if base and index \ 577 | and base == self.VREGISTERS \ 578 | and index == index_reg \ 579 | and ops[1] == taint_reg \ 580 | and not is_mov_vregs_reg: 581 | is_mov_vregs_reg = True 582 | attach_address = insn.getAddress() 583 | bb_semantic.add(insn) 584 | if opcode == OPCODE.X86.MOVZX and ops[0].getType() == OPERAND.REG \ 585 | and ops[1].getType() == OPERAND.MEM \ 586 | and self.VIP.getId() in [e[0].getId() for e in insn.getReadRegisters()] \ 587 | and not is_mov_idx_vip: 588 | index_reg = self.ctx.getParentRegister(ops[0]) 589 | is_mov_idx_vip = True 590 | bb_semantic.add(insn) 591 | elif opcode == OPCODE.X86.ADD \ 592 | and ops[0] == self.VSP and ops[1].getType() == OPERAND.IMM \ 593 | and ops[1].getValue() == 0x08 and not is_add_vsp_8: 594 | is_add_vsp_8 = True 595 | bb_semantic.add(insn) 596 | if opcode == OPCODE.X86.ADD \ 597 | and ops[0] == self.VIP and ops[1].getType() == OPERAND.IMM \ 598 | and ops[1].getValue() == 0x01 and not is_add_vip_1: 599 | is_add_vip_1 = True 600 | bb_semantic.add(insn) 601 | if opcode == OPCODE.X86.SUB \ 602 | and ops[0] == self.VIP and ops[1].getType() == OPERAND.IMM \ 603 | and ops[1].getValue() == 0x01 and not is_add_vip_1: 604 | is_add_vip_1 = True 605 | bb_semantic.add(insn) 606 | if is_mov_idx_vip and is_mov_vregs_reg and is_add_vsp_8 \ 607 | and is_mov_reg_vsp_mem and is_add_vip_1: 608 | self.fetch_values['VPOPQ'].append(attach_address) 609 | #print("[+] found VPOPQ handler") 610 | return True 611 | return False 612 | # 613 | def find_VPOPD(self,bb): 614 | is_mov_val_vsp = False 615 | is_add_vsp_4 = False 616 | is_mov_vregs_idx_val = False 617 | val = None 618 | attach_address = 0 619 | for insn in bb.getInstructions(): 620 | opcode = insn.getType() 621 | ops = insn.getOperands() 622 | if opcode == OPCODE.X86.MOV \ 623 | and ops[0].getType() == OPERAND.REG \ 624 | and ops[0].getBitSize() == 32 \ 625 | and ops[1].getType() == OPERAND.MEM \ 626 | and ops[1].getBaseRegister() == self.VSP: 627 | is_mov_val_vsp = True 628 | val = ops[0] 629 | if opcode == OPCODE.X86.ADD \ 630 | and ops[0] == self.VSP \ 631 | and ops[1].getType() == OPERAND.IMM \ 632 | and ops[1].getValue() == 0x04: 633 | is_add_vsp_4 = True 634 | if opcode == OPCODE.X86.MOV \ 635 | and ops[0].getType() == OPERAND.MEM \ 636 | and ops[0].getBaseRegister() == self.VREGISTERS \ 637 | and ops[0].getIndexRegister() != None \ 638 | and ops[1] == val: 639 | is_mov_vregs_idx_val = True 640 | attach_address = insn.getAddress() 641 | if is_mov_val_vsp and is_add_vsp_4 and is_mov_vregs_idx_val: 642 | self.fetch_values['VPOPD'].append(attach_address) 643 | return True 644 | return False 645 | # 646 | def find_VADDQ(self,bb): 647 | bb_semantics = BasicBlock() 648 | reg_a = None 649 | reg_b = None 650 | is_mov_a = False 651 | is_mov_b = False 652 | is_add_a_b = False 653 | is_mov_res = False 654 | is_pushfq = False 655 | is_pop_VSP = False 656 | attach_address = 0 657 | is_add_VIP_4 = False 658 | for insn in bb.getInstructions(): 659 | opcode = insn.getType() 660 | ops = insn.getOperands() 661 | if opcode == OPCODE.X86.ADD \ 662 | and ops[0] == self.VIP and \ 663 | ops[1].getType() == OPERAND.IMM \ 664 | and ops[1].getValue() == 0x04: 665 | is_add_VIP_4 = True 666 | attach_address = insn.getAddress() 667 | bb_semantics.add(insn) 668 | if opcode == OPCODE.X86.SUB \ 669 | and ops[0] == self.VIP and \ 670 | ops[1].getType() == OPERAND.IMM \ 671 | and ops[1].getValue() == 0x04: 672 | is_add_VIP_4 = True 673 | attach_address = insn.getAddress() 674 | bb_semantics.add(insn) 675 | if opcode == OPCODE.X86.MOV \ 676 | and ops[0].getType() == OPERAND.REG \ 677 | and ops[1].getType() == OPERAND.MEM \ 678 | and ops[1].getBaseRegister() == self.VSP: 679 | disp = ops[1].getDisplacement() 680 | if not disp or disp.getValue() == 0: 681 | is_mov_a = True 682 | reg_a = ops[0] 683 | else: 684 | is_mov_b = True 685 | reg_b = ops[0] 686 | bb_semantics.add(insn) 687 | if opcode == OPCODE.X86.MOV \ 688 | and ops[0].getType() == OPERAND.MEM \ 689 | and ops[1].getType() == OPERAND.REG \ 690 | and ops[0].getBaseRegister() == self.VSP \ 691 | and ops[0].getDisplacement() \ 692 | and ops[0].getDisplacement().getValue() == 0x08 \ 693 | and (ops[1] == reg_a or ops[1] == reg_b): 694 | is_mov_res = True 695 | bb_semantics.add(insn) 696 | if opcode == OPCODE.X86.ADD \ 697 | and ops[0].getType() == OPERAND.REG \ 698 | and ops[1].getType() == OPERAND.REG \ 699 | and reg_a in [e[0] for e in insn.getReadRegisters()] \ 700 | and reg_b in [e[0] for e in insn.getReadRegisters()]: 701 | is_add_a_b = True 702 | bb_semantics.add(insn) 703 | if opcode == OPCODE.X86.PUSHFQ: 704 | is_pushfq = True 705 | bb_semantics.add(insn) 706 | if opcode == OPCODE.X86.POP \ 707 | and ops[0].getType() == OPERAND.MEM \ 708 | and ops[0].getBaseRegister() == self.VSP: 709 | is_pop_VSP = True 710 | bb_semantics.add(insn) 711 | if is_pop_VSP and is_pushfq and is_add_a_b and is_mov_res \ 712 | and is_mov_b and is_mov_a: 713 | self.fetch_values['VADDQ'].append(attach_address) 714 | #print("[+] found VADDQ handler") 715 | return True 716 | return False 717 | # 718 | def find_VLOADQ(self,bb): 719 | reg_ptr = None 720 | reg_val = None 721 | is_mov_reg_ptr_vsp = False 722 | is_mov_val_reg_ptr = False 723 | is_mov_vsp_val = False 724 | is_add_vip_4 = False 725 | attach_address = 0 726 | bb_semantics = BasicBlock() 727 | for insn in bb.getInstructions(): 728 | opcode = insn.getType() 729 | ops = insn.getOperands() 730 | if opcode == OPCODE.X86.MOV \ 731 | and ops[0].getType() == OPERAND.REG \ 732 | and ops[1].getType() == OPERAND.MEM \ 733 | and ops[1].getBaseRegister() == self.VSP: 734 | is_mov_reg_ptr_vsp = True 735 | reg_ptr = ops[0] 736 | bb_semantics.add(insn) 737 | attach_address = insn.getAddress() 738 | if opcode == OPCODE.X86.MOV \ 739 | and ops[0].getType() == OPERAND.REG \ 740 | and ops[1].getType() == OPERAND.MEM \ 741 | and ops[1].getBaseRegister() == reg_ptr \ 742 | and ops[1].getSegmentRegister() != self.ctx.registers.ss: 743 | is_mov_val_reg_ptr = True 744 | reg_val = ops[0] 745 | bb_semantics.add(insn) 746 | if opcode == OPCODE.X86.MOV \ 747 | and ops[0].getType() == OPERAND.MEM \ 748 | and ops[1].getType() == OPERAND.REG \ 749 | and ops[0].getBaseRegister() == self.VSP \ 750 | and ops[1] == reg_val: 751 | is_mov_vsp_val = True 752 | bb_semantics.add(insn) 753 | if opcode == OPCODE.X86.ADD \ 754 | and ops[0] == self.VIP \ 755 | and ops[1].getType() == OPERAND.IMM \ 756 | and ops[1].getValue() == 0x04: 757 | is_add_vip_4 = True 758 | bb_semantics.add(insn) 759 | if opcode == OPCODE.X86.SUB \ 760 | and ops[0] == self.VIP \ 761 | and ops[1].getType() == OPERAND.IMM \ 762 | and ops[1].getValue() == 0x04: 763 | is_add_vip_4 = True 764 | bb_semantics.add(insn) 765 | if is_add_vip_4 and is_mov_vsp_val \ 766 | and is_mov_val_reg_ptr and is_mov_reg_ptr_vsp: 767 | self.fetch_values['VLOADQ'].append(attach_address) 768 | #print("[+] found VLOADQ handler") 769 | return True 770 | return False 771 | # 772 | def find_VPUSHVSP(self,bb): 773 | vsp_val_reg = None 774 | is_mov_vsp_val_reg = False 775 | is_sub_vsp_8 = False 776 | is_mov_vsp_vsp_val_reg = False 777 | is_add_vip_4 = False 778 | attach_address = 0 779 | bb_semantics = BasicBlock() 780 | for insn in bb.getInstructions(): 781 | opcode = insn.getType() 782 | ops = insn.getOperands() 783 | if opcode == OPCODE.X86.MOV \ 784 | and ops[0].getType() == OPERAND.REG \ 785 | and ops[1].getType() == OPERAND.REG \ 786 | and ops[1] == self.VSP: 787 | vsp_val_reg = ops[0] 788 | is_mov_vsp_val_reg = True 789 | bb_semantics.add(insn) 790 | attach_address = insn.getAddress() 791 | if opcode == OPCODE.X86.SUB \ 792 | and ops[0] == self.VSP \ 793 | and ops[1].getType() == OPERAND.IMM \ 794 | and ops[1].getValue() == 0x08: 795 | is_sub_vsp_8 = True 796 | bb_semantics.add(insn) 797 | if opcode == OPCODE.X86.MOV \ 798 | and ops[0].getType() == OPERAND.MEM \ 799 | and ops[1].getType() == OPERAND.REG \ 800 | and ops[0].getBaseRegister() == self.VSP \ 801 | and ops[1] == vsp_val_reg: 802 | is_mov_vsp_vsp_val_reg = True 803 | bb_semantics.add(insn) 804 | if opcode == OPCODE.X86.ADD \ 805 | and ops[0] == self.VIP \ 806 | and ops[1].getType() == OPERAND.IMM \ 807 | and ops[1].getValue() == 0x04: 808 | is_add_vip_4 = True 809 | bb_semantics.add(insn) 810 | if opcode == OPCODE.X86.SUB \ 811 | and ops[0] == self.VIP \ 812 | and ops[1].getType() == OPERAND.IMM \ 813 | and ops[1].getValue() == 0x04: 814 | is_add_vip_4 = True 815 | bb_semantics.add(insn) 816 | if is_add_vip_4 and is_sub_vsp_8 and is_mov_vsp_vsp_val_reg \ 817 | and is_mov_vsp_val_reg: 818 | #print("[+] found VPUSHVSP handler") 819 | self.fetch_values['VPUSHVSP'].append(attach_address) 820 | return True 821 | return False 822 | # 823 | def find_VANDNQ(self,bb): 824 | is_mov_a = False 825 | is_mov_b = False 826 | reg_a = None 827 | reg_b = None 828 | reg_res = None 829 | is_not_a = False 830 | is_not_b = False 831 | is_or_a_b = False 832 | is_mov_vsp_res = False 833 | is_pushfq = False 834 | is_pop_rflags = False 835 | is_add_vip_4 = False 836 | attach_address = 0 837 | bb_semantics = BasicBlock() 838 | for insn in bb.getInstructions(): 839 | opcode = insn.getType() 840 | ops = insn.getOperands() 841 | if opcode == OPCODE.X86.MOV \ 842 | and ops[0].getType() == OPERAND.REG \ 843 | and ops[1].getType() == OPERAND.MEM \ 844 | and ops[1].getBaseRegister() == self.VSP: 845 | disp = ops[1].getDisplacement() 846 | if (not disp) or (disp.getValue() == 0): 847 | is_mov_a = True 848 | reg_a = ops[0] 849 | else: 850 | is_mov_b = True 851 | reg_b = ops[0] 852 | bb_semantics.add(insn) 853 | if opcode == OPCODE.X86.NOT \ 854 | and ops[0].getType() == OPERAND.REG \ 855 | and (ops[0] == reg_a or ops[0] == reg_b): 856 | if ops[0] == reg_a: 857 | is_not_a = True 858 | else: 859 | is_not_b = True 860 | bb_semantics.add(insn) 861 | if opcode == OPCODE.X86.OR \ 862 | and ops[0].getType() == OPERAND.REG \ 863 | and ops[1].getType() == OPERAND.REG \ 864 | and reg_a in [e[0] for e in insn.getReadRegisters()] \ 865 | and reg_b in [e[0] for e in insn.getReadRegisters()]: 866 | is_or_a_b = True 867 | reg_res = ops[0] 868 | bb_semantics.add(insn) 869 | if opcode == OPCODE.X86.MOV \ 870 | and ops[0].getType() == OPERAND.MEM \ 871 | and ops[1].getType() == OPERAND.REG \ 872 | and ops[0].getBaseRegister() == self.VSP \ 873 | and ops[1] == reg_res: 874 | is_mov_vsp_res = True 875 | bb_semantics.add(insn) 876 | if opcode == OPCODE.X86.PUSHFQ: 877 | is_pushfq = True 878 | bb_semantics.add(insn) 879 | if opcode == OPCODE.X86.POP \ 880 | and ops[0].getType() == OPERAND.MEM \ 881 | and ops[0].getBaseRegister() == self.VSP: 882 | is_pop_rflags = True 883 | bb_semantics.add(insn) 884 | attach_address = insn.getAddress() 885 | if opcode == OPCODE.X86.ADD \ 886 | and ops[0] == self.VIP \ 887 | and ops[1].getType() == OPERAND.IMM \ 888 | and ops[1].getValue() == 0x04: 889 | is_add_vip_4 = True 890 | bb_semantics.add(insn) 891 | if opcode == OPCODE.X86.SUB \ 892 | and ops[0] == self.VIP \ 893 | and ops[1].getType() == OPERAND.IMM \ 894 | and ops[1].getValue() == 0x04: 895 | is_add_vip_4 = True 896 | bb_semantics.add(insn) 897 | if is_add_vip_4 and is_pop_rflags and is_pushfq \ 898 | and is_mov_vsp_res and is_or_a_b and is_not_a and is_not_b \ 899 | and is_mov_a and is_mov_b: 900 | self.fetch_values['VANDNQ'].append(attach_address) 901 | #print("[+] found VANDNQ handler") 902 | return True 903 | return False 904 | # 905 | def find_VORNQ(self,bb): 906 | is_mov_a = False 907 | is_mov_b = False 908 | reg_a = None 909 | reg_b = None 910 | reg_res = None 911 | is_not_a = False 912 | is_not_b = False 913 | is_and_a_b = False 914 | is_mov_vsp_res = False 915 | is_pushfq = False 916 | is_pop_rflags = False 917 | is_add_vip_4 = False 918 | attach_address = 0 919 | bb_semantics = BasicBlock() 920 | for insn in bb.getInstructions(): 921 | opcode = insn.getType() 922 | ops = insn.getOperands() 923 | if opcode == OPCODE.X86.MOV \ 924 | and ops[0].getType() == OPERAND.REG \ 925 | and ops[1].getType() == OPERAND.MEM \ 926 | and ops[1].getBaseRegister() == self.VSP: 927 | disp = ops[1].getDisplacement() 928 | if (not disp) or (disp.getValue() == 0): 929 | is_mov_a = True 930 | reg_a = ops[0] 931 | else: 932 | is_mov_b = True 933 | reg_b = ops[0] 934 | bb_semantics.add(insn) 935 | if opcode == OPCODE.X86.NOT \ 936 | and ops[0].getType() == OPERAND.REG \ 937 | and (ops[0] == reg_a or ops[0] == reg_b): 938 | if ops[0] == reg_a: 939 | is_not_a = True 940 | else: 941 | is_not_b = True 942 | bb_semantics.add(insn) 943 | if opcode == OPCODE.X86.AND \ 944 | and ops[0].getType() == OPERAND.REG \ 945 | and ops[1].getType() == OPERAND.REG \ 946 | and reg_a in [e[0] for e in insn.getReadRegisters()] \ 947 | and reg_b in [e[0] for e in insn.getReadRegisters()]: 948 | is_and_a_b = True 949 | reg_res = ops[0] 950 | bb_semantics.add(insn) 951 | if opcode == OPCODE.X86.MOV \ 952 | and ops[0].getType() == OPERAND.MEM \ 953 | and ops[1].getType() == OPERAND.REG \ 954 | and ops[0].getBaseRegister() == self.VSP \ 955 | and ops[1] == reg_res: 956 | is_mov_vsp_res = True 957 | bb_semantics.add(insn) 958 | if opcode == OPCODE.X86.PUSHFQ: 959 | is_pushfq = True 960 | bb_semantics.add(insn) 961 | attach_address = insn.getAddress() 962 | if opcode == OPCODE.X86.POP \ 963 | and ops[0].getType() == OPERAND.MEM \ 964 | and ops[0].getBaseRegister() == self.VSP: 965 | is_pop_rflags = True 966 | bb_semantics.add(insn) 967 | if opcode == OPCODE.X86.ADD \ 968 | and ops[0] == self.VIP \ 969 | and ops[1].getType() == OPERAND.IMM \ 970 | and ops[1].getValue() == 0x04: 971 | is_add_vip_4 = True 972 | bb_semantics.add(insn) 973 | if opcode == OPCODE.X86.SUB \ 974 | and ops[0] == self.VIP \ 975 | and ops[1].getType() == OPERAND.IMM \ 976 | and ops[1].getValue() == 0x04: 977 | is_add_vip_4 = True 978 | bb_semantics.add(insn) 979 | if is_add_vip_4 and is_pop_rflags and is_pushfq \ 980 | and is_mov_vsp_res and is_and_a_b and is_not_a and is_not_b \ 981 | and is_mov_a and is_mov_b: 982 | self.fetch_values['VORNQ'].append(attach_address) 983 | #print("[+] found VORNQ handler") 984 | return True 985 | return False 986 | # 987 | def find_VLoadToVSP(self,bb): 988 | is_mov_vsp_mem_vsp = False 989 | is_add_vip_4 = False 990 | bb_semantics = BasicBlock() 991 | attach_address = 0 992 | for insn in bb.getInstructions(): 993 | opcode = insn.getType() 994 | ops = insn.getOperands() 995 | if opcode == OPCODE.X86.MOV \ 996 | and ops[0].getType() == OPERAND.REG \ 997 | and ops[1].getType() == OPERAND.MEM \ 998 | and self.VSP in [e[0] for e in insn.getReadRegisters()] \ 999 | and self.VSP in [e[0] for e in insn.getWrittenRegisters()]: 1000 | is_mov_vsp_mem_vsp = True 1001 | bb_semantics.add(insn) 1002 | attach_address = insn.getAddress() 1003 | if opcode == OPCODE.X86.ADD \ 1004 | and ops[0] == self.VIP \ 1005 | and ops[1].getType() == OPERAND.IMM \ 1006 | and ops[1].getValue() == 0x04: 1007 | is_add_vip_4 = True 1008 | bb_semantics.add(insn) 1009 | if opcode == OPCODE.X86.SUB \ 1010 | and ops[0] == self.VIP \ 1011 | and ops[1].getType() == OPERAND.IMM \ 1012 | and ops[1].getValue() == 0x04: 1013 | is_add_vip_4 = True 1014 | bb_semantics.add(insn) 1015 | if is_add_vip_4 and is_mov_vsp_mem_vsp: 1016 | self.fetch_values['VLoadToVSP'].append(attach_address) 1017 | #print("[+] found VLoadToVSP handler") 1018 | return True 1019 | return False 1020 | # 1021 | def find_VSTORED(self,bb): 1022 | is_add_vsp_12 = False 1023 | is_mov_ptr_vsp = False 1024 | is_mov_val_vsp_8 = False 1025 | is_mov_ptr_val = False 1026 | attach_address = bb.getInstructions()[0].getAddress() 1027 | ptr = None 1028 | val = None 1029 | for insn in bb.getInstructions(): 1030 | opcode = insn.getType() 1031 | ops = insn.getOperands() 1032 | if opcode == OPCODE.X86.ADD \ 1033 | and ops[0] == self.VSP \ 1034 | and ops[1].getType() == OPERAND.IMM \ 1035 | and ops[1].getValue() == 0x0c: 1036 | is_add_vsp_12 = True 1037 | if opcode == OPCODE.X86.MOV \ 1038 | and ops[1].getType() == OPERAND.MEM \ 1039 | and ops[1].getBaseRegister() == self.VSP \ 1040 | and ops[1].getDisplacement().getValue() == 0 \ 1041 | and ops[0].getBitSize() == 64: 1042 | is_mov_ptr_vsp = True 1043 | ptr = ops[0] 1044 | if opcode == OPCODE.X86.MOV \ 1045 | and ops[1].getType() == OPERAND.MEM \ 1046 | and ops[1].getBaseRegister() == self.VSP \ 1047 | and ops[1].getDisplacement().getValue() > 0 \ 1048 | and ops[0].getBitSize() == 32: 1049 | is_mov_val_vsp_8 = True 1050 | val = ops[0] 1051 | if opcode == OPCODE.X86.MOV \ 1052 | and ops[0].getType() == OPERAND.MEM \ 1053 | and ops[0].getBaseRegister() == ptr \ 1054 | and ops[1] == val: 1055 | is_mov_ptr_val = True 1056 | if is_mov_ptr_val and is_mov_val_vsp_8 and is_mov_ptr_vsp \ 1057 | and is_add_vsp_12: 1058 | self.fetch_values['VSTORED'].append(attach_address) 1059 | return True 1060 | return False 1061 | # 1062 | def find_VSTOREQSTACK(self,bb): 1063 | is_mov_ptr_vsp = False 1064 | is_mov_val_vsp_8 = False 1065 | is_add_vsp_0x10 = False 1066 | is_mov_ss_ptr_val = False 1067 | ptr = None 1068 | val = None 1069 | attach_address = bb.getInstructions()[0].getAddress() 1070 | for insn in bb.getInstructions(): 1071 | opcode = insn.getType() 1072 | ops = insn.getOperands() 1073 | if opcode == OPCODE.X86.MOV \ 1074 | and ops[0].getType() == OPERAND.REG \ 1075 | and ops[1].getType() == OPERAND.MEM \ 1076 | and ops[1].getBaseRegister() == self.VSP \ 1077 | and ops[1].getDisplacement().getValue() == 0 \ 1078 | and ops[0].getBitSize() == 64: 1079 | is_mov_ptr_vsp = True 1080 | ptr = ops[0] 1081 | if opcode == OPCODE.X86.MOV \ 1082 | and ops[0].getType() == OPERAND.REG \ 1083 | and ops[1].getType() == OPERAND.MEM \ 1084 | and ops[1].getBaseRegister() == self.VSP \ 1085 | and ops[1].getDisplacement().getValue() > 0 \ 1086 | and ops[0].getBitSize() == 64: 1087 | is_mov_val_vsp_8 = True 1088 | val = ops[0] 1089 | if opcode == OPCODE.X86.ADD \ 1090 | and ops[0] == self.VSP \ 1091 | and ops[1].getType() == OPERAND.IMM \ 1092 | and ops[1].getValue() == 0x10: 1093 | is_add_vsp_0x10 = True 1094 | if opcode == OPCODE.X86.MOV \ 1095 | and ops[0].getType() == OPERAND.MEM \ 1096 | and ops[1].getType() == OPERAND.REG \ 1097 | and ops[0].getBaseRegister() == ptr \ 1098 | and ops[1] == val: 1099 | is_mov_ss_ptr_val = True 1100 | if is_mov_ss_ptr_val and is_add_vsp_0x10 and is_mov_val_vsp_8 \ 1101 | and is_mov_ptr_vsp: 1102 | self.fetch_values['VSTOREQSTACK'].append(attach_address) 1103 | return True 1104 | return False 1105 | # 1106 | def find_VLOADQSTACK(self,bb): 1107 | reg_ptr = None 1108 | reg_val = None 1109 | is_mov_reg_ptr_vsp = False 1110 | is_mov_val_reg_ptr = False 1111 | is_mov_vsp_val = False 1112 | is_add_vip_4 = False 1113 | bb_semantics = BasicBlock() 1114 | attach_address = 0 1115 | for insn in bb.getInstructions(): 1116 | opcode = insn.getType() 1117 | ops = insn.getOperands() 1118 | if opcode == OPCODE.X86.MOV \ 1119 | and ops[0].getType() == OPERAND.REG \ 1120 | and ops[1].getType() == OPERAND.MEM \ 1121 | and ops[1].getBaseRegister() == self.VSP: 1122 | is_mov_reg_ptr_vsp = True 1123 | reg_ptr = ops[0] 1124 | bb_semantics.add(insn) 1125 | if opcode == OPCODE.X86.MOV \ 1126 | and ops[0].getType() == OPERAND.REG \ 1127 | and ops[1].getType() == OPERAND.MEM \ 1128 | and ops[1].getBaseRegister() == reg_ptr \ 1129 | and ops[1].getSegmentRegister() == self.ctx.registers.ss: 1130 | is_mov_val_reg_ptr = True 1131 | reg_val = ops[0] 1132 | bb_semantics.add(insn) 1133 | if opcode == OPCODE.X86.MOV \ 1134 | and ops[0].getType() == OPERAND.MEM \ 1135 | and ops[1].getType() == OPERAND.REG \ 1136 | and ops[0].getBaseRegister() == self.VSP \ 1137 | and ops[1] == reg_val: 1138 | is_mov_vsp_val = True 1139 | attach_address = insn.getAddress() 1140 | bb_semantics.add(insn) 1141 | if opcode == OPCODE.X86.ADD \ 1142 | and ops[0] == self.VIP \ 1143 | and ops[1].getType() == OPERAND.IMM \ 1144 | and ops[1].getValue() == 0x04: 1145 | is_add_vip_4 = True 1146 | bb_semantics.add(insn) 1147 | if opcode == OPCODE.X86.SUB \ 1148 | and ops[0] == self.VIP \ 1149 | and ops[1].getType() == OPERAND.IMM \ 1150 | and ops[1].getValue() == 0x04: 1151 | is_add_vip_4 = True 1152 | bb_semantics.add(insn) 1153 | if is_add_vip_4 and is_mov_vsp_val \ 1154 | and is_mov_val_reg_ptr and is_mov_reg_ptr_vsp: 1155 | self.fetch_values['VLOADQSTACK'].append(attach_address) 1156 | #print("[+] found VLOADQSTACK handler") 1157 | return True 1158 | return False 1159 | # 1160 | def find_VPUSHRD(self,bb): 1161 | attach_address = 0 1162 | is_mov_val_vregs_idx = False 1163 | is_sub_vsp_4 = False 1164 | is_mov_vsp_val = False 1165 | val = None 1166 | for insn in bb.getInstructions(): 1167 | opcode = insn.getType() 1168 | ops = insn.getOperands() 1169 | if opcode == OPCODE.X86.MOV \ 1170 | and ops[0].getType() == OPERAND.REG \ 1171 | and ops[1].getType() == OPERAND.MEM \ 1172 | and ops[0].getBitSize() == 32 \ 1173 | and ops[1].getBaseRegister() == self.VREGISTERS \ 1174 | and ops[1].getIndexRegister() != None: 1175 | val = ops[0] 1176 | attach_address = insn.getAddress() 1177 | is_mov_val_vregs_idx = True 1178 | if opcode == OPCODE.X86.SUB \ 1179 | and ops[0] == self.VSP \ 1180 | and ops[1].getType() == OPERAND.IMM \ 1181 | and ops[1].getValue() == 0x04: 1182 | is_sub_vsp_4 = True 1183 | if opcode == OPCODE.X86.MOV \ 1184 | and ops[0].getType() == OPERAND.MEM \ 1185 | and ops[1].getType() == OPERAND.REG \ 1186 | and ops[0].getBaseRegister() == self.VSP \ 1187 | and ops[1] == val: 1188 | is_mov_vsp_val = True 1189 | if is_mov_val_vregs_idx and is_sub_vsp_4 and is_mov_vsp_val: 1190 | self.fetch_values['VPUSHRD'].append(attach_address) 1191 | return True 1192 | return False 1193 | # 1194 | def find_VPOPW(self,bb): 1195 | attach_address = 0 1196 | val = None 1197 | is_mov_val_vsp =False 1198 | is_add_vsp_9 = False 1199 | is_mov_vregs_idx_val = False 1200 | for insn in bb.getInstructions(): 1201 | opcode = insn.getType() 1202 | ops = insn.getOperands() 1203 | if opcode == OPCODE.X86.MOV \ 1204 | and ops[0].getType() == OPERAND.REG \ 1205 | and ops[0].getBitSize() == 16 \ 1206 | and ops[1].getType() == OPERAND.MEM \ 1207 | and ops[1].getBaseRegister() == self.VSP: 1208 | val = ops[0] 1209 | is_mov_val_vsp = True 1210 | if opcode == OPCODE.X86.ADD \ 1211 | and ops[0] == self.VSP \ 1212 | and ops[1].getType() == OPERAND.IMM \ 1213 | and ops[1].getValue() == 0x02: 1214 | is_add_vsp_9 = True 1215 | if opcode == OPCODE.X86.MOV \ 1216 | and ops[0].getType() == OPERAND.MEM \ 1217 | and ops[1].getType() == OPERAND.REG \ 1218 | and ops[0].getBaseRegister() == self.VREGISTERS \ 1219 | and ops[0].getIndexRegister() != None \ 1220 | and ops[1].getBitSize() == 16 \ 1221 | and ops[1] == val: 1222 | is_mov_vregs_idx_val = True 1223 | attach_address = insn.getAddress() 1224 | if is_mov_vregs_idx_val and is_add_vsp_9 and is_mov_val_vsp: 1225 | self.fetch_values['VPOPW'].append(attach_address) 1226 | return True 1227 | return False 1228 | # 1229 | def find_VHADDB(self,bb): 1230 | res = None 1231 | is_sub_vsp_6 = False 1232 | is_add_bb = False 1233 | is_mov_vsp_8_res = False 1234 | is_pushfq = False 1235 | is_pop_vsp = False 1236 | attach_address = bb.getInstructions()[0].getAddress() 1237 | for insn in bb.getInstructions(): 1238 | opcode = insn.getType() 1239 | ops = insn.getOperands() 1240 | if opcode == OPCODE.X86.SUB \ 1241 | and ops[0] == self.VSP \ 1242 | and ops[1].getType() == OPERAND.IMM \ 1243 | and ops[1].getValue() == 0x06: 1244 | is_sub_vsp_6 = True 1245 | if opcode == OPCODE.X86.ADD \ 1246 | and ops[0].getType() == OPERAND.REG \ 1247 | and ops[1].getType() == OPERAND.REG \ 1248 | and ops[0].getBitSize() == 8 \ 1249 | and ops[1].getBitSize() == 8: 1250 | res = self.ctx.getParentRegister(ops[0]) 1251 | is_add_bb = True 1252 | if opcode == OPCODE.X86.MOV \ 1253 | and ops[0].getType() == OPERAND.MEM \ 1254 | and ops[1].getType() == OPERAND.REG \ 1255 | and ops[0].getBaseRegister() == self.VSP \ 1256 | and ops[0].getDisplacement().getValue() == 0x08 \ 1257 | and ops[1].getBitSize() == 16 \ 1258 | and self.ctx.getParentRegister(ops[1]) == res: 1259 | is_mov_vsp_8_res = True 1260 | if opcode == OPCODE.X86.PUSHFQ: 1261 | is_pushfq = True 1262 | if opcode == OPCODE.X86.POP \ 1263 | and ops[0].getType() == OPERAND.MEM \ 1264 | and ops[0].getBaseRegister() == self.VSP: 1265 | is_pop_vsp = True 1266 | if is_pop_vsp and is_pushfq and is_mov_vsp_8_res \ 1267 | and is_add_bb and is_sub_vsp_6: 1268 | self.fetch_values['VHADDB'].append(attach_address) 1269 | return True 1270 | return False 1271 | # 1272 | def find_VPOPB(self,bb): 1273 | val = None 1274 | is_mov_val_vsp = False 1275 | is_add_vsp_2 = False 1276 | is_mov_vregs_idx_val = False 1277 | attach_address = 0 1278 | for insn in bb.getInstructions(): 1279 | opcode = insn.getType() 1280 | ops = insn.getOperands() 1281 | if opcode == OPCODE.X86.MOV \ 1282 | and ops[0].getType() == OPERAND.REG \ 1283 | and ops[1].getType() == OPERAND.MEM \ 1284 | and ops[1].getBaseRegister() == self.VSP \ 1285 | and ops[0].getBitSize() == 16: 1286 | val = self.ctx.getParentRegister(ops[0]) 1287 | is_mov_val_vsp = True 1288 | if opcode == OPCODE.X86.ADD \ 1289 | and ops[0] == self.VSP \ 1290 | and ops[1].getType() == OPERAND.IMM \ 1291 | and ops[1].getValue() == 0x02: 1292 | is_add_vsp_2 = True 1293 | if opcode == OPCODE.X86.MOV \ 1294 | and ops[0].getType() == OPERAND.MEM \ 1295 | and ops[1].getType() == OPERAND.REG \ 1296 | and ops[0].getBaseRegister() == self.VREGISTERS \ 1297 | and ops[0].getIndexRegister() != None \ 1298 | and ops[1].getBitSize() == 8 \ 1299 | and self.ctx.getParentRegister(ops[1]) == val: 1300 | is_mov_vregs_idx_val = True 1301 | attach_address = insn.getAddress() 1302 | if is_mov_vregs_idx_val and is_add_vsp_2 \ 1303 | and is_mov_val_vsp: 1304 | self.fetch_values['VPOPB'].append(attach_address) 1305 | return True 1306 | return False 1307 | # 1308 | def find_VDIVD(self,bb): 1309 | is_mov_low_vsp_4 = False 1310 | is_mov_high_vsp = False 1311 | is_mov_del_vsp_8 = False 1312 | is_sub_vsp_4 = False 1313 | is_div_del = False 1314 | is_mov_vsp_8 = False 1315 | is_mov_vsp_0xc = False 1316 | is_pushfq = False 1317 | is_pop_vsp = False 1318 | deliter = False 1319 | attach_address = bb.getInstructions()[0].getAddress() 1320 | for insn in bb.getInstructions(): 1321 | opcode = insn.getType() 1322 | ops = insn.getOperands() 1323 | if opcode == OPCODE.X86.MOV \ 1324 | and ops[0] == self.ctx.registers.eax \ 1325 | and ops[1].getType() == OPERAND.MEM \ 1326 | and ops[1].getBaseRegister() == self.VSP \ 1327 | and ops[1].getDisplacement().getValue() == 0x04: 1328 | is_mov_low_vsp_4 = True 1329 | if opcode == OPCODE.X86.MOV \ 1330 | and ops[0] == self.ctx.registers.edx \ 1331 | and ops[1].getType() == OPERAND.MEM \ 1332 | and ops[1].getBaseRegister() == self.VSP \ 1333 | and ops[1].getDisplacement().getValue() == 0: 1334 | is_mov_high_vsp = True 1335 | if opcode == OPCODE.X86.MOV \ 1336 | and ops[0].getType() == OPERAND.REG \ 1337 | and ops[0].getBitSize() == 32 \ 1338 | and ops[1].getType() == OPERAND.MEM \ 1339 | and ops[1].getBaseRegister() == self.VSP \ 1340 | and ops[1].getDisplacement().getValue() == 0x08: 1341 | deliter = ops[0] 1342 | is_mov_del_vsp_8 = True 1343 | if opcode == OPCODE.X86.SUB \ 1344 | and ops[0] == self.VSP \ 1345 | and ops[1].getType() == OPERAND.IMM \ 1346 | and ops[1].getValue() == 0x04: 1347 | is_sub_vsp_4 = True 1348 | if opcode == OPCODE.X86.DIV \ 1349 | and ops[0] == deliter: 1350 | is_div_del = True 1351 | if opcode == OPCODE.X86.MOV \ 1352 | and ops[0].getType() == OPERAND.MEM \ 1353 | and ops[1].getType() == OPERAND.REG \ 1354 | and ops[0].getBaseRegister() == self.VSP \ 1355 | and ops[0].getDisplacement().getValue() == 0x08 \ 1356 | and ops[1] == self.ctx.registers.edx: 1357 | is_mov_vsp_8 = True 1358 | if opcode == OPCODE.X86.MOV \ 1359 | and ops[0].getType() == OPERAND.MEM \ 1360 | and ops[1].getType() == OPERAND.REG \ 1361 | and ops[0].getBaseRegister() == self.VSP \ 1362 | and ops[0].getDisplacement().getValue() == 0x0c \ 1363 | and ops[1] == self.ctx.registers.eax: 1364 | is_mov_vsp_0xc = True 1365 | if opcode == OPCODE.X86.PUSHFQ: 1366 | is_pushfq = True 1367 | if opcode == OPCODE.X86.POP \ 1368 | and ops[0].getType() == OPERAND.MEM \ 1369 | and ops[0].getBaseRegister() == self.VSP: 1370 | is_pop_vsp = True 1371 | if is_pop_vsp and is_pushfq and is_mov_vsp_0xc \ 1372 | and is_mov_vsp_8 and is_div_del and is_sub_vsp_4 \ 1373 | and is_mov_del_vsp_8 and is_mov_high_vsp and is_mov_low_vsp_4: 1374 | self.fetch_values['VDIVD'].append(attach_address) 1375 | return True 1376 | return False 1377 | # 1378 | def find_VRDTSC(self,bb): 1379 | is_rdtsc = False 1380 | is_mov_vsp_edx = False 1381 | is_mov_vsp_4_eax = False 1382 | is_sub_vsp_8 = False 1383 | attach_address = bb.getInstructions()[0].getAddress() 1384 | for insn in bb.getInstructions(): 1385 | opcode = insn.getType() 1386 | ops = insn.getOperands() 1387 | if opcode == OPCODE.X86.RDTSC: 1388 | is_rdtsc = True 1389 | if opcode == OPCODE.X86.SUB \ 1390 | and ops[0] == self.VSP \ 1391 | and ops[1].getType() == OPERAND.IMM \ 1392 | and ops[1].getValue() == 0x08: 1393 | is_sub_vsp_8 = True 1394 | if opcode == OPCODE.X86.MOV \ 1395 | and ops[0].getType() == OPERAND.MEM \ 1396 | and ops[0].getBaseRegister() == self.VSP \ 1397 | and ops[0].getDisplacement().getValue() == 0 \ 1398 | and ops[1].getType() == OPERAND.REG \ 1399 | and ops[1].getBitSize() == 32 \ 1400 | and ops[1] == self.ctx.registers.edx: 1401 | is_mov_vsp_edx = True 1402 | if opcode == OPCODE.X86.MOV \ 1403 | and ops[0].getType() == OPERAND.MEM \ 1404 | and ops[0].getBaseRegister() == self.VSP \ 1405 | and ops[0].getDisplacement().getValue() == 4 \ 1406 | and ops[1].getType() == OPERAND.REG \ 1407 | and ops[1].getBitSize() == 32 \ 1408 | and ops[1] == self.ctx.registers.eax: 1409 | is_mov_vsp_4_eax = True 1410 | if is_sub_vsp_8 and is_mov_vsp_4_eax and is_mov_vsp_edx and is_rdtsc: 1411 | self.fetch_values['VRDTSC'].append(attach_address) 1412 | return True 1413 | return False 1414 | # 1415 | def find_VMULD(self,bb): 1416 | is_mov_eax_vsp_4 = False 1417 | is_mov_edx_vsp = False 1418 | is_sub_vsp_8 = False 1419 | is_mul_edx = False 1420 | is_mov_vsp_8_edx = False 1421 | is_mov_vsp_c_eax = False 1422 | is_pushfq = False 1423 | is_pop_vsp = False 1424 | attach_address = bb.getInstructions()[0].getAddress() 1425 | for insn in bb.getInstructions(): 1426 | opcode = insn.getType() 1427 | ops = insn.getOperands() 1428 | if opcode == OPCODE.X86.MOV \ 1429 | and ops[0].getType() == OPERAND.REG \ 1430 | and ops[0].getBitSize() == 32 \ 1431 | and ops[1].getType() == OPERAND.MEM \ 1432 | and ops[1].getBaseRegister() == self.VSP \ 1433 | and ops[1].getDisplacement().getValue() == 0x04: 1434 | is_mov_eax_vsp_4 = True 1435 | if opcode == OPCODE.X86.MOV \ 1436 | and ops[0].getType() == OPERAND.REG \ 1437 | and ops[0].getBitSize() == 32 \ 1438 | and ops[1].getType() == OPERAND.MEM \ 1439 | and ops[1].getBaseRegister() == self.VSP \ 1440 | and ops[1].getDisplacement().getValue() == 0x00: 1441 | is_mov_edx_vsp = True 1442 | if opcode == OPCODE.X86.SUB \ 1443 | and ops[0] == self.VSP \ 1444 | and ops[1].getType() == OPERAND.IMM \ 1445 | and ops[1].getValue() == 0x08: 1446 | is_sub_vsp_8 = True 1447 | if opcode == OPCODE.X86.MUL \ 1448 | and ops[0] == self.ctx.registers.edx: 1449 | is_mul_edx = True 1450 | if opcode == OPCODE.X86.MOV \ 1451 | and ops[0].getType() == OPERAND.MEM \ 1452 | and ops[0].getBaseRegister() == self.VSP \ 1453 | and ops[0].getDisplacement().getValue() == 0x08 \ 1454 | and ops[1].getType() == OPERAND.REG \ 1455 | and ops[1].getBitSize() == 32 \ 1456 | and ops[1] == self.ctx.registers.edx: 1457 | is_mov_vsp_8_edx = True 1458 | if opcode == OPCODE.X86.MOV \ 1459 | and ops[0].getType() == OPERAND.MEM \ 1460 | and ops[0].getBaseRegister() == self.VSP \ 1461 | and ops[0].getDisplacement().getValue() == 0x0c \ 1462 | and ops[1].getType() == OPERAND.REG \ 1463 | and ops[1].getBitSize() == 32 \ 1464 | and ops[1] == self.ctx.registers.eax: 1465 | is_mov_vsp_c_eax = True 1466 | if opcode == OPCODE.X86.PUSHFQ: 1467 | is_pushfq = True 1468 | if opcode == OPCODE.X86.POP \ 1469 | and ops[0].getType() == OPERAND.MEM \ 1470 | and ops[0].getBaseRegister() == self.VSP: 1471 | is_pop_vsp = True 1472 | if is_pop_vsp and is_pushfq and is_mov_vsp_c_eax \ 1473 | and is_mov_vsp_8_edx and is_mul_edx \ 1474 | and is_sub_vsp_8 and is_mov_edx_vsp \ 1475 | and is_mov_eax_vsp_4: 1476 | self.fetch_values['VMULD'].append(attach_address) 1477 | return True 1478 | return False 1479 | # 1480 | def analyze_vmops(self,bb): 1481 | analyzers = [ 1482 | self.find_VMULD, 1483 | self.find_VDIVD, 1484 | self.find_VRDTSC, 1485 | self.find_VPOPB, 1486 | self.find_VHADDB, 1487 | self.find_VPOPW, 1488 | self.find_VPUSHRQ, 1489 | self.find_VSHRQ, 1490 | self.find_VPUSHI64, 1491 | self.find_VPUSHI32, 1492 | self.find_VPUSHI16, 1493 | self.find_VPOPQ, 1494 | self.find_VADDQ, 1495 | self.find_VLOADQ, 1496 | self.find_VPUSHVSP, 1497 | self.find_VANDNQ, 1498 | self.find_VORNQ, 1499 | self.find_VLOADQSTACK, 1500 | self.find_VLoadToVSP, 1501 | self.find_VJMP, 1502 | self.find_VJMP2, 1503 | self.find_VSTORED, 1504 | self.find_VPOPD, 1505 | self.find_VSTOREQSTACK, 1506 | self.find_VPUSHRD 1507 | ] 1508 | for analyzer in analyzers: 1509 | if analyzer(bb): 1510 | return True 1511 | return False 1512 | # 1513 | def analyze_vmhandlers(self,bb): 1514 | #print("[*] analyze vmp handler\n") 1515 | if self.analyze_vmexit(bb): 1516 | return True 1517 | if not self.analyze_vmops(bb): 1518 | print("[-] not found vmp handler, please append vmp template handler") 1519 | print("handler code at address: %x" % bb.getInstructions()[0].getAddress()) 1520 | bb_simp = self.ctx.simplify(bb) 1521 | bb_simp = BasicBlock([bb_simp.getInstructions()[-1]]+bb_simp.getInstructions()[:-1]) 1522 | print(bb_simp) 1523 | exit(1) 1524 | return False 1525 | # 1526 | def analyze(self): 1527 | bb_entry = self.entry_point 1528 | while True: 1529 | bb = self.analyzeBasicBlock(bb_entry) 1530 | self.ctx.processing(bb,bb.getFirstAddress()) 1531 | if not self.is_find_vmenter: 1532 | is_find_vmenter = self.find_vmenter(bb) 1533 | if is_find_vmenter: 1534 | self.is_find_vmenter = True 1535 | self.bb_vmenter = bb 1536 | self.analyze_vmenter() 1537 | self.symbolizeRegistersBytecodeCtx() 1538 | print("[+] start analyzing vmp handlers") 1539 | else: 1540 | is_vmswitch_or_vmexit = self.analyze_vmhandlers(bb) 1541 | if is_vmswitch_or_vmexit: 1542 | break 1543 | bb_entry = self.getNextEntryBasicBlock() 1544 | return self.fetch_values 1545 | # 1546 | def symbolizeRegisters(self): 1547 | print("[*] symbolizing all registers") 1548 | for reg in ['rax','rcx','rdx','rbx','rsp','rbp','rsi','rdi','r8','r9','r10','r11','r12','r13','r14','r15','rip','eflags']: 1549 | self.ctx.symbolizeRegister(self.ctx.getRegister(reg),reg) 1550 | def symbolizeRegistersBytecodeCtx(self): 1551 | self.ctx_bytecode.symbolizeRegister(self.VIP) 1552 | self.ctx_bytecode.symbolizeRegister(self.VSP) 1553 | self.ctx_bytecode.symbolizeRegister(self.VREGISTERS) 1554 | # 1555 | def init_triton_dse_regs(self,ctx): 1556 | ctx.setConcreteRegisterValue(ctx.registers.rax,self.dp.regs.rax) 1557 | ctx.setConcreteRegisterValue(ctx.registers.rcx,self.dp.regs.rcx) 1558 | ctx.setConcreteRegisterValue(ctx.registers.rdx,self.dp.regs.rdx) 1559 | ctx.setConcreteRegisterValue(ctx.registers.rbx,self.dp.regs.rbx) 1560 | ctx.setConcreteRegisterValue(ctx.registers.rsp,self.dp.regs.rsp) 1561 | ctx.setConcreteRegisterValue(ctx.registers.rbp,self.dp.regs.rbp) 1562 | ctx.setConcreteRegisterValue(ctx.registers.rsi,self.dp.regs.rsi) 1563 | ctx.setConcreteRegisterValue(ctx.registers.rdi,self.dp.regs.rdi) 1564 | ctx.setConcreteRegisterValue(ctx.registers.r8,self.dp.regs.r8) 1565 | ctx.setConcreteRegisterValue(ctx.registers.r9,self.dp.regs.r9) 1566 | ctx.setConcreteRegisterValue(ctx.registers.r10,self.dp.regs.r10) 1567 | ctx.setConcreteRegisterValue(ctx.registers.r11,self.dp.regs.r11) 1568 | ctx.setConcreteRegisterValue(ctx.registers.r12,self.dp.regs.r12) 1569 | ctx.setConcreteRegisterValue(ctx.registers.r13,self.dp.regs.r13) 1570 | ctx.setConcreteRegisterValue(ctx.registers.r14,self.dp.regs.r14) 1571 | ctx.setConcreteRegisterValue(ctx.registers.r15,self.dp.regs.r15) 1572 | # 1573 | def __init_triton_dse(self): 1574 | print("[*](triton dse) init ctx") 1575 | self.ctx = TritonContext(ARCH.X86_64) 1576 | self.ctx_bytecode = TritonContext(ARCH.X86_64) 1577 | self.ctx.setAstRepresentationMode(AST_REPRESENTATION.PCODE) 1578 | self.ctx.setAstRepresentationMode(AST_REPRESENTATION.PCODE) 1579 | self.ctx_bytecode.setMode(MODE.ALIGNED_MEMORY, True) 1580 | self.ctx_bytecode.setMode(MODE.CONSTANT_FOLDING, True) 1581 | self.ctx_bytecode.setMode(MODE.AST_OPTIMIZATIONS, True) 1582 | self.ctx.setMode(MODE.ALIGNED_MEMORY, True) 1583 | self.ctx.setMode(MODE.CONSTANT_FOLDING, True) 1584 | self.ctx.setMode(MODE.AST_OPTIMIZATIONS, True) 1585 | self.init_triton_dse_regs(self.ctx) 1586 | self.init_triton_dse_regs(self.ctx_bytecode) 1587 | print("[*](triton dse) mapping and writing datas into regions") 1588 | for region in self.mu.mem_regions(): 1589 | print(f"[*](triton dse) mapping and writing data into region: va_range={hex(region[0])}-{hex(region[1])}") 1590 | data = self.mu.mem_read(region[0],region[1]-region[0]) 1591 | self.ctx.setConcreteMemoryAreaValue(region[0], data) 1592 | self.ctx_bytecode.setConcreteMemoryAreaValue(region[0], data) 1593 | # 1594 | def __get_map_prot(self,name): 1595 | if name == "UNDEFINED": 1596 | return UC_PROT_NONE 1597 | elif name == "PAGE_EXECUTE": 1598 | return UC_PROT_EXEC 1599 | elif name == "PAGE_EXECUTE_READ": 1600 | return UC_PROT_READ | UC_PROT_EXEC 1601 | elif name == "PAGE_EXECUTE_READWRITE": 1602 | return UC_PROT_ALL 1603 | elif name == "PAGE_READWRITE": 1604 | return UC_PROT_READ | UC_PROT_WRITE 1605 | elif name == "PAGE_READONLY": 1606 | return UC_PROT_READ 1607 | else: 1608 | return UC_PROT_ALL 1609 | # 1610 | def load_binary(self,dp): 1611 | self.mu = Uc(UC_ARCH_X86, UC_MODE_64) 1612 | print("[*] loading dump into unicorn and triton dse engine") 1613 | mappings = self.dp.memory.map() 1614 | print("[*](unicorn) loading memory regions into unicorn") 1615 | for m in mappings: 1616 | prot = self.__get_map_prot(m.protect.name) 1617 | base = m.base 1618 | size = m.region_size 1619 | if prot == UC_PROT_NONE: 1620 | continue 1621 | if len(m.info) > 0: 1622 | if m.info[0] == "PEB": 1623 | print("[*](unicorn) loading peb into unicorn") 1624 | self.mu.mem_map(0, size, prot) 1625 | self.mu.mem_write(0,bytes(self.dp.memory.read(base,size))) 1626 | continue 1627 | print(f"[*](unicorn) mapping memory region: va_range={hex(base)}-{hex(base+size)}, perms: {m.protect.name}") 1628 | self.mu.mem_map(base, size, prot) 1629 | print(f"[*](unicorn) writing data into region: va_range={hex(base)}-{hex(base+size)}, perms: {m.protect.name}") 1630 | data = bytes(self.dp.memory.read(base, size)) 1631 | self.mu.mem_write(base, data) 1632 | print("[*](unicorn) initializing cpu registers") 1633 | self.mu.reg_write(UC_X86_REG_RAX,self.dp.regs.rax) 1634 | self.mu.reg_write(UC_X86_REG_RCX,self.dp.regs.rcx) 1635 | self.mu.reg_write(UC_X86_REG_RDX,self.dp.regs.rdx) 1636 | self.mu.reg_write(UC_X86_REG_RBX,self.dp.regs.rbx) 1637 | self.mu.reg_write(UC_X86_REG_RSP,self.dp.regs.rsp) 1638 | self.mu.reg_write(UC_X86_REG_RBP,self.dp.regs.rbp) 1639 | self.mu.reg_write(UC_X86_REG_RSI,self.dp.regs.rsi) 1640 | self.mu.reg_write(UC_X86_REG_RDI,self.dp.regs.rdi) 1641 | self.mu.reg_write(UC_X86_REG_R8,self.dp.regs.r8) 1642 | self.mu.reg_write(UC_X86_REG_R9,self.dp.regs.r9) 1643 | self.mu.reg_write(UC_X86_REG_R10,self.dp.regs.r10) 1644 | self.mu.reg_write(UC_X86_REG_R11,self.dp.regs.r11) 1645 | self.mu.reg_write(UC_X86_REG_R12,self.dp.regs.r12) 1646 | self.mu.reg_write(UC_X86_REG_R13,self.dp.regs.r13) 1647 | self.mu.reg_write(UC_X86_REG_R14,self.dp.regs.r14) 1648 | self.mu.reg_write(UC_X86_REG_R15,self.dp.regs.r15) 1649 | print("[+] complete init execution context") 1650 | 1651 | def hook_extractor_bytecode(mu, address, size, vmp_extractor): 1652 | if vmp_extractor.is_vmp_handler(address): 1653 | vmp_extractor.gen(mu,address) 1654 | 1655 | VPUSHI64 = 0 1656 | VPUSHRQ = 1 1657 | VPUSHVSP = 2 1658 | VLoadToVSP= 3 1659 | VLOADQSTACK=4 1660 | VLOADQ = 5 1661 | VPOPQ = 6 1662 | VADDQ = 7 1663 | VANDNQ = 8 1664 | VORNQ = 9 1665 | VMENTER = 10 1666 | VMSWITCH = 11 1667 | VPUSHI32 = 12 1668 | 1669 | class VmpExtractorX64(VmpAnalyzerX64): 1670 | def __init__(self,minidumpfile,vmp_segment_range): 1671 | super().__init__(minidumpfile,vmp_segment_range) 1672 | self.vmp_handlers_attach_list = None 1673 | self.vmp_bytecode = bytes() 1674 | # 1675 | def extract_bytecode(self): 1676 | self.vmp_handlers_attach_list = super().analyze() 1677 | print("[*] start extracting bytecode") 1678 | super().load_binary(super().get_dp()) 1679 | super().get_mu().hook_add(UC_HOOK_CODE,hook_extractor_bytecode,self) 1680 | super().get_mu().emu_start(super().get_dp().regs.rip,-1) 1681 | print("[+] complete extract bytecode!") 1682 | return self.vmp_bytecode 1683 | # 1684 | def is_vmp_handler(self,address_handler): 1685 | for l in self.vmp_handlers_attach_list.values(): 1686 | if address_handler in l: 1687 | return True 1688 | return False 1689 | # 1690 | def check_handler(self,name,address_handler): 1691 | if address_handler not in self.vmp_handlers_attach_list[name]: 1692 | return False 1693 | l = self.vmp_handlers_attach_list[name] 1694 | for i in range(len(l)): 1695 | if l[i] == address_handler: 1696 | l.pop(i) 1697 | break 1698 | return True 1699 | def gen_imm32(self,imm32): 1700 | self.vmp_bytecode+=int.to_bytes(imm32,4,byteorder='little') 1701 | def gen_imm64(self,imm64): 1702 | self.vmp_bytecode+=int.to_bytes(imm64,8,byteorder='little') 1703 | def gen_opcode(self,opcode): 1704 | self.vmp_bytecode+=int.to_bytes(opcode,1,byteorder='little') 1705 | def gen_regop(self,reg_idx): 1706 | self.vmp_bytecode+=int.to_bytes(reg_idx,1,byteorder='little') 1707 | # 1708 | def gen_VPUSHI32(self,mu,address_handler): 1709 | if not self.check_handler("VPUSHI32",address_handler): 1710 | return 1711 | insn = Instruction(address_handler,bytes(mu.mem_read(address_handler,16))) 1712 | super().get_ctx().disassembly(insn) 1713 | reg_id = get_reg(super().get_ctx(),insn.getOperands()[1]) 1714 | i64 = mu.reg_read(reg_id) 1715 | self.gen_opcode(VPUSHI32) 1716 | self.gen_imm32(i64&0xffffffff) 1717 | # 1718 | def gen_VPUSHI64(self,mu,address_handler): 1719 | if not self.check_handler("VPUSHI64",address_handler): 1720 | return 1721 | insn = Instruction(address_handler,bytes(mu.mem_read(address_handler,16))) 1722 | super().get_ctx().disassembly(insn) 1723 | reg_id = get_reg(super().get_ctx(),insn.getOperands()[1]) 1724 | i64 = mu.reg_read(reg_id) 1725 | self.gen_opcode(VPUSHI64) 1726 | self.gen_imm64(i64) 1727 | # 1728 | def gen_VPUSHRQ(self,mu,address_handler): 1729 | if not self.check_handler("VPUSHRQ",address_handler): 1730 | return 1731 | insn = Instruction(address_handler,bytes(mu.mem_read(address_handler,16))) 1732 | super().get_ctx().disassembly(insn) 1733 | reg_id = get_reg(super().get_ctx(),insn.getOperands()[1].getIndexRegister()) 1734 | idx = mu.reg_read(reg_id) 1735 | self.gen_opcode(VPUSHRQ) 1736 | self.gen_regop(idx) 1737 | # 1738 | def gen_VPOPQ(self,mu,address_handler): 1739 | if not self.check_handler("VPOPQ",address_handler): 1740 | return 1741 | insn = Instruction(address_handler,bytes(mu.mem_read(address_handler,16))) 1742 | super().get_ctx().disassembly(insn) 1743 | reg_id = get_reg(super().get_ctx(),insn.getOperands()[0].getIndexRegister()) 1744 | idx = mu.reg_read(reg_id) 1745 | self.gen_opcode(VPOPQ) 1746 | self.gen_regop(idx) 1747 | # 1748 | def gen_VADDQ(self,mu,address_handler): 1749 | if not self.check_handler("VADDQ",address_handler): 1750 | return 1751 | self.gen_opcode(VADDQ) 1752 | # 1753 | def gen_VMENTER(self,mu,address_handler): 1754 | if not self.check_handler("VMENTER",address_handler): 1755 | return 1756 | self.gen_opcode(VMENTER) 1757 | # 1758 | def gen_VMSWITCH(self,mu,address_handler): 1759 | if not self.check_handler("VMSWITCH",address_handler): 1760 | return 1761 | self.gen_opcode(VMSWITCH) 1762 | mu.emu_stop() 1763 | # 1764 | def gen_VANDNQ(self,mu,address_handler): 1765 | if not self.check_handler("VANDNQ",address_handler): 1766 | return 1767 | self.gen_opcode(VANDNQ) 1768 | # 1769 | def gen_VORNQ(self,mu,address_handler): 1770 | if not self.check_handler("VORNQ",address_handler): 1771 | return 1772 | self.gen_opcode(VORNQ) 1773 | # 1774 | def gen_VPUSHVSP(self,mu,address_handler): 1775 | if not self.check_handler("VPUSHVSP",address_handler): 1776 | return 1777 | self.gen_opcode(VPUSHVSP) 1778 | # 1779 | def gen_VLoadToVSP(self,mu,address_handler): 1780 | if not self.check_handler("VLoadToVSP",address_handler): 1781 | return 1782 | self.gen_opcode(VLoadToVSP) 1783 | # 1784 | def gen_VLOADQ(self,mu,address_handler): 1785 | if not self.check_handler("VLOADQ",address_handler): 1786 | return 1787 | self.gen_opcode(VLOADQ) 1788 | # 1789 | def gen_VLOADQSTACK(self,mu,address_handler): 1790 | if not self.check_handler("VLOADQSTACK",address_handler): 1791 | return 1792 | self.gen_opcode(VLOADQSTACK) 1793 | # 1794 | def gen(self,mu,address_handler): 1795 | handlers = [ 1796 | self.gen_VADDQ, 1797 | self.gen_VANDNQ, 1798 | self.gen_VLOADQ, 1799 | self.gen_VLOADQSTACK, 1800 | self.gen_VLoadToVSP, 1801 | self.gen_VMENTER, 1802 | self.gen_VMSWITCH, 1803 | self.gen_VORNQ, 1804 | self.gen_VPOPQ, 1805 | self.gen_VPUSHI64, 1806 | self.gen_VPUSHRQ, 1807 | self.gen_VPUSHI32 1808 | ] 1809 | for hdl in handlers: 1810 | if hdl(mu,address_handler): 1811 | return 1812 | 1813 | vmp = VmpExtractorX64('hdl.dmp',{'start':0x140000000,'end':0x14097c000}) 1814 | vmp_bytecode = vmp.extract_bytecode() 1815 | from miasm.analysis.machine import Machine 1816 | from miasm.core.locationdb import LocationDB 1817 | from miasm.analysis.simplifier import IRCFGSimplifierCommon,IRCFGSimplifierSSA 1818 | machine = Machine("vmp") 1819 | loc_db = LocationDB() 1820 | mdis = machine.dis_engine(vmp_bytecode,loc_db=loc_db) 1821 | asmcfg = mdis.dis_multiblock(0) 1822 | lifter = machine.lifter_model_call(loc_db) 1823 | ircfg = lifter.new_ircfg_from_asmcfg(asmcfg) 1824 | simplifier = IRCFGSimplifierCommon(lifter) 1825 | simplifier.simplify(ircfg,ircfg.heads()[0]) 1826 | --------------------------------------------------------------------------------