Crypto: Fix endianess, avoid crashing on invalid values

This commit is contained in:
Eladash 2023-08-30 20:09:58 +03:00 committed by Megamouse
parent f554b444c0
commit af850dac99
3 changed files with 80 additions and 103 deletions

View File

@ -135,9 +135,9 @@ std::tuple<u64, s32, s32> dec_section(unsigned char* metadata)
dec[0x0E] = (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]);
dec[0x0F] = (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]);
u64 offset = swap64(*reinterpret_cast<u64*>(&dec[0]));
s32 length = swap32(*reinterpret_cast<s32*>(&dec[8]));
s32 compression_end = swap32(*reinterpret_cast<s32*>(&dec[12]));
u64 offset = read_from_ptr<be_t<u64>>(dec, 0);
s32 length = read_from_ptr<be_t<s32>>(dec, 8);
s32 compression_end = read_from_ptr<be_t<s32>>(dec, 12);
return std::make_tuple(offset, length, compression_end);
}
@ -149,7 +149,7 @@ u128 get_block_key(int block, NPD_HEADER *npd)
u128 dest_key{};
std::memcpy(&dest_key, src_key, 0xC);
s32 swappedBlock = swap32(block);
s32 swappedBlock = std::bit_cast<be_t<s32>>(block);
std::memcpy(reinterpret_cast<uchar*>(&dest_key) + 0xC, &swappedBlock, sizeof(swappedBlock));
return dest_key;
}
@ -193,9 +193,9 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
// NOTE: For NPD version 1 the metadata is not encrypted.
if (npd->version <= 1)
{
offset = swap64(*reinterpret_cast<u64*>(&metadata[0x10]));
length = swap32(*reinterpret_cast<s32*>(&metadata[0x18]));
compression_end = swap32(*reinterpret_cast<s32*>(&metadata[0x1C]));
offset = read_from_ptr<be_t<u64>>(metadata, 0x10);
length = read_from_ptr<be_t<s32>>(metadata, 0x18);
compression_end = read_from_ptr<be_t<s32>>(metadata, 0x1C);
}
else
{
@ -433,17 +433,26 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
edat_log.warning("COMPRESSED data detected!");
}
const int block_num = static_cast<int>((edat->file_size + edat->block_size - 1) / edat->block_size);
const int metadata_offset = 0x100;
const int metadata_size = metadata_section_size * block_num;
if (!edat->block_size)
{
return 1;
}
const usz block_num = utils::aligned_div<u64>(edat->file_size, edat->block_size);
constexpr usz metadata_offset = 0x100;
const usz metadata_size = utils::mul_saturate<u64>(metadata_section_size, block_num);
u64 metadata_section_offset = metadata_offset;
long bytes_read = 0;
long bytes_to_read = metadata_size;
std::unique_ptr<u8[]> metadata(new u8[metadata_size]);
std::unique_ptr<u8[]> empty_metadata(new u8[metadata_size]);
if (utils::add_saturate<u64>(utils::add_saturate<u64>(file_offset, metadata_section_offset), metadata_size) > f->size())
{
return 1;
}
while (bytes_to_read > 0)
u64 bytes_read = 0;
const auto metadata = std::make_unique<u8[]>(metadata_size);
const auto empty_metadata = std::make_unique<u8[]>(metadata_size);
while (bytes_read < metadata_size)
{
// Locate the metadata blocks.
f->seek(file_offset + metadata_section_offset);
@ -453,7 +462,6 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
// Adjust sizes.
bytes_read += metadata_section_size;
bytes_to_read -= metadata_section_size;
if (((edat->flags & EDAT_FLAG_0x20) != 0)) // Metadata block before each data block.
metadata_section_offset += (metadata_section_size + edat->block_size);
@ -553,18 +561,18 @@ bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
return true;
}
unsigned char dev[0x60] = { 0 };
unsigned char dev[0x60]{};
// Build the dev buffer (first 0x60 bytes of NPD header in big-endian).
memcpy(dev, npd, 0x60);
std::memcpy(dev, npd, 0x60);
// Fix endianness.
int version = swap32(npd->version);
int license = swap32(npd->license);
int type = swap32(npd->type);
memcpy(dev + 0x4, &version, 4);
memcpy(dev + 0x8, &license, 4);
memcpy(dev + 0xC, &type, 4);
s32 version = std::bit_cast<be_t<s32>>(npd->version);
s32 license = std::bit_cast<be_t<s32>>(npd->license);
s32 type = std::bit_cast<be_t<s32>>(npd->type);
std::memcpy(dev + 0x4, &version, 4);
std::memcpy(dev + 0x8, &license, 4);
std::memcpy(dev + 0xC, &type, 4);
// Check for an empty dev_hash (can't validate if devklic is NULL);
u128 klic;
@ -638,20 +646,20 @@ void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& E
input->read(npd_header, sizeof(npd_header));
input->read(edat_header, sizeof(edat_header));
memcpy(&NPD.magic, npd_header, 4);
NPD.version = swap32(*reinterpret_cast<s32*>(&npd_header[4]));
NPD.license = swap32(*reinterpret_cast<s32*>(&npd_header[8]));
NPD.type = swap32(*reinterpret_cast<s32*>(&npd_header[12]));
memcpy(NPD.content_id, &npd_header[16], 0x30);
memcpy(NPD.digest, &npd_header[64], 0x10);
memcpy(NPD.title_hash, &npd_header[80], 0x10);
memcpy(NPD.dev_hash, &npd_header[96], 0x10);
NPD.activate_time = swap64(*reinterpret_cast<s64*>(&npd_header[112]));
NPD.expire_time = swap64(*reinterpret_cast<s64*>(&npd_header[120]));
std::memcpy(&NPD.magic, npd_header, 4);
NPD.version = read_from_ptr<be_t<s32>>(npd_header, 4);
NPD.license = read_from_ptr<be_t<s32>>(npd_header, 8);
NPD.type = read_from_ptr<be_t<s32>>(npd_header, 12);
std::memcpy(NPD.content_id, &npd_header[16], 0x30);
std::memcpy(NPD.digest, &npd_header[64], 0x10);
std::memcpy(NPD.title_hash, &npd_header[80], 0x10);
std::memcpy(NPD.dev_hash, &npd_header[96], 0x10);
NPD.activate_time = read_from_ptr<be_t<s64>>(npd_header, 112);
NPD.expire_time = read_from_ptr<be_t<s64>>(npd_header, 120);
EDAT.flags = swap32(*reinterpret_cast<s32*>(&edat_header[0]));
EDAT.block_size = swap32(*reinterpret_cast<s32*>(&edat_header[4]));
EDAT.file_size = swap64(*reinterpret_cast<u64*>(&edat_header[8]));
EDAT.flags = read_from_ptr<be_t<s32>>(edat_header, 0);
EDAT.block_size = read_from_ptr<be_t<s32>>(edat_header, 4);
EDAT.file_size = read_from_ptr<be_t<u64>>(edat_header, 8);
}
bool extract_all_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, bool verbose)

