• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Capstone Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com>
2import sys
3from platform import system
4_python2 = sys.version_info[0] < 3
5if _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
104CS_API_MAJOR = 3
105CS_API_MINOR = 0
106
107# Package version
108CS_VERSION_MAJOR = CS_API_MAJOR
109CS_VERSION_MINOR = CS_API_MINOR
110CS_VERSION_EXTRA = 5
111
112__version__ = "%u.%u.%u" %(CS_VERSION_MAJOR, CS_VERSION_MINOR, CS_VERSION_EXTRA)
113
114# architectures
115CS_ARCH_ARM = 0
116CS_ARCH_ARM64 = 1
117CS_ARCH_MIPS = 2
118CS_ARCH_X86 = 3
119CS_ARCH_PPC = 4
120CS_ARCH_SPARC = 5
121CS_ARCH_SYSZ = 6
122CS_ARCH_XCORE = 7
123CS_ARCH_MAX = 8
124CS_ARCH_ALL = 0xFFFF
125
126# disasm mode
127CS_MODE_LITTLE_ENDIAN = 0      # little-endian mode (default mode)
128CS_MODE_ARM = 0                # ARM mode
129CS_MODE_16 = (1 << 1)          # 16-bit mode (for X86)
130CS_MODE_32 = (1 << 2)          # 32-bit mode (for X86)
131CS_MODE_64 = (1 << 3)          # 64-bit mode (for X86, PPC)
132CS_MODE_THUMB = (1 << 4)       # ARM's Thumb mode, including Thumb-2
133CS_MODE_MCLASS = (1 << 5)      # ARM's Cortex-M series
134CS_MODE_V8 = (1 << 6)          # ARMv8 A32 encodings for ARM
135CS_MODE_MICRO = (1 << 4)       # MicroMips mode (MIPS architecture)
136CS_MODE_MIPS3 = (1 << 5)       # Mips III ISA
137CS_MODE_MIPS32R6 = (1 << 6)    # Mips32r6 ISA
138CS_MODE_MIPSGP64 = (1 << 7)    # General Purpose Registers are 64-bit wide (MIPS arch)
139CS_MODE_V9 = (1 << 4)          # Sparc V9 mode (for Sparc)
140CS_MODE_BIG_ENDIAN = (1 << 31) # big-endian mode
141CS_MODE_MIPS32 = CS_MODE_32    # Mips32 ISA
142CS_MODE_MIPS64 = CS_MODE_64    # Mips64 ISA
143
144# Capstone option type
145CS_OPT_SYNTAX = 1    # Intel X86 asm syntax (CS_ARCH_X86 arch)
146CS_OPT_DETAIL = 2    # Break down instruction structure into details
147CS_OPT_MODE = 3      # Change engine's mode at run-time
148CS_OPT_MEM = 4       # Change engine's mode at run-time
149CS_OPT_SKIPDATA = 5  # Skip data when disassembling
150CS_OPT_SKIPDATA_SETUP = 6      # Setup user-defined function for SKIPDATA option
151
152# Capstone option value
153CS_OPT_OFF = 0             # Turn OFF an option - default option of CS_OPT_DETAIL
154CS_OPT_ON = 3              # Turn ON an option (CS_OPT_DETAIL)
155
156# Common instruction operand types - to be consistent across all architectures.
157CS_OP_INVALID = 0
158CS_OP_REG = 1
159CS_OP_IMM = 2
160CS_OP_MEM = 3
161CS_OP_FP  = 4
162
163# Common instruction groups - to be consistent across all architectures.
164CS_GRP_INVALID = 0  # uninitialized/invalid group.
165CS_GRP_JUMP    = 1  # all jump instructions (conditional+direct+indirect jumps)
166CS_GRP_CALL    = 2  # all call instructions
167CS_GRP_RET     = 3  # all return instructions
168CS_GRP_INT     = 4  # all interrupt instructions (int+syscall)
169CS_GRP_IRET    = 5  # all interrupt return instructions
170
171# Capstone syntax value
172CS_OPT_SYNTAX_DEFAULT = 0    # Default assembly syntax of all platforms (CS_OPT_SYNTAX)
173CS_OPT_SYNTAX_INTEL = 1    # Intel X86 asm syntax - default syntax on X86 (CS_OPT_SYNTAX, CS_ARCH_X86)
174CS_OPT_SYNTAX_ATT = 2      # ATT asm syntax (CS_OPT_SYNTAX, CS_ARCH_X86)
175CS_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
178CS_ERR_OK = 0      # No error: everything was fine
179CS_ERR_MEM = 1     # Out-Of-Memory error: cs_open(), cs_disasm()
180CS_ERR_ARCH = 2    # Unsupported architecture: cs_open()
181CS_ERR_HANDLE = 3  # Invalid handle: cs_op_count(), cs_op_index()
182CS_ERR_CSH = 4     # Invalid csh argument: cs_close(), cs_errno(), cs_option()
183CS_ERR_MODE = 5    # Invalid/unsupported mode: cs_open()
184CS_ERR_OPTION = 6  # Invalid/unsupported option: cs_option()
185CS_ERR_DETAIL = 7  # Invalid/unsupported option: cs_option()
186CS_ERR_MEMSETUP = 8
187CS_ERR_VERSION = 9 # Unsupported version (bindings)
188CS_ERR_DIET = 10   # Information irrelevant in diet engine
189CS_ERR_SKIPDATA = 11 # Access irrelevant data for "data" instruction in SKIPDATA mode
190CS_ERR_X86_ATT = 12 # X86 AT&T syntax is unsupported (opt-out at compile time)
191CS_ERR_X86_INTEL = 13 # X86 Intel syntax is unsupported (opt-out at compile time)
192
193# query id for cs_support()
194CS_SUPPORT_DIET = CS_ARCH_ALL + 1
195CS_SUPPORT_X86_REDUCE = CS_ARCH_ALL+2
196
197
198import ctypes, ctypes.util
199from os.path import split, join, dirname
200import distutils.sysconfig
201import pkg_resources
202
203import inspect
204if not hasattr(sys.modules[__name__], '__file__'):
205    __file__ = inspect.getfile(inspect.currentframe())
206
207if sys.platform == 'darwin':
208    _lib = "libcapstone.dylib"
209elif sys.platform in ('win32', 'cygwin'):
210    _lib = "capstone.dll"
211else:
212    _lib = "libcapstone.so"
213
214_found = False
215
216def _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
245for _path in _path_list:
246    _cs = _load_lib(_path)
247    if _cs is not None: break
248else:
249    raise ImportError("ERROR: fail to load the dynamic library.")
250
251
252# low-level structure for C code
253
254def 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
260def 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
264from . import arm, arm64, mips, ppc, sparc, systemz, x86, xcore
265
266class _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
278class _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
289class _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
301CS_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
303class _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
312def _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
334class 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
348def 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
356def version_bind():
357    return (CS_API_MAJOR, CS_API_MINOR, (CS_API_MAJOR << 8) + CS_API_MINOR)
358
359
360def 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
366class _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.
376def 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.
413def 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
451class 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
683class 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
891def 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