Shofel2_T124_python/GA_debugger.py

489 lines
16 KiB
Python
Raw Normal View History

2024-05-25 16:45:07 +00:00
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 *
2024-05-30 08:15:36 +00:00
from hw_in_the_loop import *
from partial_emulation import *
2024-05-25 16:45:07 +00:00
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
2024-05-30 08:15:36 +00:00
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)
2024-05-25 16:45:07 +00:00
def device_main(cd : "ConcreteDevice", args):
'''
Main function that will do execution for the device.
'''
cd.test_connection()
2024-05-30 08:15:36 +00:00
partial_emu = True
if partial_emu:
relocate_debugger(cd)
do_partial_emu(cd)
sys.exit(0)
hw_in_the_loop()
2024-05-25 16:45:07 +00:00
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, [])