1# By Dang Hoang Vu <danghvu@gmail.com>, 2014 2 3cimport pyx.ccapstone as cc 4import capstone, ctypes 5from . import arm, x86, mips, ppc, arm64, sparc, systemz, xcore, tms320c64x, CsError 6 7_diet = cc.cs_support(capstone.CS_SUPPORT_DIET) 8 9 10class CsDetail(object): 11 12 def __init__(self, arch, raw_detail = None): 13 if not raw_detail: 14 return 15 detail = ctypes.cast(raw_detail, ctypes.POINTER(capstone._cs_detail)).contents 16 17 self.regs_read = detail.regs_read 18 self.regs_read_count = detail.regs_read_count 19 self.regs_write = detail.regs_write 20 self.regs_write_count = detail.regs_write_count 21 self.groups = detail.groups 22 self.groups_count = detail.groups_count 23 24 if arch == capstone.CS_ARCH_ARM: 25 (self.usermode, self.vector_size, self.vector_data, self.cps_mode, self.cps_flag, \ 26 self.cc, self.update_flags, self.writeback, self.mem_barrier, self.operands) = \ 27 arm.get_arch_info(detail.arch.arm) 28 elif arch == capstone.CS_ARCH_ARM64: 29 (self.cc, self.update_flags, self.writeback, self.operands) = \ 30 arm64.get_arch_info(detail.arch.arm64) 31 elif arch == capstone.CS_ARCH_X86: 32 (self.prefix, self.opcode, self.rex, self.addr_size, \ 33 self.modrm, self.sib, self.disp, \ 34 self.sib_index, self.sib_scale, self.sib_base, \ 35 self.xop_cc, self.sse_cc, self.avx_cc, self.avx_sae, self.avx_rm, \ 36 self.eflags, self.operands) = x86.get_arch_info(detail.arch.x86) 37 elif arch == capstone.CS_ARCH_MIPS: 38 self.operands = mips.get_arch_info(detail.arch.mips) 39 elif arch == capstone.CS_ARCH_PPC: 40 (self.bc, self.bh, self.update_cr0, self.operands) = \ 41 ppc.get_arch_info(detail.arch.ppc) 42 elif arch == capstone.CS_ARCH_SPARC: 43 (self.cc, self.hint, self.operands) = sparc.get_arch_info(detail.arch.sparc) 44 elif arch == capstone.CS_ARCH_SYSZ: 45 (self.cc, self.operands) = systemz.get_arch_info(detail.arch.sysz) 46 elif arch == capstone.CS_ARCH_XCORE: 47 self.operands = xcore.get_arch_info(detail.arch.xcore) 48 elif arch == capstone.CS_ARCH_TMS320C64X: 49 (self.condition, self.funit, self.parallel, self.operands) = tms320c64x.get_arch_info(self._detail.arch.tms320c64x) 50 51 52cdef class CsInsn(object): 53 54 cdef cc.cs_insn _raw 55 cdef cc.csh _csh 56 cdef object _detail 57 58 def __cinit__(self, _detail): 59 self._detail = _detail 60 61 # defer to CsDetail structure for everything else. 62 def __getattr__(self, name): 63 _detail = self._detail 64 if not _detail: 65 raise CsError(capstone.CS_ERR_DETAIL) 66 return getattr(_detail, name) 67 68 # return instruction's operands. 69 @property 70 def operands(self): 71 return self._detail.operands 72 73 # return instruction's ID. 74 @property 75 def id(self): 76 return self._raw.id 77 78 # return instruction's address. 79 @property 80 def address(self): 81 return self._raw.address 82 83 # return instruction's size. 84 @property 85 def size(self): 86 return self._raw.size 87 88 # return instruction's machine bytes (which should have @size bytes). 89 @property 90 def bytes(self): 91 return bytearray(self._raw.bytes[:self._raw.size]) 92 93 # return instruction's mnemonic. 94 @property 95 def mnemonic(self): 96 if _diet: 97 # Diet engine cannot provide @mnemonic & @op_str 98 raise CsError(capstone.CS_ERR_DIET) 99 100 return self._raw.mnemonic 101 102 # return instruction's operands (in string). 103 @property 104 def op_str(self): 105 if _diet: 106 # Diet engine cannot provide @mnemonic & @op_str 107 raise CsError(capstone.CS_ERR_DIET) 108 109 return self._raw.op_str 110 111 # return list of all implicit registers being read. 112 @property 113 def regs_read(self): 114 if self._raw.id == 0: 115 raise CsError(capstone.CS_ERR_SKIPDATA) 116 117 if _diet: 118 # Diet engine cannot provide @regs_read 119 raise CsError(capstone.CS_ERR_DIET) 120 121 if self._detail: 122 detail = self._detail 123 return detail.regs_read[:detail.regs_read_count] 124 125 raise CsError(capstone.CS_ERR_DETAIL) 126 127 # return list of all implicit registers being modified 128 @property 129 def regs_write(self): 130 if self._raw.id == 0: 131 raise CsError(capstone.CS_ERR_SKIPDATA) 132 133 if _diet: 134 # Diet engine cannot provide @regs_write 135 raise CsError(capstone.CS_ERR_DIET) 136 137 if self._detail: 138 detail = self._detail 139 return detail.regs_write[:detail.regs_write_count] 140 141 raise CsError(capstone.CS_ERR_DETAIL) 142 143 # return list of semantic groups this instruction belongs to. 144 @property 145 def groups(self): 146 if self._raw.id == 0: 147 raise CsError(capstone.CS_ERR_SKIPDATA) 148 149 if _diet: 150 # Diet engine cannot provide @groups 151 raise CsError(capstone.CS_ERR_DIET) 152 153 if self._detail: 154 detail = self._detail 155 return detail.groups[:detail.groups_count] 156 157 raise CsError(capstone.CS_ERR_DETAIL) 158 159 # get the last error code 160 def errno(self): 161 return cc.cs_errno(self._csh) 162 163 # get the register name, given the register ID 164 def reg_name(self, reg_id): 165 if self._raw.id == 0: 166 raise CsError(capstone.CS_ERR_SKIPDATA) 167 168 if _diet: 169 # Diet engine cannot provide register's name 170 raise CsError(capstone.CS_ERR_DIET) 171 172 return cc.cs_reg_name(self._csh, reg_id) 173 174 # get the instruction string 175 def insn_name(self): 176 if _diet: 177 # Diet engine cannot provide instruction's name 178 raise CsError(capstone.CS_ERR_DIET) 179 180 return cc.cs_insn_name(self._csh, self.id) 181 182 # get the group string 183 def group_name(self, group_id): 184 if _diet: 185 # Diet engine cannot provide group's name 186 raise CsError(capstone.CS_ERR_DIET) 187 188 return cc.cs_group_name(self._csh, group_id) 189 190 # verify if this insn belong to group with id as @group_id 191 def group(self, group_id): 192 if self._raw.id == 0: 193 raise CsError(capstone.CS_ERR_SKIPDATA) 194 195 if _diet: 196 # Diet engine cannot provide @groups 197 raise CsError(capstone.CS_ERR_DIET) 198 199 return group_id in self.groups 200 201 # verify if this instruction implicitly read register @reg_id 202 def reg_read(self, reg_id): 203 if self._raw.id == 0: 204 raise CsError(capstone.CS_ERR_SKIPDATA) 205 206 if _diet: 207 # Diet engine cannot provide @regs_read 208 raise CsError(capstone.CS_ERR_DIET) 209 210 return reg_id in self.regs_read 211 212 # verify if this instruction implicitly modified register @reg_id 213 def reg_write(self, reg_id): 214 if self._raw.id == 0: 215 raise CsError(capstone.CS_ERR_SKIPDATA) 216 217 if _diet: 218 # Diet engine cannot provide @regs_write 219 raise CsError(capstone.CS_ERR_DIET) 220 221 return reg_id in self.regs_write 222 223 # return number of operands having same operand type @op_type 224 def op_count(self, op_type): 225 if self._raw.id == 0: 226 raise CsError(capstone.CS_ERR_SKIPDATA) 227 228 c = 0 229 for op in self._detail.operands: 230 if op.type == op_type: 231 c += 1 232 return c 233 234 # get the operand at position @position of all operands having the same type @op_type 235 def op_find(self, op_type, position): 236 if self._raw.id == 0: 237 raise CsError(capstone.CS_ERR_SKIPDATA) 238 239 c = 0 240 for op in self._detail.operands: 241 if op.type == op_type: 242 c += 1 243 if c == position: 244 return op 245 246 # Return (list-of-registers-read, list-of-registers-modified) by this instructions. 247 # This includes all the implicit & explicit registers. 248 def regs_access(self): 249 if self._raw.id == 0: 250 raise CsError(capstone.CS_ERR_SKIPDATA) 251 252 cdef cc.uint16_t regs_read[64], regs_write[64] 253 cdef cc.uint8_t read_count, write_count 254 255 status = cc.cs_regs_access(self._cs.csh, &self._raw, regs_read, &read_count, regs_write, &write_count) 256 if status != capstone.CS_ERR_OK: 257 raise CsError(status) 258 259 r1 = [] 260 for i from 0 <= i < read_count: r1.append(regs_read[i]) 261 262 w1 = [] 263 for i from 0 <= i < write_count: w1.append(regs_write[i]) 264 265 return (r1, w1) 266 267 268cdef class Cs(object): 269 270 cdef cc.csh _csh 271 cdef object _cs 272 273 def __cinit__(self, _cs): 274 cdef version = cc.cs_version(NULL, NULL) 275 if (version != (capstone.CS_API_MAJOR << 8) + capstone.CS_API_MINOR): 276 # our binding version is different from the core's API version 277 raise CsError(capstone.CS_ERR_VERSION) 278 279 self._csh = <cc.csh> _cs.csh.value 280 self._cs = _cs 281 282 283 # destructor to be called automatically when object is destroyed. 284 def __dealloc__(self): 285 if self._csh: 286 status = cc.cs_close(&self._csh) 287 if status != capstone.CS_ERR_OK: 288 raise CsError(status) 289 290 291 # Disassemble binary & return disassembled instructions in CsInsn objects 292 def disasm(self, code, addr, count=0): 293 cdef cc.cs_insn *allinsn 294 295 cdef res = cc.cs_disasm(self._csh, code, len(code), addr, count, &allinsn) 296 detail = self._cs.detail 297 arch = self._cs.arch 298 299 try: 300 for i from 0 <= i < res: 301 if detail: 302 dummy = CsInsn(CsDetail(arch, <size_t>allinsn[i].detail)) 303 else: 304 dummy = CsInsn(None) 305 306 dummy._raw = allinsn[i] 307 dummy._csh = self._csh 308 yield dummy 309 finally: 310 cc.cs_free(allinsn, res) 311 312 313 # Light function to disassemble binary. This is about 20% faster than disasm() because 314 # unlike disasm(), disasm_lite() only return tuples of (address, size, mnemonic, op_str), 315 # rather than CsInsn objects. 316 def disasm_lite(self, code, addr, count=0): 317 # TODO: dont need detail, so we might turn off detail, then turn on again when done 318 cdef cc.cs_insn *allinsn 319 320 if _diet: 321 # Diet engine cannot provide @mnemonic & @op_str 322 raise CsError(capstone.CS_ERR_DIET) 323 324 cdef res = cc.cs_disasm(self._csh, code, len(code), addr, count, &allinsn) 325 326 try: 327 for i from 0 <= i < res: 328 insn = allinsn[i] 329 yield (insn.address, insn.size, insn.mnemonic, insn.op_str) 330 finally: 331 cc.cs_free(allinsn, res) 332 333 334# print out debugging info 335def debug(): 336 if cc.cs_support(capstone.CS_SUPPORT_DIET): 337 diet = "diet" 338 else: 339 diet = "standard" 340 341 archs = { "arm": capstone.CS_ARCH_ARM, "arm64": capstone.CS_ARCH_ARM64, \ 342 "mips": capstone.CS_ARCH_MIPS, "ppc": capstone.CS_ARCH_PPC, \ 343 "sparc": capstone.CS_ARCH_SPARC, "sysz": capstone.CS_ARCH_SYSZ, \ 344 "xcore": capstone.CS_ARCH_XCORE, "tms320c64x": capstone.CS_ARCH_TMS320C64X } 345 346 all_archs = "" 347 keys = archs.keys() 348 keys.sort() 349 for k in keys: 350 if cc.cs_support(archs[k]): 351 all_archs += "-%s" %k 352 353 if cc.cs_support(capstone.CS_ARCH_X86): 354 all_archs += "-x86" 355 if cc.cs_support(capstone.CS_SUPPORT_X86_REDUCE): 356 all_archs += "_reduce" 357 358 (major, minor, _combined) = capstone.cs_version() 359 360 return "Cython-%s%s-c%u.%u-b%u.%u" %(diet, all_archs, major, minor, capstone.CS_API_MAJOR, capstone.CS_API_MINOR) 361