Skip to content

Commit c0e05e0

Browse files
committed
Deny PKG installation without RAP
1 parent 54206c6 commit c0e05e0

File tree

5 files changed

+142
-29
lines changed

5 files changed

+142
-29
lines changed

rpcs3/Crypto/unpkg.cpp

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "Emu/system_utils.hpp"
1010
#include "Emu/VFS.h"
1111
#include "unpkg.h"
12+
#include "unself.h"
1213
#include "util/sysinfo.hpp"
1314
#include "Loader/PSF.h"
1415

@@ -47,12 +48,23 @@ package_reader::package_reader(const std::string& path, fs::file file)
4748
return;
4849
}
4950

50-
const bool param_sfo_found = read_param_sfo();
51+
m_psf = psf::load_object(read_file("PARAM.SFO"), path + ":PARAM.SFO");
5152

52-
if (!param_sfo_found)
53+
if (m_psf.empty())
5354
{
5455
pkg_log.notice("PKG does not contain a PARAM.SFO");
5556
}
57+
58+
m_rap_file_path = get_rap_file_path_of_self(read_file("USRDIR/EBOOT.BIN"));
59+
60+
if (m_rap_file_path.empty())
61+
{
62+
pkg_log.notice("PKG needs RAP file: %s", m_rap_file_path);
63+
}
64+
else
65+
{
66+
pkg_log.notice("PKG need not a license file.");
67+
}
5668
}
5769

5870
package_reader::~package_reader()
@@ -577,13 +589,14 @@ bool package_reader::read_entries(std::vector<PKGEntry>& entries)
577589
return true;
578590
}
579591

580-
bool package_reader::read_param_sfo()
592+
fs::file package_reader::read_file(std::string_view relative_path)
581593
{
582594
std::vector<PKGEntry> entries;
595+
fs::file tmp;
583596

584597
if (!read_entries(entries))
585598
{
586-
return false;
599+
return tmp;
587600
}
588601

589602
std::vector<u8> data_buf;
@@ -609,13 +622,13 @@ bool package_reader::read_param_sfo()
609622
fmt::trim_back(name, "\0"sv);
610623

611624
// We're looking for the PARAM.SFO file, if there is any
612-
if (usz ndelim = name.find_first_not_of('/'); ndelim == umax || name.substr(ndelim) != "PARAM.SFO")
625+
if (usz ndelim = name.find_first_not_of('/'); ndelim == umax || name.substr(ndelim) != relative_path)
613626
{
614627
continue;
615628
}
616629

617630
// Read the package's PARAM.SFO
618-
fs::file tmp = fs::make_stream<std::vector<uchar>>();
631+
tmp = fs::make_stream<std::vector<uchar>>();
619632
{
620633
for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
621634
{
@@ -625,32 +638,21 @@ bool package_reader::read_param_sfo()
625638

626639
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), data_buf.data()) != block_size)
627640
{
628-
pkg_log.error("Failed to decrypt PARAM.SFO file");
629-
return false;
641+
pkg_log.error("Failed to decrypt %s file", relative_path);
642+
tmp.close();
643+
return tmp;
630644
}
631645

632-
if (tmp.write(data_buf.data(), block_size) != block_size)
633-
{
634-
pkg_log.error("Failed to write to temporary PARAM.SFO file");
635-
return false;
636-
}
646+
ensure(tmp.write(data_buf.data(), block_size) == block_size);
637647
}
638648

639649
tmp.seek(0);
640-
641-
m_psf = psf::load_object(tmp, name);
642-
643-
if (m_psf.empty())
644-
{
645-
// Invalid
646-
continue;
647-
}
648-
649-
return true;
650+
return tmp;
650651
}
651652
}
652653

653-
return false;
654+
tmp.close();
655+
return tmp;
654656
}
655657

656658
// TODO: maybe also check if VERSION matches

rpcs3/Crypto/unpkg.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,10 +361,15 @@ class package_reader
361361
return m_file;
362362
}
363363

364+
const std::string& gep_needed_rap_file_path() const
365+
{
366+
return m_rap_file_path;
367+
}
368+
364369
private:
365370
bool read_header();
366371
bool read_metadata();
367-
bool read_param_sfo();
372+
fs::file read_file(std::string_view relative_path);
368373
bool set_decryption_key();
369374
bool read_entries(std::vector<PKGEntry>& entries);
370375
void archive_seek(s64 new_offset, const fs::seek_mode damode = fs::seek_set);
@@ -400,4 +405,5 @@ class package_reader
400405

401406
// Expose bootable file installed (if installed such)
402407
std::string m_bootable_file_path;
408+
std::string m_rap_file_path;
403409
};

rpcs3/Crypto/unself.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,37 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
10681068
return true;
10691069
}
10701070

