From 8b3c250702f318641934efd1b77939b44d204c7f Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Fri, 7 Aug 2015 02:11:14 +0300 Subject: [PATCH] PKG decryption without the intermediate --- rpcs3/Crypto/unpkg.cpp | 270 +++++++++++++++++------------------------ rpcs3/Crypto/unpkg.h | 2 +- rpcs3/Loader/PKG.cpp | 3 +- 3 files changed, 111 insertions(+), 164 deletions(-) diff --git a/rpcs3/Crypto/unpkg.cpp b/rpcs3/Crypto/unpkg.cpp index c2fcea4452..a9f20cfa84 100644 --- a/rpcs3/Crypto/unpkg.cpp +++ b/rpcs3/Crypto/unpkg.cpp @@ -14,16 +14,15 @@ #pragma warning(pop) #include "define_new_memleakdetect.h" -// Decryption. -bool CheckHeader(const fs::file& pkg_f, PKGHeader* m_header) +static bool CheckHeader(const fs::file& pkg_f, PKGHeader& header) { - if (m_header->pkg_magic != 0x7F504B47) + if (header.pkg_magic != 0x7F504B47) { LOG_ERROR(LOADER, "PKG: Not a package file!"); return false; } - switch (const u16 type = m_header->pkg_type) + switch (const u16 type = header.pkg_type) { case PKG_RELEASE_TYPE_DEBUG: break; case PKG_RELEASE_TYPE_RELEASE: break; @@ -34,7 +33,7 @@ bool CheckHeader(const fs::file& pkg_f, PKGHeader* m_header) } } - switch (const u16 platform = m_header->pkg_platform) + switch (const u16 platform = header.pkg_platform) { case PKG_PLATFORM_TYPE_PS3: break; case PKG_PLATFORM_TYPE_PSP: break; @@ -45,46 +44,43 @@ bool CheckHeader(const fs::file& pkg_f, PKGHeader* m_header) } } - if (m_header->header_size != PKG_HEADER_SIZE && m_header->header_size != PKG_HEADER_SIZE2) + if (header.header_size != PKG_HEADER_SIZE && header.header_size != PKG_HEADER_SIZE2) { - LOG_ERROR(LOADER, "PKG: Wrong header size (0x%x)", m_header->header_size); + LOG_ERROR(LOADER, "PKG: Wrong header size (0x%x)", header.header_size); return false; } - if (m_header->pkg_size != pkg_f.size()) + if (header.pkg_size > pkg_f.size()) { - LOG_ERROR(LOADER, "PKG: File size mismatch (pkg_size=0x%llx)", m_header->pkg_size); - //return false; + LOG_ERROR(LOADER, "PKG: File size mismatch (pkg_size=0x%llx)", header.pkg_size); + return false; } - if (m_header->data_size + m_header->data_offset > m_header->pkg_size) + if (header.data_size + header.data_offset > header.pkg_size) { - 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; + LOG_ERROR(LOADER, "PKG: Data size mismatch (data_size=0x%llx, data_offset=0x%llx, file_size=0x%llx)", header.data_size, header.data_offset, header.pkg_size); + return false; } return true; } -bool LoadHeader(const fs::file& pkg_f, PKGHeader* m_header) +// PKG Decryption +bool Unpack(const fs::file& pkg_f, std::string dir) { - pkg_f.seek(0); - - if (pkg_f.read(m_header, sizeof(PKGHeader)) != sizeof(PKGHeader)) + // Save current file offset (probably zero) + const u64 start_offset = pkg_f.seek(0, from_cur); + + // Get basic PKG information + PKGHeader header; + + if (pkg_f.read(&header, sizeof(PKGHeader)) != sizeof(PKGHeader)) { LOG_ERROR(LOADER, "PKG: Package file is too short!"); return false; } - if (!CheckHeader(pkg_f, m_header)) - return false; - - return true; -} - -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)) + if (!CheckHeader(pkg_f, header)) { return false; } @@ -95,35 +91,32 @@ bool Decrypt(const fs::file& pkg_f, const fs::file& dec_pkg_f, PKGHeader* m_head 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); + // Allocate buffers with BUF_SIZE size or more if required + const u64 buffer_size = std::max(BUF_SIZE, sizeof(PKGEntry) * header.file_count); + + const std::unique_ptr buf(new u8[buffer_size]), ctr(new u8[buffer_size]); - dec_pkg_f.trunc(pkg_f.size()); - // Debug key 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] + memcpy(key + 0x00, &header.qa_digest[0], 8); // &data[0x60] + memcpy(key + 0x08, &header.qa_digest[0], 8); // &data[0x60] + memcpy(key + 0x10, &header.qa_digest[8], 8); // &data[0x68] + memcpy(key + 0x18, &header.qa_digest[8], 8); // &data[0x68] - auto decrypt = [&](std::size_t offset, std::size_t size, bool psp) + // Define decryption subfunction (`psp` arg selects the key for specific block) + auto decrypt = [&](u64 offset, u64 size, bool psp) { - if (size > BUF_SIZE) - { - buf = std::make_unique(size); - ctr = std::make_unique(size); - } - + // Initialize buffer std::memset(buf.get(), 0, size); - pkg_f.seek(m_header->data_offset + offset); + // Read the data + pkg_f.seek(start_offset + header.data_offset + offset); size = pkg_f.read(buf.get(), size); - const u32 bits = (size + HASH_LEN - 1) / HASH_LEN; + const u64 bits = (size + HASH_LEN - 1) / HASH_LEN; - if (m_header->pkg_type == PKG_RELEASE_TYPE_DEBUG) + if (header.pkg_type == PKG_RELEASE_TYPE_DEBUG) { - for (u32 j = 0; j < bits; j++) + for (u64 j = 0; j < bits; j++) { u8 hash[0x14]; sha1(key, 0x40, hash); @@ -133,15 +126,17 @@ bool Decrypt(const fs::file& pkg_f, const fs::file& dec_pkg_f, PKGHeader* m_head } } - if (m_header->pkg_type == PKG_RELEASE_TYPE_RELEASE) + if (header.pkg_type == PKG_RELEASE_TYPE_RELEASE) { + // Set decryption key aes_setkey_enc(&c, psp ? PKG_AES_KEY2 : PKG_AES_KEY, 128); - memcpy(iv, m_header->klicensee, sizeof(iv)); + // Initialize `iv` for the specific position + memcpy(iv, header.klicensee, sizeof(iv)); if (lo + offset / HASH_LEN < lo) hi++; lo += offset / HASH_LEN; - for (u32 j = 0; j < bits; j++) + for (u64 j = 0; j < bits; j++) { aes_crypt_ecb(&c, AES_ENCRYPT, iv, ctr.get() + j * HASH_LEN); @@ -151,33 +146,84 @@ bool Decrypt(const fs::file& pkg_f, const fs::file& dec_pkg_f, PKGHeader* m_head } } - for (u32 j = 0; j < size; j++) + for (u64 j = 0; j < size; j++) { buf[j] ^= ctr[j]; } } - - 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); + wxProgressDialog pdlg("PKG Decrypter / Installer", "Please wait, decrypting...", 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); + decrypt(0, header.file_count * sizeof(PKGEntry), header.pkg_platform == PKG_PLATFORM_TYPE_PSP); + + std::vector entries(header.file_count); + + std::memcpy(entries.data(), buf.get(), entries.size() * sizeof(PKGEntry)); - entries.resize(m_header->file_count); - dec_pkg_f.seek(0); - dec_pkg_f.read(entries.data(), m_header->file_count * sizeof(PKGEntry)); - for (s32 i = 0; i < entries.size(); i++) { - const bool is_psp = (entries[i].type & PKG_FILE_ENTRY_PSP) != 0; + const PKGEntry& entry = entries[i]; - decrypt(entries[i].name_offset, entries[i].name_size, is_psp); + const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0; - for (u64 j = 0; j < entries[i].file_size; j += BUF_SIZE) + decrypt(entry.name_offset, entry.name_size, is_psp); + + const std::string name(reinterpret_cast(buf.get()), entry.name_size); + + switch (entry.type & 0xff) { - decrypt(entries[i].file_offset + j, std::min(entries[i].file_size - j, BUF_SIZE), is_psp); + case PKG_FILE_ENTRY_NPDRM: + 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 + name; + + if (fs::is_file(path)) + { + LOG_WARNING(LOADER, "PKG Loader: '%s' is overwritten", path); + } + + if (fs::file out{ path, o_write | o_create | o_trunc }) + { + for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE) + { + const u64 block_size = std::min(BUF_SIZE, entry.file_size - pos); + + decrypt(entry.file_offset + pos, block_size, is_psp); + + out.write(buf.get(), block_size); + } + } + else + { + LOG_ERROR(LOADER, "PKG Loader: Could not create file '%s'", path); + return false; + } + + break; + } + + case PKG_FILE_ENTRY_FOLDER: + { + const std::string path = dir + name; + + if (!fs::is_dir(path) && !fs::create_dir(path)) + { + LOG_ERROR(LOADER, "PKG Loader: Could not create directory: %s", path); + return false; + } + + break; + } + + default: + { + LOG_ERROR(LOADER, "PKG Loader: unknown PKG file entry: 0x%x", entry.type); + return false; + } } pdlg.Update(i + 1); @@ -185,103 +231,3 @@ bool Decrypt(const fs::file& pkg_f, const fs::file& dec_pkg_f, PKGHeader* m_head return true; } - -// Unpacking. -bool UnpackEntry(const fs::file& dec_pkg_f, const PKGEntry& entry, std::string dir) -{ - auto buf = std::make_unique(BUF_SIZE); - - dec_pkg_f.seek(entry.name_offset); - dec_pkg_f.read(buf.get(), entry.name_size); - buf[entry.name_size] = 0; - - switch (entry.type & 0xff) - { - case PKG_FILE_ENTRY_NPDRM: - 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.get(), entry.name_size); - - if (fs::is_file(path)) - { - LOG_WARNING(LOADER, "PKG Loader: '%s' is overwritten", path); - } - - fs::file out(path, o_write | o_create | o_trunc); - - if (out) - { - dec_pkg_f.seek(entry.file_offset); - - for (u64 i = 0; i < entry.file_size; i += BUF_SIZE) - { - const u64 size = std::min(BUF_SIZE, entry.file_size - i); - - dec_pkg_f.read(buf.get(), size); - out.write(buf.get(), size); - } - - return true; - } - else - { - LOG_ERROR(LOADER, "PKG Loader: Could not create file '%s'", path); - return false; - } - } - - case PKG_FILE_ENTRY_FOLDER: - { - const std::string path = dir + std::string(buf.get(), entry.name_size); - - if (!fs::is_dir(path) && !fs::create_dir(path)) - { - LOG_ERROR(LOADER, "PKG Loader: Could not create directory: %s", path); - return false; - } - - return true; - } - - default: - { - LOG_ERROR(LOADER, "PKG Loader: unknown PKG file entry: 0x%x", entry.type); - return false; - } - } -} - -bool Unpack(const fs::file& pkg_f, std::string src, std::string dst) -{ - PKGHeader* m_header = (PKGHeader*) malloc (sizeof(PKGHeader)); - - // TODO: This shouldn't use current dir - std::string decryptedFile = "./dev_hdd1/" + src + ".dec"; - - fs::file dec_pkg_f(decryptedFile, o_read | o_write | o_create | o_trunc); - - std::vector m_entries; - - if (!Decrypt(pkg_f, dec_pkg_f, m_header, m_entries)) - { - return false; - } - - wxProgressDialog pdlg("PKG Decrypter / Installer", "Please wait, unpacking...", m_entries.size(), 0, wxPD_AUTO_HIDE | wxPD_APP_MODAL); - - for (const auto& entry : m_entries) - { - UnpackEntry(dec_pkg_f, entry, dst + src + "/"); - pdlg.Update(pdlg.GetValue() + 1); - } - - pdlg.Update(m_entries.size()); - - dec_pkg_f.close(); - fs::remove_file(decryptedFile); - - return true; -} diff --git a/rpcs3/Crypto/unpkg.h b/rpcs3/Crypto/unpkg.h index 182ea485b9..e973e892ef 100644 --- a/rpcs3/Crypto/unpkg.h +++ b/rpcs3/Crypto/unpkg.h @@ -61,4 +61,4 @@ struct PKGEntry namespace fs { struct file; } -bool Unpack(const fs::file& dec_pkg_f, std::string src, std::string dst); +bool Unpack(const fs::file& pkg_f, std::string dir); diff --git a/rpcs3/Loader/PKG.cpp b/rpcs3/Loader/PKG.cpp index db98408d7c..d452917234 100644 --- a/rpcs3/Loader/PKG.cpp +++ b/rpcs3/Loader/PKG.cpp @@ -17,6 +17,7 @@ bool PKGLoader::Install(const fs::file& pkg_f, std::string dest) char title_id[48]; pkg_f.seek(48); pkg_f.read(title_id, 48); + pkg_f.seek(0); std::string titleID = std::string(title_id).substr(7, 9); @@ -35,7 +36,7 @@ bool PKGLoader::Install(const fs::file& pkg_f, std::string dest) } // Decrypt and unpack the PKG file. - if (!Unpack(pkg_f, titleID, dest)) + if (!Unpack(pkg_f, dest + titleID + "/")) { LOG_ERROR(LOADER, "PKG Loader: Failed to install package!"); return false;