mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 10:42:36 +01:00
GUI: Implement full extraction of PUP
* Implement full extraction of PS3UPDAT.PUP. * Implement TAR extraction via GUI. * Use VFS to implement missing PS3 filesystem characters escaping. * Use VFS to error on illegal paths. (illegal paths such as malware pointing to "/../../..and so on../C:/Windows")
This commit is contained in:
parent
314670a347
commit
a9ddb1d3b3
@ -1,5 +1,10 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/VFS.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "Crypto/unself.h"
|
||||
|
||||
#include "TAR.h"
|
||||
|
||||
#include "util/asm.hpp"
|
||||
@ -137,7 +142,7 @@ fs::file tar_object::get_file(const std::string& path)
|
||||
}
|
||||
}
|
||||
|
||||
bool tar_object::extract(std::string path, std::string ignore)
|
||||
bool tar_object::extract(std::string vfs_mp)
|
||||
{
|
||||
if (!m_file) return false;
|
||||
|
||||
@ -148,11 +153,23 @@ bool tar_object::extract(std::string path, std::string ignore)
|
||||
const TARHeader& header = iter.second.second;
|
||||
const std::string& name = iter.first;
|
||||
|
||||
std::string result = path + name;
|
||||
std::string result = name;
|
||||
|
||||
if (result.compare(path.size(), ignore.size(), ignore) == 0)
|
||||
if (!vfs_mp.empty())
|
||||
{
|
||||
result.erase(path.size(), ignore.size());
|
||||
result = fmt::format("/%s/%s", vfs_mp, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.insert(result.begin(), '/');
|
||||
}
|
||||
|
||||
result = vfs::get(result);
|
||||
|
||||
if (result.empty())
|
||||
{
|
||||
tar_log.error("Path of entry is not mounted: '%s' (vfs_mp='%s')", name, vfs_mp);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (header.filetype)
|
||||
@ -160,6 +177,13 @@ bool tar_object::extract(std::string path, std::string ignore)
|
||||
case '\0':
|
||||
case '0':
|
||||
{
|
||||
// Create the directories which should have been mount points if vfs_mp is not empty
|
||||
if (!vfs_mp.empty() && !fs::create_path(fs::get_parent_dir(result)))
|
||||
{
|
||||
tar_log.error("TAR Loader: failed to create directory for file %s (%s)", name, fs::g_tls_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto data = get_file(name).release();
|
||||
|
||||
fs::file file(result, fs::rewrite);
|
||||
@ -194,3 +218,65 @@ bool tar_object::extract(std::string path, std::string ignore)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool extract_tar(const std::string& file_path, const std::string& dir_path)
|
||||
{
|
||||
tar_log.notice("Extracting '%s' to directory '%s'...", file_path, dir_path);
|
||||
|
||||
fs::file file(file_path);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
tar_log.error("Error opening file '%s' (%s)", file_path, fs::g_tls_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<fs::file> vec;
|
||||
|
||||
if (SCEDecrypter self_dec(file); self_dec.LoadHeaders())
|
||||
{
|
||||
// Encrypted file, decrypt
|
||||
self_dec.LoadMetadata(SCEPKG_ERK, SCEPKG_RIV);
|
||||
|
||||
if (!self_dec.DecryptData())
|
||||
{
|
||||
tar_log.error("Failed to decrypt TAR.");
|
||||
return false;
|
||||
}
|
||||
|
||||
vec = self_dec.MakeFile();
|
||||
if (vec.size() < 3)
|
||||
{
|
||||
tar_log.error("Failed to decrypt TAR.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not an encrypted file
|
||||
tar_log.warning("TAR is not encrypted, it may not be valid for this tool. Encrypted TAR are known to be found in PS3 Firmware files only.");
|
||||
}
|
||||
|
||||
if (!vfs::mount("/tar_extract", dir_path))
|
||||
{
|
||||
tar_log.error("Failed to mount '%s'", dir_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
tar_object tar(vec.empty() ? file : vec[2]);
|
||||
|
||||
const bool ok = tar.extract("/tar_extract");
|
||||
|
||||
if (ok)
|
||||
{
|
||||
tar_log.success("Extraction complete!");
|
||||
}
|
||||
else
|
||||
{
|
||||
tar_log.error("TAR contents are invalid.");
|
||||
}
|
||||
|
||||
// Unmount
|
||||
Emu.Init();
|
||||
return ok;
|
||||
}
|
||||
|
@ -33,5 +33,9 @@ public:
|
||||
|
||||
fs::file get_file(const std::string& path);
|
||||
|
||||
bool extract(std::string path, std::string ignore = ""); // extract all files in archive to path
|
||||
// Extract all files in archive to destination as VFS
|
||||
// Allow to optionally specify explicit mount point (which may be directory meant for extraction)
|
||||
bool extract(std::string vfs_mp = {});
|
||||
};
|
||||
|
||||
bool extract_tar(const std::string& file_path, const std::string& dir_path);
|
||||
|
@ -130,6 +130,7 @@ namespace gui
|
||||
const gui_save fd_cg_disasm = gui_save(main_window, "lastExplorePathCGD", "");
|
||||
const gui_save fd_log_viewer = gui_save(main_window, "lastExplorePathLOG", "");
|
||||
const gui_save fd_ext_mself = gui_save(main_window, "lastExplorePathExMSELF", "");
|
||||
const gui_save fd_ext_tar = gui_save(main_window, "lastExplorePathExTAR", "");
|
||||
|
||||
const gui_save mw_debugger = gui_save(main_window, "debuggerVisible", false);
|
||||
const gui_save mw_logger = gui_save(main_window, "loggerVisible", true);
|
||||
|
@ -836,7 +836,87 @@ void main_window::InstallPup(QString file_path)
|
||||
}
|
||||
}
|
||||
|
||||
void main_window::HandlePupInstallation(QString file_path)
|
||||
void main_window::ExtractPup()
|
||||
{
|
||||
const QString path_last_pup = m_gui_settings->GetValue(gui::fd_install_pup).toString();
|
||||
QString file_path = QFileDialog::getOpenFileName(this, tr("Select PS3UPDAT.PUP To extract"), path_last_pup, tr("PS3 update file (PS3UPDAT.PUP);;All pup files (*.pup);;All files (*.*)"));
|
||||
|
||||
if (file_path.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QString dir = QFileDialog::getExistingDirectory(this, tr("Extraction Directory"), QString{}, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
||||
|
||||
if (!dir.isEmpty())
|
||||
{
|
||||
HandlePupInstallation(file_path, dir);
|
||||
}
|
||||
}
|
||||
|
||||
void main_window::ExtractTar()
|
||||
{
|
||||
if (!m_gui_settings->GetBootConfirmation(this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Emu.SetForceBoot(true);
|
||||
Emu.Stop();
|
||||
|
||||
const QString path_last_tar = m_gui_settings->GetValue(gui::fd_ext_tar).toString();
|
||||
QStringList files = QFileDialog::getOpenFileNames(this, tr("Select TAR To extract"), path_last_tar, tr("All tar files (*.tar *.tar.aa.*);;All files (*.*)"));
|
||||
|
||||
if (files.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QString dir = QFileDialog::getExistingDirectory(this, tr("Extraction Directory"), QString{}, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
||||
|
||||
if (dir.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_gui_settings->SetValue(gui::fd_ext_tar, QFileInfo(files[0]).path());
|
||||
|
||||
progress_dialog pdlg(tr("TAR Extraction"), tr("Extracting encrypted TARs\nPlease wait..."), tr("Cancel"), 0, files.size(), false, this);
|
||||
pdlg.show();
|
||||
|
||||
QString error;
|
||||
|
||||
for (const QString& file : files)
|
||||
{
|
||||
if (pdlg.wasCanceled())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Do not abort on failure here, in case the user selected a wrong file in multi-selection while the rest are valid
|
||||
if (!extract_tar(sstr(file), sstr(dir) + '/'))
|
||||
{
|
||||
if (error.isEmpty())
|
||||
{
|
||||
error = tr("The following TAR file(s) could not be extracted:");
|
||||
}
|
||||
|
||||
error += "\n";
|
||||
error += file;
|
||||
}
|
||||
|
||||
pdlg.SetValue(pdlg.value() + 1);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
|
||||
if (!error.isEmpty())
|
||||
{
|
||||
pdlg.hide();
|
||||
QMessageBox::critical(this, tr("Tar extraction failed"), error);
|
||||
}
|
||||
}
|
||||
|
||||
void main_window::HandlePupInstallation(QString file_path, QString dir_path)
|
||||
{
|
||||
if (file_path.isEmpty())
|
||||
{
|
||||
@ -852,6 +932,7 @@ void main_window::HandlePupInstallation(QString file_path)
|
||||
Emu.Stop();
|
||||
|
||||
m_gui_settings->SetValue(gui::fd_install_pup, QFileInfo(file_path).path());
|
||||
|
||||
const std::string path = sstr(file_path);
|
||||
|
||||
auto critical = [this](QString str)
|
||||
@ -926,6 +1007,31 @@ void main_window::HandlePupInstallation(QString file_path)
|
||||
}
|
||||
|
||||
tar_object update_files(update_files_f);
|
||||
|
||||
if (!dir_path.isEmpty())
|
||||
{
|
||||
// Extract only mode, extract direct TAR entries to a user directory
|
||||
|
||||
if (!vfs::mount("/pup_extract", sstr(dir_path) + '/'))
|
||||
{
|
||||
gui_log.error("Error while extracting firmware: Failed to mount '%s'", sstr(dir_path));
|
||||
critical(tr("Firmware extraction failed: VFS mounting failed."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!update_files.extract("/pup_extract"))
|
||||
{
|
||||
gui_log.error("Error while installing firmware: TAR contents are invalid.");
|
||||
critical(tr("Firmware installation failed: Firmware contents could not be extracted."));
|
||||
}
|
||||
|
||||
gui_log.success("Extracted PUP file to %s", sstr(dir_path));
|
||||
return;
|
||||
}
|
||||
|
||||
// In regular installation we select specfic entries from the main TAR which are prefixed with "dev_flash_"
|
||||
// Those entries are TAR as well, we extract their packed files from them and that's what installed in /dev_flash
|
||||
|
||||
auto update_filenames = update_files.get_filenames();
|
||||
|
||||
update_filenames.erase(std::remove_if(
|
||||
@ -985,6 +1091,9 @@ void main_window::HandlePupInstallation(QString file_path)
|
||||
progress_dialog pdlg(tr("RPCS3 Firmware Installer"), tr("Installing firmware version %1\nPlease wait...").arg(qstr(version_string)), tr("Cancel"), 0, static_cast<int>(update_filenames.size()), false, this);
|
||||
pdlg.show();
|
||||
|
||||
// Used by tar_object::extract() as destination directory
|
||||
vfs::mount("/dev_flash", g_cfg.vfs.get_dev_flash());
|
||||
|
||||
// Synchronization variable
|
||||
atomic_t<uint> progress(0);
|
||||
{
|
||||
@ -1010,9 +1119,9 @@ void main_window::HandlePupInstallation(QString file_path)
|
||||
}
|
||||
|
||||
tar_object dev_flash_tar(dev_flash_tar_f[2]);
|
||||
if (!dev_flash_tar.extract(g_cfg.vfs.get_dev_flash(), "dev_flash/"))
|
||||
if (!dev_flash_tar.extract())
|
||||
{
|
||||
gui_log.error("Error while installing firmware: TAR contents are invalid.");
|
||||
gui_log.error("Error while installing firmware: TAR contents are invalid. (package=%s)", update_filename);
|
||||
critical(tr("Firmware installation failed: Firmware contents could not be extracted."));
|
||||
progress = -1;
|
||||
return;
|
||||
@ -1055,6 +1164,9 @@ void main_window::HandlePupInstallation(QString file_path)
|
||||
// Update with newly installed PS3 fonts
|
||||
Q_EMIT RequestGlobalStylesheetChange();
|
||||
|
||||
// Unmount
|
||||
Emu.Init();
|
||||
|
||||
if (progress == update_filenames.size())
|
||||
{
|
||||
gui_log.success("Successfully installed PS3 firmware version %s.", version_string);
|
||||
@ -2009,6 +2121,10 @@ void main_window::CreateConnects()
|
||||
|
||||
connect(ui->toolsExtractMSELFAct, &QAction::triggered, this, &main_window::ExtractMSELF);
|
||||
|
||||
connect(ui->toolsExtractPUPAct, &QAction::triggered, this, &main_window::ExtractPup);
|
||||
|
||||
connect(ui->toolsExtractTARAct, &QAction::triggered, this, &main_window::ExtractTar);
|
||||
|
||||
connect(ui->showDebuggerAct, &QAction::triggered, this, [this](bool checked)
|
||||
{
|
||||
checked ? m_debugger_frame->show() : m_debugger_frame->hide();
|
||||
|
@ -144,8 +144,10 @@ private:
|
||||
void HandlePackageInstallation(QStringList file_paths);
|
||||
|
||||
void InstallPup(QString filePath = "");
|
||||
void HandlePupInstallation(QString file_path = "");
|
||||
void ExtractPup();
|
||||
void HandlePupInstallation(QString file_path, QString dir_path = "");
|
||||
|
||||
void ExtractTar();
|
||||
void ExtractMSELF();
|
||||
|
||||
drop_type IsValidFile(const QMimeData& md, QStringList* drop_paths = nullptr);
|
||||
|
@ -256,7 +256,10 @@
|
||||
<addaction name="toolsStringSearchAct"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="toolsDecryptSprxLibsAct"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="toolsExtractMSELFAct"/>
|
||||
<addaction name="toolsExtractPUPAct"/>
|
||||
<addaction name="toolsExtractTARAct"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionopen_rsx_capture"/>
|
||||
<addaction name="separator"/>
|
||||
@ -634,6 +637,19 @@
|
||||
<string>Extract MSELF</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="toolsExtractPUPAct">
|
||||
<property name="text">
|
||||
<string>Extract PUP</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="toolsExtractTARAct">
|
||||
<property name="text">
|
||||
<string>Extract Encrypted TAR</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Extract files from special .tar files inside PS3UPDAT.PUP</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="showDebuggerAct">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
|
Loading…
Reference in New Issue
Block a user