From 9923f96431cd02dc2127f81a5546e4d047ef6c11 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Thu, 6 Aug 2015 02:59:41 +0300 Subject: [PATCH] Added PSP PKG support --- rpcs3/Crypto/key_vault.h | 4 + rpcs3/Crypto/unpkg.cpp | 154 +++++++++++++++++++-------------------- rpcs3/Crypto/unpkg.h | 11 ++- rpcs3/Loader/PKG.cpp | 2 +- 4 files changed, 89 insertions(+), 82 deletions(-) diff --git a/rpcs3/Crypto/key_vault.h b/rpcs3/Crypto/key_vault.h index 293efeda9c..5fd1ef9ba1 100644 --- a/rpcs3/Crypto/key_vault.h +++ b/rpcs3/Crypto/key_vault.h @@ -28,6 +28,10 @@ static u8 PKG_AES_KEY[0x10] = { 0x2e, 0x7b, 0x71, 0xd7, 0xc9, 0xc9, 0xa1, 0x4e, 0xa3, 0x22, 0x1f, 0x18, 0x88, 0x28, 0xb8, 0xf8 }; +static u8 PKG_AES_KEY2[0x10] = { + 0x07, 0xf2, 0xc6, 0x82, 0x90, 0xb5, 0x0d, 0x2c, 0x33, 0x81, 0x8d, 0x70, 0x9b, 0x60, 0xe6, 0x2b +}; + static u8 NP_IDPS[0x10] = { 0x5E, 0x06, 0xE0, 0x4F, 0xD9, 0x4A, 0x71, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; diff --git a/rpcs3/Crypto/unpkg.cpp b/rpcs3/Crypto/unpkg.cpp index 4c196a9cd6..c2fcea4452 100644 --- a/rpcs3/Crypto/unpkg.cpp +++ b/rpcs3/Crypto/unpkg.cpp @@ -45,7 +45,7 @@ bool CheckHeader(const fs::file& pkg_f, PKGHeader* m_header) } } - if (m_header->header_size != PKG_HEADER_SIZE) + if (m_header->header_size != PKG_HEADER_SIZE && m_header->header_size != PKG_HEADER_SIZE2) { LOG_ERROR(LOADER, "PKG: Wrong header size (0x%x)", m_header->header_size); return false; @@ -53,13 +53,13 @@ bool CheckHeader(const fs::file& pkg_f, PKGHeader* m_header) if (m_header->pkg_size != pkg_f.size()) { - LOG_ERROR(LOADER, "PKG: File size mismatch (pkg_size=0x%x)", m_header->pkg_size); + LOG_ERROR(LOADER, "PKG: File size mismatch (pkg_size=0x%llx)", m_header->pkg_size); //return false; } - if (m_header->data_size + m_header->data_offset + 0x60 != pkg_f.size()) + if (m_header->data_size + m_header->data_offset > m_header->pkg_size) { - LOG_ERROR(LOADER, "PKG: Data size mismatch (data_size=0x%x, offset=0x%x)", m_header->data_size, m_header->data_offset); + LOG_ERROR(LOADER, "PKG: Data size mismatch (data_size=0x%llx, data_offset=0x%llx, file_size=0x%llx)", m_header->data_size, m_header->data_offset, m_header->pkg_size); //return false; } @@ -82,40 +82,45 @@ bool LoadHeader(const fs::file& pkg_f, PKGHeader* m_header) return true; } -int Decrypt(const fs::file& pkg_f, const fs::file& dec_pkg_f, PKGHeader* m_header) +bool Decrypt(const fs::file& pkg_f, const fs::file& dec_pkg_f, PKGHeader* m_header, std::vector& entries) { if (!LoadHeader(pkg_f, m_header)) { - return -1; + return false; } aes_context c; + u8 iv[HASH_LEN]; - u8 buf[BUF_SIZE]; - u8 ctr[BUF_SIZE]; + be_t& hi = (be_t&)iv[0]; + be_t& lo = (be_t&)iv[8]; + + auto buf = std::make_unique(BUF_SIZE); + auto ctr = std::make_unique(BUF_SIZE); + + dec_pkg_f.trunc(pkg_f.size()); // Debug key - u8 key[0x40]; - memset(key, 0, 0x40); + u8 key[0x40] = {}; memcpy(key+0x00, &m_header->qa_digest[0], 8); // &data[0x60] memcpy(key+0x08, &m_header->qa_digest[0], 8); // &data[0x60] memcpy(key+0x10, &m_header->qa_digest[8], 8); // &data[0x68] memcpy(key+0x18, &m_header->qa_digest[8], 8); // &data[0x68] - pkg_f.seek(m_header->data_offset); - u32 parts = (m_header->data_size + BUF_SIZE - 1) / BUF_SIZE; - - wxProgressDialog pdlg("PKG Decrypter / Installer", "Please wait, decrypting...", parts, 0, wxPD_AUTO_HIDE | wxPD_APP_MODAL); - - memcpy(iv, m_header->klicensee, sizeof(iv)); - aes_setkey_enc(&c, PKG_AES_KEY, 128); - - for (u32 i = 0; i < parts; i++) + auto decrypt = [&](std::size_t offset, std::size_t size, bool psp) { - memset(buf, 0, sizeof(buf)); - u32 length = pkg_f.read(buf, BUF_SIZE); - u32 bits = (length + HASH_LEN - 1) / HASH_LEN; - + if (size > BUF_SIZE) + { + buf = std::make_unique(size); + ctr = std::make_unique(size); + } + + std::memset(buf.get(), 0, size); + + pkg_f.seek(m_header->data_offset + offset); + size = pkg_f.read(buf.get(), size); + const u32 bits = (size + HASH_LEN - 1) / HASH_LEN; + if (m_header->pkg_type == PKG_RELEASE_TYPE_DEBUG) { for (u32 j = 0; j < bits; j++) @@ -130,54 +135,64 @@ int Decrypt(const fs::file& pkg_f, const fs::file& dec_pkg_f, PKGHeader* m_heade if (m_header->pkg_type == PKG_RELEASE_TYPE_RELEASE) { + aes_setkey_enc(&c, psp ? PKG_AES_KEY2 : PKG_AES_KEY, 128); + + memcpy(iv, m_header->klicensee, sizeof(iv)); + if (lo + offset / HASH_LEN < lo) hi++; + lo += offset / HASH_LEN; + for (u32 j = 0; j < bits; j++) { - aes_crypt_ecb(&c, AES_ENCRYPT, iv, ctr + j * HASH_LEN); + aes_crypt_ecb(&c, AES_ENCRYPT, iv, ctr.get() + j * HASH_LEN); - be_t hi = *(be_t*)&iv[0]; - be_t lo = *(be_t*)&iv[8]; - lo++; - - if (lo == 0) - hi += 1; - - *(be_t*)&iv[0] = hi; - *(be_t*)&iv[8] = lo; + if (!++lo) + { + hi++; + } } - - for (u32 j = 0; j < length; j++) + + for (u32 j = 0; j < size; j++) { buf[j] ^= ctr[j]; } - } - dec_pkg_f.write(buf, length); - pdlg.Update(i); - } - pdlg.Update(parts); - return 0; -} + } -// Unpacking. -bool LoadEntries(const fs::file& dec_pkg_f, PKGHeader* m_header, PKGEntry* m_entries) -{ + dec_pkg_f.seek(offset); + dec_pkg_f.write(buf.get(), size); + }; + + wxProgressDialog pdlg("PKG Decrypter / Installer", "Please wait, decrypting...", m_header->file_count, 0, wxPD_AUTO_HIDE | wxPD_APP_MODAL); + + decrypt(0, m_header->file_count * sizeof(PKGEntry), m_header->pkg_platform == PKG_PLATFORM_TYPE_PSP); + + entries.resize(m_header->file_count); dec_pkg_f.seek(0); - dec_pkg_f.read(m_entries, sizeof(PKGEntry) * m_header->file_count); + dec_pkg_f.read(entries.data(), m_header->file_count * sizeof(PKGEntry)); - if (m_entries->name_offset / sizeof(PKGEntry) != m_header->file_count) + for (s32 i = 0; i < entries.size(); i++) { - LOG_ERROR(LOADER, "PKG: Entries are damaged!"); - return false; + const bool is_psp = (entries[i].type & PKG_FILE_ENTRY_PSP) != 0; + + decrypt(entries[i].name_offset, entries[i].name_size, is_psp); + + for (u64 j = 0; j < entries[i].file_size; j += BUF_SIZE) + { + decrypt(entries[i].file_offset + j, std::min(entries[i].file_size - j, BUF_SIZE), is_psp); + } + + pdlg.Update(i + 1); } return true; } +// Unpacking. bool UnpackEntry(const fs::file& dec_pkg_f, const PKGEntry& entry, std::string dir) { - char buf[BUF_SIZE]; + auto buf = std::make_unique(BUF_SIZE); dec_pkg_f.seek(entry.name_offset); - dec_pkg_f.read(buf, entry.name_size); + dec_pkg_f.read(buf.get(), entry.name_size); buf[entry.name_size] = 0; switch (entry.type & 0xff) @@ -186,8 +201,9 @@ bool UnpackEntry(const fs::file& dec_pkg_f, const PKGEntry& entry, std::string d case PKG_FILE_ENTRY_NPDRMEDAT: case PKG_FILE_ENTRY_SDAT: case PKG_FILE_ENTRY_REGULAR: + case PKG_FILE_ENTRY_UNK1: { - const std::string path = dir + std::string(buf, entry.name_size); + const std::string path = dir + std::string(buf.get(), entry.name_size); if (fs::is_file(path)) { @@ -200,18 +216,12 @@ bool UnpackEntry(const fs::file& dec_pkg_f, const PKGEntry& entry, std::string d { dec_pkg_f.seek(entry.file_offset); - for (u64 size = 0; size < entry.file_size;) + for (u64 i = 0; i < entry.file_size; i += BUF_SIZE) { - size += dec_pkg_f.read(buf, BUF_SIZE); + const u64 size = std::min(BUF_SIZE, entry.file_size - i); - if (size > entry.file_size) - { - out.write(buf, BUF_SIZE - (size - entry.file_size)); - } - else - { - out.write(buf, BUF_SIZE); - } + dec_pkg_f.read(buf.get(), size); + out.write(buf.get(), size); } return true; @@ -225,7 +235,7 @@ bool UnpackEntry(const fs::file& dec_pkg_f, const PKGEntry& entry, std::string d case PKG_FILE_ENTRY_FOLDER: { - const std::string path = dir + std::string(buf, entry.name_size); + const std::string path = dir + std::string(buf.get(), entry.name_size); if (!fs::is_dir(path) && !fs::create_dir(path)) { @@ -244,7 +254,7 @@ bool UnpackEntry(const fs::file& dec_pkg_f, const PKGEntry& entry, std::string d } } -int Unpack(const fs::file& pkg_f, std::string src, std::string dst) +bool Unpack(const fs::file& pkg_f, std::string src, std::string dst) { PKGHeader* m_header = (PKGHeader*) malloc (sizeof(PKGHeader)); @@ -253,21 +263,11 @@ int Unpack(const fs::file& pkg_f, std::string src, std::string dst) fs::file dec_pkg_f(decryptedFile, o_read | o_write | o_create | o_trunc); - if (Decrypt(pkg_f, dec_pkg_f, m_header) < 0) - { - return -1; - } - - dec_pkg_f.seek(0); - std::vector m_entries; - m_entries.resize(m_header->file_count); - auto m_entries_ptr = m_entries.data(); - - if (!LoadEntries(dec_pkg_f, m_header, m_entries_ptr)) + if (!Decrypt(pkg_f, dec_pkg_f, m_header, m_entries)) { - return -1; + return false; } wxProgressDialog pdlg("PKG Decrypter / Installer", "Please wait, unpacking...", m_entries.size(), 0, wxPD_AUTO_HIDE | wxPD_APP_MODAL); @@ -283,5 +283,5 @@ int Unpack(const fs::file& pkg_f, std::string src, std::string dst) dec_pkg_f.close(); fs::remove_file(decryptedFile); - return 0; + return true; } diff --git a/rpcs3/Crypto/unpkg.h b/rpcs3/Crypto/unpkg.h index 0f895a0fe6..182ea485b9 100644 --- a/rpcs3/Crypto/unpkg.h +++ b/rpcs3/Crypto/unpkg.h @@ -3,9 +3,10 @@ // Constants enum { - HASH_LEN = 16, - BUF_SIZE = 4096, - PKG_HEADER_SIZE = 0xC0, //sizeof(pkg_header) + sizeof(pkg_unk_checksum) + HASH_LEN = 16, + BUF_SIZE = 8192 * 1024, + PKG_HEADER_SIZE = 0xC0, //sizeof(pkg_header) + sizeof(pkg_unk_checksum) + PKG_HEADER_SIZE2 = 0x280, }; enum : u16 @@ -23,9 +24,11 @@ enum : u32 PKG_FILE_ENTRY_NPDRMEDAT = 2, PKG_FILE_ENTRY_REGULAR = 3, PKG_FILE_ENTRY_FOLDER = 4, + PKG_FILE_ENTRY_UNK1 = 6, PKG_FILE_ENTRY_SDAT = 9, PKG_FILE_ENTRY_OVERWRITE = 0x80000000, + PKG_FILE_ENTRY_PSP = 0x10000000, }; // Structs @@ -58,4 +61,4 @@ struct PKGEntry namespace fs { struct file; } -int Unpack(const fs::file& dec_pkg_f, std::string src, std::string dst); +bool Unpack(const fs::file& dec_pkg_f, std::string src, std::string dst); diff --git a/rpcs3/Loader/PKG.cpp b/rpcs3/Loader/PKG.cpp index e517483b2f..db98408d7c 100644 --- a/rpcs3/Loader/PKG.cpp +++ b/rpcs3/Loader/PKG.cpp @@ -35,7 +35,7 @@ bool PKGLoader::Install(const fs::file& pkg_f, std::string dest) } // Decrypt and unpack the PKG file. - if (Unpack(pkg_f, titleID, dest) < 0) + if (!Unpack(pkg_f, titleID, dest)) { LOG_ERROR(LOADER, "PKG Loader: Failed to install package!"); return false;