需要准备好的东西

  • ubuntu20.04 或者一台树莓派
  • st-link (使用树莓派的话不需要)
  • 杜邦线若干
  • Game&Watch 马里奥或塞尔达版机器一台
  • 拆机器需要用到Y型螺丝刀
  • 黑胶布或透明胶带
  • 耐心,细心以及一颗强大的心(中途可能会失败)

接线

拆开机器露出主板。

拔掉电池插线。(注意!不要硬拔!这种接口是用小的平口螺丝刀轻轻翘起来,而不是硬往后方拽,硬拽可能会把线头从塑料接口中扯出来就得上烙铁救命了。。。别问我是怎么知道的)

取电池的方法

4根杜邦线,一头剪断剥出里面的铜线露出一小截插入下图指示的调试接口(左边数第二个孔不使用),另一头接到st-link (使用树莓派的话就接在树莓派的GPIO上)。

这是马里奥版机器的接口。

mairo版

mario版

这是塞尔达版的机器接口。

zelda版

使用树莓派的话接线方法如下

下载备份原机固件用的工具

下载 game-and-watch-backup

cd ~
git clone https://github.com/ghidraninja/game-and-watch-backup.git

先不着急执行,这个工具依赖OpenOCD这个库(各种单片机嵌入式的调试工具),所以要先安装openocd,

sudo apt-get update
sudo apt-get install openocd

安装完以后看一下版本,如果是0.11.0以上的版本就可以了。如果是旧版本(比如0.10.0)因为不支持stlink所以需要手动编译最新版的openocd。目前2022年4月,在ubuntu20.04下的预编译版本是旧版的0.10.0,需要手动编译。而树莓派如果更新到最新系统,预编译版本已经是0.11.0了,直接用就好了(我怀疑是不是因为大家都用树莓派来刷GW所以早早的就更新了这个软件)。

编译并安装OpenOCD (已经是0.11.0或以上版本不需要)

先安装一下OpenOCD源文件依赖的库

sudo apt install gcc make libtool pkg-config autoconf automake texinfo libusb-1.0-0-dev

顺便也安装一下备份原机工具依赖的库

sudo apt-get install binutils-arm-none-eabi python3 libhidapi-hidraw0 libftdi1 libftdi1-2

然后下载官方的OpenOCD到本地并开始编译最新版本。目前最新版本0.11.0。

依次执行下列命令。

cd ~
git clone https://github.com/openocd-org/openocd.git
cd openocd
./bootstrap
./configure --enable-stlink

自动配置走完以后会看到如下输出(ST-Link Programmer yes),说明make对象包含了st-link支持

OpenOCD configuration summary


MPSSE mode of FTDI based devices yes (auto)
ST-Link Programmer yes
TI ICDI JTAG Programmer yes (auto)
Keil ULINK JTAG Programmer yes (auto)
Altera USB-Blaster II Compatible yes (auto)
Bitbang mode of FT232R based devices yes (auto)
Versaloon-Link JTAG Programmer yes (auto)
TI XDS110 Debug Probe yes (auto)
CMSIS-DAP v2 Compliant Debugger yes (auto)
OSBDM (JTAG only) Programmer yes (auto)
eStick/opendous JTAG Programmer yes (auto)
Olimex ARM-JTAG-EW Programmer yes (auto)
Raisonance RLink JTAG Programmer yes (auto)
USBProg JTAG Programmer yes (auto)
Andes JTAG Programmer yes (auto)
CMSIS-DAP Compliant Debugger no
Nu-Link Programmer no
Cypress KitProg Programmer no
Altera USB-Blaster Compatible no
ASIX Presto Adapter no
OpenJTAG Adapter no
Linux GPIO bitbang through libgpiod no
SEGGER J-Link Programmer yes (auto)
Bus Pirate yes (auto)
Use Capstone disassembly framework no

查看目录,可以看到已经生成了Makefile,接着就可以make了

make

漫长的等待结束后,OpenOCD就安装完毕了。末尾输出如下。

最后安装到系统中(拷贝到bin目录等)

sudo make install

安装完以后 sudo ldconfig 一下更新配置,然后查看openocd版本,可以看到已经是0.11.0了,如果仍然显示旧版版本号也别着急,后面会告诉你怎么办,继续往后看。

openocd -v

输出如下

Open On-Chip Debugger 0.11.0+dev-00640-ge83eeb44a (2022-04-15-16:43)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html

开始备份

事前准备 - 设置openocd路径(非必须)

用 openocd -v 命令查看版本号为0.11.0或以上的话跳过本步骤。

