使用 unicorn 和 capstone 库来模拟 ARM Thumb 指令的执行(一)
import binascii
import unicorn
import capstone
def printArm32Regs(mu):
for i in range(66,78):
print("R%d,value:%x"%(i-66,mu.reg_read(i)))
def testhumb():
CODE = b'\x1C\x00\x0A\x46\x1E\x00'
"""
MOV R3, R0 的机器码:0x1C 0x00(MOV R3, R0)
MOV R0, R2 的机器码:0x0A 0x46(MOV R0, R2)
MOV R2, R3 的机器码:0x1E 0x00(MOV R2, R3)
"""
cp = capstone.Cs(capstone.CS_ARCH_ARM,capstone.CS_MODE_THUMB)
for i in cp.disasm(CODE,0,len(CODE)):
print("[addr:%x]:%s %s\n" %(i.address,i.mnemonic,i.op_str))
mu = unicorn.Uc(unicorn.UC_ARCH_ARM ,unicorn.UC_MODE_THUMB)
ADDRESS = 0x1000
SIZE = 1024
mu.mem_map(ADDRESS,SIZE)
mu.mem_write(ADDRESS,CODE)
bytes = mu.mem_read(ADDRESS,10)
print("ADDRESS:%x,content:%s"%(ADDRESS,binascii.b2a_hex(bytes)))
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0,0x100)
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1,0x200)
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R2,0x300)
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R3,0x400)
printArm32Regs(mu)
mu.emu_start(ADDRESS+1,ADDRESS+4)
print("------------")
printArm32Regs(mu)
return
if __name__ == '__main__':
testhumb()
运行结果图:
代码概述
这段代码使用了 unicorn
和 capstone
库来模拟 ARM Thumb 指令的执行。它的主要功能是加载一段机器码,执行这些指令,并打印出执行前后寄存器的值。
代码结构
-
导入库
import binascii import unicorn import capstone
binascii
: 用于处理二进制和 ASCII 之间的转换。unicorn
: 一个轻量级的多架构 CPU 模拟器。capstone
: 一个轻量级的反汇编框架,用于将机器码转换为可读的汇编指令。
-
打印寄存器值的函数
def printArm32Regs(mu): for i in range(66, 78): print("R%d,value:%x" % (i - 66, mu.reg_read(i)))
- 该函数接受一个
unicorn
模拟器实例mu
作为参数。 - 它循环读取 R0 到 R12(在 ARM 中,寄存器编号从 0 开始,R0 对应 66,R1 对应 67,以此类推),并打印出每个寄存器的值。
- 该函数接受一个
-
主测试函数
def testhumb(): CODE = b'\x1C\x00\x0A\x46\x1E\x00'
- 这里定义了要执行的机器码
CODE
,它包含了三条指令的机器码。
cp = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in cp.disasm(CODE, 0, len(CODE)): print("[addr:%x]:%s %s\n" % (i.address, i.mnemonic, i.op_str))
- 使用
capstone
创建一个反汇编器实例cp
,指定架构为 ARM 和模式为 Thumb。 - 反汇编
CODE
,并打印出每条指令的地址、助记符和操作数。
- 这里定义了要执行的机器码
-
设置 Unicorn 模拟器
mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB) ADDRESS = 0x1000 SIZE = 1024 mu.mem_map(ADDRESS, SIZE) mu.mem_write(ADDRESS, CODE)
- 创建一个 Unicorn 模拟器实例
mu
,指定架构为 ARM 和模式为 Thumb。 - 映射内存区域,从
ADDRESS
开始,大小为SIZE
。 - 将机器码写入模拟器的内存中。
- 创建一个 Unicorn 模拟器实例
-
初始化寄存器
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, 0x100) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0x200) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R2, 0x300) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R3, 0x400)
- 初始化 R0、R1、R2 和 R3 寄存器的值。
-
打印寄存器值
printArm32Regs(mu)
- 在执行指令之前,打印寄存器的初始值。
-
执行指令
mu.emu_start(ADDRESS + 1, ADDRESS + 4)
- 从
ADDRESS + 1
开始执行到ADDRESS + 4
。注意,这里从ADDRESS + 1
开始执行是因为机器码的第一条指令是MOV R3, R0
,它的机器码是0x1C 0x00
,在 Thumb 模式下,指令的地址是以 2 字节为单位的。
- 从
-
再次打印寄存器值
print("------------") printArm32Regs(mu)
- 执行完指令后,再次打印寄存器的值,以查看它们是否发生了变化。
代码功能总结
- 反汇编: 代码首先将机器码反汇编为可读的汇编指令,并打印出每条指令的信息。
- 模拟执行: 使用 Unicorn 模拟器执行这些指令,并在执行前后打印寄存器的值,以便观察指令对寄存器的影响。
- 寄存器操作: 通过指令的执行,寄存器的值会根据指令的逻辑进行更新。
注意事项
- 机器码的正确性: 确保机器码对应的指令是正确的,并且在执行时不会导致未定义的行为。
- 寄存器的初始化: 在执行指令之前,确保寄存器的值已正确初始化,以便观察指令执行后的变化。