我已经蓝屏 发表于 2021-7-21 14:03

【笔记】多架构多平台CPU仿真器-Unicorn实用手册

本帖最后由 我已经蓝屏 于 2021-7-21 14:07 编辑

# 前言
本文就是写写Unicorn咋用的
不涉及原理层面


# 地址
官方网站
https://www.unicorn-engine.org/
Github
https://github.com/unicorn-engine/unicorn
我的博客(欢迎来踩)
https://kdajv.com/2021/07/21/capstoneengine/


# 介绍
**Unicorn**是一个轻量级的多平台多架构CPU仿真器
高亮特点:
- 多架构支持: Arm, Arm64 (Armv8), M68K, Mips, Sparc, & X86 (包括X86_64).
- 干净/简单/轻量级/直观的API,同时不依赖任何架构。
- 以C/C++实现,可在众多语言里使用:Pharo, Crystal, Clojure, Visual Basic, Perl, Rust, Haskell, Ruby, Python, Java, Go, .NET, Delphi/Pascal & MSVC。
- 原生支持Windows和\*nix系统(以下系统已确认支持:Mac OSX, Linux, \*BSD & Solaris)。
- 通过使用即时编译器技术以实现高性能。
- 支持各种级别的细粒度检测。
- 线程安全的设计。
- 允许在开源软件许可 GPLv2 下分发。



# 使用

## Input

```python
from unicorn import *

# 要执行的字节码
# 可以使用Keystone引擎生成
binary = b'1\xc0Ph//shh/bin\x89\xe3PS\x89\xe1\xb0\x0b'

# 规定内存地址
ADDRESS = 0x400000

print("开始模拟-X86")

# 初始化仿真器,模式为X86-32
mu = Uc(UC_ARCH_X86, UC_MODE_32)

# 给仿真器映射2MB内存
mu.mem_map(ADDRESS, 2 * 1024 * 1024)

# 将指令写到内存
mu.mem_write(ADDRESS, binary)

# 初始化仿真器的寄存器,并赋值
# ESP给了内存的最后一个位置
mu.reg_write(x86_const.UC_X86_REG_EAX, 0x1000)
mu.reg_write(x86_const.UC_X86_REG_ESP, ADDRESS + 2 * 1024 * 1024 - 1)


# 这里创建一个钩子,每执行一条指令会先执行一次这里,如果有不想执行的语句可以跳过
# 跳过方法:EIP+size
def hook(emu: Uc, addr, size, userdata):
    # 反汇编当前语句的语义,详情请看Capstone篇
    from capstone import Cs, CS_ARCH_X86, CS_MODE_32
    CS = Cs(CS_ARCH_X86, CS_MODE_32)
    instruction = list(CS.disasm(emu.mem_read(addr, size), 0))
    print(f"当前位置:{hex(addr)},指令长度:{size},指令:{instruction.mnemonic} {instruction.op_str}")
    userdata.Count += 1


# 这里可以传给钩子一个变量(注意不能是初始类型,否则就只传值而不是引用了),执行结束的时候可以读取到钩子里处理的数据
class InstCount:
    Count = 0

count = InstCount()

# 添加钩子,钩子类型为CODE,再传一个用户数据,别的暂时还不知道
mu.hook_add(UC_HOOK_CODE, hook, count)

# 开始仿真
mu.emu_start(ADDRESS, ADDRESS + len(binary))

# 仿真结束
print("仿真结束")
print(f"共执行{count.Count}条指令")

# 读寄存器
r_eax = mu.reg_read(x86_const.UC_X86_REG_EAX)
r_ebx = mu.reg_read(x86_const.UC_X86_REG_EBX)
r_ecx = mu.reg_read(x86_const.UC_X86_REG_ECX)
r_esp = mu.reg_read(x86_const.UC_X86_REG_ESP)
print(">>> EAX = 0x%x" % r_eax)
print(">>> EBX = 0x%x" % r_ebx)
print(">>> ECX = 0x%x" % r_ecx)
print(">>> ESP = 0x%x" % r_esp)

# 读栈内容
now = r_esp
while now != ADDRESS + 2 * 1024 * 1024 - 1:
    buf = mu.mem_read(now, 4)
    print(f"Stack > ESP - {hex(now)} - {buf}")
    now += 4

```

## Output

```python
开始模拟-X86
当前位置:0x400000,指令长度:2,指令:xor eax, eax
当前位置:0x400002,指令长度:1,指令:push eax
当前位置:0x400003,指令长度:5,指令:push 0x68732f2f
当前位置:0x400008,指令长度:5,指令:push 0x6e69622f
当前位置:0x40000d,指令长度:2,指令:mov ebx, esp
当前位置:0x40000f,指令长度:1,指令:push eax
当前位置:0x400010,指令长度:1,指令:push ebx
当前位置:0x400011,指令长度:2,指令:mov ecx, esp
当前位置:0x400013,指令长度:2,指令:mov al, 0xb
仿真结束
共执行9条指令
>>> EAX = 0xb
>>> EBX = 0x5ffff3
>>> ECX = 0x5fffeb
>>> ESP = 0x5fffeb
Stack > ESP - 0x5fffeb - bytearray(b'\xf3\xff_\x00')
Stack > ESP - 0x5fffef - bytearray(b'\x00\x00\x00\x00')
Stack > ESP - 0x5ffff3 - bytearray(b'/bin')
Stack > ESP - 0x5ffff7 - bytearray(b'//sh')
Stack > ESP - 0x5ffffb - bytearray(b'\x00\x00\x00\x00')
```

prospect2005 发表于 2021-7-21 14:26

虚拟机?支持sparc版的solaris系统吗?

我已经蓝屏 发表于 2021-7-21 14:38

prospect2005 发表于 2021-7-21 14:26
虚拟机?支持sparc版的solaris系统吗?
只是指令仿真器
我没接触过这两个
但是据官网上说时支持的
https://github.com/unicorn-engine/unicorn

星星之夜 发表于 2021-7-21 16:58

感谢分享
页: [1]
查看完整版本: 【笔记】多架构多平台CPU仿真器-Unicorn实用手册