当机器上有多个openocd版本存在时,如果默认的那个不是0.11.0以上的话,需要明确告诉破解工具openocd的位置。默认的是哪一个用 openocd -v 命令查看版本号就知道了。

查看各版本的位置用 whereis openocd,比如查到有一个 /usr/local/bin/openocd ,那么可以尝试 /usr/local/bin/openocd -v 看看它是不是0.11.0版本的。

确定了新版的位置后设置环境变量供破解工具使用。

export OPENOCD="/usr/local/bin/openocd"

第1步 - 检查运行环境是否OK

cd ~/game-and-watch-backup/
./1_sanity_check.sh stlink mario

需要传递两个参数,

第一个是指明使用的调试器种类:可选的有 jlink / stlink / rpi

使用树莓派的话本文中的stlink参数一律换成rpi

第二个是指明机型:目前可选的有 mario / zelda (以后老任出了新机型这个工具应该也会更新加入新机型的支持)

如果是塞尔达版的机器本文中的mario参数一律换成zelda

前面的工作我们都准备好了,所以这一步一切正常。输出如下:

Running sanity checks...
Looks good!

第2步 - 备份SPI Flash(外部Flash)

./2_backup_flash.sh stlink mario

会提示下面的信息,给机器插上电源并按下电源开关。

Make sure your Game & Watch is turned on and in the time screen. Press return when ready!

进入时钟画面后按下回车键,让程序继续运行。不出意外的话稍等片刻会提示备份成功。

备份文件保存在 backups文件夹中。

查看文件夹的内容可以看到生成了两个文件

  • flash_backup_mario.bin - 外部Flash芯片里面的固件(并不是直接用编程器能得到的东西,是经过加密的文件,加密算法用到的keyword每一台机器都是不一样的,这个keyword是这个聪明的工具根据你运行着的系统反向计算出来的)。这个文件的size应该是一样的1,048,576字节。如果size一致那么多半是没问题的。如果size不对,你最好重新跑几遍看每次大小是否都一样。如果都一样应该也没问题了。说明我的猜想并不正确,每台机器文件大小可能不一样。所以,请自行多验证几次。
  • itcm_backup_mario.bin - itcm文件本身跟我们没什么关系,这个文件后面的步骤会用到。

这个备份文件是外部Flash芯片里面的固件。

Attempting to dump flash using adapter stlink.
Running OpenOCD... (This can take up to a few minutes.)
Validating ITCM dump...
Extracting checksummed part...
dd if=backups/flash_backup_mario.bin of=backups/flash_backup_checksummed_mario.bin bs=16 skip=0 count=65024
Validating checksum...
Looks good! Successfully backed up the (encrypted) SPI flash to backups/flash_backup_mario.bin!

如果这一步提示出错,

  • 仔细检查你的接线是否正确
  • 检查系统是否正确识别出stlink设备。方法是输入以下命令查看有没有usb设备,如果有那么多半是接线有问题,插线那里接触不太好等。

    ls /dev/stlink*
  • 如果你和我一样使用的是虚拟机跑ubuntu,那么很可能上面的/dev中没有识别出stlink设备。此时要确认以下事项。
    1. VMbox升级到最新版本。
    2. VMbox的扩展包也要升级到最新版本。(扩展包如果和主程序不匹配会出现无法启动指定了USB2.0的系统)
    3. 系统设置中的USB设备选择的是USB2.0而不是USB1.1。

第3步 - 备份STM32内部flash

./3_backup_internal_flash.sh stlink mario

会提示这个步骤会改写你的外部Flash芯片,这次改写将在步骤5恢复,是否继续?(当然是y)

This step will overwrite the contents of the SPI flash chip that we backed up in step 2.
It will be restored in step 5. Continue? (y/N)

接着就开始烧写了,烧写完成后会让你做几件事,照着做就好了。

Generating encrypted flash image from backed up data...
Programming payload to SPI flash...
Flash successfully programmed. Now do the following procedure:

  • Disconnect power from the device
  • Power it again
  • Press the power button on the device
  • The LCD should show a blue screen
  • If it's not blue, you can try pressing the Time button on the device
  • Press return

稍微解释一下上面让你做的事:

  • 拔下电源(是指Game&Watch那个USB3.0口那个,不是stlink或树莓派,别瞎搞)
  • 重新插上电源
  • 按下GW侧面的电源开关
  • 此时应该显示一个蓝屏
  • 如果没有显示蓝屏,尝试按下Time按钮(如果你仍然看不到蓝屏,那么就反复执行第3步知道成功。博主这里折腾了好久才看到蓝屏,最后总结出来,电源一定要稳定,不要直接插电脑USB口)
  • 看到蓝屏后在命令行按下回车键让破解程序继续后面的工作
