Note from Wololo: for those interested, The 20th Anniversary edition of the DS4 controller is back in stock at the time of publishing this article.
Following up wololo’s PS1 hacking article (http://wololo.net/2015/09/12/playstation-20th-anniversary-the-hackers-perspective/), I’ve decided to finally talk about one of the greatest exploits that I have worked with alongside my fellow developer and close friend qwikrazor87, I also wanna clear something out about TN-X and do a special announcement at the end of the article.
First of all I wanna link to a very old blog post that I made myself and point out that I was partly wrong (I’ll explain in a bit). The post in question can be found in the following link:
http://wololo.net/talk/viewtopic.php?f=23&t=16351
tldr: the post mentions how exploiting PS1 games was useless due mostly to the inferior hardware of the PS1 unit and the lack of mass storage for ISOs and homebrews.
This is true, about the PS1, but here lies Sony’s biggest problem: PS1 games on the Vita run on top of a modified PSP emulator. It makes sense really, why code or port a PS1 emulator when you have a PSP emulator that is perfect enough to emulate POPS (and this was proven long before Sony’s official solution for PS1 games: http://wololo.net/2012/06/14/psx-on-the-vita-an-interview-with-the-mysterious-vita-hacker/).
But this is where things become interesting, PS1 exploits were now valuable provided we find a way to break out of the PS1 sandbox and trick POPS into executing our own PSP code.
They work just like any PSP usermode exploit, but there’s an extra step in between the process.
For PSP we usually go from user to kernel, on PS1 games we have to go from PS1 code execution to PSP usermode code execution to kernelmode code execution.
Obtaining code execution in a PS1 game is easier than rolling a blunt, you can do it even on an emulator for PC, you don’t need to decrypt and re-encrypt the savedata, and if PSP games were weak as f*ck imagine a 20 year old game coded by developers who assumed you had no access to the savedata.
Now the question was on getting POPS to execute our code.
At first this seemed like a tedious task, we thought we were gonna have to reverse POPS the same way we have to reverse kernel for kxploits. That is until we found that POPS used a dynamic recompiler.
Now there is something really cool about emulators using dynarec instead of interpreters: whatever flaw the emulated code has will be translated over to the native code of the host machine, and in the case of PSP usermode there is really no extra security compared to the PS1: no stack canaries (this is specially useful as it means that buffer overflows in the PS1 game will translate into a buffer overflow in POPS), there’s no NX bit either and even if there were a dynarec emulator would require access to code allocation functions to work, and of course there’s no ASLR.
So knowing this after our initial exploration of POPS’s internals we set out test our theories that any bug found in a PS1 game will affect the Vita and PSP equally. And yes it did, we managed to get some graphical glitches in Crash Bandicoot 3 that were present on both PSP and Vita. So it was a matter of finding a crash in a PS1 game that would translate into a crash in POPS that we could analyze with psplink.
A funny anecdote: I found an exploit in POPS memory card manager that affected all PS1 games, however the Vita’s memory card manager is different and thus this exploit didn’t apply.
But then I found the following crash:
host0:/> Exception - Address load/inst fetch
Thread ID - 0x03317559
Th Name - popsmain
Module ID - 0x0331EA43
Mod Name - pops
EPC - 0x08B86264
Cause - 0x10000010
BadVAddr - 0x09CF6975
Status - 0x60088613
zr:0x00000000 at:0x8009804C v0:0x098C0000 v1:0x00000000
a0:0x656F6975 a1:0x3E82FFFC a2:0x09CF6161 a3:0x656F6975
t0:0x00000000 t1:0x00010750 t2:0x00010000 t3:0x80000000
t4:0x041B00B0 t5:0x800C2618 t6:0x801FFF28 t7:0x80097FFC
s0:0x801FFEFC s1:0x801FFEF8 s2:0xFFFFFFFF s3:0x00000000
s4:0x80098048 s5:0x8020002B s6:0x00000000 s7:0x801FFFA0
t8:0x801FFF20 t9:0x00023589 k0:0x0000002D k1:0x800A0000
gp:0x00010000 sp:0x09800000 fp:0x801FFEF4 ra:0x09562A00
0x08B86264: 0x8CC60000 '....' - lw $a2, 0($a2)
disasm $epc 10
0x08B86264: 0x8CC60000 '....' - lw $a2, 0($a2)
0x08B86268: 0x3C0308B8 '...<' - lui $v1, 0x8B8
0x08B8626C: 0x24636468 'hdc$' - addiu $v1, $v1, 25704
0x08B86270: 0x0066300A '.0f.' - movz $a2, $v1, $a2
0x08B86274: 0x00C00008 '....' - jr $a2
0x08B86278: 0x00000000 '....' - nop
Essentially it loads a pointer from an address that we partially control and jumps there. That’s where the fun began: we could only reference about 4Mb of RAM that we had no control over, so we had to look at all the pointers available in that region of RAM for one that could perhaps lead to more vulnerable code and/or more control over the CPU registers. That was until qwik found this:
host0:/> memdump 0x9ffe550
- 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f - 0123456789abcdef
-----------------------------------------------------------------------------
09ffe550 - 9C 6A FD 08 20 E6 FF 09 84 D6 B7 08 00 00 00 00 - .j.. ...........
09ffe560 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe570 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe580 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe590 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe5a0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe5b0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe5c0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe5d0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe5e0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe5f0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe600 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe610 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe620 - 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F - ................
09ffe630 - 10 11 12 13 00 00 00 00 00 00 00 00 00 00 00 00 - ................
09ffe640 - D7 B3 32 F8 AB 5E 55 1E 68 2D 08 8B A4 F8 E4 EF - ..2..^U.h-......
At address 0x09ffe550 (which is within the range of memory where we can read from) you can find a pointer to memory address 0x08FD6A9C, which points to this:
host0:/> memdump 0x8fd6a9c
- 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f - 0123456789abcdef
-----------------------------------------------------------------------------
08fd6a9c - 00 50 4D 56 80 00 00 00 00 00 00 00 00 01 02 03 - .PMV............
08fd6aac - 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 - ................
08fd6abc - D7 B3 32 F8 AB 5E 55 1E 68 2D 08 8B A4 F8 E4 EF - ..2..^U.h-......
08fd6acc - D1 02 D0 17 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6adc - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6aec - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6afc - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6b0c - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6b1c - 4D 43 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - MC..............
08fd6b2c - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6b3c - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6b4c - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6b5c - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6b6c - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6b7c - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08fd6b8c - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0E - ................
What you are looking at right now is the virtual memory card of the PS1 game containing the crafted save and therefore any code that we can inject. This gets us close as we now know that we can at least jump to the memory card, however we don’t control exactly where inside the memory card to jump to, we can only jump to the very beginning of the memory card and it contains data that we cannot modify (the magic number of Sony’s virtual memory card format).
However upon close inspection we found the following:
host0:/> disasm 0x8fd6a9c 10
0x08FD6A9C: 0x564D5000 '.PMV' - bnel $s2, $t5, 0x08FEAAA0
0x08FD6AA0: 0x00000080 '....' - sll $zr, $zr, 2
0x08FD6AA4: 0x00000000 '....' - nop
0x08FD6AA8: 0x03020100 '....' - Unknown
0x08FD6AAC: 0x07060504 '....' - Unknown 0x08FD6AA0: 0x00000080 '....' - sll $zr, $zr, 2
0x08FD6AB0: 0x0B0A0908 '....' - j 0x0C282420
0x08FD6AB4: 0x0F0E0D0C '....' - jal 0x0C383430
0x08FD6AB8: 0x13121110 '....' - beq $t8, $s2, 0x08FDAEFC
0x08FD6ABC: 0xF832B3D7 '..2.' - vwb.q R402, -19500($at)
0x08FD6AC0: 0x1E555EAB '.^U.' - Unknown
The first 8 bytes of the virtual memory card are actually two valid MIPS instructions, what can we do with them? Let’s analyze the instructions for a second.
0x08FD6A9C: 0x564D5000 '.PMV' - bnel $s2, $t5, 0x08FEAAA0
First of all, this is a branch instruction, and we know it will be executed because $s2 and $t5 are not equal.
Branch instructions are of type I, and have the following instruction format:
6 bits for opcode
5 bits for rs register
5 bits for rt register
16 bits for immediate value
In the case of branches, the effective address of the branch is calculated as:
$pc + 4 + (immediate<<2)
Which means that all branches are relative to the address of the current instruction.
Now due to the fact that the immediate value is 16 bits long and is shifted left by 2 positions, this means all branches are effectively positive, meaning they will always jump ahead of current code and never back.
This means we actually have a chance of branching deeper into the memory card, perhaps in an empty area that we can fill with our own custom code?
Now the immediate value of this specific instruction is 0x5000, which means the CPU will branch to (0x5000<<2)+4, which is 0x14004 bytes ahead of the start of the memory card. Meaning the memory card has to be bigger than 0x14004 bytes for us to inject our data there. How big is the memory card though? It is exactly 0x20080 bytes big, in other words, it’s big enough so we are now sure that the CPU will branch to a region within the virtual memory card, and in our case it is so deep down in the memory card that such space isn’t even used by the game, a memdump of 0x08FEAAA0 shows this:
host0:/> memdump 0x08FEAAA0
- 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f - 0123456789abcdef
-----------------------------------------------------------------------------
08feaaa0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feaab0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feaac0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feaad0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feaae0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feaaf0 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feab00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feab10 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feab20 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feab30 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feab40 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feab50 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feab60 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feab70 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feab80 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
08feab90 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
Nothing but emptiness, and more than enough space to house the typical binloader that we use.
Now that we know the first instruction is actually a godsend, we have to look at its delay slot:
0x08FD6AA0: 0x00000080 '....' - sll $zr, $zr, 2
Ooooops, there’s actually nothing to say here, this delay slot is essentially a NOP (No Operation), it does absolutely nothing to the state of the CPU nor does it crash or change the effect of the jump. Why? Well because register $zr is a read-only register, attempting to write to it causes absolutely no change to the system, and even if it did, 0<<2 is still 0, nothing changes.
The rest of the instructions don’t bother us, we never get to execute them as the branch is already effective by that time and the system is executing code at address 0x08FEAAA0 where we have injected our own code.
From here on out we used the sceSdGetLastIndex kernel exploit that qwikrazor87 found to gain kernel access within PSX exploits. After we had confirmed kernel code execution on PSX exploits we handed the files over to Total_Noob. TN is a very talented developer and at that time we were on good terms and collaborating on some projects, this one most importantly as we were all PSX lovers.
It didn’t take much for TN to realize that a TN-V port was impossible, there was simply too many incompatibilities as Vita OS does not allow PSX games access to certain PSP hardware such as sound or wifi.
But he acted quickly and made a toned down version of his CEF CFW (notably known for TN-C, TN’s only open source CFW) nicknamed cef_psx and handed the sources to us. However many problems arose when firmware 3.18 came by TN’s latest version of cef_psx (which allowed custom menus) didn’t work. For whatever reason TN was denying us his latest sources because he said it had hacks that he didn’t want us to know. These hacks turned out to be the very well known dot trick mostly used to create EBOOT.PBP and PBOOT.PBP (used in the bubble trick). TN later named his PSX CFW TN-X (the idea was mine) and was telling everyone that he was the one responsible for PSX games with perfect sound and was even daring to take credit for qwikrazor87’s exploit. After that we all know the fiasco with TN’s little “virus” hidden in TN-X targeted at The Z because they have a love hate relationship or some sh*t like that.
So no, TN wasn’t the one responsible for PSX exploits, he was another helping hand that moved it forward. That’s called collaboration.
I really don’t care about drama, I just wanna get sh*t done so I’m just working on porting ARK to PSX exploits, all while making it fully open source as I believe knowledge should be shared, otherwise I don’t think any of us would be here right now talking about how to exploit a 20 year old console emulated by a 10 year console running in the shadows of a 3 year system that came out dead on arrival.
Now onto the announcement: for a while now a few people (including myself and an old member of the PRO Team who decided to rise from the dead) have been working on ARK-3 eCFW for PSP and PSX exploits. We’ve been working to bring the most complete, stable, customizable, fully open source experience that we can bring to ePSP users and simply due to the fact that there’s no native exploit beyond 3.52 we have decided to release it for this firmware (or higher in case Sony releases a new firmware before we release ARK-3). Some of the features we are working on include:
– Support for PSX exploits, including an unreleased PSX exploit for 3.3X+
– PEOPS SPU Sound Plugin for PS1 games running on PSP exploits.
– PRO Online
– A real alternative to the XMB (in other words: arkMenu)
– Whatever other *** we wanna do along the way
Creating a CFW is not easy work, and we are always open for collaboration and help from the scene, but we also know that not everyone has the skill to help with this task, but there’s always something you can help with, so we have decided to give exclusive beta access to ARK-3 for anyone who can supply us with anything of the following:
– PSP usermode exploits
– PSX exploits (NOTE: we are not looking for exploits in POPS, that’s a job for the big guns, we are looking for PSX code execution, not more, we’ll be making a tutorial for this soon)
– Themes and/or mods for arkMenu and/or xMenu
– Hardware (I’m looking for a Vita TV on latest firmware)
– Anything else you consider might be useful for our work
The post PlayStation 20th Anniversary: How PSX Exploits Work appeared first on Wololo.net.