Picking apart the Joy-Con: Heap Leaks, Bluetooth and Command Structures
Coming from our last post, we left off with the conclusion that the only real way to figure out Bluetooth would probably be to leak the at-runtime firmware being executed. From there I initially set out to attempt fishing out some data by using SPI writes to modify the firmware patches, however I was disappointed to discover some things: When writing to SPI, the firmware itself actually limits writes from 0x6000 to 0x10000, anywhere else will unfortunately be ignored entirely. My attempts to interface with the SPI were also unsuccessful due to difficulties dealing with 1.8v logic and lack of parts.
Since I couldn’t just dork with code patches on the SPI flash for a while, I had to examine the attack surface of the UART/USB-HID protocol. The big commands which stood out to me were the Amiibo commands which would output a very large amount of data, the SPI read command and basically anything else which actually returned data at request, which were actually pretty scarce. Experimenting with the SPI read didn’t yield much, messing with the command syntax to try reading more than 0x1C of data or trying to read from invalid locations just resulted in a normal read with nothing out of the ordinary… but what would happen if we tried to write more than we could store in the UART packet? Crafting a SPI write packet with a write size of 0xFF to some unwritten data at 0x7000 yielded some extremely interesting results when read back from the flash:
![image](https://64.media.tumblr.com/1ba0023e1a76dbf775bcf9111c2e6ee1/tumblr_inline_oqwb1cRGwF1sscx44_500.png)
For the first 0x1C of our data it has done the expected write from the packet, in this case I sent all 00s. The rest, however, is some data leaked from the heap, with a header of BADDBADD, what looks to be an address pointer and some 02 F0 00 00 value. Following that is something fairly interesting, what vaguely looks to be a fragment of a UART packet with the 92 00 .. .. data. Some heap bits don’t have that data however. The real question however is: Can we leak heap leftover from being connected over Bluetooth?
To check if this were possibly the case, I went into System Settings > amiibo > Register Owner and Nickname to get some amiibo read packets going since those packets were much larger than the usual which should have shaken the heap up a bit and got things to shuffle around. After making several heap leaks I sifted through them via scrollwheel analysis to determine any interesting patterns. Most notably, there are a few interesting things which can be seen:
Following the heap header, at 0x3C, 0x88 and 0xD4 in the GIF there are one of two types of data: The 92 00 31 .. .. packets reminiscent of the usual UART and USB-HID packets, though if it were to be UART it would be missing a header, however it does seem that there are wired packets within that heap data. Otherwise the data is… unknown, but interesting nonetheless. In some of the packets we see an XX YY value where XX tends to be 01, 10, or 11 and the second byte, in some but not all cases, seems to increment. The real significance though is the fairly limited selection of bytes: 01, 10, and 11 are all values which directly follow the initial UART header. Going off the assumption that these packets were indeed Bluetooth packets, I decided to try sending some 01 packets with my Pro Controller and what I ended up getting was… rumble. And, sure enough, I could send a few of my old test packets and I got the same extreme vibrations from my initial wired vibration tests. Taking my UART SPI read packets and cutting them down to the 01 command also worked perfectly. So with this, there is an effective model to send commands over UART, USB HID and Bluetooth HID:
![image](https://64.media.tumblr.com/5565f6c72778c148e66e3ae2729ab9fe/tumblr_inline_oqwbcioR451sscx44_500.png)
The rumble bit was actually interesting because it explains some of the actual portions of packets I didn’t quite understand before, and with that I feel that I can give a proper command structure for Joy-Con packets:
![image](https://64.media.tumblr.com/e3cc14e51957d66fc5ebf974dab6bedf/tumblr_inline_oqwbcwzIxb1sscx44_500.png)
In this structure, the first two portions highlighted in red and green are exclusive to UART-based communications. USB HID utilizes the the green, blue and yellow sections prepended with an 80 byte, and Bluetooth HID is able to take only the blue and yellow sections alone (without any checksums). Having a clearer picture of the command structure between all three methods of communications, and also knowing a bit about the vibration and IMU enable commands which use subcommands 40 and 48 accordingly, I set out to do some fuzzing to find the LED commands. Given the wide range of subcommands already under the 01 command, I figured that the player LED enable command would be a subcommand which takes one byte, with 4 bits to select which LED is lit. Also given that SPI writes were seemingly safe, I began fuzzing and a short amount of poking later I found subcommand 0x30 with no bricks from poking randomly, thankfully. Subcommand 0x30 takes one byte, the higher nibble defines which LEDs to flash and the lower nibble defines which LEDs should be lit, as I had predicted. A few commands higher at 0x38 there was the home button LED command which illuminates the home button in a nice blue color, though the format is currently unknown to me. It seems to also take one byte and it glows in and out the slowest with an argument 0xFF, which is what I used to fuzz. The fuzzing results can be seen below:
![image](https://64.media.tumblr.com/d3bf5618b1baf32ab9ca8eacfbaba29b/tumblr_inline_oqwbodKJHo1sscx44_500.jpg)
With all of that figured out, for the most part everything needed to interface the Switch Joy-Con and Pro Controllers to a PC is known. Bluetooth does, however, still have some interesting quirks. For one, I actually found that the controllers would randomly disconnect until I sent vibration data and proper timestamps along with my packets. This is actually part of how I found out that most if not all packets going to the controllers hold vibration data, which was interesting. On hardware, vibration data only ends up being sent once every 8 input packets (for UART). Additionally, it should be possible to interface with the NFC chip, however my current results have not been successful, but as of now there’s nothing stopping it from happening.
Another interesting note is that Bluetooth actually just constantly sends input packets in most of its outputs, and getting IMU data seems to require some additional commands from the usual. As far as I’ve seen, there are 3 different kinds of input packets which can be gotten over Bluetooth: The basic input packet given to even the OS, the extended status packet which has analog values, battery status, and other things. The final is the same input packet gotten over wired with command 1F, which also includes IMU data. The type of packet sent and the rate seems to depend on subcommand 03.
Last interesting note, but I actually ended up finding a correlation between some of the leaked heap data and some data from hedgeberg’s Switch reverse engineering stream. Some UART lines under the display ribbon contained data which, by chance, also popped up in my leaked heap data shortly after the stream. Specifically, this 08 20 20 03 02 01 section of bytes at 0x44:
![image](https://64.media.tumblr.com/91d4f17d802919a0885ed933b2b995dc/tumblr_inline_oqwbu62bnJ1sscx44_500.png)
![image](https://64.media.tumblr.com/dfd3aa9a3bcee23c8d843d9032b14ac5/tumblr_inline_oqwbuhSCgY1sscx44_250.png)
Those bytes definitely haven’t showed up UART at all, so I’m curious if it may indeed be some kind of common command system or something else. Definitely interesting.
A few takeaways with Bluetooth done:
- For the most part, the only thing that’s really left at all as far as commands go is finding any commands used to update the Joy-Con firmware, in addition to the IR camera commands.
- The Joy-Con firmware, while no longer immediately useful for Bluetooth, is still valuable in determining any future changes or vulnerabilities in the firmware, as such the next post will hopefully detail a method of retrieving the at-runtime firmware from the device. Knowing heap addresses might help with this.
- There is still a lot of work to do as far as amiibo support and better driver support goes.