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