mirror of
https://github.com/jakcron/nstool
synced 2024-11-15 02:06:40 +00:00
[nstool] Add heuristics for detecting CNMT and NACP without -t.
This commit is contained in:
parent
b5605ee3e9
commit
40b3b45dd6
2 changed files with 105 additions and 16 deletions
|
@ -17,6 +17,8 @@
|
|||
#include <nx/nca.h>
|
||||
#include <nx/npdm.h>
|
||||
#include <nx/romfs.h>
|
||||
#include <nx/cnmt.h>
|
||||
#include <nx/nacp.h>
|
||||
#include <nx/nso.h>
|
||||
#include <nx/nro.h>
|
||||
#include <nx/aset.h>
|
||||
|
@ -688,7 +690,7 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str)
|
|||
|
||||
FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
||||
{
|
||||
static const size_t kMaxReadSize = 0x1000;
|
||||
static const size_t kMaxReadSize = 0x4000;
|
||||
FileType file_type = FILE_INVALID;
|
||||
fnd::SimpleFile file;
|
||||
fnd::MemoryBlob scratch;
|
||||
|
@ -702,16 +704,7 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
|||
// close file
|
||||
file.close();
|
||||
|
||||
//fnd::SimpleTextOutput::hxdStyleDump(scratch.getBytes(), scratch.getSize());
|
||||
|
||||
// prepare decrypted NCA data
|
||||
byte_t nca_raw[nx::nca::kHeaderSize];
|
||||
nx::sNcaHeader* nca_header = (nx::sNcaHeader*)(nca_raw + nx::NcaUtils::sectorToOffset(1));
|
||||
|
||||
if (scratch.getSize() >= nx::nca::kHeaderSize)
|
||||
{
|
||||
nx::NcaUtils::decryptNcaHeader(scratch.getBytes(), nca_raw, mKeyset.nca.header_key);
|
||||
}
|
||||
|
||||
// _QUICK_CAST resolves to a pointer of type 'st' located at scratch.getBytes() + 'oft'
|
||||
#define _QUICK_CAST(st, oft) ((st*)(scratch.getBytes() + (oft)))
|
||||
|
@ -729,15 +722,18 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
|||
// test romfs
|
||||
else if (_ASSERT_SIZE(sizeof(nx::sRomfsHeader)) && _QUICK_CAST(nx::sRomfsHeader, 0)->header_size.get() == sizeof(nx::sRomfsHeader) && _QUICK_CAST(nx::sRomfsHeader, 0)->sections[1].offset.get() == (_QUICK_CAST(nx::sRomfsHeader, 0)->sections[0].offset.get() + _QUICK_CAST(nx::sRomfsHeader, 0)->sections[0].size.get()))
|
||||
file_type = FILE_ROMFS;
|
||||
// test nca2
|
||||
else if (_ASSERT_SIZE(nx::nca::kHeaderSize) && nca_header->signature.get() == nx::nca::kNca2Sig)
|
||||
file_type = FILE_NCA;
|
||||
// test nca3
|
||||
else if (_ASSERT_SIZE(nx::nca::kHeaderSize) && nca_header->signature.get() == nx::nca::kNca3Sig)
|
||||
file_type = FILE_NCA;
|
||||
// test npdm
|
||||
else if (_ASSERT_SIZE(sizeof(nx::sNpdmHeader)) && _QUICK_CAST(nx::sNpdmHeader, 0)->signature.get() == nx::npdm::kNpdmStructSig)
|
||||
file_type = FILE_NPDM;
|
||||
// test nca
|
||||
else if (determineValidNcaFromSample(scratch))
|
||||
file_type = FILE_NCA;
|
||||
// test cnmt
|
||||
else if (determineValidCnmtFromSample(scratch))
|
||||
file_type = FILE_CNMT;
|
||||
// test nacp
|
||||
else if (determineValidNacpFromSample(scratch))
|
||||
file_type = FILE_NACP;
|
||||
// test nso
|
||||
else if (_ASSERT_SIZE(sizeof(nx::sNsoHeader)) && _QUICK_CAST(nx::sNsoHeader, 0)->signature.get() == nx::nso::kNsoSig)
|
||||
file_type = FILE_NSO;
|
||||
|
@ -757,6 +753,95 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
|||
return file_type;
|
||||
}
|
||||
|
||||
bool UserSettings::determineValidNcaFromSample(const fnd::MemoryBlob& sample) const
|
||||
{
|
||||
// prepare decrypted NCA data
|
||||
byte_t nca_raw[nx::nca::kHeaderSize];
|
||||
nx::sNcaHeader* nca_header = (nx::sNcaHeader*)(nca_raw + nx::NcaUtils::sectorToOffset(1));
|
||||
|
||||
if (sample.getSize() < nx::nca::kHeaderSize)
|
||||
return false;
|
||||
|
||||
nx::NcaUtils::decryptNcaHeader(sample.getBytes(), nca_raw, mKeyset.nca.header_key);
|
||||
|
||||
if (nca_header->signature.get() != nx::nca::kNca2Sig && nca_header->signature.get() != nx::nca::kNca3Sig)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UserSettings::determineValidCnmtFromSample(const fnd::MemoryBlob& sample) const
|
||||
{
|
||||
if (sample.getSize() < sizeof(nx::sContentMetaHeader))
|
||||
return false;
|
||||
|
||||
const nx::sContentMetaHeader* data = (const nx::sContentMetaHeader*)sample.getBytes();
|
||||
|
||||
size_t minimum_size = sizeof(nx::sContentMetaHeader) + data->exhdr_size.get() + data->content_count.get() * sizeof(nx::sContentInfo) + data->content_meta_count.get() * sizeof(nx::sContentMetaInfo) + nx::cnmt::kDigestLen;
|
||||
|
||||
if (sample.getSize() < minimum_size)
|
||||
return false;
|
||||
|
||||
if (data->type == nx::cnmt::METATYPE_APPLICATION)
|
||||
{
|
||||
const nx::sApplicationMetaExtendedHeader* meta = (const nx::sApplicationMetaExtendedHeader*)(sample.getBytes() + sizeof(nx::sContentMetaHeader));
|
||||
if ((meta->patch_id.get() & data->id.get()) != data->id.get())
|
||||
return false;
|
||||
}
|
||||
else if (data->type == nx::cnmt::METATYPE_PATCH)
|
||||
{
|
||||
const nx::sPatchMetaExtendedHeader* meta = (const nx::sPatchMetaExtendedHeader*)(sample.getBytes() + sizeof(nx::sContentMetaHeader));
|
||||
if ((meta->application_id.get() & data->id.get()) != meta->application_id.get())
|
||||
return false;
|
||||
|
||||
minimum_size += meta->extended_data_size.get();
|
||||
}
|
||||
else if (data->type == nx::cnmt::METATYPE_ADD_ON_CONTENT)
|
||||
{
|
||||
const nx::sAddOnContentMetaExtendedHeader* meta = (const nx::sAddOnContentMetaExtendedHeader*)(sample.getBytes() + sizeof(nx::sContentMetaHeader));
|
||||
if ((meta->application_id.get() & data->id.get()) != meta->application_id.get())
|
||||
return false;
|
||||
}
|
||||
else if (data->type == nx::cnmt::METATYPE_DELTA)
|
||||
{
|
||||
const nx::sDeltaMetaExtendedHeader* meta = (const nx::sDeltaMetaExtendedHeader*)(sample.getBytes() + sizeof(nx::sContentMetaHeader));
|
||||
if ((meta->application_id.get() & data->id.get()) != meta->application_id.get())
|
||||
return false;
|
||||
|
||||
minimum_size += meta->extended_data_size.get();
|
||||
}
|
||||
|
||||
if (sample.getSize() != minimum_size)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UserSettings::determineValidNacpFromSample(const fnd::MemoryBlob& sample) const
|
||||
{
|
||||
if (sample.getSize() != sizeof(nx::sApplicationControlProperty))
|
||||
return false;
|
||||
|
||||
const nx::sApplicationControlProperty* data = (const nx::sApplicationControlProperty*)sample.getBytes();
|
||||
|
||||
if (data->logo_type > nx::nacp::LOGO_Nintendo)
|
||||
return false;
|
||||
|
||||
if (data->display_version[0] == 0)
|
||||
return false;
|
||||
|
||||
if (data->user_account_save_data_size.get() == 0 && data->user_account_save_data_journal_size.get() != 0)
|
||||
return false;
|
||||
|
||||
if (data->user_account_save_data_journal_size.get() == 0 && data->user_account_save_data_size.get() != 0)
|
||||
return false;
|
||||
|
||||
if (data->supported_language_flag.get() == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nx::npdm::InstructionType UserSettings::getInstructionTypeFromString(const std::string & type_str)
|
||||
{
|
||||
std::string str = type_str;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <fnd/types.h>
|
||||
#include <fnd/MemoryBlob.h>
|
||||
#include <nx/npdm.h>
|
||||
#include "nstool.h"
|
||||
|
||||
|
@ -101,5 +102,8 @@ private:
|
|||
void decodeHexStringToBytes(const std::string& name, const std::string& str, byte_t* out, size_t out_len);
|
||||
FileType getFileTypeFromString(const std::string& type_str);
|
||||
FileType determineFileTypeFromFile(const std::string& path);
|
||||
bool determineValidNcaFromSample(const fnd::MemoryBlob& sample) const;
|
||||
bool determineValidCnmtFromSample(const fnd::MemoryBlob& sample) const;
|
||||
bool determineValidNacpFromSample(const fnd::MemoryBlob& sample) const;
|
||||
nx::npdm::InstructionType getInstructionTypeFromString(const std::string& type_str);
|
||||
};
|
Loading…
Reference in a new issue