Unicorn模拟执行

基操

导入与初始化

1
2
3
from unicorn import *
from unicorn.x86_const import *
uc=Uc(UC_ARCH_X86,UC_MODE_64)

手动初始化内存、分配堆栈空间

1
2
3
address=0x400000 #运行地址
stack_addr=0x410000 #堆栈地址
data_addr=0x420000 #数据地址

映射内存

1
uc.mem_map(address,2*1024*1024) #加载的地址和大小

内存读写

1
2
uc.mem_read(address,length) #要读取的地址 要读取的长度
uc.mem_write(address,Sub_code) #将Sub_code的代码写入运行地址

寄存器读写

1
2
uc.reg_write(UC_X86_REG_ESP,stack_addr)
uc.reg_read(UC_X86_REG_ESP)

原神启动

1
2
uc.emu_start(Startaddr,Endaddr) #模拟开始地址 模拟结束地址
uc.emu_stop()

实操

[鹤城杯 2021]Petition

这是个elf可执行文件,Python脚本也得放在Linux下跑。

每深入一层递归函数即为正确一位,类似于单字节加密,直接一个一个字符爆。

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
45
46
47
from unicorn import *
from capstone import *
from unicorn.x86_const import *
import binascii
param=0 #记录当前正在爆破的flag位
flag=b''
def hook_scanf(uc,address,size,data):
if address==0x110b: #HOOK start中的scanf 不call直接将flag放入edi
edi=uc.reg_read(UC_X86_REG_EDI)
uc.mem_write(edi,flag)
def hook_code(uc,address,size,data):
global param
com=bytearray(b'1\xc0\x83\xf0\x01') #仔细观察递归的每一个函数开头都有xor eax,eax;xor eax,1;
code=uc.mem_read(address,5)
if code==com:#当执行到每个函数开头时 即正确一位
param=param+1
def init():
uc=Uc(UC_ARCH_X86,UC_MODE_32)
address=0x0 #运行地址
stack_addr=0x10000 #堆栈地址
uc.mem_map(address,2*1024*1024) #分配内存空间
with open("Petition","rb") as f:
code=f.read()
uc.mem_write(address,code) #程序写入
uc.reg_write(UC_X86_REG_EIP,0x1040) #直接从0x1040地址 即start地址处 开始模拟执行
uc.reg_write(UC_X86_REG_ESP,stack_addr) #esp栈顶指针指向堆栈地址
uc.mem_write(0x10c6,b'\x90\x90\x90\x90\x90') #NOP掉scanf
uc.mem_write(0x110b,b'\x90\x90\x90\x90\x90') #NOP掉printf
uc.hook_add(UC_HOOK_CODE,hook_scanf) #添加钩子
uc.hook_add(UC_HOOK_CODE,hook_code)
try:
uc.emu_start(0x1040,0x29c4)
except Exception as e:
print(e)
uc.emu_stop()
tmp=b''
for i in range(42):
for j in range(32,128):
flag=tmp+bytes([j])
print(flag)
param=0
init()
if(param==i+1):
tmp+=bytes([j])
break
init()
print(tmp)