Today I felt like going over one of the many save exploits for the gamecube I pushed out over time, these exploits load a custom homebrew executable from memory card so you can use gamecube homebrew without any modchips or any other special device, just an exploitable game and a save for it on memory card, transferred using another hacked gamecube or a hacked wii.
Specifically I felt like going over animal crossing, the most recent exploit I released right here:
For this one I dont have to go over the initial step of finding a way to execute code, I can just link you to this detailed writeup:
Indeed all the credits for finding this way to execute code go to james chambers, if you read the link above then this will continue right from where the link above ends.
So while james chambers initially used my_malloc for his address overwrite to gain code execution when loading up a custom NES game with the NES item in game, I quickly realized that this would not work out with all releases of animal crossing. As he described, you can only modify memory regions 0x80000000 to 0x807FFFFF with the PAT tag, in the US release of the game the my_malloc pointer is at 0x806D4B9C which is within that region but in for example the last japanese release that location is at 0x8115BC74, far out of reach of the PAT tag.
Luckily, I have years of gamecube experience thanks to nintendont, so I immediately had an alternative in mind that would easily be in reach of all versions and gets executed pretty much every frame by PADRead. Now what exactly is PADRead? Well, it reads out the gamecube controller plugged in, updating whatever buttons you press or analog sticks you move. The actual controller update type though can theoretically vary depending on the gamecube revision, early development models and such actually had a different setup for updating the controls and even though retail units I think always use the same function, the gamecube software development kit always has this function on a pointer for what I can only assume maximum backwards compatibility, just in case such an alternative type is needed. This pointer gets set by PADSetSpec right when the game first boots up and then never touched again.
Thanks to the gamecube software development kit being statically linked into memory, the address used for that set by PADSetSpec never changes, making it the perfect target:
See PATCH_PTR from all these different releases of the game? Thats the address I overwrite. As you can see, those are all easily within the limit of the PAT tag. So great, now we have a pointer we can write any other memory address into that then gets executed.
What do we even want to write into there?
In past exploits, this was a very simple case of executing a custom loader I wrote that just loads an executable off the memory card, but we are talking about animal crossing here. If you exit the game without first saving, then on next boot resetti will show up, which would make this exploit pretty annoying. It takes long enough to boot the game, load your town, enter your house and click on the NES already so I dont want this to take even longer. So this time, I needed some more setup than usual.
There can be more than one PAT tag in the custom NES game so following the example of james chambers, I used that to copy some small bit of code into an address not used by the game anymore, 0x80003970. Now we have 2 PAT tags. The first writing the value "0x80003970" into the pointer executed by PADRead, and the second one writing some custom code into the actual memory region 0x80003970, so now as soon as PADRead gets executed, it will jump to 0x80003970 which with the help of the second PAT tag contains some custom code. That bit of custom code is right here:
Let me go over what that code actually does.
The first bit of code to start us out with is pretty boring:
All this bit does is secure the address of PADRead and the arguments that PADRead used to call this now replaced controller update function into some other registers we dont use, so we can restore them later on when all the magic is done. Next up, we load the NES ROM address that the NES emulator uses, so now onto what this NES ROM even is.
I really did not like that in the initial demonstration from james chambers that there was no NES ROM used to display at least some message on screen when you chose to load your custom game with the NES item, so having written my own nes emulator and several nes patches, I of course decided to make up my own NES ROM:
This one is just based on a template I grabbed from another github repo that I linked at the link above, all it does is display a message on screen of the exploit either succeeding or failing:
How do I know if the exploit actually worked? Well, in the NES ROM I just set up a variable that is 0, and then when the NES ROM executes, it compares that variable with 0, if it still is 0 then I print out it failed, but if it is not 0, then I print out it succeeded. Since the PADRead function executes before that NES ROM code, my code at 0x80003970 first sets that ROM variable to 1:Warning: Spoilers inside!
The purpose of this NES ROM however is more than to just show of that hey, I can do that, it also actually contains the loader code I was talking about earlier, the one to load up an executable from memory card. So the code at 0x80003970 next up copies that bit of code from the NES ROM over into some more unused game memory at 0x80004000:
To recap what all happened so far, we have a custom NES ROM and some PAT tags that when selecting the custom NES game will display a message on screen and copy a custom loader thats capable of loading executables from memory card to 0x80004000. Next up, how are we going to execute this custom loader?
Again thanks to my knowledge from nintendont, I had the idea of just seeing which function called CARDWriteAsync. CARDWriteAsync gets executed whenever the game wants to write a sector onto the memory card, which of course is done during saving. So I just set a breakpoint of this in dolphin and wrote down the function address of where that happens, and from that address I essentially just set a new breakpoint at the start of that function, then again checked from which function address that was called and repeated that until I found the earliest function that is involved in saving. This led me to a small function pointer tree that handles mounting and opening the memory card, starting to save, updating the save date and closing and unmounting the memory card after saving is done. So all I had to do from here is just replace the function pointer in the tree that gets executed right after the card got unmounted with a jump to our custom loader at 0x80004000:
I did actually release 2 versions of this exploit, and this is where the earlier version cleaned everything up and you had to exit the NES game, leave the house, select that you want to save, wait for it to save and then the loader would load the executable. That was rather slow and I wanted to improve that, this next part is of what I had to go through to make it better.
This part probably took me the longest out of all this, trying to find a smoother way of getting to the actual executable. My memory on this is quite fuzzy because it really just was a constant loop of me setting a breakpoint in dolphin and seeing what happens, basically what I was searching for was some state variable that told the game what to do when exiting the NES game, and changing that to a state that normally gets set when you choose to save. Well, at this point I already knew what gets executed when it starts to save. So from that function pointer tree it was just a matter of setting more breakpoints to get earlier into game execution. In the end, I found the function that gets executed right when the screen fades in where you then get asked if you want to save. After finding that function, I've read through the code that actually set that function pointer and after a long search I found, in the US release located at 81266414, a variable I just called gamestate. This value decides what gets executed when game scene changes (a scene being the title screen, the save screen, game overworld etc). So here are a few of those states in dolphin:
So normally when exiting the NES, it is set to 14 (house) as you can see from the images above, after finding this magic variable I set a breakpoint for whatever sets that memory address on NES exit and found that when starting the NES game it stored that state in a separate address first (8128E97C in the US version) and then on NES exit just set the gamestate to that previously stored value. So, all I have to do now in my custom loader is modify that address and set it to 22 (game save) instead:Warning: Spoilers inside!
So now when exiting the NES game we automatically jump to the save screen instead of the house, this is a pretty good start!
Since I now knew which functions get executed when saving and also knew which ones first get executed when you enter the save screen, I just replaced that initial function on fade in directly with the function that does the saving process, so you dont even have to go through the dialog and again say that you want to save, as soon as you enter the save screen it just saves immediately instead:
With all those changes in place, I now had a very optimized way of making this exploit work, lastly I suppose we should just quickly go over the cleanup that remains. Of course, we still need the game to execute like normal so we have to restore the function PADRead uses to what it is supposed to be, this of course can just be done by calling PADSetSpec again which was used to initially set this pointer:
Now that the pointer is restored, all thats left is to call that function with the arguments PADRead initially gave us and the game is back in normal operation:
So, we now reached the end of this small custom code and even though it may be pretty short it sure did a ton of stuff.
Now you know what all happens when you choose the custom NES game in your house except for the custom loader that gets executed after saving is done. In this blog entry I wont go over the details of that custom loader because it is shared pretty much unchanged between all the save exploits I pushed out, maybe that one will be a separate entry. When I released that exploit I did also make a quick video showing it in action so you can see it for yourself:
There you have it, now you know just how much stuff is going on for something that looks so simple when you just execute it without looking closer at it. This isnt even going into the really tiny details either of how this is all put together into a usable save in the first place but that would probably just be too much to write down, I would be surprised if anyone actually made it all the way to the bottom of this blog entry anyways and understood exactly what is going on so theres no need to push any further I think.
If you DID make it down to here and actually understood some of it then I hope you learned something new, thanks for reading.
You need to be logged in to comment