Unicorn

一、功能介绍

1.初始化

(1)前置模块及架构

1
2
3
4
# -*- coding:UTF-8 -*-
from __future__ import print_function
from unicorn import *
from unicorn.x86_const import *

unicorn.x86_const这个模块是可以改变的,可以换成以下多种架构

image-20211118211823271

(2)main函数初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
print("Emulate amd64 code")
uc = Uc(UC_ARCH_X86, UC_MODE_64)
binary = "./binary"

BASE = 0
CODE = BASE + 0x0
CODE_SIZE = 2 * 1024 * 1024

# mov r8,0x123
# mov r9,0x456
CODE_DATA = b"\x49\xc7\xc0\x23\x01\x00\x00" + b"\x49\xc7\xc1\x56\x04\x00\x00"
#CODE_DATA = getCODEFromBinary(binary)

STACK = 0x7F00000000
STACK_SIZE = 2 * 1024 * 1024

FS = 0x7FF0000000
FS_SIZE = 0x100000

#初始化UC
initUC(uc,CODE,CODE_SIZE, STACK, STACK_SIZE)

UC_ARCH_X86:代表x86架构

UC_MODE_64:代表64位

UC_MODE_32:代表32位

CODE:表示装载进入内存的代码数据地址

CODE_DATA:表示自定义的代码或者从binary中获取的相关代码数据

STACK:表栈的数据地址

(3)其他初始化

①内存初始化

栈和代码数据,向CODE中写入CODE_DATA

1
2
3
4
# 获取一块内存用来保存程序的所有数据
uc.mem_map(CODE, CODE_SIZE, UC_PROT_ALL)
uc.mem_map(STACK, STACK_SIZE, UC_PROT_ALL)
uc.mem_write(CODE, CODE_DATA)

②寄存器初始化

在initUC中被调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def initReg(uc,STACK=0x7F00000000,RAX_DATA=0,RBX_DATA=0,
RCX_DATA=0,RDX_DATA=0,RSI_DATA=0,
RDI_DATA=0,R8_DATA=0,R9_DATA=0,
R10_DATA=0,R11_DATA=0,R12_DATA=0,
R13_DATA=0,R14_DATA=0,R15_DATA=0):
uc.reg_write(UC_X86_REG_RAX, RAX_DATA)
uc.reg_write(UC_X86_REG_RBX, RBX_DATA)
uc.reg_write(UC_X86_REG_RCX, RCX_DATA)
uc.reg_write(UC_X86_REG_RDX, RDX_DATA)
uc.reg_write(UC_X86_REG_RSI, RSI_DATA)
uc.reg_write(UC_X86_REG_RDI, RDI_DATA)
uc.reg_write(UC_X86_REG_R8, R8_DATA)
uc.reg_write(UC_X86_REG_R9, R9_DATA)
uc.reg_write(UC_X86_REG_R10, R10_DATA)
uc.reg_write(UC_X86_REG_R11, R11_DATA)
uc.reg_write(UC_X86_REG_R12, R12_DATA)
uc.reg_write(UC_X86_REG_R13, R13_DATA)
uc.reg_write(UC_X86_REG_R14, R14_DATA)
uc.reg_write(UC_X86_REG_R15, R15_DATA)
uc.reg_write(UC_X86_REG_RSP, STACK + 0x1000)

(4)其他常用功能函数

①从binary中获取代码

1
2
3
4
def getCODEFromBinary(binary):
with open(binary, "rb") as f:
CODE_DATA = f.read()
return CODE_DATA

②单步显示结果

1
2
3
4
5
6
7
8
9
10
def hook_OneStep(uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))
#利用ret来跳过printf函数,类似还可以跳过其他函数
if address == 0x640: # printf
rsp = uc.reg_read(UC_X86_REG_RSP)
retn_addr = u64(uc.mem_read(rsp,8))
uc.reg_write(UC_X86_REG_RIP, retn_addr)
#跳过某条指令,这里跳过rdrand rax指令
if code == b"\x48\x0F\xC7\xF0":
uc.reg_write(UC_X86_REG_RIP, address + 4)

然后这个需要在之后加入到uc的回调函数中,一般在main函数中添加,如下:

1
uc.hook_add(UC_HOOK_CODE, hook_OneStep, None, CODE, len(CODE_DATA))

添加hook_OneStep函数,使得CODE~CODE+len(CODE_DATA)中每条指令执行时都调用该hook_OneStep函数,便于我们观察运行过程。

