[nx/ncatool] Update SacEntry and NcaHeader to inherit from ISerialiseableBinary. ncatool is updated accordingly.

This commit is contained in:
jakcron 2017-07-06 20:55:58 +10:00
parent 74820d9274
commit e7172949da
5 changed files with 181 additions and 72 deletions

View file

@ -14,13 +14,13 @@ void NcaHeader::exportBinary()
// TODO: properly reconstruct NCA layout? atm in hands of user
for (size_t i = 0; i < mSections.size(); i++)
for (size_t i = 0; i < mSections.getSize(); i++)
{
// determine section index
u8 section = mSections.size() - 1 - i;
u8 section = mSections.getSize() - 1 - i;
hdr->section(section).set_start(mSections[i].start_blk);
hdr->section(section).set_end(mSections[i].end_blk);
hdr->section(section).set_start(sizeToBlockNum(mSections[i].offset));
hdr->section(section).set_end(sizeToBlockNum(mSections[i].offset) + sizeToBlockNum(mSections[i].size));
hdr->section(section).set_key_type(mSections[i].key_type);
hdr->section_hash(section) = mSections[i].hash;
}
@ -59,12 +59,12 @@ void NcaHeader::importBinary(const u8 * bytes)
if (hdr->section(section).start() == 0 && hdr->section(section).end() == 0) continue;
// add high level struct
mSections.push_back({ hdr->section(section).start(), hdr->section(section).end(), blockNumToSize(hdr->section(section).start()), blockNumToSize(hdr->section(section).end()- hdr->section(section).start()), hdr->section(section).key_type(), hdr->section_hash(section) });
mSections.addElement({ blockNumToSize(hdr->section(section).start()), blockNumToSize(hdr->section(section).end() - hdr->section(section).start()), hdr->section(section).key_type(), hdr->section_hash(section) });
}
for (size_t i = 0; i < kAesKeyNum; i++)
{
mAesKeys.push_back(hdr->aes_key(i));
mAesKeys.addElement(hdr->aes_key(i));
}
}
@ -102,33 +102,33 @@ u32 NcaHeader::getUnk() const
return mUnk0;
}
const std::vector<NcaHeader::sSection>& NcaHeader::getSections() const
const fnd::List<NcaHeader::sSection>& NcaHeader::getSections() const
{
return mSections;
}
void NcaHeader::addSection(const sSection & section)
{
if (mSections.size() >= kSectionNum)
if (mSections.getSize() >= kSectionNum)
{
throw fnd::Exception(kModuleName, "Too many NCA sections");
}
mSections.push_back(section);
mSections.addElement(section);
}
const std::vector<crypto::aes::sAes128Key>& NcaHeader::getAesKeys() const
const fnd::List<crypto::aes::sAes128Key>& NcaHeader::getAesKeys() const
{
return mAesKeys;
}
void NcaHeader::addKey(const crypto::aes::sAes128Key & key)
{
if (mAesKeys.size() >= kAesKeyNum)
if (mAesKeys.getSize() >= kAesKeyNum)
{
throw fnd::Exception(kModuleName, "Too many NCA aes keys");
}
mAesKeys.push_back(key);
mAesKeys.addElement(key);
}
void NcaHeader::clearVariables()
@ -151,6 +151,34 @@ u32 NcaHeader::sizeToBlockNum(u64 real_size) const
return align(real_size, mBlockSize)/mBlockSize;
}
bool NcaHeader::isEqual(const NcaHeader & other) const
{
return (mBlockSize == other.mBlockSize) \
&& (mNcaSize == other.mNcaSize) \
&& (mProgramId == other.mProgramId) \
&& (mUnk0 == other.mUnk0) \
&& (mSections == other.mSections) \
&& (mAesKeys == other.mAesKeys);
}
void NcaHeader::copyFrom(const NcaHeader & other)
{
if (other.getSize())
{
importBinary(other.getBytes(), other.getSize());
}
else
{
this->mBinaryBlob.clear();
mBlockSize = other.mBlockSize;
mNcaSize = other.mNcaSize;
mProgramId = other.mProgramId;
mUnk0 = other.mUnk0;
mSections = other.mSections;
mAesKeys = other.mAesKeys;
}
}
NcaHeader::NcaHeader()
{
clearVariables();
@ -158,7 +186,7 @@ NcaHeader::NcaHeader()
NcaHeader::NcaHeader(const NcaHeader & other)
{
importBinary(other.getBytes());
copyFrom(other);
}
NcaHeader::NcaHeader(const u8 * bytes)
@ -166,9 +194,14 @@ NcaHeader::NcaHeader(const u8 * bytes)
importBinary(bytes);
}
bool NcaHeader::operator==(const NcaHeader & other)
bool NcaHeader::operator==(const NcaHeader & other) const
{
return memcmp(this->getBytes(), other.getBytes(), this->getSize()) == 0;
return isEqual(other);
}
bool NcaHeader::operator!=(const NcaHeader & other) const
{
return !isEqual(other);
}
void NcaHeader::operator=(const NcaHeader & other)

View file

@ -1,8 +1,8 @@
#pragma once
#include <vector>
#include <string>
#include <fnd/types.h>
#include <fnd/memory_blob.h>
#include <fnd/List.h>
#include <crypto/aes.h>
#include <crypto/sha.h>
#include <nx/ISerialiseableBinary.h>
@ -12,12 +12,33 @@ class NcaHeader : public ISerialiseableBinary
public:
struct sSection
{
u32 start_blk;
u32 end_blk;
u64 offset;
u64 size;
u8 key_type;
crypto::sha::sSha256Hash hash;
const sSection& operator=(const sSection& other)
{
offset = other.offset;
size = other.size;
key_type = other.key_type;
hash = other.hash;
return *this;
}
bool operator==(const sSection& other) const
{
return (offset == other.offset) \
&& (size == other.size) \
&& (key_type == other.key_type) \
&& (hash == other.hash);
}
bool operator!=(const sSection& other) const
{
return operator==(other);
}
};
static const size_t kDefaultBlockSize = 0x200;
@ -26,7 +47,8 @@ public:
NcaHeader(const NcaHeader& other);
NcaHeader(const u8* bytes);
bool operator==(const NcaHeader& other);
bool operator==(const NcaHeader& other) const;
bool operator!=(const NcaHeader& other) const;
void operator=(const NcaHeader& other);
// to be used after export
@ -44,9 +66,9 @@ public:
u64 getProgramId() const;
void setProgramId(u64 program_id);
u32 getUnk() const;
const std::vector<sSection>& getSections() const;
const fnd::List<sSection>& getSections() const;
void addSection(const sSection& section);
const std::vector<crypto::aes::sAes128Key>& getAesKeys() const;
const fnd::List<crypto::aes::sAes128Key>& getAesKeys() const;
void addKey(const crypto::aes::sAes128Key& key);
private:
@ -122,10 +144,12 @@ private:
u64 mNcaSize;
u64 mProgramId;
u32 mUnk0;
std::vector<sSection> mSections;
std::vector<crypto::aes::sAes128Key> mAesKeys;
fnd::List<sSection> mSections;
fnd::List<crypto::aes::sAes128Key> mAesKeys;
void clearVariables();
u64 blockNumToSize(u32 block_num) const;
u32 sizeToBlockNum(u64 real_size) const;
bool isEqual(const NcaHeader& other) const;
void copyFrom(const NcaHeader& other);
};

View file

