run.py 11.1 KB
Newer Older
George Hotz's avatar
George Hotz committed
1
#!/usr/bin/env python3
George Hotz's avatar
George Hotz committed
2
import os
George Hotz's avatar
George Hotz committed
3
import sys
4
import math
George Hotz's avatar
George Hotz committed
5
import struct
George Hotz's avatar
George Hotz committed
6
import binascii
George Hotz's avatar
George Hotz committed
7
import traceback
George Hotz's avatar
George Hotz committed
8
from elftools.elf.elffile import ELFFile
George Hotz's avatar
George Hotz committed
9 10
from capstone import *
md = Cs(CS_ARCH_MIPS, CS_MODE_32 + CS_MODE_BIG_ENDIAN)
George Hotz's avatar
George Hotz committed
11
tracelevel = int(os.getenv("TRACE", 0))
George Hotz's avatar
George Hotz committed
12

13
from termcolor import colored, cprint
George Hotz's avatar
George Hotz committed
14 15 16
from hexdump import hexdump
from unicorn import *
from unicorn.mips_const import *
George Hotz's avatar
George Hotz committed
17
from rangetree import RangeTree
George Hotz's avatar
George Hotz committed
18 19 20

mu = Uc(UC_ARCH_MIPS, UC_MODE_32 + UC_MODE_BIG_ENDIAN)

21 22 23
# memory trie
# register trie

George Hotz's avatar
George Hotz committed
24 25 26
mregs = [UC_MIPS_REG_AT, UC_MIPS_REG_V0, UC_MIPS_REG_V1, UC_MIPS_REG_A0, UC_MIPS_REG_A1, UC_MIPS_REG_A2, UC_MIPS_REG_A3]
regs = ["at", "v0", "v1", "a0", "a1", "a2", "a3"]

George Hotz's avatar
George Hotz committed
27
heap_start = 0x20000000 # 0x20000000-0x30000000
George Hotz's avatar
George Hotz committed
28 29 30 31 32
# input oracle              @ 0x30000000
# output oracle             @ 0x30000800
# preimage oracle (write)   @ 0x30001000
# preimage oracle (read)    @ 0x31000000-0x32000000 (16 MB)

George Hotz's avatar
George Hotz committed
33
brk_start = 0x40000000  # 0x40000000-0x80000000
George Hotz's avatar
George Hotz committed
34
stack_start = 0x7FFFF000
George Hotz's avatar
George Hotz committed
35

36 37 38 39
# hmm, very slow
icount = 0
bcount = 0

40 41
instrumenting = False
instrumenting_all = False
George Hotz's avatar
George Hotz committed
42
instructions_seen = set()
43 44 45 46
def hook_code_simple(uc, address, size, user_data):
  global icount, bcount
  #assert size == 4
  try:
47 48 49 50 51 52
    newicount = size//4
    if bcount%10000 == 0 or instrumenting_all:
      if size > 0:
        dat = next(md.disasm(uc.mem_read(address, size), address))
      else:
        dat = "EMPTY BASIC BLOCK?!?"
George Hotz's avatar
George Hotz committed
53 54
      #instructions_seen.add(dat.mnemonic)
      #print(sorted(list(instructions_seen)))
George Hotz's avatar
George Hotz committed
55
      print("%10d(%2d): %8x %-80s %s" % (icount, newicount, address, r[address] if address in r else "UNKNOWN", dat))
56
    icount += newicount
57 58 59 60 61 62 63 64
    bcount += 1
    return True
  except Exception as e:
    raise e
  except:
    raise Exception

def start_instrumenting():
65 66 67 68 69
  global instrumenting, instrumenting_all
  if not instrumenting:
    if tracelevel >= 2:
      mu.hook_add(UC_HOOK_CODE, hook_code_simple, user_data=mu)
    elif tracelevel == 1:
70
      mu.hook_add(UC_HOOK_BLOCK, hook_code_simple, user_data=mu)
George Hotz's avatar
George Hotz committed
71
    if tracelevel >= 4:
72 73
      instrumenting_all = True
    instrumenting = True
74

75 76
tfd = 10
files = {}
George Hotz's avatar
George Hotz committed
77
fcnt = 0
George Hotz's avatar
George Hotz committed
78
step = 0
George Hotz's avatar
George Hotz committed
79
def hook_interrupt(uc, intno, user_data):
George Hotz's avatar
George Hotz committed
80
  global heap_start, fcnt, files, tfd, step
