import typing, pathlib, struct from ghidra_assistant.utils.archs.arm.arm_emulator import * from ghidra_assistant.ghidra_assistant import GhidraAssistant from ghidra_assistant.concrete_device import ConcreteDevice if typing.TYPE_CHECKING: from GA_debugger import * acces_str = { UC_MEM_READ : "UC_MEM_READ", UC_MEM_WRITE : "UC_MEM_WRITE", UC_MEM_FETCH : "UC_MEM_FETCH", UC_MEM_READ_UNMAPPED : "UC_MEM_READ_UNMAPPED", UC_MEM_WRITE_UNMAPPED : "UC_MEM_WRITE_UNMAPPED", UC_MEM_FETCH_UNMAPPED : "UC_MEM_FETCH_UNMAPPED", UC_MEM_WRITE_PROT : "UC_MEM_WRITE_PROT", UC_MEM_READ_PROT : "UC_MEM_READ_PROT", UC_MEM_FETCH_PROT : "UC_MEM_FETCH_PROT", UC_MEM_READ_AFTER : "UC_MEM_READ_AFTER", } def p8(value): return struct.pack(" None: super().__init__(init_uc) self.log_hw_access = True self.saved_blocks = {} try: self.ghidra = GhidraAssistant() except: pass def setup(self): self.setup_memory() self.setup_registers() self.setup_hooks() self.apply_patches() def install_debugger(self, debugger : ConcreteDevice): self.debugger = debugger def setup_memory(self): self.bootrom_path = pathlib.Path("bootrom_t124.bin") self.bootrom = self.bootrom_path.read_bytes() self.uc.mem_map(0x100000, page_align_top(len(self.bootrom)), UC_PROT_EXEC | UC_PROT_READ) self.uc.mem_write(0x100000, self.bootrom) # map IMEM self.imem_path = pathlib.Path("imem3_bct") self.imem = self.imem_path.read_bytes() self.uc.mem_map(0x40000000, 0x40000, UC_PROT_EXEC | UC_PROT_READ | UC_PROT_WRITE) self.uc.mem_write(0x40000000, self.imem) # DRAM DRAM_BASE = 0x80000000 DRAM_SIZE = 2 * GB self.uc.mem_map(DRAM_BASE, DRAM_SIZE, UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC) def setup_registers(self): self.sp = 0x4000d000 self.pc = 0x4000e000 self.is_thumb = False def hook_unmapped(self, uc, access, address, size, value, user_data): print(f"Unmapped memory access at 0x{address:x} with size {size} and access {acces_str[access]}") pass def hook_hw_access(self, uc, access, address, size, value, user_data): if self.log_hw_access: p_info(f"{hex(self.pc)} HW access at 0x{address:x} with size {size} and access {acces_str[access]}") # All unmapped memory is send to the debugger try: if access == UC_MEM_WRITE: if size == 4: self.debugger.memwrite_region(address, p32(value)) self.uc.mem_write(address, p32(value)) # self.uc.mem_write(address, self.debugger.memdump_region(address, size)) elif size == 1: self.debugger.memwrite_region(address, p8(value)) self.uc.mem_write(address, p8(value)) # self.uc.mem_write(address, self.debugger.memdump_region(address, size)) else: raise Exception("Unhandled write!") elif access == UC_MEM_READ: uc.mem_write(address, self.debugger.memdump_region(address, size)) else: raise Exception("Not handled!") except Exception as e: pass return True def setup_hooks(self): # hook unmapped self.uc.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_FETCH_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_UNMAPPED, self.hook_unmapped) # 0x6000f000 self.uc.mem_map(0x60000000, 0x10000, UC_PROT_READ | UC_PROT_WRITE) self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self.hook_hw_access, begin=0x60000000, end=0x60000000 + 0x10000) self.uc.mem_map(0x70000000, 0x100000, UC_PROT_READ | UC_PROT_WRITE) self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self.hook_hw_access, begin=0x70000000, end=0x70000000 + 0x100000) self.setup_log_hook() self.setup_hook_blocks() # self.setup_hook_EmmcValidateResponse() def apply_patches(self): # Nop out 400101f0 to 0x40010220, maybe this is restricting access to IMEM and ROM? self.sc.mov_0_r0 = self.ks.asm("mov r0, #0", as_bytes=True)[0] # self.uc.mem_write(0x400101e4, self.sc.mov_0_r0 * ((0x40010220 - 0x400101e4) // 4)) # Patch EMMCVerifyResponse self.sc.bx_lr = self.ks.asm("bx lr", as_bytes=True)[0] # self.uc.mem_write(0x4001dfb0, self.sc.mov_0_r0 + self.sc.bx_lr) pass def run(self): try: self.uc.emu_start(self.pc, 0) except Exception as e: print(str(e)) self.print_ctx(print) pass def setup_log_hook(self): UART_LOG_HOOK = 0x4001cadc def hook_log(uc, address, size, user_data): msg = self.read_string(self.R0) try: args = msg.count(b"%") arg_types = [] offset = 0 for i in range(args): c_offset = msg[offset:].find(b"%") mtype = msg[c_offset:offset + 2] offset += c_offset + 2 arg_types.append(mtype) def read_msg_var(mtype, addr): if mtype == b"%s": return self.read_string(addr) elif mtype == b"%d": return eval('b"'+ str(addr) +'"')# As int else: return eval('b"'+ hex(addr)[2:] +'"')# As hex arg_str = [] for i in range(args): if i == 0: arg_str.append(read_msg_var(arg_types[i], self.R1)) elif i == 1: arg_str.append(read_msg_var(arg_types[i], self.R2)) elif i == 2: arg_str.append(read_msg_var(arg_types[i], self.R3)) else: break for i in range(len(arg_str)): offset = msg.find(b"%") msg = msg[:offset] + arg_str[i] + msg[offset + 2:] except Exception as e: pass print(f"{hex(self.LR)} : {msg}") if(b"Sdmmc Read failed" in msg): pass return True self.uc.hook_add(UC_HOOK_CODE, hook_log, begin=UART_LOG_HOOK, end=UART_LOG_HOOK + 1) # And patch function to just return self.uc.mem_write(UART_LOG_HOOK, self.ks.asm("bx lr", as_bytes=True)[0]) def setup_hook_blocks(self): def hook_block(uc, address, size, user_data): # print(f"Block at {hex(self.LR)}") self.saved_blocks[self.LR] = self.get_registers() return True self.uc.hook_add(UC_HOOK_BLOCK, hook_block) def setup_interrupt_hook(self): RAISE_INTERRUPT = 0x4001cab8 def hook_interrupt(uc, address, size, user_data): print(f"Interrupt at {self.LR}") return True self.uc.hook_add(UC_HOOK_CODE, hook_interrupt, begin=RAISE_INTERRUPT, end=RAISE_INTERRUPT + 1) def setup_hook_EmmcValidateResponse(self): self.saved_emmc_responses = {} def hook_emmc(uc, address, size, user_data): self.saved_emmc_responses[self.pc] = self.get_registers() return True self.uc.hook_add(UC_HOOK_CODE, hook_emmc, begin=0x4001dfb0, end=0x4001e160) def do_partial_emu(debugger : ConcreteDevice): emu = PartialEmu() emu.install_debugger(debugger) emu.setup() emu.run()