Home | History | Annotate | Download | only in capstone
      1 # Capstone Python bindings, by Nguyen Anh Quynnh <aquynh (at] gmail.com>
      2 import sys
      3 from platform import system
      4 _python2 = sys.version_info[0] < 3
      5 if _python2:
      6     range = xrange
      7 
      8 __all__ = [
      9     'Cs',
     10     'CsInsn',
     11 
     12     'cs_disasm_quick',
     13     'cs_disasm_lite',
     14     'cs_version',
     15     'cs_support',
     16     'version_bind',
     17     'debug',
     18 
     19     'CS_API_MAJOR',
     20     'CS_API_MINOR',
     21 
     22     'CS_VERSION_MAJOR',
     23     'CS_VERSION_MINOR',
     24     'CS_VERSION_EXTRA',
     25 
     26     'CS_ARCH_ARM',
     27     'CS_ARCH_ARM64',
     28     'CS_ARCH_MIPS',
     29     'CS_ARCH_X86',
     30     'CS_ARCH_PPC',
     31     'CS_ARCH_SPARC',
     32     'CS_ARCH_SYSZ',
     33     'CS_ARCH_XCORE',
     34     'CS_ARCH_ALL',
     35 
     36     'CS_MODE_LITTLE_ENDIAN',
     37     'CS_MODE_BIG_ENDIAN',
     38     'CS_MODE_16',
     39     'CS_MODE_32',
     40     'CS_MODE_64',
     41     'CS_MODE_ARM',
     42     'CS_MODE_THUMB',
     43     'CS_MODE_MCLASS',
     44     'CS_MODE_MICRO',
     45     'CS_MODE_MIPS3',
     46     'CS_MODE_MIPS32R6',
     47     'CS_MODE_MIPSGP64',
     48     'CS_MODE_V8',
     49     'CS_MODE_V9',
     50     'CS_MODE_MIPS32',
     51     'CS_MODE_MIPS64',
     52 
     53     'CS_OPT_SYNTAX',
     54     'CS_OPT_SYNTAX_DEFAULT',
     55     'CS_OPT_SYNTAX_INTEL',
     56     'CS_OPT_SYNTAX_ATT',
     57     'CS_OPT_SYNTAX_NOREGNAME',
     58 
     59     'CS_OPT_DETAIL',
     60     'CS_OPT_MODE',
     61     'CS_OPT_ON',
     62     'CS_OPT_OFF',
     63 
     64     'CS_ERR_OK',
     65     'CS_ERR_MEM',
     66     'CS_ERR_ARCH',
     67     'CS_ERR_HANDLE',
     68     'CS_ERR_CSH',
     69     'CS_ERR_MODE',
     70     'CS_ERR_OPTION',
     71     'CS_ERR_DETAIL',
     72     'CS_ERR_VERSION',
     73     'CS_ERR_MEMSETUP',
     74     'CS_ERR_DIET',
     75     'CS_ERR_SKIPDATA',
     76     'CS_ERR_X86_ATT',
     77     'CS_ERR_X86_INTEL',
     78 
     79     'CS_SUPPORT_DIET',
     80     'CS_SUPPORT_X86_REDUCE',
     81     'CS_SKIPDATA_CALLBACK',
     82 
     83     'CS_OP_INVALID',
     84     'CS_OP_REG',
     85     'CS_OP_IMM',
     86     'CS_OP_MEM',
     87     'CS_OP_FP',
     88 
     89     'CS_GRP_INVALID',
     90     'CS_GRP_JUMP',
     91     'CS_GRP_CALL',
     92     'CS_GRP_RET',
     93     'CS_GRP_INT',
     94     'CS_GRP_IRET',
     95 
     96     'CsError',
     97 
     98     '__version__',
     99 ]
    100 
    101 # Capstone C interface
    102 
    103 # API version
    104 CS_API_MAJOR = 3
    105 CS_API_MINOR = 0
    106 
    107 # Package version
    108 CS_VERSION_MAJOR = CS_API_MAJOR
    109 CS_VERSION_MINOR = CS_API_MINOR
    110 CS_VERSION_EXTRA = 5
    111 
    112 __version__ = "%u.%u.%u" %(CS_VERSION_MAJOR, CS_VERSION_MINOR, CS_VERSION_EXTRA)
    113 
    114 # architectures
    115 CS_ARCH_ARM = 0
    116 CS_ARCH_ARM64 = 1
    117 CS_ARCH_MIPS = 2
    118 CS_ARCH_X86 = 3
    119 CS_ARCH_PPC = 4
    120 CS_ARCH_SPARC = 5
    121 CS_ARCH_SYSZ = 6
    122 CS_ARCH_XCORE = 7
    123 CS_ARCH_MAX = 8
    124 CS_ARCH_ALL = 0xFFFF
    125 
    126 # disasm mode
    127 CS_MODE_LITTLE_ENDIAN = 0      # little-endian mode (default mode)
    128 CS_MODE_ARM = 0                # ARM mode
    129 CS_MODE_16 = (1 << 1)          # 16-bit mode (for X86)
    130 CS_MODE_32 = (1 << 2)          # 32-bit mode (for X86)
    131 CS_MODE_64 = (1 << 3)          # 64-bit mode (for X86, PPC)
    132 CS_MODE_THUMB = (1 << 4)       # ARM's Thumb mode, including Thumb-2
    133 CS_MODE_MCLASS = (1 << 5)      # ARM's Cortex-M series
    134 CS_MODE_V8 = (1 << 6)          # ARMv8 A32 encodings for ARM
    135 CS_MODE_MICRO = (1 << 4)       # MicroMips mode (MIPS architecture)
    136 CS_MODE_MIPS3 = (1 << 5)       # Mips III ISA
    137 CS_MODE_MIPS32R6 = (1 << 6)    # Mips32r6 ISA
    138 CS_MODE_MIPSGP64 = (1 << 7)    # General Purpose Registers are 64-bit wide (MIPS arch)
    139 CS_MODE_V9 = (1 << 4)          # Sparc V9 mode (for Sparc)
    140 CS_MODE_BIG_ENDIAN = (1 << 31) # big-endian mode
    141 CS_MODE_MIPS32 = CS_MODE_32    # Mips32 ISA
    142 CS_MODE_MIPS64 = CS_MODE_64    # Mips64 ISA
    143 
    144 # Capstone option type
    145 CS_OPT_SYNTAX = 1    # Intel X86 asm syntax (CS_ARCH_X86 arch)
    146 CS_OPT_DETAIL = 2    # Break down instruction structure into details
    147 CS_OPT_MODE = 3      # Change engine's mode at run-time
    148 CS_OPT_MEM = 4       # Change engine's mode at run-time
    149 CS_OPT_SKIPDATA = 5  # Skip data when disassembling
    150 CS_OPT_SKIPDATA_SETUP = 6      # Setup user-defined function for SKIPDATA option
    151 
    152 # Capstone option value
    153 CS_OPT_OFF = 0             # Turn OFF an option - default option of CS_OPT_DETAIL
    154 CS_OPT_ON = 3              # Turn ON an option (CS_OPT_DETAIL)
    155 
    156 # Common instruction operand types - to be consistent across all architectures.
    157 CS_OP_INVALID = 0
    158 CS_OP_REG = 1
    159 CS_OP_IMM = 2
    160 CS_OP_MEM = 3
    161 CS_OP_FP  = 4
    162 
    163 # Common instruction groups - to be consistent across all architectures.
    164 CS_GRP_INVALID = 0  # uninitialized/invalid group.
    165 CS_GRP_JUMP    = 1  # all jump instructions (conditional+direct+indirect jumps)
    166 CS_GRP_CALL    = 2  # all call instructions
    167 CS_GRP_RET     = 3  # all return instructions
    168 CS_GRP_INT     = 4  # all interrupt instructions (int+syscall)
    169 CS_GRP_IRET    = 5  # all interrupt return instructions
    170 
    171 # Capstone syntax value
    172 CS_OPT_SYNTAX_DEFAULT = 0    # Default assembly syntax of all platforms (CS_OPT_SYNTAX)
    173 CS_OPT_SYNTAX_INTEL = 1    # Intel X86 asm syntax - default syntax on X86 (CS_OPT_SYNTAX, CS_ARCH_X86)
    174 CS_OPT_SYNTAX_ATT = 2      # ATT asm syntax (CS_OPT_SYNTAX, CS_ARCH_X86)
    175 CS_OPT_SYNTAX_NOREGNAME = 3   # Asm syntax prints register name with only number - (CS_OPT_SYNTAX, CS_ARCH_PPC, CS_ARCH_ARM)
    176 
    177 # Capstone error type
    178 CS_ERR_OK = 0      # No error: everything was fine
    179 CS_ERR_MEM = 1     # Out-Of-Memory error: cs_open(), cs_disasm()
    180 CS_ERR_ARCH = 2    # Unsupported architecture: cs_open()
    181 CS_ERR_HANDLE = 3  # Invalid handle: cs_op_count(), cs_op_index()
    182 CS_ERR_CSH = 4     # Invalid csh argument: cs_close(), cs_errno(), cs_option()
    183 CS_ERR_MODE = 5    # Invalid/unsupported mode: cs_open()
    184 CS_ERR_OPTION = 6  # Invalid/unsupported option: cs_option()
    185 CS_ERR_DETAIL = 7  # Invalid/unsupported option: cs_option()
    186 CS_ERR_MEMSETUP = 8
    187 CS_ERR_VERSION = 9 # Unsupported version (bindings)
    188 CS_ERR_DIET = 10   # Information irrelevant in diet engine
    189 CS_ERR_SKIPDATA = 11 # Access irrelevant data for "data" instruction in SKIPDATA mode
    190 CS_ERR_X86_ATT = 12 # X86 AT&T syntax is unsupported (opt-out at compile time)
    191 CS_ERR_X86_INTEL = 13 # X86 Intel syntax is unsupported (opt-out at compile time)
    192 
    193 # query id for cs_support()
    194 CS_SUPPORT_DIET = CS_ARCH_ALL + 1
    195 CS_SUPPORT_X86_REDUCE = CS_ARCH_ALL+2
    196 
    197 
    198 import ctypes, ctypes.util
    199 from os.path import split, join, dirname
    200 import distutils.sysconfig
    201 import pkg_resources
    202 
    203 import inspect
    204 if not hasattr(sys.modules[__name__], '__file__'):
    205     __file__ = inspect.getfile(inspect.currentframe())
    206 
    207 if sys.platform == 'darwin':
    208     _lib = "libcapstone.dylib"
    209 elif sys.platform in ('win32', 'cygwin'):
    210     _lib = "capstone.dll"
    211 else:
    212     _lib = "libcapstone.so"
    213 
    214 _found = False
    215 
    216 def _load_lib(path):
    217     lib_file = join(path, _lib)
    218     #print("Trying to load %s" %lib_file)
    219     try:
    220         return ctypes.cdll.LoadLibrary(lib_file)
    221     except OSError:
    222         # if we're on linux, try again with .so.3 extension
    223         if lib_file.endswith('.so'):
    224             try:
    225                 return ctypes.cdll.LoadLibrary(lib_file + '.3')
    226             except OSError:
    227                 return None
    228         return None
    229 
    230 _cs = None
    231 
    232 # Loading attempts, in order
    233 # - pkg_resources can get us the path to the local libraries
    234 # - we can get the path to the local libraries by parsing our filename
    235 # - global load
    236 # - python's lib directory
    237 # - last-gasp attempt at some hardcoded paths on darwin and linux
    238 
    239 _path_list = [pkg_resources.resource_filename(__name__, 'lib'),
    240               join(split(__file__)[0], 'lib'),
    241               '',
    242               distutils.sysconfig.get_python_lib(),
    243               "/usr/local/lib/" if sys.platform == 'darwin' else '/usr/lib64']
    244 
    245 for _path in _path_list:
    246     _cs = _load_lib(_path)
    247     if _cs is not None: break
    248 else:
    249     raise ImportError("ERROR: fail to load the dynamic library.")
    250 
    251 
    252 # low-level structure for C code
    253 
    254 def copy_ctypes(src):
    255     """Returns a new ctypes object which is a bitwise copy of an existing one"""
    256     dst = type(src)()
    257     ctypes.memmove(ctypes.byref(dst), ctypes.byref(src), ctypes.sizeof(type(src)))
    258     return dst
    259 
    260 def copy_ctypes_list(src):
    261     return [copy_ctypes(n) for n in src]
    262 
    263 # Weird import placement because these modules are needed by the below code but need the above functions
    264 from . import arm, arm64, mips, ppc, sparc, systemz, x86, xcore
    265 
    266 class _cs_arch(ctypes.Union):
    267     _fields_ = (
    268         ('arm64', arm64.CsArm64),
    269         ('arm', arm.CsArm),
    270         ('mips', mips.CsMips),
    271         ('x86', x86.CsX86),
    272         ('ppc', ppc.CsPpc),
    273         ('sparc', sparc.CsSparc),
    274         ('sysz', systemz.CsSysz),
    275         ('xcore', xcore.CsXcore),
    276     )
    277 
    278 class _cs_detail(ctypes.Structure):
    279     _fields_ = (
    280         ('regs_read', ctypes.c_ubyte * 12),
    281         ('regs_read_count', ctypes.c_ubyte),
    282         ('regs_write', ctypes.c_ubyte * 20),
    283         ('regs_write_count', ctypes.c_ubyte),
    284         ('groups', ctypes.c_ubyte * 8),
    285         ('groups_count', ctypes.c_ubyte),
    286         ('arch', _cs_arch),
    287     )
    288 
    289 class _cs_insn(ctypes.Structure):
    290     _fields_ = (
    291         ('id', ctypes.c_uint),
    292         ('address', ctypes.c_uint64),
    293         ('size', ctypes.c_uint16),
    294         ('bytes', ctypes.c_ubyte * 16),
    295         ('mnemonic', ctypes.c_char * 32),
    296         ('op_str', ctypes.c_char * 160),
    297         ('detail', ctypes.POINTER(_cs_detail)),
    298     )
    299 
    300 # callback for SKIPDATA option
    301 CS_SKIPDATA_CALLBACK = ctypes.CFUNCTYPE(ctypes.c_size_t, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t, ctypes.c_size_t, ctypes.c_void_p)
    302 
    303 class _cs_opt_skipdata(ctypes.Structure):
    304     _fields_ = (
    305         ('mnemonic', ctypes.c_char_p),
    306         ('callback', CS_SKIPDATA_CALLBACK),
    307         ('user_data', ctypes.c_void_p),
    308     )
    309 
    310 
    311 # setup all the function prototype
    312 def _setup_prototype(lib, fname, restype, *argtypes):
    313     getattr(lib, fname).restype = restype
    314     getattr(lib, fname).argtypes = argtypes
    315 
    316 _setup_prototype(_cs, "cs_open", ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_size_t))
    317 _setup_prototype(_cs, "cs_disasm", ctypes.c_size_t, ctypes.c_size_t, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t, \
    318         ctypes.c_uint64, ctypes.c_size_t, ctypes.POINTER(ctypes.POINTER(_cs_insn)))
    319 _setup_prototype(_cs, "cs_free", None, ctypes.c_void_p, ctypes.c_size_t)
    320 _setup_prototype(_cs, "cs_close", ctypes.c_int, ctypes.POINTER(ctypes.c_size_t))
    321 _setup_prototype(_cs, "cs_reg_name", ctypes.c_char_p, ctypes.c_size_t, ctypes.c_uint)
    322 _setup_prototype(_cs, "cs_insn_name", ctypes.c_char_p, ctypes.c_size_t, ctypes.c_uint)
    323 _setup_prototype(_cs, "cs_group_name", ctypes.c_char_p, ctypes.c_size_t, ctypes.c_uint)
    324 _setup_prototype(_cs, "cs_op_count", ctypes.c_int, ctypes.c_size_t, ctypes.POINTER(_cs_insn), ctypes.c_uint)
    325 _setup_prototype(_cs, "cs_op_index", ctypes.c_int, ctypes.c_size_t, ctypes.POINTER(_cs_insn), ctypes.c_uint, ctypes.c_uint)
    326 _setup_prototype(_cs, "cs_errno", ctypes.c_int, ctypes.c_size_t)
    327 _setup_prototype(_cs, "cs_option", ctypes.c_int, ctypes.c_size_t, ctypes.c_int, ctypes.c_void_p)
    328 _setup_prototype(_cs, "cs_version", ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
    329 _setup_prototype(_cs, "cs_support", ctypes.c_bool, ctypes.c_int)
    330 _setup_prototype(_cs, "cs_strerror", ctypes.c_char_p, ctypes.c_int)
    331 
    332 
    333 # access to error code via @errno of CsError
    334 class CsError(Exception):
    335     def __init__(self, errno):
    336         self.errno = errno
    337 
    338     if _python2:
    339         def __str__(self):
    340             return _cs.cs_strerror(self.errno)
    341 
    342     else:
    343         def __str__(self):
    344             return _cs.cs_strerror(self.errno).decode()
    345 
    346 
    347 # return the core's version
    348 def cs_version():
    349     major = ctypes.c_int()
    350     minor = ctypes.c_int()
    351     combined = _cs.cs_version(ctypes.byref(major), ctypes.byref(minor))
    352     return (major.value, minor.value, combined)
    353 
    354 
    355 # return the binding's version
    356 def version_bind():
    357     return (CS_API_MAJOR, CS_API_MINOR, (CS_API_MAJOR << 8) + CS_API_MINOR)
    358 
    359 
    360 def cs_support(query):
    361     return _cs.cs_support(query)
    362 
    363 
    364 # dummy class resembling Cs class, just for cs_disasm_quick()
    365 # this class only need to be referenced to via 2 fields: @csh & @arch
    366 class _dummy_cs(object):
    367     def __init__(self, csh, arch):
    368         self.csh = csh
    369         self.arch = arch
    370         self._detail = False
    371 
    372 
    373 # Quick & dirty Python function to disasm raw binary code
    374 # This function return CsInsn objects
    375 # NOTE: you might want to use more efficient Cs class & its methods.
    376 def cs_disasm_quick(arch, mode, code, offset, count=0):
    377     # verify version compatibility with the core before doing anything
    378     (major, minor, _combined) = cs_version()
    379     if major != CS_API_MAJOR or minor != CS_API_MINOR:
    380         # our binding version is different from the core's API version
    381         raise CsError(CS_ERR_VERSION)
    382 
    383     csh = ctypes.c_size_t()
    384     status = _cs.cs_open(arch, mode, ctypes.byref(csh))
    385     if status != CS_ERR_OK:
    386         raise CsError(status)
    387 
    388     all_insn = ctypes.POINTER(_cs_insn)()
    389     res = _cs.cs_disasm(csh, code, len(code), offset, count, ctypes.byref(all_insn))
    390     if res > 0:
    391         try:
    392             for i in range(res):
    393                 yield CsInsn(_dummy_cs(csh, arch), all_insn[i])
    394         finally:
    395             _cs.cs_free(all_insn, res)
    396     else:
    397         status = _cs.cs_errno(csh)
    398         if status != CS_ERR_OK:
    399             raise CsError(status)
    400         return
    401         yield
    402 
    403     status = _cs.cs_close(ctypes.byref(csh))
    404     if status != CS_ERR_OK:
    405         raise CsError(status)
    406 
    407 
    408 # Another quick, but lighter function to disasm raw binary code.
    409 # This function is faster than cs_disasm_quick() around 20% because
    410 # cs_disasm_lite() only return tuples of (address, size, mnemonic, op_str),
    411 # rather than CsInsn objects.
    412 # NOTE: you might want to use more efficient Cs class & its methods.
    413 def cs_disasm_lite(arch, mode, code, offset, count=0):
    414     # verify version compatibility with the core before doing anything
    415     (major, minor, _combined) = cs_version()
    416     if major != CS_API_MAJOR or minor != CS_API_MINOR:
    417         # our binding version is different from the core's API version
    418         raise CsError(CS_ERR_VERSION)
    419 
    420     if cs_support(CS_SUPPORT_DIET):
    421         # Diet engine cannot provide @mnemonic & @op_str
    422         raise CsError(CS_ERR_DIET)
    423 
    424     csh = ctypes.c_size_t()
    425     status = _cs.cs_open(arch, mode, ctypes.byref(csh))
    426     if status != CS_ERR_OK:
    427         raise CsError(status)
    428 
    429     all_insn = ctypes.POINTER(_cs_insn)()
    430     res = _cs.cs_disasm(csh, code, len(code), offset, count, ctypes.byref(all_insn))
    431     if res > 0:
    432         try:
    433             for i in range(res):
    434                 insn = all_insn[i]
    435                 yield (insn.address, insn.size, insn.mnemonic.decode('ascii'), insn.op_str.decode('ascii'))
    436         finally:
    437             _cs.cs_free(all_insn, res)
    438     else:
    439         status = _cs.cs_errno(csh)
    440         if status != CS_ERR_OK:
    441             raise CsError(status)
    442         return
    443         yield
    444 
    445     status = _cs.cs_close(ctypes.byref(csh))
    446     if status != CS_ERR_OK:
    447         raise CsError(status)
    448 
    449 
    450 # Python-style class to disasm code
    451 class CsInsn(object):
    452     def __init__(self, cs, all_info):
    453         self._raw = copy_ctypes(all_info)
    454         self._cs = cs
    455         if self._cs._detail and self._raw.id != 0:
    456             # save detail
    457             self._detail = copy_ctypes(self._raw.detail.contents)
    458 
    459     # return instruction's ID.
    460     @property
    461     def id(self):
    462         return self._raw.id
    463 
    464     # return instruction's address.
    465     @property
    466     def address(self):
    467         return self._raw.address
    468 
    469     # return instruction's size.
    470     @property
    471     def size(self):
    472         return self._raw.size
    473 
    474     # return instruction's machine bytes (which should have @size bytes).
    475     @property
    476     def bytes(self):
    477         return bytearray(self._raw.bytes)[:self._raw.size]
    478 
    479     # return instruction's mnemonic.
    480     @property
    481     def mnemonic(self):
    482         if self._cs._diet:
    483             # Diet engine cannot provide @mnemonic.
    484             raise CsError(CS_ERR_DIET)
    485 
    486         return self._raw.mnemonic.decode('ascii')
    487 
    488     # return instruction's operands (in string).
    489     @property
    490     def op_str(self):
    491         if self._cs._diet:
    492             # Diet engine cannot provide @op_str.
    493             raise CsError(CS_ERR_DIET)
    494 
    495         return self._raw.op_str.decode('ascii')
    496 
    497     # return list of all implicit registers being read.
    498     @property
    499     def regs_read(self):
    500         if self._raw.id == 0:
    501             raise CsError(CS_ERR_SKIPDATA)
    502 
    503         if self._cs._diet:
    504             # Diet engine cannot provide @regs_read.
    505             raise CsError(CS_ERR_DIET)
    506 
    507         if self._cs._detail:
    508             return self._detail.regs_read[:self._detail.regs_read_count]
    509 
    510         raise CsError(CS_ERR_DETAIL)
    511 
    512     # return list of all implicit registers being modified
    513     @property
    514     def regs_write(self):
    515         if self._raw.id == 0:
    516             raise CsError(CS_ERR_SKIPDATA)
    517 
    518         if self._cs._diet:
    519             # Diet engine cannot provide @regs_write
    520             raise CsError(CS_ERR_DIET)
    521 
    522         if self._cs._detail:
    523             return self._detail.regs_write[:self._detail.regs_write_count]
    524 
    525         raise CsError(CS_ERR_DETAIL)
    526 
    527     # return list of semantic groups this instruction belongs to.
    528     @property
    529     def groups(self):
    530         if self._raw.id == 0:
    531             raise CsError(CS_ERR_SKIPDATA)
    532 
    533         if self._cs._diet:
    534             # Diet engine cannot provide @groups
    535             raise CsError(CS_ERR_DIET)
    536 
    537         if self._cs._detail:
    538             return self._detail.groups[:self._detail.groups_count]
    539 
    540         raise CsError(CS_ERR_DETAIL)
    541 
    542     def __gen_detail(self):
    543         arch = self._cs.arch
    544         if arch == CS_ARCH_ARM:
    545             (self.usermode, self.vector_size, self.vector_data, self.cps_mode, self.cps_flag, self.cc, self.update_flags, \
    546             self.writeback, self.mem_barrier, self.operands) = arm.get_arch_info(self._detail.arch.arm)
    547         elif arch == CS_ARCH_ARM64:
    548             (self.cc, self.update_flags, self.writeback, self.operands) = \
    549                 arm64.get_arch_info(self._detail.arch.arm64)
    550         elif arch == CS_ARCH_X86:
    551             (self.prefix, self.opcode, self.rex, self.addr_size, \
    552                 self.modrm, self.sib, self.disp, \
    553                 self.sib_index, self.sib_scale, self.sib_base, self.sse_cc, \
    554                 self.avx_cc, self.avx_sae, self.avx_rm, self.operands) = x86.get_arch_info(self._detail.arch.x86)
    555         elif arch == CS_ARCH_MIPS:
    556                 self.operands = mips.get_arch_info(self._detail.arch.mips)
    557         elif arch == CS_ARCH_PPC:
    558             (self.bc, self.bh, self.update_cr0, self.operands) = \
    559                 ppc.get_arch_info(self._detail.arch.ppc)
    560         elif arch == CS_ARCH_SPARC:
    561             (self.cc, self.hint, self.operands) = sparc.get_arch_info(self._detail.arch.sparc)
    562         elif arch == CS_ARCH_SYSZ:
    563             (self.cc, self.operands) = systemz.get_arch_info(self._detail.arch.sysz)
    564         elif arch == CS_ARCH_XCORE:
    565             (self.operands) = xcore.get_arch_info(self._detail.arch.xcore)
    566 
    567 
    568     def __getattr__(self, name):
    569         if not self._cs._detail:
    570             raise CsError(CS_ERR_DETAIL)
    571 
    572         attr = object.__getattribute__
    573         if not attr(self, '_cs')._detail:
    574             raise AttributeError(name)
    575         _dict = attr(self, '__dict__')
    576         if 'operands' not in _dict:
    577             self.__gen_detail()
    578         if name not in _dict:
    579             raise AttributeError(name)
    580         return _dict[name]
    581 
    582     # get the last error code
    583     def errno(self):
    584         return _cs.cs_errno(self._cs.csh)
    585 
    586     # get the register name, given the register ID
    587     def reg_name(self, reg_id):
    588         if self._raw.id == 0:
    589             raise CsError(CS_ERR_SKIPDATA)
    590 
    591         if self._cs._diet:
    592             # Diet engine cannot provide register name
    593             raise CsError(CS_ERR_DIET)
    594 
    595         if reg_id == 0:
    596             return "(invalid)"
    597 
    598         return _cs.cs_reg_name(self._cs.csh, reg_id).decode('ascii')
    599 
    600     # get the instruction name
    601     def insn_name(self):
    602         if self._cs._diet:
    603             # Diet engine cannot provide instruction name
    604             raise CsError(CS_ERR_DIET)
    605 
    606         if self._raw.id == 0:
    607             return "(invalid)"
    608 
    609         return _cs.cs_insn_name(self._cs.csh, self.id).decode('ascii')
    610 
    611     # get the group name
    612     def group_name(self, group_id):
    613         if self._raw.id == 0:
    614             raise CsError(CS_ERR_SKIPDATA)
    615 
    616         if self._cs._diet:
    617             # Diet engine cannot provide register name
    618             raise CsError(CS_ERR_DIET)
    619 
    620         if group_id == 0:
    621             return "(invalid)"
    622 
    623         return _cs.cs_group_name(self._cs.csh, group_id).decode('ascii')
    624 
    625 
    626     # verify if this insn belong to group with id as @group_id
    627     def group(self, group_id):
    628         if self._raw.id == 0:
    629             raise CsError(CS_ERR_SKIPDATA)
    630 
    631         if self._cs._diet:
    632             # Diet engine cannot provide group information
    633             raise CsError(CS_ERR_DIET)
    634 
    635         return group_id in self.groups
    636 
    637     # verify if this instruction implicitly read register @reg_id
    638     def reg_read(self, reg_id):
    639         if self._raw.id == 0:
    640             raise CsError(CS_ERR_SKIPDATA)
    641 
    642         if self._cs._diet:
    643             # Diet engine cannot provide regs_read information
    644             raise CsError(CS_ERR_DIET)
    645 
    646         return reg_id in self.regs_read
    647 
    648     # verify if this instruction implicitly modified register @reg_id
    649     def reg_write(self, reg_id):
    650         if self._raw.id == 0:
    651             raise CsError(CS_ERR_SKIPDATA)
    652 
    653         if self._cs._diet:
    654             # Diet engine cannot provide regs_write information
    655             raise CsError(CS_ERR_DIET)
    656 
    657         return reg_id in self.regs_write
    658 
    659     # return number of operands having same operand type @op_type
    660     def op_count(self, op_type):
    661         if self._raw.id == 0:
    662             raise CsError(CS_ERR_SKIPDATA)
    663 
    664         c = 0
    665         for op in self.operands:
    666             if op.type == op_type:
    667                 c += 1
    668         return c
    669 
    670     # get the operand at position @position of all operands having the same type @op_type
    671     def op_find(self, op_type, position):
    672         if self._raw.id == 0:
    673             raise CsError(CS_ERR_SKIPDATA)
    674 
    675         c = 0
    676         for op in self.operands:
    677             if op.type == op_type:
    678                 c += 1
    679             if c == position:
    680                 return op
    681 
    682 
    683 class Cs(object):
    684     def __init__(self, arch, mode):
    685         # verify version compatibility with the core before doing anything
    686         (major, minor, _combined) = cs_version()
    687         if major != CS_API_MAJOR or minor != CS_API_MINOR:
    688             self.csh = None
    689             # our binding version is different from the core's API version
    690             raise CsError(CS_ERR_VERSION)
    691 
    692         self.arch, self._mode = arch, mode
    693         self.csh = ctypes.c_size_t()
    694         status = _cs.cs_open(arch, mode, ctypes.byref(self.csh))
    695         if status != CS_ERR_OK:
    696             self.csh = None
    697             raise CsError(status)
    698 
    699         try:
    700             import ccapstone
    701             # rewire disasm to use the faster version
    702             self.disasm = ccapstone.Cs(self).disasm
    703         except:
    704             pass
    705 
    706         if arch == CS_ARCH_X86:
    707             # Intel syntax is default for X86
    708             self._syntax = CS_OPT_SYNTAX_INTEL
    709         else:
    710             self._syntax = None
    711 
    712         self._detail = False  # by default, do not produce instruction details
    713         self._diet = cs_support(CS_SUPPORT_DIET)
    714         self._x86reduce = cs_support(CS_SUPPORT_X86_REDUCE)
    715 
    716         # default mnemonic for SKIPDATA
    717         self._skipdata_mnem = ".byte"
    718         self._skipdata = False
    719 
    720 
    721 
    722     # destructor to be called automatically when object is destroyed.
    723     def __del__(self):
    724         if self.csh:
    725             try:
    726                 status = _cs.cs_close(ctypes.byref(self.csh))
    727                 if status != CS_ERR_OK:
    728                     raise CsError(status)
    729             except: # _cs might be pulled from under our feet
    730                 pass
    731 
    732 
    733     # def option(self, opt_type, opt_value):
    734     #    return _cs.cs_option(self.csh, opt_type, opt_value)
    735 
    736 
    737     # is this a diet engine?
    738     @property
    739     def diet(self):
    740         return self._diet
    741 
    742 
    743     # is this engine compiled with X86-reduce option?
    744     @property
    745     def x86_reduce(self):
    746         return self._x86reduce
    747 
    748 
    749     # return assembly syntax.
    750     @property
    751     def syntax(self):
    752         return self._syntax
    753 
    754 
    755     # syntax setter: modify assembly syntax.
    756     @syntax.setter
    757     def syntax(self, style):
    758         status = _cs.cs_option(self.csh, CS_OPT_SYNTAX, style)
    759         if status != CS_ERR_OK:
    760             raise CsError(status)
    761         # save syntax
    762         self._syntax = style
    763 
    764 
    765     # return current skipdata status
    766     @property
    767     def skipdata(self):
    768         return self._skipdata
    769 
    770 
    771     # setter: modify skipdata status
    772     @skipdata.setter
    773     def skipdata(self, opt):
    774         if opt == False:
    775             status = _cs.cs_option(self.csh, CS_OPT_SKIPDATA, CS_OPT_OFF)
    776         else:
    777             status = _cs.cs_option(self.csh, CS_OPT_SKIPDATA, CS_OPT_ON)
    778         if status != CS_ERR_OK:
    779             raise CsError(status)
    780 
    781         # save this option
    782         self._skipdata = opt
    783 
    784 
    785     def skipdata_setup(self, opt):
    786         _skipdata_opt = _cs_opt_skipdata()
    787         _mnem, _cb, _ud = opt
    788         _skipdata_opt.mnemonic = _mnem.encode()
    789         _skipdata_opt.callback = CS_SKIPDATA_CALLBACK(_cb)
    790         _skipdata_opt.user_data = ctypes.cast(_ud, ctypes.c_void_p)
    791         status = _cs.cs_option(self.csh, CS_OPT_SKIPDATA_SETUP, ctypes.cast(ctypes.byref(_skipdata_opt), ctypes.c_void_p))
    792         if status != CS_ERR_OK:
    793             raise CsError(status)
    794 
    795         self._skipdata_opt = _skipdata_opt
    796 
    797 
    798     # check to see if this engine supports a particular arch,
    799     # or diet mode (depending on @query).
    800     def support(self, query):
    801         return cs_support(query)
    802 
    803 
    804     # is detail mode enable?
    805     @property
    806     def detail(self):
    807         return self._detail
    808 
    809 
    810     # modify detail mode.
    811     @detail.setter
    812     def detail(self, opt):  # opt is boolean type, so must be either 'True' or 'False'
    813         if opt == False:
    814             status = _cs.cs_option(self.csh, CS_OPT_DETAIL, CS_OPT_OFF)
    815         else:
    816             status = _cs.cs_option(self.csh, CS_OPT_DETAIL, CS_OPT_ON)
    817         if status != CS_ERR_OK:
    818             raise CsError(status)
    819         # save detail
    820         self._detail = opt
    821 
    822 
    823     # return disassembly mode of this engine.
    824     @property
    825     def mode(self):
    826         return self._mode
    827 
    828 
    829     # modify engine's mode at run-time.
    830     @mode.setter
    831     def mode(self, opt):  # opt is new disasm mode, of int type
    832         status = _cs.cs_option(self.csh, CS_OPT_MODE, opt)
    833         if status != CS_ERR_OK:
    834             raise CsError(status)
    835         # save mode
    836         self._mode = opt
    837 
    838 
    839     # Disassemble binary & return disassembled instructions in CsInsn objects
    840     def disasm(self, code, offset, count=0):
    841         all_insn = ctypes.POINTER(_cs_insn)()
    842         '''if not _python2:
    843             print(code)
    844             code = code.encode()
    845             print(code)'''
    846         # Hack, unicorn's memory accessors give you back bytearrays, but they
    847         # cause TypeErrors when you hand them into Capstone.
    848         if isinstance(code, bytearray):
    849             code = bytes(code)
    850         res = _cs.cs_disasm(self.csh, code, len(code), offset, count, ctypes.byref(all_insn))
    851         if res > 0:
    852             try:
    853                 for i in range(res):
    854                     yield CsInsn(self, all_insn[i])
    855             finally:
    856                 _cs.cs_free(all_insn, res)
    857         else:
    858             status = _cs.cs_errno(self.csh)
    859             if status != CS_ERR_OK:
    860                 raise CsError(status)
    861             return
    862             yield
    863 
    864 
    865     # Light function to disassemble binary. This is about 20% faster than disasm() because
    866     # unlike disasm(), disasm_lite() only return tuples of (address, size, mnemonic, op_str),
    867     # rather than CsInsn objects.
    868     def disasm_lite(self, code, offset, count=0):
    869         if self._diet:
    870             # Diet engine cannot provide @mnemonic & @op_str
    871             raise CsError(CS_ERR_DIET)
    872 
    873         all_insn = ctypes.POINTER(_cs_insn)()
    874         res = _cs.cs_disasm(self.csh, code, len(code), offset, count, ctypes.byref(all_insn))
    875         if res > 0:
    876             try:
    877                 for i in range(res):
    878                     insn = all_insn[i]
    879                     yield (insn.address, insn.size, insn.mnemonic.decode('ascii'), insn.op_str.decode('ascii'))
    880             finally:
    881                 _cs.cs_free(all_insn, res)
    882         else:
    883             status = _cs.cs_errno(self.csh)
    884             if status != CS_ERR_OK:
    885                 raise CsError(status)
    886             return
    887             yield
    888 
    889 
    890 # print out debugging info
    891 def debug():
    892     # is Cython there?
    893     try:
    894         from . import ccapstone
    895         return ccapstone.debug()
    896     except:
    897         # no Cython, fallback to Python code below
    898         pass
    899 
    900     if cs_support(CS_SUPPORT_DIET):
    901         diet = "diet"
    902     else:
    903         diet = "standard"
    904 
    905     archs = { "arm": CS_ARCH_ARM, "arm64": CS_ARCH_ARM64, \
    906         "mips": CS_ARCH_MIPS, "ppc": CS_ARCH_PPC, "sparc": CS_ARCH_SPARC, \
    907         "sysz": CS_ARCH_SYSZ, 'xcore': CS_ARCH_XCORE }
    908 
    909     all_archs = ""
    910     keys = archs.keys()
    911     for k in sorted(keys):
    912         if cs_support(archs[k]):
    913             all_archs += "-%s" % k
    914 
    915     if cs_support(CS_ARCH_X86):
    916         all_archs += "-x86"
    917         if cs_support(CS_SUPPORT_X86_REDUCE):
    918             all_archs += "_reduce"
    919 
    920     (major, minor, _combined) = cs_version()
    921 
    922     return "python-%s%s-c%u.%u-b%u.%u" % (diet, all_archs, major, minor, CS_API_MAJOR, CS_API_MINOR)
    923