George Hotz's avatar
George Hotz committed
81 82 83
  pc = uc.reg_read(UC_MIPS_REG_PC)
  if intno == 17:
    syscall_no = uc.reg_read(UC_MIPS_REG_V0)
George Hotz's avatar
George Hotz committed
84 85
    #print("step:%d pc:%0x v0:%d" % (step, pc, syscall_no))
    step += 1
86
    uc.reg_write(UC_MIPS_REG_V0, 0)
George Hotz's avatar
George Hotz committed
87
    uc.reg_write(UC_MIPS_REG_A3, 0)
George Hotz's avatar
George Hotz committed
88 89 90 91 92 93 94 95 96

    if syscall_no == 4020:
      oracle_hash = binascii.hexlify(uc.mem_read(0x30001000, 0x20)).decode('utf-8')
      dat = open("/tmp/eth/0x"+oracle_hash, "rb").read()
      #print("oracle:", oracle_hash, len(dat))
      uc.mem_write(0x31000000, struct.pack(">I", len(dat)))
      uc.mem_write(0x31000004, dat)
      return True

George Hotz's avatar
George Hotz committed
97 98 99 100 101
    if syscall_no == 4004:
      # write
      fd = uc.reg_read(UC_MIPS_REG_A0)
      buf = uc.reg_read(UC_MIPS_REG_A1)
      count = uc.reg_read(UC_MIPS_REG_A2)
102
      #print("write(%d, %x, %d)" % (fd, buf, count))
103 104 105 106 107 108 109 110
      if fd == 1:
        # stdout
        os.write(fd, colored(uc.mem_read(buf, count).decode('utf-8'), 'green').encode('utf-8'))
      elif fd == 2:
        # stderr
        os.write(fd, colored(uc.mem_read(buf, count).decode('utf-8'), 'red').encode('utf-8'))
      else:
        os.write(fd, uc.mem_read(buf, count))
George Hotz's avatar
George Hotz committed
111
      uc.reg_write(UC_MIPS_REG_A3, 0)
112 113
      if fd == 2:
        start_instrumenting()
George Hotz's avatar
George Hotz committed
114 115
      return True

George Hotz's avatar
George Hotz committed
116 117 118 119 120 121
    if syscall_no == 4218:
      # madvise
      return
    elif syscall_no == 4194:
      # rt_sigaction
      return
George Hotz's avatar
George Hotz committed
122 123 124
    elif syscall_no == 4195:
      # rt_sigprocmask
      return
George Hotz's avatar
George Hotz committed
125 126 127
    elif syscall_no == 4055:
      # fcntl
      return
George Hotz's avatar
George Hotz committed
128 129 130
    elif syscall_no == 4220:
      # fcntl64
      return
George Hotz's avatar
George Hotz committed
131 132 133 134 135 136
    elif syscall_no == 4249:
      # epoll_ctl
      return
    elif syscall_no == 4263:
      # clock_gettime
      return
George Hotz's avatar
George Hotz committed
137 138 139 140 141 142 143 144 145 146 147 148
    elif syscall_no == 4326:
      # epoll_create1
      return
    elif syscall_no == 4328:
      # pipe2
      return
    elif syscall_no == 4206:
      # sigaltstack
      return
    elif syscall_no == 4222:
      # gettid
      return
George Hotz's avatar
George Hotz committed
149 150 151
    elif syscall_no == 4166:
      # nanosleep
      return
George Hotz's avatar
George Hotz committed
152

George Hotz's avatar
George Hotz committed
153 154 155
    if syscall_no == 4005:
      filename = uc.reg_read(UC_MIPS_REG_A0)
      print('open("%s")' % uc.mem_read(filename, 0x100).split(b"\x00")[0].decode('utf-8'))
156
      uc.reg_write(UC_MIPS_REG_V0, 4)
George Hotz's avatar
George Hotz committed
157 158 159
    elif syscall_no == 4045:
      print("brk", hex(brk_start))
      uc.reg_write(UC_MIPS_REG_V0, brk_start)
George Hotz's avatar
George Hotz committed
160 161 162
    elif syscall_no == 4288:
      dfd = uc.reg_read(UC_MIPS_REG_A0)
      filename = uc.reg_read(UC_MIPS_REG_A1)
163 164 165
      filename = uc.mem_read(filename, 0x100).split(b"\x00")[0].decode('utf-8')
      files[tfd] = open(filename, "rb")
      uc.reg_write(UC_MIPS_REG_V0, tfd)
