380 Followers
30 Following
43 Posts
I wrote a blog post about the security of the STM32H730 microcontroller used in the Nintendo Alarmo. There's a vulnerability that allows dumping the protected secure bootloader of the STM32H730.
You can read more about it here: https://garyodernichts.blogspot.com/2025/11/privileged-arbitrary-code-execution-on.html
Privileged Arbitrary Code Execution on STM32H73XXX microcontrollers

This is somewhat of a follow-up to the Nintendo Alarmo blog post from last year. This time the blog post is about the security of the STM32H...

I spoke too soon heh...
Nintendo released a new Alarmo update a few hours ago. The new update contains a new 2ndloader where the signature is properly checked in USB mode. If you want to modify your Alarmo without soldering, stay on v2.0.0!

https://social.treehouse.systems/users/garyodernichts/statuses/114126385908663501
Gary (@[email protected])

Nintendo is still shipping Alarmos without signature checks in the 2ndloader. I assumed they might do something for the wide retail release, but it looks like they don't really care (for now).

Treehouse Mastodon
Nintendo is still shipping Alarmos without signature checks in the 2ndloader. I assumed they might do something for the wide retail release, but it looks like they don't really care (for now).
Wii U fun facts:
While reverse engineering the Wii U's USB Host Stack (UHS) I noticed several fun quirks in their descriptor code.
One of them was even exploitable (UDPIH), but there are some other funny quirks that I have never mentioned before. This post contains some of the minor ones I still remember.
**1.**
A USB device can have multiple configurations, which are retrieved using the GET_DESCRIPTOR request.
According to the USB specification, the index of the descriptor to be retrieved is stored in the lower byte of the wValue field.
UHS keeps this field as 0 and uses the wIndex field instead to retrieve multiple configurations.
wIndex contains the Language ID for string descriptors, and is supposed to be zero for configuration descriptors.
This causes UHS to retrieve the first configuration multiple times if the device has multiple configurations.
In practice this is not a big issue though, most devices only have a single configuration and UHS will only use the first one anyway.
**2.**
When reading multiple configurations, UHS doesn't change the size of the next configuration to be read.
So the initial read of the next configuration ends up being the full size of the previous configuration.
In practice this also isn't an issue, the device will simply respond with a short packet.
**3.**
If you set the bLength field of an interface or endpoint descriptor to zero, the parser will get stuck in an endless loop, since it will increment the loop offset with this value.
This causes the entire console to freeze when encountering a malformed configuration. Also not a big issue, but this was one of the things which lead me to discovering UDPIH :P
After my last post, it was pretty clear what everyone wanted to see on the Alarmo. So, here it is - Doom running on the Nintendo Alarmo!
There's currently no audio support. To avoid the USB loader memory size restrictions, the .wad needs to be compressed and then uncompressed to external memory on boot.
However, it's possible to load the shareware version of Doom entirely from USB, without modifying the Alarmo.
The source code and instructions can be found here: https://github.com/GaryOderNichts/alarmo_doom
Big shoutout to STM32Doom and Chocolate Doom for making this possible!
GitHub - GaryOderNichts/alarmo_doom: Doom for the Nintendo Alarmo

Doom for the Nintendo Alarmo. Contribute to GaryOderNichts/alarmo_doom development by creating an account on GitHub.

GitHub
It's possible to run custom code on the Nintendo Alarmo via USB - without opening it up!
More details in the blog post here: https://garyodernichts.blogspot.com/2024/10/looking-into-nintendo-alarmo.html
Looking into the Nintendo Alarmo

While everyone was waiting on news for the successor of the Nintendo Switch, Nintendo released the Alarmo. A small plastic alarm clock that ...

All content files on the Alarmo eMMC are stored as 'CIPH'-files. These files are AES-128-CTR encrypted and have a RSA-2048 signature (PKCS#1 v1.5 with SHA256) at the end.
Here's a simplified overview of what I figured out about the Alarmo boot process so far.

tl;dr long post with lots of tech details, skip to bottom for libctru/3ds-examples/Luma3DS commits

So, for the past two weeks, I have been reverse engineering and then documenting the New 3DS's QTM module, responsible for "Super-Stable 3D" and eye tracking, mainly its API and how it interacts with hardware, in as much detail as possible.

  • eye detection is done by running the inner camera at 30FPS then using gyro data in-between camera samples to circumvent the low camera FPS limitation ; also, eye tracking data reporting API is hardware-agnostic (I think some of it was made with VR headsets in mind, but that's just mere supposition on my part)

  • "Super-Stable 3D" is just some trigonometry on top of eye tracking, with some assumption based on calibration data (and assuming IOD (interocular distance)=62mm like the rest of the system). Essentially, it computes the horizontal deviation of the midpoints of the positions of the user's eyes from the camera and adjusts the parallax barrier's mask from its center position (from calibration) by as many (iod / 12) = 5.2mm units as needed (assuming optimal viewing distance)

  • on the hardware side, N3DS SoC and screens work differently from O3DS (this is why downgrading N3DS below 8.x results in 3D not working properly). Using a TI TCA6416A expander (clearly visible in iFixit's teardown article) via I2C, QTM repeatedly writes the parallax barrier's mask pattern (12 bits) and polarity (1 bit), and the negation of both at the following iteration, which are then sent to the LCD controller via a dedicated ribbon cable (don't we all love ribbon cables?). Interestingly, the "O3DS-like" pattern used when SS3D is disabled has a lower slit width (5 iod/12 instead of 6 iod/12)

Following this reverse-engineering work, I've added support for all service commands of all QTM services in libctru, for which v2.4.0 has been released. You can find all the nitty-gritty details of my work by checking the documentation comments inside qtm.h and qtmc.h: https://github.com/devkitPro/libctru/commit/8e55cdf05d1f2c07f350ec678d0f0d6a7a2df214

Big thanks to mtheall for his help on understanding the eye tracking data structure in QMTU_GetTrackingData.

As for the examples, 3ds-examples have been updated (though proper off-axis stereoscopy example with dedicated c3d helper functions is yet to be done): https://github.com/devkitPro/3ds-examples/commit/44faa81d79d5781c0e149e4a7005f2e005edb736

As well as Luma3DS itself for SS3D toggle, barrier position testing and calibration: https://github.com/LumaTeam/Luma3DS/commit/1c737d499f61c67a99f50382ab85f42e2049d53a and https://github.com/LumaTeam/Luma3DS/commit/2aa2013318ff541b94493dd9c73142105324edee

Add full support for all QTM services. · devkitPro/libctru@8e55cdf

This commit adds support for all service commands of all 4 QTM services (`qtm:u`, `qtm:s`, `qtm:sp` (which are built on top of one another) and `qtm:c` ("hardware check")), with precise d...

GitHub