203 lines
7.9 KiB
Python
203 lines
7.9 KiB
Python
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("<B", value)
|
|
|
|
class PartialEmu(ARM_Emulator):
|
|
def __init__(self, init_uc=True) -> 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() |