In my last entry I went into detail on how animal crossing gets to the point of total control from where we can do anything we want, now I will go over the common loader used in a very similar form in all my gamecube save exploits. How this common loader gets into memory of course differs from game to game, in the future I may write up the details for other games besides animal crossing as well.
The goal of this common loader is to load an executable off the memory card the hacked save is on and then, of course, execute it. This loader also has to be relatively tiny since it has to fit into that hacked save file so we are limited on size. Thanks to the twilight hack for wii being open source, my very first gamecube exploit was a port of that one, giving me a great start on how to make this happen.
Because of the size limitation, I cant just compile a standard executable with all the important functions builtin as is normally done on gamecube and wii because that would result in a massive bloated file, instead I have to make use of all the functions of the game that are left in memory when I gain total control. So this means I had to come up with a good structure of standard gamecube software development kit functions to call and then I of course had to locate them inside the games memory.
Luckily, I know my way around a lot of the standard sdk functions thanks to nintendont yet again, because nintendont actually patches a lot of functions to update them from gamecube mode to wii mode.
The title of choice I always look at for reference of the sdk function code is actually the megaman anniversary collection because it actually came with a leftover executable on disc that includes all the function names! Normally on any other game, all those names get wiped out on release so having a title that has all of that open to read is essential to easily finding any of those functions in all other games.
So now let me go over the very basic layout I decided on for the common loader:
-turn off audio first, else you would just hear one "beep" the entire time it loads the file because nothing updates the audio anymore, we have full code control after all
-finish up whatever was being drawn on screen, so the next application can start up a new picture
-mount the memory card
-open our executable of choice
-temporarily load the executable in unprocessed form into a separate memory location
-close our executable of choice
-unmount the memory card
-put our executable parser into memory and jump to it
While that basic layout is followed by each exploit, some of my later ones have some additional functions for either added stability or because they just work differently. I wont go into every single small difference simply because I dont remember all of them but I will go over the ones that I do remember.
This first call I will describe is one of those extra functions.
Often times, games dont just run on one thread, but end up using multiple threads to do different things. So to make sure we dont suddenly crash the system by doing something another thread does not like, we have to turn off anything else still running with the function OSDisableScheduler:
Let us have a quick look at how that machine code looks in megaman anniversary collection:
That is not a whole lot, it basically just adds 1 to some value somewhere in memory and that is it. That is not very helpful in finding this function in other games because it is such basic code, so I actually end up finding this function by looking for this following function first...Warning: Spoilers inside!
This is about the function to turn off audio. Turning off audio is very simple and is done in an identical way in all the exploits I've released so far, we simply call the function __OSStopAudioSystem:
Let me show you a small piece of its machine code from megaman anniversary collection:
You dont need to understand any of this, the important thing is just well, the text. You see, this contains some pretty unique set of instructions which are not called in this exact order anywhere else, so all I now do to find this function in other games is start a search on these 4 exact instructions:Warning: Spoilers inside!
And because all games are based on the same common gamecube software development kit, this search in for example twilight princess does of course return successful:Warning: Spoilers inside!
As you can see, that looks identical to the one in megaman anniversary collection, yay!Warning: Spoilers inside!
This method is what I use to this day to find any of the functions I need in games, though sometimes I have to go through closely related functions because they contain nice patterns to search for. Now to quickly go back onto the previous function for a second, OSDisableScheduler, its actually very closely related to __OSStopAudioSystem because both actually are called up by a function called OSResetSystem, here is a picture of that again from megaman anniversary collection:
So thanks to this function, finding the very small and generic OSDisableScheduler is rather simple, I just have to find the stop audio function first and then I just have to look for what calls the stop audio function, simple enough. I think you get the point on how I find functions, so from this point on I'll just name the ones I use and explain what I use them for.Warning: Spoilers inside!
Next up, we have to finish up the screen data we may have interrupted so the next executable can draw new things. All we do for this is say to the GPU that hey, we are done drawing, always using a very similar method. In the earliest exploits that did not stop threads, a function by the name GXDrawDone was called, it basically sets the needed GPU registers and then lets the other threads execute until the GPU gives the signal everything is finished up:
Of course, this function cannot be used in games that have threads disabled at this point, so for those we instead use GXSetDrawDone which has the slight downside of not waiting until the GPU processed that signal, but I dont think so far that caused any issues:
A special case in this is animal crossing which actually does not have GXSetDrawDone available, so for that one I basically re-implemented its code in C which is not really the nicest way but its at least functional, so I'm OK with that:
The amount of sdk functions available differs from game to game, functions that the game did not use got stripped out the final release so sometimes I have to do workarounds like that.
Before mounting the memory card, we have to do one slight modification to the game id set in memory. Every game has its own game id and that is always located right at the beginning of RAM (0x80000000). Normally for memory card access, this game id determines which files you can open, create, modify and so on. For executables on memory card, we always load a file called "boot.dol" with the fixed game id "DOLX00" so thats what we write into the beginning of RAM:
In the special case of f-zero gx, this game id location was actually deliberately changed from the beginning of RAM to somewhere else so for that we use CARDSetDiskID to set it back to where it should be:
Depending on the game we then have to re-mount the memory card, for this we call CARDMountAsync and then call CARDGetResultCode until that result is no longer -1 which tells us the mount operation finished:
In case of splinter cell I actually found that internally the game already has a function that does exactly that so all I do in that game is call that internal game function:
Finally with the memory card ready, its now time to open the "boot.dol" executable on it, usually done with CARDOpen:
Though in the special case of pokemon colosseum/pokemon xd, there is no CARDOpen, instead that title uses CARDFastOpen which does not take a name as input but instead only an entry number from the list of files on the memory card, meaning for that title we first have to go through all the entries on the memory card using CARDGetStatus and when we found the file we are looking for finally opening it:
A little more dirty but at least it works out.
Now with the executable ready to be read, we simply read it either using CARDRead in titles that still have threads enabled or CARDReadAsync and then calling CARDGetResultCode like we did earlier to mount the memory card in titles that have threads disabled, then putting the read data block into auxiliary ram, and repeating that until the whole file is read into auxiliary ram:
What exactly is auxiliary ram now exactly? On gamecube, you had your main 24MB of fast ram, thats still present on wii too. Additionally, you have also 16MB of slower ram available called auxiliary ram, used for various purposes for games. We first load the executable into this separate ram so we dont accidentally overwrite the code we still use from main ram, we will deal with the parsing of the executable later on.
At this point we are pretty much done, now all we have to do is close the file we just read and unmount the memory card again using CARDClose and CARDUnmount:
This is the last bit of special code right here, specific to f-zero gx that I developed just today, I noticed very early on that for some reason, the f-zero gx exploit was unable to load all executables I threw at it, some would just get stuck, this turned out to be a pretty difficult and long hunt for a tiny bug in the compressor those executables use, dollz. To save space when putting an executable on memory card, you can use compressors like that, and dollz happens to be one of the older ones. My personal compressor, dolxz, worked just fine with f-zero gx and other titles so I had to analyze what dollz did. The annoying thing with dollz is that there is no source code, so I had to go through its machine code and line by line try it out and see what made it crash, this was a pretty slow process but eventually I found the little bit of code that crashed it:
This bit of code seemed fine at first, all it does is save the cache at the location you call it with and the size you call it with so the new data you wrote is properly saved in ram instead of only being in cache.Warning: Spoilers inside!
After really going into detail, I found that instruction marked in yellow to be wrong, basically it compares if the length it just subtracted is greater or equal to 0, wheres instead it should just say if it is greater than 0. Why? Well, it gets called to save the entirety of the fast ram, going from 0x80000000 to 0x817FFFFF, but because it does the compare wrong it ends up with one extra cache save of 0x81800000.
This causes no harm in other titles because the address space thats technically valid normally goes all the way up to 0x8FFFFFFF so even if not that much ram is installed, the processor sees is at valid space, even if it cant do anything with it of course. Now for whatever reason, f-zero gx does NOT follow that standard address space, instead it has one address space going from 0x80000000 to 0x80FFFFFF and a second address space from 0x81000000 to 0x817FFFFF, meaning to the processor that save of 0x81800000 is actually invalid, thus leading it to crash!
Once I figured out that, I also found the game actually has a second option for address spaces in case more valid ram is available, in that case it makes the first address space go from 0x80000000 to 0x81FFFFFF and the second one from 0x82000000 to 0x82FFFFFF. So as a workaround for this crazy crash, I now just call the function that sets up this bigger address space which while technically not being usable ram, at least makes the processor think its valid space, thus fixing this crash:
That sure was a long detour to write down, now we get to finally boot into the special parser for the executable we just got into the slower ram, we copy that parser into memory that is not used by the executables we parse (0x80001800) and jump to it:
We are done with the game functions now and are now in a memory location that is unused by any of the executables we load, so we can now safely parse the executable from slow ram into its final position in the main ram, no need to worry about anything being overwritten!
Parsing standard .dol executables (which is what we loaded with the name boot.dol) is very simple, first we load up the header from slow ram into a variable we can actually use:
And now we just have to go through it which is a very simple process, the header consists of a very simple list of the location of the code it wants to copy, the location it wants to copy that code to and the length of that code, so all we do is go through the entries of that list and copy the code from the slow ram into the desired location in the fast ram:
Followed by pretty much the exact same list but instead of code this is about any other data the executable uses, this could technically be handled differently but we just use the exact same bit of code we used before but this time for the list of data:
To finish off we now just have to jump to the location marked in the .dol header as the starting point for the just loaded executable, finally bringing you to your homebrew application of choice:
Now if you've also read my previous blog entry about animal crossing you hopefully have a full picture of how much is going on in such a simple looking gamecube save exploit, hopefully this was interesting to you, thanks for reading.
You need to be logged in to comment