mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 19:45:20 +00:00
PS3UPDAT.PUP installer (#2386)
* Add PUP loader * Add .tar loader and update .pup loader * Add extract method + offset to TAR loader Also adds error checking + operator bool overload * Add firmware decryption keys to key vault * Initial seperation of SELFDecrypter This seperates SELFDecrypter into itself and SCEDecrypter. SCEDecrypter contains the logic to decrypt any file with an SCE Header. SELFDecrypter inherits from SCEDecrypter and contains the code specifically to do with ELF. DecryptData could be deduplicated more. * Add "Install Firmware" option to tools menu * SCEDecrypter: put each segment in own file Also, const-correctness, adjusted buffer size and better error handling * More SELFDecrypter refactoring * Compile fix * Add messageboxes to firmware install * Add progress bar to firmware install
This commit is contained in:
parent
b1aa87b515
commit
458dbbd15d
11 changed files with 657 additions and 124 deletions
|
@ -140,6 +140,15 @@ static u8 VSH_PUB[0x28] = {
|
|||
0x6E, 0x73, 0x6A, 0xBF, 0x81, 0xF7, 0x0E, 0xE9, 0x16, 0x1B, 0x0D, 0xDE, 0xB0, 0x26, 0x76, 0x1A, 0xFF, 0x7B, 0xC8, 0x5B
|
||||
};
|
||||
|
||||
static u8 SCEPKG_RIV[0x10] = {
|
||||
0x4A, 0xCE, 0xF0, 0x12, 0x24, 0xFB, 0xEE, 0xDF, 0x82, 0x45, 0xF8, 0xFF, 0x10, 0x21, 0x1E, 0x6E
|
||||
};
|
||||
|
||||
static u8 SCEPKG_ERK[0x20] = {
|
||||
0xA9, 0x78, 0x18, 0xBD, 0x19, 0x3A, 0x67, 0xA1, 0x6F, 0xE8, 0x3A, 0x85, 0x5E, 0x1B, 0xE9, 0xFB, 0x56, 0x40, 0x93, 0x8D,
|
||||
0x4D, 0xBC, 0xB2, 0xCB, 0x52, 0xC5, 0xA2, 0xF8, 0xB0, 0x2B, 0x10, 0x31
|
||||
};
|
||||
|
||||
class KeyVault
|
||||
{
|
||||
std::vector<SELF_KEY> sk_LV0_arr;
|
||||
|
|
|
@ -647,18 +647,17 @@ void SelfHeader::Load(const fs::file& f)
|
|||
pad = Read64(f);
|
||||
}
|
||||
|
||||
SELFDecrypter::SELFDecrypter(const fs::file& s)
|
||||
: self_f(s)
|
||||
, key_v()
|
||||
SCEDecrypter::SCEDecrypter(const fs::file& s)
|
||||
: sce_f(s)
|
||||
, data_buf_length(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool SELFDecrypter::LoadHeaders(bool isElf32)
|
||||
bool SCEDecrypter::LoadHeaders()
|
||||
{
|
||||
// Read SCE header.
|
||||
self_f.seek(0);
|
||||
sce_hdr.Load(self_f);
|
||||
sce_f.seek(0);
|
||||
sce_hdr.Load(sce_f);
|
||||
|
||||
// Check SCE magic.
|
||||
if (!sce_hdr.CheckMagic())
|
||||
|
@ -667,20 +666,235 @@ bool SELFDecrypter::LoadHeaders(bool isElf32)
|
|||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SCEDecrypter::LoadMetadata(const u8 erk[32], const u8 riv[16])
|
||||
{
|
||||
aes_context aes;
|
||||
u32 metadata_info_size = SIZE_32(meta_info);
|
||||
auto metadata_info = std::make_unique<u8[]>(metadata_info_size);
|
||||
u32 metadata_headers_size = sce_hdr.se_hsize - (SIZE_32(sce_hdr) + sce_hdr.se_meta + SIZE_32(meta_info));
|
||||
auto metadata_headers = std::make_unique<u8[]>(metadata_headers_size);
|
||||
|
||||
// Locate and read the encrypted metadata info.
|
||||
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
|
||||
sce_f.read(metadata_info.get(), metadata_info_size);
|
||||
|
||||
// Locate and read the encrypted metadata header and section header.
|
||||
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr) + metadata_info_size);
|
||||
sce_f.read(metadata_headers.get(), metadata_headers_size);
|
||||
|
||||
// Copy the necessary parameters.
|
||||
u8 metadata_key[0x20];
|
||||
u8 metadata_iv[0x10];
|
||||
memcpy(metadata_key, erk, 0x20);
|
||||
memcpy(metadata_iv, riv, 0x10);
|
||||
|
||||
// Check DEBUG flag.
|
||||
if ((sce_hdr.se_flags & 0x8000) != 0x8000)
|
||||
{
|
||||
// Decrypt the metadata info.
|
||||
aes_setkey_dec(&aes, metadata_key, 256); // AES-256
|
||||
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info.get(), metadata_info.get());
|
||||
}
|
||||
|
||||
// Load the metadata info.
|
||||
meta_info.Load(metadata_info.get());
|
||||
|
||||
// If the padding is not NULL for the key or iv fields, the metadata info
|
||||
// is not properly decrypted.
|
||||
if ((meta_info.key_pad[0] != 0x00) ||
|
||||
(meta_info.iv_pad[0] != 0x00))
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Failed to decrypt metadata info!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform AES-CTR encryption on the metadata headers.
|
||||
size_t ctr_nc_off = 0;
|
||||
u8 ctr_stream_block[0x10];
|
||||
aes_setkey_enc(&aes, meta_info.key, 128);
|
||||
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
|
||||
|
||||
// Load the metadata header.
|
||||
meta_hdr.Load(metadata_headers.get());
|
||||
|
||||
// Load the metadata section headers.
|
||||
meta_shdr.clear();
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
meta_shdr.emplace_back();
|
||||
meta_shdr.back().Load(metadata_headers.get() + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
|
||||
}
|
||||
|
||||
// Copy the decrypted data keys.
|
||||
data_keys_length = meta_hdr.key_count * 0x10;
|
||||
data_keys = std::make_unique<u8[]>(data_keys_length);
|
||||
memcpy(data_keys.get(), metadata_headers.get() + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SCEDecrypter::DecryptData()
|
||||
{
|
||||
aes_context aes;
|
||||
|
||||
// Calculate the total data size.
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
data_buf_length += meta_shdr[i].data_size;
|
||||
}
|
||||
|
||||
// Allocate a buffer to store decrypted data.
|
||||
data_buf = std::make_unique<u8[]>(data_buf_length);
|
||||
|
||||
// Set initial offset.
|
||||
u32 data_buf_offset = 0;
|
||||
|
||||
// Parse the metadata section headers to find the offsets of encrypted data.
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
size_t ctr_nc_off = 0;
|
||||
u8 ctr_stream_block[0x10];
|
||||
u8 data_key[0x10];
|
||||
u8 data_iv[0x10];
|
||||
|
||||
// Check if this is an encrypted section.
|
||||
if (meta_shdr[i].encrypted == 3)
|
||||
{
|
||||
// Make sure the key and iv are not out of boundaries.
|
||||
if ((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
|
||||
{
|
||||
// Get the key and iv from the previously stored key buffer.
|
||||
memcpy(data_key, data_keys.get() + meta_shdr[i].key_idx * 0x10, 0x10);
|
||||
memcpy(data_iv, data_keys.get() + meta_shdr[i].iv_idx * 0x10, 0x10);
|
||||
|
||||
// Allocate a buffer to hold the data.
|
||||
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
|
||||
|
||||
// Seek to the section data offset and read the encrypted data.
|
||||
sce_f.seek(meta_shdr[i].data_offset);
|
||||
sce_f.read(buf.get(), meta_shdr[i].data_size);
|
||||
|
||||
// Zero out our ctr nonce.
|
||||
memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
|
||||
|
||||
// Perform AES-CTR encryption on the data blocks.
|
||||
aes_setkey_enc(&aes, data_key, 128);
|
||||
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf.get(), buf.get());
|
||||
|
||||
// Copy the decrypted data.
|
||||
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
|
||||
sce_f.seek(meta_shdr[i].data_offset);
|
||||
sce_f.read(buf.get(), meta_shdr[i].data_size);
|
||||
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
|
||||
}
|
||||
|
||||
// Advance the buffer's offset.
|
||||
data_buf_offset += meta_shdr[i].data_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Each section gets put into its own file.
|
||||
std::vector<fs::file> SCEDecrypter::MakeFile()
|
||||
{
|
||||
std::vector<fs::file> vec;
|
||||
|
||||
// Set initial offset.
|
||||
u32 data_buf_offset = 0;
|
||||
|
||||
// Write data.
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
fs::file out_f = fs::make_stream<std::vector<u8>>();
|
||||
|
||||
bool isValid = true;
|
||||
|
||||
// Decompress if necessary.
|
||||
if (meta_shdr[i].compressed == 2)
|
||||
{
|
||||
const size_t BUFSIZE = 32 * 1024;
|
||||
u8 tempbuf[BUFSIZE];
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = meta_shdr[i].data_size;
|
||||
strm.avail_out = BUFSIZE;
|
||||
strm.next_in = data_buf.get()+data_buf_offset;
|
||||
strm.next_out = tempbuf;
|
||||
int ret = inflateInit(&strm);
|
||||
|
||||
while (strm.avail_in)
|
||||
{
|
||||
ret = inflate(&strm, Z_NO_FLUSH);
|
||||
if (ret == Z_STREAM_END)
|
||||
break;
|
||||
if (ret != Z_OK)
|
||||
isValid = false;
|
||||
|
||||
if (!strm.avail_out) {
|
||||
out_f.write(tempbuf, BUFSIZE);
|
||||
strm.next_out = tempbuf;
|
||||
strm.avail_out = BUFSIZE;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
int inflate_res = Z_OK;
|
||||
inflate_res = inflate(&strm, Z_FINISH);
|
||||
|
||||
if (inflate_res != Z_STREAM_END)
|
||||
isValid = false;
|
||||
|
||||
out_f.write(tempbuf, BUFSIZE - strm.avail_out);
|
||||
inflateEnd(&strm);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write the data.
|
||||
out_f.write(data_buf.get()+data_buf_offset, meta_shdr[i].data_size);
|
||||
}
|
||||
|
||||
// Advance the data buffer offset by data size.
|
||||
data_buf_offset += meta_shdr[i].data_size;
|
||||
|
||||
if (out_f.pos() != out_f.size())
|
||||
fmt::throw_exception("MakeELF written bytes (%llu) does not equal buffer size (%llu).", out_f.pos(), out_f.size());
|
||||
|
||||
if (isValid) vec.push_back(std::move(out_f));
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
bool SELFDecrypter::LoadHeaders()
|
||||
{
|
||||
if(!SCEDecrypter::LoadHeaders()) return false;
|
||||
|
||||
// Read SELF header.
|
||||
self_hdr.Load(self_f);
|
||||
self_hdr.Load(sce_f);
|
||||
|
||||
// Read the APP INFO.
|
||||
self_f.seek(self_hdr.se_appinfooff);
|
||||
app_info.Load(self_f);
|
||||
sce_f.seek(self_hdr.se_appinfooff);
|
||||
app_info.Load(sce_f);
|
||||
|
||||
// Read ELF header.
|
||||
self_f.seek(self_hdr.se_elfoff);
|
||||
sce_f.seek(self_hdr.se_elfoff);
|
||||
|
||||
if (isElf32)
|
||||
elf32_hdr.Load(self_f);
|
||||
elf32_hdr.Load(sce_f);
|
||||
else
|
||||
elf64_hdr.Load(self_f);
|
||||
elf64_hdr.Load(sce_f);
|
||||
|
||||
// Read ELF program headers.
|
||||
if (isElf32)
|
||||
|
@ -691,11 +905,11 @@ bool SELFDecrypter::LoadHeaders(bool isElf32)
|
|||
LOG_ERROR(LOADER, "SELF: ELF program header offset is null!");
|
||||
return false;
|
||||
}
|
||||
self_f.seek(self_hdr.se_phdroff);
|
||||
sce_f.seek(self_hdr.se_phdroff);
|
||||
for(u32 i = 0; i < elf32_hdr.e_phnum; ++i)
|
||||
{
|
||||
phdr32_arr.emplace_back();
|
||||
phdr32_arr.back().Load(self_f);
|
||||
phdr32_arr.back().Load(sce_f);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -708,40 +922,40 @@ bool SELFDecrypter::LoadHeaders(bool isElf32)
|
|||
return false;
|
||||
}
|
||||
|
||||
self_f.seek(self_hdr.se_phdroff);
|
||||
sce_f.seek(self_hdr.se_phdroff);
|
||||
|
||||
for (u32 i = 0; i < elf64_hdr.e_phnum; ++i)
|
||||
{
|
||||
phdr64_arr.emplace_back();
|
||||
phdr64_arr.back().Load(self_f);
|
||||
phdr64_arr.back().Load(sce_f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read section info.
|
||||
secinfo_arr.clear();
|
||||
self_f.seek(self_hdr.se_secinfoff);
|
||||
sce_f.seek(self_hdr.se_secinfoff);
|
||||
|
||||
for(u32 i = 0; i < ((isElf32) ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i)
|
||||
{
|
||||
secinfo_arr.emplace_back();
|
||||
secinfo_arr.back().Load(self_f);
|
||||
secinfo_arr.back().Load(sce_f);
|
||||
}
|
||||
|
||||
// Read SCE version info.
|
||||
self_f.seek(self_hdr.se_sceveroff);
|
||||
scev_info.Load(self_f);
|
||||
sce_f.seek(self_hdr.se_sceveroff);
|
||||
scev_info.Load(sce_f);
|
||||
|
||||
// Read control info.
|
||||
ctrlinfo_arr.clear();
|
||||
self_f.seek(self_hdr.se_controloff);
|
||||
sce_f.seek(self_hdr.se_controloff);
|
||||
|
||||
u32 i = 0;
|
||||
while(i < self_hdr.se_controlsize)
|
||||
{
|
||||
ctrlinfo_arr.emplace_back();
|
||||
ControlInfo &cinfo = ctrlinfo_arr.back();
|
||||
cinfo.Load(self_f);
|
||||
cinfo.Load(sce_f);
|
||||
i += cinfo.size;
|
||||
}
|
||||
|
||||
|
@ -756,12 +970,12 @@ bool SELFDecrypter::LoadHeaders(bool isElf32)
|
|||
return true;
|
||||
}
|
||||
|
||||
self_f.seek(self_hdr.se_shdroff);
|
||||
sce_f.seek(self_hdr.se_shdroff);
|
||||
|
||||
for(u32 i = 0; i < elf32_hdr.e_shnum; ++i)
|
||||
{
|
||||
shdr32_arr.emplace_back();
|
||||
shdr32_arr.back().Load(self_f);
|
||||
shdr32_arr.back().Load(sce_f);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -773,19 +987,19 @@ bool SELFDecrypter::LoadHeaders(bool isElf32)
|
|||
return true;
|
||||
}
|
||||
|
||||
self_f.seek(self_hdr.se_shdroff);
|
||||
sce_f.seek(self_hdr.se_shdroff);
|
||||
|
||||
for(u32 i = 0; i < elf64_hdr.e_shnum; ++i)
|
||||
{
|
||||
shdr64_arr.emplace_back();
|
||||
shdr64_arr.back().Load(self_f);
|
||||
shdr64_arr.back().Load(sce_f);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SELFDecrypter::ShowHeaders(bool isElf32)
|
||||
void SELFDecrypter::ShowHeaders()
|
||||
{
|
||||
LOG_NOTICE(LOADER, "SCE header");
|
||||
LOG_NOTICE(LOADER, "----------------------------------------------------");
|
||||
|
@ -902,76 +1116,8 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
|
|||
|
||||
bool SELFDecrypter::LoadMetadata()
|
||||
{
|
||||
aes_context aes;
|
||||
u32 metadata_info_size = SIZE_32(meta_info);
|
||||
auto metadata_info = std::make_unique<u8[]>(metadata_info_size);
|
||||
u32 metadata_headers_size = sce_hdr.se_hsize - (SIZE_32(sce_hdr) + sce_hdr.se_meta + SIZE_32(meta_info));
|
||||
auto metadata_headers = std::make_unique<u8[]>(metadata_headers_size);
|
||||
|
||||
// Locate and read the encrypted metadata info.
|
||||
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
|
||||
self_f.read(metadata_info.get(), metadata_info_size);
|
||||
|
||||
// Locate and read the encrypted metadata header and section header.
|
||||
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr) + metadata_info_size);
|
||||
self_f.read(metadata_headers.get(), metadata_headers_size);
|
||||
|
||||
// Find the right keyset from the key vault.
|
||||
SELF_KEY keyset = key_v.FindSelfKey(app_info.self_type, sce_hdr.se_flags, app_info.version);
|
||||
|
||||
// Copy the necessary parameters.
|
||||
u8 metadata_key[0x20];
|
||||
u8 metadata_iv[0x10];
|
||||
memcpy(metadata_key, keyset.erk, 0x20);
|
||||
memcpy(metadata_iv, keyset.riv, 0x10);
|
||||
|
||||
// Check DEBUG flag.
|
||||
if ((sce_hdr.se_flags & 0x8000) != 0x8000)
|
||||
{
|
||||
// Decrypt the NPDRM layer.
|
||||
if (!DecryptNPDRM(metadata_info.get(), metadata_info_size))
|
||||
return false;
|
||||
|
||||
// Decrypt the metadata info.
|
||||
aes_setkey_dec(&aes, metadata_key, 256); // AES-256
|
||||
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info.get(), metadata_info.get());
|
||||
}
|
||||
|
||||
// Load the metadata info.
|
||||
meta_info.Load(metadata_info.get());
|
||||
|
||||
// If the padding is not NULL for the key or iv fields, the metadata info
|
||||
// is not properly decrypted.
|
||||
if ((meta_info.key_pad[0] != 0x00) ||
|
||||
(meta_info.iv_pad[0] != 0x00))
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Failed to decrypt metadata info!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform AES-CTR encryption on the metadata headers.
|
||||
size_t ctr_nc_off = 0;
|
||||
u8 ctr_stream_block[0x10];
|
||||
aes_setkey_enc(&aes, meta_info.key, 128);
|
||||
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
|
||||
|
||||
// Load the metadata header.
|
||||
meta_hdr.Load(metadata_headers.get());
|
||||
|
||||
// Load the metadata section headers.
|
||||
meta_shdr.clear();
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
meta_shdr.emplace_back();
|
||||
meta_shdr.back().Load(metadata_headers.get() + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
|
||||
}
|
||||
|
||||
// Copy the decrypted data keys.
|
||||
data_keys_length = meta_hdr.key_count * 0x10;
|
||||
data_keys = std::make_unique<u8[]>(data_keys_length);
|
||||
memcpy(data_keys.get(), metadata_headers.get() + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
|
||||
|
||||
return true;
|
||||
return SCEDecrypter::LoadMetadata(keyset.erk,keyset.riv);
|
||||
}
|
||||
|
||||
bool SELFDecrypter::DecryptData()
|
||||
|
@ -1016,8 +1162,8 @@ bool SELFDecrypter::DecryptData()
|
|||
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
|
||||
|
||||
// Seek to the section data offset and read the encrypted data.
|
||||
self_f.seek(meta_shdr[i].data_offset);
|
||||
self_f.read(buf.get(), meta_shdr[i].data_size);
|
||||
sce_f.seek(meta_shdr[i].data_offset);
|
||||
sce_f.read(buf.get(), meta_shdr[i].data_size);
|
||||
|
||||
// Zero out our ctr nonce.
|
||||
memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
|
||||
|
@ -1038,7 +1184,7 @@ bool SELFDecrypter::DecryptData()
|
|||
return true;
|
||||
}
|
||||
|
||||
fs::file SELFDecrypter::MakeElf(bool isElf32)
|
||||
fs::file SELFDecrypter::MakeFile()
|
||||
{
|
||||
// Create a new ELF file.
|
||||
fs::file e = fs::make_stream<std::vector<u8>>();
|
||||
|
@ -1273,10 +1419,10 @@ extern fs::file decrypt_self(fs::file elf_or_self)
|
|||
bool isElf32 = IsSelfElf32(elf_or_self);
|
||||
|
||||
// Start the decrypter on this SELF file.
|
||||
SELFDecrypter self_dec(elf_or_self);
|
||||
SELFDecrypter self_dec(elf_or_self, isElf32);
|
||||
|
||||
// Load the SELF file headers.
|
||||
if (!self_dec.LoadHeaders(isElf32))
|
||||
if (!self_dec.LoadHeaders())
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Failed to load SELF file headers!");
|
||||
return fs::file{};
|
||||
|
@ -1297,7 +1443,7 @@ extern fs::file decrypt_self(fs::file elf_or_self)
|
|||
}
|
||||
|
||||
// Make a new ELF file from this SELF.
|
||||
return self_dec.MakeElf(isElf32);
|
||||
return self_dec.MakeFile();
|
||||
}
|
||||
|
||||
return elf_or_self;
|
||||
|
|
|
@ -338,16 +338,42 @@ struct SelfHeader
|
|||
void Show(){}
|
||||
};
|
||||
|
||||
class SELFDecrypter
|
||||
class SCEDecrypter
|
||||
{
|
||||
protected:
|
||||
// Main SELF file stream.
|
||||
const fs::file& self_f;
|
||||
const fs::file& sce_f;
|
||||
|
||||
// SCE, SELF and APP headers.
|
||||
// SCE headers.
|
||||
SceHeader sce_hdr;
|
||||
|
||||
// Metadata structs.
|
||||
MetadataInfo meta_info;
|
||||
MetadataHeader meta_hdr;
|
||||
std::vector<MetadataSectionHeader> meta_shdr;
|
||||
|
||||
// Internal data buffers.
|
||||
std::unique_ptr<u8[]> data_keys;
|
||||
u32 data_keys_length;
|
||||
std::unique_ptr<u8[]> data_buf;
|
||||
u32 data_buf_length;
|
||||
|
||||
public:
|
||||
SCEDecrypter(const fs::file& s);
|
||||
std::vector<fs::file> MakeFile();
|
||||
bool LoadHeaders();
|
||||
bool LoadMetadata(const u8 erk[32], const u8 riv[16]);
|
||||
bool DecryptData();
|
||||
};
|
||||
|
||||
class SELFDecrypter : private SCEDecrypter
|
||||
{
|
||||
// SELF, APP headers.
|
||||
SelfHeader self_hdr;
|
||||
AppInfo app_info;
|
||||
|
||||
|
||||
bool isElf32;
|
||||
|
||||
// ELF64 header and program header/section header arrays.
|
||||
Elf64_Ehdr elf64_hdr;
|
||||
std::vector<Elf64_Shdr> shdr64_arr;
|
||||
|
@ -363,25 +389,14 @@ class SELFDecrypter
|
|||
SCEVersionInfo scev_info;
|
||||
std::vector<ControlInfo> ctrlinfo_arr;
|
||||
|
||||
// Metadata structs.
|
||||
MetadataInfo meta_info;
|
||||
MetadataHeader meta_hdr;
|
||||
std::vector<MetadataSectionHeader> meta_shdr;
|
||||
|
||||
// Internal data buffers.
|
||||
std::unique_ptr<u8[]> data_keys;
|
||||
u32 data_keys_length;
|
||||
std::unique_ptr<u8[]> data_buf;
|
||||
u32 data_buf_length;
|
||||
|
||||
// Main key vault instance.
|
||||
KeyVault key_v;
|
||||
|
||||
public:
|
||||
SELFDecrypter(const fs::file& s);
|
||||
fs::file MakeElf(bool isElf32);
|
||||
bool LoadHeaders(bool isElf32);
|
||||
void ShowHeaders(bool isElf32);
|
||||
SELFDecrypter(const fs::file& s, bool isElf32) : SCEDecrypter(s), key_v(), isElf32(isElf32) {};
|
||||
fs::file MakeFile();
|
||||
bool LoadHeaders();
|
||||
void ShowHeaders();
|
||||
bool LoadMetadata();
|
||||
bool DecryptData();
|
||||
bool DecryptNPDRM(u8 *metadata, u32 metadata_size);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "Gui/PADManager.h"
|
||||
#include "Gui/AboutDialog.h"
|
||||
#include "Gui/GameViewer.h"
|
||||
|
@ -16,13 +17,16 @@
|
|||
#include "Gui/SettingsDialog.h"
|
||||
#include "Gui/MemoryStringSearcher.h"
|
||||
#include "Gui/CgDisasm.h"
|
||||
|
||||
#include "Crypto/unpkg.h"
|
||||
#include "Crypto/unself.h"
|
||||
|
||||
#include "Loader/PUP.h"
|
||||
#include "Loader/TAR.h"
|
||||
|
||||
#include "Utilities/Thread.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
#include "../Crypto/unself.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#ifndef _WIN32
|
||||
|
@ -51,6 +55,7 @@ enum IDs
|
|||
id_tools_rsx_debugger,
|
||||
id_tools_string_search,
|
||||
id_tools_decrypt_sprx_libraries,
|
||||
id_tools_install_firmware,
|
||||
id_tools_cg_disasm,
|
||||
id_help_about,
|
||||
id_update_dbg
|
||||
|
@ -111,6 +116,7 @@ MainFrame::MainFrame()
|
|||
menu_tools->Append(id_tools_string_search, "&String Search")->Enable(false);
|
||||
menu_tools->AppendSeparator();
|
||||
menu_tools->Append(id_tools_decrypt_sprx_libraries, "&Decrypt SPRX libraries");
|
||||
menu_tools->Append(id_tools_install_firmware, "&Install Firmware");
|
||||
|
||||
wxMenu* menu_help = new wxMenu();
|
||||
menubar->Append(menu_help, "&Help");
|
||||
|
@ -146,6 +152,8 @@ MainFrame::MainFrame()
|
|||
Bind(wxEVT_MENU, &MainFrame::ConfigVHDD, this, id_config_vhdd_manager);
|
||||
Bind(wxEVT_MENU, &MainFrame::ConfigSaveData, this, id_config_savedata_manager);
|
||||
Bind(wxEVT_MENU, &MainFrame::DecryptSPRXLibraries, this, id_tools_decrypt_sprx_libraries);
|
||||
Bind(wxEVT_MENU, &MainFrame::InstallFirmware, this, id_tools_install_firmware);
|
||||
|
||||
|
||||
Bind(wxEVT_MENU, &MainFrame::OpenELFCompiler, this, id_tools_compiler);
|
||||
Bind(wxEVT_MENU, &MainFrame::OpenKernelExplorer, this, id_tools_kernel_explorer);
|
||||
|
@ -408,6 +416,102 @@ void MainFrame::DecryptSPRXLibraries(wxCommandEvent& WXUNUSED(event))
|
|||
LOG_NOTICE(GENERAL, "Finished decrypting all SPRX libraries.");
|
||||
}
|
||||
|
||||
void MainFrame::InstallFirmware(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
wxFileDialog ctrl(this, L"Select PS3UPDAT.PUP file", wxEmptyString, wxEmptyString,
|
||||
"PS3 update file (PS3UPDAT.PUP)|PS3UPDAT.PUP",
|
||||
wxFD_OPEN);
|
||||
|
||||
if (ctrl.ShowModal() == wxID_CANCEL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fs::file pup_f(ctrl.GetPath().ToStdString());
|
||||
pup_object pup(pup_f);
|
||||
if (!pup) {
|
||||
LOG_ERROR(GENERAL, "Error while installing firmware: PUP file is invalid.");
|
||||
wxMessageBox("Error while installing firmware: PUP file is invalid.", "Failure!", wxOK | wxICON_ERROR, this);
|
||||
return;
|
||||
}
|
||||
|
||||
fs::file update_files_f = pup.get_file(0x300);
|
||||
tar_object update_files(update_files_f);
|
||||
auto updatefilenames = update_files.get_filenames();
|
||||
|
||||
updatefilenames.erase(std::remove_if(
|
||||
updatefilenames.begin(), updatefilenames.end(), [](std::string s) {return s.find("dev_flash_") == std::string::npos; }),
|
||||
updatefilenames.end());
|
||||
|
||||
wxProgressDialog pdlg("Firmware Installer", "Please wait, unpacking...", updatefilenames.size(), this, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
||||
|
||||
// Synchronization variable
|
||||
atomic_t<int> progress(0);
|
||||
{
|
||||
// Run asynchronously
|
||||
scope_thread worker("Firmware Installer", [&]
|
||||
{
|
||||
for (auto updatefilename : updatefilenames)
|
||||
{
|
||||
if (progress == -1) break;
|
||||
|
||||
fs::file updatefile = update_files.get_file(updatefilename);
|
||||
|
||||
SCEDecrypter self_dec(updatefile);
|
||||
self_dec.LoadHeaders();
|
||||
self_dec.LoadMetadata(SCEPKG_ERK, SCEPKG_RIV);
|
||||
self_dec.DecryptData();
|
||||
|
||||
auto dev_flash_tar_f = self_dec.MakeFile();
|
||||
if (dev_flash_tar_f.size() < 3) {
|
||||
LOG_ERROR(GENERAL, "Error while installing firmware: PUP contents are invalid.");
|
||||
wxMessageBox("Error while installing firmware: PUP contents are invalid.", "Failure!", wxOK | wxICON_ERROR, this);
|
||||
progress = -1;
|
||||
}
|
||||
|
||||
tar_object dev_flash_tar(dev_flash_tar_f[2]);
|
||||
if (!dev_flash_tar.extract(fs::get_executable_dir()))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Error while installing firmware: TAR contents are invalid.");
|
||||
wxMessageBox("Error while installing firmware: TAR contents are invalid.", "Failure!", wxOK | wxICON_ERROR, this);
|
||||
progress = -1;
|
||||
}
|
||||
|
||||
if(progress >= 0)
|
||||
progress += 1;
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for the completion
|
||||
while (std::this_thread::sleep_for(5ms), std::abs(progress) < pdlg.GetRange())
|
||||
{
|
||||
// Update progress window
|
||||
if (!pdlg.Update(static_cast<int>(progress)))
|
||||
{
|
||||
// Installation cancelled (signal with negative value)
|
||||
progress = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
update_files_f.close();
|
||||
pup_f.close();
|
||||
|
||||
if (progress > 0)
|
||||
{
|
||||
pdlg.Update(pdlg.GetRange());
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
}
|
||||
pdlg.Close();
|
||||
|
||||
if (progress > 0)
|
||||
{
|
||||
LOG_SUCCESS(GENERAL, "Successfully installed PS3 firmware.");
|
||||
wxMessageBox("Successfully installed PS3 firmware and LLE Modules!", "Success!", wxOK, this);
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::Pause(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
if(Emu.IsReady())
|
||||
|
|
|
@ -24,7 +24,6 @@ public:
|
|||
|
||||
private:
|
||||
void BootGame(wxCommandEvent& event);
|
||||
void BootGameAndRun(wxCommandEvent& event);
|
||||
void InstallPkg(wxCommandEvent& event);
|
||||
void BootElf(wxCommandEvent& event);
|
||||
void Pause(wxCommandEvent& event);
|
||||
|
@ -44,6 +43,7 @@ private:
|
|||
void OpenStringSearch(wxCommandEvent& evt);
|
||||
void OpenCgDisasm(wxCommandEvent& evt);
|
||||
void DecryptSPRXLibraries(wxCommandEvent& event);
|
||||
void InstallFirmware(wxCommandEvent& event);
|
||||
void AboutDialogHandler(wxCommandEvent& event);
|
||||
void UpdateUI(wxEvent& event);
|
||||
void OnKeyDown(wxKeyEvent& event);
|
||||
|
|
36
rpcs3/Loader/PUP.cpp
Normal file
36
rpcs3/Loader/PUP.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "PUP.h"
|
||||
|
||||
pup_object::pup_object(const fs::file& file): m_file(file)
|
||||
{
|
||||
PUPHeader m_header;
|
||||
m_file.read(m_header);
|
||||
if (m_header.magic != "SCEUF\0\0\0"_u64)
|
||||
{
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_file_tbl.resize(m_header.file_count);
|
||||
m_file.read(m_file_tbl);
|
||||
m_hash_tbl.resize(m_header.file_count);
|
||||
m_file.read(m_hash_tbl);
|
||||
}
|
||||
|
||||
fs::file pup_object::get_file(u64 entry_id)
|
||||
{
|
||||
if (!isValid) return fs::file();
|
||||
|
||||
for (PUPFileEntry file_entry : m_file_tbl)
|
||||
{
|
||||
if (file_entry.entry_id == entry_id)
|
||||
{
|
||||
std::vector<u8> file_buf(file_entry.data_length);
|
||||
m_file.seek(file_entry.data_offset);
|
||||
m_file.read(file_buf, file_entry.data_length);
|
||||
return fs::make_stream(std::move(file_buf));
|
||||
}
|
||||
}
|
||||
return fs::file();
|
||||
};
|
43
rpcs3/Loader/PUP.h
Normal file
43
rpcs3/Loader/PUP.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../Utilities/types.h"
|
||||
#include "../../Utilities/File.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
typedef struct {
|
||||
le_t<u64> magic;
|
||||
be_t<u64> package_version;
|
||||
be_t<u64> image_version;
|
||||
be_t<u64> file_count;
|
||||
be_t<u64> header_length;
|
||||
be_t<u64> data_length;
|
||||
} PUPHeader;
|
||||
|
||||
typedef struct {
|
||||
be_t<u64> entry_id;
|
||||
be_t<u64> data_offset;
|
||||
be_t<u64> data_length;
|
||||
u8 padding[8];
|
||||
} PUPFileEntry;
|
||||
|
||||
typedef struct {
|
||||
be_t<u64> entry_id;
|
||||
be_t<u8> hash[20];
|
||||
be_t<u8> padding[4];
|
||||
} PUPHashEntry;
|
||||
|
||||
class pup_object {
|
||||
const fs::file& m_file;
|
||||
bool isValid = true;
|
||||
|
||||
std::vector<PUPFileEntry> m_file_tbl;
|
||||
std::vector<PUPHashEntry> m_hash_tbl;
|
||||
|
||||
public:
|
||||
pup_object(const fs::file& file);
|
||||
|
||||
operator bool() const { return isValid; };
|
||||
|
||||
fs::file get_file(u64 entry_id);
|
||||
};
|
124
rpcs3/Loader/TAR.cpp
Normal file
124
rpcs3/Loader/TAR.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "TAR.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
tar_object::tar_object(const fs::file& file, size_t offset) : m_file(file), initial_offset(offset)
|
||||
{
|
||||
m_file.seek(initial_offset);
|
||||
largest_offset = initial_offset;
|
||||
}
|
||||
|
||||
TARHeader tar_object::read_header(u64 offset)
|
||||
{
|
||||
m_file.seek(offset);
|
||||
TARHeader header;
|
||||
m_file.read(header);
|
||||
return header;
|
||||
}
|
||||
|
||||
int octalToDecimal(int octalNumber)
|
||||
{
|
||||
int decimalNumber = 0, i = 0, rem;
|
||||
while (octalNumber != 0)
|
||||
{
|
||||
rem = octalNumber % 10;
|
||||
octalNumber /= 10;
|
||||
decimalNumber += rem * pow(8, i);
|
||||
++i;
|
||||
}
|
||||
return decimalNumber;
|
||||
}
|
||||
|
||||
std::vector<std::string> tar_object::get_filenames()
|
||||
{
|
||||
if (!isValid) return std::vector<std::string>();
|
||||
|
||||
std::vector<std::string> vec;
|
||||
get_file("");
|
||||
for (auto it = m_map.cbegin(); it != m_map.cend(); ++it)
|
||||
{
|
||||
vec.push_back(it->first);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
fs::file tar_object::get_file(std::string path)
|
||||
{
|
||||
if (!isValid || !m_file) return fs::file();
|
||||
|
||||
auto it = m_map.find(path);
|
||||
if (it != m_map.end())
|
||||
{
|
||||
TARHeader header = read_header(it->second);
|
||||
int size = octalToDecimal(atoi(header.size));
|
||||
std::vector<u8> buf(size);
|
||||
m_file.read(buf, size);
|
||||
int offset = ((m_file.pos() - initial_offset + 512 - 1) & ~(512 - 1)) + initial_offset; // Always keep the offset aligned to 512 bytes + the initial offset.
|
||||
m_file.seek(offset);
|
||||
return fs::make_stream(std::move(buf));
|
||||
}
|
||||
else //continue scanning from last file entered
|
||||
{
|
||||
while (m_file.pos() < m_file.size()) {
|
||||
TARHeader header = read_header(largest_offset);
|
||||
if (!isValid) return fs::file();
|
||||
|
||||
if (std::string(header.magic).find("ustar") != std::string::npos)
|
||||
m_map[header.name] = largest_offset;
|
||||
|
||||
int size = octalToDecimal(atoi(header.size));
|
||||
if (path.compare(header.name) == 0) { //path is equal, read file and advance offset to start of next block
|
||||
std::vector<u8> buf(size);
|
||||
m_file.read(buf, size);
|
||||
int offset = ((m_file.pos() - initial_offset + 512 - 1) & ~(512 - 1)) + initial_offset;
|
||||
m_file.seek(offset);
|
||||
largest_offset = offset;
|
||||
|
||||
return fs::make_stream(std::move(buf));
|
||||
}
|
||||
else { // just advance offset to next block
|
||||
m_file.seek(size, fs::seek_mode::seek_cur);
|
||||
int offset = ((m_file.pos() - initial_offset + 512 - 1) & ~(512 - 1)) + initial_offset;
|
||||
m_file.seek(offset);
|
||||
largest_offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
return fs::file();
|
||||
}
|
||||
}
|
||||
|
||||
bool tar_object::extract(std::string path)
|
||||
{
|
||||
if (!isValid || !m_file) return false;
|
||||
|
||||
get_file(""); //Make sure we have scanned all files
|
||||
for (auto iter : m_map)
|
||||
{
|
||||
TARHeader header = read_header(iter.second);
|
||||
if (std::string(header.name).empty()) continue;
|
||||
|
||||
switch (header.filetype) {
|
||||
case '0':
|
||||
{
|
||||
fs::file file(header.name, fs::rewrite);
|
||||
file.write(get_file(header.name).to_vector<u8>());
|
||||
break;
|
||||
}
|
||||
|
||||
case '5':
|
||||
{
|
||||
fs::create_dir(path + header.name);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_ERROR(GENERAL,"Tar loader: unknown file type: "+header.filetype);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
40
rpcs3/Loader/TAR.h
Normal file
40
rpcs3/Loader/TAR.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
typedef struct {
|
||||
char name[100];
|
||||
char dontcare[24];
|
||||
char size[12];
|
||||
char mtime[12];
|
||||
char chksum[8];
|
||||
char filetype;
|
||||
char linkname[100];
|
||||
char magic[6];
|
||||
char dontcare2[82];
|
||||
char prefix[155];
|
||||
char padding[12];
|
||||
} TARHeader;
|
||||
|
||||
class tar_object
|
||||
{
|
||||
const fs::file& m_file;
|
||||
|
||||
int initial_offset;
|
||||
int largest_offset; //we store the largest offset so we can continue to scan from there.
|
||||
std::map<std::string, u64> m_map; //maps path to offset of header of that file, so we only need to scan the entire file once.
|
||||
bool isValid = true;
|
||||
|
||||
TARHeader read_header(u64 offset);
|
||||
|
||||
public:
|
||||
tar_object(const fs::file& file, size_t offset = 0);
|
||||
|
||||
operator bool() const { return isValid; };
|
||||
|
||||
std::vector<std::string> get_filenames();
|
||||
|
||||
fs::file get_file(std::string path);
|
||||
|
||||
bool extract(std::string path); // extract all files in archive to path
|
||||
};
|
|
@ -374,6 +374,8 @@
|
|||
<ClCompile Include="Emu\System.cpp" />
|
||||
<ClCompile Include="Loader\ELF.cpp" />
|
||||
<ClCompile Include="Loader\PSF.cpp" />
|
||||
<ClCompile Include="Loader\PUP.cpp" />
|
||||
<ClCompile Include="Loader\TAR.cpp" />
|
||||
<ClCompile Include="Loader\TROPUSR.cpp" />
|
||||
<ClCompile Include="Loader\TRP.cpp" />
|
||||
<ClCompile Include="rpcs3_api.cpp" />
|
||||
|
@ -650,6 +652,8 @@
|
|||
<ClInclude Include="Emu\System.h" />
|
||||
<ClInclude Include="Loader\ELF.h" />
|
||||
<ClInclude Include="Loader\PSF.h" />
|
||||
<ClInclude Include="Loader\PUP.h" />
|
||||
<ClInclude Include="Loader\TAR.h" />
|
||||
<ClInclude Include="Loader\TROPUSR.h" />
|
||||
<ClInclude Include="Loader\TRP.h" />
|
||||
<ClInclude Include="ps3emu_api_enums.h" />
|
||||
|
|
|
@ -890,6 +890,12 @@
|
|||
<ClCompile Include="..\Utilities\sema.cpp">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Loader\PUP.cpp">
|
||||
<Filter>Loader</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Loader\TAR.cpp">
|
||||
<Filter>Loader</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
|
@ -1711,5 +1717,11 @@
|
|||
<ClInclude Include="Emu\Cell\Modules\cellOskDialog.h">
|
||||
<Filter>Emu\Cell\Modules</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Loader\PUP.h">
|
||||
<Filter>Loader</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Loader\TAR.h">
|
||||
<Filter>Loader</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Add table
Reference in a new issue