View File

@ -365,22 +365,14 @@ void MetadataInfo::Show() const
void MetadataHeader::Load(u8* in)
{
memcpy(&signature_input_length, in, 8);
memcpy(&unknown1, in + 8, 4);
memcpy(&section_count, in + 12, 4);
memcpy(&key_count, in + 16, 4);
memcpy(&opt_header_size, in + 20, 4);
memcpy(&unknown2, in + 24, 4);
memcpy(&unknown3, in + 28, 4);
// Endian swap.
signature_input_length = swap64(signature_input_length);
unknown1 = swap32(unknown1);
section_count = swap32(section_count);
key_count = swap32(key_count);
opt_header_size = swap32(opt_header_size);
unknown2 = swap32(unknown2);
unknown3 = swap32(unknown3);
signature_input_length = read_from_ptr<be_t<u64>>(in);
unknown1 = read_from_ptr<be_t<u32>>(in, 8);
section_count = read_from_ptr<be_t<u32>>(in, 12);
key_count = read_from_ptr<be_t<u32>>(in, 16);
opt_header_size = read_from_ptr<be_t<u32>>(in, 20);
unknown2 = read_from_ptr<be_t<u32>>(in, 24);
unknown3 = read_from_ptr<be_t<u32>>(in, 28);
}
void MetadataHeader::Show() const
@ -396,28 +388,17 @@ void MetadataHeader::Show() const
void MetadataSectionHeader::Load(u8* in)
{
memcpy(&data_offset, in, 8);
memcpy(&data_size, in + 8, 8);
memcpy(&type, in + 16, 4);
memcpy(&program_idx, in + 20, 4);
memcpy(&hashed, in + 24, 4);
memcpy(&sha1_idx, in + 28, 4);
memcpy(&encrypted, in + 32, 4);
memcpy(&key_idx, in + 36, 4);
memcpy(&iv_idx, in + 40, 4);
memcpy(&compressed, in + 44, 4);
// Endian swap.
data_offset = swap64(data_offset);
data_size = swap64(data_size);
type = swap32(type);
program_idx = swap32(program_idx);
hashed = swap32(hashed);
sha1_idx = swap32(sha1_idx);
encrypted = swap32(encrypted);
key_idx = swap32(key_idx);
iv_idx = swap32(iv_idx);
compressed = swap32(compressed);
data_offset = read_from_ptr<be_t<u64>>(in);
data_size = read_from_ptr<be_t<u64>>(in, 8);
type = read_from_ptr<be_t<u32>>(in, 16);
program_idx = read_from_ptr<be_t<u32>>(in, 20);
hashed = read_from_ptr<be_t<u32>>(in, 24);
sha1_idx = read_from_ptr<be_t<u32>>(in, 28);
encrypted = read_from_ptr<be_t<u32>>(in, 32);
key_idx = read_from_ptr<be_t<u32>>(in, 36);
iv_idx = read_from_ptr<be_t<u32>>(in, 40);
compressed = read_from_ptr<be_t<u32>>(in, 44);
}
void MetadataSectionHeader::Show() const
@ -936,19 +917,29 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
}
}
// Read section info.
m_seg_ext_hdr.clear();
self_f.seek(m_ext_hdr.segment_ext_hdr_offset);
for(u32 i = 0; i < ((isElf32) ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i)
for(u32 i = 0; i < (isElf32 ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i)
{
if (self_f.pos() >= self_f.size())
{
return false;
}
m_seg_ext_hdr.emplace_back();
m_seg_ext_hdr.back().Load(self_f);
}
if (m_ext_hdr.version_hdr_offset == 0 || utils::add_saturate<u64>(m_ext_hdr.version_hdr_offset, sizeof(version_header)) > self_f.size())
{
return false;
}
// Read SCE version info.
self_f.seek(m_ext_hdr.version_hdr_offset);
m_version_hdr.Load(self_f);
// Read control info.
@ -957,6 +948,11 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
for (u64 i = 0; i < m_ext_hdr.supplemental_hdr_size;)
{
if (self_f.pos() >= self_f.size())
{
return false;
}
m_supplemental_hdr_arr.emplace_back();
supplemental_header& cinfo = m_supplemental_hdr_arr.back();
cinfo.Load(self_f);

View File

@ -5,39 +5,12 @@
// http://www.gnu.org/licenses/gpl-2.0.txt
#include "util/types.hpp"
#include "util/asm.hpp"
#include <stdlib.h>
enum { CRYPTO_MAX_PATH = 4096 };
// Auxiliary functions (endian swap, xor, and file name).
inline u16 swap16(u16 i)
{
#if defined(__GNUG__)
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
}
char* extract_file_name(const char* file_path, char real_file_name[CRYPTO_MAX_PATH]);
std::string sha256_get_hash(const char* data, usz size, bool lower_case);