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, 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.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
50cdef 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
245cdef 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
312def 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