unpack PSP pkg files to CSO

This commit is contained in:
Martins Mozeiko
2017-11-09 22:25:40 -08:00
parent 27315027c3
commit b3ce86eb09
9 changed files with 312 additions and 25 deletions
+12 -3
View File
@@ -275,6 +275,7 @@ int main(int argc, char* argv[])
printf("pkg2zip v1.8\n");
int zipped = 1;
int cso = 5;
const char* pkg_arg = NULL;
const char* zrif_arg = NULL;
for (int i = 1; i < argc; i++)
@@ -283,6 +284,14 @@ int main(int argc, char* argv[])
{
zipped = 0;
}
else if (strncmp(argv[i], "-c", 2) == 0)
{
if (argv[i][2] != 0)
{
cso = atoi(argv[i] + 2);
cso = cso > 9 ? 9 : cso < 0 ? 0 : cso;
}
}
else
{
if (pkg_arg != NULL)
@@ -300,7 +309,7 @@ int main(int argc, char* argv[])
if (pkg_arg == NULL)
{
fprintf(stderr, "ERROR: no pkg file specified\n");
fatal("Usage: %s [-x] file.pkg [zRIF]\n", argv[0]);
fatal("Usage: %s [-x] [-c[N]] file.pkg [zRIF]\n", argv[0]);
}
printf("[*] loading...\n");
@@ -706,8 +715,8 @@ int main(int argc, char* argv[])
{
if (strcmp("USRDIR/CONTENT/EBOOT.PBP", name) == 0)
{
snprintf(path, sizeof(path), "pspemu/ISO/%s [%.9s].iso", title, id);
unpack_psp_eboot(path, item_key, iv, pkg, enc_offset, data_offset, data_size);
snprintf(path, sizeof(path), "pspemu/ISO/%s [%.9s].%s", title, id, cso ? "cso" : "iso");
unpack_psp_eboot(path, item_key, iv, pkg, enc_offset, data_offset, data_size, cso);
continue;
}
else if (strcmp("USRDIR/CONTENT/PSP-KEY.EDAT", name) == 0)
+67
View File
@@ -258,3 +258,70 @@ uint32_t crc32_done(crc32_ctx* ctx)
return ~ctx->crc[0];
}
static uint32_t crc32_gfmulv(const uint32_t* m, uint32_t v)
{
uint32_t r = 0;
while (v)
{
if (v & 1)
{
r ^= *m;
}
v >>= 1;
m++;
}
return r;
}
static void crc32_gfsq(const uint32_t* m, uint32_t* r)
{
for (size_t i = 0; i < 32; i++)
{
r[i] = crc32_gfmulv(m, m[i]);
}
}
uint32_t crc32_combine(uint32_t a, uint32_t b, uint32_t blen)
{
uint32_t m1[32];
uint32_t m2[32];
m1[0] = 0xedb88320;
for (uint32_t i = 1; i < 32; i++)
{
m1[i] = 1 << (i - 1);
}
crc32_gfsq(m1, m2);
crc32_gfsq(m2, m1);
for (;;)
{
crc32_gfsq(m1, m2);
if (blen & 1)
{
a = crc32_gfmulv(m2, a);
}
blen >>= 1;
if (blen == 0)
{
break;
}
crc32_gfsq(m2, m1);
if (blen & 1)
{
a = crc32_gfmulv(m1, a);
}
blen >>= 1;
if (blen == 0)
{
break;
}
}
return a ^ b;
}
+5
View File
@@ -9,3 +9,8 @@ typedef struct {
void crc32_init(crc32_ctx* ctx);
void crc32_update(crc32_ctx* ctx, const void* buffer, size_t size);
uint32_t crc32_done(crc32_ctx* ctx);
// returns crc32(x||y)
// where a=crc32(x) and b=crc32(y)
// crc32(x||y) = crc32(x||z) ^ crc32(y), where z=00...00 (same length as y)
uint32_t crc32_combine(uint32_t a, uint32_t b, uint32_t blen);
+45 -2
View File
@@ -36,16 +36,17 @@ void out_add_folder(const char* path)
}
}
void out_begin_file(const char* name, int compress)
uint64_t out_begin_file(const char* name, int compress)
{
if (out_zipped)
{
zip_begin_file(&out_zip, name, compress);
return zip_begin_file(&out_zip, name, compress);
}
else
{
out_file = sys_create(name);
out_file_offset = 0;
return 0;
}
}
@@ -73,3 +74,45 @@ void out_write(const void* buffer, uint32_t size)
out_file_offset += size;
}
}
void out_write_at(uint64_t offset, const void* buffer, uint32_t size)
{
if (out_zipped)
{
zip_write_file_at(&out_zip, offset, buffer, size);
}
else
{
sys_write(out_file, offset, buffer, size);
}
}
void out_set_offset(uint64_t offset)
{
if (out_zipped)
{
zip_set_offset(&out_zip, offset);
}
else
{
out_file_offset = offset;
}
}
uint32_t out_zip_get_crc32(void)
{
if (out_zipped)
{
return zip_get_crc32(&out_zip);
}
return 0;
}
void out_zip_set_crc32(uint32_t crc)
{
if (out_zipped)
{
zip_set_crc32(&out_zip, crc);
}
}
+7 -1
View File
@@ -5,6 +5,12 @@
void out_begin(const char* name, int zipped);
void out_end(void);
void out_add_folder(const char* path);
void out_begin_file(const char* name, int compress);
uint64_t out_begin_file(const char* name, int compress);
void out_end_file(void);
void out_write(const void* buffer, uint32_t size);
// hacky solution to be able to write cso header after the data is written
void out_write_at(uint64_t offset, const void* buffer, uint32_t size);
void out_set_offset(uint64_t offset);
uint32_t out_zip_get_crc32(void);
void out_zip_set_crc32(uint32_t crc);
+131 -15
View File
@@ -1,12 +1,15 @@
#include "pkg2zip_psp.h"
#include "pkg2zip_out.h"
#include "pkg2zip_crc32.h"
#include "pkg2zip_utils.h"
#include "miniz_tdef.h"
#include <assert.h>
#include <string.h>
#define PSP_ISO_BLOCK_SIZE 16
#define PSP_ISO_SECTOR_SIZE 2048
#define ISO_SECTOR_SIZE 2048
#define CSO_HEADER_SIZE 24
// https://vitadevwiki.com/vita/Keys_NonVita#PSPAESKirk4.2F7
static const uint8_t kirk7_key38[] = { 0x12, 0x46, 0x8d, 0x7e, 0x1c, 0x42, 0x20, 0x9b, 0xba, 0x54, 0x26, 0x83, 0x5e, 0xb0, 0x33, 0x03 };
@@ -308,7 +311,7 @@ static void init_psp_decrypt(aes128_key* key, uint8_t* iv, int eboot, const uint
}
}
void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size)
void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size, int cso)
{
if (item_size < 0x28)
{
@@ -341,9 +344,9 @@ void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t
}
uint32_t iso_block = get32le(psar_header + 0x0c);
if (iso_block != PSP_ISO_BLOCK_SIZE)
if (iso_block > 16)
{
fatal("ERROR: unsupported data.psar block size %u, only %u supported!", iso_block, PSP_ISO_BLOCK_SIZE);
fatal("ERROR: unsupported data.psar block size %u, max %u supported!\b", iso_block, 16);
}
uint8_t mac[16];
@@ -357,16 +360,37 @@ void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t
uint32_t iso_start = get32le(psar_header + 0x54);
uint32_t iso_end = get32le(psar_header + 0x64);
uint32_t iso_total = iso_end - iso_start - 1;
uint32_t block_count = (iso_total + PSP_ISO_BLOCK_SIZE - 1) / PSP_ISO_BLOCK_SIZE;
uint32_t block_count = (iso_total + iso_block - 1) / iso_block;
uint32_t iso_table = get32le(psar_header + 0x6c);
if (iso_table + block_count * 32 > item_size)
{
fatal("ERROR: offset table in data.psar file is too large!");
fatal("ERROR: offset table in data.psar file is too large!\n");
}
mz_uint cso_compress_flags = 0;
uint32_t cso_index = 0;
uint32_t cso_offset = 0;
uint64_t cso_size = 0;
uint32_t* cso_block = NULL;
uint32_t initial_size = 0;
uint64_t file_offset = out_begin_file(path, !cso);
if (cso)
{
cso_size = block_count * iso_block * ISO_SECTOR_SIZE;
cso_compress_flags = tdefl_create_comp_flags_from_zip_params(cso, -MZ_DEFAULT_WINDOW_BITS, MZ_DEFAULT_STRATEGY);
uint32_t cso_block_count = (uint32_t)(1 + (cso_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE);
cso_block = sys_realloc(NULL, cso_block_count * sizeof(uint32_t));
initial_size = CSO_HEADER_SIZE + cso_block_count * sizeof(uint32_t);
out_set_offset(file_offset + initial_size);
cso_offset = initial_size;
}
out_begin_file(path, 1);
for (uint32_t i = 0; i < block_count; i++)
{
uint64_t table_offset = item_offset + psar_offset + iso_table + 32 * i;
@@ -390,7 +414,7 @@ void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t
fatal("ERROR: iso block size/offset is to large!");
}
uint8_t data[PSP_ISO_BLOCK_SIZE * PSP_ISO_SECTOR_SIZE];
uint8_t data[16 * ISO_SECTOR_SIZE];
uint64_t abs_offset = item_offset + psar_offset + block_offset;
sys_read(pkg, enc_offset + abs_offset, data, block_size);
@@ -402,22 +426,114 @@ void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t
}
uint32_t out_size;
if (block_size == sizeof(data))
if (block_size == iso_block * ISO_SECTOR_SIZE)
{
out_write(data, (uint32_t)block_size);
if (cso)
{
for (size_t n = 0; n < iso_block * ISO_SECTOR_SIZE; n += ISO_SECTOR_SIZE)
{
cso_block[cso_index] = cso_offset;
uint8_t PKG_ALIGN(16) output[ISO_SECTOR_SIZE];
size_t insize = ISO_SECTOR_SIZE;
size_t outsize = sizeof(output);
tdefl_compressor c;
tdefl_init(&c, cso_compress_flags);
tdefl_status st = tdefl_compress(&c, data + n, &insize, output, &outsize, TDEFL_FINISH);
if (st == TDEFL_STATUS_DONE)
{
out_write(output, (uint32_t)outsize);
cso_offset += (uint32_t)outsize;
}
else
{
cso_block[cso_index] |= 0x80000000;
out_write(data + n, ISO_SECTOR_SIZE);
cso_offset += ISO_SECTOR_SIZE;
}
cso_index++;
}
}
else
{
out_write(data, (uint32_t)block_size);
}
}
else
{
uint8_t uncompressed[PSP_ISO_BLOCK_SIZE * PSP_ISO_SECTOR_SIZE];
uint8_t PKG_ALIGN(16) uncompressed[16 * ISO_SECTOR_SIZE];
out_size = lzrc_decompress(uncompressed, sizeof(uncompressed), data, block_size);
if (out_size != sizeof(uncompressed))
if (out_size != iso_block * ISO_SECTOR_SIZE)
{
fatal("ERROR: internal error - lzrc decompression failed! pkg may be corrupted?");
fatal("ERROR: internal error - lzrc decompression failed! pkg may be corrupted?\n");
}
if (cso)
{
for (size_t n = 0; n < iso_block * ISO_SECTOR_SIZE; n += ISO_SECTOR_SIZE)
{
cso_block[cso_index] = cso_offset;
uint8_t output[ISO_SECTOR_SIZE];
size_t insize = ISO_SECTOR_SIZE;
size_t outsize = sizeof(output);
tdefl_compressor c;
tdefl_init(&c, cso_compress_flags);
tdefl_status st = tdefl_compress(&c, uncompressed + n, &insize, output, &outsize, TDEFL_FINISH);
if (st == TDEFL_STATUS_DONE)
{
out_write(output, (uint32_t)outsize);
cso_offset += (uint32_t)outsize;
}
else
{
cso_block[cso_index] |= 0x80000000;
out_write(uncompressed + n, ISO_SECTOR_SIZE);
cso_offset += ISO_SECTOR_SIZE;
}
cso_index++;
}
}
else
{
out_write(uncompressed, (uint32_t)out_size);
}
out_write(uncompressed, (uint32_t)out_size);
}
}
if (cso)
{
cso_block[cso_index++] = cso_offset;
uint8_t cso_header[CSO_HEADER_SIZE] = { 0x43, 0x49, 0x53, 0x4f };
// header size
set32le(cso_header + 4, sizeof(cso_header));
// original size
set64le(cso_header + 8, cso_size);
// block size
set32le(cso_header + 16, ISO_SECTOR_SIZE);
// version
cso_header[20] = 1;
out_write_at(file_offset, cso_header, sizeof(cso_header));
out_write_at(file_offset + sizeof(cso_header), cso_block, cso_index * sizeof(uint32_t));
crc32_ctx cheader;
crc32_init(&cheader);
crc32_update(&cheader, cso_header, sizeof(cso_header));
crc32_update(&cheader, cso_block, cso_index * sizeof(uint32_t));
uint32_t header_crc32 = crc32_done(&cheader);
uint32_t data_crc32 = out_zip_get_crc32();
uint32_t data_len = (uint32_t)(cso_offset - initial_size);
uint32_t crc32 = crc32_combine(header_crc32, data_crc32, data_len);
out_zip_set_crc32(crc32);
sys_realloc(cso_block, 0);
}
out_end_file();
}
+1 -1
View File
@@ -1,5 +1,5 @@
#include "pkg2zip_aes.h"
#include "pkg2zip_sys.h"
void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size);
void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size, int cso);
void unpack_psp_key(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size);
+36 -2
View File
@@ -108,7 +108,7 @@ void zip_add_folder(zip* z, const char* name)
z->total += 1;
}
void zip_begin_file(zip* z, const char* name, int compress)
uint64_t zip_begin_file(zip* z, const char* name, int compress)
{
size_t name_length = strlen(name);
if (name_length > ZIP_MAX_FILENAME)
@@ -124,6 +124,7 @@ void zip_begin_file(zip* z, const char* name, int compress)
z->current = f;
crc32_init(&z->crc32);
z->crc32_set = 0;
uint8_t header[ZIP_LOCAL_HEADER_SIZE] = { 0x50, 0x4b, 0x03, 0x04 };
// version needed to extract
@@ -150,6 +151,8 @@ void zip_begin_file(zip* z, const char* name, int compress)
int flags = tdefl_create_comp_flags_from_zip_params(MZ_BEST_SPEED, -MZ_DEFAULT_WINDOW_BITS, MZ_DEFAULT_STRATEGY);
tdefl_init(&z->tdefl, flags);
}
return z->total - f->offset;
}
void zip_write_file(zip* z, const void* data, uint32_t size)
@@ -211,7 +214,10 @@ void zip_end_file(zip* z)
}
}
z->current->crc32 = crc32_done(&z->crc32);
if (!z->crc32_set)
{
z->current->crc32 = crc32_done(&z->crc32);
}
if (z->current->size != 0)
{
@@ -400,3 +406,31 @@ void zip_close(zip* z)
sys_realloc(z->files, 0);
}
void zip_write_file_at(zip* z, uint64_t offset, const void* data, uint32_t size)
{
if (z->current->compress)
{
fatal("ERROR: cannot write at specific offset for compressed files\n");
}
sys_write(z->file, z->current->offset + offset, data, size);
z->current->size += size;
z->current->compressed += size;
}
void zip_set_offset(zip* z, uint64_t offset)
{
z->total = z->current->offset + offset;
}
void zip_set_crc32(zip* z, uint32_t crc)
{
z->current->crc32 = crc;
z->crc32_set = 1;
}
uint32_t zip_get_crc32(zip* z)
{
return crc32_done(&z->crc32);
}
+8 -1
View File
@@ -20,6 +20,7 @@ typedef struct {
uint16_t date;
tdefl_compressor tdefl;
crc32_ctx crc32;
int crc32_set;
uint32_t allocated; // bytes
zip_file* files;
zip_file* current;
@@ -27,7 +28,13 @@ typedef struct {
void zip_create(zip* z, const char* name);
void zip_add_folder(zip* z, const char* name);
void zip_begin_file(zip* z, const char* name, int compress);
uint64_t zip_begin_file(zip* z, const char* name, int compress);
void zip_write_file(zip* z, const void* data, uint32_t size);
void zip_end_file(zip* z);
void zip_close(zip* z);
// hacky solution to be able to write cso header after the data is written
void zip_write_file_at(zip* z, uint64_t offset, const void* data, uint32_t size);
void zip_set_offset(zip* z, uint64_t offset);
void zip_set_crc32(zip* z, uint32_t crc);
uint32_t zip_get_crc32(zip* z);