commit 4a4ffaed1883441b76b9e7b6f4334159a482ade9 Author: Thomas Roth Date: Sun Nov 29 11:58:55 2020 +0100 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..27b76ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +backups/* +new_flash_image.bin diff --git a/1_sanity_check.sh b/1_sanity_check.sh new file mode 100755 index 0000000..8f29b01 --- /dev/null +++ b/1_sanity_check.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +echo "Running sanity checks..." +if ! openocd -v >/dev/null 2>&1; then + echo "OpenOCD does not seem to be working. Please validate that you have it installed correctly!" + exit 1 +fi + +if ! /usr/bin/env python3 -V >/dev/null 2>&1; then + echo "Could not run python3. Please validate that you have it installed correctly!" + exit 1 +fi + +if ! arm-none-eabi-objdump -v >/dev/null 2>&1; then + echo "Could not find arm-none-eabi-objdump. Please validate that it's installed and in PATH." + exit 1 +fi + +echo "Looks good!" \ No newline at end of file diff --git a/2_backup_flash.sh b/2_backup_flash.sh new file mode 100755 index 0000000..61aa8fa --- /dev/null +++ b/2_backup_flash.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +if [[ $# -ne 1 ]]; then + echo "Usage: backup_flash.sh " + exit 1 +fi + +if test -f backups/flash_backup.bin; then + echo "Already have a backup in backups/flash_backup.bin, refusing to overwrite." + exit 1 +fi + +ADAPTER=$1 + +echo "Make sure your Game & Watch is turned on and in the time screen. Press return when ready!" +read -n 1 + +mkdir -p backups + +echo "Attempting to dump flash using adapter $1." +echo "Running OpenOCD... (This will take roughly 30 seconds, you Game and Watch screen will blink in between.)" +if ! openocd -f openocd/flash_"$1".cfg >/dev/null 2>&1; then + echo "Failed to dump SPI flash from device. Verify debug connection and try again." + exit 1 +fi + +echo "Validating ITCM dump..." +if ! shasum --check shasums/itcm_backup.bin.sha1 >/dev/null 2>&1; then + echo "Failed to correctly dump ITCM. Restart Game & Watch and try again." + exit 1 +fi + + +echo "Extracting checksummed part..." +if ! dd if=backups/flash_backup.bin of=backups/flash_backup_checksummed.bin count=1040384 bs=1 >/dev/null 2>&1; then + echo "Failed to access flash_backup.bin" + echo "Verify openocd works correctly" + exit 1 +fi + +echo "Validating checksum..." +if ! shasum --check shasums/flash_backup_checksummed.bin.sha1 >/dev/null 2>&1; then + echo "Failed to verify checksum. Try again." + exit 1 +fi + +rm backups/flash_backup_checksummed.bin + +echo "Looks good! Successfully backed up the (encrypted) SPI flash to flash_backup.bin!" diff --git a/3_backup_internal_flash.sh b/3_backup_internal_flash.sh new file mode 100755 index 0000000..fa819f0 --- /dev/null +++ b/3_backup_internal_flash.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +set -e + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 " + exit 1 +fi + +ADAPTER=$1 + +if test -f backups/internal_flash_backup.bin; then + echo "Already have a backup in backups/internal_flash_backup.bin, refusing to overwrite." + exit 1 +fi + +echo "Generating encrypted flash image from backed up data..." +if ! python3 python/tcm_encrypt.py backups/flash_backup.bin backups/itcm_backup.bin payload/payload.bin new_flash_image.bin; then + echo "Failed to build encrypted flash image." + exit 1 +fi + +echo "Running flashloader..." + +if ! ./scripts/flashloader.sh $ADAPTER new_flash_image.bin; then + echo "Flashloader failed, check debug connection and try again." +fi + +echo "Flash flashed. Now do the following procedure:" +echo "- Disconnect power from the device" +echo "- Power it again" +echo "- Press and hold the power button" +echo "- Press return (while still holding the power button)!" + + +read -n 1 + +echo "Dumping internal flash..." +if ! openocd -f openocd/interface_"$1".cfg \ + -c "init;" \ + -c "halt;" \ + -c "dump_image backups/internal_flash_backup.bin 0x24000000 131072" \ + -c "exit;" >/dev/null 2>&1; then + echo "Dumping internal flash failed." + exit 1 +fi + +echo "Verifying internal flash backup..." +if ! shasum --check shasums/internal_flash_backup.bin.sha1 >/dev/null 2>&1; then + echo "The backup of the internal flash failed. Please try again." + exit 1 +fi + +rm new_flash_image.bin + +echo "Device backed up successful" \ No newline at end of file diff --git a/4_unlock_device.sh b/4_unlock_device.sh new file mode 100755 index 0000000..49fe08a --- /dev/null +++ b/4_unlock_device.sh @@ -0,0 +1,36 @@ +#!/bin/bash + + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 " + exit 1 +fi + +ADAPTER=$1 + + +echo "Unlocking your device will erase its internal flash. Even though your backup" +echo "is validated, this still can go wrong. Are you sure? (Y/y)" +read -n 1 -r +if [[ ! $REPLY =~ ^[Yy]$ ]] +then + echo "Aborted." + exit 1 +fi + +echo "Validating internal flash backup before proceeding..." +if ! shasum --check shasums/internal_flash_backup.bin.sha1 >/dev/null 2>&1; then + echo "Backup is not valid. Aborting." + exit 1 +fi + +echo "Unlocking device... (Takes up to 30 seconds.)" +if ! openocd -f openocd/interface_"$1".cfg \ + -c "init;" \ + -c "halt;" \ + -f openocd/rdp0.cfg >/dev/null 2>&1; then + echo "Unlocking device failed." + exit 1 +fi + +echo "Congratulations, your device has been unlocked. Please power-cycle it for the changes to take full effect." \ No newline at end of file diff --git a/firmware/flash_dumper.elf b/firmware/flash_dumper.elf new file mode 100755 index 0000000..ca294b4 Binary files /dev/null and b/firmware/flash_dumper.elf differ diff --git a/firmware/flash_programmer.elf b/firmware/flash_programmer.elf new file mode 100755 index 0000000..91712ec Binary files /dev/null and b/firmware/flash_programmer.elf differ diff --git a/openocd/flash.cfg b/openocd/flash.cfg new file mode 100644 index 0000000..5d0b690 --- /dev/null +++ b/openocd/flash.cfg @@ -0,0 +1,19 @@ +init +echo "Dump ITCM area" +dump_image backups/itcm_backup.bin 0x0 1300 +echo "Reset and halt" +reset halt +echo "Load image" +load_image firmware/flash_dumper.elf +reset halt +sleep 1000 +echo "Set stack pointer" +reg sp [mrw 0x20000000] +reg pc [mrw 0x20000004] +echo "Continuing, wait ca. 10 seconds" +resume +sleep 10000 +echo "Dumping" +dump_image backups/flash_backup.bin 0x24000000 1048576 +echo "Done!" +exit \ No newline at end of file diff --git a/openocd/flash_jlink.cfg b/openocd/flash_jlink.cfg new file mode 100644 index 0000000..8c42360 --- /dev/null +++ b/openocd/flash_jlink.cfg @@ -0,0 +1,4 @@ +# Use this script to upload flash dumper using a JLink + +source [find openocd/interface_jlink.cfg] +source [find openocd/flash.cfg] diff --git a/openocd/flash_stlink.cfg b/openocd/flash_stlink.cfg new file mode 100644 index 0000000..491b527 --- /dev/null +++ b/openocd/flash_stlink.cfg @@ -0,0 +1,4 @@ +# Use this script to upload flash dumper using an STLink + +source [find openocd/interface_stlink.cfg] +source [find openocd/flash.cfg] diff --git a/openocd/interface_jlink.cfg b/openocd/interface_jlink.cfg new file mode 100644 index 0000000..c3c62f6 --- /dev/null +++ b/openocd/interface_jlink.cfg @@ -0,0 +1,3 @@ +source [find interface/jlink.cfg] +transport select swd +source [find target/stm32h7x.cfg] diff --git a/openocd/interface_stlink.cfg b/openocd/interface_stlink.cfg new file mode 100644 index 0000000..5023c6c --- /dev/null +++ b/openocd/interface_stlink.cfg @@ -0,0 +1,5 @@ +source [find interface/stlink.cfg] +transport select hla_swd +source [find target/stm32h7x.cfg] + +reset_config none diff --git a/openocd/rdp0.cfg b/openocd/rdp0.cfg new file mode 100644 index 0000000..6c181ef --- /dev/null +++ b/openocd/rdp0.cfg @@ -0,0 +1,18 @@ + +init +echo "Reset and halt" +reset +halt + +# mwr +mww 0x52002008 0x08192A3B +mww 0x52002008 0x4C5D6E7F +sleep 100 +mwb 0x52002021 0xaa +sleep 100 +mwb 0x52002018 0x02 +sleep 200 +reset +sleep 10000 +# quit +exit \ No newline at end of file diff --git a/openocd/rdp1.cfg b/openocd/rdp1.cfg new file mode 100644 index 0000000..5ce5016 --- /dev/null +++ b/openocd/rdp1.cfg @@ -0,0 +1,18 @@ + +init +echo "Reset and halt" +reset +halt + +# mwr +mww 0x52002008 0x08192A3B +mww 0x52002008 0x4C5D6E7F +sleep 100 +mwb 0x52002021 0x55 +sleep 100 +mwb 0x52002018 0x02 +sleep 200 +reset +sleep 10000 +# quit +exit \ No newline at end of file diff --git a/payload/payload.S b/payload/payload.S new file mode 100644 index 0000000..7385aa3 --- /dev/null +++ b/payload/payload.S @@ -0,0 +1,66 @@ +.section .text +.global _start + +_start: +.code 16 + + +foo: + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + + + +mov r2, #0 +mov r4, #4 +loop: + ldr r0, src + ldr r1, dst + + add r0, r0, r2 + add r1, r1, r2 + + ldr r3, [r0] + str r3, [r1] + + add r2, r2, r4 + + b loop + +src: .long 0x08000000 +dst: .long 0x24000000 diff --git a/payload/payload.bin b/payload/payload.bin new file mode 100644 index 0000000..6b4add8 Binary files /dev/null and b/payload/payload.bin differ diff --git a/payload/payload.elf b/payload/payload.elf new file mode 100644 index 0000000..00981e4 Binary files /dev/null and b/payload/payload.elf differ diff --git a/python/tcm_encrypt.py b/python/tcm_encrypt.py new file mode 100644 index 0000000..db5ee50 --- /dev/null +++ b/python/tcm_encrypt.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +import argparse +import sys + +parser = argparse.ArgumentParser(description='') +parser = argparse.ArgumentParser() +parser.add_argument('flash_image', nargs=1, type=argparse.FileType('rb')) +parser.add_argument('tcm_ram_image', nargs=1, type=argparse.FileType('rb')) +parser.add_argument('code', nargs=1, type=argparse.FileType('rb')) +parser.add_argument('flash_out', nargs=1, type=argparse.FileType('wb')) +args = parser.parse_args() + + +def xor(b1, b2): + result = bytearray() + for b1, b2 in zip(b1, b2): + result.append(b1 ^ b2) + return result + + +flash_image = args.flash_image[0].read() +tcm_ram_image = args.tcm_ram_image[0].read() +code = args.code[0].read() + + +xor_image = xor(tcm_ram_image[:len(code)], flash_image) + + +new_flash_part = xor(code, xor_image) +flash_end = flash_image[len(code):] + + +args.flash_out[0].write(new_flash_part) +args.flash_out[0].write(flash_end) diff --git a/python/tcmhack.py b/python/tcmhack.py new file mode 100644 index 0000000..db5ee50 --- /dev/null +++ b/python/tcmhack.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +import argparse +import sys + +parser = argparse.ArgumentParser(description='') +parser = argparse.ArgumentParser() +parser.add_argument('flash_image', nargs=1, type=argparse.FileType('rb')) +parser.add_argument('tcm_ram_image', nargs=1, type=argparse.FileType('rb')) +parser.add_argument('code', nargs=1, type=argparse.FileType('rb')) +parser.add_argument('flash_out', nargs=1, type=argparse.FileType('wb')) +args = parser.parse_args() + + +def xor(b1, b2): + result = bytearray() + for b1, b2 in zip(b1, b2): + result.append(b1 ^ b2) + return result + + +flash_image = args.flash_image[0].read() +tcm_ram_image = args.tcm_ram_image[0].read() +code = args.code[0].read() + + +xor_image = xor(tcm_ram_image[:len(code)], flash_image) + + +new_flash_part = xor(code, xor_image) +flash_end = flash_image[len(code):] + + +args.flash_out[0].write(new_flash_part) +args.flash_out[0].write(flash_end) diff --git a/restore.sh b/restore.sh new file mode 100755 index 0000000..a90ff36 --- /dev/null +++ b/restore.sh @@ -0,0 +1,40 @@ +#!/bin/bash + + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 " + exit 1 +fi + + +if ! test -f backups/internal_flash_backup.bin; then + echo "No backup of internal flash found in backups/internal_flash_backup.bin" + exit 1 +fi + +if ! test -f backups/flash_backup.bin; then + echo "No backup of SPI flash found in backups/flash_backup.bin" + exit 1 +fi + +ADAPTER=$1 + +echo "Ok, restoring original firmware!" + +echo "Restoring SPI flash..." +if ! ./scripts/flashloader.sh $ADAPTER backups/flash_backup.bin; then + echo "Restoring SPI flash failed. Check debug connection and try again." + exit 1 +fi + +echo "Restoring internal flash..." +if ! openocd -f openocd/interface_"$1".cfg \ + -c "init;" \ + -c "halt;" \ + -c "program backups/internal_flash_backup.bin 0x08000000 verify;" \ + -c "exit;" >/dev/null 2>&1; then + echo "Restoring internal flash failed. Check debug connection and try again." + exit 1 +fi + +echo "Success, your device should be running the original firmware again!" \ No newline at end of file diff --git a/scripts/flashloader.sh b/scripts/flashloader.sh new file mode 100755 index 0000000..fee8b98 --- /dev/null +++ b/scripts/flashloader.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +ELF=firmware/flash_programmer.elf +ADDRESS=0 +SIZE=$((1024 * 1024)) +MAGIC="0xdeadbeef" +ERASE=1 +IMAGE=$2 +objdump=${OBJDUMP:-arm-none-eabi-objdump} + +function get_symbol { + name=$1 + objdump_cmd="${objdump} -t ${ELF}" + size=$(${objdump_cmd} | grep " $name" | cut -d " " -f1 | tr 'a-f' 'A-F') + printf "ibase=16\n${size}\n" | bc +} + +VAR_program_size=$(printf '0x%08x\n' $(get_symbol "program_size")) +VAR_program_address=$(printf '0x%08x\n' $(get_symbol "program_address")) +VAR_program_magic=$(printf '0x%08x\n' $(get_symbol "program_magic")) +VAR_program_done=$(printf '0x%08x\n' $(get_symbol "program_done")) +VAR_program_erase=$(printf '0x%08x\n' $(get_symbol "program_erase")) + + +if ! openocd -f openocd/interface_"$1".cfg \ + -c "init;" \ + -c "echo \"Resetting device\";" \ + -c "reset halt;" \ + -c "echo \"Programming ELF\";" \ + -c "load_image ${ELF};" \ + -c "reset halt;" \ + -c "sleep 100;" \ + -c "echo \"Loading image into RAM\";" \ + -c "load_image ${IMAGE} 0x24000000;" \ + -c "mww ${VAR_program_size} ${SIZE}" \ + -c "mww ${VAR_program_address} ${ADDRESS}" \ + -c "mww ${VAR_program_magic} ${MAGIC}" \ + -c "mww ${VAR_program_erase} ${ERASE}" \ + -c "reg sp [mrw 0x20000000];" \ + -c "reg pc [mrw 0x20000004];" \ + -c "echo \"Starting flash process\";" \ + -c "resume; exit;" >/dev/null 2>&1; then + echo "Loading failed." + exit 1 +fi + + +echo "Loaded flashloader, flashing SPI, please wait." + +echo "(If this takes more than 2 minutes something went wrong.)" +echo "(If the screen blinks rapidly, something went wrong.)" +echo "(If the screen blinks slowly, everything worked but the script didn't detect it)" +while true; do + DONE_MAGIC=$(openocd -f openocd/interface_${1}.cfg -c "init; mdw ${VAR_program_done}" -c "exit;" 2>&1 | grep ${VAR_program_done} | cut -d" " -f2) + if [[ "$DONE_MAGIC" == "cafef00d" ]]; then + echo "Done!" + break; + fi + sleep 1 +done \ No newline at end of file diff --git a/shasums/flash_backup_checksummed.bin.sha1 b/shasums/flash_backup_checksummed.bin.sha1 new file mode 100644 index 0000000..3423f9c --- /dev/null +++ b/shasums/flash_backup_checksummed.bin.sha1 @@ -0,0 +1 @@ +eea70bb171afece163fb4b293c5364ddb90637ae backups/flash_backup_checksummed.bin diff --git a/shasums/internal_flash_backup.bin.sha1 b/shasums/internal_flash_backup.bin.sha1 new file mode 100644 index 0000000..3552896 --- /dev/null +++ b/shasums/internal_flash_backup.bin.sha1 @@ -0,0 +1 @@ +efa04c387ad7b40549e15799b471a6e1cd234c76 backups/internal_flash_backup.bin diff --git a/shasums/itcm_backup.bin.sha1 b/shasums/itcm_backup.bin.sha1 new file mode 100644 index 0000000..ac04857 --- /dev/null +++ b/shasums/itcm_backup.bin.sha1 @@ -0,0 +1 @@ +ca71a54c0a22cca5c6ee129faee9f99f3a346ca0 backups/itcm_backup.bin