Gen III Remote Code Execution
Posted by: Stackout
Date: 2017-02-18 19:58:35
(of course, you could also use the GBA BIOS multiboot, but here you don't have to press any buttons on the GBA BIOS to get multiboot going! And because this is in the Gen III ROM, this can legitimately be called Gen III RCE :P)
What is this?
Have you ever connected up Pokémon Colosseum/XD to your Gen III game? Remember how it told you to turn the GBA on with the GBA->GC link cable plugged in, then it took an awfully long time on the copyright screen and then showed some Colosseum/XD thing?
The awfully long time on the copyright screen was Colosseum/XD transferring a multiboot image (basically a GBA ROM image with modifications, namely it runs from RAM lol) to your GBA. The Colosseum/XD thing on the GBA was basically your GBA executing that multiboot image.
After two days of reversing (this multiboot protocol is pretty different to the one used by the BIOS, but it does have a couple of similarities), I got a respectable PoC working. So, where else to release this to the world?
Here's an image of, basically, the very PoC I'm releasing today.
[img]http://i.imgur.com/Nb4hjPel.jpg[/img]
Where's the code?
Right here: https://github.com/Wack0/gba-gen3multiboot
If you want to modify the GBA-side payload, you can naturally find it in the [tt]gba[/tt] directory. Note that the payload is in the C language. A refreshing change of pace from position-independent ASM shellcode, I'm sure you'll agree.
To compile this, you'll need devkitPro (devkitARM for the GBA side and devkitPPC for the GC/Wii side). Remember, this is very much a proof of concept which I'm sure will be improved on by others.
How do I run it after compiling?
I assume you have a Wii (I'm not even going to mention GameCube, I think all the good ways to get homebrew running there need a Wii anyway lol), with the Homebrew Channel installed and running. Just execute the compiled [tt]gen3multiboot_wii.dol[/tt] there. When testing, I always used [tt]wiiload[/tt], it being quicker than copying a new binary to the SD card every time.
Make sure you have a GameCube controller plugged into controller port 1 and your GC->GBA link cable plugged into port 2.
Hey, it didn't work. What gives?
Code execution on the GBA side is not 100% reliable for some reason. A couple of different things could go wrong. If you see an error about KeyC derivation, you should just be able to turn your GBA off, press any button on your GameCube controller plugged into port 1, then turn your GBA back on again.
If, however, your GBA didn't seem to notice the code being transferred to it, and your Wii has frozen on a line with a CRC, you're going to need to power off both GBA and Wii, turn your Wii back on, get back into the Homebrew Channel and run [tt]gen3multiboot_wii.dol[/tt] again.
It may take a few tries but it will eventually work.
What is this "different" protocol?
If you want technical details, best see the source code.
At a higher level, it goes like this:
[li]GBA game starts, game calls the multiboot main() function[/li]
[li]GC sends a reset command, GBA sends a game code[/li]
[li]GC receives game code, maybe checks to see if it's supported, and sends it back to GBA[/li]
[li]GBA checks this, if it's wrong then GBA stops listening on the JoyBus link cable[/li]
[li]GC generates and sends the first of three 32-bit encryption keys. This is officially (yay for debug messages!) called "KeyA" (imaginative). GBA checks one of the bytes to make sure it's valid, then generates and sends its own encryption key, "KeyB".[/li]
[li]KeyA and KeyB are XORed together to form the initial value of the checksum (algorithm used is the same as the GBA BIOS multiboot). This value is also obfuscated to form a session key that will be used to encrypt most of the multiboot image when it is sent (again, algorithm used is the same as GBA BIOS)[/li]
[li]GC sends the size of the multiboot image it's about to send, in uint32s, to GBA. GC then sends the ROM header (up to 0xA0, the end of the Nintendo logo) to GBA in the clear. When this is done, GBA checks the received Nintendo logo to make sure it's the same as the Nintendo logo in the ROM of the cartridge it's executing. If not, GBA stops listening on the JoyBus link cable.[/li]
[li]GC sends the rest of the multiboot image to the GBA. Each uint32 is encrypted using the session key, which is also incremented in the same way as the GBA BIOS multiboot. Each plaintext uint32 is also checksummed, using the existing checksum as the initial value, again the algorithm used here is the same as the GBA BIOS multiboot.[/li]
[li]GBA, as it's receiving the image, decrypts it, and checksums the plaintext in the same way. After this is done, the GBA will send GC a value that can be used with the correct multiboot image checksum to derive KeyC, which was created by GBA using some historical value of its VCount register.[/li]
[li]GC receives this value, and attempts to derive KeyC. It will use the derived KeyC to create a "boot key" which is sent to GBA; if this boot key is the same as the one the GBA already created, it will call the entry point of the sent multiboot image. If not, GBA will stop listening on the JoyBus link cable.[/li]
This took me quite some time of reversing work to figure out (there was lots of info about the GBA BIOS multiboot protocol on the internet; only ARM ASM code about this one, from the Pokémon reverse engineering project). I hope it helps someone.