I'm not trying to go that much into detail but explaining things as noob-friendly as possible. In the end the goal of this guide should be that you have a simple Proof of Concept (PoC) of custom code execution working!
Prologue
At first you might think; Whats the benfit of not using an actual PSP for this?
Well firstly the available PSP Emulators like PPSSPP and JPCSP got pretty good with emulating and are quite fast nowadays. Additionally they are having everything we need for exploit-hunting implemented and thats why its way much easier and faster compared to doing all steps on an actual PSP. For example the emulators can store all savegames unencrypted as plain files where on the PSP they are always encrypted and need to be converted twice each time you change something. Additionally they have implemented debuggers and memory viewers where on PSP we needed to use additional programs.
However don't get this guide wrong: To be a hundred percent sure that your found crash really works on a PSP, you need to test it! (But from my experience thats the case for maybe 99% of the crashes.)
In the end the emulators are still emulators and may handle some instructions differently!
What are we looking for? / trying to do?
The short version is -> find a crash -> see if we can exploit it -> run a simple PoC
The most common exploits are buffer overflow exploits and what we are going to concentrate on today.
An example: Its quite common in games that you have to select and enter a username at some point, right? Depending on the game you have a limited number of characters and it wont let you enter any additional ones once reached. This text string is then saved in your savegame and will be loaded to the PSPs memory the next time you load the savedata. If we now manually extend the length of our name by messing with it via a HexEditor (and our game doesn't have any security checks for that implemeneted), it is loading more data in memory than allocated for it. If that happens we already have a buffer overflow and the additional data might cause some other things to break and load our data to other places in memory where they shouldn't exist.
The easiest and best case would be that a return address of a function gets overwritten, but more about that later. And of course there are even more exploit-types but I think thats going too far for now..
Which games should I concentrate on and why?
Full PSP Games, Demos? Minis?
A PSP game when developed makes use of different pre-existing and necessary system functions provided by Sony itself. They are normally starting with the prefix 'sce' eg 'sceIoRead' and are compiled into the game to make it work. Depending on the complexity of the game there are more of these system functions imported. For our goal of getting a simple PoC running it doesn't really matter but once you are looking to port VHBL for example, the general criterion is: The more functions the better.
A simple example: If your game does have a multiplayer mode, we can be quite sure that the needed System functions for utilizing Wifi and Ad-hoc (Net Functions) were imported and we can for example use the FTP homebrew via VHBL.
So which games should you concentrate on now with the goal of high compatability for VHBL? --> Definitely 'big' and complex PSP games.
PSP Games - expensive but often 'good programed' because of professional studios and having alot of system functions -> good compatability
PSP Minis - cheap/free but often 'badly programed' and often missing alot of functions -> bad compatability
Of course it all depends on the game itself. This is just a general advice!
So lets get started!
Setting up our Emulator
I'm going to use JPCSP here as its the 'better' one for our purpose in my opinion. (There however are differences to PPSSPP in performance and rendering some stuff for special cases though)
Enabling the debugger: (to make the debugger start by default you need to add a '-d' to the start parameters in the start.bat file) Turning save-encryption off Oh and you might want to 'learn' the assignment of keys
Getting started
At first we are going to need our Game in ISO format. (A tutorial on how to get ISOs directly from the PlayStation Servers can be found here)
For this guide I'm going to use the game Arcade Darts [US-Version] and show how it was exploited before. Just place the ISO in JPCSP's umdimages -folder.
Lets launch our JPCSP and select the game. Depending on the game you have to actually play it, select a username and that kind of stuff until it saves for the first time. Once the save dialog is over you can now close the emulator.
Editing our savegame
You can now open the saves location (JPCSP/ms0/PSP/SAVEDATA/) and it should have created the savegame containing all the normal savegame files (ICON0.png, PARAM.SFO aso) and our actual data in .bin format.
In case of Arcade Darts the file would be 'SECURE.BIN' and thats where all your Userdata is kept at.
What we are going to do now is basically filling it with junk and see if that affects the game!!
Why A(s)? Because their Hex ID would be 41 (B = 42, C = 43 aso) and thats easy to recognize later on when analyzing a crash.
Loading our custom Savegame
Yup, it crashed! And by looking at the debugger we can see that the return address ($ra) got overwritten by our AAAAA(s)! Note: As mentioned earlier this is the easiest and best case scenario since we can directly return to any address in RAM we want. This is an exploit! However, more commonly you will only have some of the registers influenced. From there you will need time, pacience, understanding of how MIPS works and probably a real PSP with PSPlink to debug everything.
and probably alot more$zr -> Zero Register, always contains 0x00000000
$at -> Assembler temporary, can generally ignore this
$v0-v1 -> Function return values, these tend to be easily changed from loading functions
$a0-a3 -> Function arguments, if you have control of these you will need to look into the following functions
$t0-t9 -> Temporaries, usually useless, may be useful depending how they are used
$s0-s7 -> Saved temporaries, should keep an eye on these in complex sections of code, can be useful
$k0-k1 -> Kernel registers, defines the exception handling
$gp -> Global data Pointer, name kind of says it all
$sp -> Stack Pointer, VERY useful in more complex sections of code, contain old $ra values and $s# values
$fp -> Frame Pointer, points to somewhere in the stack, only used by some(usually large) functions
$ra -> Return Address, easiest register to exploit if you have control
Normaly the registers should start with 0×08, 0×09, 0x48, 0x49 or have 0×00000000, 0xDEADBEEF. Other registers with unusual addresses (i.e. 0×61616161, 0x534F1B20 and any other weird looking addresses) are influenced!
The following games have direct overflows in $ra:
- the Arcade games (because they all use the same engine)
- Widgets Odyssey 1 & 2
- Splinter Cell
- the Petz games
- Apache Overkill
- FieldCommander [US]
Next up we need to locate the exact offset of our $ra. To do that we simply compartmentalize our area with different values.
Now we already know which line (the one filled with aaaaaaa) and are close to the exact offset. Just one more time should do it..
There we go!
What to do next? Right, create a very simple PoC to see that it works!
Creating an Exit Game PoC
Basically what we are doing now is redirecting the function to return to the exit-function which simply exits the game. So when the game exits after loading our savegame we know it worked.
At first we need to find out the address of the function 'sceKernelExitGame' (which every game should have!) When doing this on a PSP we can use PSP link to create us a list of all imported functions. But since we want to do everything with our emulator we are going to look for it manually in the memory.
We can now write the address 0x089B6240 to the exploited function's $ra address. Because of endianness in the HexEditor that would be
Code: Select all
40 62 9B 08
And now test it: Yes it jumps right to the exit function! However since there is not much to see with this very simplistic PoC (especially in the emulator), we will go one step further..
Creating a Color Flasher PoC
Download these simple ColorFlasher source files and compile them yourself.. or download this pre-compiled binary for Arcade Darts right here.
Find the address to jump to where we will store the ColorFlasher code
To find the real address we need to find and calculate it manually! Therefore we are going to write some unique string somewhere in the savegame with enough space.
After the savegame was loaded we can make a RAM Dump 'ramdump.bin' which will be about 32MB big and then open it again with our HexEditor
Next we simply search for the unique string and check if the savegame memory block was loaded completely or splattered all over the RAM. This one is fine..
To get the real address we need to calculate:
Code: Select all
0x08000000 + 0x00AA2190 = 0x08AA2190
Code: Select all
90 21 AA 08