def pup_extract_files(pup, output): with open(pup, "rb") as fin: header = fin.read(SCEUF_HEADER_SIZE) if header[0:5] != "SCEUF": print "Invalid PUP" return -1 cnt = u32(header, 0x18) print "-" * 80 print "PUP Version: 0x{:x}".format(u32(header, 8)) print "Firmware Version: 0x{:08X}".format(u32(header, 0x10)) print "Build Number: {}".format(u32(header, 0x14)) print "Number of Files: {}".format(cnt) print "-" * 80 for x in range(cnt): fin.seek(SCEUF_HEADER_SIZE + x * SCEUF_FILEREC_SIZE) rec = fin.read(SCEUF_FILEREC_SIZE) filetype, offset, length, flags = struct.unpack("<QQQQ", rec) filename = pup_types.get(filetype) if not filename: fin.seek(offset) hdr = fin.read(0x1000) filename = make_filename(hdr, filetype) # print "filename {} type {} offset {:x} length {:x} flags {:x}".format(filename, filetype, offset, length, flags) with open(os.path.join(output, filename), "wb") as fout: fin.seek(offset) fout.write(fin.read(length)) print "- {}".format(filename) print "-" * 80
def main(): if len(argv) != 3: print "Usage: unpack_bootimage_new.py bootimage.skprx output-dir/" return with open(argv[1], "rb") as fin: data = fin.read() data = data[data.find("SceKernelBootimage") - 4:] base_va = 0x81000000 off = u32(data, 0xCC) - base_va num = u32(data, off) for x in xrange(num): entry_off = off + 8 + 12 * x name_off = u32(data, entry_off) - base_va name = c_str(data[name_off:name_off + 0x100]) basename = name[name.rfind("/") + 1:] start = u32(data, entry_off + 4) - base_va size = u32(data, entry_off + 8) print "Writing {}...".format(name) mod = data[start:start + size] with open(os.path.join(argv[2], basename), "wb") as fout: fout.write(mod)
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 parse_thread_regs(self): data = self.notes["THREAD_REG_INFO"] num = u32(data, 4) off = 8 for x in range(num): sz = u32(data, off) regs = VitaRegs(data[off:off + sz]) # assign registers to the thread they belong to self.tid_to_thread[regs.tid].regs = regs off += sz
def parse_threads(self): self.threads = [] self.tid_to_thread = dict() data = self.notes["THREAD_INFO"] num = u32(data, 4) off = 8 for x in range(num): sz = u32(data, off) thread = VitaThread(data[off:off + sz]) self.threads.append(thread) self.tid_to_thread[thread.uid] = thread off += sz
def ipcserver(port, rpc): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(("127.0.0.1", port)) s.listen(10) while True: c, addr = s.accept() # protocol is # - request: u32 size, serialized rop obj # - response: u32 size, binary rpc response # the rop obj can contain any ropchain. common rpc stuff is appended to the end by us sz = u32(c.recv(4)) obj = c.recv(sz, socket.MSG_WAITALL) rop = pickle.loads(obj) data = rpc.exec_rop(rop) c.send(p32(len(data))) c.send(data) c.close()
def do_write_data(self, data_binary): # This is kinda hacky, write32 will append to self.rop, so make it empty for now old_rop = self.rop self.rop = [] for word in range(0, len(data_binary) // 4): data = data_binary[word*4:(word+1)*4] num = u32(data) if num == 0 and self.assume_null_init: continue addr = data_base + 4 * word self.write32(num, addr) # Now, append the old rop back # our rop is position-independent (is it?) so should be good self.rop += old_rop
def search_pointer(self, addr, passed_cid=10000, internal_use=False): """ utility function to pinpoint the location of address in memory dump """ result = [] # for each callid for cid in self.trace.cid_sequence: if cid > passed_cid: continue variables = [] # defined variables: e.g., int a=0 arguments = [] # used arguments: func(&a) calltrace = self.trace.calltrace[cid] args = calltrace.args args_dump = calltrace.args_dump args_ptr = calltrace.pointer for x in range(len(args)): current_arg = args[x][0][0] if current_arg == addr: if not internal_use: print("[*] Passed argument at [cid:%d] [arg:%dth]" % (cid, x)) else: result.append(("arg", cid, x)) for x in range(len(args)): if args[x][0][1] == 'DP': postdump = args_dump[x][1] for y in range(0, len(postdump), 4): if addr == u32(postdump[y:y + 4]): if not internal_use: print( "[*] Memory dump at [cid:%d] [arg:%dth] [idx:%d]" % (cid, x, y)) else: result.append(("dump", cid, x, y)) if not internal_use: return None else: return result
def execute(self, rop): """ Executes a ROP cmd and returns return buffer. Return buffer can be accessed in ROP via data_buffer. Note that last 0x100 bytes of return buffer can be used for scratch purposes. For example, function helper uses them to store return value of a function. As such, you should not use them when using function helper. """ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("127.0.0.1", ipc_port)) obj = pickle.dumps(rop) s.send(p32(len(obj))) s.send(obj) data_len = u32(s.recv(4)) data = s.recv(data_len, socket.MSG_WAITALL) s.close() return data
def parse_modules(self): self.modules = [] data = self.notes["MODULE_INFO"] num = u32(data, 4) off = 8 for x in range(num): # module head sz = 0x50 module = VitaModule(data[off:off + sz]) off += sz # module segs sz = module.num_segs * 0x14 module.parse_segs(data[off:off + sz]) off += sz # module foot sz = 0x10 module.parse_foot(data[off:off + sz]) off += sz self.modules.append(module)
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 __init__(self, data): self.uid = u32(data, 4) self.name = c_str(data, 8) self.stop_reason = u32(data, 0x74) self.status = u16(data, 0x30) self.pc = u32(data, 0x9C)
def main(): global core parser = ArgumentParser() parser.add_argument("-s", "--stack-size-to-print", dest="stacksize", type=int, help="Number of addresses of the stack to print", metavar="SIZE", default=24) parser.add_argument("corefile") parser.add_argument("elffile") args = parser.parse_args() stackSize = args.stacksize elf = ElfParser(args.elffile) core = CoreParser(args.corefile) # iprint("=== MODULES ===") # with indent(): # for module in core.modules: # print_module_info(module) # iprint() iprint("=== THREADS ===") crashed = [] with indent(): for thread in core.threads: if thread.stop_reason != 0: crashed.append(thread) print_thread_info(thread) iprint() for thread in crashed: iprint('=== THREAD "{}" <0x{:x}> CRASHED ({}) ==='.format( thread.name, thread.uid, str_stop_reason[thread.stop_reason])) pc = core.get_address_notation('PC', thread.pc) pc.print_disas_if_available(elf) lr = core.get_address_notation('LR', thread.regs.gpr[14]) lr.print_disas_if_available(elf) iprint("REGISTERS:") with indent(): for x in range(14): reg = reg_names.get(x, "R{}".format(x)) iprint("{}: 0x{:x}".format(reg, thread.regs.gpr[x])) iprint(pc) iprint(lr) iprint() iprint("STACK CONTENTS AROUND SP:") with indent(): sp = thread.regs.gpr[13] for x in range(-16, stackSize): addr = 4 * x + sp data = core.read_vaddr(addr, 4) if data: data = u32(data, 0) prefix = " " if addr == sp: prefix = "SP =>" data_notation = core.get_address_notation( "{} 0x{:x}".format(prefix, addr), data) iprint(data_notation)
def main(): global core elf = ElfParser(argv[2]) core = CoreParser(argv[1]) isPC = True # iprint("=== MODULES ===") # with indent(): # for module in core.modules: # print_module_info(module) # iprint() iprint("=== THREADS ===") crashed = [] with indent(): for thread in core.threads: if thread.stop_reason != 0: crashed.append(thread) print_thread_info(thread) iprint() for thread in crashed: iprint('=== THREAD "{}" <0x{:x}> CRASHED ({}) ==='.format( thread.name, thread.uid, str_stop_reason[thread.stop_reason])) module, segment, addr = core.vaddr_to_offset(thread.pc) if module and module.name.endswith(".elf"): iprint() iprint('DISASSEMBLY AROUND PC: 0x{:x}:'.format(thread.pc)) elf.disas_around_addr(addr) module, segment, addr = core.vaddr_to_offset(thread.regs.gpr[14]) if module and module.name.endswith(".elf"): iprint() iprint('DISASSEMBLY AROUND LR: 0x{:x}:'.format( thread.regs.gpr[14])) elf.disas_around_addr(addr) isPC = False else: iprint("DISASSEMBLY IS NOT AVAILABLE") iprint("REGISTERS:") with indent(): for x in range(14): reg = reg_names.get(x, "R{}".format(x)) iprint("{}: 0x{:x}".format(reg, thread.regs.gpr[x])) if module and isPC: reg = reg_names.get(14, "R{}".format(14)) iprint("{}: 0x{:x}".format(reg, thread.regs.gpr[14])) iprint("PC: 0x{:x} ({}@{} + 0x{:x})".format( thread.pc, module.name, segment.num, addr)) elif module: reg = reg_names.get(14, "R{}".format(14)) iprint("{}: 0x{:x} ({}@{} + 0x{:x})".format( reg, thread.regs.gpr[14], module.name, segment.num, addr)) iprint("PC: 0x{:x} ".format(thread.pc)) else: reg = reg_names.get(14, "R{}".format(14)) iprint("{}: 0x{:x} ".format(reg, thread.regs.gpr[14])) iprint("PC: 0x{:x} ".format(thread.pc)) iprint() iprint("STACK CONTENTS AROUND SP:") with indent(): sp = thread.regs.gpr[13] for x in range(-16, 0x18): addr = 4 * x + sp data = core.read_vaddr(addr, 4) if data: data = u32(data, 0) prefix = " " if addr == sp: prefix = "SP =>" suffix = "" module, segment, off = core.vaddr_to_offset(data) if module: suffix = "=> {}@{} + 0x{:x}".format( module.name, segment.num, off) if module.name.endswith(".elf") and segment.num == 1: suffix += " => {}".format(elf.addr2line(off)) iprint("{} 0x{:x}: 0x{:x} {}".format( prefix, addr, data, suffix))
def __init__(self, data): self.tid = u32(data, 4) self.gpr = [] for x in range(16): self.gpr.append(u32(data, 8 + 4 * x))
def __init__(self, data): self.uid = u32(data, 4) self.num_segs = u32(data, 0x4C) self.name = c_str(data, 0x24)
def __init__(self, data, num): self.num = num self.attr = u32(data, 4) self.start = u32(data, 8) self.size = u32(data, 12) self.align = u32(data, 16)
def do_write_data(self, data_binary): part1 = [ # r0 = sp G.pop_r2_pc, G.pop_pc, G.mov_r0_sp_blx_r2, # r0 += const G.pop_r1_pc, 0xDEAD, ] part2 = [ G.pop_r4_pc, G.adds_r0_r1, G.blx_r4_pop_r4_pc, 0, # [write_data_temp] = r0 G.pop_r1_pc, self.write_data_temp, G.str_r0_r1_pop_r4, 0, # r1 = [write_data_temp] G.pop_r1_pc, self.write_data_temp, G.pop_r5_r6_r7_r8_sb_pc, 0, 0, 0, 0, G.pop_pc, # sb G.ldr_r1_r1_blx_sb, # (dest) r0 = data_base G.pop_r0_pc, data_base, # (len) r2 = len(data_binary) G.pop_r2_pc, len(data_binary), G.pop_r4_pc, F.memcpy, # call memcpy(data_binary, SRC_past_rop, len) G.blx_r4_pop_r4_pc, 0, ] part1[-1] = (len(part2 + self.rop) + 2) * 4 # Append data_binary as a series of words at the end of ropchain for word in range(0, len(data_binary) // 4): data = data_binary[word*4:(word+1)*4] num = u32(data) self.rop.append(num) # Prepend data_binary writer self.rop = part1 + part2 + self.rop
def ret_arg_code(self, cid, args, args_dump, args_type, args_ptr): """ - cid: call id - args: actual argument values - args_dump: followed result from pointer array[0]=pre, array[1]=post - args_type: inferred type for each argument """ need_to_define = [] arguments = [] pointer_defined_flag = False # 1) will use raw value (basically) # 2) if pointer, we define variable and pass the address # 3) if pointer indicates 0, we allocate heap with 1000 size for x in range(len(args)): pointer_defined_flag = False # data pointer if args[x][0][1] == 'DP': # TODO: consider data-type when unpack # 1) infer filename argument (if the string contains filename information) first_string = next(strings(args_dump[x][0])) if self.sample_name.encode() in first_string: arguments.append("filename") continue else: dumped = hex(u32(args_dump[x][0])) _type = args_type[x].replace("*", "") # 1-1) infer chuck of actual sample is used in the function # TODO # 2) we allocate heap if pointed value is 0 if dumped == '0x0': # we always allocate enough space for pointer to zero (could be initialization) need_to_define.append( "%s* c%d_a%d = (%s*) calloc (%d, sizeof(%s));" % (_type, cid, x, _type, BINREAD, _type)) arguments.append("&c%d_a%d" % (cid, x)) # 3) Check pre-defined pointer # If there is, we reuse the pointer else: # print args[x][0][0] # Is the address is already referenced from the previous pointer? # print cid # print args[x][0][0] # print self.defined_pointer.keys() if args[x][0][0] not in self.defined_pointer: # print hex(args[x][0][0]) result = self.search_pointer(args[x][0][0], cid, internal_use=True) result_arg = self.check_searched_result(result, "arg") result_dump = self.check_searched_result( result, "dump") # print result_arg # print result_dump # 3-1) searches for the address from the previous operation # if there exist address in the dump (e.g., assigned after function call), # we try to use that (only if the dump exist previously) if result is not None and result_dump is not None: _cid = result_dump[1] _arg = result_dump[2] _idx = result_dump[3] ptrname = self.ret_pointer_at_dump( _cid, _arg, _idx) self.defined_pointer[args[x][0][0]] = ptrname need_to_define.append('') arguments.append(ptrname) continue # 3-2) what if the address is used by another arguments? elif False: # elif result is not None and result_arg is not None: # print result self.defined_pointer[args[x][0] [0]] = "&c%d_a%d" % (cid, x) need_to_define.append("%s c%d_a%d = %s;" % (_type, cid, x, dumped)) # 3-3) if not, we define new one else: self.defined_pointer[args[x][0] [0]] = "&c%d_a%d" % (cid, x) need_to_define.append("%s c%d_a%d = %s;" % (_type, cid, x, dumped)) pointer_defined_flag = True # if it is pre-defined, we do nothing else: need_to_define.append('') # If we don't have choice, we define new pointer arguments.append(self.defined_pointer[args[x][0][0]]) # 4) Check whether referenced value (from pointer) is defined as another pointer # e.g., arg1|A --> 0x1000, arg1|B --> A -> 0x1000 # ==> B = &A (not just raw value of A) if pointer_defined_flag == True: # now, we are selecting the referenced value (this is also address) __result = self.search_pointer(args[x][1][0], cid, internal_use=True) # print "pointer", __result __result_arg = self.check_searched_result(__result, "arg") # print __result_arg if __result_arg is not None: result_cid = __result_arg[1] result_arg = __result_arg[2] # history = {cid: (need_to_define, arguments)} # print "DEBUG", result_cid, result_arg, self.history, self.history[result_cid][1] previous_argument = self.history[result_cid][1][ result_arg] addr_previous_argument = self.ret_addr_of_var( previous_argument) # rollback del self.defined_pointer[args[x][0][0]] arguments = arguments[:-1] need_to_define = need_to_define[:-1] # append arguments self.defined_pointer[args[x][1] [0]] = addr_previous_argument arguments.append(addr_previous_argument) need_to_define.append('') elif args[x][0][1] == 'CP': """ failed trial code_pointer = args[x][0][0] self.defined_pointer[args[x][0][0]] = "&c%d_a%d" % (cid, x) need_to_define.append("%s c%d_a%d = %s;" % (_type, cid, x, code_pointer)) arguments.append(self.defined_pointer[args[x][0][0]]) """ # print self.trace.caller_baseasddr raw_value = args[x][0][0] _type = args_type[x].replace("*", "") append_str = " /* Possible code pointer offset: %s */" % hex( int(raw_value) - self.trace.caller_baseaddr) # print append_str # we provide the information about the code pointer need_to_define.append("") arguments.append(hex(raw_value) + append_str) # raw data elif args[x][0][1] == 'D': # TODO: consider data-type when unpack raw_value = args[x][0][0] _type = args_type[x].replace("*", "") need_to_define.append("") arguments.append(hex(raw_value)) return need_to_define, arguments