diff --git a/GA_debugger.py b/GA_debugger.py index edcb408..7212eab 100644 --- a/GA_debugger.py +++ b/GA_debugger.py @@ -15,24 +15,6 @@ 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. @@ -287,8 +269,10 @@ def hw_init(cd : "ConcreteDevice"): assert cd.read(4) == b"GiAs", "Failed to jump to debugger" # BCT should be loaded at 0x40020000 - - # nvbootcoldboot() + + + nvbootcoldboot() + # dump_emmc() boot_to = 0x0 if cd.arch_dbg.state.R0 == 0: # 0x40020000 should point to 0x4000e000 @@ -297,7 +281,7 @@ def hw_init(cd : "ConcreteDevice"): else: # BCT not loaded, fix this boot_to = 0x4000e000 - imem = open("/tmp/imem3_bct", 'rb').read() + imem = open("bin/imem_bct", 'rb').read() cd.memwrite_region(0x40000000, imem) # Setup sdram? @@ -317,6 +301,8 @@ def hw_init(cd : "ConcreteDevice"): # cd.restore_stack_and_jump(0x00104822 | 1) # pass + + # Apply patches # patch validation of secure os stub = f""" @@ -333,6 +319,14 @@ def hw_init(cd : "ConcreteDevice"): """ jump_stub = ks_arm.asm(jump_stub, as_bytes=True)[0] + NVBOOTSDMMCREADPAGE = 0x4001d1dc + emmc_dump_patch = cd.memdump_region(NVBOOTSDMMCREADPAGE, 0x20) + def dump_emmc_patches(): + cd.memwrite_region(NVBOOTSDMMCREADPAGE, jump_stub) + + # Add this to enable emmc hooks + # dump_emmc_patches() + # Setup code for log hook cd.memwrite_region(0x4001cadc, jump_stub) @@ -342,7 +336,29 @@ def hw_init(cd : "ConcreteDevice"): while True: try: r = cd.read(0x100) - if cd.arch_dbg.state.R0 == 0x77: + if cd.arch_dbg.state.LR == NVBOOTSDMMCREADPAGE: + # Try dumping the emmc + cd.memwrite_region(NVBOOTSDMMCREADPAGE, emmc_dump_patch) + print(f"block={cd.arch_dbg.state.R0} page={cd.arch_dbg.state.R1} buffer={hex(cd.arch_dbg.state.R2)}") + + def dirty_emmc_read(block, page, target_buffer): + cd.arch_dbg.state.LR = cd.ga_debugger_location | 1 + cd.arch_dbg.state.R0 = block + cd.arch_dbg.state.R1 = page + cd.arch_dbg.state.R2 = target_buffer + cd.arch_dbg.state.R3 = cd.ga_debugger_location | 1 + cd.restore_stack_and_jump(NVBOOTSDMMCREADPAGE | 1) + resp = cd.read(0x200) + pass + + def dump_emmc(): + out_buf = 0x40020000 + dirty_emmc_read(0, 0, out_buf) + dat = cd.memdump_region(out_buf, 512) + pass + dump_emmc() + pass + elif cd.arch_dbg.state.R0 == 0x77: # In nvtloadbinary dat = open("/tmp/bootloader.bin", 'rb').read() cd.memwrite_region(0x83d88000, dat[:0x90000]) @@ -468,16 +484,14 @@ def device_main(cd : "ConcreteDevice", args): Main function that will do execution for the device. ''' cd.test_connection() - partial_emu = True + partial_emu = False 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) + else: + hw_init(cd) + attempt_boot_bct(cd) + test_arm_asm(cd) pass if __name__ == "__main__": diff --git a/ShofEL2-for-T124/intermezzo.bin b/ShofEL2-for-T124/intermezzo.bin new file mode 100755 index 0000000..608dc27 Binary files /dev/null and b/ShofEL2-for-T124/intermezzo.bin differ diff --git a/__pycache__/partial_emulation.cpython-310.pyc b/__pycache__/partial_emulation.cpython-310.pyc deleted file mode 100644 index f11e9a5..0000000 Binary files a/__pycache__/partial_emulation.cpython-310.pyc and /dev/null differ diff --git a/bin/bootloader.bin b/bin/bootloader.bin new file mode 100644 index 0000000..21c7da1 Binary files /dev/null and b/bin/bootloader.bin differ diff --git a/bin/bootrom_t124.bin b/bin/bootrom_t124.bin new file mode 100644 index 0000000..46f2f9b Binary files /dev/null and b/bin/bootrom_t124.bin differ diff --git a/bin/dumped.bin b/bin/dumped.bin new file mode 100644 index 0000000..ab005b7 Binary files /dev/null and b/bin/dumped.bin differ diff --git a/bin/dumped_ref0.bin b/bin/dumped_ref0.bin new file mode 100644 index 0000000..ab005b7 Binary files /dev/null and b/bin/dumped_ref0.bin differ diff --git a/bin/imem_good.bin b/bin/imem_good.bin new file mode 100644 index 0000000..5a84e59 Binary files /dev/null and b/bin/imem_good.bin differ diff --git a/bin/imem_ref0_t124.bin b/bin/imem_ref0_t124.bin new file mode 100644 index 0000000..bc3ae5b Binary files /dev/null and b/bin/imem_ref0_t124.bin differ diff --git a/bin/t124_brom_hwio.pickle b/bin/t124_brom_hwio.pickle new file mode 100644 index 0000000..375b863 Binary files /dev/null and b/bin/t124_brom_hwio.pickle differ diff --git a/blog/part1.md b/blog/part1.md index 44a77d5..1e8736f 100644 --- a/blog/part1.md +++ b/blog/part1.md @@ -30,7 +30,7 @@ This will connect the device as a sdcard and allows you to reflash the bootloade But this is no fun at all and I can't introduce my debugger, so let's continue our software only approach. ## Debugger -The first goal is to build my debugger for this target. When connecting the device to a Linux based PCB I see the following message in dmesg: +The first goal is to build my debugger for this target. When connecting the device to a Linux based computer I see the following message in dmesg: ```bash [323058.201469] usb 1-5.3.1: new high-speed USB device number 91 using xhci_hcd diff --git a/inspect_hwio.py b/inspect_hwio.py new file mode 100644 index 0000000..2d02cd7 --- /dev/null +++ b/inspect_hwio.py @@ -0,0 +1,99 @@ +import argparse, pickle + +''' + sdmmc1: sdhci@700b0000 { + compatible = "nvidia,tegra210-sdhci", "nvidia,tegra132-sdhci"; + reg = <0x0 0x700b0000 0x0 0x200>; + interrupts = ; + clocks = <&tegra_car TEGRA210_CLK_SDMMC1>; + resets = <&tegra_car 14>; + reset-names = "sdhci"; + status = "disabled"; + }; + + pinmux: pinmux@700008d4 { + compatible = "nvidia,tegra210-pinmux"; + reg = <0x0 0x700008d4 0x0 0x299 /* Pad control registers */ + 0x0 0x70003000 0x0 0x290>; /* Mux registers */ + #gpio-range-cells = <3>; + status = "disabled"; + }; + + tegra_car: clock@60006000 { + compatible = "nvidia,tegra210-car", "syscon"; + reg = <0x0 0x60006000 0x0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + rtc: rtc@7000e000 { + compatible = "nvidia,tegra-rtc"; + reg = <0x0 0x7000e000 0x0 0x100>; + interrupts = <0 2 0x04>; + status = "disabled"; + }; + tegra_timer: timer@60005000 { + compatible = "nvidia,tegra210-timer"; + reg = <0x0 0x60005000 0x0 0x400>; + interrupts = , + , + , + ; + clocks = <&tegra_car TEGRA210_CLK_TIMER>; + }; + rtc: rtc@7000e000 { + compatible = "nvidia,tegra-rtc"; + reg = <0x0 0x7000e000 0x0 0x100>; + interrupts = <0 2 0x04>; + status = "disabled"; + }; + +''' + +t124_mem_map = { + 0x60006000 : + { + "name" : "tegra_car", + "size" : 0x1000, + }, + 0x60005000 : { + "name" : "tegra_timer", + "size" : 0x400, + }, + 0x70019000 : + { + "name" : "memory_controller", + "size" : 0x1000, + }, + 0x700b0000 : { + "name" : "sdhci1", + "size" : 0x200, + }, + 0x70003000 : { + "name" : "pinmux", + "size" : 0x290, + }, + 0x7000e000 : { + "name" : "rtc", + "size" : 0x100, + } +} + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Inspect HWIO") + parser.add_argument("hwio", help="Path to HWIO file") + args = parser.parse_args() + + hwio = pickle.load(open(args.hwio, "rb")) + + devices = {} + for address in hwio: + t_dev = address - (address % 0x1000) + if t_dev not in devices: + devices[t_dev] = [] + + devices[t_dev].append(address) + + for d in devices: + print(f"Device at 0x{d:04x} {t124_mem_map[d]['name'] if d in t124_mem_map else ''}") + pass \ No newline at end of file diff --git a/partial_emulation.py b/partial_emulation.py index 13274ee..c95d7af 100644 --- a/partial_emulation.py +++ b/partial_emulation.py @@ -1,8 +1,13 @@ -import typing, pathlib, struct, argparse +import typing, pathlib, struct, argparse, pickle, hashlib, time from ghidra_assistant.utils.archs.arm.arm_emulator import * from ghidra_assistant.ghidra_assistant import GhidraAssistant from ghidra_assistant.concrete_device import ConcreteDevice +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding, rsa +from cryptography.hazmat.backends import default_backend +from cryptography.exceptions import InvalidSignature + if typing.TYPE_CHECKING: from GA_debugger import * @@ -34,12 +39,17 @@ class TegraDevice(): self.fuses_visible = 0 def read(self, address, size): + if len(self.emulator.saved_hwio[address]) > 0: + val = self.emulator.saved_hwio[address].pop(0) + self.emulator.write_ptr(address, val) + return True if address == TegraDevice.CLK_RST_CONTROLLER_MISC_CLK_ENB_0: self.emulator.write_ptr(TegraDevice.CLK_RST_CONTROLLER_MISC_CLK_ENB_0, self.fuses_visible) return True raise NotImplemented def write(self, address, data): + return True if address == TegraDevice.CLK_RST_CONTROLLER_MISC_CLK_ENB_0: self.fuses_visible = data return True @@ -110,19 +120,130 @@ class EmmcDevice(TegraDevice): def __init__(self, emulator: "TegraEmulator") -> None: super().__init__(emulator) + def read(self, address, size): + pass + + def write(self, address, value): + pass + class CryptoDevice(TegraDevice): BASE = 0x70012000 SIZE = 0x1000 NAME = "Crypto" + KEY_SLOT_SELECT = BASE + 0x31c + KEY_SLOT_START = BASE + 0x320 + KEY_SLOT_END = BASE + 0x330 + SE_CONFIG_0 = BASE + 0x14 + SE_BUSY = BASE + 0x800 + SE_START = BASE + 0x8 + SE_SHA_CONFIG_0 = BASE + 0x200 + SE_SHA_MSG_LENGTH_0 = BASE + 0x204 + SE_SHA_MSG_LENGTH_2 = BASE + 0x208 + SE_SHA_MSG_LENGTH_2 = BASE + 0x20c + SE_IN_LL_ADDR_0 = BASE + 0x18 + SE_OUT_LL_ADDR_0 = BASE + 0x24 + SE_SHA_MSG_LEFT_0 = BASE + 0x214 + SE_RSA_EXP_SIZE_0 = BASE + 0x408 + SE_RSA_KEY_SIZE_0 = BASE + 0x404 + SE_RSA_KEYTABLE_ADDR_0 = BASE + 0x420 + + SE_RSA_KEY_SIZE_0_VAL_WIDTH_2048 = 3 + def __init__(self, emulator: "TegraEmulator") -> None: super().__init__(emulator) + self.keys = {} + self.current_key = b"\x00" * 16 + self.key_slot_config = 0 + self.key_slot = 0 + self.config = 0 + def read(self, address, size): + if address == CryptoDevice.SE_CONFIG_0: + self.emulator.write_ptr(CryptoDevice.SE_CONFIG_0, self.config) + elif address == CryptoDevice.SE_BUSY: + pass + else: + pass - + def write(self, address, value): + if address == CryptoDevice.KEY_SLOT_SELECT: + # Commit key to key slot + self.key_slot_config = value + self.key_slot = p32(value)[0] # First byte is keyslot + self.keys[self.key_slot] = self.current_key + elif address >= CryptoDevice.KEY_SLOT_START and address <= CryptoDevice.KEY_SLOT_END: + if self.key_slot not in self.keys: + self.keys[self.key_slot] = b"\x00" * 16 + # Keys are 16 bytes + offset = address - CryptoDevice.KEY_SLOT_START + self.current_key = self.current_key[:offset] + struct.pack(" None: + def __init__(self, hw_itm=True, saved_hwio="", init_uc=True) -> None: super().__init__(init_uc) self.log_hw_access = True + self.save_hwio = True + if saved_hwio: + self.saved_hwio = pickle.load(open(saved_hwio, "rb")) + else: + self.saved_hwio = {} self.hw_itm = hw_itm self.saved_blocks = {} try: @@ -175,10 +296,13 @@ class TegraEmulator(ARM_Emulator): def setup_devices(self): self.devices = {} - self.devices['fuse'] = FuseDevice(self) - self.devices['timer'] = TimerDevice(self) - self.devices['emmc'] = EmmcDevice(self) + # self.devices['fuse'] = FuseDevice(self) + # self.devices['timer'] = TimerDevice(self) + # self.devices['emmc'] = EmmcDevice(self) self.devices['crypto'] = CryptoDevice(self) + + # Setup EMMC data + self.emmc_raw_data = open("/home/eljakim/Source/tegrax1_plus/dump/nv-recovery-image-shield-tablet-lte-us-update3_1_1/blob", "rb").read()[0x112dc9f - 528:] self.devices['tegra'] = TegraDevice(self) # For all other devices def hook_unmapped(self, uc, access, address, size, value, user_data): @@ -190,8 +314,6 @@ class TegraEmulator(ARM_Emulator): # if self.log_hw_access: # p_info(f"{hex(self.pc)} HW access at 0x{address:x} with size {size}, value={hex(value)} and access {acces_str[access]}") - # Try and keep memory in sync with target device - if access == UC_MEM_WRITE: self.debugger.memwrite_region(address, self.uc.mem_read(address, size)) if access == UC_MEM_READ: @@ -208,6 +330,14 @@ class TegraEmulator(ARM_Emulator): val = struct.unpack(" {hex(value)}") @@ -255,8 +385,22 @@ class TegraEmulator(ARM_Emulator): elif access == UC_MEM_WRITE: dev.write(address, value) return True + + def sync_imem(self): + ''' + Syncs IMEM regions + ''' + if not hasattr(self, "debugger"): + pass + imem = self.uc.mem_read(0x40000000, 0x40000) + imem_debugger = self.debugger.memdump_region(0x40000000, 0x40000) + pass + def hook_hw_access(self, uc, access, address, size, value, user_data): + # if address == 0x70012800: + # self.sync_imem() + # pass if self.hw_itm: return self.hw_itm_handle(access, address, size, value) @@ -284,6 +428,8 @@ class TegraEmulator(ARM_Emulator): self.setup_warmboot_hook() self.setup_hook_blocks() self.setup_rcm_hooks() + self.setup_sdmmc_hooks() + self.setup_rsa_verify_hook() else: self.setup_log_hook() self.setup_hook_blocks() @@ -310,23 +456,55 @@ class TegraEmulator(ARM_Emulator): return True self.uc.hook_add(UC_HOOK_CODE, hook_warmboot, begin=0x00101f3a, end=0x00101f3a + 1) + def setup_rsa_verify_hook(self): + def hook_rsa_signature_verify(uc, address, size, user_data): # TODO not working + print(f"RSA signature verify keyslot={self.R0} keysize={self.R1} input={self.R2} message_hash={self.R3} input_size={self.R4} signature={self.R5} algo={self.R6} signature_size={self.R7}") + crypto = self.devices['crypto'] + crypto_key = crypto.rsa_key[:crypto.rsa_key_size // 8] + signature = self.uc.mem_read(self.R7, 0x100) + + modulus = int.from_bytes(crypto_key, byteorder='big') + public_numbers = rsa.RSAPublicNumbers(e=65537, n=modulus) + public_key = public_numbers.public_key(default_backend()) + + try: + public_key.verify( + signature, + b"", + padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH + ), + hashes.SHA256() + ) + print("Signature is valid.") + except InvalidSignature: + print("Signature is invalid.") + + # Verify signature + + self.PC = self.LR + self.R0 = 0 + return True + self.uc.hook_add(UC_HOOK_CODE, hook_rsa_signature_verify, begin=0x00102f08, end=0x00102f08 + 1) + 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] - bx_lr_thumb = self.ksT.asm("bx lr", as_bytes=True)[0] movs_0_r0_thumb = self.ksT.asm("movs r0, #0", as_bytes=True)[0] + # self.uc.mem_write(0x4001dfb0, self.sc.mov_0_r0 + self.sc.bx_lr) if self.target == "bootrom": #NvBootClocksIsPllStable, ret # self.uc.mem_write(0x00101730, bx_lr_thumb) # # NvBootClocksStartPll self.uc.mem_write(0x00101866, bx_lr_thumb) + # NvBootClocksPllDivRstCtrl self.uc.mem_write(0x001016ce, bx_lr_thumb) @@ -395,6 +573,24 @@ class TegraEmulator(ARM_Emulator): # And patch function to just return self.uc.mem_write(UART_LOG_HOOK, self.ks.asm("bx lr", as_bytes=True)[0]) + + def setup_sdmmc_hooks(self): + def hook_sdmmc(uc, address, size, user_data): + block = self.R0 + page = self.R1 + destbuf = self.R2 + offset = page * 0x200 + dat = self.emmc_raw_data[offset:offset + 0x200] + self.uc.mem_write(destbuf, dat) + self.PC = self.LR + return True + self.uc.hook_add(UC_HOOK_CODE, hook_sdmmc, begin=NVBOOTSDMMCREADPAGE, end=NVBOOTSDMMCREADPAGE + 1) + + def setup_se_sha_hash_hook(self): + def hook_se_sha(uc, address, size, user_data): + self.saved_se_sha[self.pc] = self.get_registers() + return True + self.uc.hook_add(UC_HOOK_CODE, hook_se_sha, begin=NVBOOTSESHAHASH, end=NVBOOTSESHAHASH + 1) def setup_hook_blocks(self, only_blocks=False): if only_blocks: @@ -405,7 +601,24 @@ class TegraEmulator(ARM_Emulator): self.uc.hook_add(UC_HOOK_BLOCK, hook_block) else: def hook_all(uc, address, size, user_data): + if self.pc == NVBOOTREADONEOBJECT: + info(f"NvBootReadOneObject context={self.R0:04x} dest={self.R1:04x} bct_obj_dst={self.R2:04x} num_copies={self.R3}") + if self.pc == 0x00104558: + # Sync 0x40000100, length 0x2000 + info(f"NvBootReadOneObject done dest={self.read_ptr(self.R1)}") + # if self.pc == NVBOOTSDMMCREADPAGE: + # # At this point we can read our on pages if requested. + # info(f"SdmmcReadPage block={self.R0} page={self.R1} destbuf={self.R2:04x}") # print(f"Block at {hex(self.LR)}") + if self.pc == 0x00102ee8: + pass + if self.pc == 0x00102f1e: + pass + elif self.pc == 0x00104418: + pass #Validatebct + elif self.pc == 0x00104452: + pass #validate done + self.saved_blocks[self.pc] = self.get_registers() return True self.uc.hook_add(UC_HOOK_CODE, hook_all, self) @@ -431,7 +644,7 @@ def do_partial_emu(debugger : ConcreteDevice, real_hw=True): emu = TegraEmulator() emu.install_debugger(debugger) else: - emu = TegraEmulator(hw_itm=False) + emu = TegraEmulator(hw_itm=False, saved_hwio="bin/t124_brom_hwio.pickle") emu.setup(target="bootrom") emu.run()