1071+
std::string SELFDecrypter::GetRapFilePath()
1072+
{
1073+
// Check if we have a valid NPDRM control info structure.
1074+
// If not, the data has no NPDRM layer.
1075+
const NPD_HEADER* npd = GetNPDHeader();
1076+
if (!npd)
1077+
{
1078+
self_log.trace("No NPDRM control info found!");
1079+
return {};
1080+
}
1081+
1082+
if (npd->license == 1) // Network license.
1083+
{
1084+
return rpcs3::utils::get_rap_file_path(npd->content_id);
1085+
}
1086+
else if (npd->license == 2) // Local license.
1087+
{
1088+
return rpcs3::utils::get_rap_file_path(npd->content_id);
1089+
}
1090+
else if (npd->license == 3) // Free license.
1091+
{
1092+
//
1093+
}
1094+
else
1095+
{
1096+
self_log.error("Invalid NPDRM license type!");
1097+
}
1098+
1099+
return {};
1100+
}
1101+
10711102
const NPD_HEADER* SELFDecrypter::GetNPDHeader() const
10721103
{
10731104
// Parse the control info structures to find the NPDRM control info.
@@ -1435,6 +1466,46 @@ fs::file decrypt_self(const fs::file& elf_or_self, const u8* klic_key, SelfAddit
14351466
return {};
14361467
}
14371468

1469+
std::string get_rap_file_path_of_self(const fs::file& elf_or_self)
1470+
{
1471+
if (!elf_or_self)
1472+
{
1473+
return {};
1474+
}
1475+
1476+
elf_or_self.seek(0);
1477+
1478+
// Check SELF header first. Check for a debug SELF.
1479+
u32 file_type = umax;
1480+
elf_or_self.read_at(0, &file_type, sizeof(file_type));
1481+
1482+
if (file_type == "SCE\0"_u32)
1483+
{
1484+
if (fs::file res = CheckDebugSelf(elf_or_self))
1485+
{
1486+
return {};
1487+
}
1488+
1489+
// Check the ELF file class (32 or 64 bit).
1490+
const bool isElf32 = IsSelfElf32(elf_or_self);
1491+
1492+
// Start the decrypter on this SELF file.
1493+
SELFDecrypter self_dec(elf_or_self);
1494+
1495+
// Load the SELF file headers.
1496+
if (!self_dec.LoadHeaders(isElf32, nullptr))
1497+
{
1498+
self_log.error("Failed to load SELF file headers!");
1499+
return {};
1500+
}
1501+
1502+
// Load and decrypt the SELF file metadata.
1503+
return self_dec.GetRapFilePath();
1504+
}
1505+
1506+
return {};
1507+
}
1508+
14381509
bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key, NPD_HEADER* npd_out)
14391510
{
14401511
if (!self)

rpcs3/Crypto/unself.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ class SELFDecrypter
481481
bool DecryptNPDRM(u8 *metadata, u32 metadata_size);
482482
const NPD_HEADER* GetNPDHeader() const;
483483
static bool GetKeyFromRap(const char *content_id, u8 *npdrm_key);
484+
std::string GetRapFilePath();
484485

485486
private:
486487
template<typename EHdr, typename SHdr, typename PHdr>
@@ -562,5 +563,6 @@ class SELFDecrypter
562563
fs::file decrypt_self(const fs::file& elf_or_self, const u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr);
563564
bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key = nullptr, NPD_HEADER* npd_out = nullptr);
564565
bool get_npdrm_self_header(const fs::file& self, NPD_HEADER& npd);
566+
std::string get_rap_file_path_of_self(const fs::file& elf_or_self);
565567

566568
u128 get_default_self_klic();

rpcs3/rpcs3qt/main_window.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,10 +1005,6 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo
10051005
}
10061006
gui_log.notice("About to install packages:\n%s", fmt::merge(path_vec, "\n"));
10071007

1008-
progress_dialog pdlg(tr("RPCS3 Package Installer"), tr("Installing package, please wait..."), tr("Cancel"), 0, 1000, false, this);
1009-
pdlg.setAutoClose(false);
1010-
pdlg.show();
1011-
10121008
package_install_result result = {};
10131009

10141010
auto get_app_info = [](compat::package_info& package)
@@ -1047,6 +1043,42 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo
10471043
readers.emplace_back(info.path.toStdString());
10481044
}
10491045

1046+
std::string missing_licenses;
1047+
std::string missing_licenses_short;
1048+
1049+
for (const auto& reader : readers)
1050+
{
1051+
if (std::string filepath = reader.gep_needed_rap_file_path(); !filepath.empty() && fs::is_file(filepath))
1052+
{
1053+
missing_licenses += filepath;
1054+
missing_licenses += "\n";
1055+
1056+
if (std::count(missing_licenses_short.begin(), missing_licenses_short.end(), '\n') < 5)
1057+
{
1058+
missing_licenses_short += std::string_view(filepath).substr(filepath.find_last_of(fs::delim) + 1);
1059+
missing_licenses_short += "\n";
1060+
}
1061+
}
1062+
}
1063+
1064+
if (!missing_licenses.empty())
1065+
{
1066+
gui_log.fatal("Failed to locate the game license file(s):\n%s"
1067+
"\nEnsure the .rap license file is placed in the dev_hdd0/home/%s/exdata folder with a lowercase file extension."
1068+
"\nIf you need assistance on dumping the license file from your PS3, read our quickstart guide: https://rpcs3.net/quickstart", missing_licenses, Emu.GetUsr());
1069+
1070+
QString error = tr("Failed to locate the game license file(s):\n\n%1\nEnsure the .rap license file(s) are placed in the dev_hdd0 folder with a lowercase file extension.").arg(QString::fromStdString(missing_licenses_short));
1071+
1072+
QMessageBox* mb = new QMessageBox(QMessageBox::Warning, tr("PKG Installer: Missing License(s) For PKG(s)"), error, QMessageBox::Ok, this, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowStaysOnTopHint);
1073+
mb->setAttribute(Qt::WA_DeleteOnClose);
1074+
mb->open();
1075+
return false;
1076+
}
1077+
1078+
progress_dialog pdlg(tr("RPCS3 Package Installer"), tr("Installing package, please wait..."), tr("Cancel"), 0, 1000, false, this);
1079+
pdlg.setAutoClose(false);
1080+
pdlg.show();
1081+
10501082
std::deque<std::string> bootable_paths;
10511083

10521084
// Run PKG unpacking asynchronously

0 commit comments

Comments
 (0)