Dumping internal flash...
Verifying internal flash backup...
Device backed up successfully

到这里,第3步就完成了。此时会在backups文件夹下生成一个 internal_flash_backup_mario.bin 文件。这是STM32芯片内部Flash的dump文件,这个文件尤其重要,由于加过密所以每一台都是不一样的。

第4步 - 解锁STM32芯片

./4_unlock_device.sh stlink mario

这个步骤会提示你当前操作将擦除STM32的内部Flash(原厂的核心程序部分),虽然有备份但依然有风险,是否继续(当然是y)

Unlocking your device will erase its internal flash. Even though your backup
is validated, this still can go wrong. Are you sure? (y/N)

稍微等待一会,没问题的话会提示你解锁成功。

Validating internal flash backup before proceeding...
Unlocking device... (Takes up to 30 seconds.)
Congratulations, your device has been unlocked.
Please power-cycle it for the changes to take full effect.

根据提示此时你可以断电再通电一次,但我看原大神的操作视频这个时间点上并没有这么做所以我并没有此时断电再通电,结果也是没问题的。

第5步 - 恢复外部 和 内部Flash

这个步骤会利用之前的备份文件还原你的系统到原厂的状态,区别仅仅是你的机器是已经解锁的状态,可以自由刷各种自制系统了,包括著名的模拟器合集平台Retro-go(这么辛苦不就是为了装这玩意吗,其实也不是,除了RetroGo还有很多有意思的玩法,自己去找找吧)

这个恢复工具的第5步可以随时反复执行,在刷了其他固件之后想换回原厂状态的时候就用这个命令就好了。所以,这个环境别丢了,以后也用得到。backups文件夹中的备份文件复制(不要剪切,移动)一份到别的地方保存起来。

好,执行命令

./5_restore.sh stlink mario
Ok, restoring original firmware! (We will not lock the device, so you won't have to repeat this procedure!)
Restoring SPI flash...
Restoring internal flash...
Success, your device should be running the original firmware again!
(You should power-cycle the device now)

看到成功的提示以后就可以断开电源并断开与stlink的连接并重新上电,开机。

wooh!恭喜你折腾了一圈回来了,但你已经不是原来的你了,你是自由的啦。

关于如何刷自制系统,另外有空再说了。

我相信你能照着本文解锁你的系统,刷自制系统应该也不是什么问题。

最后,简单说一下破解的原理

首先,老任对stm32做了防盗处理,用的是stm32本身的功能,stm32提供的防盗等级共0/1/2三级,0是不加密,1是允许debug但不允许读内部flash内容,2是所有debug都禁止。老任设置的是1级,也就是允许debug。这也是为啥能用stlink进行破解的原因。stlink正是开发st芯片用的调试器。

虽然可以debug,但无法直接导出stm32芯片内部的flash内容。那怎么弄呢,瞎搞的话会触发芯片的自毁机制,直接清空所有flash。所以得曲线救国。

目前在Debug模式下允许做的事情有:

  • 读 RAM
  • 读写 外部Flash芯片(这个没法防,外部Flash本来就是为了储存用的,你可以加密内容让人看不懂,但每个字节的数据都能明明白白的取出来),
  • 读 ITCM RAM

不可以做的事情有:

  • 读写 STM32内部Flash (这个是最后烧录自制CFW的关键)
  • 写 RAM
  • 写 ITCM RAM

ITCM RAM 是 stm32内部的一个内存设备(非主RAM)。经过各种尝试(也就是通过手动改写外部Flash),大神发现这台机器启动后不单单会从外部Flash读取FC游戏ROM加载到RAM中,并且还会从外部Flash中读取一些Arm命令到ITCM内存区域并执行!聪明的你可能猜到了,没错,虽然(这部分应该就是老任写的的官方FW以及FC模拟器了),但是,既然它会从Flash中读取代码片段并执行这就留下后患了,只要我们将自制bootloader放在Flash中,等着原FW读取到ITCM内存并执行,那么我们就相当于接管了整个CPU,基本上想干啥就干啥了。比如,这个状态下就不受加密限制可以随意导出stm32内部flash的内容了。

下一篇我会详细说明如何在破解的机器上安装自制软件以及模拟器合集RetroGo系统。

系列文章

Last modification:May 9, 2022
If you think my article is useful to you, please feel free to appreciate