capstone、keystone与unicorn-1
capstone
加坡南洋理工大学团队在 Blackhat USA 2014 上发布的一个反汇编引擎。
安装: pip install capstone
基础示例
capstone 的 API 使用起来非常简单,因此使用该框架编写工具非常容易。 下面的代码反汇编一些 x86机器码,并打印出汇编语句。
1 2 3 4 5 6 7
| from capstone import * CODE = b"\x55\x48\x8b\x05\xb8\x13\x00\x00" md = Cs(CS_ARCH_X86, CS_MODE_64) for i in md.disasm(CODE, 0x1000): print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
|
1 2 3 4 5 6 7 8
| from capstone import * CODE = b"\x55\x48\x8b\x05\xb8\x13\x00\x00" md = Cs(CS_ARCH_X86, CS_MODE_64) for insn in md.disasm(CODE, 0x1000): print("0x%x:\t%s\t%s" %(insn.address, insn.mnemonic, insn.op_str))
|
以下是每一行代码的解释:
第 1 行:导入 Python 模块 capstone。
第 3 行:要反汇编的原始二进制代码。
第 5 行:使用类 Cs 为 capstone 初始化 Python 类。 需要给这个类两个参数:硬件架构和硬件模式。
在此示例中,我们要反汇编 x86 体系结构的 64 位代码。
第 6 行:使用上面创建的 Cs 类实例的方法 disasm() 反汇编二进制代码。 disasm 的第二个参数是第一
条指令的地址,在本例中为 0x1000。 默认情况下,disasm 反汇编所有代码,直到没有更多代码或遇
到不可解释的指令。 disasm 返回一个类型为 CsInsn 的指令列表,这里的 for 循环会迭代这个列表。
第 7 行:打印出每条指令的一些内部信息。 CsInsn 类公开了反汇编指令的所有内部信息。 下面介绍了
此类中一些最常用的属性。
///想起来了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def hex_to_file(input_file, output_file): with open(input_file, 'r') as f: hex_string = f.read().strip()
bytes_data = bytes.fromhex(hex_string)
with open(output_file, 'wb') as f: f.write(bytes_data)
input_file = "11.txt" output_file = "output"
hex_to_file(input_file, output_file)
|
更高效的 API
上个示例中使用 disasm() 方法获取 CsInsn 对象。这提供了可用于反汇编指令的完整信息。但是,如果我们只需要地址、大小、助记符和 op_str 等基本数据,我们可以使用更轻量级的 API disasm_lite 。
下面是 disasm_lite 示例
1 2 3 4 5
| from capstone import * CODE = b"\x55\x48\x8b\x05\xb8\x13\x00\x00" md = Cs(CS_ARCH_X86, CS_MODE_64) for address, size, mnemonic, op_str in md.disasm_lite(CODE, 0x1000): print("0x%x:\t%s\t%s" %(address, mnemonic, op_str))
|
架构和模式
1 2 3 4 5
| from capstone import * CODE = b"\x56\x34\x21\x34\xc2\x17\x01\x00" md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS64 + CS_MODE_LITTLE_ENDIAN) for i in md.disasm(CODE, 0x1000): print("%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
|
keystone
pip install keystone-engine
1 2 3 4 5 6 7
| from keystone import * code = "mov eax, 3 \n" \ "sub edx, eax \n" \ "jmp edx" ks = Ks(KS_ARCH_X86, KS_MODE_32) encoding, count = ks.asm(code) print("%s (number of statements: %u)" %(encoding, count))
|
每行代码的注释:
第 1 行:在使用之前导入 keystone 模块。
第 3-5 行:要编译的汇编字符串。 此示例中的代码是 x86 32 位,采用 Intel 格式。 可以用 ; 或
\n 分隔此字符串中的汇编指令。
第 7 行:使用类 Ks 初始化 keystone。 这个类接受 2 个参数:硬件架构和硬件模式。 此示例处理
x86 体系结构的 32 位代码。
第 8 行:使用方法 asm 编译汇编指令。 该函数返回一个编码字节列表,以及 keystone 在编译过
程中处理的输入语句的数量。
第 9 行:打印出指令编码和处理的汇编语句数。
默认情况下,keystone 接受采用 Intel 语法的 x86 汇编。 如果要处理 x86 AT&T 语法,可以简单地切换到语法 AT&T,如下所示。
1 2 3 4 5 6 7 8
| from keystone import * code = "movl $3, %eax \n" \ "sub %eax, %edx \n" \ "jmp *%edx" ks = Ks(KS_ARCH_X86, KS_MODE_32) ks.syntax = KS_OPT_SYNTAX_ATT encoding, count = ks.asm(code) print("%s (number of statements: %u)" %(encoding, count))
|
unicorn
unicorn 引擎的用处:
任务 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| from unicorn import * from unicorn.x86_const import * from capstone import *
mu = Uc(UC_ARCH_X86,UC_MODE_64)
BASE = 0x400000 STACK_ADDR = 0x0 STACK_SIZE = 1024 * 1024 mu.mem_map(BASE, 1024 * 1024) mu.mem_map(STACK_ADDR, STACK_SIZE)
mu.mem_write(BASE, open("./fibonacci", 'rb').read()) mu.reg_write(UC_X86_REG_RSP, STACK_ADDR + STACK_SIZE - 8)
def hook_code(mu, address, size, user_data): for ins in cs.disasm(mu.mem_read(address, size), 0, 1): print(hex(address), ins.mnemonic, ins.op_str) mu.hook_add(UC_HOOK_CODE, hook_code)
mu.emu_start(0x4004E0, 0x400575)
'''Traceback (most recent call last): File "solve.py", line 15, in <module> mu.emu_start(0x4004E0, 0x400575) File "python38\lib\site-packages\unicorn\unicorn.py", line 547, in emu_start raise UcError(status) unicorn.unicorn.UcError: Invalid memory read (UC_ERR_READ_UNMAPPED)''' '''这段代码添加了一个钩子。我们定义了自己的函数 hook_code,它在每条指令的仿真之前被调用。它需 要以下参数: Uc 实例 指令地址 指令大小 用户数据(我们可以在 hook_add() 的可选参数中传递这个值)'''
'''0x4004e0 push rbp 0x4004e1 push rbx 0x4004e2 xor esi, esi 0x4004e4 mov ebp, 0x4007e1 0x4004e9 xor ebx, ebx 0x4004eb sub rsp, 0x18 0x4004ef mov rdi, qword ptr [rip + 0x200b42]
|
1 2 3 4 5 6
| def hook_code(mu, address, size, user_data): if address in [0x4004EF, 0x4004F6, 0x400502, 0x40054F, 0x400560]: mu.reg_write(UC_X86_REG_RIP, address + size) if address == 0x400560: value = mu.reg_read(UC_X86_REG_RDI) print(chr(value))
|