Home | History | Annotate | Download | only in pyx
      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