GameCart Research - The Crystal Website This is kind of a thought i've had on-and-off while in the vita hacking scene, and that is wouldn't it be cool to have a "Flash Cartridge" something like an r4 on the DS. which ultimately leads to the question "How do gamecarts work anyway?" and more generally; "How does the vita know if a gamecart is "legit" or not?" if you've been around here awhile, you may have heard that it uses something called "CMD56" to validate gamecarts; and if you've been around even longer, you may know this article from wololo.net about dumping game cartridges with custom hardware. if your looking into something like this, its often good to read up on previous research, since chances are your not the first to look at something, from this we can learn 2 things; - Game Cartridges are just regular MMC cards, and use the SD Protocol
- The Vita verifies that their legitimate by sony, using the generic command "CMD56" ?
- CMD56 consists of 10 requests and 10 responses, totaling 20 packets in total ??
there have been many attempts to look into and try figure out how the "CMD56" authentication works, however no one ever came up with anything from it. All this research however was done before the vita was ever hacked, it pre-dates henkaku, or taihen which actually makes alot of what they did figure out, kind of a bit impressive. but like this also asks more questions than it answers, like what the heck is CMD56 anyway? ..
well to answer this we need to pull out either, the "SD Specifications Part 1 Physical Layer Specification"; from the SD group or "JEDEC STANDARD Embedded Multi-Media Card (e•MMC) Electrical Standard (5.1)", both of these are kinda gatekept by capitalism, and demand a large amount of money in order to access, and even then probably only under NDA. however with a bit of messing around with your favorite search engine with certain tags like "filetype:pdf" you it is possible to find it just sitting on random http servers w directory listings enabled; which is much cheaper and much easier and no NDAs!
anyway, once you have that, you can find that the way SD & eMMC cards work is via some numbered commands theres also CMD1, CMD2, CMD3, etc which all mean different things, the one we're interested in here as has been kinda hinted at is CMD56, which is defined under "Application Specific Commands" and is labeled "Generic Command" or "GEN_CMD; its defined as "the same as single block read or write commands (CMD24/CMD17) ... but the argument denotes the direction of the transfer, not its address ... the data block is not memory payload data but has a vendor specific format and meaning ..."
What this effectively means is that this on any given SD or eMMC card, this command can be used for everything
so sony just uses them for an authentication mechanism, vita gamearts are just regular MMC devices with a different firmware, one that responds to CMD56 in whatever way the playstation vita expects; this is presumably what they mean by "10 requests, 10 responses", perhaps it does 10 "write" operations and 10 "read" operations, totaling 20 'packets' .. but unfortunately though this also means the actual gamecart mechanism itself is non-standard, and so in order to get anywhere it'll have to be reverse engineered and would have to reverse engineer it; from either the console itself or the gamecarts itself, or maybe(?) what we can figure out from just looking at the packets in a hex editor; there is actually some efforts to try reverse engineer it before hand, like this project from motoharu which looks promising, however if you actually look into it you'll find its very incomplete; and seems to be more(?) of a reverse engineering of the entirity of GcAuthMgr, (which does more than just GC Auth, thanks Sony-) but moreso, that would would mean that even if it were complete, it would only contain the part for the vita part of the communication, not the cartridge side;
which leaves that naturally of course, i am going to have to reverse engineer it myself, first order of business then would be to try obtain a dump of the actual cmd56 packets thankfully this is much easier for us to do now, than it was back when everyone else tried, while everyone else had to resort to very expensive oscilloscopes or custom FPGA solutions to log the packets, for us today, its as simple as just writing a plugin to hook the consoles cmd56 "read" and "write" functions in the kernel,
but we have to find them first; on the vita, the authentication is handled by "GcAuthMgr" located at os0:/kd/gcauthmgr.skprx; and- there is also a 'secure processor' (F00D) counterpart, os0:/kd/gcauthmgr_sm.self, and there are some NIDs documented on the vita developer wiki which indicates a function named "cmd56_handshake" with NID 0x68781760; looking at this in Ghidra, there is are two constant calls to SceSdifForDriver, and it's pretty obvious these are for sending and receiving from CMD56 (also Sdif, in SceSdif likely stands for "SD-Interface") after finding that, its pretty trivial to write some code to hook these functions with TaiHen to log everything sent or received I opted to try make it use the pcap format becuase it's actually relatively simple, and allows me to analyze these packets using standard tools for packet analysis like Wireshark Which even allows me to write a descriptor in LUA to parse out the format; to figure out how it works; packetlog.c:
#include <stdio.h> #include <stdarg.h> #include <vitasdkkern.h> #include <taihen.h>
#include "pcap.h"
static int sendHook = -1; static tai_hook_ref_t sendHookRef;
static int recvHook = -1; static tai_hook_ref_t recvHookRef;
int SceSdifSendGcPacket_Patched(void* instance, char* buffer, int bufferSz) { write_pcap_packet(buffer, bufferSz, 1); int ret = TAI_CONTINUE(int, sendHookRef, instance, buffer, bufferSz); return ret; }
int SceSdifReceiveGcPacket_Patched(void* instance, char* buffer, int bufferSz) { int ret = TAI_CONTINUE(int, recvHookRef, instance, buffer, bufferSz); write_pcap_packet(buffer, bufferSz, 0); return ret; }
void _start() __attribute__ ((weak, alias ("module_start"))); int module_start(SceSize argc, const void *args) {
write_pcap_hdr(); sendHook = taiHookFunctionImportForKernel(KERNEL_PID, &sendHookRef, "SceSblGcAuthMgr", 0x96D306FA, // SceSdifForDriver 0xB0996641, // SceSdifSendGcPacket SceSdifSendGcPacket_Patched); ksceKernelPrintf("[started] %x %x\n", sendHook, sendHookRef); recvHook = taiHookFunctionImportForKernel(KERNEL_PID, &recvHookRef, "SceSblGcAuthMgr", 0x96D306FA, // SceSdifForDriver 0x134E06C4, // SceSdifReceiveGcPacket SceSdifReceiveGcPacket_Patched); ksceKernelPrintf("[started] %x %x\n", recvHook, recvHookRef); } int module_stop(SceSize argc, const void *args) { if (recvHook >= 0) taiHookReleaseForKernel(recvHook, recvHookRef); if (sendHook >= 0) taiHookReleaseForKernel(sendHook, sendHookRef); return SCE_KERNEL_STOP_SUCCESS; }  |
and now we just insert a vita cart into the device and ... the cart doesn't authenticate at all anymore?? what? why?? well if we take a look at ghidra decompilation. between one of the scesdifSend and sceSdifRecv there is a timing check! it calls ksceKernelGetSystemTimeWide() at before sending anything, and then again after sending it.and if it takes more than 5000 microseconds it'll fail the authentication! .. but.. why?? for what reason could they possibly have done this? its not just a timeout since it only seems to cover 2 of the 20 packets!
Well lets just say, i have a suspicion ..
This if you were not around in the really early vita hacking days, is "The Cobra Blackfin". it is effectively the only ever successful attack on vita gamecart authentication, it was the first time you could load backup games on your vita, predating the henkaku hack and later utilites like NoNpDrm (or even Vitamin) the way this bad boy worked, is NOT by reverse-enginering and reimplementing CMD56, but instead by connecting over the internet and proxying the CMD56 authentication to who actually owns the game.
and in that same wololo article about it also mentions an interesting observation, being that: "Cobra blackfin is not compatible with 3.60, The Blackfin team advises people to stay on 3.57." and wouldn't you know- it just so happens that this timing check is missing on 3.57 and older, so this must be how Sony patched the cobra blackfin! the network connection to the gamecart would be very slow, such that a timing check is enough to effectively block this approach; and we must be hitting this same timing check when dumping packets as the IO on the vita is also very slow-
anyway this is important information for anyone who might want to implement their own flashcarts, that they have to respond fast enough (around 5000us).in order for it to have any chance of working on an unmodified vita; however since we have complete control over the vita kernel, i don't actually need to care about it for our purposes of just trying to dump the packets and can simply just patch out the patch-
the approach i took was simply hooking "ksceKernelGetSystemTimeWide" to always return 0; rather amusingly, this should also allow you to use a Cobra Blackfin on FW3.60; but anyway, after adding this, it finally works! i can now view the CMD56 packets in Wireshark!
NOTE: all the code for the packet logging and for the cobra blackfin patches, can be found in the "PythonWhiteFin" on my git server.
So, we have packet logs, so now to try explain whats actually happening, first- i want to say the obvious but CANNOT figure out the format from just packet logs alone; this is why Cobra Blackfin and other researchers back then never got any further than this, and not just that but you CANNOT figure out what its doing without being able to read the F00D/Secure Processor code and decryption access; which is likely why Motoharu's attempt never got anywhere ... (well, that and GcAuthMgr decompiled code is awful to read..)
Let it be abundantly clear here: i am standing on the shoulders of giants for the following analysis:
A high-level overview: The authentication has 2 primary security features; - eMMC lock out, can only accept read/write commands after authentication is complete.
- A per-card encryption key to derive a per-game decryption key for SceNpDrm.
These are accomplished by doing the following - The Console verifying its connected to a real Vita Cartridge
- The Cartridge verifying its connected to a real PS Vita
Only then can the contents be read, and encryption keys be disclosed to the console the keys are actually never revealed to the primary (ARM7) CPU directly, they reside entirely within the consoles security subprocessor (F00D) furthermore verification of the console and cart are not done via Asymmetric private/public keys as you might expect; instead, the security is using Symmetric Keys, and depends on the Consoles hardware-sealed "bigmac" keyrings; these are accessed via a device exposed to the Security Processor where you never provide the key directly instead you provide a keyring (in this case, 0x345 and 0x348 are used respectively-) the cartridge has a copy of these keys, and the entire security of the authentication depends on these not being known; there are multiple vulnerabilities however that makes this not necessarily the case in general, to understand that, we need to dig a bit deeper;
in this we will refer to packets going TO the gamecart as "requests" and activity coming FROM the gamecart, as "responses"; all packets are always exactly 1028 bytes long, however do not ever actually use the full size, or more generally- a "write" to CMD56 is a "request", while a "read" from CMD56, is a "response"; first about requests, all requests start with the same header, which always begins with a constant 32-byte value:
"DD1025441523FDC0F9E91526DC2AE084A903A297D4BBF852D3D4942C8903CC77"
the format of the header is as follows: | Name | Type | Offset | Length | Description | | magic | byte | 0x00 | 0x20 | Always "DD1025441523FDC0F9E91526DC2AE084A903A297D4BBF852D3D4942C8903CC77" | | expected_response_code | int32le | 0x20 | 0x4 | A unique code expected to get reflected back from the cart | | request_size | int32le | 0x24 | 0x4 | The total size of the request size + additional data size; | | expected_response_size | int32le | 0x28 | 0x4 | Expected size of the response from the cart. | | command | int8 | 0x2C | 0x1 | A unique number for every command | | unknown | int32le | 0x2D | 0x4 | Always 0x00, not sure what its for | | additonal_data_size | int32le | 0x31 | 0x4 | Always the same value as request_size |
Following that, is the actual contents of the request, a "request_size" buffer of which the format of differs for every command value, after each request; the console then attempts to 'read' a response; each response also has the same header at the start, but different data inside depending on what command was sent to it; and is then padded out to exactly 1028 bytes. the response header is not in the same format and is instead as follows: | Name | Type | Offset | Length | Description | | response_code | int32le | 0x00 | 0x4 | should match the expected_response_code from the request | | additional_data_size | int32le | 0x04 | 0x4 | Always 0x00 | | response_size | int16be | 0x08 | 0x2 | Size of response, should be equal to endian-swapped expected_response_size from request. | | error_code | int8 | 0x10 | 0x1 | 0 On success, not 0 on error. |
And then anything following that for the next "response_size" buffer of which the format differs for every command value received; and padded out after that until it fits in exactly 1028 bytes; NOTE: An error is most likely thrown if the actual size of the response does not fit the expected response size, however i did not test this- the vita never makes a request where this would be the case, therefore the cartridge never would trigger this case in normal use; trying to test these things on-console is a bit annoying; but it is something to look into at some point As for the commands, there are 8 (known) commands, that are used for the gc authentication, i have tried my best to come up with "reasonable" names for them, they are as follows | Command Name | Command ID | | START | 0xC4 | | GET_STATUS | 0xC2 | | GENERATE_RANDOM_KEY | 0xA1 | | VERIFY_RANDOM_KEY | 0xA2 | | GENERATE_SECONDARY_KEY | 0xA3 | | VERIFY_SECONDARY_KEY | 0xA4 | | P18_KEY_AND_CMAC_SIGNATURE | 0xB1 | | P20_KEY_AND_CMAC_SIGNATURE | 0xC1 |
a few observant of you might notice that given this is 8 commands, but there are 20 packets in total (10 request, and 10 responses); however only 8 unique commands are shown here, this is because some commands are re-used throughout the authentication process; below is a diagram i made of the communication flow between the vita and the gamecart; that i put together, as well as a kind of rough explaination, of what each section contains; i will go into further detail about some of this later- The first few commands are relatively simple, in such a way that they can even be figured out by just looking at packet logs. (and are some of the few motoharu actually had implemented.) but lets go over them one by one; in more technical detail how they actually work; - START:
probably used to initialize the the authentication process, or possibly just for identifying it even does cmd56 at all.
- No request data is ever sent for this request
- Response size is 0x13;
- Response code used by the vita is 0x31
- Response is exactly "00000000000000000000000000010104"
- GET_STATUS:
used to determine the state of the game cart. they start out locked initially if the device is locked, typically all reads and writes will fail, the authentication process is used to unlock the device. - No request data is ever sent for this request
- Response size is 0x5;
- Response code used by the vita is 0x23
- Response is "0000" when locked, "0001" when unlocked.
after this is when it kind of stops being so easy though, the rest of the commands will require significant effort to explain;
This page is still under construction and not finished, (or more generally, research is still ongoing) i will write more here later; |