PSM pkg unpacking for use with NoPsmDrm

This commit is contained in:
Martins Mozeiko
2017-10-31 00:28:00 -07:00
parent 68e7dc8051
commit 827f3822d2
4 changed files with 128 additions and 36 deletions
+6 -4
View File
@@ -2,14 +2,15 @@
[![Travis CI Build Status][img_travis]][travis] [![AppVeyor Build Status][img_appveyor]][appveyor] [![Downloads][img_downloads]][downloads] [![Release][img_latest]][latest] [![License][img_license]][license]
Utility that decrypts PlayStation Vita pkg file and creates zip package. Supports also PSX pkg files for use with [Adrenaline](https://github.com/TheOfficialFloW/Adrenaline).
Utility that decrypts PlayStation Vita pkg file and creates zip package. Supported pkg files - main application, DLC, patch and PSM files. Supports also PSX files for use with [Adrenaline](https://github.com/TheOfficialFloW/Adrenaline).
Optionally saves [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm) license into work.bin file. You must provide license key.
Optionally writes [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm) or [NoPsmDrm](https://github.com/frangarcj/NoPsmDrm) fake license file from zRIF string. You must provide license key.
# Requirements
* [Henkaku](https://henkaku.xyz/) / [Enso](https://enso.henkaku.xyz/)
* [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm)
* [NoPsmDrm](https://github.com/frangarcj/NoPsmDrm) for PSM titles
* to use DLC pkg files, [VitaShell](https://github.com/TheOfficialFloW/VitaShell) **v1.76** or newer required
* [Adrenaline](https://github.com/TheOfficialFloW/Adrenaline) for PSX titles
@@ -19,12 +20,13 @@ Optionally saves [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm) license i
* **small**, has no external library dependencies and uses very minimal dynamic memory allocations.
* **fast**, uses AESNI hardware accelerated AES decryption if supported by CPU (requires [AESNI](https://en.wikipedia.org/wiki/AES_instruction_set) and [SSSE3](https://en.wikipedia.org/wiki/SSSE3) instructions).
* **simple**, creates zip package with same folder structure that Vita expects (just drag & drop all file from zip archive to ux0:). Zip file is created directly from pkg without any intermediate temporary files.
* **DLC** and **PATCH** pkg unpacking.
* **DLC**, **PATCH** and **PSM** pkg unpacking.
* **PSX** pkg unpacking.
Limitations:
* currently no PSM, PSP or PSP Mini pkg files are supported.
* currently no PSP or PSP Mini pkg files are supported.
* currently no actual title name is extracted for PSM pkg files.
# Usage
+116 -26
View File
@@ -236,22 +236,25 @@ static void find_psp_sfo(const aes128_key* key, const aes128_key* ps3_key, const
static const char* get_region(const char* id)
{
if (memcmp(id, "PCSE", 4) == 0 || memcmp(id, "PCSA", 4) == 0)
if (memcmp(id, "PCSE", 4) == 0 || memcmp(id, "PCSA", 4) == 0 ||
memcmp(id, "NPNA", 4) == 0)
{
return "USA";
}
else if (memcmp(id, "PCSF", 4) == 0 || memcmp(id, "PCSB", 4) == 0)
else if (memcmp(id, "PCSF", 4) == 0 || memcmp(id, "PCSB", 4) == 0 ||
memcmp(id, "NPOA", 4) == 0)
{
return "EUR";
}
else if (memcmp(id, "PCSC", 4) == 0 || memcmp(id, "VCJS", 4) == 0 ||
memcmp(id, "PCSG", 4) == 0 || memcmp(id, "VLJS", 4) == 0 ||
memcmp(id, "VLJM", 4) == 0)
memcmp(id, "VLJM", 4) == 0 || memcmp(id, "NPPA", 4) == 0)
{
return "JPN";
}
else if (memcmp(id, "VCAS", 4) == 0 || memcmp(id, "PCSH", 4) == 0 ||
memcmp(id, "VLAS", 4) == 0 || memcmp(id, "PCSD", 4) == 0)
memcmp(id, "VLAS", 4) == 0 || memcmp(id, "PCSD", 4) == 0 ||
memcmp(id, "NPQA", 4) == 0)
{
return "ASA";
}
@@ -434,9 +437,10 @@ int main(int argc, char* argv[])
meta_offset += 2 * sizeof(uint32_t) + size;
}
int psx = content_type == 6;
int psp = content_type == 7 || content_type == 0xf; // PSX, PSP, PSPMini
int dlc = content_type == 0x16; // APP
int psx = content_type == 6; // PSX
int psp = content_type == 7 || content_type == 0xf; // PSP, PSPMini
int dlc = content_type == 0x16; // Vita APP
int psm = content_type == 0x18; // Vita PSM
aes128_key ps3_key;
uint8_t main_key[16];
@@ -475,23 +479,36 @@ int main(int argc, char* argv[])
const char* id = content + 7;
const char* id2 = id + 13;
// https://github.com/TheOfficialFloW/NoNpDrm/blob/v1.1/src/main.c#L42
uint8_t rif[512];
// first 512 - for vita games - https://github.com/TheOfficialFloW/NoNpDrm/blob/v1.1/src/main.c#L42
// 1024 is used for PSM
uint8_t rif[1024];
uint32_t rif_size = 0;
if (psp || psx)
{
find_psp_sfo(&key, &ps3_key, iv, pkg, pkg_size, enc_offset, items_offset, item_count, title);
id = (char*)pkg_header + 0x37;
}
else
{
parse_sfo(pkg, sfo_offset, sfo_size, &patch, title, content, min_version, pkg_version);
if (psm)
{
memcpy(content, pkg_header + 0x30, 0x30);
rif_size = 1024;
}
else
{
parse_sfo(pkg, sfo_offset, sfo_size, &patch, title, content, min_version, pkg_version);
rif_size = 512;
}
if (!patch && zrif_arg != NULL)
{
zrif_decode(zrif_arg, rif);
if (strncmp((char*)rif + 0x10, content, 0x30) != 0)
zrif_decode(zrif_arg, rif, rif_size);
const char* rif_contentid = (char*)rif + (psm ? 0x50 : 0x10);
if (strncmp(rif_contentid, content, 0x30) != 0)
{
fatal("ERROR: zRIF content id '%s' doesn't match pkg '%s'\n", rif + 0x10, content);
fatal("ERROR: zRIF content id '%s' doesn't match pkg '%s'\n", rif_contentid, content);
}
}
}
@@ -501,14 +518,12 @@ int main(int argc, char* argv[])
char root[1024];
if (psp)
{
id = (char*)pkg_header + 0x37;
const char* type_str = content_type == 7 ? "PSP" : "PSPMini";
snprintf(root, sizeof(root), "%s [%.9s] [%s]%s", title, id, type_str, ext);
printf("[*] unpacking %s\n", type_str);
}
else if (psx)
{
id = (char*)pkg_header + 0x37;
snprintf(root, sizeof(root), "%s [%.9s] [PSX]%s", title, id, ext);
printf("[*] unpacking PSX\n");
}
@@ -522,6 +537,11 @@ int main(int argc, char* argv[])
snprintf(root, sizeof(root), "%s [%.9s] [%s] [PATCH] [v%s]%s", title, id, get_region(id), pkg_version, ext);
printf("[*] unpacking PATCH\n");
}
else if (psm)
{
snprintf(root, sizeof(root), "%.9s [%s]%s", id, get_region(id), ext);
printf("[*] unpacking PSM\n");
}
else
{
snprintf(root, sizeof(root), "%s [%.9s] [%s]%s", title, id, get_region(id), ext);
@@ -583,6 +603,16 @@ int main(int argc, char* argv[])
sys_vstrncat(root, sizeof(root), "/");
}
else if (psm)
{
sys_vstrncat(root, sizeof(root), "psm");
out_add_folder(root);
sys_vstrncat(root, sizeof(root), "/%.9s", id);
out_add_folder(root);
sys_vstrncat(root, sizeof(root), "/");
}
else
{
sys_vstrncat(root, sizeof(root), "app");
@@ -644,7 +674,17 @@ int main(int argc, char* argv[])
if (flags == 4 || flags == 18)
{
if (!psx)
if (psm)
{
// skip "content/" prefix
char* slash = strchr(name, '/');
if (slash != NULL)
{
snprintf(path, sizeof(path), "%sRO/%s/", root, name + 8);
out_add_folder(path);
}
}
else if (!psx)
{
snprintf(path, sizeof(path), "%s%s/", root, name);
out_add_folder(path);
@@ -674,6 +714,11 @@ int main(int argc, char* argv[])
continue;
}
}
else if (psm)
{
// skip "content/" prefix
snprintf(path, sizeof(path), "%sRO/%s", root, name + 8);
}
else
{
snprintf(path, sizeof(path), "%s%s", root, name);
@@ -702,9 +747,9 @@ int main(int argc, char* argv[])
}
}
if (!psp && !psx)
if (!psp && !psx && !psm)
{
printf("[*] creating head.bin\n");
printf("[*] creating sce_sys/package/head.bin\n");
snprintf(path, sizeof(path), "%ssce_sys/package/head.bin", root);
out_begin_file(path);
@@ -722,9 +767,9 @@ int main(int argc, char* argv[])
out_end_file();
}
if (!psp && !psx)
if (!psp && !psx && !psm)
{
printf("[*] creating tail.bin\n");
printf("[*] creating sce_sys/package/tail.bin\n");
snprintf(path, sizeof(path), "%ssce_sys/package/tail.bin", root);
out_begin_file(path);
@@ -740,9 +785,9 @@ int main(int argc, char* argv[])
out_end_file();
}
if (!psp && !psx)
if (!psp && !psx && !psm)
{
printf("[*] creating stat.bin\n");
printf("[*] creating sce_sys/package/stat.bin\n");
snprintf(path, sizeof(path), "%ssce_sys/package/stat.bin", root);
uint8_t stat[768] = { 0 };
@@ -753,17 +798,62 @@ int main(int argc, char* argv[])
if (!psp && !psx && !patch && zrif_arg != NULL)
{
printf("[*] creating work.bin\n");
snprintf(path, sizeof(path), "%ssce_sys/package/work.bin", root);
if (psm)
{
printf("[*] creating RO/License\n");
snprintf(path, sizeof(path), "%sRO/License", root);
out_add_folder(path);
printf("[*] creating RO/License/FAKE.rif\n");
snprintf(path, sizeof(path), "%sRO/License/FAKE.rif", root);
}
else
{
printf("[*] creating sce_sys/package/work.bin\n");
snprintf(path, sizeof(path), "%ssce_sys/package/work.bin", root);
}
out_begin_file(path);
out_write(rif, sizeof(rif));
out_write(rif, rif_size);
out_end_file();
}
if (psm)
{
printf("[*] creating RW\n");
snprintf(path, sizeof(path), "%sRW", root);
out_add_folder(path);
printf("[*] creating RW/Documents\n");
snprintf(path, sizeof(path), "%sRW/Documents", root);
out_add_folder(path);
printf("[*] creating RW/Temp\n");
snprintf(path, sizeof(path), "%sRW/Temp", root);
out_add_folder(path);
printf("[*] creating RW/System\n");
snprintf(path, sizeof(path), "%sRW/System", root);
out_add_folder(path);
printf("[*] creating RW/System/content_id\n");
snprintf(path, sizeof(path), "%sRW/System/content_id", root);
out_begin_file(path);
out_write(pkg_header + 0x30, 0x30);
out_end_file();
printf("[*] creating RW/System/pm.dat\n");
snprintf(path, sizeof(path), "%sRW/System/pm.dat", root);
uint8_t pm[1 << 16] = { 0 };
out_begin_file(path);
out_write(pm, sizeof(pm));
out_end_file();
}
out_end();
if (!psp && !psx && !dlc)
if (!psp && !psx && !dlc && !psm)
{
printf("[*] minimum fw version required: %s\n", min_version);
}
+5 -5
View File
@@ -171,17 +171,17 @@ static uint32_t zlib_inflate(const uint8_t* in, uint32_t inlen, uint8_t* out, ui
return dlen;
}
void zrif_decode(const char* str, uint8_t* rif)
void zrif_decode(const char* str, uint8_t* rif, uint32_t rif_size)
{
uint8_t raw[512];
uint8_t raw[1024];
uint32_t len = base64_decode(str, raw);
uint8_t out[512 + sizeof(zrif_dict)];
uint8_t out[sizeof(zrif_dict) + 1024];
len = zlib_inflate(raw, len, out, sizeof(out));
if (len != 512)
if (len != rif_size)
{
fatal("ERROR: wrong size of zRIF, is it corrupted?\n");
}
memcpy(rif, out, 512);
memcpy(rif, out, rif_size);
}
+1 -1
View File
@@ -2,4 +2,4 @@
#include <stdint.h>
void zrif_decode(const char* str, uint8_t* rif);
void zrif_decode(const char* str, uint8_t* rif, uint32_t rif_size);