Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 36 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| #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 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 }; | |
| static const uint8_t kirk7_key39[] = { 0xc4, 0x3b, 0xb6, 0xd6, 0x53, 0xee, 0x67, 0x49, 0x3e, 0xa9, 0x5f, 0xbc, 0x0c, 0xed, 0x6f, 0x8a }; | |
| static const uint8_t kirk7_key63[] = { 0x9c, 0x9b, 0x13, 0x72, 0xf8, 0xc6, 0x40, 0xcf, 0x1c, 0x62, 0xf5, 0xd5, 0x92, 0xdd, 0xb5, 0x82 }; | |
| // https://vitadevwiki.com/vita/Keys_NonVita#PSPAMHashKey | |
| static const uint8_t amctl_hashkey_3[] = { 0xe3, 0x50, 0xed, 0x1d, 0x91, 0x0a, 0x1f, 0xd0, 0x29, 0xbb, 0x1c, 0x3e, 0xf3, 0x40, 0x77, 0xfb }; | |
| static const uint8_t amctl_hashkey_4[] = { 0x13, 0x5f, 0xa4, 0x7c, 0xab, 0x39, 0x5b, 0xa4, 0x76, 0xb8, 0xcc, 0xa9, 0x8f, 0x3a, 0x04, 0x45 }; | |
| static const uint8_t amctl_hashkey_5[] = { 0x67, 0x8d, 0x7f, 0xa3, 0x2a, 0x9c, 0xa0, 0xd1, 0x50, 0x8a, 0xd8, 0x38, 0x5e, 0x4b, 0x01, 0x7e }; | |
| // lzrc decompression code from libkirk by tpu | |
| typedef struct { | |
| // input stream | |
| const uint8_t* input; | |
| uint32_t in_ptr; | |
| uint32_t in_len; | |
| // output stream | |
| uint8_t* output; | |
| uint32_t out_ptr; | |
| uint32_t out_len; | |
| // range decode | |
| uint32_t range; | |
| uint32_t code; | |
| uint32_t out_code; | |
| uint8_t lc; | |
| uint8_t bm_literal[8][256]; | |
| uint8_t bm_dist_bits[8][39]; | |
| uint8_t bm_dist[18][8]; | |
| uint8_t bm_match[8][8]; | |
| uint8_t bm_len[8][31]; | |
| } lzrc_decode; | |
| static void rc_init(lzrc_decode* rc, void* out, int out_len, const void* in, int in_len) | |
| { | |
| if (in_len < 5) | |
| { | |
| sys_error("ERROR: internal error - lzrc input underflow! pkg may be corrupted?\n"); | |
| } | |
| rc->input = in; | |
| rc->in_len = in_len; | |
| rc->in_ptr = 5; | |
| rc->output = out; | |
| rc->out_len = out_len; | |
| rc->out_ptr = 0; | |
| rc->range = 0xffffffff; | |
| rc->lc = rc->input[0]; | |
| rc->code = get32be(rc->input + 1); | |
| rc->out_code = 0xffffffff; | |
| memset(rc->bm_literal, 0x80, sizeof(rc->bm_literal)); | |
| memset(rc->bm_dist_bits, 0x80, sizeof(rc->bm_dist_bits)); | |
| memset(rc->bm_dist, 0x80, sizeof(rc->bm_dist)); | |
| memset(rc->bm_match, 0x80, sizeof(rc->bm_match)); | |
| memset(rc->bm_len, 0x80, sizeof(rc->bm_len)); | |
| } | |
| static void normalize(lzrc_decode* rc) | |
| { | |
| if (rc->range < 0x01000000) | |
| { | |
| rc->range <<= 8; | |
| rc->code = (rc->code << 8) + rc->input[rc->in_ptr]; | |
| rc->in_ptr++; | |
| } | |
| } | |
| static int rc_bit(lzrc_decode* rc, uint8_t *prob) | |
| { | |
| uint32_t bound; | |
| normalize(rc); | |
| bound = (rc->range >> 8) * (*prob); | |
| *prob -= *prob >> 3; | |
| if (rc->code < bound) | |
| { | |
| rc->range = bound; | |
| *prob += 31; | |
| return 1; | |
| } | |
| else | |
| { | |
| rc->code -= bound; | |
| rc->range -= bound; | |
| return 0; | |
| } | |
| } | |
| static int rc_bittree(lzrc_decode* rc, uint8_t *probs, int limit) | |
| { | |
| int number = 1; | |
| do | |
| { | |
| number = (number << 1) + rc_bit(rc, probs + number); | |
| } | |
| while (number < limit); | |
| return number; | |
| } | |
| static int rc_number(lzrc_decode* rc, uint8_t *prob, uint32_t n) | |
| { | |
| int number = 1; | |
| if (n > 3) | |
| { | |
| number = (number << 1) + rc_bit(rc, prob + 3); | |
| if (n > 4) | |
| { | |
| number = (number << 1) + rc_bit(rc, prob + 3); | |
| if (n > 5) | |
| { | |
| // direct bits | |
| normalize(rc); | |
| for (uint32_t i = 0; i < n - 5; i++) | |
| { | |
| rc->range >>= 1; | |
| number <<= 1; | |
| if (rc->code < rc->range) | |
| { | |
| number += 1; | |
| } | |
| else | |
| { | |
| rc->code -= rc->range; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (n > 0) | |
| { | |
| number = (number << 1) + rc_bit(rc, prob); | |
| if (n > 1) | |
| { | |
| number = (number << 1) + rc_bit(rc, prob + 1); | |
| if (n > 2) | |
| { | |
| number = (number << 1) + rc_bit(rc, prob + 2); | |
| } | |
| } | |
| } | |
| return number; | |
| } | |
| static int lzrc_decompress(void* out, int out_len, const void* in, int in_len) | |
| { | |
| lzrc_decode rc; | |
| rc_init(&rc, out, out_len, in, in_len); | |
| if (rc.lc & 0x80) | |
| { | |
| // plain text | |
| memcpy(rc.output, rc.input + 5, rc.code); | |
| return rc.code; | |
| } | |
| int rc_state = 0; | |
| uint8_t last_byte = 0; | |
| for (;;) | |
| { | |
| uint32_t match_step = 0; | |
| int bit = rc_bit(&rc, &rc.bm_match[rc_state][match_step]); | |
| if (bit == 0) // literal | |
| { | |
| if (rc_state > 0) | |
| { | |
| rc_state -= 1; | |
| } | |
| int byte = rc_bittree(&rc, &rc.bm_literal[((last_byte >> rc.lc) & 0x07)][0], 0x100); | |
| byte -= 0x100; | |
| if (rc.out_ptr == rc.out_len) | |
| { | |
| sys_error("ERROR: internal error - lzrc output overflow! pkg may be corrupted?\n"); | |
| } | |
| rc.output[rc.out_ptr++] = (uint8_t)byte; | |
| last_byte = (uint8_t)byte; | |
| } | |
| else // match | |
| { | |
| // find bits of match length | |
| uint32_t len_bits = 0; | |
| for (int i = 0; i < 7; i++) | |
| { | |
| match_step += 1; | |
| bit = rc_bit(&rc, &rc.bm_match[rc_state][match_step]); | |
| if (bit == 0) | |
| { | |
| break; | |
| } | |
| len_bits += 1; | |
| } | |
| // find match length | |
| uint32_t match_len; | |
| if (len_bits == 0) | |
| { | |
| match_len = 1; | |
| } | |
| else | |
| { | |
| uint32_t len_state = ((len_bits - 1) << 2) + ((rc.out_ptr << (len_bits - 1)) & 0x03); | |
| match_len = rc_number(&rc, &rc.bm_len[rc_state][len_state], len_bits); | |
| if (match_len == 0xFF) | |
| { | |
| // end of stream | |
| return rc.out_ptr; | |
| } | |
| } | |
| // find number of bits of match distance | |
| uint32_t dist_state = 0; | |
| uint32_t limit = 8; | |
| if (match_len > 2) | |
| { | |
| dist_state += 7; | |
| limit = 44; | |
| } | |
| int dist_bits = rc_bittree(&rc, &rc.bm_dist_bits[len_bits][dist_state], limit); | |
| dist_bits -= limit; | |
| // find match distance | |
| uint32_t match_dist; | |
| if (dist_bits > 0) | |
| { | |
| match_dist = rc_number(&rc, &rc.bm_dist[dist_bits][0], dist_bits); | |
| } | |
| else | |
| { | |
| match_dist = 1; | |
| } | |
| // copy match bytes | |
| if (match_dist > rc.out_ptr) | |
| { | |
| sys_error("ERROR: internal error - lzrc match_dist out of range! pkg may be corrupted?\n"); | |
| } | |
| if (rc.out_ptr + match_len + 1 > rc.out_len) | |
| { | |
| sys_error("ERROR: internal error - lzrc output overflow! pkg may be corrupted?\n"); | |
| } | |
| const uint8_t* match_src = rc.output + rc.out_ptr - match_dist; | |
| for (uint32_t i = 0; i <= match_len; i++) | |
| { | |
| rc.output[rc.out_ptr++] = *match_src++; | |
| } | |
| last_byte = match_src[-1]; | |
| rc_state = 6 + ((rc.out_ptr + 1) & 1); | |
| } | |
| } | |
| } | |
| static void init_psp_decrypt(aes128_key* key, uint8_t* iv, int eboot, const uint8_t* mac, const uint8_t* header, uint32_t offset1, uint32_t offset2) | |
| { | |
| uint8_t tmp[16]; | |
| aes128_init_dec(key, kirk7_key63); | |
| if (eboot) | |
| { | |
| aes128_ecb_decrypt(key, header + offset1, tmp); | |
| } | |
| else | |
| { | |
| memcpy(tmp, header + offset1, 16); | |
| } | |
| aes128_key aes; | |
| aes128_init_dec(&aes, kirk7_key38); | |
| aes128_ecb_decrypt(&aes, tmp, tmp); | |
| for (size_t i = 0; i < 16; i++) | |
| { | |
| iv[i] = mac[i] ^ tmp[i] ^ header[offset2 + i] ^ amctl_hashkey_3[i] ^ amctl_hashkey_5[i]; | |
| } | |
| aes128_init_dec(&aes, kirk7_key39); | |
| aes128_ecb_decrypt(&aes, iv, iv); | |
| for (size_t i = 0; i < 16; i++) | |
| { | |
| iv[i] ^= amctl_hashkey_4[i]; | |
| } | |
| } | |
| static void init_psx_keys(uint8_t* iv, const uint8_t* mac, const uint8_t* header, uint32_t offset1) | |
| { | |
| uint8_t tmp[16]; | |
| memcpy(tmp, header + offset1, 16); | |
| aes128_key aes; | |
| aes128_init_dec(&aes, kirk7_key38); | |
| aes128_ecb_decrypt(&aes, tmp, tmp); | |
| for (size_t i = 0; i < 16; i++) | |
| { | |
| iv[i] = mac[i] ^ tmp[i] ^ amctl_hashkey_3[i]; | |
| } | |
| } | |
| 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) | |
| { | |
| sys_error("ERROR: eboot.pbp file is to short!\n"); | |
| } | |
| uint8_t eboot_header[0x28]; | |
| sys_read(pkg, enc_offset + item_offset, eboot_header, sizeof(eboot_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, item_offset / 16, eboot_header, sizeof(eboot_header)); | |
| if (memcmp(eboot_header, "\x00PBP", 4) != 0) | |
| { | |
| sys_error("ERROR: wrong eboot.pbp header signature!\n"); | |
| } | |
| uint32_t psar_offset = get32le(eboot_header + 0x24); | |
| if (psar_offset + 256 > item_size) | |
| { | |
| sys_error("ERROR: eboot.pbp file is to short!\n"); | |
| } | |
| assert(psar_offset % 16 == 0); | |
| uint8_t psar_header[256]; | |
| sys_read(pkg, enc_offset + item_offset + psar_offset, psar_header, sizeof(psar_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + psar_offset) / 16, psar_header, sizeof(psar_header)); | |
| if (memcmp(psar_header, "NPUMDIMG", 8) != 0) | |
| { | |
| sys_error("ERROR: wrong data.psar header signature!\n"); | |
| } | |
| uint32_t iso_block = get32le(psar_header + 0x0c); | |
| if (iso_block > 16) | |
| { | |
| sys_error("ERROR: unsupported data.psar block size %u, max %u supported!\b", iso_block, 16); | |
| } | |
| uint8_t mac[16]; | |
| aes128_cmac(kirk7_key38, psar_header, 0xc0, mac); | |
| aes128_key psp_key; | |
| uint8_t psp_iv[16]; | |
| init_psp_decrypt(&psp_key, psp_iv, 1, mac, psar_header, 0xc0, 0xa0); | |
| aes128_psp_decrypt(&psp_key, psp_iv, 0, psar_header + 0x40, 0x60); | |
| 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 + iso_block - 1) / iso_block; | |
| uint32_t iso_table = get32le(psar_header + 0x6c); | |
| if (iso_table + block_count * 32 > item_size) | |
| { | |
| sys_error("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_FIXED); | |
| 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; | |
| } | |
| for (uint32_t i = 0; i < block_count; i++) | |
| { | |
| uint64_t table_offset = item_offset + psar_offset + iso_table + 32 * i; | |
| uint8_t table[32]; | |
| sys_read(pkg, enc_offset + table_offset, table, sizeof(table)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, table_offset / 16, table, sizeof(table)); | |
| uint32_t t[8]; | |
| for (size_t k = 0; k < 8; k++) | |
| { | |
| t[k] = get32le(table + k * 4); | |
| } | |
| uint32_t block_offset = t[4] ^ t[2] ^ t[3]; | |
| uint32_t block_size = t[5] ^ t[1] ^ t[2]; | |
| uint32_t block_flags = t[6] ^ t[0] ^ t[3]; | |
| if (psar_offset + block_size > item_size) | |
| { | |
| sys_error("ERROR: iso block size/offset is to large!\n"); | |
| } | |
| uint8_t PKG_ALIGN(16) data[16 * ISO_SECTOR_SIZE]; | |
| uint64_t abs_offset = item_offset + psar_offset + block_offset; | |
| sys_output_progress(enc_offset + abs_offset); | |
| sys_read(pkg, enc_offset + abs_offset, data, block_size); | |
| aes128_ctr_xor(pkg_key, pkg_iv, abs_offset / 16, data, block_size); | |
| if ((block_flags & 4) == 0) | |
| { | |
| aes128_psp_decrypt(&psp_key, psp_iv, block_offset / 16, data, block_size); | |
| } | |
| uint32_t out_size; | |
| if (block_size == iso_block * ISO_SECTOR_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 PKG_ALIGN(16) uncompressed[16 * ISO_SECTOR_SIZE]; | |
| out_size = lzrc_decompress(uncompressed, sizeof(uncompressed), data, block_size); | |
| if (out_size != iso_block * ISO_SECTOR_SIZE) | |
| { | |
| sys_error("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); | |
| } | |
| } | |
| } | |
| 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(); | |
| } | |
| 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) | |
| { | |
| if (item_size < 0x90 + 0xa0) | |
| { | |
| sys_error("ERROR: PSP-KEY.EDAT file is to short!\n"); | |
| } | |
| uint8_t key_header[0xa0]; | |
| sys_read(pkg, enc_offset + item_offset + 0x90, key_header, sizeof(key_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + 0x90) / 16, key_header, sizeof(key_header)); | |
| if (memcmp(key_header, "\x00PGD", 4) != 0) | |
| { | |
| sys_error("ERROR: wrong PSP-KEY.EDAT header signature!\n"); | |
| } | |
| uint32_t key_index = get32le(key_header + 4); | |
| uint32_t drm_type = get32le(key_header + 8); | |
| if (key_index != 1 || drm_type != 1) | |
| { | |
| sys_error("ERROR: unsupported PSP-KEY.EDAT file, key/drm type is wrong!\n"); | |
| } | |
| uint8_t mac[16]; | |
| aes128_cmac(kirk7_key38, key_header, 0x70, mac); | |
| aes128_key psp_key; | |
| uint8_t psp_iv[16]; | |
| init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x10); | |
| aes128_psp_decrypt(&psp_key, psp_iv, 0, key_header + 0x30, 0x30); | |
| uint32_t data_size = get32le(key_header + 0x44); | |
| uint32_t data_offset = get32le(key_header + 0x4c); | |
| if (data_size != 0x10 || data_offset != 0x90) | |
| { | |
| sys_error("ERROR: unsupported PSP-KEY.EDAT file, data/offset is wrong!\n"); | |
| } | |
| init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x30); | |
| aes128_psp_decrypt(&psp_key, psp_iv, 0, key_header + 0x90, 0x10); | |
| out_begin_file(path, 0); | |
| out_write(key_header + 0x90, 0x10); | |
| out_end_file(); | |
| } | |
| void unpack_psp_edat(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) | |
| { | |
| if (item_size < 0x90 + 0xa0) | |
| { | |
| sys_error("ERROR: EDAT file is to short!\n"); | |
| } | |
| uint8_t item_header[90]; | |
| sys_read(pkg, enc_offset + item_offset, item_header, sizeof(item_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, (item_offset) / 16, item_header, sizeof(item_header)); | |
| uint8_t key_header_offset = item_header[0xC]; | |
| uint8_t key_header[0xa0]; | |
| sys_read(pkg, enc_offset + item_offset + key_header_offset, key_header, sizeof(key_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + key_header_offset) / 16, key_header, sizeof(key_header)); | |
| if (memcmp(key_header, "\x00PGD", 4) != 0) | |
| { | |
| sys_error("ERROR: wrong EDAT header signature!\n"); | |
| } | |
| uint32_t key_index = get32le(key_header + 4); | |
| uint32_t drm_type = get32le(key_header + 8); | |
| if (key_index != 1 || drm_type != 1) | |
| { | |
| sys_error("ERROR: unsupported EDAT file, key/drm type is wrong!\n"); | |
| } | |
| uint8_t mac[16]; | |
| aes128_cmac(kirk7_key38, key_header, 0x70, mac); | |
| aes128_key psp_key; | |
| uint8_t psp_iv[16]; | |
| init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x10); | |
| aes128_psp_decrypt(&psp_key, psp_iv, 0, key_header + 0x30, 0x30); | |
| uint32_t data_size = get32le(key_header + 0x44); | |
| uint32_t data_offset = get32le(key_header + 0x4c); | |
| if (data_offset != 0x90) | |
| { | |
| sys_error("ERROR: unsupported EDAT file, data offset is wrong!\n"); | |
| } | |
| init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x30); | |
| uint32_t block_size = 0x10; | |
| uint32_t block_count = ((data_size + (block_size - 1)) / block_size ); | |
| out_begin_file(path, 0); | |
| for (uint32_t i = 0; i < block_count; i++) | |
| { | |
| uint8_t block[0x10]; | |
| uint32_t block_offset = (data_offset + (i * block_size)); | |
| sys_read(pkg, enc_offset + item_offset + key_header_offset + block_offset, block, block_size); | |
| aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + key_header_offset + block_offset) / 16, block, block_size); | |
| aes128_psp_decrypt(&psp_key, psp_iv, i * block_size / 16, block, block_size); | |
| uint32_t out_size = 0x10; | |
| if ( ((i + 1) * block_size) > data_size ) | |
| { | |
| out_size = data_size - (i * block_size); | |
| } | |
| out_write(block, out_size); | |
| } | |
| out_end_file(); | |
| } | |
| void unpack_keys_bin(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) | |
| { | |
| if (item_size < 0x28 + 0x10 + 0x1f0) | |
| { | |
| sys_error("ERROR: EBOOT file is to short!\n"); | |
| } | |
| uint8_t eboot_header[0x28]; | |
| sys_read(pkg, enc_offset + item_offset, eboot_header, sizeof(eboot_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, item_offset / 16, eboot_header, sizeof(eboot_header)); | |
| if (memcmp(eboot_header, "\x00PBP", 4) != 0) | |
| { | |
| sys_error("ERROR: wrong eboot header signature!\n"); | |
| } | |
| uint32_t psar_offset = get32le(eboot_header + 0x24); | |
| if (psar_offset + 16 > item_size) | |
| { | |
| sys_error("ERROR: eboot file is to short!\n"); | |
| } | |
| assert(psar_offset % 16 == 0); | |
| uint8_t psar_header[16]; | |
| sys_read(pkg, enc_offset + item_offset + psar_offset, psar_header, sizeof(psar_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + psar_offset) / 16, psar_header, sizeof(psar_header)); | |
| uint32_t key_header_offset = 0; | |
| if (memcmp(psar_header, "PSTITLE", 7) == 0) | |
| { | |
| key_header_offset = 0x1f0; | |
| } | |
| else if (memcmp(psar_header, "PSISO", 5) == 0) | |
| { | |
| key_header_offset = 0x3f0; | |
| } | |
| else | |
| { | |
| sys_error("ERROR: wrong psar header signature!\n"); | |
| } | |
| uint8_t key_header[0x90]; | |
| sys_read(pkg, enc_offset + item_offset + psar_offset+ 16 + key_header_offset, key_header, sizeof(key_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + psar_offset + 16 + key_header_offset) / 16, key_header, sizeof(key_header)); | |
| if (memcmp(key_header, "\x00PGD", 4) != 0) | |
| { | |
| sys_error("ERROR: wrong key header signature!\n"); | |
| } | |
| uint32_t key_index = get32le(key_header + 4); | |
| uint32_t drm_type = get32le(key_header + 8); | |
| if (key_index != 1 || drm_type != 1) | |
| { | |
| sys_error("ERROR: unsupported EBOOT file, key/drm type is unsupported!\n"); | |
| } | |
| uint8_t mac[16]; | |
| aes128_cmac(kirk7_key38, key_header, 0x70, mac); | |
| uint8_t keys_bin[16]; | |
| init_psx_keys(keys_bin, mac, key_header, 0x70); | |
| out_begin_file(path, 0); | |
| out_write(keys_bin, sizeof(keys_bin)); | |
| out_end_file(); | |
| } | |
| void get_psp_theme_title(char* title, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset) | |
| { | |
| uint8_t item_header[90]; | |
| sys_read(pkg, enc_offset + item_offset, item_header, sizeof(item_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, (item_offset) / 16, item_header, sizeof(item_header)); | |
| uint8_t key_header_offset = item_header[0xC]; | |
| uint8_t key_header[0xa0]; | |
| sys_read(pkg, enc_offset + item_offset + key_header_offset, key_header, sizeof(key_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + key_header_offset) / 16, key_header, sizeof(key_header)); | |
| if (memcmp(key_header, "\x00PGD", 4) != 0) | |
| { | |
| sys_error("ERROR: wrong EDAT header signature!\n"); | |
| } | |
| uint32_t key_index = get32le(key_header + 4); | |
| uint32_t drm_type = get32le(key_header + 8); | |
| if (key_index != 1 || drm_type != 1) | |
| { | |
| sys_error("ERROR: unsupported EDAT file, key/drm type is wrong!\n"); | |
| } | |
| uint8_t mac[16]; | |
| aes128_cmac(kirk7_key38, key_header, 0x70, mac); | |
| aes128_key psp_key; | |
| uint8_t psp_iv[16]; | |
| init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x10); | |
| aes128_psp_decrypt(&psp_key, psp_iv, 0, key_header + 0x30, 0x30); | |
| uint32_t data_offset = get32le(key_header + 0x4c); | |
| uint8_t theme_header[256]; | |
| sys_read(pkg, enc_offset + item_offset + key_header_offset + data_offset, theme_header, sizeof(theme_header)); | |
| aes128_ctr_xor(pkg_key, pkg_iv, (item_offset + key_header_offset + data_offset)/ 16, theme_header, sizeof(theme_header)); | |
| init_psp_decrypt(&psp_key, psp_iv, 0, mac, key_header, 0x70, 0x30); | |
| aes128_psp_decrypt(&psp_key, psp_iv, 0, theme_header, sizeof(theme_header)); | |
| if (memcmp(theme_header, "\x00PTF", 4) != 0) | |
| { | |
| sys_error("ERROR: wrong PTF header signature!\n"); | |
| } | |
| memcpy(title,theme_header+8, 128); | |
| title[128]=0; | |
| } |