③整块显示结果

1
2
def hook_block(uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size))

这个也是需要添加的

1
uc.hook_add(UC_HOOK_BLOCK, hook_block)

我们运行代码可以是一块一块的:

1
2
3
# 运行一段代码
uc.emu_start(CODE, CODE + 0x7*2)
uc.emu_start(CODE + 0x7 * 2, CODE + 0x7 * 4)

这个整块的block函数就是用在每块代码开始的时候运行该块函数

④无效内存访问的回调函数

1
2
3
4
5
6
7
8
9
10
11
12
def hook_MemInvalid(uc, access, address, size, value, user_data):
if access == UC_MEM_WRITE_UNMAPPED:
print(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x"%(address, size, value))
# map this memory in with 2MB in size
uc.mem_map(0xaaaa0000, 2 * 1024*1024)
# return True to indicate we want to continue emulation
return True
else:
# return False to indicate we want to stop emulation
return False

#uc.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_MemInvalid)

⑤内存访问的回调函数

1
2
3
4
5
6
7
8
def hook_MemAccess(uc, access, address, size, value, user_data):
if access == UC_MEM_WRITE:
print(">>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x"%(address, size, value))
else: # READ
print(">>> Memory is being READ at 0x%x, data size = %u"%(address, size))

#uc.hook_add(UC_HOOK_MEM_WRITE, hook_MemAccess)
#uc.hook_add(UC_HOOK_MEM_READ, hook_MemAccess)

2.启动unicorn

通常使用如下代码启动

1
2
3
4
5
6
try:
# 运行一段代码
uc.emu_start(CODE, CODE + 0x7*3)
uc.emu_start(CODE + 0x7 * 3, CODE + 0x7 * 4)
except UcError as e:
print("ERROR: %s" % e)

这里就代表运行了两块代码,分别为CODE ~ CODE + 0x7*3CODE + 0x7 * 3 ~ CODE + 0x7 * 4

参考:

使用unicorn来自动化解题 - 安全客,安全资讯平台 (anquanke.com)

CTFWIKI

3.调试器

详情参考:

[原创] Unicorn 在 Android 的应用-Android安全-看雪论坛-安全社区|安全招聘|bbs.pediy.com

这里将一些东西改了下,原本无名侠师傅的调试器代码中就自带不同ARCH和MODE,只不过分享上述文章时,改掉了只有ARM,这里我加了回去,顺带加入了mips架构,另外还加了一些个人常用的命令,并且在linux下的某些\n换行问题(split函数),这里也一并修改了。然后从一个PWN选手的角度,加了一点功能,输入help查看所有功能

uniDbg.py

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
import re

from unicorn import *
from uniDbg_REG import *
import sys
import hexdump
import capstone as cp

BPT_EXECUTE = 1
BPT_MEMREAD = 2
UDBG_MODE_ALL = 1
UDBG_MODE_FAST = 2



def str2int(s):
if s.startswith('0x') or s.startswith("0X"):
return int(s[2:], 16)
return int(s)

def numToStr(num):
amountByte = 0
str = ""
while True:
if (num == 0):
amountByte = 1
break
elif (num >= pow(16, amountByte * 2)):
amountByte += 1
continue
else:
break
for i in range(1, amountByte + 1):
newNum = num >> 8
myChr = num - newNum * 0x100
str += chr(myChr)
num = newNum
str = ''.join(reversed(str))
return str

def advance_dump(data, base):
PY3K = sys.version_info >= (3, 0)
generator = hexdump.genchunks(data, 16)
retstr = ''
for addr, d in enumerate(generator):
# 00000000:
line = '%08X: ' % (base + addr * 16)
# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
dumpstr = hexdump.dump(d)
line += dumpstr[:8 * 3]
if len(d) > 8: # insert separator if needed
line += ' ' + dumpstr[8 * 3:]
# ................
# calculate indentation, which may be different for the last line
pad = 2
if len(d) < 16:
pad += 3 * (16 - len(d))
if len(d) <= 8:
pad += 1
line += ' ' * pad

for byte in d:
# printable ASCII range 0x20 to 0x7E
if not PY3K:
byte = ord(byte)
if 0x20 <= byte <= 0x7E:
line += chr(byte)
else:
line += '.'
retstr += line + '\n'
return retstr


def _dbg_trace(mu, address, size, self):
self._tracks.append(address)
if not self._is_step and self._tmp_bpt == 0:
if address not in self._list_bpt:
return

if self._tmp_bpt != address and self._tmp_bpt != 0:
return

return _dbg_trace_internal(mu, address, size, self)


def _dbg_memory(mu, access, address, length, value, self):
pc = mu.reg_read(arm_const.UC_ARM_REG_PC)
print("memory error: pc: %x access: %x address: %x length: %x value: %x" %
(pc, access, address, length, value))
_dbg_trace_internal(mu, pc, 4, self)
mu.emu_stop()
return True


def _dbg_trace_internal(mu, address, size, self):
self._is_step = False
print("======================= Registers =======================")
self.dump_reg()
print("======================= Disassembly =====================")
self.dump_asm(address, size * self.dis_count)

while True:
raw_command = input(">")
if raw_command == '':
raw_command = self._last_command
self._last_command = raw_command
command = []
for c in raw_command.split():
if c != "":
command.append(c)
try:
if command[0] == 'set':
if command[1] == 'reg': # set reg regname value
self.write_reg(command[2], str2int(command[3]))
elif command[1] == 'bpt':
self.add_bpt(str2int(command[2]))
elif command[1].startswith('0x') or command[1].startswith('0X'):
self.write_mem(str2int(command[1]),str2int(command[2]))
else:
print("[Debugger Error]command error see help.")
elif command[0].startswith('x'):
view_amount = int(self.read_ex_viewRow(command[0])[2:],10)
if(view_amount == 0):
view_amount = 1
read_fmt_str = self.read_ex_last(command[0])
if(read_fmt_str == 'gx'):
content = self.read_mem(str2int(command[1]), view_amount * 8)
self.fmt_print_mem(read_fmt_str,str2int(command[1]),content)
elif(read_fmt_str == 'wx'):
content = self.read_mem(str2int(command[1]), view_amount * 4)
self.fmt_print_mem(read_fmt_str,str2int(command[1]), content)
elif(read_fmt_str == 's'):
content = self.read_mem(str2int(command[1]), view_amount * 32)
self.fmt_print_mem(read_fmt_str,str2int(command[1]), content)
elif(read_fmt_str == 'si'):
self.dump_asm(str2int(command[1]), view_amount * self.dis_count)

elif command[0] == 's' or command[0] == 'step':
# self._tmp_bpt = address + size
self._tmp_bpt = 0
self._is_step = True
break
elif command[0] == 'n' or command[0] == 'next':
self._tmp_bpt = address + size
self._is_step = False
break
elif command[0] == 'r' or command[0] == 'run':
self._tmp_bpt = 0
self._is_step = False
break
elif command[0] == 'dump':
if len(command) >= 3:
nsize = str2int(command[2])
else:
nsize = 4 * 16
self.dump_mem(str2int(command[1]), nsize)
elif command[0] == 'i' or command[0] == 'info':
if command[1] == 'b' or command[1] == 'bpt':
self.list_bpt()
elif command[0] == 'd' or command[0] == 'delete':
self.del_bpt(str2int(command[1]))
elif command[0] == 'stop':
break
elif command[0] == 't':
self._castone = self._capstone_thumb
print("======================= Disassembly =====================")
self.dump_asm(address, size * self.dis_count)
elif command[0] == 'a':
self._castone = self._capstone_arm
print("======================= Disassembly =====================")
self.dump_asm(address, size * self.dis_count)
elif command[0] == 'f':
print(" == recent ==")
for i in self._tracks[-10:-1]:
print(self.sym_handler(i))
elif command[0] == 'help':
self.show_help()
elif command[0] == 'context':
print("======================= Registers =======================")
self.dump_reg()
print("======================= Disassembly =====================")
self.dump_asm(address, size * self.dis_count)
else:
print("Command Not Found!")

except:
print("[Debugger Error]command error see help.")


class UnicornDebugger:
def __init__(self, mu, mode=UDBG_MODE_ALL):
self._ex_seg = re.compile(r'x/\d+')
self._tracks = []
self._mu = mu
self._arch = mu._arch
self._mode = mu._mode
self._list_bpt = []
self._tmp_bpt = 0
self._error = ''
self._last_command = ''
self.dis_count = 5
self._is_step = False
self.sym_handler = self._default_sym_handler
self._capstone_arch = None
self._capstone_mode = None

# if self._arch != UC_ARCH_ARM:
# mu.emu_stop()
# raise RuntimeError("arch:%d is not supported! " % self._arch)
if self._arch == UC_ARCH_ARM:
capstone_arch = cp.CS_ARCH_ARM
elif self._arch == UC_ARCH_ARM64:
capstone_arch = cp.CS_ARCH_ARM64
elif self._arch == UC_ARCH_X86:
capstone_arch = cp.CS_ARCH_X86
elif self._arch == UC_ARCH_MIPS:
capstone_arch = cp.CS_ARCH_MIPS
else:
mu.emu_stop()
raise RuntimeError("arch:%d is not supported! " % self._arch)

if self._mode == UC_MODE_THUMB:
capstone_mode = cp.CS_MODE_THUMB
elif self._mode == UC_MODE_ARM:
capstone_mode = cp.CS_MODE_ARM
elif self._mode == UC_MODE_MIPS32:
capstone_mode = cp.CS_MODE_MIPS32
elif self._mode == UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN:
capstone_mode = cp.CS_MODE_MIPS32 + cp.CS_MODE_BIG_ENDIAN
elif self._mode == UC_MODE_MIPS64:
capstone_mode = cp.CS_MODE_MIPS64
elif self._mode == UC_MODE_MIPS64 + UC_MODE_BIG_ENDIAN:
capstone_mode = cp.CS_MODE_MIPS64 + cp.CS_MODE_BIG_ENDIAN
elif self._mode == UC_MODE_32:
capstone_mode = cp.CS_MODE_32
elif self._mode == UC_MODE_64:
capstone_mode = cp.CS_MODE_64
else:
mu.emu_stop()
raise RuntimeError("mode:%d is not supported! " % self._mode)

self._capstone_mode = cp.Cs(capstone_arch, capstone_mode)
self._capstone_arch = cp.Cs(capstone_arch, capstone_mode)
self._capstone = self._capstone_mode

if mode == UDBG_MODE_ALL:
mu.hook_add(UC_HOOK_CODE, _dbg_trace, self)

mu.hook_add(UC_HOOK_MEM_UNMAPPED, _dbg_memory, self)
mu.hook_add(UC_HOOK_MEM_FETCH_PROT, _dbg_memory, self)

self._regs = REG_TABLE[self._arch]

def dump_mem(self, addr, size):
data = self._mu.mem_read(addr, size)
print(advance_dump(data, addr))

def dump_asm(self, addr, size):
md = self._capstone
code = self._mu.mem_read(addr, size)
count = 0
for ins in md.disasm(code, addr):
if count >= self.dis_count:
break
print("%s:\t%s\t%s" % (self.sym_handler(ins.address), ins.mnemonic, ins.op_str))

def dump_reg(self):
result_format = ""
count = 0
for rid in self._regs:
rname = self._regs[rid]
value = self._mu.reg_read(rid)
if count < 4:
result_format = result_format + rname + '=' + hex(value) + '\t'
count += 1
else:
count = 1
result_format += '\n' + rname + '=' + hex(value) + '\t'
print(result_format)

def write_reg(self, reg_name, value):
for rid in self._regs:
rname = self._regs[rid]
if rname == reg_name:
self._mu.reg_write(rid, value)
return
print("[Debugger Error] Reg not found:%s " % reg_name)

def write_mem(self,addr,value):
str = numToStr(value)
self._mu.mem_write(addr,str.encode('UTF-8'))
return

def read_mem(self,addr,length):
str = self._mu.mem_read(addr,length)
return str

def show_help(self):
help_info = """
# commands
# set reg <regname> <value>
# set bpt <addr>
# n[ext]
# s[etp]
# r[un]
# dump <addr> <size>
# i[nfo] b[pt]
# d[elete] <bpt_Idx>
# stop
# a/t change arm/thumb
# f show ins flow
# x/<rowAmount><gx/s/si/wx>
"""
print(help_info)

def list_bpt(self):
for idx in range(len(self._list_bpt)):
print("[%d] %s" % (idx, self.sym_handler(self._list_bpt[idx])))

def add_bpt(self, addr):
self._list_bpt.append(addr)

def del_bpt(self, idx):
del self._list_bpt[idx]
#self._list_bpt.remove(addr)

def get_tracks(self):
for i in self._tracks[-100:-1]:
# print (self.sym_handler(i))
pass
return self._tracks

def _default_sym_handler(self, address):
return hex(address)

def set_symbol_name_handler(self, handler):
self.sym_handler = handler

def read_ex_viewRow(self,str):
return self._ex_seg.findall(str)[0]

def read_ex_last(self,str):
return self._ex_seg.sub('',str)

def fmt_print_mem(self,fmt_str,addr,content):
if(self._mode >= UC_MODE_BIG_ENDIAN):
ending = 'big'
else:
ending = 'little'
count = 0
while(count < len(content)):
if(fmt_str == 'gx'):
print("0x{:0>16x}\t0x{:0>16x}\t0x{:0>16x}".format(addr,int.from_bytes(content[0+count:8+count],byteorder = ending),
int.from_bytes(content[8+count:16+count],byteorder = ending)))
count += 8
elif(fmt_str == 'wx'):
print("0x{:0>8x}\t0x{:0>8x}\t0x{:0>8x}".format(addr,int.from_bytes(content[0+count:4+count],byteorder = ending),
int.from_bytes(content[4+count:8+count],byteorder = ending)))
count += 4
elif(fmt_str == 's'):
print("0x{:0>16x}\t{:>32s}".format(addr,numToStr(int.from_bytes(content[0+count:32+count],byteorder = ending))))
count += 32

uniDbg_REG.py

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from unicorn import *

REG_ARM = {arm_const.UC_ARM_REG_R0: "R0",
arm_const.UC_ARM_REG_R1: "R1",
arm_const.UC_ARM_REG_R2: "R2",
arm_const.UC_ARM_REG_R3: "R3",
arm_const.UC_ARM_REG_R4: "R4",
arm_const.UC_ARM_REG_R5: "R5",
arm_const.UC_ARM_REG_R6: "R6",
arm_const.UC_ARM_REG_R7: "R7",
arm_const.UC_ARM_REG_R8: "R8",
arm_const.UC_ARM_REG_R9: "R9",
arm_const.UC_ARM_REG_R10: "R10",
arm_const.UC_ARM_REG_R11: "R11",
arm_const.UC_ARM_REG_R12: "R12",
arm_const.UC_ARM_REG_R13: "R13",
arm_const.UC_ARM_REG_R14: "R14",
arm_const.UC_ARM_REG_R15: "R15",
arm_const.UC_ARM_REG_PC: "PC",
arm_const.UC_ARM_REG_SP: "SP",
arm_const.UC_ARM_REG_LR: "LR"
}

REG_ARM64 = {arm64_const.UC_ARM64_REG_X0: "X0",
arm64_const.UC_ARM64_REG_X1: "X1",
arm64_const.UC_ARM64_REG_X2: "X2",
arm64_const.UC_ARM64_REG_X3: "X3",
arm64_const.UC_ARM64_REG_X4: "X4",
arm64_const.UC_ARM64_REG_X5: "X5",
arm64_const.UC_ARM64_REG_X6: "X6",
arm64_const.UC_ARM64_REG_X7: "X7",
arm64_const.UC_ARM64_REG_X8: "X8",
arm64_const.UC_ARM64_REG_X9: "X9",
arm64_const.UC_ARM64_REG_X10: "X10",
arm64_const.UC_ARM64_REG_X11: "X11",
arm64_const.UC_ARM64_REG_X12: "X12",
arm64_const.UC_ARM64_REG_X13: "X13",
arm64_const.UC_ARM64_REG_X14: "X14",
arm64_const.UC_ARM64_REG_X15: "X15",
arm64_const.UC_ARM64_REG_X16: "X16",
arm64_const.UC_ARM64_REG_X17: "X17",
arm64_const.UC_ARM64_REG_X18: "X18",
arm64_const.UC_ARM64_REG_X19: "X19",
arm64_const.UC_ARM64_REG_X20: "X20",
arm64_const.UC_ARM64_REG_X21: "X21",
arm64_const.UC_ARM64_REG_X22: "X22",
arm64_const.UC_ARM64_REG_X23: "X23",
arm64_const.UC_ARM64_REG_X24: "X24",
arm64_const.UC_ARM64_REG_X25: "X25",
arm64_const.UC_ARM64_REG_X26: "P26",
arm64_const.UC_ARM64_REG_X27: "X27",
arm64_const.UC_ARM64_REG_X28: "X28",
arm64_const.UC_ARM64_REG_X29: "X29",
arm64_const.UC_ARM64_REG_SP: "SP",
arm64_const.UC_ARM64_REG_PC: "PC",
}


REG_AMD64 = {x86_const.UC_X86_REG_RAX: "RAX",
x86_const.UC_X86_REG_RBX: "RBX",
x86_const.UC_X86_REG_RCX: "RCX",
x86_const.UC_X86_REG_RDX: "RDX",
x86_const.UC_X86_REG_RDI: "RDI",
x86_const.UC_X86_REG_RSI: "RSI",
x86_const.UC_X86_REG_R8: "R8",
x86_const.UC_X86_REG_R9: "R9",
x86_const.UC_X86_REG_R10: "R10",
x86_const.UC_X86_REG_R11: "R11",
x86_const.UC_X86_REG_R12: "R12",
x86_const.UC_X86_REG_R13: "R13",
x86_const.UC_X86_REG_R14: "R14",
x86_const.UC_X86_REG_R15: "R15",
x86_const.UC_X86_REG_RBP: "RBP",
x86_const.UC_X86_REG_RSP: "RSP",
x86_const.UC_X86_REG_RIP: "RIP",
}

REG_MIPS = {
mips_const.UC_MIPS_REG_ZERO: "ZERO",
mips_const.UC_MIPS_REG_V0: "V0",
mips_const.UC_MIPS_REG_V1: "V1",
mips_const.UC_MIPS_REG_AT: "AT",
mips_const.UC_MIPS_REG_A0: "A0",
mips_const.UC_MIPS_REG_A1: "A1",
mips_const.UC_MIPS_REG_A2: "A2",
mips_const.UC_MIPS_REG_A3: "A3",
mips_const.UC_MIPS_REG_T0: "T0",
mips_const.UC_MIPS_REG_T1: "T1",
mips_const.UC_MIPS_REG_T2: "T2",
mips_const.UC_MIPS_REG_T3: "T3",
mips_const.UC_MIPS_REG_T4: "T4",
mips_const.UC_MIPS_REG_T5: "T5",
mips_const.UC_MIPS_REG_T6: "T6",
mips_const.UC_MIPS_REG_T7: "T7",
mips_const.UC_MIPS_REG_S0: "S0",
mips_const.UC_MIPS_REG_S1: "S1",
mips_const.UC_MIPS_REG_S2: "S2",
mips_const.UC_MIPS_REG_S3: "S3",
mips_const.UC_MIPS_REG_S4: "S4",
mips_const.UC_MIPS_REG_S5: "S5",
mips_const.UC_MIPS_REG_S6: "S6",
mips_const.UC_MIPS_REG_S7: "S7",
mips_const.UC_MIPS_REG_T8: "T8",
mips_const.UC_MIPS_REG_T9: "T9",
mips_const.UC_MIPS_REG_GP: "GP",
mips_const.UC_MIPS_REG_SP: "SP",
mips_const.UC_MIPS_REG_FP: "FP",
mips_const.UC_MIPS_REG_RA: "RA",
}


REG_TABLE = {UC_ARCH_ARM: REG_ARM, UC_ARCH_X86: REG_AMD64,UC_ARCH_MIPS: REG_MIPS,UC_ARCH_ARM64:REG_ARM64}

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
THUMB = asm('''
mov rax,0x10
mov rdx,0x20
''')
ADDRESS = 0x10000
#注意大端序的话需要加上设置成UC_MODE_64+UC_MODE_BIG_ENDIAN
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.mem_map(ADDRESS, 2 * 0x10000)
mu.mem_write(ADDRESS, THUMB)

#debugger attach
#需要注意的是,这个需要放在hook函数之前才行,不然可能出现一些意想不到的错误
udbg = UnicornDebugger(mu)
udbg.add_bpt(ADDRESS)

# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(THUMB))

调试起来之后,输入help获取相关的命令信息

image-20220221185425958

添加命令:

其实无名侠师傅的模板已经写得挺好了,我们只要往上加就行了,主要还是设置UnicornDebugger这个类,在中断处理这里添加命令

image-20220221185630168

4.模板

我们用unicorn还是使用模板来进行调试更好,这样能加快分析的速度,这里就给出自己的一个常用模板

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# -*- coding:UTF-8 -*-
from __future__ import print_function
from pwn import*
from unicorn import *
from unicorn.x86_const import *
from unicornDbg import uniDbg
import ipdb

context.arch = 'amd64'

def hook_OneStep(uc, address, size, user_data):
#print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address, size))
# 利用ret来跳过printf函数,类似还可以跳过其他函数
if address == 0x640: # printf
rsp = uc.reg_read(UC_X86_REG_RSP)
retn_addr = u64(uc.mem_read(rsp, 8))
uc.reg_write(UC_X86_REG_RIP, retn_addr)
# 跳过某条指令,这里跳过mov rax,1指令
CODE = uc.mem_read(address,7)
if CODE == asm('''mov rax,1'''):
#if CODE == b"\xb9\x00\x00\x00\x00":
uc.reg_write(UC_X86_REG_RIP,address + 7)


def hook_code(mu, address, size, user_data):
print('>>> Tracing instruction at 0x%x, instruction size = 0x%x' %(address, size))


def initReg(uc, STACK_ADDRESS=0x7F00000000, RAX=0, RBX=0,
RCX=0, RDX=0, RSI=0,
RDI=0, R8=0, R9=0,
R10=0, R11=0, R12=0,
R13=0, R14=0, R15=0):
uc.reg_write(UC_X86_REG_RAX, RAX)
uc.reg_write(UC_X86_REG_RBX, RBX)
uc.reg_write(UC_X86_REG_RCX, RCX)
uc.reg_write(UC_X86_REG_RDX, RDX)
uc.reg_write(UC_X86_REG_RSI, RSI)
uc.reg_write(UC_X86_REG_RDI, RDI)
uc.reg_write(UC_X86_REG_R8, R8)
uc.reg_write(UC_X86_REG_R9, R9)
uc.reg_write(UC_X86_REG_R10, R10)
uc.reg_write(UC_X86_REG_R11, R11)
uc.reg_write(UC_X86_REG_R12, R12)
uc.reg_write(UC_X86_REG_R13, R13)
uc.reg_write(UC_X86_REG_R14, R14)
uc.reg_write(UC_X86_REG_R15, R15)
uc.reg_write(UC_X86_REG_RSP, STACK_ADDRESS + 0x100)
uc.reg_write(UC_X86_REG_RBP, STACK_ADDRESS + 0x200)


def initUC(uc, CODE_DATA,CODE_ADDRESS=0x40000,CODE_SIZE=2 * 1024 * 1024,
STACK_ADDRESS=0x7F00000000, STACK_SIZE=2 * 1024 * 1024):
# 获取一块内存用来保存程序的所有数据
uc.mem_map(CODE_ADDRESS, CODE_SIZE, UC_PROT_ALL)
uc.mem_map(STACK_ADDRESS, STACK_SIZE, UC_PROT_ALL)
uc.mem_write(CODE_ADDRESS, CODE_DATA)



#".text"/".bss"/".got.plt" 即各段名称
def getCODEFromBinary(binary,start = None,count=0x100):
elf = ELF(binary, checksec=False)
if(start == None):
with open(binary, "rb") as f:
CODE_DATA = f.read()
return CODE_DATA
elif( isinstance(start,str)):
start_addr = elf.get_section_by_name(start).header.sh_addr
count = elf.get_section_by_name(start).header.sh_size
else:
start_addr = start
CODE_DATA = elf.read(start_addr,count)
return CODE_DATA



if __name__ == '__main__':
print("Emulate amd64 code")
uc = Uc(UC_ARCH_X86, UC_MODE_64)
binary = "/home/hacker/Desktop/test/note/note"

CODE_ADDRESS = 0x400000
START_ADDRESS = CODE_ADDRESS + 0x0

CODE_DATA = asm('''
mov rax,1
mov rdx,1
mov rcx,1
''')
#CODE_DATA = getCODEFromBinary(binary,".text")

# 初始化UC
initUC(uc, CODE_DATA,CODE_ADDRESS)
initReg(uc, RSI=0x20, RCX=0x40)

#需要放在hook函数之前
udbg = uniDbg.UnicornDebugger(uc)
udbg.add_bpt(START_ADDRESS)

# 添加hook_OneStep函数,使得CODE_ADDRESS~CODE_ADDRESS+len(CODE_DATA)中每条指令执行时
# 都调用该hook_OneStep函数
uc.hook_add(UC_HOOK_CODE, hook_OneStep, None, START_ADDRESS,START_ADDRESS+len(CODE_DATA))

try:
# 运行一段代码
uc.emu_start(START_ADDRESS, START_ADDRESS + 0x30)
except UcError as e:
print("ERROR: %s" % e)