Fix some memory leaks in unedat.cpp.

This commit is contained in:
lioncash 2014-03-31 08:56:24 -04:00
parent 01dbb8eb9a
commit e47e37a8d2

View file

@ -1,135 +1,135 @@
#include "stdafx.h" #include "stdafx.h"
#include "unedat.h" #include "unedat.h"
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);
switch (mode) { switch (mode) {
case 0x10000000: case 0x10000000:
// Encrypted ERK. // Encrypted ERK.
// Decrypt the key with EDAT_KEY + EDAT_IV and copy the original IV. // Decrypt the key with EDAT_KEY + EDAT_IV and copy the original IV.
aescbc128_decrypt(version ? EDAT_KEY_1 : EDAT_KEY_0, EDAT_IV, key, key_final, 0x10); aescbc128_decrypt(version ? EDAT_KEY_1 : EDAT_KEY_0, EDAT_IV, key, key_final, 0x10);
memcpy(iv_final, iv, 0x10); memcpy(iv_final, iv, 0x10);
break; break;
case 0x20000000: case 0x20000000:
// Default ERK. // Default ERK.
// Use EDAT_KEY and EDAT_IV. // Use EDAT_KEY and EDAT_IV.
memcpy(key_final, version ? EDAT_KEY_1 : EDAT_KEY_0, 0x10); memcpy(key_final, version ? EDAT_KEY_1 : EDAT_KEY_0, 0x10);
memcpy(iv_final, EDAT_IV, 0x10); memcpy(iv_final, EDAT_IV, 0x10);
break; break;
case 0x00000000: case 0x00000000:
// Unencrypted ERK. // Unencrypted ERK.
// Use the original key and iv. // Use the original key and iv.
memcpy(key_final, key, 0x10); memcpy(key_final, key, 0x10);
memcpy(iv_final, iv, 0x10); memcpy(iv_final, iv, 0x10);
break; break;
}; };
} }
void generate_hash(int hash_mode, int version, unsigned char *hash_final, unsigned char *hash)
{
int mode = (int) (hash_mode & 0xF0000000);
switch (mode) {
case 0x10000000:
// Encrypted HASH.
// Decrypt the hash with EDAT_KEY + EDAT_IV.
aescbc128_decrypt(version ? EDAT_KEY_1 : EDAT_KEY_0, EDAT_IV, hash, hash_final, 0x10);
break;
case 0x20000000:
// Default HASH.
// Use EDAT_HASH.
memcpy(hash_final, version ? EDAT_HASH_1 : EDAT_HASH_0, 0x10);
break;
case 0x00000000:
// Unencrypted ERK.
// Use the original hash.
memcpy(hash_final, hash, 0x10);
break;
};
}
bool crypto(int hash_mode, int crypto_mode, int version, unsigned char *in, unsigned char *out, int lenght, unsigned char *key, unsigned char *iv, unsigned char *hash, unsigned char *test_hash) void generate_hash(int hash_mode, int version, unsigned char *hash_final, unsigned char *hash)
{ {
// Setup buffers for key, iv and hash. int mode = (int) (hash_mode & 0xF0000000);
unsigned char key_final[0x10] = {}; switch (mode) {
unsigned char iv_final[0x10] = {}; case 0x10000000:
unsigned char hash_final_10[0x10] = {}; // Encrypted HASH.
unsigned char hash_final_14[0x14] = {}; // Decrypt the hash with EDAT_KEY + EDAT_IV.
aescbc128_decrypt(version ? EDAT_KEY_1 : EDAT_KEY_0, EDAT_IV, hash, hash_final, 0x10);
// Generate crypto key and hash. break;
generate_key(crypto_mode, version, key_final, iv_final, key, iv); case 0x20000000:
if ((hash_mode & 0xFF) == 0x01) // Default HASH.
generate_hash(hash_mode, version, hash_final_14, hash); // Use EDAT_HASH.
else memcpy(hash_final, version ? EDAT_HASH_1 : EDAT_HASH_0, 0x10);
generate_hash(hash_mode, version, hash_final_10, hash); break;
case 0x00000000:
if ((crypto_mode & 0xFF) == 0x01) // No algorithm. // Unencrypted ERK.
{ // Use the original hash.
memcpy(out, in, lenght); memcpy(hash_final, hash, 0x10);
} break;
else if ((crypto_mode & 0xFF) == 0x02) // AES128-CBC };
{ }
aescbc128_decrypt(key_final, iv_final, in, out, lenght);
} bool crypto(int hash_mode, int crypto_mode, int version, unsigned char *in, unsigned char *out, int lenght, unsigned char *key, unsigned char *iv, unsigned char *hash, unsigned char *test_hash)
else {
{ // Setup buffers for key, iv and hash.
ConLog.Error("EDAT: Unknown crypto algorithm!\n"); unsigned char key_final[0x10] = {};
return false; unsigned char iv_final[0x10] = {};
} unsigned char hash_final_10[0x10] = {};
unsigned char hash_final_14[0x14] = {};
if ((hash_mode & 0xFF) == 0x01) // 0x14 SHA1-HMAC
{ // Generate crypto key and hash.
return hmac_hash_compare(hash_final_14, 0x14, in, lenght, test_hash); generate_key(crypto_mode, version, key_final, iv_final, key, iv);
} if ((hash_mode & 0xFF) == 0x01)
else if ((hash_mode & 0xFF) == 0x02) // 0x10 AES-CMAC generate_hash(hash_mode, version, hash_final_14, hash);
{ else
return cmac_hash_compare(hash_final_10, 0x10, in, lenght, test_hash); generate_hash(hash_mode, version, hash_final_10, hash);
}
else if ((hash_mode & 0xFF) == 0x04) //0x10 SHA1-HMAC if ((crypto_mode & 0xFF) == 0x01) // No algorithm.
{ {
return hmac_hash_compare(hash_final_10, 0x10, in, lenght, test_hash); memcpy(out, in, lenght);
} }
else else if ((crypto_mode & 0xFF) == 0x02) // AES128-CBC
{ {
ConLog.Error("EDAT: Unknown hashing algorithm!\n"); aescbc128_decrypt(key_final, iv_final, in, out, lenght);
return false; }
} else
} {
ConLog.Error("EDAT: Unknown crypto algorithm!\n");
unsigned char* dec_section(unsigned char* metadata) return false;
{ }
unsigned char *dec = new unsigned char[0x10];
dec[0x00] = (metadata[0xC] ^ metadata[0x8] ^ metadata[0x10]); if ((hash_mode & 0xFF) == 0x01) // 0x14 SHA1-HMAC
dec[0x01] = (metadata[0xD] ^ metadata[0x9] ^ metadata[0x11]); {
dec[0x02] = (metadata[0xE] ^ metadata[0xA] ^ metadata[0x12]); return hmac_hash_compare(hash_final_14, 0x14, in, lenght, test_hash);
dec[0x03] = (metadata[0xF] ^ metadata[0xB] ^ metadata[0x13]); }
dec[0x04] = (metadata[0x4] ^ metadata[0x8] ^ metadata[0x14]); else if ((hash_mode & 0xFF) == 0x02) // 0x10 AES-CMAC
dec[0x05] = (metadata[0x5] ^ metadata[0x9] ^ metadata[0x15]); {
dec[0x06] = (metadata[0x6] ^ metadata[0xA] ^ metadata[0x16]); return cmac_hash_compare(hash_final_10, 0x10, in, lenght, test_hash);
dec[0x07] = (metadata[0x7] ^ metadata[0xB] ^ metadata[0x17]); }
dec[0x08] = (metadata[0xC] ^ metadata[0x0] ^ metadata[0x18]); else if ((hash_mode & 0xFF) == 0x04) //0x10 SHA1-HMAC
dec[0x09] = (metadata[0xD] ^ metadata[0x1] ^ metadata[0x19]); {
dec[0x0A] = (metadata[0xE] ^ metadata[0x2] ^ metadata[0x1A]); return hmac_hash_compare(hash_final_10, 0x10, in, lenght, test_hash);
dec[0x0B] = (metadata[0xF] ^ metadata[0x3] ^ metadata[0x1B]); }
dec[0x0C] = (metadata[0x4] ^ metadata[0x0] ^ metadata[0x1C]); else
dec[0x0D] = (metadata[0x5] ^ metadata[0x1] ^ metadata[0x1D]); {
dec[0x0E] = (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]); ConLog.Error("EDAT: Unknown hashing algorithm!\n");
dec[0x0F] = (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]); return false;
}
}
unsigned char* dec_section(unsigned char* metadata)
{
unsigned char *dec = new unsigned char[0x10];
dec[0x00] = (metadata[0xC] ^ metadata[0x8] ^ metadata[0x10]);
dec[0x01] = (metadata[0xD] ^ metadata[0x9] ^ metadata[0x11]);
dec[0x02] = (metadata[0xE] ^ metadata[0xA] ^ metadata[0x12]);
dec[0x03] = (metadata[0xF] ^ metadata[0xB] ^ metadata[0x13]);
dec[0x04] = (metadata[0x4] ^ metadata[0x8] ^ metadata[0x14]);
dec[0x05] = (metadata[0x5] ^ metadata[0x9] ^ metadata[0x15]);
dec[0x06] = (metadata[0x6] ^ metadata[0xA] ^ metadata[0x16]);
dec[0x07] = (metadata[0x7] ^ metadata[0xB] ^ metadata[0x17]);
dec[0x08] = (metadata[0xC] ^ metadata[0x0] ^ metadata[0x18]);
dec[0x09] = (metadata[0xD] ^ metadata[0x1] ^ metadata[0x19]);
dec[0x0A] = (metadata[0xE] ^ metadata[0x2] ^ metadata[0x1A]);
dec[0x0B] = (metadata[0xF] ^ metadata[0x3] ^ metadata[0x1B]);
dec[0x0C] = (metadata[0x4] ^ metadata[0x0] ^ metadata[0x1C]);
dec[0x0D] = (metadata[0x5] ^ metadata[0x1] ^ metadata[0x1D]);
dec[0x0E] = (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]);
dec[0x0F] = (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]);
return dec; return dec;
} }
unsigned char* get_block_key(int block, NPD_HEADER *npd) unsigned char* 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]; unsigned char *dest_key = new unsigned char[0x10];
memcpy(dest_key, src_key, 0xC); memcpy(dest_key, src_key, 0xC);
dest_key[0xC] = (block >> 24 & 0xFF); dest_key[0xC] = (block >> 24 & 0xFF);
dest_key[0xD] = (block >> 16 & 0xFF); dest_key[0xD] = (block >> 16 & 0xFF);
dest_key[0xE] = (block >> 8 & 0xFF); dest_key[0xE] = (block >> 8 & 0xFF);
dest_key[0xF] = (block & 0xFF); dest_key[0xF] = (block & 0xFF);
return dest_key; return dest_key;
} }
// EDAT/SDAT functions. // EDAT/SDAT functions.
int decrypt_data(wxFile *in, wxFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd, unsigned char* crypt_key, bool verbose) int decrypt_data(wxFile *in, wxFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd, unsigned char* crypt_key, bool verbose)
@ -301,20 +301,17 @@ int decrypt_data(wxFile *in, wxFile *out, EDAT_SDAT_HEADER *edat, NPD_HEADER *np
return 0; return 0;
} }
int check_data(unsigned char *key, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd, wxFile *f, bool verbose) static bool check_flags(EDAT_SDAT_HEADER *edat, NPD_HEADER *npd)
{ {
f->Seek(0); if (edat == nullptr || npd == nullptr)
unsigned char *header = new unsigned char[0xA0]; return false;
unsigned char *tmp = new unsigned char[0xA0];
unsigned char *hash_result = new unsigned char[0x10];
// Check NPD version and EDAT flags. if (npd->version == 0 || npd->version == 1)
if ((npd->version == 0) || (npd->version == 1))
{ {
if (edat->flags & 0x7EFFFFFE) if (edat->flags & 0x7EFFFFFE)
{ {
ConLog.Error("EDAT: Bad header flags!\n"); ConLog.Error("EDAT: Bad header flags!\n");
return 1; return false;
} }
} }
else if (npd->version == 2) else if (npd->version == 2)
@ -322,93 +319,113 @@ int check_data(unsigned char *key, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd, wxFi
if (edat->flags & 0x7EFFFFE0) if (edat->flags & 0x7EFFFFE0)
{ {
ConLog.Error("EDAT: Bad header flags!\n"); ConLog.Error("EDAT: Bad header flags!\n");
return 1; return false;
} }
} }
else if ((npd->version == 3) || (npd->version == 4)) else if (npd->version == 3 || npd->version == 4)
{ {
if (edat->flags & 0x7EFFFFC0) if (edat->flags & 0x7EFFFFC0)
{ {
ConLog.Error("EDAT: Bad header flags!\n"); ConLog.Error("EDAT: Bad header flags!\n");
return 1; return false;
} }
} }
else else if (npd->version > 4)
{ {
ConLog.Error("EDAT: Unknown version!\n"); ConLog.Error("EDAT: Unknown version - %d\n", npd->version);
return false;
}
return true;
}
int check_data(unsigned char *key, EDAT_SDAT_HEADER *edat, NPD_HEADER *npd, wxFile *f, bool verbose)
{
f->Seek(0);
unsigned char *header = new unsigned char[0xA0];
unsigned char *tmp = new unsigned char[0xA0];
unsigned char *hash_result = new unsigned char[0x10];
// Check NPD version and EDAT flags.
if (!check_flags(edat, npd))
{
delete[] header;
delete[] tmp;
delete[] hash_result;
return 1; return 1;
} }
// Read in the file header. // Read in the file header.
f->Read(header, 0xA0); f->Read(header, 0xA0);
f->Read(hash_result, 0x10); f->Read(hash_result, 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; 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)
{ {
ConLog.Warning("EDAT: DEBUG data detected!\n"); ConLog.Warning("EDAT: DEBUG data detected!\n");
hash_mode |= 0x01000000; hash_mode |= 0x01000000;
} }
// Setup header key and iv buffers. // Setup header key and iv buffers.
unsigned char header_key[0x10] = {}; unsigned char header_key[0x10] = {};
unsigned char header_iv[0x10] = {}; unsigned char header_iv[0x10] = {};
// Test the header hash (located at offset 0xA0). // Test the header hash (located at offset 0xA0).
if (!crypto(hash_mode, crypto_mode, (npd->version == 4), header, tmp, 0xA0, header_key, header_iv, key, hash_result)) if (!crypto(hash_mode, crypto_mode, (npd->version == 4), header, tmp, 0xA0, header_key, header_iv, key, hash_result))
{ {
if (verbose) if (verbose)
ConLog.Warning("EDAT: Header hash is invalid!\n"); ConLog.Warning("EDAT: Header hash is invalid!\n");
} }
// Parse the metadata info. // Parse the metadata info.
int metadata_section_size = 0x10; int metadata_section_size = 0x10;
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0)) if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0))
{ {
ConLog.Warning("EDAT: COMPRESSED data detected!\n"); ConLog.Warning("EDAT: COMPRESSED data detected!\n");
metadata_section_size = 0x20; metadata_section_size = 0x20;
} }
int block_num = (int) ((edat->file_size + edat->block_size - 1) / edat->block_size); int block_num = (int) ((edat->file_size + edat->block_size - 1) / edat->block_size);
int bytes_read = 0; int bytes_read = 0;
int metadata_offset = 0x100; int metadata_offset = 0x100;
long bytes_to_read = metadata_section_size * block_num; long bytes_to_read = metadata_section_size * block_num;
while (bytes_to_read > 0) while (bytes_to_read > 0)
{ {
// Locate the metadata blocks. // Locate the metadata blocks.
int block_size = (0x3C00 > bytes_to_read) ? (int) bytes_to_read : 0x3C00; // 0x3C00 is the maximum block size. int block_size = (0x3C00 > bytes_to_read) ? (int) bytes_to_read : 0x3C00; // 0x3C00 is the maximum block size.
f->Seek(metadata_offset + bytes_read); f->Seek(metadata_offset + bytes_read);
unsigned char *data = new unsigned char[block_size]; unsigned char *data = new unsigned char[block_size];
// Read in the metadata. // Read in the metadata.
tmp = new unsigned char[block_size]; tmp = new unsigned char[block_size];
f->Read(data, block_size); f->Read(data, block_size);
// Check the generated hash against the metadata hash located at offset 0x90 in the header. // Check the generated hash against the metadata hash located at offset 0x90 in the header.
memset(hash_result, 0, 0x10); memset(hash_result, 0, 0x10);
f->Seek(0x90); f->Seek(0x90);
f->Read(hash_result, 0x10); f->Read(hash_result, 0x10);
// Generate the hash for this block. // Generate the hash for this block.
if (!crypto(hash_mode, crypto_mode, (npd->version == 4), data, tmp, block_size, header_key, header_iv, key, hash_result)) if (!crypto(hash_mode, crypto_mode, (npd->version == 4), data, tmp, block_size, header_key, header_iv, key, hash_result))
{ {
if (verbose) if (verbose)
ConLog.Warning("EDAT: Metadata hash from block 0x%08x is invalid!\n", metadata_offset + bytes_read); ConLog.Warning("EDAT: Metadata hash from block 0x%08x is invalid!\n", metadata_offset + bytes_read);
} }
// Adjust sizes. // Adjust sizes.
bytes_read += block_size; bytes_read += block_size;
bytes_to_read -= block_size; bytes_to_read -= block_size;
delete[] data; delete[] data;
} }
// Cleanup. // Cleanup.
delete[] header; delete[] header;
delete[] tmp; delete[] tmp;
delete[] hash_result; delete[] hash_result;
return 0; return 0;
@ -501,6 +518,8 @@ bool extract_data(wxFile *input, wxFile *output, const char* input_file_name, un
if(memcmp(NPD->magic, npd_magic, 4)) if(memcmp(NPD->magic, npd_magic, 4))
{ {
ConLog.Error("EDAT: File has invalid NPD header."); ConLog.Error("EDAT: File has invalid NPD header.");
delete NPD;
delete EDAT;
return 1; return 1;
} }
@ -556,6 +575,8 @@ bool extract_data(wxFile *input, wxFile *output, const char* input_file_name, un
if (!test) if (!test)
{ {
ConLog.Error("EDAT: A valid RAP file is needed!"); ConLog.Error("EDAT: A valid RAP file is needed!");
delete NPD;
delete EDAT;
return 1; return 1;
} }
} }
@ -670,4 +691,4 @@ int DecryptEDAT(const std::string& input_file_name, const std::string& output_fi
input.Close(); input.Close();
output.Close(); output.Close();
return 0; return 0;
} }