From 9547d32fd4c657636eed55b0d938726b5655b773 Mon Sep 17 00:00:00 2001 From: Martins Mozeiko Date: Thu, 26 Oct 2017 00:09:15 -0700 Subject: [PATCH] added DLC support, removed hexkey & dummy work.bin support --- README.md | 18 ++++-- pkg2zip.c | 154 ++++++++++++++++++------------------------------ pkg2zip_utils.c | 29 --------- pkg2zip_utils.h | 1 - 4 files changed, 71 insertions(+), 131 deletions(-) diff --git a/README.md b/README.md index fe93dc3..9c02131 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Utility that decrypts PlayStation Vita pkg file and creates zip package. Optionally saves [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm) license into work.bin file. You must provide license key. +DLC support available when VitaShell will include [this pull request](https://github.com/TheOfficialFloW/VitaShell/pull/310). + # Features * **portable**, written in cross-platform C code, runs on Windows, GNU/Linux, macOS (system dependent functionality is isolated in sys.c file). @@ -12,15 +14,22 @@ Optionally saves [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm) license i * **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. Limitations: -* currently works only for main application pkg files, no DLC. + +* currently no PSM or update pkg files are supported. # Usage -Execute `pkg2zip package.pkg` to create `title [id] [region].zip` file. Title, ID and region is automatically detected from pkg file. +If you have zRIF fake license, then execute: -If you have raw license key (32 hex characters) you can execute `pkg2zip package.pkg hexkey` to try to generate work.bin file (works for most pkg files). + pkg2zip package.pkg zRIF_STRING -If you have working zRIF string, then execute `pkg2zip package.pkg zRIF_string` to create work.bin file from zRIF encoding. +This will create `title [id] [region].zip` file. Title, ID and region is automatically detected from pkg file. It will include work.bin file. + +If you don't have zRIF fake license, but just want to unpack files, then omit last argument: + + pkg2zip package.pkg zRIF_STRING + +Resulting zip file will not include work.bin. # Generating zRIF string @@ -56,7 +65,6 @@ On Windows you can build either with MinGW (get [MinGW-w64](http://www.msys2.org * https://github.com/RikuKH3/unpkg_vita * https://github.com/St4rk/PkgDecrypt -* https://github.com/TheRadziu/PkgDecrypt * https://github.com/weaknespase/PkgDecrypt # License diff --git a/pkg2zip.c b/pkg2zip.c index b0aa598..acbbe5b 100644 --- a/pkg2zip.c +++ b/pkg2zip.c @@ -18,8 +18,6 @@ static const uint8_t pkg_vita_2[] = { 0xe3, 0x1a, 0x70, 0xc9, 0xce, 0x1d, 0xd7, static const uint8_t pkg_vita_3[] = { 0x42, 0x3a, 0xca, 0x3a, 0x2b, 0xd5, 0x64, 0x9f, 0x96, 0x86, 0xab, 0xad, 0x6f, 0xd8, 0x80, 0x1f }; static const uint8_t pkg_vita_4[] = { 0xaf, 0x07, 0xfd, 0x59, 0x65, 0x25, 0x27, 0xba, 0xf1, 0x33, 0x89, 0x66, 0x8b, 0x17, 0xd9, 0xea }; -static const uint8_t rif_header[] = { 0, 1, 0, 1, 0, 1, 0, 2, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01 }; - // http://vitadevwiki.com/vita/System_File_Object_(SFO)_(PSF)#Internal_Structure // https://github.com/TheOfficialFloW/VitaShell/blob/1.74/sfo.h#L29 static void parse_sfo(sys_file f, uint64_t sfo_offset, uint32_t sfo_size, char* title, char* content, char* min_version) @@ -161,10 +159,10 @@ static const char* get_region(const char* id) int main(int argc, char* argv[]) { - printf("pkg2zip v1.3\n"); + printf("pkg2zip v1.4\n"); if (argc < 2 || argc > 3) { - fatal("Usage: %s file.pkg [NoNpDrmKey | zRIF]\n", argv[0]); + fatal("Usage: %s file.pkg [zRIF]\n", argv[0]); } printf("[*] loading...\n"); @@ -202,7 +200,6 @@ int main(int argc, char* argv[]) fatal("ERROR: pkg file is too small\n"); } - uint32_t drm_type = 0; uint32_t content_type = 0; uint32_t sfo_offset = 0; uint32_t sfo_size = 0; @@ -217,11 +214,7 @@ int main(int argc, char* argv[]) uint32_t type = get32be(block + 0); uint32_t size = get32be(block + 4); - if (type == 1) - { - drm_type = get32be(block + 8); - } - else if (type == 2) + if (type == 2) { content_type = get32be(block + 8); } @@ -287,11 +280,14 @@ int main(int argc, char* argv[]) if (dlc) { snprintf(path, sizeof(path), "%s [%.9s] [%s] [DLC].zip", title, id, get_region(id)); + printf("[*] unpacking DLC\n"); } else { snprintf(path, sizeof(path), "%s [%.9s] [%s].zip", title, id, get_region(id)); + printf("[*] unpacking APP\n"); } + printf("[*] creating '%s' archive\n", path); zip z; @@ -304,6 +300,9 @@ int main(int argc, char* argv[]) snprintf(path, sizeof(path), "addcont/%.9s/", id); zip_add_folder(&z, path); + + snprintf(path, sizeof(path), "addcont/%.9s/%s/", id, id2); + zip_add_folder(&z, path); } else { @@ -314,18 +313,26 @@ int main(int argc, char* argv[]) zip_add_folder(&z, path); } + char root[64]; + if (dlc) + { + snprintf(root, sizeof(root), "addcont/%.9s/%s", id, id2); + } + else + { + snprintf(root, sizeof(root), "app/%.9s", id); + } + printf("[*] decrypting...\n"); aes128_key key; aes128_init(&key, main_key); - uint8_t work_sku_flag = (drm_type == 3 || drm_type == 13) ? 3 : 0; - - for (uint32_t item_index=0; item_index= 14 && flags <= 17) || - flags == 19 || flags == 21 || flags == 22) + else { - if (dlc) - { - if (strncmp("sce_sys/package/", name, 16) == 0) - { - continue; - } - snprintf(path, sizeof(path), "addcont/%.9s/%s/%s", id, id2, name); - } - else - { - snprintf(path, sizeof(path), "app/%.9s/%s", id, name); - } + snprintf(path, sizeof(path), "%s/%s", root, name); uint64_t offset = data_offset; @@ -389,21 +382,12 @@ int main(int argc, char* argv[]) uint8_t buffer[1 << 16]; uint32_t size = (uint32_t)min64(data_size, sizeof(buffer)); sys_read(pkg, enc_offset + offset, buffer, size); - aes128_ctr_xor(&key, iv, offset / 16, buffer, size); - if (memcmp("sce_sys/package/temp.bin", name, name_size) == 0) + if (decrypt) { - // process data at beginning of file - if (offset == data_offset) - { - // https://github.com/TheOfficialFloW/NoNpDrm/blob/v1.1/src/main.c#L116 - uint32_t sku_flag = get32be(buffer + 252); - if (sku_flag == 1 || sku_flag == 3) - { - work_sku_flag = 3; - } - } + aes128_ctr_xor(&key, iv, offset / 16, buffer, size); } + zip_write_file(&z, buffer, size); offset += size; data_size -= size; @@ -413,10 +397,9 @@ int main(int argc, char* argv[]) } } - if (!dlc) { printf("[*] creating head.bin\n"); - snprintf(path, sizeof(path), "app/%.9s/sce_sys/package/head.bin", id); + snprintf(path, sizeof(path), "%s/sce_sys/package/head.bin", root); zip_begin_file(&z, path); uint64_t head_size = enc_offset + items_size; @@ -431,9 +414,11 @@ int main(int argc, char* argv[]) head_offset += size; } zip_end_file(&z); + } + { printf("[*] creating tail.bin\n"); - snprintf(path, sizeof(path), "app/%.9s/sce_sys/package/tail.bin", id); + snprintf(path, sizeof(path), "%s/sce_sys/package/tail.bin", root); uint8_t tail[480]; zip_begin_file(&z, path); @@ -442,55 +427,32 @@ int main(int argc, char* argv[]) zip_end_file(&z); } - if (dlc) { - zip_add_folder(&z, "license/"); - zip_add_folder(&z, "license/addcont/"); + printf("[*] creating stat.bin\n"); + snprintf(path, sizeof(path), "%s/sce_sys/package/stat.bin", root); - snprintf(path, sizeof(path), "license/addcont/%.9s/", id); - zip_add_folder(&z, path); - - snprintf(path, sizeof(path), "license/addcont/%.9s/%s/", id, id2); - zip_add_folder(&z, path); - - snprintf(path, sizeof(path), "license/addcont/%.9s/%s/6488b73b912a753a492e2714e9b38bc7.rif", id, id2); - } - else - { - snprintf(path, sizeof(path), "app/%.9s/sce_sys/package/work.bin", id); + uint8_t stat[768] = { 0 }; + zip_begin_file(&z, path); + zip_write_file(&z, stat, sizeof(stat)); + zip_end_file(&z); } if (argc == 3) { - if (strlen(argv[2]) == 32) - { - printf("[*] generating rif file with '%s' key\n", argv[2]); + printf("[*] creating fake rif license\n"); + snprintf(path, sizeof(path), "%s/sce_sys/package/work.bin", root); - memcpy(rif, rif_header, sizeof(rif_header)); - memcpy(rif + 0x10, content, 0x30); - get_hex_bytes16(argv[2], rif + 0x50); - rif[255] = work_sku_flag; - } - else - { - printf("[*] saving zRIF to rif file\n"); - } + zip_begin_file(&z, path); + zip_write_file(&z, rif, sizeof(rif)); + zip_end_file(&z); } - else - { - printf("[*] creating dummy rif file\n"); - - memcpy(rif, rif_header, sizeof(rif_header)); - memcpy(rif + 0x10, id, 0x30); - rif[255] = work_sku_flag; - } - - zip_begin_file(&z, path); - zip_write_file(&z, rif, sizeof(rif)); - zip_end_file(&z); zip_close(&z); - printf("[*] minimum fw version required: %s\n", min_version); + if (!dlc) + { + printf("[*] minimum fw version required: %s\n", min_version); + } + printf("[*] done!\n"); } diff --git a/pkg2zip_utils.c b/pkg2zip_utils.c index ff2a5b9..5e24fae 100644 --- a/pkg2zip_utils.c +++ b/pkg2zip_utils.c @@ -13,32 +13,3 @@ void fatal(const char* msg, ...) exit(EXIT_FAILURE); } - -static int hex2byte(char ch) -{ - if (ch >= '0' && ch <= '9') - { - return ch - '0'; - } - else if (ch >= 'A' && ch <= 'Z') - { - return ch - 'A' + 10; - } - else if (ch >= 'a' && ch <= 'z') - { - return ch - 'a' + 10; - } - else - { - fatal("ERROR: invalid '%c' hex character\n", ch); - } -} - -void get_hex_bytes16(const char* str, uint8_t* bytes) -{ - for (size_t i = 0; i < 16; i++) - { - bytes[i] = (uint8_t)((hex2byte(str[0]) << 4) + hex2byte(str[1])); - str += 2; - } -} \ No newline at end of file diff --git a/pkg2zip_utils.h b/pkg2zip_utils.h index 1fd57c8..dae6677 100644 --- a/pkg2zip_utils.h +++ b/pkg2zip_utils.h @@ -9,7 +9,6 @@ #endif void NORETURN fatal(const char* msg, ...) ; -void get_hex_bytes16(const char* str, uint8_t* bytes); static inline uint32_t min32(uint32_t a, uint32_t b) {