[PS3] How to find the Dev’s KLicensee (by mysis)
Note from Wololo: This tutorial was initially published by /Talk member mysis, as part of our monthly tutorial contest. Mysis won the “Mods award” prize (a $10 PSN Code) for his entry, awarded by myself and the moderators. You can find the original post here.
In this tutorial the aim is to understand how the klicensee is being set and a way is described on how to find it.
This merely should be giving a technical point of view on this topic.
KLicensee stands for key licensee. Its part of the DRM system being used on the Playstation 3.
Developers prepare content to be protected and define a 128 Bit key (KLicensee) that is responsible for proper access rights and part of the decryption of it. Content can be game files, prx modules or other game executables. Encryption is done by Sony, decryption by console.
Reading encrypted content is possible once the klicensee has been registered via sceNpDrmIsAvailable-Api. Then we are accessing the content as if it was a regular file. The Identification is done via GameOS itself, decryption via appldr.
Why do we need to find out about klicensee and why can this be useful?
* Resigning content (for lower firmwares – i still like mine on 4.46 rebug, some even 3.55)
* Modding protected game content
* Hidden/externally set klicensee’s inside other files
If you know about reverse code engineering then you would simply fire up IDA,
look for places where the api is being called and get the key. If not, then you might would use one of the bruteforcer tools outta there.
But what if developers are hiding it?
Or if it is not inside the game executable?
That’s where this tutorial might give you an insight about.
Understanding Klicensee Register
Whenever an application/game calls SceNpDrmIsAvailable-Api it executes code from the SystemUtility Library “libsysutil_np” providing the key.
(Here is a small print of the function – Example: Firmware 4.23)
“sub_7D0C” then prepares a CXml-Document of this information and sends it over to VSH using the “NPDR”-Magic with created elements “drmrequest” and “reqtype” = 0.
That means registering the key to the system and lower levels is not happening on the game side, but VSH-Side.
When we take a look at VSH, specifically the “NPDR”-Handler, we see that it is indeed checking “drmrequest”-Element (@ 0xEC8F8) :
The CXml-Document by now has been converted back into a regular Buffer of size 0x118, with the format as sent on game-side:
+0x00 Game Process id
+0x14 NPDRM File Path
Later in that code, when everything has been done, VSH creates an CXml-Document with a response to the game process. (0 = OK, ErrorCode when failed)
But that is not interesting to us at the moment. We will now examine the “vshnet_5EE098BC”-Export a little bit more.
Register r3 contains our 0 “reqtype” given as well from the game side.
I broke it down to that much what interests us and removed other request Types from the picture:
@ 0x24F234 : cmpwi cr7, r3, 0 # request Type == 0 @ 0x24F254 : is whats happening
So, request Type 0 simply copies the buffer to VSH memory (0x715340, yellow highlighted) and then begins a Job (loc_24F330) with the function at stru_6C9F60.
That performs various checks (time, license, ….) on klicensee with a supplied content file, as well as makes up a final structure for registering it to lower levels (Lv2kernel) with System Call 0x1D6.
This is a quite generic process and independent to what the game developers did on their side to the klicensee, as they need to use sceNpDrmIsAvailable-Api once.
Finding klicensee always again
Now that we found where klicensee will be stored, we can always find it again.
We only have to read the information stored at address 0x715340 – ingame, and again in this example firmware 4.23.
Accessing VSH memory can be done via custom VSH-Plugins, either running via CFW or PRX-Loader.
As Firmware to Firmware and VSH-Types offsets change the best would be to have something generic.
You might have noticed the “drmrequest”-Function is provided as VSH-Export. That means we can simply get the function offset via its NID:
(void*&)(vshnet_5EE098BC) = (void*)((int)getNIDfunc("vshnet",0x5EE098BC)); int * func_start = (int*&)(*((int*&)vshnet_5EE098BC));
Next would be finding the memcpy and taking the destination address.
But ….. we can actually take “dword_715338” at the function start ([i]see picture3 highlighted[/i]), because its only 8 bytes before our lovely buffer.
Add 8 to it and we come to the buffer we want.
seg001:000000000024F1BC 3D 20 >00 71< lis r9, dword_715338@h … seg001:000000000024F1D0 90 69 >53 38< stw r3, dword_715338@l(r9)
npklic_struct_offset = (((*func_start) & 0x0000FFFF) << 16) + ((*(func_start+5)) & 0x0000FFFF) + (8 + 4);
The first part of this code retrieves lower 16 bit (00 71) @ 0x24F1BC instruction bytes, second part retrieves lower 16 Bit (53 38) five instructions afterwards @ 0x 24F1D0 in order to build address 0x715338,
( +8 equals our 0x715340 at the memcpy!) Yet there only lies the [i]games process id[/i], +4 again and we are at where klicensee is stored!
And here is basically my code for printing/logging it to file/console Output and showing it to the user:
log("KLicensee: "); log(hex_dump(kl,npklic_struct_offset,0x10)); // char* hex_dump( char *, void *, size ) log("\n"); vshtask_A02D46E7(0, kl);