在目标集和内存布局被定义后,Avatar²的真正分析部分就可以进行了,我们可以将之表示为execution-phase执行阶段。
为了告知Avatar²安装阶段已经完成而且实际执行可以开始了,目标需要首先被初始化。
from avatar2 import * avatar = Avatar() # Target setup [...] # Memory setup [...] # Initialize all targets and prepare for execution avatar.init_targets()
在安装阶段,Avatar²可以与每个目标交互以控制其执行或修改内存与寄存器值。
控制目标执行
通过使用一组和传统调试器提供的功能相似的功能,Avatar²可以控制目标的执行。尤其是,所有的目标支持基础功能例如持续执行,执行下一步,停止执行。此外,只要潜在目标支持这些特性,断点和观察点也可以被设置。
然而,与传统调试器相比,当目标执行时Avatar²不会挂起,因为分析师可能想要建立复杂的并行执行编排方案。因此,目标提供了一个wait()方法,强制要求avatar脚本等到目标停止执行。
让我们看看目标执行在Avatar² 脚本中是如何实现的:
# 获取一个之前初始化了的目标 qemu = avatar.targets['QemuTarget0'] # 设置断点 bkpt = qemu.set_breakpoint(0x800e34) # 继续执行 qemu.cont() # 在做其他事前,等待断点被触发 qemu.wait() # 移除断点 qemu.remove_breakpoint(bkpt) # 执行一条指令 qemu.step()
控制目标寄存器
Avatar可以用非常简单的方式检查并修改一个目标的寄存器状态:
# 获取寄存器值
r0 = qemu.regs.r0
# 设置寄存器值
qemu.regs.r0 = 0x41414141
在该封装下,分别会调用函数 read_register
与write_register
, 当然直接调用它们也是可以的。
# Get the content of a register
r0 = qemu.read_register("r0")
# Set the content of a register
qemu.write_register("r0", 0x41414141)
# 同样功能的缩写
r0 = qemu.rr("r0")
qemu.wr("r0", 0x41414141)
控制目标内存
与目标的寄存器状态相似,常常也需要获得或修改目标的内存内容,这与读写寄存器一样简单:
# read 4 bytes from addres 0x20000000
qemu.read_memory(0x20000000, 4)
# write 4 bytes to address 0x20000000
qemu.write_memory(0x20000000, 4, 0xdeadbeef)
# 缩写
qemu.rm(0x20000000, 4)
qemu.wm(0x20000000, 4, 0xdeadbeef)
在目标间转移执行状态
Avatar²的一个更有趣的特性就是能够在执行期间转移目标间的状态,以允许一个成功的编排。看看下面这个例子,它包括目标设置,内存布局说明,以及从一个目标到另一个目标的执行(与状态)的转移:
from avatar2 import *
sample = 'firmware.bin'
openocd_conf = 'nucleo-l152re.cfg'
# 用惯例输出目录创造avatar实例
avatar = Avatar(output_directory='/tmp/myavatar')
# 增加第一个目标
qemu = avatar.add_target(QemuTarget,
gdb_executable="arm-none-eabi-gdb",
firmware=sample, cpu_model="cortex-m3",
executable="targets/qemu/arm-softmmu/qemu-system-")
# 增加第二个目标
nucleo = avatar.add_target(OpenOCDTarget,
gdb_executable="arm-none-eabi-gdb",
openocd_script=openocd_conf)
# 设置惯例gdb端口避免冲突
qemu.gdb_port = 1234
nucleo.gdb_port = 1235
# 指定第一片内存范围
rom = avatar.add_memory_range(0x08000000, 0x1000000, name='rom',
file=sample)
# 指定第二片内存范围
ram = avatar.add_memory_range(0x20000000, 0x14000, name='ram')
# 初始化目标
avatar.init_targets()
# 在nucleo上执行到一个特定地址
nucleo.set_breakpoint(0x800B570)
nucleo.cont()
nucleo.wait()
# 转移状态到qemu上
avatar.transfer_state(nucleo, qemu, sync_regs=True, synced_ranges=[ram])
# 继续在qemu上执行
qemu.cont()