#include #include #include #include #include "aes.h" #include "md5.c" #ifdef MAX_PATH #ifndef PATH_MAX #define PATH_MAX MAX_PATH #endif #else #ifndef PATH_MAX #define PATH_MAX (0x7FFF) #endif #endif #define DEBUG 1 #define PSSE_BLOCK_SIZE (0x8000) #define PSSE_SIG_BLOCK_SIZE (0x80000) #define PSSE_SIG_SIZE (0x400) #define AES_KEY_SIZE (0x10) #define AES_IV_SIZE (0x10) const uint8_t header_iv[AES_IV_SIZE] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; // IV for the encrypted PSSE Header const uint8_t header_key[AES_KEY_SIZE] = {0x4E, 0x29, 0x8B, 0x40, 0xF5, 0x31, 0xF4, 0x69, 0xD2, 0x1F, 0x75, 0xB1, 0x33, 0xC3, 0x07, 0xBE}; // Key used to decrypt the encrypted PSSE Header const uint8_t runtime_title_key[AES_KEY_SIZE] = {0xA8, 0x69, 0x3C, 0x4D, 0xF0, 0xAE, 0xED, 0xBC, 0x9A, 0xBF, 0xD8, 0x21, 0x36, 0x92, 0x91, 0x2D}; // Header used to decrypt runtime libaries, eg. Sce.PlayStation.Core.dll. const uint8_t header_key_psmdev[AES_KEY_SIZE] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; // Key used to decrypt the encrypted PSSE Header in PSM dev, const char* runtime_content_id = "IP9100-NPXS10074_00-0000000000000000"; static char title_content_id[0x30]; static uint8_t title_iv[AES_IV_SIZE]; static uint8_t title_key[AES_KEY_SIZE]; // psse header typedef struct psse_header{ char magic[0x4]; uint32_t version; uint64_t file_size; uint32_t psse_type; char content_id[0x2C]; uint8_t md5_hash[0x10]; uint8_t file_name[0x20]; uint8_t file_iv[0x10]; uint8_t unk[0x600]; } psse_header; // rif header typedef struct ScePsmDrmLicense { char magic[0x8]; uint32_t unk1; uint32_t unk2; uint64_t account_id; uint32_t unk3; uint32_t unk4; uint64_t start_time; uint64_t expiration_time; uint8_t activation_checksum[0x20]; char content_id[0x30]; uint8_t unk5[0x80]; uint8_t unk6[0x20]; uint8_t key[0x10]; uint8_t signature[0x1D0]; uint8_t rsa_signature[0x100]; } ScePsmDrmLicense; // psse_block_ref struct typedef struct psse_block_ref{ uint8_t block_data[PSSE_BLOCK_SIZE]; size_t block_size; } psse_block_ref; typedef struct decrypted_file{ uint8_t* data; size_t file_size; } decrypted_file; // Debug print buffer contents void print_buffer(char* buffer_title, uint8_t* buffer, size_t buffer_sz){ printf("[*] %s: ", buffer_title); for(int i = 0; i < buffer_sz; i++) { printf("%02X", buffer[i]); } printf("\n"); } // Convert a path to one for whatever OS your using. void fix_paths(char* path){ size_t sz = strlen(path); for(int i = 0; i < sz; i++){ #ifdef _WIN32 if(path[i] == '/') #else if(path[i] == '\\') #endif { #ifdef _WIN32 path[i] = '\\'; #else path[i] = '/'; #endif } } } // Convert a path to one for whatever OS your using. int is_dir(char* path){ size_t sz = strlen(path); if(path[sz] == '/' || path[sz] == '\\') return 1; return 0; } // Reads title key and content id from a rif // returns <0 on fail, 0 on success. int read_rif(char* rif_path){ ScePsmDrmLicense rif; FILE* rif_fd = fopen(rif_path, "rb"); if(rif_fd == NULL){ return -1; } fread(&rif, sizeof(ScePsmDrmLicense), 1, rif_fd); fclose(rif_fd); // Read important stuff. strncpy(title_content_id, rif.content_id, sizeof(title_content_id)); memcpy(title_key, rif.key, sizeof(title_key)); if(strlen(title_content_id) != 0x24){ return -2; } return 0x0; } // Returns the total filesize of a file. size_t get_file_size(char* file_path){ FILE* get_size_fd = fopen(file_path, "rb"); fseek(get_size_fd, 0, SEEK_END); size_t size = ftell(get_size_fd); fclose(get_size_fd); return size; } // Calculate IV for this block. uint8_t* roll_iv(uint64_t block_id) { uint8_t* new_iv = (uint8_t*)malloc(sizeof(title_iv)); memset(new_iv,0x00, sizeof(title_iv)); memcpy(new_iv, &block_id, sizeof(uint64_t)); for(int i = 0; i < sizeof(title_iv); i++){ new_iv[i] = new_iv[i] ^ title_iv[i]; } return new_iv; } // Decrypts a specific block inside a PSSE file. psse_block_ref decrypt_block(FILE* psse_file, uint64_t block_id, uint64_t total_blocks, uint32_t file_size){ psse_block_ref ref; memset(&ref, 0x00, sizeof(psse_block_ref)); uint8_t* new_iv = roll_iv(block_id); uint64_t block_loc = block_id * PSSE_BLOCK_SIZE; size_t total_read = PSSE_BLOCK_SIZE; uint64_t trim_to = total_read; if(block_id == 0){ // Skip to filedata block_loc = sizeof(psse_header); total_read -= sizeof(psse_header); trim_to = total_read; } else if(block_loc % PSSE_SIG_BLOCK_SIZE == 0){ // Skip signature block block_loc += PSSE_SIG_SIZE; total_read -= PSSE_SIG_SIZE; trim_to = total_read; } uint64_t rd_amt = ((block_loc - sizeof(psse_header)) - (PSSE_SIG_SIZE*(block_loc / PSSE_SIG_BLOCK_SIZE))); // Total amount of bytes read so far. if (block_id >= total_blocks) { // Is this the last block? total_read = file_size - rd_amt; trim_to = total_read; total_read += ((AES_BLOCK_SIZE) - (total_read % (AES_BLOCK_SIZE))); } uint8_t* block_data = malloc(total_read); fseek(psse_file, block_loc, SEEK_SET); fread(block_data, total_read, 0x1, psse_file); ref.block_size = trim_to; // Decrypt block uint32_t aes_game_ctx[0x3C]; aes_key_setup(title_key, aes_game_ctx, 0x80); aes_decrypt_cbc(block_data, total_read, ref.block_data, aes_game_ctx, 0x80, new_iv); free(block_data); free(new_iv); return ref; } // returns decrypted data on success, empty filesize on error decrypted_file decrypt_file(char* psse_file){ size_t total_filesize = get_file_size(psse_file); uint64_t total_blocks = (total_filesize / 0x8000); uint8_t title_key_copy[sizeof(title_key)]; uint32_t aes_header_ctx[0x3C]; FILE* psse_file_fd = fopen(psse_file, "rb"); decrypted_file plaintext_file; memset(&plaintext_file, 0x00, sizeof(decrypted_file)); psse_header file_psse_header; memset(&file_psse_header, 0x00, sizeof(psse_header)); fread(&file_psse_header, sizeof(psse_header), 0x1, psse_file_fd); #ifdef DEBUG printf("[*] Decrypting: %s\n", psse_file); #endif // Check magic number if(!((strncmp(file_psse_header.magic, "PSSE", 0x4) == 0) || (strncmp(file_psse_header.magic, "PSME", 0x4) == 0))){ #ifdef DEBUG printf("[*] %s Is not a valid PSSE file.\n", psse_file); #endif return plaintext_file; } // Chceck version if(file_psse_header.version != 0x01){ #ifdef DEBUG printf("[*] %s has unknown PSSE version %i.\n", psse_file, file_psse_header.version); #endif return plaintext_file; } // Check psse type if(file_psse_header.psse_type != 0x01){ #ifdef DEBUG printf("[*] %s has unknown PSSE type %i.\n", psse_file, file_psse_header.version); #endif return plaintext_file; } int psm_dev = 0; // Check content id if(strlen(file_psse_header.content_id) == 0x24) { if(strcmp(file_psse_header.content_id, title_content_id) == 0){ // Retail PSM aes_key_setup(header_key, aes_header_ctx, 0x80); } else if(strcmp(file_psse_header.content_id, runtime_content_id)){ // Runtime Libary memcpy(title_key_copy, title_key, sizeof(title_key)); memcpy(title_key, runtime_title_key, sizeof(title_key)); aes_key_setup(header_key, aes_header_ctx, 0x80); psm_dev = 1; } else{ return plaintext_file; } } else { // Debug PSM aes_key_setup(header_key_psmdev, aes_header_ctx, 0x80); } aes_decrypt_cbc(file_psse_header.file_iv, sizeof(title_iv), title_iv, aes_header_ctx, 0x80, header_iv); char* plaintext = malloc(file_psse_header.file_size); uintptr_t plaintext_ptr = 0; MD5Context md5_ctx; md5Init(&md5_ctx); for(int i = 0; i <= total_blocks; i++){ psse_block_ref block_ref = decrypt_block(psse_file_fd, i, total_blocks, file_psse_header.file_size); md5Update(&md5_ctx, block_ref.block_data, block_ref.block_size); memcpy(plaintext+plaintext_ptr, block_ref.block_data, block_ref.block_size); plaintext_ptr += block_ref.block_size; } md5Finalize(&md5_ctx); if(memcmp(file_psse_header.md5_hash, md5_ctx.digest, 0x10) != 0){ #ifdef DEBUG printf("[*] MD5 Hash did not match expected.\n"); print_buffer("Got MD5", md5_ctx.digest, 0x10); print_buffer("Expected MD5", file_psse_header.md5_hash, 0x10); #endif return plaintext_file; } fclose(psse_file_fd); plaintext_file.data = plaintext; plaintext_file.file_size = file_psse_header.file_size; if(psm_dev){ memcpy(title_key, title_key_copy, sizeof(title_key)); } return plaintext_file; } int decrypt_all_files(char* application_folder, char* psse_list, size_t psse_list_sz){ uint64_t start_point = 0; size_t sz = 0; char rel_path[PATH_MAX]; for(uint64_t i = 0; i < psse_list_sz; i++){ if(psse_list[i] == '\r' || psse_list[i] == '\n') { if (sz == 0){ goto next; } char* cur_filename = (char*)malloc(sz+1); memset(cur_filename, 0x00, sz+1); strncpy(cur_filename, (psse_list+start_point), sz); memset(rel_path, 0x00, sizeof(rel_path)); snprintf(rel_path, sizeof(rel_path), "%s\\%s", application_folder, cur_filename); free(cur_filename); fix_paths(rel_path); if(is_dir(rel_path)){ goto next; } decrypted_file dec_file = decrypt_file(rel_path); if(dec_file.data == NULL){ #ifdef DEBUG printf("[*] Decryption failed.\n"); #endif return -3; } // Write decrypted file to disk. FILE* dec_file_fd = fopen(rel_path, "wb"); if(dec_file_fd == NULL){ #ifdef DEBUG printf("[*] Failed to open %s for writing.\n", rel_path); #endif return -4; } fwrite(dec_file.data, dec_file.file_size, 0x1, dec_file_fd); fclose(dec_file_fd); free(dec_file.data); next: start_point = i+1; sz = 0; continue; } sz++; } } int main(int argc, char** argv) { memset(title_content_id, 0x00, sizeof(title_content_id)); memset(title_iv, 0x00, sizeof(title_iv)); memset(title_key, 0x00, sizeof(title_key)); if(argc <= 1){ printf("PSSE Decryptor.\n"); printf("Created by Li / SilicaAndPina ..\n"); printf("Usage: %s \n",argv[0]); return 0; } char* psm_folder = argv[1]; char application_folder[PATH_MAX]; char psse_list[PATH_MAX]; char rif_file[PATH_MAX]; snprintf(application_folder, (PATH_MAX-1), "%s\\RO\\Application", psm_folder); snprintf(psse_list, (PATH_MAX-1), "%s\\psse.list", application_folder); snprintf(rif_file, (PATH_MAX-1), "%s\\RO\\License\\FAKE.rif", psm_folder); fix_paths(application_folder); fix_paths(psse_list); fix_paths(rif_file); if(read_rif(rif_file) < 0){ printf("[*] Unable to read RIF: %s\n", rif_file); return -1; } #ifdef DEBUG print_buffer("[*] Title Key", title_key, sizeof(title_key)); #endif decrypted_file plaintext_psse_list = decrypt_file(psse_list); if(plaintext_psse_list.data == NULL){ printf("[*] Decryption failed.\n"); return -2; } // Write decrypted psse.list. FILE* psse_list_fd = fopen(psse_list, "wb"); fwrite(plaintext_psse_list.data, plaintext_psse_list.file_size, 0x1, psse_list_fd); fclose(psse_list_fd); int res = decrypt_all_files(application_folder, plaintext_psse_list.data, plaintext_psse_list.file_size); if (res < 0){ return res; } free(plaintext_psse_list.data); }