一文弄懂终极反汇编器Capstone在Python中的使用

admin 2025年1月11日14:28:51评论11 views字数 11516阅读38分23秒阅读模式
一文弄懂终极反汇编器Capstone在Python中的使用

最近在研究IDA中使用python脚本来自动化的完成一些指令集的修改,其中用到了终极反汇编引擎,为了写脚本的时候游刃有余,笔者翻阅了Capstone的官方文档及其翻阅了Python的Capstone实现库中的python接口的声明与实现,特此全面记录一下基础api和一些方法的表示的含义和使用,因笔者从事的安卓逆向分析工作,其中的代码都为arm汇编代码。

一文弄懂终极反汇编器Capstone在Python中的使用
 目录

一. Capstone的介绍

二. Python3的Capstone支持

三.Capstone属性解析

.Capstone方法解析

.operands(操作数)解析

. 总结

一.Capstone的介绍

1.1 Capstone是什么

Capstone是一个轻量级的多平台,多架构的反编译框架,可以把二进制数据解析为汇编指令并对其灵活的操作。

1.2 Capstone的特点

1.2.1 纯c语言实现,速度快

1.2.2 多线程使用中线程安全

1.2.3 多种语言都可以使用,具体看官方对其介绍

1.2.4 可以对汇编指令进行分割,像一把灵活的手术刀一样

1.2.5 支持windows mac linux 

1.2.6 用着舒服因为ida支持python脚本,所以结合ida软件提供的功能,可以对恶意软件进行分析

1.2.7支持多架构:

一文弄懂终极反汇编器Capstone在Python中的使用

二. Python3的Capstone支持

首先是安装,在python3环境下直接 pip install Capstone即可安装成功(笔者用的豆瓣的源没有安装成功),于是去下载轮子,然后直接pip安装即可

https://files.pythonhosted.org/packages/6a/71/d409f50f7cf7235513f3e636bb1aeb53d729106c1c77495c48fc09c41a54/capstone-4.0.2-py2.py3-none-win_amd64.whl

一文弄懂终极反汇编器Capstone在Python中的使用

去翻阅python的源码看到,Capstone在python上面的库包,并不是用python完全实现了capsonte,用c封装的动态链接库,然后开放接口让python调用,同时拥有一些python文件,大概能分为两类,一类是在某种平台的一些api的属性定义

一文弄懂终极反汇编器Capstone在Python中的使用

另一类是定义第一类中定义的架构中一些表示特殊含义的常量定义

一文弄懂终极反汇编器Capstone在Python中的使用

剩下的部分都在在init初始中后文会在capstone的属性方法中详细为大家介绍。

二. Capstone属性解析

在分析属性之前首先看一个demo

from capstone import *CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"md = Cs(CS_ARCH_ARM, CS_MODE_ARM)for i in md.disasm(CODE, 0x1000):    print("0x%x:t%st%s" %(i.address, i.mnemonic, i.op_str))

一文弄懂终极反汇编器Capstone在Python中的使用

`from capstone import *`:首先是导入了capstone里面所有的方法和属性。

`CODE = `要被解析的字节码

`md = Cs(CS_ARCH_ARM, CS_MODE_ARM)`:第一个为架构,因为笔者要让capstone翻译为arm架构下的汇编指令,第二个为反汇编的模式

`md.disasm(CODE, 0x1000)`:第一个参数为反汇编的字节码,第二个参数为指令开始的偏移地址,必须填。

`(i.address, i.mnemonic, i.op_str)`:这个地方就是像手术刀一样可以解析指令的地址和指令的助记符和指令的操作数。

2.1 找出md.disasm返回对象CsInSn的属性

frfrom capstone import *CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"md = Cs(CS_ARCH_ARM, CS_MODE_ARM)for i in md.disasm(CODE, 0x1000):    print(type(i))    print(dir(i))

一文弄懂终极反汇编器Capstone在Python中的使用

知己知彼方能百战不殆,我们逐个去分析其属性,在上方结果图片中,有些为属性将在下文分析,有些为方法将在三中进行分析。

