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 gamecarts 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 entirety 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 5000µs).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 sony's 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 | same as 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 | 0x3 + request body 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
- Request size is 0x3
- Response size is 0x13;
- Expected Response Code is 0x31
- Response is always 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
- Request size is 0x3;
- Response size is 0x5;
- Expected Response Code is 0x23;
- Response is "0000" when locked, "0001" when unlocked.
NOTE: the vitas GcAuthMgr driver expects the cart to be locked at this point; and will abort the authentication process if the cart reports that it is already unlocked!
after this it stops being so easy and requires a fair bit of reverse engineering to understand, this is a general overview on how it works, for complete details see LibCmd56 - GENERATE_RANDOM_KEY
this is a used to derive a unique per-session key; this key is then used to encrypt all communications from then forward;- No request data is ever sent for this request
- Request size is 0x3;
- Response size is 0x2b;
- Expected Response Code is 0x2
- Response data: is a 16-bit key id and 0x20 byte random number sequence generated by the cartridge;
then after this, both the cart and the vita, will generate a unique the 'session key' is derived via the following algorithm : first the 16 bit "key id" has 4 valid values, which can be used to pick one of 4 key seeds; these values are 0x8001, 0x8002, 0x8003, and 0x1; the key seeds are as follows: | Key ID | Key Seed | | 0x1 | 7f1fd065dd2f40b3e26579a6390b616d | | 0x8001 | 6f2285ed463a6e57c5f3550ddcc81feb | | 0x8002 | da9608b528825d6d13a7af1446b8ec08 | | 0x8003 | 368b2eb5437a821862a6c95596d8c135 |
The selected keyseed is then decrypted with AES-256-ECB using Keyslot 0x345 and saved in Keyslot 0x21 .. then a AES-128-CMAC is calculated of the the random number provided by the cart using the decrypted keyseed from the previous step; the result of this CMAC is stored in a temporary buffer if the Key ID is 0x8001, 0x8002, or 0x8003, then we are done and the result of this CMAC operation is the session key, however- in the case where the Key ID is 0x1 there is an extra step- where the resulting CMAC hash, is then; decrypted again with AES-128-CBC but this time, using Keyring 0x348 and saved in Keyslot 0x24 and an IV of "8b14c8a1e96f30a7f101a96a3033c55b"; then the resulting plaintext is used as the session key; i have the algorithm implemented in C below : cmd56_sm.c:
void derive_primary_key(uint8_t* session_key_out, uint8_t* cart_random, int key_id) { uint8_t* keyseed; switch (key_id) { case PROTOTYPE_KEY_ID1: keyseed = GCAUTHMGR_0x8001_KEY; break; case PROTOTYPE_KEY_ID2: keyseed = GCAUTHMGR_0x8002_KEY; break; case PROTOTYPE_KEY_ID3: keyseed = GCAUTHMGR_0x8003_KEY; break; case RETAIL_KEY_ID: keyseed = GCAUTHMGR_0x1_KEY; break; } AES_256_ECB_decrypt(BIGMAC_KEY_0x345, keyseed, 0x10); AES_128_CMAC(keyseed, cart_random, 0x20, session_key_out); if (key_id == 0x1) { AES_128_CBC_decrypt(BIGMAC_KEY_0x348, session_key_out, 0x10, GCAUTHMGR_0x1_IV); } }  | 英雄伝説 閃の軌跡Ⅱ showing Key ID: 0x01 | NOTE: despite the code for deriving a key based on other Key ID than 0x1 is still present in the secure kernel which lacks the last 0x348 decrypt step; it won't actually work in practice; the vita kernel's (gcauthmgr.skprx) after FW0.998 or so; will check if(key_id > 0x8001) then fail the authentication if true; - this locks out all Key ID except 0x1* from being used in any retail firmware and as a result 0x8001-0x8003 are in only usable on prototype firmware. the session key is used with AES-128-CBC decrypt using an IV of all 0s likewise i dont think any Key ID besides 0x1 is ever used; all the games i have report KeyID 0x1 you can check this in GcToolKit .. if you see anything else in here you may have a prototype cartridge!
- EXCHANGE_SHARED_RANDOM
this is used to validate that the vita derived the right key; it is done by both the vita and the cart exchanging a random sequence. then the vita checking if decrypt result matches what was sent to it.- Request data: vita includes the received key_id from GENERATE_RANDOM_KEY
and then a random 0x10 bytes sequence (.vita_part); - Request size is 0x15;
- Response size is 0x23;
- Expected Response Code is 0x3;
- Response data: 0x20 byte random data from cart and vita:
top 0x10 is generated by cartridge (.cart_part), and then lower 0x10 is from the same (.vita_part) which the vita sent in request; the entire response is encrypted with the session_key.
the packet response is received by the vita, and the packet is decrypted using the session key; and the lower 0x10 bytes are compared to the random bytes the console sent to the cart; and if they match then the authentication continues; otherwise it fails right here, thus requiring that the cart must have the derived the same session_key. as the vita- as if this is not the case then the .vita_part from the cart will not match what was generated by the vita; an example of this in C is available here: vita_exchange_random.c:
if (response->error_code == GC_AUTH_OK) { decrypt_cbc_zero_iv(&state->session_key, response->data, 0x20);
uint8_t* got_cart_part = response->data + 0x00; uint8_t* got_vita_part = response->data + 0x10;
if (memcmp(got_vita_part + 0x1, state->shared_random.vita_part + 0x1, sizeof(state->shared_random.vita_part)-0x1) == 0) { LOG("(VITA) cart and vita have the same shared_random.vita_part ...\n"); return GC_AUTH_OK; } else { LOG("(VITA) invalid shared_random.vita_part!\n"); return GC_AUTH_ERROR_VERIFY_SHARED_RANDOM_INVALID; } }
- EXCHANGE_SECONDARY_KEY_AND_VERIFY_CHALLENGE
this is used for two purposes, the first; is to exchange a new key, which we will call the "secondary_key", as well as this it is used for the cart to verify that the *vita* generated the correct session key.- Request data: a new random 0x10 byte sequence which will be used as "secondary key" after that,
the full 0x20 byte contents of the shared_random; but with index 0x00 and 0x10 logically OR'd with 0x80; finally the entire section is encrypted using the session_key from earlier, - Request size is 0x33;
- Response size is 0x23;
- Expected Response code is 0x3;
- No response data is ever sent for this request;
on the gamecart, it will then decrypt the data using the session_key; then it will put the secondary_key in memory somewhere, and then will take the shared_random generated from the previous step logical OR bytes at index 0x00 and 0x10 from it with 0x80; then compare the result to what the vita sent to the cart- if these do match, then the cart will set the cart_status to 0x0001 (CART_UNLOCKED) and will now accept read and write commands from here onwards; however if they do not match, then response_code and error_code are both set to 0xF1, and the cart_status remains 0x0000 (CART_LOCKED) thus requiring that the vita also derived the same session_key as the cart. because if it didn't then the shared_random wouldn't match, and in that case; the cart is never unlocked; once again i have written an example of this in C this time from the cartridge side of things: cart_cmd56_validate_secondary_key.c: void handle_secondary_key_and_verify_challenge(gc_cmd56_state* state, cmd56_request* request, cmd56_response* response) { cmd56_response_start(request, response);
// decrypt 0x30 bytes of the request ... decrypt_cbc_zero_iv(&state->session_key, request->data, 0x30); uint8_t* secondary_key_buf = request->data;
// log everything uint8_t* got_secondary_key = secondary_key_buf + 0x00; uint8_t* got_challenge = secondary_key_buf + 0x10;
AES_init_ctx(&state->secondary_key, got_secondary_key);
// calculate challenge bytes ... uint8_t exp_challenge[0x20]; memcpy(exp_challenge, &state->shared_random, sizeof(shared_value));
exp_challenge[0x00] |= 0x80; exp_challenge[0x10] |= 0x80;
if (memcmp(exp_challenge, got_challenge, 0x20) == 0) { LOG("(GC) exp_challenge == got_challenge, so authenticated as real PSVita.\n"); state->lock_status = GC_UNLOCKED; } else { LOG("(GC) exp_challenge != got_challenge so it's not a valid ps vita\n");
state->lock_status = GC_LOCKED; cmd56_response_error(response, 0xF1); } } NOTE: vita will simply check if the error_code result is 0x00,and fail if its not that (like i.e if its 0xF1)
after this request, the vita sends GET_STATUS again; refer to the previous documentation on this method from the start- however this time it expects if the response is 0x0001 (GC_UNLOCKED) and will fail the authentication if it is still locked, this check could be skipped with CFW; however it would be redundant since this would also mean that the vita is unable to read any sectors of the gamecart- so you wont be able to play the game on it,
however moving on from there we have now validated the cart and vita, and the rest of the protocol is about exchanging two per-cart keys; (p18 and p20) which are used to derive the klicensee value for decrypting the game executable. and is encrypted with the "secondary_key" provided from the previous step it is effectively used as a new 'session_key' .. once again this is AES-128-CBC using an IV of all 0x00 bytes.
Security Implications: both "EXCHANGE_SECONDARY_KEY_AND_VERIFY_CHALLENGE" & :EXCHANGE_SHARED_RANDOM" combined, require that; both the vita and the cart generate the same session_key, which in order to know that should also require that the vita knows bbmac 0x345 and 0x348. however, EXCHANGE_SECONDARY_KEY_AND_VERIFY_CHALLENGE is only actually needed if you are trying to read an offical cart someone trying to implement a game cartridge would not need to fully implement this; you can simply always unlock the cart and always return a success code (0x00) regardless of if the vita provides the correct shared_random or not; decrypting the packet using the session key and obtaining the secondary_key is still nessecary though; as that is used to exchange the p18 & p20 keys as well as the CMAC challenge that occurs later- so its not too useful Cryptographic features: the algorithm surprisingly uses only symmetric cryptography, one would think that you would use a public key on the cart; and a private key on the vita; and instead relies on shared secrets; so if the key derivation here is the basis of all the security of this then that means the security is in theory* based around 0x345 and 0x348 keyrings; unless another vulnerability is found in the key derivation algorithm, extracting those keyrings is non-trivial because 0x345 and 0x348 are hardware-sealed keys; that are never directly accessible by the console or exposed to the CPU in any form; if they ever were extracted then this protocol would be broken forever; Sony prevents reusing ciphertext from previous steps in the algorithm, as the first 0x10 bytes is always used as like random input (secondary_key, cart_random) the fact that shared_random response will swap the order around from what received from the console and so on, this is likely done on purpose, as the AES-CBC functions used have a static IV of all 0's, meaning that it is equivilent to AES-ECB on the first 0x10 blocks by swapping the vita_random and cart_random in the response to "EXCHANGE_SHARED_RANDOM" it can ensure that the first AES block is the cart_random; and so then you cannot simply take the encrypted ciphertext from there and re-use it in the "EXCHANGE_SECONDARY_KEY_AND_VERIFY_CHALLENGE" as the first 0x10 block in that one is the "secondary_key0" furthermore, the logical OR with 0x80 effectively completely changes these AES blocks from the first time anyway- so any attempt to reuse ciphertext from a previus step would be foiled anyway; also the "GENERATE_RANDOM_KEY" step is the basis of all the security; everything else that comes afterwards is encrypted using the session key derived from that step, even the the secondary_key that is used later is simply encrypted using the previous session_key from this step, Vulnerabilities: * the check blocking the prototype key_id is if(key_id > 0x8001) when it should be >= this means, that a key_id of exactly 0x8001 is allowed; this allows for a custom cartridge to be able to skip the last decrypt step, allowing you to (via the racoon exploit) extract the decrypt from 0x345 and run the CMAC manually allowing the session key to be determined; * the random number used for the CMAC generation is what is sent via the cart in the "GENERATE_RANDOM_KEY" packet; and is controlled 100% by the cartridge itself; therefore a custom cart can simply provide the same "random number" every time, doing this will result in the same session key being derived every time; this allows you to use key_id 0x1, and use (the racoon exploit) again to extract the final resulting session key ... combining these approaches, allows you to discover a session key without even needing raccoon exploit at all; because the CMAC result is actually stored to a temporary buffer, and not to another bigmac keyring; and on these prototype key_id the CMAC result is the session key this allows the final session key to be extracted with only F00D Code Execution, and no raccoon exploit at all- keep in mind: that this only allows for creating a flash cartridge, but NOT for making a cart dumper, you would still need to dump cartridges on console (doing this also requires f00d code execution btw!) However as cool as all this is, it has also all become completely irrelevant since a few years ago, all hardware sealed keys for the console were extracted, including 0x345 & 0x348; however; was not the case when i started looking into this initially so you can just use them directly- they are: | Keyring | Value | | 0x345 | 74C39CA4EF4F122915C71EDA46C88B55BBAD1F4033D755CEA0563CC341F92E66 | | 0x348 | C026281413FA462CCDEED4BD6D08C37CA6C9322ABD4C40ADE72A0F544F4013AD |
which effectively means the security of vita cartridges are effectively broken forever at this point.
This page is still under construction and not finished, (or more generally, research is still ongoing) i will write more here later; |