added hw device in the loop emulator

This commit is contained in:
Eljakim Herrewijnen 2024-05-30 10:15:36 +02:00
parent b10e3fc359
commit a1ab8650bc
53 changed files with 578 additions and 3 deletions

View File

@ -6,6 +6,8 @@ from ghidra_assistant.utils.debugger.debugger_archs.ga_arm_thumb import GA_arm_t
from exploit import *
from keystone import *
from t210 import *
from hw_in_the_loop import *
from partial_emulation import *
ks_arm = Ks(KS_ARCH_ARM, KS_MODE_ARM)
ks_thumb = Ks(KS_ARCH_ARM, KS_MODE_THUMB)
@ -447,11 +449,32 @@ def hw_init(cd : "ConcreteDevice"):
# coldboot()
pass
def relocate_debugger(cd : ConcreteDevice):
'''
Works, relocates the debugger to the end of IRAM
'''
reloc = open('/home/eljakim/Source/gupje/source/bin/nvidia_shield_t/debugger_reloc.bin', 'rb').read()
cd.memwrite_region(0x4003c000, reloc)
cd.restore_stack_and_jump(0x4003c000 | 1)
assert cd.read(0x100) == b"GiAs"
# And relocate the debugger
cd.relocate_debugger(0x40011000, 0x4003c000, 0x4003e000)
def device_main(cd : "ConcreteDevice", args):
'''
Main function that will do execution for the device.
'''
cd.test_connection()
partial_emu = True
if partial_emu:
relocate_debugger(cd)
do_partial_emu(cd)
sys.exit(0)
hw_in_the_loop()
hw_init(cd)
attempt_boot_bct(cd)
test_arm_asm(cd)

Binary file not shown.

Binary file not shown.

37
blog/part2.md Normal file
View File

@ -0,0 +1,37 @@
```
1073803520:b'Checking whether Onsemi FG present \n'
1073813020:b'%s(): error code 0x%08x %s\n'
1073811068:b'%s(): error code 0x%08x %s\n'
1073811120:b'NvTbootI2c: Read failed for slave 0x%02x, offset 0x%02x with error code 0x%08x\n'
1073799404:b'[TegraBoot] (version %s)\n'
1073799412:b'Processing in cold boot mode\n'
1073799416:b'Reset reason: %s\n'
1073847520:b'Battery Present\n'
1073813020:b'%s(): error code 0x%08x %s\n'
1073811068:b'%s(): error code 0x%08x %s\n'
1073811120:b'NvTbootI2c: Read failed for slave 0x%02x, offset 0x%02x with error code 0x%08x\n'
1073806920:b'Error MAX17048 vcell read failed.\n'
1073801444:b'Failed to determine battery voltage\n'
1073818392:b'Error getting nvdumper carve out address! Booting normally!\n'
1073818500:b'Sdram initialization is successful \n'
1073801672:b'PMU BoardId: %d\n'
1073843272:b'CPU power rail is up \n'
1073813604:b'Performing RAM repair\n'
1073843308:b'CPU clock init successful \n'
1073863832:b'%s with error 0x%x in %s func at %d line \n'
1073863916:b'Command complete wait failed with error 0x%x Interrupt 0x%x\n'
1073866976:b'Number of retries left %d\n'
1073863832:b'%s with error 0x%x in %s func at %d line \n'
1073863916:b'Command complete wait failed with error 0x%x Interrupt 0x%x\n'
1073866976:b'Number of retries left %d\n'
1073863832:b'%s with error 0x%x in %s func at %d line \n'
1073863916:b'Command complete wait failed with error 0x%x Interrupt 0x%x\n'
1073866976:b'Number of retries left %d\n'
1073867180:b'Send command failed with 0x%x\n'
1073870972:b'%s with error 0x%x in %s func at %d line \n'
1073870844:b'Identify card failed with 0x%x\n'
1073862468:b'%s with error 0x%x in %s func at %d line \n'
1073862636:b'Sdmmc Init failed with 0x%x error\n'
1073844216:b'Error in %s: 0x%x !\n'
1073799640:b'Error is %x \n'
```

19
hw_in_the_loop.py Normal file
View File

@ -0,0 +1,19 @@
import typing
if typing.TYPE_CHECKING:
from GA_debugger import *
class HWDevice():
def __init__(self) -> None:
pass
class CryptoEngine(HWDevice):
def __init__(self) -> None:
pass
# NvBootSeAesCmacGenerateSubkey
# NvBootSeAesEncrypt
def hw_in_the_loop():
pass

203
partial_emulation.py Normal file
View File

@ -0,0 +1,203 @@
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()

View File

@ -20,12 +20,14 @@ ghidra_assistant/utils/__pycache__/ga_server.cpython-310.pyc,,
ghidra_assistant/utils/__pycache__/utils.cpython-310.pyc,,
ghidra_assistant/utils/archs/__pycache__/asm_utils.cpython-310.pyc,,
ghidra_assistant/utils/archs/arm/__pycache__/armT_processor_state.cpython-310.pyc,,
ghidra_assistant/utils/archs/arm/__pycache__/arm_emulator.cpython-310.pyc,,
ghidra_assistant/utils/archs/arm/armT_processor_state.py,sha256=ZdsI6Q9mLv-YZEmJSEwKUmsh7903--nfa-dlhfi8QtQ,9466
ghidra_assistant/utils/archs/arm/arm_emulator.py,sha256=Wq7Tyiph3KYmvmMnRG8dl4TFKJEeNP8dtDrm_XpBLew,7798
ghidra_assistant/utils/archs/arm64/__pycache__/arm64_emulator.cpython-310.pyc,,
ghidra_assistant/utils/archs/arm64/__pycache__/arm64_processor_state.cpython-310.pyc,,
ghidra_assistant/utils/archs/arm64/__pycache__/asm_arm64.cpython-310.pyc,,
ghidra_assistant/utils/archs/arm64/__pycache__/uc_emulator.cpython-310.pyc,,
ghidra_assistant/utils/archs/arm64/arm64_emulator.py,sha256=Ncn3KcxfLgBuhvO6L7S8mLurRZeETnO3uxoc_6XHQd0,17049
ghidra_assistant/utils/archs/arm64/arm64_emulator.py,sha256=MtAM0DjxagGJZfN5SQuzKJ2tf7kqcYs4r_9a07pZg9o,17044
ghidra_assistant/utils/archs/arm64/arm64_processor_state.py,sha256=GqKoqwbCDhznJEbIgefvlsTcn6ensMD-q70bQMWsgvo,17633
ghidra_assistant/utils/archs/arm64/asm_arm64.py,sha256=k96Xp7hEhQWD6lbbmT2bAKuwJCz5VDRF6gx2koMuDW8,2562
ghidra_assistant/utils/archs/arm64/misc/MMU/__pycache__/arm64_mmu.cpython-310.pyc,,

View File

@ -0,0 +1,291 @@
from unicorn.arm_const import *
from unicorn import *
from capstone import *
from keystone import *
from ..asm_utils import ShellcodeCrafter
from ...utils import *
class ARM_Emulator:
'''
Class that will interact with the unicorn engine for emulating ARM code.
'''
def __init__(self, init_uc = True):
if init_uc:
self.uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
# Disassembler configuration
self.md = Cs(CS_ARCH_ARM, CS_MODE_ARM)
self.mdT = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
self.cs = self.md
self.csT = self.mdT
self.ks = Ks(KS_ARCH_ARM, KS_MODE_ARM)
self.ksT = Ks(KS_ARCH_ARM, KS_MODE_THUMB)
self.md.detail = True
self.setup_shellcode()
def setup_shellcode(self):
self.sc = ShellcodeCrafter(self.ks, self.cs)
self.scT = ShellcodeCrafter(self.ksT, self.csT)
def get_mapping(self, address):
for mem in self.uc.mem_regions():
if address >= mem[0] and address < mem[1]:
return mem
return None
def is_mapped(self, address):
if self.get_mapping(address) != None:
return True
return False
def read_string(self, at):
if at == 0:
return b''
s = b''
while 1:
b = self.uc.mem_read(at, 1)
at += 1
if b == b'\0':
return s
s += b
return s
def write_ptr(self, at, ptr):
return self.uc.mem_write(at, p32(ptr))
def read_ptr(self, at):
return u32(self.uc.mem_read(at, 4))
def add_breakpoint(self, at, target_fun):
self.uc.hook_add(UC_HOOK_CODE, target_fun, None, at, at + 1)
def get_registers(self):
# X0 - X32
return [self.uc.reg_read(x) for x in [UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3, UC_ARM_REG_R4, UC_ARM_REG_R5, UC_ARM_REG_R6, UC_ARM_REG_R7, UC_ARM_REG_R8, UC_ARM_REG_R9, UC_ARM_REG_R10, UC_ARM_REG_R11, UC_ARM_REG_R12, UC_ARM_REG_R13, UC_ARM_REG_R14, UC_ARM_REG_R15, UC_ARM_REG_SP, UC_ARM_REG_LR, UC_ARM_REG_PC]]
def disasm(self, address = None, dlen=0x80):
if not address:
address = self.pc
instructions = []
for instruction in self.md.disasm(self.uc.mem_read(address, dlen), address):
instructions.append(instruction)
return instructions
def print_ctx(self, print_fn=p_info):
state = f"""
PC: 0x{self.PC:8x}\t LR: 0x{self.LR:8x}\t SP: 0x{self.SP:8x}\t FP: 0x{self.FP:8x}\t
R0: 0x{self.R0:8x}\t R1: 0x{self.R1:8x}\t R2: 0x{self.R2:8x}\t R3: 0x{self.R3:8x}\t
R4: 0x{self.R4:8x}\t R5: 0x{self.R5:8x}\t R6: 0x{self.R6:8x}\t R7: 0x{self.R7:8x}\t
R8: 0x{self.R8:8x}\t R9: 0x{self.R9:8x}\tR10: 0x{self.R10:8x}\tR11: 0x{self.R11:8x}\t
R12: 0x{self.R12:8x}\tR13: 0x{self.R13:8x}\tR14: 0x{self.R14:8x}\tR15: 0x{self.R15:8x}\t
"""
print_fn(state)
# ========= Registers =========
@property
def pc(self):
return self.uc.reg_read(UC_ARM_REG_PC)
@pc.setter
def pc(self, value):
self.uc.reg_write(UC_ARM_REG_PC, value)
@property
def PC(self):
return self.uc.reg_read(UC_ARM_REG_PC)
@PC.setter
def PC(self, value):
self.uc.reg_write(UC_ARM_REG_PC, value)
@property
def SP(self):
return self.uc.reg_read(UC_ARM_REG_SP)
@SP.setter
def SP(self, value):
self.uc.reg_write(UC_ARM_REG_SP, value)
@property
def LR(self):
return self.uc.reg_read(UC_ARM_REG_LR)
@LR.setter
def LR(self, value):
self.uc.reg_write(UC_ARM_REG_LR, value)
@property
def FP(self):
return self.uc.reg_read(UC_ARM_REG_R11)
@FP.setter
def FP(self, value):
self.uc.reg_write(UC_ARM_REG_R11, value)
@property
def R0(self):
return self.uc.reg_read(UC_ARM_REG_R0)
@R0.setter
def R0(self, value):
self.uc.reg_write(UC_ARM_REG_R0, value)
@property
def R1(self):
return self.uc.reg_read(UC_ARM_REG_R1)
@R1.setter
def R1(self, value):
self.uc.reg_write(UC_ARM_REG_R1, value)
@property
def R2(self):
return self.uc.reg_read(UC_ARM_REG_R2)
@R2.setter
def R2(self, value):
self.uc.reg_write(UC_ARM_REG_R2, value)
@property
def R3(self):
return self.uc.reg_read(UC_ARM_REG_R3)
@R3.setter
def R3(self, value):
self.uc.reg_write(UC_ARM_REG_R3, value)
@property
def R4(self):
return self.uc.reg_read(UC_ARM_REG_R4)
@R4.setter
def R4(self, value):
self.uc.reg_write(UC_ARM_REG_R4, value)
@property
def R5(self):
return self.uc.reg_read(UC_ARM_REG_R5)
@R5.setter
def R5(self, value):
self.uc.reg_write(UC_ARM_REG_R5, value)
@property
def R6(self):
return self.uc.reg_read(UC_ARM_REG_R6)
@R6.setter
def R6(self, value):
self.uc.reg_write(UC_ARM_REG_R6, value)
@property
def R7(self):
return self.uc.reg_read(UC_ARM_REG_R7)
@R7.setter
def R7(self, value):
self.uc.reg_write(UC_ARM_REG_R7, value)
@property
def R8(self):
return self.uc.reg_read(UC_ARM_REG_R8)
@R8.setter
def R8(self, value):
self.uc.reg_write(UC_ARM_REG_R8, value)
@property
def R9(self):
return self.uc.reg_read(UC_ARM_REG_R9)
@R9.setter
def R9(self, value):
self.uc.reg_write(UC_ARM_REG_R9, value)
@property
def R10(self):
return self.uc.reg_read(UC_ARM_REG_R10)
@R10.setter
def R10(self, value):
self.uc.reg_write(UC_ARM_REG_R10, value)
@property
def R11(self):
return self.uc.reg_read(UC_ARM_REG_R11)
@R11.setter
def R11(self, value):
self.uc.reg_write(UC_ARM_REG_R11, value)
@property
def R12(self):
return self.uc.reg_read(UC_ARM_REG_R12)
@R12.setter
def R12(self, value):
self.uc.reg_write(UC_ARM_REG_R12, value)
@property
def R13(self):
return self.uc.reg_read(UC_ARM_REG_R13)
@R13.setter
def R13(self, value):
self.uc.reg_write(UC_ARM_REG_R13, value)
@property
def R14(self):
return self.uc.reg_read(UC_ARM_REG_R14)
@R14.setter
def R14(self, value):
self.uc.reg_write(UC_ARM_REG_R14, value)
@property
def R15(self):
return self.uc.reg_read(UC_ARM_REG_R15)
@R15.setter
def R15(self, value):
self.uc.reg_write(UC_ARM_REG_R15, value)
@property
def cpsr(self):
return self.uc.reg_read(UC_ARM_REG_CPSR)
@cpsr.setter
def cpsr(self, value):
self.uc.reg_write(UC_ARM_REG_CPSR, value)
@property
def spsr(self):
return self.uc.reg_read(UC_ARM_REG_SPSR)
@spsr.setter
def spsr(self, value):
self.uc.reg_write(UC_ARM_REG_SPSR, value)
@property
def zf(self):
return self.cpsr & 0x40000000
@zf.setter
def zf(self, value):
if value:
self.cpsr |= 0x40000000
else:
self.cpsr &= ~0x40000000
@property
def is_thumb(self):
return self.cpsr & 0x20
@is_thumb.setter
def is_thumb(self, value):
if value:
self.cpsr |= 0x20 # Set the thumb bit
else:
self.cpsr &= ~0x20 # Clear the thumb bit

View File

@ -40,7 +40,7 @@ class ARM64UC_Emulator():
s = b''
while 1:
b = self.uc.mem_read(at, 1)
at += 12
at += 1
if b == b'\0':
return s
s += b