2.2逐个分析

from capstone import *CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"md = Cs(CS_ARCH_ARM, CS_MODE_ARM)md.detail = Truefor i in md.disasm(CODE, 0x1000):    print("-----------------------------------")    print("0x%x:t%st%s" % (i.address, i.mnemonic, i.op_str))    print("指令的id:",i.id)    print("指令的地址:0x%x" %(i.address))    print("指令的助记符:%s" %(i.mnemonic))    print("指令的操作数:%s" % ( i.op_str))    print("指令的机器字节:%s" % (i.bytes))    print("指令的大小:",i.size)    print("正在读取的隐式寄存器的列表:", i.regs_read)    if len(i.regs_read) > 0:        for g in i.regs_read:            print(i.reg_name(g))    print("正在写入的隐式寄存器的列表:", i.regs_write)    if len(i.regs_write) > 0:        for g in i.regs_write:            print(i.reg_name(g))    print("此指令所属语义组的列表:", i.groups)    if len(i.groups) > 0:        for g in i.groups:            print(i.group_name(g))    print("-----------------------------------")-----------------------------------0x1000:  mcreq  p2, #0, r0, c3, c1, #7指令的id: 76指令的地址:0x1000指令的助记符:mcreq指令的操作数:p2, #0, r0, c3, c1, #7指令的机器字节:bytearray(b'xf1x02x03x0e')指令的大小: 4正在读取的隐式寄存器的列表: []正在写入的隐式寄存器的列表: []此指令所属语义组的列表: [6, 147]privilegearm----------------------------------------------------------------------0x1004:  mov  r0, #0指令的id: 82指令的地址:0x1004指令的助记符:mov指令的操作数:r0, #0指令的机器字节:bytearray(b'x00x00xa0xe3')指令的大小: 4正在读取的隐式寄存器的列表: []正在写入的隐式寄存器的列表: []此指令所属语义组的列表: [147]arm----------------------------------------------------------------------0x1008:  strb  r3, [r1, r2]指令的id: 205指令的地址:0x1008指令的助记符:strb指令的操作数:r3, [r1, r2]指令的机器字节:bytearray(b'x020xc1xe7')指令的大小: 4正在读取的隐式寄存器的列表: []正在写入的隐式寄存器的列表: []此指令所属语义组的列表: [147]arm----------------------------------------------------------------------0x100c:  cmp  r3, #0指令的id: 23指令的地址:0x100c指令的助记符:cmp指令的操作数:r3, #0指令的机器字节:bytearray(b'x00x00Sxe3')指令的大小: 4正在读取的隐式寄存器的列表: []正在写入的隐式寄存器的列表: [3]cpsr此指令所属语义组的列表: [147]arm-----------------------------------

从上文中除i.regs_read,i.regs_write,i.groups都很熟悉了,那么直接可以只研究这三个

2.2.1 groups:

也就是这个指令属于的类别集合,例如为Call指令或者JUMP指令,在源码定义在

一文弄懂终极反汇编器Capstone在Python中的使用

例如在上面demo中的

一文弄懂终极反汇编器Capstone在Python中的使用

2.2.2regs_read 和 regs_write

所谓的隐式也就是这条指令背后所做的操作 光从表面指令看不到 例如push肯定要操作栈指针 下面的指令需要自我感悟

-----------------------------------0x1000:  svcvs  #0x636f6e正在读取的隐式寄存器的列表: [11]pc正在写入的隐式寄存器的列表: [10]lr----------------------------------------------------------------------0x100c:  cmp  r3#0正在读取的隐式寄存器的列表: []正在写入的隐式寄存器的列表: [3]cpsr----------------------------------------------------------------------0x1000:  POP     {R4-R7,R11,PC}正在读取的隐式寄存器的列表: [12]sp正在写入的隐式寄存器的列表: [12]sp-----------------------------------

三. Capstone方法解析

3.1 reg_name

reg_name(self, reg_id, default=None)

