moved files and cleaned a bit of code

This commit is contained in:
Eljakim Herrewijnen 2025-01-03 16:04:24 +01:00
parent 59eaf299ad
commit 43ad022669
13 changed files with 367 additions and 41 deletions

View File

@ -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__":

BIN
ShofEL2-for-T124/intermezzo.bin Executable file

Binary file not shown.

BIN
bin/bootloader.bin Normal file

Binary file not shown.

BIN
bin/bootrom_t124.bin Normal file

Binary file not shown.

BIN
bin/dumped.bin Normal file

Binary file not shown.

BIN
bin/dumped_ref0.bin Normal file

Binary file not shown.

BIN
bin/imem_good.bin Normal file

Binary file not shown.

BIN
bin/imem_ref0_t124.bin Normal file

Binary file not shown.

BIN
bin/t124_brom_hwio.pickle Normal file

Binary file not shown.

View File

@ -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

99
inspect_hwio.py Normal file
View File

@ -0,0 +1,99 @@
import argparse, pickle
'''
sdmmc1: sdhci@700b0000 {
compatible = "nvidia,tegra210-sdhci", "nvidia,tegra132-sdhci";
reg = <0x0 0x700b0000 0x0 0x200>;
interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
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 = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>;
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

View File

@ -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("<I", value) + self.current_key[offset + 4:]
elif address == CryptoDevice.SE_CONFIG_0:
self.config = value
elif address == CryptoDevice.SE_SHA_MSG_LENGTH_0:
self.msg_length = value
elif address == CryptoDevice.SE_SHA_CONFIG_0:
self.sha_config_0 = value
elif address == CryptoDevice.SE_IN_LL_ADDR_0:
self.in_ll_address = value
elif address == CryptoDevice.SE_OUT_LL_ADDR_0:
self.out_ll_address = value
elif address == CryptoDevice.SE_SHA_MSG_LEFT_0:
self.sha_msg_left = value
elif address == CryptoDevice.SE_START:
self.emulator.write_ptr(CryptoDevice.SE_BUSY, 1) # Busy
# Parse SE linked list
last_block = self.emulator.read_ptr(self.in_ll_address)
# Read block
def hash_block():
block_address = self.emulator.read_ptr(self.in_ll_address + 4)
size = self.emulator.read_ptr(self.in_ll_address + 8)
block = self.emulator.uc.mem_read(block_address, size)
self.in_ll_address += 12 # Move to next block
out_address = self.emulator.read_ptr(self.out_ll_address + 4)
out_size = self.emulator.read_ptr(self.out_ll_address + 8)
digest = hashlib.sha256(block).digest()
self.emulator.uc.mem_write(out_address, digest[:out_size])
while last_block != 0:
hash_block()
# Last block
hash_block()
self.emulator.write_ptr(CryptoDevice.SE_BUSY, 0) # Set CE to not busy
pass
# elif address == CryptoDevice.SE_SHA_MSG_LENGTH_2:
# self.msg_length_2 = value
elif address == CryptoDevice.SE_RSA_EXP_SIZE_0:
self.rsa_exp_size = value
elif address == CryptoDevice.SE_RSA_KEY_SIZE_0:
assert value == CryptoDevice.SE_RSA_KEY_SIZE_0_VAL_WIDTH_2048, "Only 2048 bit keys supported"
self.rsa_key_size = 2048
elif address == CryptoDevice.SE_RSA_KEYTABLE_ADDR_0:
# Copies ll key into CE
last_block = self.emulator.read_ptr(self.in_ll_address)
assert last_block == 0, "Only one block supported"
block_address = self.emulator.read_ptr(self.in_ll_address + 4)
size = self.emulator.read_ptr(self.in_ll_address + 8)
self.rsa_key = self.emulator.uc.mem_read(block_address, size)
else:
if value != 0:
pass
self.emulator.write_ptr(address, value)
NVBOOTREADONEOBJECT = 0x00104c40
NVBOOTSDMMCREADPAGE = 0x0010a5d6
NVBOOTSESHAHASH = 0x00102b6c
NVBOOTSERSAPSSSIGNATUREVERIFY = 0x00102ed2
class TegraEmulator(ARM_Emulator):
def __init__(self, hw_itm=True, init_uc=True) -> 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("<I", val)[0]
elif len(val) == 1:
val = struct.unpack("<B", val)[0]
# save all access for emulation
if self.save_hwio:
if address not in self.saved_hwio:
self.saved_hwio[address] = [val]
else:
self.saved_hwio[address].append(val)
p_info(f"{hex(self.pc)} READ at 0x{address:x} with size {size} value={hex(val)} | {hex(address)} <- {hex(val)}")
elif access == UC_MEM_WRITE:
p_info(f"{hex(self.pc)} WRITE at 0x{address:x} with size {size} value={hex(value)} | {hex(address)} -> {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()