unicorn start!
效仿大佬的文章简单地给自己作个学习记录,就当作写了篇日记了
Unicorn 入门学习
unicorn 是用于执行多种cpu框架的虚拟架构系统
下面进行模仿arm架构,执行arm指令
首先在python中导入unicorn
from unicorn import *
如何根据自己要执行指令对应哪个cpu架构,来导入相应的处理器相关的常量
from unicorn.arm_const import *
from unicorn.arm64_const import *
from unicorn.m68k_const import *
from unicorn.mips_const import *
from unicorn.sparc_const import *
from unicorn.x86_const import *
寄存器常量命名规则:
UC_ + 指令集 + REG + 大写寄存器名
UC_ARMREG + 大写寄存器名 (UC_ARM_REG_R0)
UC_X86REG + 大写寄存器名 (UC_X86_REG_EAX)
这里我们选择unicorn.arm_const
创建arm虚拟机(UC)
Uc 是unicorn的主类,Uc对象则代表了一个独立的虚拟机实例,它有独立的寄存器和内存等资源,不同Uc对象之间的数据是独立的。Uc的构造函数有两个参数 arch 和 mode,用来指定模拟执行的指令集和对应的位数或模式。
arch常量参数一般以 UCARCH 开头,MODE常量以UCMODE 开头。
# Test ARM
def test_arm():
print("Emulate ARM code")
try:
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM) # 创建代码
# 其它代码添加到此处
return mu
except UcError as e:
print("ERROR: %s" % e)
mu = test_arm() # mu就是arm虚拟机
映射内存
想用Unicorn模拟执行代码,是不能将代码字节流直接以参数形式传递给Unicorn,而是将要执行的代码写入到Unicorn 的虚拟内存中。Uc 虚拟机实例初始内存是没有任何映射的,在读写内存之前使用uc_mem_map函数映射一段内存
这里开始地址和地址大小都要和0x1000==对齐==
否则就是我这种错误:
ADDRESS = 0x10000
mu.mem_map(ADDRESS, 0x1000) # 这里大小自己估算
写入代码
将想要执行的代码写进我们创建的内存中
mem_write的第二个参数必须是byte字节数组,只支持python的byte数组,不能是String或者bytearray。
ARM_CODE = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0"
# mov r0, #0x37;
# sub r1, r2, r3
mu.mem_write(ADDRESS, ARM_CODE)
给寄存器初始赋值
mu.reg_write(UC_ARM_REG_R0, 0x0)
mu.reg_write(UC_ARM_REG_R2, 0x4)
mu.reg_write(UC_ARM_REG_R3, 0x1)
# 这里r1的结果预估是0x3
添加指令级的Hook
hook类型:
- 指令执行类:
- UC_HOOK_INTR
- UC_HOOK_INSN
- UC_HOOK_CODE
- UC_HOOK_BLOCK
- 内存访问类:
- UC_HOOK_MEM_READ
- UC_HOOK_MEM_WRITE
- UC_HOOK_MEM_FETCH
- UC_HOOK_MEM_READ_AFTER
- UC_HOOK_MEM_PROT
- UC_HOOK_MEM_FETCH_INVALID
- UC_HOOK_MEM_INVALID
- UC_HOOK_MEM_VALID
- 异常处理类:
- UC_HOOK_MEM_READ_UNMAPPED
- UC_HOOK_MEM_WRITE_UNMAPPED
- UC_HOOK_MEM_FETCH_UNMAPPED
def hook_add(self, htype, callback, user_data=None, begin=1, end=0, arg1=0):
pass
mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=0x1000)
以上是添加hook的操作
在begin…end范围内的每一条指令被执行前都会调用callback。
hook函数编写:
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
执行虚拟机
emu_start 可以通过timeout参数设置最长执行时长,防止线程死在虚拟机里面。emu_start 执行完成后,可以通过读取内存或寄存器的方式来获取执行结果。
def emu_start(self, begin, until, timeout=0, count=0):
pass
mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))
执行成功,hook函数一共被调用两次(指令有两条)
获取寄存器结果
r0 = mu.reg_read(UC_ARM_REG_R0)
r1 = mu.reg_read(UC_ARM_REG_R1)
print(">>> R0 = 0x%x" % r0)
print(">>> R1 = 0x%x" % r1)
和推理的结果相同!!!!
个人测试全过程:
完整代码:
from unicorn import *
from unicorn.arm_const import *
ARM_CODE = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0"
# mov r0, #0x37;
# sub r1, r2, r3
# Test ARM
# callback for tracing instructions
def hook_code(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
def test_arm():
print("Emulate ARM code")
try:
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
# map 2MB memory for this emulation
ADDRESS = 0x10000
mu.mem_map(ADDRESS, 2 * 0x10000)
mu.mem_write(ADDRESS, ARM_CODE)
mu.reg_write(UC_ARM_REG_R0, 0x1234)
mu.reg_write(UC_ARM_REG_R2, 0x6789)
mu.reg_write(UC_ARM_REG_R3, 0x3333)
mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))
r0 = mu.reg_read(UC_ARM_REG_R0)
r1 = mu.reg_read(UC_ARM_REG_R1)
print(">>> R0 = 0x%x" % r0)
print(">>> R1 = 0x%x" % r1)
except UcError as e:
print("ERROR: %s" % e)
文章引用Pandaos大佬的文章:[原创] Unicorn 在 Android 的应用-Android安全-看雪论坛-安全社区|安全招聘|bbs.pediy.com
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jaytp@qq.com