George Hotz's avatar
George Hotz committed
166
      print('openat("%s") = %d' % (filename, tfd))
167
      tfd += 1
George Hotz's avatar
George Hotz committed
168 169 170 171 172 173 174 175 176 177 178
    elif syscall_no == 4238:
      addr = uc.reg_read(UC_MIPS_REG_A0)
      print("futex", hex(addr))
      uc.mem_write(addr, b"\x00\x00\x00\x01")
      #raise Exception("not support")
      uc.reg_write(UC_MIPS_REG_V0, 1)
      uc.reg_write(UC_MIPS_REG_A3, 0)
      fcnt += 1
      if fcnt == 20:
        raise Exception("too much futex")
      return True
George Hotz's avatar
George Hotz committed
179 180
    elif syscall_no == 4120:
      print("clone not supported")
George Hotz's avatar
George Hotz committed
181 182
      #uc.reg_write(UC_MIPS_REG_V0, -1)
      uc.reg_write(UC_MIPS_REG_V0, 1238238)
George Hotz's avatar
George Hotz committed
183 184
      uc.reg_write(UC_MIPS_REG_A3, 0)
      return True
185 186 187
    elif syscall_no == 4006:
      fd = uc.reg_read(UC_MIPS_REG_A0)
      if fd >= 10:
George Hotz's avatar
George Hotz committed
188
        #print("close(%d)" % fd)
189 190 191
        files[fd].close()
        del files[fd]
      uc.reg_write(UC_MIPS_REG_V0, 0)
192 193 194 195
    elif syscall_no == 4003:
      fd = uc.reg_read(UC_MIPS_REG_A0)
      buf = uc.reg_read(UC_MIPS_REG_A1)
      count = uc.reg_read(UC_MIPS_REG_A2)
George Hotz's avatar
George Hotz committed
196 197
      # changing this works if we want smaller oracle
      #count = 4
198
      if fd == 4:
199 200
        val = b"2097152\n"
        uc.mem_write(buf, val)
George Hotz's avatar
George Hotz committed
201
        print("read", fd, hex(buf), count)
202
        uc.reg_write(UC_MIPS_REG_V0, len(val))
203 204 205
      else:
        ret = files[fd].read(count)
        uc.mem_write(buf, ret)
George Hotz's avatar
George Hotz committed
206
        #print("read", fd, hex(buf), count, len(ret))
207
        uc.reg_write(UC_MIPS_REG_V0, len(ret))
George Hotz's avatar
George Hotz committed
208
    elif syscall_no == 4246:
209
      a0 = uc.reg_read(UC_MIPS_REG_A0)
210 211 212 213
      if icount > 0:
        print("exit(%d) ran %.2f million instructions, %d binary searches" % (a0, icount/1_000_000, math.ceil(math.log2(icount))))
      else:
        print("exit(%d)" % a0)
George Hotz's avatar
George Hotz committed
214 215
      sys.stdout.flush()
      sys.stderr.flush()
George Hotz's avatar
George Hotz committed
216
      #os._exit(a0)
George Hotz's avatar
George Hotz committed
217 218 219 220
    elif syscall_no == 4090:
      a0 = uc.reg_read(UC_MIPS_REG_A0)
      a1 = uc.reg_read(UC_MIPS_REG_A1)
      a2 = uc.reg_read(UC_MIPS_REG_A2)
George Hotz's avatar
George Hotz committed
221 222 223 224
      a3 = uc.reg_read(UC_MIPS_REG_A3)
      a4 = uc.reg_read(UC_MIPS_REG_T0)
      a5 = uc.reg_read(UC_MIPS_REG_T1)
      print("mmap", hex(a0), hex(a1), hex(a2), hex(a3), hex(a4), hex(a5), "at", hex(heap_start))
George Hotz's avatar
George Hotz committed
225 226 227 228
      if a0 == 0:
        print("malloced new")
        #mu.mem_map(heap_start, a1)
        uc.reg_write(UC_MIPS_REG_V0, heap_start)
George Hotz's avatar
George Hotz committed
229
        heap_start += a1
George Hotz's avatar
George Hotz committed
230 231
      else:
        uc.reg_write(UC_MIPS_REG_V0, a0)
George Hotz's avatar
George Hotz committed
232
    else:
George Hotz's avatar
George Hotz committed
233
      print("syscall", syscall_no, hex(pc))
George Hotz's avatar
George Hotz committed
234 235 236 237 238 239 240
      jj = []
      for i,r in zip(mregs, regs):
        jj += "%s: %8x " % (r, uc.reg_read(i))
      print(''.join(jj))
    return True

  print("interrupt", intno, hex(pc))
