mirror of
https://github.com/jakcron/nstool
synced 2024-11-22 21:49:30 +00:00
Start development for implemented SDK XCI support
This commit is contained in:
parent
2f22ac205c
commit
3d0810de6d
6 changed files with 137 additions and 27 deletions
2
deps/libnintendo-hac
vendored
2
deps/libnintendo-hac
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 07ad9cb0c08e12795337e16fbc45b5adf9e5e3f1
|
Subproject commit 1c8cba84c14f5353bdb94d6e14285dbb0e5fc99d
|
|
@ -10,6 +10,7 @@ GameCardProcess::GameCardProcess() :
|
||||||
mCliOutputMode(_BIT(OUTPUT_BASIC)),
|
mCliOutputMode(_BIT(OUTPUT_BASIC)),
|
||||||
mVerify(false),
|
mVerify(false),
|
||||||
mListFs(false),
|
mListFs(false),
|
||||||
|
mProccessExtendedHeader(false),
|
||||||
mRootPfs(),
|
mRootPfs(),
|
||||||
mExtractInfo()
|
mExtractInfo()
|
||||||
{
|
{
|
||||||
|
@ -73,18 +74,46 @@ void GameCardProcess::importHeader()
|
||||||
throw fnd::Exception(kModuleName, "No file reader set.");
|
throw fnd::Exception(kModuleName, "No file reader set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// read header page
|
// allocate memory for header
|
||||||
(*mFile)->read((byte_t*)&mHdrPage, 0, sizeof(nn::hac::sGcHeaderPage));
|
scratch.alloc(sizeof(nn::hac::sSdkGcHeader));
|
||||||
|
|
||||||
// allocate memory for and decrypt sXciHeader
|
// read header region
|
||||||
scratch.alloc(sizeof(nn::hac::sGcHeader));
|
(*mFile)->read((byte_t*)scratch.data(), 0, sizeof(nn::hac::sSdkGcHeader));
|
||||||
|
|
||||||
|
// determine if this is a SDK XCI or a "Community" XCI
|
||||||
|
if (((nn::hac::sSdkGcHeader*)scratch.data())->signed_header.header.st_magic.get() == nn::hac::gc::kGcHeaderStructMagic)
|
||||||
|
{
|
||||||
|
mIsTrueSdkXci = true;
|
||||||
|
mGcHeaderOffset = sizeof(nn::hac::sGcKeyDataRegion);
|
||||||
|
}
|
||||||
|
else if (((nn::hac::sGcHeader_Rsa2048Signed*)scratch.data())->header.st_magic.get() == nn::hac::gc::kGcHeaderStructMagic)
|
||||||
|
{
|
||||||
|
mIsTrueSdkXci = false;
|
||||||
|
mGcHeaderOffset = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "GameCard image did not have expected magic bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
nn::hac::sGcHeader_Rsa2048Signed* hdr_ptr = (nn::hac::sGcHeader_Rsa2048Signed*)(scratch.data() + mGcHeaderOffset);
|
||||||
|
|
||||||
|
// generate hash of raw header
|
||||||
|
fnd::sha::Sha256((byte_t*)&hdr_ptr->header, sizeof(nn::hac::sGcHeader), mHdrHash.bytes);
|
||||||
|
|
||||||
|
// save the signature
|
||||||
|
memcpy(mHdrSignature, hdr_ptr->signature, fnd::rsa::kRsa2048Size);
|
||||||
|
|
||||||
|
// decrypt extended header
|
||||||
fnd::aes::sAes128Key header_key;
|
fnd::aes::sAes128Key header_key;
|
||||||
mKeyCfg.getXciHeaderKey(header_key);
|
if (mKeyCfg.getXciHeaderKey(header_key))
|
||||||
nn::hac::GameCardUtils::decryptXciHeader((const byte_t*)&mHdrPage.header, scratch.data(), header_key.key);
|
{
|
||||||
|
nn::hac::GameCardUtils::decryptXciHeader(&hdr_ptr->header, header_key.key);
|
||||||
|
mProccessExtendedHeader = true;
|
||||||
|
}
|
||||||
|
|
||||||
// deserialise header
|
// deserialise header
|
||||||
mHdr.fromBytes(scratch.data(), scratch.size());
|
mHdr.fromBytes((byte_t*)&hdr_ptr->header, sizeof(nn::hac::sGcHeader));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameCardProcess::displayHeader()
|
void GameCardProcess::displayHeader()
|
||||||
|
@ -110,7 +139,7 @@ void GameCardProcess::displayHeader()
|
||||||
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
|
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
|
||||||
{
|
{
|
||||||
std::cout << " InitialData:" << std::endl;
|
std::cout << " InitialData:" << std::endl;
|
||||||
std::cout << " KekIndex: " << std::dec << (uint32_t)mHdr.getKekIndex() << std::endl;
|
std::cout << " KekIndex: " << getKekIndexStr(mHdr.getKekIndex()) << "(" << std::dec << (uint32_t)mHdr.getKekIndex() << ")" << std::endl;
|
||||||
std::cout << " TitleKeyDecIndex: " << std::dec << (uint32_t)mHdr.getTitleKeyDecIndex() << std::endl;
|
std::cout << " TitleKeyDecIndex: " << std::dec << (uint32_t)mHdr.getTitleKeyDecIndex() << std::endl;
|
||||||
std::cout << " Hash:" << std::endl;
|
std::cout << " Hash:" << std::endl;
|
||||||
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes, 0x10, true, ":") << std::endl;
|
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes, 0x10, true, ":") << std::endl;
|
||||||
|
@ -158,10 +187,10 @@ void GameCardProcess::displayHeader()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (mHdr.getFwVerMinor() != 0)
|
if (mProccessExtendedHeader)
|
||||||
{
|
{
|
||||||
std::cout << "[GameCard Extended Header]" << std::endl;
|
std::cout << "[GameCard Extended Header]" << std::endl;
|
||||||
std::cout << " FwVersion: v" << std::dec << mHdr.getFwVerMajor() << "." << mHdr.getFwVerMinor() << std::endl;
|
std::cout << " FwVersion: v" << std::dec << mHdr.getFwVersion() << "(" << getCardFwVersionDescriptionStr(mHdr.getFwVersion()) << ")" << std::endl;
|
||||||
std::cout << " AccCtrl1: 0x" << std::hex << mHdr.getAccCtrl1() << std::endl;
|
std::cout << " AccCtrl1: 0x" << std::hex << mHdr.getAccCtrl1() << std::endl;
|
||||||
std::cout << " CardClockRate: " << getCardClockRate(mHdr.getAccCtrl1()) << std::endl;
|
std::cout << " CardClockRate: " << getCardClockRate(mHdr.getAccCtrl1()) << std::endl;
|
||||||
std::cout << " Wait1TimeRead: 0x" << std::hex << mHdr.getWait1TimeRead() << std::endl;
|
std::cout << " Wait1TimeRead: 0x" << std::hex << mHdr.getWait1TimeRead() << std::endl;
|
||||||
|
@ -169,12 +198,13 @@ void GameCardProcess::displayHeader()
|
||||||
std::cout << " Wait1TimeWrite: 0x" << std::hex << mHdr.getWait1TimeWrite() << std::endl;
|
std::cout << " Wait1TimeWrite: 0x" << std::hex << mHdr.getWait1TimeWrite() << std::endl;
|
||||||
std::cout << " Wait2TimeWrite: 0x" << std::hex << mHdr.getWait2TimeWrite() << std::endl;
|
std::cout << " Wait2TimeWrite: 0x" << std::hex << mHdr.getWait2TimeWrite() << std::endl;
|
||||||
std::cout << " FwMode: 0x" << std::hex << mHdr.getFwMode() << std::endl;
|
std::cout << " FwMode: 0x" << std::hex << mHdr.getFwMode() << std::endl;
|
||||||
|
std::cout << " CompatibilityType: " << getCardCompatibiltyType(mHdr.getCompatibilityType()) << "(" << std::dec << mHdr.getCompatibilityType() << ")" << std::endl;
|
||||||
std::cout << " Update Partition Info:" << std::endl;
|
std::cout << " Update Partition Info:" << std::endl;
|
||||||
#define _SPLIT_VER(ver) std::dec << ((ver>>26) & 0x3f) << "." << ((ver>>20) & 0x3f) << "." << ((ver>>16) & 0xf) << "." << (ver & 0xffff)
|
#define _SPLIT_VER(ver) std::dec << ((ver>>26) & 0x3f) << "." << ((ver>>20) & 0x3f) << "." << ((ver>>16) & 0xf) << "." << (ver & 0xffff)
|
||||||
std::cout << " CUP Version: v" << std::dec << mHdr.getUppVersion() << " (" << _SPLIT_VER(mHdr.getUppVersion()) << ")" << std::endl;
|
std::cout << " CUP Version: v" << std::dec << mHdr.getUppVersion() << " (" << _SPLIT_VER(mHdr.getUppVersion()) << ")" << std::endl;
|
||||||
#undef _SPLIT_VER
|
#undef _SPLIT_VER
|
||||||
std::cout << " CUP TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getUppId() << std::endl;
|
std::cout << " CUP TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getUppId() << std::endl;
|
||||||
std::cout << " Partition Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getUppHash(), 8, true, ":") << std::endl;
|
std::cout << " CUP Digest: " << fnd::SimpleTextOutput::arrayToString(mHdr.getUppHash(), 8, true, ":") << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,10 +221,9 @@ bool GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte
|
||||||
void GameCardProcess::validateXciSignature()
|
void GameCardProcess::validateXciSignature()
|
||||||
{
|
{
|
||||||
fnd::rsa::sRsa2048Key header_sign_key;
|
fnd::rsa::sRsa2048Key header_sign_key;
|
||||||
fnd::sha::sSha256Hash calc_hash;
|
|
||||||
fnd::sha::Sha256((byte_t*)&mHdrPage.header, sizeof(nn::hac::sGcHeader), calc_hash.bytes);
|
|
||||||
mKeyCfg.getXciHeaderSignKey(header_sign_key);
|
mKeyCfg.getXciHeaderSignKey(header_sign_key);
|
||||||
if (fnd::rsa::pkcs::rsaVerify(header_sign_key, fnd::sha::HASH_SHA256, calc_hash.bytes, mHdrPage.signature) != 0)
|
if (fnd::rsa::pkcs::rsaVerify(header_sign_key, fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrSignature) != 0)
|
||||||
{
|
{
|
||||||
std::cout << "[WARNING] GameCard Header Signature: FAIL" << std::endl;
|
std::cout << "[WARNING] GameCard Header Signature: FAIL" << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -238,6 +267,26 @@ void GameCardProcess::processPartitionPfs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* GameCardProcess::getKekIndexStr(byte_t kek_index) const
|
||||||
|
{
|
||||||
|
const char* str = nullptr;
|
||||||
|
|
||||||
|
switch (kek_index)
|
||||||
|
{
|
||||||
|
case (nn::hac::gc::KEK_PROD):
|
||||||
|
str = "Production";
|
||||||
|
break;
|
||||||
|
case (nn::hac::gc::KEK_DEV):
|
||||||
|
str = "Development";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
const char* GameCardProcess::getRomSizeStr(byte_t rom_size) const
|
const char* GameCardProcess::getRomSizeStr(byte_t rom_size) const
|
||||||
{
|
{
|
||||||
const char* str = nullptr;
|
const char* str = nullptr;
|
||||||
|
@ -282,8 +331,37 @@ const char* GameCardProcess::getHeaderFlagStr(byte_t flag) const
|
||||||
case (nn::hac::gc::FLAG_HISTORY_ERASE):
|
case (nn::hac::gc::FLAG_HISTORY_ERASE):
|
||||||
str = "HistoryErase";
|
str = "HistoryErase";
|
||||||
break;
|
break;
|
||||||
case (nn::hac::gc::FLAG_REPAIR_TOOL):
|
case (nn::hac::gc::FLAG_REPAIR_TIME_REVISOR_TOOL):
|
||||||
str = "RepairTool";
|
str = "RepairTimeRevisorTool";
|
||||||
|
break;
|
||||||
|
case (nn::hac::gc::FLAG_ALLOW_CUP_TO_CHINA):
|
||||||
|
str = "AllowCupToChina";
|
||||||
|
break;
|
||||||
|
case (nn::hac::gc::FLAG_ALLOW_CUP_TO_GLOBAL):
|
||||||
|
str = "AllowCupToGlobal";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* GameCardProcess::getCardFwVersionDescriptionStr(uint64_t version) const
|
||||||
|
{
|
||||||
|
const char* str = nullptr;
|
||||||
|
|
||||||
|
switch (version)
|
||||||
|
{
|
||||||
|
case (nn::hac::gc::FWVER_DEV):
|
||||||
|
str = "ForDevelopment";
|
||||||
|
break;
|
||||||
|
case (nn::hac::gc::FWVER_PROD):
|
||||||
|
str = "1.0.0+";
|
||||||
|
break;
|
||||||
|
case (nn::hac::gc::FWVER_PROD_SINCE_4_0_0NUP):
|
||||||
|
str = "4.0.0+";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
str = "Unknown";
|
str = "Unknown";
|
||||||
|
@ -313,3 +391,23 @@ const char* GameCardProcess::getCardClockRate(uint32_t acc_ctrl_1) const
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* GameCardProcess::getCardCompatibiltyType(byte_t flag) const
|
||||||
|
{
|
||||||
|
const char* str = nullptr;
|
||||||
|
|
||||||
|
switch (flag)
|
||||||
|
{
|
||||||
|
case (nn::hac::gc::COMPAT_GLOBAL):
|
||||||
|
str = "Global";
|
||||||
|
break;
|
||||||
|
case (nn::hac::gc::COMPAT_CHINA):
|
||||||
|
str = "China";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ private:
|
||||||
KeyConfiguration mKeyCfg;
|
KeyConfiguration mKeyCfg;
|
||||||
CliOutputMode mCliOutputMode;
|
CliOutputMode mCliOutputMode;
|
||||||
bool mVerify;
|
bool mVerify;
|
||||||
|
bool mListFs;
|
||||||
|
|
||||||
struct sExtractInfo
|
struct sExtractInfo
|
||||||
{
|
{
|
||||||
|
@ -53,10 +54,16 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool mListFs;
|
|
||||||
|
|
||||||
nn::hac::sGcHeaderPage mHdrPage;
|
|
||||||
|
bool mIsTrueSdkXci;
|
||||||
|
bool mIsSdkXciEncrypted;
|
||||||
|
size_t mGcHeaderOffset;
|
||||||
|
bool mProccessExtendedHeader;
|
||||||
|
byte_t mHdrSignature[fnd::rsa::kRsa2048Size];
|
||||||
|
fnd::sha::sSha256Hash mHdrHash;
|
||||||
nn::hac::GameCardHeader mHdr;
|
nn::hac::GameCardHeader mHdr;
|
||||||
|
|
||||||
PfsProcess mRootPfs;
|
PfsProcess mRootPfs;
|
||||||
fnd::List<sExtractInfo> mExtractInfo;
|
fnd::List<sExtractInfo> mExtractInfo;
|
||||||
|
|
||||||
|
@ -68,7 +75,10 @@ private:
|
||||||
void processPartitionPfs();
|
void processPartitionPfs();
|
||||||
|
|
||||||
// strings
|
// strings
|
||||||
|
const char* getKekIndexStr(byte_t kek_index) const;
|
||||||
const char* getRomSizeStr(byte_t rom_size) const;
|
const char* getRomSizeStr(byte_t rom_size) const;
|
||||||
const char* getHeaderFlagStr(byte_t flag) const;
|
const char* getHeaderFlagStr(byte_t flag) const;
|
||||||
|
const char* getCardFwVersionDescriptionStr(uint64_t version) const;
|
||||||
const char* getCardClockRate(uint32_t acc_ctrl_1) const;
|
const char* getCardClockRate(uint32_t acc_ctrl_1) const;
|
||||||
|
const char* getCardCompatibiltyType(byte_t flag) const;
|
||||||
};
|
};
|
|
@ -609,7 +609,7 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str)
|
||||||
|
|
||||||
FileType type;
|
FileType type;
|
||||||
if (str == "gc" || str == "gamecard" || str == "xci")
|
if (str == "gc" || str == "gamecard" || str == "xci")
|
||||||
type = FILE_GC;
|
type = FILE_GAMECARD;
|
||||||
else if (str == "nsp")
|
else if (str == "nsp")
|
||||||
type = FILE_NSP;
|
type = FILE_NSP;
|
||||||
else if (str == "partitionfs" || str == "hashedpartitionfs" \
|
else if (str == "partitionfs" || str == "hashedpartitionfs" \
|
||||||
|
@ -666,9 +666,11 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
||||||
#define _TYPE_PTR(st) ((st*)(scratch.data()))
|
#define _TYPE_PTR(st) ((st*)(scratch.data()))
|
||||||
#define _ASSERT_SIZE(sz) (scratch.size() >= (sz))
|
#define _ASSERT_SIZE(sz) (scratch.size() >= (sz))
|
||||||
|
|
||||||
// test npdm
|
// test gamecard
|
||||||
if (_ASSERT_SIZE(sizeof(nn::hac::sGcHeaderPage)) && _TYPE_PTR(nn::hac::sGcHeaderPage)->header.st_magic.get() == nn::hac::gc::kGcHeaderStructMagic)
|
if (_ASSERT_SIZE(sizeof(nn::hac::sGcHeader_Rsa2048Signed)) && _TYPE_PTR(nn::hac::sGcHeader_Rsa2048Signed)->header.st_magic.get() == nn::hac::gc::kGcHeaderStructMagic)
|
||||||
file_type = FILE_GC;
|
file_type = FILE_GAMECARD;
|
||||||
|
else if (_ASSERT_SIZE(sizeof(nn::hac::sSdkGcHeader)) && _TYPE_PTR(nn::hac::sSdkGcHeader)->signed_header.header.st_magic.get() == nn::hac::gc::kGcHeaderStructMagic)
|
||||||
|
file_type = FILE_GAMECARD;
|
||||||
// test pfs0
|
// test pfs0
|
||||||
else if (_ASSERT_SIZE(sizeof(nn::hac::sPfsHeader)) && _TYPE_PTR(nn::hac::sPfsHeader)->st_magic.get() == nn::hac::pfs::kPfsStructMagic)
|
else if (_ASSERT_SIZE(sizeof(nn::hac::sPfsHeader)) && _TYPE_PTR(nn::hac::sPfsHeader)->st_magic.get() == nn::hac::pfs::kPfsStructMagic)
|
||||||
file_type = FILE_PARTITIONFS;
|
file_type = FILE_PARTITIONFS;
|
||||||
|
|
|
@ -10,7 +10,7 @@ static const size_t kNcaKeakNum = nn::hac::nca::kKeyAreaEncryptionKeyNum;
|
||||||
|
|
||||||
enum FileType
|
enum FileType
|
||||||
{
|
{
|
||||||
FILE_GC,
|
FILE_GAMECARD,
|
||||||
FILE_NSP,
|
FILE_NSP,
|
||||||
FILE_PARTITIONFS,
|
FILE_PARTITIONFS,
|
||||||
FILE_ROMFS,
|
FILE_ROMFS,
|
||||||
|
|
|
@ -40,7 +40,7 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
fnd::SharedPtr<fnd::IFile> inputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read));
|
fnd::SharedPtr<fnd::IFile> inputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read));
|
||||||
|
|
||||||
if (user_set.getFileType() == FILE_GC)
|
if (user_set.getFileType() == FILE_GAMECARD)
|
||||||
{
|
{
|
||||||
GameCardProcess obj;
|
GameCardProcess obj;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue