from ghidra_assistant.utils.utils import * from ghidra_assistant.utils.archs.asm_utils import * from ghidra_assistant.concrete_device import * from qiling.const import QL_ARCH from ghidra_assistant.utils.debugger.debugger_archs.ga_arm_thumb import GA_arm_thumb_debugger 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) logger = setup_logger("") # get root logger logger.setLevel(logging.DEBUG) def boot_bct(): rcm = TegraRCM() rcm.dev.read_chip_id() file = "/home/eljakim/Source/tegrax1_plus/source/Shofel2_T124_python/ShofEL2-for-T124/boot_bct.bin" rcm.send_payload(file, thumb=1) def boot_gardenia(): rcm = TegraRCM() rcm.dev.read_chip_id() file = "/tmp/gardenia/build/bootloader" rcm.send_payload(file, thumb=1) r = rcm.dev.read(0x10000) try: r2 =rcm.dev.read(0x10000) except: time.sleep(1) pass def device_setup(concrete_device : "ConcreteDevice"): ''' This function has to return a device object that handles the communication between the host and the device. Must implement at least the following: * read * write In this function you can also setup known values that the concrete debugger will use, like the target architecture and locations of the debugger and the vbar ''' #Setup architecture concrete_device.arch = QL_ARCH.ARM concrete_device.ga_debugger_location = 0x4000E000 concrete_device.ga_vbar_location = 0x40011000 concrete_device.ga_storage_location = 0x40013000 concrete_device.ga_stack_location = 0x40014000 rcm = TegraRCM() rcm.dev.read_chip_id() file = "/home/eljakim/Source/ghidra_assistent/source/utils/debugger/remote_shellcode/bin/nvidia_shield_t/debugger.bin" file = "/home/eljakim/Source/gupje/source/bin/nvidia_shield/debugger.bin" file = "/home/eljakim/Source/gupje/source/bin/nvidia_shield_t/debugger.bin" # file = "/tmp/gardenia/build/bootloader" rcm.send_payload(file, thumb=1) concrete_device.arch_dbg = GA_arm_thumb_debugger(concrete_device.ga_vbar_location, concrete_device.ga_debugger_location, concrete_device.ga_storage_location) assert(rcm.dev.read(0x100) == b"GiAs") concrete_device.arch_dbg.read = rcm.dev.read concrete_device.arch_dbg.write = rcm.dev.write #Overwrite all calls to make the concrete target function properly concrete_device.copy_functions() # And add custom functions def read_u32(address): return u32(concrete_device.memdump_region(address, 4)) def write_u32(address, value): concrete_device.memwrite_region(address, p32(value)) def read_str(address): d = b"" while True: c = concrete_device.memdump_region(address, 1) if c == b"\x00": break d += c address += 1 return d concrete_device.read_u32 = read_u32 concrete_device.write_u32 = write_u32 concrete_device.read_str = read_str concrete_device.debugger_main = concrete_device.get_debugger_location() return rcm def test_arm_asm(cd : "ConcreteDevice"): cd.arch_dbg.state.auto_sync = False shellcode = f""" .align 1 start: ldr r0, =addr_some_addr ldr r1, =0x1000 ldr r2, =addr_debugger_storage ldr r4, =addr_usb_write bx r4 bx lr ldr r0, =addr_debugger_main bx r0 b flush ldr r0, =addr_debugger_storage ldr r1, =0x77 ldr r2, =0x77 str r0, [r0] str r1, [r0, #4] str r2, [r0, #8] bx lr flush: @ Flush the instruction cache @ mov r0, #0 @ Select the instruction cache @ mcr p15, 0, r0, c7, c5 @ Invalidate the entire instruction cache @ Flush the data cache @ mov r0, #0 @ Select the data cache @ mcr p15, 0, r0, c7, c6 @ Invalidate the entire data cache bx lr ldr r0, =addr_debugger_main bx r0 .align 3 addr_debugger_storage: .word 0x40013000 addr_usb_write: .word 0x001065C0 addr_rom_start: .word 0x00100000 addr_debugger_main : .word 0x4000E000 addr_some_addr: .word 0x40020000 .align 1 addr_debugger_main_t: .word 0x4000E001 """ asm = ks_arm.asm(shellcode, as_bytes=True)[0] cd.memwrite_region(0x40013000 + 0x2000, asm) cd.jump_to(0x40013000 + 0x2000) #branch arm try: r = cd.read(0x200) pass except Exception as e: print(str(e)) pass pass def dump_full_dram_context(cd : "ConcreteDevice"): # BootROM rom = cd.memdump_region(0x100000, 0x17000) imem = cd.memdump_region(0x40000000, 0xfc00) SETUP_SDRAM = 0x00101a14 def attempt_boot_bct(cd : "ConcreteDevice"): dat = open("imem_good.bin", 'rb').read() cd.memwrite_region(0x40000000, dat[:0xe000]) cd.write(b"MAIN") cd.arch_dbg.state.auto_sync = False cd.arch_dbg.state.print_ctx(print) dump_full_dram_context(cd) jump_stub = f""" .align 1 ldr r0, addr_debugger_main_t bx r0 .align 4 addr_debugger_main_t: .word 0x4000E001 """ jump_stub = ks_thumb.asm(jump_stub, as_bytes=True)[0] cd.memwrite_region(SETUP_SDRAM, jump_stub) # Test dumb restore_and_jump # cd.arch_dbg.state.R0 = 0x00101c18 # cd.arch_dbg.state.R3 = 0x4000E001 def test_restore_and_jump(): cd.arch_dbg.state.LR = 0x4000E001 cd.restore_stack_and_jump(0x00101c28 + 1) # Some dumb function that sets r0 assert cd.read(100) == b"GiAs" cd.arch_dbg.state.LR = 0x4000E001 cd.arch_dbg.state.R0 = 0x00101c18 cd.restore_stack_and_jump(SETUP_SDRAM + 1) cd.jump_to(0x00101c28 + 1) pass CLK_RST_CONTROLLER_MISC_CLK_ENB = 0x48 IROM_LEN = 0x00010000 def hw_init(cd : "ConcreteDevice"): # Get chip id # 0x14017 cd.arch_dbg.state.print_ctx(print) def NvBootUtilGetTimeUS(): cd.arch_dbg.state.LR = cd.arch_dbg.debugger_addr | 1 cd.arch_dbg.restore_stack_and_jump(0x00100c7e | 1) assert cd.read(4) == b'GiAs', "Debugger crashed?" return cd.arch_dbg.state.R0 chip_id = u32(cd.memdump_region(mmio_reg32(BASE_ADDRESSES['APB_MISC_BASE'], APB_MISC_GP_HIDREV), 4)) chip_version = (chip_id >> 4) & 0xF def clock_enable_fuse(): addr = mmio_reg32(BASE_ADDRESSES['CLOCK_BASE'], CLK_RST_CONTROLLER_MISC_CLK_ENB) val = cd.read_u32(addr) enable = 1 cd.write_u32(addr, val & 0xEFFFFFFF | ((enable & 1) << 28)) # clock_enable_fuse() # Clear tz ram, # cd.memwrite_region(BASE_ADDRESSES['TZRAM_BASE'], b"\x00" * BASE_ADDRESSES['TZRAM_SIZE']) # TODO # clock_enable_se # clock_enable_fuse # fuse_disable_program # mc_enable # _config_oscillators # _config_gpios # init_sdram def setup_sdram(): nvbootcontext = 0x40002404 cd.arch_dbg.state.R0 = nvbootcontext cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 cd.restore_stack_and_jump(0x00101a14 | 1) pass # setup_sdram() def relocate_debugger(): ''' 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) relocate_debugger() cd.memdump_region(0x40020000, 0x100) # setup_sdram() def nvbootmain(): # cd.arch_dbg.state.R1 = 0x40000000 cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 cd.restore_stack_and_jump(0x00101318 | 1) pass # nvbootmain() NVCOLDBOOT = 0x00101ad2 def nvbootcoldboot(): ''' Works, attempts to load the BCT from the EMMC ''' nvbootinfo = 0x40000000 cd.write_u32(nvbootinfo, 0x400001) cd.write_u32(nvbootinfo + 4, 0x400001) cd.write_u32(nvbootinfo + 8, 0x400001) # cd.write_u32(nvbootinfo + 0xc, 0x00000001) # Boot type, set later also cd.write_u32(nvbootinfo + 0x10, 5) #Irom # cd.write_u32(nvbootinfo + 0x10, 9) def NvBootClocksGetOscFreq(): return cd.read_u32(cd.read_u32(0x00100214) + 0x10) >> 0x1c cd.write_u32(nvbootinfo + 0x28, NvBootClocksGetOscFreq()) #Irom cd.write_u32(nvbootinfo + 0xf0, nvbootinfo + 256) # End of boot info # Move debugger in r0, to jump to that on failure cd.arch_dbg.state.R0 = 0x40020000 # cd.ga_debugger_location | 1 cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 cd.restore_stack_and_jump(NVCOLDBOOT | 1) assert cd.read(4) == b"GiAs", "Failed to jump to debugger" # BCT should be loaded at 0x40020000 # nvbootcoldboot() boot_to = 0x0 if cd.arch_dbg.state.R0 == 0: # 0x40020000 should point to 0x4000e000 boot_to = cd.read_u32(0x40020000) assert boot_to == 0x4000e000, "BCT not loaded correctly?" else: # BCT not loaded, fix this boot_to = 0x4000e000 imem = open("/tmp/imem3_bct", 'rb').read() cd.memwrite_region(0x40000000, imem) # Setup sdram? # cd.arch_dbg.state.R0 = 0x40002404 # cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 # cd.restore_stack_and_jump(0x00101a14 | 1) # pass # NvBootSdramQueryTotalSize # cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 # cd.restore_stack_and_jump(0x00105dcc | 1) # pass #nvloadbootloader # cd.arch_dbg.state.R0 = 0x40002404 # cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 # cd.restore_stack_and_jump(0x00104822 | 1) # pass # Apply patches # patch validation of secure os stub = f""" mov r0, 0x0 bx lr """ cd.memwrite_region(0x4001a2c0, ks_thumb.asm(stub, as_bytes=True)[0]) jump_stub = f""" ldr r12, addr_debugger_main_t bx r12 .align 4 addr_debugger_main_t: .word {hex(cd.ga_debugger_location | 1)} """ jump_stub = ks_arm.asm(jump_stub, as_bytes=True)[0] # Setup code for log hook cd.memwrite_region(0x4001cadc, jump_stub) cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 cd.restore_stack_and_jump(boot_to) while True: try: r = cd.read(0x100) if cd.arch_dbg.state.R0 == 0x77: # In nvtloadbinary dat = open("/tmp/bootloader.bin", 'rb').read() cd.memwrite_region(0x83d88000, dat[:0x90000]) cd.arch_dbg.state.R0 = 0 cd.restore_stack_and_jump(cd.arch_dbg.state.LR) continue elif cd.arch_dbg.state.R0 == 0x76: cd.arch_dbg.state.R0 = 0 cd.restore_stack_and_jump(0x4000e188) continue else: # In log msg = cd.read_str(cd.arch_dbg.state.R0) # Parse arguments, TODO args = msg.count(b"%") if b"Bootloader downloaded" in msg: # Lets try dump the correct bootloader pass elif b"corrupted" in msg or b"GPT failed" in msg: # Restore bootloader print(msg) dat = open("/tmp/bootloader.bin", 'rb').read() cd.memwrite_region(0x83d88000, dat[:0x90000]) cd.memwrite_region(0x83d90260, ks_thumb.asm("mov r0, r0", as_bytes=True)[0] * 2) # cd.memwrite_region(0x83e130e6, b"\x00") # Fastboot unlock? # Remove fastboot lock shellcode = f""" mov r0, 0x1 bx lr """ cd.memwrite_region(0x83dd0eb0, ks_arm.asm(shellcode, as_bytes=True)[0]) # Jump to bootloader loaded cd.arch_dbg.state.R0 = 0 cd.arch_dbg.state.LR = 0x40018ea0 cd.restore_stack_and_jump(cd.arch_dbg.state.LR) continue elif b"WB0" in msg: cd.arch_dbg.state.print_ctx(print) cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 cd.restore_stack_and_jump(0x4000e188) continue elif b"Sdram initialization" in msg: pass print(f"{hex(cd.arch_dbg.state.LR)}:{msg}") cd.restore_stack_and_jump(cd.arch_dbg.state.LR) except Exception as e: pass # Jump in bootloader cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 cd.restore_stack_and_jump(boot_to) # ARM code pass # Continue next of init fase cd.arch_dbg.state.print_ctx(print) cd.arch_dbg.state.R0 = 0 # Set as success cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 cd.restore_stack_and_jump(0x00101b76 | 1) pass def NvBootMainSecureRomExit(): cd.arch_dbg.state.R0 = 0 #warmboot cd.arch_dbg.state.R1 = boot_to cd.arch_dbg.state.R2 = 0x0 # Security bitfield cd.arch_dbg.state.R3 = 0x0 # debug bitfield cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 cd.restore_stack_and_jump(0x0010121e | 1) NvBootMainSecureRomExit() def NvBootArcDisable(): cd.arch_dbg.state.R0 = boot_to cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 cd.restore_stack_and_jump(0x0010166e | 1) cd.read(4) NvBootArcDisable() # def NvBootMainAsmSecureExit(): # cd.arch_dbg.state.R0 = boot_to # cd.arch_dbg.state.R1 = cd.read_u32(0x001015d0) # cd.arch_dbg.state.R2 = cd.read_u32(0x001015f0) # cd.arch_dbg.state.R3 = 0 # cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 # cd.restore_stack_and_jump(0x00100d80 | 1) # NvBootMainAsmSecureExit() pass # cd.memwrite_region(BASE_ADDRESSES['IROM_BASE'], b"\x00" * 0xc000)#)open("imem_good.bin", 'rb').read()[:IROM_LEN] def resetfullchip(): cd.R0 = BASE_ADDRESSES['IROM_BASE'] cd.R1 = BASE_ADDRESSES['IROM_BASE'] cd.R2 = BASE_ADDRESSES['IROM_BASE'] # cd.restore_stack_and_jump(0x00100624 | 1) cd.restore_stack_and_jump(0x0010122c | 1) pass # 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) pass if __name__ == "__main__": logger = setup_logger("DEBUGGER") logger.setLevel(logging.INFO) cd = ConcreteDevice(None, False) cd.dev = device_setup(cd) cd.device_main = device_main cd.device_main(cd, [])