George Hotz's avatar
George Hotz committed
241
  if intno != 17:
George Hotz's avatar
George Hotz committed
242
    raise unicorn.UcError(0)
George Hotz's avatar
George Hotz committed
243
  return True
George Hotz's avatar
George Hotz committed
244

George Hotz's avatar
George Hotz committed
245 246 247 248
cnt = 0
def hook_code(uc, address, size, user_data):
  global cnt
  cnt += 1
George Hotz's avatar
George Hotz committed
249 250

  """
George Hotz's avatar
George Hotz committed
251 252 253
  dat = mu.mem_read(address, size)
  if dat == "\x0c\x00\x00\x00" or dat == "\x00\x00\x00\x0c":
    raise Exception("syscall")
George Hotz's avatar
George Hotz committed
254
  """
George Hotz's avatar
George Hotz committed
255 256 257
  
  #if cnt == 2000:
  #  raise Exception("too many instructions")
George Hotz's avatar
George Hotz committed
258 259
  try:
    print(">>> Tracing instruction at 0x%x, instruction size = %u" % (address, size))
George Hotz's avatar
George Hotz committed
260
    """
George Hotz's avatar
George Hotz committed
261 262 263
    jj = []
    for i in range(16):
      jj += "r%d: %x " % (i, uc.reg_read(i))
George Hotz's avatar
George Hotz committed
264 265
    print(''.join(jj))
    """
George Hotz's avatar
George Hotz committed
266 267 268 269 270 271 272
    #print('    code hook: pc=%08x sp=%08x' % (
    #  uc.reg_read(UC_MIPS_REG_PC),
    #  uc.reg_read(UC_MIPS_REG_SP)
    #  ))
  except:
    raise Exception("ctrl-c")

George Hotz's avatar
George Hotz committed
273 274
#elf = open("test", "rb")
elf = open("go-ethereum", "rb")
George Hotz's avatar
George Hotz committed
275 276 277
data = elf.read()
elf.seek(0)

George Hotz's avatar
George Hotz committed
278 279
#rte = data.find(b"\x08\x02\x2c\x95")
#print(hex(rte))
George Hotz's avatar
George Hotz committed
280

281
# program memory (16 MB)
282 283 284
prog_size = (len(data)+0xFFF) & ~0xFFF
mu.mem_map(0, prog_size)
print("malloced 0x%x for program" % prog_size)
George Hotz's avatar
George Hotz committed
285

George Hotz's avatar
George Hotz committed
286
# heap (256 MB) @ 0x20000000
287
mu.mem_map(heap_start, 256*1024*1024)
George Hotz's avatar
George Hotz committed
288

George Hotz's avatar
George Hotz committed
289 290 291
# brk (1024 MB) @ 0x40000000
mu.mem_map(brk_start, 1024*1024*1024)

292
# input oracle
George Hotz's avatar
George Hotz committed
293
mu.mem_map(0x30000000, 0x2000000)
George Hotz's avatar
George Hotz committed
294

295 296 297 298
if len(sys.argv) > 1:
  inputs = open("/tmp/eth/"+sys.argv[1], "rb").read()
else:
  inputs = open("/tmp/eth/13284469", "rb").read()
George Hotz's avatar
George Hotz committed
299
mu.mem_write(0x30000000, inputs)
300

George Hotz's avatar
George Hotz committed
301 302
# regs at 0xC0000000 in merkle

George Hotz's avatar
George Hotz committed
303
elffile = ELFFile(elf)
George Hotz's avatar
George Hotz committed
304 305 306 307
for seg in elffile.iter_segments():
  print(seg.header, hex(seg.header.p_vaddr))
  mu.mem_write(seg.header.p_vaddr, seg.data())

George Hotz's avatar
George Hotz committed
308
entry = elffile.header.e_entry
George Hotz's avatar
George Hotz committed
309
print("entrypoint: 0x%x" % entry)
George Hotz's avatar
George Hotz committed
310
#hexdump(mu.mem_read(entry, 0x10))
George Hotz's avatar
George Hotz committed
311