@ -6,14 +6,31 @@ SacEntry::SacEntry() :
{
}
SacEntry::SacEntry(const SacEntry & other)
SacEntry::SacEntry(const std::string & name, bool isServer) :
mIsServer(isServer),
mName(name)
{
importBinary(other.getBytes(), other.getSize());
exportBinary();
}
SacEntry::SacEntry(const u8 * bytes)
SacEntry::SacEntry(const SacEntry & other)
{
importBinary(bytes);
copyFrom(other);
}
bool SacEntry::operator==(const SacEntry & other) const
{
return isEqual(other);
}
bool SacEntry::operator!=(const SacEntry & other) const
{
return !isEqual(other);
}
void SacEntry::operator=(const SacEntry & other)
{
copyFrom(other);
}
const u8 * SacEntry::getBytes() const
@ -47,9 +64,20 @@ void SacEntry::exportBinary()
}
void SacEntry::importBinary(const u8 * bytes)
{
throw fnd::Exception(kModuleName, "Unsupported operation. importBinary(const u8* bytes) is not supported for variable size structures.");
}
void SacEntry::importBinary(const u8 * bytes, size_t len)
{
bool isServer = (bytes[0] & SAC_IS_SERVER) == SAC_IS_SERVER;
size_t nameLen = (bytes[0] & SAC_NAME_LEN_MASK);
if (nameLen+1 > len)
{
throw fnd::Exception(kModuleName, "SAC entry is too small");
}
if (nameLen == 0)
{
throw fnd::Exception(kModuleName, "SAC entry has no service name");
@ -66,15 +94,6 @@ void SacEntry::importBinary(const u8 * bytes)
mName = std::string((const char*)(mBinaryBlob.getBytes() + 1), nameLen);
}
void SacEntry::importBinary(const u8 * bytes, size_t len)
{
importBinary(bytes);
if (getSize() != len)
{
throw fnd::Exception(kModuleName, "SAC Entry has unexpected size");
}
}
bool SacEntry::isServer() const
{
return mIsServer;
@ -99,3 +118,22 @@ void SacEntry::setName(const std::string & name)
mName = name;
}
bool SacEntry::isEqual(const SacEntry & other) const
{
return (mIsServer == other.mIsServer) \
&& (mName == other.mName);
}
void SacEntry::copyFrom(const SacEntry & other)
{
if (other.getSize())
{
importBinary(other.getBytes(), other.getSize());
}
else
{
this->mIsServer = other.mIsServer;
this->mName = other.mName;
}
}

View file

@ -6,9 +6,14 @@
class SacEntry : public ISerialiseableBinary
{
public:
SacEntry();
SacEntry(const std::string& name, bool isServer);
SacEntry(const SacEntry& other);
SacEntry(const u8* bytes);
bool operator==(const SacEntry& other) const;
bool operator!=(const SacEntry& other) const;
void operator=(const SacEntry& other);
// to be used after export
const u8* getBytes() const;
@ -40,4 +45,7 @@ private:
// variables
bool mIsServer;
std::string mName;
bool isEqual(const SacEntry& other) const;
void copyFrom(const SacEntry& other);
};

View file

@ -57,46 +57,52 @@ int main(int argc, char** argv)
return 1;
}
fnd::MemoryBlob nca;
fnd::io::readFile(argv[1], nca);
u8 sector[kNcaSectorSize];
// nca test
if (argc == 2)
try
{
decryptNcaSectorXts(nca, sector, 1, nx::crypto::aes::nca_header_key[0], nx::crypto::aes::nca_header_key[1]);
fnd::MemoryBlob nca;
fnd::io::readFile(argv[1], nca);
NcaHeader hdr;
hdr.importBinary(sector);
u8 sector[kNcaSectorSize];
printf("NCA Header\n");
printf(" Size: 0x%" PRIx64 "\n", hdr.getNcaSize());
printf(" ProgID: 0x%016" PRIx64 "\n", hdr.getProgramId());
printf(" Unk0: 0x%" PRIx32 "\n", hdr.getUnk());
printf(" Sections:\n");
for (size_t i = 0; i < hdr.getSections().size(); i++)
// nca test
if (argc == 2)
{
const NcaHeader::sSection& section = hdr.getSections()[i];
printf(" %lu:\n", i);
printf(" Start Blk: %" PRId32 "\n", section.start_blk);
printf(" End Blk: %" PRId32 "\n", section.end_blk);
printf(" Offset: 0x%" PRIx64 "\n", section.offset);
printf(" Size: 0x%" PRIx64 "\n", section.size);
printf(" KeyType: 0x%02x\n", section.key_type);
printf(" Hash: ");
hexDump(section.hash.bytes, crypto::sha::kSha256HashLen);
printf("\n");
}
printf(" AES Keys:\n");
for (size_t i = 0; i < hdr.getAesKeys().size(); i++)
{
printf(" %lu: ", i);
hexDump(hdr.getAesKeys()[i].key, crypto::aes::kAes128KeySize);
printf("\n");
}
decryptNcaSectorXts(nca, sector, 1, nx::crypto::aes::nca_header_key[0], nx::crypto::aes::nca_header_key[1]);
NcaHeader hdr;
hdr.importBinary(sector);
printf("[NCA Header]\n");
printf(" Size: 0x%" PRIx64 "\n", hdr.getNcaSize());
printf(" ProgID: 0x%016" PRIx64 "\n", hdr.getProgramId());
printf(" Unk0: 0x%" PRIx32 "\n", hdr.getUnk());
printf(" Sections:\n");
for (size_t i = 0; i < hdr.getSections().getSize(); i++)
{
const NcaHeader::sSection& section = hdr.getSections()[i];
printf(" %lu:\n", i);
//printf(" Start Blk: %" PRId32 "\n", section.start_blk);
//printf(" End Blk: %" PRId32 "\n", section.end_blk);
printf(" Offset: 0x%" PRIx64 "\n", section.offset);
printf(" Size: 0x%" PRIx64 "\n", section.size);
printf(" KeyType: 0x%02x\n", section.key_type);
printf(" Hash: ");
hexDump(section.hash.bytes, crypto::sha::kSha256HashLen);
printf("\n");
}
printf(" AES Keys:\n");
for (size_t i = 0; i < hdr.getAesKeys().getSize(); i++)
{
printf(" %lu: ", i);
hexDump(hdr.getAesKeys()[i].key, crypto::aes::kAes128KeySize);
printf("\n");
}
}
} catch (const fnd::Exception& e)
{
printf("[%s%sERROR] %s\n", e.module(), strlen(e.module()) > 0 ? " " : "", e.what());
}
return 0;