├── README.md ├── testcase └── test.c └── vmx_intrinsics.py /README.md: -------------------------------------------------------------------------------- 1 | # VMX_INTRINSICS 2 | 3 | ## Overview 4 | 5 | This is a port of Dougall J'[dj_vmx_intrinsics](https://github.com/dougallj/dj_ida_plugins/tree/master/dj_vmx_intrinsics) to 6 | [IDAPython](https://github.com/idapython/src). 7 | 8 | This plugin allow to display unhandled VMX instructions into their respective intrinsic form when using the decompiler: 9 | 10 | Original output (the value of the register `RAX` is not even displayed): 11 | 12 | _RCX = 0x41424344i64; 13 | __asm { vmwrite rax, rcx } 14 | 15 | Output with the plugin (the value of the VMCS field is now displayed correctly): 16 | 17 | v8 = __vmx_vmwrite(0x681Eui64, 0x41424344ui64); 18 | 19 | Some renaming was necessary as MICROCODE API has changed (cf `ida_hexrays.py`). 20 | 21 | We use the operand type (`mop_a/mop_addr_t`) for output pointer as second argument for `vmread`/`vmclear`/`vmptrld`/`vmptrst`/`vmxon`. 22 | 23 | e.g: 24 | 25 | mov eax, 4816h 26 | vmread rax, rax 27 | 28 | will produce: 29 | 30 | v3 = 0x4816i64; 31 | __vmx_vmread(v3, &v3); 32 | 33 | We have added the `GLBLOW` & `GLBHIGH` for visible_memory and spoiled memory to avoid optimization. 34 | 35 | ## Requirements 36 | 37 | * IDA Pro >= 7.4 38 | 39 | ## Installation 40 | 41 | Copy the file `vmx_instrincis.py` to the IDA plugins folder. 42 | 43 | ## Features 44 | 45 | Intrinsics implemented: 46 | 47 | * `__vmx_off` 48 | * `__vmx_on` 49 | * `__vmx_vmclear` 50 | * `__vmx_vmlaunch` 51 | * `__vmx_vmptrld` 52 | * `__vmx_vmptrst` 53 | * `__vmx_vmread` 54 | * `__vmx_vmresume` 55 | * `__vmx_vmwrite` -------------------------------------------------------------------------------- /testcase/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define GUEST_RIP 0x0000681E 6 | 7 | #pragma optimize("", off) 8 | void test_all_vmx(void) 9 | { 10 | int status; 11 | uint64_t FakePhysicalAddress = 0xB8000; 12 | uint64_t old_rip = 0x00; 13 | uint64_t new_rip = 0x41424344; 14 | 15 | __vmx_off(); 16 | status = __vmx_on(&FakePhysicalAddress); 17 | if (status) { 18 | printf("[-] __vmx_on failed : %d\n", status); 19 | } 20 | status = __vmx_vmclear(&FakePhysicalAddress); 21 | if (status) { 22 | printf("[-] __vmx_vmclear failed : %d\n", status); 23 | } 24 | __vmx_vmlaunch(); 25 | status = __vmx_vmptrld(&FakePhysicalAddress); 26 | if (status) { 27 | printf("[-] __vmx_vmptrld failed : %d\n", status); 28 | } 29 | __vmx_vmptrst(&FakePhysicalAddress); 30 | __vmx_vmread(GUEST_RIP, &old_rip); 31 | if (__vmx_vmwrite(GUEST_RIP, new_rip) != 0) { 32 | return; 33 | } 34 | status = __vmx_vmresume(); 35 | if (status) { 36 | printf("[-] __vmx_vmresume failed : %d\n", status); 37 | } 38 | } 39 | 40 | int main(int arg, char* argv[]) 41 | { 42 | test_all_vmx(); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /vmx_intrinsics.py: -------------------------------------------------------------------------------- 1 | # 2 | # This script is mostly based on the previous work done by https://dougallj.wordpress.com/2018/06/04/writing-a-hex-rays-plugin-vmx-intrinsics/ 3 | # Now it's a python plugin and some renaming was necessary as MICROCODE API has changed (cf ida_hexrays.py) 4 | # Use of mop_a/mop_addr_t for taking the address of registers for vmread/vmclear/vmptrld/vmptrst/vmxon 5 | # 6 | 7 | import idaapi 8 | import ida_hexrays 9 | import ida_allins 10 | import ida_idp 11 | 12 | # Identify 'tt' register 13 | # import ida_hexrays 14 | # for i in range(0, 0xFF): 15 | # print("0x{0:02X} => {1}".format(i, ida_hexrays.get_mreg_name(i, 1))) 16 | IDA_TT = 0xC0 17 | 18 | def is_gte_ida74(): 19 | major, minor = map(int, idaapi.get_kernel_version().split(".")) 20 | return (major == 7 and minor >= 4) 21 | 22 | class MicroInstruction(ida_hexrays.minsn_t): 23 | 24 | def __init__(self, opcode, ea): 25 | ida_hexrays.minsn_t.__init__(self, ea) 26 | self.opcode = opcode 27 | self.l.zero() 28 | self.r.zero() 29 | self.d.zero() 30 | 31 | class CallBuilder(): 32 | 33 | def __init__(self, cdg, name, return_type=idaapi.tinfo_t(idaapi.BT_VOID)): 34 | self.emitted = False 35 | self.cdg = cdg 36 | self.callinfo = ida_hexrays.mcallinfo_t() 37 | self.callinfo.callee = idaapi.BADADDR 38 | self.callinfo.solid_args = 0x00 39 | self.callinfo.call_spd = 0x00 40 | self.callinfo.stkargs_top = 0x00 41 | self.callinfo.cc = idaapi.CM_CC_FASTCALL 42 | self.callinfo.return_type = return_type 43 | self.callinfo.flags = idaapi.FCI_SPLOK | idaapi.FCI_FINAL | idaapi.FCI_PROP 44 | self.callinfo.role = idaapi.ROLE_UNK 45 | 46 | glbhigh_off = cdg.mba.get_stack_region().off + cdg.mba.get_stack_region().size 47 | # what memory is visible to the call : GLBLOW - GLBHIGH 48 | self.callinfo.visible_memory.add(ida_hexrays.ivl_t(0x00, 0x100000)) 49 | self.callinfo.visible_memory.add(ida_hexrays.ivl_t(glbhigh_off, 0xFFFFFFFFFFFFFFFF - glbhigh_off)) 50 | # spoiled locations : GLBLOW - GLBHIGH 51 | self.callinfo.spoiled.mem.add(ida_hexrays.ivl_t(0x00, 0x100000)) 52 | self.callinfo.spoiled.mem.add(ida_hexrays.ivl_t(glbhigh_off, 0xFFFFFFFFFFFFFFFF - glbhigh_off)) 53 | 54 | self.callins = MicroInstruction(ida_hexrays.m_call, self.cdg.insn.ea) 55 | self.callins.l.make_helper(name) 56 | self.callins.d.t = ida_hexrays.mop_f 57 | self.callins.d.size = 0x00 58 | self.callins.d.f = self.callinfo 59 | 60 | if (return_type.is_void()): 61 | self.ins = self.callins 62 | else: 63 | self.callins.d.size = return_type.get_size() 64 | self.ins = MicroInstruction(ida_hexrays.m_mov, self.cdg.insn.ea) 65 | self.ins.l.t = ida_hexrays.mop_d 66 | self.ins.l.d = self.callins 67 | self.ins.l.size = self.callins.d.size 68 | self.ins.d.t = ida_hexrays.mop_r 69 | self.ins.d.r = 0x00 70 | self.ins.d.size = self.callins.d.size 71 | 72 | def add_register_argument(self, t, operand): 73 | ca = ida_hexrays.mcallarg_t() 74 | ca.t = idaapi.mop_r 75 | ca.r = operand 76 | ca.type = t 77 | ca.size = t.get_size() 78 | self.callinfo.args.push_back(ca) 79 | self.callinfo.solid_args += 1 80 | 81 | def add_register_address_argument(self, t, operand): 82 | addr_t = ida_hexrays.mop_addr_t() 83 | addr_t.t = idaapi.mop_r 84 | addr_t.r = operand 85 | addr_t.type = t 86 | addr_t.size = t.get_size() 87 | addr_t.insize = t.get_size() 88 | addr_t.outsize = t.get_size() 89 | 90 | ca = ida_hexrays.mcallarg_t() 91 | ca.t = idaapi.mop_a 92 | ca.a = addr_t 93 | t.create_ptr(t) 94 | ca.type = t 95 | ca.size = t.get_size() 96 | self.callinfo.args.push_back(ca) 97 | self.callinfo.solid_args += 1 98 | 99 | def set_return_register(self, reg): 100 | self.ins.d.r = reg 101 | 102 | def emit(self): 103 | if self.emitted == False: 104 | self.cdg.mb.insert_into_block(self.ins , self.cdg.mb.tail) 105 | self.emitted = True 106 | 107 | def emit_und_reg(self, reg, size): 108 | ins = MicroInstruction(ida_hexrays.m_und, self.cdg.insn.ea) 109 | ins.d.t = idaapi.mop_r 110 | ins.d.r = reg 111 | ins.d.size = size 112 | self.cdg.mb.insert_into_block(ins, self.cdg.mb.tail) 113 | 114 | def emit_reg_equals_number(self, result_reg, reg, number, size): 115 | ins = MicroInstruction(ida_hexrays.m_setz, self.cdg.insn.ea) 116 | ins.l.t = idaapi.mop_r 117 | ins.l.r = reg 118 | ins.l.size = size 119 | ins.r.make_number(number, size) 120 | ins.d.t = idaapi.mop_r 121 | ins.d.r = result_reg 122 | ins.d.size = 1; 123 | self.cdg.mb.insert_into_block(ins, self.cdg.mb.tail) 124 | 125 | class VMXFilter(ida_hexrays.microcode_filter_t): 126 | 127 | def __init__(self, itype): 128 | ida_hexrays.microcode_filter_t.__init__(self) 129 | self.itype = itype 130 | self.installed = False 131 | self.toggle_install() 132 | 133 | def match(self, cdg): 134 | return cdg.insn.itype == self.itype 135 | 136 | def install(self): 137 | ida_hexrays.install_microcode_filter(self, True); 138 | self.installed = True 139 | 140 | def uninstall(self): 141 | ida_hexrays.install_microcode_filter(self, False); 142 | self.installed = False 143 | 144 | def toggle_install(self): 145 | if self.installed: 146 | self.uninstall() 147 | else: 148 | self.install() 149 | 150 | class VMXVmwrite(VMXFilter): 151 | 152 | def __init__(self): 153 | VMXFilter.__init__(self, ida_allins.NN_vmwrite) 154 | self.name = "__vmx_vmwrite" 155 | 156 | def apply(self, cdg): 157 | builder = CallBuilder(cdg, self.name, idaapi.tinfo_t(idaapi.BT_INT8 | idaapi.BTMT_UNSIGNED)) 158 | builder.add_register_argument(idaapi.tinfo_t(idaapi.BT_INT64 | idaapi.BTMT_UNSIGNED), cdg.load_operand(0)) 159 | builder.add_register_argument(idaapi.tinfo_t(idaapi.BT_INT64 | idaapi.BTMT_UNSIGNED), cdg.load_operand(1)) 160 | builder.emit() 161 | builder.set_return_register(IDA_TT) 162 | builder.emit_reg_equals_number(ida_hexrays.mr_zf, IDA_TT, 1, 1) 163 | builder.emit_reg_equals_number(ida_hexrays.mr_cf, IDA_TT, 2, 1) 164 | return idaapi.MERR_OK 165 | 166 | class VMXVmread(VMXFilter): 167 | 168 | def __init__(self): 169 | VMXFilter.__init__(self, ida_allins.NN_vmread) 170 | self.name = "__vmx_vmread" 171 | 172 | def apply(self, cdg): 173 | builder = CallBuilder(cdg, self.name, idaapi.tinfo_t(idaapi.BT_INT8 | idaapi.BTMT_UNSIGNED)) 174 | builder.add_register_argument(idaapi.tinfo_t(idaapi.BT_INT64 | idaapi.BTMT_UNSIGNED), cdg.load_operand(1)) 175 | builder.add_register_address_argument(idaapi.tinfo_t(idaapi.BT_INT64 | idaapi.BTMT_UNSIGNED), cdg.load_operand(0)) 176 | builder.emit() 177 | builder.set_return_register(IDA_TT) 178 | builder.emit_und_reg(ida_hexrays.mr_zf, 1) 179 | builder.emit_und_reg(ida_hexrays.mr_cf, 1) 180 | return idaapi.MERR_OK 181 | 182 | class VMXVoidReturn(VMXFilter): 183 | 184 | def __init__(self, itype, name): 185 | VMXFilter.__init__(self, itype) 186 | self.name = name 187 | 188 | def apply(self, cdg): 189 | builder = CallBuilder(cdg, self.name, idaapi.tinfo_t(idaapi.BT_INT8 | idaapi.BTMT_UNSIGNED)) 190 | builder.emit() 191 | builder.set_return_register(IDA_TT) 192 | builder.emit_reg_equals_number(ida_hexrays.mr_zf, IDA_TT, 1, 1) 193 | builder.emit_reg_equals_number(ida_hexrays.mr_cf, IDA_TT, 2, 1) 194 | return idaapi.MERR_OK 195 | 196 | class VMXVoid(VMXFilter): 197 | 198 | def __init__(self, itype, name): 199 | VMXFilter.__init__(self, itype) 200 | self.name = name 201 | 202 | def apply(self, cdg): 203 | builder = CallBuilder(cdg, self.name) 204 | builder.emit() 205 | builder.emit_und_reg(ida_hexrays.mr_zf, 1) 206 | builder.emit_und_reg(ida_hexrays.mr_cf, 1) 207 | return idaapi.MERR_OK 208 | 209 | class VMXVMCSReturn(VMXFilter): 210 | 211 | def __init__(self, itype, name): 212 | VMXFilter.__init__(self, itype) 213 | self.name = name 214 | 215 | def apply(self, cdg): 216 | builder = CallBuilder(cdg, self.name, idaapi.tinfo_t(idaapi.BT_INT8 | idaapi.BTMT_UNSIGNED)) 217 | builder.add_register_address_argument(idaapi.tinfo_t(idaapi.BT_INT64 | idaapi.BTMT_UNSIGNED), cdg.load_operand(0)) 218 | builder.emit() 219 | builder.set_return_register(IDA_TT) 220 | builder.emit_reg_equals_number(ida_hexrays.mr_zf, IDA_TT, 1, 1) 221 | builder.emit_reg_equals_number(ida_hexrays.mr_cf, IDA_TT, 2, 1) 222 | return idaapi.MERR_OK 223 | 224 | class VMXIntrinsicIDAPlugin(idaapi.plugin_t): 225 | flags = idaapi.PLUGIN_HIDE 226 | comment = "VMX intrinsics plugin for Hex-Rays decompiler" 227 | help = "" 228 | wanted_name = "VMXIntrinsic" 229 | wanted_hotkey = "" 230 | 231 | def __init__(self): 232 | self.filters = [] 233 | 234 | def init(self): 235 | if is_gte_ida74() == False: 236 | print("[-] {0} : for ida >= 7.4 only".format(self.wanted_name)) 237 | return idaapi.PLUGIN_SKIP 238 | if idaapi.ph.id != idaapi.PLFM_386 and not idaapi.get_inf_structure().is_64bit(): 239 | print("[-] {0} : for x64 only".format(self.wanted_name)) 240 | return idaapi.PLUGIN_SKIP 241 | if not ida_hexrays.init_hexrays_plugin(): 242 | print("[-] {0} : no decompiler available, skipping".format(self.wanted_name)) 243 | return idaapi.PLUGIN_SKIP 244 | self.add_filter(VMXVmwrite()) 245 | self.add_filter(VMXVmread()) 246 | self.add_filter(VMXVoidReturn(ida_allins.NN_vmlaunch, "__vmx_vmlaunch")) 247 | self.add_filter(VMXVoidReturn(ida_allins.NN_vmresume, "__vmx_vmresume")) 248 | self.add_filter(VMXVoid(ida_allins.NN_vmxoff, "__vmx_off")) 249 | self.add_filter(VMXVMCSReturn(ida_allins.NN_vmclear, "__vmx_vmclear")) 250 | self.add_filter(VMXVMCSReturn(ida_allins.NN_vmptrld, "__vmx_vmptrld")) 251 | self.add_filter(VMXVMCSReturn(ida_allins.NN_vmptrst, "__vmx_vmptrst")) 252 | self.add_filter(VMXVMCSReturn(ida_allins.NN_vmxon, "__vmx_on")) 253 | 254 | return idaapi.PLUGIN_KEEP 255 | 256 | def run(self, arg): 257 | idaapi.warning("[-] {0} cannot be run as a script in IDA.".format(self.wanted_name)) 258 | 259 | def add_filter(self, f): 260 | self.filters.append(f) 261 | 262 | def term(self): 263 | for f in self.filters: 264 | f.toggle_install() 265 | self.filters = [] 266 | 267 | def PLUGIN_ENTRY(): 268 | return VMXIntrinsicIDAPlugin() 269 | --------------------------------------------------------------------------------