Hacking consoles: a learning journey (part 4.5)
Hi, and welcome to this first “bonus” entry in my Learning Journey. Last time, we were able to take control of Patapon 2 and run the game exit instruction, but I didn’t explain much of it. Well, if what you want are explanations, you’re in the right place! Today, we will be covering the exploit from the ground up, but instead of detailing how we did it, we’ll see why it worked.
Part 1, the crash:
Let’s start things off at the beginning, shall we? The very first thing we did in order to get this exploit going was to crash the game using a forged save file. But why did that work, and why did we do it? Well, one example I like to use when explaining this is that of a roleplay book.
Technically, what made the game crash is called a “buffer overflow”. This kind of problem happens when a program expects a list of elements to be a certain size, so anything written that goes beyond that size ends up somewhere unwanted.
For this example, let’s use a made-up game: The Call of Patapon.
When you open this roleplay book, you are greeted with a question: What is your name, adventurer?
The answer slot is a certain size, and you can only write so much in it, but what would happen if you wrote too much? Where would it end up?
Well, if you write too much, the book becomes unreadable, and you just can’t go on. But, if you take a closer look, you can see that our uppercase Q (in blue) just overwrote what page the reader should jump to afterwards… I wonder if we could make something out of that? If the reader is stupid enough to go to whatever page we write over “2”, then maybe we can do something out of that?
Part 2, manipulating the console:
After crashing the game and realising we actually have control over where the games goes next, we had to do something out of that. Sadly, since we can’t just modify the game’s code like that, we need to find somewhere to write our instructions. There must be a way in, right?
Well, you see, whenever a game saves data, it writes it in an external file. The thing is, the game needs to load that file again in order to read everything that’s in it. The great thing in all of that is that the save file is something we have control over, to some extent.
Granted we can decrypt the file (we used Savegame Deemer to do that) and then re-encrypt it, we can make the game load anything in its memory. Normally, it wouldn’t be an issue, since the game never goes there to read instructions, but there’s a difference: we’re now telling the game what part of its memory to execute next.
Using PSPLink in the last post, we could get a reading of the game’s memory as it was at the time of the crash. Searching through it, we found that our savegame was loaded as-is somewhere in it, which meant that whatever we could write in it would be loaded too. In order to know where to point the game, we needed to know the exact address where what we wrote would end up in the memory.
So, to sum up: since the save file goes in the memory, we need to write something in it, find its exact address, and then put that address in the right place of the initial buffer overflow.
In more technical terms, we are manipulating the $ra (Return Address) variable of the processor in order to get it to execute unsigned code, which it normally wouldn’t do. That $ra variable would be the page number, in our roleplay book example. In itself, the $ra variable is what tells the program where to read stuff when it’s done with whatever it’s doing in the moment. If we control it, we can point the execution to anywhere instead of the intended return point.
Now, you may be asking yourself: what do we write now? What can we put in the savefile that will be understood and executed by the processor?
You see, when you make a program, you have to write the code for it, and then compile it. Compiling a program is done automatically, and one of the things it does is to import functions.
In a program, functions are bits of code that will be used again, sometimes very rarely, but sometimes almost ubiquitously. One function, for example, will output whatever text you give to it (this function, in C for example, is called cout).
But writing this time and time again every time you create a program would be endless. And so would be writing it once per program and calling it every time you use it. In fact, when you write a program, you will want to import functions from various libraries (collections of useful function that you can call without having to write again).
If you want a real world counterpart, take a car. You can make a car by yourself, create four wheels, an engine, blinkers, electronics and all of this, but the easier solution would be to order the tires and engine from someone, maybe take some spare lightbulbs for the blinkers and do the electronic yourself. Well, you can think of the tire and engine shops as libraries, from which you call the tire and engine functions to include in your program. This is the concept behind function imports.
Now, the reason why I am talking about compilers and function imports is because that’s essential for us: since we can’t really do what we want because we’re still trapped inside Patapon 2, we can only use the function that this game imported when it was compiled. The PSP has much more to offer, but we’re limited to those ones. The way to know which functions are imported and where they are located is a little tool called prxtool. If we use it as-is, however, we won’t know the functions’ names exactly, but only what libraries they belong to; that’s where the xml file comes into play, since it’s the translator we need to identify specific functions.
Part 3, writing the code to run:
And now, we dive (a little) into Assembly. Last time, I ended the post by saying that I could call the function to close the game (which is not the same as a crash, since I exited properly), and I didn’t explain it then. Well, now’s the occasion!
It might be underwhelming to some, and very expected for people who know Assembly, but we are not going to write 1’s and 0’s ourselves. Assembly is more or less the lowest you can go when programming since it’s closest to the machine, but it still has a syntax, and is still very readable to us.
Assembly has got a relatively small list of possible instructions, since it’s so low-level, but we won’t need much there. Since we already have the address for a lot of functions that Patapon 2 use, we can try calling one as a proof-of-concept. The clearest one to see, and the one I chose, is called sceKernelExitGame, and is located at address 0x08A884D4 in the game’s memory. Hence, if we can tell the game to jump there, this function will be called.
Fortunately, we won’t have to do any weird hexadecimal save file editing to get the program to jump to the address, since we already have control over what is executed. We just have to use the “jal 0x08A884D4” instruction to make the game exit.
What jal does is that it tells the machine to go execute whatever is there, and when it’s done, come back to the instruction right after that jal one. Funnily enough, we’re using exactly what we exploited earlier: here, the $ra variable is where the instruction right after jal is stored.
So, when we say “jal 0x08A884D4”, what we really say is “go to that place, read some code, and come back here afterwards”. It’s just that the code itself is the game exit routine.
(I’ve been told to put “.set noat” and “.set noreorder” before my code, but I don’t know what it does. Feel free to enlighten me.)
Part 4, compiling the code, and planting it:
Great, now we have a bit of code to play with. The only issue, though, is that we need to put it in the save file. We could put it like this, but the machine wouldn’t be able to read it as code, and we’d just end up with the same crash we had before.
If we want the machine to be able to read our code, we need to compile it. Compiling is, in short, the act of translating code into machine language, in binary. The commands for that are very simple, and use tools present in the Minimalist PSP SDK:
psp-objcopy -O binary a.out a.bin
Now, we’re left with a little file named “a.binary”. This is our machine-ready code, and if we just open it in our favourite hex editor alongside with the decrypted savefile, we can work some magic.
We need to put the contents of a.bin right in place of the pattern we chose in the save file. Be careful to overwrite what’s there, and not to insert anything before it, because we don’t want the file’s size to change, or the contents to be displaced. Whenever this is all done, what’s left is to start up Patapon 2, get our name to show up on screen as usual, and savour the fact that instead of a frozen screen, the game actually nicely closes.
Well, that was hard to explain. I’m glad I got everything to work out, and if you have any question, feel free to shoot a message towards @theoct0 as always. I’d love to receive any and every criticism you might have, any tips and tricks, all in all anything you’d like to shoot my way. But for now, until the next post, farewell!