run.py 10.6 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 11
from capstone import *
md = Cs(CS_ARCH_MIPS, CS_MODE_32 + CS_MODE_BIG_ENDIAN)

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

mu = Uc(UC_ARCH_MIPS, UC_MODE_32 + UC_MODE_BIG_ENDIAN)

20 21 22
# memory trie
# register trie

George Hotz's avatar
George Hotz committed
23 24 25
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
26
SIZE = 16*1024*1024
27

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

George Hotz's avatar
George Hotz committed
34
brk_start = 0x40000000  # 0x40000000-0x80000000
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
42 43 44 45
def hook_code_simple(uc, address, size, user_data):
  global icount, bcount
  #assert size == 4
  try:
46 47 48 49 50 51 52 53
    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?!?"
      print("%10d(%2d): %8x %-80s %s" % (icount, newicount, address, r[address], dat))
    icount += newicount
54 55 56 57 58 59 60 61
    bcount += 1
    return True
  except Exception as e:
    raise e
  except:
    raise Exception

def start_instrumenting():
62 63 64 65 66 67
  global instrumenting, instrumenting_all
  if not instrumenting:
    tracelevel = int(os.getenv("TRACE", 0))
    if tracelevel >= 2:
      mu.hook_add(UC_HOOK_CODE, hook_code_simple, user_data=mu)
    elif tracelevel == 1:
68
      mu.hook_add(UC_HOOK_BLOCK, hook_code_simple, user_data=mu)
69 70 71
    if tracelevel >= 3:
      instrumenting_all = True
    instrumenting = True
72

73 74
tfd = 10
files = {}
George Hotz's avatar
George Hotz committed
75
fcnt = 0
George Hotz's avatar
George Hotz committed
76
def hook_interrupt(uc, intno, user_data):
77
  global heap_start, fcnt, files, tfd
George Hotz's avatar
George Hotz committed
78 79 80
  pc = uc.reg_read(UC_MIPS_REG_PC)
  if intno == 17:
    syscall_no = uc.reg_read(UC_MIPS_REG_V0)
81
    uc.reg_write(UC_MIPS_REG_V0, 0)
George Hotz's avatar
George Hotz committed
82
    uc.reg_write(UC_MIPS_REG_A3, 0)
George Hotz's avatar
George Hotz committed
83 84 85 86 87 88 89 90 91

    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
92 93 94 95 96
    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)
97
      #print("write(%d, %x, %d)" % (fd, buf, count))
98 99 100 101 102 103 104 105
      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
106
      uc.reg_write(UC_MIPS_REG_A3, 0)
107 108
      if fd == 2:
        start_instrumenting()
George Hotz's avatar
George Hotz committed
109 110
      return True

George Hotz's avatar
George Hotz committed
111 112 113 114 115 116
    if syscall_no == 4218:
      # madvise
      return
    elif syscall_no == 4194:
      # rt_sigaction
      return
George Hotz's avatar
George Hotz committed
117 118 119
    elif syscall_no == 4195:
      # rt_sigprocmask
      return
George Hotz's avatar
George Hotz committed
120 121 122
    elif syscall_no == 4055:
      # fcntl
      return
George Hotz's avatar
George Hotz committed
123 124 125
    elif syscall_no == 4220:
      # fcntl64
      return
George Hotz's avatar
George Hotz committed
126 127 128 129 130 131
    elif syscall_no == 4249:
      # epoll_ctl
      return
    elif syscall_no == 4263:
      # clock_gettime
      return
George Hotz's avatar
George Hotz committed
132 133 134 135 136 137 138 139 140 141 142 143
    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
144 145 146
    elif syscall_no == 4166:
      # nanosleep
      return
George Hotz's avatar
George Hotz committed
147

George Hotz's avatar
George Hotz committed
148 149 150
    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'))
151
      uc.reg_write(UC_MIPS_REG_V0, 4)
George Hotz's avatar
George Hotz committed
152 153 154
    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
155 156 157
    elif syscall_no == 4288:
      dfd = uc.reg_read(UC_MIPS_REG_A0)
      filename = uc.reg_read(UC_MIPS_REG_A1)
158 159 160
      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
161
      print('openat("%s") = %d' % (filename, tfd))
162
      tfd += 1
George Hotz's avatar
George Hotz committed
163 164 165 166 167 168 169 170 171 172 173
    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
174 175
    elif syscall_no == 4120:
      print("clone not supported")
George Hotz's avatar
George Hotz committed
176 177
      #uc.reg_write(UC_MIPS_REG_V0, -1)
      uc.reg_write(UC_MIPS_REG_V0, 1238238)
George Hotz's avatar
George Hotz committed
178 179
      uc.reg_write(UC_MIPS_REG_A3, 0)
      return True
180 181 182
    elif syscall_no == 4006:
      fd = uc.reg_read(UC_MIPS_REG_A0)
      if fd >= 10:
George Hotz's avatar
George Hotz committed
183
        #print("close(%d)" % fd)
184 185 186
        files[fd].close()
        del files[fd]
      uc.reg_write(UC_MIPS_REG_V0, 0)
187 188 189 190
    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
191 192
      # changing this works if we want smaller oracle
      #count = 4
193
      if fd == 4:
194 195
        val = b"2097152\n"
        uc.mem_write(buf, val)
George Hotz's avatar
George Hotz committed
196
        print("read", fd, hex(buf), count)
197
        uc.reg_write(UC_MIPS_REG_V0, len(val))
198 199 200
      else:
        ret = files[fd].read(count)
        uc.mem_write(buf, ret)
George Hotz's avatar
George Hotz committed
201
        #print("read", fd, hex(buf), count, len(ret))
202
        uc.reg_write(UC_MIPS_REG_V0, len(ret))
George Hotz's avatar
George Hotz committed
203
    elif syscall_no == 4246:
204
      a0 = uc.reg_read(UC_MIPS_REG_A0)
205 206 207 208
      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
209 210
      sys.stdout.flush()
      sys.stderr.flush()
George Hotz's avatar
George Hotz committed
211
      #os._exit(a0)
George Hotz's avatar
George Hotz committed
212 213 214 215
    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
216 217 218 219
      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
220 221 222 223
      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
224
        heap_start += a1
George Hotz's avatar
George Hotz committed
225 226
      else:
        uc.reg_write(UC_MIPS_REG_V0, a0)
George Hotz's avatar
George Hotz committed
227
    else:
George Hotz's avatar
George Hotz committed
228
      print("syscall", syscall_no, hex(pc))
George Hotz's avatar
George Hotz committed
229 230 231 232 233 234 235
      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
236
  if intno != 17:
George Hotz's avatar
George Hotz committed
237
    raise unicorn.UcError(0)
George Hotz's avatar
George Hotz committed
238
  return True
George Hotz's avatar
George Hotz committed
239

George Hotz's avatar
George Hotz committed
240 241 242 243
cnt = 0
def hook_code(uc, address, size, user_data):
  global cnt
  cnt += 1
George Hotz's avatar
George Hotz committed
244 245

  """
George Hotz's avatar
George Hotz committed
246 247 248
  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
249
  """
George Hotz's avatar
George Hotz committed
250 251 252
  
  #if cnt == 2000:
  #  raise Exception("too many instructions")
George Hotz's avatar
George Hotz committed
253 254
  try:
    print(">>> Tracing instruction at 0x%x, instruction size = %u" % (address, size))
George Hotz's avatar
George Hotz committed
255
    """
George Hotz's avatar
George Hotz committed
256 257 258
    jj = []
    for i in range(16):
      jj += "r%d: %x " % (i, uc.reg_read(i))
George Hotz's avatar
George Hotz committed
259 260
    print(''.join(jj))
    """
George Hotz's avatar
George Hotz committed
261 262 263 264 265 266 267
    #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
268 269
#elf = open("test", "rb")
elf = open("go-ethereum", "rb")
George Hotz's avatar
George Hotz committed
270 271 272
data = elf.read()
elf.seek(0)

George Hotz's avatar
George Hotz committed
273 274
#rte = data.find(b"\x08\x02\x2c\x95")
#print(hex(rte))
George Hotz's avatar
George Hotz committed
275

276
# program memory (16 MB)
George Hotz's avatar
George Hotz committed
277 278
mu.mem_map(0, SIZE)

