466 lines
16 KiB
Python
466 lines
16 KiB
Python
|
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 *
|
||
|
|
||
|
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 device_main(cd : "ConcreteDevice", args):
|
||
|
'''
|
||
|
Main function that will do execution for the device.
|
||
|
'''
|
||
|
cd.test_connection()
|
||
|
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, [])
|