mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-04 14:01:25 +12:00
Mself / Sdata: on the fly partial decoding support (#2468)
This commit is contained in:
parent
039e295e53
commit
87fe93ee9a
8 changed files with 616 additions and 459 deletions
|
@ -140,8 +140,7 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size)
|
||||||
unsigned int range = 0xFFFFFFFF;
|
unsigned int range = 0xFFFFFFFF;
|
||||||
unsigned int code = (in[1] << 24) | (in[2] << 16) | (in[3] << 8) | in[4];
|
unsigned int code = (in[1] << 24) | (in[2] << 16) | (in[3] << 8) | in[4];
|
||||||
|
|
||||||
// TODO:: Syphurith: There was a check against the unsigned char head. if (head < 0) would always be false.. I don't know are you tried to if (head > 0x80)?
|
if (head > 0x80) // Check if we have a valid starting byte.
|
||||||
if (head < 0) // Check if we have a valid starting byte.
|
|
||||||
{
|
{
|
||||||
// The dictionary header is invalid, the data is not compressed.
|
// The dictionary header is invalid, the data is not compressed.
|
||||||
result = -1;
|
result = -1;
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#include "key_vault.h"
|
#include "key_vault.h"
|
||||||
#include "unedat.h"
|
#include "unedat.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
void generate_key(int crypto_mode, int version, unsigned char *key_final, unsigned char *iv_final, unsigned char *key, unsigned char *iv)
|
void generate_key(int crypto_mode, int version, unsigned char *key_final, unsigned char *iv_final, unsigned char *key, unsigned char *iv)
|
||||||
{
|
{
|
||||||
int mode = (int)(crypto_mode & 0xF0000000);
|
int mode = (int)(crypto_mode & 0xF0000000);
|
||||||
|
@ -100,9 +102,9 @@ bool decrypt(int hash_mode, int crypto_mode, int version, unsigned char *in, uns
|
||||||
}
|
}
|
||||||
|
|
||||||
// EDAT/SDAT functions.
|
// EDAT/SDAT functions.
|
||||||
unsigned char* dec_section(unsigned char* metadata)
|
std::tuple<u64, s32, s32> dec_section(unsigned char* metadata)
|
||||||
{
|
{
|
||||||
unsigned char *dec = new unsigned char[0x10];
|
std::array<u8, 0x10> dec;
|
||||||
dec[0x00] = (metadata[0xC] ^ metadata[0x8] ^ metadata[0x10]);
|
dec[0x00] = (metadata[0xC] ^ metadata[0x8] ^ metadata[0x10]);
|
||||||
dec[0x01] = (metadata[0xD] ^ metadata[0x9] ^ metadata[0x11]);
|
dec[0x01] = (metadata[0xD] ^ metadata[0x9] ^ metadata[0x11]);
|
||||||
dec[0x02] = (metadata[0xE] ^ metadata[0xA] ^ metadata[0x12]);
|
dec[0x02] = (metadata[0xE] ^ metadata[0xA] ^ metadata[0x12]);
|
||||||
|
@ -119,58 +121,55 @@ unsigned char* dec_section(unsigned char* metadata)
|
||||||
dec[0x0D] = (metadata[0x5] ^ metadata[0x1] ^ metadata[0x1D]);
|
dec[0x0D] = (metadata[0x5] ^ metadata[0x1] ^ metadata[0x1D]);
|
||||||
dec[0x0E] = (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]);
|
dec[0x0E] = (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]);
|
||||||
dec[0x0F] = (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]);
|
dec[0x0F] = (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]);
|
||||||
return dec;
|
|
||||||
|
u64 offset = swap64(*(u64*)&dec[0]);
|
||||||
|
s32 length = swap32(*(s32*)&dec[8]);
|
||||||
|
s32 compression_end = swap32(*(s32*)&dec[12]);
|
||||||
|
|
||||||
|
return std::make_tuple(offset, length, compression_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* get_block_key(int block, NPD_HEADER *npd)
|
std::array<u8, 0x10> get_block_key(int block, NPD_HEADER *npd)
|
||||||
{
|
{
|
||||||
unsigned char empty_key[0x10] = {};
|
unsigned char empty_key[0x10] = {};
|
||||||
unsigned char *src_key = (npd->version <= 1) ? empty_key : npd->dev_hash;
|
unsigned char *src_key = (npd->version <= 1) ? empty_key : npd->dev_hash;
|
||||||
unsigned char *dest_key = new unsigned char[0x10];
|
std::array<u8, 0x10> dest_key{};
|
||||||
memcpy(dest_key, src_key, 0xC);
|
memcpy(dest_key.data(), src_key, 0xC);
|
||||||
dest_key[0xC] = (block >> 24 & 0xFF);
|
|
||||||
dest_key[0xD] = (block >> 16 & 0xFF);
|
s32 swappedBlock = swap32(block);
|
||||||
dest_key[0xE] = (block >> 8 & 0xFF);
|
memcpy(&dest_key[0xC], &swappedBlock, sizeof(swappedBlock));
|
||||||
dest_key[0xF] = (block & 0xFF);
|
|
||||||
return dest_key;
|
return dest_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
// EDAT/SDAT decryption.
|
// for out data, allocate a buffer the size of 'edat->block_size'
|
||||||
int decrypt_data(const fs::file* in, const fs::file* out, EDAT_HEADER *edat, NPD_HEADER *npd, unsigned char* crypt_key, bool verbose)
|
// returns number of bytes written, -1 for error
|
||||||
|
s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *npd, u8* crypt_key, u32 block_num, u32 total_blocks, u64 size_left)
|
||||||
{
|
{
|
||||||
// Get metadata info and setup buffers.
|
// Get metadata info and setup buffers.
|
||||||
int block_num = (int)((edat->file_size + edat->block_size - 1) / edat->block_size);
|
const int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
|
||||||
int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
|
const int metadata_offset = 0x100;
|
||||||
int metadata_offset = 0x100;
|
|
||||||
|
|
||||||
unsigned char *enc_data;
|
std::unique_ptr<u8> enc_data;
|
||||||
unsigned char *dec_data;
|
std::unique_ptr<u8> dec_data;
|
||||||
unsigned char *b_key;
|
u8 hash[0x10] = { 0 };
|
||||||
unsigned char *iv;
|
u8 key_result[0x10] = { 0 };
|
||||||
|
u8 hash_result[0x14] = { 0 };
|
||||||
|
|
||||||
unsigned char hash[0x10];
|
u64 offset = 0;
|
||||||
unsigned char key_result[0x10];
|
u64 metadata_sec_offset = 0;
|
||||||
unsigned char hash_result[0x14];
|
s32 length = 0;
|
||||||
memset(hash, 0, 0x10);
|
s32 compression_end = 0;
|
||||||
memset(key_result, 0, 0x10);
|
|
||||||
memset(hash_result, 0, 0x14);
|
|
||||||
|
|
||||||
unsigned long long offset = 0;
|
|
||||||
unsigned long long metadata_sec_offset = 0;
|
|
||||||
int length = 0;
|
|
||||||
int compression_end = 0;
|
|
||||||
unsigned char empty_iv[0x10] = {};
|
unsigned char empty_iv[0x10] = {};
|
||||||
|
|
||||||
// Decrypt the metadata.
|
const u64 file_offset = in->pos();
|
||||||
for (int i = 0; i < block_num; i++)
|
|
||||||
{
|
|
||||||
memset(hash_result, 0, 0x14);
|
memset(hash_result, 0, 0x14);
|
||||||
|
|
||||||
|
// Decrypt the metadata.
|
||||||
if ((edat->flags & EDAT_COMPRESSED_FLAG) != 0)
|
if ((edat->flags & EDAT_COMPRESSED_FLAG) != 0)
|
||||||
{
|
{
|
||||||
metadata_sec_offset = metadata_offset + (unsigned long long) i * metadata_section_size;
|
metadata_sec_offset = metadata_offset + (unsigned long long) block_num * metadata_section_size;
|
||||||
|
|
||||||
in->seek(metadata_sec_offset);
|
in->seek(file_offset + metadata_sec_offset);
|
||||||
|
|
||||||
unsigned char metadata[0x20];
|
unsigned char metadata[0x20];
|
||||||
memset(metadata, 0, 0x20);
|
memset(metadata, 0, 0x20);
|
||||||
|
@ -186,11 +185,7 @@ int decrypt_data(const fs::file* in, const fs::file* out, EDAT_HEADER *edat, NPD
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unsigned char *result = dec_section(metadata);
|
std::tie(offset, length, compression_end) = dec_section(metadata);
|
||||||
offset = swap64(*(unsigned long long*)&result[0]);
|
|
||||||
length = swap32(*(int*)&result[8]);
|
|
||||||
compression_end = swap32(*(int*)&result[12]);
|
|
||||||
delete[] result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(hash_result, metadata, 0x10);
|
memcpy(hash_result, metadata, 0x10);
|
||||||
|
@ -198,8 +193,8 @@ int decrypt_data(const fs::file* in, const fs::file* out, EDAT_HEADER *edat, NPD
|
||||||
else if ((edat->flags & EDAT_FLAG_0x20) != 0)
|
else if ((edat->flags & EDAT_FLAG_0x20) != 0)
|
||||||
{
|
{
|
||||||
// If FLAG 0x20, the metadata precedes each data block.
|
// If FLAG 0x20, the metadata precedes each data block.
|
||||||
metadata_sec_offset = metadata_offset + (unsigned long long) i * (metadata_section_size + length);
|
metadata_sec_offset = metadata_offset + (u64) block_num * (metadata_section_size + length);
|
||||||
in->seek(metadata_sec_offset);
|
in->seek(file_offset + metadata_sec_offset);
|
||||||
|
|
||||||
unsigned char metadata[0x20];
|
unsigned char metadata[0x20];
|
||||||
memset(metadata, 0, 0x20);
|
memset(metadata, 0, 0x20);
|
||||||
|
@ -207,49 +202,46 @@ int decrypt_data(const fs::file* in, const fs::file* out, EDAT_HEADER *edat, NPD
|
||||||
memcpy(hash_result, metadata, 0x14);
|
memcpy(hash_result, metadata, 0x14);
|
||||||
|
|
||||||
// If FLAG 0x20 is set, apply custom xor.
|
// If FLAG 0x20 is set, apply custom xor.
|
||||||
int j;
|
for (int j = 0; j < 0x10; j++)
|
||||||
for (j = 0; j < 0x10; j++)
|
|
||||||
hash_result[j] = (unsigned char)(metadata[j] ^ metadata[j + 0x10]);
|
hash_result[j] = (unsigned char)(metadata[j] ^ metadata[j + 0x10]);
|
||||||
|
|
||||||
offset = metadata_sec_offset + 0x20;
|
offset = metadata_sec_offset + 0x20;
|
||||||
length = edat->block_size;
|
length = edat->block_size;
|
||||||
|
|
||||||
if ((i == (block_num - 1)) && (edat->file_size % edat->block_size))
|
if ((block_num == (total_blocks - 1)) && (edat->file_size % edat->block_size))
|
||||||
length = (int)(edat->file_size % edat->block_size);
|
length = (int)(edat->file_size % edat->block_size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
metadata_sec_offset = metadata_offset + (unsigned long long) i * metadata_section_size;
|
metadata_sec_offset = metadata_offset + (u64) block_num * metadata_section_size;
|
||||||
in->seek(metadata_sec_offset);
|
in->seek(file_offset + metadata_sec_offset);
|
||||||
|
|
||||||
in->read(hash_result, 0x10);
|
in->read(hash_result, 0x10);
|
||||||
offset = metadata_offset + (unsigned long long) i * edat->block_size + (unsigned long long) block_num * metadata_section_size;
|
offset = metadata_offset + (u64) block_num * edat->block_size + (u64) block_num * metadata_section_size;
|
||||||
length = edat->block_size;
|
length = edat->block_size;
|
||||||
|
|
||||||
if ((i == (block_num - 1)) && (edat->file_size % edat->block_size))
|
if ((block_num == (total_blocks - 1)) && (edat->file_size % edat->block_size))
|
||||||
length = (int)(edat->file_size % edat->block_size);
|
length = (int)(edat->file_size % edat->block_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locate the real data.
|
// Locate the real data.
|
||||||
int pad_length = length;
|
const int pad_length = length;
|
||||||
length = (int)((pad_length + 0xF) & 0xFFFFFFF0);
|
length = (int)((pad_length + 0xF) & 0xFFFFFFF0);
|
||||||
|
|
||||||
// Setup buffers for decryption and read the data.
|
// Setup buffers for decryption and read the data.
|
||||||
enc_data = new unsigned char[length];
|
enc_data.reset(new u8[length]{ 0 });
|
||||||
dec_data = new unsigned char[length];
|
dec_data.reset(new u8[length]{ 0 });
|
||||||
memset(enc_data, 0, length);
|
|
||||||
memset(dec_data, 0, length);
|
|
||||||
memset(hash, 0, 0x10);
|
memset(hash, 0, 0x10);
|
||||||
memset(key_result, 0, 0x10);
|
memset(key_result, 0, 0x10);
|
||||||
|
|
||||||
in->seek(offset);
|
in->seek(file_offset + offset);
|
||||||
in->read(enc_data, length);
|
in->read(enc_data.get(), length);
|
||||||
|
|
||||||
// Generate a key for the current block.
|
// Generate a key for the current block.
|
||||||
b_key = get_block_key(i, npd);
|
std::array<u8, 0x10> b_key = get_block_key(block_num, npd);
|
||||||
|
|
||||||
// Encrypt the block key with the crypto key.
|
// Encrypt the block key with the crypto key.
|
||||||
aesecb128_encrypt(crypt_key, b_key, key_result);
|
aesecb128_encrypt(crypt_key, b_key.data(), key_result);
|
||||||
if ((edat->flags & EDAT_FLAG_0x10) != 0)
|
if ((edat->flags & EDAT_FLAG_0x10) != 0)
|
||||||
aesecb128_encrypt(crypt_key, key_result, hash); // If FLAG 0x10 is set, encrypt again to get the final hash.
|
aesecb128_encrypt(crypt_key, key_result, hash); // If FLAG 0x10 is set, encrypt again to get the final hash.
|
||||||
else
|
else
|
||||||
|
@ -278,83 +270,78 @@ int decrypt_data(const fs::file* in, const fs::file* out, EDAT_HEADER *edat, NPD
|
||||||
crypto_mode |= 0x01000000;
|
crypto_mode |= 0x01000000;
|
||||||
hash_mode |= 0x01000000;
|
hash_mode |= 0x01000000;
|
||||||
// Simply copy the data without the header or the footer.
|
// Simply copy the data without the header or the footer.
|
||||||
memcpy(dec_data, enc_data, length);
|
memcpy(dec_data.get(), enc_data.get(), length);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// IV is null if NPD version is 1 or 0.
|
// IV is null if NPD version is 1 or 0.
|
||||||
iv = (npd->version <= 1) ? empty_iv : npd->digest;
|
u8* iv = (npd->version <= 1) ? empty_iv : npd->digest;
|
||||||
// Call main crypto routine on this data block.
|
// Call main crypto routine on this data block.
|
||||||
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), enc_data, dec_data, length, key_result, iv, hash, hash_result))
|
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), enc_data.get(), dec_data.get(), length, key_result, iv, hash, hash_result))
|
||||||
{
|
{
|
||||||
if (verbose)
|
LOG_ERROR(LOADER, "EDAT: Block at offset 0x%llx has invalid hash!", (u64)offset);
|
||||||
LOG_WARNING(LOADER, "EDAT: Block at offset 0x%llx has invalid hash!", (u64)offset);
|
|
||||||
|
|
||||||
delete[] enc_data;
|
|
||||||
delete[] dec_data;
|
|
||||||
delete[] b_key;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply additional compression if needed and write the decrypted data.
|
// Apply additional de-compression if needed and write the decrypted data.
|
||||||
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0) && compression_end)
|
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0) && compression_end)
|
||||||
{
|
{
|
||||||
int decomp_size = (int)edat->file_size;
|
const int res = decompress(out, dec_data.get(), edat->block_size);
|
||||||
unsigned char *decomp_data = new unsigned char[decomp_size];
|
|
||||||
memset(decomp_data, 0, decomp_size);
|
|
||||||
|
|
||||||
if (verbose)
|
size_left -= res;
|
||||||
LOG_NOTICE(LOADER, "EDAT: Decompressing data...");
|
|
||||||
|
|
||||||
int res = decompress(decomp_data, dec_data, decomp_size);
|
if (size_left == 0)
|
||||||
out->write(decomp_data, res);
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
{
|
|
||||||
LOG_NOTICE(LOADER, "EDAT: Compressed block size: %d", pad_length);
|
|
||||||
LOG_NOTICE(LOADER, "EDAT: Decompressed block size: %d", res);
|
|
||||||
}
|
|
||||||
|
|
||||||
edat->file_size -= res;
|
|
||||||
|
|
||||||
if (edat->file_size == 0)
|
|
||||||
{
|
{
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOADER, "EDAT: Decompression failed!");
|
LOG_ERROR(LOADER, "EDAT: Decompression failed!");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
LOG_NOTICE(LOADER, "EDAT: Successfully decompressed!");
|
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
delete[] decomp_data;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
out->write(dec_data, pad_length);
|
memcpy(out, dec_data.get(), pad_length);
|
||||||
|
return pad_length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] enc_data;
|
// EDAT/SDAT decryption.
|
||||||
delete[] dec_data;
|
// reset file to beginning of data before calling
|
||||||
delete[] b_key;
|
int decrypt_data(const fs::file* in, const fs::file* out, EDAT_HEADER *edat, NPD_HEADER *npd, unsigned char* crypt_key, bool verbose)
|
||||||
|
{
|
||||||
|
const int total_blocks = (int)((edat->file_size + edat->block_size - 1) / edat->block_size);
|
||||||
|
u64 size_left = (int)edat->file_size;
|
||||||
|
std::unique_ptr<u8> data(new u8[edat->block_size]);
|
||||||
|
|
||||||
|
for (int i = 0; i < total_blocks; i++)
|
||||||
|
{
|
||||||
|
in->seek(0);
|
||||||
|
memset(data.get(), 0, edat->block_size);
|
||||||
|
u64 res = decrypt_block(in, data.get(), edat, npd, crypt_key, i, total_blocks, size_left);
|
||||||
|
if (res == -1)
|
||||||
|
{
|
||||||
|
LOG_ERROR(LOADER, "EDAT: Decrypt Block failed!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
size_left -= res;
|
||||||
|
out->write(data.get(), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set file offset to beginning before calling
|
||||||
int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs::file* f, bool verbose)
|
int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs::file* f, bool verbose)
|
||||||
{
|
{
|
||||||
f->seek(0);
|
u8 header[0xA0] = { 0 };
|
||||||
unsigned char header[0xA0];
|
u8 empty_header[0xA0] = { 0 };
|
||||||
unsigned char empty_header[0xA0];
|
u8 header_hash[0x10] = { 0 };
|
||||||
unsigned char header_hash[0x10];
|
u8 metadata_hash[0x10] = { 0 };
|
||||||
unsigned char metadata_hash[0x10];
|
|
||||||
memset(header, 0, 0xA0);
|
const u64 file_offset = f->pos();
|
||||||
memset(empty_header, 0, 0xA0);
|
|
||||||
memset(header_hash, 0, 0x10);
|
|
||||||
memset(metadata_hash, 0, 0x10);
|
|
||||||
|
|
||||||
// Check NPD version and flags.
|
// Check NPD version and flags.
|
||||||
if ((npd->version == 0) || (npd->version == 1))
|
if ((npd->version == 0) || (npd->version == 1))
|
||||||
|
@ -391,12 +378,12 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||||
f->read(header, 0xA0);
|
f->read(header, 0xA0);
|
||||||
|
|
||||||
// Read in the header and metadata section hashes.
|
// Read in the header and metadata section hashes.
|
||||||
f->seek(0x90);
|
f->seek(file_offset + 0x90);
|
||||||
f->read(metadata_hash, 0x10);
|
f->read(metadata_hash, 0x10);
|
||||||
f->read(header_hash, 0x10);
|
f->read(header_hash, 0x10);
|
||||||
|
|
||||||
// Setup the hashing mode and the crypto mode used in the file.
|
// Setup the hashing mode and the crypto mode used in the file.
|
||||||
int crypto_mode = 0x1;
|
const int crypto_mode = 0x1;
|
||||||
int hash_mode = ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) == 0) ? 0x00000002 : 0x10000002;
|
int hash_mode = ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) == 0) ? 0x00000002 : 0x10000002;
|
||||||
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
|
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
|
||||||
{
|
{
|
||||||
|
@ -407,10 +394,8 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup header key and iv buffers.
|
// Setup header key and iv buffers.
|
||||||
unsigned char header_key[0x10];
|
unsigned char header_key[0x10] = { 0 };
|
||||||
unsigned char header_iv[0x10];
|
unsigned char header_iv[0x10] = { 0 };
|
||||||
memset(header_key, 0, 0x10);
|
|
||||||
memset(header_iv, 0, 0x10);
|
|
||||||
|
|
||||||
// Test the header hash (located at offset 0xA0).
|
// Test the header hash (located at offset 0xA0).
|
||||||
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), header, empty_header, 0xA0, header_key, header_iv, key, header_hash))
|
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), header, empty_header, 0xA0, header_key, header_iv, key, header_hash))
|
||||||
|
@ -427,30 +412,30 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the metadata info.
|
// Parse the metadata info.
|
||||||
int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
|
const int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
|
||||||
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0))
|
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0))
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
LOG_WARNING(LOADER, "EDAT: COMPRESSED data detected!");
|
LOG_WARNING(LOADER, "EDAT: COMPRESSED data detected!");
|
||||||
}
|
}
|
||||||
|
|
||||||
int block_num = (int)((edat->file_size + edat->block_size - 1) / edat->block_size);
|
const int block_num = (int)((edat->file_size + edat->block_size - 1) / edat->block_size);
|
||||||
int metadata_offset = 0x100;
|
const int metadata_offset = 0x100;
|
||||||
int metadata_size = metadata_section_size * block_num;
|
const int metadata_size = metadata_section_size * block_num;
|
||||||
long long metadata_section_offset = metadata_offset;
|
u64 metadata_section_offset = metadata_offset;
|
||||||
|
|
||||||
long bytes_read = 0;
|
long bytes_read = 0;
|
||||||
long bytes_to_read = metadata_size;
|
long bytes_to_read = metadata_size;
|
||||||
unsigned char *metadata = new unsigned char[metadata_size];
|
std::unique_ptr<u8> metadata(new u8[metadata_size]);
|
||||||
unsigned char *empty_metadata = new unsigned char[metadata_size];
|
std::unique_ptr<u8> empty_metadata(new u8[metadata_size]);
|
||||||
|
|
||||||
while (bytes_to_read > 0)
|
while (bytes_to_read > 0)
|
||||||
{
|
{
|
||||||
// Locate the metadata blocks.
|
// Locate the metadata blocks.
|
||||||
f->seek(metadata_section_offset);
|
f->seek(file_offset + metadata_section_offset);
|
||||||
|
|
||||||
// Read in the metadata.
|
// Read in the metadata.
|
||||||
f->read(metadata + bytes_read, metadata_section_size);
|
f->read(metadata.get() + bytes_read, metadata_section_size);
|
||||||
|
|
||||||
// Adjust sizes.
|
// Adjust sizes.
|
||||||
bytes_read += metadata_section_size;
|
bytes_read += metadata_section_size;
|
||||||
|
@ -463,7 +448,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the metadata section hash (located at offset 0x90).
|
// Test the metadata section hash (located at offset 0x90).
|
||||||
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), metadata, empty_metadata, metadata_size, header_key, header_iv, key, metadata_hash))
|
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), metadata.get(), empty_metadata.get(), metadata_size, header_key, header_iv, key, metadata_hash))
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
LOG_WARNING(LOADER, "EDAT: Metadata section hash is invalid!");
|
LOG_WARNING(LOADER, "EDAT: Metadata section hash is invalid!");
|
||||||
|
@ -472,31 +457,21 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||||
// Checking ECDSA signatures.
|
// Checking ECDSA signatures.
|
||||||
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) == 0)
|
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) == 0)
|
||||||
{
|
{
|
||||||
LOG_NOTICE(LOADER, "EDAT: Checking signatures...");
|
|
||||||
|
|
||||||
// Setup buffers.
|
// Setup buffers.
|
||||||
unsigned char metadata_signature[0x28];
|
unsigned char metadata_signature[0x28] = { 0 };
|
||||||
unsigned char header_signature[0x28];
|
unsigned char header_signature[0x28] = { 0 };
|
||||||
unsigned char signature_hash[20];
|
unsigned char signature_hash[20] = { 0 };
|
||||||
unsigned char signature_r[0x15];
|
unsigned char signature_r[0x15] = { 0 };
|
||||||
unsigned char signature_s[0x15];
|
unsigned char signature_s[0x15] = { 0 };
|
||||||
unsigned char zero_buf[0x15];
|
unsigned char zero_buf[0x15] = { 0 };
|
||||||
memset(metadata_signature, 0, 0x28);
|
|
||||||
memset(header_signature, 0, 0x28);
|
|
||||||
memset(signature_hash, 0, 20);
|
|
||||||
memset(signature_r, 0, 0x15);
|
|
||||||
memset(signature_s, 0, 0x15);
|
|
||||||
memset(zero_buf, 0, 0x15);
|
|
||||||
|
|
||||||
// Setup ECDSA curve and public key.
|
// Setup ECDSA curve and public key.
|
||||||
ecdsa_set_curve(VSH_CURVE_P, VSH_CURVE_A, VSH_CURVE_B, VSH_CURVE_N, VSH_CURVE_GX, VSH_CURVE_GY);
|
ecdsa_set_curve(VSH_CURVE_P, VSH_CURVE_A, VSH_CURVE_B, VSH_CURVE_N, VSH_CURVE_GX, VSH_CURVE_GY);
|
||||||
ecdsa_set_pub(VSH_PUB);
|
ecdsa_set_pub(VSH_PUB);
|
||||||
|
|
||||||
|
|
||||||
// Read in the metadata and header signatures.
|
// Read in the metadata and header signatures.
|
||||||
f->seek(0xB0);
|
f->seek(file_offset + 0xB0);
|
||||||
f->read(metadata_signature, 0x28);
|
f->read(metadata_signature, 0x28);
|
||||||
f->seek(0xD8);
|
|
||||||
f->read(header_signature, 0x28);
|
f->read(header_signature, 0x28);
|
||||||
|
|
||||||
// Checking metadata signature.
|
// Checking metadata signature.
|
||||||
|
@ -511,14 +486,13 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||||
if ((edat->flags & EDAT_FLAG_0x20) != 0) //Sony failed again, they used buffer from 0x100 with half size of real metadata.
|
if ((edat->flags & EDAT_FLAG_0x20) != 0) //Sony failed again, they used buffer from 0x100 with half size of real metadata.
|
||||||
{
|
{
|
||||||
int metadata_buf_size = block_num * 0x10;
|
int metadata_buf_size = block_num * 0x10;
|
||||||
unsigned char *metadata_buf = new unsigned char[metadata_buf_size];
|
std::unique_ptr<u8> metadata_buf(new u8[metadata_buf_size]);
|
||||||
f->seek(metadata_offset);
|
f->seek(file_offset + metadata_offset);
|
||||||
f->read(metadata_buf, metadata_buf_size);
|
f->read(metadata_buf.get(), metadata_buf_size);
|
||||||
sha1(metadata_buf, metadata_buf_size, signature_hash);
|
sha1(metadata_buf.get(), metadata_buf_size, signature_hash);
|
||||||
delete[] metadata_buf;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sha1(metadata, metadata_size, signature_hash);
|
sha1(metadata.get(), metadata_size, signature_hash);
|
||||||
|
|
||||||
if (!ecdsa_verify(signature_hash, signature_r, signature_s))
|
if (!ecdsa_verify(signature_hash, signature_r, signature_s))
|
||||||
{
|
{
|
||||||
|
@ -526,11 +500,8 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||||
if (((unsigned long long)edat->block_size * block_num) > 0x100000000)
|
if (((unsigned long long)edat->block_size * block_num) > 0x100000000)
|
||||||
LOG_WARNING(LOADER, "EDAT: *Due to large file size, metadata signature status may be incorrect!");
|
LOG_WARNING(LOADER, "EDAT: *Due to large file size, metadata signature status may be incorrect!");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
LOG_NOTICE(LOADER, "EDAT: Metadata signature is valid!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Checking header signature.
|
// Checking header signature.
|
||||||
// Setup header signature r and s.
|
// Setup header signature r and s.
|
||||||
memset(signature_r, 0, 0x15);
|
memset(signature_r, 0, 0x15);
|
||||||
|
@ -544,23 +515,16 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||||
{
|
{
|
||||||
// Setup header signature hash.
|
// Setup header signature hash.
|
||||||
memset(signature_hash, 0, 20);
|
memset(signature_hash, 0, 20);
|
||||||
unsigned char *header_buf = new unsigned char[0xD8];
|
std::unique_ptr<u8> header_buf(new u8[0xD8]);
|
||||||
f->seek(0x00);
|
f->seek(file_offset);
|
||||||
f->read(header_buf, 0xD8);
|
f->read(header_buf.get(), 0xD8);
|
||||||
sha1(header_buf, 0xD8, signature_hash );
|
sha1(header_buf.get(), 0xD8, signature_hash );
|
||||||
delete[] header_buf;
|
|
||||||
|
|
||||||
if (ecdsa_verify(signature_hash, signature_r, signature_s))
|
if (!ecdsa_verify(signature_hash, signature_r, signature_s))
|
||||||
LOG_NOTICE(LOADER, "EDAT: Header signature is valid!");
|
|
||||||
else
|
|
||||||
LOG_WARNING(LOADER, "EDAT: Header signature is invalid!");
|
LOG_WARNING(LOADER, "EDAT: Header signature is invalid!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup.
|
|
||||||
delete[] metadata;
|
|
||||||
delete[] empty_metadata;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,16 +533,14 @@ int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEA
|
||||||
int title_hash_result = 0;
|
int title_hash_result = 0;
|
||||||
int dev_hash_result = 0;
|
int dev_hash_result = 0;
|
||||||
|
|
||||||
int file_name_length = (int) strlen(file_name);
|
const int file_name_length = (int) strlen(file_name);
|
||||||
unsigned char *buf = new unsigned char[0x30 + file_name_length];
|
std::unique_ptr<u8> buf(new u8[0x30 + file_name_length]);
|
||||||
unsigned char dev[0x60];
|
unsigned char dev[0x60] = { 0 };
|
||||||
unsigned char key[0x10];
|
unsigned char key[0x10] = { 0 };
|
||||||
memset(dev, 0, 0x60);
|
|
||||||
memset(key, 0, 0x10);
|
|
||||||
|
|
||||||
// Build the title buffer (content_id + file_name).
|
// Build the title buffer (content_id + file_name).
|
||||||
memcpy(buf, npd->content_id, 0x30);
|
memcpy(buf.get(), npd->content_id, 0x30);
|
||||||
memcpy(buf + 0x30, file_name, file_name_length);
|
memcpy(buf.get() + 0x30, file_name, file_name_length);
|
||||||
|
|
||||||
// Build the dev buffer (first 0x60 bytes of NPD header in big-endian).
|
// Build the dev buffer (first 0x60 bytes of NPD header in big-endian).
|
||||||
memcpy(dev, npd, 0x60);
|
memcpy(dev, npd, 0x60);
|
||||||
|
@ -592,7 +554,7 @@ int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEA
|
||||||
memcpy(dev + 0xC, &type, 4);
|
memcpy(dev + 0xC, &type, 4);
|
||||||
|
|
||||||
// Hash with NPDRM_OMAC_KEY_3 and compare with title_hash.
|
// Hash with NPDRM_OMAC_KEY_3 and compare with title_hash.
|
||||||
title_hash_result = cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf, 0x30 + file_name_length, npd->title_hash, 0x10);
|
title_hash_result = cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf.get(), 0x30 + file_name_length, npd->title_hash, 0x10);
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
{
|
{
|
||||||
|
@ -624,7 +586,7 @@ int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEA
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Generate klicensee xor key.
|
// Generate klicensee xor key.
|
||||||
xor_key(key, klicensee, NP_OMAC_KEY_2, 0x10);
|
xor_key(key, klicensee, NP_OMAC_KEY_2);
|
||||||
|
|
||||||
// Hash with generated key and compare with dev_hash.
|
// Hash with generated key and compare with dev_hash.
|
||||||
dev_hash_result = cmac_hash_compare(key, 0x10, dev, 0x60, npd->dev_hash, 0x10);
|
dev_hash_result = cmac_hash_compare(key, 0x10, dev, 0x60, npd->dev_hash, 0x10);
|
||||||
|
@ -638,102 +600,100 @@ int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] buf;
|
|
||||||
|
|
||||||
return (title_hash_result && dev_hash_result);
|
return (title_hash_result && dev_hash_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool extract_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, unsigned char* rifkey, bool verbose)
|
void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& EDAT)
|
||||||
{
|
{
|
||||||
// Setup NPD and EDAT/SDAT structs.
|
|
||||||
NPD_HEADER *NPD = new NPD_HEADER();
|
|
||||||
EDAT_HEADER *EDAT = new EDAT_HEADER();
|
|
||||||
|
|
||||||
// Read in the NPD and EDAT/SDAT headers.
|
|
||||||
char npd_header[0x80];
|
char npd_header[0x80];
|
||||||
char edat_header[0x10];
|
char edat_header[0x10];
|
||||||
input->read(npd_header, sizeof(npd_header));
|
input->read(npd_header, sizeof(npd_header));
|
||||||
input->read(edat_header, sizeof(edat_header));
|
input->read(edat_header, sizeof(edat_header));
|
||||||
|
|
||||||
memcpy(NPD->magic, npd_header, 4);
|
memcpy(&NPD.magic, npd_header, 4);
|
||||||
NPD->version = swap32(*(int*)&npd_header[4]);
|
NPD.version = swap32(*(int*)&npd_header[4]);
|
||||||
NPD->license = swap32(*(int*)&npd_header[8]);
|
NPD.license = swap32(*(int*)&npd_header[8]);
|
||||||
NPD->type = swap32(*(int*)&npd_header[12]);
|
NPD.type = swap32(*(int*)&npd_header[12]);
|
||||||
memcpy(NPD->content_id, (unsigned char*)&npd_header[16], 0x30);
|
memcpy(NPD.content_id, (unsigned char*)&npd_header[16], 0x30);
|
||||||
memcpy(NPD->digest, (unsigned char*)&npd_header[64], 0x10);
|
memcpy(NPD.digest, (unsigned char*)&npd_header[64], 0x10);
|
||||||
memcpy(NPD->title_hash, (unsigned char*)&npd_header[80], 0x10);
|
memcpy(NPD.title_hash, (unsigned char*)&npd_header[80], 0x10);
|
||||||
memcpy(NPD->dev_hash, (unsigned char*)&npd_header[96], 0x10);
|
memcpy(NPD.dev_hash, (unsigned char*)&npd_header[96], 0x10);
|
||||||
NPD->unk1 = swap64(*(u64*)&npd_header[112]);
|
NPD.unk1 = swap64(*(u64*)&npd_header[112]);
|
||||||
NPD->unk2 = swap64(*(u64*)&npd_header[120]);
|
NPD.unk2 = swap64(*(u64*)&npd_header[120]);
|
||||||
|
|
||||||
unsigned char npd_magic[4] = {0x4E, 0x50, 0x44, 0x00}; //NPD0
|
EDAT.flags = swap32(*(int*)&edat_header[0]);
|
||||||
if (memcmp(NPD->magic, npd_magic, 4))
|
EDAT.block_size = swap32(*(int*)&edat_header[4]);
|
||||||
{
|
EDAT.file_size = swap64(*(u64*)&edat_header[8]);
|
||||||
LOG_ERROR(LOADER, "EDAT: %s has invalid NPD header or already decrypted.", input_file_name);
|
|
||||||
delete NPD;
|
|
||||||
delete EDAT;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EDAT->flags = swap32(*(int*)&edat_header[0]);
|
bool extract_all_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, unsigned char* rifkey, bool verbose)
|
||||||
EDAT->block_size = swap32(*(int*)&edat_header[4]);
|
{
|
||||||
EDAT->file_size = swap64(*(u64*)&edat_header[8]);
|
// Setup NPD and EDAT/SDAT structs.
|
||||||
|
NPD_HEADER NPD;
|
||||||
|
EDAT_HEADER EDAT;
|
||||||
|
|
||||||
|
// Read in the NPD and EDAT/SDAT headers.
|
||||||
|
read_npd_edat_header(input, NPD, EDAT);
|
||||||
|
|
||||||
|
unsigned char npd_magic[4] = {0x4E, 0x50, 0x44, 0x00}; //NPD0
|
||||||
|
if (memcmp(&NPD.magic, npd_magic, 4))
|
||||||
|
{
|
||||||
|
LOG_ERROR(LOADER, "EDAT: %s has invalid NPD header or already decrypted.", input_file_name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
{
|
{
|
||||||
LOG_NOTICE(LOADER, "NPD HEADER");
|
LOG_NOTICE(LOADER, "NPD HEADER");
|
||||||
LOG_NOTICE(LOADER, "NPD version: %d", NPD->version);
|
LOG_NOTICE(LOADER, "NPD version: %d", NPD.version);
|
||||||
LOG_NOTICE(LOADER, "NPD license: %d", NPD->license);
|
LOG_NOTICE(LOADER, "NPD license: %d", NPD.license);
|
||||||
LOG_NOTICE(LOADER, "NPD type: %d", NPD->type);
|
LOG_NOTICE(LOADER, "NPD type: %d", NPD.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set decryption key.
|
// Set decryption key.
|
||||||
unsigned char key[0x10];
|
u8 key[0x10] = { 0 };
|
||||||
memset(key, 0, 0x10);
|
|
||||||
|
|
||||||
// Check EDAT/SDAT flag.
|
// Check EDAT/SDAT flag.
|
||||||
if ((EDAT->flags & SDAT_FLAG) == SDAT_FLAG)
|
if ((EDAT.flags & SDAT_FLAG) == SDAT_FLAG)
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
{
|
{
|
||||||
LOG_NOTICE(LOADER, "SDAT HEADER");
|
LOG_NOTICE(LOADER, "SDAT HEADER");
|
||||||
LOG_NOTICE(LOADER, "SDAT flags: 0x%08X", EDAT->flags);
|
LOG_NOTICE(LOADER, "SDAT flags: 0x%08X", EDAT.flags);
|
||||||
LOG_NOTICE(LOADER, "SDAT block size: 0x%08X", EDAT->block_size);
|
LOG_NOTICE(LOADER, "SDAT block size: 0x%08X", EDAT.block_size);
|
||||||
LOG_NOTICE(LOADER, "SDAT file size: 0x%08X", (u64)EDAT->file_size);
|
LOG_NOTICE(LOADER, "SDAT file size: 0x%08X", (u64)EDAT.file_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate SDAT key.
|
// Generate SDAT key.
|
||||||
xor_key(key, NPD->dev_hash, SDAT_KEY, 0x10);
|
xor_key(key, NPD.dev_hash, SDAT_KEY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
{
|
{
|
||||||
LOG_NOTICE(LOADER, "EDAT HEADER");
|
LOG_NOTICE(LOADER, "EDAT HEADER");
|
||||||
LOG_NOTICE(LOADER, "EDAT flags: 0x%08X", EDAT->flags);
|
LOG_NOTICE(LOADER, "EDAT flags: 0x%08X", EDAT.flags);
|
||||||
LOG_NOTICE(LOADER, "EDAT block size: 0x%08X", EDAT->block_size);
|
LOG_NOTICE(LOADER, "EDAT block size: 0x%08X", EDAT.block_size);
|
||||||
LOG_NOTICE(LOADER, "EDAT file size: 0x%08X", (u64)EDAT->file_size);
|
LOG_NOTICE(LOADER, "EDAT file size: 0x%08X", (u64)EDAT.file_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform header validation (EDAT only).
|
// Perform header validation (EDAT only).
|
||||||
char real_file_name[MAX_PATH];
|
char real_file_name[MAX_PATH];
|
||||||
extract_file_name(input_file_name, real_file_name);
|
extract_file_name(input_file_name, real_file_name);
|
||||||
if (!validate_npd_hashes(real_file_name, devklic, NPD, verbose))
|
if (!validate_npd_hashes(real_file_name, devklic, &NPD, verbose))
|
||||||
{
|
{
|
||||||
// Ignore header validation in DEBUG data.
|
// Ignore header validation in DEBUG data.
|
||||||
if ((EDAT->flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
|
if ((EDAT.flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOADER, "EDAT: NPD hash validation failed!");
|
LOG_ERROR(LOADER, "EDAT: NPD hash validation failed!");
|
||||||
delete NPD;
|
|
||||||
delete EDAT;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select EDAT key.
|
// Select EDAT key.
|
||||||
if ((NPD->license & 0x3) == 0x3) // Type 3: Use supplied devklic.
|
if ((NPD.license & 0x3) == 0x3) // Type 3: Use supplied devklic.
|
||||||
memcpy(key, devklic, 0x10);
|
memcpy(key, devklic, 0x10);
|
||||||
else if ((NPD->license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key).
|
else if ((NPD.license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key).
|
||||||
{
|
{
|
||||||
memcpy(key, rifkey, 0x10);
|
memcpy(key, rifkey, 0x10);
|
||||||
|
|
||||||
|
@ -751,16 +711,12 @@ bool extract_data(const fs::file* input, const fs::file* output, const char* inp
|
||||||
if (!test)
|
if (!test)
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOADER, "EDAT: A valid RAP file is needed for this EDAT file!");
|
LOG_ERROR(LOADER, "EDAT: A valid RAP file is needed for this EDAT file!");
|
||||||
delete NPD;
|
|
||||||
delete EDAT;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((NPD->license & 0x1) == 0x1) // Type 1: Use network activation.
|
else if ((NPD.license & 0x1) == 0x1) // Type 1: Use network activation.
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOADER, "EDAT: Network license not supported!");
|
LOG_ERROR(LOADER, "EDAT: Network license not supported!");
|
||||||
delete NPD;
|
|
||||||
delete EDAT;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,45 +741,32 @@ bool extract_data(const fs::file* input, const fs::file* output, const char* inp
|
||||||
LOG_NOTICE(LOADER, "%02X", key[i]);
|
LOG_NOTICE(LOADER, "%02X", key[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_NOTICE(LOADER, "EDAT: Parsing data...");
|
input->seek(0);
|
||||||
if (check_data(key, EDAT, NPD, input, verbose))
|
if (check_data(key, &EDAT, &NPD, input, verbose))
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOADER, "EDAT: Data parsing failed!");
|
LOG_ERROR(LOADER, "EDAT: Data parsing failed!");
|
||||||
delete NPD;
|
|
||||||
delete EDAT;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
LOG_NOTICE(LOADER, "EDAT: Data successfully parsed!");
|
|
||||||
|
|
||||||
LOG_NOTICE(LOADER, "EDAT: Decrypting data...");
|
input->seek(0);
|
||||||
if (decrypt_data(input, output, EDAT, NPD, key, verbose))
|
if (decrypt_data(input, output, &EDAT, &NPD, key, verbose))
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOADER, "EDAT: Data decryption failed!");
|
LOG_ERROR(LOADER, "EDAT: Data decryption failed!");
|
||||||
delete NPD;
|
|
||||||
delete EDAT;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
LOG_NOTICE(LOADER, "EDAT: Data successfully decrypted!");
|
|
||||||
|
|
||||||
delete NPD;
|
|
||||||
delete EDAT;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DecryptEDAT(const std::string& input_file_name, const std::string& output_file_name, int mode, const std::string& rap_file_name, unsigned char *custom_klic, bool verbose)
|
// Decrypts full file
|
||||||
|
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose)
|
||||||
{
|
{
|
||||||
// Prepare the files.
|
// Prepare the files.
|
||||||
fs::file input(input_file_name);
|
input.seek(0);
|
||||||
fs::file output(output_file_name, fs::rewrite);
|
|
||||||
|
|
||||||
// Set keys (RIF and DEVKLIC).
|
// Set keys (RIF and DEVKLIC).
|
||||||
unsigned char rifkey[0x10];
|
unsigned char rifkey[0x10] = { 0 };
|
||||||
unsigned char devklic[0x10];
|
unsigned char devklic[0x10] = { 0 };
|
||||||
memset(rifkey, 0, 0x10);
|
|
||||||
memset(devklic, 0, 0x10);
|
|
||||||
|
|
||||||
// Select the EDAT key mode.
|
// Select the EDAT key mode.
|
||||||
switch (mode)
|
switch (mode)
|
||||||
|
@ -858,21 +801,13 @@ int DecryptEDAT(const std::string& input_file_name, const std::string& output_fi
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOADER, "EDAT: Invalid custom klic!");
|
LOG_ERROR(LOADER, "EDAT: Invalid custom klic!");
|
||||||
return -1;
|
return fs::file{};
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(LOADER, "EDAT: Invalid mode!");
|
LOG_ERROR(LOADER, "EDAT: Invalid mode!");
|
||||||
return -1;
|
return fs::file{};
|
||||||
}
|
|
||||||
|
|
||||||
// Check the input/output files.
|
|
||||||
if (!input || !output)
|
|
||||||
{
|
|
||||||
LOG_ERROR(LOADER, "EDAT: Failed to open files!");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the RAP file, if provided.
|
// Read the RAP file, if provided.
|
||||||
|
@ -880,8 +815,7 @@ int DecryptEDAT(const std::string& input_file_name, const std::string& output_fi
|
||||||
{
|
{
|
||||||
fs::file rap(rap_file_name);
|
fs::file rap(rap_file_name);
|
||||||
|
|
||||||
unsigned char rapkey[0x10];
|
unsigned char rapkey[0x10] = { 0 };
|
||||||
memset(rapkey, 0, 0x10);
|
|
||||||
|
|
||||||
rap.read(rapkey, 0x10);
|
rap.read(rapkey, 0x10);
|
||||||
|
|
||||||
|
@ -889,12 +823,92 @@ int DecryptEDAT(const std::string& input_file_name, const std::string& output_fi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the bad output file if any errors arise.
|
// Delete the bad output file if any errors arise.
|
||||||
if (extract_data(&input, &output, input_file_name.c_str(), devklic, rifkey, verbose))
|
fs::file output = fs::make_stream<std::vector<u8>>();
|
||||||
|
if (extract_all_data(&input, &output, input_file_name.c_str(), devklic, rifkey, verbose))
|
||||||
{
|
{
|
||||||
output.release();
|
output.release();
|
||||||
fs::remove_file(output_file_name);
|
return fs::file{};
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output.seek(0);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDATADecrypter::SDATADecrypter(fs::file&& input, u64 offset)
|
||||||
|
: sdata_file(std::move(input)), file_offset(offset)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDATADecrypter::ReadHeader()
|
||||||
|
{
|
||||||
|
sdata_file.seek(file_offset);
|
||||||
|
// Read in the NPD and EDAT/SDAT headers.
|
||||||
|
read_npd_edat_header(&sdata_file, npdHeader, edatHeader);
|
||||||
|
|
||||||
|
unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0
|
||||||
|
if (memcmp(&npdHeader.magic, npd_magic, 4))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for SDAT flag.
|
||||||
|
if ((edatHeader.flags & SDAT_FLAG) != SDAT_FLAG)
|
||||||
|
{
|
||||||
|
fmt::raw_error("Only SDATA decyption supported in sdatadecrypter");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Generate SDAT key.
|
||||||
|
xor_key(dec_key.data(), npdHeader.dev_hash, SDAT_KEY);
|
||||||
|
|
||||||
|
sdata_file.seek(file_offset);
|
||||||
|
|
||||||
|
// k the ecdsa_verify function in this check_data function takes a ridiculous amount of time
|
||||||
|
// like it slows down load time by a factor of x20, at least, so its ignored for now
|
||||||
|
|
||||||
|
/*if (check_data(dec_key.data(), &edatHeader, &npdHeader, &sdata_file, false))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
file_size = edatHeader.file_size;
|
||||||
|
total_blocks = (u32)((edatHeader.file_size + edatHeader.block_size - 1) / edatHeader.block_size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 SDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
|
||||||
|
{
|
||||||
|
if (pos > edatHeader.file_size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// now we need to offset things to account for the actual 'range' requested
|
||||||
|
const u64 startOffset = pos % edatHeader.block_size;
|
||||||
|
|
||||||
|
const u32 num_blocks = static_cast<u32>(std::ceil((startOffset + size) / (double)edatHeader.block_size));
|
||||||
|
const u64 bufSize = num_blocks*edatHeader.block_size;
|
||||||
|
if (data_buf_size < (bufSize))
|
||||||
|
{
|
||||||
|
data_buf.reset(new u8[bufSize]);
|
||||||
|
data_buf_size = bufSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find and decrypt block range covering pos + size
|
||||||
|
const u32 starting_block = static_cast<u32>(pos / edatHeader.block_size);
|
||||||
|
u64 writeOffset = 0;
|
||||||
|
for (u32 i = starting_block; i < (starting_block + num_blocks); ++i)
|
||||||
|
{
|
||||||
|
sdata_file.seek(file_offset);
|
||||||
|
u64 res = decrypt_block(&sdata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, dec_key.data(), i, total_blocks, edatHeader.file_size);
|
||||||
|
if (res == -1)
|
||||||
|
{
|
||||||
|
LOG_ERROR(LOADER, "Error Decrypting data");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
writeOffset += res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 bytesWrote = std::min<u64>(writeOffset - startOffset, size);
|
||||||
|
|
||||||
|
memmove(data, &data_buf[startOffset], bytesWrote);
|
||||||
|
return bytesWrote;
|
||||||
|
}
|
||||||
|
|
|
@ -4,33 +4,95 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#define SDAT_FLAG 0x01000000
|
constexpr u32 SDAT_FLAG = 0x01000000;
|
||||||
#define EDAT_COMPRESSED_FLAG 0x00000001
|
constexpr u32 EDAT_COMPRESSED_FLAG = 0x00000001;
|
||||||
#define EDAT_FLAG_0x02 0x00000002
|
constexpr u32 EDAT_FLAG_0x02 = 0x00000002;
|
||||||
#define EDAT_ENCRYPTED_KEY_FLAG 0x00000008
|
constexpr u32 EDAT_ENCRYPTED_KEY_FLAG = 0x00000008;
|
||||||
#define EDAT_FLAG_0x10 0x00000010
|
constexpr u32 EDAT_FLAG_0x10 = 0x00000010;
|
||||||
#define EDAT_FLAG_0x20 0x00000020
|
constexpr u32 EDAT_FLAG_0x20 = 0x00000020;
|
||||||
#define EDAT_DEBUG_DATA_FLAG 0x80000000
|
constexpr u32 EDAT_DEBUG_DATA_FLAG = 0x80000000;
|
||||||
|
|
||||||
typedef struct
|
struct NPD_HEADER
|
||||||
{
|
{
|
||||||
unsigned char magic[4];
|
u32 magic;
|
||||||
int version;
|
s32 version;
|
||||||
int license;
|
s32 license;
|
||||||
int type;
|
s32 type;
|
||||||
unsigned char content_id[0x30];
|
u8 content_id[0x30];
|
||||||
unsigned char digest[0x10];
|
u8 digest[0x10];
|
||||||
unsigned char title_hash[0x10];
|
u8 title_hash[0x10];
|
||||||
unsigned char dev_hash[0x10];
|
u8 dev_hash[0x10];
|
||||||
unsigned long long unk1;
|
u64 unk1;
|
||||||
unsigned long long unk2;
|
u64 unk2;
|
||||||
} NPD_HEADER;
|
};
|
||||||
|
|
||||||
typedef struct
|
struct EDAT_HEADER
|
||||||
{
|
{
|
||||||
int flags;
|
s32 flags;
|
||||||
int block_size;
|
s32 block_size;
|
||||||
unsigned long long file_size;
|
u64 file_size;
|
||||||
} EDAT_HEADER;
|
};
|
||||||
|
|
||||||
int DecryptEDAT(const std::string& input_file_name, const std::string& output_file_name, int mode, const std::string& rap_file_name, unsigned char *custom_klic, bool verbose);
|
// Decrypts full file, or null/empty file
|
||||||
|
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose);
|
||||||
|
|
||||||
|
struct SDATADecrypter final : fs::file_base
|
||||||
|
{
|
||||||
|
// file stream
|
||||||
|
const fs::file sdata_file;
|
||||||
|
const u64 file_offset;
|
||||||
|
u64 file_size{0};
|
||||||
|
u32 total_blocks{0};
|
||||||
|
u64 pos{0};
|
||||||
|
|
||||||
|
NPD_HEADER npdHeader;
|
||||||
|
EDAT_HEADER edatHeader;
|
||||||
|
|
||||||
|
// Internal data buffers.
|
||||||
|
std::unique_ptr<u8[]> data_buf;
|
||||||
|
u64 data_buf_size{0};
|
||||||
|
|
||||||
|
std::array<u8, 0x10> dec_key{};
|
||||||
|
public:
|
||||||
|
SDATADecrypter(fs::file&& input, u64 offset=0);
|
||||||
|
~SDATADecrypter() override {}
|
||||||
|
// false if invalid
|
||||||
|
bool ReadHeader();
|
||||||
|
u64 ReadData(u64 pos, u8* data, u64 size);
|
||||||
|
|
||||||
|
fs::stat_t stat() override
|
||||||
|
{
|
||||||
|
fs::stat_t stats;
|
||||||
|
stats.is_directory = false;
|
||||||
|
stats.is_writable = false;
|
||||||
|
stats.size = file_size;
|
||||||
|
stats.atime = -1;
|
||||||
|
stats.ctime = -1;
|
||||||
|
stats.mtime = -1;
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
bool trunc(u64 length) override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
u64 read(void* buffer, u64 size) override
|
||||||
|
{
|
||||||
|
u64 bytesRead = ReadData(pos, (u8*)buffer, size);
|
||||||
|
pos += bytesRead;
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
u64 write(const void* buffer, u64 size) override
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 seek(s64 offset, fs::seek_mode whence) override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
whence == fs::seek_set ? pos = offset :
|
||||||
|
whence == fs::seek_cur ? pos = offset + pos :
|
||||||
|
whence == fs::seek_end ? pos = offset + size() :
|
||||||
|
(fmt::raw_error("SDATADecrypter::seek(): invalid whence"), 0);
|
||||||
|
}
|
||||||
|
u64 size() override { return file_size; }
|
||||||
|
};
|
||||||
|
|
|
@ -9,28 +9,10 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
// Auxiliary functions (endian swap, xor and prng).
|
// Auxiliary functions (endian swap, xor and prng).
|
||||||
u16 swap16(u16 i)
|
|
||||||
{
|
|
||||||
return ((i & 0xFF00) >> 8) | ((i & 0xFF) << 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 swap32(u32 i)
|
void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2)
|
||||||
{
|
{
|
||||||
return ((i & 0xFF000000) >> 24) | ((i & 0xFF0000) >> 8) | ((i & 0xFF00) << 8) | ((i & 0xFF) << 24);
|
for(int i = 0; i < 0x10; i++)
|
||||||
}
|
|
||||||
|
|
||||||
u64 swap64(u64 i)
|
|
||||||
{
|
|
||||||
return ((i & 0x00000000000000ff) << 56) | ((i & 0x000000000000ff00) << 40) |
|
|
||||||
((i & 0x0000000000ff0000) << 24) | ((i & 0x00000000ff000000) << 8) |
|
|
||||||
((i & 0x000000ff00000000) >> 8) | ((i & 0x0000ff0000000000) >> 24) |
|
|
||||||
((i & 0x00ff000000000000) >> 40) | ((i & 0xff00000000000000) >> 56);
|
|
||||||
}
|
|
||||||
|
|
||||||
void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < size; i++)
|
|
||||||
{
|
{
|
||||||
dest[i] = src1[i] ^ src2[i];
|
dest[i] = src1[i] ^ src2[i];
|
||||||
}
|
}
|
||||||
|
@ -38,16 +20,10 @@ void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2, int
|
||||||
|
|
||||||
void prng(unsigned char *dest, int size)
|
void prng(unsigned char *dest, int size)
|
||||||
{
|
{
|
||||||
unsigned char *buffer = new unsigned char[size];
|
|
||||||
srand((u32)time(0));
|
srand((u32)time(0));
|
||||||
|
|
||||||
int i;
|
for(int i = 0; i < size; i++)
|
||||||
for(i = 0; i < size; i++)
|
dest[i] = (unsigned char)(rand() & 0xFF);
|
||||||
buffer[i] = (unsigned char)(rand() & 0xFF);
|
|
||||||
|
|
||||||
memcpy(dest, buffer, size);
|
|
||||||
|
|
||||||
delete[] buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hex string conversion auxiliary functions.
|
// Hex string conversion auxiliary functions.
|
||||||
|
@ -147,22 +123,11 @@ void aesecb128_encrypt(unsigned char *key, unsigned char *in, unsigned char *out
|
||||||
|
|
||||||
bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len)
|
bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len)
|
||||||
{
|
{
|
||||||
unsigned char *out = new unsigned char[key_len];
|
std::unique_ptr<u8> out(new u8[key_len]);
|
||||||
|
|
||||||
sha1_hmac(key, key_len, in, in_len, out);
|
sha1_hmac(key, key_len, in, in_len, out.get());
|
||||||
|
|
||||||
for (int i = 0; i < hash_len; i++)
|
return std::memcmp(out.get(), hash, hash_len) == 0;
|
||||||
{
|
|
||||||
if (out[i] != hash[i])
|
|
||||||
{
|
|
||||||
delete[] out;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] out;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash)
|
void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash)
|
||||||
|
@ -172,24 +137,13 @@ void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_
|
||||||
|
|
||||||
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len)
|
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len)
|
||||||
{
|
{
|
||||||
unsigned char *out = new unsigned char[key_len];
|
std::unique_ptr<u8> out(new u8[key_len]);
|
||||||
|
|
||||||
aes_context ctx;
|
aes_context ctx;
|
||||||
aes_setkey_enc(&ctx, key, 128);
|
aes_setkey_enc(&ctx, key, 128);
|
||||||
aes_cmac(&ctx, in_len, in, out);
|
aes_cmac(&ctx, in_len, in, out.get());
|
||||||
|
|
||||||
for (int i = 0; i < hash_len; i++)
|
return std::memcmp(out.get(), hash, hash_len) == 0;
|
||||||
{
|
|
||||||
if (out[i] != hash[i])
|
|
||||||
{
|
|
||||||
delete[] out;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] out;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash)
|
void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash)
|
||||||
|
|
|
@ -15,10 +15,40 @@
|
||||||
#include "ec.h"
|
#include "ec.h"
|
||||||
|
|
||||||
// Auxiliary functions (endian swap, xor, prng and file name).
|
// Auxiliary functions (endian swap, xor, prng and file name).
|
||||||
u16 swap16(u16 i);
|
inline u16 swap16(u16 i)
|
||||||
u32 swap32(u32 i);
|
{
|
||||||
u64 swap64(u64 i);
|
#if defined(__GNUG__)
|
||||||
void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size);
|
return __builtin_bswap16(i);
|
||||||
|
#else
|
||||||
|
return _byteswap_ushort(i);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u32 swap32(u32 i)
|
||||||
|
{
|
||||||
|
#if defined(__GNUG__)
|
||||||
|
return __builtin_bswap32(i);
|
||||||
|
#else
|
||||||
|
return _byteswap_ulong(i);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u64 swap64(u64 i)
|
||||||
|
{
|
||||||
|
#if defined(__GNUG__)
|
||||||
|
return __builtin_bswap64(i);
|
||||||
|
#else
|
||||||
|
return _byteswap_uint64(i);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2);
|
||||||
|
inline void xor_key_sse(u8* dest, const u8* src1, const u8* src2)
|
||||||
|
{
|
||||||
|
_mm_storeu_si128(&(((__m128i*)dest)[0]),
|
||||||
|
_mm_xor_si128(_mm_loadu_si128((__m128i*)src1), _mm_loadu_si128((__m128i*)src2)));
|
||||||
|
}
|
||||||
|
|
||||||
void prng(unsigned char *dest, int size);
|
void prng(unsigned char *dest, int size);
|
||||||
char* extract_file_name(const char* file_path, char real_file_name[MAX_PATH]);
|
char* extract_file_name(const char* file_path, char real_file_name[MAX_PATH]);
|
||||||
|
|
||||||
|
|
|
@ -89,11 +89,16 @@ s32 npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& enc_drm_path_local = vfs::get(enc_drm_path);
|
const std::string& enc_drm_path_local = vfs::get(enc_drm_path);
|
||||||
const std::string& dec_drm_path_local = vfs::get(dec_drm_path);
|
const fs::file enc_file(enc_drm_path_local);
|
||||||
|
|
||||||
if (DecryptEDAT(enc_drm_path_local, dec_drm_path_local, 8, rap_lpath, k_licensee, false) >= 0)
|
if (const fs::file dec_file = DecryptEDAT(enc_file, enc_drm_path_local, 8, rap_lpath, k_licensee, false))
|
||||||
{
|
{
|
||||||
// If decryption succeeds, replace the encrypted file with it.
|
// If decryption succeeds, replace the encrypted file with it.
|
||||||
|
const std::string& dec_drm_path_local = vfs::get(dec_drm_path);
|
||||||
|
|
||||||
|
fs::file dec_out(dec_drm_path_local, fs::rewrite);
|
||||||
|
dec_out.write(dec_file.to_vector<u8>());
|
||||||
|
|
||||||
fs::remove_file(enc_drm_path_local);
|
fs::remove_file(enc_drm_path_local);
|
||||||
fs::rename(dec_drm_path_local, enc_drm_path_local);
|
fs::rename(dec_drm_path_local, enc_drm_path_local);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "Emu/Cell/PPUThread.h"
|
||||||
|
#include "Crypto/unedat.h"
|
||||||
#include "Emu/VFS.h"
|
#include "Emu/VFS.h"
|
||||||
#include "Emu/IdManager.h"
|
#include "Emu/IdManager.h"
|
||||||
#include "Utilities/StrUtil.h"
|
#include "Utilities/StrUtil.h"
|
||||||
|
@ -23,6 +25,37 @@ lv2_fs_mount_point g_mp_sys_dev_bdvd;
|
||||||
lv2_fs_mount_point g_mp_sys_app_home;
|
lv2_fs_mount_point g_mp_sys_app_home;
|
||||||
lv2_fs_mount_point g_mp_sys_host_root;
|
lv2_fs_mount_point g_mp_sys_host_root;
|
||||||
|
|
||||||
|
bool verify_mself(u32 fd, fs::file const& mself_file)
|
||||||
|
{
|
||||||
|
FsMselfHeader mself_header;
|
||||||
|
if (!mself_file.read<FsMselfHeader>(mself_header))
|
||||||
|
{
|
||||||
|
sys_fs.error("verify_mself: Didn't read expected bytes for header.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mself_header.m_magic != 0x4D534600)
|
||||||
|
{
|
||||||
|
sys_fs.error("verify_mself: Header magic is incorrect.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mself_header.m_format_version != 1)
|
||||||
|
{
|
||||||
|
sys_fs.error("verify_mself: Unexpected header format version.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if (mself_header.m_entry_size != sizeof(FsMselfEntry))
|
||||||
|
{
|
||||||
|
sys_fs.error("verify_mself: Unexpected header entry size.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
lv2_fs_mount_point* lv2_fs_object::get_mp(const char* filename)
|
lv2_fs_mount_point* lv2_fs_object::get_mp(const char* filename)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -75,8 +108,6 @@ struct lv2_file::file_view : fs::file_base
|
||||||
|
|
||||||
u64 read(void* buffer, u64 size) override
|
u64 read(void* buffer, u64 size) override
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_file->mp->mutex);
|
|
||||||
|
|
||||||
const u64 old_pos = m_file->file.pos();
|
const u64 old_pos = m_file->file.pos();
|
||||||
const u64 new_pos = m_file->file.seek(m_off + m_pos);
|
const u64 new_pos = m_file->file.seek(m_off + m_pos);
|
||||||
const u64 result = m_file->file.read(buffer, size);
|
const u64 result = m_file->file.read(buffer, size);
|
||||||
|
@ -182,7 +213,17 @@ error_code sys_fs_open(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, s32 mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & ~(CELL_FS_O_ACCMODE | CELL_FS_O_CREAT | CELL_FS_O_TRUNC | CELL_FS_O_APPEND | CELL_FS_O_EXCL))
|
if (flags & CELL_FS_O_MSELF)
|
||||||
|
{
|
||||||
|
open_mode = fs::read;
|
||||||
|
// mself can be mself or mself | rdonly
|
||||||
|
if (flags & ~(CELL_FS_O_MSELF | CELL_FS_O_RDONLY))
|
||||||
|
{
|
||||||
|
open_mode = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & ~(CELL_FS_O_ACCMODE | CELL_FS_O_CREAT | CELL_FS_O_TRUNC | CELL_FS_O_APPEND | CELL_FS_O_EXCL | CELL_FS_O_MSELF))
|
||||||
{
|
{
|
||||||
open_mode = {}; // error
|
open_mode = {}; // error
|
||||||
}
|
}
|
||||||
|
@ -211,6 +252,30 @@ error_code sys_fs_open(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, s32 mode
|
||||||
return CELL_ENOENT;
|
return CELL_ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((flags & CELL_FS_O_MSELF) && (!verify_mself(*fd, file)))
|
||||||
|
return CELL_ENOTMSELF;
|
||||||
|
|
||||||
|
// sdata encryption arg flag
|
||||||
|
const be_t<u32>* casted_args = static_cast<const be_t<u32> *>(arg.get_ptr());
|
||||||
|
if (size == 8 && casted_args[0] == 0x180 && casted_args[1] == 0x10)
|
||||||
|
{
|
||||||
|
// check if the file has the NPD header, or else assume its not encrypted
|
||||||
|
u32 magic;
|
||||||
|
file.read<u32>(magic);
|
||||||
|
file.seek(0);
|
||||||
|
if (magic == "NPD\0"_u32)
|
||||||
|
{
|
||||||
|
auto sdata_file = std::make_unique<SDATADecrypter>(std::move(file));
|
||||||
|
if (!sdata_file->ReadHeader())
|
||||||
|
{
|
||||||
|
sys_fs.error("sys_fs_open(%s): Error reading sdata header!", path);
|
||||||
|
return CELL_EFSSPECIFIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.reset(std::move(sdata_file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (const u32 id = idm::make<lv2_fs_object, lv2_file>(path.get_ptr(), std::move(file), mode, flags))
|
if (const u32 id = idm::make<lv2_fs_object, lv2_file>(path.get_ptr(), std::move(file), mode, flags))
|
||||||
{
|
{
|
||||||
*fd = id;
|
*fd = id;
|
||||||
|
@ -559,8 +624,16 @@ error_code sys_fs_fcntl(u32 fd, u32 op, vm::ptr<void> _arg, u32 _size)
|
||||||
return CELL_EBADF;
|
return CELL_EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
auto sdata_file = std::make_unique<SDATADecrypter>(lv2_file::make_view(file, arg->offset));
|
||||||
if (const u32 id = idm::make<lv2_fs_object, lv2_file>(file->mp, lv2_file::make_view(file, arg->offset), file->mode, file->flags))
|
|
||||||
|
if (!sdata_file->ReadHeader())
|
||||||
|
{
|
||||||
|
return CELL_EFSSPECIFIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::file stream;
|
||||||
|
stream.reset(std::move(sdata_file));
|
||||||
|
if (const u32 id = idm::make<lv2_fs_object, lv2_file>(file->mp, std::move(stream), file->mode, file->flags))
|
||||||
{
|
{
|
||||||
arg->out_code = CELL_OK;
|
arg->out_code = CELL_OK;
|
||||||
arg->out_fd = id;
|
arg->out_fd = id;
|
||||||
|
|
|
@ -91,6 +91,26 @@ struct CellFsUtimbuf
|
||||||
|
|
||||||
CHECK_SIZE_ALIGN(CellFsUtimbuf, 16, 4);
|
CHECK_SIZE_ALIGN(CellFsUtimbuf, 16, 4);
|
||||||
|
|
||||||
|
// MSelf file structs
|
||||||
|
struct FsMselfHeader
|
||||||
|
{
|
||||||
|
be_t<u32> m_magic;
|
||||||
|
be_t<u32> m_format_version;
|
||||||
|
be_t<u64> m_file_size;
|
||||||
|
be_t<u32> m_entry_num;
|
||||||
|
be_t<u32> m_entry_size;
|
||||||
|
u8 m_reserve[40];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FsMselfEntry
|
||||||
|
{
|
||||||
|
char m_name[32];
|
||||||
|
be_t<u64> m_offset;
|
||||||
|
be_t<u64> m_size;
|
||||||
|
u8 m_reserve[16];
|
||||||
|
};
|
||||||
|
|
||||||
struct lv2_fs_mount_point;
|
struct lv2_fs_mount_point;
|
||||||
|
|
||||||
struct lv2_fs_object
|
struct lv2_fs_object
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue