run.py 10.8 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
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)))
55 56
      print("%10d(%2d): %8x %-80s %s" % (icount, newicount, address, r[address], dat))
    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 70
  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:
71
      mu.hook_add(UC_HOOK_BLOCK, hook_code_simple, user_data=mu)
72 73 74
    if tracelevel >= 3:
      instrumenting_all = True
    instrumenting = True
75

76 77
tfd = 10
files = {}
George Hotz's avatar
George Hotz committed
78
fcnt = 0
George Hotz's avatar
George Hotz committed
79
def hook_interrupt(uc, intno, user_data):
80
  global heap_start, fcnt, files, tfd
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)
84
    uc.reg_write(UC_MIPS_REG_V0, 0)
George Hotz's avatar
George Hotz committed
85
    uc.reg_write(UC_MIPS_REG_A3, 0)
George Hotz's avatar
George Hotz committed
86 87 88 89 90 91 92 93 94

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

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

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

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

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

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

279
# program memory (16 MB)
George Hotz's avatar
George Hotz committed
280 281
mu.mem_map(0, SIZE)

George Hotz's avatar
George Hotz committed
282
# heap (256 MB) @ 0x20000000
283
mu.mem_map(heap_start, 256*1024*1024)
George Hotz's avatar
George Hotz committed
284

George Hotz's avatar
George Hotz committed
285 286 287
# brk (1024 MB) @ 0x40000000
mu.mem_map(brk_start, 1024*1024*1024)

288
# input oracle
George Hotz's avatar
George Hotz committed
289
mu.mem_map(0x30000000, 0x2000000)
George Hotz's avatar
George Hotz committed
290

291 292 293 294
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
295
mu.mem_write(0x30000000, inputs)
296

George Hotz's avatar
George Hotz committed
297 298
# regs at 0xC0000000 in merkle

George Hotz's avatar
George Hotz committed
299
elffile = ELFFile(elf)
George Hotz's avatar
George Hotz committed
300 301 302 303
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
304 305
entry = elffile.header.e_entry
print("entrypoint: %x" % entry)
George Hotz's avatar
George Hotz committed
306
#hexdump(mu.mem_read(entry, 0x10))
George Hotz's avatar
George Hotz committed
307

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

George Hotz's avatar
George Hotz committed
310
# http://articles.manugarg.com/aboutelfauxiliaryvectors.html
311
_AT_PAGESZ = 6
312 313 314
mu.mem_write(SIZE-0x2000, struct.pack(">IIIIIIII",
  1,  # argc
  SIZE-0x1000, 0,  # argv
315 316
  SIZE-0x400, 0,  # envp
  _AT_PAGESZ, 0x1000, 0)) # auxv
George Hotz's avatar
George Hotz committed
317 318

# block
319
#mu.mem_write(SIZE-0x800, b"13284469\x00")
320 321
#mu.mem_write(SIZE-0x800, b"13284469\x00")

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

George Hotz's avatar
George Hotz committed
324
#hexdump(mu.mem_read(SIZE-0x2000, 0x100))
George Hotz's avatar
George Hotz committed
325 326 327

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

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

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

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

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

George Hotz's avatar
George Hotz committed
367 368
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
369 370 371 372 373 374 375 376

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
377

George Hotz's avatar
George Hotz committed
378
real_hash = binascii.hexlify(inputs[-0x40:-0x20])
George Hotz's avatar
George Hotz committed
379 380 381 382 383
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")