10 Days of Hacking, Day 5: The PSP, Part 2
Lets dive directly into what I consider some of the best hacks in the modern Scene of the PSP.
With the creation of the PSP 2000 V3, PSP 3000 and later PSP Go, a fully permanent Custom Firmware became apparently impossible on these devices, leaving developers to focus on other, more volatile, methods to hack the system without touching flash0. This lead to two things: kernel exploits with LCFW and usermode exploits with HBL.
LAZY GAME DEVELOPERS
I’ve already explained more or less how usermode exploits, specially buffer overflows, work. But it is interesting to note that buffer overflows are the result of bad memory management on the developer’s side, and are one of the simplest and easiest to avoid hacks out there. You can sometimes make a mistake and allow for a buffer overflow to happen, but when a lot of games have simple buffer overflows, most of the time in the same place, is a sign of developers going lazy.
Going back to the topic at hand, as I said, back then usermode exploits was an area left untouched, why would you develop there? we had permanent Custom Firmwares appearing almost as fast as Sony’s Official Firmwares. But then as I said, CFW became impossible on newer PSP revisions, and with Dark_Alex’s retirement, a new CFW did not seem like a possibility. Then we got what appeared to be the savior of those people stuck with unhackable devices: the Gripshift exploit.
The Gripshift exploit promised a way of running unsigned code, mainly homebrews and emulators, on any device up to date, but it fell into obscurity due to the lack of a proper tool a la Noobz’ eLoader that would allow to play homebrews, or a kernel exploit that would allow to downgrade, Sony quickly patched the exploit and everybody’s hope went down to the floor again.
A few after that, a developer and part of the PC Gaming Master Race going by the name m0skit0, using an exploit (found by kgsws) in the game Medal Of Honor Heroes, or Heroes 2, or both, I donno, ask him, and started working on his own homebrew loader with the help of ab5000, cause you know, he’s that awesome. m0skit0 (the developer, not the malaria-spreading insect) was contacted by another developer who we call wololo because he can make you switch teams by chanting his name over and over again, who proposed to port his project into another exploit, Patapon 2, that later on got leaked to the public by some asshole looking for 5 minutes of internet fame. Since the site m0skito was using to maintain the project got down, they moved development over to wololo.net/talk and create public SVN repositories of the project in googlecode, and thus Half Byte Loader was born.
Patapon 2 was a great exploit and Half Byte Loader was a great tool to use it, not only did it allow to run homebrews on the latest firmware at the time (6.20), it was also the first tool to do so on the PSP Go. Since the Patapon 2 exploit existed in the demo version of the game, everyone could freely download that onto their PSP’s, copy HBL’s installation files and have some homebrew magic on their previously unhackable device.
Half Byte Loader set the grounds for many more usermode exploits to come and has been actively maintained by the community over all these years, being ported over to the Vita and a lot of other usermode exploits.
HBL is a great piece of software if you consider the greatly limited environment it is running in, so big props to all devs that has made it what it is today.
FOOLING THE KERNEL
I kinda skipped the 5.03 kernel exploit and checkHEN by davee, but that’s because I mostly wanted to separate them into two categories. It’s true that later PSP models prevented the use of permanent custom firmwares, but the PSP 3000 was greeted with a kernel exploit and HEN (later LCFW). But after that, kernel exploits remained hidden by developers, until 6.20 when PSP developer Total_Noob released a kernel exploit that existed all the way up to 6.35, although his HEN originally only worked on 6.20. There’s a lot I can talk about the 6.XX era, Total_Noob, Neur0n, Team PRO, HBL, etc, but I wanna focus on explaining all the kernel exploits that most caught my attention as they all shared something in common: a stupid mistake by Sony, aka a missing check on one of the arguments.
The first one we have is the 5.03 kernel exploit (I don’t remember who the author was, I only know davee only coded the HEN). This kernel exploit existed in the module psheet.prx, most specifically in the function sceDRMInstallGetFileInfo.
Let’s take a quick look at the code:
0x000000E0: 0xAFB40010 '....' - sw $s4, 16($sp)
0x000000E4: 0x34620108 '..b4' - ori $v0, $v1, 0x108
0x000000E8: 0x00C0A021 '!...' - move $s4, $a2
0x000000EC: 0xAFB3000C '....' - sw $s3, 12($sp)
0x000000F0: 0x00A09821 '!...' - move $s3, $a1
0x000000F4: 0xAFB10004 '....' - sw $s1, 4($sp)
0x000000F8: 0x00E08821 '!...' - move $s1, $a3
0x00000140: 0x02602821 '!(`.' - move $a1, $s3
0x00000144: 0x02402021 '! @.' - move $a0, $s2
0x00000148: 0x02803021 '!0..' - move $a2, $s4
0x0000014C: 0x0C0001E8 '....' - jal sub_000007A0
0x00000150: 0x02203821 '!8 .' - move $a3, $s1
I have omitted a lot of code on purpose. So the function pretty much grabs your arguments, calls sceIoOpen, calls another subroutine and then calls sub_000007A0 with argument 0 being the return value of the call to sceIoOpen, argument 1 being the argument 1 we passed to the function and argument 2 being the argument 2 we passed to the function and arg3 being the arg3 we passed. Now we take a look at sub_000007A0:
; Subroutine sub_000007A0 - Address 0x000007A0
sub_000007A0: ; Refs: 0x0000014C
0x000007C0: 0x00E09821 '!...' - move $s3, $a3
0x000007EC: 0x24060108 '...$' - li $a2, 264
0x000007F0: 0x02602021 '! `.' - move $a0, $s3
0x000007F4: 0x0C000928 '(...' - jal memset
0x000007F8: 0x00002821 '!(..' - move $a1, $zr
This subroutine makes two calls to memset, on the second one, arg0, the destination of the write, comes from the third argument passed to the subroutine, which we know from the previous function that is the arg3 we passed to sceDRMInstallGetFileInfo, which is not at all checked, so we can memset to 0 about 66 instructions in any part of RAM, including kernel RAM.
Next comes the 6.20 kernel exploit released by Total_Noob, this one is explained more in-depth by davee here: http://lolhax.org/2010/12/23/arcanum/
but this is more or less how it works:
The vulnerability exists in sceUtility_private_764F5A3C, here’s interesting part of the code:
0x00002800: 0x0C002229 ')"..' - jal scePower_driver_1A41E0ED
0x00002804: 0x001BDAC0 '....' - sll $k1, $k1, 11
It shifts k1 to the left by 11 bits, this is a normal procedure done by Sony to filter out unwanted pointers to kernel RAM. Kernel RAM always start with 0×8, which in binary is 1000, when we call a kernel function from user space interrupt manager sets k1 to 0×100000, so when the function shifts it 11 bits to the left, it becomes 0×80000000. If you were to pass a pointer to kernel RAM the pointer would look something like this 0x8XXXXXXX, when you do the logic operation AND with this pointer and the k1 value 0×80000000, then the result will be 0×80000000, if the pointer would not start with 8 or bigger, then the result would be 0. The PSP checks this value for negativity, in two’s complement (a method of representing signed numbers in binary) anything starting with 0 is positive, starting with 1 is negative. If we pass a kernel pointer then the result will be negative and the kernel will return an error, that is at least the theory, Sony has to do all these checks manually and sometimes they forget something. In this case not only they forget to do any check (they just shift k1 but do nothing to it), if we check the function being called right after the k1 shift (scePower_driver_1A41E0ED), we see this:
0x00000800: 0x001BDAC0 '....' - sll $k1, $k1, 11
This function shifts k1 again, if k1 was previously shifted into 0×80000000 and is shifted again then it becomes 0×40000000000, but the PSP is a 32bit machine, and k1 is a 32 bit register, you can’t hold that number, so it overflows into 0, allowing it to pass all k1 checks and eventually reaching this piece of code:
loc_000008D8: ; Refs: 0x000008A4
0x000008D8: 0xAC710000 '..q.' - sw $s1, 0($v1)
0x000008DC: 0x02202021 '! .' - move $a0, $s1
0x000008E0: 0x00008021 '!...' - move $s0, $zr
0x000008E4: 0xAC600004 '..`.' - sw $zr, 4($v1)
0x000008E8: 0x8CB10204 '....' - lw $s1, 516($a1)
0x000008EC: 0xAC60000C '..`.' - sw $zr, 12($v1)
0x000008F0: 0xAC710008 '..q.' - sw $s1, 8($v1)
0x000008F4: 0x8CA50204 '....' - lw $a1, 516($a1)
this code stores a bunch of values at the address pointer by $v1, address that we control, so we can overwrite whatever we want in kernel RAM.
Now onto the last one. This one was found by some1 and existed in httpstorage, in the function sceHttpStorageOpen.
This function doesn’t do any k1 checks, but it does check for the only arg in the function, and if a value other than 0 or 1, the function should return an error, or does it? This is the code that should return an error:
loc_0000005C: ; Refs: 0x00000034 0x000000D4
; Data ref 0x000009F0 ... 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000
0x0000005C: 0x267409F0 '..t&' - addiu $s4, $s3, 2544
0x00000060: 0x02348821 '!.4.' - addu $s1, $s1, $s4
0x00000064: 0x8E240000 '..$.' - lw $a0, 0($s1)
0x00000068: 0x04820005 '....' - bltzl $a0, loc_00000080
0x0000006C: 0x0240D821 '!.@.' - move $k1, $s2
0x00000070: 0x0C0001B3 '....' - jal IoFileMgrForKernel_810C4BC3
0x00000074: 0x2413FFFF '...$' - li $s3, -1
0x00000078: 0xAE330000 '..3.' - sw $s3, 0($s1)
0x0000007C: 0x0240D821 '!.@.' - move $k1, $s2
it loads a global value and adds it into $s1, then stores -1 on that address. The kernel exploit works by modifying this global value so that $s1 ends up pointing to the area in kernel RAM we wish to modify, and then enable the VFPU to be able to use -1 (0xFFFFFFFF in binary) as an instruction (vsync 0xFFFF). Why the hell do they do some code if they know the argument is not what they wanted? beats me, but we thanks to them we got a kernel exploit and downgrader for 6.39.
There are many other stupid mistakes that granted us with full kernel access, the entire kermit_wlan module was full of them and they were even easier, they never did any arg or k1 check on the arguments we passed and they directly copied stuff there. It’s like they fired the entire PSP staff and contracted people who had no idea how the PSP security works.
But I’m not gonna go into great details about those as they are still Vita specific tasks, and this is mostly a PSP section.
OBTAINING THE KEYS
Another mayor development that happened in this era of PSP hacking is the discovery of the PSP ECDSA keys that allowed us not only to sign homebrews so we could run it on Official Firmware, but also sign our own Kernel prx’s.
This lead to an interesting discovery by kgsws, which eventually lead to the creation of the permapatch.
The permapatch works by faking one of the kernel modules so that the PSP loads it instead of the normal one, once the module is loaded, we can take over control of the entire PSP’s kernel and load a Custom Firmware instantly, since this faked prx is in flash0 and is loaded on boot, this hack becomes the first permanent hack after the IPL and Pandora hacks, allowing many PSP’s to have full CFW where they were previously thought impossible.
The sad part is that Sony themselves discovered this method way before the scene could, and they changed the way flash0 was encrypted in firmwares above 6.20. For some reason some people believe the permapatch is overrated, but from both a user perspective and a hacker perspective, the permapatch is awesome, if you think otherwise, get the hell out of this programming and hacking forum you n00b.
Overall the PSP scene has moved away to the new Vita scene, specially the ePSP, which, when hacked, offers all the homebrew possibilities the PSP offered back then.
With this ends the two-part episode of PSP hacking, we watched what were the hacks that allowed us to be were we are at now and we have watched some of the modern hacks that are still valuable and used today. Stay tuned for the next episode, and I promise there will be less technical stuff and much less text.