George Hotz's avatar
George Hotz committed
312
"""
George Hotz's avatar
George Hotz committed
313
mu.reg_write(UC_MIPS_REG_SP, stack_start-0x2000)
George Hotz's avatar
George Hotz committed
314

George Hotz's avatar
George Hotz committed
315
# http://articles.manugarg.com/aboutelfauxiliaryvectors.html
316
_AT_PAGESZ = 6
George Hotz's avatar
George Hotz committed
317 318 319 320
mu.mem_write(stack_start-0x2000, struct.pack(">IIIIII",
  0,  # argc
  0,  # argv
  0,  # envp
321
  _AT_PAGESZ, 0x1000, 0)) # auxv
George Hotz's avatar
George Hotz committed
322
mu.mem_write(stack_start-0x400, b"GOGC=off\x00")
George Hotz's avatar
George Hotz committed
323 324 325 326 327 328
"""

# moved to MIPS
start = open("startup.bin", "rb").read() + struct.pack(">I", entry)
mu.mem_write(0, start)
entry = 0
George Hotz's avatar
George Hotz committed
329

George Hotz's avatar
George Hotz committed
330
r = RangeTree()
331 332 333
for section in elffile.iter_sections():
  try:
    for nsym, symbol in enumerate(section.iter_symbols()):
George Hotz's avatar
George Hotz committed
334 335 336 337 338 339
      ss = symbol['st_value']
      se = ss+symbol['st_size']
      #print(ss, se)
      if ss != se:
        r[ss:se] = symbol.name
      #print(nsym, symbol.name, symbol['st_value'], symbol['st_size'])
340 341 342 343
      if symbol.name == "runtime.gcenable":
        print(nsym, symbol.name)
        # nop gcenable
        mu.mem_write(symbol['st_value'], b"\x03\xe0\x00\x08\x00\x00\x00\x00")
George Hotz's avatar
George Hotz committed
344 345 346 347 348
      if symbol.name == "github.com/ethereum/go-ethereum/oracle.Halt":
         #00400000: 2004dead ; <input:0> li $a0, 57005
        # 00400004: 00042400 ; <input:1> sll $a0, $a0, 16
        # 00400008: 00800008 ; <input:2> jr $a0
        mu.mem_write(symbol['st_value'], b"\x20\x04\xde\xad\x00\x04\x24\x00\x00\x80\x00\x08")
349
  except Exception:
George Hotz's avatar
George Hotz committed
350
    #traceback.print_exc()
351 352
    pass

George Hotz's avatar
George Hotz committed
353
#mu.hook_add(UC_HOOK_BLOCK, hook_code, user_data=mu)
George Hotz's avatar
George Hotz committed
354

George Hotz's avatar
George Hotz committed
355
died_well = False
George Hotz's avatar
George Hotz committed
356

357
def hook_mem_invalid(uc, access, address, size, value, user_data):
George Hotz's avatar
George Hotz committed
358
  global died_well
359
  pc = uc.reg_read(UC_MIPS_REG_PC)
George Hotz's avatar
George Hotz committed
360 361
  if pc == 0xDEAD0000:
    died_well = True
George Hotz's avatar
George Hotz committed
362
    return False
363 364
  print("UNMAPPED MEMORY:", access, hex(address), size, "at", hex(pc))
  return False
365
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid)
George Hotz's avatar
George Hotz committed
366
mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, hook_mem_invalid)
367

George Hotz's avatar
George Hotz committed
368 369
mu.hook_add(UC_HOOK_INTR, hook_interrupt)
#mu.hook_add(UC_HOOK_INSN, hook_interrupt, None, 1, 0, 0x0c000000)
George Hotz's avatar
George Hotz committed
370

George Hotz's avatar
George Hotz committed
371
if tracelevel == 3:
George Hotz's avatar
George Hotz committed
372 373
  start_instrumenting()

374 375 376 377 378 379
with open("/tmp/minigeth.bin", "wb") as f:
  f.write(mu.mem_read(0, prog_size))

if os.getenv("COMPILE", None) == "1":
  exit(0)

George Hotz's avatar
George Hotz committed
380
try:
381
  mu.emu_start(entry, -1)
George Hotz's avatar
George Hotz committed
382 383 384 385 386
except unicorn.UcError:
  pass

if not died_well:
  raise Exception("program exitted early")
George Hotz's avatar
George Hotz committed
387

George Hotz's avatar
George Hotz committed
388
real_hash = binascii.hexlify(inputs[-0x40:-0x20])
George Hotz's avatar
George Hotz committed
389 390 391 392 393
compare_hash = binascii.hexlify(mu.mem_read(0x30000800, 0x20))
print("compare", real_hash, "to computed", compare_hash)

if real_hash != compare_hash:
  raise Exception("wrong hash")