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