最近在研究IDA中使用python脚本来自动化的完成一些指令集的修改,其中用到了终极反汇编引擎,为了写脚本的时候游刃有余,笔者翻阅了Capstone的官方文档及其翻阅了Python的Capstone实现库中的python接口的声明与实现,特此全面记录一下基础api和一些方法的表示的含义和使用,因笔者从事的安卓逆向分析工作,其中的代码都为arm汇编代码。
⊙一. 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支持多架构:
二. 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
去翻阅python的源码看到,Capstone在python上面的库包,并不是用python完全实现了capsonte,用c封装的动态链接库,然后开放接口让python调用,同时拥有一些python文件,大概能分为两类,一类是在某种平台的一些api的属性定义
另一类是定义第一类中定义的架构中一些表示特殊含义的常量定义
剩下的部分都在在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))
`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))
知己知彼方能百战不殆,我们逐个去分析其属性,在上方结果图片中,有些为属性将在下文分析,有些为方法将在三中进行分析。
2.2逐个分析
from capstone import *
CODE = b"xf1x02x03x0ex00x00xa0xe3x02x30xc1xe7x00x00x53xe3"
md = Cs(CS_ARCH_ARM, CS_MODE_ARM)
md.detail = True
for 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]
privilege
arm
-----------------------------------
-----------------------------------
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指令,在源码定义在
例如在上面demo中的
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 = True
for 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 = True
for 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 = True
for 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 = True
for 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 = True
for 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 = True
for 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 = True
CODE = 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 = True
CODE = 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 = True
for 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 = 66
ARM_OP_SYSREG = 67
在想要了解操作数的类型的时候 可以跑一下这个脚本就知道了
from __future__ import print_function
from capstone import *
from capstone.arm import *
from xprint import to_hex, to_x_32
ARM_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 Cs
def 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中的使用
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论