[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 // 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 // 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_start(sizeToBlockNum(mSections[i].offset));
hdr->section(section).set_end(mSections[i].end_blk); 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(section).set_key_type(mSections[i].key_type);
hdr->section_hash(section) = mSections[i].hash; 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; if (hdr->section(section).start() == 0 && hdr->section(section).end() == 0) continue;
// add high level struct // 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++) 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; return mUnk0;
} }
const std::vector<NcaHeader::sSection>& NcaHeader::getSections() const const fnd::List<NcaHeader::sSection>& NcaHeader::getSections() const
{ {
return mSections; return mSections;
} }
void NcaHeader::addSection(const sSection & section) void NcaHeader::addSection(const sSection & section)
{ {
if (mSections.size() >= kSectionNum) if (mSections.getSize() >= kSectionNum)
{ {
throw fnd::Exception(kModuleName, "Too many NCA sections"); 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; return mAesKeys;
} }
void NcaHeader::addKey(const crypto::aes::sAes128Key & key) 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"); throw fnd::Exception(kModuleName, "Too many NCA aes keys");
} }
mAesKeys.push_back(key); mAesKeys.addElement(key);
} }
void NcaHeader::clearVariables() void NcaHeader::clearVariables()
@ -151,6 +151,34 @@ u32 NcaHeader::sizeToBlockNum(u64 real_size) const
return align(real_size, mBlockSize)/mBlockSize; 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() NcaHeader::NcaHeader()
{ {
clearVariables(); clearVariables();
@ -158,7 +186,7 @@ NcaHeader::NcaHeader()
NcaHeader::NcaHeader(const NcaHeader & other) NcaHeader::NcaHeader(const NcaHeader & other)
{ {
importBinary(other.getBytes()); copyFrom(other);
} }
NcaHeader::NcaHeader(const u8 * bytes) NcaHeader::NcaHeader(const u8 * bytes)
@ -166,9 +194,14 @@ NcaHeader::NcaHeader(const u8 * bytes)
importBinary(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) void NcaHeader::operator=(const NcaHeader & other)

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <vector>
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/memory_blob.h> #include <fnd/memory_blob.h>
#include <fnd/List.h>
#include <crypto/aes.h> #include <crypto/aes.h>
#include <crypto/sha.h> #include <crypto/sha.h>
#include <nx/ISerialiseableBinary.h> #include <nx/ISerialiseableBinary.h>
@ -12,12 +12,33 @@ class NcaHeader : public ISerialiseableBinary
public: public:
struct sSection struct sSection
{ {
u32 start_blk;
u32 end_blk;
u64 offset; u64 offset;
u64 size; u64 size;
u8 key_type; u8 key_type;
crypto::sha::sSha256Hash hash; 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; static const size_t kDefaultBlockSize = 0x200;
@ -26,7 +47,8 @@ public:
NcaHeader(const NcaHeader& other); NcaHeader(const NcaHeader& other);
NcaHeader(const u8* bytes); 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); void operator=(const NcaHeader& other);
// to be used after export // to be used after export
@ -44,9 +66,9 @@ public:
u64 getProgramId() const; u64 getProgramId() const;
void setProgramId(u64 program_id); void setProgramId(u64 program_id);
u32 getUnk() const; u32 getUnk() const;
const std::vector<sSection>& getSections() const; const fnd::List<sSection>& getSections() const;
void addSection(const sSection& section); 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); void addKey(const crypto::aes::sAes128Key& key);
private: private:
@ -122,10 +144,12 @@ private:
u64 mNcaSize; u64 mNcaSize;
u64 mProgramId; u64 mProgramId;
u32 mUnk0; u32 mUnk0;
std::vector<sSection> mSections; fnd::List<sSection> mSections;
std::vector<crypto::aes::sAes128Key> mAesKeys; fnd::List<crypto::aes::sAes128Key> mAesKeys;
void clearVariables(); void clearVariables();
u64 blockNumToSize(u32 block_num) const; u64 blockNumToSize(u32 block_num) const;
u32 sizeToBlockNum(u64 real_size) 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 const u8 * SacEntry::getBytes() const
@ -47,9 +64,20 @@ void SacEntry::exportBinary()
} }
void SacEntry::importBinary(const u8 * bytes) 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; bool isServer = (bytes[0] & SAC_IS_SERVER) == SAC_IS_SERVER;
size_t nameLen = (bytes[0] & SAC_NAME_LEN_MASK); 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) if (nameLen == 0)
{ {
throw fnd::Exception(kModuleName, "SAC entry has no service name"); 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); 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 bool SacEntry::isServer() const
{ {
return mIsServer; return mIsServer;
@ -99,3 +118,22 @@ void SacEntry::setName(const std::string & name)
mName = 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 class SacEntry : public ISerialiseableBinary
{ {
public:
SacEntry(); SacEntry();
SacEntry(const std::string& name, bool isServer);
SacEntry(const SacEntry& other); 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 // to be used after export
const u8* getBytes() const; const u8* getBytes() const;
@ -40,4 +45,7 @@ private:
// variables // variables
bool mIsServer; bool mIsServer;
std::string mName; 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; return 1;
} }
fnd::MemoryBlob nca; try
fnd::io::readFile(argv[1], nca);
u8 sector[kNcaSectorSize];
// nca test
if (argc == 2)
{ {
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; u8 sector[kNcaSectorSize];
hdr.importBinary(sector);
printf("NCA Header\n"); // nca test
printf(" Size: 0x%" PRIx64 "\n", hdr.getNcaSize()); if (argc == 2)
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++)
{ {
const NcaHeader::sSection& section = hdr.getSections()[i]; decryptNcaSectorXts(nca, sector, 1, nx::crypto::aes::nca_header_key[0], nx::crypto::aes::nca_header_key[1]);
printf(" %lu:\n", i);
printf(" Start Blk: %" PRId32 "\n", section.start_blk); NcaHeader hdr;
printf(" End Blk: %" PRId32 "\n", section.end_blk); hdr.importBinary(sector);
printf(" Offset: 0x%" PRIx64 "\n", section.offset);
printf(" Size: 0x%" PRIx64 "\n", section.size); printf("[NCA Header]\n");
printf(" KeyType: 0x%02x\n", section.key_type); printf(" Size: 0x%" PRIx64 "\n", hdr.getNcaSize());
printf(" Hash: "); printf(" ProgID: 0x%016" PRIx64 "\n", hdr.getProgramId());
hexDump(section.hash.bytes, crypto::sha::kSha256HashLen); printf(" Unk0: 0x%" PRIx32 "\n", hdr.getUnk());
printf("\n"); printf(" Sections:\n");
} for (size_t i = 0; i < hdr.getSections().getSize(); i++)
printf(" AES Keys:\n"); {
for (size_t i = 0; i < hdr.getAesKeys().size(); i++) const NcaHeader::sSection& section = hdr.getSections()[i];
{ printf(" %lu:\n", i);
printf(" %lu: ", i); //printf(" Start Blk: %" PRId32 "\n", section.start_blk);
hexDump(hdr.getAesKeys()[i].key, crypto::aes::kAes128KeySize); //printf(" End Blk: %" PRId32 "\n", section.end_blk);
printf("\n"); 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; return 0;