在上文已经用到,给一个寄存器的id返回一个寄存器的名称

举个栗子:

from capstone import *CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"md = Cs(CS_ARCH_ARM, CS_MODE_ARM)md.detail = Truefor i in md.disasm(CODE, 0x1000):    if len(i.regs_write) > 0:        for g in i.regs_write:            print(i.reg_name(g))

3.2 reg_name

group_name(self, group_id, default=None)

在上文已经用到,给一个组的ida返回组的名称

from capstone import *CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"md = Cs(CS_ARCH_ARM, CS_MODE_ARM)md.detail = Truefor i in md.disasm(CODE, 0x1000):    if len(i.groups) > 0:        for g in i.groups:            print(i.group_name(g))

3.3 insn_name

insn_name(self, default=None)

得到当前助记符的名字

举个栗子:

from capstone import *CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"md = Cs(CS_ARCH_ARM, CS_MODE_ARM)md.detail = Truefor i in md.disasm(CODE, 0x1000):    print(i.insn_name())

3.4 group

 group(self, group_id)

验证指令的group_id是否属于组

举个栗子:

from capstone import *CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"md = Cs(CS_ARCH_ARM, CS_MODE_ARM)md.detail = Truefor i in md.disasm(CODE, 0x1000):    if len(i.groups) > 0:        for g in i.groups:            print(i.group(g))

3.5 reg_red

reg_read(self, reg_id)

传递一个寄存器的id验证这个寄存器在这条指令中是不是隐形被读

举个栗子:

from capstone import *CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"md = Cs(CS_ARCH_ARM, CS_MODE_ARM)md.detail = Truefor i in md.disasm(CODE, 0x1000):    print("-----------------------------------")    print("0x%x:t%st%s" % (i.address, i.mnemonic, i.op_str))    print("正在写入的隐式寄存器的列表:", i.regs_write)    if len(i.regs_write) > 0:        for g in i.regs_write:            print(i.reg_name(g))            print(i.reg_read(g))    print("-----------------------------------")

3.6 reg_write

reg_write(self, reg_id)

传递一个寄存器的id验证这个寄存器在这条指令中是不是隐形被写

举个栗子:

from capstone import *CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"md = Cs(CS_ARCH_ARM, CS_MODE_ARM)md.detail = Truefor i in md.disasm(CODE, 0x1000):    print("-----------------------------------")    print("0x%x:t%st%s" % (i.address, i.mnemonic, i.op_str))    print("正在写入的隐式寄存器的列表:", i.regs_write)    if len(i.regs_write) > 0:        for g in i.regs_write:            print(i.reg_name(g))            print(i.regs_write(g))    print("-----------------------------------")

3.7 op_count

 op_count(self, op_type)

按照传递操作数的类型 返回操作数的数量

/// Common instruction operand types - to be consistent across all architectures.typedef enum cs_op_type {  CS_OP_INVALID = 0,  ///< uninitialized/invalid operand.  CS_OP_REG,          ///< Register operand.  CS_OP_IMM,          ///< Immediate operand.  CS_OP_MEM,          ///< Memory operand.  CS_OP_FP,           ///< Floating-Point operand.} cs_op_type;

举个栗子:

from keystone import *from capstone import *def enArm(dis_str):    try:        ks_arm = Ks(KS_ARCH_ARM, KS_MODE_ARM)        encoding, count = ks_arm.asm(dis_str)        return bytes(encoding)    except Exception as e:        return "nocontent".encode()md = Cs(CS_ARCH_ARM, CS_MODE_ARM)md.detail = TrueCODE = enArm("popeq   {R1-R7}")for i in md.disasm(CODE, 0x1000):    print(i.op_count (1))

3.8 op_find

op_find(self, op_type, position)

根据操作数的类型和所在的位置找到符合的operand

