class PSPEmulator(): custom_memory = bytearray(2**32) accessed_mem = {} verbose = True interactive = False ccp_cmd = {"process": False, "start": 0x0, "end": 0x0} qemu = None def __init__(self): self.avatar = None self.close = False self.trace_parse = None self.trace_file = None self.ignored_addresses = set() def init(self, entry_addr, qemu_bin, qemu_args, verbose=False, interactive=False): self.avatar = Avatar(output_directory=AVATAR_DIR) self.avatar.log.setLevel('DEBUG') PSPEmulator.verbose = verbose PSPEmulator.interactive = interactive PSPEmulator.qemu = self.avatar.add_target( QemuTarget, name='qemu1', executable=qemu_bin, gdb_executable="arm-eabi-gdb", additional_args=qemu_args) PSPEmulator.qemu.cpu_model = 'cortex-a8' self.avatar.watchmen.add_watchman('RemoteMemoryRead', 'before', self.before_remote_memory_access, is_async=False) self.avatar.watchmen.add_watchman('RemoteMemoryWrite', 'before', self.before_remote_memory_access, is_async=False) PSPEmulator.qemu.entry_address = entry_addr def load_file(self, address, mem_size, filename, offset, file_size): self.avatar.add_memory_range(address, mem_size, file=filename, file_offset=offset, file_bytes=file_size) def load_custom_mem(self, address, filename, offset=0, size=0): f = open(filename, 'rb') if offset != 0: f.seek(offset) if size != 0: data = f.read(size) else: data = f.read() self.custom_memory[address:address + len(data)] = data def add_memory_range(self, start, end, permissions='rw-'): if ((end - start) % 0x1000) != 0: print("[PSPEmulator] ERROR: memory ranges must be page aligned" "(0x%.8x)" % start) self.avatar.add_memory_range(start, end - start, permission=permissions) def set_memory_value(self, address, value, size=4): if size != 1 and size != 4: print("[PSPEmulator] ERROR: Only 1 or 4 bytes are supported") return if size == 1: PSPEmulator.custom_memory[address] = value elif size == 4: bval = (value).to_bytes(4, byteorder='big') PSPEmulator.custom_memory[address] = bval[0] PSPEmulator.custom_memory[address + 1] = bval[1] PSPEmulator.custom_memory[address + 2] = bval[2] PSPEmulator.custom_memory[address + 3] = bval[3] def qemu_init(self): PSPEmulator.qemu.init() def set_breakpoint(self, address): PSPEmulator.qemu.set_breakpoint(address) def watch_memory_range(self, start, end, permissions='rw-'): if ((end - start) % 0x1000) != 0: print("[PSPEmulator] ERROR: watched memory ranges must be page" "aligned (0x%.8x)" % start) return self.avatar.add_memory_range(start, end - start, emulate=CustomMemoryPeripheral, permission=permissions) def add_virtual_ccp(self, address): if not PSPEmulator.qemu: print("[PSPEmulator] ERROR: PSPEmulator not initialized yet. Call" "init() first") return self.avatar.add_memory_range(address, 0x1000, emulate=VirtualCCP, permission='rw-') # self.ignored_addresses.add(address) def add_misc_dev(self, address): if not PSPEmulator.qemu: print("[PSPEmulator] ERROR: PSPEmulator not initialized yet. Call" "init() first") return self.avatar.add_memory_range(address, 0x1000, emulate=VirtMisc, permission='rw-') def add_virtual_timer(self, address): if not PSPEmulator.qemu: print("[PSPEmulator] ERROR: PSPEmulator not initialized yet. Call" "init() first") return self.avatar.add_memory_range(address, 0x1000, name="VirtualTimer", emulate=VirtualTimer, permission='rw-') # self.ignored_addresses.add(address) def watch_memory(self, address=None, size=None): # TODO: Automatically configure "remaining", i.e. not yet configured # memory ranges to be backed by our CustomMemoryPeripheral print(self.avatar.memory_ranges) for interval in self.avatar.memory_ranges: print("0x%x 0x%x" % (interval.begin, interval.end)) def __del__(self): self.avatar.shutdown() if self.trace_file and self.trace_parse: command = 'python2.7 %s %s /tmp/trace > /tmp/parsed' % \ (self.trace_parse, self.trace_file) print("[PSPEmulator] Calling %s" % command) os.system(command) def exit(self): self.close = True self.__del__() def disconnect_gdb(self): PSPEmulator.qemu.gdb.remote_disconnect() def connect_gdb(self): PSPEmulator.qemu.gdb.remote_connect() def run(self): while not self.close: if PSPEmulator.qemu.state != TargetStates.EXITED: PSPEmulator.qemu.cont() PSPEmulator.qemu.wait(TargetStates.EXITED | TargetStates.STOPPED) if PSPEmulator.qemu.state == TargetStates.STOPPED: if PSPEmulator.ccp_cmd["process"] is True: print("[ccp_dev] Parsing new cmd at pc=0x%.8x" % PSPEmulator.qemu.read_register("pc")) self.print_ccp_cmds(PSPEmulator.ccp_cmd["start"], PSPEmulator.ccp_cmd["end"]) PSPEmulator.ccp_cmd["process"] = False else: embed(banner1="QEMU entered STOPPED state at pc=0x%.8x" % PSPEmulator.qemu.read_register("pc")) else: print("[PSPEmulator] Qemu exited with state: %s" % str(PSPEmulator.qemu.state)) self.exit() def print_ccp_cmds(self, start, end): cmds = (end - start) // 0x20 for e in range(0x0, cmds): dwords = [ PSPEmulator.qemu.read_memory(i, 0x4) for i in range(start + (e * 0x20), start + (e * 0x20) + 0x20, 0x4) ] print("\n[ccp_dev] Processing ccp cmd %d" % e) cmt, engine = ccp_parse.parse_dword0(dwords[0]) print("[ccp_dev]\t %s" % cmt) print("[ccp_dev]\t Length of src data 0x%x" % dwords[1]) print("[ccp_dev]\t Src ptr 0x%x" % dwords[2]) print("[ccp_dev]\t %s" % ccp_parse.parse_dword3(dwords[3])) print("[ccp_dev]\t %s" % ccp_parse.parse_dword4(engine, dwords[4])) print("[ccp_dev]\t %s" % ccp_parse.parse_dword5(engine, dwords[5])) print("[ccp_dev]\t Low 32bit key ptr: 0x%x" % dwords[6]) print("[ccp_dev]\t High 16bit key ptr + mem type: 0x%x" % dwords[7]) print() def swap32(i): return struct.unpack("<I", struct.pack(">I", i))[0] def enable_tracing(self, trace_parse, trace_file): self.trace_file = trace_file self.trace_parse = trace_parse PSPEmulator.qemu.qmp.execute_command('trace-event-set-state', { 'name': 'exec_tb', 'enable': True }) PSPEmulator.qemu.qmp.execute_command('trace-event-set-state', { 'name': 'guest_mem_before_exec', 'enable': True }) def before_remote_memory_access(self, avatar, remote_memory_msg, **kwargs): address = remote_memory_msg.address pc = remote_memory_msg.pc # Ignore pages belonging to emulated devices if (address & 0xFFFFF000) in self.ignored_addresses: return read_or_write = '' if isinstance(remote_memory_msg, RemoteMemoryReadMessage): read_or_write = 'read' else: # if isinstance(remote_memory_msg, RemoteMemoryWriteMessage): read_or_write = 'write' # if not PSPEmulator.ccp_cmd["process"]: # print('[custom_memory] New %s at 0x%.8x from PC: 0x%.8x' % (read_or_write, address, pc)) if address not in PSPEmulator.accessed_mem: PSPEmulator.accessed_mem[address] = set() # print("BeforeMemaccess: pc 0x%.8x addr 0x%.8x" % (pc, address)) PSPEmulator.accessed_mem[address].add((pc, read_or_write)) def print_custom_memory(self): for i in PSPEmulator.accessed_mem: val = int.from_bytes(PSPEmulator.custom_memory[i:i + 4], 'little') print('\t0x%.8x: \t0x%.8x \taccessed_by: %s' % (i, val, repr([ v[1] + ':' + hex(v[0]) for v in PSPEmulator.accessed_mem[i] ])))
gdb_additional_args=[args.elf], gdb_executable="arm-none-eabi-gdb", gdb_port=2331) avatar.watchmen.add_watchman('BreakpointHit', 'before', handle_bp, is_async=True) avatar.init_targets() memories = [(0x20000000, 0x50000)] if args.db == None: db = os.path.splitext(args.elf)[0] + ".sqlite" else: db = args.db Recorder = State_Recorder(db, gdb, memories, args.elf) with open(args.functions, 'rb') as infile: functions = yaml.safe_load(infile) for f in functions: print "Setting Breakpoint: ", f Recorder.add_function(f) gdb.protocols.execution.console_command('load') gdb.protocols.execution.console_command('monitor reset') gdb.cont() embed() gdb.stop() avatar.shutdown()