George Hotz's avatar
George Hotz committed
279
# heap (256 MB) @ 0x20000000
280
mu.mem_map(heap_start, 256*1024*1024)
George Hotz's avatar
George Hotz committed
281

George Hotz's avatar
George Hotz committed
282 283 284
# brk (1024 MB) @ 0x40000000
mu.mem_map(brk_start, 1024*1024*1024)

285
# input oracle
George Hotz's avatar
George Hotz committed
286
mu.mem_map(0x30000000, 0x2000000)
George Hotz's avatar
George Hotz committed
287

George Hotz's avatar
George Hotz committed
288 289
inputs = open("/tmp/eth/13284469", "rb").read()
mu.mem_write(0x30000000, inputs)
290

George Hotz's avatar
George Hotz committed
291 292
# regs at 0xC0000000 in merkle

George Hotz's avatar
George Hotz committed
293
elffile = ELFFile(elf)
George Hotz's avatar
George Hotz committed
294 295 296 297
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
298 299
entry = elffile.header.e_entry
print("entrypoint: %x" % entry)
George Hotz's avatar
George Hotz committed
300
#hexdump(mu.mem_read(entry, 0x10))
George Hotz's avatar
George Hotz committed
301

George Hotz's avatar
George Hotz committed
302
mu.reg_write(UC_MIPS_REG_SP, SIZE-0x2000)
George Hotz's avatar
George Hotz committed
303

George Hotz's avatar
George Hotz committed
304
# http://articles.manugarg.com/aboutelfauxiliaryvectors.html
305
_AT_PAGESZ = 6
306 307 308
mu.mem_write(SIZE-0x2000, struct.pack(">IIIIIIII",
  1,  # argc
  SIZE-0x1000, 0,  # argv
309 310
  SIZE-0x400, 0,  # envp
  _AT_PAGESZ, 0x1000, 0)) # auxv
George Hotz's avatar
George Hotz committed
311 312

# block
313
#mu.mem_write(SIZE-0x800, b"13284469\x00")
314 315
#mu.mem_write(SIZE-0x800, b"13284469\x00")

316
mu.mem_write(SIZE-0x400, b"GOGC=off\x00")
George Hotz's avatar
George Hotz committed
317

George Hotz's avatar
George Hotz committed
318
#hexdump(mu.mem_read(SIZE-0x2000, 0x100))
George Hotz's avatar
George Hotz committed
319 320 321

# nop osinit
#mu.mem_write(0x44524, b"\x03\xe0\x00\x08\x00\x00\x00\x00")
George Hotz's avatar
George Hotz committed
322

George Hotz's avatar
George Hotz committed
323
r = RangeTree()
324 325 326
for section in elffile.iter_sections():
  try:
    for nsym, symbol in enumerate(section.iter_symbols()):
George Hotz's avatar
George Hotz committed
327 328 329 330 331 332
      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'])
333 334 335 336
      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
337 338 339 340 341
      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")
342
  except Exception:
George Hotz's avatar
George Hotz committed
343
    #traceback.print_exc()
344 345
    pass

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

George Hotz's avatar
George Hotz committed
348
died_well = False
George Hotz's avatar
George Hotz committed
349

350
def hook_mem_invalid(uc, access, address, size, value, user_data):
George Hotz's avatar
George Hotz committed
351
  global died_well
352
  pc = uc.reg_read(UC_MIPS_REG_PC)
George Hotz's avatar
George Hotz committed
353 354
  if pc == 0xDEAD0000:
    died_well = True
George Hotz's avatar
George Hotz committed
355
    return False
356 357
  print("UNMAPPED MEMORY:", access, hex(address), size, "at", hex(pc))
  return False
358
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid)
George Hotz's avatar
George Hotz committed
359
mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, hook_mem_invalid)
360

George Hotz's avatar
George Hotz committed
361 362
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
363 364 365 366 367 368 369 370

try:
  mu.emu_start(entry, 0)
except unicorn.UcError:
  pass

if not died_well:
  raise Exception("program exitted early")
George Hotz's avatar
George Hotz committed
371 372 373 374 375 376 377

real_hash = binascii.hexlify(inputs[-0x20:])
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")