from keystone import *from capstone import *def enArm(dis_str):    try:        ks_arm = Ks(KS_ARCH_ARM, KS_MODE_ARM)        encoding, count = ks_arm.asm(dis_str)        return bytes(encoding)    except Exception as e:        return "nocontent".encode()md = Cs(CS_ARCH_ARM, CS_MODE_ARM)md.detail = TrueCODE = enArm("popeq   {R1-R7}")for i in md.disasm(CODE, 0x1000):   print(   i.op_find( 2,1)) #找不到立即数 在操作数1   print(   i.op_find( 1,1))#找得到

3.9 regs_access

regs_access(self)

这个指令返回读的和被修改的寄存器,并且包括隐式和显式寄存器

举个栗子:

from capstone import *CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"md = Cs(CS_ARCH_ARM, CS_MODE_ARM)md.detail = Truefor i in md.disasm(CODE, 0x1000):    print("-----------------------------------")    print("0x%x:t%st%s" % (i.address, i.mnemonic, i.op_str))    print("正在写入的隐式寄存器的列表:", i.regs_write)    if len(i.regs_write) > 0:        for g in i.regs_write:            print(i.reg_name(g))            print(i.regs_write(g))    print("-----------------------------------")

.operands(操作数)解析

在上文2.1的位置CSlnsn这个类也提供了operand这个属性,这个属性主要提供了指令的操作数的列表,在逆向过程中会遇到判断指令的操作数的问题在第五章节以arm指令为例子详细分析。

首先在源码中arm_const.py中找到操作数的类型

ARM_OP_INVALID = 0 #操作数的有效ARM_OP_REG = 1 #寄存器ARM_OP_IMM = 2 #立即数ARM_OP_MEM = 3 #内存引用ARM_OP_FP = 4 #实数ARM_OP_CIMM = 64 #协处理器的寄存器 ARM_OP_PIMM = 65协处理器指令p开头ARM_OP_SETEND = 66ARM_OP_SYSREG = 67

在想要了解操作数的类型的时候 可以跑一下这个脚本就知道了

