Putting a piglet picture on my Alarmo


piglet on alarmo

Problem statement

When I heard about Gary’s Nintendo Alarmo hack, I had to have one1, too.

99.90 CHF and a week of waiting later, I could get to work.

This is (hopefully) a relatively thorough description of the journey from start to a piglet picture2.

Road to piglet

The process is relatively simple, consisting of only 3 steps:

  1. Getting the keys to the kingdom
  2. Injecting payload over USB
  3. Profit!

Let’s a go.

Getting the keys to the kingdom

In order to be able to run an arbitrary payload, it needs to be encrypted by a 128 bit key3.

So the first step is getting the key.

Gary in his article explains how:

First, you remove the screw close to the USB-C port4, and gently twist the screen bezel to get in.

Then, attach SWD debugger (in my case ST-Link v25) to the right pins6:

pcbite in alarmo

The right pins can be found all over twitter. I liked the picture from @timschuerewegen because it shows which testpoints you can use (backup) – and the testpoints are much easier to solder to, if you need that.

With the testpoints connected to SWCLK, SWDIO, and GND (and power still flowing via the USB-C), it’s time to openocd the hell out of this.

Here’s roughly what I’ve done:

$ git clone https://github.com/GaryOderNichts/alarmo
$ cd alarmo/key_bruteforcer/
$ make
[...]
$ openocd -f interface/stlink.cfg -f target/stm32h7x.cfg
Open On-Chip Debugger 0.12.0
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". \
	To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. \
	The results might differ compared to plain JTAG/SWD
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 1800 kHz
Info : STLINK V2J29S7 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.269241
Info : [stm32h7x.cpu0] Cortex-M7 r1p2 processor detected
Info : [stm32h7x.cpu0] target has 8 breakpoints, 4 watchpoints
Info : starting gdb server for stm32h7x.cpu0 on 3333
Info : Listening on port 3333 for gdb connections
[...]

With that underway, in a second shell (from the same directory):

$ telnet 0 4444
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
Open On-Chip Debugger
> halt
[stm32h7x.cpu0] halted due to debug-request, current mode: Thread 
xPSR: 0x21000000 pc: 0x7004e95e psp: 0x200117f0
> load_image payload.elf
228 bytes written at address 0x20000000
downloaded 228 bytes in 0.002641s (84.308 KiB/s)

> resume 0x20000000
[stm32h7x.cpu0] halted due to breakpoint, current mode: Thread 
xPSR: 0x21000000 pc: 0x200000ca psp: 0x200117a8
> dump_image aes_data.bin 0x24000000 0x50
dumped 80 bytes in 0.001181s (66.152 KiB/s)

> exit
Connection closed by foreign host.

End result of which is the aes_data.bin file you can use to crack the key:

$ ./bruteforcer aes_data.bin 
Bruteforcing key part 1 / 4... **.3% (0x****0000 / 0xffffffff)
Found key part 1 / 4: ********
Bruteforcing key part 2 / 4... **.0% (0x****0000 / 0xffffffff)
Found key part 2 / 4: ********
Bruteforcing key part 3 / 4... **.0% (0x****0000 / 0xffffffff)
Found key part 3 / 4: ********
Bruteforcing key part 4 / 4... __.7% (0x****0000 / 0xffffffff)
Found key part 4 / 4: ********
Found key! Took 398 seconds.
Key:    ********************************
IV:     ************************00000000

(obviously the real values are censored, and I ain’t sharing7)

Injecting payload over USB

Again, thanks to Gary & co, running an arbitrary payload is super easy, barely an inconvenience.

From the same repo, you can get your very own cat picture by simply doing:

$ cd ../
$ pwd
/home/wejn/[...]/alarmo
$ git submodule update --init --recursive
[...]
$ cd usb_payload/

Now, you need the Key and IV from the bruteforcer output above, in order to create the right key.py:

cat - >key.py  <<'EOF'
AES_KEY = bytes.fromhex('********************************')
AES_IV = bytes.fromhex('************************')
EOF

(Note how the IV is shorter by 4 bytes than compared to the key)

Then, theoretically, all you need is make. For me, the problem was:

$ make
[...]
encrypting ... a.bin
Traceback (most recent call last):
  File "/home/wejn/[...]/alarmo/usb_payload/ciphtool.py", line 6, in <module>
    from Crypto.Cipher import AES
ModuleNotFoundError: No module named 'Crypto'
[...]

A while later, I figured out that for some reason, on my system (Devuan) one needs a small tweak to get to the finish line:

$ sed -i 's,Crypto.Cipher,Cryptodome.Cipher,' ciphtool.py 
$ make
encrypting ... a.bin

With that, the three finger salute8, I could get a cat on the display.

But… I want a piglet!

Getting a custom payload made

Don’t let the presence of cat.png fool ya. The princess is in another castle. It’s actually in source/cat.inc:

$ head -n 3 source/cat.inc |sed -r 's/(.{1,60}).*/\1/'
static const uint8_t image_data[] = {
0x3b, 0x39, 0x36, 0x2d, 0x2a, 0x26, 0x2c, 0x29, 0x25, 0x2a, 
0x3b, 0x38, 0x36, 0x2f, 0x2c, 0x28, 0x2f, 0x2b, 0x27, 0x2c, 

Now, if you dig into the format, it’s actually raw BGR pixels of an image rotated by -90°.

So nothing we can’t fix. Given 320x240 input image, the following img2inc.rb script will get you where you wanna go:

#!/usr/bin/env ruby

img_raw = `convert cat.png -rotate -90 rgb:-`
img = []
img_raw.bytes.each_slice(3) { |e| img << e.reverse }
img.flatten!

puts "static const uint8_t image_data[] = {"
img.each_slice(80/6) { |e|
  puts(e.map { |x| "0x%02x," % x.ord }.join(" "))
}
puts "};"

So given my own cat.png:

not really a cat a beloved pic of mine from Gabor Vereb

one shell invocation:

$ ruby img2inc.rb > source/cat.inc 
$ make
main.c
linking ... usb_payload.elf
creating ... usb_payload.bin
encrypting ... a.bin

the three finger salute together with:

$ mount /media/sda1
$ cp a.bin /media/sda1
$ sync
$ cp MarkFile /media/sda1
$ umount /media/sda1

and a bit of waiting… gets you to the beauty showcased at the top of the post:

piglet on alarmo

Closing words

You might be wondering why I’m simply walking through someone’s hard work without adding much of my own9.

To be honest, it’s not every day that I get to pull out ST-Link and run openocd on a target.

And Gary’s article glossed over a bunch of cute little details that might not be immediately obvious (they weren’t to me).

So, next time I’m passing through these woods, I’d like to have a map. This map.

Now, if you’ll excuse me, I have a piglet picture to admire. And some C code to read.

  1. No, not just an Alarmo clock. A hacked Alarmo clock. Duh.

  2. Which is obviously just the start of all the more interesting things one can do with it. Doom, etc.

  3. Akhschually, it was also supposed to be authenticated via an RSA signature on the binary, too, but Nintendo somehow kept that door unlocked. Thanks, Ninjas!

  4. Gary says phillips, mine was tri-wing #0. Oh well.

  5. All over AliExpress, for $2 or something. You can also use pico-probe, flipper, or any other fancy SWD debugger.

  6. Having a pcbite set is of great help. But soldering would work equally well.

  7. And if you can’t be bothered to do these few basic steps, maybe you shouldn’t be trying to run a custom payload on a hardware you own, yes?

  8. Press all three buttons on the Alarmo, and then plug the power in.

  9. I mean, the Ruby script might be worth something to someone, but it’s hardly revolutionary.