from __future__ import print_functionfrom capstone import *from capstone.arm import *from xprint import to_hex, to_x_32ARM_CODE = b"x86x48x60xf4xEDxFFxFFxEBx04xe0x2dxe5x00x00x00x00xe0x83x22xe5xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3x00x02x01xf1x05x40xd0xe8xf4x80x00x00"ARM_CODE2 = b"xd1xe8x00xf0xf0x24x04x07x1fx3cxf2xc0x00x00x4fxf0x00x01x46x6c"THUMB_CODE = b"x70x47x00xf0x10xe8xebx46x83xb0xc9x68x1fxb1x30xbfxafxf3x20x84x52xf8x23xf0"all_tests = (        (CS_ARCH_ARM, CS_MODE_ARM, ARM_CODE, "ARM", None),        (CS_ARCH_ARM, CS_MODE_THUMB, THUMB_CODE, "Thumb", None),        (CS_ARCH_ARM, CS_MODE_THUMB, ARM_CODE2, "Thumb-mixed", None),        )def print_insn_detail(insn):    print("0x%x:t%st%s" % (insn.address, insn.mnemonic, insn.op_str))    if insn.id == 0:        return    if len(insn.operands) > 0:        print("top_count: %u" % len(insn.operands))        c = 0        for i in insn.operands:            if i.type == ARM_OP_REG:                print("ttoperands[%u].type: REG = %s" % (c, insn.reg_name(i.reg)))            if i.type == ARM_OP_IMM:                print("ttoperands[%u].type: IMM = 0x%s" % (c, to_x_32(i.imm)))            if i.type == ARM_OP_PIMM:                print("ttoperands[%u].type: P-IMM = %u" % (c, i.imm))            if i.type == ARM_OP_CIMM:                print("ttoperands[%u].type: C-IMM = %u" % (c, i.imm))            if i.type == ARM_OP_FP:                print("ttoperands[%u].type: FP = %f" % (c, i.fp))            if i.type == ARM_OP_SYSREG:                print("ttoperands[%u].type: SYSREG = %u" % (c, i.reg))            if i.type == ARM_OP_SETEND:                if i.setend == ARM_SETEND_BE:                    print("ttoperands[%u].type: SETEND = be" % c)                else:                    print("ttoperands[%u].type: SETEND = le" % c)            if i.type == ARM_OP_MEM:                print("ttoperands[%u].type: MEM" % c)                if i.mem.base != 0:                    print("tttoperands[%u].mem.base: REG = %s"                         % (c, insn.reg_name(i.mem.base)))                if i.mem.index != 0:                    print("tttoperands[%u].mem.index: REG = %s"                         % (c, insn.reg_name(i.mem.index)))                if i.mem.scale != 1:                    print("tttoperands[%u].mem.scale: %u"                         % (c, i.mem.scale))                if i.mem.disp != 0:                    print("tttoperands[%u].mem.disp: 0x%s"                         % (c, to_x_32(i.mem.disp)))                if i.mem.lshift != 0:                    print("tttoperands[%u].mem.lshift: 0x%s"                         % (c, to_x_32(i.mem.lshift)))            if i.neon_lane != -1:                print("ttoperands[%u].neon_lane = %u" % (c, i.neon_lane))            if i.access == CS_AC_READ:                print("ttoperands[%u].access: READn" % (c))            elif i.access == CS_AC_WRITE:                print("ttoperands[%u].access: WRITEn" % (c))            elif i.access == CS_AC_READ | CS_AC_WRITE:                print("ttoperands[%u].access: READ | WRITEn" % (c))            if i.shift.type != ARM_SFT_INVALID and i.shift.value:                print("tttShift: %u = %u"                     % (i.shift.type, i.shift.value))            if i.vector_index != -1:                print("tttoperands[%u].vector_index = %u" %(c, i.vector_index))            if i.subtracted:                print("tttoperands[%u].subtracted = True" %c)            c += 1    if insn.update_flags:        print("tUpdate-flags: True")    if insn.writeback:        print("tWrite-back: True")    if not insn.cc in [ARM_CC_AL, ARM_CC_INVALID]:        print("tCode condition: %u" % insn.cc)    if insn.cps_mode:        print("tCPSI-mode: %u" %(insn.cps_mode))    if insn.cps_flag:        print("tCPSI-flag: %u" %(insn.cps_flag))    if insn.vector_data:        print("tVector-data: %u" %(insn.vector_data))    if insn.vector_size:        print("tVector-size: %u" %(insn.vector_size))    if insn.usermode:        print("tUser-mode: True")    if insn.mem_barrier:        print("tMemory-barrier: %u" %(insn.mem_barrier))    (regs_read, regs_write) = insn.regs_access()    if len(regs_read) > 0:        print("tRegisters read:", end="")        for r in regs_read:            print(" %s" %(insn.reg_name(r)), end="")        print("")    if len(regs_write) > 0:        print("tRegisters modified:", end="")        for r in regs_write:            print(" %s" %(insn.reg_name(r)), end="")        print("")# ## Test class Csdef test_class():    for (arch, mode, code, comment, syntax) in all_tests:        print("*" * 16)        print("Platform: %s" % comment)        print("Code: %s" % to_hex(code))        print("Disasm:")        try:            md = Cs(arch, mode)            if syntax is not None:                md.syntax = syntax            md.detail = True            for insn in md.disasm(code, 0x80001000):                print_insn_detail(insn)                print ()        except CsError as e:            print("ERROR: %s" % e)if __name__ == '__main__':    test_class()

六. 总结

capstone在逆向分析中对于自动化处理非常有意义。

如果想要了解爬虫源码分析问题和js逆向请关注我朋友:

如果想要了解二进制逆向请关注我:

我是BestToYou,分享工作或日常学习中关于二进制逆向和分析的一些思路和一些自己闲暇时刻调试的一些程序,文中若有错误的地方,恳请大家联系我批评指正。

原文始发于微信公众号(二进制科学):一文弄懂终极反汇编器Capstone在Python中的使用

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月11日14:28:51
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   一文弄懂终极反汇编器Capstone在Python中的使用https://cn-sec.com/archives/1886650.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息