From 24306d80ee202e0041f24d3740f71a476faffed5 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 24 Sep 2021 18:19:22 +0800 Subject: [PATCH 001/119] Catch when NCA partitions fail to be processed due to apparent corruption. --- src/NcaProcess.cpp | 70 ++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index 51cad14..6ed73d3 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -570,43 +570,47 @@ void NcaProcess::processPartitions() continue; } - if (partition.format_type == nn::hac::nca::FormatType::PartitionFs) - { - PfsProcess pfs; - pfs.setInputFile(partition.reader); - pfs.setCliOutputMode(mCliOutputMode); - pfs.setListFs(mListFs); - if (mHdr.getContentType() == nn::hac::nca::ContentType::Program) + try { + if (partition.format_type == nn::hac::nca::FormatType::PartitionFs) { - pfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + nn::hac::ContentArchiveUtil::getProgramContentParititionIndexAsString((nn::hac::nca::ProgramContentPartitionIndex)index)); + PfsProcess pfs; + pfs.setInputFile(partition.reader); + pfs.setCliOutputMode(mCliOutputMode); + pfs.setListFs(mListFs); + if (mHdr.getContentType() == nn::hac::nca::ContentType::Program) + { + pfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + nn::hac::ContentArchiveUtil::getProgramContentParititionIndexAsString((nn::hac::nca::ProgramContentPartitionIndex)index)); + } + else + { + pfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/"); + } + + if (mPartitionPath[index].doExtract) + pfs.setExtractPath(mPartitionPath[index].path); + pfs.process(); } - else + else if (partition.format_type == nn::hac::nca::FormatType::RomFs) { - pfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/"); - } - - if (mPartitionPath[index].doExtract) - pfs.setExtractPath(mPartitionPath[index].path); - pfs.process(); - } - else if (partition.format_type == nn::hac::nca::FormatType::RomFs) - { - RomfsProcess romfs; - romfs.setInputFile(partition.reader); - romfs.setCliOutputMode(mCliOutputMode); - romfs.setListFs(mListFs); - if (mHdr.getContentType() == nn::hac::nca::ContentType::Program) - { - romfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + nn::hac::ContentArchiveUtil::getProgramContentParititionIndexAsString((nn::hac::nca::ProgramContentPartitionIndex)index)); - } - else - { - romfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/"); - } + RomfsProcess romfs; + romfs.setInputFile(partition.reader); + romfs.setCliOutputMode(mCliOutputMode); + romfs.setListFs(mListFs); + if (mHdr.getContentType() == nn::hac::nca::ContentType::Program) + { + romfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + nn::hac::ContentArchiveUtil::getProgramContentParititionIndexAsString((nn::hac::nca::ProgramContentPartitionIndex)index)); + } + else + { + romfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/"); + } - if (mPartitionPath[index].doExtract) - romfs.setExtractPath(mPartitionPath[index].path); - romfs.process(); + if (mPartitionPath[index].doExtract) + romfs.setExtractPath(mPartitionPath[index].path); + romfs.process(); + } + } catch (const fnd::Exception& e) { + std::cout << "[WARNING] NCA Partition " << std::dec << index << " not readable (" << e.error() << ")." << std::endl; } } } From 80951a2fb2e9bc3ab0b657c30b1313856e942aaa Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 25 Sep 2021 10:21:45 +0800 Subject: [PATCH 002/119] Add libtoolchain --- .gitmodules | 3 +++ deps/libtoolchain | 1 + 2 files changed, 4 insertions(+) create mode 160000 deps/libtoolchain diff --git a/.gitmodules b/.gitmodules index 6775067..bda8ed5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "deps/libmbedtls"] path = deps/libmbedtls url = https://github.com/jakcron/libmbedtls +[submodule "deps/libtoolchain"] + path = deps/libtoolchain + url = https://github.com/jakcron/libtoolchain.git diff --git a/deps/libtoolchain b/deps/libtoolchain new file mode 160000 index 0000000..c730303 --- /dev/null +++ b/deps/libtoolchain @@ -0,0 +1 @@ +Subproject commit c730303b8c628ca0f853c9ac02d9efe49b76061d From 2f362f35fe790d3a65f0fb8e2389ed84ad6118f6 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 25 Sep 2021 10:45:58 +0800 Subject: [PATCH 003/119] Add libfmt and update dependencies. --- .gitmodules | 3 + build/visualstudio/nstool/nstool.vcxproj | 390 +++++++++++------------ deps/libfmt | 1 + deps/libfnd | 2 +- deps/liblz4 | 2 +- deps/libmbedtls | 2 +- deps/libnintendo-es | 2 +- deps/libnintendo-hac | 2 +- deps/libnintendo-hac-hb | 2 +- deps/libnintendo-pki | 2 +- 10 files changed, 206 insertions(+), 202 deletions(-) create mode 160000 deps/libfmt diff --git a/.gitmodules b/.gitmodules index bda8ed5..61d11ae 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "deps/libtoolchain"] path = deps/libtoolchain url = https://github.com/jakcron/libtoolchain.git +[submodule "deps/libfmt"] + path = deps/libfmt + url = https://github.com/jakcron/libfmt.git diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index f56dfbe..e07dcf0 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -1,196 +1,196 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {775EF5EB-CA49-4994-8AC4-47B4A5385266} - nstool - 10.0.17763.0 - - - - Application - true - v141 - MultiByte - - - Application - false - v141 - true - MultiByte - - - Application - true - v141 - MultiByte - - - Application - false - v141 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - - - Level3 - Disabled - true - true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include - - - - - Level3 - Disabled - true - true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include - - - - - Level3 - MaxSpeed - true - true - true - true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include - - - true - true - - - - - Level3 - MaxSpeed - true - true - true - true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include - - - true - true - - - - - {4e578016-34ba-4a1e-b8ec-37a48780b6ca} - - - {e741aded-7900-4e07-8db0-d008c336c3fb} - - - {8616d6c9-c8de-4c3f-afc2-625636664c2b} - - - {24d001b4-d439-4967-9371-dc3e0523eb19} - - - {8885c125-83fb-4f73-a93a-c712b1434d54} - - - {0bef63a0-2801-4563-ab65-1e2fd881c3af} - - - {7a7c66f3-2b5b-4e23-85d8-2a74fedad92c} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {775EF5EB-CA49-4994-8AC4-47B4A5385266} + nstool + 10.0 + + + + Application + true + v142 + MultiByte + + + Application + false + v142 + true + MultiByte + + + Application + true + v142 + MultiByte + + + Application + false + v142 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + + + + + Level3 + Disabled + true + true + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + + + + + Level3 + MaxSpeed + true + true + true + true + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + + + true + true + + + + + {4e578016-34ba-4a1e-b8ec-37a48780b6ca} + + + {e741aded-7900-4e07-8db0-d008c336c3fb} + + + {8616d6c9-c8de-4c3f-afc2-625636664c2b} + + + {24d001b4-d439-4967-9371-dc3e0523eb19} + + + {8885c125-83fb-4f73-a93a-c712b1434d54} + + + {0bef63a0-2801-4563-ab65-1e2fd881c3af} + + + {7a7c66f3-2b5b-4e23-85d8-2a74fedad92c} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/libfmt b/deps/libfmt new file mode 160000 index 0000000..e91b9aa --- /dev/null +++ b/deps/libfmt @@ -0,0 +1 @@ +Subproject commit e91b9aa49f2970ec3c05a1b9307d3aa4a625ba76 diff --git a/deps/libfnd b/deps/libfnd index 19c1683..b6a74e0 160000 --- a/deps/libfnd +++ b/deps/libfnd @@ -1 +1 @@ -Subproject commit 19c1683060a8b39b737da8505b5e23660ed86282 +Subproject commit b6a74e03d2c3fb272e900f32554b2cc0d18e7d26 diff --git a/deps/liblz4 b/deps/liblz4 index 0e5a8c2..298f2ec 160000 --- a/deps/liblz4 +++ b/deps/liblz4 @@ -1 +1 @@ -Subproject commit 0e5a8c29295a9046fff4ad5371a8ea682c7e0cb3 +Subproject commit 298f2ecfa6c966472eda9a4f2b93121a060d1867 diff --git a/deps/libmbedtls b/deps/libmbedtls index bc43e5e..30ef43f 160000 --- a/deps/libmbedtls +++ b/deps/libmbedtls @@ -1 +1 @@ -Subproject commit bc43e5e079529455749d81d1a3b77a9574d5ab01 +Subproject commit 30ef43f1d6ef00d9d94e19270ae8b646e821ecc7 diff --git a/deps/libnintendo-es b/deps/libnintendo-es index 9e3f1ea..9b8760c 160000 --- a/deps/libnintendo-es +++ b/deps/libnintendo-es @@ -1 +1 @@ -Subproject commit 9e3f1ea763be033f60b3b2db0b2d6e2aac462a37 +Subproject commit 9b8760cbd052912f112b034aee29778483951df4 diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index afbbe39..8d9173c 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit afbbe3900d4c0dab6b3c4cd06927aff227cc1f95 +Subproject commit 8d9173c9b1ee1544d8ad4329edd22cc73bde4f13 diff --git a/deps/libnintendo-hac-hb b/deps/libnintendo-hac-hb index 95fb4d7..150fb51 160000 --- a/deps/libnintendo-hac-hb +++ b/deps/libnintendo-hac-hb @@ -1 +1 @@ -Subproject commit 95fb4d7762eb5b395fa5023fd3a9b6f34151505a +Subproject commit 150fb51a9126ff562cdddf93f66f031179386802 diff --git a/deps/libnintendo-pki b/deps/libnintendo-pki index 5097871..66910b6 160000 --- a/deps/libnintendo-pki +++ b/deps/libnintendo-pki @@ -1 +1 @@ -Subproject commit 5097871222f6e2cd07b7b8e8b58551e913eb1c15 +Subproject commit 66910b6172efc7f698d028b584507b69cc70c3a9 From 6bc4cd8a8ca2cb978362f2d43d4a122ec5ca62a3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 26 Sep 2021 10:32:52 +0800 Subject: [PATCH 004/119] Update dependencies. --- deps/libnintendo-es | 2 +- deps/libnintendo-hac | 2 +- deps/libnintendo-hac-hb | 2 +- deps/libnintendo-pki | 2 +- deps/libtoolchain | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deps/libnintendo-es b/deps/libnintendo-es index 9b8760c..f96b4f9 160000 --- a/deps/libnintendo-es +++ b/deps/libnintendo-es @@ -1 +1 @@ -Subproject commit 9b8760cbd052912f112b034aee29778483951df4 +Subproject commit f96b4f96552da4258f4212427137d6ff40c45392 diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 8d9173c..33cfca7 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 8d9173c9b1ee1544d8ad4329edd22cc73bde4f13 +Subproject commit 33cfca799ccdcc4c34f73696da9fe31f104dd23f diff --git a/deps/libnintendo-hac-hb b/deps/libnintendo-hac-hb index 150fb51..db9af77 160000 --- a/deps/libnintendo-hac-hb +++ b/deps/libnintendo-hac-hb @@ -1 +1 @@ -Subproject commit 150fb51a9126ff562cdddf93f66f031179386802 +Subproject commit db9af77ec23d170ec703a13cd5cd9b67cd47d3a3 diff --git a/deps/libnintendo-pki b/deps/libnintendo-pki index 66910b6..040405b 160000 --- a/deps/libnintendo-pki +++ b/deps/libnintendo-pki @@ -1 +1 @@ -Subproject commit 66910b6172efc7f698d028b584507b69cc70c3a9 +Subproject commit 040405bc6f9936e4e61c80b5ab384b3d95c395cd diff --git a/deps/libtoolchain b/deps/libtoolchain index c730303..0be82cf 160000 --- a/deps/libtoolchain +++ b/deps/libtoolchain @@ -1 +1 @@ -Subproject commit c730303b8c628ca0f853c9ac02d9efe49b76061d +Subproject commit 0be82cf582b4af7539f57a261e17e5769f4f8cfa From 60acda6615762aecec2c8fe52df5e62c2519549f Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 28 Sep 2021 19:15:54 +0800 Subject: [PATCH 005/119] Begin migration from libfnd to libtoolchain --- build/visualstudio/nstool.sln | 280 +++-- build/visualstudio/nstool/nstool.vcxproj | 86 +- .../nstool/nstool.vcxproj.filters | 202 +-- deps/libfnd | 2 +- deps/liblz4 | 2 +- deps/libmbedtls | 2 +- deps/libnintendo-es | 2 +- deps/libnintendo-hac | 2 +- deps/libnintendo-hac-hb | 2 +- deps/libnintendo-pki | 2 +- deps/libtoolchain | 2 +- makefile | 22 +- src/AssetProcess.cpp | 90 +- src/AssetProcess.h | 22 +- src/CnmtProcess.cpp | 31 +- src/CnmtProcess.h | 16 +- src/CompressedArchiveIFile.cpp | 195 --- src/CompressedArchiveIFile.h | 51 - src/ElfSymbolParser.cpp | 16 +- src/ElfSymbolParser.h | 15 +- src/EsTikProcess.cpp | 50 +- src/EsTikProcess.h | 28 +- src/GameCardProcess.cpp | 58 +- src/GameCardProcess.h | 27 +- src/IniProcess.cpp | 42 +- src/IniProcess.h | 20 +- src/KeyBag.cpp | 568 +++++++++ src/KeyBag.h | 76 ++ src/KeyConfiguration.cpp | 413 ------- src/KeyConfiguration.h | 217 ---- src/KipProcess.cpp | 48 +- src/KipProcess.h | 19 +- src/MetaProcess.cpp | 48 +- src/MetaProcess.h | 24 +- src/NacpProcess.cpp | 134 +- src/NacpProcess.h | 16 +- src/NcaProcess.cpp | 108 +- src/NcaProcess.h | 34 +- src/NroProcess.cpp | 48 +- src/NroProcess.h | 25 +- src/NsoProcess.cpp | 60 +- src/NsoProcess.h | 21 +- src/PfsProcess.cpp | 52 +- src/PfsProcess.h | 18 +- src/PkiCertProcess.cpp | 46 +- src/PkiCertProcess.h | 27 +- src/PkiValidator.cpp | 48 +- src/PkiValidator.h | 25 +- src/RoMetadataProcess.cpp | 52 +- src/RoMetadataProcess.h | 21 +- src/RomfsProcess.cpp | 64 +- src/RomfsProcess.h | 33 +- src/SdkApiString.cpp | 20 +- src/SdkApiString.h | 8 +- src/Settings.cpp | 819 +++++++++++++ src/Settings.h | 155 +++ src/UserSettings.cpp | 1086 ----------------- src/UserSettings.h | 138 --- src/common.h | 62 - src/elf.h | 458 +++++++ src/main.cpp | 256 ++-- src/types.h | 26 + src/util.cpp | 100 ++ src/util.h | 13 + src/version.h | 4 +- 65 files changed, 3311 insertions(+), 3346 deletions(-) delete mode 100644 src/CompressedArchiveIFile.cpp delete mode 100644 src/CompressedArchiveIFile.h create mode 100644 src/KeyBag.cpp create mode 100644 src/KeyBag.h delete mode 100644 src/KeyConfiguration.cpp delete mode 100644 src/KeyConfiguration.h create mode 100644 src/Settings.cpp create mode 100644 src/Settings.h delete mode 100644 src/UserSettings.cpp delete mode 100644 src/UserSettings.h delete mode 100644 src/common.h create mode 100644 src/elf.h create mode 100644 src/types.h create mode 100644 src/util.cpp create mode 100644 src/util.h diff --git a/build/visualstudio/nstool.sln b/build/visualstudio/nstool.sln index ad181a1..202b2b9 100644 --- a/build/visualstudio/nstool.sln +++ b/build/visualstudio/nstool.sln @@ -1,129 +1,151 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.2036 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nstool", "nstool\nstool.vcxproj", "{775EF5EB-CA49-4994-8AC4-47B4A5385266}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deps", "deps", "{05929EAE-4471-4E8E-A6F3-793A81623D7F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfnd", "..\..\deps\libfnd\build\visualstudio\libfnd\libfnd.vcxproj", "{4E578016-34BA-4A1E-B8EC-37A48780B6CA}" - ProjectSection(ProjectDependencies) = postProject - {E741ADED-7900-4E07-8DB0-D008C336C3FB} = {E741ADED-7900-4E07-8DB0-D008C336C3FB} - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblz4", "..\..\deps\liblz4\build\visualstudio\liblz4\liblz4.vcxproj", "{E741ADED-7900-4E07-8DB0-D008C336C3FB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-es", "..\..\deps\libnintendo-es\build\visualstudio\libnintendo-es\libnintendo-es.vcxproj", "{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}" - ProjectSection(ProjectDependencies) = postProject - {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac", "..\..\deps\libnintendo-hac\build\visualstudio\libnintendo-hac\libnintendo-hac.vcxproj", "{8885C125-83FB-4F73-A93A-C712B1434D54}" - ProjectSection(ProjectDependencies) = postProject - {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac-hb", "..\..\deps\libnintendo-hac-hb\build\visualstudio\libnintendo-hac-hb\libnintendo-hac-hb.vcxproj", "{24D001B4-D439-4967-9371-DC3E0523EB19}" - ProjectSection(ProjectDependencies) = postProject - {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} - {8885C125-83FB-4F73-A93A-C712B1434D54} = {8885C125-83FB-4F73-A93A-C712B1434D54} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-pki", "..\..\deps\libnintendo-pki\build\visualstudio\libnintendo-pki\libnintendo-pki.vcxproj", "{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}" - ProjectSection(ProjectDependencies) = postProject - {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmbedtls", "..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj", "{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.ActiveCfg = Debug|x64 - {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.Build.0 = Debug|x64 - {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.ActiveCfg = Debug|Win32 - {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.Build.0 = Debug|Win32 - {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.ActiveCfg = Release|x64 - {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.Build.0 = Release|x64 - {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.ActiveCfg = Release|Win32 - {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.Build.0 = Release|Win32 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.ActiveCfg = Debug|x64 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.Build.0 = Debug|x64 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.ActiveCfg = Debug|Win32 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.Build.0 = Debug|Win32 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.ActiveCfg = Release|x64 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.Build.0 = Release|x64 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.ActiveCfg = Release|Win32 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.Build.0 = Release|Win32 - {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.ActiveCfg = Debug|x64 - {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.Build.0 = Debug|x64 - {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.ActiveCfg = Debug|Win32 - {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.Build.0 = Debug|Win32 - {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.ActiveCfg = Release|x64 - {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.Build.0 = Release|x64 - {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.ActiveCfg = Release|Win32 - {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.Build.0 = Release|Win32 - {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x64.ActiveCfg = Debug|x64 - {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x64.Build.0 = Debug|x64 - {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x86.ActiveCfg = Debug|Win32 - {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x86.Build.0 = Debug|Win32 - {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x64.ActiveCfg = Release|x64 - {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x64.Build.0 = Release|x64 - {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x86.ActiveCfg = Release|Win32 - {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x86.Build.0 = Release|Win32 - {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x64.ActiveCfg = Debug|x64 - {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x64.Build.0 = Debug|x64 - {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x86.ActiveCfg = Debug|Win32 - {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x86.Build.0 = Debug|Win32 - {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x64.ActiveCfg = Release|x64 - {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x64.Build.0 = Release|x64 - {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x86.ActiveCfg = Release|Win32 - {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x86.Build.0 = Release|Win32 - {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x64.ActiveCfg = Debug|x64 - {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x64.Build.0 = Debug|x64 - {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x86.ActiveCfg = Debug|Win32 - {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x86.Build.0 = Debug|Win32 - {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x64.ActiveCfg = Release|x64 - {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x64.Build.0 = Release|x64 - {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x86.ActiveCfg = Release|Win32 - {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x86.Build.0 = Release|Win32 - {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x64.ActiveCfg = Debug|x64 - {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x64.Build.0 = Debug|x64 - {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x86.ActiveCfg = Debug|Win32 - {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x86.Build.0 = Debug|Win32 - {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x64.ActiveCfg = Release|x64 - {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x64.Build.0 = Release|x64 - {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x86.ActiveCfg = Release|Win32 - {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x86.Build.0 = Release|Win32 - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.ActiveCfg = Debug|x64 - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.Build.0 = Debug|x64 - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.ActiveCfg = Debug|Win32 - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.Build.0 = Debug|Win32 - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.ActiveCfg = Release|x64 - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.Build.0 = Release|x64 - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.ActiveCfg = Release|Win32 - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} - {E741ADED-7900-4E07-8DB0-D008C336C3FB} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} - {8616D6C9-C8DE-4C3F-AFC2-625636664C2B} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} - {8885C125-83FB-4F73-A93A-C712B1434D54} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} - {24D001B4-D439-4967-9371-DC3E0523EB19} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} - {0BEF63A0-2801-4563-AB65-1E2FD881C3AF} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {ABDCFB40-D6B3-44A9-92B5-0D7AB38D9FB8} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31229.75 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nstool", "nstool\nstool.vcxproj", "{775EF5EB-CA49-4994-8AC4-47B4A5385266}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deps", "deps", "{05929EAE-4471-4E8E-A6F3-793A81623D7F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfnd", "..\..\deps\libfnd\build\visualstudio\libfnd\libfnd.vcxproj", "{4E578016-34BA-4A1E-B8EC-37A48780B6CA}" + ProjectSection(ProjectDependencies) = postProject + {E741ADED-7900-4E07-8DB0-D008C336C3FB} = {E741ADED-7900-4E07-8DB0-D008C336C3FB} + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblz4", "..\..\deps\liblz4\build\visualstudio\liblz4\liblz4.vcxproj", "{E741ADED-7900-4E07-8DB0-D008C336C3FB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-es", "..\..\deps\libnintendo-es\build\visualstudio\libnintendo-es\libnintendo-es.vcxproj", "{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}" + ProjectSection(ProjectDependencies) = postProject + {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac", "..\..\deps\libnintendo-hac\build\visualstudio\libnintendo-hac\libnintendo-hac.vcxproj", "{8885C125-83FB-4F73-A93A-C712B1434D54}" + ProjectSection(ProjectDependencies) = postProject + {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac-hb", "..\..\deps\libnintendo-hac-hb\build\visualstudio\libnintendo-hac-hb\libnintendo-hac-hb.vcxproj", "{24D001B4-D439-4967-9371-DC3E0523EB19}" + ProjectSection(ProjectDependencies) = postProject + {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} + {8885C125-83FB-4F73-A93A-C712B1434D54} = {8885C125-83FB-4F73-A93A-C712B1434D54} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-pki", "..\..\deps\libnintendo-pki\build\visualstudio\libnintendo-pki\libnintendo-pki.vcxproj", "{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}" + ProjectSection(ProjectDependencies) = postProject + {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmbedtls", "..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj", "{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtoolchain", "..\..\deps\libtoolchain\build\visualstudio\libtoolchain\libtoolchain.vcxproj", "{E194E4B8-1482-40A2-901B-75D4387822E9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfmt", "..\..\deps\libfmt\build\visualstudio\libfmt\libfmt.vcxproj", "{F4B0540E-0AAE-4006-944B-356944EF61FA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.ActiveCfg = Debug|x64 + {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.Build.0 = Debug|x64 + {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.ActiveCfg = Debug|Win32 + {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.Build.0 = Debug|Win32 + {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.ActiveCfg = Release|x64 + {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.Build.0 = Release|x64 + {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.ActiveCfg = Release|Win32 + {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.Build.0 = Release|Win32 + {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.ActiveCfg = Debug|x64 + {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.Build.0 = Debug|x64 + {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.ActiveCfg = Debug|Win32 + {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.Build.0 = Debug|Win32 + {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.ActiveCfg = Release|x64 + {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.Build.0 = Release|x64 + {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.ActiveCfg = Release|Win32 + {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.Build.0 = Release|Win32 + {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.ActiveCfg = Debug|x64 + {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.Build.0 = Debug|x64 + {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.ActiveCfg = Debug|Win32 + {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.Build.0 = Debug|Win32 + {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.ActiveCfg = Release|x64 + {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.Build.0 = Release|x64 + {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.ActiveCfg = Release|Win32 + {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.Build.0 = Release|Win32 + {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x64.ActiveCfg = Debug|x64 + {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x64.Build.0 = Debug|x64 + {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x86.ActiveCfg = Debug|Win32 + {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x86.Build.0 = Debug|Win32 + {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x64.ActiveCfg = Release|x64 + {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x64.Build.0 = Release|x64 + {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x86.ActiveCfg = Release|Win32 + {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x86.Build.0 = Release|Win32 + {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x64.ActiveCfg = Debug|x64 + {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x64.Build.0 = Debug|x64 + {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x86.ActiveCfg = Debug|Win32 + {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x86.Build.0 = Debug|Win32 + {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x64.ActiveCfg = Release|x64 + {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x64.Build.0 = Release|x64 + {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x86.ActiveCfg = Release|Win32 + {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x86.Build.0 = Release|Win32 + {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x64.ActiveCfg = Debug|x64 + {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x64.Build.0 = Debug|x64 + {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x86.ActiveCfg = Debug|Win32 + {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x86.Build.0 = Debug|Win32 + {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x64.ActiveCfg = Release|x64 + {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x64.Build.0 = Release|x64 + {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x86.ActiveCfg = Release|Win32 + {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x86.Build.0 = Release|Win32 + {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x64.ActiveCfg = Debug|x64 + {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x64.Build.0 = Debug|x64 + {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x86.ActiveCfg = Debug|Win32 + {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x86.Build.0 = Debug|Win32 + {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x64.ActiveCfg = Release|x64 + {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x64.Build.0 = Release|x64 + {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x86.ActiveCfg = Release|Win32 + {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x86.Build.0 = Release|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.ActiveCfg = Debug|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.Build.0 = Debug|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.ActiveCfg = Debug|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.Build.0 = Debug|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.ActiveCfg = Release|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.Build.0 = Release|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.ActiveCfg = Release|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.Build.0 = Release|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x64.ActiveCfg = Debug|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x64.Build.0 = Debug|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x86.ActiveCfg = Debug|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x86.Build.0 = Debug|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x64.ActiveCfg = Release|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x64.Build.0 = Release|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x86.ActiveCfg = Release|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x86.Build.0 = Release|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.ActiveCfg = Debug|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.Build.0 = Debug|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.ActiveCfg = Debug|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.Build.0 = Debug|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.ActiveCfg = Release|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.Build.0 = Release|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.ActiveCfg = Release|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} + {E741ADED-7900-4E07-8DB0-D008C336C3FB} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} + {8616D6C9-C8DE-4C3F-AFC2-625636664C2B} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} + {8885C125-83FB-4F73-A93A-C712B1434D54} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} + {24D001B4-D439-4967-9371-DC3E0523EB19} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} + {0BEF63A0-2801-4563-AB65-1E2FD881C3AF} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} + {E194E4B8-1482-40A2-901B-75D4387822E9} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} + {F4B0540E-0AAE-4006-944B-356944EF61FA} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ABDCFB40-D6B3-44A9-92B5-0D7AB38D9FB8} + EndGlobalSection +EndGlobal diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index e07dcf0..e4db0c3 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -76,7 +76,8 @@ Disabled true true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + MultiThreadedDebug @@ -85,7 +86,8 @@ Disabled true true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + MultiThreadedDebug @@ -96,7 +98,8 @@ true true true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + MultiThreaded true @@ -111,7 +114,8 @@ true true true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + MultiThreaded true @@ -119,76 +123,46 @@ - + + {f4b0540e-0aae-4006-944b-356944ef61fa} + + {4e578016-34ba-4a1e-b8ec-37a48780b6ca} - + {e741aded-7900-4e07-8db0-d008c336c3fb} - + {8616d6c9-c8de-4c3f-afc2-625636664c2b} - + {24d001b4-d439-4967-9371-dc3e0523eb19} - + {8885c125-83fb-4f73-a93a-c712b1434d54} - + {0bef63a0-2801-4563-ab65-1e2fd881c3af} - + {7a7c66f3-2b5b-4e23-85d8-2a74fedad92c} + + {e194e4b8-1482-40a2-901b-75d4387822e9} + - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + diff --git a/build/visualstudio/nstool/nstool.vcxproj.filters b/build/visualstudio/nstool/nstool.vcxproj.filters index 82a1d26..44261e8 100644 --- a/build/visualstudio/nstool/nstool.vcxproj.filters +++ b/build/visualstudio/nstool/nstool.vcxproj.filters @@ -1,156 +1,48 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + \ No newline at end of file diff --git a/deps/libfnd b/deps/libfnd index b6a74e0..27705ae 160000 --- a/deps/libfnd +++ b/deps/libfnd @@ -1 +1 @@ -Subproject commit b6a74e03d2c3fb272e900f32554b2cc0d18e7d26 +Subproject commit 27705aeb2394dbe21f580cc742792537e3a0cbed diff --git a/deps/liblz4 b/deps/liblz4 index 298f2ec..555e430 160000 --- a/deps/liblz4 +++ b/deps/liblz4 @@ -1 +1 @@ -Subproject commit 298f2ecfa6c966472eda9a4f2b93121a060d1867 +Subproject commit 555e43086b348a640179ddf2ecd86d8503c12b85 diff --git a/deps/libmbedtls b/deps/libmbedtls index 30ef43f..597bb57 160000 --- a/deps/libmbedtls +++ b/deps/libmbedtls @@ -1 +1 @@ -Subproject commit 30ef43f1d6ef00d9d94e19270ae8b646e821ecc7 +Subproject commit 597bb57271177d776682104dc7c8ea8b62670d1d diff --git a/deps/libnintendo-es b/deps/libnintendo-es index f96b4f9..7572682 160000 --- a/deps/libnintendo-es +++ b/deps/libnintendo-es @@ -1 +1 @@ -Subproject commit f96b4f96552da4258f4212427137d6ff40c45392 +Subproject commit 7572682f1fa819fd7ef1a4b639cb14fa67128f0d diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 33cfca7..02c6703 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 33cfca799ccdcc4c34f73696da9fe31f104dd23f +Subproject commit 02c6703c9720d98197b3bb697fdab12008a9829e diff --git a/deps/libnintendo-hac-hb b/deps/libnintendo-hac-hb index db9af77..e52fb0e 160000 --- a/deps/libnintendo-hac-hb +++ b/deps/libnintendo-hac-hb @@ -1 +1 @@ -Subproject commit db9af77ec23d170ec703a13cd5cd9b67cd47d3a3 +Subproject commit e52fb0e1ebc558f68bb438bd278b05704c5aaeb3 diff --git a/deps/libnintendo-pki b/deps/libnintendo-pki index 040405b..b8a1066 160000 --- a/deps/libnintendo-pki +++ b/deps/libnintendo-pki @@ -1 +1 @@ -Subproject commit 040405bc6f9936e4e61c80b5ab384b3d95c395cd +Subproject commit b8a10663a99f7b5fedf11139549e8bf1b289c0e0 diff --git a/deps/libtoolchain b/deps/libtoolchain index 0be82cf..7948f58 160000 --- a/deps/libtoolchain +++ b/deps/libtoolchain @@ -1 +1 @@ -Subproject commit 0be82cf582b4af7539f57a261e17e5769f4f8cfa +Subproject commit 7948f581d32ebbfdf5a872b96c37938690f665e7 diff --git a/makefile b/makefile index 4f4ca61..f8c302a 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,6 @@ # C++/C Recursive Project Makefile # (c) Jack -# Version 3 +# Version 4 # Project Name PROJECT_NAME = nstool @@ -13,7 +13,7 @@ PROJECT_SRC_SUBDIRS = $(PROJECT_SRC_PATH) #PROJECT_TESTSRC_PATH = test #PROJECT_TESTSRC_SUBDIRS = $(PROJECT_TESTSRC_PATH) PROJECT_BIN_PATH = bin -#PROJECT_DOCS_PATH = docs +#PROJECT_DOCS_PATH = docs #PROJECT_DOXYFILE_PATH = Doxyfile # Determine if the root makefile has been established, and if not establish this makefile as the root makefile @@ -31,8 +31,8 @@ PROJECT_SONAME = $(PROJECT_NAME).so.$(PROJECT_SO_VER_MAJOR) PROJECT_SO_FILENAME = $(PROJECT_SONAME).$(PROJECT_SO_VER_MINOR).$(PROJECT_SO_VER_PATCH) # Project Dependencies -PROJECT_DEPEND_LOCAL = nintendo-hac-hb nintendo-hac nintendo-es nintendo-pki fnd mbedtls lz4 -PROJECT_DEPEND_EXTERNAL = +PROJECT_DEPEND = mbedtls lz4 toolchain fmt nintendo-hac nintendo-hac-hb nintendo-es nintendo-pki +PROJECT_DEPEND_LOCAL_DIR = libmbedtls liblz4 libtoolchain libfmt libnintendo-hac libnintendo-hac-hb libnintendo-es libnintendo-pki # Generate compiler flags for including project include path ifneq ($(PROJECT_INCLUDE_PATH),) @@ -40,14 +40,14 @@ ifneq ($(PROJECT_INCLUDE_PATH),) endif # Generate compiler flags for local included dependencies -ifneq ($(PROJECT_DEPEND_LOCAL),) - LIB += $(foreach dep,$(PROJECT_DEPEND_LOCAL), -L"$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(dep)/bin" -l$(dep)) - INC += $(foreach dep,$(PROJECT_DEPEND_LOCAL), -I"$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(dep)/include") +ifneq ($(PROJECT_DEPEND_LOCAL_DIR),) + LIB += $(foreach dep,$(PROJECT_DEPEND_LOCAL_DIR), -L"$(ROOT_PROJECT_DEPENDENCY_PATH)/$(dep)/bin") + INC += $(foreach dep,$(PROJECT_DEPEND_LOCAL_DIR), -I"$(ROOT_PROJECT_DEPENDENCY_PATH)/$(dep)/include") endif # Generate compiler flags for external dependencies -ifneq ($(PROJECT_DEPEND_EXTERNAL),) - LIB += $(foreach dep,$(PROJECT_DEPEND_EXTERNAL), -l$(dep)) +ifneq ($(PROJECT_DEPEND),) + LIB += $(foreach dep,$(PROJECT_DEPEND), -l$(dep)) endif # Detect Platform @@ -170,8 +170,8 @@ endif # Dependencies .PHONY: deps deps: - @$(foreach lib,$(PROJECT_DEPEND_LOCAL), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(lib)" && $(MAKE) static_lib && cd "$(PROJECT_PATH)";) + @$(foreach lib,$(PROJECT_DEPEND_LOCAL_DIR), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/$(lib)" && $(MAKE) static_lib && cd "$(PROJECT_PATH)";) .PHONY: clean_deps clean_deps: - @$(foreach lib,$(PROJECT_DEPEND_LOCAL), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(lib)" && $(MAKE) clean && cd "$(PROJECT_PATH)";) \ No newline at end of file + @$(foreach lib,$(PROJECT_DEPEND_LOCAL_DIR), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/$(lib)" && $(MAKE) clean && cd "$(PROJECT_PATH)";) \ No newline at end of file diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index fcca0ce..1a9bf53 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -1,115 +1,103 @@ -#include -#include -#include -#include -#include #include "AssetProcess.h" +#include "utils.h" -AssetProcess::AssetProcess() : +nstool::AssetProcess::AssetProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false) { } -void AssetProcess::process() +void nstool::AssetProcess::process() { importHeader(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) displayHeader(); processSections(); -} +} -void AssetProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::AssetProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void AssetProcess::setCliOutputMode(CliOutputMode type) +void nstool::AssetProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void AssetProcess::setVerifyMode(bool verify) +void nstool::AssetProcess::setVerifyMode(bool verify) { mVerify = verify; } -void AssetProcess::setListFs(bool list) +void nstool::AssetProcess::setListFs(bool list) { mRomfs.setListFs(list); } -void AssetProcess::setIconExtractPath(const std::string& path) +void nstool::AssetProcess::setIconExtractPath(const std::string& path) { mIconExtractPath = path; } -void AssetProcess::setNacpExtractPath(const std::string& path) +void nstool::AssetProcess::setNacpExtractPath(const std::string& path) { mNacpExtractPath = path; } -void AssetProcess::setRomfsExtractPath(const std::string& path) +void nstool::AssetProcess::setRomfsExtractPath(const std::string& path) { mRomfs.setExtractPath(path); } -void AssetProcess::importHeader() +void nstool::AssetProcess::importHeader() { - fnd::Vec scratch; + tc::ByteData scratch; - if (*mFile == nullptr) + if (mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } - if ((*mFile)->size() < sizeof(nn::hac::sAssetHeader)) + size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + + if (file_size < sizeof(nn::hac::sAssetHeader)) { - throw fnd::Exception(kModuleName, "Corrupt ASET: file too small"); + throw tc::Exception(kModuleName, "Corrupt ASET: file too small"); } scratch.alloc(sizeof(nn::hac::sAssetHeader)); - (*mFile)->read(scratch.data(), 0, scratch.size()); + mFile->read(scratch.data(), 0, scratch.size()); mHdr.fromBytes(scratch.data(), scratch.size()); } -void AssetProcess::processSections() +void nstool::AssetProcess::processSections() { - if (mHdr.getIconInfo().size > 0 && mIconExtractPath.isSet) + size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + + if (mHdr.getIconInfo().size > 0 && mIconExtractPath.isSet()) { - if ((mHdr.getIconInfo().size + mHdr.getIconInfo().offset) > (*mFile)->size()) - throw fnd::Exception(kModuleName, "ASET geometry for icon beyond file size"); + if ((mHdr.getIconInfo().size + mHdr.getIconInfo().offset) > file_size) + throw tc::Exception(kModuleName, "ASET geometry for icon beyond file size"); - fnd::SimpleFile outfile(mIconExtractPath.var, fnd::SimpleFile::Create); - fnd::Vec cache; - - cache.alloc(mHdr.getIconInfo().size); - (*mFile)->read(cache.data(), mHdr.getIconInfo().offset, cache.size()); - outfile.write(cache.data(), cache.size()); - outfile.close(); + writeSubStreamToFile(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getIconInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getIconInfo().size), mIconExtractPath.get()); } if (mHdr.getNacpInfo().size > 0) { - if ((mHdr.getNacpInfo().size + mHdr.getNacpInfo().offset) > (*mFile)->size()) - throw fnd::Exception(kModuleName, "ASET geometry for nacp beyond file size"); + if ((mHdr.getNacpInfo().size + mHdr.getNacpInfo().offset) > file_size) + throw tc::Exception(kModuleName, "ASET geometry for nacp beyond file size"); - if (mNacpExtractPath.isSet) + if (mNacpExtractPath.isSet()) { - fnd::SimpleFile outfile(mNacpExtractPath.var, fnd::SimpleFile::Create); - fnd::Vec cache; - - cache.alloc(mHdr.getNacpInfo().size); - (*mFile)->read(cache.data(), mHdr.getNacpInfo().offset, cache.size()); - outfile.write(cache.data(), cache.size()); - outfile.close(); + writeSubStreamToFile(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().size), mNacpExtractPath.get()); } - mNacp.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size)); + mNacp.setInputFile(std::make_shared(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().size))); mNacp.setCliOutputMode(mCliOutputMode); mNacp.setVerifyMode(mVerify); @@ -118,10 +106,10 @@ void AssetProcess::processSections() if (mHdr.getRomfsInfo().size > 0) { - if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > (*mFile)->size()) - throw fnd::Exception(kModuleName, "ASET geometry for romfs beyond file size"); + if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > file_size) + throw tc::Exception(kModuleName, "ASET geometry for romfs beyond file size"); - mRomfs.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size)); + mRomfs.setInputFile(std::make_shared(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getRomfsInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getRomfsInfo().size))); mRomfs.setCliOutputMode(mCliOutputMode); mRomfs.setVerifyMode(mVerify); @@ -129,9 +117,9 @@ void AssetProcess::processSections() } } -void AssetProcess::displayHeader() +void nstool::AssetProcess::displayHeader() { - if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) + if (mCliOutputMode.show_layout) { std::cout << "[ASET Header]" << std::endl; std::cout << " Icon:" << std::endl; diff --git a/src/AssetProcess.h b/src/AssetProcess.h index 7364d15..262de5a 100644 --- a/src/AssetProcess.h +++ b/src/AssetProcess.h @@ -1,13 +1,11 @@ #pragma once -#include -#include -#include -#include -#include +#include "types.h" #include "NacpProcess.h" #include "RomfsProcess.h" -#include "common.h" +#include + +namespace nstool { class AssetProcess { @@ -16,7 +14,7 @@ public: void process(); - void setInputFile(const fnd::SharedPtr& file); + void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -30,12 +28,12 @@ public: private: const std::string kModuleName = "AssetProcess"; - fnd::SharedPtr mFile; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; - sOptional mIconExtractPath; - sOptional mNacpExtractPath; + tc::Optional mIconExtractPath; + tc::Optional mNacpExtractPath; nn::hac::AssetHeader mHdr; NacpProcess mNacp; @@ -44,4 +42,6 @@ private: void importHeader(); void processSections(); void displayHeader(); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/CnmtProcess.cpp b/src/CnmtProcess.cpp index 8e8c4ed..5849c16 100644 --- a/src/CnmtProcess.cpp +++ b/src/CnmtProcess.cpp @@ -3,53 +3,50 @@ #include #include -#include -#include - #include -CnmtProcess::CnmtProcess() : +nstool::CnmtProcess::CnmtProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false) { } -void CnmtProcess::process() +void nstool::CnmtProcess::process() { importCnmt(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) displayCnmt(); } -void CnmtProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::CnmtProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void CnmtProcess::setCliOutputMode(CliOutputMode type) +void nstool::CnmtProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void CnmtProcess::setVerifyMode(bool verify) +void nstool::CnmtProcess::setVerifyMode(bool verify) { mVerify = verify; } -const nn::hac::ContentMeta& CnmtProcess::getContentMeta() const +const nn::hac::ContentMeta& nstool::CnmtProcess::getContentMeta() const { return mCnmt; } -void CnmtProcess::importCnmt() +void nstool::CnmtProcess::importCnmt() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } scratch.alloc((*mFile)->size()); @@ -58,7 +55,7 @@ void CnmtProcess::importCnmt() mCnmt.fromBytes(scratch.data(), scratch.size()); } -void CnmtProcess::displayCnmt() +void nstool::CnmtProcess::displayCnmt() { std::cout << "[ContentMeta]" << std::endl; std::cout << " TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getTitleId() << std::endl; @@ -178,7 +175,7 @@ void CnmtProcess::displayCnmt() std::cout << " Digest: " << fnd::SimpleTextOutput::arrayToString(mCnmt.getDigest().data(), mCnmt.getDigest().size(), false, "") << std::endl; } -void CnmtProcess::displayContentMetaInfo(const nn::hac::ContentMetaInfo& content_meta_info, const std::string& prefix) +void nstool::CnmtProcess::displayContentMetaInfo(const nn::hac::ContentMetaInfo& content_meta_info, const std::string& prefix) { std::cout << prefix << "Id: 0x" << std::hex << std::setw(16) << std::setfill('0') << content_meta_info.getTitleId() << std::endl; std::cout << prefix << "Version: " << nn::hac::ContentMetaUtil::getVersionAsString(content_meta_info.getTitleVersion()) << " (v" << std::dec << content_meta_info.getTitleVersion() << ")"<< std::endl; @@ -210,7 +207,7 @@ void CnmtProcess::displayContentMetaInfo(const nn::hac::ContentMetaInfo& content } } -void CnmtProcess::displayContentMetaInfoList(const std::vector& content_meta_info_list, const std::string& prefix) +void nstool::CnmtProcess::displayContentMetaInfoList(const std::vector& content_meta_info_list, const std::string& prefix) { for (size_t i = 0; i < content_meta_info_list.size(); i++) { diff --git a/src/CnmtProcess.h b/src/CnmtProcess.h index 2353f68..99c9462 100644 --- a/src/CnmtProcess.h +++ b/src/CnmtProcess.h @@ -1,11 +1,9 @@ #pragma once -#include -#include -#include -#include +#include "types.h" + #include -#include "common.h" +namespace nstool { class CnmtProcess { @@ -14,7 +12,7 @@ public: void process(); - void setInputFile(const fnd::SharedPtr& file); + void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -23,7 +21,7 @@ public: private: const std::string kModuleName = "CnmtProcess"; - fnd::SharedPtr mFile; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; @@ -34,4 +32,6 @@ private: void displayContentMetaInfo(const nn::hac::ContentMetaInfo& content_meta_info, const std::string& prefix); void displayContentMetaInfoList(const std::vector& content_meta_info_list, const std::string& prefix); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/CompressedArchiveIFile.cpp b/src/CompressedArchiveIFile.cpp deleted file mode 100644 index 4467bfc..0000000 --- a/src/CompressedArchiveIFile.cpp +++ /dev/null @@ -1,195 +0,0 @@ -#include "CompressedArchiveIFile.h" -#include - -#include - -CompressedArchiveIFile::CompressedArchiveIFile(const fnd::SharedPtr& base_file, size_t compression_meta_offset) : - mFile(base_file), - mCompEntries(), - mLogicalFileSize(0), - mCacheCapacity(nn::hac::compression::kRomfsBlockSize), - mCurrentCacheDataSize(0), - mCache(std::shared_ptr(new byte_t[mCacheCapacity])), - mScratch(std::shared_ptr(new byte_t[mCacheCapacity])) -{ - // determine and check the compression metadata size - size_t compression_meta_size = (*mFile)->size() - compression_meta_offset; - if (compression_meta_size % sizeof(nn::hac::sCompressionEntry)) - { - fnd::Exception(kModuleName, "Invalid compression meta size"); - } - - // import raw metadata - std::shared_ptr entries_raw = std::shared_ptr(new byte_t[compression_meta_size]); - (*mFile)->read(entries_raw.get(), compression_meta_offset, compression_meta_size); - - // process metadata entries - nn::hac::sCompressionEntry* entries = (nn::hac::sCompressionEntry*)entries_raw.get(); - for (size_t idx = 0, num = compression_meta_size / sizeof(nn::hac::sCompressionEntry); idx < num; idx++) - { - if (idx == 0) - { - if (entries[idx].physical_offset.get() != 0x0) - throw fnd::Exception(kModuleName, "Entry 0 had a non-zero physical offset"); - if (entries[idx].virtual_offset.get() != 0x0) - throw fnd::Exception(kModuleName, "Entry 0 had a non-zero virtual offset"); - } - else - { - if (entries[idx].physical_offset.get() != align(entries[idx - 1].physical_offset.get() + entries[idx - 1].physical_size.get(), nn::hac::compression::kRomfsBlockAlign)) - throw fnd::Exception(kModuleName, "Entry was not physically aligned with previous entry"); - if (entries[idx].virtual_offset.get() <= entries[idx - 1].virtual_offset.get()) - throw fnd::Exception(kModuleName, "Entry was not virtually aligned with previous entry"); - - // set previous entry virtual_size = this->virtual_offset - prev->virtual_offset; - mCompEntries[mCompEntries.size() - 1].virtual_size = uint32_t(entries[idx].virtual_offset.get() - mCompEntries[mCompEntries.size() - 1].virtual_offset); - } - - if (entries[idx].physical_size.get() > nn::hac::compression::kRomfsBlockSize) - throw fnd::Exception(kModuleName, "Entry physical size was too large"); - - switch ((nn::hac::compression::CompressionType)entries[idx].compression_type) - { - case (nn::hac::compression::CompressionType::None): - case (nn::hac::compression::CompressionType::Lz4): - break; - default: - throw fnd::Exception(kModuleName, "Unsupported CompressionType"); - } - - mCompEntries.push_back({(nn::hac::compression::CompressionType)entries[idx].compression_type, entries[idx].virtual_offset.get(), 0, entries[idx].physical_offset.get(), entries[idx].physical_size.get()}); - } - - // determine logical file size and final entry size - importEntryDataToCache(mCompEntries.size() - 1); - mCompEntries[mCurrentEntryIndex].virtual_size = mCurrentCacheDataSize; - mLogicalFileSize = mCompEntries[mCurrentEntryIndex].virtual_offset + mCompEntries[mCurrentEntryIndex].virtual_size; - - /* - for (auto itr = mCompEntries.begin(); itr != mCompEntries.end(); itr++) - { - std::cout << "entry " << std::endl; - std::cout << " type: " << (uint32_t)itr->compression_type << std::endl; - std::cout << " phys_addr: 0x" << std::hex << itr->physical_offset << std::endl; - std::cout << " phys_size: 0x" << std::hex << itr->physical_size << std::endl; - std::cout << " virt_addr: 0x" << std::hex << itr->virtual_offset << std::endl; - std::cout << " virt_size: 0x" << std::hex << itr->virtual_size << std::endl; - } - - std::cout << "logical size: 0x" << std::hex << mLogicalFileSize << std::endl; - */ -} - -size_t CompressedArchiveIFile::size() -{ - return mLogicalFileSize; -} - -void CompressedArchiveIFile::seek(size_t offset) -{ - mLogicalOffset = std::min(offset, mLogicalFileSize); -} - -void CompressedArchiveIFile::read(byte_t* out, size_t len) -{ - // limit len to the end of the logical file - len = std::min(len, mLogicalFileSize - mLogicalOffset); - - for (size_t pos = 0, entry_index = getEntryIndexForLogicalOffset(mLogicalOffset); pos < len; entry_index++) - { - // importing entry into cache (this does nothing if the entry is already imported) - importEntryDataToCache(entry_index); - - // write padding if required - if (mCompEntries[entry_index].virtual_size > mCurrentCacheDataSize) - { - memset(mCache.get() + mCurrentCacheDataSize, 0, mCompEntries[entry_index].virtual_size - mCurrentCacheDataSize); - } - - // determine subset of cache to copy out - size_t read_offset = mLogicalOffset - (size_t)mCompEntries[entry_index].virtual_offset; - size_t read_size = std::min(len, (size_t)mCompEntries[entry_index].virtual_size - read_offset); - - memcpy(out + pos, mCache.get() + read_offset, read_size); - - // update position/logical offset - pos += read_size; - mLogicalOffset += read_size; - } -} - -void CompressedArchiveIFile::read(byte_t* out, size_t offset, size_t len) -{ - seek(offset); - read(out, len); -} - -void CompressedArchiveIFile::write(const byte_t* out, size_t len) -{ - throw fnd::Exception(kModuleName, "write() not supported"); -} - -void CompressedArchiveIFile::write(const byte_t* out, size_t offset, size_t len) -{ - throw fnd::Exception(kModuleName, "write() not supported"); -} - -void CompressedArchiveIFile::importEntryDataToCache(size_t entry_index) -{ - // return if entry already imported - if (mCurrentEntryIndex == entry_index && mCurrentCacheDataSize != 0) - return; - - // save index - mCurrentEntryIndex = entry_index; - - // reference entry - CompressionEntry& entry = mCompEntries[mCurrentEntryIndex]; - - if (entry.compression_type == nn::hac::compression::CompressionType::None) - { - (*mFile)->read(mCache.get(), entry.physical_offset, entry.physical_size); - mCurrentCacheDataSize = entry.physical_size; - } - else if (entry.compression_type == nn::hac::compression::CompressionType::Lz4) - { - (*mFile)->read(mScratch.get(), entry.physical_offset, entry.physical_size); - - mCurrentCacheDataSize = 0; - fnd::lz4::decompressData(mScratch.get(), entry.physical_size, mCache.get(), uint32_t(mCacheCapacity), mCurrentCacheDataSize); - - if (mCurrentCacheDataSize == 0) - { - throw fnd::Exception(kModuleName, "Decompression of final block failed"); - } - } -} - -size_t CompressedArchiveIFile::getEntryIndexForLogicalOffset(size_t logical_offset) -{ - // rule out bad offset - if (logical_offset > mLogicalFileSize) - throw fnd::Exception(kModuleName, "illegal logical offset"); - - size_t entry_index = 0; - - // try the current comp entry - if (mCompEntries[mCurrentEntryIndex].virtual_offset <= logical_offset && \ - mCompEntries[mCurrentEntryIndex].virtual_offset + mCompEntries[mCurrentEntryIndex].virtual_size >= logical_offset) - { - entry_index = mCurrentEntryIndex; - } - else - { - for (size_t index = 0; index < mCompEntries.size(); index++) - { - if (mCompEntries[index].virtual_offset <= logical_offset && \ - mCompEntries[index].virtual_offset + mCompEntries[index].virtual_size >= logical_offset) - { - entry_index = index; - } - } - } - - return entry_index; -} \ No newline at end of file diff --git a/src/CompressedArchiveIFile.h b/src/CompressedArchiveIFile.h deleted file mode 100644 index 795bb49..0000000 --- a/src/CompressedArchiveIFile.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -class CompressedArchiveIFile : public fnd::IFile -{ -public: - CompressedArchiveIFile(const fnd::SharedPtr& file, size_t compression_meta_offset); - - size_t size(); - void seek(size_t offset); - void read(byte_t* out, size_t len); - void read(byte_t* out, size_t offset, size_t len); - void write(const byte_t* out, size_t len); - void write(const byte_t* out, size_t offset, size_t len); -private: - const std::string kModuleName = "CompressedArchiveIFile"; - std::stringstream mErrorSs; - - struct CompressionEntry - { - nn::hac::compression::CompressionType compression_type; - uint64_t virtual_offset; - uint32_t virtual_size; - uint64_t physical_offset; - uint32_t physical_size; - }; - - // raw data - fnd::SharedPtr mFile; - - // compression metadata - std::vector mCompEntries; - size_t mLogicalFileSize; - size_t mLogicalOffset; - - // cached decompressed entry - size_t mCacheCapacity; // capacity - size_t mCurrentEntryIndex; // index of entry currently associated with the cache - uint32_t mCurrentCacheDataSize; // size of data currently in cache - std::shared_ptr mCache; // where decompressed data resides - std::shared_ptr mScratch; // same size as cache, but is used for storing data pre-compression - - // this will import entry to cache - void importEntryDataToCache(size_t entry_index); - size_t getEntryIndexForLogicalOffset(size_t logical_offset); -}; \ No newline at end of file diff --git a/src/ElfSymbolParser.cpp b/src/ElfSymbolParser.cpp index b57c9f2..8bb66dd 100644 --- a/src/ElfSymbolParser.cpp +++ b/src/ElfSymbolParser.cpp @@ -1,26 +1,26 @@ #include "ElfSymbolParser.h" -ElfSymbolParser::ElfSymbolParser() +nstool::ElfSymbolParser::ElfSymbolParser() { mSymbolList.clear(); } -void ElfSymbolParser::operator=(const ElfSymbolParser& other) +void nstool::ElfSymbolParser::operator=(const ElfSymbolParser& other) { mSymbolList = other.mSymbolList; } -bool ElfSymbolParser::operator==(const ElfSymbolParser& other) const +bool nstool::ElfSymbolParser::operator==(const ElfSymbolParser& other) const { return mSymbolList == other.mSymbolList; } -bool ElfSymbolParser::operator!=(const ElfSymbolParser& other) const +bool nstool::ElfSymbolParser::operator!=(const ElfSymbolParser& other) const { return !(*this == other); } -void ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit) +void nstool::ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit) { size_t dynSymSize = is64Bit ? sizeof(fnd::Elf64_Sym) : sizeof(fnd::Elf32_Sym); @@ -46,17 +46,17 @@ void ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, cons if (name_pos >= dyn_str_size) { - throw fnd::Exception(kModuleName, "Out of bounds symbol name offset"); + throw tc::Exception(kModuleName, "Out of bounds symbol name offset"); } //for (; dyn_str[name_pos] == 0x00 && name_pos < dyn_str_size; name_pos++); symbol.name = std::string((char*)&dyn_str[name_pos]); - mSymbolList.addElement(symbol); + mSymbolList.push_back(symbol); } } -const fnd::List& ElfSymbolParser::getSymbolList() const +const std::vector& nstool::ElfSymbolParser::getSymbolList() const { return mSymbolList; } \ No newline at end of file diff --git a/src/ElfSymbolParser.h b/src/ElfSymbolParser.h index e06bdf3..ea8bedb 100644 --- a/src/ElfSymbolParser.h +++ b/src/ElfSymbolParser.h @@ -1,7 +1,8 @@ #pragma once -#include -#include -#include +#include "types.h" +#include "elf.h" + +namespace nstool { class ElfSymbolParser { @@ -40,10 +41,12 @@ public: void parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit); - const fnd::List& getSymbolList() const; + const std::vector& getSymbolList() const; private: const std::string kModuleName = "ElfSymbolParser"; // data - fnd::List mSymbolList; -}; \ No newline at end of file + std::vector mSymbolList; +}; + +} \ No newline at end of file diff --git a/src/EsTikProcess.cpp b/src/EsTikProcess.cpp index 41da96c..561b6fb 100644 --- a/src/EsTikProcess.cpp +++ b/src/EsTikProcess.cpp @@ -8,57 +8,57 @@ -EsTikProcess::EsTikProcess() : +nstool::EsTikProcess::EsTikProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false) { } -void EsTikProcess::process() +void nstool::EsTikProcess::process() { importTicket(); if (mVerify) verifyTicket(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) displayTicket(); } -void EsTikProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::EsTikProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void EsTikProcess::setKeyCfg(const KeyConfiguration& keycfg) +void nstool::EsTikProcess::setKeyCfg(const KeyBag& keycfg) { mKeyCfg = keycfg; } -void EsTikProcess::setCertificateChain(const fnd::List>& certs) +void nstool::EsTikProcess::setCertificateChain(const std::vector>& certs) { mCerts = certs; } -void EsTikProcess::setCliOutputMode(CliOutputMode mode) +void nstool::EsTikProcess::setCliOutputMode(CliOutputMode mode) { mCliOutputMode = mode; } -void EsTikProcess::setVerifyMode(bool verify) +void nstool::EsTikProcess::setVerifyMode(bool verify) { mVerify = verify; } -void EsTikProcess::importTicket() +void nstool::EsTikProcess::importTicket() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } scratch.alloc((*mFile)->size()); @@ -66,10 +66,10 @@ void EsTikProcess::importTicket() mTik.fromBytes(scratch.data(), scratch.size()); } -void EsTikProcess::verifyTicket() +void nstool::EsTikProcess::verifyTicket() { PkiValidator pki_validator; - fnd::Vec tik_hash; + tc::ByteData tik_hash; switch (nn::pki::sign::getHashAlgo(mTik.getSignature().getSignType())) { @@ -89,13 +89,13 @@ void EsTikProcess::verifyTicket() pki_validator.addCertificates(mCerts); pki_validator.validateSignature(mTik.getBody().getIssuer(), mTik.getSignature().getSignType(), mTik.getSignature().getSignature(), tik_hash); } - catch (const fnd::Exception& e) + catch (const tc::Exception& e) { std::cout << "[WARNING] Ticket signature could not be validated (" << e.error() << ")" << std::endl; } } -void EsTikProcess::displayTicket() +void nstool::EsTikProcess::displayTicket() { #define _SPLIT_VER(ver) (uint32_t)((ver>>10) & 0x3f) << "." << (uint32_t)((ver>>4) & 0x3f) << "." << (uint32_t)((ver>>0) & 0xf) @@ -104,7 +104,7 @@ void EsTikProcess::displayTicket() std::cout << "[ES Ticket]" << std::endl; std::cout << " SignType: " << getSignTypeStr(mTik.getSignature().getSignType()); - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) std::cout << " (0x" << std::hex << mTik.getSignature().getSignType() << ")"; std::cout << std::endl; @@ -129,7 +129,7 @@ void EsTikProcess::displayTicket() } std::cout << " Version: v" << _SPLIT_VER(body.getTicketVersion()); - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) std::cout << " (" << (uint32_t)body.getTicketVersion() << ")"; std::cout << std::endl; @@ -144,16 +144,16 @@ void EsTikProcess::displayTicket() } } - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) { std::cout << " Reserved Region:" << std::endl; fnd::SimpleTextOutput::hexDump(body.getReservedRegion(), 8, 0x10, 4); } - if (body.getTicketId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (body.getTicketId() != 0 || mCliOutputMode.show_extended_info) std::cout << " TicketId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getTicketId() << std::endl; - if (body.getDeviceId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (body.getDeviceId() != 0 || mCliOutputMode.show_extended_info) std::cout << " DeviceId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getDeviceId() << std::endl; std::cout << " RightsId: " << std::endl << " "; @@ -167,7 +167,7 @@ void EsTikProcess::displayTicket() #undef _SPLIT_VER } -const char* EsTikProcess::getSignTypeStr(uint32_t type) const +const char* nstool::EsTikProcess::getSignTypeStr(uint32_t type) const { const char* str = nullptr; switch(type) @@ -197,7 +197,7 @@ const char* EsTikProcess::getSignTypeStr(uint32_t type) const return str; } -const char* EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const +const char* nstool::EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const { const char* str = nullptr; switch(flag) @@ -215,7 +215,7 @@ const char* EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const return str; } -const char* EsTikProcess::getLicenseTypeStr(byte_t flag) const +const char* nstool::EsTikProcess::getLicenseTypeStr(byte_t flag) const { const char* str = nullptr; switch(flag) @@ -245,7 +245,7 @@ const char* EsTikProcess::getLicenseTypeStr(byte_t flag) const return str; } -const char* EsTikProcess::getPropertyFlagStr(byte_t flag) const +const char* nstool::EsTikProcess::getPropertyFlagStr(byte_t flag) const { const char* str = nullptr; switch(flag) diff --git a/src/EsTikProcess.h b/src/EsTikProcess.h index cf3e36e..075759c 100644 --- a/src/EsTikProcess.h +++ b/src/EsTikProcess.h @@ -1,14 +1,12 @@ #pragma once -#include -#include -#include -#include -#include +#include "types.h" +#include "KeyBag.h" + #include #include #include -#include "KeyConfiguration.h" -#include "common.h" + +namespace nstool { class EsTikProcess { @@ -17,21 +15,21 @@ public: void process(); - void setInputFile(const fnd::SharedPtr& file); - void setKeyCfg(const KeyConfiguration& keycfg); - void setCertificateChain(const fnd::List>& certs); + void setInputFile(const std::shared_ptr& file); + void setKeyCfg(const KeyBag& keycfg); + void setCertificateChain(const std::vector>& certs); void setCliOutputMode(CliOutputMode mode); void setVerifyMode(bool verify); private: const std::string kModuleName = "EsTikProcess"; - fnd::SharedPtr mFile; - KeyConfiguration mKeyCfg; + std::shared_ptr mFile; + KeyBag mKeyCfg; CliOutputMode mCliOutputMode; bool mVerify; - fnd::List> mCerts; + std::vector> mCerts; nn::pki::SignedData mTik; @@ -42,4 +40,6 @@ private: const char* getTitleKeyPersonalisationStr(byte_t flag) const; const char* getLicenseTypeStr(byte_t flag) const; const char* getPropertyFlagStr(byte_t flag) const; -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/GameCardProcess.cpp b/src/GameCardProcess.cpp index b0cfb4a..d06a6ce 100644 --- a/src/GameCardProcess.cpp +++ b/src/GameCardProcess.cpp @@ -7,9 +7,9 @@ #include #include "GameCardProcess.h" -GameCardProcess::GameCardProcess() : +nstool::GameCardProcess::GameCardProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false), mListFs(false), mProccessExtendedHeader(false), @@ -18,7 +18,7 @@ GameCardProcess::GameCardProcess() : { } -void GameCardProcess::process() +void nstool::GameCardProcess::process() { importHeader(); @@ -27,7 +27,7 @@ void GameCardProcess::process() validateXciSignature(); // display header - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) displayHeader(); // process root partition @@ -37,43 +37,43 @@ void GameCardProcess::process() processPartitionPfs(); } -void GameCardProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::GameCardProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void GameCardProcess::setKeyCfg(const KeyConfiguration& keycfg) +void nstool::GameCardProcess::setKeyCfg(const KeyBag& keycfg) { mKeyCfg = keycfg; } -void GameCardProcess::setCliOutputMode(CliOutputMode type) +void nstool::GameCardProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void GameCardProcess::setVerifyMode(bool verify) +void nstool::GameCardProcess::setVerifyMode(bool verify) { mVerify = verify; } -void GameCardProcess::setPartitionForExtract(const std::string& partition_name, const std::string& extract_path) +void nstool::GameCardProcess::setPartitionForExtract(const std::string& partition_name, const std::string& extract_path) { - mExtractInfo.addElement({partition_name, extract_path}); + mExtractInfo.push_back({partition_name, extract_path}); } -void GameCardProcess::setListFs(bool list_fs) +void nstool::GameCardProcess::setListFs(bool list_fs) { mListFs = list_fs; } -void GameCardProcess::importHeader() +void nstool::GameCardProcess::importHeader() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } // allocate memory for header @@ -95,7 +95,7 @@ void GameCardProcess::importHeader() } else { - throw fnd::Exception(kModuleName, "GameCard image did not have expected magic bytes"); + throw tc::Exception(kModuleName, "GameCard image did not have expected magic bytes"); } nn::hac::sGcHeader_Rsa2048Signed* hdr_ptr = (nn::hac::sGcHeader_Rsa2048Signed*)(scratch.data() + mGcHeaderOffset); @@ -107,7 +107,7 @@ void GameCardProcess::importHeader() memcpy(mHdrSignature, hdr_ptr->signature, fnd::rsa::kRsa2048Size); // decrypt extended header - fnd::aes::sAes128Key header_key; + KeyBag::aes128_key_t header_key; if (mKeyCfg.getXciHeaderKey(header_key)) { nn::hac::GameCardUtil::decryptXciHeader(&hdr_ptr->header, header_key.key); @@ -118,12 +118,12 @@ void GameCardProcess::importHeader() mHdr.fromBytes((byte_t*)&hdr_ptr->header, sizeof(nn::hac::sGcHeader)); } -void GameCardProcess::displayHeader() +void nstool::GameCardProcess::displayHeader() { std::cout << "[GameCard Header]" << std::endl; std::cout << " CardHeaderVersion: " << std::dec << (uint32_t)mHdr.getCardHeaderVersion() << std::endl; std::cout << " RomSize: " << nn::hac::GameCardUtil::getRomSizeAsString((nn::hac::gc::RomSize)mHdr.getRomSizeType()); - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) std::cout << " (0x" << std::hex << (uint32_t)mHdr.getRomSizeType() << ")"; std::cout << std::endl; std::cout << " PackageId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getPackageId() << std::endl; @@ -138,7 +138,7 @@ void GameCardProcess::displayHeader() } } } - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) { std::cout << " KekIndex: " << nn::hac::GameCardUtil::getKekIndexAsString((nn::hac::gc::KekIndex)mHdr.getKekIndex()) << " (" << std::dec << (uint32_t)mHdr.getKekIndex() << ")" << std::endl; std::cout << " TitleKeyDecIndex: " << std::dec << (uint32_t)mHdr.getTitleKeyDecIndex() << std::endl; @@ -147,7 +147,7 @@ void GameCardProcess::displayHeader() std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes, 0x10, true, ":") << std::endl; std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes+0x10, 0x10, true, ":") << std::endl; } - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) { std::cout << " Extended Header AesCbc IV:" << std::endl; std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getAesCbcIv().iv, sizeof(mHdr.getAesCbcIv().iv), true, ":") << std::endl; @@ -155,7 +155,7 @@ void GameCardProcess::displayHeader() std::cout << " SelSec: 0x" << std::hex << mHdr.getSelSec() << std::endl; std::cout << " SelT1Key: 0x" << std::hex << mHdr.getSelT1Key() << std::endl; std::cout << " SelKey: 0x" << std::hex << mHdr.getSelKey() << std::endl; - if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) + if (mCliOutputMode.show_layout) { std::cout << " RomAreaStartPage: 0x" << std::hex << mHdr.getRomAreaStartPage(); if (mHdr.getRomAreaStartPage() != (uint32_t)(-1)) @@ -180,7 +180,7 @@ void GameCardProcess::displayHeader() std::cout << " PartitionFs Header:" << std::endl; std::cout << " Offset: 0x" << std::hex << mHdr.getPartitionFsAddress() << std::endl; std::cout << " Size: 0x" << std::hex << mHdr.getPartitionFsSize() << std::endl; - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) { std::cout << " Hash:" << std::endl; std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes, 0x10, true, ":") << std::endl; @@ -208,9 +208,9 @@ void GameCardProcess::displayHeader() } } -bool GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash, bool use_salt, byte_t salt) +bool nstool::GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash, bool use_salt, byte_t salt) { - fnd::Vec scratch; + tc::ByteData scratch; fnd::sha::sSha256Hash calc_hash; if (use_salt) { @@ -228,12 +228,12 @@ bool GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte return calc_hash.compare(test_hash); } -bool GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash) +bool nstool::GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash) { return validateRegionOfFile(offset, len, test_hash, false, 0); } -void GameCardProcess::validateXciSignature() +void nstool::GameCardProcess::validateXciSignature() { fnd::rsa::sRsa2048Key header_sign_key; @@ -244,7 +244,7 @@ void GameCardProcess::validateXciSignature() } } -void GameCardProcess::processRootPfs() +void nstool::GameCardProcess::processRootPfs() { if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().bytes, mHdr.getCompatibilityType() != nn::hac::gc::COMPAT_GLOBAL, mHdr.getCompatibilityType()) == false) { @@ -258,9 +258,9 @@ void GameCardProcess::processRootPfs() mRootPfs.process(); } -void GameCardProcess::processPartitionPfs() +void nstool::GameCardProcess::processPartitionPfs() { - const fnd::List& rootPartitions = mRootPfs.getPfsHeader().getFileList(); + const std::vector& rootPartitions = mRootPfs.getPfsHeader().getFileList(); for (size_t i = 0; i < rootPartitions.size(); i++) { // this must be validated here because only the size of the root partiton header is known at verification time diff --git a/src/GameCardProcess.h b/src/GameCardProcess.h index b32ec0f..61243b7 100644 --- a/src/GameCardProcess.h +++ b/src/GameCardProcess.h @@ -1,14 +1,11 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include "KeyConfiguration.h" +#include "types.h" +#include "KeyBag.h" #include "PfsProcess.h" -#include "common.h" +#include + +namespace nstool { class GameCardProcess { @@ -18,8 +15,8 @@ public: void process(); // generic - void setInputFile(const fnd::SharedPtr& file); - void setKeyCfg(const KeyConfiguration& keycfg); + void setInputFile(const std::shared_ptr& file); + void setKeyCfg(const KeyBag& keycfg); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -31,8 +28,8 @@ private: const std::string kModuleName = "GameCardProcess"; const std::string kXciMountPointName = "gamecard:/"; - fnd::SharedPtr mFile; - KeyConfiguration mKeyCfg; + std::shared_ptr mFile; + KeyBag mKeyCfg; CliOutputMode mCliOutputMode; bool mVerify; bool mListFs; @@ -65,7 +62,7 @@ private: nn::hac::GameCardHeader mHdr; PfsProcess mRootPfs; - fnd::List mExtractInfo; + std::vector mExtractInfo; void importHeader(); void displayHeader(); @@ -74,4 +71,6 @@ private: void validateXciSignature(); void processRootPfs(); void processPartitionPfs(); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/IniProcess.cpp b/src/IniProcess.cpp index d6b6af1..d976309 100644 --- a/src/IniProcess.cpp +++ b/src/IniProcess.cpp @@ -9,20 +9,20 @@ #include "KipProcess.h" -IniProcess::IniProcess() : +nstool::IniProcess::IniProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false), mDoExtractKip(false), mKipExtractPath() { } -void IniProcess::process() +void nstool::IniProcess::process() { importHeader(); importKipList(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) { displayHeader(); displayKipList(); @@ -33,39 +33,39 @@ void IniProcess::process() } } -void IniProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::IniProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void IniProcess::setCliOutputMode(CliOutputMode type) +void nstool::IniProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void IniProcess::setVerifyMode(bool verify) +void nstool::IniProcess::setVerifyMode(bool verify) { mVerify = verify; } -void IniProcess::setKipExtractPath(const std::string& path) +void nstool::IniProcess::setKipExtractPath(const std::string& path) { mDoExtractKip = true; mKipExtractPath = path; } -void IniProcess::importHeader() +void nstool::IniProcess::importHeader() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } if ((*mFile)->size() < sizeof(nn::hac::sIniHeader)) { - throw fnd::Exception(kModuleName, "Corrupt INI: file too small"); + throw tc::Exception(kModuleName, "Corrupt INI: file too small"); } scratch.alloc(sizeof(nn::hac::sIniHeader)); @@ -74,14 +74,14 @@ void IniProcess::importHeader() mHdr.fromBytes(scratch.data(), scratch.size()); } -void IniProcess::importKipList() +void nstool::IniProcess::importKipList() { // kip pos info size_t kip_pos = sizeof(nn::hac::sIniHeader); size_t kip_size = 0; // tmp data to determine size - fnd::Vec hdr_raw; + tc::ByteData hdr_raw; nn::hac::KernelInitialProcessHeader hdr; hdr_raw.alloc(sizeof(nn::hac::sKipHeader)); @@ -90,19 +90,19 @@ void IniProcess::importKipList() (*mFile)->read(hdr_raw.data(), kip_pos, hdr_raw.size()); hdr.fromBytes(hdr_raw.data(), hdr_raw.size()); kip_size = getKipSizeFromHeader(hdr); - mKipList.addElement(new fnd::OffsetAdjustedIFile(mFile, kip_pos, kip_size)); + mKipList.push_back(new fnd::OffsetAdjustedIFile(mFile, kip_pos, kip_size)); kip_pos += kip_size; } } -void IniProcess::displayHeader() +void nstool::IniProcess::displayHeader() { std::cout << "[INI Header]" << std::endl; std::cout << " Size: 0x" << std::hex << mHdr.getSize() << std::endl; std::cout << " KIP Num: " << std::dec << (uint32_t)mHdr.getKipNum() << std::endl; } -void IniProcess::displayKipList() +void nstool::IniProcess::displayKipList() { for (size_t i = 0; i < mKipList.size(); i++) { @@ -116,9 +116,9 @@ void IniProcess::displayKipList() } } -void IniProcess::extractKipList() +void nstool::IniProcess::extractKipList() { - fnd::Vec cache; + tc::ByteData cache; nn::hac::KernelInitialProcessHeader hdr; @@ -151,7 +151,7 @@ void IniProcess::extractKipList() // get kip file size out_size = (*mKipList[i])->size(); // extract kip - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) printf("extract=[%s]\n", out_path.c_str()); (*mKipList[i])->seek(0); @@ -164,7 +164,7 @@ void IniProcess::extractKipList() } } -size_t IniProcess::getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const +size_t nstool::IniProcess::getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const { return sizeof(nn::hac::sKipHeader) + hdr.getTextSegmentInfo().file_layout.size + hdr.getRoSegmentInfo().file_layout.size + hdr.getDataSegmentInfo().file_layout.size; } \ No newline at end of file diff --git a/src/IniProcess.h b/src/IniProcess.h index dbfad5e..365f715 100644 --- a/src/IniProcess.h +++ b/src/IniProcess.h @@ -1,14 +1,10 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include "types.h" + #include #include -#include "common.h" +namespace nstool { class IniProcess { @@ -17,7 +13,7 @@ public: void process(); - void setInputFile(const fnd::SharedPtr& file); + void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -27,7 +23,7 @@ private: const std::string kKipExtention = ".kip"; const size_t kCacheSize = 0x10000; - fnd::SharedPtr mFile; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; @@ -35,7 +31,7 @@ private: std::string mKipExtractPath; nn::hac::IniHeader mHdr; - fnd::List> mKipList; + std::vector> mKipList; void importHeader(); void importKipList(); @@ -44,4 +40,6 @@ private: void extractKipList(); size_t getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const; -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp new file mode 100644 index 0000000..6e00cf0 --- /dev/null +++ b/src/KeyBag.cpp @@ -0,0 +1,568 @@ +#include "KeyBag.h" + +#include "util.h" +#include + +#include +#include + +#include +#include +#include +#include + +nstool::KeyBagInitializer::KeyBagInitializer(bool isDev, const tc::Optional& keyfile_path, const tc::Optional& tik_path, const tc::Optional& cert_path) +{ + if (keyfile_path.isSet()) + { + importBaseKeyFile(keyfile_path.get(), isDev); + } + if (cert_path.isSet()) + { + importCertificateChain(cert_path.get()); + } + if (tik_path.isSet()) + { + importTicket(tik_path.get()); + } +} + +void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_path, bool isDev) +{ + std::shared_ptr keyfile_stream = std::make_shared(tc::io::FileStream(keyfile_path, tc::io::FileMode::Open, tc::io::FileAccess::Read)); + + // import keyfile into a dictionary + std::map keyfile_dict; + processResFile(keyfile_stream, keyfile_dict); + + // sources for key derivation + std::map master_key; + tc::Optional package2_key_source; + tc::Optional ticket_titlekek_source; + std::array, 3> key_area_key_source; + tc::Optional aes_kek_generation_source; + tc::Optional aes_key_generation_source; + tc::Optional nca_header_kek_source; + tc::Optional nca_header_key_source; + tc::Optional pki_root_sign_key; + + // macros for importing + +#define _SAVE_AES128KEY(key_name, dst) \ + { \ + std::string key,val; \ + tc::ByteData dec_val; \ + aes128_key_t tmp_aes128_key; \ + key = (key_name); \ + val = keyfile_dict[key]; \ + if (val.empty() == false) { \ + dec_val = tc::cli::FormatUtil::hexStringToBytes(val); \ + if (dec_val.size() != tmp_aes128_key.size()) \ + throw tc::ArgumentException("nstool::KeyBagInitializer", "Key: \"" + key_name + "\" has incorrect length"); \ + memcpy(tmp_aes128_key.data(), dec_val.data(), tmp_aes128_key.size()); \ + (dst) = tmp_aes128_key; \ + } \ + } + +#define _SAVE_AES128XTSKEY(key_name, dst) \ + { \ + std::string key,val; \ + tc::ByteData dec_val; \ + aes128_xtskey_t tmp_aes128_xtskey; \ + key = (key_name); \ + val = keyfile_dict[key]; \ + if (val.empty() == false) { \ + dec_val = tc::cli::FormatUtil::hexStringToBytes(val); \ + if (dec_val.size() != sizeof(tmp_aes128_xtskey)) \ + throw tc::ArgumentException("nstool::KeyBagInitializer", "Key: \"" + key_name + "\" has incorrect length"); \ + memcpy(tmp_aes128_xtskey[0].data(), dec_val.data(), tmp_aes128_xtskey[0].size()); \ + memcpy(tmp_aes128_xtskey[1].data(), dec_val.data()+tmp_aes128_xtskey[0].size(), tmp_aes128_xtskey[1].size()); \ + (dst) = tmp_aes128_xtskey; \ + } \ + } + +#define _SAVE_RSAKEY(key_name, dst, bitsize) \ + { \ + std::string key_mod,key_prv,val_mod,val_prv; \ + tc::ByteData dec_val; \ + rsa_key_t tmp_rsa_key; \ + key_mod = fmt::format("{:s}_modulus", (key_name)); \ + key_prv = fmt::format("{:s}_private", (key_name)); \ + val_mod = keyfile_dict[key_mod]; \ + val_prv = keyfile_dict[key_prv]; \ + if (val_mod.empty() == false) { \ + dec_val = tc::cli::FormatUtil::hexStringToBytes(val_mod); \ + if (dec_val.size() == (bitsize) >> 3) { \ + tmp_rsa_key.n = dec_val; \ + if (val_prv.empty() == false) { \ + dec_val = tc::cli::FormatUtil::hexStringToBytes(val_prv); \ + if (dec_val.size() == (bitsize) >> 3) { \ + tmp_rsa_key.d = dec_val; \ + (dst) = tc::crypto::RsaPrivateKey(tmp_rsa_key.n.data(), tmp_rsa_key.n.size(), tmp_rsa_key.d.data(), tmp_rsa_key.d.size()); \ + } \ + else { \ + fmt::print("[WARNING] Key: \"{:s}\" has incorrect length (was: {:d}, expected {:d})\n", key_prv, val_prv.size(), ((bitsize) >> 3)*2); \ + } \ + } \ + else { \ + (dst) = tc::crypto::RsaPublicKey(tmp_rsa_key.n.data(), tmp_rsa_key.n.size()); \ + } \ + } \ + else {\ + fmt::print("[WARNING] Key: \"{:s}\" has incorrect length (was: {:d}, expected {:d})\n", key_mod, val_mod.size(), ((bitsize) >> 3)*2); \ + } \ + } \ + } + + // keynames + enum NameVariantIndex + { + NNTOOLS, + LEGACY_HACTOOL, + LEGACY_0 + }; + + static const size_t kNameVariantNum = 3; + + std::vector kMasterBase = { "master" }; + std::vector kPkg1Base = { "package1" }; + std::vector kPkg2Base = { "package2" }; + std::vector kXciHeaderBase = { "xci_header" }; + std::vector kContentArchiveHeaderBase = { "nca_header", "header" }; + std::vector kAcidBase = { "acid" }; + std::vector kNrrCertBase = { "nrr_certificate" }; + std::vector kPkiRootBase = { "pki_root" }; + std::vector kTicketCommonKeyBase = { "ticket_commonkey", "titlekek" }; + std::vector kNcaKeyAreaEncKeyBase = { "nca_key_area_key", "key_area_key", "nca_body_keak" }; + std::vector kNcaKeyAreaEncKeyHwBase = { "nca_key_area_key_hw", "key_area_hw_key" }; + std::vector kKekGenBase = { "aes_kek_generation" }; + std::vector kKeyGenBase = { "aes_key_generation" }; + + // misc str + const std::string kKeyStr = "key"; + const std::string kKekStr = "kek"; + const std::string kSourceStr = "source"; + const std::string kSignKey = "sign_key"; + const std::string kModulusStr = "modulus"; + const std::string kPrivateStr = "private"; + std::vector kNcaKeyAreaKeyIndexStr = { "application", "ocean", "system" }; + + static const size_t kMasterKeyMax = 0x20; + /**/ + + // import key data + for (size_t name_idx = 0; name_idx < kNameVariantNum; name_idx++) + { + /* internal key sources */ + if (name_idx < kMasterBase.size()) + { + for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + { + // std::map master_key; + //fmt::print("{:s}_key_{:02x}\n", kMasterBase[name_idx], mkey_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kMasterBase[name_idx], kKeyStr, mkey_rev), master_key[mkey_rev]); + } + } + + if (name_idx < kPkg2Base.size()) + { + // tc::Optional package2_key_source; + //fmt::print("{:s}_key_source\n", kPkg2Base[name_idx]); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:s}", kPkg2Base[name_idx], kKeyStr, kSourceStr), package2_key_source); + } + + if (name_idx < kTicketCommonKeyBase.size()) + { + // tc::Optional ticket_titlekek_source; + //fmt::print("{:s}_source\n", kTicketCommonKeyBase[name_idx]); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}", kTicketCommonKeyBase[name_idx], kSourceStr), ticket_titlekek_source); + } + + if (name_idx < kNcaKeyAreaEncKeyBase.size()) + { + // std::array, 3> key_area_key_source; + + for (size_t keak_idx = 0; keak_idx < kNcaKeyAreaKeyIndexStr.size(); keak_idx++) + { + //fmt::print("{:s}_{:s}_source\n", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx]); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:s}", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], kSourceStr), key_area_key_source[keak_idx]); + } + } + + if (name_idx < kKekGenBase.size()) + { + // tc::Optional aes_kek_generation_source; + //fmt::print("{:s}_source\n", kKekGenBase[name_idx]); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}", kKekGenBase[name_idx], kSourceStr), aes_kek_generation_source); + } + + if (name_idx < kKeyGenBase.size()) + { + // tc::Optional aes_key_generation_source; + //fmt::print("{:s}_source\n", kKeyGenBase[name_idx]); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}", kKeyGenBase[name_idx], kSourceStr), aes_key_generation_source); + } + + if (name_idx < kContentArchiveHeaderBase.size()) + { + // tc::Optional nca_header_kek_source; + //fmt::print("{:s}_kek_source\n", kContentArchiveHeaderBase[name_idx]); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kKekStr, kSourceStr), nca_header_kek_source); + } + + if (name_idx < kContentArchiveHeaderBase.size()) + { + // tc::Optional nca_header_key_source; + //fmt::print("{:s}_key_source\n", kContentArchiveHeaderBase[name_idx]); + _SAVE_AES128XTSKEY(fmt::format("{:s}_{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kKeyStr, kSourceStr), nca_header_key_source); + } + + /* package1 */ + // package1_key_xx + if (name_idx < kPkg1Base.size()) + { + for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + { + //fmt::print("{:s}_key_{:02x}\n", kPkg1Base[name_idx], mkey_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kPkg1Base[name_idx], kKeyStr, mkey_rev), pkg1_key[mkey_rev]); + } + } + + /* package2 */ + if (name_idx < kPkg2Base.size()) + { + // package2_key_xx + for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + { + //fmt::print("{:s}_key_{:02x}\n", kPkg2Base[name_idx], mkey_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kPkg2Base[name_idx], kKeyStr, mkey_rev), pkg2_key[mkey_rev]); + } + + // package2_sign_key + //fmt::print("{:s}_{:s}_{:s}\n", kPkg2Base[name_idx], kSignKey, kPrivateStr); + //fmt::print("{:s}_{:s}_{:s}\n", kPkg2Base[name_idx], kSignKey, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kPkg2Base[name_idx], kSignKey), pkg2_sign_key, 2048); + } + + /* eticket */ + // ticket common key + if (name_idx < kTicketCommonKeyBase.size()) + { + for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + { + //fmt::print("{:s}_{:02x}\n", kTicketCommonKeyBase[name_idx], mkey_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:02x}", kTicketCommonKeyBase[name_idx], mkey_rev), etik_common_key[mkey_rev]); + } + } + + /* NCA keys */ + if (name_idx < kContentArchiveHeaderBase.size()) + { + // nca header key + //fmt::print("{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kKeyStr); + //_SAVE_AES128XTSKEY(fmt::format("{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kKeyStr), nca_header_key); + + // nca header sign0 key (generations) + for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + { + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev, kPrivateStr); + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev), nca_header_sign0_key[mkey_rev], 2048); + } + // nca header sign0 key (generation 0) + //fmt::print("{:s}_{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, kPrivateStr); + //fmt::print("{:s}_{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kSignKey), nca_header_sign0_key[0], 2048); + + } + + // nca body key (unused since prototype format) + + // nca key area encryption keys + if (name_idx < kNcaKeyAreaEncKeyBase.size()) + { + for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + { + for (size_t keak_idx = 0; keak_idx < kNcaKeyAreaKeyIndexStr.size(); keak_idx++) + { + //fmt::print("{:s}_{:s}_{:02x}\n", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev), nca_key_area_encryption_key[keak_idx][mkey_rev]); + } + } + } + // nca key area "hw" encryption keys + if (name_idx < kNcaKeyAreaEncKeyHwBase.size()) + { + for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + { + for (size_t keak_idx = 0; keak_idx < kNcaKeyAreaKeyIndexStr.size(); keak_idx++) + { + //fmt::print("{:s}_{:s}_{:02x}\n", kNcaKeyAreaEncKeyHwBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kNcaKeyAreaEncKeyHwBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev), nca_key_area_encryption_key_hw[keak_idx][mkey_rev]); + } + } + } + + /* ACID */ + if (name_idx < kAcidBase.size()) + { + // acid sign key (generations) + for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + { + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kAcidBase[name_idx], kSignKey, mkey_rev, kPrivateStr); + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kAcidBase[name_idx], kSignKey, mkey_rev, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kAcidBase[name_idx], kSignKey, mkey_rev), acid_sign_key[mkey_rev], 2048); + } + // acid sign key (generation 0) + //fmt::print("{:s}_{:s}_{:s}\n", kAcidBase[name_idx], kSignKey, kPrivateStr); + //fmt::print("{:s}_{:s}_{:s}\n", kAcidBase[name_idx], kSignKey, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kAcidBase[name_idx], kSignKey), acid_sign_key[0], 2048); + } + + /* NRR certificate */ + if (name_idx < kNrrCertBase.size()) + { + // nrr certificate sign key (generations) + for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + { + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kNrrCertBase[name_idx], kSignKey, mkey_rev, kPrivateStr); + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kNrrCertBase[name_idx], kSignKey, mkey_rev, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kNrrCertBase[name_idx], kSignKey, mkey_rev), nrr_certificate_sign_key[mkey_rev], 2048); + } + // nrr certificate sign key (generation 0) + //fmt::print("{:s}_{:s}_{:s}\n", kNrrCertBase[name_idx], kSignKey, kPrivateStr); + //fmt::print("{:s}_{:s}_{:s}\n", kNrrCertBase[name_idx], kSignKey, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kNrrCertBase[name_idx], kSignKey), nrr_certificate_sign_key[0], 2048); + } + + /* XCI header */ + if (name_idx < kXciHeaderBase.size()) + { + // xci header key (based on index) + for (size_t kek_index = 0; kek_index < 8; kek_index++) + { + //fmt::print("{:s}_{:s}_{:02x}\n", kXciHeaderBase[name_idx], kKeyStr, kek_index); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kXciHeaderBase[name_idx], kKeyStr, kek_index), xci_header_key[kek_index]); + } + // xci header key (old lable) + //fmt::print("{:s}_{:s}\n", kXciHeaderBase[name_idx], kKeyStr); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}", kXciHeaderBase[name_idx], kKeyStr), xci_header_key[isDev ? nn::hac::gc::KEK_DEV : nn::hac::gc::KEK_PROD]); + + // xci header sign key + //fmt::print("{:s}_{:s}_{:s}\n", kXciHeaderBase[name_idx], kSignKey, kPrivateStr); + //fmt::print("{:s}_{:s}_{:s}\n", kXciHeaderBase[name_idx], kSignKey, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kXciHeaderBase[name_idx], kSignKey), xci_header_sign_key, 2048); + } + + /* PKI */ + if (name_idx < kPkiRootBase.size()) + { + // tc::Optional pki_root_sign_key; + //fmt::print("{:s}_{:s}_{:s}\n", kPkiRootBase[name_idx], kSignKey, kPrivateStr); + //fmt::print("{:s}_{:s}_{:s}\n", kPkiRootBase[name_idx], kSignKey, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kPkiRootBase[name_idx], kSignKey), pki_root_sign_key, 4096); + } + + + } + +#undef _SAVE_RSAKEY +#undef _SAVE_AES128XTSKEY +#undef _SAVE_AES128KEY + + // Derive Keys + for (auto itr = master_key.begin(); itr != master_key.end(); itr++) + { + if (aes_kek_generation_source.isSet() && aes_key_generation_source.isSet()) + { + if (itr->first == 0 && nca_header_kek_source.isSet() && nca_header_key_source.isSet()) + { + if (nca_header_key.isNull()) + { + aes128_key_t nca_header_kek_tmp; + nn::hac::AesKeygen::generateKey(nca_header_kek_tmp.data(), aes_kek_generation_source.get().data(), nca_header_kek_source.get().data(), aes_key_generation_source.get().data(), itr->second.data()); + + aes128_xtskey_t nca_header_key_tmp; + nn::hac::AesKeygen::generateKey(nca_header_key_tmp[0].data(), nca_header_key_source.get()[0].data(), nca_header_kek_tmp.data()); + nn::hac::AesKeygen::generateKey(nca_header_key_tmp[1].data(), nca_header_key_source.get()[1].data(), nca_header_kek_tmp.data()); + + nca_header_key = nca_header_key_tmp; + } + } + + for (size_t keak_idx = 0; keak_idx < nn::hac::nca::kKeyAreaEncryptionKeyNum; keak_idx++) + { + if (key_area_key_source[keak_idx].isSet() && nca_key_area_encryption_key[keak_idx].find(itr->first) != nca_key_area_encryption_key[keak_idx].end()) + { + aes128_key_t nca_key_area_encryption_key_tmp; + nn::hac::AesKeygen::generateKey(nca_key_area_encryption_key_tmp.data(), aes_kek_generation_source.get().data(), key_area_key_source[keak_idx].get().data(), aes_key_generation_source.get().data(), itr->second.data()); + nca_key_area_encryption_key[keak_idx][itr->first] = nca_key_area_encryption_key_tmp; + } + } + } + if (ticket_titlekek_source.isSet() && etik_common_key.find(itr->first) == etik_common_key.end()) + { + aes128_key_t etik_common_key_tmp; + nn::hac::AesKeygen::generateKey(etik_common_key_tmp.data(), ticket_titlekek_source.get().data(), itr->second.data()); + etik_common_key[itr->first] = etik_common_key_tmp; + } + if (package2_key_source.isSet() && pkg2_key.find(itr->first) == pkg2_key.end()) + { + aes128_key_t pkg2_key_tmp; + nn::hac::AesKeygen::generateKey(pkg2_key_tmp.data(), package2_key_source.get().data(), itr->second.data()); + pkg2_key[itr->first] = pkg2_key_tmp; + } + } + + // Save PKI Root Key + if (pki_root_sign_key.isSet()) + { + broadon_rsa_signer["Root"] = { tc::ByteData(), pki_root_sign_key.get() }; + } +} + +void nstool::KeyBagInitializer::importTitleKeyFile(const tc::io::Path& keyfile_path) +{ + +} + +void nstool::KeyBagInitializer::importCertificateChain(const tc::io::Path& cert_path) +{ + // save file path string for error messages + std::string cert_path_str; + tc::io::PathUtil::pathToUnixUTF8(cert_path, cert_path_str); + + // open cert file + std::shared_ptr certfile_stream; + try { + certfile_stream = std::make_shared(tc::io::FileStream(cert_path, tc::io::FileMode::Open, tc::io::FileAccess::Read)); + } + catch (tc::io::FileNotFoundException& e) { + fmt::print("[WARNING] Failed to open certificate file \"{:s}\" ({:s}).\n", cert_path_str, e.error()); + return; + } + + // check size + size_t cert_raw_size = tc::io::IOUtil::castInt64ToSize(certfile_stream->length()); + if (cert_raw_size > 0x10000) + { + fmt::print("[WARNING] Certificate file \"{:s}\" was too large.\n", cert_path_str); + return; + } + + // import cert data + tc::ByteData cert_raw = tc::ByteData(cert_raw_size); + certfile_stream->seek(0, tc::io::SeekOrigin::Begin); + certfile_stream->read(cert_raw.data(), cert_raw.size()); + + nn::pki::SignedData cert; + try { + for (size_t f_pos = 0; f_pos < cert_raw.size(); f_pos += cert.getBytes().size()) + { + cert.fromBytes(cert_raw.data() + f_pos, cert_raw.size() - f_pos); + + std::string cert_identity = fmt::format("{:s}-{:s}", cert.getBody().getIssuer(), cert.getBody().getSubject()); + + switch (cert.getBody().getPublicKeyType()) { + case nn::pki::cert::PublicKeyType::RSA2048: + broadon_rsa_signer[cert_identity] = { cert.getBytes(), cert.getBody().getRsa2048PublicKey() }; + break; + case nn::pki::cert::PublicKeyType::RSA4096: + broadon_rsa_signer[cert_identity] = { cert.getBytes(), cert.getBody().getRsa4096PublicKey() }; + break; + case nn::pki::cert::PublicKeyType::ECDSA240: + fmt::print("[WARNING] Certificate {:s} will not be imported. ecc233 public keys are not supported yet.\n", cert_identity); + break; + default: + fmt::print("[WARNING] Certificate {:s} will not be imported. Unknown public key type.\n", cert_identity); + } + } + } + catch (tc::Exception& e) { + fmt::print("[WARNING] Certificate file \"{:s}\" is corrupted ({:s}).\n", cert_path_str, e.error()); + return; + } +} + +void nstool::KeyBagInitializer::importTicket(const tc::io::Path& tik_path) +{ + // save file path string for error messages + std::string tik_path_str; + tc::io::PathUtil::pathToUnixUTF8(tik_path, tik_path_str); + + // open cert file + std::shared_ptr tik_stream; + try { + tik_stream = std::make_shared(tc::io::FileStream(tik_path, tc::io::FileMode::Open, tc::io::FileAccess::Read)); + } + catch (tc::io::FileNotFoundException& e) { + fmt::print("[WARNING] Failed to open ticket \"{:s}\" ({:s}).\n", tik_path_str, e.error()); + return; + } + + // check size + size_t tik_raw_size = tc::io::IOUtil::castInt64ToSize(tik_stream->length()); + if (tik_raw_size > 0x10000) + { + fmt::print("[WARNING] Ticket \"{:s}\" was too large.\n", tik_path_str); + return; + } + + // import cert data + tc::ByteData tik_raw = tc::ByteData(tik_raw_size); + tik_stream->seek(0, tc::io::SeekOrigin::Begin); + tik_stream->read(tik_raw.data(), tik_raw.size()); + + nn::pki::SignedData tik; + try { + // de serialise ticket + tik.fromBytes(tik_raw.data(), tik_raw.size()); + + // save rights id + rights_id_t rights_id; + memcpy(rights_id.data(), tik.getBody().getRightsId(), rights_id.size()); + + // check ticket is not personalised + if (tik.getBody().getTitleKeyEncType() != nn::es::ticket::AES128_CBC) + { + fmt::print("[WARNING] Ticket \"{:s}\" will not be imported. Personalised tickets are not supported.\n", tc::cli::FormatUtil::formatBytesAsString(rights_id.data(), rights_id.size(), true, "")); + return; + } + + // save enc title key + aes128_key_t enc_title_key; + memcpy(enc_title_key.data(), tik.getBody().getEncTitleKey(), enc_title_key.size()); + + // save the encrypted title key as the fallback enc content key incase the ticket was malformed and workarounds to decrypt it in isolation fail + if (fallback_enc_content_key.isNull()) + { + fallback_enc_content_key = enc_title_key; + } + + // determine key to decrypt title key + byte_t common_key_index = tik.getBody().getCommonKeyId(); + + // work around for bad scene tickets where they don't set the commonkey id field (detect scene ticket with ffff.... signature) + if (common_key_index != rights_id[15] && tik.getSignature().getBytes()[0x00] == 0xff && tik.getSignature().getBytes()[0x01] == 0xff) + { + common_key_index = rights_id[15]; + } + if (etik_common_key.find(tik.getBody().getCommonKeyId()) == etik_common_key.end()) + { + fmt::print("[WARNING] Ticket \"{:s}\" will not be imported. Could not decrypt title key.\n", tc::cli::FormatUtil::formatBytesAsString(rights_id.data(), rights_id.size(), true, "")); + return; + } + + // decrypt title key + aes128_key_t dec_title_key; + tc::crypto::DecryptAes128Ecb(dec_title_key.data(), enc_title_key.data(), sizeof(aes128_key_t), etik_common_key[common_key_index].data(), sizeof(aes128_key_t)); + + // add to key dict + external_content_keys[rights_id] = dec_title_key; + + } + catch (tc::Exception& e) { + fmt::print("[WARNING] Ticket \"{:s}\" is corrupted ({:s}).\n", tik_path_str, e.error()); + return; + } +} \ No newline at end of file diff --git a/src/KeyBag.h b/src/KeyBag.h new file mode 100644 index 0000000..e319fb6 --- /dev/null +++ b/src/KeyBag.h @@ -0,0 +1,76 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nstool { + +struct KeyBag +{ + using aes128_key_t = nn::hac::detail::aes128_key_t; + using aes128_xtskey_t = nn::hac::detail::aes128_xtskey_t; + using rsa_key_t = tc::crypto::RsaKey; + using rights_id_t = nn::hac::detail::rights_id_t; + using key_generation_t = byte_t; + using broadon_issuer_t = std::string; + static const size_t kNcaKeakNum = nn::hac::nca::kKeyAreaEncryptionKeyNum; + + + // acid + std::map acid_sign_key; + + // pkg1 and pkg2 + std::map pkg1_key; + std::map pkg2_key; + tc::Optional pkg2_sign_key; + + // nca + tc::Optional nca_header_key; + std::map nca_header_sign0_key; + std::array, kNcaKeakNum> nca_key_area_encryption_key; + std::array, kNcaKeakNum> nca_key_area_encryption_key_hw; + + // external content keys (nca<->ticket) + std::map external_content_keys; + tc::Optional fallback_enc_content_key; // encrypted content key to be used when external_content_keys does not have the required content key (usually taken raw from ticket) + tc::Optional fallback_content_key; // content key to be used when external_content_keys does not have the required content key (usually already decrypted from ticket) + + // nrr + std::map nrr_certificate_sign_key; + + // xci + tc::Optional xci_header_sign_key; + std::map xci_header_key; + + // ticket + std::map etik_common_key; + + // BroadOn signer profiles (for es cert and es tik) + // BroadOn RSA Keys + struct BroadOnRsaSignerProfile + { + tc::ByteData certificate; + rsa_key_t key; + }; + std::map broadon_rsa_signer; +}; + +class KeyBagInitializer : public KeyBag +{ +public: + KeyBagInitializer(bool isDev, const tc::Optional& keyfile_path, const tc::Optional& tik_path, const tc::Optional& cert_path); +private: + KeyBagInitializer(); + + void importBaseKeyFile(const tc::io::Path& keyfile_path, bool isDev); + void importTitleKeyFile(const tc::io::Path& keyfile_path); + void importCertificateChain(const tc::io::Path& cert_path); + void importTicket(const tc::io::Path& tik_path); +}; + +} \ No newline at end of file diff --git a/src/KeyConfiguration.cpp b/src/KeyConfiguration.cpp deleted file mode 100644 index 8164091..0000000 --- a/src/KeyConfiguration.cpp +++ /dev/null @@ -1,413 +0,0 @@ -#include "KeyConfiguration.h" -#include -#include -#include - -KeyConfiguration::KeyConfiguration() -{ - clearGeneralKeyConfiguration(); - clearNcaExternalKeys(); -} - -KeyConfiguration::KeyConfiguration(const KeyConfiguration& other) -{ - *this = other; -} - -void KeyConfiguration::operator=(const KeyConfiguration& other) -{ - mPkg2SignKey = other.mPkg2SignKey; - mXciHeaderSignKey = other.mXciHeaderSignKey; - - mContentArchiveHeaderKey = other.mContentArchiveHeaderKey; - mXciHeaderKey = other.mXciHeaderKey; - - for (size_t i = 0; i < kMasterKeyNum; i++) - { - mAcidSignKey[i] = other.mAcidSignKey[i]; - mContentArchiveHeader0SignKey[i] = other.mContentArchiveHeader0SignKey[i]; - mNrrCertificateSignKey[i] = other.mNrrCertificateSignKey[i]; - mPkg2Key[i] = other.mPkg2Key[i]; - mPkg1Key[i] = other.mPkg1Key[i]; - mNcaKeyAreaEncryptionKey[0][i] = other.mNcaKeyAreaEncryptionKey[0][i]; - mNcaKeyAreaEncryptionKey[1][i] = other.mNcaKeyAreaEncryptionKey[1][i]; - mNcaKeyAreaEncryptionKey[2][i] = other.mNcaKeyAreaEncryptionKey[2][i]; - mNcaKeyAreaEncryptionKeyHw[0][i] = other.mNcaKeyAreaEncryptionKeyHw[0][i]; - mNcaKeyAreaEncryptionKeyHw[1][i] = other.mNcaKeyAreaEncryptionKeyHw[1][i]; - mNcaKeyAreaEncryptionKeyHw[2][i] = other.mNcaKeyAreaEncryptionKeyHw[2][i]; - mETicketCommonKey[i] = other.mETicketCommonKey[i]; - } - - mPkiRootKeyList = other.mPkiRootKeyList; - - mNcaExternalContentKeyList = other.mNcaExternalContentKeyList; -} - -void KeyConfiguration::importHactoolGenericKeyfile(const std::string& path) -{ - clearGeneralKeyConfiguration(); - - fnd::ResourceFileReader res; - try - { - res.processFile(path); - } - catch (const fnd::Exception&) - { - throw fnd::Exception(kModuleName, "Failed to open key file: " + path); - } - - // internally used sources - fnd::aes::sAes128Key master_key[kMasterKeyNum] = { kNullAesKey }; - fnd::aes::sAes128Key package2_key_source = kNullAesKey; - fnd::aes::sAes128Key ticket_titlekek_source = kNullAesKey; - fnd::aes::sAes128Key key_area_key_source[kNcaKeakNum] = { kNullAesKey, kNullAesKey, kNullAesKey }; - fnd::aes::sAes128Key aes_kek_generation_source = kNullAesKey; - fnd::aes::sAes128Key aes_key_generation_source = kNullAesKey; - fnd::aes::sAes128Key nca_header_kek_source = kNullAesKey; - fnd::aes::sAesXts128Key nca_header_key_source = kNullAesXtsKey; - fnd::rsa::sRsa4096Key pki_root_sign_key = kNullRsa4096Key; - -#define _CONCAT_2_STRINGS(str1, str2) ((str1) + "_" + (str2)) -#define _CONCAT_3_STRINGS(str1, str2, str3) _CONCAT_2_STRINGS(_CONCAT_2_STRINGS(str1, str2), str3) -#define _CONCAT_4_STRINGS(str1, str2, str3, str4) _CONCAT_2_STRINGS(_CONCAT_2_STRINGS(_CONCAT_2_STRINGS(str1, str2), str3), str4) - - std::string key,val; - fnd::Vec dec_array; - -#define _SAVE_KEYDATA(key_name, array, len) \ - key = (key_name); \ - val = res[key]; \ - if (val.empty() == false) { \ - fnd::SimpleTextOutput::stringToArray(val, dec_array); \ - if (dec_array.size() != len) \ - throw fnd::Exception(kModuleName, "Key: \"" + key_name + "\" has incorrect length"); \ - memcpy(array, dec_array.data(), len); \ - } - - for (size_t nameidx = 0; nameidx < kNameVariantNum; nameidx++) - { - // import sources - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kKeyStr, kSourceStr), package2_key_source.key, 0x10); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[nameidx], kSourceStr), ticket_titlekek_source.key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kSourceStr), key_area_key_source[0].key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kSourceStr), key_area_key_source[1].key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kSourceStr), key_area_key_source[2].key, 0x10); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kKekGenBase[nameidx], kSourceStr), aes_kek_generation_source.key, 0x10); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kKeyGenBase[nameidx], kSourceStr), aes_key_generation_source.key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kContentArchiveHeaderBase[nameidx], kKekStr, kSourceStr), nca_header_kek_source.key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kContentArchiveHeaderBase[nameidx], kKeyStr, kSourceStr), nca_header_key_source.key, 0x20); - - // Store Key Variants/Derivatives - for (size_t mkeyidx = 0; mkeyidx < kMasterKeyNum; mkeyidx++) - { - - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kMasterBase[nameidx], kKeyStr, kKeyIndex[mkeyidx]), master_key[mkeyidx].key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg1Base[nameidx], kKeyStr, kKeyIndex[mkeyidx]), mPkg1Key[mkeyidx].key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kKeyStr, kKeyIndex[mkeyidx]), mPkg2Key[mkeyidx].key, 0x10); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[nameidx], kKeyIndex[mkeyidx]), mETicketCommonKey[mkeyidx].key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[0][mkeyidx].key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[1][mkeyidx].key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[2][mkeyidx].key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[0][mkeyidx].key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[1][mkeyidx].key, 0x10); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[2][mkeyidx].key, 0x10); - - _SAVE_KEYDATA(_CONCAT_4_STRINGS(kContentArchiveHeaderBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kPrivateStr), mContentArchiveHeader0SignKey[mkeyidx].priv_exponent, fnd::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_4_STRINGS(kContentArchiveHeaderBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kModulusStr), mContentArchiveHeader0SignKey[mkeyidx].modulus, fnd::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_4_STRINGS(kAcidBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kPrivateStr), mAcidSignKey[mkeyidx].priv_exponent, fnd::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_4_STRINGS(kAcidBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kModulusStr), mAcidSignKey[mkeyidx].modulus, fnd::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_4_STRINGS(kNrrCertBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kPrivateStr), mNrrCertificateSignKey[mkeyidx].priv_exponent, fnd::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_4_STRINGS(kNrrCertBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kModulusStr), mNrrCertificateSignKey[mkeyidx].modulus, fnd::rsa::kRsa2048Size); - } - - // store nca header key - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kContentArchiveHeaderBase[nameidx], kKeyStr), mContentArchiveHeaderKey.key[0], 0x20); - - // store xci header key - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kKeyStr), mXciHeaderKey.key, 0x10); - - // store rsa keys - - // legacy header nca key name - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kContentArchiveHeaderBase[nameidx], kSignKey, kPrivateStr), mContentArchiveHeader0SignKey[0].priv_exponent, fnd::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kContentArchiveHeaderBase[nameidx], kSignKey, kModulusStr), mContentArchiveHeader0SignKey[0].modulus, fnd::rsa::kRsa2048Size); - - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kXciHeaderBase[nameidx], kSignKey, kPrivateStr), mXciHeaderSignKey.priv_exponent, fnd::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kXciHeaderBase[nameidx], kSignKey, kModulusStr), mXciHeaderSignKey.modulus, fnd::rsa::kRsa2048Size); - - // legacy acid header key name - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kAcidBase[nameidx], kSignKey, kPrivateStr), mAcidSignKey[0].priv_exponent, fnd::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kAcidBase[nameidx], kSignKey, kModulusStr), mAcidSignKey[0].modulus, fnd::rsa::kRsa2048Size); - - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kSignKey, kPrivateStr), mPkg2SignKey.priv_exponent, fnd::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kSignKey, kModulusStr), mPkg2SignKey.modulus, fnd::rsa::kRsa2048Size); - - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkiRootBase[nameidx], kSignKey, kPrivateStr), pki_root_sign_key.priv_exponent, fnd::rsa::kRsa4096Size); - _SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkiRootBase[nameidx], kSignKey, kModulusStr), pki_root_sign_key.modulus, fnd::rsa::kRsa4096Size); - } - -#undef _SAVE_KEYDATA -#undef _CONCAT_3_STRINGS -#undef _CONCAT_2_STRINGS - - // Derive keys - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (master_key[i] != kNullAesKey) - { - if (aes_kek_generation_source != kNullAesKey && aes_key_generation_source != kNullAesKey) - { - if (i == 0 && nca_header_kek_source != kNullAesKey && nca_header_key_source != kNullAesXtsKey) - { - if (mContentArchiveHeaderKey == kNullAesXtsKey) - { - fnd::aes::sAes128Key nca_header_kek; - nn::hac::AesKeygen::generateKey(nca_header_kek.key, aes_kek_generation_source.key, nca_header_kek_source.key, aes_key_generation_source.key, master_key[i].key); - nn::hac::AesKeygen::generateKey(mContentArchiveHeaderKey.key[0], nca_header_key_source.key[0], nca_header_kek.key); - nn::hac::AesKeygen::generateKey(mContentArchiveHeaderKey.key[1], nca_header_key_source.key[1], nca_header_kek.key); - } - } - - for (size_t j = 0; j < nn::hac::nca::kKeyAreaEncryptionKeyNum; j++) - { - if (key_area_key_source[j] != kNullAesKey && mNcaKeyAreaEncryptionKey[j][i] == kNullAesKey) - { - nn::hac::AesKeygen::generateKey(mNcaKeyAreaEncryptionKey[j][i].key, aes_kek_generation_source.key, key_area_key_source[j].key, aes_key_generation_source.key, master_key[i].key); - } - } - } - - if (ticket_titlekek_source != kNullAesKey && mETicketCommonKey[i] == kNullAesKey) - { - nn::hac::AesKeygen::generateKey(mETicketCommonKey[i].key, ticket_titlekek_source.key, master_key[i].key); - } - if (package2_key_source != kNullAesKey && mPkg2Key[i] == kNullAesKey) - { - nn::hac::AesKeygen::generateKey(mPkg2Key[i].key, package2_key_source.key, master_key[i].key); - } - } - } - - // populate pki root keys - if (pki_root_sign_key != kNullRsa4096Key) - { - sPkiRootKey tmp; - - tmp.name = nn::pki::sign::kRootIssuerStr; - tmp.key_type = nn::pki::sign::SIGN_ALGO_RSA4096; - tmp.rsa4096_key = pki_root_sign_key; - - mPkiRootKeyList.addElement(tmp); - } -} - - -void KeyConfiguration::clearGeneralKeyConfiguration() -{ - - mPkg2SignKey = kNullRsa2048Key; - - mXciHeaderSignKey = kNullRsa2048Key; - mPkiRootKeyList.clear(); - - mContentArchiveHeaderKey = kNullAesXtsKey; - mXciHeaderKey = kNullAesKey; - - for (size_t i = 0; i < kMasterKeyNum; i++) - { - mAcidSignKey[i] = kNullRsa2048Key; - mContentArchiveHeader0SignKey[i] = kNullRsa2048Key; - mNrrCertificateSignKey[i] = kNullRsa2048Key; - mPkg1Key[i] = kNullAesKey; - mPkg2Key[i] = kNullAesKey; - mETicketCommonKey[i] = kNullAesKey; - for (size_t j = 0; j < kNcaKeakNum; j++) - { - mNcaKeyAreaEncryptionKey[j][i] = kNullAesKey; - mNcaKeyAreaEncryptionKeyHw[j][i] = kNullAesKey; - } - } -} - -void KeyConfiguration::clearNcaExternalKeys() -{ - mNcaExternalContentKeyList.clear(); -} - -bool KeyConfiguration::getContentArchiveHeaderKey(fnd::aes::sAesXts128Key& key) const -{ - return copyOutKeyResourceIfExists(mContentArchiveHeaderKey, key, kNullAesXtsKey); -} - -bool KeyConfiguration::getContentArchiveHeader0SignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const -{ - // TODO: This needs to be changed to support multiple keys - if (key_generation >= kMasterKeyNum) - { - return false; - } - - return copyOutKeyResourceIfExists(mContentArchiveHeader0SignKey[key_generation], key, kNullRsa2048Key); -} - -bool KeyConfiguration::getAcidSignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const -{ - // TODO: This needs to be changed to support multiple keys - if (key_generation >= kMasterKeyNum) - { - return false; - } - - return copyOutKeyResourceIfExists(mAcidSignKey[key_generation], key, kNullRsa2048Key); -} - -bool KeyConfiguration::getNcaKeyAreaEncryptionKey(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const -{ - if (keak_type >= kNcaKeakNum || masterkey_index >= kMasterKeyNum) - { - return false; - } - return copyOutKeyResourceIfExists(mNcaKeyAreaEncryptionKey[keak_type][masterkey_index], key, kNullAesKey); -} - -bool KeyConfiguration::getNcaKeyAreaEncryptionKeyHw(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const -{ - if (keak_type >= kNcaKeakNum || masterkey_index >= kMasterKeyNum) - { - return false; - } - return copyOutKeyResourceIfExists(mNcaKeyAreaEncryptionKeyHw[keak_type][masterkey_index], key, kNullAesKey); -} - -void KeyConfiguration::addNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], const fnd::aes::sAes128Key& key) -{ - sNcaExternalContentKey tmp; - memcpy(tmp.rights_id.data, rights_id, nn::hac::nca::kRightsIdLen); - tmp.key = key; - - if (mNcaExternalContentKeyList.hasElement(tmp)) - return; - - mNcaExternalContentKeyList.addElement(tmp); -} - -bool KeyConfiguration::getNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], fnd::aes::sAes128Key& key) const -{ - sRightsId id; - bool res_exists = false; - - memcpy(id.data, rights_id, nn::hac::nca::kRightsIdLen); - for (size_t i = 0; i < mNcaExternalContentKeyList.size(); i++) - { - if (mNcaExternalContentKeyList[i].rights_id == id) - { - res_exists = true; - key = mNcaExternalContentKeyList[i].key; - break; - } - } - - return res_exists; -} - -bool KeyConfiguration::getNrrCertificateSignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const -{ - // TODO: This needs to be changed to support multiple keys - if (key_generation >= kMasterKeyNum) - { - return false; - } - - return copyOutKeyResourceIfExists(mNrrCertificateSignKey[key_generation], key, kNullRsa2048Key); -} - -bool KeyConfiguration::getPkg1Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const -{ - if (masterkey_index >= kMasterKeyNum) - { - return false; - } - return copyOutKeyResourceIfExists(mPkg1Key[masterkey_index], key, kNullAesKey); -} - -bool KeyConfiguration::getPkg2Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const -{ - if (masterkey_index >= kMasterKeyNum) - { - return false; - } - return copyOutKeyResourceIfExists(mPkg2Key[masterkey_index], key, kNullAesKey); -} - -bool KeyConfiguration::getPkg2SignKey(fnd::rsa::sRsa2048Key& key) const -{ - return copyOutKeyResourceIfExists(mPkg2SignKey, key, kNullRsa2048Key); -} - -bool KeyConfiguration::getXciHeaderSignKey(fnd::rsa::sRsa2048Key& key) const -{ - return copyOutKeyResourceIfExists(mXciHeaderSignKey, key, kNullRsa2048Key); -} - -bool KeyConfiguration::getXciHeaderKey(fnd::aes::sAes128Key& key) const -{ - return copyOutKeyResourceIfExists(mXciHeaderKey, key, kNullAesKey); -} - -bool KeyConfiguration::getETicketCommonKey(byte_t masterkey_index, fnd::aes::sAes128Key& key) const -{ - if (masterkey_index >= kMasterKeyNum) - { - return false; - } - return copyOutKeyResourceIfExists(mETicketCommonKey[masterkey_index], key, kNullAesKey); -} - -bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa4096Key& key) const -{ - bool res_exists = false; - for (size_t i = 0; i < mPkiRootKeyList.size(); i++) - { - if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_RSA4096) - { - res_exists = true; - key = mPkiRootKeyList[i].rsa4096_key; - break; - } - } - - return res_exists; -} - -bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa2048Key& key) const -{ - bool res_exists = false; - for (size_t i = 0; i < mPkiRootKeyList.size(); i++) - { - if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_RSA2048) - { - res_exists = true; - key = mPkiRootKeyList[i].rsa2048_key; - break; - } - } - - return res_exists; -} - -bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::ecdsa::sEcdsa240Key& key) const -{ - bool res_exists = false; - for (size_t i = 0; i < mPkiRootKeyList.size(); i++) - { - if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_ECDSA240) - { - res_exists = true; - key = mPkiRootKeyList[i].ecdsa240_key; - break; - } - } - - return res_exists; -} \ No newline at end of file diff --git a/src/KeyConfiguration.h b/src/KeyConfiguration.h deleted file mode 100644 index 4a112c6..0000000 --- a/src/KeyConfiguration.h +++ /dev/null @@ -1,217 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class KeyConfiguration -{ -public: - KeyConfiguration(); - KeyConfiguration(const KeyConfiguration& other); - - void operator=(const KeyConfiguration& other); - - void importHactoolGenericKeyfile(const std::string& path); - //void importHactoolTitleKeyfile(const std::string& path); - - void clearGeneralKeyConfiguration(); - void clearNcaExternalKeys(); - - // nca keys - bool getContentArchiveHeaderKey(fnd::aes::sAesXts128Key& key) const; - bool getContentArchiveHeader0SignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const; - bool getAcidSignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const; - bool getNcaKeyAreaEncryptionKey(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const; - bool getNcaKeyAreaEncryptionKeyHw(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const; - - // external content keys - void addNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], const fnd::aes::sAes128Key& key); - bool getNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], fnd::aes::sAes128Key& key) const; - - // nrr key - bool getNrrCertificateSignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const; - - // pkg1/pkg2 - bool getPkg1Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const; - bool getPkg2Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const; - bool getPkg2SignKey(fnd::rsa::sRsa2048Key& key) const; - - // xci keys - bool getXciHeaderSignKey(fnd::rsa::sRsa2048Key& key) const; - bool getXciHeaderKey(fnd::aes::sAes128Key& key) const; - - // ticket - bool getETicketCommonKey(byte_t masterkey_index, fnd::aes::sAes128Key& key) const; - - // pki - bool getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa4096Key& key) const; - bool getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa2048Key& key) const; - bool getPkiRootSignKey(const std::string& root_name, fnd::ecdsa::sEcdsa240Key& key) const; -private: - const std::string kModuleName = "KeyConfiguration"; - const fnd::aes::sAes128Key kNullAesKey = {{0}}; - const fnd::aes::sAesXts128Key kNullAesXtsKey = {{{0}}}; - const fnd::rsa::sRsa4096Key kNullRsa4096Key = {{0}, {0}, {0}}; - const fnd::rsa::sRsa2048Key kNullRsa2048Key = {{0}, {0}, {0}}; - static const size_t kMasterKeyNum = 0x20; - static const size_t kNcaKeakNum = nn::hac::nca::kKeyAreaEncryptionKeyNum; - - // keynames - enum NameVariantIndex - { - NNTOOLS, - LEGACY_HACTOOL, - LEGACY_0 - }; - static const size_t kNameVariantNum = 3; - const std::string kMasterBase[kNameVariantNum] = { "master", "master", "master" }; - const std::string kPkg1Base[kNameVariantNum] = { "package1", "package1", "package1" }; - const std::string kPkg2Base[kNameVariantNum] = { "package2", "package2", "package2" }; - const std::string kXciHeaderBase[kNameVariantNum] = { "xci_header", "xci_header", "xci_header" }; - const std::string kContentArchiveHeaderBase[kNameVariantNum] = { "nca_header", "header", "nca_header" }; - const std::string kAcidBase[kNameVariantNum] = { "acid", "acid", "acid" }; - const std::string kNrrCertBase[kNameVariantNum] = { "nrr_certificate", "nrr_certificate", "nrr_certificate" }; - const std::string kPkiRootBase[kNameVariantNum] = { "pki_root", "pki_root", "pki_root" }; - const std::string kTicketCommonKeyBase[kNameVariantNum] = { "ticket_commonkey", "titlekek", "ticket_commonkey" }; - const std::string kNcaKeyAreaEncKeyBase[kNameVariantNum] = { "nca_key_area_key", "key_area_key", "nca_body_keak" }; - const std::string kNcaKeyAreaEncKeyHwBase[kNameVariantNum] = { "nca_key_area_key_hw", "key_area_hw_key", "nca_key_area_key_hw" }; - const std::string kKekGenBase[kNameVariantNum] = { "aes_kek_generation", "aes_kek_generation", "aes_kek_generation" }; - const std::string kKeyGenBase[kNameVariantNum] = { "aes_key_generation", "aes_key_generation", "aes_key_generation" }; - - // misc str - const std::string kKeyStr = "key"; - const std::string kKekStr = "kek"; - const std::string kSourceStr = "source"; - const std::string kSignKey = "sign_key"; - const std::string kModulusStr = "modulus"; - const std::string kPrivateStr = "private"; - const std::string kNcaKeyAreaKeyIndexStr[kNcaKeakNum] = { "application", "ocean", "system" }; - const std::string kKeyIndex[kMasterKeyNum] = {"00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f"}; - - struct sRightsId - { - byte_t data[nn::hac::nca::kRightsIdLen]; - - void operator=(const sRightsId& other) - { - memcpy(this->data, other.data, nn::hac::nca::kRightsIdLen); - } - - bool operator==(const sRightsId& other) const - { - return memcmp(this->data, other.data, nn::hac::nca::kRightsIdLen) == 0; - } - - bool operator!=(const sRightsId& other) const - { - return !(operator==(other)); - } - }; - - struct sNcaExternalContentKey - { - sRightsId rights_id; - fnd::aes::sAes128Key key; - - void operator=(const sNcaExternalContentKey& other) - { - rights_id = other.rights_id; - key = other.key; - } - - bool operator==(const sNcaExternalContentKey& other) const - { - return (rights_id == other.rights_id) \ - && (key == other.key); - } - - bool operator!=(const sNcaExternalContentKey& other) const - { - return !(operator==(other)); - } - }; - - struct sPkiRootKey - { - std::string name; - nn::pki::sign::SignatureAlgo key_type; - fnd::rsa::sRsa4096Key rsa4096_key; - fnd::rsa::sRsa2048Key rsa2048_key; - fnd::ecdsa::sEcdsa240Key ecdsa240_key; - - void operator=(const sPkiRootKey& other) - { - name = other.name; - key_type = other.key_type; - rsa4096_key = other.rsa4096_key; - rsa2048_key = other.rsa2048_key; - ecdsa240_key = other.ecdsa240_key; - } - - bool operator==(const sPkiRootKey& other) const - { - return (name == other.name) \ - && (key_type == other.key_type) \ - && (rsa4096_key == other.rsa4096_key) \ - && (rsa2048_key == other.rsa2048_key) \ - && (ecdsa240_key == other.ecdsa240_key); - } - - bool operator!=(const sPkiRootKey& other) const - { - return !(operator==(other)); - } - }; - - - /* general key config */ - // acid - fnd::rsa::sRsa2048Key mAcidSignKey[kMasterKeyNum]; - - // pkg1 and pkg2 - fnd::aes::sAes128Key mPkg1Key[kMasterKeyNum]; - fnd::rsa::sRsa2048Key mPkg2SignKey; - fnd::aes::sAes128Key mPkg2Key[kMasterKeyNum]; - - // nca - fnd::rsa::sRsa2048Key mContentArchiveHeader0SignKey[kMasterKeyNum]; - fnd::aes::sAesXts128Key mContentArchiveHeaderKey; - fnd::aes::sAes128Key mNcaKeyAreaEncryptionKey[kNcaKeakNum][kMasterKeyNum]; - fnd::aes::sAes128Key mNcaKeyAreaEncryptionKeyHw[kNcaKeakNum][kMasterKeyNum]; - - // nrr - fnd::rsa::sRsa2048Key mNrrCertificateSignKey[kMasterKeyNum]; - - // xci - fnd::rsa::sRsa2048Key mXciHeaderSignKey; - fnd::aes::sAes128Key mXciHeaderKey; - - // ticket - fnd::aes::sAes128Key mETicketCommonKey[kMasterKeyNum]; - - // pki - fnd::List mPkiRootKeyList; - - /* Nca External Keys */ - fnd::List mNcaExternalContentKeyList; - - template - bool copyOutKeyResourceIfExists(const T& src, T& dst, const T& null_sample) const - { - bool resource_exists = false; - - if (src != null_sample) - { - resource_exists = true; - dst = src; - } - - return resource_exists; - } -}; \ No newline at end of file diff --git a/src/KipProcess.cpp b/src/KipProcess.cpp index 2c787e2..dbaae88 100644 --- a/src/KipProcess.cpp +++ b/src/KipProcess.cpp @@ -9,51 +9,51 @@ #include -KipProcess::KipProcess(): +nstool::KipProcess::KipProcess(): mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false) { } -void KipProcess::process() +void nstool::KipProcess::process() { importHeader(); //importCodeSegments(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) { displayHeader(); displayKernelCap(mHdr.getKernelCapabilities()); } } -void KipProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::KipProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void KipProcess::setCliOutputMode(CliOutputMode type) +void nstool::KipProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void KipProcess::setVerifyMode(bool verify) +void nstool::KipProcess::setVerifyMode(bool verify) { mVerify = verify; } -void KipProcess::importHeader() +void nstool::KipProcess::importHeader() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } if ((*mFile)->size() < sizeof(nn::hac::sKipHeader)) { - throw fnd::Exception(kModuleName, "Corrupt KIP: file too small"); + throw tc::Exception(kModuleName, "Corrupt KIP: file too small"); } scratch.alloc(sizeof(nn::hac::sKipHeader)); @@ -62,10 +62,10 @@ void KipProcess::importHeader() mHdr.fromBytes(scratch.data(), scratch.size()); } -void KipProcess::importCodeSegments() +void nstool::KipProcess::importCodeSegments() { #ifdef _KIP_COMPRESSION_IMPLEMENTED - fnd::Vec scratch; + tc::ByteData scratch; uint32_t decompressed_len; #endif @@ -79,7 +79,7 @@ void KipProcess::importCodeSegments() fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mTextBlob.data(), (uint32_t)mTextBlob.size(), decompressed_len); if (decompressed_len != mTextBlob.size()) { - throw fnd::Exception(kModuleName, "KIP text segment failed to decompress"); + throw tc::Exception(kModuleName, "KIP text segment failed to decompress"); } } else @@ -102,7 +102,7 @@ void KipProcess::importCodeSegments() fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mRoBlob.data(), (uint32_t)mRoBlob.size(), decompressed_len); if (decompressed_len != mRoBlob.size()) { - throw fnd::Exception(kModuleName, "KIP ro segment failed to decompress"); + throw tc::Exception(kModuleName, "KIP ro segment failed to decompress"); } } else @@ -125,7 +125,7 @@ void KipProcess::importCodeSegments() fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mDataBlob.data(), (uint32_t)mDataBlob.size(), decompressed_len); if (decompressed_len != mDataBlob.size()) { - throw fnd::Exception(kModuleName, "KIP data segment failed to decompress"); + throw tc::Exception(kModuleName, "KIP data segment failed to decompress"); } } else @@ -139,7 +139,7 @@ void KipProcess::importCodeSegments() #endif } -void KipProcess::displayHeader() +void nstool::KipProcess::displayHeader() { std::cout << "[KIP Header]" << std::endl; std::cout << " Meta:" << std::endl; @@ -151,7 +151,7 @@ void KipProcess::displayHeader() std::cout << " UseSecureMemory: " << std::boolalpha << mHdr.getUseSecureMemoryFlag() << std::endl; std::cout << " Program Sections:" << std::endl; std::cout << " .text:" << std::endl; - if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) + if (mCliOutputMode.show_layout) { std::cout << " FileOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.offset << std::endl; std::cout << " FileSize: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.size << (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl; @@ -159,7 +159,7 @@ void KipProcess::displayHeader() std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.offset << std::endl; std::cout << " MemorySize: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.size << std::endl; std::cout << " .ro:" << std::endl; - if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) + if (mCliOutputMode.show_layout) { std::cout << " FileOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.offset << std::endl; std::cout << " FileSize: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.size << (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl; @@ -167,7 +167,7 @@ void KipProcess::displayHeader() std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.offset << std::endl; std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.size << std::endl; std::cout << " .data:" << std::endl; - if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) + if (mCliOutputMode.show_layout) { std::cout << " FileOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.offset << std::endl; std::cout << " FileSize: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.size << (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl; @@ -179,7 +179,7 @@ void KipProcess::displayHeader() } -void KipProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern) +void nstool::KipProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern) { std::cout << "[Kernel Capabilities]" << std::endl; if (kern.getThreadInfo().isSet()) @@ -207,8 +207,8 @@ void KipProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern) } if (kern.getMemoryMaps().isSet()) { - fnd::List maps = kern.getMemoryMaps().getMemoryMaps(); - fnd::List ioMaps = kern.getMemoryMaps().getIoMemoryMaps(); + std::vector maps = kern.getMemoryMaps().getMemoryMaps(); + std::vector ioMaps = kern.getMemoryMaps().getIoMemoryMaps(); std::cout << " MemoryMaps:" << std::endl; for (size_t i = 0; i < maps.size(); i++) @@ -223,7 +223,7 @@ void KipProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern) } if (kern.getInterupts().isSet()) { - fnd::List interupts = kern.getInterupts().getInteruptList(); + std::vector interupts = kern.getInterupts().getInteruptList(); std::cout << " Interupts Flags:" << std::endl; for (uint32_t i = 0; i < interupts.size(); i++) { diff --git a/src/KipProcess.h b/src/KipProcess.h index 09580e1..cd844c9 100644 --- a/src/KipProcess.h +++ b/src/KipProcess.h @@ -1,12 +1,9 @@ #pragma once -#include -#include -#include -#include -#include +#include "types.h" + #include -#include "common.h" +namespace nstool { class KipProcess { @@ -15,21 +12,23 @@ public: void process(); - void setInputFile(const fnd::SharedPtr& file); + void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); private: const std::string kModuleName = "KipProcess"; - fnd::SharedPtr mFile; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; nn::hac::KernelInitialProcessHeader mHdr; - fnd::Vec mTextBlob, mRoBlob, mDataBlob; + tc::ByteData mTextBlob, mRoBlob, mDataBlob; void importHeader(); void importCodeSegments(); void displayHeader(); void displayKernelCap(const nn::hac::KernelCapabilityControl& kern); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/MetaProcess.cpp b/src/MetaProcess.cpp index 40877bb..a906125 100644 --- a/src/MetaProcess.cpp +++ b/src/MetaProcess.cpp @@ -10,14 +10,14 @@ #include -MetaProcess::MetaProcess() : +nstool::MetaProcess::MetaProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false) { } -void MetaProcess::process() +void nstool::MetaProcess::process() { importMeta(); @@ -27,7 +27,7 @@ void MetaProcess::process() validateAciFromAcid(mMeta.getAccessControlInfo(), mMeta.getAccessControlInfoDesc()); } - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) { // npdm binary displayMetaHeader(mMeta); @@ -39,7 +39,7 @@ void MetaProcess::process() displayKernelCap(mMeta.getAccessControlInfo().getKernelCapabilities()); // acid binary - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) { displayAciDescHdr(mMeta.getAccessControlInfoDesc()); displayFac(mMeta.getAccessControlInfoDesc().getFileSystemAccessControl()); @@ -49,38 +49,38 @@ void MetaProcess::process() } } -void MetaProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::MetaProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void MetaProcess::setKeyCfg(const KeyConfiguration& keycfg) +void nstool::MetaProcess::setKeyCfg(const KeyBag& keycfg) { mKeyCfg = keycfg; } -void MetaProcess::setCliOutputMode(CliOutputMode type) +void nstool::MetaProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void MetaProcess::setVerifyMode(bool verify) +void nstool::MetaProcess::setVerifyMode(bool verify) { mVerify = verify; } -const nn::hac::Meta& MetaProcess::getMeta() const +const nn::hac::Meta& nstool::MetaProcess::getMeta() const { return mMeta; } -void MetaProcess::importMeta() +void nstool::MetaProcess::importMeta() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } scratch.alloc((*mFile)->size()); @@ -89,12 +89,12 @@ void MetaProcess::importMeta() mMeta.fromBytes(scratch.data(), scratch.size()); } -void MetaProcess::validateAcidSignature(const nn::hac::AccessControlInfoDesc& acid, byte_t key_generation) +void nstool::MetaProcess::validateAcidSignature(const nn::hac::AccessControlInfoDesc& acid, byte_t key_generation) { try { fnd::rsa::sRsa2048Key acid_sign_key; if (mKeyCfg.getAcidSignKey(acid_sign_key, key_generation) != true) - throw fnd::Exception(); + throw tc::Exception(); acid.validateSignature(acid_sign_key); } @@ -104,7 +104,7 @@ void MetaProcess::validateAcidSignature(const nn::hac::AccessControlInfoDesc& ac } -void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, const nn::hac::AccessControlInfoDesc& acid) +void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, const nn::hac::AccessControlInfoDesc& acid) { // check Program ID if (acid.getProgramIdRestrict().min > 0 && aci.getProgramId() < acid.getProgramIdRestrict().min) @@ -279,7 +279,7 @@ void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, con } } -void MetaProcess::displayMetaHeader(const nn::hac::Meta& hdr) +void nstool::MetaProcess::displayMetaHeader(const nn::hac::Meta& hdr) { std::cout << "[Meta Header]" << std::endl; std::cout << " ACID KeyGeneration: " << std::dec << (uint32_t)hdr.getAccessControlInfoDescKeyGeneration() << std::endl; @@ -301,13 +301,13 @@ void MetaProcess::displayMetaHeader(const nn::hac::Meta& hdr) } } -void MetaProcess::displayAciHdr(const nn::hac::AccessControlInfo& aci) +void nstool::MetaProcess::displayAciHdr(const nn::hac::AccessControlInfo& aci) { std::cout << "[Access Control Info]" << std::endl; std::cout << " ProgramID: 0x" << std::hex << std::setw(16) << std::setfill('0') << aci.getProgramId() << std::endl; } -void MetaProcess::displayAciDescHdr(const nn::hac::AccessControlInfoDesc& acid) +void nstool::MetaProcess::displayAciDescHdr(const nn::hac::AccessControlInfoDesc& acid) { std::cout << "[Access Control Info Desc]" << std::endl; std::cout << " Flags: " << std::endl; @@ -319,7 +319,7 @@ void MetaProcess::displayAciDescHdr(const nn::hac::AccessControlInfoDesc& acid) std::cout << " Max: 0x" << std::hex << std::setw(16) << std::setfill('0') << acid.getProgramIdRestrict().max << std::endl; } -void MetaProcess::displayFac(const nn::hac::FileSystemAccessControl& fac) +void nstool::MetaProcess::displayFac(const nn::hac::FileSystemAccessControl& fac) { std::cout << "[FS Access Control]" << std::endl; std::cout << " Format Version: " << std::dec << (uint32_t)fac.getFormatVersion() << std::endl; @@ -353,7 +353,7 @@ void MetaProcess::displayFac(const nn::hac::FileSystemAccessControl& fac) // output string info std::cout << nn::hac::FileSystemAccessUtil::getFsAccessFlagAsString(nn::hac::fac::FsAccessFlag(flag)); - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) std::cout << " (bit " << std::dec << (uint32_t)flag << ")"; } std::cout << std::endl; @@ -379,7 +379,7 @@ void MetaProcess::displayFac(const nn::hac::FileSystemAccessControl& fac) } -void MetaProcess::displaySac(const nn::hac::ServiceAccessControl& sac) +void nstool::MetaProcess::displaySac(const nn::hac::ServiceAccessControl& sac) { std::cout << "[Service Access Control]" << std::endl; std::cout << " Service List:" << std::endl; @@ -391,7 +391,7 @@ void MetaProcess::displaySac(const nn::hac::ServiceAccessControl& sac) fnd::SimpleTextOutput::dumpStringList(service_name_list, 60, 4); } -void MetaProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern) +void nstool::MetaProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern) { std::cout << "[Kernel Capabilities]" << std::endl; if (kern.getThreadInfo().isSet()) @@ -435,7 +435,7 @@ void MetaProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern) } if (kern.getInterupts().isSet()) { - fnd::List interupts = kern.getInterupts().getInteruptList(); + std::vector interupts = kern.getInterupts().getInteruptList(); std::cout << " Interupts Flags:" << std::endl; for (uint32_t i = 0; i < interupts.size(); i++) { diff --git a/src/MetaProcess.h b/src/MetaProcess.h index 85618a5..56155a9 100644 --- a/src/MetaProcess.h +++ b/src/MetaProcess.h @@ -1,12 +1,10 @@ #pragma once -#include -#include -#include -#include -#include -#include "KeyConfiguration.h" +#include "types.h" +#include "KeyBag.h" -#include "common.h" +#include + +namespace nstool { class MetaProcess { @@ -15,8 +13,8 @@ public: void process(); - void setInputFile(const fnd::SharedPtr& file); - void setKeyCfg(const KeyConfiguration& keycfg); + void setInputFile(const std::shared_ptr& file); + void setKeyCfg(const KeyBag& keycfg); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -25,8 +23,8 @@ public: private: const std::string kModuleName = "MetaProcess"; - fnd::SharedPtr mFile; - KeyConfiguration mKeyCfg; + std::shared_ptr mFile; + KeyBag mKeyCfg; CliOutputMode mCliOutputMode; bool mVerify; @@ -43,4 +41,6 @@ private: void displayFac(const nn::hac::FileSystemAccessControl& fac); void displaySac(const nn::hac::ServiceAccessControl& sac); void displayKernelCap(const nn::hac::KernelCapabilityControl& kern); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/NacpProcess.cpp b/src/NacpProcess.cpp index 1dbe44e..e454709 100644 --- a/src/NacpProcess.cpp +++ b/src/NacpProcess.cpp @@ -9,48 +9,48 @@ #include -NacpProcess::NacpProcess() : +nstool::NacpProcess::NacpProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode((1 << OUTPUT_BASIC)), mVerify(false) { } -void NacpProcess::process() +void nstool::NacpProcess::process() { importNacp(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) displayNacp(); } -void NacpProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::NacpProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void NacpProcess::setCliOutputMode(CliOutputMode type) +void nstool::NacpProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void NacpProcess::setVerifyMode(bool verify) +void nstool::NacpProcess::setVerifyMode(bool verify) { mVerify = verify; } -const nn::hac::ApplicationControlProperty& NacpProcess::getApplicationControlProperty() const +const nn::hac::ApplicationControlProperty& nstool::NacpProcess::getApplicationControlProperty() const { return mNacp; } -void NacpProcess::importNacp() +void nstool::NacpProcess::importNacp() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } scratch.alloc((*mFile)->size()); @@ -59,7 +59,7 @@ void NacpProcess::importNacp() mNacp.fromBytes(scratch.data(), scratch.size()); } -void NacpProcess::displayNacp() +void nstool::NacpProcess::displayNacp() { std::cout << "[ApplicationControlProperty]" << std::endl; @@ -74,7 +74,7 @@ void NacpProcess::displayNacp() std::cout << " Publisher: " << itr->publisher << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " Title: None" << std::endl; } @@ -84,25 +84,25 @@ void NacpProcess::displayNacp() { std::cout << " ISBN: " << mNacp.getIsbn() << std::endl; } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " ISBN: (NotSet)" << std::endl; } // StartupUserAccount - if (mNacp.getStartupUserAccount() != nn::hac::nacp::StartupUserAccount::None || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getStartupUserAccount() != nn::hac::nacp::StartupUserAccount::None || mCliOutputMode.show_extended_info) { std::cout << " StartupUserAccount: " << nn::hac::ApplicationControlPropertyUtil::getStartupUserAccountAsString(mNacp.getStartupUserAccount()) << std::endl; } // UserAccountSwitchLock - if (mNacp.getUserAccountSwitchLock() != nn::hac::nacp::UserAccountSwitchLock::Disable || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getUserAccountSwitchLock() != nn::hac::nacp::UserAccountSwitchLock::Disable || mCliOutputMode.show_extended_info) { std::cout << " UserAccountSwitchLock: " << nn::hac::ApplicationControlPropertyUtil::getUserAccountSwitchLockAsString(mNacp.getUserAccountSwitchLock()) << std::endl; } // AddOnContentRegistrationType - if (mNacp.getAddOnContentRegistrationType() != nn::hac::nacp::AddOnContentRegistrationType::AllOnLaunch || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getAddOnContentRegistrationType() != nn::hac::nacp::AddOnContentRegistrationType::AllOnLaunch || mCliOutputMode.show_extended_info) { std::cout << " AddOnContentRegistrationType: " << nn::hac::ApplicationControlPropertyUtil::getAddOnContentRegistrationTypeAsString(mNacp.getAddOnContentRegistrationType()) << std::endl; } @@ -116,7 +116,7 @@ void NacpProcess::displayNacp() std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getAttributeFlagAsString(*itr) << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " Attribute: None" << std::endl; } @@ -130,7 +130,7 @@ void NacpProcess::displayNacp() std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getLanguageAsString(*itr) << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " SupportedLanguage: None" << std::endl; } @@ -144,37 +144,37 @@ void NacpProcess::displayNacp() std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getParentalControlFlagAsString(*itr) << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " ParentalControl: None" << std::endl; } // Screenshot - if (mNacp.getScreenshot() != nn::hac::nacp::Screenshot::Allow || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getScreenshot() != nn::hac::nacp::Screenshot::Allow || mCliOutputMode.show_extended_info) { std::cout << " Screenshot: " << nn::hac::ApplicationControlPropertyUtil::getScreenshotAsString(mNacp.getScreenshot()) << std::endl; } // VideoCapture - if (mNacp.getVideoCapture() != nn::hac::nacp::VideoCapture::Disable || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getVideoCapture() != nn::hac::nacp::VideoCapture::Disable || mCliOutputMode.show_extended_info) { std::cout << " VideoCapture: " << nn::hac::ApplicationControlPropertyUtil::getVideoCaptureAsString(mNacp.getVideoCapture()) << std::endl; } // DataLossConfirmation - if (mNacp.getDataLossConfirmation() != nn::hac::nacp::DataLossConfirmation::None || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getDataLossConfirmation() != nn::hac::nacp::DataLossConfirmation::None || mCliOutputMode.show_extended_info) { std::cout << " DataLossConfirmation: " << nn::hac::ApplicationControlPropertyUtil::getDataLossConfirmationAsString(mNacp.getDataLossConfirmation()) << std::endl; } // PlayLogPolicy - if (mNacp.getPlayLogPolicy() != nn::hac::nacp::PlayLogPolicy::All || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getPlayLogPolicy() != nn::hac::nacp::PlayLogPolicy::All || mCliOutputMode.show_extended_info) { std::cout << " PlayLogPolicy: " << nn::hac::ApplicationControlPropertyUtil::getPlayLogPolicyAsString(mNacp.getPlayLogPolicy()) << std::endl; } // PresenceGroupId - if (mNacp.getPresenceGroupId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getPresenceGroupId() != 0 || mCliOutputMode.show_extended_info) { std::cout << " PresenceGroupId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getPresenceGroupId() << std::endl; } @@ -190,7 +190,7 @@ void NacpProcess::displayNacp() std::cout << " Age: " << std::dec << (uint32_t)itr->age << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " RatingAge: None" << std::endl; } @@ -200,49 +200,49 @@ void NacpProcess::displayNacp() { std::cout << " DisplayVersion: " << mNacp.getDisplayVersion() << std::endl; } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " DisplayVersion: (NotSet)" << std::endl; } // AddOnContentBaseId - if (mNacp.getAddOnContentBaseId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getAddOnContentBaseId() != 0 || mCliOutputMode.show_extended_info) { std::cout << " AddOnContentBaseId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getAddOnContentBaseId() << std::endl; } // SaveDataOwnerId - if (mNacp.getSaveDataOwnerId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getSaveDataOwnerId() != 0 || mCliOutputMode.show_extended_info) { std::cout << " SaveDataOwnerId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getSaveDataOwnerId() << std::endl; } // UserAccountSaveDataSize - if (mNacp.getUserAccountSaveDataSize().size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getUserAccountSaveDataSize().size != 0 || mCliOutputMode.show_extended_info) { std::cout << " UserAccountSaveDataSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataSize().size) << std::endl; } // UserAccountSaveDataJournalSize - if (mNacp.getUserAccountSaveDataSize().journal_size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getUserAccountSaveDataSize().journal_size != 0 || mCliOutputMode.show_extended_info) { std::cout << " UserAccountSaveDataJournalSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataSize().journal_size) << std::endl; } // DeviceSaveDataSize - if (mNacp.getDeviceSaveDataSize().size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getDeviceSaveDataSize().size != 0 || mCliOutputMode.show_extended_info) { std::cout << " DeviceSaveDataSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataSize().size) << std::endl; } // DeviceSaveDataJournalSize - if (mNacp.getDeviceSaveDataSize().journal_size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getDeviceSaveDataSize().journal_size != 0 || mCliOutputMode.show_extended_info) { std::cout << " DeviceSaveDataJournalSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataSize().journal_size) << std::endl; } // BcatDeliveryCacheStorageSize - if (mNacp.getBcatDeliveryCacheStorageSize() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getBcatDeliveryCacheStorageSize() != 0 || mCliOutputMode.show_extended_info) { std::cout << " BcatDeliveryCacheStorageSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getBcatDeliveryCacheStorageSize()) << std::endl; } @@ -252,7 +252,7 @@ void NacpProcess::displayNacp() { std::cout << " ApplicationErrorCodeCategory: " << mNacp.getApplicationErrorCodeCategory() << std::endl; } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " ApplicationErrorCodeCategory: (NotSet)" << std::endl; } @@ -266,49 +266,49 @@ void NacpProcess::displayNacp() std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << *itr << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " LocalCommunicationId: None" << std::endl; } // LogoType - //if (mNacp.getLogoType() != nn::hac::nacp::LogoType::Nintendo || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + //if (mNacp.getLogoType() != nn::hac::nacp::LogoType::Nintendo || mCliOutputMode.show_extended_info) //{ std::cout << " LogoType: " << nn::hac::ApplicationControlPropertyUtil::getLogoTypeAsString(mNacp.getLogoType()) << std::endl; //} // LogoHandling - if (mNacp.getLogoHandling() != nn::hac::nacp::LogoHandling::Auto || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getLogoHandling() != nn::hac::nacp::LogoHandling::Auto || mCliOutputMode.show_extended_info) { std::cout << " LogoHandling: " << nn::hac::ApplicationControlPropertyUtil::getLogoHandlingAsString(mNacp.getLogoHandling()) << std::endl; } // RuntimeAddOnContentInstall - if (mNacp.getRuntimeAddOnContentInstall() != nn::hac::nacp::RuntimeAddOnContentInstall::Deny || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getRuntimeAddOnContentInstall() != nn::hac::nacp::RuntimeAddOnContentInstall::Deny || mCliOutputMode.show_extended_info) { std::cout << " RuntimeAddOnContentInstall: " << nn::hac::ApplicationControlPropertyUtil::getRuntimeAddOnContentInstallAsString(mNacp.getRuntimeAddOnContentInstall()) << std::endl; } // RuntimeParameterDelivery - if (mNacp.getRuntimeParameterDelivery() != nn::hac::nacp::RuntimeParameterDelivery::Always || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getRuntimeParameterDelivery() != nn::hac::nacp::RuntimeParameterDelivery::Always || mCliOutputMode.show_extended_info) { std::cout << " RuntimeParameterDelivery: " << nn::hac::ApplicationControlPropertyUtil::getRuntimeParameterDeliveryAsString(mNacp.getRuntimeParameterDelivery()) << std::endl; } // CrashReport - if (mNacp.getCrashReport() != nn::hac::nacp::CrashReport::Deny || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getCrashReport() != nn::hac::nacp::CrashReport::Deny || mCliOutputMode.show_extended_info) { std::cout << " CrashReport: " << nn::hac::ApplicationControlPropertyUtil::getCrashReportAsString(mNacp.getCrashReport()) << std::endl; } // Hdcp - if (mNacp.getHdcp() != nn::hac::nacp::Hdcp::None || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getHdcp() != nn::hac::nacp::Hdcp::None || mCliOutputMode.show_extended_info) { std::cout << " Hdcp: " << nn::hac::ApplicationControlPropertyUtil::getHdcpAsString(mNacp.getHdcp()) << std::endl; } // SeedForPsuedoDeviceId - if (mNacp.getSeedForPsuedoDeviceId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getSeedForPsuedoDeviceId() != 0 || mCliOutputMode.show_extended_info) { std::cout << " SeedForPsuedoDeviceId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getSeedForPsuedoDeviceId() << std::endl; } @@ -318,7 +318,7 @@ void NacpProcess::displayNacp() { std::cout << " BcatPassphase: " << mNacp.getBcatPassphase() << std::endl; } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " BcatPassphase: (NotSet)" << std::endl; } @@ -332,61 +332,61 @@ void NacpProcess::displayNacp() std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getStartupUserAccountOptionFlagAsString(*itr) << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " StartupUserAccountOption: None" << std::endl; } // UserAccountSaveDataSizeMax - if (mNacp.getUserAccountSaveDataMax().size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getUserAccountSaveDataMax().size != 0 || mCliOutputMode.show_extended_info) { std::cout << " UserAccountSaveDataSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataMax().size) << std::endl; } // UserAccountSaveDataJournalSizeMax - if (mNacp.getUserAccountSaveDataMax().journal_size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getUserAccountSaveDataMax().journal_size != 0 || mCliOutputMode.show_extended_info) { std::cout << " UserAccountSaveDataJournalSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataMax().journal_size) << std::endl; } // DeviceSaveDataSizeMax - if (mNacp.getDeviceSaveDataMax().size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getDeviceSaveDataMax().size != 0 || mCliOutputMode.show_extended_info) { std::cout << " DeviceSaveDataSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataMax().size) << std::endl; } // DeviceSaveDataJournalSizeMax - if (mNacp.getDeviceSaveDataMax().journal_size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getDeviceSaveDataMax().journal_size != 0 || mCliOutputMode.show_extended_info) { std::cout << " DeviceSaveDataJournalSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataMax().journal_size) << std::endl; } // TemporaryStorageSize - if (mNacp.getTemporaryStorageSize() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getTemporaryStorageSize() != 0 || mCliOutputMode.show_extended_info) { std::cout << " TemporaryStorageSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getTemporaryStorageSize()) << std::endl; } // CacheStorageSize - if (mNacp.getCacheStorageSize().size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getCacheStorageSize().size != 0 || mCliOutputMode.show_extended_info) { std::cout << " CacheStorageSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageSize().size) << std::endl; } // CacheStorageJournalSize - if (mNacp.getCacheStorageSize().journal_size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getCacheStorageSize().journal_size != 0 || mCliOutputMode.show_extended_info) { std::cout << " CacheStorageJournalSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageSize().journal_size) << std::endl; } // CacheStorageDataAndJournalSizeMax - if (mNacp.getCacheStorageDataAndJournalSizeMax() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getCacheStorageDataAndJournalSizeMax() != 0 || mCliOutputMode.show_extended_info) { std::cout << " CacheStorageDataAndJournalSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageDataAndJournalSizeMax()) << std::endl; } // CacheStorageIndexMax - if (mNacp.getCacheStorageIndexMax() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getCacheStorageIndexMax() != 0 || mCliOutputMode.show_extended_info) { std::cout << " CacheStorageIndexMax: 0x" << std::hex << std::setw(4) << std::setfill('0') << mNacp.getCacheStorageIndexMax() << std::endl; } @@ -400,13 +400,13 @@ void NacpProcess::displayNacp() std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << *itr << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " PlayLogQueryableApplicationId: None" << std::endl; } // PlayLogQueryCapability - if (mNacp.getPlayLogQueryCapability() != nn::hac::nacp::PlayLogQueryCapability::None || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getPlayLogQueryCapability() != nn::hac::nacp::PlayLogQueryCapability::None || mCliOutputMode.show_extended_info) { std::cout << " PlayLogQueryCapability: " << nn::hac::ApplicationControlPropertyUtil::getPlayLogQueryCapabilityAsString(mNacp.getPlayLogQueryCapability()) << std::endl; } @@ -420,13 +420,13 @@ void NacpProcess::displayNacp() std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getRepairFlagAsString(*itr) << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " Repair: None" << std::endl; } // ProgramIndex - if (mNacp.getProgramIndex() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getProgramIndex() != 0 || mCliOutputMode.show_extended_info) { std::cout << " ProgramIndex: 0x" << std::hex << std::setw(2) << std::setfill('0') << (uint32_t)mNacp.getProgramIndex() << std::endl; } @@ -440,7 +440,7 @@ void NacpProcess::displayNacp() std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getRequiredNetworkServiceLicenseOnLaunchFlagAsString(*itr) << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " RequiredNetworkServiceLicenseOnLaunch: None" << std::endl; } @@ -456,7 +456,7 @@ void NacpProcess::displayNacp() std::cout << " GroupId: 0x" << std::hex << std::setw(16) << std::setfill('0') << detect_config.send_data_configuration.group_id << std::endl; std::cout << " Key: " << fnd::SimpleTextOutput::arrayToString(detect_config.send_data_configuration.key, nn::hac::nacp::kNeighborDetectionGroupConfigurationKeyLength, false, "") << std::endl; } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " SendGroupConfig: None" << std::endl; } @@ -472,18 +472,18 @@ void NacpProcess::displayNacp() std::cout << " Key: " << fnd::SimpleTextOutput::arrayToString(detect_config.receivable_data_configuration[i].key, nn::hac::nacp::kNeighborDetectionGroupConfigurationKeyLength, false, "") << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " ReceivableGroupConfig: None" << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " NeighborDetectionClientConfiguration: None" << std::endl; } // JitConfiguration - if (mNacp.getJitConfiguration().is_enabled || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getJitConfiguration().is_enabled || mCliOutputMode.show_extended_info) { std::cout << " JitConfiguration:" << std::endl; std::cout << " IsEnabled: " << std::boolalpha << mNacp.getJitConfiguration().is_enabled << std::endl; @@ -491,19 +491,19 @@ void NacpProcess::displayNacp() } // PlayReportPermission - if (mNacp.getPlayReportPermission() != nn::hac::nacp::PlayReportPermission::None || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getPlayReportPermission() != nn::hac::nacp::PlayReportPermission::None || mCliOutputMode.show_extended_info) { std::cout << " PlayReportPermission: " << nn::hac::ApplicationControlPropertyUtil::getPlayReportPermissionAsString(mNacp.getPlayReportPermission()) << std::endl; } // CrashScreenshotForProd - if (mNacp.getCrashScreenshotForProd() != nn::hac::nacp::CrashScreenshotForProd::Deny || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getCrashScreenshotForProd() != nn::hac::nacp::CrashScreenshotForProd::Deny || mCliOutputMode.show_extended_info) { std::cout << " CrashScreenshotForProd: " << nn::hac::ApplicationControlPropertyUtil::getCrashScreenshotForProdAsString(mNacp.getCrashScreenshotForProd()) << std::endl; } // CrashScreenshotForDev - if (mNacp.getCrashScreenshotForDev() != nn::hac::nacp::CrashScreenshotForDev::Deny || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mNacp.getCrashScreenshotForDev() != nn::hac::nacp::CrashScreenshotForDev::Deny || mCliOutputMode.show_extended_info) { std::cout << " CrashScreenshotForDev: " << nn::hac::ApplicationControlPropertyUtil::getCrashScreenshotForDevAsString(mNacp.getCrashScreenshotForDev()) << std::endl; } @@ -518,7 +518,7 @@ void NacpProcess::displayNacp() std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << *itr << std::endl; } } - else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + else if (mCliOutputMode.show_extended_info) { std::cout << " AccessibleLaunchRequiredVersion: None" << std::endl; } diff --git a/src/NacpProcess.h b/src/NacpProcess.h index 7084d81..e458124 100644 --- a/src/NacpProcess.h +++ b/src/NacpProcess.h @@ -1,11 +1,9 @@ #pragma once -#include -#include -#include -#include +#include "types.h" + #include -#include "common.h" +namespace nstool { class NacpProcess { @@ -14,7 +12,7 @@ public: void process(); - void setInputFile(const fnd::SharedPtr& file); + void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -23,7 +21,7 @@ public: private: const std::string kModuleName = "NacpProcess"; - fnd::SharedPtr mFile; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; @@ -31,4 +29,6 @@ private: void importNacp(); void displayNacp(); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index 6ed73d3..e0019e3 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -18,9 +18,9 @@ #include #include -NcaProcess::NcaProcess() : +nstool::NcaProcess::NcaProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false), mListFs(false) { @@ -30,7 +30,7 @@ NcaProcess::NcaProcess() : } } -void NcaProcess::process() +void nstool::NcaProcess::process() { // import header importHeader(); @@ -46,67 +46,67 @@ void NcaProcess::process() validateNcaSignatures(); // display header - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) displayHeader(); // process partition processPartitions(); } -void NcaProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::NcaProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void NcaProcess::setKeyCfg(const KeyConfiguration& keycfg) +void nstool::NcaProcess::setKeyCfg(const KeyBag& keycfg) { mKeyCfg = keycfg; } -void NcaProcess::setCliOutputMode(CliOutputMode type) +void nstool::NcaProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void NcaProcess::setVerifyMode(bool verify) +void nstool::NcaProcess::setVerifyMode(bool verify) { mVerify = verify; } -void NcaProcess::setPartition0ExtractPath(const std::string& path) +void nstool::NcaProcess::setPartition0ExtractPath(const std::string& path) { mPartitionPath[0].path = path; mPartitionPath[0].doExtract = true; } -void NcaProcess::setPartition1ExtractPath(const std::string& path) +void nstool::NcaProcess::setPartition1ExtractPath(const std::string& path) { mPartitionPath[1].path = path; mPartitionPath[1].doExtract = true; } -void NcaProcess::setPartition2ExtractPath(const std::string& path) +void nstool::NcaProcess::setPartition2ExtractPath(const std::string& path) { mPartitionPath[2].path = path; mPartitionPath[2].doExtract = true; } -void NcaProcess::setPartition3ExtractPath(const std::string& path) +void nstool::NcaProcess::setPartition3ExtractPath(const std::string& path) { mPartitionPath[3].path = path; mPartitionPath[3].doExtract = true; } -void NcaProcess::setListFs(bool list_fs) +void nstool::NcaProcess::setListFs(bool list_fs) { mListFs = list_fs; } -void NcaProcess::importHeader() +void nstool::NcaProcess::importHeader() { if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } // read header block @@ -124,10 +124,10 @@ void NcaProcess::importHeader() mHdr.fromBytes((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader)); } -void NcaProcess::generateNcaBodyEncryptionKeys() +void nstool::NcaProcess::generateNcaBodyEncryptionKeys() { // create zeros key - fnd::aes::sAes128Key zero_aesctr_key; + KeyBag::aes128_key_t zero_aesctr_key; memset(zero_aesctr_key.key, 0, sizeof(zero_aesctr_key)); // get key data from header @@ -136,8 +136,8 @@ void NcaProcess::generateNcaBodyEncryptionKeys() // process key area sKeys::sKeyAreaKey kak; - fnd::aes::sAes128Key key_area_enc_key; - const fnd::aes::sAes128Key* key_area = (const fnd::aes::sAes128Key*) mHdr.getKeyArea(); + KeyBag::aes128_key_t key_area_enc_key; + const KeyBag::aes128_key_t* key_area = (const KeyBag::aes128_key_t*) mHdr.getKeyArea(); for (size_t i = 0; i < nn::hac::nca::kKeyAreaKeyNum; i++) { @@ -161,7 +161,7 @@ void NcaProcess::generateNcaBodyEncryptionKeys() { kak.decrypted = false; } - mContentKey.kak_list.addElement(kak); + mContentKey.kak_list.push_back(kak); } } @@ -171,14 +171,14 @@ void NcaProcess::generateNcaBodyEncryptionKeys() // if this has a rights id, the key needs to be sourced from a ticket if (mHdr.hasRightsId() == true) { - fnd::aes::sAes128Key tmp_key; + KeyBag::aes128_key_t tmp_key; if (mKeyCfg.getNcaExternalContentKey(mHdr.getRightsId(), tmp_key) == true) { mContentKey.aes_ctr = tmp_key; } else if (mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserTitleKey, tmp_key) == true) { - fnd::aes::sAes128Key common_key; + KeyBag::aes128_key_t common_key; if (mKeyCfg.getETicketCommonKey(masterkey_rev, common_key) == true) { nn::hac::AesKeygen::generateKey(tmp_key.key, tmp_key.key, common_key.key); @@ -189,7 +189,7 @@ void NcaProcess::generateNcaBodyEncryptionKeys() // otherwise decrypt key area else { - fnd::aes::sAes128Key kak_aes_ctr = zero_aesctr_key; + KeyBag::aes128_key_t kak_aes_ctr = zero_aesctr_key; for (size_t i = 0; i < mContentKey.kak_list.size(); i++) { if (mContentKey.kak_list[i].index == nn::hac::nca::KEY_AESCTR && mContentKey.kak_list[i].decrypted) @@ -205,26 +205,26 @@ void NcaProcess::generateNcaBodyEncryptionKeys() } // if the keys weren't generated, check if the keys were supplied by the user - if (mContentKey.aes_ctr.isSet == false) + if (mContentKey.aes_ctr.isNull()) { - if (mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserBodyKey, mContentKey.aes_ctr.var) == true) + if (mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserBodyKey, mContentKey.aes_ctr.get()) == true) mContentKey.aes_ctr.isSet = true; } - if (_HAS_BIT(mCliOutputMode, OUTPUT_KEY_DATA)) + if (mCliOutputMode.show_keydata) { - if (mContentKey.aes_ctr.isSet) + if (mContentKey.aes_ctr.isSet()) { std::cout << "[NCA Content Key]" << std::endl; - std::cout << " AES-CTR Key: " << fnd::SimpleTextOutput::arrayToString(mContentKey.aes_ctr.var.key, sizeof(mContentKey.aes_ctr.var), true, ":") << std::endl; + std::cout << " AES-CTR Key: " << fnd::SimpleTextOutput::arrayToString(mContentKey.aes_ctr.get().key, sizeof(mContentKey.aes_ctr.get()), true, ":") << std::endl; } } } -void NcaProcess::generatePartitionConfiguration() +void nstool::NcaProcess::generatePartitionConfiguration() { std::stringstream error; @@ -244,7 +244,7 @@ void NcaProcess::generatePartitionConfiguration() { error.clear(); error << "NCA FS Header [" << partition.header_index << "] Hash: FAIL \n"; - throw fnd::Exception(kModuleName, error.str()); + throw tc::Exception(kModuleName, error.str()); } @@ -252,7 +252,7 @@ void NcaProcess::generatePartitionConfiguration() { error.clear(); error << "NCA FS Header [" << partition.header_index << "] Version(" << fs_header.version.get() << "): UNSUPPORTED"; - throw fnd::Exception(kModuleName, error.str()); + throw tc::Exception(kModuleName, error.str()); } // setup AES-CTR @@ -269,9 +269,9 @@ void NcaProcess::generatePartitionConfiguration() { // info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_SHA256); nn::hac::HierarchicalSha256Header hdr; - fnd::List hash_layers; + std::vector hash_layers; fnd::LayeredIntegrityMetadata::sLayer data_layer; - fnd::List master_hash_list; + std::vector master_hash_list; // import raw data hdr.fromBytes(fs_header.hash_info, nn::hac::nca::kHashInfoLen); @@ -287,10 +287,10 @@ void NcaProcess::generatePartitionConfiguration() } else { - hash_layers.addElement(layer); + hash_layers.push_back(layer); } } - master_hash_list.addElement(hdr.getMasterHash()); + master_hash_list.push_back(hdr.getMasterHash()); // write data into metadata info.layered_intergrity_metadata.setAlignHashToBlock(false); @@ -302,9 +302,9 @@ void NcaProcess::generatePartitionConfiguration() { // info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_INTEGRITY); nn::hac::HierarchicalIntegrityHeader hdr; - fnd::List hash_layers; + std::vector hash_layers; fnd::LayeredIntegrityMetadata::sLayer data_layer; - fnd::List master_hash_list; + std::vector master_hash_list; hdr.fromBytes(fs_header.hash_info, nn::hac::nca::kHashInfoLen); for (size_t i = 0; i < hdr.getLayerInfo().size(); i++) @@ -312,14 +312,14 @@ void NcaProcess::generatePartitionConfiguration() fnd::LayeredIntegrityMetadata::sLayer layer; layer.offset = hdr.getLayerInfo()[i].offset; layer.size = hdr.getLayerInfo()[i].size; - layer.block_size = _BIT(hdr.getLayerInfo()[i].block_size); + layer.block_size = (1 << hdr.getLayerInfo()[i].block_size); if (i + 1 == hdr.getLayerInfo().size()) { data_layer = layer; } else { - hash_layers.addElement(layer); + hash_layers.push_back(layer); } } @@ -342,7 +342,7 @@ void NcaProcess::generatePartitionConfiguration() default: error.clear(); error << "FormatType(" << nn::hac::ContentArchiveUtil::getFormatTypeAsString(info.format_type) << "): UNKNOWN"; - throw fnd::Exception(kModuleName, error.str()); + throw tc::Exception(kModuleName, error.str()); } // create reader based on encryption type0 @@ -352,21 +352,21 @@ void NcaProcess::generatePartitionConfiguration() } else if (info.enc_type == nn::hac::nca::EncryptionType::AesCtr) { - if (mContentKey.aes_ctr.isSet == false) - throw fnd::Exception(kModuleName, "AES-CTR Key was not determined"); - info.reader = new fnd::OffsetAdjustedIFile(new fnd::AesCtrWrappedIFile(mFile, mContentKey.aes_ctr.var, info.aes_ctr), info.offset, info.size); + if (mContentKey.aes_ctr.isNull()) + throw tc::Exception(kModuleName, "AES-CTR Key was not determined"); + info.reader = new fnd::OffsetAdjustedIFile(new fnd::AesCtrWrappedIFile(mFile, mContentKey.aes_ctr.get(), info.aes_ctr), info.offset, info.size); } else if (info.enc_type == nn::hac::nca::EncryptionType::AesXts || info.enc_type == nn::hac::nca::EncryptionType::AesCtrEx) { error.clear(); error << "EncryptionType(" << nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type) << "): UNSUPPORTED"; - throw fnd::Exception(kModuleName, error.str()); + throw tc::Exception(kModuleName, error.str()); } else { error.clear(); error << "EncryptionType(" << nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type) << "): UNKNOWN"; - throw fnd::Exception(kModuleName, error.str()); + throw tc::Exception(kModuleName, error.str()); } // filter out unrecognised hash types, and hash based readers @@ -378,17 +378,17 @@ void NcaProcess::generatePartitionConfiguration() { error.clear(); error << "HashType(" << nn::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type) << "): UNKNOWN"; - throw fnd::Exception(kModuleName, error.str()); + throw tc::Exception(kModuleName, error.str()); } } - catch (const fnd::Exception& e) + catch (const tc::Exception& e) { info.fail_reason = std::string(e.error()); } } } -void NcaProcess::validateNcaSignatures() +void nstool::NcaProcess::validateNcaSignatures() { // validate signature[0] fnd::rsa::sRsa2048Key sign0_key; @@ -445,7 +445,7 @@ void NcaProcess::validateNcaSignatures() } } -void NcaProcess::displayHeader() +void nstool::NcaProcess::displayHeader() { std::cout << "[NCA Header]" << std::endl; std::cout << " Format Type: " << nn::hac::ContentArchiveUtil::getFormatHeaderVersionAsString((nn::hac::nca::HeaderFormatVersion)mHdr.getFormatVersion()) << std::endl; @@ -463,7 +463,7 @@ void NcaProcess::displayHeader() std::cout << " RightsId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getRightsId(), nn::hac::nca::kRightsIdLen, true, "") << std::endl; } - if (mContentKey.kak_list.size() > 0 && _HAS_BIT(mCliOutputMode, OUTPUT_KEY_DATA)) + if (mContentKey.kak_list.size() > 0 && mCliOutputMode.show_keydata) { std::cout << " Key Area:" << std::endl; std::cout << " <--------------------------------------------------------------------------------------------------------->" << std::endl; @@ -486,7 +486,7 @@ void NcaProcess::displayHeader() std::cout << " <--------------------------------------------------------------------------------------------------------->" << std::endl; } - if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) + if (mCliOutputMode.show_layout) { std::cout << " Partitions:" << std::endl; for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++) @@ -551,7 +551,7 @@ void NcaProcess::displayHeader() } -void NcaProcess::processPartitions() +void nstool::NcaProcess::processPartitions() { for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++) { @@ -609,13 +609,13 @@ void NcaProcess::processPartitions() romfs.setExtractPath(mPartitionPath[index].path); romfs.process(); } - } catch (const fnd::Exception& e) { + } catch (const tc::Exception& e) { std::cout << "[WARNING] NCA Partition " << std::dec << index << " not readable (" << e.error() << ")." << std::endl; } } } -const char* NcaProcess::getContentTypeForMountStr(nn::hac::nca::ContentType cont_type) const +const char* nstool::NcaProcess::getContentTypeForMountStr(nn::hac::nca::ContentType cont_type) const { const char* str = nullptr; diff --git a/src/NcaProcess.h b/src/NcaProcess.h index d467077..0f7538b 100644 --- a/src/NcaProcess.h +++ b/src/NcaProcess.h @@ -1,14 +1,10 @@ #pragma once -#include -#include -#include -#include -#include +#include "types.h" +#include "KeyBag.h" + #include -#include "KeyConfiguration.h" - -#include "common.h" +namespace nstool { class NcaProcess { @@ -18,8 +14,8 @@ public: void process(); // generic - void setInputFile(const fnd::SharedPtr& file); - void setKeyCfg(const KeyConfiguration& keycfg); + void setInputFile(const std::shared_ptr& file); + void setKeyCfg(const KeyBag& keycfg); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -35,8 +31,8 @@ private: const std::string kNpdmExefsPath = "main.npdm"; // user options - fnd::SharedPtr mFile; - KeyConfiguration mKeyCfg; + std::shared_ptr mFile; + KeyBag mKeyCfg; CliOutputMode mCliOutputMode; bool mVerify; @@ -60,8 +56,8 @@ private: { byte_t index; bool decrypted; - fnd::aes::sAes128Key enc; - fnd::aes::sAes128Key dec; + KeyBag::aes128_key_t enc; + KeyBag::aes128_key_t dec; void operator=(const sKeyAreaKey& other) { @@ -84,14 +80,14 @@ private: return !(*this == other); } }; - fnd::List kak_list; + std::vector kak_list; - sOptional aes_ctr; + tc::Optional aes_ctr; } mContentKey; struct sPartitionInfo { - fnd::SharedPtr reader; + std::shared_ptr reader; std::string fail_reason; size_t offset; size_t size; @@ -112,4 +108,6 @@ private: void processPartitions(); const char* getContentTypeForMountStr(nn::hac::nca::ContentType cont_type) const; -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/NroProcess.cpp b/src/NroProcess.cpp index 465c1cc..c76d845 100644 --- a/src/NroProcess.cpp +++ b/src/NroProcess.cpp @@ -7,19 +7,19 @@ #include #include "NroProcess.h" -NroProcess::NroProcess(): +nstool::NroProcess::NroProcess(): mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false) { } -void NroProcess::process() +void nstool::NroProcess::process() { importHeader(); importCodeSegments(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) displayHeader(); processRoMeta(); @@ -28,73 +28,73 @@ void NroProcess::process() mAssetProc.process(); } -void NroProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::NroProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void NroProcess::setCliOutputMode(CliOutputMode type) +void nstool::NroProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void NroProcess::setVerifyMode(bool verify) +void nstool::NroProcess::setVerifyMode(bool verify) { mVerify = verify; } -void NroProcess::setIs64BitInstruction(bool flag) +void nstool::NroProcess::setIs64BitInstruction(bool flag) { mRoMeta.setIs64BitInstruction(flag); } -void NroProcess::setListApi(bool listApi) +void nstool::NroProcess::setListApi(bool listApi) { mRoMeta.setListApi(listApi); } -void NroProcess::setListSymbols(bool listSymbols) +void nstool::NroProcess::setListSymbols(bool listSymbols) { mRoMeta.setListSymbols(listSymbols); } -void NroProcess::setAssetListFs(bool list) +void nstool::NroProcess::setAssetListFs(bool list) { mAssetProc.setListFs(list); } -void NroProcess::setAssetIconExtractPath(const std::string& path) +void nstool::NroProcess::setAssetIconExtractPath(const std::string& path) { mAssetProc.setIconExtractPath(path); } -void NroProcess::setAssetNacpExtractPath(const std::string& path) +void nstool::NroProcess::setAssetNacpExtractPath(const std::string& path) { mAssetProc.setNacpExtractPath(path); } -void NroProcess::setAssetRomfsExtractPath(const std::string& path) +void nstool::NroProcess::setAssetRomfsExtractPath(const std::string& path) { mAssetProc.setRomfsExtractPath(path); } -const RoMetadataProcess& NroProcess::getRoMetadataProcess() const +const RoMetadataProcess& nstool::NroProcess::getRoMetadataProcess() const { return mRoMeta; } -void NroProcess::importHeader() +void nstool::NroProcess::importHeader() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } if ((*mFile)->size() < sizeof(nn::hac::sNroHeader)) { - throw fnd::Exception(kModuleName, "Corrupt NRO: file too small"); + throw tc::Exception(kModuleName, "Corrupt NRO: file too small"); } scratch.alloc(sizeof(nn::hac::sNroHeader)); @@ -104,7 +104,7 @@ void NroProcess::importHeader() // setup homebrew extension nn::hac::sNroHeader* raw_hdr = (nn::hac::sNroHeader*)scratch.data(); - if (((le_uint64_t*)raw_hdr->reserved_0)->get() == nn::hac::nro::kNroHomebrewStructMagic && (*mFile)->size() > mHdr.getNroSize()) + if (((tc::bn::le64*)raw_hdr->reserved_0)->get() == nn::hac::nro::kNroHomebrewStructMagic && (*mFile)->size() > mHdr.getNroSize()) { mIsHomebrewNro = true; mAssetProc.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getNroSize(), (*mFile)->size() - mHdr.getNroSize())); @@ -115,7 +115,7 @@ void NroProcess::importHeader() mIsHomebrewNro = false; } -void NroProcess::importCodeSegments() +void nstool::NroProcess::importCodeSegments() { mTextBlob.alloc(mHdr.getTextInfo().size); (*mFile)->read(mTextBlob.data(), mHdr.getTextInfo().memory_offset, mTextBlob.size()); @@ -125,7 +125,7 @@ void NroProcess::importCodeSegments() (*mFile)->read(mDataBlob.data(), mHdr.getDataInfo().memory_offset, mDataBlob.size()); } -void NroProcess::displayHeader() +void nstool::NroProcess::displayHeader() { std::cout << "[NRO Header]" << std::endl; std::cout << " RoCrt: " << std::endl; @@ -140,7 +140,7 @@ void NroProcess::displayHeader() std::cout << " .ro:" << std::endl; std::cout << " Offset: 0x" << std::hex << mHdr.getRoInfo().memory_offset << std::endl; std::cout << " Size: 0x" << std::hex << mHdr.getRoInfo().size << std::endl; - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) { std::cout << " .api_info:" << std::endl; std::cout << " Offset: 0x" << std::hex << mHdr.getRoEmbeddedInfo().memory_offset << std::endl; @@ -159,7 +159,7 @@ void NroProcess::displayHeader() std::cout << " Size: 0x" << std::hex << mHdr.getBssSize() << std::endl; } -void NroProcess::processRoMeta() +void nstool::NroProcess::processRoMeta() { if (mRoBlob.size()) { diff --git a/src/NroProcess.h b/src/NroProcess.h index 2ef222f..420b58d 100644 --- a/src/NroProcess.h +++ b/src/NroProcess.h @@ -1,15 +1,12 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include +#include "types.h" +#include "RoMetadataProcess.h" #include "AssetProcess.h" -#include "common.h" -#include "RoMetadataProcess.h" +#include +#include + +namespace nstool { class NroProcess { @@ -18,7 +15,7 @@ public: void process(); - void setInputFile(const fnd::SharedPtr& file); + void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -36,12 +33,12 @@ public: private: const std::string kModuleName = "NroProcess"; - fnd::SharedPtr mFile; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; nn::hac::NroHeader mHdr; - fnd::Vec mTextBlob, mRoBlob, mDataBlob; + tc::ByteData mTextBlob, mRoBlob, mDataBlob; RoMetadataProcess mRoMeta; bool mIsHomebrewNro; AssetProcess mAssetProc; @@ -50,4 +47,6 @@ private: void importCodeSegments(); void displayHeader(); void processRoMeta(); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/NsoProcess.cpp b/src/NsoProcess.cpp index 7b8db33..dbdc11f 100644 --- a/src/NsoProcess.cpp +++ b/src/NsoProcess.cpp @@ -6,9 +6,9 @@ #include #include "NsoProcess.h" -NsoProcess::NsoProcess(): +nstool::NsoProcess::NsoProcess(): mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false), mIs64BitInstruction(true), mListApi(false), @@ -16,63 +16,63 @@ NsoProcess::NsoProcess(): { } -void NsoProcess::process() +void nstool::NsoProcess::process() { importHeader(); importCodeSegments(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) displayNsoHeader(); processRoMeta(); } -void NsoProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::NsoProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void NsoProcess::setCliOutputMode(CliOutputMode type) +void nstool::NsoProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void NsoProcess::setVerifyMode(bool verify) +void nstool::NsoProcess::setVerifyMode(bool verify) { mVerify = verify; } -void NsoProcess::setIs64BitInstruction(bool flag) +void nstool::NsoProcess::setIs64BitInstruction(bool flag) { mRoMeta.setIs64BitInstruction(flag); } -void NsoProcess::setListApi(bool listApi) +void nstool::NsoProcess::setListApi(bool listApi) { mRoMeta.setListApi(listApi); } -void NsoProcess::setListSymbols(bool listSymbols) +void nstool::NsoProcess::setListSymbols(bool listSymbols) { mRoMeta.setListSymbols(listSymbols); } -const RoMetadataProcess& NsoProcess::getRoMetadataProcess() const +const RoMetadataProcess& nstool::NsoProcess::getRoMetadataProcess() const { return mRoMeta; } -void NsoProcess::importHeader() +void nstool::NsoProcess::importHeader() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } if ((*mFile)->size() < sizeof(nn::hac::sNsoHeader)) { - throw fnd::Exception(kModuleName, "Corrupt NSO: file too small"); + throw tc::Exception(kModuleName, "Corrupt NSO: file too small"); } scratch.alloc(sizeof(nn::hac::sNsoHeader)); @@ -81,9 +81,9 @@ void NsoProcess::importHeader() mHdr.fromBytes(scratch.data(), scratch.size()); } -void NsoProcess::importCodeSegments() +void nstool::NsoProcess::importCodeSegments() { - fnd::Vec scratch; + tc::ByteData scratch; uint32_t decompressed_len; fnd::sha::sSha256Hash calc_hash; @@ -96,7 +96,7 @@ void NsoProcess::importCodeSegments() fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mTextBlob.data(), (uint32_t)mTextBlob.size(), decompressed_len); if (decompressed_len != mTextBlob.size()) { - throw fnd::Exception(kModuleName, "NSO text segment failed to decompress"); + throw tc::Exception(kModuleName, "NSO text segment failed to decompress"); } } else @@ -109,7 +109,7 @@ void NsoProcess::importCodeSegments() fnd::sha::Sha256(mTextBlob.data(), mTextBlob.size(), calc_hash.bytes); if (calc_hash != mHdr.getTextSegmentInfo().hash) { - throw fnd::Exception(kModuleName, "NSO text segment failed SHA256 verification"); + throw tc::Exception(kModuleName, "NSO text segment failed SHA256 verification"); } } @@ -122,7 +122,7 @@ void NsoProcess::importCodeSegments() fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mRoBlob.data(), (uint32_t)mRoBlob.size(), decompressed_len); if (decompressed_len != mRoBlob.size()) { - throw fnd::Exception(kModuleName, "NSO ro segment failed to decompress"); + throw tc::Exception(kModuleName, "NSO ro segment failed to decompress"); } } else @@ -135,7 +135,7 @@ void NsoProcess::importCodeSegments() fnd::sha::Sha256(mRoBlob.data(), mRoBlob.size(), calc_hash.bytes); if (calc_hash != mHdr.getRoSegmentInfo().hash) { - throw fnd::Exception(kModuleName, "NSO ro segment failed SHA256 verification"); + throw tc::Exception(kModuleName, "NSO ro segment failed SHA256 verification"); } } @@ -148,7 +148,7 @@ void NsoProcess::importCodeSegments() fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mDataBlob.data(), (uint32_t)mDataBlob.size(), decompressed_len); if (decompressed_len != mDataBlob.size()) { - throw fnd::Exception(kModuleName, "NSO data segment failed to decompress"); + throw tc::Exception(kModuleName, "NSO data segment failed to decompress"); } } else @@ -161,16 +161,16 @@ void NsoProcess::importCodeSegments() fnd::sha::Sha256(mDataBlob.data(), mDataBlob.size(), calc_hash.bytes); if (calc_hash != mHdr.getDataSegmentInfo().hash) { - throw fnd::Exception(kModuleName, "NSO data segment failed SHA256 verification"); + throw tc::Exception(kModuleName, "NSO data segment failed SHA256 verification"); } } } -void NsoProcess::displayNsoHeader() +void nstool::NsoProcess::displayNsoHeader() { std::cout << "[NSO Header]" << std::endl; std::cout << " ModuleId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getModuleId().data, nn::hac::nso::kModuleIdSize, false, "") << std::endl; - if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) + if (mCliOutputMode.show_layout) { std::cout << " Program Segments:" << std::endl; std::cout << " .module_name:" << std::endl; @@ -190,18 +190,18 @@ void NsoProcess::displayNsoHeader() std::cout << " .text:" << std::endl; std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.offset << std::endl; std::cout << " MemorySize: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.size << std::endl; - if (mHdr.getTextSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mHdr.getTextSegmentInfo().is_hashed && mCliOutputMode.show_extended_info) { std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getTextSegmentInfo().hash.bytes, 32, false, "") << std::endl; } std::cout << " .ro:" << std::endl; std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.offset << std::endl; std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.size << std::endl; - if (mHdr.getRoSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mHdr.getRoSegmentInfo().is_hashed && mCliOutputMode.show_extended_info) { std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getRoSegmentInfo().hash.bytes, 32, false, "") << std::endl; } - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) { std::cout << " .api_info:" << std::endl; std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoEmbeddedInfo().offset << std::endl; @@ -217,7 +217,7 @@ void NsoProcess::displayNsoHeader() std::cout << " .data:" << std::endl; std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.offset << std::endl; std::cout << " MemorySize: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.size << std::endl; - if (mHdr.getDataSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mHdr.getDataSegmentInfo().is_hashed && mCliOutputMode.show_extended_info) { std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getDataSegmentInfo().hash.bytes, 32, false, "") << std::endl; } @@ -225,7 +225,7 @@ void NsoProcess::displayNsoHeader() std::cout << " MemorySize: 0x" << std::hex << mHdr.getBssSize() << std::endl; } -void NsoProcess::processRoMeta() +void nstool::NsoProcess::processRoMeta() { if (mRoBlob.size()) { diff --git a/src/NsoProcess.h b/src/NsoProcess.h index 2d29784..2f4fbb2 100644 --- a/src/NsoProcess.h +++ b/src/NsoProcess.h @@ -1,14 +1,11 @@ #pragma once -#include -#include -#include -#include -#include +#include "types.h" +#include "RoMetadataProcess.h" + #include #include -#include "common.h" -#include "RoMetadataProcess.h" +namespace nstool { class NsoProcess { @@ -17,7 +14,7 @@ public: void process(); - void setInputFile(const fnd::SharedPtr& file); + void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -29,7 +26,7 @@ public: private: const std::string kModuleName = "NsoProcess"; - fnd::SharedPtr mFile; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; bool mIs64BitInstruction; @@ -37,11 +34,13 @@ private: bool mListSymbols; nn::hac::NsoHeader mHdr; - fnd::Vec mTextBlob, mRoBlob, mDataBlob; + tc::ByteData mTextBlob, mRoBlob, mDataBlob; RoMetadataProcess mRoMeta; void importHeader(); void importCodeSegments(); void displayNsoHeader(); void processRoMeta(); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/PfsProcess.cpp b/src/PfsProcess.cpp index 36287e5..425df18 100644 --- a/src/PfsProcess.cpp +++ b/src/PfsProcess.cpp @@ -9,9 +9,9 @@ #include -PfsProcess::PfsProcess() : +nstool::PfsProcess::PfsProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false), mExtractPath(), mExtract(false), @@ -21,14 +21,14 @@ PfsProcess::PfsProcess() : { } -void PfsProcess::process() +void nstool::PfsProcess::process() { importHeader(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) { displayHeader(); - if (mListFs || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mListFs || mCliOutputMode.show_extended_info) displayFs(); } if (mPfs.getFsType() == mPfs.TYPE_HFS0 && mVerify) @@ -37,49 +37,49 @@ void PfsProcess::process() extractFs(); } -void PfsProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::PfsProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void PfsProcess::setCliOutputMode(CliOutputMode type) +void nstool::PfsProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void PfsProcess::setVerifyMode(bool verify) +void nstool::PfsProcess::setVerifyMode(bool verify) { mVerify = verify; } -void PfsProcess::setMountPointName(const std::string& mount_name) +void nstool::PfsProcess::setMountPointName(const std::string& mount_name) { mMountName = mount_name; } -void PfsProcess::setExtractPath(const std::string& path) +void nstool::PfsProcess::setExtractPath(const std::string& path) { mExtract = true; mExtractPath = path; } -void PfsProcess::setListFs(bool list_fs) +void nstool::PfsProcess::setListFs(bool list_fs) { mListFs = list_fs; } -const nn::hac::PartitionFsHeader& PfsProcess::getPfsHeader() const +const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const { return mPfs; } -void PfsProcess::importHeader() +void nstool::PfsProcess::importHeader() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } // open minimum header to get full header size @@ -87,7 +87,7 @@ void PfsProcess::importHeader() (*mFile)->read(scratch.data(), 0, scratch.size()); if (validateHeaderMagic(((nn::hac::sPfsHeader*)scratch.data())) == false) { - throw fnd::Exception(kModuleName, "Corrupt Header"); + throw tc::Exception(kModuleName, "Corrupt Header"); } size_t pfsHeaderSize = determineHeaderSize(((nn::hac::sPfsHeader*)scratch.data())); @@ -97,7 +97,7 @@ void PfsProcess::importHeader() mPfs.fromBytes(scratch.data(), scratch.size()); } -void PfsProcess::displayHeader() +void nstool::PfsProcess::displayHeader() { std::cout << "[PartitionFS]" << std::endl; std::cout << " Type: " << nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType()) << std::endl; @@ -111,13 +111,13 @@ void PfsProcess::displayHeader() } } -void PfsProcess::displayFs() +void nstool::PfsProcess::displayFs() { for (size_t i = 0; i < mPfs.getFileList().size(); i++) { const nn::hac::PartitionFsHeader::sFile& file = mPfs.getFileList()[i]; std::cout << " " << file.name; - if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) + if (mCliOutputMode.show_layout) { switch (mPfs.getFsType()) { @@ -134,7 +134,7 @@ void PfsProcess::displayFs() } } -size_t PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr) +size_t nstool::PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr) { size_t fileEntrySize = 0; if (hdr->st_magic.get() == nn::hac::pfs::kPfsStructMagic) @@ -145,15 +145,15 @@ size_t PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr) return sizeof(nn::hac::sPfsHeader) + hdr->file_num.get() * fileEntrySize + hdr->name_table_size.get(); } -bool PfsProcess::validateHeaderMagic(const nn::hac::sPfsHeader* hdr) +bool nstool::PfsProcess::validateHeaderMagic(const nn::hac::sPfsHeader* hdr) { return hdr->st_magic.get() == nn::hac::pfs::kPfsStructMagic || hdr->st_magic.get() == nn::hac::pfs::kHashedPfsStructMagic; } -void PfsProcess::validateHfs() +void nstool::PfsProcess::validateHfs() { fnd::sha::sSha256Hash hash; - const fnd::List& file = mPfs.getFileList(); + const std::vector& file = mPfs.getFileList(); for (size_t i = 0; i < file.size(); i++) { mCache.alloc(file[i].hash_protected_size); @@ -166,7 +166,7 @@ void PfsProcess::validateHfs() } } -void PfsProcess::extractFs() +void nstool::PfsProcess::extractFs() { // allocate only when extractDir is invoked mCache.alloc(kCacheSize); @@ -175,7 +175,7 @@ void PfsProcess::extractFs() fnd::io::makeDirectory(mExtractPath); fnd::SimpleFile outFile; - const fnd::List& file = mPfs.getFileList(); + const std::vector& file = mPfs.getFileList(); std::string file_path; for (size_t i = 0; i < file.size(); i++) @@ -184,7 +184,7 @@ void PfsProcess::extractFs() fnd::io::appendToPath(file_path, mExtractPath); fnd::io::appendToPath(file_path, file[i].name); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) printf("extract=[%s]\n", file_path.c_str()); outFile.open(file_path, outFile.Create); diff --git a/src/PfsProcess.h b/src/PfsProcess.h index 8ae6fd7..2736809 100644 --- a/src/PfsProcess.h +++ b/src/PfsProcess.h @@ -1,11 +1,9 @@ #pragma once -#include -#include -#include -#include +#include "types.h" + #include -#include "common.h" +namespace nstool { class PfsProcess { @@ -15,7 +13,7 @@ public: void process(); // generic - void setInputFile(const fnd::SharedPtr& file); + void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -30,7 +28,7 @@ private: const std::string kModuleName = "PfsProcess"; static const size_t kCacheSize = 0x10000; - fnd::SharedPtr mFile; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; @@ -39,7 +37,7 @@ private: std::string mMountName; bool mListFs; - fnd::Vec mCache; + tc::ByteData mCache; nn::hac::PartitionFsHeader mPfs; @@ -50,4 +48,6 @@ private: bool validateHeaderMagic(const nn::hac::sPfsHeader* hdr); void validateHfs(); void extractFs(); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/PkiCertProcess.cpp b/src/PkiCertProcess.cpp index 4433ae7..45ce6b7 100644 --- a/src/PkiCertProcess.cpp +++ b/src/PkiCertProcess.cpp @@ -6,51 +6,51 @@ #include "PkiCertProcess.h" #include "PkiValidator.h" -PkiCertProcess::PkiCertProcess() : +nstool::PkiCertProcess::PkiCertProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false) { } -void PkiCertProcess::process() +void nstool::PkiCertProcess::process() { importCerts(); if (mVerify) validateCerts(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) displayCerts(); } -void PkiCertProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::PkiCertProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void PkiCertProcess::setKeyCfg(const KeyConfiguration& keycfg) +void nstool::PkiCertProcess::setKeyCfg(const KeyBag& keycfg) { mKeyCfg = keycfg; } -void PkiCertProcess::setCliOutputMode(CliOutputMode mode) +void nstool::PkiCertProcess::setCliOutputMode(CliOutputMode mode) { mCliOutputMode = mode; } -void PkiCertProcess::setVerifyMode(bool verify) +void nstool::PkiCertProcess::setVerifyMode(bool verify) { mVerify = verify; } -void PkiCertProcess::importCerts() +void nstool::PkiCertProcess::importCerts() { - fnd::Vec scratch; + tc::ByteData scratch; if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } scratch.alloc((*mFile)->size()); @@ -60,11 +60,11 @@ void PkiCertProcess::importCerts() for (size_t f_pos = 0; f_pos < scratch.size(); f_pos += cert.getBytes().size()) { cert.fromBytes(scratch.data() + f_pos, scratch.size() - f_pos); - mCert.addElement(cert); + mCert.push_back(cert); } } -void PkiCertProcess::validateCerts() +void nstool::PkiCertProcess::validateCerts() { PkiValidator pki; @@ -73,14 +73,14 @@ void PkiCertProcess::validateCerts() pki.setKeyCfg(mKeyCfg); pki.addCertificates(mCert); } - catch (const fnd::Exception& e) + catch (const tc::Exception& e) { std::cout << "[WARNING] " << e.error() << std::endl; return; } } -void PkiCertProcess::displayCerts() +void nstool::PkiCertProcess::displayCerts() { for (size_t i = 0; i < mCert.size(); i++) { @@ -88,19 +88,19 @@ void PkiCertProcess::displayCerts() } } -void PkiCertProcess::displayCert(const nn::pki::SignedData& cert) +void nstool::PkiCertProcess::displayCert(const nn::pki::SignedData& cert) { std::cout << "[NNPKI Certificate]" << std::endl; std::cout << " SignType " << getSignTypeStr(cert.getSignature().getSignType()); - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) std::cout << " (0x" << std::hex << cert.getSignature().getSignType() << ") (" << getEndiannessStr(cert.getSignature().isLittleEndian()) << ")"; std::cout << std::endl; std::cout << " Issuer: " << cert.getBody().getIssuer() << std::endl; std::cout << " Subject: " << cert.getBody().getSubject() << std::endl; std::cout << " PublicKeyType: " << getPublicKeyTypeStr(cert.getBody().getPublicKeyType()); - if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mCliOutputMode.show_extended_info) std::cout << " (" << std::dec << cert.getBody().getPublicKeyType() << ")"; std::cout << std::endl; std::cout << " CertID: 0x" << std::hex << cert.getBody().getCertId() << std::endl; @@ -131,12 +131,12 @@ void PkiCertProcess::displayCert(const nn::pki::SignedData -#include -#include -#include -#include -#include +#include "types.h" +#include "KeyBag.h" + #include #include -#include "KeyConfiguration.h" -#include "common.h" + +namespace nstool { class PkiCertProcess { @@ -17,8 +14,8 @@ public: void process(); - void setInputFile(const fnd::SharedPtr& file); - void setKeyCfg(const KeyConfiguration& keycfg); + void setInputFile(const std::shared_ptr& file); + void setKeyCfg(const KeyBag& keycfg); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -26,12 +23,12 @@ private: const std::string kModuleName = "PkiCertProcess"; static const size_t kSmallHexDumpLen = 0x10; - fnd::SharedPtr mFile; - KeyConfiguration mKeyCfg; + std::shared_ptr mFile; + KeyBag mKeyCfg; CliOutputMode mCliOutputMode; bool mVerify; - fnd::List> mCert; + std::vector> mCert; void importCerts(); void validateCerts(); @@ -42,4 +39,6 @@ private: const char* getSignTypeStr(nn::pki::sign::SignatureId type) const; const char* getEndiannessStr(bool isLittleEndian) const; const char* getPublicKeyTypeStr(nn::pki::cert::PublicKeyType type) const; -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/PkiValidator.cpp b/src/PkiValidator.cpp index 37bb9b5..d608e90 100644 --- a/src/PkiValidator.cpp +++ b/src/PkiValidator.cpp @@ -4,15 +4,15 @@ #include #include "PkiValidator.h" -PkiValidator::PkiValidator() +nstool::PkiValidator::PkiValidator() { clearCertificates(); } -void PkiValidator::setKeyCfg(const KeyConfiguration& keycfg) +void nstool::PkiValidator::setKeyCfg(const KeyBag& keycfg) { // save a copy of the certificate bank - fnd::List> old_certs = mCertificateBank; + std::vector> old_certs = mCertificateBank; // clear the certificate bank mCertificateBank.clear(); @@ -27,7 +27,7 @@ void PkiValidator::setKeyCfg(const KeyConfiguration& keycfg) } } -void PkiValidator::addCertificates(const fnd::List>& certs) +void nstool::PkiValidator::addCertificates(const std::vector>& certs) { for (size_t i = 0; i < certs.size(); i++) { @@ -35,12 +35,12 @@ void PkiValidator::addCertificates(const fnd::List& cert) +void nstool::PkiValidator::addCertificate(const nn::pki::SignedData& cert) { std::string cert_ident; nn::pki::sign::SignatureAlgo cert_sign_algo; nn::pki::sign::HashAlgo cert_hash_algo; - fnd::Vec cert_hash; + tc::ByteData cert_hash; try { @@ -48,7 +48,7 @@ void PkiValidator::addCertificate(const nn::pki::SignedData& signature, const fnd::Vec& hash) const +void nstool::PkiValidator::validateSignature(const std::string& issuer, nn::pki::sign::SignatureId signature_id, const tc::ByteData& signature, const tc::ByteData& hash) const { nn::pki::sign::SignatureAlgo sign_algo = nn::pki::sign::getSignatureAlgo(signature_id); nn::pki::sign::HashAlgo hash_algo = nn::pki::sign::getHashAlgo(signature_id); @@ -112,11 +112,11 @@ void PkiValidator::validateSignature(const std::string& issuer, nn::pki::sign::S } else if (mKeyCfg.getPkiRootSignKey(issuer, ecdsa_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240) { - throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); + throw tc::Exception(kModuleName, "ECDSA signatures are not supported"); } else { - throw fnd::Exception(kModuleName, "Public key for issuer \"" + issuer + "\" does not exist."); + throw tc::Exception(kModuleName, "Public key for issuer \"" + issuer + "\" does not exist."); } } else @@ -135,34 +135,34 @@ void PkiValidator::validateSignature(const std::string& issuer, nn::pki::sign::S } else if (issuer_pubk_type == nn::pki::cert::ECDSA240 && sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240) { - throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); + throw tc::Exception(kModuleName, "ECDSA signatures are not supported"); } else { - throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type"); + throw tc::Exception(kModuleName, "Mismatch between issuer public key and signature type"); } } if (sig_validate_res != 0) { - throw fnd::Exception(kModuleName, "Incorrect signature"); + throw tc::Exception(kModuleName, "Incorrect signature"); } } -void PkiValidator::makeCertIdent(const nn::pki::SignedData& cert, std::string& ident) const +void nstool::PkiValidator::makeCertIdent(const nn::pki::SignedData& cert, std::string& ident) const { makeCertIdent(cert.getBody().getIssuer(), cert.getBody().getSubject(), ident); } -void PkiValidator::makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const +void nstool::PkiValidator::makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const { ident = issuer + nn::pki::sign::kIdentDelimiter + subject; ident = ident.substr(0, _MIN(ident.length(),64)); } -bool PkiValidator::doesCertExist(const std::string& ident) const +bool nstool::PkiValidator::doesCertExist(const std::string& ident) const { bool exists = false; std::string full_cert_name; @@ -179,7 +179,7 @@ bool PkiValidator::doesCertExist(const std::string& ident) const return exists; } -const nn::pki::SignedData& PkiValidator::getCert(const std::string& ident) const +const nn::pki::SignedData& nstool::PkiValidator::getCert(const std::string& ident) const { std::string full_cert_name; for (size_t i = 0; i < mCertificateBank.size(); i++) @@ -191,10 +191,10 @@ const nn::pki::SignedData& PkiValidator::getCert(const } } - throw fnd::Exception(kModuleName, "Issuer certificate does not exist"); + throw tc::Exception(kModuleName, "Issuer certificate does not exist"); } -fnd::sha::HashType PkiValidator::getCryptoHashAlgoFromEsSignHashAlgo(nn::pki::sign::HashAlgo hash_algo) const +fnd::sha::HashType nstool::PkiValidator::getCryptoHashAlgoFromEsSignHashAlgo(nn::pki::sign::HashAlgo hash_algo) const { fnd::sha::HashType hash_type = fnd::sha::HASH_SHA1; diff --git a/src/PkiValidator.h b/src/PkiValidator.h index 7b7a0ab..6cc2006 100644 --- a/src/PkiValidator.h +++ b/src/PkiValidator.h @@ -1,34 +1,35 @@ #pragma once -#include -#include -#include -#include +#include "types.h" +#include "KeyBag.h" + #include #include -#include -#include "KeyConfiguration.h" + +namespace nstool { class PkiValidator { public: PkiValidator(); - void setKeyCfg(const KeyConfiguration& keycfg); - void addCertificates(const fnd::List>& certs); + void setKeyCfg(const KeyBag& keycfg); + void addCertificates(const std::vector>& certs); void addCertificate(const nn::pki::SignedData& cert); void clearCertificates(); - void validateSignature(const std::string& issuer, nn::pki::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const; + void validateSignature(const std::string& issuer, nn::pki::sign::SignatureId signature_id, const tc::ByteData& signature, const tc::ByteData& hash) const; private: const std::string kModuleName = "NNPkiValidator"; - KeyConfiguration mKeyCfg; - fnd::List> mCertificateBank; + KeyBag mKeyCfg; + std::vector> mCertificateBank; void makeCertIdent(const nn::pki::SignedData& cert, std::string& ident) const; void makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const; bool doesCertExist(const std::string& ident) const; const nn::pki::SignedData& getCert(const std::string& ident) const; fnd::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(nn::pki::sign::HashAlgo hash_algo) const; -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/RoMetadataProcess.cpp b/src/RoMetadataProcess.cpp index 81e9b47..019c4ef 100644 --- a/src/RoMetadataProcess.cpp +++ b/src/RoMetadataProcess.cpp @@ -5,8 +5,8 @@ #include "RoMetadataProcess.h" -RoMetadataProcess::RoMetadataProcess() : - mCliOutputMode(_BIT(OUTPUT_BASIC)), +nstool::RoMetadataProcess::RoMetadataProcess() : + mCliOutputMode(true, false, false, false), mIs64BitInstruction(true), mListApi(false), mListSymbols(false), @@ -23,90 +23,90 @@ RoMetadataProcess::RoMetadataProcess() : } -void RoMetadataProcess::process() +void nstool::RoMetadataProcess::process() { importApiList(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) displayRoMetaData(); } -void RoMetadataProcess::setRoBinary(const fnd::Vec& bin) +void nstool::RoMetadataProcess::setRoBinary(const tc::ByteData& bin) { mRoBlob = bin; } -void RoMetadataProcess::setApiInfo(size_t offset, size_t size) +void nstool::RoMetadataProcess::setApiInfo(size_t offset, size_t size) { mApiInfo.offset = offset; mApiInfo.size = size; } -void RoMetadataProcess::setDynSym(size_t offset, size_t size) +void nstool::RoMetadataProcess::setDynSym(size_t offset, size_t size) { mDynSym.offset = offset; mDynSym.size = size; } -void RoMetadataProcess::setDynStr(size_t offset, size_t size) +void nstool::RoMetadataProcess::setDynStr(size_t offset, size_t size) { mDynStr.offset = offset; mDynStr.size = size; } -void RoMetadataProcess::setCliOutputMode(CliOutputMode type) +void nstool::RoMetadataProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void RoMetadataProcess::setIs64BitInstruction(bool flag) +void nstool::RoMetadataProcess::setIs64BitInstruction(bool flag) { mIs64BitInstruction = flag; } -void RoMetadataProcess::setListApi(bool listApi) +void nstool::RoMetadataProcess::setListApi(bool listApi) { mListApi = listApi; } -void RoMetadataProcess::setListSymbols(bool listSymbols) +void nstool::RoMetadataProcess::setListSymbols(bool listSymbols) { mListSymbols = listSymbols; } -const std::vector& RoMetadataProcess::getSdkVerApiList() const +const std::vector& nstool::RoMetadataProcess::getSdkVerApiList() const { return mSdkVerApiList; } -const std::vector& RoMetadataProcess::getPublicApiList() const +const std::vector& nstool::RoMetadataProcess::getPublicApiList() const { return mPublicApiList; } -const std::vector& RoMetadataProcess::getDebugApiList() const +const std::vector& nstool::RoMetadataProcess::getDebugApiList() const { return mDebugApiList; } -const std::vector& RoMetadataProcess::getPrivateApiList() const +const std::vector& nstool::RoMetadataProcess::getPrivateApiList() const { return mPrivateApiList; } -const std::vector& RoMetadataProcess::getGuidelineApiList() const +const std::vector& nstool::RoMetadataProcess::getGuidelineApiList() const { return mGuidelineApiList; } -const fnd::List& RoMetadataProcess::getSymbolList() const +const std::vector& nstool::RoMetadataProcess::getSymbolList() const { return mSymbolList.getSymbolList(); } -void RoMetadataProcess::importApiList() +void nstool::RoMetadataProcess::importApiList() { if (mRoBlob.size() == 0) { - throw fnd::Exception(kModuleName, "No ro binary set."); + throw tc::Exception(kModuleName, "No ro binary set."); } if (mApiInfo.size > 0) @@ -147,11 +147,11 @@ void RoMetadataProcess::importApiList() } } -void RoMetadataProcess::displayRoMetaData() +void nstool::RoMetadataProcess::displayRoMetaData() { size_t api_num = mSdkVerApiList.size() + mPublicApiList.size() + mDebugApiList.size() + mPrivateApiList.size(); - if (api_num > 0 && (mListApi || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))) + if (api_num > 0 && (mListApi || mCliOutputMode.show_extended_info)) { std::cout << "[SDK API List]" << std::endl; if (mSdkVerApiList.size() > 0) @@ -191,7 +191,7 @@ void RoMetadataProcess::displayRoMetaData() } } } - if (mSymbolList.getSymbolList().size() > 0 && (mListSymbols || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))) + if (mSymbolList.getSymbolList().size() > 0 && (mListSymbols || mCliOutputMode.show_extended_info)) { std::cout << "[Symbol List]" << std::endl; for (size_t i = 0; i < mSymbolList.getSymbolList().size(); i++) @@ -202,7 +202,7 @@ void RoMetadataProcess::displayRoMetaData() } } -const char* RoMetadataProcess::getSectionIndexStr(uint16_t shn_index) const +const char* nstool::RoMetadataProcess::getSectionIndexStr(uint16_t shn_index) const { const char* str; switch (shn_index) @@ -235,7 +235,7 @@ const char* RoMetadataProcess::getSectionIndexStr(uint16_t shn_index) const return str; } -const char* RoMetadataProcess::getSymbolTypeStr(byte_t symbol_type) const +const char* nstool::RoMetadataProcess::getSymbolTypeStr(byte_t symbol_type) const { const char* str; switch (symbol_type) @@ -274,7 +274,7 @@ const char* RoMetadataProcess::getSymbolTypeStr(byte_t symbol_type) const return str; } -const char* RoMetadataProcess::getSymbolBindingStr(byte_t symbol_binding) const +const char* nstool::RoMetadataProcess::getSymbolBindingStr(byte_t symbol_binding) const { const char* str; switch (symbol_binding) diff --git a/src/RoMetadataProcess.h b/src/RoMetadataProcess.h index df854c1..4a13cc9 100644 --- a/src/RoMetadataProcess.h +++ b/src/RoMetadataProcess.h @@ -1,14 +1,11 @@ #pragma once -#include -#include -#include -#include +#include "types.h" +#include "SdkApiString.h" +#include "ElfSymbolParser.h" #include -#include "common.h" -#include "SdkApiString.h" -#include "ElfSymbolParser.h" +namespace nstool { class RoMetadataProcess { @@ -17,7 +14,7 @@ public: void process(); - void setRoBinary(const fnd::Vec& bin); + void setRoBinary(const tc::ByteData& bin); void setApiInfo(size_t offset, size_t size); void setDynSym(size_t offset, size_t size); void setDynStr(size_t offset, size_t size); @@ -33,7 +30,7 @@ public: const std::vector& getDebugApiList() const; const std::vector& getPrivateApiList() const; const std::vector& getGuidelineApiList() const; - const fnd::List& getSymbolList() const; + const std::vector& getSymbolList() const; private: const std::string kModuleName = "RoMetadataProcess"; @@ -52,7 +49,7 @@ private: sLayout mApiInfo; sLayout mDynSym; sLayout mDynStr; - fnd::Vec mRoBlob; + tc::ByteData mRoBlob; std::vector mSdkVerApiList; std::vector mPublicApiList; std::vector mDebugApiList; @@ -67,4 +64,6 @@ private: const char* getSectionIndexStr(uint16_t shn_index) const; const char* getSymbolTypeStr(byte_t symbol_type) const; const char* getSymbolBindingStr(byte_t symbol_binding) const; -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/RomfsProcess.cpp b/src/RomfsProcess.cpp index f03e0be..93207e6 100644 --- a/src/RomfsProcess.cpp +++ b/src/RomfsProcess.cpp @@ -6,9 +6,9 @@ #include "CompressedArchiveIFile.h" #include "RomfsProcess.h" -RomfsProcess::RomfsProcess() : +nstool::RomfsProcess::RomfsProcess() : mFile(), - mCliOutputMode(_BIT(OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false), mExtractPath(), mExtract(false), @@ -22,14 +22,14 @@ RomfsProcess::RomfsProcess() : mRootDir.file_list.clear(); } -void RomfsProcess::process() +void nstool::RomfsProcess::process() { resolveRomfs(); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) { displayHeader(); - if (mListFs || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + if (mListFs || mCliOutputMode.show_extended_info) displayFs(); } @@ -37,43 +37,43 @@ void RomfsProcess::process() extractFs(); } -void RomfsProcess::setInputFile(const fnd::SharedPtr& file) +void nstool::RomfsProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void RomfsProcess::setCliOutputMode(CliOutputMode type) +void nstool::RomfsProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; } -void RomfsProcess::setVerifyMode(bool verify) +void nstool::RomfsProcess::setVerifyMode(bool verify) { mVerify = verify; } -void RomfsProcess::setMountPointName(const std::string& mount_name) +void nstool::RomfsProcess::setMountPointName(const std::string& mount_name) { mMountName = mount_name; } -void RomfsProcess::setExtractPath(const std::string& path) +void nstool::RomfsProcess::setExtractPath(const std::string& path) { mExtract = true; mExtractPath = path; } -void RomfsProcess::setListFs(bool list_fs) +void nstool::RomfsProcess::setListFs(bool list_fs) { mListFs = list_fs; } -const RomfsProcess::sDirectory& RomfsProcess::getRootDir() const +const nstool::RomfsProcess::sDirectory& nstool::RomfsProcess::getRootDir() const { return mRootDir; } -void RomfsProcess::printTab(size_t tab) const +void nstool::RomfsProcess::printTab(size_t tab) const { for (size_t i = 0; i < tab; i++) { @@ -81,18 +81,18 @@ void RomfsProcess::printTab(size_t tab) const } } -void RomfsProcess::displayFile(const sFile& file, size_t tab) const +void nstool::RomfsProcess::displayFile(const sFile& file, size_t tab) const { printTab(tab); std::cout << file.name; - if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) + if (mCliOutputMode.show_layout) { std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ")"; } std::cout << std::endl; } -void RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const +void nstool::RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const { if (dir.name.empty() == false) { @@ -110,7 +110,7 @@ void RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const } } -void RomfsProcess::displayHeader() +void nstool::RomfsProcess::displayHeader() { std::cout << "[RomFS]" << std::endl; std::cout << " DirNum: " << std::dec << mDirNum << std::endl; @@ -124,12 +124,12 @@ void RomfsProcess::displayHeader() } } -void RomfsProcess::displayFs() +void nstool::RomfsProcess::displayFs() { displayDir(mRootDir, 1); } -void RomfsProcess::extractDir(const std::string& path, const sDirectory& dir) +void nstool::RomfsProcess::extractDir(const std::string& path, const sDirectory& dir) { std::string dir_path; std::string file_path; @@ -150,7 +150,7 @@ void RomfsProcess::extractDir(const std::string& path, const sDirectory& dir) fnd::io::appendToPath(file_path, dir_path); fnd::io::appendToPath(file_path, dir.file_list[i].name); - if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + if (mCliOutputMode.show_basic_info) std::cout << "extract=[" << file_path << "]" << std::endl; outFile.open(file_path, outFile.Create); @@ -170,14 +170,14 @@ void RomfsProcess::extractDir(const std::string& path, const sDirectory& dir) } -void RomfsProcess::extractFs() +void nstool::RomfsProcess::extractFs() { // allocate only when extractDir is invoked mCache.alloc(kCacheSize); extractDir(mExtractPath, mRootDir); } -bool RomfsProcess::validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const +bool nstool::RomfsProcess::validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const { bool validLayout = true; @@ -199,7 +199,7 @@ bool RomfsProcess::validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const return validLayout; } -void RomfsProcess::importDirectory(uint32_t dir_offset, sDirectory& dir) +void nstool::RomfsProcess::importDirectory(uint32_t dir_offset, sDirectory& dir) { nn::hac::sRomfsDirEntry* d_node = get_dir_node(dir_offset); @@ -229,7 +229,7 @@ void RomfsProcess::importDirectory(uint32_t dir_offset, sDirectory& dir) printf(" name=%s\n", f_node->name); */ - dir.file_list.addElement({std::string(f_node->name(), f_node->name_size.get()), mHdr.data_offset.get() + f_node->offset.get(), f_node->size.get()}); + dir.file_list.push_back({std::string(f_node->name(), f_node->name_size.get()), mHdr.data_offset.get() + f_node->offset.get(), f_node->size.get()}); file_addr = f_node->sibling.get(); mFileNum++; @@ -239,7 +239,7 @@ void RomfsProcess::importDirectory(uint32_t dir_offset, sDirectory& dir) { nn::hac::sRomfsDirEntry* c_node = get_dir_node(child_addr); - dir.dir_list.addElement({std::string(c_node->name(), c_node->name_size.get())}); + dir.dir_list.push_back({std::string(c_node->name(), c_node->name_size.get())}); importDirectory(child_addr, dir.dir_list.atBack()); child_addr = c_node->sibling.get(); @@ -247,11 +247,11 @@ void RomfsProcess::importDirectory(uint32_t dir_offset, sDirectory& dir) } } -void RomfsProcess::resolveRomfs() +void nstool::RomfsProcess::resolveRomfs() { if (*mFile == nullptr) { - throw fnd::Exception(kModuleName, "No file reader set."); + throw tc::Exception(kModuleName, "No file reader set."); } // read header @@ -260,7 +260,7 @@ void RomfsProcess::resolveRomfs() // logic check on the header layout if (validateHeaderLayout(&mHdr) == false) { - throw fnd::Exception(kModuleName, "Invalid ROMFS Header"); + throw tc::Exception(kModuleName, "Invalid ROMFS Header"); } // check for romfs compression @@ -284,7 +284,7 @@ void RomfsProcess::resolveRomfs() if ((entry[1].virtual_offset.get() >= romfs_metadata_begin_offset && entry[1].virtual_offset.get() < romfs_metadata_end_offset) == false || \ entry[1].compression_type != (byte_t)nn::hac::compression::CompressionType::Lz4) { - throw fnd::Exception(kModuleName, "RomFs appears corrupted (bad final compression entry virtual offset/compression type)"); + throw tc::Exception(kModuleName, "RomFs appears corrupted (bad final compression entry virtual offset/compression type)"); } // the first compression entry follows the physical placement of the final data chunk (specified in the final compression entry) @@ -293,7 +293,7 @@ void RomfsProcess::resolveRomfs() // quick check to make sure the offset at least before the last entry offset if (first_entry_offset >= (physical_size - sizeof(nn::hac::sCompressionEntry))) { - throw fnd::Exception(kModuleName, "RomFs appears corrupted (bad final compression entry physical offset/size)"); + throw tc::Exception(kModuleName, "RomFs appears corrupted (bad final compression entry physical offset/size)"); } // read first compression entry @@ -306,7 +306,7 @@ void RomfsProcess::resolveRomfs() entry[0].physical_size.get() != 0x200 || \ entry[0].compression_type != (byte_t)nn::hac::compression::CompressionType::None) { - throw fnd::Exception(kModuleName, "RomFs appears corrupted (bad first compression entry)"); + throw tc::Exception(kModuleName, "RomFs appears corrupted (bad first compression entry)"); } // wrap mFile in a class to transparantly decompress the image. @@ -331,7 +331,7 @@ void RomfsProcess::resolveRomfs() || get_dir_node(0)->hash.get() != nn::hac::romfs::kInvalidAddr \ || get_dir_node(0)->name_size.get() != 0) { - throw fnd::Exception(kModuleName, "Invalid root directory node"); + throw tc::Exception(kModuleName, "Invalid root directory node"); } // import directory into internal structure diff --git a/src/RomfsProcess.h b/src/RomfsProcess.h index d6cee47..dc39ff4 100644 --- a/src/RomfsProcess.h +++ b/src/RomfsProcess.h @@ -1,13 +1,9 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include "types.h" + #include -#include "common.h" +namespace nstool { class RomfsProcess { @@ -18,8 +14,8 @@ public: struct sDirectory { std::string name; - fnd::List dir_list; - fnd::List file_list; + std::vector dir_list; + std::vector file_list; void operator=(const sDirectory& other) { @@ -82,7 +78,7 @@ public: void process(); // generic - void setInputFile(const fnd::SharedPtr& file); + void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -96,8 +92,13 @@ private: const std::string kModuleName = "RomfsProcess"; static const size_t kCacheSize = 0x10000; - fnd::SharedPtr mFile; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; + bool mShowBasicInfo; + bool mShowExtendedInfo; + bool mShowLayoutInfo; + bool mShowKeydata; + bool mVerbose; bool mVerify; std::string mExtractPath; @@ -105,13 +106,13 @@ private: std::string mMountName; bool mListFs; - fnd::Vec mCache; + tc::ByteData mCache; size_t mDirNum; size_t mFileNum; nn::hac::sRomfsHeader mHdr; - fnd::Vec mDirNodes; - fnd::Vec mFileNodes; + tc::ByteData mDirNodes; + tc::ByteData mFileNodes; sDirectory mRootDir; inline nn::hac::sRomfsDirEntry* get_dir_node(uint32_t offset) { return (nn::hac::sRomfsDirEntry*)(mDirNodes.data() + offset); } @@ -131,4 +132,6 @@ private: bool validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const; void importDirectory(uint32_t dir_offset, sDirectory& dir); void resolveRomfs(); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/SdkApiString.cpp b/src/SdkApiString.cpp index 1f3c9e6..6714772 100644 --- a/src/SdkApiString.cpp +++ b/src/SdkApiString.cpp @@ -1,13 +1,13 @@ #include #include "SdkApiString.h" -SdkApiString::SdkApiString(const std::string& full_str) : +nstool::SdkApiString::SdkApiString(const std::string& full_str) : SdkApiString(API_MIDDLEWARE, "", "") { resolveApiString(full_str); } -SdkApiString::SdkApiString(ApiType type, const std::string& vender_name, const std::string& module_name) : +nstool::SdkApiString::SdkApiString(ApiType type, const std::string& vender_name, const std::string& module_name) : mApiType(type), mVenderName(vender_name), mModuleName(module_name) @@ -15,44 +15,44 @@ SdkApiString::SdkApiString(ApiType type, const std::string& vender_name, const s } -void SdkApiString::operator=(const SdkApiString& other) +void nstool::SdkApiString::operator=(const SdkApiString& other) { mApiType = other.mApiType; mVenderName = other.mVenderName; mModuleName = other.mModuleName; } -SdkApiString::ApiType SdkApiString::getApiType() const +nstool::SdkApiString::ApiType nstool::SdkApiString::getApiType() const { return mApiType; } -void SdkApiString::setApiType(ApiType type) +void nstool::SdkApiString::setApiType(ApiType type) { mApiType = type; } -const std::string& SdkApiString::getVenderName() const +const std::string& nstool::SdkApiString::getVenderName() const { return mVenderName; } -void SdkApiString::setVenderName(const std::string& name) +void nstool::SdkApiString::setVenderName(const std::string& name) { mVenderName = name; } -const std::string& SdkApiString::getModuleName() const +const std::string& nstool::SdkApiString::getModuleName() const { return mModuleName; } -void SdkApiString::setModuleName(const std::string& name) +void nstool::SdkApiString::setModuleName(const std::string& name) { mModuleName = name; } -void SdkApiString::resolveApiString(const std::string& full_str) +void nstool::SdkApiString::resolveApiString(const std::string& full_str) { std::stringstream list_stream(full_str); std::string api_type, vender, module; diff --git a/src/SdkApiString.h b/src/SdkApiString.h index c1a8ffa..7e3a396 100644 --- a/src/SdkApiString.h +++ b/src/SdkApiString.h @@ -1,5 +1,7 @@ #pragma once -#include +#include "types.h" + +namespace nstool { class SdkApiString { @@ -42,4 +44,6 @@ private: std::string mModuleName; void resolveApiString(const std::string& full_str); -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/src/Settings.cpp b/src/Settings.cpp new file mode 100644 index 0000000..a9a6bce --- /dev/null +++ b/src/Settings.cpp @@ -0,0 +1,819 @@ +#include +#include +#include +#include +#include +#include "types.h" +#include "version.h" +#include "Settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class UnkOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + UnkOptionHandler(const std::string& module_label) : mModuleLabel(module_label) + {} + + const std::vector& getOptionStrings() const + { + throw tc::InvalidOperationException("getOptionStrings() not defined for UnkOptionHandler."); + } + + void processOption(const std::string& option, const std::vector& params) + { + throw tc::Exception(mModuleLabel, "Unrecognized option: \"" + option + "\""); + } +private: + std::string mModuleLabel; +}; + +class DeprecatedOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + DeprecatedOptionHandler(const std::string& warn_message, const std::vector& opts) : + mWarnMessage(warn_message), + mOptStrings(opts) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + fmt::print("[WARNING] Option \"{}\" is deprecated.{}{}\n", option, (mWarnMessage.empty() ? "" : " "), mWarnMessage); + } +private: + std::string mWarnMessage; + std::vector mOptStrings; +}; + +class FlagOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + FlagOptionHandler(bool& flag, const std::vector& opts) : + mFlag(flag), + mOptStrings(opts) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() != 0) + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" is a flag, that takes no parameters.", option)); + } + + mFlag = true; + } +private: + bool& mFlag; + std::vector mOptStrings; +}; + +class SingleParamStringOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + SingleParamStringOptionHandler(tc::Optional& param, const std::vector& opts) : + mParam(param), + mOptStrings(opts) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() != 1) + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires a parameter.", option)); + } + + mParam = params[0]; + } +private: + tc::Optional& mParam; + std::vector mOptStrings; +}; + +class SingleParamPathOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + SingleParamPathOptionHandler(tc::Optional& param, const std::vector& opts) : + mParam(param), + mOptStrings(opts) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() != 1) + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires a parameter.", option)); + } + + mParam = params[0]; + } +private: + tc::Optional& mParam; + std::vector mOptStrings; +}; + +class SingleParamSizetOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + SingleParamSizetOptionHandler(size_t& param, const std::vector& opts) : + mParam(param), + mOptStrings(opts) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() != 1) + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires a parameter.", option)); + } + + mParam = strtoul(params[0].c_str(), nullptr, 0); + } +private: + size_t& mParam; + std::vector mOptStrings; +}; + +class SingleParamAesKeyOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + SingleParamAesKeyOptionHandler(tc::Optional& param, const std::vector& opts) : + mParam(param), + mOptStrings(opts) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() != 1) + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires a parameter.", option)); + } + + tc::ByteData key_raw = tc::cli::FormatUtil::hexStringToBytes(params[0]); + if (key_raw.size() != sizeof(nstool::KeyBag::aes128_key_t)) + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option: \"{:s}\", requires an AES128 key as the parameter (must be 32 hex chars).", option)); + } + + nstool::KeyBag::aes128_key_t key_tmp; + memcpy(key_tmp.data(), key_raw.data(), key_tmp.size()); + + mParam = key_tmp; + } +private: + tc::Optional& mParam; + std::vector mOptStrings; +}; + +class FileTypeOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + FileTypeOptionHandler(nstool::Settings::FileType& param, const std::vector& opts) : + mParam(param), + mOptStrings(opts) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() != 1) + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires a parameter.", option)); + } + + if (params[0] == "gc" \ + || params[0] == "gamecard" \ + || params[0] == "xci" \ + || params[0] == "xcie" \ + || params[0] == "xcir") + { + mParam = nstool::Settings::FILE_TYPE_GAMECARD; + } + else if (params[0] == "nsp") + { + mParam = nstool::Settings::FILE_TYPE_NSP; + } + else if (params[0] == "partitionfs" || params[0] == "hashedpartitionfs" \ + || params[0] == "pfs" || params[0] == "pfs0" \ + || params[0] == "hfs" || params[0] == "hfs0") + { + mParam = nstool::Settings::FILE_TYPE_PARTITIONFS; + } + else if (params[0] == "romfs") + { + mParam = nstool::Settings::FILE_TYPE_ROMFS; + } + else if (params[0] == "nca" || params[0] == "contentarchive") + { + mParam = nstool::Settings::FILE_TYPE_NCA; + } + else if (params[0] == "meta" || params[0] == "npdm") + { + mParam = nstool::Settings::FILE_TYPE_META; + } + else if (params[0] == "cnmt") + { + mParam = nstool::Settings::FILE_TYPE_CNMT; + } + else if (params[0] == "nso") + { + mParam = nstool::Settings::FILE_TYPE_NSO; + } + else if (params[0] == "nro") + { + mParam = nstool::Settings::FILE_TYPE_NRO; + } + else if (params[0] == "ini") + { + mParam = nstool::Settings::FILE_TYPE_INI; + } + else if (params[0] == "kip") + { + mParam = nstool::Settings::FILE_TYPE_KIP; + } + else if (params[0] == "nacp") + { + mParam = nstool::Settings::FILE_TYPE_NACP; + } + else if (params[0] == "cert") + { + mParam = nstool::Settings::FILE_TYPE_PKI_CERT; + } + else if (params[0] == "tik") + { + mParam = nstool::Settings::FILE_TYPE_ES_TIK; + } + else if (params[0] == "aset" || params[0] == "asset") + { + mParam = nstool::Settings::FILE_TYPE_HB_ASSET; + } + else + { + throw tc::ArgumentException(fmt::format("File type \"{}\" unrecognised.", params[0])); + } + } +private: + nstool::Settings::FileType& mParam; + std::vector mOptStrings; +}; + +class InstructionTypeOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + InstructionTypeOptionHandler(bool& param, const std::vector& opts) : + mParam(param), + mOptStrings(opts) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() != 1) + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires a parameter.", option)); + } + + if (params[0] == "32bit") + { + mParam = false; + } + else if (params[0] == "64bit") + { + mParam = true; + } + else + { + throw tc::ArgumentException(fmt::format("Instruction type \"{}\" unrecognised. Try \"32bit\" or \"64bit\"", params[0])); + } + } +private: + bool& mParam; + std::vector mOptStrings; +}; + +nstool::SettingsInitializer::SettingsInitializer(const std::vector& args) : + Settings(), + mModuleLabel("nstool::SettingsInitializer"), + mTitleKey(), + mBodyKey(), + mTikPath(), + mCertPath() +{ + // parse input arguments + parse_args(args); + if (infile.path.isNull()) + throw tc::ArgumentException(mModuleLabel, "No input file was specified."); + + // locate key file, if not specfied + if (mKeysetPath.isNull()) + { + std::string home_path_str; + if (tc::os::getEnvVar("HOME", home_path_str) || tc::os::getEnvVar("USERPROFILE", home_path_str)) + { + tc::io::Path keyfile_path = tc::io::Path(home_path_str); + keyfile_path.push_back(".switch"); + keyfile_path.push_back(opt.is_dev ? "dev.keys" : "prod.keys"); + + try { + tc::io::FileStream test = tc::io::FileStream(keyfile_path, tc::io::FileMode::Open, tc::io::FileAccess::Read); + + mKeysetPath = keyfile_path; + } + catch (tc::io::FileNotFoundException&) { + fmt::print("[WARNING] Failed to load \"{}\" keyfile. Maybe specify it with \"-k \"?\n", opt.is_dev ? "dev.keys" : "prod.keys"); + } + } + else { + fmt::print("[WARNING] Failed to located \"{}\" keyfile. Maybe specify it with \"-k \"?\n", opt.is_dev ? "dev.keys" : "prod.keys"); + } + } + + // generate keybag + opt.keybag = KeyBagInitializer(opt.is_dev, mKeysetPath, mTikPath, mCertPath); + opt.keybag.fallback_enc_content_key = mTitleKey; + opt.keybag.fallback_content_key = mBodyKey; + + // determine filetype if not manually specified + if (infile.filetype == FILE_TYPE_ERROR) + { + determine_filetype(); + if (infile.filetype == FILE_TYPE_ERROR) + { + throw tc::ArgumentException(mModuleLabel, "Input file type was undetermined."); + } + } +} + +void nstool::SettingsInitializer::parse_args(const std::vector& args) +{ + // check for minimum arguments + if (args.size() < 2) + { + usage_text(); + throw tc::ArgumentException(mModuleLabel, "Not enough arguments."); + } + + // detect request for help + for (auto itr = ++(args.begin()); itr != args.end(); itr++) + { + if (*itr == "-h" || *itr == "--help" || *itr == "-help") + { + usage_text(); + throw tc::ArgumentException(mModuleLabel, "Help required."); + } + } + + // save input file + infile.path = tc::io::Path(args.back()); + + // test new option parser + tc::cli::OptionParser opts; + + // register unk option handler + opts.registerUnrecognisedOptionHandler(std::shared_ptr(new UnkOptionHandler(mModuleLabel))); + + // register handler for deprecated options DeprecatedOptionHandler + // none just yet + + // get option flags + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(opt.show_layout, {"--showlayout"}))); + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(opt.show_keydata, { "--showkeys" }))); + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(opt.verbose, {"-v", "--verbose"}))); + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(opt.verify, {"-y", "--verify"}))); + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(opt.is_dev, {"-d", "--dev"}))); + + // process input file type + opts.registerOptionHandler(std::shared_ptr(new FileTypeOptionHandler(infile.filetype, { "-t", "--intype" }))); + + // get user-provided keydata + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(mKeysetPath, {"-k", "--keyset"}))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamAesKeyOptionHandler(mTitleKey, {"--titlekey"}))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamAesKeyOptionHandler(mBodyKey, {"--bodykey"}))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(mTikPath, {"--tik"}))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(mCertPath, {"--cert"}))); + + // code options + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(code.list_api, { "--listapi" }))); + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(code.list_symbols, { "--listsym" }))); + opts.registerOptionHandler(std::shared_ptr(new InstructionTypeOptionHandler(code.is_64bit_instruction, { "--insttype" }))); + + // fs options + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(fs.show_fs_tree, { "--listfs" }))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(fs.extract_path, { "--fsdir" }))); + + // xci options + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.update_extract_path, { "--update" }))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.normal_extract_path, { "--normal" }))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.secure_extract_path, { "--secure" }))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.logo_extract_path, { "--logo" }))); + + // nca options + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part0_extract_path, { "--part0" }))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part1_extract_path, { "--part1" }))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part2_extract_path, { "--part2" }))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part3_extract_path, { "--part3" }))); + + // kip options + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(kip.extract_path, { "--kipdir" }))); + + // aset options + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(aset.icon_extract_path, { "--icon" }))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(aset.nacp_extract_path, { "--nacp" }))); + + + // process option + opts.processOptions(args, 1, args.size() - 2); +} + +void nstool::SettingsInitializer::determine_filetype() +{ + //std::string infile_path_str; + //tc::io::PathUtil::pathToUnixUTF8(infile.path.get(), infile_path_str); + //fmt::print("infile path = \"{}\"\n", infile_path_str); + + auto file = tc::io::StreamSource(std::make_shared(tc::io::FileStream(infile.path.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read))); + + auto raw_data = file.pullData(0, 0x5000); + +#define _TYPE_PTR(st) ((st*)(raw_data.data())) +#define _ASSERT_FILE_SIZE(sz) (file.length() >= (sz)) + + // do easy tests + + // detect "scene" XCI + if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sGcHeader_Rsa2048Signed)) + && _TYPE_PTR(nn::hac::sGcHeader_Rsa2048Signed)->header.st_magic.unwrap() == nn::hac::gc::kGcHeaderStructMagic) + { + infile.filetype = FILE_TYPE_GAMECARD; + } + // detect "SDK" XCI + else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sSdkGcHeader)) + && _TYPE_PTR(nn::hac::sSdkGcHeader)->signed_header.header.st_magic.unwrap() == nn::hac::gc::kGcHeaderStructMagic) + { + infile.filetype = FILE_TYPE_GAMECARD; + } + // detect PFS0 + else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sPfsHeader)) + && _TYPE_PTR(nn::hac::sPfsHeader)->st_magic.unwrap() == nn::hac::pfs::kPfsStructMagic) + { + infile.filetype = FILE_TYPE_PARTITIONFS; + } + // detect HFS0 + else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sPfsHeader)) + && _TYPE_PTR(nn::hac::sPfsHeader)->st_magic.unwrap() == nn::hac::pfs::kHashedPfsStructMagic) + { + infile.filetype = FILE_TYPE_PARTITIONFS; + } + // detect ROMFS + else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sRomfsHeader)) + && _TYPE_PTR(nn::hac::sRomfsHeader)->header_size.unwrap() == sizeof(nn::hac::sRomfsHeader) + && _TYPE_PTR(nn::hac::sRomfsHeader)->sections[1].offset.unwrap() == (_TYPE_PTR(nn::hac::sRomfsHeader)->sections[0].offset.unwrap() + _TYPE_PTR(nn::hac::sRomfsHeader)->sections[0].size.unwrap())) + { + infile.filetype = FILE_TYPE_ROMFS; + } + // detect NPDM + else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sMetaHeader)) + && _TYPE_PTR(nn::hac::sMetaHeader)->st_magic.unwrap() == nn::hac::meta::kMetaStructMagic) + { + infile.filetype = FILE_TYPE_META; + } + // detect NSO + else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sNsoHeader)) + && _TYPE_PTR(nn::hac::sNsoHeader)->st_magic.unwrap() == nn::hac::nso::kNsoStructMagic) + { + infile.filetype = FILE_TYPE_NSO; + } + // detect NRO + else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sNroHeader)) + && _TYPE_PTR(nn::hac::sNroHeader)->st_magic.unwrap() == nn::hac::nro::kNroStructMagic) + { + infile.filetype = FILE_TYPE_NRO; + } + // detect INI + else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sIniHeader)) + && _TYPE_PTR(nn::hac::sIniHeader)->st_magic.unwrap() == nn::hac::ini::kIniStructMagic) + { + infile.filetype = FILE_TYPE_INI; + } + // detect KIP + else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sKipHeader)) + && _TYPE_PTR(nn::hac::sKipHeader)->st_magic.unwrap() == nn::hac::kip::kKipStructMagic) + { + infile.filetype = FILE_TYPE_KIP; + } + // detect HB ASET + else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sAssetHeader)) + && _TYPE_PTR(nn::hac::sAssetHeader)->st_magic.unwrap() == nn::hac::aset::kAssetStructMagic) + { + infile.filetype = FILE_TYPE_KIP; + } + + // more complicated tests + + // detect NCA + else if (determineValidNcaFromSample(raw_data)) + { + infile.filetype = FILE_TYPE_NCA; + } + // detect Certificate + else if (determineValidEsCertFromSample(raw_data)) + { + infile.filetype = FILE_TYPE_PKI_CERT; + } + // detect Ticket + else if (determineValidEsTikFromSample(raw_data)) + { + infile.filetype = FILE_TYPE_ES_TIK; + } + // detect Ticket + else if (determineValidCnmtFromSample(raw_data)) + { + infile.filetype = FILE_TYPE_CNMT; + } + // detect Ticket + else if (determineValidNacpFromSample(raw_data)) + { + infile.filetype = FILE_TYPE_NACP; + } +#undef _TYPE_PTR +#undef _ASSERT_FILE_SIZE +} + +void nstool::SettingsInitializer::usage_text() +{ + fmt::print("{:s} v{:d}.{:d}.{:d} (C) {:s}\n", APP_NAME, VER_MAJOR, VER_MINOR, VER_PATCH, AUTHORS); + fmt::print("Built: {:s} {:s}\n\n", __TIME__, __DATE__); + fmt::print("Usage: {:s} [options... ] \n", BIN_NAME); + fmt::print("\n General Options:\n"); + fmt::print(" -d, --dev Use devkit keyset.\n"); + fmt::print(" -k, --keyset Specify keyset file.\n"); + fmt::print(" -t, --type Specify input file type. [xci, pfs, romfs, nca, meta, cnmt, nso, nro, ini, kip, nacp, aset, cert, tik]\n"); + fmt::print(" -y, --verify Verify file.\n"); + fmt::print("\n Output Options:\n"); + fmt::print(" --showkeys Show keys generated.\n"); + fmt::print(" --showlayout Show layout metadata.\n"); + fmt::print(" -v, --verbose Verbose output.\n"); + fmt::print("\n XCI (GameCard Image)\n"); + fmt::print(" {:s} [--listfs] [--update --logo --normal --secure ] <.xci file>\n", BIN_NAME); + fmt::print(" --listfs Print file system in embedded partitions.\n"); + fmt::print(" --update Extract \"update\" partition to directory.\n"); + fmt::print(" --logo Extract \"logo\" partition to directory.\n"); + fmt::print(" --normal Extract \"normal\" partition to directory.\n"); + fmt::print(" --secure Extract \"secure\" partition to directory.\n"); + fmt::print("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n"); + fmt::print(" {:s} [--listfs] [--fsdir ] \n", BIN_NAME); + fmt::print(" --listfs Print file system.\n"); + fmt::print(" --fsdir Extract file system to directory.\n"); + fmt::print("\n NCA (Nintendo Content Archive)\n"); + fmt::print(" {:s} [--listfs] [--bodykey --titlekey ] [--part0 ...] <.nca file>\n", BIN_NAME); + fmt::print(" --listfs Print file system in embedded partitions.\n"); + fmt::print(" --titlekey Specify title key extracted from ticket.\n"); + fmt::print(" --bodykey Specify body encryption key.\n"); + fmt::print(" --tik Specify ticket to source title key.\n"); + fmt::print(" --cert Specify certificate chain to verify ticket.\n"); + fmt::print(" --part0 Extract \"partition 0\" to directory.\n"); + fmt::print(" --part1 Extract \"partition 1\" to directory.\n"); + fmt::print(" --part2 Extract \"partition 2\" to directory.\n"); + fmt::print(" --part3 Extract \"partition 3\" to directory.\n"); + fmt::print("\n NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)\n"); + fmt::print(" {:s} [--listapi --listsym] [--insttype ] \n", BIN_NAME); + fmt::print(" --listapi Print SDK API List.\n"); + fmt::print(" --listsym Print Code Symbols.\n"); + fmt::print(" --insttype Specify instruction type [64bit|32bit] (64bit is assumed).\n"); + fmt::print("\n INI (Initial Process List Blob)\n"); + fmt::print(" {:s} [--kipdir ] \n", BIN_NAME); + fmt::print(" --kipdir Extract embedded KIPs to directory.\n"); + fmt::print("\n ASET (Homebrew Asset Blob)\n"); + fmt::print(" {:s} [--listfs] [--icon --nacp --fsdir ] \n", BIN_NAME); + fmt::print(" --listfs Print filesystem in embedded RomFS partition.\n"); + fmt::print(" --icon Extract icon partition to file.\n"); + fmt::print(" --nacp Extract NACP partition to file.\n"); + fmt::print(" --fsdir Extract RomFS partition to directory.\n"); +} + +bool nstool::SettingsInitializer::determineValidNcaFromSample(const tc::ByteData& sample) const +{ + if (sample.size() < nn::hac::nca::kHeaderSize) + { + return false; + } + + if (opt.keybag.nca_header_key.isNull()) + { + fmt::print("[WARNING] Failed to load NCA Header Key.\n"); + return false; + } + + nn::hac::detail::aes128_xtskey_t key = opt.keybag.nca_header_key.get(); + + //fmt::print("NCA header key: {} {}\n", tc::cli::FormatUtil::formatBytesAsString(opt.keybag.nca_header_key.get()[0].data(), opt.keybag.nca_header_key.get()[0].size(), true, ""), tc::cli::FormatUtil::formatBytesAsString(opt.keybag.nca_header_key.get()[1].data(), opt.keybag.nca_header_key.get()[1].size(), true, "")); + + // init aes-xts + tc::crypto::Aes128XtsEncryptor enc; + enc.initialize(key[0].data(), key[0].size(), key[1].data(), key[1].size(), nn::hac::nca::kSectorSize, false); + + // decrypt main header + byte_t raw_hdr[nn::hac::nca::kSectorSize]; + enc.decrypt(raw_hdr, sample.data() + nn::hac::ContentArchiveUtil::sectorToOffset(1), nn::hac::nca::kSectorSize, 1); + nn::hac::sContentArchiveHeader* hdr = (nn::hac::sContentArchiveHeader*)(raw_hdr); + + /* + fmt::print("NCA Header Raw:\n"); + fmt::print("{:s}\n", tc::cli::FormatUtil::formatBytesAsHxdHexString(sample.data() + nn::hac::ContentArchiveUtil::sectorToOffset(1), nn::hac::nca::kSectorSize)); + fmt::print("{:s}\n", tc::cli::FormatUtil::formatBytesAsHxdHexString(raw_hdr, nn::hac::nca::kSectorSize)); + */ + + if (hdr->st_magic.unwrap() != nn::hac::nca::kNca2StructMagic && hdr->st_magic.unwrap() != nn::hac::nca::kNca3StructMagic) + { + return false; + } + + return true; +} + +bool nstool::SettingsInitializer::determineValidCnmtFromSample(const tc::ByteData& sample) const +{ + if (sample.size() < sizeof(nn::hac::sContentMetaHeader)) + return false; + + const nn::hac::sContentMetaHeader* data = (const nn::hac::sContentMetaHeader*)sample.data(); + + size_t minimum_size = sizeof(nn::hac::sContentMetaHeader) + data->exhdr_size.unwrap() + data->content_count.unwrap() * sizeof(nn::hac::sContentInfo) + data->content_meta_count.unwrap() * sizeof(nn::hac::sContentMetaInfo) + nn::hac::cnmt::kDigestLen; + + if (sample.size() < minimum_size) + return false; + + // include exthdr/data check if applicable + if (data->exhdr_size.unwrap() > 0) + { + if (data->type == (byte_t)nn::hac::cnmt::ContentMetaType::Application) + { + const nn::hac::sApplicationMetaExtendedHeader* meta = (const nn::hac::sApplicationMetaExtendedHeader*)(sample.data() + sizeof(nn::hac::sContentMetaHeader)); + if ((meta->patch_id.unwrap() & data->id.unwrap()) != data->id.unwrap()) + return false; + } + else if (data->type == (byte_t)nn::hac::cnmt::ContentMetaType::Patch) + { + const nn::hac::sPatchMetaExtendedHeader* meta = (const nn::hac::sPatchMetaExtendedHeader*)(sample.data() + sizeof(nn::hac::sContentMetaHeader)); + if ((meta->application_id.unwrap() & data->id.unwrap()) != meta->application_id.unwrap()) + return false; + + minimum_size += meta->extended_data_size.unwrap(); + } + else if (data->type == (byte_t)nn::hac::cnmt::ContentMetaType::AddOnContent) + { + const nn::hac::sAddOnContentMetaExtendedHeader* meta = (const nn::hac::sAddOnContentMetaExtendedHeader*)(sample.data() + sizeof(nn::hac::sContentMetaHeader)); + if ((meta->application_id.unwrap() & data->id.unwrap()) != meta->application_id.unwrap()) + return false; + } + else if (data->type == (byte_t)nn::hac::cnmt::ContentMetaType::Delta) + { + const nn::hac::sDeltaMetaExtendedHeader* meta = (const nn::hac::sDeltaMetaExtendedHeader*)(sample.data() + sizeof(nn::hac::sContentMetaHeader)); + if ((meta->application_id.unwrap() & data->id.unwrap()) != meta->application_id.unwrap()) + return false; + + minimum_size += meta->extended_data_size.unwrap(); + } + else if (data->type == (byte_t)nn::hac::cnmt::ContentMetaType::SystemUpdate) + { + const nn::hac::sSystemUpdateMetaExtendedHeader* meta = (const nn::hac::sSystemUpdateMetaExtendedHeader*)(sample.data() + sizeof(nn::hac::sContentMetaHeader)); + + minimum_size += meta->extended_data_size.unwrap(); + } + } + + if (sample.size() != minimum_size) + return false; + + return true; +} + +bool nstool::SettingsInitializer::determineValidNacpFromSample(const tc::ByteData& sample) const +{ + if (sample.size() != sizeof(nn::hac::sApplicationControlProperty)) + return false; + + const nn::hac::sApplicationControlProperty* data = (const nn::hac::sApplicationControlProperty*)sample.data(); + + if (data->logo_type > (byte_t)nn::hac::nacp::LogoType::Nintendo) + return false; + + if (data->display_version[0] == 0) + return false; + + if (data->user_account_save_data_size.unwrap() == 0 && data->user_account_save_data_journal_size.unwrap() != 0) + return false; + + if (data->user_account_save_data_journal_size.unwrap() == 0 && data->user_account_save_data_size.unwrap() != 0) + return false; + + if (*((uint32_t*)(&data->supported_language_flag)) == 0) + return false; + + return true; +} + +bool nstool::SettingsInitializer::determineValidEsCertFromSample(const tc::ByteData& sample) const +{ + nn::pki::SignatureBlock sign; + + try + { + sign.fromBytes(sample.data(), sample.size()); + } + catch (...) + { + return false; + } + + if (sign.isLittleEndian() == true) + return false; + + if (sign.getSignType() != nn::pki::sign::SIGN_ID_RSA4096_SHA256 && sign.getSignType() != nn::pki::sign::SIGN_ID_RSA2048_SHA256 && sign.getSignType() != nn::pki::sign::SIGN_ID_ECDSA240_SHA256) + return false; + + return true; +} + +bool nstool::SettingsInitializer::determineValidEsTikFromSample(const tc::ByteData& sample) const +{ + nn::pki::SignatureBlock sign; + + try + { + sign.fromBytes(sample.data(), sample.size()); + } + catch (...) + { + return false; + } + + if (sign.isLittleEndian() == false) + return false; + + if (sign.getSignType() != nn::pki::sign::SIGN_ID_RSA2048_SHA256) + return false; + + const nn::es::sTicketBody_v2* body = (const nn::es::sTicketBody_v2*)(sample.data() + sign.getBytes().size()); + + if ((body->issuer.str().substr(0, 5) == "Root-" + && body->issuer.str().substr(16, 2) == "XS") == false) + return false; + + return true; +} \ No newline at end of file diff --git a/src/Settings.h b/src/Settings.h new file mode 100644 index 0000000..75564c1 --- /dev/null +++ b/src/Settings.h @@ -0,0 +1,155 @@ +#pragma once +#include "types.h" +#include +#include +#include +#include + +#include "KeyBag.h" + +namespace nstool { + +struct Settings +{ + enum FileType + { + FILE_TYPE_ERROR, + FILE_TYPE_GAMECARD, + FILE_TYPE_NSP, + FILE_TYPE_PARTITIONFS, + FILE_TYPE_ROMFS, + FILE_TYPE_NCA, + FILE_TYPE_META, + FILE_TYPE_CNMT, + FILE_TYPE_NSO, + FILE_TYPE_NRO, + FILE_TYPE_NACP, + FILE_TYPE_INI, + FILE_TYPE_KIP, + FILE_TYPE_PKI_CERT, + FILE_TYPE_ES_TIK, + FILE_TYPE_HB_ASSET, + }; + + struct InputFileOptions + { + FileType filetype; + tc::Optional path; + } infile; + + struct Options + { + CliOutputMode cli_output_mode; + bool verify; + bool is_dev; + KeyBag keybag; + } opt; + + // code options + struct CodeOptions + { + bool list_api; + bool list_symbols; + bool is_64bit_instruction; // true=64bit, false=32bit + } code; + + // Generic FS options + struct FsOptions + { + bool show_fs_tree; + tc::Optional extract_path; + } fs; + + // XCI options + struct XciOptions + { + tc::Optional update_extract_path; + tc::Optional logo_extract_path; + tc::Optional normal_extract_path; + tc::Optional secure_extract_path; + } xci; + + // NCA options + struct NcaOptions + { + tc::Optional part0_extract_path; + tc::Optional part1_extract_path; + tc::Optional part2_extract_path; + tc::Optional part3_extract_path; + } nca; + + // KIP options + struct KipOptions + { + tc::Optional extract_path; + } kip; + + // ASET Options + struct AsetOptions + { + tc::Optional icon_extract_path; + tc::Optional nacp_extract_path; + } aset; + + Settings() + { + infile.filetype = FILE_TYPE_ERROR; + infile.path = tc::Optional(); + + opt.cli_output_mode = CliOutputMode(); + opt.verify = false; + opt.is_dev = false; + opt.keybag = KeyBag(); + + code.list_api = false; + code.list_symbols = false; + code.is_64bit_instruction = true; + + xci.update_extract_path = tc::Optional(); + xci.logo_extract_path = tc::Optional(); + xci.normal_extract_path = tc::Optional(); + xci.secure_extract_path = tc::Optional(); + + fs.show_fs_tree = false; + fs.extract_path = tc::Optional(); + + nca.part0_extract_path = tc::Optional(); + nca.part1_extract_path = tc::Optional(); + nca.part2_extract_path = tc::Optional(); + nca.part3_extract_path = tc::Optional(); + + kip.extract_path = tc::Optional(); + + aset.icon_extract_path = tc::Optional(); + aset.nacp_extract_path = tc::Optional(); + } +}; + +class SettingsInitializer : public Settings +{ +public: + SettingsInitializer(const std::vector& args); +private: + void parse_args(const std::vector& args); + void determine_filetype(); + void usage_text(); + + std::string mModuleLabel; + + bool mShowLayout; + bool mShowKeydata; + + tc::Optional mKeysetPath; + tc::Optional mTitleKey; + tc::Optional mBodyKey; + tc::Optional mTikPath; + tc::Optional mCertPath; + + bool determineValidNcaFromSample(const tc::ByteData& raw_data) const; + bool determineValidEsCertFromSample(const tc::ByteData& raw_data) const; + bool determineValidEsTikFromSample(const tc::ByteData& raw_data) const; + bool determineValidCnmtFromSample(const tc::ByteData& raw_data) const; + bool determineValidNacpFromSample(const tc::ByteData& raw_data) const; +}; + +} \ No newline at end of file diff --git a/src/UserSettings.cpp b/src/UserSettings.cpp deleted file mode 100644 index 3356974..0000000 --- a/src/UserSettings.cpp +++ /dev/null @@ -1,1086 +0,0 @@ -#include "UserSettings.h" -#include "version.h" -#include "PkiValidator.h" -#include "KeyConfiguration.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -UserSettings::UserSettings() -{} - -void UserSettings::parseCmdArgs(const std::vector& arg_list) -{ - sCmdArgs args; - populateCmdArgs(arg_list, args); - populateKeyset(args); - populateUserSettings(args); - if (_HAS_BIT(mOutputMode, OUTPUT_KEY_DATA)) - dumpKeyConfig(); -} - -void UserSettings::showHelp() -{ - printf("%s v%d.%d.%d (C) %s\n", APP_NAME, VER_MAJOR, VER_MINOR, VER_PATCH, AUTHORS); - printf("Built: %s %s\n\n", __TIME__, __DATE__); - - printf("Usage: %s [options... ] \n", BIN_NAME); - printf("\n General Options:\n"); - printf(" -d, --dev Use devkit keyset.\n"); - printf(" -k, --keyset Specify keyset file.\n"); - printf(" -t, --type Specify input file type. [xci, pfs, romfs, nca, meta, cnmt, nso, nro, ini, kip, nacp, aset, cert, tik]\n"); - printf(" -y, --verify Verify file.\n"); - printf("\n Output Options:\n"); - printf(" --showkeys Show keys generated.\n"); - printf(" --showlayout Show layout metadata.\n"); - printf(" -v, --verbose Verbose output.\n"); - printf("\n XCI (GameCard Image)\n"); - printf(" %s [--listfs] [--update --logo --normal --secure ] <.xci file>\n", BIN_NAME); - printf(" --listfs Print file system in embedded partitions.\n"); - printf(" --update Extract \"update\" partition to directory.\n"); - printf(" --logo Extract \"logo\" partition to directory.\n"); - printf(" --normal Extract \"normal\" partition to directory.\n"); - printf(" --secure Extract \"secure\" partition to directory.\n"); - printf("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n"); - printf(" %s [--listfs] [--fsdir ] \n", BIN_NAME); - printf(" --listfs Print file system.\n"); - printf(" --fsdir Extract file system to directory.\n"); - printf("\n NCA (Nintendo Content Archive)\n"); - printf(" %s [--listfs] [--bodykey --titlekey ] [--part0 ...] <.nca file>\n", BIN_NAME); - printf(" --listfs Print file system in embedded partitions.\n"); - printf(" --titlekey Specify title key extracted from ticket.\n"); - printf(" --bodykey Specify body encryption key.\n"); - printf(" --tik Specify ticket to source title key.\n"); - printf(" --cert Specify certificate chain to verify ticket.\n"); - printf(" --part0 Extract \"partition 0\" to directory.\n"); - printf(" --part1 Extract \"partition 1\" to directory.\n"); - printf(" --part2 Extract \"partition 2\" to directory.\n"); - printf(" --part3 Extract \"partition 3\" to directory.\n"); - printf("\n NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)\n"); - printf(" %s [--listapi --listsym] [--insttype ] \n", BIN_NAME); - printf(" --listapi Print SDK API List.\n"); - printf(" --listsym Print Code Symbols.\n"); - printf(" --insttype Specify instruction type [64bit|32bit] (64bit is assumed).\n"); - printf("\n INI (Initial Process List Blob)\n"); - printf(" %s [--kipdir ] \n", BIN_NAME); - printf(" --kipdir Extract embedded KIPs to directory.\n"); - printf("\n ASET (Homebrew Asset Blob)\n"); - printf(" %s [--listfs] [--icon --nacp --fsdir ] \n", BIN_NAME); - printf(" --listfs Print filesystem in embedded RomFS partition.\n"); - printf(" --icon Extract icon partition to file.\n"); - printf(" --nacp Extract NACP partition to file.\n"); - printf(" --fsdir Extract RomFS partition to directory.\n"); - -} - -const std::string UserSettings::getInputPath() const -{ - return mInputPath; -} - -const KeyConfiguration& UserSettings::getKeyCfg() const -{ - return mKeyCfg; -} - -FileType UserSettings::getFileType() const -{ - return mFileType; -} - -bool UserSettings::isVerifyFile() const -{ - return mVerifyFile; -} - -CliOutputMode UserSettings::getCliOutputMode() const -{ - return mOutputMode; -} - -bool UserSettings::isListFs() const -{ - return mListFs; -} - -bool UserSettings::isListApi() const -{ - return mListApi; -} -bool UserSettings::isListSymbols() const -{ - return mListSymbols; -} - -bool UserSettings::getIs64BitInstruction() const -{ - return mIs64BitInstruction; -} - -const sOptional& UserSettings::getXciUpdatePath() const -{ - return mXciUpdatePath; -} - -const sOptional& UserSettings::getXciLogoPath() const -{ - return mXciLogoPath; -} - -const sOptional& UserSettings::getXciNormalPath() const -{ - return mXciNormalPath; -} - -const sOptional& UserSettings::getXciSecurePath() const -{ - return mXciSecurePath; -} - -const sOptional& UserSettings::getFsPath() const -{ - return mFsPath; -} - -const sOptional& UserSettings::getNcaPart0Path() const -{ - return mNcaPart0Path; -} - -const sOptional& UserSettings::getNcaPart1Path() const -{ - return mNcaPart1Path; -} - -const sOptional& UserSettings::getNcaPart2Path() const -{ - return mNcaPart2Path; -} - -const sOptional& UserSettings::getNcaPart3Path() const -{ - return mNcaPart3Path; -} - -const sOptional& UserSettings::getKipExtractPath() const -{ - return mKipExtractPath; -} - -const sOptional& UserSettings::getAssetIconPath() const -{ - return mAssetIconPath; -} - -const sOptional& UserSettings::getAssetNacpPath() const -{ - return mAssetNacpPath; -} - -const fnd::List>& UserSettings::getCertificateChain() const -{ - return mCertChain; -} - -void UserSettings::populateCmdArgs(const std::vector& arg_list, sCmdArgs& cmd_args) -{ - // show help text - if (arg_list.size() < 2) - { - showHelp(); - throw fnd::Exception(kModuleName, "Not enough arguments."); - } - - cmd_args.input_path = arg_list.back(); - - for (size_t i = 1; i < arg_list.size(); i++) - { - if (arg_list[i] == "-h" || arg_list[i] == "--help") - { - showHelp(); - throw fnd::Exception(kModuleName, "Nothing to do."); - } - } - - for (size_t i = 1; i+1 < arg_list.size(); i++) - { - bool hasParamter = arg_list[i+1][0] != '-' && i+2 < arg_list.size(); - - if (arg_list[i] == "-d" || arg_list[i] == "--dev") - { - if (hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " does not take a parameter."); - cmd_args.devkit_keys = true; - } - - else if (arg_list[i] == "-y" || arg_list[i] == "--verify") - { - if (hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " does not take a parameter."); - cmd_args.verify_file = true; - } - - else if (arg_list[i] == "--showkeys") - { - if (hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " does not take a parameter."); - cmd_args.show_keys = true; - } - - else if (arg_list[i] == "--showlayout") - { - if (hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " does not take a parameter."); - cmd_args.show_layout = true; - } - - else if (arg_list[i] == "-v" || arg_list[i] == "--verbose") - { - if (hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " does not take a parameter."); - cmd_args.verbose_output = true; - } - - else if (arg_list[i] == "-k" || arg_list[i] == "--keyset") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.keyset_path = arg_list[i+1]; - } - - else if (arg_list[i] == "-t" || arg_list[i] == "--type") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.file_type = arg_list[i+1]; - } - - else if (arg_list[i] == "--listfs") - { - if (hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " does not take a parameter."); - cmd_args.list_fs = true; - } - - else if (arg_list[i] == "--update") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.update_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--normal") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.normal_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--secure") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.secure_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--logo") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.logo_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--fsdir") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.fs_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--titlekey") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.nca_titlekey = arg_list[i+1]; - } - - else if (arg_list[i] == "--bodykey") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.nca_bodykey = arg_list[i+1]; - } - - else if (arg_list[i] == "--tik") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.ticket_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--cert") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.cert_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--part0") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.part0_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--part1") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.part1_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--part2") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.part2_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--part3") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.part3_path = arg_list[i+1]; - } - - else if (arg_list[i] == "--listapi") - { - if (hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " does not take a parameter."); - cmd_args.list_api = true; - } - - else if (arg_list[i] == "--listsym") - { - if (hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " does not take a parameter."); - cmd_args.list_sym = true; - } - - else if (arg_list[i] == "--insttype") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.inst_type = arg_list[i + 1]; - } - - else if (arg_list[i] == "--kipdir") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.kip_extract_path = arg_list[i + 1]; - } - - else if (arg_list[i] == "--icon") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.asset_icon_path = arg_list[i + 1]; - } - - else if (arg_list[i] == "--nacp") - { - if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); - cmd_args.asset_nacp_path = arg_list[i + 1]; - } - - else - { - throw fnd::Exception(kModuleName, arg_list[i] + " is not recognised."); - } - - i += hasParamter; - } -} - -void UserSettings::populateKeyset(sCmdArgs& args) -{ - if (args.keyset_path.isSet) - { - mKeyCfg.importHactoolGenericKeyfile(*args.keyset_path); - } - else - { - // open other resource files in $HOME/.switch/prod.keys (or $HOME/.switch/dev.keys if -d/--dev is set). - std::string keyset_path; - getSwitchPath(keyset_path); - if (keyset_path.empty()) - return; - - fnd::io::appendToPath(keyset_path, kGeneralKeyfileName[args.devkit_keys.isSet]); - - try - { - mKeyCfg.importHactoolGenericKeyfile(keyset_path); - } - catch (const fnd::Exception&) - { - return; - } - - } - - - - if (args.nca_bodykey.isSet) - { - fnd::aes::sAes128Key tmp_key; - fnd::Vec tmp_raw; - fnd::SimpleTextOutput::stringToArray(args.nca_bodykey.var, tmp_raw); - if (tmp_raw.size() != sizeof(fnd::aes::sAes128Key)) - throw fnd::Exception(kModuleName, "Key: \"--bodykey\" has incorrect length"); - memcpy(tmp_key.key, tmp_raw.data(), 16); - mKeyCfg.addNcaExternalContentKey(kDummyRightsIdForUserBodyKey, tmp_key); - } - - if (args.nca_titlekey.isSet) - { - fnd::aes::sAes128Key tmp_key; - fnd::Vec tmp_raw; - fnd::SimpleTextOutput::stringToArray(args.nca_titlekey.var, tmp_raw); - if (tmp_raw.size() != sizeof(fnd::aes::sAes128Key)) - throw fnd::Exception(kModuleName, "Key: \"--titlekey\" has incorrect length"); - memcpy(tmp_key.key, tmp_raw.data(), 16); - mKeyCfg.addNcaExternalContentKey(kDummyRightsIdForUserTitleKey, tmp_key); - } - - // import certificate chain - if (args.cert_path.isSet) - { - fnd::SimpleFile cert_file; - fnd::Vec cert_raw; - nn::pki::SignedData cert; - - cert_file.open(args.cert_path.var, fnd::SimpleFile::Read); - cert_raw.alloc(cert_file.size()); - cert_file.read(cert_raw.data(), cert_raw.size()); - - for (size_t i = 0; i < cert_raw.size(); i+= cert.getBytes().size()) - { - cert.fromBytes(cert_raw.data() + i, cert_raw.size() - i); - mCertChain.addElement(cert); - } - } - - // get titlekey from ticket - if (args.ticket_path.isSet) - { - fnd::SimpleFile tik_file; - fnd::Vec tik_raw; - nn::pki::SignedData tik; - - // open and import ticket - tik_file.open(args.ticket_path.var, fnd::SimpleFile::Read); - tik_raw.alloc(tik_file.size()); - tik_file.read(tik_raw.data(), tik_raw.size()); - tik.fromBytes(tik_raw.data(), tik_raw.size()); - - // validate ticket signature - if (mCertChain.size() > 0) - { - PkiValidator pki_validator; - fnd::Vec tik_hash; - - switch (nn::pki::sign::getHashAlgo(tik.getSignature().getSignType())) - { - case (nn::pki::sign::HASH_ALGO_SHA1): - tik_hash.alloc(fnd::sha::kSha1HashLen); - fnd::sha::Sha1(tik.getBody().getBytes().data(), tik.getBody().getBytes().size(), tik_hash.data()); - break; - case (nn::pki::sign::HASH_ALGO_SHA256): - tik_hash.alloc(fnd::sha::kSha256HashLen); - fnd::sha::Sha256(tik.getBody().getBytes().data(), tik.getBody().getBytes().size(), tik_hash.data()); - break; - } - - try - { - pki_validator.setKeyCfg(mKeyCfg); - pki_validator.addCertificates(mCertChain); - pki_validator.validateSignature(tik.getBody().getIssuer(), tik.getSignature().getSignType(), tik.getSignature().getSignature(), tik_hash); - } - catch (const fnd::Exception& e) - { - std::cout << "[WARNING] Ticket signature could not be validated (" << e.error() << ")" << std::endl; - } - - } - - // extract title key - if (tik.getBody().getTitleKeyEncType() == nn::es::ticket::AES128_CBC) - { - fnd::aes::sAes128Key enc_title_key; - memcpy(enc_title_key.key, tik.getBody().getEncTitleKey(), 16); - fnd::aes::sAes128Key common_key, external_content_key; - if (mKeyCfg.getETicketCommonKey(nn::hac::ContentArchiveUtil::getMasterKeyRevisionFromKeyGeneration(tik.getBody().getCommonKeyId()), common_key) == true) - { - nn::hac::AesKeygen::generateKey(external_content_key.key, tik.getBody().getEncTitleKey(), common_key.key); - mKeyCfg.addNcaExternalContentKey(tik.getBody().getRightsId(), external_content_key); - } - else - { - std::cout << "[WARNING] Titlekey not imported from ticket because commonkey was not available" << std::endl; - } - } - else - { - std::cout << "[WARNING] Titlekey not imported from ticket because it is personalised" << std::endl; - } - } -} - -void UserSettings::populateUserSettings(sCmdArgs& args) -{ - // check invalid input - if (args.input_path.isSet == false) - throw fnd::Exception(kModuleName, "No input file specified"); - - // save arguments - mInputPath = *args.input_path; - mVerifyFile = args.verify_file.isSet; - mListFs = args.list_fs.isSet; - mXciUpdatePath = args.update_path; - mXciNormalPath = args.normal_path; - mXciSecurePath = args.secure_path; - mXciLogoPath = args.logo_path; - - mFsPath = args.fs_path; - mNcaPart0Path = args.part0_path; - mNcaPart1Path = args.part1_path; - mNcaPart2Path = args.part2_path; - mNcaPart3Path = args.part3_path; - - mKipExtractPath = args.kip_extract_path; - - // determine the architecture type for NSO/NRO - if (args.inst_type.isSet) - mIs64BitInstruction = getIs64BitInstructionFromString(*args.inst_type); - else - mIs64BitInstruction = true; // default 64bit - - mListApi = args.list_api.isSet; - mListSymbols = args.list_sym.isSet; - - mAssetIconPath = args.asset_icon_path; - mAssetNacpPath = args.asset_nacp_path; - - // determine output mode - mOutputMode = _BIT(OUTPUT_BASIC); - if (args.verbose_output.isSet) - { - mOutputMode |= _BIT(OUTPUT_KEY_DATA); - mOutputMode |= _BIT(OUTPUT_LAYOUT); - mOutputMode |= _BIT(OUTPUT_EXTENDED); - } - if (args.show_keys.isSet) - { - mOutputMode |= _BIT(OUTPUT_KEY_DATA); - } - if (args.show_layout.isSet) - { - mOutputMode |= _BIT(OUTPUT_LAYOUT); - } - - // determine input file type - if (args.file_type.isSet) - mFileType = getFileTypeFromString(*args.file_type); - else - mFileType = determineFileTypeFromFile(mInputPath); - - // check is the input file could be identified - if (mFileType == FILE_INVALID) - throw fnd::Exception(kModuleName, "Unknown file type."); -} - -FileType UserSettings::getFileTypeFromString(const std::string& type_str) -{ - std::string str = type_str; - std::transform(str.begin(), str.end(), str.begin(), ::tolower); - - FileType type; - if (str == "gc" || str == "gamecard" || str == "xci") - type = FILE_GAMECARD; - else if (str == "nsp") - type = FILE_NSP; - else if (str == "partitionfs" || str == "hashedpartitionfs" \ - || str == "pfs" || str == "pfs0" \ - || str == "hfs" || str == "hfs0") - type = FILE_PARTITIONFS; - else if (str == "romfs") - type = FILE_ROMFS; - else if (str == "nca" || str == "contentarchive") - type = FILE_NCA; - else if (str == "meta" || str == "npdm") - type = FILE_META; - else if (str == "cnmt") - type = FILE_CNMT; - else if (str == "nso") - type = FILE_NSO; - else if (str == "nro") - type = FILE_NRO; - else if (str == "ini") - type = FILE_INI; - else if (str == "kip") - type = FILE_KIP; - else if (str == "nacp") - type = FILE_NACP; - else if (str == "cert") - type = FILE_PKI_CERT; - else if (str == "tik") - type = FILE_ES_TIK; - else if (str == "aset" || str == "asset") - type = FILE_HB_ASSET; - else - type = FILE_INVALID; - - return type; -} - -FileType UserSettings::determineFileTypeFromFile(const std::string& path) -{ - static const size_t kMaxReadSize = 0x5000; - FileType file_type = FILE_INVALID; - fnd::SimpleFile file; - fnd::Vec scratch; - - // open file - file.open(path, file.Read); - - // read file - scratch.alloc(_MIN(kMaxReadSize, file.size())); - file.read(scratch.data(), 0, scratch.size()); - // close file - file.close(); - - // _TYPE_PTR resolves to a pointer of type 'st' located at scratch.data() -#define _TYPE_PTR(st) ((st*)(scratch.data())) -#define _ASSERT_SIZE(sz) (scratch.size() >= (sz)) - - // test gamecard - 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_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 - else if (_ASSERT_SIZE(sizeof(nn::hac::sPfsHeader)) && _TYPE_PTR(nn::hac::sPfsHeader)->st_magic.get() == nn::hac::pfs::kPfsStructMagic) - file_type = FILE_PARTITIONFS; - // test hfs0 - else if (_ASSERT_SIZE(sizeof(nn::hac::sPfsHeader)) && _TYPE_PTR(nn::hac::sPfsHeader)->st_magic.get() == nn::hac::pfs::kHashedPfsStructMagic) - file_type = FILE_PARTITIONFS; - // test romfs - else if (_ASSERT_SIZE(sizeof(nn::hac::sRomfsHeader)) && _TYPE_PTR(nn::hac::sRomfsHeader)->header_size.get() == sizeof(nn::hac::sRomfsHeader) && _TYPE_PTR(nn::hac::sRomfsHeader)->sections[1].offset.get() == (_TYPE_PTR(nn::hac::sRomfsHeader)->sections[0].offset.get() + _TYPE_PTR(nn::hac::sRomfsHeader)->sections[0].size.get())) - file_type = FILE_ROMFS; - // test npdm - else if (_ASSERT_SIZE(sizeof(nn::hac::sMetaHeader)) && _TYPE_PTR(nn::hac::sMetaHeader)->st_magic.get() == nn::hac::meta::kMetaStructMagic) - file_type = FILE_META; - // test nca - else if (determineValidNcaFromSample(scratch)) - file_type = FILE_NCA; - // test nso - else if (_ASSERT_SIZE(sizeof(nn::hac::sNsoHeader)) && _TYPE_PTR(nn::hac::sNsoHeader)->st_magic.get() == nn::hac::nso::kNsoStructMagic) - file_type = FILE_NSO; - // test nro - else if (_ASSERT_SIZE(sizeof(nn::hac::sNroHeader)) && _TYPE_PTR(nn::hac::sNroHeader)->st_magic.get() == nn::hac::nro::kNroStructMagic) - file_type = FILE_NRO; - // test ini - else if (_ASSERT_SIZE(sizeof(nn::hac::sIniHeader)) && _TYPE_PTR(nn::hac::sIniHeader)->st_magic.get() == nn::hac::ini::kIniStructMagic) - file_type = FILE_INI; - // test kip - else if (_ASSERT_SIZE(sizeof(nn::hac::sKipHeader)) && _TYPE_PTR(nn::hac::sKipHeader)->st_magic.get() == nn::hac::kip::kKipStructMagic) - file_type = FILE_KIP; - // test pki certificate - else if (determineValidEsCertFromSample(scratch)) - file_type = FILE_PKI_CERT; - // test ticket - else if (determineValidEsTikFromSample(scratch)) - file_type = FILE_ES_TIK; - // test hb asset - else if (_ASSERT_SIZE(sizeof(nn::hac::sAssetHeader)) && _TYPE_PTR(nn::hac::sAssetHeader)->st_magic.get() == nn::hac::aset::kAssetStructMagic) - file_type = FILE_HB_ASSET; - - // do heuristics - // test cnmt - else if (determineValidCnmtFromSample(scratch)) - file_type = FILE_CNMT; - // test nacp - else if (determineValidNacpFromSample(scratch)) - file_type = FILE_NACP; - - - // else unrecognised - else - file_type = FILE_INVALID; - -#undef _ASSERT_SIZE -#undef _TYPE_PTR - - return file_type; -} - -bool UserSettings::determineValidNcaFromSample(const fnd::Vec& sample) const -{ - // prepare decrypted NCA data - byte_t nca_raw[nn::hac::nca::kHeaderSize]; - nn::hac::sContentArchiveHeader* nca_header = (nn::hac::sContentArchiveHeader*)(nca_raw + nn::hac::ContentArchiveUtil::sectorToOffset(1)); - - if (sample.size() < nn::hac::nca::kHeaderSize) - return false; - - fnd::aes::sAesXts128Key header_key; - mKeyCfg.getContentArchiveHeaderKey(header_key); - nn::hac::ContentArchiveUtil::decryptContentArchiveHeader(sample.data(), nca_raw, header_key); - - if (nca_header->st_magic.get() != nn::hac::nca::kNca2StructMagic && nca_header->st_magic.get() != nn::hac::nca::kNca3StructMagic) - return false; - - return true; -} - -bool UserSettings::determineValidCnmtFromSample(const fnd::Vec& sample) const -{ - if (sample.size() < sizeof(nn::hac::sContentMetaHeader)) - return false; - - const nn::hac::sContentMetaHeader* data = (const nn::hac::sContentMetaHeader*)sample.data(); - - size_t minimum_size = sizeof(nn::hac::sContentMetaHeader) + data->exhdr_size.get() + data->content_count.get() * sizeof(nn::hac::sContentInfo) + data->content_meta_count.get() * sizeof(nn::hac::sContentMetaInfo) + nn::hac::cnmt::kDigestLen; - - if (sample.size() < minimum_size) - return false; - - // include exthdr/data check if applicable - if (data->exhdr_size.get() > 0) - { - if (data->type == (byte_t)nn::hac::cnmt::ContentMetaType::Application) - { - const nn::hac::sApplicationMetaExtendedHeader* meta = (const nn::hac::sApplicationMetaExtendedHeader*)(sample.data() + sizeof(nn::hac::sContentMetaHeader)); - if ((meta->patch_id.get() & data->id.get()) != data->id.get()) - return false; - } - else if (data->type == (byte_t)nn::hac::cnmt::ContentMetaType::Patch) - { - const nn::hac::sPatchMetaExtendedHeader* meta = (const nn::hac::sPatchMetaExtendedHeader*)(sample.data() + sizeof(nn::hac::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 == (byte_t)nn::hac::cnmt::ContentMetaType::AddOnContent) - { - const nn::hac::sAddOnContentMetaExtendedHeader* meta = (const nn::hac::sAddOnContentMetaExtendedHeader*)(sample.data() + sizeof(nn::hac::sContentMetaHeader)); - if ((meta->application_id.get() & data->id.get()) != meta->application_id.get()) - return false; - } - else if (data->type == (byte_t)nn::hac::cnmt::ContentMetaType::Delta) - { - const nn::hac::sDeltaMetaExtendedHeader* meta = (const nn::hac::sDeltaMetaExtendedHeader*)(sample.data() + sizeof(nn::hac::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 == (byte_t)nn::hac::cnmt::ContentMetaType::SystemUpdate) - { - const nn::hac::sSystemUpdateMetaExtendedHeader* meta = (const nn::hac::sSystemUpdateMetaExtendedHeader*)(sample.data() + sizeof(nn::hac::sContentMetaHeader)); - - minimum_size += meta->extended_data_size.get(); - } - } - - if (sample.size() != minimum_size) - return false; - - return true; -} - -bool UserSettings::determineValidNacpFromSample(const fnd::Vec& sample) const -{ - if (sample.size() != sizeof(nn::hac::sApplicationControlProperty)) - return false; - - const nn::hac::sApplicationControlProperty* data = (const nn::hac::sApplicationControlProperty*)sample.data(); - - if (data->logo_type > (byte_t)nn::hac::nacp::LogoType::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; -} - -bool UserSettings::determineValidEsCertFromSample(const fnd::Vec& sample) const -{ - nn::pki::SignatureBlock sign; - - try - { - sign.fromBytes(sample.data(), sample.size()); - } - catch (...) - { - return false; - } - - if (sign.isLittleEndian() == true) - return false; - - if (sign.getSignType() != nn::pki::sign::SIGN_ID_RSA4096_SHA256 && sign.getSignType() != nn::pki::sign::SIGN_ID_RSA2048_SHA256 && sign.getSignType() != nn::pki::sign::SIGN_ID_ECDSA240_SHA256) - return false; - - return true; -} - -bool UserSettings::determineValidEsTikFromSample(const fnd::Vec& sample) const -{ - nn::pki::SignatureBlock sign; - - try - { - sign.fromBytes(sample.data(), sample.size()); - } - catch (...) - { - return false; - } - - if (sign.isLittleEndian() == false) - return false; - - if (sign.getSignType() != nn::pki::sign::SIGN_ID_RSA2048_SHA256) - return false; - - return true; -} - -bool UserSettings::getIs64BitInstructionFromString(const std::string & type_str) -{ - std::string str = type_str; - std::transform(str.begin(), str.end(), str.begin(), ::tolower); - - bool flag; - if (str == "32bit") - flag = false; - else if (str == "64bit") - flag = true; - else - throw fnd::Exception(kModuleName, "Unsupported instruction type: " + str); - - return flag; -} - -void UserSettings::getHomePath(std::string& path) const -{ - // open other resource files in $HOME/.switch/prod.keys (or $HOME/.switch/dev.keys if -d/--dev is set). - path.clear(); - if (path.empty()) fnd::io::getEnvironVar(path, "HOME"); - if (path.empty()) fnd::io::getEnvironVar(path, "USERPROFILE"); - if (path.empty()) return; -} - -void UserSettings::getSwitchPath(std::string& path) const -{ - std::string home; - home.clear(); - getHomePath(home); - if (home.empty()) - return; - - path.clear(); - fnd::io::appendToPath(path, home); - fnd::io::appendToPath(path, kHomeSwitchDirStr); -} - -void UserSettings::dumpKeyConfig() const -{ - fnd::aes::sAes128Key aes_key; - fnd::aes::sAesXts128Key aesxts_key; - fnd::rsa::sRsa2048Key rsa2048_key; - fnd::rsa::sRsa4096Key rsa4096_key; - - const std::string kKeyIndex[kMasterKeyNum] = {"00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f"}; - - - std::cout << "[KeyConfiguration]" << std::endl; - std::cout << " NCA Keys:" << std::endl; - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getContentArchiveHeader0SignKey(rsa2048_key, byte_t(i)) == true) - dumpRsa2048Key(rsa2048_key, "Header0-SignatureKey-" + kKeyIndex[i], 2); - } - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getAcidSignKey(rsa2048_key, byte_t(i)) == true) - dumpRsa2048Key(rsa2048_key, "Acid-SignatureKey-" + kKeyIndex[i], 2); - } - - if (mKeyCfg.getContentArchiveHeaderKey(aesxts_key) == true) - dumpAesXtsKey(aesxts_key, "Header-EncryptionKey", 2); - - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 0, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKey-Application-" + kKeyIndex[i], 2); - if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 1, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKey-Ocean-" + kKeyIndex[i], 2); - if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 2, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKey-System-" + kKeyIndex[i], 2); - } - - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 0, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-Application-" + kKeyIndex[i], 2); - if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 1, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-Ocean-" + kKeyIndex[i], 2); - if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 2, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-System-" + kKeyIndex[i], 2); - } - - std::cout << " NRR Keys:" << std::endl; - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getNrrCertificateSignKey(rsa2048_key, byte_t(i)) == true) - dumpRsa2048Key(rsa2048_key, "Certificate-SignatureKey-" + kKeyIndex[i], 2); - } - - std::cout << " XCI Keys:" << std::endl; - if (mKeyCfg.getXciHeaderSignKey(rsa2048_key) == true) - dumpRsa2048Key(rsa2048_key, "Header-SignatureKey", 2); - if (mKeyCfg.getXciHeaderKey(aes_key) == true) - dumpAesKey(aes_key, "ExtendedHeader-EncryptionKey", 2); - - - - - std::cout << " Package1 Keys:" << std::endl; - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getPkg1Key(byte_t(i), aes_key) == true) - dumpAesKey(aes_key, "EncryptionKey-" + kKeyIndex[i], 2); - } - - std::cout << " Package2 Keys:" << std::endl; - if (mKeyCfg.getPkg2SignKey(rsa2048_key) == true) - dumpRsa2048Key(rsa2048_key, "Signature Key", 2); - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getPkg2Key(byte_t(i), aes_key) == true) - dumpAesKey(aes_key, "EncryptionKey-" + kKeyIndex[i], 2); - } - - std::cout << " ETicket Keys:" << std::endl; - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getETicketCommonKey(byte_t(i), aes_key) == true) - dumpAesKey(aes_key, "CommonKey-" + kKeyIndex[i], 2); - } - - if (mKeyCfg.getPkiRootSignKey("Root", rsa4096_key) == true) - dumpRsa4096Key(rsa4096_key, "NNPKI Root Key", 1); -} - -void UserSettings::dumpRsa2048Key(const fnd::rsa::sRsa2048Key& key, const std::string& name, size_t indent) const -{ - std::string indent_str; - - indent_str.clear(); - for (size_t i = 0; i < indent; i++) - { - indent_str += " "; - } - - std::cout << indent_str << name << ":" << std::endl; - if (key.modulus[0] != 0x00 && key.modulus[1] != 0x00) - { - std::cout << indent_str << " Modulus:" << std::endl; - for (size_t i = 0; i < 0x10; i++) - { - std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.modulus + i * 0x10, 0x10, true, ":") << std::endl; - } - } - if (key.priv_exponent[0] != 0x00 && key.priv_exponent[1] != 0x00) - { - std::cout << indent_str << " Private Exponent:" << std::endl; - for (size_t i = 0; i < 0x10; i++) - { - std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.priv_exponent + i * 0x10, 0x10, true, ":") << std::endl; - } - } -} - -void UserSettings::dumpRsa4096Key(const fnd::rsa::sRsa4096Key& key, const std::string& name, size_t indent) const -{ - std::string indent_str; - - indent_str.clear(); - for (size_t i = 0; i < indent; i++) - { - indent_str += " "; - } - - std::cout << indent_str << name << ":" << std::endl; - if (key.modulus[0] != 0x00 && key.modulus[1] != 0x00) - { - std::cout << indent_str << " Modulus:" << std::endl; - for (size_t i = 0; i < 0x20; i++) - { - std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.modulus + i * 0x10, 0x10, true, ":") << std::endl; - } - } - if (key.priv_exponent[0] != 0x00 && key.priv_exponent[1] != 0x00) - { - std::cout << indent_str << " Private Exponent:" << std::endl; - for (size_t i = 0; i < 0x20; i++) - { - std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.priv_exponent + i * 0x10, 0x10, true, ":") << std::endl; - } - } -} - -void UserSettings::dumpAesKey(const fnd::aes::sAes128Key& key, const std::string& name, size_t indent) const -{ - std::string indent_str; - - indent_str.clear(); - for (size_t i = 0; i < indent; i++) - { - indent_str += " "; - } - - std::cout << indent_str << name << ":" << std::endl; - std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.key, 0x10, true, ":") << std::endl; -} - -void UserSettings::dumpAesXtsKey(const fnd::aes::sAesXts128Key& key, const std::string& name, size_t indent) const -{ - std::string indent_str; - - indent_str.clear(); - for (size_t i = 0; i < indent; i++) - { - indent_str += " "; - } - - std::cout << indent_str << name << ":" << std::endl; - std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.key[0], 0x20, true, ":") << std::endl; -} \ No newline at end of file diff --git a/src/UserSettings.h b/src/UserSettings.h deleted file mode 100644 index 379fb9b..0000000 --- a/src/UserSettings.h +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include "common.h" -#include "KeyConfiguration.h" - -class UserSettings -{ -public: - UserSettings(); - - void parseCmdArgs(const std::vector& arg_list); - void showHelp(); - - // generic options - const std::string getInputPath() const; - const KeyConfiguration& getKeyCfg() const; - FileType getFileType() const; - bool isVerifyFile() const; - CliOutputMode getCliOutputMode() const; - - // specialised toggles - bool isListFs() const; - bool isListApi() const; - bool isListSymbols() const; - bool getIs64BitInstruction() const; - - // specialised paths - const sOptional& getXciUpdatePath() const; - const sOptional& getXciLogoPath() const; - const sOptional& getXciNormalPath() const; - const sOptional& getXciSecurePath() const; - const sOptional& getFsPath() const; - const sOptional& getNcaPart0Path() const; - const sOptional& getNcaPart1Path() const; - const sOptional& getNcaPart2Path() const; - const sOptional& getNcaPart3Path() const; - const sOptional& getKipExtractPath() const; - const sOptional& getAssetIconPath() const; - const sOptional& getAssetNacpPath() const; - const fnd::List>& getCertificateChain() const; - -private: - const std::string kModuleName = "UserSettings"; - - const std::string kHomeSwitchDirStr = ".switch"; - const std::string kGeneralKeyfileName[2] = { "prod.keys", "dev.keys" }; - const std::string kTitleKeyfileName = "title.keys"; - - - struct sCmdArgs - { - sCmdArgs() {} - sOptional input_path; - sOptional devkit_keys; - sOptional keyset_path; - sOptional file_type; - sOptional verify_file; - sOptional show_keys; - sOptional show_layout; - sOptional verbose_output; - sOptional list_fs; - sOptional update_path; - sOptional logo_path; - sOptional normal_path; - sOptional secure_path; - sOptional fs_path; - sOptional nca_titlekey; - sOptional nca_bodykey; - sOptional ticket_path; - sOptional cert_path; - sOptional part0_path; - sOptional part1_path; - sOptional part2_path; - sOptional part3_path; - sOptional kip_extract_path; - sOptional list_api; - sOptional list_sym; - sOptional inst_type; - sOptional asset_icon_path; - sOptional asset_nacp_path; - }; - - std::string mInputPath; - FileType mFileType; - KeyConfiguration mKeyCfg; - bool mVerifyFile; - CliOutputMode mOutputMode; - - bool mListFs; - sOptional mXciUpdatePath; - sOptional mXciLogoPath; - sOptional mXciNormalPath; - sOptional mXciSecurePath; - sOptional mFsPath; - - sOptional mNcaPart0Path; - sOptional mNcaPart1Path; - sOptional mNcaPart2Path; - sOptional mNcaPart3Path; - - sOptional mKipExtractPath; - - sOptional mAssetIconPath; - sOptional mAssetNacpPath; - - fnd::List> mCertChain; - - bool mListApi; - bool mListSymbols; - bool mIs64BitInstruction; - - void populateCmdArgs(const std::vector& arg_list, sCmdArgs& cmd_args); - void populateKeyset(sCmdArgs& args); - void populateUserSettings(sCmdArgs& args); - FileType getFileTypeFromString(const std::string& type_str); - FileType determineFileTypeFromFile(const std::string& path); - bool determineValidNcaFromSample(const fnd::Vec& sample) const; - bool determineValidCnmtFromSample(const fnd::Vec& sample) const; - bool determineValidNacpFromSample(const fnd::Vec& sample) const; - bool determineValidEsCertFromSample(const fnd::Vec& sample) const; - bool determineValidEsTikFromSample(const fnd::Vec& sample) const; - bool getIs64BitInstructionFromString(const std::string& type_str); - void getHomePath(std::string& path) const; - void getSwitchPath(std::string& path) const; - - void dumpKeyConfig() const; - void dumpRsa2048Key(const fnd::rsa::sRsa2048Key& key, const std::string& name, size_t indent) const; - void dumpRsa4096Key(const fnd::rsa::sRsa4096Key& key, const std::string& name, size_t indent) const; - void dumpAesKey(const fnd::aes::sAes128Key& key, const std::string& name, size_t indent) const; - void dumpAesXtsKey(const fnd::aes::sAesXts128Key& key, const std::string& name, size_t indent) const; -}; \ No newline at end of file diff --git a/src/common.h b/src/common.h deleted file mode 100644 index 8625a8f..0000000 --- a/src/common.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -static const size_t kMasterKeyNum = 0x20; -static const size_t kNcaKeakNum = nn::hac::nca::kKeyAreaEncryptionKeyNum; - -enum FileType -{ - FILE_GAMECARD, - FILE_NSP, - FILE_PARTITIONFS, - FILE_ROMFS, - FILE_NCA, - FILE_META, - FILE_CNMT, - FILE_NSO, - FILE_NRO, - FILE_NACP, - FILE_INI, - FILE_KIP, - FILE_PKI_CERT, - FILE_ES_TIK, - FILE_HB_ASSET, - FILE_INVALID = -1, -}; - -enum CliOutputModeFlag -{ - OUTPUT_BASIC, - OUTPUT_LAYOUT, - OUTPUT_KEY_DATA, - OUTPUT_EXTENDED -}; - -typedef byte_t CliOutputMode; - -template -struct sOptional -{ - bool isSet; - T var; - inline sOptional() : isSet(false) {} - inline sOptional(const T& other) : isSet(true), var(other) {} - inline sOptional(const sOptional& other) : isSet(other.isSet), var(other.var) {} - inline const T& operator=(const T& other) { isSet = true; var = other; return var; } - inline const sOptional& operator=(const sOptional& other) - { - isSet = other.isSet; - if (isSet) { - var = other.var; - } - return *this; - } - inline T& operator*() { return var; } -}; - -const byte_t kDummyRightsIdForUserTitleKey[nn::hac::nca::kRightsIdLen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -const byte_t kDummyRightsIdForUserBodyKey[nn::hac::nca::kRightsIdLen] = {0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe}; \ No newline at end of file diff --git a/src/elf.h b/src/elf.h new file mode 100644 index 0000000..0fe891c --- /dev/null +++ b/src/elf.h @@ -0,0 +1,458 @@ +#pragma once +#include "types.h" + +namespace nstool +{ + namespace elf + { + /* These constants are for the segment types stored in the image headers */ + enum SegmentType + { + PT_NULL = 0, + PT_LOAD = 1, + PT_DYNAMIC = 2, + PT_INTERP = 3, + PT_NOTE = 4, + PT_SHLIB = 5, + PT_PHDR = 6, + PT_TLS = 7, /* Thread local storage segment */ + PT_LOOS = 0x60000000, /* OS-specific */ + PT_HIOS = 0x6fffffff, /* OS-specific */ + PT_LOPROC = 0x70000000, + PT_HIPROC = 0x7fffffff + }; + + /* These constants define the different elf file types */ + enum ElfType + { + ET_NONE = 0, + ET_REL = 1, + ET_EXEC = 2, + ET_DYN = 3, + ET_CORE = 4, + ET_LOPROC = 0xff00, + ET_HIPROC = 0xffff + }; + + /* This is the info that is needed to parse the dynamic section of the file */ + enum DynamicSectionType + { + DT_NULL = 0, + DT_NEEDED = 1, + DT_PLTRELSZ = 2, + DT_PLTGOT = 3, + DT_HASH = 4, + DT_STRTAB = 5, + DT_SYMTAB = 6, + DT_RELA = 7, + DT_RELASZ = 8, + DT_RELAENT = 9, + DT_STRSZ = 10, + DT_SYMENT = 11, + DT_INIT = 12, + DT_FINI = 13, + DT_SONAME = 14, + DT_RPATH = 15, + DT_SYMBOLIC = 16, + DT_REL = 17, + DT_RELSZ = 18, + DT_RELENT = 19, + DT_PLTREL = 20, + DT_DEBUG = 21, + DT_TEXTREL = 22, + DT_JMPREL = 23, + DT_ENCODING = 32, + OLD_DT_LOOS = 0x60000000, + DT_LOOS = 0x6000000d, + DT_HIOS = 0x6ffff000, + DT_VALRNGLO = 0x6ffffd00, + DT_VALRNGHI = 0x6ffffdff, + DT_ADDRRNGLO = 0x6ffffe00, + DT_ADDRRNGHI = 0x6ffffeff, + DT_VERSYM = 0x6ffffff0, + DT_RELACOUNT = 0x6ffffff9, + DT_RELCOUNT = 0x6ffffffa, + DT_FLAGS_1 = 0x6ffffffb, + DT_VERDEF = 0x6ffffffc, + DT_VERDEFNUM = 0x6ffffffd, + DT_VERNEED = 0x6ffffffe, + DT_VERNEEDNUM = 0x6fffffff, + OLD_DT_HIOS = 0x6fffffff, + DT_LOPROC = 0x70000000, + DT_HIPROC = 0x7fffffff + }; + + /* This info is needed when parsing the symbol table */ + enum SymbolBinding + { + STB_LOCAL = 0, + STB_GLOBAL = 1, + STB_WEAK = 2, + STB_LOOS = 10, + STB_HIOS = 12, + STB_LOPROC, + STB_HIPROC = 0xf + }; + + enum SymbolType + { + STT_NOTYPE = 0, + STT_OBJECT = 1, + STT_FUNC = 2, + STT_SECTION = 3, + STT_FILE = 4, + STT_COMMON = 5, + STT_TLS = 6, + STT_LOOS = 10, + STT_HIOS = 12, + STT_LOPROC, + STT_HIPROC = 0xf + }; + + /* These constants define the permissions on sections in the program + header, p_flags. */ + enum PermissionFlag + { + PF_R = 0x4, + PF_W = 0x2, + PF_X = 0x1 + }; + + /* sh_type */ + enum SectionHeaderType + { + SHT_NULL = 0, + SHT_PROGBITS = 1, + SHT_SYMTAB = 2, + SHT_STRTAB = 3, + SHT_RELA = 4, + SHT_HASH = 5, + SHT_DYNAMIC = 6, + SHT_NOTE = 7, + SHT_NOBITS = 8, + SHT_REL = 9, + SHT_SHLIB = 10, + SHT_DYNSYM = 11, + SHT_NUM = 12, + SHT_LOPROC = 0x70000000, + SHT_HIPROC = 0x7fffffff, + SHT_LOUSER = 0x80000000, + SHT_HIUSER = 0xffffffff + }; + + /* sh_flags */ + enum SectionHeaderFlag + { + SHF_WRITE = 0x1, + SHF_ALLOC = 0x2, + SHF_EXECINSTR = 0x4, + SHF_RELA_LIVEPATCH = 0x00100000, + SHF_RO_AFTER_INIT = 0x00200000, + SHF_MASKPROC = 0xf0000000 + }; + + /* special section indexes */ + enum SpecialSectionIndex + { + SHN_UNDEF = 0, + SHN_LORESERVE = 0xff00, + SHN_LOPROC = 0xff00, + SHN_HIPROC = 0xff1f, + SHN_LOOS = 0xff20, + SHN_HIOS = 0xff3f, + SHN_ABS = 0xfff1, + SHN_COMMON = 0xfff2, + SHN_HIRESERVE = 0xffff + }; + + enum ElfIdentIndex + { + EI_MAG0 = 0, /* e_ident[] indexes */ + EI_MAG1 = 1, + EI_MAG2 = 2, + EI_MAG3 = 3, + EI_CLASS = 4, + EI_DATA = 5, + EI_VERSION = 6, + EI_OSABI = 7, + EI_PAD = 8 + }; + + enum ElfClass + { + ELFCLASSNONE = 0, /* EI_CLASS */ + ELFCLASS32 = 1, + ELFCLASS64 = 2, + ELFCLASSNUM = 3 + }; + + enum ElfData + { + ELFDATANONE = 0, /* e_ident[EI_DATA] */ + ELFDATA2LSB = 1, + ELFDATA2MSB = 2 + }; + + enum ElfVersion + { + EV_NONE = 0, /* e_version, EI_VERSION */ + EV_CURRENT = 1, + EV_NUM = 2, + }; + + enum ElfOsAbi + { + ELFOSABI_NONE = 0, + ELFOSABI_LINUX =3 + }; + + + /* + * Notes used in ET_CORE. Architectures export some of the arch register sets + * using the corresponding note types via the PTRACE_GETREGSET and + * PTRACE_SETREGSET requests. + */ + enum NoteType + { + NT_PRSTATUS = 1, + NT_PRFPREG = 2, + NT_PRPSINFO = 3, + NT_TASKSTRUCT = 4, + NT_AUXV = 6, + /* + * Note to userspace developers: size of NT_SIGINFO note may increase + * in the future to accomodate more fields, don't assume it is fixed! + */ + NT_SIGINFO = 0x53494749, + NT_FILE = 0x46494c45, + NT_PRXFPREG = 0x46e62b7f, /* copied from gdb5.1/include/elf/common.h */ + NT_PPC_VMX = 0x100, /* PowerPC Altivec/VMX registers */ + NT_PPC_SPE = 0x101, /* PowerPC SPE/EVR registers */ + NT_PPC_VSX = 0x102, /* PowerPC VSX registers */ + NT_PPC_TAR = 0x103, /* Target Address Register */ + NT_PPC_PPR = 0x104, /* Program Priority Register */ + NT_PPC_DSCR = 0x105, /* Data Stream Control Register */ + NT_PPC_EBB = 0x106, /* Event Based Branch Registers */ + NT_PPC_PMU = 0x107, /* Performance Monitor Registers */ + NT_PPC_TM_CGPR = 0x108, /* TM checkpointed GPR Registers */ + NT_PPC_TM_CFPR = 0x109, /* TM checkpointed FPR Registers */ + NT_PPC_TM_CVMX = 0x10a, /* TM checkpointed VMX Registers */ + NT_PPC_TM_CVSX = 0x10b, /* TM checkpointed VSX Registers */ + NT_PPC_TM_SPR = 0x10c, /* TM Special Purpose Registers */ + NT_PPC_TM_CTAR = 0x10d, /* TM checkpointed Target Address Register */ + NT_PPC_TM_CPPR = 0x10e, /* TM checkpointed Program Priority Register */ + NT_PPC_TM_CDSCR = 0x10f, /* TM checkpointed Data Stream Control Register */ + NT_PPC_PKEY = 0x110, /* Memory Protection Keys registers */ + NT_386_TLS = 0x200, /* i386 TLS slots (struct user_desc) */ + NT_386_IOPERM = 0x201, /* x86 io permission bitmap (1=deny) */ + NT_X86_XSTATE = 0x202, /* x86 extended state using xsave */ + NT_S390_HIGH_GPRS = 0x300, /* s390 upper register halves */ + NT_S390_TIMER = 0x301, /* s390 timer register */ + NT_S390_TODCMP = 0x302, /* s390 TOD clock comparator register */ + NT_S390_TODPREG = 0x303, /* s390 TOD programmable register */ + NT_S390_CTRS = 0x304, /* s390 control registers */ + NT_S390_PREFIX = 0x305, /* s390 prefix register */ + NT_S390_LAST_BREAK = 0x306, /* s390 breaking event address */ + NT_S390_SYSTEM_CALL = 0x307, /* s390 system call restart data */ + NT_S390_TDB = 0x308, /* s390 transaction diagnostic block */ + NT_S390_VXRS_LOW = 0x309, /* s390 vector registers 0-15 upper half */ + NT_S390_VXRS_HIGH = 0x30a, /* s390 vector registers 16-31 */ + NT_S390_GS_CB = 0x30b, /* s390 guarded storage registers */ + NT_S390_GS_BC = 0x30c, /* s390 guarded storage broadcast control block */ + NT_S390_RI_CB = 0x30d, /* s390 runtime instrumentation */ + NT_ARM_VFP = 0x400, /* ARM VFP/NEON registers */ + NT_ARM_TLS = 0x401, /* ARM TLS register */ + NT_ARM_HW_BREAK = 0x402, /* ARM hardware breakpoint registers */ + NT_ARM_HW_WATCH = 0x403, /* ARM hardware watchpoint registers */ + NT_ARM_SYSTEM_CALL = 0x404, /* ARM system call number */ + NT_ARM_SVE = 0x405, /* ARM Scalable Vector Extension registers */ + NT_ARC_V2 = 0x600, /* ARCv2 accumulator/extra registers */ + NT_VMCOREDD = 0x700, /* Vmcore Device Dump Note */ + NT_MIPS_DSP = 0x800, /* MIPS DSP ASE registers */ + NT_MIPS_FP_MODE = 0x801, /* MIPS floating-point mode */ + }; + + static const size_t kEIdentSize = 0x10; + static const byte_t kElfMagic[sizeof(uint32_t)] = {0x7f, 'E', 'L', 'F'}; + + + inline byte_t get_elf_st_bind(byte_t st_info) { return st_info >> 4; } + inline byte_t get_elf_st_type(byte_t st_info) { return st_info & 0xf; } + inline byte_t get_elf_st_info(byte_t st_bind, byte_t st_type) { return (st_type & 0xf) | ((st_bind & 0xf) << 4);} + + /* The following are used with relocations */ + #define ELF32_R_SYM(x) ((x) >> 8) + #define ELF32_R_TYPE(x) ((x) & 0xff) + + #define ELF64_R_SYM(i) ((i) >> 32) + #define ELF64_R_TYPE(i) ((i) & 0xffffffff) + } + + struct Elf32_Dyn + { + int32_t d_tag; + union{ + int32_t d_val; + uint32_t d_ptr; + } d_un; + }; + + struct Elf64_Dyn + { + int64_t d_tag; /* entry tag value */ + union { + uint64_t d_val; + uint64_t d_ptr; + } d_un; + }; + + struct Elf32_Rel + { + uint32_t r_offset; + uint32_t r_info; + }; + + struct Elf64_Rel + { + uint64_t r_offset; /* Location at which to apply the action */ + uint64_t r_info; /* index and type of relocation */ + }; + + struct Elf32_Rela + { + uint32_t r_offset; + uint32_t r_info; + int32_t r_addend; + }; + + struct Elf64_Rela + { + uint64_t r_offset; /* Location at which to apply the action */ + uint64_t r_info; /* index and type of relocation */ + int64_t r_addend; /* Constant addend used to compute value */ + }; + + struct Elf32_Sym + { + uint32_t st_name; + uint32_t st_value; + uint32_t st_size; + byte_t st_info; + byte_t st_other; + uint16_t st_shndx; + }; + + struct Elf64_Sym + { + uint32_t st_name; /* Symbol name, index in string tbl */ + byte_t st_info; /* Type and binding attributes */ + byte_t st_other; /* No defined meaning, 0 */ + uint16_t st_shndx; /* Associated section index */ + uint64_t st_value; /* Value of the symbol */ + uint64_t st_size; /* Associated symbol size */ + }; + + struct Elf32_Ehdr + { + byte_t e_ident[elf::kEIdentSize]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint32_t e_entry; /* Entry point */ + uint32_t e_phoff; + uint32_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; + }; + + struct Elf64_Ehdr + { + byte_t e_ident[elf::kEIdentSize]; /* ELF "magic number" */ + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint64_t e_entry; /* Entry point virtual address */ + uint64_t e_phoff; /* Program header table file offset */ + uint64_t e_shoff; /* Section header table file offset */ + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; + }; + + struct Elf32_Phdr + { + uint32_t p_type; + uint32_t p_offset; + uint32_t p_vaddr; + uint32_t p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; + }; + + struct Elf64_Phdr + { + uint32_t p_type; + uint32_t p_flags; + uint64_t p_offset; /* Segment file offset */ + uint64_t p_vaddr; /* Segment virtual address */ + uint64_t p_paddr; /* Segment physical address */ + uint64_t p_filesz; /* Segment size in file */ + uint64_t p_memsz; /* Segment size in memory */ + uint64_t p_align; /* Segment alignment, file & memory */ + }; + + struct Elf32_Shdr + { + uint32_t sh_name; + uint32_t sh_type; + uint32_t sh_flags; + uint32_t sh_addr; + uint32_t sh_offset; + uint32_t sh_size; + uint32_t sh_link; + uint32_t sh_info; + uint32_t sh_addralign; + uint32_t sh_entsize; + }; + + struct Elf64_Shdr + { + uint32_t sh_name; /* Section name, index in string tbl */ + uint32_t sh_type; /* Type of section */ + uint64_t sh_flags; /* Miscellaneous section attributes */ + uint64_t sh_addr; /* Section virtual addr at execution */ + uint64_t sh_offset; /* Section file offset */ + uint64_t sh_size; /* Size of section in bytes */ + uint32_t sh_link; /* Index of another section */ + uint32_t sh_info; /* Additional section information */ + uint64_t sh_addralign; /* Section alignment */ + uint64_t sh_entsize; /* Entry size if section holds table */ + }; + + /* Note header in a PT_NOTE section */ + struct Elf32_Nhdr + { + uint32_t n_namesz; /* Name size */ + uint32_t n_descsz; /* Content size */ + uint32_t n_type; /* Content type */ + }; + + /* Note header in a PT_NOTE section */ + struct Elf64_Nhdr + { + uint32_t n_namesz; /* Name size */ + uint32_t n_descsz; /* Content size */ + uint32_t n_type; /* Content type */ + }; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index bad4a3e..4c6ba2f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,8 @@ -#include -#include -#include -#include -#include "UserSettings.h" +#include +#include +#include "Settings.h" + +/* #include "GameCardProcess.h" #include "PfsProcess.h" #include "RomfsProcess.h" @@ -17,129 +17,116 @@ #include "PkiCertProcess.h" #include "EsTikProcess.h" #include "AssetProcess.h" +*/ -#ifdef _WIN32 -int wmain(int argc, wchar_t** argv) -#else -int main(int argc, char** argv) -#endif +int umain(const std::vector& args, const std::vector& env) { - std::vector args; - for (size_t i = 0; i < (size_t)argc; i++) + try { -#ifdef _WIN32 - args.push_back(fnd::StringConv::ConvertChar16ToChar8(std::u16string((char16_t*)argv[i]))); -#else - args.push_back(argv[i]); -#endif - } + nstool::Settings set = nstool::SettingsInitializer(args); + + std::shared_ptr infile_stream = std::make_shared(tc::io::FileStream(set.infile.path.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read)); - UserSettings user_set; - try { - user_set.parseCmdArgs(args); - - fnd::SharedPtr inputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read)); - - if (user_set.getFileType() == FILE_GAMECARD) + if (set.infile.filetype == nstool::Settings::FILE_TYPE_GAMECARD) { GameCardProcess obj; - obj.setInputFile(inputFile); + obj.setInputFile(infile_stream); - obj.setKeyCfg(user_set.getKeyCfg()); + obj.setKeyCfg(set.opt.keybag); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); - if (user_set.getXciUpdatePath().isSet) - obj.setPartitionForExtract(nn::hac::gc::kUpdatePartitionStr, user_set.getXciUpdatePath().var); - if (user_set.getXciLogoPath().isSet) - obj.setPartitionForExtract(nn::hac::gc::kLogoPartitionStr, user_set.getXciLogoPath().var); - if (user_set.getXciNormalPath().isSet) - obj.setPartitionForExtract(nn::hac::gc::kNormalPartitionStr, user_set.getXciNormalPath().var); - if (user_set.getXciSecurePath().isSet) - obj.setPartitionForExtract(nn::hac::gc::kSecurePartitionStr, user_set.getXciSecurePath().var); + if (set.xci.update_extract_path.isSet()) + obj.setPartitionForExtract(nn::hac::gc::kUpdatePartitionStr, set.xci.update_extract_path.get()); + if (set.xci.logo_extract_path.isSet()) + obj.setPartitionForExtract(nn::hac::gc::kLogoPartitionStr, set.xci.logo_extract_path.get()); + if (user_set.getXciNormalPath().isSet()) + obj.setPartitionForExtract(nn::hac::gc::kNormalPartitionStr, user_set.getXciNormalPath().get()); + if (user_set.getXciSecurePath().isSet()) + obj.setPartitionForExtract(nn::hac::gc::kSecurePartitionStr, user_set.getXciSecurePath().get()); obj.setListFs(user_set.isListFs()); obj.process(); } - else if (user_set.getFileType() == FILE_PARTITIONFS || user_set.getFileType() == FILE_NSP) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PARTITIONFS || set.infile.filetype == nstool::Settings::FILE_TYPE_NSP) { PfsProcess obj; - obj.setInputFile(inputFile); + obj.setInputFile(infile_stream); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); - if (user_set.getFsPath().isSet) - obj.setExtractPath(user_set.getFsPath().var); + if (user_set.getFsPath().isSet()) + obj.setExtractPath(user_set.getFsPath().get()); obj.setListFs(user_set.isListFs()); obj.process(); } - else if (user_set.getFileType() == FILE_ROMFS) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ROMFS) { RomfsProcess obj; - obj.setInputFile(inputFile); + obj.setInputFile(infile_stream); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); - if (user_set.getFsPath().isSet) - obj.setExtractPath(user_set.getFsPath().var); + if (user_set.getFsPath().isSet()) + obj.setExtractPath(user_set.getFsPath().get()); obj.setListFs(user_set.isListFs()); obj.process(); } - else if (user_set.getFileType() == FILE_NCA) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NCA) { NcaProcess obj; - obj.setInputFile(inputFile); - obj.setKeyCfg(user_set.getKeyCfg()); + obj.setInputFile(infile_stream); + obj.setKeyCfg(set.opt.keybag); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); - if (user_set.getNcaPart0Path().isSet) - obj.setPartition0ExtractPath(user_set.getNcaPart0Path().var); - if (user_set.getNcaPart1Path().isSet) - obj.setPartition1ExtractPath(user_set.getNcaPart1Path().var); - if (user_set.getNcaPart2Path().isSet) - obj.setPartition2ExtractPath(user_set.getNcaPart2Path().var); - if (user_set.getNcaPart3Path().isSet) - obj.setPartition3ExtractPath(user_set.getNcaPart3Path().var); + if (user_set.getNcaPart0Path().isSet()) + obj.setPartition0ExtractPath(user_set.getNcaPart0Path().get()); + if (user_set.getNcaPart1Path().isSet()) + obj.setPartition1ExtractPath(user_set.getNcaPart1Path().get()); + if (user_set.getNcaPart2Path().isSet()) + obj.setPartition2ExtractPath(user_set.getNcaPart2Path().get()); + if (user_set.getNcaPart3Path().isSet()) + obj.setPartition3ExtractPath(user_set.getNcaPart3Path().get()); obj.setListFs(user_set.isListFs()); obj.process(); } - else if (user_set.getFileType() == FILE_META) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_META) { MetaProcess obj; - obj.setInputFile(inputFile); - obj.setKeyCfg(user_set.getKeyCfg()); + obj.setInputFile(infile_stream); + obj.setKeyCfg(set.opt.keybag); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); obj.process(); } - else if (user_set.getFileType() == FILE_CNMT) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT) { CnmtProcess obj; - obj.setInputFile(inputFile); + obj.setInputFile(infile_stream); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); obj.process(); } - else if (user_set.getFileType() == FILE_NSO) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NSO) { NsoProcess obj; - obj.setInputFile(inputFile); + obj.setInputFile(infile_stream); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); obj.setIs64BitInstruction(user_set.getIs64BitInstruction()); obj.setListApi(user_set.isListApi()); @@ -147,111 +134,162 @@ int main(int argc, char** argv) obj.process(); } - else if (user_set.getFileType() == FILE_NRO) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NRO) { NroProcess obj; - obj.setInputFile(inputFile); + obj.setInputFile(infile_stream); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); obj.setIs64BitInstruction(user_set.getIs64BitInstruction()); obj.setListApi(user_set.isListApi()); obj.setListSymbols(user_set.isListSymbols()); - if (user_set.getAssetIconPath().isSet) - obj.setAssetIconExtractPath(user_set.getAssetIconPath().var); - if (user_set.getAssetNacpPath().isSet) - obj.setAssetNacpExtractPath(user_set.getAssetNacpPath().var); + if (user_set.getAssetIconPath().isSet()) + obj.setAssetIconExtractPath(user_set.getAssetIconPath().get()); + if (user_set.getAssetNacpPath().isSet()) + obj.setAssetNacpExtractPath(user_set.getAssetNacpPath().get()); - if (user_set.getFsPath().isSet) - obj.setAssetRomfsExtractPath(user_set.getFsPath().var); + if (user_set.getFsPath().isSet()) + obj.setAssetRomfsExtractPath(user_set.getFsPath().get()); obj.setAssetListFs(user_set.isListFs()); obj.process(); } - else if (user_set.getFileType() == FILE_NACP) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NACP) { NacpProcess obj; - obj.setInputFile(inputFile); + obj.setInputFile(infile_stream); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); obj.process(); } - else if (user_set.getFileType() == FILE_INI) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_INI) { IniProcess obj; - obj.setInputFile(inputFile); + obj.setInputFile(infile_stream); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); - if (user_set.getKipExtractPath().isSet) - obj.setKipExtractPath(user_set.getKipExtractPath().var); + if (user_set.getKipExtractPath().isSet()) + obj.setKipExtractPath(user_set.getKipExtractPath().get()); obj.process(); } - else if (user_set.getFileType() == FILE_KIP) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_KIP) { KipProcess obj; - obj.setInputFile(inputFile); + obj.setInputFile(infile_stream); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); obj.process(); } - else if (user_set.getFileType() == FILE_PKI_CERT) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PKI_CERT) { PkiCertProcess obj; - obj.setInputFile(inputFile); - obj.setKeyCfg(user_set.getKeyCfg()); + obj.setInputFile(infile_stream); + obj.setKeyCfg(set.opt.keybag); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); obj.process(); } - else if (user_set.getFileType() == FILE_ES_TIK) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ES_TIK) { EsTikProcess obj; - obj.setInputFile(inputFile); - obj.setKeyCfg(user_set.getKeyCfg()); + obj.setInputFile(infile_stream); + obj.setKeyCfg(set.opt.keybag); obj.setCertificateChain(user_set.getCertificateChain()); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); obj.process(); } - else if (user_set.getFileType() == FILE_HB_ASSET) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_HB_ASSET) { AssetProcess obj; - obj.setInputFile(inputFile); + obj.setInputFile(infile_stream); obj.setCliOutputMode(user_set.getCliOutputMode()); - obj.setVerifyMode(user_set.isVerifyFile()); + obj.setVerifyMode(set.opt.verify); - if (user_set.getAssetIconPath().isSet) - obj.setIconExtractPath(user_set.getAssetIconPath().var); - if (user_set.getAssetNacpPath().isSet) - obj.setNacpExtractPath(user_set.getAssetNacpPath().var); + if (user_set.getAssetIconPath().isSet()) + obj.setIconExtractPath(user_set.getAssetIconPath().get()); + if (user_set.getAssetNacpPath().isSet()) + obj.setNacpExtractPath(user_set.getAssetNacpPath().get()); - if (user_set.getFsPath().isSet) - obj.setRomfsExtractPath(user_set.getFsPath().var); + if (user_set.getFsPath().isSet()) + obj.setRomfsExtractPath(user_set.getFsPath().get()); obj.setListFs(user_set.isListFs()); obj.process(); } - else + + switch (set.infile.filetype) { - throw fnd::Exception("main", "Unhandled file type"); + case nstool::Settings::FILE_TYPE_GAMECARD : + fmt::print("## FILE_TYPE_GAMECARD ##\n"); + break; + case nstool::Settings::FILE_TYPE_NSP : + fmt::print("## FILE_TYPE_NSP ##\n"); + break; + case nstool::Settings::FILE_TYPE_PARTITIONFS : + fmt::print("## FILE_TYPE_PARTITIONFS ##\n"); + break; + case nstool::Settings::FILE_TYPE_ROMFS : + fmt::print("## FILE_TYPE_ROMFS ##\n"); + break; + case nstool::Settings::FILE_TYPE_NCA : + fmt::print("## FILE_TYPE_NCA ##\n"); + break; + case nstool::Settings::FILE_TYPE_META : + fmt::print("## FILE_TYPE_META ##\n"); + break; + case nstool::Settings::FILE_TYPE_CNMT : + fmt::print("## FILE_TYPE_CNMT ##\n"); + break; + case nstool::Settings::FILE_TYPE_NSO : + fmt::print("## FILE_TYPE_NSO ##\n"); + break; + case nstool::Settings::FILE_TYPE_NRO : + fmt::print("## FILE_TYPE_NRO ##\n"); + break; + case nstool::Settings::FILE_TYPE_NACP : + fmt::print("## FILE_TYPE_NACP ##\n"); + break; + case nstool::Settings::FILE_TYPE_INI : + fmt::print("## FILE_TYPE_INI ##\n"); + break; + case nstool::Settings::FILE_TYPE_KIP : + fmt::print("## FILE_TYPE_KIP ##\n"); + break; + case nstool::Settings::FILE_TYPE_PKI_CERT : + fmt::print("## FILE_TYPE_PKI_CERT ##\n"); + break; + case nstool::Settings::FILE_TYPE_ES_TIK : + fmt::print("## FILE_TYPE_ES_TIK ##\n"); + break; + case nstool::Settings::FILE_TYPE_HB_ASSET : + fmt::print("## FILE_TYPE_HB_ASSET ##\n"); + break; + default: + fmt::print("## unknown({}) ##\n", (int)set.infile.filetype); + break; } + } - catch (const fnd::Exception& e) { - printf("\n\n%s\n", e.what()); + catch (tc::Exception& e) + { + fmt::print("[{0}{1}ERROR] {2}\n", e.module(), (strlen(e.module()) != 0 ? " ": ""), e.error()); + return 1; } return 0; } \ No newline at end of file diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..5721307 --- /dev/null +++ b/src/types.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include +#include +#include +#include + + +namespace nstool { + +struct CliOutputMode +{ + bool show_basic_info; + bool show_extended_info; + bool show_layout; + bool show_keydata; + + CliOutputMode() : show_basic_info(false), show_extended_info(false), show_layout(false), show_keydata(false) + {} + + CliOutputMode(bool show_basic_info, bool show_extended_info, bool show_layout, bool show_keydata) : show_basic_info(show_basic_info), show_extended_info(show_extended_info), show_layout(show_layout), show_keydata(show_keydata) + {} +}; + +} \ No newline at end of file diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..ccb157b --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,100 @@ +#include "util.h" + +#include +#include +#include + +#include +#include +#include + +inline bool isNotPrintable(char chr) { return isprint(chr) == false; } + +void nstool::processResFile(const std::shared_ptr& file, std::map& dict) +{ + if (file == nullptr || !file->canRead() || file->length() == 0) + { + return; + } + + std::stringstream in_stream; + + // populate string stream + tc::ByteData cache = tc::ByteData(0x1000); + file->seek(0, tc::io::SeekOrigin::Begin); + for (int64_t pos = 0; pos < file->length();) + { + size_t bytes_read = file->read(cache.data(), cache.size()); + + in_stream << std::string((char*)cache.data(), bytes_read); + + pos += tc::io::IOUtil::castSizeToInt64(bytes_read); + } + + // process stream + std::string line, key, value; + while (std::getline(in_stream, line)) + { + // read up to comment line + if (line.find(";") != std::string::npos) + line = line.substr(0, line.find(";")); + + // change chars to lower string + std::transform(line.begin(), line.end(), line.begin(), ::tolower); + + // strip whitespace + line.erase(std::remove(line.begin(), line.end(), ' '), line.end()); + + // strip nonprintable + line.erase(std::remove_if(line.begin(), line.end(), isNotPrintable), line.end()); + + // skip lines that don't have '=' + if (line.find("=") == std::string::npos) + continue; + + key = line.substr(0,line.find("=")); + value = line.substr(line.find("=")+1); + + // skip if key or value is empty + if (key.empty() || value.empty()) + continue; + + //std::cout << "[" + key + "]=(" + value + ")" << std::endl; + + dict[key] = value; + } + +} + +void nstool::writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size) +{ + writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileAccess::OpenOrCreate, tc::io::FileMode::Write)), cache_size); +} + + +void nstool::writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size) +{ + writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileAccess::OpenOrCreate, tc::io::FileMode::Write)), cache_size); +} + +void nstool::writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size) +{ + // iterate thru child files + tc::ByteData cache = tc::ByteData(cache_size); + size_t cache_read_len; + + in_stream->seek(0, tc::io::SeekOrigin::Begin); + out_stream->seek(0, tc::io::SeekOrigin::Begin); + for (int64_t remaining_data = in_stream->length(); remaining_data > 0;) + { + cache_read_len = in_stream->read(cache.data(), cache.size()); + if (cache_read_len == 0) + { + throw tc::io::IOException(mModuleLabel, "Failed to read from RomFs file."); + } + + out_stream->write(cache.data(), cache_read_len); + + remaining_data -= int64_t(cache_read_len); + } +} \ No newline at end of file diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..04e01fb --- /dev/null +++ b/src/util.h @@ -0,0 +1,13 @@ +#pragma once +#include "types.h" + +namespace nstool +{ + +void processResFile(const std::shared_ptr& file, std::map& dict); + +void writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size = 0x10000); +void writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size = 0x10000); +void writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size = 0x10000); + +} \ No newline at end of file diff --git a/src/version.h b/src/version.h index 25ed5b5..cdf2ade 100644 --- a/src/version.h +++ b/src/version.h @@ -2,6 +2,6 @@ #define APP_NAME "NSTool" #define BIN_NAME "nstool" #define VER_MAJOR 1 -#define VER_MINOR 4 -#define VER_PATCH 1 +#define VER_MINOR 6 +#define VER_PATCH 0 #define AUTHORS "jakcron" \ No newline at end of file From 0e39d482147ce8e99f2b2bd4d929d393498e8e9e Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 29 Sep 2021 13:00:01 +0800 Subject: [PATCH 006/119] Update Visual Studio project files. --- build/visualstudio/nstool/nstool.vcxproj | 36 ++++++ .../nstool/nstool.vcxproj.filters | 108 ++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index e4db0c3..05e9c1e 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -152,15 +152,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/visualstudio/nstool/nstool.vcxproj.filters b/build/visualstudio/nstool/nstool.vcxproj.filters index 44261e8..4aa666c 100644 --- a/build/visualstudio/nstool/nstool.vcxproj.filters +++ b/build/visualstudio/nstool/nstool.vcxproj.filters @@ -30,6 +30,60 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -44,5 +98,59 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file From 0f1623163891e019fe5d5bdbd652dc5fb123f30e Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 29 Sep 2021 18:28:49 +0800 Subject: [PATCH 007/119] Add getTruncatedBytesString() to utils. --- src/util.cpp | 18 ++++++++++++++++++ src/util.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/src/util.cpp b/src/util.cpp index ccb157b..8db21e2 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -97,4 +97,22 @@ void nstool::writeStreamToStream(const std::shared_ptr& in_stre remaining_data -= int64_t(cache_read_len); } +} + +std::string nstool::getTruncatedBytesString(const byte_t* data, size_t len, bool do_not_truncate) +{ + if (data == nullptr) { return fmt::format(""); } + + std::string str = ""; + + if (len <= 8 || do_not_truncate) + { + str = tc::cli::FormatUtil::formatBytesAsString(data, len, true, ""); + } + else + { + str = fmt::format("{:02X}{:02X}{:02X}{:02X}...{:02X}{:02X}{:02X}{:02X}", data[0], data[1], data[2], data[3], data[len-4], data[len-3], data[len-2], data[len-1]); + } + + return str; } \ No newline at end of file diff --git a/src/util.h b/src/util.h index 04e01fb..2611acc 100644 --- a/src/util.h +++ b/src/util.h @@ -10,4 +10,6 @@ void writeSubStreamToFile(const std::shared_ptr& in_stream, int void writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size = 0x10000); void writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size = 0x10000); +std::string getTruncatedBytesString(const byte_t* data, size_t len, bool do_not_truncate); + } \ No newline at end of file From 5c970a2986fef601096ad67ee3be915fb97cb2c8 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 30 Sep 2021 19:40:27 +0800 Subject: [PATCH 008/119] Prepared for future ecc signature support. --- src/KeyBag.cpp | 7 ++++--- src/KeyBag.h | 12 ++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 6e00cf0..49aff54 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -417,7 +417,7 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa // Save PKI Root Key if (pki_root_sign_key.isSet()) { - broadon_rsa_signer["Root"] = { tc::ByteData(), pki_root_sign_key.get() }; + broadon_signer["Root"] = { tc::ByteData(), nn::pki::sign::SIGN_ALGO_RSA4096, pki_root_sign_key.get() }; } } @@ -465,12 +465,13 @@ void nstool::KeyBagInitializer::importCertificateChain(const tc::io::Path& cert_ switch (cert.getBody().getPublicKeyType()) { case nn::pki::cert::PublicKeyType::RSA2048: - broadon_rsa_signer[cert_identity] = { cert.getBytes(), cert.getBody().getRsa2048PublicKey() }; + broadon_signer[cert_identity] = { cert.getBytes(), nn::pki::sign::SIGN_ALGO_RSA2048, cert.getBody().getRsa2048PublicKey() }; break; case nn::pki::cert::PublicKeyType::RSA4096: - broadon_rsa_signer[cert_identity] = { cert.getBytes(), cert.getBody().getRsa4096PublicKey() }; + broadon_signer[cert_identity] = { cert.getBytes(), nn::pki::sign::SIGN_ALGO_RSA4096, cert.getBody().getRsa4096PublicKey() }; break; case nn::pki::cert::PublicKeyType::ECDSA240: + // broadon_signer[cert_identity] = { cert.getBytes(), nn::pki::sign::SIGN_ALGO_ECDSA240, cert.getBody().getRsa4096PublicKey() }; fmt::print("[WARNING] Certificate {:s} will not be imported. ecc233 public keys are not supported yet.\n", cert_identity); break; default: diff --git a/src/KeyBag.h b/src/KeyBag.h index e319fb6..b83445d 100644 --- a/src/KeyBag.h +++ b/src/KeyBag.h @@ -15,6 +15,7 @@ struct KeyBag using aes128_key_t = nn::hac::detail::aes128_key_t; using aes128_xtskey_t = nn::hac::detail::aes128_xtskey_t; using rsa_key_t = tc::crypto::RsaKey; + //using ecc_key_t = tc::crypto::EccKey; using rights_id_t = nn::hac::detail::rights_id_t; using key_generation_t = byte_t; using broadon_issuer_t = std::string; @@ -51,13 +52,16 @@ struct KeyBag std::map etik_common_key; // BroadOn signer profiles (for es cert and es tik) - // BroadOn RSA Keys - struct BroadOnRsaSignerProfile + // BroadOn Keys + struct BroadOnSignerProfile { tc::ByteData certificate; - rsa_key_t key; + + nn::pki::sign::SignatureAlgo key_type; + rsa_key_t rsa_key; + // ecc_key_t ecc_key; }; - std::map broadon_rsa_signer; + std::map broadon_signer; }; class KeyBagInitializer : public KeyBag From 6a3bd0e562df31e2f3b513ceb2c87b03ccb3fea1 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 30 Sep 2021 19:40:45 +0800 Subject: [PATCH 009/119] Ported PkiValidator to libtoolchain. --- src/PkiValidator.cpp | 135 +++++++++++++++++++++++++------------------ src/PkiValidator.h | 3 +- 2 files changed, 81 insertions(+), 57 deletions(-) diff --git a/src/PkiValidator.cpp b/src/PkiValidator.cpp index d608e90..e25902a 100644 --- a/src/PkiValidator.cpp +++ b/src/PkiValidator.cpp @@ -1,10 +1,11 @@ -#include -#include -#include -#include #include "PkiValidator.h" -nstool::PkiValidator::PkiValidator() +#include +#include +#include + +nstool::PkiValidator::PkiValidator() : + mModuleName("nstool::PkiValidator") { clearCertificates(); } @@ -48,7 +49,7 @@ void nstool::PkiValidator::addCertificate(const nn::pki::SignedDatasecond.key_type) { - sig_validate_res = fnd::rsa::pkcs::rsaVerify(rsa2048_pub, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + throw tc::Exception(mModuleName, fmt::print("Public key for issuer \"{:s}\" cannot verify this signature.", issuer); } - else if (mKeyCfg.getPkiRootSignKey(issuer, ecdsa_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240) + + if (sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240) { - throw tc::Exception(kModuleName, "ECDSA signatures are not supported"); - } - else - { - throw tc::Exception(kModuleName, "Public key for issuer \"" + issuer + "\" does not exist."); + throw tc::Exception(mModuleName, "ECDSA signatures are not supported"); } + + rsa_key = itr->second.rsa_key; } else { @@ -127,25 +129,65 @@ void nstool::PkiValidator::validateSignature(const std::string& issuer, nn::pki: if (issuer_pubk_type == nn::pki::cert::RSA4096 && sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096) { - sig_validate_res = fnd::rsa::pkcs::rsaVerify(issuer_cert.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + rsa_key = issuer_cert.getRsa4098PublicKey(); } else if (issuer_pubk_type == nn::pki::cert::RSA2048 && sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048) { - sig_validate_res = fnd::rsa::pkcs::rsaVerify(issuer_cert.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + rsa_key = issuer_cert.getRsa2048PublicKey(); } else if (issuer_pubk_type == nn::pki::cert::ECDSA240 && sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240) { - throw tc::Exception(kModuleName, "ECDSA signatures are not supported"); + // ecc_key = issuer_cert.getEcdsa240PublicKey(); + throw tc::Exception(mModuleName, "ECDSA signatures are not supported"); } else { - throw tc::Exception(kModuleName, "Mismatch between issuer public key and signature type"); + throw tc::Exception(mModuleName, "Mismatch between issuer public key and signature type"); } } - if (sig_validate_res != 0) + // verify signature + switch (signature_id) { + case (SIGN_ID_RSA4096_SHA1): + sig_validate_res = tc::crypto::VerifyRsa4096Pkcs1Sha1(signature.data(), hash.data(), rsa_key); + break; + case (SIGN_ID_RSA2048_SHA1): + sig_validate_res = tc::crypto::VerifyRsa2048Pkcs1Sha1(signature.data(), hash.data(), rsa_key); + break; + case (SIGN_ID_ECDSA240_SHA1): + sig_validate_res = false; + break; + case (SIGN_ID_RSA4096_SHA256): + sig_validate_res = tc::crypto::VerifyRsa4096Pkcs1Sha256(signature.data(), hash.data(), rsa_key); + break; + case (SIGN_ID_RSA2048_SHA256): + sig_validate_res = tc::crypto::VerifyRsa2048Pkcs1Sha256(signature.data(), hash.data(), rsa_key); + break; + case (SIGN_ID_ECDSA240_SHA256): + sig_validate_res = false; + break; + } + if (sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096) { - throw tc::Exception(kModuleName, "Incorrect signature"); + sig_validate_res = fnd::rsa::pkcs::rsaVerify(issuer_cert.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + } + else if (sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048) + { + sig_validate_res = fnd::rsa::pkcs::rsaVerify(issuer_cert.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + } + else if (sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240) + { + throw tc::Exception(mModuleName, "ECDSA signatures are not supported"); + } + else + { + throw tc::Exception(mModuleName, "Mismatch between issuer public key and signature type"); + } + + + if (sig_valid == false) + { + throw tc::Exception(mModuleName, "Incorrect signature"); } @@ -159,7 +201,7 @@ void nstool::PkiValidator::makeCertIdent(const nn::pki::SignedData(ident.length(),64)); } bool nstool::PkiValidator::doesCertExist(const std::string& ident) const @@ -191,22 +233,5 @@ const nn::pki::SignedData& nstool::PkiValidator::getCe } } - throw tc::Exception(kModuleName, "Issuer certificate does not exist"); -} - -fnd::sha::HashType nstool::PkiValidator::getCryptoHashAlgoFromEsSignHashAlgo(nn::pki::sign::HashAlgo hash_algo) const -{ - fnd::sha::HashType hash_type = fnd::sha::HASH_SHA1; - - switch (hash_algo) - { - case (nn::pki::sign::HASH_ALGO_SHA1): - hash_type = fnd::sha::HASH_SHA1; - break; - case (nn::pki::sign::HASH_ALGO_SHA256): - hash_type = fnd::sha::HASH_SHA256; - break; - }; - - return hash_type; + throw tc::Exception(mModuleName, "Issuer certificate does not exist"); } \ No newline at end of file diff --git a/src/PkiValidator.h b/src/PkiValidator.h index 6cc2006..27ebf57 100644 --- a/src/PkiValidator.h +++ b/src/PkiValidator.h @@ -20,7 +20,7 @@ public: void validateSignature(const std::string& issuer, nn::pki::sign::SignatureId signature_id, const tc::ByteData& signature, const tc::ByteData& hash) const; private: - const std::string kModuleName = "NNPkiValidator"; + std::string mModuleName; KeyBag mKeyCfg; std::vector> mCertificateBank; @@ -29,7 +29,6 @@ private: void makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const; bool doesCertExist(const std::string& ident) const; const nn::pki::SignedData& getCert(const std::string& ident) const; - fnd::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(nn::pki::sign::HashAlgo hash_algo) const; }; } \ No newline at end of file From b51ce51b15c22774bed47d049035105082bd7add Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 30 Sep 2021 19:41:17 +0800 Subject: [PATCH 010/119] Port AssetHeader to libtoolchain. --- src/AssetProcess.cpp | 60 +++++++++++++++++++++----------------------- src/AssetProcess.h | 8 +++--- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index 1a9bf53..ffc0d1e 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -1,8 +1,9 @@ #include "AssetProcess.h" -#include "utils.h" +#include "util.h" nstool::AssetProcess::AssetProcess() : + mModuleName("nstool::AssetProcess::"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false) @@ -37,17 +38,17 @@ void nstool::AssetProcess::setListFs(bool list) mRomfs.setListFs(list); } -void nstool::AssetProcess::setIconExtractPath(const std::string& path) +void nstool::AssetProcess::setIconExtractPath(const tc::io::Path& path) { mIconExtractPath = path; } -void nstool::AssetProcess::setNacpExtractPath(const std::string& path) +void nstool::AssetProcess::setNacpExtractPath(const tc::io::Path& path) { mNacpExtractPath = path; } -void nstool::AssetProcess::setRomfsExtractPath(const std::string& path) +void nstool::AssetProcess::setRomfsExtractPath(const tc::io::Path& path) { mRomfs.setExtractPath(path); } @@ -55,49 +56,46 @@ void nstool::AssetProcess::setRomfsExtractPath(const std::string& path) void nstool::AssetProcess::importHeader() { - tc::ByteData scratch; - if (mFile == nullptr) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "No file reader set."); } - size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); - - if (file_size < sizeof(nn::hac::sAssetHeader)) + if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sAssetHeader))) { - throw tc::Exception(kModuleName, "Corrupt ASET: file too small"); + throw tc::Exception(mModuleName, "Corrupt ASET: file too small"); } - scratch.alloc(sizeof(nn::hac::sAssetHeader)); - mFile->read(scratch.data(), 0, scratch.size()); + tc::ByteData scratch = tc::ByteData(sizeof(nn::hac::sAssetHeader)); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); mHdr.fromBytes(scratch.data(), scratch.size()); } void nstool::AssetProcess::processSections() { - size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + int64_t file_size = mFile->length(); if (mHdr.getIconInfo().size > 0 && mIconExtractPath.isSet()) { if ((mHdr.getIconInfo().size + mHdr.getIconInfo().offset) > file_size) - throw tc::Exception(kModuleName, "ASET geometry for icon beyond file size"); + throw tc::Exception(mModuleName, "ASET geometry for icon beyond file size"); - writeSubStreamToFile(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getIconInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getIconInfo().size), mIconExtractPath.get()); + writeSubStreamToFile(mFile, mHdr.getIconInfo().offset, mHdr.getIconInfo().size, mIconExtractPath.get()); } if (mHdr.getNacpInfo().size > 0) { if ((mHdr.getNacpInfo().size + mHdr.getNacpInfo().offset) > file_size) - throw tc::Exception(kModuleName, "ASET geometry for nacp beyond file size"); + throw tc::Exception(mModuleName, "ASET geometry for nacp beyond file size"); if (mNacpExtractPath.isSet()) { - writeSubStreamToFile(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().size), mNacpExtractPath.get()); + writeSubStreamToFile(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size, mNacpExtractPath.get()); } - mNacp.setInputFile(std::make_shared(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().size))); + mNacp.setInputFile(std::make_shared(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size)); mNacp.setCliOutputMode(mCliOutputMode); mNacp.setVerifyMode(mVerify); @@ -107,9 +105,9 @@ void nstool::AssetProcess::processSections() if (mHdr.getRomfsInfo().size > 0) { if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > file_size) - throw tc::Exception(kModuleName, "ASET geometry for romfs beyond file size"); + throw tc::Exception(mModuleName, "ASET geometry for romfs beyond file size"); - mRomfs.setInputFile(std::make_shared(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getRomfsInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getRomfsInfo().size))); + mRomfs.setInputFile(std::make_shared(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size)); mRomfs.setCliOutputMode(mCliOutputMode); mRomfs.setVerifyMode(mVerify); @@ -121,16 +119,16 @@ void nstool::AssetProcess::displayHeader() { if (mCliOutputMode.show_layout) { - std::cout << "[ASET Header]" << std::endl; - std::cout << " Icon:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getIconInfo().offset << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getIconInfo().size << std::endl; - std::cout << " NACP:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getNacpInfo().offset << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getNacpInfo().size << std::endl; - std::cout << " RomFS:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getRomfsInfo().offset << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getRomfsInfo().size << std::endl; + fmt::print("[ASET Header]\n"); + fmt::print(" Icon:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getIconInfo().offset); + fmt::print(" Size: 0x{:x}\n", mHdr.getIconInfo().size); + fmt::print(" NACP:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getNacpInfo().offset); + fmt::print(" Size: 0x{:x}\n", mHdr.getNacpInfo().size); + fmt::print(" RomFS:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getRomfsInfo().offset); + fmt::print(" Size: 0x{:x}\n", mHdr.getRomfsInfo().size); } } diff --git a/src/AssetProcess.h b/src/AssetProcess.h index 262de5a..5d1ea5b 100644 --- a/src/AssetProcess.h +++ b/src/AssetProcess.h @@ -20,13 +20,13 @@ public: void setListFs(bool list); - void setIconExtractPath(const std::string& path); - void setNacpExtractPath(const std::string& path); - void setRomfsExtractPath(const std::string& path); + void setIconExtractPath(const tc::io::Path& path); + void setNacpExtractPath(const tc::io::Path& path); + void setRomfsExtractPath(const tc::io::Path& path); private: - const std::string kModuleName = "AssetProcess"; + std::string mModuleName; std::shared_ptr mFile; CliOutputMode mCliOutputMode; From f86716b95bed9da6d86cb8ac9acedf0ef2b00a47 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 30 Sep 2021 19:41:32 +0800 Subject: [PATCH 011/119] Update libnintendo-hac-hb --- deps/libnintendo-hac-hb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libnintendo-hac-hb b/deps/libnintendo-hac-hb index e52fb0e..1956c9b 160000 --- a/deps/libnintendo-hac-hb +++ b/deps/libnintendo-hac-hb @@ -1 +1 @@ -Subproject commit e52fb0e1ebc558f68bb438bd278b05704c5aaeb3 +Subproject commit 1956c9be9a01d90d84e2691c76d707781694f3be From 0c6efad6037cb3ffbf3212ecebb7862b1016d6b3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 30 Sep 2021 19:41:57 +0800 Subject: [PATCH 012/119] Port CnmtProcess to libtoolchain. --- src/CnmtProcess.cpp | 163 ++++++++++++++++++++++---------------------- src/CnmtProcess.h | 2 +- 2 files changed, 83 insertions(+), 82 deletions(-) diff --git a/src/CnmtProcess.cpp b/src/CnmtProcess.cpp index 5849c16..75a46c4 100644 --- a/src/CnmtProcess.cpp +++ b/src/CnmtProcess.cpp @@ -1,11 +1,9 @@ #include "CnmtProcess.h" -#include -#include - #include nstool::CnmtProcess::CnmtProcess() : + mModuleName("nstool::CnmtProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false) @@ -42,96 +40,102 @@ const nn::hac::ContentMeta& nstool::CnmtProcess::getContentMeta() const void nstool::CnmtProcess::importCnmt() { - tc::ByteData scratch; - - if (*mFile == nullptr) + if (mFile == nullptr) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "No file reader set."); } - scratch.alloc((*mFile)->size()); - (*mFile)->read(scratch.data(), 0, scratch.size()); + + // check if file_size is greater than 20MB, don't import. + size_t cnmt_file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + if (cnmt_file_size > (0x100000 * 20)) + { + throw tc::Exception(mModuleName, "File too large."); + } + // read cnmt + tc::ByteData scratch = tc::ByteData(cnmt_file_size); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // parse cnmt mCnmt.fromBytes(scratch.data(), scratch.size()); } void nstool::CnmtProcess::displayCnmt() { - std::cout << "[ContentMeta]" << std::endl; - std::cout << " TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getTitleId() << std::endl; - std::cout << " Version: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getTitleVersion()) << " (v" << std::dec << mCnmt.getTitleVersion() << ")"<< std::endl; - std::cout << " Type: " << nn::hac::ContentMetaUtil::getContentMetaTypeAsString(mCnmt.getContentMetaType()) << " (" << std::dec << (uint32_t)mCnmt.getContentMetaType() << ")" << std::endl; - std::cout << " Attributes: 0x" << std::hex << mCnmt.getAttribute().to_ullong(); - if (mCnmt.getAttribute().any()) + const nn::hac::sContentMetaHeader* cnmt_hdr = (const nn::hac::sContentMetaHeader*)mCnmt.getBytes().data(); + fmt::print("[ContentMeta]\n"); + fmt::print(" TitleId: 0x{:016x}\n", mCnmt.getTitleId()); + fmt::print(" Version: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getTitleVersion()), mCnmt.getTitleVersion()); + fmt::print(" Type: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getContentMetaTypeAsString(mCnmt.getContentMetaType()), (uint32_t)mCnmt.getContentMetaType()); + fmt::print(" Attributes: 0x{:x}", *((byte_t*)&cnmt_hdr->attributes)); + if (mCnmt.getAttribute().size()) { std::vector attribute_list; - for (size_t flag = 0; flag < mCnmt.getAttribute().size(); flag++) + for (auto itr = mCnmt.getAttribute().begin(); itr != mCnmt.getAttribute().end(); itr++) { - - if (mCnmt.getAttribute().test(flag)) - { - attribute_list.push_back(nn::hac::ContentMetaUtil::getContentMetaAttributeFlagAsString(nn::hac::cnmt::ContentMetaAttributeFlag(flag))); - } + attribute_list.push_back(nn::hac::ContentMetaUtil::getContentMetaAttributeFlagAsString(nn::hac::cnmt::ContentMetaAttributeFlag(*itr))); } - std::cout << " ["; + fmt::print(" ["); for (auto itr = attribute_list.begin(); itr != attribute_list.end(); itr++) { - std::cout << *itr; + fmt::print("{:s}",*itr); if ((itr + 1) != attribute_list.end()) { - std::cout << ", "; + fmt::print(", "); } } - std::cout << "]"; + fmt::print("]"); } - std::cout << std::endl; + fmt::print("\n"); - std::cout << " StorageId: " << nn::hac::ContentMetaUtil::getStorageIdAsString(mCnmt.getStorageId()) << " (" << std::dec << (uint32_t)mCnmt.getStorageId() << ")" << std::endl; - std::cout << " ContentInstallType: " << nn::hac::ContentMetaUtil::getContentInstallTypeAsString(mCnmt.getContentInstallType()) << " (" << std::dec << (uint32_t)mCnmt.getContentInstallType() << ")" << std::endl; - std::cout << " RequiredDownloadSystemVersion: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getRequiredDownloadSystemVersion()) << " (v" << mCnmt.getRequiredDownloadSystemVersion() << ")"<< std::endl; + fmt::print(" StorageId: {:s} ({:d})\n", nn::hac::ContentMetaUtil::getStorageIdAsString(mCnmt.getStorageId()), (uint32_t)mCnmt.getStorageId()); + fmt::print(" ContentInstallType: {:s} ({:d})\n", nn::hac::ContentMetaUtil::getContentInstallTypeAsString(mCnmt.getContentInstallType()),(uint32_t)mCnmt.getContentInstallType()); + fmt::print(" RequiredDownloadSystemVersion: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getRequiredDownloadSystemVersion()), mCnmt.getRequiredDownloadSystemVersion()); switch(mCnmt.getContentMetaType()) { case (nn::hac::cnmt::ContentMetaType::Application): - std::cout << " ApplicationExtendedHeader:" << std::endl; - std::cout << " RequiredApplicationVersion: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getApplicationMetaExtendedHeader().getRequiredApplicationVersion()) << " (v" << std::dec << mCnmt.getApplicationMetaExtendedHeader().getRequiredApplicationVersion() << ")"<< std::endl; - std::cout << " RequiredSystemVersion: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getApplicationMetaExtendedHeader().getRequiredSystemVersion()) << " (v" << std::dec << mCnmt.getApplicationMetaExtendedHeader().getRequiredSystemVersion() << ")"<< std::endl; - std::cout << " PatchId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getApplicationMetaExtendedHeader().getPatchId() << std::endl; + fmt::print(" ApplicationExtendedHeader:\n"); + fmt::print(" RequiredApplicationVersion: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getApplicationMetaExtendedHeader().getRequiredApplicationVersion()), mCnmt.getApplicationMetaExtendedHeader().getRequiredApplicationVersion()); + fmt::print(" RequiredSystemVersion: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getApplicationMetaExtendedHeader().getRequiredSystemVersion()), mCnmt.getApplicationMetaExtendedHeader().getRequiredSystemVersion()); + fmt::print(" PatchId: 0x{:016x}\n", mCnmt.getApplicationMetaExtendedHeader().getPatchId()); break; case (nn::hac::cnmt::ContentMetaType::Patch): - std::cout << " PatchMetaExtendedHeader:" << std::endl; - std::cout << " RequiredSystemVersion: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getPatchMetaExtendedHeader().getRequiredSystemVersion()) << " (v" << std::dec << mCnmt.getPatchMetaExtendedHeader().getRequiredSystemVersion() << ")"<< std::endl; - std::cout << " ApplicationId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getPatchMetaExtendedHeader().getApplicationId() << std::endl; + fmt::print(" PatchMetaExtendedHeader:\n"); + fmt::print(" RequiredSystemVersion: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getPatchMetaExtendedHeader().getRequiredSystemVersion()), mCnmt.getPatchMetaExtendedHeader().getRequiredSystemVersion()); + fmt::print(" ApplicationId: 0x{:016x}\n", mCnmt.getPatchMetaExtendedHeader().getApplicationId()); break; case (nn::hac::cnmt::ContentMetaType::AddOnContent): - std::cout << " AddOnContentMetaExtendedHeader:" << std::endl; - std::cout << " RequiredApplicationVersion: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getAddOnContentMetaExtendedHeader().getRequiredApplicationVersion()) << " (v" << std::dec << mCnmt.getAddOnContentMetaExtendedHeader().getRequiredApplicationVersion() << ")" << std::endl; - std::cout << " ApplicationId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getAddOnContentMetaExtendedHeader().getApplicationId() << std::endl; + fmt::print(" AddOnContentMetaExtendedHeader:\n"); + fmt::print(" RequiredApplicationVersion: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getAddOnContentMetaExtendedHeader().getRequiredApplicationVersion()), mCnmt.getAddOnContentMetaExtendedHeader().getRequiredApplicationVersion()); + fmt::print(" ApplicationId: 0x{:016x}\n", mCnmt.getAddOnContentMetaExtendedHeader().getApplicationId()); break; case (nn::hac::cnmt::ContentMetaType::Delta): - std::cout << " DeltaMetaExtendedHeader:" << std::endl; - std::cout << " ApplicationId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getDeltaMetaExtendedHeader().getApplicationId() << std::endl; + fmt::print(" DeltaMetaExtendedHeader:\n"); + fmt::print(" ApplicationId: 0x{:016x}\n", mCnmt.getDeltaMetaExtendedHeader().getApplicationId()); break; default: break; } if (mCnmt.getContentInfo().size() > 0) { - printf(" ContentInfo:\n"); + fmt::print(" ContentInfo:\n"); for (size_t i = 0; i < mCnmt.getContentInfo().size(); i++) { const nn::hac::ContentInfo& info = mCnmt.getContentInfo()[i]; - std::cout << " " << std::dec << i << std::endl; - std::cout << " Type: " << nn::hac::ContentMetaUtil::getContentTypeAsString(info.getContentType()) << " (" << std::dec << (uint32_t)info.getContentType() << ")" << std::endl; - std::cout << " Id: " << fnd::SimpleTextOutput::arrayToString(info.getContentId().data(), info.getContentId().size(), false, "") << std::endl; - std::cout << " Size: 0x" << std::hex << info.getContentSize() << std::endl; - std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(info.getContentHash().bytes, sizeof(info.getContentHash()), false, "") << std::endl; + fmt::print(" {:d}\n", i); + fmt::print(" Type: {:s} ({:d})\n", nn::hac::ContentMetaUtil::getContentTypeAsString(info.getContentType()), (uint32_t)info.getContentType()); + fmt::print(" Id: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(info.getContentId().data(), info.getContentId().size(), false, "")); + fmt::print(" Size: 0x{:x}\n", info.getContentSize()); + fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(info.getContentHash().data(), info.getContentHash().size(), false, "")); } } if (mCnmt.getContentMetaInfo().size() > 0) { - std::cout << " ContentMetaInfo:" << std::endl; + fmt::print(" ContentMetaInfo:\n"); displayContentMetaInfoList(mCnmt.getContentMetaInfo(), " "); } @@ -139,31 +143,31 @@ void nstool::CnmtProcess::displayCnmt() if (mCnmt.getContentMetaType() == nn::hac::cnmt::ContentMetaType::Patch && mCnmt.getPatchMetaExtendedHeader().getExtendedDataSize() != 0) { // this is stubbed as the raw output is for development purposes - //std::cout << " PatchMetaExtendedData:" << std::endl; - //fnd::SimpleTextOutput::hxdStyleDump(mCnmt.getPatchMetaExtendedData().data(), mCnmt.getPatchMetaExtendedData().size()); + //fmt::print(" PatchMetaExtendedData:\n"); + //tc::cli::FormatUtil::formatBytesAsHxdHexString(mCnmt.getPatchMetaExtendedData().data(), mCnmt.getPatchMetaExtendedData().size()); } else if (mCnmt.getContentMetaType() == nn::hac::cnmt::ContentMetaType::Delta && mCnmt.getDeltaMetaExtendedHeader().getExtendedDataSize() != 0) { // this is stubbed as the raw output is for development purposes - //std::cout << " DeltaMetaExtendedData:" << std::endl; - //fnd::SimpleTextOutput::hxdStyleDump(mCnmt.getDeltaMetaExtendedData().data(), mCnmt.getDeltaMetaExtendedData().size()); + //fmt::print(" DeltaMetaExtendedData:\n"); + //tc::cli::FormatUtil::formatBytesAsHxdHexString(mCnmt.getDeltaMetaExtendedData().data(), mCnmt.getDeltaMetaExtendedData().size()); } else if (mCnmt.getContentMetaType() == nn::hac::cnmt::ContentMetaType::SystemUpdate && mCnmt.getSystemUpdateMetaExtendedHeader().getExtendedDataSize() != 0) { - std::cout << " SystemUpdateMetaExtendedData:" << std::endl; - std::cout << " FormatVersion: " << std::dec << mCnmt.getSystemUpdateMetaExtendedData().getFormatVersion() << std::endl; - std::cout << " FirmwareVariation:" << std::endl; + fmt::print(" SystemUpdateMetaExtendedData:\n"); + fmt::print(" FormatVersion: {:d}\n", mCnmt.getSystemUpdateMetaExtendedData().getFormatVersion()); + fmt::print(" FirmwareVariation:\n"); auto variation_info = mCnmt.getSystemUpdateMetaExtendedData().getFirmwareVariationInfo(); for (size_t i = 0; i < mCnmt.getSystemUpdateMetaExtendedData().getFirmwareVariationInfo().size(); i++) { - std::cout << " " << std::dec << i << std::endl; - std::cout << " FirmwareVariationId: 0x" << std::hex << variation_info[i].variation_id << std::endl; + fmt::print(" {:d}\n", i); + fmt::print(" FirmwareVariationId: 0x{:x}\n", variation_info[i].variation_id); if (mCnmt.getSystemUpdateMetaExtendedData().getFormatVersion() == 2) { - std::cout << " ReferToBase: " << std::boolalpha << variation_info[i].meta.empty() << std::endl; + fmt::print(" ReferToBase: {}\n", variation_info[i].meta.empty()); if (variation_info[i].meta.empty() == false) { - std::cout << " ContentMeta:" << std::endl; + fmt::print(" ContentMeta:\n"); displayContentMetaInfoList(variation_info[i].meta, " "); } } @@ -172,48 +176,45 @@ void nstool::CnmtProcess::displayCnmt() - std::cout << " Digest: " << fnd::SimpleTextOutput::arrayToString(mCnmt.getDigest().data(), mCnmt.getDigest().size(), false, "") << std::endl; + fmt::print(" Digest: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mCnmt.getDigest().data(), mCnmt.getDigest().size(), false, "")); } void nstool::CnmtProcess::displayContentMetaInfo(const nn::hac::ContentMetaInfo& content_meta_info, const std::string& prefix) { - std::cout << prefix << "Id: 0x" << std::hex << std::setw(16) << std::setfill('0') << content_meta_info.getTitleId() << std::endl; - std::cout << prefix << "Version: " << nn::hac::ContentMetaUtil::getVersionAsString(content_meta_info.getTitleVersion()) << " (v" << std::dec << content_meta_info.getTitleVersion() << ")"<< std::endl; - std::cout << prefix << "Type: " << nn::hac::ContentMetaUtil::getContentMetaTypeAsString(content_meta_info.getContentMetaType()) << " (" << std::dec << (uint32_t)content_meta_info.getContentMetaType() << ")" << std::endl; - std::cout << prefix << "Attributes: 0x" << std::hex << content_meta_info.getAttribute().to_ullong(); - if (content_meta_info.getAttribute().any()) + const nn::hac::sContentMetaInfo* content_meta_info_raw = (const nn::hac::sContentMetaInfo*)content_meta_info.getBytes().data(); + fmt::print("{:s}Id: 0x{:016x}\n", prefix, content_meta_info.getTitleId()); + fmt::print("{:s}Version: {:s} (v{:d})\n", prefix, nn::hac::ContentMetaUtil::getVersionAsString(content_meta_info.getTitleVersion()), content_meta_info.getTitleVersion()); + fmt::print("{:s}Type: {:s} ({:d})\n", prefix, nn::hac::ContentMetaUtil::getContentMetaTypeAsString(content_meta_info.getContentMetaType()), (uint32_t)content_meta_info.getContentMetaType()); + fmt::print("{:s}Attributes: 0x{:x}", prefix, *((byte_t*)&content_meta_info_raw->attributes) ); + if (content_meta_info.getAttribute().size()) { std::vector attribute_list; - for (size_t flag = 0; flag < content_meta_info.getAttribute().size(); flag++) + for (auto itr = content_meta_info.getAttribute().begin(); itr != content_meta_info.getAttribute().end(); itr++) { - - if (content_meta_info.getAttribute().test(flag)) - { - attribute_list.push_back(nn::hac::ContentMetaUtil::getContentMetaAttributeFlagAsString(nn::hac::cnmt::ContentMetaAttributeFlag(flag))); - } + attribute_list.push_back(nn::hac::ContentMetaUtil::getContentMetaAttributeFlagAsString(nn::hac::cnmt::ContentMetaAttributeFlag(*itr))); } - std::cout << " ["; + fmt::print(" ["); for (auto itr = attribute_list.begin(); itr != attribute_list.end(); itr++) { - std::cout << *itr; + fmt::print("{:s}",*itr); if ((itr + 1) != attribute_list.end()) { - std::cout << ", "; + fmt::print(", "); } } - std::cout << "]"; + fmt::print("]"); } + fmt::print("\n"); } void nstool::CnmtProcess::displayContentMetaInfoList(const std::vector& content_meta_info_list, const std::string& prefix) { for (size_t i = 0; i < content_meta_info_list.size(); i++) - { - const nn::hac::ContentMetaInfo& info = mCnmt.getContentMetaInfo()[i]; - std::cout << prefix << std::dec << i << std::endl; - displayContentMetaInfo(info, prefix + " "); - std::cout << std::endl; - } + { + const nn::hac::ContentMetaInfo& info = mCnmt.getContentMetaInfo()[i]; + fmt::print("{:s}{:d}\n", i); + displayContentMetaInfo(info, prefix + " "); + } } \ No newline at end of file diff --git a/src/CnmtProcess.h b/src/CnmtProcess.h index 99c9462..bcb407d 100644 --- a/src/CnmtProcess.h +++ b/src/CnmtProcess.h @@ -19,7 +19,7 @@ public: const nn::hac::ContentMeta& getContentMeta() const; private: - const std::string kModuleName = "CnmtProcess"; + std::string mModuleName; std::shared_ptr mFile; CliOutputMode mCliOutputMode; From 258173915c96746e3261c757e871d7d6c877175b Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 30 Sep 2021 19:42:15 +0800 Subject: [PATCH 013/119] Port ElfSymbolParser to libtoolchain. --- src/ElfSymbolParser.cpp | 25 +++++++++++++------------ src/ElfSymbolParser.h | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/ElfSymbolParser.cpp b/src/ElfSymbolParser.cpp index 8bb66dd..d4dfc3a 100644 --- a/src/ElfSymbolParser.cpp +++ b/src/ElfSymbolParser.cpp @@ -1,8 +1,9 @@ #include "ElfSymbolParser.h" -nstool::ElfSymbolParser::ElfSymbolParser() +nstool::ElfSymbolParser::ElfSymbolParser() : + mModuleName("nstool::ElfSymbolParser"), + mSymbolList() { - mSymbolList.clear(); } void nstool::ElfSymbolParser::operator=(const ElfSymbolParser& other) @@ -22,7 +23,7 @@ bool nstool::ElfSymbolParser::operator!=(const ElfSymbolParser& other) const void nstool::ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit) { - size_t dynSymSize = is64Bit ? sizeof(fnd::Elf64_Sym) : sizeof(fnd::Elf32_Sym); + size_t dynSymSize = is64Bit ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym); sElfSymbol symbol; for (size_t i = 0; i < dyn_sym_size; i += dynSymSize) @@ -31,22 +32,22 @@ void nstool::ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_si if (is64Bit) { - name_pos = le_word(((fnd::Elf64_Sym*)(dyn_sym + i))->st_name); - symbol.shn_index = le_hword(((fnd::Elf64_Sym*)(dyn_sym + i))->st_shndx); - symbol.symbol_type = fnd::elf::get_elf_st_type(((fnd::Elf64_Sym*)(dyn_sym + i))->st_info); - symbol.symbol_binding = fnd::elf::get_elf_st_bind(((fnd::Elf64_Sym*)(dyn_sym + i))->st_info); + name_pos = tc::bn::detail::__le_uint32(((Elf64_Sym*)(dyn_sym + i))->st_name); + symbol.shn_index = tc::bn::detail::__le_uint16(((Elf64_Sym*)(dyn_sym + i))->st_shndx); + symbol.symbol_type = elf::get_elf_st_type(((Elf64_Sym*)(dyn_sym + i))->st_info); + symbol.symbol_binding = elf::get_elf_st_bind(((Elf64_Sym*)(dyn_sym + i))->st_info); } else { - name_pos = le_word(((fnd::Elf32_Sym*)(dyn_sym + i))->st_name); - symbol.shn_index = le_hword(((fnd::Elf32_Sym*)(dyn_sym + i))->st_shndx); - symbol.symbol_type = fnd::elf::get_elf_st_type(((fnd::Elf32_Sym*)(dyn_sym + i))->st_info); - symbol.symbol_binding = fnd::elf::get_elf_st_bind(((fnd::Elf32_Sym*)(dyn_sym + i))->st_info); + name_pos = tc::bn::detail::__le_uint32(((Elf32_Sym*)(dyn_sym + i))->st_name); + symbol.shn_index = tc::bn::detail::__le_uint16(((Elf32_Sym*)(dyn_sym + i))->st_shndx); + symbol.symbol_type = elf::get_elf_st_type(((Elf32_Sym*)(dyn_sym + i))->st_info); + symbol.symbol_binding = elf::get_elf_st_bind(((Elf32_Sym*)(dyn_sym + i))->st_info); } if (name_pos >= dyn_str_size) { - throw tc::Exception(kModuleName, "Out of bounds symbol name offset"); + throw tc::Exception(mModuleName, "Out of bounds symbol name offset"); } //for (; dyn_str[name_pos] == 0x00 && name_pos < dyn_str_size; name_pos++); diff --git a/src/ElfSymbolParser.h b/src/ElfSymbolParser.h index ea8bedb..f2ad56d 100644 --- a/src/ElfSymbolParser.h +++ b/src/ElfSymbolParser.h @@ -43,7 +43,7 @@ public: const std::vector& getSymbolList() const; private: - const std::string kModuleName = "ElfSymbolParser"; + std::string mModuleName; // data std::vector mSymbolList; From 09e6b8a85755d15222d1a36b7b4537713a15cae3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 1 Oct 2021 17:39:34 +0800 Subject: [PATCH 014/119] Move some files around to test each process as it's ported. --- src/AssetProcess.cpp | 20 +-- src/AssetProcess.h | 4 +- src/CnmtProcess.cpp | 3 +- src/EsTikProcess.cpp | 143 +++++++++-------- src/EsTikProcess.h | 11 +- src/GameCardProcess.h | 8 +- src/IniProcess.h | 5 +- src/KeyBag.h | 1 + src/NcaProcess.h | 28 ++-- src/NroProcess.h | 6 +- src/PfsProcess.h | 5 +- src/PkiValidator.cpp | 53 ++----- src/RomfsProcess.h | 9 +- src/Settings.cpp | 26 ++- src/Settings.h | 1 + src/main.cpp | 176 +++++++++++---------- src/{ => not_ported}/GameCardProcess.cpp | 0 src/{ => not_ported}/IniProcess.cpp | 0 src/{ => not_ported}/KipProcess.cpp | 0 src/{ => not_ported}/MetaProcess.cpp | 0 src/{ => not_ported}/NacpProcess.cpp | 0 src/{ => not_ported}/NcaProcess.cpp | 0 src/{ => not_ported}/NroProcess.cpp | 0 src/{ => not_ported}/NsoProcess.cpp | 0 src/{ => not_ported}/PfsProcess.cpp | 0 src/{ => not_ported}/PkiCertProcess.cpp | 0 src/{ => not_ported}/RoMetadataProcess.cpp | 0 src/{ => not_ported}/RomfsProcess.cpp | 0 src/types.h | 1 + src/util.cpp | 6 +- 30 files changed, 258 insertions(+), 248 deletions(-) rename src/{ => not_ported}/GameCardProcess.cpp (100%) rename src/{ => not_ported}/IniProcess.cpp (100%) rename src/{ => not_ported}/KipProcess.cpp (100%) rename src/{ => not_ported}/MetaProcess.cpp (100%) rename src/{ => not_ported}/NacpProcess.cpp (100%) rename src/{ => not_ported}/NcaProcess.cpp (100%) rename src/{ => not_ported}/NroProcess.cpp (100%) rename src/{ => not_ported}/NsoProcess.cpp (100%) rename src/{ => not_ported}/PfsProcess.cpp (100%) rename src/{ => not_ported}/PkiCertProcess.cpp (100%) rename src/{ => not_ported}/RoMetadataProcess.cpp (100%) rename src/{ => not_ported}/RomfsProcess.cpp (100%) diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index ffc0d1e..06873d6 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -35,7 +35,7 @@ void nstool::AssetProcess::setVerifyMode(bool verify) void nstool::AssetProcess::setListFs(bool list) { - mRomfs.setListFs(list); + //mRomfs.setListFs(list); } void nstool::AssetProcess::setIconExtractPath(const tc::io::Path& path) @@ -50,7 +50,7 @@ void nstool::AssetProcess::setNacpExtractPath(const tc::io::Path& path) void nstool::AssetProcess::setRomfsExtractPath(const tc::io::Path& path) { - mRomfs.setExtractPath(path); + //mRomfs.setExtractPath(path); } @@ -95,11 +95,11 @@ void nstool::AssetProcess::processSections() writeSubStreamToFile(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size, mNacpExtractPath.get()); } - mNacp.setInputFile(std::make_shared(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size)); - mNacp.setCliOutputMode(mCliOutputMode); - mNacp.setVerifyMode(mVerify); + //mNacp.setInputFile(std::make_shared(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size)); + //mNacp.setCliOutputMode(mCliOutputMode); + //mNacp.setVerifyMode(mVerify); - mNacp.process(); + //mNacp.process(); } if (mHdr.getRomfsInfo().size > 0) @@ -107,11 +107,11 @@ void nstool::AssetProcess::processSections() if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > file_size) throw tc::Exception(mModuleName, "ASET geometry for romfs beyond file size"); - mRomfs.setInputFile(std::make_shared(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size)); - mRomfs.setCliOutputMode(mCliOutputMode); - mRomfs.setVerifyMode(mVerify); + //mRomfs.setInputFile(std::make_shared(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size)); + //mRomfs.setCliOutputMode(mCliOutputMode); + //mRomfs.setVerifyMode(mVerify); - mRomfs.process(); + //mRomfs.process(); } } diff --git a/src/AssetProcess.h b/src/AssetProcess.h index 5d1ea5b..a2ce24b 100644 --- a/src/AssetProcess.h +++ b/src/AssetProcess.h @@ -36,8 +36,8 @@ private: tc::Optional mNacpExtractPath; nn::hac::AssetHeader mHdr; - NacpProcess mNacp; - RomfsProcess mRomfs; + //NacpProcess mNacp; + //RomfsProcess mRomfs; void importHeader(); void processSections(); diff --git a/src/CnmtProcess.cpp b/src/CnmtProcess.cpp index 75a46c4..785c453 100644 --- a/src/CnmtProcess.cpp +++ b/src/CnmtProcess.cpp @@ -44,7 +44,6 @@ void nstool::CnmtProcess::importCnmt() { throw tc::Exception(mModuleName, "No file reader set."); } - // check if file_size is greater than 20MB, don't import. size_t cnmt_file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); @@ -68,7 +67,7 @@ void nstool::CnmtProcess::displayCnmt() fmt::print("[ContentMeta]\n"); fmt::print(" TitleId: 0x{:016x}\n", mCnmt.getTitleId()); fmt::print(" Version: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getTitleVersion()), mCnmt.getTitleVersion()); - fmt::print(" Type: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getContentMetaTypeAsString(mCnmt.getContentMetaType()), (uint32_t)mCnmt.getContentMetaType()); + fmt::print(" Type: {:s} ({:d})\n", nn::hac::ContentMetaUtil::getContentMetaTypeAsString(mCnmt.getContentMetaType()), (uint32_t)mCnmt.getContentMetaType()); fmt::print(" Attributes: 0x{:x}", *((byte_t*)&cnmt_hdr->attributes)); if (mCnmt.getAttribute().size()) { diff --git a/src/EsTikProcess.cpp b/src/EsTikProcess.cpp index 561b6fb..78ee18a 100644 --- a/src/EsTikProcess.cpp +++ b/src/EsTikProcess.cpp @@ -1,14 +1,10 @@ -#include -#include -#include -#include -#include #include "EsTikProcess.h" #include "PkiValidator.h" - +#include nstool::EsTikProcess::EsTikProcess() : + mModuleName("nstool::EsTikProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false) @@ -53,16 +49,23 @@ void nstool::EsTikProcess::setVerifyMode(bool verify) void nstool::EsTikProcess::importTicket() { - tc::ByteData scratch; - - - if (*mFile == nullptr) + if (mFile == nullptr) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "No file reader set."); } - scratch.alloc((*mFile)->size()); - (*mFile)->read(scratch.data(), 0, scratch.size()); + // check if file_size is greater than 20MB, don't import. + size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + if (file_size > (0x100000 * 20)) + { + throw tc::Exception(mModuleName, "File too large."); + } + + // read cnmt + tc::ByteData scratch = tc::ByteData(file_size); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + mTik.fromBytes(scratch.data(), scratch.size()); } @@ -74,12 +77,12 @@ void nstool::EsTikProcess::verifyTicket() switch (nn::pki::sign::getHashAlgo(mTik.getSignature().getSignType())) { case (nn::pki::sign::HASH_ALGO_SHA1): - tik_hash.alloc(fnd::sha::kSha1HashLen); - fnd::sha::Sha1(mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size(), tik_hash.data()); + tik_hash = tc::ByteData(tc::crypto::Sha1Generator::kHashSize); + tc::crypto::GenerateSha1Hash(tik_hash.data(), mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size()); break; case (nn::pki::sign::HASH_ALGO_SHA256): - tik_hash.alloc(fnd::sha::kSha256HashLen); - fnd::sha::Sha256(mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size(), tik_hash.data()); + tik_hash = tc::ByteData(tc::crypto::Sha256Generator::kHashSize); + tc::crypto::GenerateSha256Hash(tik_hash.data(), mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size()); break; } @@ -91,85 +94,73 @@ void nstool::EsTikProcess::verifyTicket() } catch (const tc::Exception& e) { - std::cout << "[WARNING] Ticket signature could not be validated (" << e.error() << ")" << std::endl; + fmt::print("[WARNING] Ticket signature could not be validated ({:s})\n", e.error()); } } void nstool::EsTikProcess::displayTicket() { -#define _SPLIT_VER(ver) (uint32_t)((ver>>10) & 0x3f) << "." << (uint32_t)((ver>>4) & 0x3f) << "." << (uint32_t)((ver>>0) & 0xf) - const nn::es::TicketBody_V2& body = mTik.getBody(); - std::cout << "[ES Ticket]" << std::endl; - - std::cout << " SignType: " << getSignTypeStr(mTik.getSignature().getSignType()); + fmt::print("[ES Ticket]\n"); + fmt::print(" SignType: {:s}", getSignTypeStr(mTik.getSignature().getSignType())); if (mCliOutputMode.show_extended_info) - std::cout << " (0x" << std::hex << mTik.getSignature().getSignType() << ")"; - std::cout << std::endl; + fmt::print(" (0x{:x})", mTik.getSignature().getSignType()); + fmt::print("\n"); - std::cout << " Issuer: " << body.getIssuer() << std::endl; - std::cout << " Title Key:" << std::endl; - std::cout << " EncMode: " << getTitleKeyPersonalisationStr(body.getTitleKeyEncType()) << std::endl; - std::cout << " KeyGeneration: " << std::dec << (uint32_t)body.getCommonKeyId() << std::endl; + fmt::print(" Issuer: {:s}\n", body.getIssuer()); + fmt::print(" Title Key:\n"); + fmt::print(" EncMode: {:s}\n", getTitleKeyPersonalisationStr(body.getTitleKeyEncType())); + fmt::print(" KeyGeneration: {:d}\n", (uint32_t)body.getCommonKeyId()); if (body.getTitleKeyEncType() == nn::es::ticket::RSA2048) { - std::cout << " Data:" << std::endl; - for (size_t i = 0; i < 0x10; i++) - std::cout << " " << fnd::SimpleTextOutput::arrayToString(body.getEncTitleKey() + 0x10*i, 0x10, true, ":") << std::endl; + fmt::print(" Data:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(body.getEncTitleKey(), 0x100, true, ":", 0x10, 6, false)); } else if (body.getTitleKeyEncType() == nn::es::ticket::AES128_CBC) { - std::cout << " Data:" << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(body.getEncTitleKey(), 0x10, true, ":") << std::endl; + fmt::print(" Data:\n"); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getEncTitleKey(), 0x10, true, ":")); } else { - std::cout << " Data: " << std::endl; + fmt::print(" Data: \n"); } - - std::cout << " Version: v" << _SPLIT_VER(body.getTicketVersion()); - if (mCliOutputMode.show_extended_info) - std::cout << " (" << (uint32_t)body.getTicketVersion() << ")"; - std::cout << std::endl; - - std::cout << " License Type: " << getLicenseTypeStr(body.getLicenseType()) << std::endl; - + fmt::print(" Version: {:s} (v{:d})\n", getTitleVersionStr(body.getTicketVersion()), body.getTicketVersion()); + fmt::print(" License Type: {:s}\n", getLicenseTypeStr(body.getLicenseType())); if (body.getPropertyFlags().size() > 0) { - std::cout << " Flags:" << std::endl; + nn::es::sTicketBody_v2* raw_body = (nn::es::sTicketBody_v2*)body.getBytes().data(); + fmt::print(" PropertyMask: 0x{:04x}\n", ((tc::bn::le16*)&raw_body->property_mask)->unwrap()); for (size_t i = 0; i < body.getPropertyFlags().size(); i++) { - std::cout << " " << getPropertyFlagStr(body.getPropertyFlags()[i]) << std::endl; + fmt::print(" {:s}\n", getPropertyFlagStr(body.getPropertyFlags()[i])); } } - if (mCliOutputMode.show_extended_info) { - std::cout << " Reserved Region:" << std::endl; - fnd::SimpleTextOutput::hexDump(body.getReservedRegion(), 8, 0x10, 4); + fmt::print(" Reserved Region:\n"); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getReservedRegion(), 8, true, "")); } if (body.getTicketId() != 0 || mCliOutputMode.show_extended_info) - std::cout << " TicketId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getTicketId() << std::endl; + fmt::print(" TicketId: 0x{:016x}\n", body.getTicketId()); if (body.getDeviceId() != 0 || mCliOutputMode.show_extended_info) - std::cout << " DeviceId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getDeviceId() << std::endl; + fmt::print(" DeviceId: 0x{:016x}\n", body.getDeviceId()); - std::cout << " RightsId: " << std::endl << " "; - fnd::SimpleTextOutput::hexDump(body.getRightsId(), 16); + fmt::print(" RightsId: \n"); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getRightsId(), 16, true, "")); - std::cout << " SectionTotalSize: 0x" << std::hex << body.getSectionTotalSize() << std::endl; - std::cout << " SectionHeaderOffset: 0x" << std::hex << body.getSectionHeaderOffset() << std::endl; - std::cout << " SectionNum: 0x" << std::hex << body.getSectionNum() << std::endl; - std::cout << " SectionEntrySize: 0x" << std::hex << body.getSectionEntrySize() << std::endl; - -#undef _SPLIT_VER + fmt::print(" SectionTotalSize: 0x{:x}\n", body.getSectionTotalSize()); + fmt::print(" SectionHeaderOffset: 0x{:x}\n", body.getSectionHeaderOffset()); + fmt::print(" SectionNum: 0x{:x}\n", body.getSectionNum()); + fmt::print(" SectionEntrySize: 0x{:x}\n", body.getSectionEntrySize()); } -const char* nstool::EsTikProcess::getSignTypeStr(uint32_t type) const +std::string nstool::EsTikProcess::getSignTypeStr(uint32_t type) const { - const char* str = nullptr; + std::string str; switch(type) { case (nn::pki::sign::SIGN_ID_RSA4096_SHA1): @@ -197,9 +188,9 @@ const char* nstool::EsTikProcess::getSignTypeStr(uint32_t type) const return str; } -const char* nstool::EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const +std::string nstool::EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const { - const char* str = nullptr; + std::string str; switch(flag) { case (nn::es::ticket::AES128_CBC): @@ -209,15 +200,15 @@ const char* nstool::EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) con str = "Personalised (RSA2048)"; break; default: - str = "Unknown"; + str = fmt::format("Unknown ({:d})", flag); break; } return str; } -const char* nstool::EsTikProcess::getLicenseTypeStr(byte_t flag) const +std::string nstool::EsTikProcess::getLicenseTypeStr(byte_t flag) const { - const char* str = nullptr; + std::string str; switch(flag) { case (nn::es::ticket::LICENSE_PERMANENT): @@ -239,15 +230,15 @@ const char* nstool::EsTikProcess::getLicenseTypeStr(byte_t flag) const str = "Service"; break; default: - str = "Unknown"; + str = fmt::format("Unknown ({:d})", flag); break; } return str; } -const char* nstool::EsTikProcess::getPropertyFlagStr(byte_t flag) const +std::string nstool::EsTikProcess::getPropertyFlagStr(byte_t flag) const { - const char* str = nullptr; + std::string str; switch(flag) { case (nn::es::ticket::FLAG_PRE_INSTALL): @@ -259,9 +250,23 @@ const char* nstool::EsTikProcess::getPropertyFlagStr(byte_t flag) const case (nn::es::ticket::FLAG_ALLOW_ALL_CONTENT): str = "AllContent"; break; + case (nn::es::ticket::FLAG_DEVICE_LINK_INDEPENDENT): + str = "DeviceLinkIndependent"; + break; + case (nn::es::ticket::FLAG_VOLATILE): + str = "Volatile"; + break; + case (nn::es::ticket::FLAG_ELICENSE_REQUIRED): + str = "ELicenseRequired"; + break; default: - str = "Unknown"; + str = fmt::format("Unknown ({:d})", flag); break; } return str; +} + +std::string nstool::EsTikProcess::getTitleVersionStr(uint16_t version) const +{ + return fmt::format("{:d}.{:d}.{:d}", ((version>>10) & 0x3f), ((version>>4) & 0x3f), ((version>>0) & 0xf)); } \ No newline at end of file diff --git a/src/EsTikProcess.h b/src/EsTikProcess.h index 075759c..d146b65 100644 --- a/src/EsTikProcess.h +++ b/src/EsTikProcess.h @@ -22,7 +22,7 @@ public: void setVerifyMode(bool verify); private: - const std::string kModuleName = "EsTikProcess"; + std::string mModuleName; std::shared_ptr mFile; KeyBag mKeyCfg; @@ -36,10 +36,11 @@ private: void importTicket(); void verifyTicket(); void displayTicket(); - const char* getSignTypeStr(uint32_t type) const; - const char* getTitleKeyPersonalisationStr(byte_t flag) const; - const char* getLicenseTypeStr(byte_t flag) const; - const char* getPropertyFlagStr(byte_t flag) const; + std::string getSignTypeStr(uint32_t type) const; + std::string getTitleKeyPersonalisationStr(byte_t flag) const; + std::string getLicenseTypeStr(byte_t flag) const; + std::string getPropertyFlagStr(byte_t flag) const; + std::string getTitleVersionStr(uint16_t version) const; }; } \ No newline at end of file diff --git a/src/GameCardProcess.h b/src/GameCardProcess.h index 61243b7..3f294a8 100644 --- a/src/GameCardProcess.h +++ b/src/GameCardProcess.h @@ -21,7 +21,7 @@ public: void setVerifyMode(bool verify); // xci specific - void setPartitionForExtract(const std::string& partition_name, const std::string& extract_path); + void setPartitionForExtract(const std::string& partition_name, const tc::io::Path& extract_path); void setListFs(bool list_fs); private: @@ -37,7 +37,7 @@ private: struct sExtractInfo { std::string partition_name; - std::string extract_path; + tc::io::Path extract_path; void operator=(const sExtractInfo& other) { @@ -45,7 +45,7 @@ private: extract_path = other.extract_path; } - bool operator==(const std::string& name) const + bool operator==(const tc::io::Path& name) const { return name == partition_name; } @@ -57,7 +57,7 @@ private: bool mIsSdkXciEncrypted; size_t mGcHeaderOffset; bool mProccessExtendedHeader; - byte_t mHdrSignature[fnd::rsa::kRsa2048Size]; + nn::hac::detail::rsa2048_signature_t mHdrSignature; fnd::sha::sSha256Hash mHdrHash; nn::hac::GameCardHeader mHdr; diff --git a/src/IniProcess.h b/src/IniProcess.h index 365f715..59499ae 100644 --- a/src/IniProcess.h +++ b/src/IniProcess.h @@ -17,7 +17,7 @@ public: void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - void setKipExtractPath(const std::string& path); + void setKipExtractPath(const tc::io::Path& path); private: const std::string kModuleName = "IniProcess"; const std::string kKipExtention = ".kip"; @@ -27,8 +27,7 @@ private: CliOutputMode mCliOutputMode; bool mVerify; - bool mDoExtractKip; - std::string mKipExtractPath; + tc::Optional mKipExtractPath; nn::hac::IniHeader mHdr; std::vector> mKipList; diff --git a/src/KeyBag.h b/src/KeyBag.h index b83445d..0e3ab9b 100644 --- a/src/KeyBag.h +++ b/src/KeyBag.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include diff --git a/src/NcaProcess.h b/src/NcaProcess.h index 0f7538b..4a11c91 100644 --- a/src/NcaProcess.h +++ b/src/NcaProcess.h @@ -20,10 +20,10 @@ public: void setVerifyMode(bool verify); // nca specfic - void setPartition0ExtractPath(const std::string& path); - void setPartition1ExtractPath(const std::string& path); - void setPartition2ExtractPath(const std::string& path); - void setPartition3ExtractPath(const std::string& path); + void setPartition0ExtractPath(const tc::io::Path& path); + void setPartition1ExtractPath(const tc::io::Path& path); + void setPartition2ExtractPath(const tc::io::Path& path); + void setPartition3ExtractPath(const tc::io::Path& path); void setListFs(bool list_fs); private: @@ -36,11 +36,7 @@ private: CliOutputMode mCliOutputMode; bool mVerify; - struct sExtract - { - std::string path; - bool doExtract; - } mPartitionPath[nn::hac::nca::kPartitionNum]; + std::array, nn::hac::nca::kPartitionNum> mPartitionPath; bool mListFs; @@ -82,23 +78,25 @@ private: }; std::vector kak_list; - tc::Optional aes_ctr; + tc::Optional aes_ctr; } mContentKey; struct sPartitionInfo { std::shared_ptr reader; std::string fail_reason; - size_t offset; - size_t size; + int64_t offset; + int64_t size; // meta data nn::hac::nca::FormatType format_type; nn::hac::nca::HashType hash_type; nn::hac::nca::EncryptionType enc_type; - fnd::LayeredIntegrityMetadata layered_intergrity_metadata; - fnd::aes::sAesIvCtr aes_ctr; - } mPartitions[nn::hac::nca::kPartitionNum]; + //fnd::LayeredIntegrityMetadata layered_intergrity_metadata; + nn::hac::detail::aes_iv_t aes_ctr; + } + + std::array mPartitions; void importHeader(); void generateNcaBodyEncryptionKeys(); diff --git a/src/NroProcess.h b/src/NroProcess.h index 420b58d..efc8610 100644 --- a/src/NroProcess.h +++ b/src/NroProcess.h @@ -25,9 +25,9 @@ public: // for homebrew NROs with Asset blobs appended void setAssetListFs(bool list); - void setAssetIconExtractPath(const std::string& path); - void setAssetNacpExtractPath(const std::string& path); - void setAssetRomfsExtractPath(const std::string& path); + void setAssetIconExtractPath(const tc::io::Path& path); + void setAssetNacpExtractPath(const tc::io::Path& path); + void setAssetRomfsExtractPath(const tc::io::Path& path); const RoMetadataProcess& getRoMetadataProcess() const; private: diff --git a/src/PfsProcess.h b/src/PfsProcess.h index 2736809..50a482b 100644 --- a/src/PfsProcess.h +++ b/src/PfsProcess.h @@ -19,7 +19,7 @@ public: // pfs specific void setMountPointName(const std::string& mount_name); - void setExtractPath(const std::string& path); + void setExtractPath(const tc::io::Path& path); void setListFs(bool list_fs); const nn::hac::PartitionFsHeader& getPfsHeader() const; @@ -32,8 +32,7 @@ private: CliOutputMode mCliOutputMode; bool mVerify; - std::string mExtractPath; - bool mExtract; + tc::Optional mExtractPath; std::string mMountName; bool mListFs; diff --git a/src/PkiValidator.cpp b/src/PkiValidator.cpp index e25902a..861b15d 100644 --- a/src/PkiValidator.cpp +++ b/src/PkiValidator.cpp @@ -77,7 +77,7 @@ void nstool::PkiValidator::addCertificate(const nn::pki::SignedDatasecond.key_type) { - throw tc::Exception(mModuleName, fmt::print("Public key for issuer \"{:s}\" cannot verify this signature.", issuer); + throw tc::Exception(mModuleName, fmt::format("Public key for issuer \"{:s}\" cannot verify this signature.", issuer)); } if (sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240) @@ -129,7 +127,7 @@ void nstool::PkiValidator::validateSignature(const std::string& issuer, nn::pki: if (issuer_pubk_type == nn::pki::cert::RSA4096 && sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096) { - rsa_key = issuer_cert.getRsa4098PublicKey(); + rsa_key = issuer_cert.getRsa4096PublicKey(); } else if (issuer_pubk_type == nn::pki::cert::RSA2048 && sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048) { @@ -148,42 +146,25 @@ void nstool::PkiValidator::validateSignature(const std::string& issuer, nn::pki: // verify signature switch (signature_id) { - case (SIGN_ID_RSA4096_SHA1): - sig_validate_res = tc::crypto::VerifyRsa4096Pkcs1Sha1(signature.data(), hash.data(), rsa_key); + case (nn::pki::sign::SIGN_ID_RSA4096_SHA1): + sig_valid = tc::crypto::VerifyRsa4096Pkcs1Sha1(signature.data(), hash.data(), rsa_key); break; - case (SIGN_ID_RSA2048_SHA1): - sig_validate_res = tc::crypto::VerifyRsa2048Pkcs1Sha1(signature.data(), hash.data(), rsa_key); + case (nn::pki::sign::SIGN_ID_RSA2048_SHA1): + sig_valid = tc::crypto::VerifyRsa2048Pkcs1Sha1(signature.data(), hash.data(), rsa_key); break; - case (SIGN_ID_ECDSA240_SHA1): - sig_validate_res = false; + case (nn::pki::sign::SIGN_ID_ECDSA240_SHA1): + sig_valid = false; break; - case (SIGN_ID_RSA4096_SHA256): - sig_validate_res = tc::crypto::VerifyRsa4096Pkcs1Sha256(signature.data(), hash.data(), rsa_key); + case (nn::pki::sign::SIGN_ID_RSA4096_SHA256): + sig_valid = tc::crypto::VerifyRsa4096Pkcs1Sha256(signature.data(), hash.data(), rsa_key); break; - case (SIGN_ID_RSA2048_SHA256): - sig_validate_res = tc::crypto::VerifyRsa2048Pkcs1Sha256(signature.data(), hash.data(), rsa_key); + case (nn::pki::sign::SIGN_ID_RSA2048_SHA256): + sig_valid = tc::crypto::VerifyRsa2048Pkcs1Sha256(signature.data(), hash.data(), rsa_key); break; - case (SIGN_ID_ECDSA240_SHA256): - sig_validate_res = false; + case (nn::pki::sign::SIGN_ID_ECDSA240_SHA256): + sig_valid = false; break; } - if (sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096) - { - sig_validate_res = fnd::rsa::pkcs::rsaVerify(issuer_cert.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); - } - else if (sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048) - { - sig_validate_res = fnd::rsa::pkcs::rsaVerify(issuer_cert.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); - } - else if (sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240) - { - throw tc::Exception(mModuleName, "ECDSA signatures are not supported"); - } - else - { - throw tc::Exception(mModuleName, "Mismatch between issuer public key and signature type"); - } - if (sig_valid == false) { diff --git a/src/RomfsProcess.h b/src/RomfsProcess.h index dc39ff4..35575c4 100644 --- a/src/RomfsProcess.h +++ b/src/RomfsProcess.h @@ -45,8 +45,8 @@ public: struct sFile { std::string name; - uint64_t offset; - uint64_t size; + int64_t offset; + int64_t size; void operator=(const sFile& other) { @@ -84,7 +84,7 @@ public: // romfs specific void setMountPointName(const std::string& mount_name); - void setExtractPath(const std::string& path); + void setExtractPath(const tc::io::Path& path); void setListFs(bool list_fs); const sDirectory& getRootDir() const; @@ -101,8 +101,7 @@ private: bool mVerbose; bool mVerify; - std::string mExtractPath; - bool mExtract; + tc::Optional mExtractPath; std::string mMountName; bool mListFs; diff --git a/src/Settings.cpp b/src/Settings.cpp index a9a6bce..4e15cbb 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -349,6 +349,9 @@ private: nstool::SettingsInitializer::SettingsInitializer(const std::vector& args) : Settings(), mModuleLabel("nstool::SettingsInitializer"), + mShowLayout(false), + mShowKeydata(false), + mVerbose(false), mTitleKey(), mBodyKey(), mTikPath(), @@ -359,6 +362,23 @@ nstool::SettingsInitializer::SettingsInitializer(const std::vector& if (infile.path.isNull()) throw tc::ArgumentException(mModuleLabel, "No input file was specified."); + // determine CLI output mode + opt.cli_output_mode.show_basic_info = true; + if (mVerbose) + { + opt.cli_output_mode.show_extended_info = true; + opt.cli_output_mode.show_layout = true; + opt.cli_output_mode.show_keydata = true; + } + if (mShowKeydata) + { + opt.cli_output_mode.show_keydata = true; + } + if (mShowLayout) + { + opt.cli_output_mode.show_layout = true; + } + // locate key file, if not specfied if (mKeysetPath.isNull()) { @@ -431,9 +451,9 @@ void nstool::SettingsInitializer::parse_args(const std::vector& arg // none just yet // get option flags - opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(opt.show_layout, {"--showlayout"}))); - opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(opt.show_keydata, { "--showkeys" }))); - opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(opt.verbose, {"-v", "--verbose"}))); + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(mShowLayout, {"--showlayout"}))); + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(mShowKeydata, { "--showkeys" }))); + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(mVerbose, {"-v", "--verbose"}))); opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(opt.verify, {"-y", "--verify"}))); opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(opt.is_dev, {"-d", "--dev"}))); diff --git a/src/Settings.h b/src/Settings.h index 75564c1..6ba3f5c 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -138,6 +138,7 @@ private: bool mShowLayout; bool mShowKeydata; + bool mVerbose; tc::Optional mKeysetPath; tc::Optional mTitleKey; diff --git a/src/main.cpp b/src/main.cpp index 4c6ba2f..4a86c61 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,22 +2,22 @@ #include #include "Settings.h" -/* -#include "GameCardProcess.h" -#include "PfsProcess.h" -#include "RomfsProcess.h" -#include "NcaProcess.h" -#include "MetaProcess.h" + +//#include "GameCardProcess.h" +//#include "PfsProcess.h" +//#include "RomfsProcess.h" +//#include "NcaProcess.h" +//#include "MetaProcess.h" #include "CnmtProcess.h" -#include "NsoProcess.h" -#include "NroProcess.h" -#include "NacpProcess.h" -#include "IniProcess.h" -#include "KipProcess.h" -#include "PkiCertProcess.h" +//#include "NsoProcess.h" +//#include "NroProcess.h" +//#include "NacpProcess.h" +//#include "IniProcess.h" +//#include "KipProcess.h" +//#include "PkiCertProcess.h" #include "EsTikProcess.h" #include "AssetProcess.h" -*/ + int umain(const std::vector& args, const std::vector& env) { @@ -27,208 +27,214 @@ int umain(const std::vector& args, const std::vector& std::shared_ptr infile_stream = std::make_shared(tc::io::FileStream(set.infile.path.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read)); + /* if (set.infile.filetype == nstool::Settings::FILE_TYPE_GAMECARD) { - GameCardProcess obj; + nstool::GameCardProcess obj; obj.setInputFile(infile_stream); obj.setKeyCfg(set.opt.keybag); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); if (set.xci.update_extract_path.isSet()) obj.setPartitionForExtract(nn::hac::gc::kUpdatePartitionStr, set.xci.update_extract_path.get()); if (set.xci.logo_extract_path.isSet()) obj.setPartitionForExtract(nn::hac::gc::kLogoPartitionStr, set.xci.logo_extract_path.get()); - if (user_set.getXciNormalPath().isSet()) - obj.setPartitionForExtract(nn::hac::gc::kNormalPartitionStr, user_set.getXciNormalPath().get()); - if (user_set.getXciSecurePath().isSet()) - obj.setPartitionForExtract(nn::hac::gc::kSecurePartitionStr, user_set.getXciSecurePath().get()); - obj.setListFs(user_set.isListFs()); + if (set.xci.normal_extract_path.isSet()) + obj.setPartitionForExtract(nn::hac::gc::kNormalPartitionStr, set.xci.normal_extract_path.get()); + if (set.xci.secure_extract_path.isSet()) + obj.setPartitionForExtract(nn::hac::gc::kSecurePartitionStr, set.xci.secure_extract_path.get()); + obj.setListFs(set.fs.show_fs_tree); obj.process(); } + */ + /* else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PARTITIONFS || set.infile.filetype == nstool::Settings::FILE_TYPE_NSP) { - PfsProcess obj; + nstool::PfsProcess obj; obj.setInputFile(infile_stream); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (user_set.getFsPath().isSet()) - obj.setExtractPath(user_set.getFsPath().get()); - obj.setListFs(user_set.isListFs()); + if (set.fs.extract_path.isSet()) + obj.setExtractPath(set.fs.extract_path.get()); + obj.setListFs(set.fs.show_fs_tree); obj.process(); } else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ROMFS) { - RomfsProcess obj; + nstool::RomfsProcess obj; obj.setInputFile(infile_stream); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (user_set.getFsPath().isSet()) - obj.setExtractPath(user_set.getFsPath().get()); - obj.setListFs(user_set.isListFs()); + if (set.fs.extract_path.isSet()) + obj.setExtractPath(set.fs.extract_path.get()); + obj.setListFs(set.fs.show_fs_tree); obj.process(); } else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NCA) { - NcaProcess obj; + nstool::NcaProcess obj; obj.setInputFile(infile_stream); obj.setKeyCfg(set.opt.keybag); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (user_set.getNcaPart0Path().isSet()) - obj.setPartition0ExtractPath(user_set.getNcaPart0Path().get()); - if (user_set.getNcaPart1Path().isSet()) - obj.setPartition1ExtractPath(user_set.getNcaPart1Path().get()); - if (user_set.getNcaPart2Path().isSet()) - obj.setPartition2ExtractPath(user_set.getNcaPart2Path().get()); - if (user_set.getNcaPart3Path().isSet()) - obj.setPartition3ExtractPath(user_set.getNcaPart3Path().get()); - obj.setListFs(user_set.isListFs()); + if (set.nca.part0_extract_path.isSet()) + obj.setPartition0ExtractPath(set.nca.part0_extract_path.get()); + if (set.nca.part1_extract_path.isSet()) + obj.setPartition1ExtractPath(set.nca.part1_extract_path.get()); + if (set.nca.part2_extract_path.isSet()) + obj.setPartition2ExtractPath(set.nca.part2_extract_path.get()); + if (set.nca.part3_extract_path.isSet()) + obj.setPartition3ExtractPath(set.nca.part3_extract_path.get()); + obj.setListFs(set.fs.show_fs_tree); obj.process(); } else if (set.infile.filetype == nstool::Settings::FILE_TYPE_META) { - MetaProcess obj; + nstool::MetaProcess obj; obj.setInputFile(infile_stream); obj.setKeyCfg(set.opt.keybag); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); obj.process(); } - else if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT) + + else*/ if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT) { - CnmtProcess obj; + nstool::CnmtProcess obj; obj.setInputFile(infile_stream); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); obj.process(); } + /* else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NSO) { - NsoProcess obj; + nstool::NsoProcess obj; obj.setInputFile(infile_stream); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - obj.setIs64BitInstruction(user_set.getIs64BitInstruction()); - obj.setListApi(user_set.isListApi()); - obj.setListSymbols(user_set.isListSymbols()); + obj.setIs64BitInstruction(set.code.is_64bit_instruction); + obj.setListApi(set.code.list_api); + obj.setListSymbols(set.code.list_symbols); obj.process(); } else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NRO) { - NroProcess obj; + nstool::NroProcess obj; obj.setInputFile(infile_stream); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - obj.setIs64BitInstruction(user_set.getIs64BitInstruction()); - obj.setListApi(user_set.isListApi()); - obj.setListSymbols(user_set.isListSymbols()); + obj.setIs64BitInstruction(set.code.is_64bit_instruction); + obj.setListApi(set.code.list_api); + obj.setListSymbols(set.code.list_symbols); - if (user_set.getAssetIconPath().isSet()) - obj.setAssetIconExtractPath(user_set.getAssetIconPath().get()); - if (user_set.getAssetNacpPath().isSet()) - obj.setAssetNacpExtractPath(user_set.getAssetNacpPath().get()); + if (set.aset.icon_extract_path.isSet()) + obj.setAssetIconExtractPath(set.aset.icon_extract_path.get()); + if (set.aset.nacp_extract_path.isSet()) + obj.setAssetNacpExtractPath(set.aset.nacp_extract_path.get()); - if (user_set.getFsPath().isSet()) - obj.setAssetRomfsExtractPath(user_set.getFsPath().get()); - obj.setAssetListFs(user_set.isListFs()); + if (set.fs.extract_path.isSet()) + obj.setAssetRomfsExtractPath(set.fs.extract_path.get()); + obj.setAssetListFs(set.fs.show_fs_tree); obj.process(); } else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NACP) { - NacpProcess obj; + nstool::NacpProcess obj; obj.setInputFile(infile_stream); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); obj.process(); } else if (set.infile.filetype == nstool::Settings::FILE_TYPE_INI) { - IniProcess obj; + nstool::IniProcess obj; obj.setInputFile(infile_stream); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (user_set.getKipExtractPath().isSet()) - obj.setKipExtractPath(user_set.getKipExtractPath().get()); + if (set.kip.extract_path.isSet()) + obj.setKipExtractPath(set.kip.extract_path.get()); obj.process(); } else if (set.infile.filetype == nstool::Settings::FILE_TYPE_KIP) { - KipProcess obj; + nstool::KipProcess obj; obj.setInputFile(infile_stream); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); obj.process(); } else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PKI_CERT) { - PkiCertProcess obj; + nstool::PkiCertProcess obj; obj.setInputFile(infile_stream); obj.setKeyCfg(set.opt.keybag); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); obj.process(); } + */ else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ES_TIK) { - EsTikProcess obj; + nstool::EsTikProcess obj; obj.setInputFile(infile_stream); obj.setKeyCfg(set.opt.keybag); - obj.setCertificateChain(user_set.getCertificateChain()); - obj.setCliOutputMode(user_set.getCliOutputMode()); + //obj.setCertificateChain(user_set.getCertificateChain()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); obj.process(); } else if (set.infile.filetype == nstool::Settings::FILE_TYPE_HB_ASSET) { - AssetProcess obj; + nstool::AssetProcess obj; obj.setInputFile(infile_stream); - obj.setCliOutputMode(user_set.getCliOutputMode()); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (user_set.getAssetIconPath().isSet()) - obj.setIconExtractPath(user_set.getAssetIconPath().get()); - if (user_set.getAssetNacpPath().isSet()) - obj.setNacpExtractPath(user_set.getAssetNacpPath().get()); + if (set.aset.icon_extract_path.isSet()) + obj.setIconExtractPath(set.aset.icon_extract_path.get()); + if (set.aset.nacp_extract_path.isSet()) + obj.setNacpExtractPath(set.aset.nacp_extract_path.get()); - if (user_set.getFsPath().isSet()) - obj.setRomfsExtractPath(user_set.getFsPath().get()); - obj.setListFs(user_set.isListFs()); + if (set.fs.extract_path.isSet()) + obj.setRomfsExtractPath(set.fs.extract_path.get()); + obj.setListFs(set.fs.show_fs_tree); obj.process(); } diff --git a/src/GameCardProcess.cpp b/src/not_ported/GameCardProcess.cpp similarity index 100% rename from src/GameCardProcess.cpp rename to src/not_ported/GameCardProcess.cpp diff --git a/src/IniProcess.cpp b/src/not_ported/IniProcess.cpp similarity index 100% rename from src/IniProcess.cpp rename to src/not_ported/IniProcess.cpp diff --git a/src/KipProcess.cpp b/src/not_ported/KipProcess.cpp similarity index 100% rename from src/KipProcess.cpp rename to src/not_ported/KipProcess.cpp diff --git a/src/MetaProcess.cpp b/src/not_ported/MetaProcess.cpp similarity index 100% rename from src/MetaProcess.cpp rename to src/not_ported/MetaProcess.cpp diff --git a/src/NacpProcess.cpp b/src/not_ported/NacpProcess.cpp similarity index 100% rename from src/NacpProcess.cpp rename to src/not_ported/NacpProcess.cpp diff --git a/src/NcaProcess.cpp b/src/not_ported/NcaProcess.cpp similarity index 100% rename from src/NcaProcess.cpp rename to src/not_ported/NcaProcess.cpp diff --git a/src/NroProcess.cpp b/src/not_ported/NroProcess.cpp similarity index 100% rename from src/NroProcess.cpp rename to src/not_ported/NroProcess.cpp diff --git a/src/NsoProcess.cpp b/src/not_ported/NsoProcess.cpp similarity index 100% rename from src/NsoProcess.cpp rename to src/not_ported/NsoProcess.cpp diff --git a/src/PfsProcess.cpp b/src/not_ported/PfsProcess.cpp similarity index 100% rename from src/PfsProcess.cpp rename to src/not_ported/PfsProcess.cpp diff --git a/src/PkiCertProcess.cpp b/src/not_ported/PkiCertProcess.cpp similarity index 100% rename from src/PkiCertProcess.cpp rename to src/not_ported/PkiCertProcess.cpp diff --git a/src/RoMetadataProcess.cpp b/src/not_ported/RoMetadataProcess.cpp similarity index 100% rename from src/RoMetadataProcess.cpp rename to src/not_ported/RoMetadataProcess.cpp diff --git a/src/RomfsProcess.cpp b/src/not_ported/RomfsProcess.cpp similarity index 100% rename from src/RomfsProcess.cpp rename to src/not_ported/RomfsProcess.cpp diff --git a/src/types.h b/src/types.h index 5721307..2d22f1c 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include diff --git a/src/util.cpp b/src/util.cpp index 8db21e2..b8fb0cc 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -68,13 +68,13 @@ void nstool::processResFile(const std::shared_ptr& file, std::m void nstool::writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size) { - writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileAccess::OpenOrCreate, tc::io::FileMode::Write)), cache_size); + writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache_size); } void nstool::writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size) { - writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileAccess::OpenOrCreate, tc::io::FileMode::Write)), cache_size); + writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache_size); } void nstool::writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size) @@ -90,7 +90,7 @@ void nstool::writeStreamToStream(const std::shared_ptr& in_stre cache_read_len = in_stream->read(cache.data(), cache.size()); if (cache_read_len == 0) { - throw tc::io::IOException(mModuleLabel, "Failed to read from RomFs file."); + throw tc::io::IOException("nstool::writeStreamToStream()", "Failed to read from source streeam."); } out_stream->write(cache.data(), cache_read_len); From 0fbfddae1de35d17480a83e3d8ec76d76c0fd9e4 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 1 Oct 2021 17:40:46 +0800 Subject: [PATCH 015/119] Update libnintendo-es with new propertymask flags. --- deps/libnintendo-es | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libnintendo-es b/deps/libnintendo-es index 7572682..bcbbf13 160000 --- a/deps/libnintendo-es +++ b/deps/libnintendo-es @@ -1 +1 @@ -Subproject commit 7572682f1fa819fd7ef1a4b639cb14fa67128f0d +Subproject commit bcbbf13670eaabfa21697887227d1aabea794f9d From d63356e0125f76bb586abf23105d9b8838a7ad82 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 1 Oct 2021 20:42:34 +0800 Subject: [PATCH 016/119] Move NacpProcess back. --- src/{not_ported => }/NacpProcess.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{not_ported => }/NacpProcess.cpp (100%) diff --git a/src/not_ported/NacpProcess.cpp b/src/NacpProcess.cpp similarity index 100% rename from src/not_ported/NacpProcess.cpp rename to src/NacpProcess.cpp From bf53ac46401e1df8f9aa9ca00092c9b60abf1e7e Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 1 Oct 2021 20:46:35 +0800 Subject: [PATCH 017/119] Port NacpProcess to libtoolchain. --- src/NacpProcess.cpp | 219 ++++++++++++++++++++++---------------------- src/NacpProcess.h | 2 +- 2 files changed, 111 insertions(+), 110 deletions(-) diff --git a/src/NacpProcess.cpp b/src/NacpProcess.cpp index e454709..f2e673c 100644 --- a/src/NacpProcess.cpp +++ b/src/NacpProcess.cpp @@ -1,17 +1,11 @@ #include "NacpProcess.h" -#include -#include -#include - -#include -#include - #include nstool::NacpProcess::NacpProcess() : + mModuleName("nstool::NacpProcess"), mFile(), - mCliOutputMode((1 << OUTPUT_BASIC)), + mCliOutputMode(true, false, false, false), mVerify(false) { } @@ -46,480 +40,487 @@ const nn::hac::ApplicationControlProperty& nstool::NacpProcess::getApplicationCo void nstool::NacpProcess::importNacp() { - tc::ByteData scratch; - - if (*mFile == nullptr) + if (mFile == nullptr) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "No file reader set."); } - scratch.alloc((*mFile)->size()); - (*mFile)->read(scratch.data(), 0, scratch.size()); + // check if file_size is greater than 20MB, don't import. + size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + if (file_size > (0x100000 * 20)) + { + throw tc::Exception(mModuleName, "File too large."); + } + + // read cnmt + tc::ByteData scratch = tc::ByteData(file_size); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); mNacp.fromBytes(scratch.data(), scratch.size()); } void nstool::NacpProcess::displayNacp() { - std::cout << "[ApplicationControlProperty]" << std::endl; + fmt::print("[ApplicationControlProperty]\n"); // Title if (mNacp.getTitle().size() > 0) { - std::cout << " Title:" << std::endl; + fmt::print(" Title:\n"); for (auto itr = mNacp.getTitle().begin(); itr != mNacp.getTitle().end(); itr++) { - std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getLanguageAsString(itr->language) << ":" << std::endl; - std::cout << " Name: " << itr->name << std::endl; - std::cout << " Publisher: " << itr->publisher << std::endl; + fmt::print(" {:s}:\n", nn::hac::ApplicationControlPropertyUtil::getLanguageAsString(itr->language)); + fmt::print(" Name: {:s}\n", itr->name); + fmt::print(" Publisher: {:s}\n", itr->publisher); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " Title: None" << std::endl; + fmt::print(" Title: None\n"); } // Isbn if (mNacp.getIsbn().empty() == false) { - std::cout << " ISBN: " << mNacp.getIsbn() << std::endl; + fmt::print(" ISBN: {:s}\n", mNacp.getIsbn()); } else if (mCliOutputMode.show_extended_info) { - std::cout << " ISBN: (NotSet)" << std::endl; + fmt::print(" ISBN: (NotSet)\n"); } // StartupUserAccount if (mNacp.getStartupUserAccount() != nn::hac::nacp::StartupUserAccount::None || mCliOutputMode.show_extended_info) { - std::cout << " StartupUserAccount: " << nn::hac::ApplicationControlPropertyUtil::getStartupUserAccountAsString(mNacp.getStartupUserAccount()) << std::endl; + fmt::print(" StartupUserAccount: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getStartupUserAccountAsString(mNacp.getStartupUserAccount())); } // UserAccountSwitchLock if (mNacp.getUserAccountSwitchLock() != nn::hac::nacp::UserAccountSwitchLock::Disable || mCliOutputMode.show_extended_info) { - std::cout << " UserAccountSwitchLock: " << nn::hac::ApplicationControlPropertyUtil::getUserAccountSwitchLockAsString(mNacp.getUserAccountSwitchLock()) << std::endl; + fmt::print(" UserAccountSwitchLock: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getUserAccountSwitchLockAsString(mNacp.getUserAccountSwitchLock())); } // AddOnContentRegistrationType if (mNacp.getAddOnContentRegistrationType() != nn::hac::nacp::AddOnContentRegistrationType::AllOnLaunch || mCliOutputMode.show_extended_info) { - std::cout << " AddOnContentRegistrationType: " << nn::hac::ApplicationControlPropertyUtil::getAddOnContentRegistrationTypeAsString(mNacp.getAddOnContentRegistrationType()) << std::endl; + fmt::print(" AddOnContentRegistrationType: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getAddOnContentRegistrationTypeAsString(mNacp.getAddOnContentRegistrationType())); } // Attribute if (mNacp.getAttribute().size() > 0) { - std::cout << " Attribute:" << std::endl; + fmt::print(" Attribute:\n"); for (auto itr = mNacp.getAttribute().begin(); itr != mNacp.getAttribute().end(); itr++) { - std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getAttributeFlagAsString(*itr) << std::endl; + fmt::print(" {:s}\n", nn::hac::ApplicationControlPropertyUtil::getAttributeFlagAsString(*itr)); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " Attribute: None" << std::endl; + fmt::print(" Attribute: None\n"); } // SupportedLanguage if (mNacp.getSupportedLanguage().size() > 0) { - std::cout << " SupportedLanguage:" << std::endl; + fmt::print(" SupportedLanguage:\n"); for (auto itr = mNacp.getSupportedLanguage().begin(); itr != mNacp.getSupportedLanguage().end(); itr++) { - std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getLanguageAsString(*itr) << std::endl; + fmt::print(" {:s}\n", nn::hac::ApplicationControlPropertyUtil::getLanguageAsString(*itr)); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " SupportedLanguage: None" << std::endl; + fmt::print(" SupportedLanguage: None\n"); } // ParentalControl if (mNacp.getParentalControl().size() > 0) { - std::cout << " ParentalControl:" << std::endl; + fmt::print(" ParentalControl:\n"); for (auto itr = mNacp.getParentalControl().begin(); itr != mNacp.getParentalControl().end(); itr++) { - std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getParentalControlFlagAsString(*itr) << std::endl; + fmt::print(" {:s}\n", nn::hac::ApplicationControlPropertyUtil::getParentalControlFlagAsString(*itr)); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " ParentalControl: None" << std::endl; + fmt::print(" ParentalControl: None\n"); } // Screenshot if (mNacp.getScreenshot() != nn::hac::nacp::Screenshot::Allow || mCliOutputMode.show_extended_info) { - std::cout << " Screenshot: " << nn::hac::ApplicationControlPropertyUtil::getScreenshotAsString(mNacp.getScreenshot()) << std::endl; + fmt::print(" Screenshot: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getScreenshotAsString(mNacp.getScreenshot())); } // VideoCapture if (mNacp.getVideoCapture() != nn::hac::nacp::VideoCapture::Disable || mCliOutputMode.show_extended_info) { - std::cout << " VideoCapture: " << nn::hac::ApplicationControlPropertyUtil::getVideoCaptureAsString(mNacp.getVideoCapture()) << std::endl; + fmt::print(" VideoCapture: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getVideoCaptureAsString(mNacp.getVideoCapture())); } // DataLossConfirmation if (mNacp.getDataLossConfirmation() != nn::hac::nacp::DataLossConfirmation::None || mCliOutputMode.show_extended_info) { - std::cout << " DataLossConfirmation: " << nn::hac::ApplicationControlPropertyUtil::getDataLossConfirmationAsString(mNacp.getDataLossConfirmation()) << std::endl; + fmt::print(" DataLossConfirmation: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getDataLossConfirmationAsString(mNacp.getDataLossConfirmation())); } // PlayLogPolicy if (mNacp.getPlayLogPolicy() != nn::hac::nacp::PlayLogPolicy::All || mCliOutputMode.show_extended_info) { - std::cout << " PlayLogPolicy: " << nn::hac::ApplicationControlPropertyUtil::getPlayLogPolicyAsString(mNacp.getPlayLogPolicy()) << std::endl; + fmt::print(" PlayLogPolicy: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getPlayLogPolicyAsString(mNacp.getPlayLogPolicy())); } // PresenceGroupId if (mNacp.getPresenceGroupId() != 0 || mCliOutputMode.show_extended_info) { - std::cout << " PresenceGroupId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getPresenceGroupId() << std::endl; + fmt::print(" PresenceGroupId: 0x{:016x}\n", mNacp.getPresenceGroupId()); } // RatingAge if (mNacp.getRatingAge().size() > 0) { - std::cout << " RatingAge:" << std::endl; + fmt::print(" RatingAge:\n"); for (auto itr = mNacp.getRatingAge().begin(); itr != mNacp.getRatingAge().end(); itr++) { - std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getOrganisationAsString(itr->organisation) << ":" << std::endl; - std::cout << " Age: " << std::dec << (uint32_t)itr->age << std::endl; + fmt::print(" {:s}:\n", nn::hac::ApplicationControlPropertyUtil::getOrganisationAsString(itr->organisation)); + fmt::print(" Age: {:d}\n", itr->age); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " RatingAge: None" << std::endl; + fmt::print(" RatingAge: None\n"); } // DisplayVersion if (mNacp.getDisplayVersion().empty() == false) { - std::cout << " DisplayVersion: " << mNacp.getDisplayVersion() << std::endl; + fmt::print(" DisplayVersion: {:s}\n", mNacp.getDisplayVersion()); } else if (mCliOutputMode.show_extended_info) { - std::cout << " DisplayVersion: (NotSet)" << std::endl; + fmt::print(" DisplayVersion: (NotSet)\n"); } // AddOnContentBaseId if (mNacp.getAddOnContentBaseId() != 0 || mCliOutputMode.show_extended_info) { - std::cout << " AddOnContentBaseId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getAddOnContentBaseId() << std::endl; + fmt::print(" AddOnContentBaseId: 0x{:016x}\n", mNacp.getAddOnContentBaseId()); } // SaveDataOwnerId if (mNacp.getSaveDataOwnerId() != 0 || mCliOutputMode.show_extended_info) { - std::cout << " SaveDataOwnerId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getSaveDataOwnerId() << std::endl; + fmt::print(" SaveDataOwnerId: 0x{:016x}\n", mNacp.getSaveDataOwnerId()); } // UserAccountSaveDataSize if (mNacp.getUserAccountSaveDataSize().size != 0 || mCliOutputMode.show_extended_info) { - std::cout << " UserAccountSaveDataSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataSize().size) << std::endl; + fmt::print(" UserAccountSaveDataSize: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataSize().size)); } // UserAccountSaveDataJournalSize if (mNacp.getUserAccountSaveDataSize().journal_size != 0 || mCliOutputMode.show_extended_info) { - std::cout << " UserAccountSaveDataJournalSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataSize().journal_size) << std::endl; + fmt::print(" UserAccountSaveDataJournalSize: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataSize().journal_size)); } // DeviceSaveDataSize if (mNacp.getDeviceSaveDataSize().size != 0 || mCliOutputMode.show_extended_info) { - std::cout << " DeviceSaveDataSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataSize().size) << std::endl; + fmt::print(" DeviceSaveDataSize: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataSize().size)); } // DeviceSaveDataJournalSize if (mNacp.getDeviceSaveDataSize().journal_size != 0 || mCliOutputMode.show_extended_info) { - std::cout << " DeviceSaveDataJournalSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataSize().journal_size) << std::endl; + fmt::print(" DeviceSaveDataJournalSize: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataSize().journal_size)); } // BcatDeliveryCacheStorageSize if (mNacp.getBcatDeliveryCacheStorageSize() != 0 || mCliOutputMode.show_extended_info) { - std::cout << " BcatDeliveryCacheStorageSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getBcatDeliveryCacheStorageSize()) << std::endl; + fmt::print(" BcatDeliveryCacheStorageSize: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getBcatDeliveryCacheStorageSize())); } // ApplicationErrorCodeCategory if (mNacp.getApplicationErrorCodeCategory().empty() == false) { - std::cout << " ApplicationErrorCodeCategory: " << mNacp.getApplicationErrorCodeCategory() << std::endl; + fmt::print(" ApplicationErrorCodeCategory: {:s}\n", mNacp.getApplicationErrorCodeCategory()); } else if (mCliOutputMode.show_extended_info) { - std::cout << " ApplicationErrorCodeCategory: (NotSet)" << std::endl; + fmt::print(" ApplicationErrorCodeCategory: (NotSet)\n"); } // LocalCommunicationId if (mNacp.getLocalCommunicationId().size() > 0) { - std::cout << " LocalCommunicationId:" << std::endl; + fmt::print(" LocalCommunicationId:\n"); for (auto itr = mNacp.getLocalCommunicationId().begin(); itr != mNacp.getLocalCommunicationId().end(); itr++) { - std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << *itr << std::endl; + fmt::print(" 0x{:016x}\n", *itr); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " LocalCommunicationId: None" << std::endl; + fmt::print(" LocalCommunicationId: None\n"); } // LogoType //if (mNacp.getLogoType() != nn::hac::nacp::LogoType::Nintendo || mCliOutputMode.show_extended_info) //{ - std::cout << " LogoType: " << nn::hac::ApplicationControlPropertyUtil::getLogoTypeAsString(mNacp.getLogoType()) << std::endl; + fmt::print(" LogoType: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getLogoTypeAsString(mNacp.getLogoType())); //} // LogoHandling if (mNacp.getLogoHandling() != nn::hac::nacp::LogoHandling::Auto || mCliOutputMode.show_extended_info) { - std::cout << " LogoHandling: " << nn::hac::ApplicationControlPropertyUtil::getLogoHandlingAsString(mNacp.getLogoHandling()) << std::endl; + fmt::print(" LogoHandling: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getLogoHandlingAsString(mNacp.getLogoHandling())); } // RuntimeAddOnContentInstall if (mNacp.getRuntimeAddOnContentInstall() != nn::hac::nacp::RuntimeAddOnContentInstall::Deny || mCliOutputMode.show_extended_info) { - std::cout << " RuntimeAddOnContentInstall: " << nn::hac::ApplicationControlPropertyUtil::getRuntimeAddOnContentInstallAsString(mNacp.getRuntimeAddOnContentInstall()) << std::endl; + fmt::print(" RuntimeAddOnContentInstall: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getRuntimeAddOnContentInstallAsString(mNacp.getRuntimeAddOnContentInstall())); } // RuntimeParameterDelivery if (mNacp.getRuntimeParameterDelivery() != nn::hac::nacp::RuntimeParameterDelivery::Always || mCliOutputMode.show_extended_info) { - std::cout << " RuntimeParameterDelivery: " << nn::hac::ApplicationControlPropertyUtil::getRuntimeParameterDeliveryAsString(mNacp.getRuntimeParameterDelivery()) << std::endl; + fmt::print(" RuntimeParameterDelivery: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getRuntimeParameterDeliveryAsString(mNacp.getRuntimeParameterDelivery())); } // CrashReport if (mNacp.getCrashReport() != nn::hac::nacp::CrashReport::Deny || mCliOutputMode.show_extended_info) { - std::cout << " CrashReport: " << nn::hac::ApplicationControlPropertyUtil::getCrashReportAsString(mNacp.getCrashReport()) << std::endl; + fmt::print(" CrashReport: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getCrashReportAsString(mNacp.getCrashReport())); } // Hdcp if (mNacp.getHdcp() != nn::hac::nacp::Hdcp::None || mCliOutputMode.show_extended_info) { - std::cout << " Hdcp: " << nn::hac::ApplicationControlPropertyUtil::getHdcpAsString(mNacp.getHdcp()) << std::endl; + fmt::print(" Hdcp: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getHdcpAsString(mNacp.getHdcp())); } // SeedForPsuedoDeviceId if (mNacp.getSeedForPsuedoDeviceId() != 0 || mCliOutputMode.show_extended_info) { - std::cout << " SeedForPsuedoDeviceId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getSeedForPsuedoDeviceId() << std::endl; + fmt::print(" SeedForPsuedoDeviceId: 0x{:016x}\n", mNacp.getSeedForPsuedoDeviceId()); } // BcatPassphase if (mNacp.getBcatPassphase().empty() == false) { - std::cout << " BcatPassphase: " << mNacp.getBcatPassphase() << std::endl; + fmt::print(" BcatPassphase: {:s}\n", mNacp.getBcatPassphase()); } else if (mCliOutputMode.show_extended_info) { - std::cout << " BcatPassphase: (NotSet)" << std::endl; + fmt::print(" BcatPassphase: (NotSet)\n"); } // StartupUserAccountOption if (mNacp.getStartupUserAccountOption().size() > 0) { - std::cout << " StartupUserAccountOption:" << std::endl; + fmt::print(" StartupUserAccountOption:\n"); for (auto itr = mNacp.getStartupUserAccountOption().begin(); itr != mNacp.getStartupUserAccountOption().end(); itr++) { - std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getStartupUserAccountOptionFlagAsString(*itr) << std::endl; + fmt::print(" {:s}\n", nn::hac::ApplicationControlPropertyUtil::getStartupUserAccountOptionFlagAsString(*itr)); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " StartupUserAccountOption: None" << std::endl; + fmt::print(" StartupUserAccountOption: None\n"); } // UserAccountSaveDataSizeMax if (mNacp.getUserAccountSaveDataMax().size != 0 || mCliOutputMode.show_extended_info) { - std::cout << " UserAccountSaveDataSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataMax().size) << std::endl; + fmt::print(" UserAccountSaveDataSizeMax: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataMax().size)); } // UserAccountSaveDataJournalSizeMax if (mNacp.getUserAccountSaveDataMax().journal_size != 0 || mCliOutputMode.show_extended_info) { - std::cout << " UserAccountSaveDataJournalSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataMax().journal_size) << std::endl; + fmt::print(" UserAccountSaveDataJournalSizeMax: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataMax().journal_size)); } // DeviceSaveDataSizeMax if (mNacp.getDeviceSaveDataMax().size != 0 || mCliOutputMode.show_extended_info) { - std::cout << " DeviceSaveDataSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataMax().size) << std::endl; + fmt::print(" DeviceSaveDataSizeMax: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataMax().size)); } // DeviceSaveDataJournalSizeMax if (mNacp.getDeviceSaveDataMax().journal_size != 0 || mCliOutputMode.show_extended_info) { - std::cout << " DeviceSaveDataJournalSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataMax().journal_size) << std::endl; + fmt::print(" DeviceSaveDataJournalSizeMax: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataMax().journal_size)); } // TemporaryStorageSize if (mNacp.getTemporaryStorageSize() != 0 || mCliOutputMode.show_extended_info) { - std::cout << " TemporaryStorageSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getTemporaryStorageSize()) << std::endl; + fmt::print(" TemporaryStorageSize: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getTemporaryStorageSize())); } // CacheStorageSize if (mNacp.getCacheStorageSize().size != 0 || mCliOutputMode.show_extended_info) { - std::cout << " CacheStorageSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageSize().size) << std::endl; + fmt::print(" CacheStorageSize: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageSize().size)); } // CacheStorageJournalSize if (mNacp.getCacheStorageSize().journal_size != 0 || mCliOutputMode.show_extended_info) { - std::cout << " CacheStorageJournalSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageSize().journal_size) << std::endl; + fmt::print(" CacheStorageJournalSize: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageSize().journal_size)); } // CacheStorageDataAndJournalSizeMax if (mNacp.getCacheStorageDataAndJournalSizeMax() != 0 || mCliOutputMode.show_extended_info) { - std::cout << " CacheStorageDataAndJournalSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageDataAndJournalSizeMax()) << std::endl; + fmt::print(" CacheStorageDataAndJournalSizeMax: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageDataAndJournalSizeMax())); } // CacheStorageIndexMax if (mNacp.getCacheStorageIndexMax() != 0 || mCliOutputMode.show_extended_info) { - std::cout << " CacheStorageIndexMax: 0x" << std::hex << std::setw(4) << std::setfill('0') << mNacp.getCacheStorageIndexMax() << std::endl; + fmt::print(" CacheStorageIndexMax: 0x{:04x}\n", mNacp.getCacheStorageIndexMax()); } // PlayLogQueryableApplicationId if (mNacp.getPlayLogQueryableApplicationId().size() > 0) { - std::cout << " PlayLogQueryableApplicationId:" << std::endl; + fmt::print(" PlayLogQueryableApplicationId:\n"); for (auto itr = mNacp.getPlayLogQueryableApplicationId().begin(); itr != mNacp.getPlayLogQueryableApplicationId().end(); itr++) { - std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << *itr << std::endl; + fmt::print(" 0x{:016x}\n", *itr); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " PlayLogQueryableApplicationId: None" << std::endl; + fmt::print(" PlayLogQueryableApplicationId: None\n"); } // PlayLogQueryCapability if (mNacp.getPlayLogQueryCapability() != nn::hac::nacp::PlayLogQueryCapability::None || mCliOutputMode.show_extended_info) { - std::cout << " PlayLogQueryCapability: " << nn::hac::ApplicationControlPropertyUtil::getPlayLogQueryCapabilityAsString(mNacp.getPlayLogQueryCapability()) << std::endl; + fmt::print(" PlayLogQueryCapability: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getPlayLogQueryCapabilityAsString(mNacp.getPlayLogQueryCapability())); } // Repair if (mNacp.getRepair().size() > 0) { - std::cout << " Repair:" << std::endl; + fmt::print(" Repair:\n"); for (auto itr = mNacp.getRepair().begin(); itr != mNacp.getRepair().end(); itr++) { - std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getRepairFlagAsString(*itr) << std::endl; + fmt::print(" {:s}\n", nn::hac::ApplicationControlPropertyUtil::getRepairFlagAsString(*itr)); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " Repair: None" << std::endl; + fmt::print(" Repair: None\n"); } // ProgramIndex if (mNacp.getProgramIndex() != 0 || mCliOutputMode.show_extended_info) { - std::cout << " ProgramIndex: 0x" << std::hex << std::setw(2) << std::setfill('0') << (uint32_t)mNacp.getProgramIndex() << std::endl; + fmt::print(" ProgramIndex: 0x{:02x}\n", mNacp.getProgramIndex()); } // RequiredNetworkServiceLicenseOnLaunch if (mNacp.getRequiredNetworkServiceLicenseOnLaunch().size() > 0) { - std::cout << " RequiredNetworkServiceLicenseOnLaunch:" << std::endl; + fmt::print(" RequiredNetworkServiceLicenseOnLaunch:\n"); for (auto itr = mNacp.getRequiredNetworkServiceLicenseOnLaunch().begin(); itr != mNacp.getRequiredNetworkServiceLicenseOnLaunch().end(); itr++) { - std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getRequiredNetworkServiceLicenseOnLaunchFlagAsString(*itr) << std::endl; + fmt::print(" {:s}\n", nn::hac::ApplicationControlPropertyUtil::getRequiredNetworkServiceLicenseOnLaunchFlagAsString(*itr)); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " RequiredNetworkServiceLicenseOnLaunch: None" << std::endl; + fmt::print(" RequiredNetworkServiceLicenseOnLaunch: None\n"); } // NeighborDetectionClientConfiguration auto detect_config = mNacp.getNeighborDetectionClientConfiguration(); if (detect_config.countSendGroupConfig() > 0 || detect_config.countReceivableGroupConfig() > 0) { - std::cout << " NeighborDetectionClientConfiguration:" << std::endl; + fmt::print(" NeighborDetectionClientConfiguration:\n"); if (detect_config.countSendGroupConfig() > 0) { - std::cout << " SendGroupConfig:" << std::endl; - std::cout << " GroupId: 0x" << std::hex << std::setw(16) << std::setfill('0') << detect_config.send_data_configuration.group_id << std::endl; - std::cout << " Key: " << fnd::SimpleTextOutput::arrayToString(detect_config.send_data_configuration.key, nn::hac::nacp::kNeighborDetectionGroupConfigurationKeyLength, false, "") << std::endl; + fmt::print(" SendGroupConfig:\n"); + fmt::print(" GroupId: 0x{:016x}\n", detect_config.send_data_configuration.group_id); + fmt::print(" Key: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(detect_config.send_data_configuration.key.data(), detect_config.send_data_configuration.key.size(), false, "")); } else if (mCliOutputMode.show_extended_info) { - std::cout << " SendGroupConfig: None" << std::endl; + fmt::print(" SendGroupConfig: None\n"); } if (detect_config.countReceivableGroupConfig() > 0) { - std::cout << " ReceivableGroupConfig:" << std::endl; + fmt::print(" ReceivableGroupConfig:\n"); for (size_t i = 0; i < nn::hac::nacp::kReceivableGroupConfigurationCount; i++) { if (detect_config.receivable_data_configuration[i].isNull()) continue; - std::cout << " GroupId: 0x" << std::hex << std::setw(16) << std::setfill('0') << detect_config.receivable_data_configuration[i].group_id << std::endl; - std::cout << " Key: " << fnd::SimpleTextOutput::arrayToString(detect_config.receivable_data_configuration[i].key, nn::hac::nacp::kNeighborDetectionGroupConfigurationKeyLength, false, "") << std::endl; + fmt::print(" GroupId: 0x{:016x}\n", detect_config.receivable_data_configuration[i].group_id); + fmt::print(" Key: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(detect_config.receivable_data_configuration[i].key.data(), detect_config.receivable_data_configuration[i].key.size(), false, "")); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " ReceivableGroupConfig: None" << std::endl; + fmt::print(" ReceivableGroupConfig: None\n"); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " NeighborDetectionClientConfiguration: None" << std::endl; + fmt::print(" NeighborDetectionClientConfiguration: None\n"); } // JitConfiguration if (mNacp.getJitConfiguration().is_enabled || mCliOutputMode.show_extended_info) { - std::cout << " JitConfiguration:" << std::endl; - std::cout << " IsEnabled: " << std::boolalpha << mNacp.getJitConfiguration().is_enabled << std::endl; - std::cout << " MemorySize: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getJitConfiguration().memory_size << std::endl; + fmt::print(" JitConfiguration:\n"); + fmt::print(" IsEnabled: {}\n", mNacp.getJitConfiguration().is_enabled); + fmt::print(" MemorySize: 0x{:016x}\n", mNacp.getJitConfiguration().memory_size); } // PlayReportPermission if (mNacp.getPlayReportPermission() != nn::hac::nacp::PlayReportPermission::None || mCliOutputMode.show_extended_info) { - std::cout << " PlayReportPermission: " << nn::hac::ApplicationControlPropertyUtil::getPlayReportPermissionAsString(mNacp.getPlayReportPermission()) << std::endl; + fmt::print(" PlayReportPermission: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getPlayReportPermissionAsString(mNacp.getPlayReportPermission())); } // CrashScreenshotForProd if (mNacp.getCrashScreenshotForProd() != nn::hac::nacp::CrashScreenshotForProd::Deny || mCliOutputMode.show_extended_info) { - std::cout << " CrashScreenshotForProd: " << nn::hac::ApplicationControlPropertyUtil::getCrashScreenshotForProdAsString(mNacp.getCrashScreenshotForProd()) << std::endl; + fmt::print(" CrashScreenshotForProd: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getCrashScreenshotForProdAsString(mNacp.getCrashScreenshotForProd())); } // CrashScreenshotForDev if (mNacp.getCrashScreenshotForDev() != nn::hac::nacp::CrashScreenshotForDev::Deny || mCliOutputMode.show_extended_info) { - std::cout << " CrashScreenshotForDev: " << nn::hac::ApplicationControlPropertyUtil::getCrashScreenshotForDevAsString(mNacp.getCrashScreenshotForDev()) << std::endl; + fmt::print(" CrashScreenshotForDev: {:s}\n", nn::hac::ApplicationControlPropertyUtil::getCrashScreenshotForDevAsString(mNacp.getCrashScreenshotForDev())); } // AccessibleLaunchRequiredVersion if (mNacp.getAccessibleLaunchRequiredVersionApplicationId().size() > 0) { - std::cout << " AccessibleLaunchRequiredVersion:" << std::endl; - std::cout << " ApplicationId:" << std::endl; + fmt::print(" AccessibleLaunchRequiredVersion:\n"); + fmt::print(" ApplicationId:\n"); for (auto itr = mNacp.getAccessibleLaunchRequiredVersionApplicationId().begin(); itr != mNacp.getAccessibleLaunchRequiredVersionApplicationId().end(); itr++) { - std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << *itr << std::endl; + fmt::print(" 0x{:016x}\n", *itr); } } else if (mCliOutputMode.show_extended_info) { - std::cout << " AccessibleLaunchRequiredVersion: None" << std::endl; + fmt::print(" AccessibleLaunchRequiredVersion: None\n"); } } \ No newline at end of file diff --git a/src/NacpProcess.h b/src/NacpProcess.h index e458124..6d88485 100644 --- a/src/NacpProcess.h +++ b/src/NacpProcess.h @@ -19,7 +19,7 @@ public: const nn::hac::ApplicationControlProperty& getApplicationControlProperty() const; private: - const std::string kModuleName = "NacpProcess"; + std::string mModuleName; std::shared_ptr mFile; CliOutputMode mCliOutputMode; From a4effb5b6a53be6b79cc1c7f7a483de7c59f43c4 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 1 Oct 2021 20:46:47 +0800 Subject: [PATCH 018/119] Misc --- src/CnmtProcess.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/CnmtProcess.cpp b/src/CnmtProcess.cpp index 785c453..0703e1d 100644 --- a/src/CnmtProcess.cpp +++ b/src/CnmtProcess.cpp @@ -171,9 +171,7 @@ void nstool::CnmtProcess::displayCnmt() } } } - } - - + } fmt::print(" Digest: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mCnmt.getDigest().data(), mCnmt.getDigest().size(), false, "")); } From 651c8e090dd2a0370163ce7528714f8c4dea8758 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 1 Oct 2021 20:48:44 +0800 Subject: [PATCH 019/119] Add connections for NacpProcess. --- src/AssetProcess.cpp | 8 ++++---- src/AssetProcess.h | 2 +- src/main.cpp | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index 06873d6..dfa9658 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -95,11 +95,11 @@ void nstool::AssetProcess::processSections() writeSubStreamToFile(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size, mNacpExtractPath.get()); } - //mNacp.setInputFile(std::make_shared(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size)); - //mNacp.setCliOutputMode(mCliOutputMode); - //mNacp.setVerifyMode(mVerify); + mNacp.setInputFile(std::make_shared(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size)); + mNacp.setCliOutputMode(mCliOutputMode); + mNacp.setVerifyMode(mVerify); - //mNacp.process(); + mNacp.process(); } if (mHdr.getRomfsInfo().size > 0) diff --git a/src/AssetProcess.h b/src/AssetProcess.h index a2ce24b..f81aa39 100644 --- a/src/AssetProcess.h +++ b/src/AssetProcess.h @@ -36,7 +36,7 @@ private: tc::Optional mNacpExtractPath; nn::hac::AssetHeader mHdr; - //NacpProcess mNacp; + NacpProcess mNacp; //RomfsProcess mRomfs; void importHeader(); diff --git a/src/main.cpp b/src/main.cpp index 4a86c61..15f0d70 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,7 +11,7 @@ #include "CnmtProcess.h" //#include "NsoProcess.h" //#include "NroProcess.h" -//#include "NacpProcess.h" +#include "NacpProcess.h" //#include "IniProcess.h" //#include "KipProcess.h" //#include "PkiCertProcess.h" @@ -162,6 +162,7 @@ int umain(const std::vector& args, const std::vector& obj.process(); } + */ else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NACP) { nstool::NacpProcess obj; @@ -172,6 +173,7 @@ int umain(const std::vector& args, const std::vector& obj.process(); } + /* else if (set.infile.filetype == nstool::Settings::FILE_TYPE_INI) { nstool::IniProcess obj; From d9b2b51ac1d16708ded44164f7dc7dc98b7d4645 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 1 Oct 2021 21:01:29 +0800 Subject: [PATCH 020/119] Move RoMetadataProcess back --- src/{not_ported => }/RoMetadataProcess.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{not_ported => }/RoMetadataProcess.cpp (100%) diff --git a/src/not_ported/RoMetadataProcess.cpp b/src/RoMetadataProcess.cpp similarity index 100% rename from src/not_ported/RoMetadataProcess.cpp rename to src/RoMetadataProcess.cpp From d92e32c5155da011733063937f88b6484e4d978a Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 1 Oct 2021 21:14:46 +0800 Subject: [PATCH 021/119] Port RoMetadatProcess to libtoolchain. --- src/RoMetadataProcess.cpp | 102 +++++++++++++++++++------------------- src/RoMetadataProcess.h | 20 ++++---- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/src/RoMetadataProcess.cpp b/src/RoMetadataProcess.cpp index 019c4ef..8066e5a 100644 --- a/src/RoMetadataProcess.cpp +++ b/src/RoMetadataProcess.cpp @@ -1,11 +1,11 @@ +#include "RoMetadataProcess.h" + #include #include #include -#include - -#include "RoMetadataProcess.h" nstool::RoMetadataProcess::RoMetadataProcess() : + mModuleName("nstool::RoMetadataProcess"), mCliOutputMode(true, false, false, false), mIs64BitInstruction(true), mListApi(false), @@ -72,32 +72,32 @@ void nstool::RoMetadataProcess::setListSymbols(bool listSymbols) mListSymbols = listSymbols; } -const std::vector& nstool::RoMetadataProcess::getSdkVerApiList() const +const std::vector& nstool::RoMetadataProcess::getSdkVerApiList() const { return mSdkVerApiList; } -const std::vector& nstool::RoMetadataProcess::getPublicApiList() const +const std::vector& nstool::RoMetadataProcess::getPublicApiList() const { return mPublicApiList; } -const std::vector& nstool::RoMetadataProcess::getDebugApiList() const +const std::vector& nstool::RoMetadataProcess::getDebugApiList() const { return mDebugApiList; } -const std::vector& nstool::RoMetadataProcess::getPrivateApiList() const +const std::vector& nstool::RoMetadataProcess::getPrivateApiList() const { return mPrivateApiList; } -const std::vector& nstool::RoMetadataProcess::getGuidelineApiList() const +const std::vector& nstool::RoMetadataProcess::getGuidelineApiList() const { return mGuidelineApiList; } -const std::vector& nstool::RoMetadataProcess::getSymbolList() const +const std::vector& nstool::RoMetadataProcess::getSymbolList() const { return mSymbolList.getSymbolList(); } @@ -106,7 +106,7 @@ void nstool::RoMetadataProcess::importApiList() { if (mRoBlob.size() == 0) { - throw tc::Exception(kModuleName, "No ro binary set."); + throw tc::Exception(mModuleName, "No ro binary set."); } if (mApiInfo.size > 0) @@ -153,79 +153,79 @@ void nstool::RoMetadataProcess::displayRoMetaData() if (api_num > 0 && (mListApi || mCliOutputMode.show_extended_info)) { - std::cout << "[SDK API List]" << std::endl; + fmt::print("[SDK API List]\n"); if (mSdkVerApiList.size() > 0) { - std::cout << " Sdk Revision: " << mSdkVerApiList[0].getModuleName() << std::endl; + fmt::print(" Sdk Revision: {:s}\n", mSdkVerApiList[0].getModuleName()); } if (mPublicApiList.size() > 0) { - std::cout << " Public APIs:" << std::endl; + fmt::print(" Public APIs:\n"); for (size_t i = 0; i < mPublicApiList.size(); i++) { - std::cout << " " << mPublicApiList[i].getModuleName() << " (vender: " << mPublicApiList[i].getVenderName() << ")" << std::endl; + fmt::print(" {:s} (vender: {:s})\n", mPublicApiList[i].getModuleName(), mPublicApiList[i].getVenderName()); } } if (mDebugApiList.size() > 0) { - std::cout << " Debug APIs:" << std::endl; + fmt::print(" Debug APIs:\n"); for (size_t i = 0; i < mDebugApiList.size(); i++) { - std::cout << " " << mDebugApiList[i].getModuleName() << " (vender: " << mDebugApiList[i].getVenderName() << ")" << std::endl; + fmt::print(" {:s} (vender: {:s})\n", mDebugApiList[i].getModuleName(), mDebugApiList[i].getVenderName()); } } if (mPrivateApiList.size() > 0) { - std::cout << " Private APIs:" << std::endl; + fmt::print(" Private APIs:\n"); for (size_t i = 0; i < mPrivateApiList.size(); i++) { - std::cout << " " << mPrivateApiList[i].getModuleName() << " (vender: " << mPrivateApiList[i].getVenderName() << ")" << std::endl; + fmt::print(" {:s} (vender: {:s})\n", mPrivateApiList[i].getModuleName(), mPrivateApiList[i].getVenderName()); } } if (mGuidelineApiList.size() > 0) { - std::cout << " Guideline APIs:" << std::endl; + fmt::print(" Guideline APIs:\n"); for (size_t i = 0; i < mGuidelineApiList.size(); i++) { - std::cout << " " << mGuidelineApiList[i].getModuleName() << " (vender: " << mGuidelineApiList[i].getVenderName() << ")" << std::endl; + fmt::print(" {:s} (vender: {:s})\n", mGuidelineApiList[i].getModuleName(), mGuidelineApiList[i].getVenderName()); } } } if (mSymbolList.getSymbolList().size() > 0 && (mListSymbols || mCliOutputMode.show_extended_info)) { - std::cout << "[Symbol List]" << std::endl; + fmt::print("[Symbol List]\n"); for (size_t i = 0; i < mSymbolList.getSymbolList().size(); i++) { const ElfSymbolParser::sElfSymbol& symbol = mSymbolList.getSymbolList()[i]; - std::cout << " " << symbol.name << " [SHN=" << getSectionIndexStr(symbol.shn_index) << " (" << std::hex << std::setw(4) << std::setfill('0') << symbol.shn_index << ")][STT=" << getSymbolTypeStr(symbol.symbol_type) << "][STB=" << getSymbolBindingStr(symbol.symbol_binding) << "]" << std::endl; + fmt::print(" {:s} [SHN={:s} ({:04x})][STT={:s}][STB={:s}]\n", symbol.name, getSectionIndexStr(symbol.shn_index), symbol.shn_index, getSymbolTypeStr(symbol.symbol_type), getSymbolBindingStr(symbol.symbol_binding)); } } } -const char* nstool::RoMetadataProcess::getSectionIndexStr(uint16_t shn_index) const +std::string nstool::RoMetadataProcess::getSectionIndexStr(uint16_t shn_index) const { - const char* str; + std::string str; switch (shn_index) { - case (fnd::elf::SHN_UNDEF): + case (elf::SHN_UNDEF): str = "UNDEF"; break; - case (fnd::elf::SHN_LOPROC): + case (elf::SHN_LOPROC): str = "LOPROC"; break; - case (fnd::elf::SHN_HIPROC): + case (elf::SHN_HIPROC): str = "HIPROC"; break; - case (fnd::elf::SHN_LOOS): + case (elf::SHN_LOOS): str = "LOOS"; break; - case (fnd::elf::SHN_HIOS): + case (elf::SHN_HIOS): str = "HIOS"; break; - case (fnd::elf::SHN_ABS): + case (elf::SHN_ABS): str = "ABS"; break; - case (fnd::elf::SHN_COMMON): + case (elf::SHN_COMMON): str = "COMMON"; break; default: @@ -235,36 +235,36 @@ const char* nstool::RoMetadataProcess::getSectionIndexStr(uint16_t shn_index) co return str; } -const char* nstool::RoMetadataProcess::getSymbolTypeStr(byte_t symbol_type) const +std::string nstool::RoMetadataProcess::getSymbolTypeStr(byte_t symbol_type) const { - const char* str; + std::string str; switch (symbol_type) { - case (fnd::elf::STT_NOTYPE): + case (elf::STT_NOTYPE): str = "NOTYPE"; break; - case (fnd::elf::STT_OBJECT): + case (elf::STT_OBJECT): str = "OBJECT"; break; - case (fnd::elf::STT_FUNC): + case (elf::STT_FUNC): str = "FUNC"; break; - case (fnd::elf::STT_SECTION): + case (elf::STT_SECTION): str = "SECTION"; break; - case (fnd::elf::STT_FILE): + case (elf::STT_FILE): str = "FILE"; break; - case (fnd::elf::STT_LOOS): + case (elf::STT_LOOS): str = "LOOS"; break; - case (fnd::elf::STT_HIOS): + case (elf::STT_HIOS): str = "HIOS"; break; - case (fnd::elf::STT_LOPROC): + case (elf::STT_LOPROC): str = "LOPROC"; break; - case (fnd::elf::STT_HIPROC): + case (elf::STT_HIPROC): str = "HIPROC"; break; default: @@ -274,30 +274,30 @@ const char* nstool::RoMetadataProcess::getSymbolTypeStr(byte_t symbol_type) cons return str; } -const char* nstool::RoMetadataProcess::getSymbolBindingStr(byte_t symbol_binding) const +std::string nstool::RoMetadataProcess::getSymbolBindingStr(byte_t symbol_binding) const { - const char* str; + std::string str; switch (symbol_binding) { - case (fnd::elf::STB_LOCAL): + case (elf::STB_LOCAL): str = "LOCAL"; break; - case (fnd::elf::STB_GLOBAL): + case (elf::STB_GLOBAL): str = "GLOBAL"; break; - case (fnd::elf::STB_WEAK): + case (elf::STB_WEAK): str = "WEAK"; break; - case (fnd::elf::STB_LOOS): + case (elf::STB_LOOS): str = "LOOS"; break; - case (fnd::elf::STB_HIOS): + case (elf::STB_HIOS): str = "HIOS"; break; - case (fnd::elf::STB_LOPROC): + case (elf::STB_LOPROC): str = "LOPROC"; break; - case (fnd::elf::STB_HIPROC): + case (elf::STB_HIPROC): str = "HIPROC"; break; default: diff --git a/src/RoMetadataProcess.h b/src/RoMetadataProcess.h index 4a13cc9..54bf085 100644 --- a/src/RoMetadataProcess.h +++ b/src/RoMetadataProcess.h @@ -25,14 +25,14 @@ public: void setListApi(bool listApi); void setListSymbols(bool listSymbols); - const std::vector& getSdkVerApiList() const; - const std::vector& getPublicApiList() const; - const std::vector& getDebugApiList() const; - const std::vector& getPrivateApiList() const; - const std::vector& getGuidelineApiList() const; - const std::vector& getSymbolList() const; + const std::vector& getSdkVerApiList() const; + const std::vector& getPublicApiList() const; + const std::vector& getDebugApiList() const; + const std::vector& getPrivateApiList() const; + const std::vector& getGuidelineApiList() const; + const std::vector& getSymbolList() const; private: - const std::string kModuleName = "RoMetadataProcess"; + std::string mModuleName; CliOutputMode mCliOutputMode; bool mIs64BitInstruction; @@ -61,9 +61,9 @@ private: void importApiList(); void displayRoMetaData(); - const char* getSectionIndexStr(uint16_t shn_index) const; - const char* getSymbolTypeStr(byte_t symbol_type) const; - const char* getSymbolBindingStr(byte_t symbol_binding) const; + std::string getSectionIndexStr(uint16_t shn_index) const; + std::string getSymbolTypeStr(byte_t symbol_type) const; + std::string getSymbolBindingStr(byte_t symbol_binding) const; }; } \ No newline at end of file From 06e8454c018a7e3cabe3fdd678f44b21685f7d37 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 11:14:50 +0800 Subject: [PATCH 022/119] Support dumping keys. --- src/Settings.cpp | 234 ++++++++++++++++++++++++++++++++++++++++++++++- src/Settings.h | 4 +- src/util.cpp | 18 ++++ src/util.h | 1 + 4 files changed, 252 insertions(+), 5 deletions(-) diff --git a/src/Settings.cpp b/src/Settings.cpp index 4e15cbb..a8ff451 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -1,11 +1,15 @@ +#include "Settings.h" +#include "types.h" +#include "version.h" +#include "util.h" + #include #include #include #include #include -#include "types.h" -#include "version.h" -#include "Settings.h" + + #include #include @@ -408,6 +412,12 @@ nstool::SettingsInitializer::SettingsInitializer(const std::vector& opt.keybag.fallback_enc_content_key = mTitleKey; opt.keybag.fallback_content_key = mBodyKey; + // dump keys if requires + if (opt.cli_output_mode.show_keydata) + { + dump_keys(); + } + // determine filetype if not manually specified if (infile.filetype == FILE_TYPE_ERROR) { @@ -614,7 +624,7 @@ void nstool::SettingsInitializer::determine_filetype() #undef _ASSERT_FILE_SIZE } -void nstool::SettingsInitializer::usage_text() +void nstool::SettingsInitializer::usage_text() const { fmt::print("{:s} v{:d}.{:d}.{:d} (C) {:s}\n", APP_NAME, VER_MAJOR, VER_MINOR, VER_PATCH, AUTHORS); fmt::print("Built: {:s} {:s}\n\n", __TIME__, __DATE__); @@ -666,6 +676,222 @@ void nstool::SettingsInitializer::usage_text() fmt::print(" --fsdir Extract RomFS partition to directory.\n"); } +void nstool::SettingsInitializer::dump_keys() const +{ + fmt::print("[KeyConfiguration]\n"); + fmt::print(" NCA Keys:\n"); + for (auto itr = opt.keybag.nca_header_sign0_key.begin(); itr != opt.keybag.nca_header_sign0_key.end(); itr++) + { + dump_rsa_key(itr->second, fmt::format("Header0-SignatureKey-{:02x}", itr->first), 4, opt.cli_output_mode.show_extended_info); + } + for (auto itr = opt.keybag.acid_sign_key.begin(); itr != opt.keybag.acid_sign_key.end(); itr++) + { + dump_rsa_key(itr->second, fmt::format("Acid-SignatureKey-{:02x}", itr->first), 4, opt.cli_output_mode.show_extended_info); + } + if (opt.keybag.nca_header_key.isSet()) + { + fmt::print(" Header-EncryptionKey:\n"); + fmt::print(" Key0: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(opt.keybag.nca_header_key.get()[0].data(), opt.keybag.nca_header_key.get()[0].size(), true, ":")); + fmt::print(" Key1: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(opt.keybag.nca_header_key.get()[1].data(), opt.keybag.nca_header_key.get()[1].size(), true, ":")); + } + std::vector kaek_label = {"Application", "Ocean", "System"}; + for (size_t kaek_index = 0; kaek_index < opt.keybag.nca_key_area_encryption_key.size(); kaek_index++) + { + for (auto itr = opt.keybag.nca_key_area_encryption_key[kaek_index].begin(); itr != opt.keybag.nca_key_area_encryption_key[kaek_index].end(); itr++) + { + fmt::print(" KeyAreaEncryptionKey-{:s}-{:02x}:\n {:s}\n", kaek_label[kaek_index], itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + } + for (size_t kaek_index = 0; kaek_index < opt.keybag.nca_key_area_encryption_key_hw.size(); kaek_index++) + { + for (auto itr = opt.keybag.nca_key_area_encryption_key_hw[kaek_index].begin(); itr != opt.keybag.nca_key_area_encryption_key_hw[kaek_index].end(); itr++) + { + fmt::print(" KeyAreaEncryptionKeyHw-{:s}-{:02x}:\n {:s}\n", kaek_label[kaek_index], itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + } + fmt::print(" NRR Keys:\n"); + for (auto itr = opt.keybag.nrr_certificate_sign_key.begin(); itr != opt.keybag.nrr_certificate_sign_key.end(); itr++) + { + dump_rsa_key(itr->second, fmt::format("Certificate-SignatureKey-{:02x}", itr->first), 4, opt.cli_output_mode.show_extended_info); + } + fmt::print(" XCI Keys:\n"); + if (opt.keybag.xci_header_sign_key.isSet()) + { + dump_rsa_key(opt.keybag.xci_header_sign_key.get(), fmt::format("Header-SignatureKey"), 4, opt.cli_output_mode.show_extended_info); + } + for (auto itr = opt.keybag.xci_header_key.begin(); itr != opt.keybag.xci_header_key.end(); itr++) + { + fmt::print(" ExtendedHeader-EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + + fmt::print(" Package1 Keys:\n"); + for (auto itr = opt.keybag.pkg1_key.begin(); itr != opt.keybag.pkg1_key.end(); itr++) + { + fmt::print(" EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + + fmt::print(" Package2 Keys:\n"); + if (opt.keybag.pkg2_sign_key.isSet()) + { + dump_rsa_key(opt.keybag.pkg2_sign_key.get(), fmt::format("Header-SignatureKey"), 4, opt.cli_output_mode.show_extended_info); + } + for (auto itr = opt.keybag.pkg2_key.begin(); itr != opt.keybag.pkg2_key.end(); itr++) + { + fmt::print(" EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + + fmt::print(" ETicket Keys:\n"); + for (auto itr = opt.keybag.etik_common_key.begin(); itr != opt.keybag.etik_common_key.end(); itr++) + { + fmt::print(" CommonKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + + fmt::print(" BroadOn Signer Profiles:\n"); + for (auto itr = opt.keybag.broadon_signer.begin(); itr != opt.keybag.broadon_signer.end(); itr++) + { + fmt::print(" {:s}:\n", itr->first); + fmt::print(" SignType: "); + switch(itr->second.key_type) { + case nn::pki::sign::SIGN_ALGO_RSA2048: + fmt::print("RSA-2048\n"); + break; + case nn::pki::sign::SIGN_ALGO_RSA4096: + fmt::print("RSA-4096\n"); + break; + case nn::pki::sign::SIGN_ALGO_ECDSA240: + fmt::print("ECDSA-240\n"); + break; + default: + fmt::print("Unknown\n"); + } + switch(itr->second.key_type) { + case nn::pki::sign::SIGN_ALGO_RSA2048: + case nn::pki::sign::SIGN_ALGO_RSA4096: + dump_rsa_key(itr->second.rsa_key, "RsaKey", 6, opt.cli_output_mode.show_extended_info); + break; + case nn::pki::sign::SIGN_ALGO_ECDSA240: + default: + break; + } + } + + /* + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getContentArchiveHeader0SignKey(rsa2048_key, byte_t(i)) == true) + dumpRsa2048Key(rsa2048_key, "Header0-SignatureKey-" + kKeyIndex[i], 2); + } + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getAcidSignKey(rsa2048_key, byte_t(i)) == true) + dumpRsa2048Key(rsa2048_key, "Acid-SignatureKey-" + kKeyIndex[i], 2); + } + + if (mKeyCfg.getContentArchiveHeaderKey(aesxts_key) == true) + dumpAesXtsKey(aesxts_key, "Header-EncryptionKey", 2); + + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 0, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKey-Application-" + kKeyIndex[i], 2); + if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 1, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKey-Ocean-" + kKeyIndex[i], 2); + if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 2, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKey-System-" + kKeyIndex[i], 2); + } + + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 0, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-Application-" + kKeyIndex[i], 2); + if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 1, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-Ocean-" + kKeyIndex[i], 2); + if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 2, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-System-" + kKeyIndex[i], 2); + } + + std::cout << " NRR Keys:" << std::endl; + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getNrrCertificateSignKey(rsa2048_key, byte_t(i)) == true) + dumpRsa2048Key(rsa2048_key, "Certificate-SignatureKey-" + kKeyIndex[i], 2); + } + + std::cout << " XCI Keys:" << std::endl; + if (mKeyCfg.getXciHeaderSignKey(rsa2048_key) == true) + dumpRsa2048Key(rsa2048_key, "Header-SignatureKey", 2); + if (mKeyCfg.getXciHeaderKey(aes_key) == true) + dumpAesKey(aes_key, "ExtendedHeader-EncryptionKey", 2); + + + + + std::cout << " Package1 Keys:" << std::endl; + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getPkg1Key(byte_t(i), aes_key) == true) + dumpAesKey(aes_key, "EncryptionKey-" + kKeyIndex[i], 2); + } + + std::cout << " Package2 Keys:" << std::endl; + if (mKeyCfg.getPkg2SignKey(rsa2048_key) == true) + dumpRsa2048Key(rsa2048_key, "Signature Key", 2); + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getPkg2Key(byte_t(i), aes_key) == true) + dumpAesKey(aes_key, "EncryptionKey-" + kKeyIndex[i], 2); + } + + std::cout << " ETicket Keys:" << std::endl; + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getETicketCommonKey(byte_t(i), aes_key) == true) + dumpAesKey(aes_key, "CommonKey-" + kKeyIndex[i], 2); + } + + if (mKeyCfg.getPkiRootSignKey("Root", rsa4096_key) == true) + dumpRsa4096Key(rsa4096_key, "NNPKI Root Key", 1); + */ +} + +void nstool::SettingsInitializer::dump_rsa_key(const KeyBag::rsa_key_t& key, const std::string& label, size_t indent, bool expanded_key_data) const +{ + std::string indent_str; + + indent_str.clear(); + for (size_t i = 0; i < indent; i++) + { + indent_str += " "; + } + + fmt::print("{:s}{:s}:\n", indent_str, label); + if (key.n.size() > 0) + { + if (expanded_key_data) + { + fmt::print("{:s} Modulus:\n", indent_str); + fmt::print("{:s} {:s}", indent_str, tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(key.n.data(), key.n.size(), true, ":", 0x10, indent + 4, false)); + } + else + { + fmt::print("{:s} Modulus: {:s}\n", indent_str, getTruncatedBytesString(key.n.data(), key.n.size())); + } + } + if (key.d.size() > 0) + { + if (expanded_key_data) + { + fmt::print("{:s} Private Exponent:\n", indent_str); + fmt::print("{:s} {:s}", indent_str, tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(key.d.data(), key.d.size(), true, ":", 0x10, indent + 4, false)); + } + else + { + fmt::print("{:s} Private Exponent: {:s}\n", indent_str, getTruncatedBytesString(key.d.data(), key.d.size())); + } + } +} + + bool nstool::SettingsInitializer::determineValidNcaFromSample(const tc::ByteData& sample) const { if (sample.size() < nn::hac::nca::kHeaderSize) diff --git a/src/Settings.h b/src/Settings.h index 6ba3f5c..c0a1a27 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -132,7 +132,9 @@ public: private: void parse_args(const std::vector& args); void determine_filetype(); - void usage_text(); + void usage_text() const; + void dump_keys() const; + void dump_rsa_key(const KeyBag::rsa_key_t& key, const std::string& label, size_t indent, bool expanded_key_data) const; std::string mModuleLabel; diff --git a/src/util.cpp b/src/util.cpp index b8fb0cc..435fc2d 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -99,6 +99,24 @@ void nstool::writeStreamToStream(const std::shared_ptr& in_stre } } +std::string nstool::getTruncatedBytesString(const byte_t* data, size_t len) +{ + if (data == nullptr) { return fmt::format(""); } + + std::string str = ""; + + if (len <= 8) + { + str = tc::cli::FormatUtil::formatBytesAsString(data, len, true, ""); + } + else + { + str = fmt::format("{:02X}{:02X}{:02X}{:02X}...{:02X}{:02X}{:02X}{:02X}", data[0], data[1], data[2], data[3], data[len-4], data[len-3], data[len-2], data[len-1]); + } + + return str; +} + std::string nstool::getTruncatedBytesString(const byte_t* data, size_t len, bool do_not_truncate) { if (data == nullptr) { return fmt::format(""); } diff --git a/src/util.h b/src/util.h index 2611acc..aa1d962 100644 --- a/src/util.h +++ b/src/util.h @@ -10,6 +10,7 @@ void writeSubStreamToFile(const std::shared_ptr& in_stream, int void writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size = 0x10000); void writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size = 0x10000); +std::string getTruncatedBytesString(const byte_t* data, size_t len); std::string getTruncatedBytesString(const byte_t* data, size_t len, bool do_not_truncate); } \ No newline at end of file From f66ed2de0e083347c2370674c5a8b421555d7bc3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 12:08:34 +0800 Subject: [PATCH 023/119] Automatically load rsa public keys if not user supplied. --- src/KeyBag.cpp | 332 ++++++++++++++++++++++++++++++++++++++++++++++++- src/KeyBag.h | 3 + 2 files changed, 334 insertions(+), 1 deletion(-) diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 49aff54..6f2e5d5 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -3,6 +3,7 @@ #include "util.h" #include +#include #include #include @@ -25,6 +26,9 @@ nstool::KeyBagInitializer::KeyBagInitializer(bool isDev, const tc::Optional kProdNcaHeaderSign0Modulus = + { + { + 0x00, + {0xBF, 0xBE, 0x40, 0x6C, 0xF4, 0xA7, 0x80, 0xE9, 0xF0, 0x7D, 0x0C, 0x99, 0x61, 0x1D, 0x77, 0x2F, + 0x96, 0xBC, 0x4B, 0x9E, 0x58, 0x38, 0x1B, 0x03, 0xAB, 0xB1, 0x75, 0x49, 0x9F, 0x2B, 0x4D, 0x58, + 0x34, 0xB0, 0x05, 0xA3, 0x75, 0x22, 0xBE, 0x1A, 0x3F, 0x03, 0x73, 0xAC, 0x70, 0x68, 0xD1, 0x16, + 0xB9, 0x04, 0x46, 0x5E, 0xB7, 0x07, 0x91, 0x2F, 0x07, 0x8B, 0x26, 0xDE, 0xF6, 0x00, 0x07, 0xB2, + 0xB4, 0x51, 0xF8, 0x0D, 0x0A, 0x5E, 0x58, 0xAD, 0xEB, 0xBC, 0x9A, 0xD6, 0x49, 0xB9, 0x64, 0xEF, + 0xA7, 0x82, 0xB5, 0xCF, 0x6D, 0x70, 0x13, 0xB0, 0x0F, 0x85, 0xF6, 0xA9, 0x08, 0xAA, 0x4D, 0x67, + 0x66, 0x87, 0xFA, 0x89, 0xFF, 0x75, 0x90, 0x18, 0x1E, 0x6B, 0x3D, 0xE9, 0x8A, 0x68, 0xC9, 0x26, + 0x04, 0xD9, 0x80, 0xCE, 0x3F, 0x5E, 0x92, 0xCE, 0x01, 0xFF, 0x06, 0x3B, 0xF2, 0xC1, 0xA9, 0x0C, + 0xCE, 0x02, 0x6F, 0x16, 0xBC, 0x92, 0x42, 0x0A, 0x41, 0x64, 0xCD, 0x52, 0xB6, 0x34, 0x4D, 0xAE, + 0xC0, 0x2E, 0xDE, 0xA4, 0xDF, 0x27, 0x68, 0x3C, 0xC1, 0xA0, 0x60, 0xAD, 0x43, 0xF3, 0xFC, 0x86, + 0xC1, 0x3E, 0x6C, 0x46, 0xF7, 0x7C, 0x29, 0x9F, 0xFA, 0xFD, 0xF0, 0xE3, 0xCE, 0x64, 0xE7, 0x35, + 0xF2, 0xF6, 0x56, 0x56, 0x6F, 0x6D, 0xF1, 0xE2, 0x42, 0xB0, 0x83, 0x40, 0xA5, 0xC3, 0x20, 0x2B, + 0xCC, 0x9A, 0xAE, 0xCA, 0xED, 0x4D, 0x70, 0x30, 0xA8, 0x70, 0x1C, 0x70, 0xFD, 0x13, 0x63, 0x29, + 0x02, 0x79, 0xEA, 0xD2, 0xA7, 0xAF, 0x35, 0x28, 0x32, 0x1C, 0x7B, 0xE6, 0x2F, 0x1A, 0xAA, 0x40, + 0x7E, 0x32, 0x8C, 0x27, 0x42, 0xFE, 0x82, 0x78, 0xEC, 0x0D, 0xEB, 0xE6, 0x83, 0x4B, 0x6D, 0x81, + 0x04, 0x40, 0x1A, 0x9E, 0x9A, 0x67, 0xF6, 0x72, 0x29, 0xFA, 0x04, 0xF0, 0x9D, 0xE4, 0xF4, 0x03,} + }, + { + 0x01, + {0xAD, 0xE3, 0xE1, 0xFA, 0x04, 0x35, 0xE5, 0xB6, 0xDD, 0x49, 0xEA, 0x89, 0x29, 0xB1, 0xFF, 0xB6, + 0x43, 0xDF, 0xCA, 0x96, 0xA0, 0x4A, 0x13, 0xDF, 0x43, 0xD9, 0x94, 0x97, 0x96, 0x43, 0x65, 0x48, + 0x70, 0x58, 0x33, 0xA2, 0x7D, 0x35, 0x7B, 0x96, 0x74, 0x5E, 0x0B, 0x5C, 0x32, 0x18, 0x14, 0x24, + 0xC2, 0x58, 0xB3, 0x6C, 0x22, 0x7A, 0xA1, 0xB7, 0xCB, 0x90, 0xA7, 0xA3, 0xF9, 0x7D, 0x45, 0x16, + 0xA5, 0xC8, 0xED, 0x8F, 0xAD, 0x39, 0x5E, 0x9E, 0x4B, 0x51, 0x68, 0x7D, 0xF8, 0x0C, 0x35, 0xC6, + 0x3F, 0x91, 0xAE, 0x44, 0xA5, 0x92, 0x30, 0x0D, 0x46, 0xF8, 0x40, 0xFF, 0xD0, 0xFF, 0x06, 0xD2, + 0x1C, 0x7F, 0x96, 0x18, 0xDC, 0xB7, 0x1D, 0x66, 0x3E, 0xD1, 0x73, 0xBC, 0x15, 0x8A, 0x2F, 0x94, + 0xF3, 0x00, 0xC1, 0x83, 0xF1, 0xCD, 0xD7, 0x81, 0x88, 0xAB, 0xDF, 0x8C, 0xEF, 0x97, 0xDD, 0x1B, + 0x17, 0x5F, 0x58, 0xF6, 0x9A, 0xE9, 0xE8, 0xC2, 0x2F, 0x38, 0x15, 0xF5, 0x21, 0x07, 0xF8, 0x37, + 0x90, 0x5D, 0x2E, 0x02, 0x40, 0x24, 0x15, 0x0D, 0x25, 0xB7, 0x26, 0x5D, 0x09, 0xCC, 0x4C, 0xF4, + 0xF2, 0x1B, 0x94, 0x70, 0x5A, 0x9E, 0xEE, 0xED, 0x77, 0x77, 0xD4, 0x51, 0x99, 0xF5, 0xDC, 0x76, + 0x1E, 0xE3, 0x6C, 0x8C, 0xD1, 0x12, 0xD4, 0x57, 0xD1, 0xB6, 0x83, 0xE4, 0xE4, 0xFE, 0xDA, 0xE9, + 0xB4, 0x3B, 0x33, 0xE5, 0x37, 0x8A, 0xDF, 0xB5, 0x7F, 0x89, 0xF1, 0x9B, 0x9E, 0xB0, 0x15, 0xB2, + 0x3A, 0xFE, 0xEA, 0x61, 0x84, 0x5B, 0x7D, 0x4B, 0x23, 0x12, 0x0B, 0x83, 0x12, 0xF2, 0x22, 0x6B, + 0xB9, 0x22, 0x96, 0x4B, 0x26, 0x0B, 0x63, 0x5E, 0x96, 0x57, 0x52, 0xA3, 0x67, 0x64, 0x22, 0xCA, + 0xD0, 0x56, 0x3E, 0x74, 0xB5, 0x98, 0x1F, 0x0D, 0xF8, 0xB3, 0x34, 0xE6, 0x98, 0x68, 0x5A, 0xAD,} + }, + }; + + static const std::vector kProdAcidSignModulus = + { + { + 0x00, + {0xDD, 0xC8, 0xDD, 0xF2, 0x4E, 0x6D, 0xF0, 0xCA, 0x9E, 0xC7, 0x5D, 0xC7, 0x7B, 0xAD, 0xFE, 0x7D, + 0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50, + 0x2E, 0xFC, 0x9D, 0x94, 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57, + 0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, 0x1B, 0xFE, 0x30, 0x20, + 0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21, + 0xE7, 0x8B, 0xE9, 0x75, 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2, + 0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, 0x5E, 0x01, 0x30, 0xA4, + 0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE, + 0x1C, 0x82, 0x78, 0xB5, 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10, + 0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, 0x73, 0xB0, 0x31, 0x53, + 0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD, + 0x40, 0x9A, 0xA2, 0x8B, 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08, + 0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, 0x69, 0x2B, 0x31, 0x6A, + 0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA, + 0x37, 0xEA, 0xE8, 0x1E, 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B, + 0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, 0x7D, 0x35, 0x22, 0xFD,} + }, + { + 0x01, + {0xE7, 0xAA, 0x25, 0xC8, 0x01, 0xA5, 0x14, 0x6B, 0x01, 0x60, 0x3E, 0xD9, 0x96, 0x5A, 0xBF, 0x90, + 0xAC, 0xA7, 0xFD, 0x9B, 0x5B, 0xBD, 0x8A, 0x26, 0xB0, 0xCB, 0x20, 0x28, 0x9A, 0x72, 0x12, 0xF5, + 0x20, 0x65, 0xB3, 0xB9, 0x84, 0x58, 0x1F, 0x27, 0xBC, 0x7C, 0xA2, 0xC9, 0x9E, 0x18, 0x95, 0xCF, + 0xC2, 0x73, 0x2E, 0x74, 0x8C, 0x66, 0xE5, 0x9E, 0x79, 0x2B, 0xB8, 0x07, 0x0C, 0xB0, 0x4E, 0x8E, + 0xAB, 0x85, 0x21, 0x42, 0xC4, 0xC5, 0x6D, 0x88, 0x9C, 0xDB, 0x15, 0x95, 0x3F, 0x80, 0xDB, 0x7A, + 0x9A, 0x7D, 0x41, 0x56, 0x25, 0x17, 0x18, 0x42, 0x4D, 0x8C, 0xAC, 0xA5, 0x7B, 0xDB, 0x42, 0x5D, + 0x59, 0x35, 0x45, 0x5D, 0x8A, 0x02, 0xB5, 0x70, 0xC0, 0x72, 0x35, 0x46, 0xD0, 0x1D, 0x60, 0x01, + 0x4A, 0xCC, 0x1C, 0x46, 0xD3, 0xD6, 0x35, 0x52, 0xD6, 0xE1, 0xF8, 0x3B, 0x5D, 0xEA, 0xDD, 0xB8, + 0xFE, 0x7D, 0x50, 0xCB, 0x35, 0x23, 0x67, 0x8B, 0xB6, 0xE4, 0x74, 0xD2, 0x60, 0xFC, 0xFD, 0x43, + 0xBF, 0x91, 0x08, 0x81, 0xC5, 0x4F, 0x5D, 0x16, 0x9A, 0xC4, 0x9A, 0xC6, 0xF6, 0xF3, 0xE1, 0xF6, + 0x5C, 0x07, 0xAA, 0x71, 0x6C, 0x13, 0xA4, 0xB1, 0xB3, 0x66, 0xBF, 0x90, 0x4C, 0x3D, 0xA2, 0xC4, + 0x0B, 0xB8, 0x3D, 0x7A, 0x8C, 0x19, 0xFA, 0xFF, 0x6B, 0xB9, 0x1F, 0x02, 0xCC, 0xB6, 0xD3, 0x0C, + 0x7D, 0x19, 0x1F, 0x47, 0xF9, 0xC7, 0x40, 0x01, 0xFA, 0x46, 0xEA, 0x0B, 0xD4, 0x02, 0xE0, 0x3D, + 0x30, 0x9A, 0x1A, 0x0F, 0xEA, 0xA7, 0x66, 0x55, 0xF7, 0xCB, 0x28, 0xE2, 0xBB, 0x99, 0xE4, 0x83, + 0xC3, 0x43, 0x03, 0xEE, 0xDC, 0x1F, 0x02, 0x23, 0xDD, 0xD1, 0x2D, 0x39, 0xA4, 0x65, 0x75, 0x03, + 0xEF, 0x37, 0x9C, 0x06, 0xD6, 0xFA, 0xA1, 0x15, 0xF0, 0xDB, 0x17, 0x47, 0x26, 0x4F, 0x49, 0x03} + }, + }; + + static const nn::hac::detail::rsa2048_block_t kDevPackage2HeaderModulus = { + 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99, + 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, + 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, + 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38, + 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83, + 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0, + 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6, + 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50, + 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7, + 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42, + 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E, + 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8, + 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7, + 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4, + 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46, + 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B + }; + + static const std::vector kDevNcaHeaderSign0Modulus = + { + { + 0x00, + {0xD8, 0xF1, 0x18, 0xEF, 0x32, 0x72, 0x4C, 0xA7, 0x47, 0x4C, 0xB9, 0xEA, 0xB3, 0x04, 0xA8, 0xA4, + 0xAC, 0x99, 0x08, 0x08, 0x04, 0xBF, 0x68, 0x57, 0xB8, 0x43, 0x94, 0x2B, 0xC7, 0xB9, 0x66, 0x49, + 0x85, 0xE5, 0x8A, 0x9B, 0xC1, 0x00, 0x9A, 0x6A, 0x8D, 0xD0, 0xEF, 0xCE, 0xFF, 0x86, 0xC8, 0x5C, + 0x5D, 0xE9, 0x53, 0x7B, 0x19, 0x2A, 0xA8, 0xC0, 0x22, 0xD1, 0xF3, 0x22, 0x0A, 0x50, 0xF2, 0x2B, + 0x65, 0x05, 0x1B, 0x9E, 0xEC, 0x61, 0xB5, 0x63, 0xA3, 0x6F, 0x3B, 0xBA, 0x63, 0x3A, 0x53, 0xF4, + 0x49, 0x2F, 0xCF, 0x03, 0xCC, 0xD7, 0x50, 0x82, 0x1B, 0x29, 0x4F, 0x08, 0xDE, 0x1B, 0x6D, 0x47, + 0x4F, 0xA8, 0xB6, 0x6A, 0x26, 0xA0, 0x83, 0x3F, 0x1A, 0xAF, 0x83, 0x8F, 0x0E, 0x17, 0x3F, 0xFE, + 0x44, 0x1C, 0x56, 0x94, 0x2E, 0x49, 0x83, 0x83, 0x03, 0xE9, 0xB6, 0xAD, 0xD5, 0xDE, 0xE3, 0x2D, + 0xA1, 0xD9, 0x66, 0x20, 0x5D, 0x1F, 0x5E, 0x96, 0x5D, 0x5B, 0x55, 0x0D, 0xD4, 0xB4, 0x77, 0x6E, + 0xAE, 0x1B, 0x69, 0xF3, 0xA6, 0x61, 0x0E, 0x51, 0x62, 0x39, 0x28, 0x63, 0x75, 0x76, 0xBF, 0xB0, + 0xD2, 0x22, 0xEF, 0x98, 0x25, 0x02, 0x05, 0xC0, 0xD7, 0x6A, 0x06, 0x2C, 0xA5, 0xD8, 0x5A, 0x9D, + 0x7A, 0xA4, 0x21, 0x55, 0x9F, 0xF9, 0x3E, 0xBF, 0x16, 0xF6, 0x07, 0xC2, 0xB9, 0x6E, 0x87, 0x9E, + 0xB5, 0x1C, 0xBE, 0x97, 0xFA, 0x82, 0x7E, 0xED, 0x30, 0xD4, 0x66, 0x3F, 0xDE, 0xD8, 0x1B, 0x4B, + 0x15, 0xD9, 0xFB, 0x2F, 0x50, 0xF0, 0x9D, 0x1D, 0x52, 0x4C, 0x1C, 0x4D, 0x8D, 0xAE, 0x85, 0x1E, + 0xEA, 0x7F, 0x86, 0xF3, 0x0B, 0x7B, 0x87, 0x81, 0x98, 0x23, 0x80, 0x63, 0x4F, 0x2F, 0xB0, 0x62, + 0xCC, 0x6E, 0xD2, 0x46, 0x13, 0x65, 0x2B, 0xD6, 0x44, 0x33, 0x59, 0xB5, 0x8F, 0xB9, 0x4A, 0xA9,} + }, + { + 0x01, + {0x9A, 0xBC, 0x88, 0xBD, 0x0A, 0xBE, 0xD7, 0x0C, 0x9B, 0x42, 0x75, 0x65, 0x38, 0x5E, 0xD1, 0x01, + 0xCD, 0x12, 0xAE, 0xEA, 0xE9, 0x4B, 0xDB, 0xB4, 0x5E, 0x36, 0x10, 0x96, 0xDA, 0x3D, 0x2E, 0x66, + 0xD3, 0x99, 0x13, 0x8A, 0xBE, 0x67, 0x41, 0xC8, 0x93, 0xD9, 0x3E, 0x42, 0xCE, 0x34, 0xCE, 0x96, + 0xFA, 0x0B, 0x23, 0xCC, 0x2C, 0xDF, 0x07, 0x3F, 0x3B, 0x24, 0x4B, 0x12, 0x67, 0x3A, 0x29, 0x36, + 0xA3, 0xAA, 0x06, 0xF0, 0x65, 0xA5, 0x85, 0xBA, 0xFD, 0x12, 0xEC, 0xF1, 0x60, 0x67, 0xF0, 0x8F, + 0xD3, 0x5B, 0x01, 0x1B, 0x1E, 0x84, 0xA3, 0x5C, 0x65, 0x36, 0xF9, 0x23, 0x7E, 0xF3, 0x26, 0x38, + 0x64, 0x98, 0xBA, 0xE4, 0x19, 0x91, 0x4C, 0x02, 0xCF, 0xC9, 0x6D, 0x86, 0xEC, 0x1D, 0x41, 0x69, + 0xDD, 0x56, 0xEA, 0x5C, 0xA3, 0x2A, 0x58, 0xB4, 0x39, 0xCC, 0x40, 0x31, 0xFD, 0xFB, 0x42, 0x74, + 0xF8, 0xEC, 0xEA, 0x00, 0xF0, 0xD9, 0x28, 0xEA, 0xFA, 0x2D, 0x00, 0xE1, 0x43, 0x53, 0xC6, 0x32, + 0xF4, 0xA2, 0x07, 0xD4, 0x5F, 0xD4, 0xCB, 0xAC, 0xCA, 0xFF, 0xDF, 0x84, 0xD2, 0x86, 0x14, 0x3C, + 0xDE, 0x22, 0x75, 0xA5, 0x73, 0xFF, 0x68, 0x07, 0x4A, 0xF9, 0x7C, 0x2C, 0xCC, 0xDE, 0x45, 0xB6, + 0x54, 0x82, 0x90, 0x36, 0x1F, 0x2C, 0x51, 0x96, 0xC5, 0x0A, 0x53, 0x5B, 0xF0, 0x8B, 0x4A, 0xAA, + 0x3B, 0x68, 0x97, 0x19, 0x17, 0x1F, 0x01, 0xB8, 0xED, 0xB9, 0x9A, 0x5E, 0x08, 0xC5, 0x20, 0x1E, + 0x6A, 0x09, 0xF0, 0xE9, 0x73, 0xA3, 0xBE, 0x10, 0x06, 0x02, 0xE9, 0xFB, 0x85, 0xFA, 0x5F, 0x01, + 0xAC, 0x60, 0xE0, 0xED, 0x7D, 0xB9, 0x49, 0xA8, 0x9E, 0x98, 0x7D, 0x91, 0x40, 0x05, 0xCF, 0xF9, + 0x1A, 0xFC, 0x40, 0x22, 0xA8, 0x96, 0x5B, 0xB0, 0xDC, 0x7A, 0xF5, 0xB7, 0xE9, 0x91, 0x4C, 0x49,} + }, + }; + + static const std::vector kDevAcidSignModulus = + { + { + 0x00, + {0xD6, 0x34, 0xA5, 0x78, 0x6C, 0x68, 0xCE, 0x5A, 0xC2, 0x37, 0x17, 0xF3, 0x82, 0x45, 0xC6, 0x89, + 0xE1, 0x2D, 0x06, 0x67, 0xBF, 0xB4, 0x06, 0x19, 0x55, 0x6B, 0x27, 0x66, 0x0C, 0xA4, 0xB5, 0x87, + 0x81, 0x25, 0xF4, 0x30, 0xBC, 0x53, 0x08, 0x68, 0xA2, 0x48, 0x49, 0x8C, 0x3F, 0x38, 0x40, 0x9C, + 0xC4, 0x26, 0xF4, 0x79, 0xE2, 0xA1, 0x85, 0xF5, 0x5C, 0x7F, 0x58, 0xBA, 0xA6, 0x1C, 0xA0, 0x8B, + 0x84, 0x16, 0x14, 0x6F, 0x85, 0xD9, 0x7C, 0xE1, 0x3C, 0x67, 0x22, 0x1E, 0xFB, 0xD8, 0xA7, 0xA5, + 0x9A, 0xBF, 0xEC, 0x0E, 0xCF, 0x96, 0x7E, 0x85, 0xC2, 0x1D, 0x49, 0x5D, 0x54, 0x26, 0xCB, 0x32, + 0x7C, 0xF6, 0xBB, 0x58, 0x03, 0x80, 0x2B, 0x5D, 0xF7, 0xFB, 0xD1, 0x9D, 0xC7, 0xC6, 0x2E, 0x53, + 0xC0, 0x6F, 0x39, 0x2C, 0x1F, 0xA9, 0x92, 0xF2, 0x4D, 0x7D, 0x4E, 0x74, 0xFF, 0xE4, 0xEF, 0xE4, + 0x7C, 0x3D, 0x34, 0x2A, 0x71, 0xA4, 0x97, 0x59, 0xFF, 0x4F, 0xA2, 0xF4, 0x66, 0x78, 0xD8, 0xBA, + 0x99, 0xE3, 0xE6, 0xDB, 0x54, 0xB9, 0xE9, 0x54, 0xA1, 0x70, 0xFC, 0x05, 0x1F, 0x11, 0x67, 0x4B, + 0x26, 0x8C, 0x0C, 0x3E, 0x03, 0xD2, 0xA3, 0x55, 0x5C, 0x7D, 0xC0, 0x5D, 0x9D, 0xFF, 0x13, 0x2F, + 0xFD, 0x19, 0xBF, 0xED, 0x44, 0xC3, 0x8C, 0xA7, 0x28, 0xCB, 0xE5, 0xE0, 0xB1, 0xA7, 0x9C, 0x33, + 0x8D, 0xB8, 0x6E, 0xDE, 0x87, 0x18, 0x22, 0x60, 0xC4, 0xAE, 0xF2, 0x87, 0x9F, 0xCE, 0x09, 0x5C, + 0xB5, 0x99, 0xA5, 0x9F, 0x49, 0xF2, 0xD7, 0x58, 0xFA, 0xF9, 0xC0, 0x25, 0x7D, 0xD6, 0xCB, 0xF3, + 0xD8, 0x6C, 0xA2, 0x69, 0x91, 0x68, 0x73, 0xB1, 0x94, 0x6F, 0xA3, 0xF3, 0xB9, 0x7D, 0xF8, 0xE0, + 0x72, 0x9E, 0x93, 0x7B, 0x7A, 0xA2, 0x57, 0x60, 0xB7, 0x5B, 0xA9, 0x84, 0xAE, 0x64, 0x88, 0x69} + }, + { + 0x01, + {0xBC, 0xA5, 0x6A, 0x7E, 0xEA, 0x38, 0x34, 0x62, 0xA6, 0x10, 0x18, 0x3C, 0xE1, 0x63, 0x7B, 0xF0, + 0xD3, 0x08, 0x8C, 0xF5, 0xC5, 0xC4, 0xC7, 0x93, 0xE9, 0xD9, 0xE6, 0x32, 0xF3, 0xA0, 0xF6, 0x6E, + 0x8A, 0x98, 0x76, 0x47, 0x33, 0x47, 0x65, 0x02, 0x70, 0xDC, 0x86, 0x5F, 0x3D, 0x61, 0x5A, 0x70, + 0xBC, 0x5A, 0xCA, 0xCA, 0x50, 0xAD, 0x61, 0x7E, 0xC9, 0xEC, 0x27, 0xFF, 0xE8, 0x64, 0x42, 0x9A, + 0xEE, 0xBE, 0xC3, 0xD1, 0x0B, 0xC0, 0xE9, 0xBF, 0x83, 0x8D, 0xC0, 0x0C, 0xD8, 0x00, 0x5B, 0x76, + 0x90, 0xD2, 0x4B, 0x30, 0x84, 0x35, 0x8B, 0x1E, 0x20, 0xB7, 0xE4, 0xDC, 0x63, 0xE5, 0xDF, 0xCD, + 0x00, 0x5F, 0x81, 0x5F, 0x67, 0xC5, 0x8B, 0xDF, 0xFC, 0xE1, 0x37, 0x5F, 0x07, 0xD9, 0xDE, 0x4F, + 0xE6, 0x7B, 0xF1, 0xFB, 0xA1, 0x5A, 0x71, 0x40, 0xFE, 0xBA, 0x1E, 0xAE, 0x13, 0x22, 0xD2, 0xFE, + 0x37, 0xA2, 0xB6, 0x8B, 0xAB, 0xEB, 0x84, 0x81, 0x4E, 0x7C, 0x1E, 0x02, 0xD1, 0xFB, 0xD7, 0x5D, + 0x11, 0x84, 0x64, 0xD2, 0x4D, 0xBB, 0x50, 0x00, 0x67, 0x54, 0xE2, 0x77, 0x89, 0xBA, 0x0B, 0xE7, + 0x05, 0x57, 0x9A, 0x22, 0x5A, 0xEC, 0x76, 0x1C, 0xFD, 0xE8, 0xA8, 0x18, 0x16, 0x41, 0x65, 0x03, + 0xFA, 0xC4, 0xA6, 0x31, 0x5C, 0x1A, 0x7F, 0xAB, 0x11, 0xC8, 0x4A, 0x99, 0xB9, 0xE6, 0xCF, 0x62, + 0x21, 0xA6, 0x72, 0x47, 0xDB, 0xBA, 0x96, 0x26, 0x4E, 0x2E, 0xD4, 0x8C, 0x46, 0xD6, 0xA7, 0x1A, + 0x6C, 0x32, 0xA7, 0xDF, 0x85, 0x1C, 0x03, 0xC3, 0x6D, 0xA9, 0xE9, 0x68, 0xF4, 0x17, 0x1E, 0xB2, + 0x70, 0x2A, 0xA1, 0xE5, 0xE1, 0xF3, 0x8F, 0x6F, 0x63, 0xAC, 0xEB, 0x72, 0x0B, 0x4C, 0x4A, 0x36, + 0x3C, 0x60, 0x91, 0x9F, 0x6E, 0x1C, 0x71, 0xEA, 0xD0, 0x78, 0x78, 0xA0, 0x2E, 0xC6, 0x32, 0x6B} + }, + }; + + if (isDev) + { + if (xci_header_sign_key.isNull()) + xci_header_sign_key = tc::crypto::RsaPublicKey(kXciHeaderSignModulus.data(), kXciHeaderSignModulus.size()); + + if (xci_cert_sign_key.isNull()) + xci_cert_sign_key = tc::crypto::RsaPublicKey(kXciCertSignModulus.data(), kXciCertSignModulus.size()); + + if (pkg2_sign_key.isNull()) + pkg2_sign_key = tc::crypto::RsaPublicKey(kDevPackage2HeaderModulus.data(), kDevPackage2HeaderModulus.size()); + + for (auto itr = kDevNcaHeaderSign0Modulus.begin(); itr != kDevNcaHeaderSign0Modulus.end(); itr++) + { + if (nca_header_sign0_key.find(itr->generation) == nca_header_sign0_key.end()) + nca_header_sign0_key[itr->generation] = tc::crypto::RsaPublicKey(itr->modulus.data(), itr->modulus.size()); + } + + for (auto itr = kDevAcidSignModulus.begin(); itr != kDevAcidSignModulus.end(); itr++) + { + if (acid_sign_key.find(itr->generation) == acid_sign_key.end()) + acid_sign_key[itr->generation] = tc::crypto::RsaPublicKey(itr->modulus.data(), itr->modulus.size()); + } + } + else + { + if (xci_header_sign_key.isNull()) + xci_header_sign_key = tc::crypto::RsaPublicKey(kXciHeaderSignModulus.data(), kXciHeaderSignModulus.size()); + + if (xci_cert_sign_key.isNull()) + xci_cert_sign_key = tc::crypto::RsaPublicKey(kXciCertSignModulus.data(), kXciCertSignModulus.size()); + + if (pkg2_sign_key.isNull()) + pkg2_sign_key = tc::crypto::RsaPublicKey(kProdPackage2HeaderModulus.data(), kProdPackage2HeaderModulus.size()); + + for (auto itr = kProdNcaHeaderSign0Modulus.begin(); itr != kProdNcaHeaderSign0Modulus.end(); itr++) + { + if (nca_header_sign0_key.find(itr->generation) == nca_header_sign0_key.end()) + nca_header_sign0_key[itr->generation] = tc::crypto::RsaPublicKey(itr->modulus.data(), itr->modulus.size()); + } + + for (auto itr = kProdAcidSignModulus.begin(); itr != kProdAcidSignModulus.end(); itr++) + { + if (acid_sign_key.find(itr->generation) == acid_sign_key.end()) + acid_sign_key[itr->generation] = tc::crypto::RsaPublicKey(itr->modulus.data(), itr->modulus.size()); + } + } } \ No newline at end of file diff --git a/src/KeyBag.h b/src/KeyBag.h index 0e3ab9b..a7cb369 100644 --- a/src/KeyBag.h +++ b/src/KeyBag.h @@ -48,6 +48,7 @@ struct KeyBag // xci tc::Optional xci_header_sign_key; std::map xci_header_key; + tc::Optional xci_cert_sign_key; // ticket std::map etik_common_key; @@ -76,6 +77,8 @@ private: void importTitleKeyFile(const tc::io::Path& keyfile_path); void importCertificateChain(const tc::io::Path& cert_path); void importTicket(const tc::io::Path& tik_path); + + void importKnownKeys(bool isDev); }; } \ No newline at end of file From f9c43a0fb52b20b312b1f3f0d4146400a16387ed Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 12:10:23 +0800 Subject: [PATCH 024/119] Misc --- src/KeyBag.cpp | 60 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 6f2e5d5..f4ff0f1 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -710,40 +710,40 @@ void nstool::KeyBagInitializer::importKnownKeys(bool isDev) { 0x00, {0xDD, 0xC8, 0xDD, 0xF2, 0x4E, 0x6D, 0xF0, 0xCA, 0x9E, 0xC7, 0x5D, 0xC7, 0x7B, 0xAD, 0xFE, 0x7D, - 0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50, - 0x2E, 0xFC, 0x9D, 0x94, 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57, - 0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, 0x1B, 0xFE, 0x30, 0x20, - 0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21, - 0xE7, 0x8B, 0xE9, 0x75, 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2, - 0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, 0x5E, 0x01, 0x30, 0xA4, - 0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE, - 0x1C, 0x82, 0x78, 0xB5, 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10, - 0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, 0x73, 0xB0, 0x31, 0x53, - 0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD, - 0x40, 0x9A, 0xA2, 0x8B, 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08, - 0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, 0x69, 0x2B, 0x31, 0x6A, - 0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA, - 0x37, 0xEA, 0xE8, 0x1E, 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B, - 0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, 0x7D, 0x35, 0x22, 0xFD,} + 0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50, + 0x2E, 0xFC, 0x9D, 0x94, 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57, + 0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, 0x1B, 0xFE, 0x30, 0x20, + 0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21, + 0xE7, 0x8B, 0xE9, 0x75, 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2, + 0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, 0x5E, 0x01, 0x30, 0xA4, + 0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE, + 0x1C, 0x82, 0x78, 0xB5, 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10, + 0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, 0x73, 0xB0, 0x31, 0x53, + 0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD, + 0x40, 0x9A, 0xA2, 0x8B, 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08, + 0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, 0x69, 0x2B, 0x31, 0x6A, + 0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA, + 0x37, 0xEA, 0xE8, 0x1E, 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B, + 0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, 0x7D, 0x35, 0x22, 0xFD,} }, { 0x01, {0xE7, 0xAA, 0x25, 0xC8, 0x01, 0xA5, 0x14, 0x6B, 0x01, 0x60, 0x3E, 0xD9, 0x96, 0x5A, 0xBF, 0x90, - 0xAC, 0xA7, 0xFD, 0x9B, 0x5B, 0xBD, 0x8A, 0x26, 0xB0, 0xCB, 0x20, 0x28, 0x9A, 0x72, 0x12, 0xF5, - 0x20, 0x65, 0xB3, 0xB9, 0x84, 0x58, 0x1F, 0x27, 0xBC, 0x7C, 0xA2, 0xC9, 0x9E, 0x18, 0x95, 0xCF, - 0xC2, 0x73, 0x2E, 0x74, 0x8C, 0x66, 0xE5, 0x9E, 0x79, 0x2B, 0xB8, 0x07, 0x0C, 0xB0, 0x4E, 0x8E, - 0xAB, 0x85, 0x21, 0x42, 0xC4, 0xC5, 0x6D, 0x88, 0x9C, 0xDB, 0x15, 0x95, 0x3F, 0x80, 0xDB, 0x7A, - 0x9A, 0x7D, 0x41, 0x56, 0x25, 0x17, 0x18, 0x42, 0x4D, 0x8C, 0xAC, 0xA5, 0x7B, 0xDB, 0x42, 0x5D, - 0x59, 0x35, 0x45, 0x5D, 0x8A, 0x02, 0xB5, 0x70, 0xC0, 0x72, 0x35, 0x46, 0xD0, 0x1D, 0x60, 0x01, - 0x4A, 0xCC, 0x1C, 0x46, 0xD3, 0xD6, 0x35, 0x52, 0xD6, 0xE1, 0xF8, 0x3B, 0x5D, 0xEA, 0xDD, 0xB8, - 0xFE, 0x7D, 0x50, 0xCB, 0x35, 0x23, 0x67, 0x8B, 0xB6, 0xE4, 0x74, 0xD2, 0x60, 0xFC, 0xFD, 0x43, - 0xBF, 0x91, 0x08, 0x81, 0xC5, 0x4F, 0x5D, 0x16, 0x9A, 0xC4, 0x9A, 0xC6, 0xF6, 0xF3, 0xE1, 0xF6, - 0x5C, 0x07, 0xAA, 0x71, 0x6C, 0x13, 0xA4, 0xB1, 0xB3, 0x66, 0xBF, 0x90, 0x4C, 0x3D, 0xA2, 0xC4, - 0x0B, 0xB8, 0x3D, 0x7A, 0x8C, 0x19, 0xFA, 0xFF, 0x6B, 0xB9, 0x1F, 0x02, 0xCC, 0xB6, 0xD3, 0x0C, - 0x7D, 0x19, 0x1F, 0x47, 0xF9, 0xC7, 0x40, 0x01, 0xFA, 0x46, 0xEA, 0x0B, 0xD4, 0x02, 0xE0, 0x3D, - 0x30, 0x9A, 0x1A, 0x0F, 0xEA, 0xA7, 0x66, 0x55, 0xF7, 0xCB, 0x28, 0xE2, 0xBB, 0x99, 0xE4, 0x83, - 0xC3, 0x43, 0x03, 0xEE, 0xDC, 0x1F, 0x02, 0x23, 0xDD, 0xD1, 0x2D, 0x39, 0xA4, 0x65, 0x75, 0x03, - 0xEF, 0x37, 0x9C, 0x06, 0xD6, 0xFA, 0xA1, 0x15, 0xF0, 0xDB, 0x17, 0x47, 0x26, 0x4F, 0x49, 0x03} + 0xAC, 0xA7, 0xFD, 0x9B, 0x5B, 0xBD, 0x8A, 0x26, 0xB0, 0xCB, 0x20, 0x28, 0x9A, 0x72, 0x12, 0xF5, + 0x20, 0x65, 0xB3, 0xB9, 0x84, 0x58, 0x1F, 0x27, 0xBC, 0x7C, 0xA2, 0xC9, 0x9E, 0x18, 0x95, 0xCF, + 0xC2, 0x73, 0x2E, 0x74, 0x8C, 0x66, 0xE5, 0x9E, 0x79, 0x2B, 0xB8, 0x07, 0x0C, 0xB0, 0x4E, 0x8E, + 0xAB, 0x85, 0x21, 0x42, 0xC4, 0xC5, 0x6D, 0x88, 0x9C, 0xDB, 0x15, 0x95, 0x3F, 0x80, 0xDB, 0x7A, + 0x9A, 0x7D, 0x41, 0x56, 0x25, 0x17, 0x18, 0x42, 0x4D, 0x8C, 0xAC, 0xA5, 0x7B, 0xDB, 0x42, 0x5D, + 0x59, 0x35, 0x45, 0x5D, 0x8A, 0x02, 0xB5, 0x70, 0xC0, 0x72, 0x35, 0x46, 0xD0, 0x1D, 0x60, 0x01, + 0x4A, 0xCC, 0x1C, 0x46, 0xD3, 0xD6, 0x35, 0x52, 0xD6, 0xE1, 0xF8, 0x3B, 0x5D, 0xEA, 0xDD, 0xB8, + 0xFE, 0x7D, 0x50, 0xCB, 0x35, 0x23, 0x67, 0x8B, 0xB6, 0xE4, 0x74, 0xD2, 0x60, 0xFC, 0xFD, 0x43, + 0xBF, 0x91, 0x08, 0x81, 0xC5, 0x4F, 0x5D, 0x16, 0x9A, 0xC4, 0x9A, 0xC6, 0xF6, 0xF3, 0xE1, 0xF6, + 0x5C, 0x07, 0xAA, 0x71, 0x6C, 0x13, 0xA4, 0xB1, 0xB3, 0x66, 0xBF, 0x90, 0x4C, 0x3D, 0xA2, 0xC4, + 0x0B, 0xB8, 0x3D, 0x7A, 0x8C, 0x19, 0xFA, 0xFF, 0x6B, 0xB9, 0x1F, 0x02, 0xCC, 0xB6, 0xD3, 0x0C, + 0x7D, 0x19, 0x1F, 0x47, 0xF9, 0xC7, 0x40, 0x01, 0xFA, 0x46, 0xEA, 0x0B, 0xD4, 0x02, 0xE0, 0x3D, + 0x30, 0x9A, 0x1A, 0x0F, 0xEA, 0xA7, 0x66, 0x55, 0xF7, 0xCB, 0x28, 0xE2, 0xBB, 0x99, 0xE4, 0x83, + 0xC3, 0x43, 0x03, 0xEE, 0xDC, 0x1F, 0x02, 0x23, 0xDD, 0xD1, 0x2D, 0x39, 0xA4, 0x65, 0x75, 0x03, + 0xEF, 0x37, 0x9C, 0x06, 0xD6, 0xFA, 0xA1, 0x15, 0xF0, 0xDB, 0x17, 0x47, 0x26, 0x4F, 0x49, 0x03} }, }; From 9d99c2084298720f8d19d26cc6d1c09df7be0962 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 12:11:37 +0800 Subject: [PATCH 025/119] Misc. --- src/KeyBag.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index f4ff0f1..27d8ea4 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -687,21 +687,21 @@ void nstool::KeyBagInitializer::importKnownKeys(bool isDev) { 0x01, {0xAD, 0xE3, 0xE1, 0xFA, 0x04, 0x35, 0xE5, 0xB6, 0xDD, 0x49, 0xEA, 0x89, 0x29, 0xB1, 0xFF, 0xB6, - 0x43, 0xDF, 0xCA, 0x96, 0xA0, 0x4A, 0x13, 0xDF, 0x43, 0xD9, 0x94, 0x97, 0x96, 0x43, 0x65, 0x48, - 0x70, 0x58, 0x33, 0xA2, 0x7D, 0x35, 0x7B, 0x96, 0x74, 0x5E, 0x0B, 0x5C, 0x32, 0x18, 0x14, 0x24, - 0xC2, 0x58, 0xB3, 0x6C, 0x22, 0x7A, 0xA1, 0xB7, 0xCB, 0x90, 0xA7, 0xA3, 0xF9, 0x7D, 0x45, 0x16, - 0xA5, 0xC8, 0xED, 0x8F, 0xAD, 0x39, 0x5E, 0x9E, 0x4B, 0x51, 0x68, 0x7D, 0xF8, 0x0C, 0x35, 0xC6, - 0x3F, 0x91, 0xAE, 0x44, 0xA5, 0x92, 0x30, 0x0D, 0x46, 0xF8, 0x40, 0xFF, 0xD0, 0xFF, 0x06, 0xD2, - 0x1C, 0x7F, 0x96, 0x18, 0xDC, 0xB7, 0x1D, 0x66, 0x3E, 0xD1, 0x73, 0xBC, 0x15, 0x8A, 0x2F, 0x94, - 0xF3, 0x00, 0xC1, 0x83, 0xF1, 0xCD, 0xD7, 0x81, 0x88, 0xAB, 0xDF, 0x8C, 0xEF, 0x97, 0xDD, 0x1B, - 0x17, 0x5F, 0x58, 0xF6, 0x9A, 0xE9, 0xE8, 0xC2, 0x2F, 0x38, 0x15, 0xF5, 0x21, 0x07, 0xF8, 0x37, - 0x90, 0x5D, 0x2E, 0x02, 0x40, 0x24, 0x15, 0x0D, 0x25, 0xB7, 0x26, 0x5D, 0x09, 0xCC, 0x4C, 0xF4, - 0xF2, 0x1B, 0x94, 0x70, 0x5A, 0x9E, 0xEE, 0xED, 0x77, 0x77, 0xD4, 0x51, 0x99, 0xF5, 0xDC, 0x76, - 0x1E, 0xE3, 0x6C, 0x8C, 0xD1, 0x12, 0xD4, 0x57, 0xD1, 0xB6, 0x83, 0xE4, 0xE4, 0xFE, 0xDA, 0xE9, - 0xB4, 0x3B, 0x33, 0xE5, 0x37, 0x8A, 0xDF, 0xB5, 0x7F, 0x89, 0xF1, 0x9B, 0x9E, 0xB0, 0x15, 0xB2, - 0x3A, 0xFE, 0xEA, 0x61, 0x84, 0x5B, 0x7D, 0x4B, 0x23, 0x12, 0x0B, 0x83, 0x12, 0xF2, 0x22, 0x6B, - 0xB9, 0x22, 0x96, 0x4B, 0x26, 0x0B, 0x63, 0x5E, 0x96, 0x57, 0x52, 0xA3, 0x67, 0x64, 0x22, 0xCA, - 0xD0, 0x56, 0x3E, 0x74, 0xB5, 0x98, 0x1F, 0x0D, 0xF8, 0xB3, 0x34, 0xE6, 0x98, 0x68, 0x5A, 0xAD,} + 0x43, 0xDF, 0xCA, 0x96, 0xA0, 0x4A, 0x13, 0xDF, 0x43, 0xD9, 0x94, 0x97, 0x96, 0x43, 0x65, 0x48, + 0x70, 0x58, 0x33, 0xA2, 0x7D, 0x35, 0x7B, 0x96, 0x74, 0x5E, 0x0B, 0x5C, 0x32, 0x18, 0x14, 0x24, + 0xC2, 0x58, 0xB3, 0x6C, 0x22, 0x7A, 0xA1, 0xB7, 0xCB, 0x90, 0xA7, 0xA3, 0xF9, 0x7D, 0x45, 0x16, + 0xA5, 0xC8, 0xED, 0x8F, 0xAD, 0x39, 0x5E, 0x9E, 0x4B, 0x51, 0x68, 0x7D, 0xF8, 0x0C, 0x35, 0xC6, + 0x3F, 0x91, 0xAE, 0x44, 0xA5, 0x92, 0x30, 0x0D, 0x46, 0xF8, 0x40, 0xFF, 0xD0, 0xFF, 0x06, 0xD2, + 0x1C, 0x7F, 0x96, 0x18, 0xDC, 0xB7, 0x1D, 0x66, 0x3E, 0xD1, 0x73, 0xBC, 0x15, 0x8A, 0x2F, 0x94, + 0xF3, 0x00, 0xC1, 0x83, 0xF1, 0xCD, 0xD7, 0x81, 0x88, 0xAB, 0xDF, 0x8C, 0xEF, 0x97, 0xDD, 0x1B, + 0x17, 0x5F, 0x58, 0xF6, 0x9A, 0xE9, 0xE8, 0xC2, 0x2F, 0x38, 0x15, 0xF5, 0x21, 0x07, 0xF8, 0x37, + 0x90, 0x5D, 0x2E, 0x02, 0x40, 0x24, 0x15, 0x0D, 0x25, 0xB7, 0x26, 0x5D, 0x09, 0xCC, 0x4C, 0xF4, + 0xF2, 0x1B, 0x94, 0x70, 0x5A, 0x9E, 0xEE, 0xED, 0x77, 0x77, 0xD4, 0x51, 0x99, 0xF5, 0xDC, 0x76, + 0x1E, 0xE3, 0x6C, 0x8C, 0xD1, 0x12, 0xD4, 0x57, 0xD1, 0xB6, 0x83, 0xE4, 0xE4, 0xFE, 0xDA, 0xE9, + 0xB4, 0x3B, 0x33, 0xE5, 0x37, 0x8A, 0xDF, 0xB5, 0x7F, 0x89, 0xF1, 0x9B, 0x9E, 0xB0, 0x15, 0xB2, + 0x3A, 0xFE, 0xEA, 0x61, 0x84, 0x5B, 0x7D, 0x4B, 0x23, 0x12, 0x0B, 0x83, 0x12, 0xF2, 0x22, 0x6B, + 0xB9, 0x22, 0x96, 0x4B, 0x26, 0x0B, 0x63, 0x5E, 0x96, 0x57, 0x52, 0xA3, 0x67, 0x64, 0x22, 0xCA, + 0xD0, 0x56, 0x3E, 0x74, 0xB5, 0x98, 0x1F, 0x0D, 0xF8, 0xB3, 0x34, 0xE6, 0x98, 0x68, 0x5A, 0xAD,} }, }; From ac5f20804fc2ac2ffed8b1d0cc3a57331d48e4f1 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 12:13:35 +0800 Subject: [PATCH 026/119] Misc --- src/Settings.cpp | 82 +++--------------------------------------------- 1 file changed, 4 insertions(+), 78 deletions(-) diff --git a/src/Settings.cpp b/src/Settings.cpp index a8ff451..13cf557 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -723,6 +723,10 @@ void nstool::SettingsInitializer::dump_keys() const { fmt::print(" ExtendedHeader-EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); } + if (opt.keybag.xci_cert_sign_key.isSet()) + { + dump_rsa_key(opt.keybag.xci_cert_sign_key.get(), fmt::format("CERT-SignatureKey"), 4, opt.cli_output_mode.show_extended_info); + } fmt::print(" Package1 Keys:\n"); for (auto itr = opt.keybag.pkg1_key.begin(); itr != opt.keybag.pkg1_key.end(); itr++) @@ -774,84 +778,6 @@ void nstool::SettingsInitializer::dump_keys() const break; } } - - /* - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getContentArchiveHeader0SignKey(rsa2048_key, byte_t(i)) == true) - dumpRsa2048Key(rsa2048_key, "Header0-SignatureKey-" + kKeyIndex[i], 2); - } - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getAcidSignKey(rsa2048_key, byte_t(i)) == true) - dumpRsa2048Key(rsa2048_key, "Acid-SignatureKey-" + kKeyIndex[i], 2); - } - - if (mKeyCfg.getContentArchiveHeaderKey(aesxts_key) == true) - dumpAesXtsKey(aesxts_key, "Header-EncryptionKey", 2); - - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 0, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKey-Application-" + kKeyIndex[i], 2); - if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 1, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKey-Ocean-" + kKeyIndex[i], 2); - if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 2, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKey-System-" + kKeyIndex[i], 2); - } - - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 0, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-Application-" + kKeyIndex[i], 2); - if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 1, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-Ocean-" + kKeyIndex[i], 2); - if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 2, aes_key) == true) - dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-System-" + kKeyIndex[i], 2); - } - - std::cout << " NRR Keys:" << std::endl; - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getNrrCertificateSignKey(rsa2048_key, byte_t(i)) == true) - dumpRsa2048Key(rsa2048_key, "Certificate-SignatureKey-" + kKeyIndex[i], 2); - } - - std::cout << " XCI Keys:" << std::endl; - if (mKeyCfg.getXciHeaderSignKey(rsa2048_key) == true) - dumpRsa2048Key(rsa2048_key, "Header-SignatureKey", 2); - if (mKeyCfg.getXciHeaderKey(aes_key) == true) - dumpAesKey(aes_key, "ExtendedHeader-EncryptionKey", 2); - - - - - std::cout << " Package1 Keys:" << std::endl; - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getPkg1Key(byte_t(i), aes_key) == true) - dumpAesKey(aes_key, "EncryptionKey-" + kKeyIndex[i], 2); - } - - std::cout << " Package2 Keys:" << std::endl; - if (mKeyCfg.getPkg2SignKey(rsa2048_key) == true) - dumpRsa2048Key(rsa2048_key, "Signature Key", 2); - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getPkg2Key(byte_t(i), aes_key) == true) - dumpAesKey(aes_key, "EncryptionKey-" + kKeyIndex[i], 2); - } - - std::cout << " ETicket Keys:" << std::endl; - for (size_t i = 0; i < kMasterKeyNum; i++) - { - if (mKeyCfg.getETicketCommonKey(byte_t(i), aes_key) == true) - dumpAesKey(aes_key, "CommonKey-" + kKeyIndex[i], 2); - } - - if (mKeyCfg.getPkiRootSignKey("Root", rsa4096_key) == true) - dumpRsa4096Key(rsa4096_key, "NNPKI Root Key", 1); - */ } void nstool::SettingsInitializer::dump_rsa_key(const KeyBag::rsa_key_t& key, const std::string& label, size_t indent, bool expanded_key_data) const From f8493e668621598d871b2719e395cf3b4b2a2553 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 12:15:32 +0800 Subject: [PATCH 027/119] Support importing XCI cert sign key. --- src/KeyBag.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 27d8ea4..15af9f3 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -132,6 +132,7 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa std::vector kPkg1Base = { "package1" }; std::vector kPkg2Base = { "package2" }; std::vector kXciHeaderBase = { "xci_header" }; + std::vector kXciCertBase = { "xci_cert" }; std::vector kContentArchiveHeaderBase = { "nca_header", "header" }; std::vector kAcidBase = { "acid" }; std::vector kNrrCertBase = { "nrr_certificate" }; @@ -357,6 +358,12 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa //fmt::print("{:s}_{:s}_{:s}\n", kXciHeaderBase[name_idx], kSignKey, kModulusStr); _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kXciHeaderBase[name_idx], kSignKey), xci_header_sign_key, 2048); } + /* XCI cert */ + if (name_idx < kXciCertBase.size()) + { + // xci cert sign key + _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kXciCertBase[name_idx], kSignKey), xci_cert_sign_key, 2048); + } /* PKI */ if (name_idx < kPkiRootBase.size()) From ab2686fd783f500a43f27b65643b1ba78768e5a3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 13:42:45 +0800 Subject: [PATCH 028/119] Move PfsProcess --- src/{not_ported => }/PfsProcess.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{not_ported => }/PfsProcess.cpp (100%) diff --git a/src/not_ported/PfsProcess.cpp b/src/PfsProcess.cpp similarity index 100% rename from src/not_ported/PfsProcess.cpp rename to src/PfsProcess.cpp From 5f1e8b27de0b21c429bff35c46c0c87177d9d11c Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 22:53:21 +0800 Subject: [PATCH 029/119] Port PfsProcess to libtoolchain --- deps/libnintendo-hac | 2 +- src/PfsProcess.cpp | 130 +++++++++++++++++++++---------------------- src/PfsProcess.h | 5 +- src/main.cpp | 11 ++-- src/util.cpp | 18 +++++- src/util.h | 4 ++ 6 files changed, 91 insertions(+), 79 deletions(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 02c6703..17c5c71 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 02c6703c9720d98197b3bb697fdab12008a9829e +Subproject commit 17c5c7177d8a899d01826091b3637e133b1f1c8e diff --git a/src/PfsProcess.cpp b/src/PfsProcess.cpp index 425df18..bf26009 100644 --- a/src/PfsProcess.cpp +++ b/src/PfsProcess.cpp @@ -1,20 +1,16 @@ #include "PfsProcess.h" - -#include -#include - -#include -#include +#include "util.h" #include +#include nstool::PfsProcess::PfsProcess() : + mModuleName("nstool::PfsProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false), mExtractPath(), - mExtract(false), mMountName(), mListFs(false), mPfs() @@ -33,7 +29,7 @@ void nstool::PfsProcess::process() } if (mPfs.getFsType() == mPfs.TYPE_HFS0 && mVerify) validateHfs(); - if (mExtract) + if (mExtractPath.isSet()) extractFs(); } @@ -57,9 +53,8 @@ void nstool::PfsProcess::setMountPointName(const std::string& mount_name) mMountName = mount_name; } -void nstool::PfsProcess::setExtractPath(const std::string& path) +void nstool::PfsProcess::setExtractPath(const tc::io::Path& path) { - mExtract = true; mExtractPath = path; } @@ -75,125 +70,126 @@ const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const void nstool::PfsProcess::importHeader() { + if (mFile == nullptr) + { + throw tc::Exception(mModuleName, "No file reader set."); + } + tc::ByteData scratch; - if (*mFile == nullptr) + // read base header to determine complete header size + if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sPfsHeader))) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "Corrupt PartitionFs: File too small"); } - - // open minimum header to get full header size - scratch.alloc(sizeof(nn::hac::sPfsHeader)); - (*mFile)->read(scratch.data(), 0, scratch.size()); + + scratch = tc::ByteData(sizeof(nn::hac::sPfsHeader)); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); if (validateHeaderMagic(((nn::hac::sPfsHeader*)scratch.data())) == false) { - throw tc::Exception(kModuleName, "Corrupt Header"); + throw tc::Exception(mModuleName, "Corrupt PartitionFs: Header had incorrect struct magic."); } + + // read complete size header size_t pfsHeaderSize = determineHeaderSize(((nn::hac::sPfsHeader*)scratch.data())); - - // open minimum header to get full header size - scratch.alloc(pfsHeaderSize); - (*mFile)->read(scratch.data(), 0, scratch.size()); + if (mFile->length() < tc::io::IOUtil::castSizeToInt64(pfsHeaderSize)) + { + throw tc::Exception(mModuleName, "Corrupt PartitionFs: File too small"); + } + + scratch = tc::ByteData(pfsHeaderSize); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // process PFS mPfs.fromBytes(scratch.data(), scratch.size()); } void nstool::PfsProcess::displayHeader() { - std::cout << "[PartitionFS]" << std::endl; - std::cout << " Type: " << nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType()) << std::endl; - std::cout << " FileNum: " << std::dec << mPfs.getFileList().size() << std::endl; + fmt::print("[PartitionFS]\n"); + fmt::print(" Type: {:s}\n", nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType())); + fmt::print(" FileNum: {:d}\n", mPfs.getFileList().size()); if (mMountName.empty() == false) { - std::cout << " MountPoint: " << mMountName; + fmt::print(" MountPoint: {:s}"); if (mMountName.at(mMountName.length()-1) != '/') - std::cout << "/"; - std::cout << std::endl; + fmt::print("/"); + fmt::print("\n"); } } void nstool::PfsProcess::displayFs() { - for (size_t i = 0; i < mPfs.getFileList().size(); i++) + auto file_list = mPfs.getFileList(); + for (auto itr = file_list.begin(); itr != file_list.end(); itr++) { - const nn::hac::PartitionFsHeader::sFile& file = mPfs.getFileList()[i]; - std::cout << " " << file.name; + fmt::print(" {:s}", itr->name); if (mCliOutputMode.show_layout) { switch (mPfs.getFsType()) { case (nn::hac::PartitionFsHeader::TYPE_PFS0): - std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ")"; + fmt::print(" (offset=0x{:x}, size=0x{:x})", itr->offset, itr->size); break; case (nn::hac::PartitionFsHeader::TYPE_HFS0): - std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ", hash_protected_size=0x" << file.hash_protected_size << ")"; + fmt::print(" (offset=0x{:x}, size=0x{:x}, hash_protected_size=0x{:x})", itr->offset, itr->size, itr->hash_protected_size); break; } } - std::cout << std::endl; + fmt::print("\n"); } } size_t nstool::PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr) { size_t fileEntrySize = 0; - if (hdr->st_magic.get() == nn::hac::pfs::kPfsStructMagic) + if (hdr->st_magic.unwrap() == nn::hac::pfs::kPfsStructMagic) fileEntrySize = sizeof(nn::hac::sPfsFile); else fileEntrySize = sizeof(nn::hac::sHashedPfsFile); - return sizeof(nn::hac::sPfsHeader) + hdr->file_num.get() * fileEntrySize + hdr->name_table_size.get(); + return sizeof(nn::hac::sPfsHeader) + hdr->file_num.unwrap() * fileEntrySize + hdr->name_table_size.unwrap(); } bool nstool::PfsProcess::validateHeaderMagic(const nn::hac::sPfsHeader* hdr) { - return hdr->st_magic.get() == nn::hac::pfs::kPfsStructMagic || hdr->st_magic.get() == nn::hac::pfs::kHashedPfsStructMagic; + return hdr->st_magic.unwrap() == nn::hac::pfs::kPfsStructMagic || hdr->st_magic.unwrap() == nn::hac::pfs::kHashedPfsStructMagic; } void nstool::PfsProcess::validateHfs() { - fnd::sha::sSha256Hash hash; - const std::vector& file = mPfs.getFileList(); - for (size_t i = 0; i < file.size(); i++) + nn::hac::detail::sha256_hash_t hash; + auto file_list = mPfs.getFileList(); + for (auto itr = file_list.begin(); itr != file_list.end(); itr++) { - mCache.alloc(file[i].hash_protected_size); - (*mFile)->read(mCache.data(), file[i].offset, file[i].hash_protected_size); - fnd::sha::Sha256(mCache.data(), file[i].hash_protected_size, hash.bytes); - if (hash != file[i].hash) + tc::ByteData cache = tc::ByteData(tc::io::IOUtil::castInt64ToSize(itr->hash_protected_size)); + mFile->seek(itr->offset, tc::io::SeekOrigin::Begin); + mFile->read(cache.data(), cache.size()); + tc::crypto::GenerateSha256Hash(hash.data(), cache.data(), cache.size()); + if (hash != itr->hash) { - printf("[WARNING] HFS0 %s%s%s: FAIL (bad hash)\n", !mMountName.empty()? mMountName.c_str() : "", (!mMountName.empty() && mMountName.at(mMountName.length()-1) != '/' )? "/" : "", file[i].name.c_str()); + fmt::print("[WARNING] HFS0 {:s}{:s}{:s}: FAIL (bad hash)\n", !mMountName.empty()? mMountName.c_str() : "", (!mMountName.empty() && mMountName.at(mMountName.length()-1) != '/' )? "/" : "", itr->name); } } } void nstool::PfsProcess::extractFs() { - // allocate only when extractDir is invoked - mCache.alloc(kCacheSize); + // create extract directory + tc::io::LocalStorage fs; + fs.createDirectory(mExtractPath.get()); - // make extract dir - fnd::io::makeDirectory(mExtractPath); + // extract files + tc::ByteData cache_for_extract = tc::ByteData(kCacheSize); - fnd::SimpleFile outFile; - const std::vector& file = mPfs.getFileList(); - - std::string file_path; - for (size_t i = 0; i < file.size(); i++) + auto file_list = mPfs.getFileList(); + for (auto itr = file_list.begin(); itr != file_list.end(); itr++) { - file_path.clear(); - fnd::io::appendToPath(file_path, mExtractPath); - fnd::io::appendToPath(file_path, file[i].name); + tc::io::Path extract_path = mExtractPath.get() + itr->name; - if (mCliOutputMode.show_basic_info) - printf("extract=[%s]\n", file_path.c_str()); - - outFile.open(file_path, outFile.Create); - (*mFile)->seek(file[i].offset); - for (size_t j = 0; j < ((file[i].size / kCacheSize) + ((file[i].size % kCacheSize) != 0)); j++) - { - (*mFile)->read(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize)); - outFile.write(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize)); - } - outFile.close(); + writeSubStreamToFile(mFile, itr->offset, itr->size, extract_path, cache_for_extract); } } \ No newline at end of file diff --git a/src/PfsProcess.h b/src/PfsProcess.h index 50a482b..cef266f 100644 --- a/src/PfsProcess.h +++ b/src/PfsProcess.h @@ -25,9 +25,10 @@ public: const nn::hac::PartitionFsHeader& getPfsHeader() const; private: - const std::string kModuleName = "PfsProcess"; static const size_t kCacheSize = 0x10000; + std::string mModuleName; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; @@ -36,8 +37,6 @@ private: std::string mMountName; bool mListFs; - tc::ByteData mCache; - nn::hac::PartitionFsHeader mPfs; void importHeader(); diff --git a/src/main.cpp b/src/main.cpp index 15f0d70..a1e811e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,7 +4,7 @@ //#include "GameCardProcess.h" -//#include "PfsProcess.h" +#include "PfsProcess.h" //#include "RomfsProcess.h" //#include "NcaProcess.h" //#include "MetaProcess.h" @@ -50,9 +50,7 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - */ - /* - else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PARTITIONFS || set.infile.filetype == nstool::Settings::FILE_TYPE_NSP) + else*/ if (set.infile.filetype == nstool::Settings::FILE_TYPE_PARTITIONFS || set.infile.filetype == nstool::Settings::FILE_TYPE_NSP) { nstool::PfsProcess obj; @@ -66,6 +64,7 @@ int umain(const std::vector& args, const std::vector& obj.process(); } + /* else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ROMFS) { nstool::RomfsProcess obj; @@ -113,8 +112,8 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - - else*/ if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT) + */ + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT) { nstool::CnmtProcess obj; diff --git a/src/util.cpp b/src/util.cpp index 435fc2d..0147dce 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -66,21 +66,29 @@ void nstool::processResFile(const std::shared_ptr& file, std::m } +void nstool::writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, tc::ByteData& cache) +{ + writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache); +} + void nstool::writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size) { writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache_size); } +void nstool::writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, tc::ByteData& cache) +{ + writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache); +} void nstool::writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size) { writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache_size); } -void nstool::writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size) +void nstool::writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, tc::ByteData& cache) { // iterate thru child files - tc::ByteData cache = tc::ByteData(cache_size); size_t cache_read_len; in_stream->seek(0, tc::io::SeekOrigin::Begin); @@ -99,6 +107,12 @@ void nstool::writeStreamToStream(const std::shared_ptr& in_stre } } +void nstool::writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size) +{ + tc::ByteData cache = tc::ByteData(cache_size); + writeStreamToStream(in_stream, out_stream, cache); +} + std::string nstool::getTruncatedBytesString(const byte_t* data, size_t len) { if (data == nullptr) { return fmt::format(""); } diff --git a/src/util.h b/src/util.h index aa1d962..d54275f 100644 --- a/src/util.h +++ b/src/util.h @@ -6,10 +6,14 @@ namespace nstool void processResFile(const std::shared_ptr& file, std::map& dict); +void writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, tc::ByteData& cache); void writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size = 0x10000); +void writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, tc::ByteData& cache); void writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size = 0x10000); +void writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, tc::ByteData& cache); void writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size = 0x10000); + std::string getTruncatedBytesString(const byte_t* data, size_t len); std::string getTruncatedBytesString(const byte_t* data, size_t len, bool do_not_truncate); From 7a9ef48a385da79b04b1b4d868f4c66cf8d58a14 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 22:57:12 +0800 Subject: [PATCH 030/119] Add FsProcess --- src/FsProcess.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++++++ src/FsProcess.h | 35 +++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 src/FsProcess.cpp create mode 100644 src/FsProcess.h diff --git a/src/FsProcess.cpp b/src/FsProcess.cpp new file mode 100644 index 0000000..3a7270d --- /dev/null +++ b/src/FsProcess.cpp @@ -0,0 +1,129 @@ +#include "FsProcess.h" +#include + +nstool::FsProcess::FsProcess() : + mModuleLabel("nstool::FsProcess"), + mInputFs(), + mShowFs(false), + mExtractPath() +{ + +} + +void nstool::FsProcess::setInputFileSystem(const std::shared_ptr& input_fs) +{ + mInputFs = input_fs; +} + +void nstool::FsProcess::setFsLabel(const std::string& fs_label) +{ + mFsLabel = fs_label; +} + +void nstool::FsProcess::setCliOutputMode(bool show_fs) +{ + mShowFs = show_fs; +} + +void nstool::FsProcess::setExtractPath(const tc::io::Path& extract_path) +{ + mExtractPath = extract_path; +} + +void nstool::FsProcess::process() +{ + if (mInputFs == nullptr) + { + throw tc::InvalidOperationException(mModuleLabel, "No input filesystem"); + } + + if (mShowFs) + printFs(); + + if (mExtractPath.isSet()) + extractFs(); +} + +void nstool::FsProcess::printFs() +{ + fmt::print("[{:s}FsTree]\n", (mFsLabel.isSet() ? (mFsLabel.get() + "/") : "")); + visitDir(tc::io::Path("/"), tc::io::Path("/"), false, true); +} + +void nstool::FsProcess::extractFs() +{ + fmt::print("[{:s}FsExtract]\n", (mFsLabel.isSet() ? (mFsLabel.get() + "/") : "")); + visitDir(tc::io::Path("/"), mExtractPath.get(), true, false); +} + +void nstool::FsProcess::visitDir(const tc::io::Path& v_path, const tc::io::Path& l_path, bool extract_fs, bool print_fs) +{ + tc::io::LocalStorage local_fs; + + // get listing for directory + tc::io::sDirectoryListing info; + mInputFs->getDirectoryListing(v_path, info); + + if (print_fs) + { + for (size_t i = 0; i < v_path.size(); i++) + fmt::print(" ");; + + fmt::print("{:s}/\n", ((v_path.size() == 1) ? "Root:" : v_path.back())); + } + if (extract_fs) + { + // create local dir + local_fs.createDirectory(l_path); + } + + // iterate thru child files + tc::ByteData cache = tc::ByteData(0x10000); + size_t cache_read_len; + tc::io::Path out_path; + std::string out_path_str; + std::shared_ptr in_stream; + std::shared_ptr out_stream; + for (auto itr = info.file_list.begin(); itr != info.file_list.end(); itr++) + { + if (print_fs) + { + for (size_t i = 0; i < v_path.size(); i++) + fmt::print(" "); + fmt::print(" {:s}\n", *itr); + } + if (extract_fs) + { + // build out path + out_path = l_path + *itr; + tc::io::PathUtil::pathToUnixUTF8(out_path, out_path_str); + + fmt::print("Saving {:s}...\n", out_path_str); + + // begin export + mInputFs->openFile(v_path + *itr, tc::io::FileMode::Open, tc::io::FileAccess::Read, in_stream); + local_fs.openFile(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write, out_stream); + + in_stream->seek(0, tc::io::SeekOrigin::Begin); + out_stream->seek(0, tc::io::SeekOrigin::Begin); + for (int64_t remaining_data = in_stream->length(); remaining_data > 0;) + { + cache_read_len = in_stream->read(cache.data(), cache.size()); + if (cache_read_len == 0) + { + throw tc::io::IOException(mModuleLabel, fmt::format("Failed to read from {:s}file.", (mFsLabel.isSet() ? (mFsLabel.get() + " ") : ""))); + } + + out_stream->write(cache.data(), cache_read_len); + + remaining_data -= int64_t(cache_read_len); + } + } + } + + // iterate thru child dirs + for (auto itr = info.dir_list.begin(); itr != info.dir_list.end(); itr++) + { + visitDir(v_path + *itr, l_path + *itr, extract_fs, print_fs); + } +} \ No newline at end of file diff --git a/src/FsProcess.h b/src/FsProcess.h new file mode 100644 index 0000000..b40dbb6 --- /dev/null +++ b/src/FsProcess.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +#include "types.h" + +namespace nstool +{ + +class FsProcess +{ +public: + FsProcess(); + + void setInputFileSystem(const std::shared_ptr& input_fs); + void setFsLabel(const std::string& fs_label); + void setCliOutputMode(bool show_fs); + void setExtractPath(const tc::io::Path& extract_path); + + void process(); +private: + std::string mModuleLabel; + + std::shared_ptr mInputFs; + tc::Optional mFsLabel; + bool mShowFs; + tc::Optional mExtractPath; + + void printFs(); + void extractFs(); + + void visitDir(const tc::io::Path& v_path, const tc::io::Path& l_path, bool extract_fs, bool print_fs); +}; + +} \ No newline at end of file From 649bfb1f11976d9b3fd548304d2f68a293f2605b Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 22:57:35 +0800 Subject: [PATCH 031/119] Change PfsProcess to use FsProcess and VirtualFileSystem --- src/PfsProcess.cpp | 100 ++++++++++----------------------------------- src/PfsProcess.h | 12 +++--- 2 files changed, 27 insertions(+), 85 deletions(-) diff --git a/src/PfsProcess.cpp b/src/PfsProcess.cpp index bf26009..e389221 100644 --- a/src/PfsProcess.cpp +++ b/src/PfsProcess.cpp @@ -4,17 +4,21 @@ #include #include +#include "FsProcess.h" +#include +#include + nstool::PfsProcess::PfsProcess() : mModuleName("nstool::PfsProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false), - mExtractPath(), - mMountName(), - mListFs(false), - mPfs() + mPfs(), + mFileSystem(), + mFsProcess() { + mFsProcess.setFsLabel("PartitionFS"); } void nstool::PfsProcess::process() @@ -24,13 +28,9 @@ void nstool::PfsProcess::process() if (mCliOutputMode.show_basic_info) { displayHeader(); - if (mListFs || mCliOutputMode.show_extended_info) - displayFs(); } - if (mPfs.getFsType() == mPfs.TYPE_HFS0 && mVerify) - validateHfs(); - if (mExtractPath.isSet()) - extractFs(); + + mFsProcess.process(); } void nstool::PfsProcess::setInputFile(const std::shared_ptr& file) @@ -50,17 +50,17 @@ void nstool::PfsProcess::setVerifyMode(bool verify) void nstool::PfsProcess::setMountPointName(const std::string& mount_name) { - mMountName = mount_name; + mFsProcess.setFsLabel(mount_name); } void nstool::PfsProcess::setExtractPath(const tc::io::Path& path) { - mExtractPath = path; + mFsProcess.setExtractPath(path); } void nstool::PfsProcess::setListFs(bool list_fs) { - mListFs = list_fs; + mFsProcess.setCliOutputMode(list_fs); } const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const @@ -68,6 +68,11 @@ const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const return mPfs; } +const std::shared_ptr& nstool::PfsProcess::getFileSystem() const +{ + return mFileSystem; +} + void nstool::PfsProcess::importHeader() { if (mFile == nullptr) @@ -104,6 +109,10 @@ void nstool::PfsProcess::importHeader() // process PFS mPfs.fromBytes(scratch.data(), scratch.size()); + + // create virtual filesystem + mFileSystem = std::make_shared(tc::io::VirtualFileSystem(nn::hac::PartitionFsMetaGenerator(mFile, mVerify ? nn::hac::PartitionFsMetaGenerator::ValidationMode_Warn : nn::hac::PartitionFsMetaGenerator::ValidationMode_None))); + mFsProcess.setInputFileSystem(mFileSystem); } void nstool::PfsProcess::displayHeader() @@ -111,36 +120,6 @@ void nstool::PfsProcess::displayHeader() fmt::print("[PartitionFS]\n"); fmt::print(" Type: {:s}\n", nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType())); fmt::print(" FileNum: {:d}\n", mPfs.getFileList().size()); - if (mMountName.empty() == false) - { - fmt::print(" MountPoint: {:s}"); - if (mMountName.at(mMountName.length()-1) != '/') - fmt::print("/"); - fmt::print("\n"); - } -} - -void nstool::PfsProcess::displayFs() -{ - auto file_list = mPfs.getFileList(); - for (auto itr = file_list.begin(); itr != file_list.end(); itr++) - { - fmt::print(" {:s}", itr->name); - if (mCliOutputMode.show_layout) - { - switch (mPfs.getFsType()) - { - case (nn::hac::PartitionFsHeader::TYPE_PFS0): - fmt::print(" (offset=0x{:x}, size=0x{:x})", itr->offset, itr->size); - break; - case (nn::hac::PartitionFsHeader::TYPE_HFS0): - fmt::print(" (offset=0x{:x}, size=0x{:x}, hash_protected_size=0x{:x})", itr->offset, itr->size, itr->hash_protected_size); - break; - } - - } - fmt::print("\n"); - } } size_t nstool::PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr) @@ -157,39 +136,4 @@ size_t nstool::PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr) bool nstool::PfsProcess::validateHeaderMagic(const nn::hac::sPfsHeader* hdr) { return hdr->st_magic.unwrap() == nn::hac::pfs::kPfsStructMagic || hdr->st_magic.unwrap() == nn::hac::pfs::kHashedPfsStructMagic; -} - -void nstool::PfsProcess::validateHfs() -{ - nn::hac::detail::sha256_hash_t hash; - auto file_list = mPfs.getFileList(); - for (auto itr = file_list.begin(); itr != file_list.end(); itr++) - { - tc::ByteData cache = tc::ByteData(tc::io::IOUtil::castInt64ToSize(itr->hash_protected_size)); - mFile->seek(itr->offset, tc::io::SeekOrigin::Begin); - mFile->read(cache.data(), cache.size()); - tc::crypto::GenerateSha256Hash(hash.data(), cache.data(), cache.size()); - if (hash != itr->hash) - { - fmt::print("[WARNING] HFS0 {:s}{:s}{:s}: FAIL (bad hash)\n", !mMountName.empty()? mMountName.c_str() : "", (!mMountName.empty() && mMountName.at(mMountName.length()-1) != '/' )? "/" : "", itr->name); - } - } -} - -void nstool::PfsProcess::extractFs() -{ - // create extract directory - tc::io::LocalStorage fs; - fs.createDirectory(mExtractPath.get()); - - // extract files - tc::ByteData cache_for_extract = tc::ByteData(kCacheSize); - - auto file_list = mPfs.getFileList(); - for (auto itr = file_list.begin(); itr != file_list.end(); itr++) - { - tc::io::Path extract_path = mExtractPath.get() + itr->name; - - writeSubStreamToFile(mFile, itr->offset, itr->size, extract_path, cache_for_extract); - } } \ No newline at end of file diff --git a/src/PfsProcess.h b/src/PfsProcess.h index cef266f..9c13efd 100644 --- a/src/PfsProcess.h +++ b/src/PfsProcess.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include "FsProcess.h" #include @@ -23,6 +24,7 @@ public: void setListFs(bool list_fs); const nn::hac::PartitionFsHeader& getPfsHeader() const; + const std::shared_ptr& getFileSystem() const; private: static const size_t kCacheSize = 0x10000; @@ -33,19 +35,15 @@ private: CliOutputMode mCliOutputMode; bool mVerify; - tc::Optional mExtractPath; - std::string mMountName; - bool mListFs; - nn::hac::PartitionFsHeader mPfs; + std::shared_ptr mFileSystem; + FsProcess mFsProcess; + void importHeader(); void displayHeader(); - void displayFs(); size_t determineHeaderSize(const nn::hac::sPfsHeader* hdr); bool validateHeaderMagic(const nn::hac::sPfsHeader* hdr); - void validateHfs(); - void extractFs(); }; } \ No newline at end of file From 345eb897c6693d3fd6832ba493e4c4c3025585c8 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 22:57:50 +0800 Subject: [PATCH 032/119] Update libnintendo-hac --- deps/libnintendo-hac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 17c5c71..22e8040 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 17c5c7177d8a899d01826091b3637e133b1f1c8e +Subproject commit 22e80406394a7bc0bcd2a9cc52a67be654239007 From d373132e082457aec83428c924f8a7acd96b9639 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 2 Oct 2021 23:02:46 +0800 Subject: [PATCH 033/119] Change behaviour in how keydata is shown. --- src/Settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Settings.cpp b/src/Settings.cpp index 13cf557..51dd188 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -413,7 +413,7 @@ nstool::SettingsInitializer::SettingsInitializer(const std::vector& opt.keybag.fallback_content_key = mBodyKey; // dump keys if requires - if (opt.cli_output_mode.show_keydata) + if (mShowKeydata) // but not opt.cli_output_mode.show_keydata, since this that enabled by toggling -v,--verbose, personally I don't think a summary of imported keydata should be included in verbose output. { dump_keys(); } From 35cb70748792a05c1f7dd9057b55dfb3b06cb32e Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 3 Oct 2021 10:47:11 +0800 Subject: [PATCH 034/119] Port RomfsProcess to libtoolchain. --- deps/libnintendo-hac | 2 +- src/AssetProcess.cpp | 16 ++++-- src/AssetProcess.h | 2 +- src/RomfsProcess.cpp | 134 +++++++++++++++++++++++++++++++++++++++++++ src/RomfsProcess.h | 108 ++++------------------------------ src/main.cpp | 5 +- 6 files changed, 159 insertions(+), 108 deletions(-) create mode 100644 src/RomfsProcess.cpp diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 22e8040..a4bef5a 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 22e80406394a7bc0bcd2a9cc52a67be654239007 +Subproject commit a4bef5a52859625027f75b71d802b1a53af18534 diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index dfa9658..7c933db 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -35,7 +35,7 @@ void nstool::AssetProcess::setVerifyMode(bool verify) void nstool::AssetProcess::setListFs(bool list) { - //mRomfs.setListFs(list); + mRomfs.setListFs(list); } void nstool::AssetProcess::setIconExtractPath(const tc::io::Path& path) @@ -50,7 +50,7 @@ void nstool::AssetProcess::setNacpExtractPath(const tc::io::Path& path) void nstool::AssetProcess::setRomfsExtractPath(const tc::io::Path& path) { - //mRomfs.setExtractPath(path); + mRomfs.setExtractPath(path); } @@ -82,6 +82,10 @@ void nstool::AssetProcess::processSections() if ((mHdr.getIconInfo().size + mHdr.getIconInfo().offset) > file_size) throw tc::Exception(mModuleName, "ASET geometry for icon beyond file size"); + std::string icon_extract_path_str; + tc::io::PathUtil::pathToUnixUTF8(mIconExtractPath.get(), icon_extract_path_str); + + fmt::print("Saving {:s}...", icon_extract_path_str); writeSubStreamToFile(mFile, mHdr.getIconInfo().offset, mHdr.getIconInfo().size, mIconExtractPath.get()); } @@ -107,11 +111,11 @@ void nstool::AssetProcess::processSections() if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > file_size) throw tc::Exception(mModuleName, "ASET geometry for romfs beyond file size"); - //mRomfs.setInputFile(std::make_shared(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size)); - //mRomfs.setCliOutputMode(mCliOutputMode); - //mRomfs.setVerifyMode(mVerify); + mRomfs.setInputFile(std::make_shared(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size)); + mRomfs.setCliOutputMode(mCliOutputMode); + mRomfs.setVerifyMode(mVerify); - //mRomfs.process(); + mRomfs.process(); } } diff --git a/src/AssetProcess.h b/src/AssetProcess.h index f81aa39..5d1ea5b 100644 --- a/src/AssetProcess.h +++ b/src/AssetProcess.h @@ -37,7 +37,7 @@ private: nn::hac::AssetHeader mHdr; NacpProcess mNacp; - //RomfsProcess mRomfs; + RomfsProcess mRomfs; void importHeader(); void processSections(); diff --git a/src/RomfsProcess.cpp b/src/RomfsProcess.cpp new file mode 100644 index 0000000..e1e24c1 --- /dev/null +++ b/src/RomfsProcess.cpp @@ -0,0 +1,134 @@ +#include "RomfsProcess.h" +#include "util.h" + +#include +#include + + +nstool::RomfsProcess::RomfsProcess() : + mModuleName("nstool::RomfsProcess"), + mFile(), + mCliOutputMode(true, false, false, false), + mVerify(false), + mDirNum(0), + mFileNum(0), + mFileSystem(), + mFsProcess() +{ + mFsProcess.setFsLabel("RomFS"); +} + +void nstool::RomfsProcess::process() +{ + importHeader(); + + if (mCliOutputMode.show_basic_info) + { + displayHeader(); + } + + mFsProcess.process(); +} + +void nstool::RomfsProcess::setInputFile(const std::shared_ptr& file) +{ + mFile = file; +} + +void nstool::RomfsProcess::setCliOutputMode(CliOutputMode type) +{ + mCliOutputMode = type; +} + +void nstool::RomfsProcess::setVerifyMode(bool verify) +{ + mVerify = verify; +} + +void nstool::RomfsProcess::setMountPointName(const std::string& mount_name) +{ + mFsProcess.setFsLabel(mount_name); +} + +void nstool::RomfsProcess::setExtractPath(const tc::io::Path& path) +{ + mFsProcess.setExtractPath(path); +} + +void nstool::RomfsProcess::setListFs(bool list_fs) +{ + mFsProcess.setCliOutputMode(list_fs); +} + +const std::shared_ptr& nstool::RomfsProcess::getFileSystem() const +{ + return mFileSystem; +} + +void nstool::RomfsProcess::importHeader() +{ + if (mFile == nullptr) + { + throw tc::Exception(mModuleName, "No file reader set."); + } + + tc::ByteData scratch; + + // read base header to determine complete header size + if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sRomfsHeader))) + { + throw tc::Exception(mModuleName, "Corrupt RomFs: File too small"); + } + + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read((byte_t*)&mRomfsHeader, sizeof(mRomfsHeader)); + if (mRomfsHeader.header_size.unwrap() != sizeof(nn::hac::sRomfsHeader) || + mRomfsHeader.dir_hash_bucket.offset.unwrap() != sizeof(nn::hac::sRomfsHeader) || + mRomfsHeader.data_offset.unwrap() != align(mRomfsHeader.header_size.unwrap(), nn::hac::romfs::kRomfsHeaderAlign)) + { + throw tc::ArgumentOutOfRangeException("nn::hac::RomFsMetaGenerator", "Corrupt RomFs: RomFsHeader is corrupted."); + } + + // get dir entry ptr + tc::ByteData dir_entry_table = tc::ByteData(tc::io::IOUtil::castInt64ToSize(mRomfsHeader.dir_entry.size.unwrap())); + mFile->seek(mRomfsHeader.dir_entry.offset.unwrap(), tc::io::SeekOrigin::Begin); + mFile->read(dir_entry_table.data(), dir_entry_table.size()); + + // get file entry ptr + tc::ByteData file_entry_table = tc::ByteData(tc::io::IOUtil::castInt64ToSize(mRomfsHeader.file_entry.size.unwrap())); + mFile->seek(mRomfsHeader.file_entry.offset.unwrap(), tc::io::SeekOrigin::Begin); + mFile->read(file_entry_table.data(), file_entry_table.size()); + + // count dir num + mDirNum = 0; + for (uint32_t v_addr = 0; size_t(v_addr) < dir_entry_table.size();) + { + uint32_t total_size = sizeof(nn::hac::sRomfsDirEntry) + align(((nn::hac::sRomfsDirEntry*)(dir_entry_table.data() + v_addr))->name_size.unwrap(), 4); + + mDirNum += 1; + + v_addr += total_size; + } + + // count file num + mFileNum = 0; + for (uint32_t v_addr = 0; size_t(v_addr) < file_entry_table.size();) + { + uint32_t total_size = sizeof(nn::hac::sRomfsFileEntry) + align(((nn::hac::sRomfsFileEntry*)(file_entry_table.data() + v_addr))->name_size.unwrap(), 4); + + mFileNum += 1; + + v_addr += total_size; + } + + // create virtual filesystem + mFileSystem = std::make_shared(tc::io::VirtualFileSystem(nn::hac::RomFsMetaGenerator(mFile))); + mFsProcess.setInputFileSystem(mFileSystem); +} + +void nstool::RomfsProcess::displayHeader() +{ + fmt::print("[RomFS]\n"); + fmt::print(" DirNum: {:d}\n", mDirNum); + fmt::print(" FileNum: {:d}\n", mFileNum); +} \ No newline at end of file diff --git a/src/RomfsProcess.h b/src/RomfsProcess.h index 35575c4..c7609a7 100644 --- a/src/RomfsProcess.h +++ b/src/RomfsProcess.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include "FsProcess.h" #include @@ -8,71 +9,6 @@ namespace nstool { class RomfsProcess { public: - struct sDirectory; - struct sFile; - - struct sDirectory - { - std::string name; - std::vector dir_list; - std::vector file_list; - - void operator=(const sDirectory& other) - { - name = other.name; - dir_list = other.dir_list; - file_list = other.file_list; - } - - bool operator==(const sDirectory& other) const - { - return (name == other.name) \ - && (dir_list == other.dir_list) \ - && (file_list == other.file_list); - } - - bool operator!=(const sDirectory& other) const - { - return !operator==(other); - } - - bool operator==(const std::string& other) const - { - return (name == other); - } - }; - - struct sFile - { - std::string name; - int64_t offset; - int64_t size; - - void operator=(const sFile& other) - { - name = other.name; - offset = other.offset; - size = other.size; - } - - bool operator==(const sFile& other) const - { - return (name == other.name) \ - && (offset == other.offset) \ - && (size == other.size); - } - - bool operator!=(const sFile& other) const - { - return !operator==(other); - } - - bool operator==(const std::string& other) const - { - return (name == other); - } - }; - RomfsProcess(); void process(); @@ -82,55 +18,31 @@ public: void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - // romfs specific + // pfs specific void setMountPointName(const std::string& mount_name); void setExtractPath(const tc::io::Path& path); void setListFs(bool list_fs); - const sDirectory& getRootDir() const; + const std::shared_ptr& getFileSystem() const; + private: - const std::string kModuleName = "RomfsProcess"; static const size_t kCacheSize = 0x10000; + std::string mModuleName; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; - bool mShowBasicInfo; - bool mShowExtendedInfo; - bool mShowLayoutInfo; - bool mShowKeydata; - bool mVerbose; bool mVerify; - tc::Optional mExtractPath; - std::string mMountName; - bool mListFs; - - tc::ByteData mCache; - + nn::hac::sRomfsHeader mRomfsHeader; size_t mDirNum; size_t mFileNum; - nn::hac::sRomfsHeader mHdr; - tc::ByteData mDirNodes; - tc::ByteData mFileNodes; - sDirectory mRootDir; - inline nn::hac::sRomfsDirEntry* get_dir_node(uint32_t offset) { return (nn::hac::sRomfsDirEntry*)(mDirNodes.data() + offset); } - inline nn::hac::sRomfsFileEntry* get_file_node(uint32_t offset) { return (nn::hac::sRomfsFileEntry*)(mFileNodes.data() + offset); } - - - void printTab(size_t tab) const; - void displayFile(const sFile& file, size_t tab) const; - void displayDir(const sDirectory& dir, size_t tab) const; + std::shared_ptr mFileSystem; + FsProcess mFsProcess; + void importHeader(); void displayHeader(); - void displayFs(); - - void extractDir(const std::string& path, const sDirectory& dir); - void extractFs(); - - bool validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const; - void importDirectory(uint32_t dir_offset, sDirectory& dir); - void resolveRomfs(); }; } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index a1e811e..b697ad8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,7 @@ //#include "GameCardProcess.h" #include "PfsProcess.h" -//#include "RomfsProcess.h" +#include "RomfsProcess.h" //#include "NcaProcess.h" //#include "MetaProcess.h" #include "CnmtProcess.h" @@ -64,7 +64,7 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - /* + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ROMFS) { nstool::RomfsProcess obj; @@ -79,6 +79,7 @@ int umain(const std::vector& args, const std::vector& obj.process(); } + /* else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NCA) { nstool::NcaProcess obj; From f734f8b4c64ea7d42be6b1ec8591bb664cee4875 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 3 Oct 2021 10:47:54 +0800 Subject: [PATCH 035/119] Move old RomfsProcess header to not_ported. --- src/not_ported/RomfsProcess.h | 136 ++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/not_ported/RomfsProcess.h diff --git a/src/not_ported/RomfsProcess.h b/src/not_ported/RomfsProcess.h new file mode 100644 index 0000000..35575c4 --- /dev/null +++ b/src/not_ported/RomfsProcess.h @@ -0,0 +1,136 @@ +#pragma once +#include "types.h" + +#include + +namespace nstool { + +class RomfsProcess +{ +public: + struct sDirectory; + struct sFile; + + struct sDirectory + { + std::string name; + std::vector dir_list; + std::vector file_list; + + void operator=(const sDirectory& other) + { + name = other.name; + dir_list = other.dir_list; + file_list = other.file_list; + } + + bool operator==(const sDirectory& other) const + { + return (name == other.name) \ + && (dir_list == other.dir_list) \ + && (file_list == other.file_list); + } + + bool operator!=(const sDirectory& other) const + { + return !operator==(other); + } + + bool operator==(const std::string& other) const + { + return (name == other); + } + }; + + struct sFile + { + std::string name; + int64_t offset; + int64_t size; + + void operator=(const sFile& other) + { + name = other.name; + offset = other.offset; + size = other.size; + } + + bool operator==(const sFile& other) const + { + return (name == other.name) \ + && (offset == other.offset) \ + && (size == other.size); + } + + bool operator!=(const sFile& other) const + { + return !operator==(other); + } + + bool operator==(const std::string& other) const + { + return (name == other); + } + }; + + RomfsProcess(); + + void process(); + + // generic + void setInputFile(const std::shared_ptr& file); + void setCliOutputMode(CliOutputMode type); + void setVerifyMode(bool verify); + + // romfs specific + void setMountPointName(const std::string& mount_name); + void setExtractPath(const tc::io::Path& path); + void setListFs(bool list_fs); + + const sDirectory& getRootDir() const; +private: + const std::string kModuleName = "RomfsProcess"; + static const size_t kCacheSize = 0x10000; + + std::shared_ptr mFile; + CliOutputMode mCliOutputMode; + bool mShowBasicInfo; + bool mShowExtendedInfo; + bool mShowLayoutInfo; + bool mShowKeydata; + bool mVerbose; + bool mVerify; + + tc::Optional mExtractPath; + std::string mMountName; + bool mListFs; + + tc::ByteData mCache; + + size_t mDirNum; + size_t mFileNum; + nn::hac::sRomfsHeader mHdr; + tc::ByteData mDirNodes; + tc::ByteData mFileNodes; + sDirectory mRootDir; + + inline nn::hac::sRomfsDirEntry* get_dir_node(uint32_t offset) { return (nn::hac::sRomfsDirEntry*)(mDirNodes.data() + offset); } + inline nn::hac::sRomfsFileEntry* get_file_node(uint32_t offset) { return (nn::hac::sRomfsFileEntry*)(mFileNodes.data() + offset); } + + + void printTab(size_t tab) const; + void displayFile(const sFile& file, size_t tab) const; + void displayDir(const sDirectory& dir, size_t tab) const; + + void displayHeader(); + void displayFs(); + + void extractDir(const std::string& path, const sDirectory& dir); + void extractFs(); + + bool validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const; + void importDirectory(uint32_t dir_offset, sDirectory& dir); + void resolveRomfs(); +}; + +} \ No newline at end of file From 41fee26d1fd47590b1851de6f7f4c48e5c99d9a1 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 3 Oct 2021 10:56:39 +0800 Subject: [PATCH 036/119] Remove unneeded include. --- src/PfsProcess.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PfsProcess.cpp b/src/PfsProcess.cpp index e389221..81779a1 100644 --- a/src/PfsProcess.cpp +++ b/src/PfsProcess.cpp @@ -4,7 +4,6 @@ #include #include -#include "FsProcess.h" #include #include From 5670cb7e16d154bfeb6c81cf5fda4ee1be60008c Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 3 Oct 2021 10:56:55 +0800 Subject: [PATCH 037/119] Fix bug in detecting corrupt RomFs. --- deps/libnintendo-hac | 2 +- src/RomfsProcess.cpp | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index a4bef5a..02a5acb 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit a4bef5a52859625027f75b71d802b1a53af18534 +Subproject commit 02a5acb78bfa55edfeebcc42b4591b3d43424575 diff --git a/src/RomfsProcess.cpp b/src/RomfsProcess.cpp index e1e24c1..8e61bc5 100644 --- a/src/RomfsProcess.cpp +++ b/src/RomfsProcess.cpp @@ -83,10 +83,10 @@ void nstool::RomfsProcess::importHeader() mFile->seek(0, tc::io::SeekOrigin::Begin); mFile->read((byte_t*)&mRomfsHeader, sizeof(mRomfsHeader)); if (mRomfsHeader.header_size.unwrap() != sizeof(nn::hac::sRomfsHeader) || - mRomfsHeader.dir_hash_bucket.offset.unwrap() != sizeof(nn::hac::sRomfsHeader) || + mRomfsHeader.dir_entry.offset.unwrap() != (mRomfsHeader.dir_hash_bucket.offset.unwrap() + mRomfsHeader.dir_hash_bucket.size.unwrap()) || mRomfsHeader.data_offset.unwrap() != align(mRomfsHeader.header_size.unwrap(), nn::hac::romfs::kRomfsHeaderAlign)) { - throw tc::ArgumentOutOfRangeException("nn::hac::RomFsMetaGenerator", "Corrupt RomFs: RomFsHeader is corrupted."); + throw tc::ArgumentOutOfRangeException(mModuleName, "Corrupt RomFs: RomFsHeader is corrupted."); } // get dir entry ptr @@ -105,7 +105,11 @@ void nstool::RomfsProcess::importHeader() { uint32_t total_size = sizeof(nn::hac::sRomfsDirEntry) + align(((nn::hac::sRomfsDirEntry*)(dir_entry_table.data() + v_addr))->name_size.unwrap(), 4); - mDirNum += 1; + // don't count root directory + if (v_addr != 0) + { + mDirNum += 1; + } v_addr += total_size; } From ece2906cde1820ac2dbf5a7e0eb32173028b8d2f Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 3 Oct 2021 12:01:23 +0800 Subject: [PATCH 038/119] Added input stream property checks. --- src/AssetProcess.cpp | 4 ++++ src/CnmtProcess.cpp | 4 ++++ src/EsTikProcess.cpp | 4 ++++ src/NacpProcess.cpp | 14 +++++++++----- src/PfsProcess.cpp | 4 ++++ src/RomfsProcess.cpp | 4 ++++ 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index 7c933db..ef6ea40 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -60,6 +60,10 @@ void nstool::AssetProcess::importHeader() { throw tc::Exception(mModuleName, "No file reader set."); } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); + } if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sAssetHeader))) { diff --git a/src/CnmtProcess.cpp b/src/CnmtProcess.cpp index 0703e1d..c8c6039 100644 --- a/src/CnmtProcess.cpp +++ b/src/CnmtProcess.cpp @@ -44,6 +44,10 @@ void nstool::CnmtProcess::importCnmt() { throw tc::Exception(mModuleName, "No file reader set."); } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); + } // check if file_size is greater than 20MB, don't import. size_t cnmt_file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); diff --git a/src/EsTikProcess.cpp b/src/EsTikProcess.cpp index 78ee18a..5a4108e 100644 --- a/src/EsTikProcess.cpp +++ b/src/EsTikProcess.cpp @@ -53,6 +53,10 @@ void nstool::EsTikProcess::importTicket() { throw tc::Exception(mModuleName, "No file reader set."); } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); + } // check if file_size is greater than 20MB, don't import. size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); diff --git a/src/NacpProcess.cpp b/src/NacpProcess.cpp index f2e673c..5aeaadb 100644 --- a/src/NacpProcess.cpp +++ b/src/NacpProcess.cpp @@ -44,12 +44,16 @@ void nstool::NacpProcess::importNacp() { throw tc::Exception(mModuleName, "No file reader set."); } - - // check if file_size is greater than 20MB, don't import. - size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); - if (file_size > (0x100000 * 20)) + if (mFile->canRead() == false || mFile->canSeek() == false) { - throw tc::Exception(mModuleName, "File too large."); + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); + } + + // check if file_size does matches expected size + size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + if (file_size != sizeof(nn::hac::sApplicationControlProperty)) + { + throw tc::Exception(mModuleName, "File was incorrect size."); } // read cnmt diff --git a/src/PfsProcess.cpp b/src/PfsProcess.cpp index 81779a1..4dd59e9 100644 --- a/src/PfsProcess.cpp +++ b/src/PfsProcess.cpp @@ -78,6 +78,10 @@ void nstool::PfsProcess::importHeader() { throw tc::Exception(mModuleName, "No file reader set."); } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); + } tc::ByteData scratch; diff --git a/src/RomfsProcess.cpp b/src/RomfsProcess.cpp index 8e61bc5..ca1bf6c 100644 --- a/src/RomfsProcess.cpp +++ b/src/RomfsProcess.cpp @@ -71,6 +71,10 @@ void nstool::RomfsProcess::importHeader() { throw tc::Exception(mModuleName, "No file reader set."); } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); + } tc::ByteData scratch; From 7ede5a081b318e5b2ec16da6f4d0b9fe002f2adb Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 3 Oct 2021 12:03:39 +0800 Subject: [PATCH 039/119] Moved GameCardProcess --- src/{not_ported => }/GameCardProcess.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{not_ported => }/GameCardProcess.cpp (100%) diff --git a/src/not_ported/GameCardProcess.cpp b/src/GameCardProcess.cpp similarity index 100% rename from src/not_ported/GameCardProcess.cpp rename to src/GameCardProcess.cpp From a3996eaf0c17c1bfa0eb41bcce657ff679482f71 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 8 Oct 2021 17:56:40 +0800 Subject: [PATCH 040/119] Add ExtractJob --- src/types.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/types.h b/src/types.h index 2d22f1c..f11c14d 100644 --- a/src/types.h +++ b/src/types.h @@ -24,4 +24,9 @@ struct CliOutputMode {} }; +struct ExtractJob { + tc::io::Path virtual_path; + tc::io::Path extract_path; +}; + } \ No newline at end of file From 6d9510cde455915e914075132228aae9f025080a Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 8 Oct 2021 17:59:27 +0800 Subject: [PATCH 041/119] Add the ability to import initial_data_kek --- src/KeyBag.cpp | 13 +++++++++++++ src/KeyBag.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 15af9f3..684ddf1 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -132,6 +132,7 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa std::vector kPkg1Base = { "package1" }; std::vector kPkg2Base = { "package2" }; std::vector kXciHeaderBase = { "xci_header" }; + std::vector kXciInitialDataBase = { "xci_initial_data" }; std::vector kXciCertBase = { "xci_cert" }; std::vector kContentArchiveHeaderBase = { "nca_header", "header" }; std::vector kAcidBase = { "acid" }; @@ -358,6 +359,18 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa //fmt::print("{:s}_{:s}_{:s}\n", kXciHeaderBase[name_idx], kSignKey, kModulusStr); _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kXciHeaderBase[name_idx], kSignKey), xci_header_sign_key, 2048); } + + /* XCI InitialData */ + if (name_idx < kXciInitialDataBase.size()) + { + // xci initial data key (based on index) + for (size_t kek_index = 0; kek_index < 8; kek_index++) + { + //fmt::print("{:s}_{:s}_{:02x}\n", kXciInitialDataBase[name_idx], kKekStr, kek_index); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kXciInitialDataBase[name_idx], kKekStr, kek_index), xci_initial_data_kek[kek_index]); + } + } + /* XCI cert */ if (name_idx < kXciCertBase.size()) { diff --git a/src/KeyBag.h b/src/KeyBag.h index a7cb369..e198932 100644 --- a/src/KeyBag.h +++ b/src/KeyBag.h @@ -48,6 +48,7 @@ struct KeyBag // xci tc::Optional xci_header_sign_key; std::map xci_header_key; + std::map xci_initial_data_kek; tc::Optional xci_cert_sign_key; // ticket From 9d41a1c913501f1d6286bce80569aae1c05fa0ed Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 8 Oct 2021 18:02:32 +0800 Subject: [PATCH 042/119] Update libnintendo-hac --- deps/libnintendo-hac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 02a5acb..e2fb1f3 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 02a5acb78bfa55edfeebcc42b4591b3d43424575 +Subproject commit e2fb1f3ad788533631d8855d484d14bec33ac961 From e7dfa8ad44ad220fdfb6987b8c18810841aa5dab Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 8 Oct 2021 18:05:51 +0800 Subject: [PATCH 043/119] Port GameCardProcess & use new ExtractJob system. User can now specify custom extract jobs with specific virtual paths. --- src/AssetProcess.cpp | 31 +++-- src/AssetProcess.h | 9 +- src/FsProcess.cpp | 189 ++++++++++++++++++++++++--- src/FsProcess.h | 27 +++- src/GameCardProcess.cpp | 281 ++++++++++++++++++++-------------------- src/GameCardProcess.h | 43 ++---- src/PfsProcess.cpp | 58 ++++----- src/PfsProcess.h | 17 ++- src/RomfsProcess.cpp | 48 +++---- src/RomfsProcess.h | 16 +-- src/Settings.cpp | 82 ++++++++++-- src/Settings.h | 14 +- src/main.cpp | 34 ++--- 13 files changed, 502 insertions(+), 347 deletions(-) diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index ef6ea40..7111f5c 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -8,15 +8,7 @@ nstool::AssetProcess::AssetProcess() : mCliOutputMode(true, false, false, false), mVerify(false) { -} - -void nstool::AssetProcess::process() -{ - importHeader(); - if (mCliOutputMode.show_basic_info) - displayHeader(); - processSections(); -} +} void nstool::AssetProcess::setInputFile(const std::shared_ptr& file) { @@ -33,11 +25,6 @@ void nstool::AssetProcess::setVerifyMode(bool verify) mVerify = verify; } -void nstool::AssetProcess::setListFs(bool list) -{ - mRomfs.setListFs(list); -} - void nstool::AssetProcess::setIconExtractPath(const tc::io::Path& path) { mIconExtractPath = path; @@ -48,11 +35,23 @@ void nstool::AssetProcess::setNacpExtractPath(const tc::io::Path& path) mNacpExtractPath = path; } -void nstool::AssetProcess::setRomfsExtractPath(const tc::io::Path& path) +void nstool::AssetProcess::setRomfsShowFsTree(bool show_fs_tree) { - mRomfs.setExtractPath(path); + mRomfs.setShowFsTree(show_fs_tree); } +void nstool::AssetProcess::setRomfsExtractJobs(const std::vector& extract_jobs) +{ + mRomfs.setExtractJobs(extract_jobs); +} + +void nstool::AssetProcess::process() +{ + importHeader(); + if (mCliOutputMode.show_basic_info) + displayHeader(); + processSections(); +} void nstool::AssetProcess::importHeader() { diff --git a/src/AssetProcess.h b/src/AssetProcess.h index 5d1ea5b..96f40ec 100644 --- a/src/AssetProcess.h +++ b/src/AssetProcess.h @@ -12,18 +12,17 @@ class AssetProcess public: AssetProcess(); - void process(); - void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - void setListFs(bool list); - void setIconExtractPath(const tc::io::Path& path); void setNacpExtractPath(const tc::io::Path& path); - void setRomfsExtractPath(const tc::io::Path& path); + + void setRomfsShowFsTree(bool show_fs_tree); + void setRomfsExtractJobs(const std::vector& extract_jobs); + void process(); private: std::string mModuleName; diff --git a/src/FsProcess.cpp b/src/FsProcess.cpp index 3a7270d..78e2aee 100644 --- a/src/FsProcess.cpp +++ b/src/FsProcess.cpp @@ -1,11 +1,20 @@ #include "FsProcess.h" -#include +#include "util.h" + +#include +#include +#include nstool::FsProcess::FsProcess() : mModuleLabel("nstool::FsProcess"), mInputFs(), - mShowFs(false), - mExtractPath() + mFsFormatName(), + mShowFsInfo(false), + mProperties(), + mShowFsTree(false), + mFsRootLabel(), + mExtractJobs(), + mDataCache(0x10000) { } @@ -15,19 +24,34 @@ void nstool::FsProcess::setInputFileSystem(const std::shared_ptr& properties) { - mExtractPath = extract_path; + mProperties = properties; +} + +void nstool::FsProcess::setShowFsTree(bool show_fs_tree) +{ + mShowFsTree = show_fs_tree; +} + +void nstool::FsProcess::setFsRootLabel(const std::string& root_label) +{ + mFsRootLabel = root_label; +} + +void nstool::FsProcess::setExtractJobs(const std::vector& extract_jobs) +{ + mExtractJobs = extract_jobs; } void nstool::FsProcess::process() @@ -37,23 +61,147 @@ void nstool::FsProcess::process() throw tc::InvalidOperationException(mModuleLabel, "No input filesystem"); } - if (mShowFs) - printFs(); + if (mShowFsInfo) + { + fmt::print("[{:s}]\n", mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem/Info"); + for (auto itr = mProperties.begin(); itr != mProperties.end(); itr++) + { + fmt::print(" {:s}\n", *itr); + } + } - if (mExtractPath.isSet()) + if (mShowFsTree) + { + printFs(); + } + + + if (mExtractJobs.empty() == false) + { extractFs(); + } } void nstool::FsProcess::printFs() { - fmt::print("[{:s}FsTree]\n", (mFsLabel.isSet() ? (mFsLabel.get() + "/") : "")); + fmt::print("[{:s}/Tree]\n", (mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem")); visitDir(tc::io::Path("/"), tc::io::Path("/"), false, true); } void nstool::FsProcess::extractFs() { - fmt::print("[{:s}FsExtract]\n", (mFsLabel.isSet() ? (mFsLabel.get() + "/") : "")); - visitDir(tc::io::Path("/"), mExtractPath.get(), true, false); + fmt::print("[{:s}/Extract]\n", (mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem")); + + for (auto itr = mExtractJobs.begin(); itr != mExtractJobs.end(); itr++) + { + std::string path_str; + tc::io::PathUtil::pathToUnixUTF8(itr->virtual_path, path_str); + + // check if root path (legacy case) + if (itr->virtual_path == tc::io::Path("/")) + { + visitDir(tc::io::Path("/"), itr->extract_path, true, false); + + //fmt::print("Root Dir Virtual Path: \"{:s}\"\n", path_str); + + // root directory extract successful, continue to next job + continue; + } + + // otherwise determine if this is a file or subdirectory + try { + std::shared_ptr file_stream; + mInputFs->openFile(itr->virtual_path, tc::io::FileMode::Open, tc::io::FileAccess::Read, file_stream); + + //fmt::print("Valid File Path: \"{:s}\"\n", path_str); + + // the output path for this file will depend on the user specified extract path + std::shared_ptr local_fs = std::make_shared(tc::io::LocalStorage()); + + // case: the extract_path is a valid path to an existing directory + // behaviour: extract the file, preserving the original filename, to the specified directory + // method: try getDirectoryListing(itr->extract_path), if this is does not throw, then we can be sure this is a valid path to a directory, file_extract_path = itr->extract_path + itr->virtual_path.back() + + try { + tc::io::sDirectoryListing dir_listing; + local_fs->getDirectoryListing(itr->extract_path, dir_listing); + + tc::io::Path file_extract_path = itr->extract_path + itr->virtual_path.back(); + + std::string file_extract_path_str; + tc::io::PathUtil::pathToUnixUTF8(file_extract_path, file_extract_path_str); + + fmt::print("Saving {:s}...\n", file_extract_path_str); + + writeStreamToFile(file_stream, itr->extract_path + itr->virtual_path.back(), mDataCache); + + continue; + + } catch (tc::io::DirectoryNotFoundException& e) { + // acceptable exception, just means directory didn't exist + } + + // case: the extract_path up until the last element is a valid path to an existing directory, but the full path specifies neither a directory or a file + // behaviour: treat extract_path as the intended location to write the extracted file (the original filename is not preserved, instead specified by the user in the final element of the extract path) + // method: since this checks n-1 elements, it implies a path with more than one element, so that must be accounted for, as relative paths are valid and single element paths aren't always root + + try { + std::string test_path_str; + + // get path to parent directory + tc::io::Path parent_dir_path = itr->extract_path; + + // replace final path element with the current directory alias + parent_dir_path.pop_back(); // remove filename + parent_dir_path.push_back("."); // replace with the current dir name alias + tc::io::PathUtil::pathToUnixUTF8(parent_dir_path, test_path_str); + + // test parent directory exists + tc::io::sDirectoryListing dir_listing; + local_fs->getDirectoryListing(parent_dir_path, dir_listing); + + std::string file_extract_path_str; + tc::io::PathUtil::pathToUnixUTF8(itr->extract_path, file_extract_path_str); + + fmt::print("Saving {:s} as {:s}...\n", path_str, file_extract_path_str); + + writeStreamToFile(file_stream, itr->extract_path, mDataCache); + + continue; + } catch (tc::io::DirectoryNotFoundException& e) { + // acceptable exception, just means the parent directory didn't exist + } + + + // extract path could not be determined, inform the user and skip this job + std::string literal_extract_path_str; + tc::io::PathUtil::pathToUnixUTF8(itr->extract_path, literal_extract_path_str); + fmt::print("[WARNING] Extract path was invalid, and was skipped: {:s}\n", literal_extract_path_str); + continue; + } catch (tc::io::FileNotFoundException& e) { + // acceptable exception, just means file didn't exist + } + + // not a file, attempt to process this as a directory + try { + tc::io::sDirectoryListing dir_listing; + mInputFs->getDirectoryListing(itr->virtual_path, dir_listing); + + + visitDir(itr->virtual_path, itr->extract_path, true, false); + + //fmt::print("Valid Directory Path: \"{:s}\"\n", path_str); + + // directory extract successful, continue to next job + continue; + + } catch (tc::io::DirectoryNotFoundException& e) { + // acceptable exception, just means directory didn't exist + } + + fmt::print("[WARNING] Failed to extract virtual path: \"{:s}\"\n", path_str); + } + } void nstool::FsProcess::visitDir(const tc::io::Path& v_path, const tc::io::Path& l_path, bool extract_fs, bool print_fs) @@ -67,9 +215,9 @@ void nstool::FsProcess::visitDir(const tc::io::Path& v_path, const tc::io::Path& if (print_fs) { for (size_t i = 0; i < v_path.size(); i++) - fmt::print(" ");; + fmt::print(" "); - fmt::print("{:s}/\n", ((v_path.size() == 1) ? "Root:" : v_path.back())); + fmt::print("{:s}/\n", ((v_path.size() == 1) ? (mFsRootLabel.isSet() ? (mFsRootLabel.get() + ":") : "Root:") : v_path.back())); } if (extract_fs) { @@ -78,7 +226,6 @@ void nstool::FsProcess::visitDir(const tc::io::Path& v_path, const tc::io::Path& } // iterate thru child files - tc::ByteData cache = tc::ByteData(0x10000); size_t cache_read_len; tc::io::Path out_path; std::string out_path_str; @@ -108,13 +255,13 @@ void nstool::FsProcess::visitDir(const tc::io::Path& v_path, const tc::io::Path& out_stream->seek(0, tc::io::SeekOrigin::Begin); for (int64_t remaining_data = in_stream->length(); remaining_data > 0;) { - cache_read_len = in_stream->read(cache.data(), cache.size()); + cache_read_len = in_stream->read(mDataCache.data(), mDataCache.size()); if (cache_read_len == 0) { - throw tc::io::IOException(mModuleLabel, fmt::format("Failed to read from {:s}file.", (mFsLabel.isSet() ? (mFsLabel.get() + " ") : ""))); + throw tc::io::IOException(mModuleLabel, fmt::format("Failed to read from {:s}file.", (mFsFormatName.isSet() ? (mFsFormatName.get() + " ") : ""))); } - out_stream->write(cache.data(), cache_read_len); + out_stream->write(mDataCache.data(), cache_read_len); remaining_data -= int64_t(cache_read_len); } diff --git a/src/FsProcess.h b/src/FsProcess.h index b40dbb6..d9b5e3b 100644 --- a/src/FsProcess.h +++ b/src/FsProcess.h @@ -13,18 +13,33 @@ public: FsProcess(); void setInputFileSystem(const std::shared_ptr& input_fs); - void setFsLabel(const std::string& fs_label); - void setCliOutputMode(bool show_fs); - void setExtractPath(const tc::io::Path& extract_path); + void setFsFormatName(const std::string& fs_format_name); + void setFsProperties(const std::vector& properties); + void setShowFsInfo(bool show_fs_info); + void setShowFsTree(bool show_fs_tree); + void setFsRootLabel(const std::string& root_label); + void setExtractJobs(const std::vector& extract_jobs); void process(); private: std::string mModuleLabel; std::shared_ptr mInputFs; - tc::Optional mFsLabel; - bool mShowFs; - tc::Optional mExtractPath; + + // fs info + tc::Optional mFsFormatName; + bool mShowFsInfo; + std::vector mProperties; + + // fs tree + bool mShowFsTree; + tc::Optional mFsRootLabel; + + // extract jobs + std::vector mExtractJobs; + + // cache for file extract + tc::ByteData mDataCache; void printFs(); void extractFs(); diff --git a/src/GameCardProcess.cpp b/src/GameCardProcess.cpp index d06a6ce..d16a85b 100644 --- a/src/GameCardProcess.cpp +++ b/src/GameCardProcess.cpp @@ -1,20 +1,25 @@ -#include -#include -#include -#include +#include "GameCardProcess.h" + +#include +#include + #include #include #include -#include "GameCardProcess.h" + +#include +#include "FsProcess.h" + nstool::GameCardProcess::GameCardProcess() : + mModuleName("nstool::GameCardProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false), mListFs(false), mProccessExtendedHeader(false), mRootPfs(), - mExtractInfo() + mExtractJobs() { } @@ -30,11 +35,8 @@ void nstool::GameCardProcess::process() if (mCliOutputMode.show_basic_info) displayHeader(); - // process root partition + // process nested HFS0 processRootPfs(); - - // process partitions - processPartitionPfs(); } void nstool::GameCardProcess::setInputFile(const std::shared_ptr& file) @@ -57,60 +59,69 @@ void nstool::GameCardProcess::setVerifyMode(bool verify) mVerify = verify; } -void nstool::GameCardProcess::setPartitionForExtract(const std::string& partition_name, const std::string& extract_path) +void nstool::GameCardProcess::setExtractJobs(const std::vector extract_jobs) { - mExtractInfo.push_back({partition_name, extract_path}); + mExtractJobs = extract_jobs; } -void nstool::GameCardProcess::setListFs(bool list_fs) +void nstool::GameCardProcess::setShowFsTree(bool show_fs_tree) { - mListFs = list_fs; + mListFs = show_fs_tree; } void nstool::GameCardProcess::importHeader() { - tc::ByteData scratch; - - if (*mFile == nullptr) + if (mFile == nullptr) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "No file reader set."); + } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); } + // check stream is large enough for header + if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sSdkGcHeader))) + { + throw tc::Exception(mModuleName, "Corrupt GameCard Image: File too small."); + } + // allocate memory for header - scratch.alloc(sizeof(nn::hac::sSdkGcHeader)); + tc::ByteData scratch = tc::ByteData(sizeof(nn::hac::sSdkGcHeader)); // read header region - (*mFile)->read((byte_t*)scratch.data(), 0, sizeof(nn::hac::sSdkGcHeader)); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); // 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) + if (((nn::hac::sSdkGcHeader*)scratch.data())->signed_header.header.st_magic.unwrap() == 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) + else if (((nn::hac::sGcHeader_Rsa2048Signed*)scratch.data())->header.st_magic.unwrap() == nn::hac::gc::kGcHeaderStructMagic) { mIsTrueSdkXci = false; mGcHeaderOffset = 0; } else { - throw tc::Exception(kModuleName, "GameCard image did not have expected magic bytes"); + throw tc::Exception(mModuleName, "Corrupt GameCard Image: Unexpected 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); + tc::crypto::GenerateSha256Hash(mHdrHash.data(), (byte_t*)&hdr_ptr->header, sizeof(nn::hac::sGcHeader)); // save the signature - memcpy(mHdrSignature, hdr_ptr->signature, fnd::rsa::kRsa2048Size); + memcpy(mHdrSignature.data(), hdr_ptr->signature.data(), mHdrSignature.size()); // decrypt extended header - KeyBag::aes128_key_t header_key; - if (mKeyCfg.getXciHeaderKey(header_key)) + byte_t xci_header_key_index = hdr_ptr->header.key_flag & 7; + if (mKeyCfg.xci_header_key.find(xci_header_key_index) != mKeyCfg.xci_header_key.end()) { - nn::hac::GameCardUtil::decryptXciHeader(&hdr_ptr->header, header_key.key); + nn::hac::GameCardUtil::decryptXciHeader(&hdr_ptr->header, mKeyCfg.xci_header_key[xci_header_key_index].data()); mProccessExtendedHeader = true; } @@ -120,164 +131,156 @@ void nstool::GameCardProcess::importHeader() void nstool::GameCardProcess::displayHeader() { - std::cout << "[GameCard Header]" << std::endl; - std::cout << " CardHeaderVersion: " << std::dec << (uint32_t)mHdr.getCardHeaderVersion() << std::endl; - std::cout << " RomSize: " << nn::hac::GameCardUtil::getRomSizeAsString((nn::hac::gc::RomSize)mHdr.getRomSizeType()); + const nn::hac::sGcHeader* raw_hdr = (const nn::hac::sGcHeader*)mHdr.getBytes().data(); + + fmt::print("[GameCard/Header]\n"); + fmt::print(" CardHeaderVersion: {:d}\n", mHdr.getCardHeaderVersion()); + fmt::print(" RomSize: {:s}", nn::hac::GameCardUtil::getRomSizeAsString((nn::hac::gc::RomSize)mHdr.getRomSizeType())); if (mCliOutputMode.show_extended_info) - std::cout << " (0x" << std::hex << (uint32_t)mHdr.getRomSizeType() << ")"; - std::cout << std::endl; - std::cout << " PackageId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getPackageId() << std::endl; - std::cout << " Flags: 0x" << std::dec << (uint32_t)mHdr.getFlags() << std::endl; - if (mHdr.getFlags() != 0) + fmt::print(" (0x{:x})", mHdr.getRomSizeType()); + fmt::print("\n"); + fmt::print(" PackageId: 0x{:016x}\n", mHdr.getPackageId()); + fmt::print(" Flags: 0x{:02x}\n", *((byte_t*)&raw_hdr->flags)); + for (auto itr = mHdr.getFlags().begin(); itr != mHdr.getFlags().end(); itr++) { - for (uint32_t i = 0; i < 8; i++) - { - if (_HAS_BIT(mHdr.getFlags(), i)) - { - std::cout << " " << nn::hac::GameCardUtil::getHeaderFlagsAsString((nn::hac::gc::HeaderFlags)i) << std::endl; - } - } + fmt::print(" {:s}\n", nn::hac::GameCardUtil::getHeaderFlagsAsString((nn::hac::gc::HeaderFlags)*itr)); + } + + + if (mCliOutputMode.show_extended_info) + { + fmt::print(" KekIndex: {:s} ({:d})\n", nn::hac::GameCardUtil::getKekIndexAsString((nn::hac::gc::KekIndex)mHdr.getKekIndex()), mHdr.getKekIndex()); + fmt::print(" TitleKeyDecIndex: {:d}\n", mHdr.getTitleKeyDecIndex()); + fmt::print(" InitialData:\n"); + fmt::print(" Hash:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getInitialDataHash().data(), mHdr.getInitialDataHash().size(), true, ":", 0x10, 6, false)); } if (mCliOutputMode.show_extended_info) { - std::cout << " KekIndex: " << nn::hac::GameCardUtil::getKekIndexAsString((nn::hac::gc::KekIndex)mHdr.getKekIndex()) << " (" << std::dec << (uint32_t)mHdr.getKekIndex() << ")" << std::endl; - std::cout << " TitleKeyDecIndex: " << std::dec << (uint32_t)mHdr.getTitleKeyDecIndex() << std::endl; - std::cout << " InitialData:" << 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, 0x10, true, ":") << std::endl; + fmt::print(" Extended Header AesCbc IV:\n"); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getAesCbcIv().data(), mHdr.getAesCbcIv().size(), true, ":")); } - if (mCliOutputMode.show_extended_info) - { - std::cout << " Extended Header AesCbc IV:" << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getAesCbcIv().iv, sizeof(mHdr.getAesCbcIv().iv), true, ":") << std::endl; - } - std::cout << " SelSec: 0x" << std::hex << mHdr.getSelSec() << std::endl; - std::cout << " SelT1Key: 0x" << std::hex << mHdr.getSelT1Key() << std::endl; - std::cout << " SelKey: 0x" << std::hex << mHdr.getSelKey() << std::endl; + fmt::print(" SelSec: 0x{:x}\n", mHdr.getSelSec()); + fmt::print(" SelT1Key: 0x{:x}\n", mHdr.getSelT1Key()); + fmt::print(" SelKey: 0x{:x}\n", mHdr.getSelKey()); if (mCliOutputMode.show_layout) { - std::cout << " RomAreaStartPage: 0x" << std::hex << mHdr.getRomAreaStartPage(); + fmt::print(" RomAreaStartPage: 0x{:x}", mHdr.getRomAreaStartPage()); if (mHdr.getRomAreaStartPage() != (uint32_t)(-1)) - std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getRomAreaStartPage()) << ")"; - std::cout << std::endl; + fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getRomAreaStartPage())); + fmt::print("\n"); - std::cout << " BackupAreaStartPage: 0x" << std::hex << mHdr.getBackupAreaStartPage(); + fmt::print(" BackupAreaStartPage: 0x{:x}", mHdr.getBackupAreaStartPage()); if (mHdr.getBackupAreaStartPage() != (uint32_t)(-1)) - std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getBackupAreaStartPage()) << ")"; - std::cout << std::endl; + fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getBackupAreaStartPage())); + fmt::print("\n"); - std::cout << " ValidDataEndPage: 0x" << std::hex << mHdr.getValidDataEndPage(); + fmt::print(" ValidDataEndPage: 0x{:x}", mHdr.getValidDataEndPage()); if (mHdr.getValidDataEndPage() != (uint32_t)(-1)) - std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage()) << ")"; - std::cout << std::endl; + fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage())); + fmt::print("\n"); - std::cout << " LimArea: 0x" << std::hex << mHdr.getLimAreaPage(); + fmt::print(" LimArea: 0x{:x}", mHdr.getLimAreaPage()); if (mHdr.getLimAreaPage() != (uint32_t)(-1)) - std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getLimAreaPage()) << ")"; - std::cout << std::endl; + fmt::print(" (0x{:x})", nn::hac::GameCardUtil::blockToAddr(mHdr.getLimAreaPage())); + fmt::print("\n"); - std::cout << " PartitionFs Header:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getPartitionFsAddress() << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getPartitionFsSize() << std::endl; + fmt::print(" PartitionFs Header:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getPartitionFsAddress()); + fmt::print(" Size: 0x{:x}\n", mHdr.getPartitionFsSize()); if (mCliOutputMode.show_extended_info) { - std::cout << " Hash:" << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes, 0x10, true, ":") << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes+0x10, 0x10, true, ":") << std::endl; + fmt::print(" Hash:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getPartitionFsHash().data(), mHdr.getPartitionFsHash().size(), true, ":", 0x10, 6, false)); } } if (mProccessExtendedHeader) { - std::cout << "[GameCard Extended Header]" << std::endl; - std::cout << " FwVersion: v" << std::dec << mHdr.getFwVersion() << " (" << nn::hac::GameCardUtil::getCardFwVersionDescriptionAsString((nn::hac::gc::FwVersion)mHdr.getFwVersion()) << ")" << std::endl; - std::cout << " AccCtrl1: 0x" << std::hex << mHdr.getAccCtrl1() << std::endl; - std::cout << " CardClockRate: " << nn::hac::GameCardUtil::getCardClockRateAsString((nn::hac::gc::CardClockRate)mHdr.getAccCtrl1()) << std::endl; - std::cout << " Wait1TimeRead: 0x" << std::hex << mHdr.getWait1TimeRead() << std::endl; - std::cout << " Wait2TimeRead: 0x" << std::hex << mHdr.getWait2TimeRead() << std::endl; - std::cout << " Wait1TimeWrite: 0x" << std::hex << mHdr.getWait1TimeWrite() << std::endl; - std::cout << " Wait2TimeWrite: 0x" << std::hex << mHdr.getWait2TimeWrite() << std::endl; - std::cout << " SdkAddon Version: " << nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getFwMode()) << " (v" << std::dec << mHdr.getFwMode() << ")" << std::endl; - std::cout << " CompatibilityType: " << nn::hac::GameCardUtil::getCompatibilityTypeAsString((nn::hac::gc::CompatibilityType)mHdr.getCompatibilityType()) << " (" << std::dec << (uint32_t) mHdr.getCompatibilityType() << ")" << std::endl; - std::cout << " Update Partition Info:" << std::endl; - std::cout << " CUP Version: " << nn::hac::ContentMetaUtil::getVersionAsString(mHdr.getUppVersion()) << " (v" << std::dec << mHdr.getUppVersion() << ")" << std::endl; - std::cout << " CUP TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getUppId() << std::endl; - std::cout << " CUP Digest: " << fnd::SimpleTextOutput::arrayToString(mHdr.getUppHash(), 8, true, ":") << std::endl; + fmt::print("[GameCard/ExtendedHeader]\n"); + fmt::print(" FwVersion: v{:d} ({:s})\n", mHdr.getFwVersion(), nn::hac::GameCardUtil::getCardFwVersionDescriptionAsString((nn::hac::gc::FwVersion)mHdr.getFwVersion())); + fmt::print(" AccCtrl1: 0x{:x}\n", mHdr.getAccCtrl1()); + fmt::print(" CardClockRate: {:s}\n", nn::hac::GameCardUtil::getCardClockRateAsString((nn::hac::gc::CardClockRate)mHdr.getAccCtrl1())); + fmt::print(" Wait1TimeRead: 0x{:x}\n", mHdr.getWait1TimeRead()); + fmt::print(" Wait2TimeRead: 0x{:x}\n", mHdr.getWait2TimeRead()); + fmt::print(" Wait1TimeWrite: 0x{:x}\n", mHdr.getWait1TimeWrite()); + fmt::print(" Wait2TimeWrite: 0x{:x}\n", mHdr.getWait2TimeWrite()); + fmt::print(" SdkAddon Version: {:s} (v{:d})\n", nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getFwMode()), mHdr.getFwMode()); + fmt::print(" CompatibilityType: {:s} ({:d})\n", nn::hac::GameCardUtil::getCompatibilityTypeAsString((nn::hac::gc::CompatibilityType)mHdr.getCompatibilityType()), mHdr.getCompatibilityType()); + fmt::print(" Update Partition Info:\n"); + fmt::print(" CUP Version: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mHdr.getUppVersion()), mHdr.getUppVersion()); + fmt::print(" CUP TitleId: 0x{:016x}\n", mHdr.getUppId()); + fmt::print(" CUP Digest: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getUppHash().data(), mHdr.getUppHash().size(), true, ":")); } } -bool nstool::GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash, bool use_salt, byte_t salt) +bool nstool::GameCardProcess::validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash, bool use_salt, byte_t salt) { - tc::ByteData scratch; - fnd::sha::sSha256Hash calc_hash; + // read region into memory + tc::ByteData scratch = tc::ByteData(tc::io::IOUtil::castInt64ToSize(len)); + mFile->seek(offset, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // update hash + tc::crypto::Sha256Generator sha256_gen; + sha256_gen.initialize(); + sha256_gen.update(scratch.data(), scratch.size()); if (use_salt) - { - scratch.alloc(len + 1); - scratch.data()[len] = salt; - } - else - { - scratch.alloc(len); - } + sha256_gen.update(&salt, sizeof(salt)); - (*mFile)->read(scratch.data(), offset, len); - fnd::sha::Sha256(scratch.data(), scratch.size(), calc_hash.bytes); + // calculate hash + nn::hac::detail::sha256_hash_t calc_hash; + sha256_gen.getHash(calc_hash.data()); - return calc_hash.compare(test_hash); + return memcmp(calc_hash.data(), test_hash, calc_hash.size()) == 0; } -bool nstool::GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash) +bool nstool::GameCardProcess::validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash) { return validateRegionOfFile(offset, len, test_hash, false, 0); } void nstool::GameCardProcess::validateXciSignature() { - fnd::rsa::sRsa2048Key header_sign_key; - - mKeyCfg.getXciHeaderSignKey(header_sign_key); - if (fnd::rsa::pkcs::rsaVerify(header_sign_key, fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrSignature) != 0) + if (mKeyCfg.xci_header_sign_key.isSet()) { - std::cout << "[WARNING] GameCard Header Signature: FAIL" << std::endl; + if (tc::crypto::VerifyRsa2048Pkcs1Sha256(mHdrSignature.data(), mHdrHash.data(), mKeyCfg.xci_header_sign_key.get()) == false) + { + fmt::print("[WARNING] GameCard Header Signature: FAIL\n"); + } + } + else + { + fmt::print("[WARNING] GameCard Header Signature: FAIL (Failed to load rsa public key.)\n"); } } void nstool::GameCardProcess::processRootPfs() { - if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().bytes, mHdr.getCompatibilityType() != nn::hac::gc::COMPAT_GLOBAL, mHdr.getCompatibilityType()) == false) + if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().data(), mHdr.getCompatibilityType() != nn::hac::gc::COMPAT_GLOBAL, mHdr.getCompatibilityType()) == false) { - std::cout << "[WARNING] GameCard Root HFS0: FAIL (bad hash)" << std::endl; + fmt::print("[WARNING] GameCard Root HFS0: FAIL (bad hash)\n"); } - mRootPfs.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize())); - mRootPfs.setListFs(mListFs); - mRootPfs.setVerifyMode(false); - mRootPfs.setCliOutputMode(mCliOutputMode); - mRootPfs.setMountPointName(kXciMountPointName); - mRootPfs.process(); -} -void nstool::GameCardProcess::processPartitionPfs() -{ - const std::vector& rootPartitions = mRootPfs.getPfsHeader().getFileList(); - for (size_t i = 0; i < rootPartitions.size(); i++) - { - // this must be validated here because only the size of the root partiton header is known at verification time - if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress() + rootPartitions[i].offset, rootPartitions[i].hash_protected_size, rootPartitions[i].hash.bytes) == false) - { - std::cout << "[WARNING] GameCard " << rootPartitions[i].name << " Partition HFS0: FAIL (bad hash)" << std::endl; - } + std::shared_ptr gc_fs_raw = std::make_shared(tc::io::SubStream(mFile, mHdr.getPartitionFsAddress(), nn::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage()+1) - mHdr.getPartitionFsAddress())); - PfsProcess tmp; - tmp.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getPartitionFsAddress() + rootPartitions[i].offset, rootPartitions[i].size)); - tmp.setListFs(mListFs); - tmp.setVerifyMode(mVerify); - tmp.setCliOutputMode(mCliOutputMode); - tmp.setMountPointName(kXciMountPointName + rootPartitions[i].name); - if (mExtractInfo.hasElement(rootPartitions[i].name)) - tmp.setExtractPath(mExtractInfo.getElement(rootPartitions[i].name).extract_path); - - tmp.process(); - } + auto gc_vfs_meta = nn::hac::GameCardFsMetaGenerator(gc_fs_raw, mHdr.getPartitionFsSize(), mVerify ? nn::hac::GameCardFsMetaGenerator::ValidationMode_Warn : nn::hac::GameCardFsMetaGenerator::ValidationMode_None); + std::shared_ptr gc_vfs = std::make_shared(tc::io::VirtualFileSystem(gc_vfs_meta) ); + + FsProcess fs_proc; + + fs_proc.setInputFileSystem(gc_vfs); + fs_proc.setFsFormatName("PartitionFS"); + fs_proc.setFsProperties({ + fmt::format("Type: Nested HFS0"), + fmt::format("DirNum: {:d}", gc_vfs_meta.dir_entries.empty() ? 0 : gc_vfs_meta.dir_entries.size() - 1), // -1 to not include root directory + fmt::format("FileNum: {:d}", gc_vfs_meta.file_entries.size()) + }); + fs_proc.setShowFsInfo(mCliOutputMode.show_basic_info); + fs_proc.setShowFsTree(mListFs); + fs_proc.setFsRootLabel(kXciMountPointName); + fs_proc.setExtractJobs(mExtractJobs); + + fs_proc.process(); } \ No newline at end of file diff --git a/src/GameCardProcess.h b/src/GameCardProcess.h index 3f294a8..0be04a8 100644 --- a/src/GameCardProcess.h +++ b/src/GameCardProcess.h @@ -12,65 +12,46 @@ class GameCardProcess public: GameCardProcess(); - void process(); - // generic void setInputFile(const std::shared_ptr& file); void setKeyCfg(const KeyBag& keycfg); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - // xci specific - void setPartitionForExtract(const std::string& partition_name, const tc::io::Path& extract_path); - void setListFs(bool list_fs); + // fs specific + void setShowFsTree(bool show_fs_tree); + void setExtractJobs(const std::vector extract_jobs); + + void process(); private: - const std::string kModuleName = "GameCardProcess"; - const std::string kXciMountPointName = "gamecard:/"; + const std::string kXciMountPointName = "gamecard"; + + std::string mModuleName; std::shared_ptr mFile; KeyBag mKeyCfg; CliOutputMode mCliOutputMode; bool mVerify; bool mListFs; - - struct sExtractInfo - { - std::string partition_name; - tc::io::Path extract_path; - - void operator=(const sExtractInfo& other) - { - partition_name = other.partition_name; - extract_path = other.extract_path; - } - - bool operator==(const tc::io::Path& name) const - { - return name == partition_name; - } - }; - - bool mIsTrueSdkXci; bool mIsSdkXciEncrypted; size_t mGcHeaderOffset; bool mProccessExtendedHeader; nn::hac::detail::rsa2048_signature_t mHdrSignature; - fnd::sha::sSha256Hash mHdrHash; + nn::hac::detail::sha256_hash_t mHdrHash; nn::hac::GameCardHeader mHdr; PfsProcess mRootPfs; - std::vector mExtractInfo; + std::vector mExtractJobs; void importHeader(); void displayHeader(); - bool validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash, bool use_salt, byte_t salt); - bool validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash); + bool validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash, bool use_salt, byte_t salt); + bool validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash); void validateXciSignature(); void processRootPfs(); - void processPartitionPfs(); }; } \ No newline at end of file diff --git a/src/PfsProcess.cpp b/src/PfsProcess.cpp index 4dd59e9..b32606a 100644 --- a/src/PfsProcess.cpp +++ b/src/PfsProcess.cpp @@ -17,19 +17,7 @@ nstool::PfsProcess::PfsProcess() : mFileSystem(), mFsProcess() { - mFsProcess.setFsLabel("PartitionFS"); -} - -void nstool::PfsProcess::process() -{ - importHeader(); - - if (mCliOutputMode.show_basic_info) - { - displayHeader(); - } - - mFsProcess.process(); + mFsProcess.setFsFormatName("PartitionFS"); } void nstool::PfsProcess::setInputFile(const std::shared_ptr& file) @@ -40,6 +28,7 @@ void nstool::PfsProcess::setInputFile(const std::shared_ptr& fi void nstool::PfsProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; + mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info); } void nstool::PfsProcess::setVerifyMode(bool verify) @@ -47,32 +36,22 @@ void nstool::PfsProcess::setVerifyMode(bool verify) mVerify = verify; } -void nstool::PfsProcess::setMountPointName(const std::string& mount_name) +void nstool::PfsProcess::setShowFsTree(bool show_fs_tree) { - mFsProcess.setFsLabel(mount_name); + mFsProcess.setShowFsTree(show_fs_tree); } -void nstool::PfsProcess::setExtractPath(const tc::io::Path& path) +void nstool::PfsProcess::setFsRootLabel(const std::string& root_label) { - mFsProcess.setExtractPath(path); + mFsProcess.setFsRootLabel(root_label); } -void nstool::PfsProcess::setListFs(bool list_fs) +void nstool::PfsProcess::setExtractJobs(const std::vector& extract_jobs) { - mFsProcess.setCliOutputMode(list_fs); + mFsProcess.setExtractJobs(extract_jobs); } -const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const -{ - return mPfs; -} - -const std::shared_ptr& nstool::PfsProcess::getFileSystem() const -{ - return mFileSystem; -} - -void nstool::PfsProcess::importHeader() +void nstool::PfsProcess::process() { if (mFile == nullptr) { @@ -116,13 +95,24 @@ void nstool::PfsProcess::importHeader() // create virtual filesystem mFileSystem = std::make_shared(tc::io::VirtualFileSystem(nn::hac::PartitionFsMetaGenerator(mFile, mVerify ? nn::hac::PartitionFsMetaGenerator::ValidationMode_Warn : nn::hac::PartitionFsMetaGenerator::ValidationMode_None))); mFsProcess.setInputFileSystem(mFileSystem); + + // set properties for FsProcess + mFsProcess.setFsProperties({ + fmt::format("Type: {:s}", nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType())), + fmt::format("FileNum: {:d}", mPfs.getFileList().size()) + }); + + mFsProcess.process(); } -void nstool::PfsProcess::displayHeader() +const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const { - fmt::print("[PartitionFS]\n"); - fmt::print(" Type: {:s}\n", nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType())); - fmt::print(" FileNum: {:d}\n", mPfs.getFileList().size()); + return mPfs; +} + +const std::shared_ptr& nstool::PfsProcess::getFileSystem() const +{ + return mFileSystem; } size_t nstool::PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr) diff --git a/src/PfsProcess.h b/src/PfsProcess.h index 9c13efd..00b205c 100644 --- a/src/PfsProcess.h +++ b/src/PfsProcess.h @@ -11,18 +11,19 @@ class PfsProcess public: PfsProcess(); - void process(); - // generic void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - // pfs specific - void setMountPointName(const std::string& mount_name); - void setExtractPath(const tc::io::Path& path); - void setListFs(bool list_fs); + // fs specific + void setShowFsTree(bool show_fs_tree); + void setFsRootLabel(const std::string& root_label); + void setExtractJobs(const std::vector& extract_jobs); + void process(); + + // post process() get PFS/FS out const nn::hac::PartitionFsHeader& getPfsHeader() const; const std::shared_ptr& getFileSystem() const; @@ -39,9 +40,7 @@ private: std::shared_ptr mFileSystem; FsProcess mFsProcess; - - void importHeader(); - void displayHeader(); + size_t determineHeaderSize(const nn::hac::sPfsHeader* hdr); bool validateHeaderMagic(const nn::hac::sPfsHeader* hdr); }; diff --git a/src/RomfsProcess.cpp b/src/RomfsProcess.cpp index ca1bf6c..23e737f 100644 --- a/src/RomfsProcess.cpp +++ b/src/RomfsProcess.cpp @@ -15,19 +15,7 @@ nstool::RomfsProcess::RomfsProcess() : mFileSystem(), mFsProcess() { - mFsProcess.setFsLabel("RomFS"); -} - -void nstool::RomfsProcess::process() -{ - importHeader(); - - if (mCliOutputMode.show_basic_info) - { - displayHeader(); - } - - mFsProcess.process(); + mFsProcess.setFsFormatName("RomFS"); } void nstool::RomfsProcess::setInputFile(const std::shared_ptr& file) @@ -38,6 +26,7 @@ void nstool::RomfsProcess::setInputFile(const std::shared_ptr& void nstool::RomfsProcess::setCliOutputMode(CliOutputMode type) { mCliOutputMode = type; + mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info); } void nstool::RomfsProcess::setVerifyMode(bool verify) @@ -45,27 +34,22 @@ void nstool::RomfsProcess::setVerifyMode(bool verify) mVerify = verify; } -void nstool::RomfsProcess::setMountPointName(const std::string& mount_name) +void nstool::RomfsProcess::setFsRootLabel(const std::string& root_label) { - mFsProcess.setFsLabel(mount_name); + mFsProcess.setFsRootLabel(root_label); } -void nstool::RomfsProcess::setExtractPath(const tc::io::Path& path) +void nstool::RomfsProcess::setExtractJobs(const std::vector& extract_jobs) { - mFsProcess.setExtractPath(path); + mFsProcess.setExtractJobs(extract_jobs); } -void nstool::RomfsProcess::setListFs(bool list_fs) +void nstool::RomfsProcess::setShowFsTree(bool list_fs) { - mFsProcess.setCliOutputMode(list_fs); + mFsProcess.setShowFsTree(list_fs); } -const std::shared_ptr& nstool::RomfsProcess::getFileSystem() const -{ - return mFileSystem; -} - -void nstool::RomfsProcess::importHeader() +void nstool::RomfsProcess::process() { if (mFile == nullptr) { @@ -132,11 +116,13 @@ void nstool::RomfsProcess::importHeader() // create virtual filesystem mFileSystem = std::make_shared(tc::io::VirtualFileSystem(nn::hac::RomFsMetaGenerator(mFile))); mFsProcess.setInputFileSystem(mFileSystem); -} -void nstool::RomfsProcess::displayHeader() -{ - fmt::print("[RomFS]\n"); - fmt::print(" DirNum: {:d}\n", mDirNum); - fmt::print(" FileNum: {:d}\n", mFileNum); + // set properties for FsProcess + mFsProcess.setFsProperties({ + fmt::format("DirNum: {:d}", mDirNum), + fmt::format("FileNum: {:d}", mFileNum) + }); + + // process filesystem + mFsProcess.process(); } \ No newline at end of file diff --git a/src/RomfsProcess.h b/src/RomfsProcess.h index c7609a7..6f2304c 100644 --- a/src/RomfsProcess.h +++ b/src/RomfsProcess.h @@ -11,20 +11,17 @@ class RomfsProcess public: RomfsProcess(); - void process(); - // generic void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - // pfs specific - void setMountPointName(const std::string& mount_name); - void setExtractPath(const tc::io::Path& path); - void setListFs(bool list_fs); - - const std::shared_ptr& getFileSystem() const; + // fs specific + void setFsRootLabel(const std::string& root_label); + void setExtractJobs(const std::vector& extract_jobs); + void setShowFsTree(bool show_fs_tree); + void process(); private: static const size_t kCacheSize = 0x10000; @@ -40,9 +37,6 @@ private: std::shared_ptr mFileSystem; FsProcess mFsProcess; - - void importHeader(); - void displayHeader(); }; } \ No newline at end of file diff --git a/src/Settings.cpp b/src/Settings.cpp index 51dd188..8e47291 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -350,6 +350,68 @@ private: std::vector mOptStrings; }; +class ExtractDataPathOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + ExtractDataPathOptionHandler(std::vector& jobs, const std::vector& opts) : + mJobs(jobs), + mOptStrings(opts) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() == 1) + { + mJobs.push_back({tc::io::Path("/"), tc::io::Path(params[0])}); + } + else if (params.size() == 2) + { + mJobs.push_back({tc::io::Path(params[0]), tc::io::Path(params[1])}); + } + else + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires parameters in the format \"[] \".", option)); + } + } +private: + std::vector& mJobs; + std::vector mOptStrings; +}; + +class CustomExtractDataPathOptionHandler : public tc::cli::OptionParser::IOptionHandler +{ +public: + CustomExtractDataPathOptionHandler(std::vector& jobs, const std::vector& opts, const tc::io::Path& custom_path) : + mJobs(jobs), + mOptStrings(opts), + mCustomPath(custom_path) + {} + + const std::vector& getOptionStrings() const + { + return mOptStrings; + } + + void processOption(const std::string& option, const std::vector& params) + { + if (params.size() != 1) + { + throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires a parameter.", option)); + } + + mJobs.push_back({mCustomPath, tc::io::Path(params[0])}); + } +private: + std::vector& mJobs; + std::vector mOptStrings; + tc::io::Path mCustomPath; +}; + nstool::SettingsInitializer::SettingsInitializer(const std::vector& args) : Settings(), mModuleLabel("nstool::SettingsInitializer"), @@ -484,19 +546,19 @@ void nstool::SettingsInitializer::parse_args(const std::vector& arg // fs options opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(fs.show_fs_tree, { "--listfs" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(fs.extract_path, { "--fsdir" }))); + opts.registerOptionHandler(std::shared_ptr(new ExtractDataPathOptionHandler(fs.extract_jobs, { "--fsdir", "-x", "--extract" }))); // xci options - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.update_extract_path, { "--update" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.normal_extract_path, { "--normal" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.secure_extract_path, { "--secure" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(xci.logo_extract_path, { "--logo" }))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--update" }, tc::io::Path("/update/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--normal" }, tc::io::Path("/normal/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--secure" }, tc::io::Path("/secure/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--logo" }, tc::io::Path("/logo/")))); // nca options - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part0_extract_path, { "--part0" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part1_extract_path, { "--part1" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part2_extract_path, { "--part2" }))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.part3_extract_path, { "--part3" }))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part0" }, tc::io::Path("/0/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part1" }, tc::io::Path("/1/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part2" }, tc::io::Path("/2/")))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part3" }, tc::io::Path("/3/")))); // kip options opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(kip.extract_path, { "--kipdir" }))); @@ -552,7 +614,7 @@ void nstool::SettingsInitializer::determine_filetype() // detect ROMFS else if (_ASSERT_FILE_SIZE(sizeof(nn::hac::sRomfsHeader)) && _TYPE_PTR(nn::hac::sRomfsHeader)->header_size.unwrap() == sizeof(nn::hac::sRomfsHeader) - && _TYPE_PTR(nn::hac::sRomfsHeader)->sections[1].offset.unwrap() == (_TYPE_PTR(nn::hac::sRomfsHeader)->sections[0].offset.unwrap() + _TYPE_PTR(nn::hac::sRomfsHeader)->sections[0].size.unwrap())) + && _TYPE_PTR(nn::hac::sRomfsHeader)->dir_entry.offset.unwrap() == (_TYPE_PTR(nn::hac::sRomfsHeader)->dir_hash_bucket.offset.unwrap() + _TYPE_PTR(nn::hac::sRomfsHeader)->dir_hash_bucket.size.unwrap())) { infile.filetype = FILE_TYPE_ROMFS; } diff --git a/src/Settings.h b/src/Settings.h index c0a1a27..0be0384 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -57,7 +57,7 @@ struct Settings struct FsOptions { bool show_fs_tree; - tc::Optional extract_path; + std::vector extract_jobs; } fs; // XCI options @@ -105,18 +105,8 @@ struct Settings code.list_symbols = false; code.is_64bit_instruction = true; - xci.update_extract_path = tc::Optional(); - xci.logo_extract_path = tc::Optional(); - xci.normal_extract_path = tc::Optional(); - xci.secure_extract_path = tc::Optional(); - fs.show_fs_tree = false; - fs.extract_path = tc::Optional(); - - nca.part0_extract_path = tc::Optional(); - nca.part1_extract_path = tc::Optional(); - nca.part2_extract_path = tc::Optional(); - nca.part3_extract_path = tc::Optional(); + fs.extract_jobs = std::vector(); kip.extract_path = tc::Optional(); diff --git a/src/main.cpp b/src/main.cpp index b697ad8..8036edd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ #include "Settings.h" -//#include "GameCardProcess.h" +#include "GameCardProcess.h" #include "PfsProcess.h" #include "RomfsProcess.h" //#include "NcaProcess.h" @@ -27,7 +27,6 @@ int umain(const std::vector& args, const std::vector& std::shared_ptr infile_stream = std::make_shared(tc::io::FileStream(set.infile.path.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read)); - /* if (set.infile.filetype == nstool::Settings::FILE_TYPE_GAMECARD) { nstool::GameCardProcess obj; @@ -38,29 +37,22 @@ int umain(const std::vector& args, const std::vector& obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (set.xci.update_extract_path.isSet()) - obj.setPartitionForExtract(nn::hac::gc::kUpdatePartitionStr, set.xci.update_extract_path.get()); - if (set.xci.logo_extract_path.isSet()) - obj.setPartitionForExtract(nn::hac::gc::kLogoPartitionStr, set.xci.logo_extract_path.get()); - if (set.xci.normal_extract_path.isSet()) - obj.setPartitionForExtract(nn::hac::gc::kNormalPartitionStr, set.xci.normal_extract_path.get()); - if (set.xci.secure_extract_path.isSet()) - obj.setPartitionForExtract(nn::hac::gc::kSecurePartitionStr, set.xci.secure_extract_path.get()); - obj.setListFs(set.fs.show_fs_tree); - + obj.setShowFsTree(set.fs.show_fs_tree); + obj.setExtractJobs(set.fs.extract_jobs); + obj.process(); } - else*/ if (set.infile.filetype == nstool::Settings::FILE_TYPE_PARTITIONFS || set.infile.filetype == nstool::Settings::FILE_TYPE_NSP) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PARTITIONFS || set.infile.filetype == nstool::Settings::FILE_TYPE_NSP) { nstool::PfsProcess obj; obj.setInputFile(infile_stream); + obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (set.fs.extract_path.isSet()) - obj.setExtractPath(set.fs.extract_path.get()); - obj.setListFs(set.fs.show_fs_tree); + obj.setShowFsTree(set.fs.show_fs_tree); + obj.setExtractJobs(set.fs.extract_jobs); obj.process(); } @@ -73,9 +65,8 @@ int umain(const std::vector& args, const std::vector& obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - if (set.fs.extract_path.isSet()) - obj.setExtractPath(set.fs.extract_path.get()); - obj.setListFs(set.fs.show_fs_tree); + obj.setShowFsTree(set.fs.show_fs_tree); + obj.setExtractJobs(set.fs.extract_jobs); obj.process(); } @@ -234,9 +225,8 @@ int umain(const std::vector& args, const std::vector& if (set.aset.nacp_extract_path.isSet()) obj.setNacpExtractPath(set.aset.nacp_extract_path.get()); - if (set.fs.extract_path.isSet()) - obj.setRomfsExtractPath(set.fs.extract_path.get()); - obj.setListFs(set.fs.show_fs_tree); + obj.setRomfsShowFsTree(set.fs.show_fs_tree); + obj.setRomfsExtractJobs(set.fs.extract_jobs); obj.process(); } From 43883c1a174e2fcc535954836847a94bad4f1ce6 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 8 Oct 2021 18:52:57 +0800 Subject: [PATCH 044/119] Update VisualStudio project files and silence warnings. --- build/visualstudio/nstool/nstool.vcxproj | 10 +-- .../nstool/nstool.vcxproj.filters | 76 ++++++++----------- deps/libnintendo-hac | 2 +- deps/libtoolchain | 2 +- src/FsProcess.cpp | 8 +- src/KeyBag.cpp | 66 ++++++++-------- 6 files changed, 74 insertions(+), 90 deletions(-) diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index 05e9c1e..7eeb132 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -154,8 +154,10 @@ + + @@ -181,18 +183,12 @@ + - - - - - - - diff --git a/build/visualstudio/nstool/nstool.vcxproj.filters b/build/visualstudio/nstool/nstool.vcxproj.filters index 4aa666c..7358652 100644 --- a/build/visualstudio/nstool/nstool.vcxproj.filters +++ b/build/visualstudio/nstool/nstool.vcxproj.filters @@ -15,39 +15,33 @@ - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files Header Files + + Header Files + Header Files Header Files + + Header Files + Header Files Header Files + + Header Files + Header Files @@ -84,20 +78,20 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files @@ -110,36 +104,24 @@ Source Files + + Source Files + Source Files - + Source Files - - Source Files - - + Source Files Source Files - - Source Files - - - Source Files - - - Source Files - Source Files - - Source Files - Source Files @@ -152,5 +134,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index e2fb1f3..6a6f57c 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit e2fb1f3ad788533631d8855d484d14bec33ac961 +Subproject commit 6a6f57c66b56daeebc1631e639ea88e474965cb7 diff --git a/deps/libtoolchain b/deps/libtoolchain index 7948f58..3d15a0e 160000 --- a/deps/libtoolchain +++ b/deps/libtoolchain @@ -1 +1 @@ -Subproject commit 7948f581d32ebbfdf5a872b96c37938690f665e7 +Subproject commit 3d15a0eb1eff3c36fb4ac8a64196627e46e105ab diff --git a/src/FsProcess.cpp b/src/FsProcess.cpp index 78e2aee..d148336 100644 --- a/src/FsProcess.cpp +++ b/src/FsProcess.cpp @@ -137,7 +137,7 @@ void nstool::FsProcess::extractFs() continue; - } catch (tc::io::DirectoryNotFoundException& e) { + } catch (tc::io::DirectoryNotFoundException&) { // acceptable exception, just means directory didn't exist } @@ -168,7 +168,7 @@ void nstool::FsProcess::extractFs() writeStreamToFile(file_stream, itr->extract_path, mDataCache); continue; - } catch (tc::io::DirectoryNotFoundException& e) { + } catch (tc::io::DirectoryNotFoundException&) { // acceptable exception, just means the parent directory didn't exist } @@ -178,7 +178,7 @@ void nstool::FsProcess::extractFs() tc::io::PathUtil::pathToUnixUTF8(itr->extract_path, literal_extract_path_str); fmt::print("[WARNING] Extract path was invalid, and was skipped: {:s}\n", literal_extract_path_str); continue; - } catch (tc::io::FileNotFoundException& e) { + } catch (tc::io::FileNotFoundException&) { // acceptable exception, just means file didn't exist } @@ -195,7 +195,7 @@ void nstool::FsProcess::extractFs() // directory extract successful, continue to next job continue; - } catch (tc::io::DirectoryNotFoundException& e) { + } catch (tc::io::DirectoryNotFoundException&) { // acceptable exception, just means directory didn't exist } diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 684ddf1..3938e5a 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -153,7 +153,7 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa const std::string kPrivateStr = "private"; std::vector kNcaKeyAreaKeyIndexStr = { "application", "ocean", "system" }; - static const size_t kMasterKeyMax = 0x20; + static const size_t kKeyGenerationNum = 0x100; /**/ // import key data @@ -162,11 +162,11 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa /* internal key sources */ if (name_idx < kMasterBase.size()) { - for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + for (size_t keygen_rev = 0; keygen_rev < kKeyGenerationNum; keygen_rev++) { // std::map master_key; - //fmt::print("{:s}_key_{:02x}\n", kMasterBase[name_idx], mkey_rev); - _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kMasterBase[name_idx], kKeyStr, mkey_rev), master_key[mkey_rev]); + //fmt::print("{:s}_key_{:02x}\n", kMasterBase[name_idx], keygen_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kMasterBase[name_idx], kKeyStr, keygen_rev), master_key[(byte_t)keygen_rev]); } } @@ -227,10 +227,10 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa // package1_key_xx if (name_idx < kPkg1Base.size()) { - for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + for (size_t keygen_rev = 0; keygen_rev < kKeyGenerationNum; keygen_rev++) { - //fmt::print("{:s}_key_{:02x}\n", kPkg1Base[name_idx], mkey_rev); - _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kPkg1Base[name_idx], kKeyStr, mkey_rev), pkg1_key[mkey_rev]); + //fmt::print("{:s}_key_{:02x}\n", kPkg1Base[name_idx], keygen_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kPkg1Base[name_idx], kKeyStr, keygen_rev), pkg1_key[(byte_t)keygen_rev]); } } @@ -238,10 +238,10 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa if (name_idx < kPkg2Base.size()) { // package2_key_xx - for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + for (size_t keygen_rev = 0; keygen_rev < kKeyGenerationNum; keygen_rev++) { - //fmt::print("{:s}_key_{:02x}\n", kPkg2Base[name_idx], mkey_rev); - _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kPkg2Base[name_idx], kKeyStr, mkey_rev), pkg2_key[mkey_rev]); + //fmt::print("{:s}_key_{:02x}\n", kPkg2Base[name_idx], keygen_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kPkg2Base[name_idx], kKeyStr, keygen_rev), pkg2_key[(byte_t)keygen_rev]); } // package2_sign_key @@ -254,10 +254,10 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa // ticket common key if (name_idx < kTicketCommonKeyBase.size()) { - for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + for (size_t keygen_rev = 0; keygen_rev < kKeyGenerationNum; keygen_rev++) { - //fmt::print("{:s}_{:02x}\n", kTicketCommonKeyBase[name_idx], mkey_rev); - _SAVE_AES128KEY(fmt::format("{:s}_{:02x}", kTicketCommonKeyBase[name_idx], mkey_rev), etik_common_key[mkey_rev]); + //fmt::print("{:s}_{:02x}\n", kTicketCommonKeyBase[name_idx], keygen_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:02x}", kTicketCommonKeyBase[name_idx], keygen_rev), etik_common_key[(byte_t)keygen_rev]); } } @@ -269,11 +269,11 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa //_SAVE_AES128XTSKEY(fmt::format("{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kKeyStr), nca_header_key); // nca header sign0 key (generations) - for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + for (size_t keygen_rev = 0; keygen_rev < kKeyGenerationNum; keygen_rev++) { - //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev, kPrivateStr); - //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev, kModulusStr); - _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev), nca_header_sign0_key[mkey_rev], 2048); + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, keygen_rev, kPrivateStr); + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, keygen_rev, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kContentArchiveHeaderBase[name_idx], kSignKey, keygen_rev), nca_header_sign0_key[(byte_t)keygen_rev], 2048); } // nca header sign0 key (generation 0) //fmt::print("{:s}_{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, kPrivateStr); @@ -287,24 +287,24 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa // nca key area encryption keys if (name_idx < kNcaKeyAreaEncKeyBase.size()) { - for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + for (size_t keygen_rev = 0; keygen_rev < kKeyGenerationNum; keygen_rev++) { for (size_t keak_idx = 0; keak_idx < kNcaKeyAreaKeyIndexStr.size(); keak_idx++) { - //fmt::print("{:s}_{:s}_{:02x}\n", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev); - _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev), nca_key_area_encryption_key[keak_idx][mkey_rev]); + //fmt::print("{:s}_{:s}_{:02x}\n", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], keygen_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], keygen_rev), nca_key_area_encryption_key[keak_idx][(byte_t)keygen_rev]); } } } // nca key area "hw" encryption keys if (name_idx < kNcaKeyAreaEncKeyHwBase.size()) { - for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + for (size_t keygen_rev = 0; keygen_rev < kKeyGenerationNum; keygen_rev++) { for (size_t keak_idx = 0; keak_idx < kNcaKeyAreaKeyIndexStr.size(); keak_idx++) { - //fmt::print("{:s}_{:s}_{:02x}\n", kNcaKeyAreaEncKeyHwBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev); - _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kNcaKeyAreaEncKeyHwBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev), nca_key_area_encryption_key_hw[keak_idx][mkey_rev]); + //fmt::print("{:s}_{:s}_{:02x}\n", kNcaKeyAreaEncKeyHwBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], keygen_rev); + _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kNcaKeyAreaEncKeyHwBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], keygen_rev), nca_key_area_encryption_key_hw[keak_idx][(byte_t)keygen_rev]); } } } @@ -313,11 +313,11 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa if (name_idx < kAcidBase.size()) { // acid sign key (generations) - for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + for (size_t keygen_rev = 0; keygen_rev < kKeyGenerationNum; keygen_rev++) { - //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kAcidBase[name_idx], kSignKey, mkey_rev, kPrivateStr); - //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kAcidBase[name_idx], kSignKey, mkey_rev, kModulusStr); - _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kAcidBase[name_idx], kSignKey, mkey_rev), acid_sign_key[mkey_rev], 2048); + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kAcidBase[name_idx], kSignKey, keygen_rev, kPrivateStr); + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kAcidBase[name_idx], kSignKey, keygen_rev, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kAcidBase[name_idx], kSignKey, keygen_rev), acid_sign_key[(byte_t)keygen_rev], 2048); } // acid sign key (generation 0) //fmt::print("{:s}_{:s}_{:s}\n", kAcidBase[name_idx], kSignKey, kPrivateStr); @@ -329,11 +329,11 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa if (name_idx < kNrrCertBase.size()) { // nrr certificate sign key (generations) - for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++) + for (size_t keygen_rev = 0; keygen_rev < kKeyGenerationNum; keygen_rev++) { - //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kNrrCertBase[name_idx], kSignKey, mkey_rev, kPrivateStr); - //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kNrrCertBase[name_idx], kSignKey, mkey_rev, kModulusStr); - _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kNrrCertBase[name_idx], kSignKey, mkey_rev), nrr_certificate_sign_key[mkey_rev], 2048); + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kNrrCertBase[name_idx], kSignKey, keygen_rev, kPrivateStr); + //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kNrrCertBase[name_idx], kSignKey, keygen_rev, kModulusStr); + _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kNrrCertBase[name_idx], kSignKey, keygen_rev), nrr_certificate_sign_key[(byte_t)keygen_rev], 2048); } // nrr certificate sign key (generation 0) //fmt::print("{:s}_{:s}_{:s}\n", kNrrCertBase[name_idx], kSignKey, kPrivateStr); @@ -345,7 +345,7 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa if (name_idx < kXciHeaderBase.size()) { // xci header key (based on index) - for (size_t kek_index = 0; kek_index < 8; kek_index++) + for (byte_t kek_index = 0; kek_index < 8; kek_index++) { //fmt::print("{:s}_{:s}_{:02x}\n", kXciHeaderBase[name_idx], kKeyStr, kek_index); _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kXciHeaderBase[name_idx], kKeyStr, kek_index), xci_header_key[kek_index]); @@ -364,7 +364,7 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa if (name_idx < kXciInitialDataBase.size()) { // xci initial data key (based on index) - for (size_t kek_index = 0; kek_index < 8; kek_index++) + for (byte_t kek_index = 0; kek_index < 8; kek_index++) { //fmt::print("{:s}_{:s}_{:02x}\n", kXciInitialDataBase[name_idx], kKekStr, kek_index); _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kXciInitialDataBase[name_idx], kKekStr, kek_index), xci_initial_data_kek[kek_index]); From d2f1bed0e805d729da1f9c6264f9a54c29ad465c Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 10 Oct 2021 09:35:04 +0800 Subject: [PATCH 045/119] Updated libnintendo-hac --- deps/libnintendo-hac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 6a6f57c..0f31c13 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 6a6f57c66b56daeebc1631e639ea88e474965cb7 +Subproject commit 0f31c13719e40712d96c301dc403adefec37fb6a From e623fa1b395b637867b054d5133ede0bcea04c75 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 10 Oct 2021 17:20:43 +0800 Subject: [PATCH 046/119] Update dependencies. --- deps/libnintendo-hac | 2 +- deps/libtoolchain | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 0f31c13..c3fcfdb 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 0f31c13719e40712d96c301dc403adefec37fb6a +Subproject commit c3fcfdb3711ded6e4b69b352e039d50b0be54355 diff --git a/deps/libtoolchain b/deps/libtoolchain index 3d15a0e..20321bf 160000 --- a/deps/libtoolchain +++ b/deps/libtoolchain @@ -1 +1 @@ -Subproject commit 3d15a0eb1eff3c36fb4ac8a64196627e46e105ab +Subproject commit 20321bf5033fe97b8f1a0e0727105d0798f60165 From 2d5f95fbcf0e7fcf08dff89f2f0c4634e933da19 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 10 Oct 2021 17:22:55 +0800 Subject: [PATCH 047/119] Port PkiCertProcess to libtoolchain. --- build/visualstudio/nstool/nstool.vcxproj | 1 + .../nstool/nstool.vcxproj.filters | 3 + src/PkiCertProcess.cpp | 228 ++++++++++++++++++ src/PkiCertProcess.h | 10 +- src/main.cpp | 4 +- src/not_ported/PkiCertProcess.cpp | 193 --------------- 6 files changed, 238 insertions(+), 201 deletions(-) create mode 100644 src/PkiCertProcess.cpp delete mode 100644 src/not_ported/PkiCertProcess.cpp diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index 7eeb132..f452499 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -189,6 +189,7 @@ + diff --git a/build/visualstudio/nstool/nstool.vcxproj.filters b/build/visualstudio/nstool/nstool.vcxproj.filters index 7358652..69e6c9f 100644 --- a/build/visualstudio/nstool/nstool.vcxproj.filters +++ b/build/visualstudio/nstool/nstool.vcxproj.filters @@ -140,5 +140,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/src/PkiCertProcess.cpp b/src/PkiCertProcess.cpp new file mode 100644 index 0000000..6ddb2b1 --- /dev/null +++ b/src/PkiCertProcess.cpp @@ -0,0 +1,228 @@ +#include "PkiCertProcess.h" +#include "PkiValidator.h" +#include "util.h" + +#include + +nstool::PkiCertProcess::PkiCertProcess() : + mModuleName("nstool::PkiCertProcess"), + mFile(), + mCliOutputMode(true, false, false, false), + mVerify(false) +{ +} + +void nstool::PkiCertProcess::process() +{ + importCerts(); + + if (mVerify) + validateCerts(); + + if (mCliOutputMode.show_basic_info) + displayCerts(); +} + +void nstool::PkiCertProcess::setInputFile(const std::shared_ptr& file) +{ + mFile = file; +} + +void nstool::PkiCertProcess::setKeyCfg(const KeyBag& keycfg) +{ + mKeyCfg = keycfg; +} + +void nstool::PkiCertProcess::setCliOutputMode(CliOutputMode mode) +{ + mCliOutputMode = mode; +} + +void nstool::PkiCertProcess::setVerifyMode(bool verify) +{ + mVerify = verify; +} + +void nstool::PkiCertProcess::importCerts() +{ + if (mFile == nullptr) + { + throw tc::Exception(mModuleName, "No file reader set."); + } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); + } + + // check if file_size is greater than 20MB, don't import. + size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + if (file_size > (0x100000 * 20)) + { + throw tc::Exception(mModuleName, "File too large."); + } + + // import certs + tc::ByteData scratch = tc::ByteData(file_size); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + nn::pki::SignedData cert; + for (size_t f_pos = 0; f_pos < scratch.size(); f_pos += cert.getBytes().size()) + { + cert.fromBytes(scratch.data() + f_pos, scratch.size() - f_pos); + mCert.push_back(cert); + } +} + +void nstool::PkiCertProcess::validateCerts() +{ + PkiValidator pki; + + try + { + pki.setKeyCfg(mKeyCfg); + pki.addCertificates(mCert); + } + catch (const tc::Exception& e) + { + fmt::print("[WARNING] {}\n", e.error()); + return; + } +} + +void nstool::PkiCertProcess::displayCerts() +{ + for (size_t i = 0; i < mCert.size(); i++) + { + displayCert(mCert[i]); + } +} + +void nstool::PkiCertProcess::displayCert(const nn::pki::SignedData& cert) +{ + fmt::print("[NNPKI Certificate]\n"); + + fmt::print(" SignType {:s}", getSignTypeStr(cert.getSignature().getSignType())); + if (mCliOutputMode.show_extended_info) + fmt::print(" (0x{:x}) ({:s})", cert.getSignature().getSignType(), getEndiannessStr(cert.getSignature().isLittleEndian())); + fmt::print("\n"); + + fmt::print(" Issuer: {:s}\n", cert.getBody().getIssuer()); + fmt::print(" Subject: {:s}\n", cert.getBody().getSubject()); + fmt::print(" PublicKeyType: {:s}", getPublicKeyTypeStr(cert.getBody().getPublicKeyType())); + if (mCliOutputMode.show_extended_info) + fmt::print(" ({:d})", cert.getBody().getPublicKeyType()); + fmt::print("\n"); + fmt::print(" CertID: 0x{:x}\n", cert.getBody().getCertId()); + + if (cert.getBody().getPublicKeyType() == nn::pki::cert::RSA4096) + { + fmt::print(" PublicKey:\n"); + if (mCliOutputMode.show_extended_info) + { + fmt::print(" Modulus:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getRsa4096PublicKey().n.data(), cert.getBody().getRsa4096PublicKey().n.size(), true, "", 0x10, 6, false)); + fmt::print(" Public Exponent:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getRsa4096PublicKey().e.data(), cert.getBody().getRsa4096PublicKey().e.size(), true, "", 0x10, 6, false)); + } + else + { + fmt::print(" Modulus:\n"); + fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getRsa4096PublicKey().n.data(), cert.getBody().getRsa4096PublicKey().n.size())); + fmt::print(" Public Exponent:\n"); + fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getRsa4096PublicKey().e.data(), cert.getBody().getRsa4096PublicKey().e.size())); + } + } + else if (cert.getBody().getPublicKeyType() == nn::pki::cert::RSA2048) + { + fmt::print(" PublicKey:\n"); + if (mCliOutputMode.show_extended_info) + { + fmt::print(" Modulus:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getRsa2048PublicKey().n.data(), cert.getBody().getRsa2048PublicKey().n.size(), true, "", 0x10, 6, false)); + fmt::print(" Public Exponent:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getRsa2048PublicKey().e.data(), cert.getBody().getRsa2048PublicKey().e.size(), true, "", 0x10, 6, false)); + } + else + { + fmt::print(" Modulus:\n"); + fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getRsa2048PublicKey().n.data(), cert.getBody().getRsa2048PublicKey().n.size())); + fmt::print(" Public Exponent:\n"); + fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getRsa2048PublicKey().e.data(), cert.getBody().getRsa2048PublicKey().e.size())); + } + } + else if (cert.getBody().getPublicKeyType() == nn::pki::cert::ECDSA240) + { + fmt::print(" PublicKey:\n"); + if (mCliOutputMode.show_extended_info) + { + fmt::print(" Modulus:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getEcdsa240PublicKey().r.data(), cert.getBody().getEcdsa240PublicKey().r.size(), true, "", 0x10, 6, false)); + fmt::print(" Public Exponent:\n"); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getEcdsa240PublicKey().s.data(), cert.getBody().getEcdsa240PublicKey().s.size(), true, "", 0x10, 6, false)); + } + else + { + fmt::print(" Modulus:\n"); + fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getEcdsa240PublicKey().r.data(), cert.getBody().getEcdsa240PublicKey().r.size())); + fmt::print(" Public Exponent:\n"); + fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getEcdsa240PublicKey().s.data(), cert.getBody().getEcdsa240PublicKey().s.size())); + } + } +} + +std::string nstool::PkiCertProcess::getSignTypeStr(nn::pki::sign::SignatureId type) const +{ + std::string str; + switch (type) + { + case (nn::pki::sign::SIGN_ID_RSA4096_SHA1): + str = "RSA4096-SHA1"; + break; + case (nn::pki::sign::SIGN_ID_RSA2048_SHA1): + str = "RSA2048-SHA1"; + break; + case (nn::pki::sign::SIGN_ID_ECDSA240_SHA1): + str = "ECDSA240-SHA1"; + break; + case (nn::pki::sign::SIGN_ID_RSA4096_SHA256): + str = "RSA4096-SHA256"; + break; + case (nn::pki::sign::SIGN_ID_RSA2048_SHA256): + str = "RSA2048-SHA256"; + break; + case (nn::pki::sign::SIGN_ID_ECDSA240_SHA256): + str = "ECDSA240-SHA256"; + break; + default: + str = "Unknown"; + break; + } + return str; +} + +std::string nstool::PkiCertProcess::getEndiannessStr(bool isLittleEndian) const +{ + return isLittleEndian ? "LittleEndian" : "BigEndian"; +} + +std::string nstool::PkiCertProcess::getPublicKeyTypeStr(nn::pki::cert::PublicKeyType type) const +{ + std::string str; + switch (type) + { + case (nn::pki::cert::RSA4096): + str = "RSA4096"; + break; + case (nn::pki::cert::RSA2048): + str = "RSA2048"; + break; + case (nn::pki::cert::ECDSA240): + str = "ECDSA240"; + break; + default: + str = "Unknown"; + break; + } + return str; +} \ No newline at end of file diff --git a/src/PkiCertProcess.h b/src/PkiCertProcess.h index b5794c5..2c4c5cf 100644 --- a/src/PkiCertProcess.h +++ b/src/PkiCertProcess.h @@ -20,8 +20,7 @@ public: void setVerifyMode(bool verify); private: - const std::string kModuleName = "PkiCertProcess"; - static const size_t kSmallHexDumpLen = 0x10; + std::string mModuleName; std::shared_ptr mFile; KeyBag mKeyCfg; @@ -35,10 +34,9 @@ private: void displayCerts(); void displayCert(const nn::pki::SignedData& cert); - size_t getHexDumpLen(size_t max_size) const; - const char* getSignTypeStr(nn::pki::sign::SignatureId type) const; - const char* getEndiannessStr(bool isLittleEndian) const; - const char* getPublicKeyTypeStr(nn::pki::cert::PublicKeyType type) const; + std::string getSignTypeStr(nn::pki::sign::SignatureId type) const; + std::string getEndiannessStr(bool isLittleEndian) const; + std::string getPublicKeyTypeStr(nn::pki::cert::PublicKeyType type) const; }; } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8036edd..2eb2230 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,7 +14,7 @@ #include "NacpProcess.h" //#include "IniProcess.h" //#include "KipProcess.h" -//#include "PkiCertProcess.h" +#include "PkiCertProcess.h" #include "EsTikProcess.h" #include "AssetProcess.h" @@ -188,6 +188,7 @@ int umain(const std::vector& args, const std::vector& obj.process(); } + */ else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PKI_CERT) { nstool::PkiCertProcess obj; @@ -199,7 +200,6 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - */ else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ES_TIK) { nstool::EsTikProcess obj; diff --git a/src/not_ported/PkiCertProcess.cpp b/src/not_ported/PkiCertProcess.cpp deleted file mode 100644 index 45ce6b7..0000000 --- a/src/not_ported/PkiCertProcess.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include -#include -#include -#include -#include -#include "PkiCertProcess.h" -#include "PkiValidator.h" - -nstool::PkiCertProcess::PkiCertProcess() : - mFile(), - mCliOutputMode(true, false, false, false), - mVerify(false) -{ -} - -void nstool::PkiCertProcess::process() -{ - importCerts(); - - if (mVerify) - validateCerts(); - - if (mCliOutputMode.show_basic_info) - displayCerts(); -} - -void nstool::PkiCertProcess::setInputFile(const std::shared_ptr& file) -{ - mFile = file; -} - -void nstool::PkiCertProcess::setKeyCfg(const KeyBag& keycfg) -{ - mKeyCfg = keycfg; -} - -void nstool::PkiCertProcess::setCliOutputMode(CliOutputMode mode) -{ - mCliOutputMode = mode; -} - -void nstool::PkiCertProcess::setVerifyMode(bool verify) -{ - mVerify = verify; -} - -void nstool::PkiCertProcess::importCerts() -{ - tc::ByteData scratch; - - if (*mFile == nullptr) - { - throw tc::Exception(kModuleName, "No file reader set."); - } - - scratch.alloc((*mFile)->size()); - (*mFile)->read(scratch.data(), 0, scratch.size()); - - nn::pki::SignedData cert; - for (size_t f_pos = 0; f_pos < scratch.size(); f_pos += cert.getBytes().size()) - { - cert.fromBytes(scratch.data() + f_pos, scratch.size() - f_pos); - mCert.push_back(cert); - } -} - -void nstool::PkiCertProcess::validateCerts() -{ - PkiValidator pki; - - try - { - pki.setKeyCfg(mKeyCfg); - pki.addCertificates(mCert); - } - catch (const tc::Exception& e) - { - std::cout << "[WARNING] " << e.error() << std::endl; - return; - } -} - -void nstool::PkiCertProcess::displayCerts() -{ - for (size_t i = 0; i < mCert.size(); i++) - { - displayCert(mCert[i]); - } -} - -void nstool::PkiCertProcess::displayCert(const nn::pki::SignedData& cert) -{ - std::cout << "[NNPKI Certificate]" << std::endl; - - std::cout << " SignType " << getSignTypeStr(cert.getSignature().getSignType()); - if (mCliOutputMode.show_extended_info) - std::cout << " (0x" << std::hex << cert.getSignature().getSignType() << ") (" << getEndiannessStr(cert.getSignature().isLittleEndian()) << ")"; - std::cout << std::endl; - - std::cout << " Issuer: " << cert.getBody().getIssuer() << std::endl; - std::cout << " Subject: " << cert.getBody().getSubject() << std::endl; - std::cout << " PublicKeyType: " << getPublicKeyTypeStr(cert.getBody().getPublicKeyType()); - if (mCliOutputMode.show_extended_info) - std::cout << " (" << std::dec << cert.getBody().getPublicKeyType() << ")"; - std::cout << std::endl; - std::cout << " CertID: 0x" << std::hex << cert.getBody().getCertId() << std::endl; - - if (cert.getBody().getPublicKeyType() == nn::pki::cert::RSA4096) - { - std::cout << " PublicKey:" << std::endl; - std::cout << " Modulus:" << std::endl; - fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().modulus, getHexDumpLen(fnd::rsa::kRsa4096Size), 0x10, 6); - std::cout << " Public Exponent:" << std::endl; - fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().public_exponent, fnd::rsa::kRsaPublicExponentSize, 0x10, 6); - } - else if (cert.getBody().getPublicKeyType() == nn::pki::cert::RSA2048) - { - std::cout << " PublicKey:" << std::endl; - std::cout << " Modulus:" << std::endl; - fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().modulus, getHexDumpLen(fnd::rsa::kRsa2048Size), 0x10, 6); - std::cout << " Public Exponent:" << std::endl; - fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().public_exponent, fnd::rsa::kRsaPublicExponentSize, 0x10, 6); - } - else if (cert.getBody().getPublicKeyType() == nn::pki::cert::ECDSA240) - { - std::cout << " PublicKey:" << std::endl; - std::cout << " R:" << std::endl; - fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().r, getHexDumpLen(fnd::ecdsa::kEcdsa240Size), 0x10, 6); - std::cout << " S:" << std::endl; - fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().s, getHexDumpLen(fnd::ecdsa::kEcdsa240Size), 0x10, 6); - } -} - -size_t nstool::PkiCertProcess::getHexDumpLen(size_t max_size) const -{ - return mCliOutputMode.show_extended_info ? max_size : kSmallHexDumpLen; -} - -const char* nstool::PkiCertProcess::getSignTypeStr(nn::pki::sign::SignatureId type) const -{ - const char* str; - switch (type) - { - case (nn::pki::sign::SIGN_ID_RSA4096_SHA1): - str = "RSA4096-SHA1"; - break; - case (nn::pki::sign::SIGN_ID_RSA2048_SHA1): - str = "RSA2048-SHA1"; - break; - case (nn::pki::sign::SIGN_ID_ECDSA240_SHA1): - str = "ECDSA240-SHA1"; - break; - case (nn::pki::sign::SIGN_ID_RSA4096_SHA256): - str = "RSA4096-SHA256"; - break; - case (nn::pki::sign::SIGN_ID_RSA2048_SHA256): - str = "RSA2048-SHA256"; - break; - case (nn::pki::sign::SIGN_ID_ECDSA240_SHA256): - str = "ECDSA240-SHA256"; - break; - default: - str = "Unknown"; - break; - } - return str; -} - -const char* nstool::PkiCertProcess::getEndiannessStr(bool isLittleEndian) const -{ - return isLittleEndian ? "LittleEndian" : "BigEndian"; -} - -const char* nstool::PkiCertProcess::getPublicKeyTypeStr(nn::pki::cert::PublicKeyType type) const -{ - const char* str; - switch (type) - { - case (nn::pki::cert::RSA4096): - str = "RSA4096"; - break; - case (nn::pki::cert::RSA2048): - str = "RSA2048"; - break; - case (nn::pki::cert::ECDSA240): - str = "ECDSA240"; - break; - default: - str = "Unknown"; - break; - } - return str; -} \ No newline at end of file From 948c0e965faf3c160e25dfbac8ec0de08ab3d56b Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 10 Oct 2021 17:23:06 +0800 Subject: [PATCH 048/119] Misc typo --- src/EsTikProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EsTikProcess.cpp b/src/EsTikProcess.cpp index 5a4108e..e0b1ed9 100644 --- a/src/EsTikProcess.cpp +++ b/src/EsTikProcess.cpp @@ -65,7 +65,7 @@ void nstool::EsTikProcess::importTicket() throw tc::Exception(mModuleName, "File too large."); } - // read cnmt + // read ticket tc::ByteData scratch = tc::ByteData(file_size); mFile->seek(0, tc::io::SeekOrigin::Begin); mFile->read(scratch.data(), scratch.size()); From aeb87156b57bdecd0a833f8d6d7b1ec9e69cd1a0 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 11 Oct 2021 20:18:43 +0800 Subject: [PATCH 049/119] Port NsoProcess and NroProcess to libtoolchain. --- build/visualstudio/nstool/nstool.vcxproj | 10 +- .../nstool/nstool.vcxproj.filters | 6 + src/NroProcess.cpp | 192 ++++++++++++ src/NroProcess.h | 13 +- src/NsoProcess.cpp | 285 ++++++++++++++++++ src/NsoProcess.h | 9 +- src/main.cpp | 11 +- src/not_ported/NroProcess.cpp | 174 ----------- src/not_ported/NsoProcess.cpp | 240 --------------- 9 files changed, 505 insertions(+), 435 deletions(-) create mode 100644 src/NroProcess.cpp create mode 100644 src/NsoProcess.cpp delete mode 100644 src/not_ported/NroProcess.cpp delete mode 100644 src/not_ported/NsoProcess.cpp diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index f452499..655fbb7 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -76,7 +76,7 @@ Disabled true true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include MultiThreadedDebug @@ -86,7 +86,7 @@ Disabled true true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include MultiThreadedDebug @@ -98,7 +98,7 @@ true true true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include MultiThreaded @@ -114,7 +114,7 @@ true true true - $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include + $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include MultiThreaded @@ -188,6 +188,8 @@ + + diff --git a/build/visualstudio/nstool/nstool.vcxproj.filters b/build/visualstudio/nstool/nstool.vcxproj.filters index 69e6c9f..a866cc6 100644 --- a/build/visualstudio/nstool/nstool.vcxproj.filters +++ b/build/visualstudio/nstool/nstool.vcxproj.filters @@ -143,5 +143,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/src/NroProcess.cpp b/src/NroProcess.cpp new file mode 100644 index 0000000..66a061c --- /dev/null +++ b/src/NroProcess.cpp @@ -0,0 +1,192 @@ +#include "NroProcess.h" + +#include + +nstool::NroProcess::NroProcess() : + mModuleName("nstool::NroProcess"), + mFile(), + mCliOutputMode(true, false, false, false), + mVerify(false) +{ +} + +void nstool::NroProcess::process() +{ + importHeader(); + importCodeSegments(); + + if (mCliOutputMode.show_basic_info) + displayHeader(); + + processRoMeta(); + + if (mIsHomebrewNro) + mAssetProc.process(); +} + +void nstool::NroProcess::setInputFile(const std::shared_ptr& file) +{ + mFile = file; +} + +void nstool::NroProcess::setCliOutputMode(CliOutputMode type) +{ + mCliOutputMode = type; +} + +void nstool::NroProcess::setVerifyMode(bool verify) +{ + mVerify = verify; +} + +void nstool::NroProcess::setIs64BitInstruction(bool flag) +{ + mRoMeta.setIs64BitInstruction(flag); +} + +void nstool::NroProcess::setListApi(bool listApi) +{ + mRoMeta.setListApi(listApi); +} + +void nstool::NroProcess::setListSymbols(bool listSymbols) +{ + mRoMeta.setListSymbols(listSymbols); +} + +void nstool::NroProcess::setAssetIconExtractPath(const tc::io::Path& path) +{ + mAssetProc.setIconExtractPath(path); +} + +void nstool::NroProcess::setAssetNacpExtractPath(const tc::io::Path& path) +{ + mAssetProc.setNacpExtractPath(path); +} + +void nstool::NroProcess::setAssetRomfsShowFsTree(bool show_fs_tree) +{ + mAssetProc.setRomfsShowFsTree(show_fs_tree); +} + +void nstool::NroProcess::setAssetRomfsExtractJobs(const std::vector& extract_jobs) +{ + mAssetProc.setRomfsExtractJobs(extract_jobs); +} + +const nstool::RoMetadataProcess& nstool::NroProcess::getRoMetadataProcess() const +{ + return mRoMeta; +} + +void nstool::NroProcess::importHeader() +{ + if (mFile == nullptr) + { + throw tc::Exception(mModuleName, "No file reader set."); + } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); + } + + // check if file_size is smaller than NRO header size + if (tc::io::IOUtil::castInt64ToSize(mFile->length()) < sizeof(nn::hac::sNroHeader)) + { + throw tc::Exception(mModuleName, "Corrupt NRO: file too small."); + } + + // read nro + tc::ByteData scratch = tc::ByteData(sizeof(nn::hac::sNroHeader)); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // parse nro header + mHdr.fromBytes(scratch.data(), scratch.size()); + + // setup homebrew extension + nn::hac::sNroHeader* raw_hdr = (nn::hac::sNroHeader*)scratch.data(); + + int64_t file_size = mFile->length(); + if (((tc::bn::le64*)raw_hdr->reserved_0.data())->unwrap() == nn::hac::nro::kNroHomebrewStructMagic && file_size > int64_t(mHdr.getNroSize())) + { + mIsHomebrewNro = true; + mAssetProc.setInputFile(std::make_shared(tc::io::SubStream(mFile, int64_t(mHdr.getNroSize()), file_size - int64_t(mHdr.getNroSize())))); + mAssetProc.setCliOutputMode(mCliOutputMode); + mAssetProc.setVerifyMode(mVerify); + } + else + mIsHomebrewNro = false; +} + +void nstool::NroProcess::importCodeSegments() +{ + if (mHdr.getTextInfo().size > 0) + { + mTextBlob = tc::ByteData(mHdr.getTextInfo().size); + mFile->seek(mHdr.getTextInfo().memory_offset, tc::io::SeekOrigin::Begin); + mFile->read(mTextBlob.data(), mTextBlob.size()); + } + + if (mHdr.getRoInfo().size > 0) + { + mRoBlob = tc::ByteData(mHdr.getRoInfo().size); + mFile->seek(mHdr.getRoInfo().memory_offset, tc::io::SeekOrigin::Begin); + mFile->read(mRoBlob.data(), mRoBlob.size()); + } + + if (mHdr.getDataInfo().size > 0) + { + mDataBlob = tc::ByteData(mHdr.getDataInfo().size); + mFile->seek(mHdr.getDataInfo().memory_offset, tc::io::SeekOrigin::Begin); + mFile->read(mDataBlob.data(), mDataBlob.size()); + } +} + +void nstool::NroProcess::displayHeader() +{ + fmt::print("[NRO Header]\n"); + fmt::print(" RoCrt: \n"); + fmt::print(" EntryPoint: 0x{:x}\n", mHdr.getRoCrtEntryPoint()); + fmt::print(" ModOffset: 0x{:x}\n", mHdr.getRoCrtModOffset()); + fmt::print(" ModuleId: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getModuleId().data(), mHdr.getModuleId().size(), false, "")); + fmt::print(" NroSize: 0x{:x}\n", mHdr.getNroSize()); + fmt::print(" Program Sections:\n"); + fmt::print(" .text:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getTextInfo().memory_offset); + fmt::print(" Size: 0x{:x}\n", mHdr.getTextInfo().size); + fmt::print(" .ro:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getRoInfo().memory_offset); + fmt::print(" Size: 0x{:x}\n", mHdr.getRoInfo().size); + if (mCliOutputMode.show_extended_info) + { + fmt::print(" .api_info:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getRoEmbeddedInfo().memory_offset); + fmt::print(" Size: 0x{:x}\n", mHdr.getRoEmbeddedInfo().size); + fmt::print(" .dynstr:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getRoDynStrInfo().memory_offset); + fmt::print(" Size: 0x{:x}\n", mHdr.getRoDynStrInfo().size); + fmt::print(" .dynsym:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getRoDynSymInfo().memory_offset); + fmt::print(" Size: 0x{:x}\n", mHdr.getRoDynSymInfo().size); + } + fmt::print(" .data:\n"); + fmt::print(" Offset: 0x{:x}\n", mHdr.getDataInfo().memory_offset); + fmt::print(" Size: 0x{:x}\n", mHdr.getDataInfo().size); + fmt::print(" .bss:\n"); + fmt::print(" Size: 0x{:x}\n", mHdr.getBssSize()); +} + +void nstool::NroProcess::processRoMeta() +{ + if (mRoBlob.size()) + { + // setup ro metadata + mRoMeta.setApiInfo(mHdr.getRoEmbeddedInfo().memory_offset, mHdr.getRoEmbeddedInfo().size); + mRoMeta.setDynSym(mHdr.getRoDynSymInfo().memory_offset, mHdr.getRoDynSymInfo().size); + mRoMeta.setDynStr(mHdr.getRoDynStrInfo().memory_offset, mHdr.getRoDynStrInfo().size); + mRoMeta.setRoBinary(mRoBlob); + mRoMeta.setCliOutputMode(mCliOutputMode); + mRoMeta.process(); + } +} \ No newline at end of file diff --git a/src/NroProcess.h b/src/NroProcess.h index efc8610..ac93711 100644 --- a/src/NroProcess.h +++ b/src/NroProcess.h @@ -3,7 +3,6 @@ #include "RoMetadataProcess.h" #include "AssetProcess.h" -#include #include namespace nstool { @@ -24,14 +23,14 @@ public: void setListSymbols(bool listSymbols); // for homebrew NROs with Asset blobs appended - void setAssetListFs(bool list); void setAssetIconExtractPath(const tc::io::Path& path); void setAssetNacpExtractPath(const tc::io::Path& path); - void setAssetRomfsExtractPath(const tc::io::Path& path); + void setAssetRomfsShowFsTree(bool show_fs_tree); + void setAssetRomfsExtractJobs(const std::vector& extract_jobs); - const RoMetadataProcess& getRoMetadataProcess() const; + const nstool::RoMetadataProcess& getRoMetadataProcess() const; private: - const std::string kModuleName = "NroProcess"; + std::string mModuleName; std::shared_ptr mFile; CliOutputMode mCliOutputMode; @@ -39,9 +38,9 @@ private: nn::hac::NroHeader mHdr; tc::ByteData mTextBlob, mRoBlob, mDataBlob; - RoMetadataProcess mRoMeta; + nstool::RoMetadataProcess mRoMeta; bool mIsHomebrewNro; - AssetProcess mAssetProc; + nstool::AssetProcess mAssetProc; void importHeader(); void importCodeSegments(); diff --git a/src/NsoProcess.cpp b/src/NsoProcess.cpp new file mode 100644 index 0000000..7680d49 --- /dev/null +++ b/src/NsoProcess.cpp @@ -0,0 +1,285 @@ +#include "NsoProcess.h" + +#include + +nstool::NsoProcess::NsoProcess() : + mModuleName("nstool::NsoProcess"), + mFile(), + mCliOutputMode(true, false, false, false), + mVerify(false), + mIs64BitInstruction(true), + mListApi(false), + mListSymbols(false) +{ +} + +void nstool::NsoProcess::process() +{ + importHeader(); + importCodeSegments(); + if (mCliOutputMode.show_basic_info) + displayNsoHeader(); + + processRoMeta(); +} + +void nstool::NsoProcess::setInputFile(const std::shared_ptr& file) +{ + mFile = file; +} + +void nstool::NsoProcess::setCliOutputMode(CliOutputMode type) +{ + mCliOutputMode = type; +} + +void nstool::NsoProcess::setVerifyMode(bool verify) +{ + mVerify = verify; +} + +void nstool::NsoProcess::setIs64BitInstruction(bool flag) +{ + mRoMeta.setIs64BitInstruction(flag); +} + +void nstool::NsoProcess::setListApi(bool listApi) +{ + mRoMeta.setListApi(listApi); +} + +void nstool::NsoProcess::setListSymbols(bool listSymbols) +{ + mRoMeta.setListSymbols(listSymbols); +} + +const nstool::RoMetadataProcess& nstool::NsoProcess::getRoMetadataProcess() const +{ + return mRoMeta; +} + +void nstool::NsoProcess::importHeader() +{ + if (mFile == nullptr) + { + throw tc::Exception(mModuleName, "No file reader set."); + } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); + } + + // check if file_size is smaller than NSO header size + size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + if (file_size < sizeof(nn::hac::sNsoHeader)) + { + throw tc::Exception(mModuleName, "Corrupt NSO: file too small."); + } + + // read nso + tc::ByteData scratch = tc::ByteData(sizeof(nn::hac::sNsoHeader)); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // parse nso header + mHdr.fromBytes(scratch.data(), scratch.size()); +} + +void nstool::NsoProcess::importCodeSegments() +{ + tc::ByteData scratch; + nn::hac::detail::sha256_hash_t calc_hash; + + // process text segment + if (mHdr.getTextSegmentInfo().is_compressed) + { + // allocate/read compressed text + scratch = tc::ByteData(mHdr.getTextSegmentInfo().file_layout.size); + mFile->seek(mHdr.getTextSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // allocate for decompressed text segment + mTextBlob = tc::ByteData(mHdr.getTextSegmentInfo().memory_layout.size); + + // decompress text segment + if (decompressData(scratch.data(), scratch.size(), mTextBlob.data(), mTextBlob.size()) != mTextBlob.size()) + { + throw tc::Exception(mModuleName, "NSO text segment failed to decompress"); + } + } + else + { + // read text segment directly (not compressed) + mTextBlob = tc::ByteData(mHdr.getTextSegmentInfo().file_layout.size); + mFile->seek(mHdr.getTextSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(mTextBlob.data(), mTextBlob.size()); + } + if (mHdr.getTextSegmentInfo().is_hashed) + { + tc::crypto::GenerateSha256Hash(calc_hash.data(), mTextBlob.data(), mTextBlob.size()); + if (calc_hash != mHdr.getTextSegmentInfo().hash) + { + throw tc::Exception(mModuleName, "NSO text segment failed SHA256 verification"); + } + } + + // process ro segment + if (mHdr.getRoSegmentInfo().is_compressed) + { + // allocate/read compressed ro segment + scratch = tc::ByteData(mHdr.getRoSegmentInfo().file_layout.size); + mFile->seek(mHdr.getRoSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // allocate for decompressed ro segment + mRoBlob = tc::ByteData(mHdr.getRoSegmentInfo().memory_layout.size); + + // decompress ro segment + if (decompressData(scratch.data(), scratch.size(), mRoBlob.data(), mRoBlob.size()) != mRoBlob.size()) + { + throw tc::Exception(mModuleName, "NSO ro segment failed to decompress"); + } + } + else + { + // read ro segment directly (not compressed) + mRoBlob = tc::ByteData(mHdr.getRoSegmentInfo().file_layout.size); + mFile->seek(mHdr.getRoSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(mRoBlob.data(), mRoBlob.size()); + } + if (mHdr.getRoSegmentInfo().is_hashed) + { + tc::crypto::GenerateSha256Hash(calc_hash.data(), mRoBlob.data(), mRoBlob.size()); + if (calc_hash != mHdr.getRoSegmentInfo().hash) + { + throw tc::Exception(mModuleName, "NSO ro segment failed SHA256 verification"); + } + } + + // process ro segment + if (mHdr.getDataSegmentInfo().is_compressed) + { + // allocate/read compressed ro segment + scratch = tc::ByteData(mHdr.getDataSegmentInfo().file_layout.size); + mFile->seek(mHdr.getDataSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // allocate for decompressed ro segment + mDataBlob = tc::ByteData(mHdr.getDataSegmentInfo().memory_layout.size); + + // decompress ro segment + if (decompressData(scratch.data(), scratch.size(), mDataBlob.data(), mDataBlob.size()) != mDataBlob.size()) + { + throw tc::Exception(mModuleName, "NSO data segment failed to decompress"); + } + } + else + { + // read ro segment directly (not compressed) + mDataBlob = tc::ByteData(mHdr.getDataSegmentInfo().file_layout.size); + mFile->seek(mHdr.getDataSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(mDataBlob.data(), mDataBlob.size()); + } + if (mHdr.getDataSegmentInfo().is_hashed) + { + tc::crypto::GenerateSha256Hash(calc_hash.data(), mDataBlob.data(), mDataBlob.size()); + if (calc_hash != mHdr.getDataSegmentInfo().hash) + { + throw tc::Exception(mModuleName, "NSO data segment failed SHA256 verification"); + } + } +} + +void nstool::NsoProcess::displayNsoHeader() +{ + fmt::print("[NSO Header]\n"); + fmt::print(" ModuleId: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getModuleId().data(), mHdr.getModuleId().size(), false, "")); + if (mCliOutputMode.show_layout) + { + fmt::print(" Program Segments:\n"); + fmt::print(" .module_name:\n"); + fmt::print(" FileOffset: 0x{:x}\n", mHdr.getModuleNameInfo().offset); + fmt::print(" FileSize: 0x{:x}\n", mHdr.getModuleNameInfo().size); + fmt::print(" .text:\n"); + fmt::print(" FileOffset: 0x{:x}\n", mHdr.getTextSegmentInfo().file_layout.offset); + fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getTextSegmentInfo().file_layout.size, (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "")); + fmt::print(" .ro:\n"); + fmt::print(" FileOffset: 0x{:x}\n", mHdr.getRoSegmentInfo().file_layout.offset); + fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getRoSegmentInfo().file_layout.size, (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "")); + fmt::print(" .data:\n"); + fmt::print(" FileOffset: 0x{:x}\n", mHdr.getDataSegmentInfo().file_layout.offset); + fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getDataSegmentInfo().file_layout.size, (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "")); + } + fmt::print(" Program Sections:\n"); + fmt::print(" .text:\n"); + fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getTextSegmentInfo().memory_layout.offset); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getTextSegmentInfo().memory_layout.size); + if (mHdr.getTextSegmentInfo().is_hashed && mCliOutputMode.show_extended_info) + { + fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getTextSegmentInfo().hash.data(), mHdr.getTextSegmentInfo().hash.size(), false, "")); + } + fmt::print(" .ro:\n"); + fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoSegmentInfo().memory_layout.offset); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoSegmentInfo().memory_layout.size); + if (mHdr.getRoSegmentInfo().is_hashed && mCliOutputMode.show_extended_info) + { + fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getRoSegmentInfo().hash.data(), mHdr.getRoSegmentInfo().hash.size(), false, "")); + } + if (mCliOutputMode.show_extended_info) + { + fmt::print(" .api_info:\n"); + fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoEmbeddedInfo().offset); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoEmbeddedInfo().size); + fmt::print(" .dynstr:\n"); + fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoDynStrInfo().offset); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoDynStrInfo().size); + fmt::print(" .dynsym:\n"); + fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoDynSymInfo().offset); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoDynSymInfo().size); + } + + fmt::print(" .data:\n"); + fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getDataSegmentInfo().memory_layout.offset); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getDataSegmentInfo().memory_layout.size); + if (mHdr.getDataSegmentInfo().is_hashed && mCliOutputMode.show_extended_info) + { + fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getDataSegmentInfo().hash.data(), mHdr.getDataSegmentInfo().hash.size(), false, "")); + } + fmt::print(" .bss:\n"); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getBssSize()); +} + +void nstool::NsoProcess::processRoMeta() +{ + if (mRoBlob.size()) + { + // setup ro metadata + mRoMeta.setApiInfo(mHdr.getRoEmbeddedInfo().offset, mHdr.getRoEmbeddedInfo().size); + mRoMeta.setDynSym(mHdr.getRoDynSymInfo().offset, mHdr.getRoDynSymInfo().size); + mRoMeta.setDynStr(mHdr.getRoDynStrInfo().offset, mHdr.getRoDynStrInfo().size); + mRoMeta.setRoBinary(mRoBlob); + mRoMeta.setCliOutputMode(mCliOutputMode); + mRoMeta.process(); + } +} + +size_t nstool::NsoProcess::decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity) +{ + if (src_len >= LZ4_MAX_INPUT_SIZE) + { + return 0; + } + + int32_t src_len_input = int32_t(src_len); + int32_t dst_capcacity_input = (dst_capacity < LZ4_MAX_INPUT_SIZE) ? int32_t(dst_capacity) : LZ4_MAX_INPUT_SIZE; + + int32_t decomp_size = LZ4_decompress_safe((const char*)src, (char*)dst, src_len_input, dst_capcacity_input); + + if (decomp_size < 0) + { + memset(dst, 0, dst_capacity); + return 0; + } + + return size_t(decomp_size); +} \ No newline at end of file diff --git a/src/NsoProcess.h b/src/NsoProcess.h index 2f4fbb2..5856e7a 100644 --- a/src/NsoProcess.h +++ b/src/NsoProcess.h @@ -22,9 +22,9 @@ public: void setListApi(bool listApi); void setListSymbols(bool listSymbols); - const RoMetadataProcess& getRoMetadataProcess() const; + const nstool::RoMetadataProcess& getRoMetadataProcess() const; private: - const std::string kModuleName = "NsoProcess"; + std::string mModuleName; std::shared_ptr mFile; CliOutputMode mCliOutputMode; @@ -35,12 +35,15 @@ private: nn::hac::NsoHeader mHdr; tc::ByteData mTextBlob, mRoBlob, mDataBlob; - RoMetadataProcess mRoMeta; + nstool::RoMetadataProcess mRoMeta; void importHeader(); void importCodeSegments(); void displayNsoHeader(); void processRoMeta(); + + size_t decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity); + }; } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 2eb2230..be46c10 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,8 +9,8 @@ //#include "NcaProcess.h" //#include "MetaProcess.h" #include "CnmtProcess.h" -//#include "NsoProcess.h" -//#include "NroProcess.h" +#include "NsoProcess.h" +#include "NroProcess.h" #include "NacpProcess.h" //#include "IniProcess.h" //#include "KipProcess.h" @@ -115,7 +115,6 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - /* else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NSO) { nstool::NsoProcess obj; @@ -147,13 +146,11 @@ int umain(const std::vector& args, const std::vector& if (set.aset.nacp_extract_path.isSet()) obj.setAssetNacpExtractPath(set.aset.nacp_extract_path.get()); - if (set.fs.extract_path.isSet()) - obj.setAssetRomfsExtractPath(set.fs.extract_path.get()); - obj.setAssetListFs(set.fs.show_fs_tree); + obj.setAssetRomfsShowFsTree(set.fs.show_fs_tree); + obj.setAssetRomfsExtractJobs(set.fs.extract_jobs); obj.process(); } - */ else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NACP) { nstool::NacpProcess obj; diff --git a/src/not_ported/NroProcess.cpp b/src/not_ported/NroProcess.cpp deleted file mode 100644 index c76d845..0000000 --- a/src/not_ported/NroProcess.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "NroProcess.h" - -nstool::NroProcess::NroProcess(): - mFile(), - mCliOutputMode(true, false, false, false), - mVerify(false) -{ -} - -void nstool::NroProcess::process() -{ - importHeader(); - importCodeSegments(); - - if (mCliOutputMode.show_basic_info) - displayHeader(); - - processRoMeta(); - - if (mIsHomebrewNro) - mAssetProc.process(); -} - -void nstool::NroProcess::setInputFile(const std::shared_ptr& file) -{ - mFile = file; -} - -void nstool::NroProcess::setCliOutputMode(CliOutputMode type) -{ - mCliOutputMode = type; -} - -void nstool::NroProcess::setVerifyMode(bool verify) -{ - mVerify = verify; -} - -void nstool::NroProcess::setIs64BitInstruction(bool flag) -{ - mRoMeta.setIs64BitInstruction(flag); -} - -void nstool::NroProcess::setListApi(bool listApi) -{ - mRoMeta.setListApi(listApi); -} - -void nstool::NroProcess::setListSymbols(bool listSymbols) -{ - mRoMeta.setListSymbols(listSymbols); -} - -void nstool::NroProcess::setAssetListFs(bool list) -{ - mAssetProc.setListFs(list); -} - -void nstool::NroProcess::setAssetIconExtractPath(const std::string& path) -{ - mAssetProc.setIconExtractPath(path); -} - -void nstool::NroProcess::setAssetNacpExtractPath(const std::string& path) -{ - mAssetProc.setNacpExtractPath(path); -} - -void nstool::NroProcess::setAssetRomfsExtractPath(const std::string& path) -{ - mAssetProc.setRomfsExtractPath(path); -} - -const RoMetadataProcess& nstool::NroProcess::getRoMetadataProcess() const -{ - return mRoMeta; -} - -void nstool::NroProcess::importHeader() -{ - tc::ByteData scratch; - - if (*mFile == nullptr) - { - throw tc::Exception(kModuleName, "No file reader set."); - } - - if ((*mFile)->size() < sizeof(nn::hac::sNroHeader)) - { - throw tc::Exception(kModuleName, "Corrupt NRO: file too small"); - } - - scratch.alloc(sizeof(nn::hac::sNroHeader)); - (*mFile)->read(scratch.data(), 0, scratch.size()); - - mHdr.fromBytes(scratch.data(), scratch.size()); - - // setup homebrew extension - nn::hac::sNroHeader* raw_hdr = (nn::hac::sNroHeader*)scratch.data(); - if (((tc::bn::le64*)raw_hdr->reserved_0)->get() == nn::hac::nro::kNroHomebrewStructMagic && (*mFile)->size() > mHdr.getNroSize()) - { - mIsHomebrewNro = true; - mAssetProc.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getNroSize(), (*mFile)->size() - mHdr.getNroSize())); - mAssetProc.setCliOutputMode(mCliOutputMode); - mAssetProc.setVerifyMode(mVerify); - } - else - mIsHomebrewNro = false; -} - -void nstool::NroProcess::importCodeSegments() -{ - mTextBlob.alloc(mHdr.getTextInfo().size); - (*mFile)->read(mTextBlob.data(), mHdr.getTextInfo().memory_offset, mTextBlob.size()); - mRoBlob.alloc(mHdr.getRoInfo().size); - (*mFile)->read(mRoBlob.data(), mHdr.getRoInfo().memory_offset, mRoBlob.size()); - mDataBlob.alloc(mHdr.getDataInfo().size); - (*mFile)->read(mDataBlob.data(), mHdr.getDataInfo().memory_offset, mDataBlob.size()); -} - -void nstool::NroProcess::displayHeader() -{ - std::cout << "[NRO Header]" << std::endl; - std::cout << " RoCrt: " << std::endl; - std::cout << " EntryPoint: 0x" << std::hex << mHdr.getRoCrtEntryPoint() << std::endl; - std::cout << " ModOffset: 0x" << std::hex << mHdr.getRoCrtModOffset() << std::endl; - std::cout << " ModuleId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getModuleId().data, nn::hac::nro::kModuleIdSize, false, "") << std::endl; - std::cout << " NroSize: 0x" << std::hex << mHdr.getNroSize() << std::endl; - std::cout << " Program Sections:" << std::endl; - std::cout << " .text:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getTextInfo().memory_offset << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getTextInfo().size << std::endl; - std::cout << " .ro:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getRoInfo().memory_offset << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getRoInfo().size << std::endl; - if (mCliOutputMode.show_extended_info) - { - std::cout << " .api_info:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getRoEmbeddedInfo().memory_offset << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getRoEmbeddedInfo().size << std::endl; - std::cout << " .dynstr:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getRoDynStrInfo().memory_offset << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getRoDynStrInfo().size << std::endl; - std::cout << " .dynsym:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getRoDynSymInfo().memory_offset << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getRoDynSymInfo().size << std::endl; - } - std::cout << " .data:" << std::endl; - std::cout << " Offset: 0x" << std::hex << mHdr.getDataInfo().memory_offset << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getDataInfo().size << std::endl; - std::cout << " .bss:" << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getBssSize() << std::endl; -} - -void nstool::NroProcess::processRoMeta() -{ - if (mRoBlob.size()) - { - // setup ro metadata - mRoMeta.setApiInfo(mHdr.getRoEmbeddedInfo().memory_offset, mHdr.getRoEmbeddedInfo().size); - mRoMeta.setDynSym(mHdr.getRoDynSymInfo().memory_offset, mHdr.getRoDynSymInfo().size); - mRoMeta.setDynStr(mHdr.getRoDynStrInfo().memory_offset, mHdr.getRoDynStrInfo().size); - mRoMeta.setRoBinary(mRoBlob); - mRoMeta.setCliOutputMode(mCliOutputMode); - mRoMeta.process(); - } -} \ No newline at end of file diff --git a/src/not_ported/NsoProcess.cpp b/src/not_ported/NsoProcess.cpp deleted file mode 100644 index dbdc11f..0000000 --- a/src/not_ported/NsoProcess.cpp +++ /dev/null @@ -1,240 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "NsoProcess.h" - -nstool::NsoProcess::NsoProcess(): - mFile(), - mCliOutputMode(true, false, false, false), - mVerify(false), - mIs64BitInstruction(true), - mListApi(false), - mListSymbols(false) -{ -} - -void nstool::NsoProcess::process() -{ - importHeader(); - importCodeSegments(); - if (mCliOutputMode.show_basic_info) - displayNsoHeader(); - - processRoMeta(); -} - -void nstool::NsoProcess::setInputFile(const std::shared_ptr& file) -{ - mFile = file; -} - -void nstool::NsoProcess::setCliOutputMode(CliOutputMode type) -{ - mCliOutputMode = type; -} - -void nstool::NsoProcess::setVerifyMode(bool verify) -{ - mVerify = verify; -} - -void nstool::NsoProcess::setIs64BitInstruction(bool flag) -{ - mRoMeta.setIs64BitInstruction(flag); -} - -void nstool::NsoProcess::setListApi(bool listApi) -{ - mRoMeta.setListApi(listApi); -} - -void nstool::NsoProcess::setListSymbols(bool listSymbols) -{ - mRoMeta.setListSymbols(listSymbols); -} - -const RoMetadataProcess& nstool::NsoProcess::getRoMetadataProcess() const -{ - return mRoMeta; -} - -void nstool::NsoProcess::importHeader() -{ - tc::ByteData scratch; - - if (*mFile == nullptr) - { - throw tc::Exception(kModuleName, "No file reader set."); - } - - if ((*mFile)->size() < sizeof(nn::hac::sNsoHeader)) - { - throw tc::Exception(kModuleName, "Corrupt NSO: file too small"); - } - - scratch.alloc(sizeof(nn::hac::sNsoHeader)); - (*mFile)->read(scratch.data(), 0, scratch.size()); - - mHdr.fromBytes(scratch.data(), scratch.size()); -} - -void nstool::NsoProcess::importCodeSegments() -{ - tc::ByteData scratch; - uint32_t decompressed_len; - fnd::sha::sSha256Hash calc_hash; - - // process text segment - if (mHdr.getTextSegmentInfo().is_compressed) - { - scratch.alloc(mHdr.getTextSegmentInfo().file_layout.size); - (*mFile)->read(scratch.data(), mHdr.getTextSegmentInfo().file_layout.offset, scratch.size()); - mTextBlob.alloc(mHdr.getTextSegmentInfo().memory_layout.size); - fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mTextBlob.data(), (uint32_t)mTextBlob.size(), decompressed_len); - if (decompressed_len != mTextBlob.size()) - { - throw tc::Exception(kModuleName, "NSO text segment failed to decompress"); - } - } - else - { - mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size); - (*mFile)->read(mTextBlob.data(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.size()); - } - if (mHdr.getTextSegmentInfo().is_hashed) - { - fnd::sha::Sha256(mTextBlob.data(), mTextBlob.size(), calc_hash.bytes); - if (calc_hash != mHdr.getTextSegmentInfo().hash) - { - throw tc::Exception(kModuleName, "NSO text segment failed SHA256 verification"); - } - } - - // process ro segment - if (mHdr.getRoSegmentInfo().is_compressed) - { - scratch.alloc(mHdr.getRoSegmentInfo().file_layout.size); - (*mFile)->read(scratch.data(), mHdr.getRoSegmentInfo().file_layout.offset, scratch.size()); - mRoBlob.alloc(mHdr.getRoSegmentInfo().memory_layout.size); - fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mRoBlob.data(), (uint32_t)mRoBlob.size(), decompressed_len); - if (decompressed_len != mRoBlob.size()) - { - throw tc::Exception(kModuleName, "NSO ro segment failed to decompress"); - } - } - else - { - mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size); - (*mFile)->read(mRoBlob.data(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.size()); - } - if (mHdr.getRoSegmentInfo().is_hashed) - { - fnd::sha::Sha256(mRoBlob.data(), mRoBlob.size(), calc_hash.bytes); - if (calc_hash != mHdr.getRoSegmentInfo().hash) - { - throw tc::Exception(kModuleName, "NSO ro segment failed SHA256 verification"); - } - } - - // process data segment - if (mHdr.getDataSegmentInfo().is_compressed) - { - scratch.alloc(mHdr.getDataSegmentInfo().file_layout.size); - (*mFile)->read(scratch.data(), mHdr.getDataSegmentInfo().file_layout.offset, scratch.size()); - mDataBlob.alloc(mHdr.getDataSegmentInfo().memory_layout.size); - fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mDataBlob.data(), (uint32_t)mDataBlob.size(), decompressed_len); - if (decompressed_len != mDataBlob.size()) - { - throw tc::Exception(kModuleName, "NSO data segment failed to decompress"); - } - } - else - { - mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size); - (*mFile)->read(mDataBlob.data(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.size()); - } - if (mHdr.getDataSegmentInfo().is_hashed) - { - fnd::sha::Sha256(mDataBlob.data(), mDataBlob.size(), calc_hash.bytes); - if (calc_hash != mHdr.getDataSegmentInfo().hash) - { - throw tc::Exception(kModuleName, "NSO data segment failed SHA256 verification"); - } - } -} - -void nstool::NsoProcess::displayNsoHeader() -{ - std::cout << "[NSO Header]" << std::endl; - std::cout << " ModuleId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getModuleId().data, nn::hac::nso::kModuleIdSize, false, "") << std::endl; - if (mCliOutputMode.show_layout) - { - std::cout << " Program Segments:" << std::endl; - std::cout << " .module_name:" << std::endl; - std::cout << " FileOffset: 0x" << std::hex << mHdr.getModuleNameInfo().offset << std::endl; - std::cout << " FileSize: 0x" << std::hex << mHdr.getModuleNameInfo().size << std::endl; - std::cout << " .text:" << std::endl; - std::cout << " FileOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.offset << std::endl; - std::cout << " FileSize: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.size << (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl; - std::cout << " .ro:" << std::endl; - std::cout << " FileOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.offset << std::endl; - std::cout << " FileSize: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.size << (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl; - std::cout << " .data:" << std::endl; - std::cout << " FileOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.offset << std::endl; - std::cout << " FileSize: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.size << (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl; - } - std::cout << " Program Sections:" << std::endl; - std::cout << " .text:" << std::endl; - std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.offset << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.size << std::endl; - if (mHdr.getTextSegmentInfo().is_hashed && mCliOutputMode.show_extended_info) - { - std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getTextSegmentInfo().hash.bytes, 32, false, "") << std::endl; - } - std::cout << " .ro:" << std::endl; - std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.offset << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.size << std::endl; - if (mHdr.getRoSegmentInfo().is_hashed && mCliOutputMode.show_extended_info) - { - std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getRoSegmentInfo().hash.bytes, 32, false, "") << std::endl; - } - if (mCliOutputMode.show_extended_info) - { - std::cout << " .api_info:" << std::endl; - std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoEmbeddedInfo().offset << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoEmbeddedInfo().size << std::endl; - std::cout << " .dynstr:" << std::endl; - std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoDynStrInfo().offset << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoDynStrInfo().size << std::endl; - std::cout << " .dynsym:" << std::endl; - std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoDynSymInfo().offset << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoDynSymInfo().size << std::endl; - } - - std::cout << " .data:" << std::endl; - std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.offset << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.size << std::endl; - if (mHdr.getDataSegmentInfo().is_hashed && mCliOutputMode.show_extended_info) - { - std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getDataSegmentInfo().hash.bytes, 32, false, "") << std::endl; - } - std::cout << " .bss:" << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getBssSize() << std::endl; -} - -void nstool::NsoProcess::processRoMeta() -{ - if (mRoBlob.size()) - { - // setup ro metadata - mRoMeta.setApiInfo(mHdr.getRoEmbeddedInfo().offset, mHdr.getRoEmbeddedInfo().size); - mRoMeta.setDynSym(mHdr.getRoDynSymInfo().offset, mHdr.getRoDynSymInfo().size); - mRoMeta.setDynStr(mHdr.getRoDynStrInfo().offset, mHdr.getRoDynStrInfo().size); - mRoMeta.setRoBinary(mRoBlob); - mRoMeta.setCliOutputMode(mCliOutputMode); - mRoMeta.process(); - } -} \ No newline at end of file From 1444dff37af22184445e8743c03215fcf3fd7c68 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 11 Oct 2021 20:20:14 +0800 Subject: [PATCH 050/119] Move MetaProcess --- src/{not_ported => }/MetaProcess.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{not_ported => }/MetaProcess.cpp (100%) diff --git a/src/not_ported/MetaProcess.cpp b/src/MetaProcess.cpp similarity index 100% rename from src/not_ported/MetaProcess.cpp rename to src/MetaProcess.cpp From 672828839af8611e23f655732f43738bf075e1ef Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 11 Oct 2021 22:12:23 +0800 Subject: [PATCH 051/119] Port MetaProcess to libtoolchain. --- build/visualstudio/nstool/nstool.vcxproj | 1 + .../nstool/nstool.vcxproj.filters | 3 + src/MetaProcess.cpp | 251 +++++++++--------- src/MetaProcess.h | 4 +- src/main.cpp | 4 +- 5 files changed, 133 insertions(+), 130 deletions(-) diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index 655fbb7..bd7757d 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -187,6 +187,7 @@ + diff --git a/build/visualstudio/nstool/nstool.vcxproj.filters b/build/visualstudio/nstool/nstool.vcxproj.filters index a866cc6..a18c5e9 100644 --- a/build/visualstudio/nstool/nstool.vcxproj.filters +++ b/build/visualstudio/nstool/nstool.vcxproj.filters @@ -149,5 +149,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/src/MetaProcess.cpp b/src/MetaProcess.cpp index a906125..c96aa08 100644 --- a/src/MetaProcess.cpp +++ b/src/MetaProcess.cpp @@ -1,16 +1,12 @@ #include "MetaProcess.h" -#include -#include - #include #include #include #include -#include - nstool::MetaProcess::MetaProcess() : + mModuleName("nstool::MetaProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false) @@ -76,15 +72,26 @@ const nn::hac::Meta& nstool::MetaProcess::getMeta() const void nstool::MetaProcess::importMeta() { - tc::ByteData scratch; - - if (*mFile == nullptr) + if (mFile == nullptr) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "No file reader set."); + } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); } - scratch.alloc((*mFile)->size()); - (*mFile)->read(scratch.data(), 0, scratch.size()); + // check if file_size is greater than 20MB, don't import. + size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + if (file_size > (0x100000 * 20)) + { + throw tc::Exception(mModuleName, "File too large."); + } + + // read meta + tc::ByteData scratch = tc::ByteData(file_size); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); mMeta.fromBytes(scratch.data(), scratch.size()); } @@ -92,14 +99,15 @@ void nstool::MetaProcess::importMeta() void nstool::MetaProcess::validateAcidSignature(const nn::hac::AccessControlInfoDesc& acid, byte_t key_generation) { try { - fnd::rsa::sRsa2048Key acid_sign_key; - if (mKeyCfg.getAcidSignKey(acid_sign_key, key_generation) != true) - throw tc::Exception(); + if (mKeyCfg.acid_sign_key.find(key_generation) == mKeyCfg.acid_sign_key.end()) + { + throw tc::Exception("Failed to load rsa public key"); + } - acid.validateSignature(acid_sign_key); + acid.validateSignature(mKeyCfg.acid_sign_key.at(key_generation)); } - catch (...) { - std::cout << "[WARNING] ACID Signature: FAIL" << std::endl; + catch (tc::Exception& e) { + fmt::print("[WARNING] ACID Signature: FAIL ({:s})\n", e.error()); } } @@ -109,20 +117,27 @@ void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& // check Program ID if (acid.getProgramIdRestrict().min > 0 && aci.getProgramId() < acid.getProgramIdRestrict().min) { - std::cout << "[WARNING] ACI ProgramId: FAIL (Outside Legal Range)" << std::endl; + fmt::print("[WARNING] ACI ProgramId: FAIL (Outside Legal Range)\n"); } else if (acid.getProgramIdRestrict().max > 0 && aci.getProgramId() > acid.getProgramIdRestrict().max) { - std::cout << "[WARNING] ACI ProgramId: FAIL (Outside Legal Range)" << std::endl; + fmt::print("[WARNING] ACI ProgramId: FAIL (Outside Legal Range)\n"); } auto fs_access = aci.getFileSystemAccessControl().getFsAccess(); auto desc_fs_access = acid.getFileSystemAccessControl().getFsAccess(); for (size_t i = 0; i < fs_access.size(); i++) { - if (fs_access.test(i) && desc_fs_access.test(i) == false) + bool rightFound = false; + for (size_t j = 0; j < desc_fs_access.size() && rightFound == false; j++) { - std::cout << "[WARNING] ACI/FAC FsaRights: FAIL (" << nn::hac::FileSystemAccessUtil::getFsAccessFlagAsString(nn::hac::fac::FsAccessFlag(i)) << " not permitted)" << std::endl; + if (fs_access[i] == desc_fs_access[j]) + rightFound = true; + } + + if (rightFound == false) + { + fmt::print("[WARNING] ACI/FAC FsaRights: FAIL ({:s} not permitted)\n", nn::hac::FileSystemAccessUtil::getFsAccessFlagAsString(fs_access[i])); } } @@ -138,7 +153,7 @@ void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& if (rightFound == false) { - std::cout << "[WARNING] ACI/FAC ContentOwnerId: FAIL (" << std::hex << std::setw(16) << std::setfill('0') << aci.getFileSystemAccessControl().getContentOwnerIdList()[i] << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/FAC ContentOwnerId: FAIL (0x{:016x} not permitted)\n", aci.getFileSystemAccessControl().getContentOwnerIdList()[i]); } } @@ -154,7 +169,7 @@ void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& if (rightFound == false) { - std::cout << "[WARNING] ACI/FAC SaveDataOwnerId: FAIL (" << std::hex << std::setw(16) << std::setfill('0') << aci.getFileSystemAccessControl().getSaveDataOwnerIdList()[i].id << "(" << std::dec << (uint32_t)aci.getFileSystemAccessControl().getSaveDataOwnerIdList()[i].access_type << ") not permitted)" << std::endl; + fmt::print("[WARNING] ACI/FAC SaveDataOwnerId: FAIL (0x{:016x} ({:d}) not permitted)\n", aci.getFileSystemAccessControl().getSaveDataOwnerIdList()[i].id, aci.getFileSystemAccessControl().getSaveDataOwnerIdList()[i].access_type); } } @@ -170,7 +185,7 @@ void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& if (rightFound == false) { - std::cout << "[WARNING] ACI/SAC ServiceList: FAIL (" << aci.getServiceAccessControl().getServiceList()[i].getName() << (aci.getServiceAccessControl().getServiceList()[i].isServer()? " (Server)" : "") << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/SAC ServiceList: FAIL ({:s}{:s} not permitted)\n", aci.getServiceAccessControl().getServiceList()[i].getName(), (aci.getServiceAccessControl().getServiceList()[i].isServer()? " (Server)" : "")); } } @@ -178,19 +193,19 @@ void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& // check thread info if (aci.getKernelCapabilities().getThreadInfo().getMaxCpuId() != acid.getKernelCapabilities().getThreadInfo().getMaxCpuId()) { - std::cout << "[WARNING] ACI/KC ThreadInfo/MaxCpuId: FAIL (" << std::dec << (uint32_t)aci.getKernelCapabilities().getThreadInfo().getMaxCpuId() << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC ThreadInfo/MaxCpuId: FAIL ({:d} not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMaxCpuId()); } if (aci.getKernelCapabilities().getThreadInfo().getMinCpuId() != acid.getKernelCapabilities().getThreadInfo().getMinCpuId()) { - std::cout << "[WARNING] ACI/KC ThreadInfo/MinCpuId: FAIL (" << std::dec << (uint32_t)aci.getKernelCapabilities().getThreadInfo().getMinCpuId() << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC ThreadInfo/MinCpuId: FAIL ({:d} not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMinCpuId()); } if (aci.getKernelCapabilities().getThreadInfo().getMaxPriority() != acid.getKernelCapabilities().getThreadInfo().getMaxPriority()) { - std::cout << "[WARNING] ACI/KC ThreadInfo/MaxPriority: FAIL (" << std::dec << (uint32_t)aci.getKernelCapabilities().getThreadInfo().getMaxPriority() << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC ThreadInfo/MaxPriority: FAIL ({:d} not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMaxPriority()); } if (aci.getKernelCapabilities().getThreadInfo().getMinPriority() != acid.getKernelCapabilities().getThreadInfo().getMinPriority()) { - std::cout << "[WARNING] ACI/KC ThreadInfo/MinPriority: FAIL (" << std::dec << (uint32_t)aci.getKernelCapabilities().getThreadInfo().getMinPriority() << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC ThreadInfo/MinPriority: FAIL ({:d} not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMinPriority()); } // check system calls auto syscall_ids = aci.getKernelCapabilities().getSystemCalls().getSystemCallIds(); @@ -199,7 +214,7 @@ void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& { if (syscall_ids.test(i) && desc_syscall_ids.test(i) == false) { - std::cout << "[WARNING] ACI/KC SystemCallList: FAIL (" << nn::hac::KernelCapabilityUtil::getSystemCallIdAsString(nn::hac::kc::SystemCallId(i)) << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC SystemCallList: FAIL ({:s} not permitted)\n", nn::hac::KernelCapabilityUtil::getSystemCallIdAsString(nn::hac::kc::SystemCallId(i))); } } // check memory maps @@ -216,7 +231,7 @@ void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& { auto map = aci.getKernelCapabilities().getMemoryMaps().getMemoryMaps()[i]; - std::cout << "[WARNING] ACI/KC MemoryMap: FAIL (0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)map.addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(map.addr + map.size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(map.perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(map.type) << ") not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC MemoryMap: FAIL ({:s} not permitted)\n", formatMappingAsString(map)); } } for (size_t i = 0; i < aci.getKernelCapabilities().getMemoryMaps().getIoMemoryMaps().size(); i++) @@ -232,7 +247,7 @@ void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& { auto map = aci.getKernelCapabilities().getMemoryMaps().getIoMemoryMaps()[i]; - std::cout << "[WARNING] ACI/KC IoMemoryMap: FAIL (0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)map.addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(map.addr + map.size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(map.perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(map.type) << ") not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC IoMemoryMap: FAIL ({:s} not permitted)\n", formatMappingAsString(map)); } } // check interupts @@ -247,25 +262,25 @@ void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& if (rightFound == false) { - std::cout << "[WARNING] ACI/KC InteruptsList: FAIL (0x" << std::hex << (uint32_t)aci.getKernelCapabilities().getInterupts().getInteruptList()[i] << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC InteruptsList: FAIL (0x{:x} not permitted)\n", aci.getKernelCapabilities().getInterupts().getInteruptList()[i]); } } // check misc params if (aci.getKernelCapabilities().getMiscParams().getProgramType() != acid.getKernelCapabilities().getMiscParams().getProgramType()) { - std::cout << "[WARNING] ACI/KC ProgramType: FAIL (" << std::dec << (uint32_t)aci.getKernelCapabilities().getMiscParams().getProgramType() << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC ProgramType: FAIL ({:d} not permitted)\n", aci.getKernelCapabilities().getMiscParams().getProgramType()); } // check kernel version uint32_t aciKernelVersion = (uint32_t)aci.getKernelCapabilities().getKernelVersion().getVerMajor() << 16 | (uint32_t)aci.getKernelCapabilities().getKernelVersion().getVerMinor(); uint32_t acidKernelVersion = (uint32_t)acid.getKernelCapabilities().getKernelVersion().getVerMajor() << 16 | (uint32_t)acid.getKernelCapabilities().getKernelVersion().getVerMinor(); if (aciKernelVersion < acidKernelVersion) { - std::cout << "[WARNING] ACI/KC RequiredKernelVersion: FAIL (" << std::dec << aci.getKernelCapabilities().getKernelVersion().getVerMajor() << "." << aci.getKernelCapabilities().getKernelVersion().getVerMinor() << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC RequiredKernelVersion: FAIL ({:d}.{:d} not permitted)\n", aci.getKernelCapabilities().getKernelVersion().getVerMajor(), aci.getKernelCapabilities().getKernelVersion().getVerMinor()); } // check handle table size if (aci.getKernelCapabilities().getHandleTableSize().getHandleTableSize() > acid.getKernelCapabilities().getHandleTableSize().getHandleTableSize()) { - std::cout << "[WARNING] ACI/KC HandleTableSize: FAIL (0x" << std::hex << (uint32_t)aci.getKernelCapabilities().getHandleTableSize().getHandleTableSize() << " too large)" << std::endl; + fmt::print("[WARNING] ACI/KC HandleTableSize: FAIL (0x{:x} too large)\n", aci.getKernelCapabilities().getHandleTableSize().getHandleTableSize()); } // check misc flags auto misc_flags = aci.getKernelCapabilities().getMiscFlags().getMiscFlags(); @@ -274,205 +289,187 @@ void nstool::MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& { if (misc_flags.test(i) && desc_misc_flags.test(i) == false) { - std::cout << "[WARNING] ACI/KC MiscFlag: FAIL (" << nn::hac::KernelCapabilityUtil::getMiscFlagsBitAsString(nn::hac::kc::MiscFlagsBit(i)) << " not permitted)" << std::endl; + fmt::print("[WARNING] ACI/KC MiscFlag: FAIL ({:s} not permitted)\n", nn::hac::KernelCapabilityUtil::getMiscFlagsBitAsString(nn::hac::kc::MiscFlagsBit(i))); } } } void nstool::MetaProcess::displayMetaHeader(const nn::hac::Meta& hdr) { - std::cout << "[Meta Header]" << std::endl; - std::cout << " ACID KeyGeneration: " << std::dec << (uint32_t)hdr.getAccessControlInfoDescKeyGeneration() << std::endl; - std::cout << " Flags:" << std::endl; - std::cout << " Is64BitInstruction: " << std::boolalpha << hdr.getIs64BitInstructionFlag() << std::endl; - std::cout << " ProcessAddressSpace: " << nn::hac::MetaUtil::getProcessAddressSpaceAsString(hdr.getProcessAddressSpace()) << std::endl; - std::cout << " OptimizeMemoryAllocation: " << std::boolalpha << hdr.getOptimizeMemoryAllocationFlag() << std::endl; - std::cout << " SystemResourceSize: 0x" << std::hex << hdr.getSystemResourceSize() << std::endl; - std::cout << " Main Thread Params:" << std::endl; - std::cout << " Priority: " << std::dec << (uint32_t)hdr.getMainThreadPriority() << std::endl; - std::cout << " CpuId: " << std::dec << (uint32_t)hdr.getMainThreadCpuId() << std::endl; - std::cout << " StackSize: 0x" << std::hex << hdr.getMainThreadStackSize() << std::endl; - std::cout << " TitleInfo:" << std::endl; - std::cout << " Version: v" << std::dec << hdr.getVersion() << std::endl; - std::cout << " Name: " << hdr.getName() << std::endl; + fmt::print("[Meta Header]\n"); + fmt::print(" ACID KeyGeneration: {:d}\n", hdr.getAccessControlInfoDescKeyGeneration()); + fmt::print(" Flags:\n"); + fmt::print(" Is64BitInstruction: {}\n", hdr.getIs64BitInstructionFlag()); + fmt::print(" ProcessAddressSpace: {:s}\n", nn::hac::MetaUtil::getProcessAddressSpaceAsString(hdr.getProcessAddressSpace())); + fmt::print(" OptimizeMemoryAllocation: {}\n", hdr.getOptimizeMemoryAllocationFlag()); + fmt::print(" SystemResourceSize: 0x{:x}\n", hdr.getSystemResourceSize()); + fmt::print(" Main Thread Params:\n"); + fmt::print(" Priority: {:d}\n", hdr.getMainThreadPriority()); + fmt::print(" CpuId: {:d}\n", hdr.getMainThreadCpuId()); + fmt::print(" StackSize: 0x{:x}\n", hdr.getMainThreadStackSize()); + fmt::print(" TitleInfo:\n"); + fmt::print(" Version: v{:d}\n", hdr.getVersion()); + fmt::print(" Name: {:s}\n", hdr.getName()); if (hdr.getProductCode().length()) { - std::cout << " ProductCode: " << hdr.getProductCode() << std::endl; + fmt::print(" ProductCode: {:s}\n", hdr.getProductCode()); } } void nstool::MetaProcess::displayAciHdr(const nn::hac::AccessControlInfo& aci) { - std::cout << "[Access Control Info]" << std::endl; - std::cout << " ProgramID: 0x" << std::hex << std::setw(16) << std::setfill('0') << aci.getProgramId() << std::endl; + fmt::print("[Access Control Info]\n"); + fmt::print(" ProgramID: 0x{:016x}\n", aci.getProgramId()); } void nstool::MetaProcess::displayAciDescHdr(const nn::hac::AccessControlInfoDesc& acid) { - std::cout << "[Access Control Info Desc]" << std::endl; - std::cout << " Flags: " << std::endl; - std::cout << " Production: " << std::boolalpha << acid.getProductionFlag() << std::endl; - std::cout << " Unqualified Approval: " << std::boolalpha << acid.getUnqualifiedApprovalFlag() << std::endl; - std::cout << " Memory Region: " << nn::hac::AccessControlInfoUtil::getMemoryRegionAsString(acid.getMemoryRegion()) << " (" << std::dec << (uint32_t)acid.getMemoryRegion() << ")" << std::endl; - std::cout << " ProgramID Restriction" << std::endl; - std::cout << " Min: 0x" << std::hex << std::setw(16) << std::setfill('0') << acid.getProgramIdRestrict().min << std::endl; - std::cout << " Max: 0x" << std::hex << std::setw(16) << std::setfill('0') << acid.getProgramIdRestrict().max << std::endl; + fmt::print("[Access Control Info Desc]\n"); + fmt::print(" Flags: \n"); + fmt::print(" Production: {}\n", acid.getProductionFlag()); + fmt::print(" Unqualified Approval: {}\n", acid.getUnqualifiedApprovalFlag()); + fmt::print(" Memory Region: {:s} ({:d})\n", nn::hac::AccessControlInfoUtil::getMemoryRegionAsString(acid.getMemoryRegion()), acid.getMemoryRegion()); + fmt::print(" ProgramID Restriction\n"); + fmt::print(" Min: 0x{:016x}\n", acid.getProgramIdRestrict().min); + fmt::print(" Max: 0x{:016x}\n", acid.getProgramIdRestrict().max); } void nstool::MetaProcess::displayFac(const nn::hac::FileSystemAccessControl& fac) { - std::cout << "[FS Access Control]" << std::endl; - std::cout << " Format Version: " << std::dec << (uint32_t)fac.getFormatVersion() << std::endl; + fmt::print("[FS Access Control]\n"); + fmt::print(" Format Version: {:d}\n", fac.getFormatVersion()); - auto fs_access = fac.getFsAccess(); - if (fs_access.any()) + if (fac.getFsAccess().size()) { - std::cout << " FsAccess:" << std::endl; - // TODO this formatting loop could be a utils function - for (size_t flag = 0, printed = 0; flag < fs_access.size(); flag++) + std::vector fs_access_str_list; + for (auto itr = fac.getFsAccess().begin(); itr != fac.getFsAccess().end(); itr++) { - // skip unset flags - if (fs_access.test(flag) == false) - continue; - - // format the strings - // for each 10 printed we do a new line - if (printed % 10 == 0) + std::string flag_string = nn::hac::FileSystemAccessUtil::getFsAccessFlagAsString(nn::hac::fac::FsAccessFlag(*itr)); + if (mCliOutputMode.show_extended_info) { - // skip new line when we haven't printed anything - if (printed != 0) - std::cout << std::endl; - std::cout << " "; + fs_access_str_list.push_back(fmt::format("{:s} (bit {:d})", flag_string, *itr)); } - // within a line we want to separate the next string from the last one with a comma and a space else { - std::cout << ", "; + fs_access_str_list.push_back(flag_string); } - printed++; - - // output string info - std::cout << nn::hac::FileSystemAccessUtil::getFsAccessFlagAsString(nn::hac::fac::FsAccessFlag(flag)); - if (mCliOutputMode.show_extended_info) - std::cout << " (bit " << std::dec << (uint32_t)flag << ")"; + } - std::cout << std::endl; + + fmt::print(" FsAccess:\n"); + fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(fs_access_str_list, 60, 4)); } if (fac.getContentOwnerIdList().size()) { - std::cout << " Content Owner IDs:" << std::endl; + fmt::print(" Content Owner IDs:\n"); for (size_t i = 0; i < fac.getContentOwnerIdList().size(); i++) { - std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << fac.getContentOwnerIdList()[i] << std::endl; + fmt::print(" 0x{:016x}\n", fac.getContentOwnerIdList()[i]); } } if (fac.getSaveDataOwnerIdList().size()) { - std::cout << " Save Data Owner IDs:" << std::endl; + fmt::print(" Save Data Owner IDs:\n"); for (size_t i = 0; i < fac.getSaveDataOwnerIdList().size(); i++) { - std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << fac.getSaveDataOwnerIdList()[i].id << " (" << nn::hac::FileSystemAccessUtil::getSaveDataOwnerAccessModeAsString(fac.getSaveDataOwnerIdList()[i].access_type) << ")" << std::endl; + fmt::print(" 0x{:016x} ({:s})\n", fac.getSaveDataOwnerIdList()[i].id, nn::hac::FileSystemAccessUtil::getSaveDataOwnerAccessModeAsString(fac.getSaveDataOwnerIdList()[i].access_type)); } } - } void nstool::MetaProcess::displaySac(const nn::hac::ServiceAccessControl& sac) { - std::cout << "[Service Access Control]" << std::endl; - std::cout << " Service List:" << std::endl; + fmt::print("[Service Access Control]\n"); + fmt::print(" Service List:\n"); std::vector service_name_list; for (size_t i = 0; i < sac.getServiceList().size(); i++) { service_name_list.push_back(sac.getServiceList()[i].getName() + (sac.getServiceList()[i].isServer() ? "(isSrv)" : "")); } - fnd::SimpleTextOutput::dumpStringList(service_name_list, 60, 4); + fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(service_name_list, 60, 4)); } void nstool::MetaProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern) { - std::cout << "[Kernel Capabilities]" << std::endl; + fmt::print("[Kernel Capabilities]\n"); if (kern.getThreadInfo().isSet()) { nn::hac::ThreadInfoHandler threadInfo = kern.getThreadInfo(); - std::cout << " Thread Priority:" << std::endl; - std::cout << " Min: " << std::dec << (uint32_t)threadInfo.getMinPriority() << std::endl; - std::cout << " Max: " << std::dec << (uint32_t)threadInfo.getMaxPriority() << std::endl; - std::cout << " CpuId:" << std::endl; - std::cout << " Min: " << std::dec << (uint32_t)threadInfo.getMinCpuId() << std::endl; - std::cout << " Max: " << std::dec << (uint32_t)threadInfo.getMaxCpuId() << std::endl; + fmt::print(" Thread Priority:\n"); + fmt::print(" Min: {:d}\n", threadInfo.getMinPriority()); + fmt::print(" Max: {:d}\n", threadInfo.getMaxPriority()); + fmt::print(" CpuId:\n"); + fmt::print(" Min: {:d}\n", threadInfo.getMinCpuId()); + fmt::print(" Max: {:d}\n", threadInfo.getMaxCpuId()); } if (kern.getSystemCalls().isSet()) { auto syscall_ids = kern.getSystemCalls().getSystemCallIds(); - std::cout << " SystemCalls:" << std::endl; + fmt::print(" SystemCalls:\n"); std::vector syscall_names; for (size_t syscall_id = 0; syscall_id < syscall_ids.size(); syscall_id++) { if (syscall_ids.test(syscall_id)) syscall_names.push_back(nn::hac::KernelCapabilityUtil::getSystemCallIdAsString(nn::hac::kc::SystemCallId(syscall_id))); } - fnd::SimpleTextOutput::dumpStringList(syscall_names, 60, 4); + fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(syscall_names, 60, 4)); } if (kern.getMemoryMaps().isSet()) { auto maps = kern.getMemoryMaps().getMemoryMaps(); auto ioMaps = kern.getMemoryMaps().getIoMemoryMaps(); - std::cout << " MemoryMaps:" << std::endl; + fmt::print(" MemoryMaps:\n"); for (size_t i = 0; i < maps.size(); i++) { - std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)maps[i].addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(maps[i].addr + maps[i].size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(maps[i].perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(maps[i].type) << ")" << std::endl; + fmt::print(" {:s}\n", formatMappingAsString(maps[i])); } - //std::cout << " IoMaps:" << std::endl; + //fmt::print(" IoMaps:\n"); for (size_t i = 0; i < ioMaps.size(); i++) { - std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)ioMaps[i].addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(ioMaps[i].addr + ioMaps[i].size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(ioMaps[i].perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(ioMaps[i].type) << ")" << std::endl; + fmt::print(" {:s}\n", formatMappingAsString(ioMaps[i])); } } if (kern.getInterupts().isSet()) { - std::vector interupts = kern.getInterupts().getInteruptList(); - std::cout << " Interupts Flags:" << std::endl; - for (uint32_t i = 0; i < interupts.size(); i++) + std::vector interupts; + for (auto itr = kern.getInterupts().getInteruptList().begin(); itr != kern.getInterupts().getInteruptList().end(); itr++) { - if (i % 10 == 0) - { - if (i != 0) - std::cout << std::endl; - std::cout << " "; - } - std::cout << "0x" << std::hex << (uint32_t)interupts[i]; - if (interupts[i] != interupts.atBack()) - std::cout << ", "; + interupts.push_back(fmt::format("0x{:x}", *itr)); } - std::cout << std::endl; + fmt::print(" Interupts Flags:\n"); + fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(interupts, 60, 4)); } if (kern.getMiscParams().isSet()) { - std::cout << " ProgramType: " << nn::hac::KernelCapabilityUtil::getProgramTypeAsString(kern.getMiscParams().getProgramType()) << " (" << std::dec << (uint32_t)kern.getMiscParams().getProgramType() << ")" << std::endl; + fmt::print(" ProgramType: {:s} ({:d})\n", nn::hac::KernelCapabilityUtil::getProgramTypeAsString(kern.getMiscParams().getProgramType()), kern.getMiscParams().getProgramType()); } if (kern.getKernelVersion().isSet()) { - std::cout << " Kernel Version: " << std::dec << (uint32_t)kern.getKernelVersion().getVerMajor() << "." << (uint32_t)kern.getKernelVersion().getVerMinor() << std::endl; + fmt::print(" Kernel Version: {:d}.{:d}\n", kern.getKernelVersion().getVerMajor(), kern.getKernelVersion().getVerMinor()); } if (kern.getHandleTableSize().isSet()) { - std::cout << " Handle Table Size: 0x" << std::hex << kern.getHandleTableSize().getHandleTableSize() << std::endl; + fmt::print(" Handle Table Size: 0x{:x}\n", kern.getHandleTableSize().getHandleTableSize()); } if (kern.getMiscFlags().isSet()) { auto misc_flags = kern.getMiscFlags().getMiscFlags(); - std::cout << " Misc Flags:" << std::endl; + fmt::print(" Misc Flags:\n"); std::vector misc_flags_names; for (size_t misc_flags_bit = 0; misc_flags_bit < misc_flags.size(); misc_flags_bit++) { if (misc_flags.test(misc_flags_bit)) misc_flags_names.push_back(nn::hac::KernelCapabilityUtil::getMiscFlagsBitAsString(nn::hac::kc::MiscFlagsBit(misc_flags_bit))); } - fnd::SimpleTextOutput::dumpStringList(misc_flags_names, 60, 4); + fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(misc_flags_names, 60, 4)); } +} + +std::string nstool::MetaProcess::formatMappingAsString(const nn::hac::MemoryMappingHandler::sMemoryMapping& map) const +{ + return fmt::format("0x{:016x} - 0x{:016x} (perm={:s}) (type={:s})", ((uint64_t)map.addr << 12), (((uint64_t)(map.addr + map.size) << 12) - 1), nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(map.perm), nn::hac::KernelCapabilityUtil::getMappingTypeAsString(map.type)); } \ No newline at end of file diff --git a/src/MetaProcess.h b/src/MetaProcess.h index 56155a9..3b83797 100644 --- a/src/MetaProcess.h +++ b/src/MetaProcess.h @@ -21,7 +21,7 @@ public: const nn::hac::Meta& getMeta() const; private: - const std::string kModuleName = "MetaProcess"; + std::string mModuleName; std::shared_ptr mFile; KeyBag mKeyCfg; @@ -41,6 +41,8 @@ private: void displayFac(const nn::hac::FileSystemAccessControl& fac); void displaySac(const nn::hac::ServiceAccessControl& sac); void displayKernelCap(const nn::hac::KernelCapabilityControl& kern); + + std::string formatMappingAsString(const nn::hac::MemoryMappingHandler::sMemoryMapping& map) const; }; } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index be46c10..2a0e23e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,7 @@ #include "PfsProcess.h" #include "RomfsProcess.h" //#include "NcaProcess.h" -//#include "MetaProcess.h" +#include "MetaProcess.h" #include "CnmtProcess.h" #include "NsoProcess.h" #include "NroProcess.h" @@ -93,6 +93,7 @@ int umain(const std::vector& args, const std::vector& obj.process(); } + */ else if (set.infile.filetype == nstool::Settings::FILE_TYPE_META) { nstool::MetaProcess obj; @@ -104,7 +105,6 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - */ else if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT) { nstool::CnmtProcess obj; From 054776b58616d48c882f10cc1864b348c2410d7e Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 12 Oct 2021 14:26:19 +0800 Subject: [PATCH 052/119] Move IniProcess and KipProcess --- build/visualstudio/nstool/nstool.vcxproj | 2 + .../nstool/nstool.vcxproj.filters | 6 +++ src/{not_ported => }/IniProcess.cpp | 37 +++++++++++-------- src/IniProcess.h | 3 +- src/{not_ported => }/KipProcess.cpp | 0 5 files changed, 32 insertions(+), 16 deletions(-) rename src/{not_ported => }/IniProcess.cpp (76%) rename src/{not_ported => }/KipProcess.cpp (100%) diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index bd7757d..0b4e846 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -185,7 +185,9 @@ + + diff --git a/build/visualstudio/nstool/nstool.vcxproj.filters b/build/visualstudio/nstool/nstool.vcxproj.filters index a18c5e9..a6434e6 100644 --- a/build/visualstudio/nstool/nstool.vcxproj.filters +++ b/build/visualstudio/nstool/nstool.vcxproj.filters @@ -152,5 +152,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/src/not_ported/IniProcess.cpp b/src/IniProcess.cpp similarity index 76% rename from src/not_ported/IniProcess.cpp rename to src/IniProcess.cpp index d976309..86354f9 100644 --- a/src/not_ported/IniProcess.cpp +++ b/src/IniProcess.cpp @@ -10,6 +10,7 @@ nstool::IniProcess::IniProcess() : + mModuleName("nstool::IniProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false), @@ -27,7 +28,7 @@ void nstool::IniProcess::process() displayHeader(); displayKipList(); } - if (mDoExtractKip) + if (mKipExtractPath.isSet()) { extractKipList(); } @@ -48,29 +49,35 @@ void nstool::IniProcess::setVerifyMode(bool verify) mVerify = verify; } -void nstool::IniProcess::setKipExtractPath(const std::string& path) +void nstool::IniProcess::setKipExtractPath(const tc::io::Path& path) { - mDoExtractKip = true; mKipExtractPath = path; } void nstool::IniProcess::importHeader() { - tc::ByteData scratch; - - if (*mFile == nullptr) + if (mFile == nullptr) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "No file reader set."); + } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); } - if ((*mFile)->size() < sizeof(nn::hac::sIniHeader)) + // check if file_size is smaller than INI header size + size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); + if (file_size < sizeof(nn::hac::sIniHeader)) { - throw tc::Exception(kModuleName, "Corrupt INI: file too small"); + throw tc::Exception(mModuleName, "Corrupt NSO: file too small."); } - scratch.alloc(sizeof(nn::hac::sIniHeader)); - (*mFile)->read(scratch.data(), 0, scratch.size()); + // read ini + tc::ByteData scratch = tc::ByteData(sizeof(nn::hac::sIniHeader)); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + // parse ini header mHdr.fromBytes(scratch.data(), scratch.size()); } @@ -97,9 +104,9 @@ void nstool::IniProcess::importKipList() void nstool::IniProcess::displayHeader() { - std::cout << "[INI Header]" << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getSize() << std::endl; - std::cout << " KIP Num: " << std::dec << (uint32_t)mHdr.getKipNum() << std::endl; + fmt::print("[INI Header]\n"); + fmt::print(" Size: 0x{:x}\n", mHdr.getSize()); + fmt::print(" KIP Num: {:d}\n", mHdr.getKipNum()); } void nstool::IniProcess::displayKipList() @@ -123,7 +130,7 @@ void nstool::IniProcess::extractKipList() // allocate cache memory - cache.alloc(kCacheSize); + cache = tc::ByteData(kCacheSize); // make extract dir fnd::io::makeDirectory(mKipExtractPath); diff --git a/src/IniProcess.h b/src/IniProcess.h index 59499ae..e5ee8a3 100644 --- a/src/IniProcess.h +++ b/src/IniProcess.h @@ -19,10 +19,11 @@ public: void setKipExtractPath(const tc::io::Path& path); private: - const std::string kModuleName = "IniProcess"; const std::string kKipExtention = ".kip"; const size_t kCacheSize = 0x10000; + std::string mModuleName; + std::shared_ptr mFile; CliOutputMode mCliOutputMode; bool mVerify; diff --git a/src/not_ported/KipProcess.cpp b/src/KipProcess.cpp similarity index 100% rename from src/not_ported/KipProcess.cpp rename to src/KipProcess.cpp From afd1c972729d89f200cad2b2fbd9d5b0d6e1f86a Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 13 Oct 2021 13:39:45 +0800 Subject: [PATCH 053/119] Misc --- src/IniProcess.cpp | 26 +++++++++----------------- src/IniProcess.h | 9 +++++++-- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/IniProcess.cpp b/src/IniProcess.cpp index 86354f9..38a2222 100644 --- a/src/IniProcess.cpp +++ b/src/IniProcess.cpp @@ -1,20 +1,12 @@ -#include -#include -#include -#include -#include -#include -#include #include "IniProcess.h" -#include "KipProcess.h" +#include "KipProcess.h" nstool::IniProcess::IniProcess() : mModuleName("nstool::IniProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false), - mDoExtractKip(false), mKipExtractPath() { } @@ -84,20 +76,20 @@ void nstool::IniProcess::importHeader() void nstool::IniProcess::importKipList() { // kip pos info - size_t kip_pos = sizeof(nn::hac::sIniHeader); - size_t kip_size = 0; + int64_t kip_pos = sizeof(nn::hac::sIniHeader); + int64_t kip_size = 0; // tmp data to determine size - tc::ByteData hdr_raw; + nn::hac::sKipHeader hdr_raw; nn::hac::KernelInitialProcessHeader hdr; - hdr_raw.alloc(sizeof(nn::hac::sKipHeader)); for (size_t i = 0; i < mHdr.getKipNum(); i++) { - (*mFile)->read(hdr_raw.data(), kip_pos, hdr_raw.size()); - hdr.fromBytes(hdr_raw.data(), hdr_raw.size()); + mFile->seek(kip_pos, tc::io::SeekOrigin::Begin); + mFile->read((byte_t*)&hdr_raw, sizeof(hdr_raw)); + hdr.fromBytes((byte_t*)&hdr_raw, sizeof(hdr_raw)); kip_size = getKipSizeFromHeader(hdr); - mKipList.push_back(new fnd::OffsetAdjustedIFile(mFile, kip_pos, kip_size)); + mKipList.push_back(std::make_shared(tc::io::SubStream(mFile, kip_pos, kip_size))); kip_pos += kip_size; } } @@ -171,7 +163,7 @@ void nstool::IniProcess::extractKipList() } } -size_t nstool::IniProcess::getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const +int64_t nstool::IniProcess::getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const { return sizeof(nn::hac::sKipHeader) + hdr.getTextSegmentInfo().file_layout.size + hdr.getRoSegmentInfo().file_layout.size + hdr.getDataSegmentInfo().file_layout.size; } \ No newline at end of file diff --git a/src/IniProcess.h b/src/IniProcess.h index e5ee8a3..747df38 100644 --- a/src/IniProcess.h +++ b/src/IniProcess.h @@ -31,7 +31,12 @@ private: tc::Optional mKipExtractPath; nn::hac::IniHeader mHdr; - std::vector> mKipList; + struct InnerKipInfo + { + nn::hac::KernelInitialProcessHeader hdr; + std::shared_ptr stream; + }; + std::vector mKipList; void importHeader(); void importKipList(); @@ -39,7 +44,7 @@ private: void displayKipList(); void extractKipList(); - size_t getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const; + int64_t getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const; }; } \ No newline at end of file From defccc6ddf0617d9f7b4dfd27bf76e75180ce46d Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 13 Oct 2021 15:33:47 +0800 Subject: [PATCH 054/119] Port IniProcess & KipProcess to libtoolchain. --- src/IniProcess.cpp | 65 +++++------- src/KipProcess.cpp | 245 +++++++++++++++++++++++---------------------- src/KipProcess.h | 5 +- 3 files changed, 153 insertions(+), 162 deletions(-) diff --git a/src/IniProcess.cpp b/src/IniProcess.cpp index 38a2222..cf329c8 100644 --- a/src/IniProcess.cpp +++ b/src/IniProcess.cpp @@ -1,5 +1,6 @@ #include "IniProcess.h" +#include "util.h" #include "KipProcess.h" nstool::IniProcess::IniProcess() : @@ -58,10 +59,9 @@ void nstool::IniProcess::importHeader() } // check if file_size is smaller than INI header size - size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length()); - if (file_size < sizeof(nn::hac::sIniHeader)) + if (tc::io::IOUtil::castInt64ToSize(mFile->length()) < sizeof(nn::hac::sIniHeader)) { - throw tc::Exception(mModuleName, "Corrupt NSO: file too small."); + throw tc::Exception(mModuleName, "Corrupt INI: file too small."); } // read ini @@ -76,7 +76,7 @@ void nstool::IniProcess::importHeader() void nstool::IniProcess::importKipList() { // kip pos info - int64_t kip_pos = sizeof(nn::hac::sIniHeader); + int64_t kip_pos = tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sIniHeader)); int64_t kip_size = 0; // tmp data to determine size @@ -89,7 +89,7 @@ void nstool::IniProcess::importKipList() mFile->read((byte_t*)&hdr_raw, sizeof(hdr_raw)); hdr.fromBytes((byte_t*)&hdr_raw, sizeof(hdr_raw)); kip_size = getKipSizeFromHeader(hdr); - mKipList.push_back(std::make_shared(tc::io::SubStream(mFile, kip_pos, kip_size))); + mKipList.push_back({hdr, std::make_shared(tc::io::SubStream(mFile, kip_pos, kip_size))}); kip_pos += kip_size; } } @@ -103,11 +103,11 @@ void nstool::IniProcess::displayHeader() void nstool::IniProcess::displayKipList() { - for (size_t i = 0; i < mKipList.size(); i++) + for (auto itr = mKipList.begin(); itr != mKipList.end(); itr++) { KipProcess obj; - obj.setInputFile(mKipList[i]); + obj.setInputFile(itr->stream); obj.setCliOutputMode(mCliOutputMode); obj.setVerifyMode(mVerify); @@ -117,53 +117,34 @@ void nstool::IniProcess::displayKipList() void nstool::IniProcess::extractKipList() { - tc::ByteData cache; - nn::hac::KernelInitialProcessHeader hdr; - - // allocate cache memory - cache = tc::ByteData(kCacheSize); + tc::ByteData cache = tc::ByteData(kCacheSize); // make extract dir - fnd::io::makeDirectory(mKipExtractPath); - + tc::io::LocalStorage local_fs; + local_fs.createDirectory(mKipExtractPath.get()); - // outfile object for writing KIP - fnd::SimpleFile out_file; - std::string out_path; - size_t out_size; + // out path for extracted KIP + tc::io::Path out_path; + std::string out_path_str; - for (size_t i = 0; i < mKipList.size(); i++) + // extract KIPs + for (auto itr = mKipList.begin(); itr != mKipList.end(); itr++) { - // read header - (*mKipList[i])->read(cache.data(), 0, cache.size()); - hdr.fromBytes(cache.data(), cache.size()); + out_path = mKipExtractPath.get(); + out_path += fmt::format("{:s}.kip", itr->hdr.getName()); - // generate path - out_path.clear(); - fnd::io::appendToPath(out_path, mKipExtractPath); - fnd::io::appendToPath(out_path, hdr.getName() + kKipExtention); + tc::io::PathUtil::pathToUnixUTF8(out_path, out_path_str); - // open file - out_file.open(out_path, fnd::SimpleFile::Create); - - // get kip file size - out_size = (*mKipList[i])->size(); - // extract kip if (mCliOutputMode.show_basic_info) - printf("extract=[%s]\n", out_path.c_str()); + fmt::print("Saving {:s}...\n", out_path_str); - (*mKipList[i])->seek(0); - for (size_t j = 0; j < ((out_size / kCacheSize) + ((out_size % kCacheSize) != 0)); j++) - { - (*mKipList[i])->read(cache.data(), _MIN(out_size - (kCacheSize * j), kCacheSize)); - out_file.write(cache.data(), _MIN(out_size - (kCacheSize * j), kCacheSize)); - } - out_file.close(); + writeStreamToFile(itr->stream, out_path, cache); } } int64_t nstool::IniProcess::getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const -{ - return sizeof(nn::hac::sKipHeader) + hdr.getTextSegmentInfo().file_layout.size + hdr.getRoSegmentInfo().file_layout.size + hdr.getDataSegmentInfo().file_layout.size; +{ + // the order of elements in a KIP are sequential, there are no file offsets + return int64_t(sizeof(nn::hac::sKipHeader)) + int64_t(hdr.getTextSegmentInfo().file_layout.size + hdr.getRoSegmentInfo().file_layout.size + hdr.getDataSegmentInfo().file_layout.size); } \ No newline at end of file diff --git a/src/KipProcess.cpp b/src/KipProcess.cpp index dbaae88..2b7468b 100644 --- a/src/KipProcess.cpp +++ b/src/KipProcess.cpp @@ -1,15 +1,11 @@ #include "KipProcess.h" -#include -#include - -#include -#include -#include - #include -nstool::KipProcess::KipProcess(): +#include + +nstool::KipProcess::KipProcess() : + mModuleName("nstool::KipProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false) @@ -19,7 +15,7 @@ nstool::KipProcess::KipProcess(): void nstool::KipProcess::process() { importHeader(); - //importCodeSegments(); + //importCodeSegments(); // code segments not imported because compression not supported yet if (mCliOutputMode.show_basic_info) { displayHeader(); @@ -44,223 +40,234 @@ void nstool::KipProcess::setVerifyMode(bool verify) void nstool::KipProcess::importHeader() { - tc::ByteData scratch; - - if (*mFile == nullptr) + if (mFile == nullptr) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "No file reader set."); + } + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); } - if ((*mFile)->size() < sizeof(nn::hac::sKipHeader)) + // check if file_size is smaller than KIP header size + if (tc::io::IOUtil::castInt64ToSize(mFile->length()) < sizeof(nn::hac::sKipHeader)) { - throw tc::Exception(kModuleName, "Corrupt KIP: file too small"); + throw tc::Exception(mModuleName, "Corrupt KIP: file too small."); } - scratch.alloc(sizeof(nn::hac::sKipHeader)); - (*mFile)->read(scratch.data(), 0, scratch.size()); + // read kip + tc::ByteData scratch = tc::ByteData(sizeof(nn::hac::sKipHeader)); + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + // parse kip header mHdr.fromBytes(scratch.data(), scratch.size()); } void nstool::KipProcess::importCodeSegments() { -#ifdef _KIP_COMPRESSION_IMPLEMENTED tc::ByteData scratch; - uint32_t decompressed_len; -#endif // process text segment -#ifdef _KIP_COMPRESSION_IMPLEMENTED if (mHdr.getTextSegmentInfo().is_compressed) { - scratch.alloc(mHdr.getTextSegmentInfo().file_layout.size); - (*mFile)->read(scratch.data(), mHdr.getTextSegmentInfo().file_layout.offset, scratch.size()); - mTextBlob.alloc(mHdr.getTextSegmentInfo().memory_layout.size); - fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mTextBlob.data(), (uint32_t)mTextBlob.size(), decompressed_len); - if (decompressed_len != mTextBlob.size()) + // allocate/read compressed text + scratch = tc::ByteData(mHdr.getTextSegmentInfo().file_layout.size); + mFile->seek(mHdr.getTextSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // allocate for decompressed text segment + mTextBlob = tc::ByteData(mHdr.getTextSegmentInfo().memory_layout.size); + + // decompress text segment + if (decompressData(scratch.data(), scratch.size(), mTextBlob.data(), mTextBlob.size()) != mTextBlob.size()) { - throw tc::Exception(kModuleName, "KIP text segment failed to decompress"); + throw tc::Exception(mModuleName, "KIP text segment failed to decompress"); } } else { - mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size); - (*mFile)->read(mTextBlob.data(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.size()); + // read text segment directly (not compressed) + mTextBlob = tc::ByteData(mHdr.getTextSegmentInfo().file_layout.size); + mFile->seek(mHdr.getTextSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(mTextBlob.data(), mTextBlob.size()); } -#else - mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size); - (*mFile)->read(mTextBlob.data(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.size()); -#endif // process ro segment -#ifdef _KIP_COMPRESSION_IMPLEMENTED if (mHdr.getRoSegmentInfo().is_compressed) { - scratch.alloc(mHdr.getRoSegmentInfo().file_layout.size); - (*mFile)->read(scratch.data(), mHdr.getRoSegmentInfo().file_layout.offset, scratch.size()); - mRoBlob.alloc(mHdr.getRoSegmentInfo().memory_layout.size); - fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mRoBlob.data(), (uint32_t)mRoBlob.size(), decompressed_len); - if (decompressed_len != mRoBlob.size()) + // allocate/read compressed ro segment + scratch = tc::ByteData(mHdr.getRoSegmentInfo().file_layout.size); + mFile->seek(mHdr.getRoSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // allocate for decompressed ro segment + mRoBlob = tc::ByteData(mHdr.getRoSegmentInfo().memory_layout.size); + + // decompress ro segment + if (decompressData(scratch.data(), scratch.size(), mRoBlob.data(), mRoBlob.size()) != mRoBlob.size()) { - throw tc::Exception(kModuleName, "KIP ro segment failed to decompress"); + throw tc::Exception(mModuleName, "KIP ro segment failed to decompress"); } } else { - mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size); - (*mFile)->read(mRoBlob.data(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.size()); + // read ro segment directly (not compressed) + mRoBlob = tc::ByteData(mHdr.getRoSegmentInfo().file_layout.size); + mFile->seek(mHdr.getRoSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(mRoBlob.data(), mRoBlob.size()); } -#else - mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size); - (*mFile)->read(mRoBlob.data(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.size()); -#endif - // process data segment -#ifdef _KIP_COMPRESSION_IMPLEMENTED + // process ro segment if (mHdr.getDataSegmentInfo().is_compressed) { - scratch.alloc(mHdr.getDataSegmentInfo().file_layout.size); - (*mFile)->read(scratch.data(), mHdr.getDataSegmentInfo().file_layout.offset, scratch.size()); - mDataBlob.alloc(mHdr.getDataSegmentInfo().memory_layout.size); - fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mDataBlob.data(), (uint32_t)mDataBlob.size(), decompressed_len); - if (decompressed_len != mDataBlob.size()) + // allocate/read compressed ro segment + scratch = tc::ByteData(mHdr.getDataSegmentInfo().file_layout.size); + mFile->seek(mHdr.getDataSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(scratch.data(), scratch.size()); + + // allocate for decompressed ro segment + mDataBlob = tc::ByteData(mHdr.getDataSegmentInfo().memory_layout.size); + + // decompress ro segment + if (decompressData(scratch.data(), scratch.size(), mDataBlob.data(), mDataBlob.size()) != mDataBlob.size()) { - throw tc::Exception(kModuleName, "KIP data segment failed to decompress"); + throw tc::Exception(mModuleName, "KIP data segment failed to decompress"); } } else { - mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size); - (*mFile)->read(mDataBlob.data(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.size()); + // read ro segment directly (not compressed) + mDataBlob = tc::ByteData(mHdr.getDataSegmentInfo().file_layout.size); + mFile->seek(mHdr.getDataSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin); + mFile->read(mDataBlob.data(), mDataBlob.size()); } -#else - mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size); - (*mFile)->read(mDataBlob.data(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.size()); -#endif +} + +size_t nstool::KipProcess::decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity) +{ + throw tc::NotImplementedException(mModuleName, "KIP decompression not implemented yet."); } void nstool::KipProcess::displayHeader() { - std::cout << "[KIP Header]" << std::endl; - std::cout << " Meta:" << std::endl; - std::cout << " Name: " << mHdr.getName() << std::endl; - std::cout << " TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getTitleId() << std::endl; - std::cout << " Version: v" << std::dec << mHdr.getVersion() << std::endl; - std::cout << " Is64BitInstruction: " << std::boolalpha << mHdr.getIs64BitInstructionFlag() << std::endl; - std::cout << " Is64BitAddressSpace: " << std::boolalpha << mHdr.getIs64BitAddressSpaceFlag() << std::endl; - std::cout << " UseSecureMemory: " << std::boolalpha << mHdr.getUseSecureMemoryFlag() << std::endl; - std::cout << " Program Sections:" << std::endl; - std::cout << " .text:" << std::endl; + fmt::print("[KIP Header]\n"); + fmt::print(" Meta:\n"); + fmt::print(" Name: {:s}\n", mHdr.getName()); + fmt::print(" TitleId: 0x{:016x}\n", mHdr.getTitleId()); + fmt::print(" Version: v{:d}\n", mHdr.getVersion()); + fmt::print(" Is64BitInstruction: {}\n", mHdr.getIs64BitInstructionFlag()); + fmt::print(" Is64BitAddressSpace: {}\n", mHdr.getIs64BitAddressSpaceFlag()); + fmt::print(" UseSecureMemory: {}\n", mHdr.getUseSecureMemoryFlag()); + fmt::print(" Program Sections:\n"); + fmt::print(" .text:\n"); if (mCliOutputMode.show_layout) { - std::cout << " FileOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.offset << std::endl; - std::cout << " FileSize: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.size << (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl; + fmt::print(" FileOffset: 0x{:x}\n", mHdr.getTextSegmentInfo().file_layout.offset); + fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getTextSegmentInfo().file_layout.size, (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "")); } - std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.offset << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.size << std::endl; - std::cout << " .ro:" << std::endl; + fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getTextSegmentInfo().memory_layout.offset); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getTextSegmentInfo().memory_layout.size); + fmt::print(" .ro:\n"); if (mCliOutputMode.show_layout) { - std::cout << " FileOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.offset << std::endl; - std::cout << " FileSize: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.size << (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl; + fmt::print(" FileOffset: 0x{:x}\n", mHdr.getRoSegmentInfo().file_layout.offset); + fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getRoSegmentInfo().file_layout.size, (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "")); } - std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.offset << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.size << std::endl; - std::cout << " .data:" << std::endl; + fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoSegmentInfo().memory_layout.offset); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoSegmentInfo().memory_layout.size); + fmt::print(" .data:\n"); if (mCliOutputMode.show_layout) { - std::cout << " FileOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.offset << std::endl; - std::cout << " FileSize: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.size << (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl; + fmt::print(" FileOffset: 0x{:x}\n", mHdr.getDataSegmentInfo().file_layout.offset); + fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getDataSegmentInfo().file_layout.size, (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "")); } - std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.offset << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.size << std::endl; - std::cout << " .bss:" << std::endl; - std::cout << " MemorySize: 0x" << std::hex << mHdr.getBssSize() << std::endl; + fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getDataSegmentInfo().memory_layout.offset); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getDataSegmentInfo().memory_layout.size); + fmt::print(" .bss:\n"); + fmt::print(" MemorySize: 0x{:x}\n", mHdr.getBssSize()); } void nstool::KipProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern) { - std::cout << "[Kernel Capabilities]" << std::endl; + fmt::print("[Kernel Capabilities]\n"); if (kern.getThreadInfo().isSet()) { nn::hac::ThreadInfoHandler threadInfo = kern.getThreadInfo(); - std::cout << " Thread Priority:" << std::endl; - std::cout << " Min: " << std::dec << (uint32_t)threadInfo.getMinPriority() << std::endl; - std::cout << " Max: " << std::dec << (uint32_t)threadInfo.getMaxPriority() << std::endl; - std::cout << " CpuId:" << std::endl; - std::cout << " Min: " << std::dec << (uint32_t)threadInfo.getMinCpuId() << std::endl; - std::cout << " Max: " << std::dec << (uint32_t)threadInfo.getMaxCpuId() << std::endl; + fmt::print(" Thread Priority:\n"); + fmt::print(" Min: {:d}\n", threadInfo.getMinPriority()); + fmt::print(" Max: {:d}\n", threadInfo.getMaxPriority()); + fmt::print(" CpuId:\n"); + fmt::print(" Min: {:d}\n", threadInfo.getMinCpuId()); + fmt::print(" Max: {:d}\n", threadInfo.getMaxCpuId()); } if (kern.getSystemCalls().isSet()) { auto syscall_ids = kern.getSystemCalls().getSystemCallIds(); - std::cout << " SystemCalls:" << std::endl; + fmt::print(" SystemCalls:\n"); std::vector syscall_names; for (size_t syscall_id = 0; syscall_id < syscall_ids.size(); syscall_id++) { if (syscall_ids.test(syscall_id)) syscall_names.push_back(nn::hac::KernelCapabilityUtil::getSystemCallIdAsString(nn::hac::kc::SystemCallId(syscall_id))); } - fnd::SimpleTextOutput::dumpStringList(syscall_names, 60, 4); + fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(syscall_names, 60, 4)); } if (kern.getMemoryMaps().isSet()) { - std::vector maps = kern.getMemoryMaps().getMemoryMaps(); - std::vector ioMaps = kern.getMemoryMaps().getIoMemoryMaps(); + auto maps = kern.getMemoryMaps().getMemoryMaps(); + auto ioMaps = kern.getMemoryMaps().getIoMemoryMaps(); - std::cout << " MemoryMaps:" << std::endl; + fmt::print(" MemoryMaps:\n"); for (size_t i = 0; i < maps.size(); i++) { - std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)maps[i].addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(maps[i].addr + maps[i].size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(maps[i].perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(maps[i].type) << ")" << std::endl; + fmt::print(" {:s}\n", formatMappingAsString(maps[i])); } - //std::cout << " IoMaps:" << std::endl; + //fmt::print(" IoMaps:\n"); for (size_t i = 0; i < ioMaps.size(); i++) { - std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)ioMaps[i].addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(ioMaps[i].addr + ioMaps[i].size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(ioMaps[i].perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(ioMaps[i].type) << ")" << std::endl; + fmt::print(" {:s}\n", formatMappingAsString(ioMaps[i])); } } if (kern.getInterupts().isSet()) { - std::vector interupts = kern.getInterupts().getInteruptList(); - std::cout << " Interupts Flags:" << std::endl; - for (uint32_t i = 0; i < interupts.size(); i++) + std::vector interupts; + for (auto itr = kern.getInterupts().getInteruptList().begin(); itr != kern.getInterupts().getInteruptList().end(); itr++) { - if (i % 10 == 0) - { - if (i != 0) - std::cout << std::endl; - std::cout << " "; - } - std::cout << "0x" << std::hex << (uint32_t)interupts[i]; - if (interupts[i] != interupts.atBack()) - std::cout << ", "; + interupts.push_back(fmt::format("0x{:x}", *itr)); } - std::cout << std::endl; + fmt::print(" Interupts Flags:\n"); + fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(interupts, 60, 4)); } if (kern.getMiscParams().isSet()) { - std::cout << " ProgramType: " << nn::hac::KernelCapabilityUtil::getProgramTypeAsString(kern.getMiscParams().getProgramType()) << " (" << std::dec << (uint32_t)kern.getMiscParams().getProgramType() << ")" << std::endl; + fmt::print(" ProgramType: {:s} ({:d})\n", nn::hac::KernelCapabilityUtil::getProgramTypeAsString(kern.getMiscParams().getProgramType()), kern.getMiscParams().getProgramType()); } if (kern.getKernelVersion().isSet()) { - std::cout << " Kernel Version: " << std::dec << (uint32_t)kern.getKernelVersion().getVerMajor() << "." << (uint32_t)kern.getKernelVersion().getVerMinor() << std::endl; + fmt::print(" Kernel Version: {:d}.{:d}\n", kern.getKernelVersion().getVerMajor(), kern.getKernelVersion().getVerMinor()); } if (kern.getHandleTableSize().isSet()) { - std::cout << " Handle Table Size: 0x" << std::hex << kern.getHandleTableSize().getHandleTableSize() << std::endl; + fmt::print(" Handle Table Size: 0x{:x}\n", kern.getHandleTableSize().getHandleTableSize()); } if (kern.getMiscFlags().isSet()) { auto misc_flags = kern.getMiscFlags().getMiscFlags(); - std::cout << " Misc Flags:" << std::endl; + fmt::print(" Misc Flags:\n"); std::vector misc_flags_names; for (size_t misc_flags_bit = 0; misc_flags_bit < misc_flags.size(); misc_flags_bit++) { if (misc_flags.test(misc_flags_bit)) misc_flags_names.push_back(nn::hac::KernelCapabilityUtil::getMiscFlagsBitAsString(nn::hac::kc::MiscFlagsBit(misc_flags_bit))); } - fnd::SimpleTextOutput::dumpStringList(misc_flags_names, 60, 4); + fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(misc_flags_names, 60, 4)); } +} + +std::string nstool::KipProcess::formatMappingAsString(const nn::hac::MemoryMappingHandler::sMemoryMapping& map) const +{ + return fmt::format("0x{:016x} - 0x{:016x} (perm={:s}) (type={:s})", ((uint64_t)map.addr << 12), (((uint64_t)(map.addr + map.size) << 12) - 1), nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(map.perm), nn::hac::KernelCapabilityUtil::getMappingTypeAsString(map.type)); } \ No newline at end of file diff --git a/src/KipProcess.h b/src/KipProcess.h index cd844c9..b9bfd46 100644 --- a/src/KipProcess.h +++ b/src/KipProcess.h @@ -16,7 +16,7 @@ public: void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); private: - const std::string kModuleName = "KipProcess"; + std::string mModuleName; std::shared_ptr mFile; CliOutputMode mCliOutputMode; @@ -27,8 +27,11 @@ private: void importHeader(); void importCodeSegments(); + size_t decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity); void displayHeader(); void displayKernelCap(const nn::hac::KernelCapabilityControl& kern); + + std::string formatMappingAsString(const nn::hac::MemoryMappingHandler::sMemoryMapping& map) const; }; } \ No newline at end of file From 78262bc68006d813acda68c56b2a33a18a732cb2 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 13 Oct 2021 15:41:02 +0800 Subject: [PATCH 055/119] Misc --- src/AssetProcess.cpp | 18 ++++++------- src/AssetProcess.h | 5 ++-- src/CnmtProcess.h | 1 - src/EsTikProcess.h | 1 - src/FsProcess.cpp | 56 +++++++++++++++++++------------------- src/FsProcess.h | 4 +-- src/GameCardProcess.h | 5 ++-- src/PfsProcess.cpp | 62 +++++++++++++++++++++---------------------- src/PfsProcess.h | 4 +-- src/RomfsProcess.cpp | 62 +++++++++++++++++++++---------------------- src/RomfsProcess.h | 4 +-- 11 files changed, 109 insertions(+), 113 deletions(-) diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index 7111f5c..b384db7 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -8,7 +8,15 @@ nstool::AssetProcess::AssetProcess() : mCliOutputMode(true, false, false, false), mVerify(false) { -} +} + +void nstool::AssetProcess::process() +{ + importHeader(); + if (mCliOutputMode.show_basic_info) + displayHeader(); + processSections(); +} void nstool::AssetProcess::setInputFile(const std::shared_ptr& file) { @@ -45,14 +53,6 @@ void nstool::AssetProcess::setRomfsExtractJobs(const std::vector& file); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); @@ -21,9 +23,6 @@ public: void setRomfsShowFsTree(bool show_fs_tree); void setRomfsExtractJobs(const std::vector& extract_jobs); - - void process(); - private: std::string mModuleName; diff --git a/src/CnmtProcess.h b/src/CnmtProcess.h index bcb407d..ed38187 100644 --- a/src/CnmtProcess.h +++ b/src/CnmtProcess.h @@ -17,7 +17,6 @@ public: void setVerifyMode(bool verify); const nn::hac::ContentMeta& getContentMeta() const; - private: std::string mModuleName; diff --git a/src/EsTikProcess.h b/src/EsTikProcess.h index d146b65..1af1c30 100644 --- a/src/EsTikProcess.h +++ b/src/EsTikProcess.h @@ -20,7 +20,6 @@ public: void setCertificateChain(const std::vector>& certs); void setCliOutputMode(CliOutputMode mode); void setVerifyMode(bool verify); - private: std::string mModuleName; diff --git a/src/FsProcess.cpp b/src/FsProcess.cpp index d148336..f192589 100644 --- a/src/FsProcess.cpp +++ b/src/FsProcess.cpp @@ -19,6 +19,34 @@ nstool::FsProcess::FsProcess() : } +void nstool::FsProcess::process() +{ + if (mInputFs == nullptr) + { + throw tc::InvalidOperationException(mModuleLabel, "No input filesystem"); + } + + if (mShowFsInfo) + { + fmt::print("[{:s}]\n", mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem/Info"); + for (auto itr = mProperties.begin(); itr != mProperties.end(); itr++) + { + fmt::print(" {:s}\n", *itr); + } + } + + if (mShowFsTree) + { + printFs(); + } + + + if (mExtractJobs.empty() == false) + { + extractFs(); + } +} + void nstool::FsProcess::setInputFileSystem(const std::shared_ptr& input_fs) { mInputFs = input_fs; @@ -54,34 +82,6 @@ void nstool::FsProcess::setExtractJobs(const std::vector& ex mExtractJobs = extract_jobs; } -void nstool::FsProcess::process() -{ - if (mInputFs == nullptr) - { - throw tc::InvalidOperationException(mModuleLabel, "No input filesystem"); - } - - if (mShowFsInfo) - { - fmt::print("[{:s}]\n", mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem/Info"); - for (auto itr = mProperties.begin(); itr != mProperties.end(); itr++) - { - fmt::print(" {:s}\n", *itr); - } - } - - if (mShowFsTree) - { - printFs(); - } - - - if (mExtractJobs.empty() == false) - { - extractFs(); - } -} - void nstool::FsProcess::printFs() { fmt::print("[{:s}/Tree]\n", (mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem")); diff --git a/src/FsProcess.h b/src/FsProcess.h index d9b5e3b..70fc909 100644 --- a/src/FsProcess.h +++ b/src/FsProcess.h @@ -12,6 +12,8 @@ class FsProcess public: FsProcess(); + void process(); + void setInputFileSystem(const std::shared_ptr& input_fs); void setFsFormatName(const std::string& fs_format_name); void setFsProperties(const std::vector& properties); @@ -19,8 +21,6 @@ public: void setShowFsTree(bool show_fs_tree); void setFsRootLabel(const std::string& root_label); void setExtractJobs(const std::vector& extract_jobs); - - void process(); private: std::string mModuleLabel; diff --git a/src/GameCardProcess.h b/src/GameCardProcess.h index 0be04a8..7e984f5 100644 --- a/src/GameCardProcess.h +++ b/src/GameCardProcess.h @@ -12,6 +12,8 @@ class GameCardProcess public: GameCardProcess(); + void process(); + // generic void setInputFile(const std::shared_ptr& file); void setKeyCfg(const KeyBag& keycfg); @@ -21,9 +23,6 @@ public: // fs specific void setShowFsTree(bool show_fs_tree); void setExtractJobs(const std::vector extract_jobs); - - void process(); - private: const std::string kXciMountPointName = "gamecard"; diff --git a/src/PfsProcess.cpp b/src/PfsProcess.cpp index b32606a..6099951 100644 --- a/src/PfsProcess.cpp +++ b/src/PfsProcess.cpp @@ -20,37 +20,6 @@ nstool::PfsProcess::PfsProcess() : mFsProcess.setFsFormatName("PartitionFS"); } -void nstool::PfsProcess::setInputFile(const std::shared_ptr& file) -{ - mFile = file; -} - -void nstool::PfsProcess::setCliOutputMode(CliOutputMode type) -{ - mCliOutputMode = type; - mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info); -} - -void nstool::PfsProcess::setVerifyMode(bool verify) -{ - mVerify = verify; -} - -void nstool::PfsProcess::setShowFsTree(bool show_fs_tree) -{ - mFsProcess.setShowFsTree(show_fs_tree); -} - -void nstool::PfsProcess::setFsRootLabel(const std::string& root_label) -{ - mFsProcess.setFsRootLabel(root_label); -} - -void nstool::PfsProcess::setExtractJobs(const std::vector& extract_jobs) -{ - mFsProcess.setExtractJobs(extract_jobs); -} - void nstool::PfsProcess::process() { if (mFile == nullptr) @@ -105,6 +74,37 @@ void nstool::PfsProcess::process() mFsProcess.process(); } +void nstool::PfsProcess::setInputFile(const std::shared_ptr& file) +{ + mFile = file; +} + +void nstool::PfsProcess::setCliOutputMode(CliOutputMode type) +{ + mCliOutputMode = type; + mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info); +} + +void nstool::PfsProcess::setVerifyMode(bool verify) +{ + mVerify = verify; +} + +void nstool::PfsProcess::setShowFsTree(bool show_fs_tree) +{ + mFsProcess.setShowFsTree(show_fs_tree); +} + +void nstool::PfsProcess::setFsRootLabel(const std::string& root_label) +{ + mFsProcess.setFsRootLabel(root_label); +} + +void nstool::PfsProcess::setExtractJobs(const std::vector& extract_jobs) +{ + mFsProcess.setExtractJobs(extract_jobs); +} + const nn::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const { return mPfs; diff --git a/src/PfsProcess.h b/src/PfsProcess.h index 00b205c..e3ff3c5 100644 --- a/src/PfsProcess.h +++ b/src/PfsProcess.h @@ -11,6 +11,8 @@ class PfsProcess public: PfsProcess(); + void process(); + // generic void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); @@ -21,8 +23,6 @@ public: void setFsRootLabel(const std::string& root_label); void setExtractJobs(const std::vector& extract_jobs); - void process(); - // post process() get PFS/FS out const nn::hac::PartitionFsHeader& getPfsHeader() const; const std::shared_ptr& getFileSystem() const; diff --git a/src/RomfsProcess.cpp b/src/RomfsProcess.cpp index 23e737f..1c5bfe1 100644 --- a/src/RomfsProcess.cpp +++ b/src/RomfsProcess.cpp @@ -18,37 +18,6 @@ nstool::RomfsProcess::RomfsProcess() : mFsProcess.setFsFormatName("RomFS"); } -void nstool::RomfsProcess::setInputFile(const std::shared_ptr& file) -{ - mFile = file; -} - -void nstool::RomfsProcess::setCliOutputMode(CliOutputMode type) -{ - mCliOutputMode = type; - mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info); -} - -void nstool::RomfsProcess::setVerifyMode(bool verify) -{ - mVerify = verify; -} - -void nstool::RomfsProcess::setFsRootLabel(const std::string& root_label) -{ - mFsProcess.setFsRootLabel(root_label); -} - -void nstool::RomfsProcess::setExtractJobs(const std::vector& extract_jobs) -{ - mFsProcess.setExtractJobs(extract_jobs); -} - -void nstool::RomfsProcess::setShowFsTree(bool list_fs) -{ - mFsProcess.setShowFsTree(list_fs); -} - void nstool::RomfsProcess::process() { if (mFile == nullptr) @@ -125,4 +94,35 @@ void nstool::RomfsProcess::process() // process filesystem mFsProcess.process(); +} + +void nstool::RomfsProcess::setInputFile(const std::shared_ptr& file) +{ + mFile = file; +} + +void nstool::RomfsProcess::setCliOutputMode(CliOutputMode type) +{ + mCliOutputMode = type; + mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info); +} + +void nstool::RomfsProcess::setVerifyMode(bool verify) +{ + mVerify = verify; +} + +void nstool::RomfsProcess::setFsRootLabel(const std::string& root_label) +{ + mFsProcess.setFsRootLabel(root_label); +} + +void nstool::RomfsProcess::setExtractJobs(const std::vector& extract_jobs) +{ + mFsProcess.setExtractJobs(extract_jobs); +} + +void nstool::RomfsProcess::setShowFsTree(bool list_fs) +{ + mFsProcess.setShowFsTree(list_fs); } \ No newline at end of file diff --git a/src/RomfsProcess.h b/src/RomfsProcess.h index 6f2304c..9d99609 100644 --- a/src/RomfsProcess.h +++ b/src/RomfsProcess.h @@ -11,6 +11,8 @@ class RomfsProcess public: RomfsProcess(); + void process(); + // generic void setInputFile(const std::shared_ptr& file); void setCliOutputMode(CliOutputMode type); @@ -20,8 +22,6 @@ public: void setFsRootLabel(const std::string& root_label); void setExtractJobs(const std::vector& extract_jobs); void setShowFsTree(bool show_fs_tree); - - void process(); private: static const size_t kCacheSize = 0x10000; From 6666eb74d76614ed9e599e5884593fac1750a0e6 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 13 Oct 2021 16:14:41 +0800 Subject: [PATCH 056/119] Enable IniProcess and KipProcess in main() --- src/main.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2a0e23e..be71960 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,8 +12,8 @@ #include "NsoProcess.h" #include "NroProcess.h" #include "NacpProcess.h" -//#include "IniProcess.h" -//#include "KipProcess.h" +#include "IniProcess.h" +#include "KipProcess.h" #include "PkiCertProcess.h" #include "EsTikProcess.h" #include "AssetProcess.h" @@ -161,7 +161,6 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - /* else if (set.infile.filetype == nstool::Settings::FILE_TYPE_INI) { nstool::IniProcess obj; @@ -185,7 +184,6 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - */ else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PKI_CERT) { nstool::PkiCertProcess obj; From 29aeb8e6af9fc8d13558d42d0d7891979644588e Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 13 Oct 2021 16:16:05 +0800 Subject: [PATCH 057/119] Move NcaProcess --- build/visualstudio/nstool/nstool.vcxproj | 1 + build/visualstudio/nstool/nstool.vcxproj.filters | 3 +++ src/{not_ported => }/NcaProcess.cpp | 0 3 files changed, 4 insertions(+) rename src/{not_ported => }/NcaProcess.cpp (100%) diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index 0b4e846..309584a 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -191,6 +191,7 @@ + diff --git a/build/visualstudio/nstool/nstool.vcxproj.filters b/build/visualstudio/nstool/nstool.vcxproj.filters index a6434e6..f8362e7 100644 --- a/build/visualstudio/nstool/nstool.vcxproj.filters +++ b/build/visualstudio/nstool/nstool.vcxproj.filters @@ -158,5 +158,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/src/not_ported/NcaProcess.cpp b/src/NcaProcess.cpp similarity index 100% rename from src/not_ported/NcaProcess.cpp rename to src/NcaProcess.cpp From de4b75db37aa0b31e9d1f131068103b86b513924 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 13 Oct 2021 16:17:46 +0800 Subject: [PATCH 058/119] Misc --- src/IniProcess.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/IniProcess.h b/src/IniProcess.h index 747df38..b717e5b 100644 --- a/src/IniProcess.h +++ b/src/IniProcess.h @@ -19,7 +19,6 @@ public: void setKipExtractPath(const tc::io::Path& path); private: - const std::string kKipExtention = ".kip"; const size_t kCacheSize = 0x10000; std::string mModuleName; From 91ac1937bf76e5053a0e5b956f36842950f3a203 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 15 Oct 2021 15:58:31 +0800 Subject: [PATCH 059/119] Update libnintendo-hac --- deps/libnintendo-hac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index c3fcfdb..16aa32f 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit c3fcfdb3711ded6e4b69b352e039d50b0be54355 +Subproject commit 16aa32f02e7a3c5f0580b59ae5ab330a62a0b61d From 83e2403dbc49f01254238b0c58af07b1b3e48d06 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 15 Oct 2021 17:29:29 +0800 Subject: [PATCH 060/119] First pass NcaProcess impl --- deps/libnintendo-hac | 2 +- src/NcaProcess.cpp | 452 +++++++++++++++++++++---------------------- src/NcaProcess.h | 36 ++-- src/main.cpp | 7 +- 4 files changed, 251 insertions(+), 246 deletions(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 16aa32f..6fc9b94 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 16aa32f02e7a3c5f0580b59ae5ab330a62a0b61d +Subproject commit 6fc9b94f377d41efceef9713215f5c7be9042d9d diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index e0019e3..a6ff3a2 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -4,30 +4,30 @@ #include "RomfsProcess.h" #include "MetaProcess.h" -#include -#include -#include +//#include +//#include +//#include -#include -#include -#include -#include +//#include +//#include +//#include +//#include #include #include #include #include +#include +#include nstool::NcaProcess::NcaProcess() : + mModuleName("nstool::NcaProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false), - mListFs(false) + mFileSystem(), + mFsProcess() { - for (size_t i = 0; i < nn::hac::nca::kPartitionNum; i++) - { - mPartitionPath[i].doExtract = false; - } } void nstool::NcaProcess::process() @@ -73,52 +73,54 @@ void nstool::NcaProcess::setVerifyMode(bool verify) mVerify = verify; } -void nstool::NcaProcess::setPartition0ExtractPath(const std::string& path) +void nstool::NcaProcess::setShowFsTree(bool show_fs_tree) { - mPartitionPath[0].path = path; - mPartitionPath[0].doExtract = true; + mFsProcess.setShowFsTree(show_fs_tree); } -void nstool::NcaProcess::setPartition1ExtractPath(const std::string& path) +void nstool::NcaProcess::setFsRootLabel(const std::string& root_label) { - mPartitionPath[1].path = path; - mPartitionPath[1].doExtract = true; + mFsProcess.setFsRootLabel(root_label); } -void nstool::NcaProcess::setPartition2ExtractPath(const std::string& path) +void nstool::NcaProcess::setExtractJobs(const std::vector& extract_jobs) { - mPartitionPath[2].path = path; - mPartitionPath[2].doExtract = true; + mFsProcess.setExtractJobs(extract_jobs); } -void nstool::NcaProcess::setPartition3ExtractPath(const std::string& path) +const std::shared_ptr& nstool::NcaProcess::getFileSystem() const { - mPartitionPath[3].path = path; - mPartitionPath[3].doExtract = true; -} - -void nstool::NcaProcess::setListFs(bool list_fs) -{ - mListFs = list_fs; + return mFileSystem; } void nstool::NcaProcess::importHeader() { - if (*mFile == nullptr) + if (mFile == nullptr) { - throw tc::Exception(kModuleName, "No file reader set."); + throw tc::Exception(mModuleName, "No file reader set."); } - + if (mFile->canRead() == false || mFile->canSeek() == false) + { + throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions."); + } + // read header block - (*mFile)->read((byte_t*)&mHdrBlock, 0, sizeof(nn::hac::sContentArchiveHeaderBlock)); - + if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sContentArchiveHeaderBlock))) + { + throw tc::Exception(mModuleName, "Corrupt NCA: File too small."); + } + mFile->seek(0, tc::io::SeekOrigin::Begin); + mFile->read((byte_t*)(&mHdrBlock), sizeof(nn::hac::sContentArchiveHeaderBlock)); + // decrypt header block - fnd::aes::sAesXts128Key header_key; - mKeyCfg.getContentArchiveHeaderKey(header_key); - nn::hac::ContentArchiveUtil::decryptContentArchiveHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, header_key); + if (mKeyCfg.nca_header_key.isNull()) + { + throw tc::Exception(mModuleName, "Failed to decrypt NCA header. (nca_header_key could not be loaded)"); + } + nn::hac::ContentArchiveUtil::decryptContentArchiveHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, mKeyCfg.nca_header_key.get()); // generate header hash - fnd::sha::Sha256((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader), mHdrHash.bytes); + tc::crypto::GenerateSha256Hash(mHdrHash.data(), (byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader)); // proccess main header mHdr.fromBytes((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader)); @@ -128,7 +130,7 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys() { // create zeros key KeyBag::aes128_key_t zero_aesctr_key; - memset(zero_aesctr_key.key, 0, sizeof(zero_aesctr_key)); + memset(zero_aesctr_key.data(), 0, zero_aesctr_key.size()); // get key data from header byte_t masterkey_rev = nn::hac::ContentArchiveUtil::getMasterKeyRevisionFromKeyGeneration(mHdr.getKeyGeneration()); @@ -136,7 +138,7 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys() // process key area sKeys::sKeyAreaKey kak; - KeyBag::aes128_key_t key_area_enc_key; + //KeyBag::aes128_key_t key_area_enc_key; const KeyBag::aes128_key_t* key_area = (const KeyBag::aes128_key_t*) mHdr.getKeyArea(); for (size_t i = 0; i < nn::hac::nca::kKeyAreaKeyNum; i++) @@ -145,17 +147,18 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys() { kak.index = (byte_t)i; kak.enc = key_area[i]; + kak.decrypted = false; // key[0-3] - if (i < 4 && mKeyCfg.getNcaKeyAreaEncryptionKey(masterkey_rev, keak_index, key_area_enc_key) == true) + if (i < 4 && mKeyCfg.nca_key_area_encryption_key[keak_index].find(masterkey_rev) != mKeyCfg.nca_key_area_encryption_key[keak_index].end()) { kak.decrypted = true; - nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key); + nn::hac::AesKeygen::generateKey(kak.dec.data(), kak.enc.data(), mKeyCfg.nca_key_area_encryption_key[keak_index][masterkey_rev].data()); } // key[KEY_AESCTR_HW] - else if (i == nn::hac::nca::KEY_AESCTR_HW && mKeyCfg.getNcaKeyAreaEncryptionKeyHw(masterkey_rev, keak_index, key_area_enc_key) == true) + else if (i == nn::hac::nca::KEY_AESCTR_HW && mKeyCfg.nca_key_area_encryption_key_hw[keak_index].find(masterkey_rev) != mKeyCfg.nca_key_area_encryption_key_hw[keak_index].end()) { kak.decrypted = true; - nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key); + nn::hac::AesKeygen::generateKey(kak.dec.data(), kak.enc.data(), mKeyCfg.nca_key_area_encryption_key_hw[keak_index][masterkey_rev].data()); } else { @@ -165,69 +168,64 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys() } } - // set flag to indicate that the keys are not available - mContentKey.aes_ctr.isSet = false; + // clear content key + mContentKey.aes_ctr = tc::Optional(); // if this has a rights id, the key needs to be sourced from a ticket if (mHdr.hasRightsId() == true) { KeyBag::aes128_key_t tmp_key; - if (mKeyCfg.getNcaExternalContentKey(mHdr.getRightsId(), tmp_key) == true) + if (mKeyCfg.external_content_keys.find(mHdr.getRightsId()) != mKeyCfg.external_content_keys.end()) { - mContentKey.aes_ctr = tmp_key; + mContentKey.aes_ctr = mKeyCfg.external_content_keys[mHdr.getRightsId()]; } - else if (mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserTitleKey, tmp_key) == true) + else if (mKeyCfg.fallback_content_key.isSet()) { - KeyBag::aes128_key_t common_key; - if (mKeyCfg.getETicketCommonKey(masterkey_rev, common_key) == true) + mContentKey.aes_ctr = mKeyCfg.fallback_content_key.get(); + } + else if (mKeyCfg.fallback_enc_content_key.isSet()) + { + tmp_key = mKeyCfg.fallback_enc_content_key.get(); + if (mKeyCfg.etik_common_key.find(masterkey_rev) != mKeyCfg.etik_common_key.end()) { - nn::hac::AesKeygen::generateKey(tmp_key.key, tmp_key.key, common_key.key); + nn::hac::AesKeygen::generateKey(tmp_key.data(), tmp_key.data(), mKeyCfg.etik_common_key[masterkey_rev].data()); + mContentKey.aes_ctr = tmp_key; } - mContentKey.aes_ctr = tmp_key; } } - // otherwise decrypt key area + // otherwise used decrypt key area else { - KeyBag::aes128_key_t kak_aes_ctr = zero_aesctr_key; for (size_t i = 0; i < mContentKey.kak_list.size(); i++) { if (mContentKey.kak_list[i].index == nn::hac::nca::KEY_AESCTR && mContentKey.kak_list[i].decrypted) { - kak_aes_ctr = mContentKey.kak_list[i].dec; + mContentKey.aes_ctr = mContentKey.kak_list[i].dec; } } - - if (kak_aes_ctr != zero_aesctr_key) - { - mContentKey.aes_ctr = kak_aes_ctr; - } } // if the keys weren't generated, check if the keys were supplied by the user if (mContentKey.aes_ctr.isNull()) { - if (mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserBodyKey, mContentKey.aes_ctr.get()) == true) - mContentKey.aes_ctr.isSet = true; + if (mKeyCfg.fallback_content_key.isSet()) + { + mContentKey.aes_ctr = mKeyCfg.fallback_content_key.get(); + } } - if (mCliOutputMode.show_keydata) { if (mContentKey.aes_ctr.isSet()) { - std::cout << "[NCA Content Key]" << std::endl; - std::cout << " AES-CTR Key: " << fnd::SimpleTextOutput::arrayToString(mContentKey.aes_ctr.get().key, sizeof(mContentKey.aes_ctr.get()), true, ":") << std::endl; + fmt::print("[NCA Content Key]\n"); + fmt::print(" AES-CTR Key: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mContentKey.aes_ctr.get().data(), mContentKey.aes_ctr.get().size(), true, ":")); } } - - } void nstool::NcaProcess::generatePartitionConfiguration() { - std::stringstream error; - for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++) { // get reference to relevant structures @@ -238,147 +236,143 @@ void nstool::NcaProcess::generatePartitionConfiguration() sPartitionInfo& info = mPartitions[partition.header_index]; // validate header hash - fnd::sha::sSha256Hash fs_header_hash; - fnd::sha::Sha256((const byte_t*)&mHdrBlock.fs_header[partition.header_index], sizeof(nn::hac::sContentArchiveFsHeader), fs_header_hash.bytes); - if (fs_header_hash.compare(partition.fs_header_hash) == false) + nn::hac::detail::sha256_hash_t fs_header_hash; + tc::crypto::GenerateSha256Hash(fs_header_hash.data(), (const byte_t*)&mHdrBlock.fs_header[partition.header_index], sizeof(nn::hac::sContentArchiveFsHeader)); + if (fs_header_hash != partition.fs_header_hash) { - error.clear(); - error << "NCA FS Header [" << partition.header_index << "] Hash: FAIL \n"; - throw tc::Exception(kModuleName, error.str()); + throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Hash: FAIL", partition.header_index)); } - if (fs_header.version.get() != nn::hac::nca::kDefaultFsHeaderVersion) + if (fs_header.version.unwrap() != nn::hac::nca::kDefaultFsHeaderVersion) { - error.clear(); - error << "NCA FS Header [" << partition.header_index << "] Version(" << fs_header.version.get() << "): UNSUPPORTED"; - throw tc::Exception(kModuleName, error.str()); + throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Version({:d}): UNSUPPORTED", partition.header_index, fs_header.version.unwrap())); } // setup AES-CTR - nn::hac::ContentArchiveUtil::getNcaPartitionAesCtr(&fs_header, info.aes_ctr.iv); + nn::hac::ContentArchiveUtil::getNcaPartitionAesCtr(&fs_header, info.aes_ctr.data()); // save partition config + if (tc::is_uint64_t_too_large_for_int64_t(partition.offset)) + { + throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Offset({:x}): Too large", partition.header_index, partition.offset)); + } + if (tc::is_uint64_t_too_large_for_int64_t(partition.size)) + { + throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Size({:x}): Too large", partition.header_index, partition.size)); + } + info.reader = nullptr; - info.offset = partition.offset; - info.size = partition.size; + info.offset = int64_t(partition.offset); + info.size = int64_t(partition.size); info.format_type = (nn::hac::nca::FormatType)fs_header.format_type; info.hash_type = (nn::hac::nca::HashType)fs_header.hash_type; info.enc_type = (nn::hac::nca::EncryptionType)fs_header.encryption_type; if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256) { - // info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_SHA256); nn::hac::HierarchicalSha256Header hdr; - std::vector hash_layers; - fnd::LayeredIntegrityMetadata::sLayer data_layer; - std::vector master_hash_list; // import raw data - hdr.fromBytes(fs_header.hash_info, nn::hac::nca::kHashInfoLen); + hdr.fromBytes(fs_header.hash_info.data(), fs_header.hash_info.size()); for (size_t i = 0; i < hdr.getLayerInfo().size(); i++) { - fnd::LayeredIntegrityMetadata::sLayer layer; + nn::hac::HierarchicalValidatedStream::StreamInfo::LayerInfo layer; layer.offset = hdr.getLayerInfo()[i].offset; layer.size = hdr.getLayerInfo()[i].size; layer.block_size = hdr.getHashBlockSize(); if (i + 1 == hdr.getLayerInfo().size()) { - data_layer = layer; + info.hashed_stream_info.data_layer_info = layer; } else { - hash_layers.push_back(layer); + info.hashed_stream_info.hash_layer_info.push_back(layer); } } - master_hash_list.push_back(hdr.getMasterHash()); - - // write data into metadata - info.layered_intergrity_metadata.setAlignHashToBlock(false); - info.layered_intergrity_metadata.setHashLayerInfo(hash_layers); - info.layered_intergrity_metadata.setDataLayerInfo(data_layer); - info.layered_intergrity_metadata.setMasterHashList(master_hash_list); + info.hashed_stream_info.master_hash_data = tc::ByteData(hdr.getMasterHash().data(), hdr.getMasterHash().size()); + info.hashed_stream_info.align_hash_layer_to_block = false; } else if (info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) { // info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_INTEGRITY); nn::hac::HierarchicalIntegrityHeader hdr; - std::vector hash_layers; - fnd::LayeredIntegrityMetadata::sLayer data_layer; - std::vector master_hash_list; - hdr.fromBytes(fs_header.hash_info, nn::hac::nca::kHashInfoLen); + hdr.fromBytes(fs_header.hash_info.data(), fs_header.hash_info.size()); for (size_t i = 0; i < hdr.getLayerInfo().size(); i++) { - fnd::LayeredIntegrityMetadata::sLayer layer; + nn::hac::HierarchicalValidatedStream::StreamInfo::LayerInfo layer; layer.offset = hdr.getLayerInfo()[i].offset; layer.size = hdr.getLayerInfo()[i].size; layer.block_size = (1 << hdr.getLayerInfo()[i].block_size); if (i + 1 == hdr.getLayerInfo().size()) { - data_layer = layer; + info.hashed_stream_info.data_layer_info = layer; } else { - hash_layers.push_back(layer); + info.hashed_stream_info.hash_layer_info.push_back(layer); } } - - // write data into metadata - info.layered_intergrity_metadata.setAlignHashToBlock(true); - info.layered_intergrity_metadata.setHashLayerInfo(hash_layers); - info.layered_intergrity_metadata.setDataLayerInfo(data_layer); - info.layered_intergrity_metadata.setMasterHashList(hdr.getMasterHashList()); + info.hashed_stream_info.master_hash_data = tc::ByteData(hdr.getMasterHashList().size() * sizeof(nn::hac::detail::sha256_hash_t)); + for (size_t i = 0; i < hdr.getMasterHashList().size(); i++) + { + ((nn::hac::detail::sha256_hash_t*)info.hashed_stream_info.master_hash_data.data())[i] = hdr.getMasterHashList()[i]; + } + info.hashed_stream_info.align_hash_layer_to_block = false; } // create reader try { - // filter out unrecognised format types - switch (info.format_type) - { - case (nn::hac::nca::FormatType::PartitionFs): - case (nn::hac::nca::FormatType::RomFs): - break; - default: - error.clear(); - error << "FormatType(" << nn::hac::ContentArchiveUtil::getFormatTypeAsString(info.format_type) << "): UNKNOWN"; - throw tc::Exception(kModuleName, error.str()); - } + // create reader based on encryption type0 if (info.enc_type == nn::hac::nca::EncryptionType::None) { - info.reader = new fnd::OffsetAdjustedIFile(mFile, info.offset, info.size); + info.reader = std::make_shared(tc::io::SubStream(mFile, info.offset, info.size)); } else if (info.enc_type == nn::hac::nca::EncryptionType::AesCtr) { if (mContentKey.aes_ctr.isNull()) - throw tc::Exception(kModuleName, "AES-CTR Key was not determined"); - info.reader = new fnd::OffsetAdjustedIFile(new fnd::AesCtrWrappedIFile(mFile, mContentKey.aes_ctr.get(), info.aes_ctr), info.offset, info.size); + throw tc::Exception(mModuleName, "AES-CTR Key was not determined"); + //info.reader = new fnd::OffsetAdjustedIFile(new fnd::AesCtrWrappedIFile(mFile, mContentKey.aes_ctr.get(), info.aes_ctr), info.offset, info.size); + info.reader = std::make_shared(tc::crypto::Aes128CtrEncryptedStream(mFile, mContentKey.aes_ctr.get(), info.aes_ctr)); + info.reader = std::make_shared(tc::io::SubStream(info.reader, info.offset, info.size)); + } else if (info.enc_type == nn::hac::nca::EncryptionType::AesXts || info.enc_type == nn::hac::nca::EncryptionType::AesCtrEx) { - error.clear(); - error << "EncryptionType(" << nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type) << "): UNSUPPORTED"; - throw tc::Exception(kModuleName, error.str()); + throw tc::Exception(mModuleName, fmt::format("EncryptionType({:s}): UNSUPPORTED", nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type))); } else { - error.clear(); - error << "EncryptionType(" << nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type) << "): UNKNOWN"; - throw tc::Exception(kModuleName, error.str()); + throw tc::Exception(mModuleName, fmt::format("EncryptionType({:s}): UNKNOWN", nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type))); } // filter out unrecognised hash types, and hash based readers if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256 || info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) { - info.reader = new fnd::LayeredIntegrityWrappedIFile(info.reader, info.layered_intergrity_metadata); + //info.reader = new fnd::LayeredIntegrityWrappedIFile(info.reader, info.layered_intergrity_metadata); + info.reader = std::make_shared(nn::hac::HierarchicalValidatedStream(info.reader, info.hashed_stream_info)); } else if (info.hash_type != nn::hac::nca::HashType::None) { - error.clear(); - error << "HashType(" << nn::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type) << "): UNKNOWN"; - throw tc::Exception(kModuleName, error.str()); + throw tc::Exception(mModuleName, fmt::format("HashType({:s}): UNKNOWN", nn::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type))); + } + + // filter out unrecognised format types + switch (info.format_type) + { + case (nn::hac::nca::FormatType::PartitionFs): + info.fs_meta = nn::hac::PartitionFsMetaGenerator(info.reader); + info.fs_reader = std::make_shared(tc::io::VirtualFileSystem(info.fs_meta)); + break; + case (nn::hac::nca::FormatType::RomFs): + info.fs_meta = nn::hac::RomFsMetaGenerator(info.reader); + info.fs_reader = std::make_shared(tc::io::VirtualFileSystem(info.fs_meta)); + break; + default: + throw tc::Exception(mModuleName, fmt::format("FormatType({:s}): UNKNOWN", nn::hac::ContentArchiveUtil::getFormatTypeAsString(info.format_type))); } } catch (const tc::Exception& e) @@ -391,160 +385,163 @@ void nstool::NcaProcess::generatePartitionConfiguration() void nstool::NcaProcess::validateNcaSignatures() { // validate signature[0] - fnd::rsa::sRsa2048Key sign0_key; - mKeyCfg.getContentArchiveHeader0SignKey(sign0_key, mHdr.getSignatureKeyGeneration()); - if (fnd::rsa::pss::rsaVerify(sign0_key, fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_main) != 0) + if (mKeyCfg.nca_header_sign0_key.find(mHdr.getSignatureKeyGeneration()) != mKeyCfg.nca_header_sign0_key.end()) { - std::cout << "[WARNING] NCA Header Main Signature: FAIL" << std::endl; + if (tc::crypto::VerifyRsa2048PssSha256(mHdrBlock.signature_main.data(), mHdrHash.data(), mKeyCfg.nca_header_sign0_key[mHdr.getSignatureKeyGeneration()]) == false) + { + fmt::print("[WARNING] NCA Header Main Signature: FAIL\n"); + } } + else + { + fmt::print("[WARNING] NCA Header Main Signature: FAIL (could not load header key)\n"); + } + // validate signature[1] if (mHdr.getContentType() == nn::hac::nca::ContentType::Program) { - if (mPartitions[nn::hac::nca::PARTITION_CODE].format_type == nn::hac::nca::FormatType::PartitionFs) - { - if (*mPartitions[nn::hac::nca::PARTITION_CODE].reader != nullptr) + try { + if (mPartitions[nn::hac::nca::PARTITION_CODE].format_type == nn::hac::nca::FormatType::PartitionFs) { - PfsProcess exefs; - exefs.setInputFile(mPartitions[nn::hac::nca::PARTITION_CODE].reader); - exefs.setCliOutputMode(0); - exefs.process(); - - // open main.npdm - if (exefs.getPfsHeader().getFileList().hasElement(kNpdmExefsPath) == true) + if (mPartitions[nn::hac::nca::PARTITION_CODE].fs_reader != nullptr) { - const nn::hac::PartitionFsHeader::sFile& file = exefs.getPfsHeader().getFileList().getElement(kNpdmExefsPath); + std::shared_ptr npdm_file; + try { + mPartitions[nn::hac::nca::PARTITION_CODE].fs_reader->openFile(tc::io::Path(kNpdmExefsPath), tc::io::FileMode::Open, tc::io::FileAccess::Read, npdm_file); + } + catch (tc::io::FileNotFoundException&) { + throw tc::Exception(fmt::format("\"{:s}\" not present in ExeFs", kNpdmExefsPath)); + } MetaProcess npdm; - npdm.setInputFile(new fnd::OffsetAdjustedIFile(mPartitions[nn::hac::nca::PARTITION_CODE].reader, file.offset, file.size)); + npdm.setInputFile(npdm_file); npdm.setKeyCfg(mKeyCfg); npdm.setVerifyMode(true); - npdm.setCliOutputMode(0); + npdm.setCliOutputMode(CliOutputMode(false, false, false, false)); npdm.process(); - if (fnd::rsa::pss::rsaVerify(npdm.getMeta().getAccessControlInfoDesc().getContentArchiveHeaderSignature2Key(), fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_acid) != 0) + if (tc::crypto::VerifyRsa2048PssSha256(mHdrBlock.signature_acid.data(), mHdrHash.data(), npdm.getMeta().getAccessControlInfoDesc().getContentArchiveHeaderSignature2Key()) == false) { - std::cout << "[WARNING] NCA Header ACID Signature: FAIL" << std::endl; + throw tc::Exception("Bad signature"); } - } else { - std::cout << "[WARNING] NCA Header ACID Signature: FAIL (\"" << kNpdmExefsPath << "\" not present in ExeFs)" << std::endl; + throw tc::Exception("ExeFs was not mounted"); } } else { - std::cout << "[WARNING] NCA Header ACID Signature: FAIL (ExeFs unreadable)" << std::endl; + throw tc::Exception("No ExeFs partition"); } } - else - { - std::cout << "[WARNING] NCA Header ACID Signature: FAIL (No ExeFs partition)" << std::endl; + catch (tc::Exception& e) { + fmt::print("[WARNING] NCA Header ACID Signature: FAIL ({:s})\n", e.error()); } } } void nstool::NcaProcess::displayHeader() { - std::cout << "[NCA Header]" << std::endl; - std::cout << " Format Type: " << nn::hac::ContentArchiveUtil::getFormatHeaderVersionAsString((nn::hac::nca::HeaderFormatVersion)mHdr.getFormatVersion()) << std::endl; - std::cout << " Dist. Type: " << nn::hac::ContentArchiveUtil::getDistributionTypeAsString(mHdr.getDistributionType()) << std::endl; - std::cout << " Content Type: " << nn::hac::ContentArchiveUtil::getContentTypeAsString(mHdr.getContentType()) << std::endl; - std::cout << " Key Generation: " << std::dec << (uint32_t)mHdr.getKeyGeneration() << std::endl; - std::cout << " Sig. Generation: " << std::dec << (uint32_t)mHdr.getSignatureKeyGeneration() << std::endl; - std::cout << " Kaek Index: " << nn::hac::ContentArchiveUtil::getKeyAreaEncryptionKeyIndexAsString((nn::hac::nca::KeyAreaEncryptionKeyIndex)mHdr.getKeyAreaEncryptionKeyIndex()) << " (" << std::dec << (uint32_t)mHdr.getKeyAreaEncryptionKeyIndex() << ")" << std::endl; - std::cout << " Size: 0x" << std::hex << mHdr.getContentSize() << std::endl; - std::cout << " ProgID: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getProgramId() << std::endl; - std::cout << " Content Index: " << std::dec << mHdr.getContentIndex() << std::endl; - std::cout << " SdkAddon Ver.: " << nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getSdkAddonVersion()) << " (v" << std::dec << mHdr.getSdkAddonVersion() << ")" << std::endl; + fmt::print("[NCA Header]\n"); + fmt::print(" Format Type: {:s}\n", nn::hac::ContentArchiveUtil::getFormatHeaderVersionAsString((nn::hac::nca::HeaderFormatVersion)mHdr.getFormatVersion())); + fmt::print(" Dist. Type: {:s}\n", nn::hac::ContentArchiveUtil::getDistributionTypeAsString(mHdr.getDistributionType())); + fmt::print(" Content Type: {:s}\n", nn::hac::ContentArchiveUtil::getContentTypeAsString(mHdr.getContentType())); + fmt::print(" Key Generation: {:d}\n", mHdr.getKeyGeneration()); + fmt::print(" Sig. Generation: {:d}\n", mHdr.getSignatureKeyGeneration()); + fmt::print(" Kaek Index: {:s} ({:d})\n", nn::hac::ContentArchiveUtil::getKeyAreaEncryptionKeyIndexAsString((nn::hac::nca::KeyAreaEncryptionKeyIndex)mHdr.getKeyAreaEncryptionKeyIndex()), mHdr.getKeyAreaEncryptionKeyIndex()); + fmt::print(" Size: 0x{:x}\n", mHdr.getContentSize()); + fmt::print(" ProgID: 0x{:016x}\n", mHdr.getProgramId()); + fmt::print(" Content Index: {:d}\n", mHdr.getContentIndex()); + fmt::print(" SdkAddon Ver.: {:s} (v{:d})\n", nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getSdkAddonVersion()), mHdr.getSdkAddonVersion()); if (mHdr.hasRightsId()) { - std::cout << " RightsId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getRightsId(), nn::hac::nca::kRightsIdLen, true, "") << std::endl; + fmt::print(" RightsId: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getRightsId().data(), mHdr.getRightsId().size(), true, "")); } if (mContentKey.kak_list.size() > 0 && mCliOutputMode.show_keydata) { - std::cout << " Key Area:" << std::endl; - std::cout << " <--------------------------------------------------------------------------------------------------------->" << std::endl; - std::cout << " | IDX | ENCRYPTED KEY | DECRYPTED KEY |" << std::endl; - std::cout << " |-----|-------------------------------------------------|-------------------------------------------------|" << std::endl; + fmt::print(" Key Area:\n"); + fmt::print(" <--------------------------------------------------------------------------------------------------------->\n"); + fmt::print(" | IDX | ENCRYPTED KEY | DECRYPTED KEY |\n"); + fmt::print(" |-----|-------------------------------------------------|-------------------------------------------------|\n"); for (size_t i = 0; i < mContentKey.kak_list.size(); i++) { - std::cout << " | " << std::dec << std::setw(3) << std::setfill(' ') << (uint32_t)mContentKey.kak_list[i].index << " | "; - - std::cout << fnd::SimpleTextOutput::arrayToString(mContentKey.kak_list[i].enc.key, 16, true, ":") << " | "; - + fmt::print(" | {:3d} | {:s} | ", mContentKey.kak_list[i].index, tc::cli::FormatUtil::formatBytesAsString(mContentKey.kak_list[i].enc.data(), mContentKey.kak_list[i].enc.size(), true, ":")); + if (mContentKey.kak_list[i].decrypted) - std::cout << fnd::SimpleTextOutput::arrayToString(mContentKey.kak_list[i].dec.key, 16, true, ":"); + fmt::print("{:s}", tc::cli::FormatUtil::formatBytesAsString(mContentKey.kak_list[i].dec.data(), mContentKey.kak_list[i].dec.size(), true, ":")); else - std::cout << " "; + fmt::print(" "); - std::cout << " |" << std::endl; + fmt::print(" |\n"); } - std::cout << " <--------------------------------------------------------------------------------------------------------->" << std::endl; + fmt::print(" <--------------------------------------------------------------------------------------------------------->\n"); } if (mCliOutputMode.show_layout) { - std::cout << " Partitions:" << std::endl; + fmt::print(" Partitions:\n"); for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++) { uint32_t index = mHdr.getPartitionEntryList()[i].header_index; sPartitionInfo& info = mPartitions[index]; if (info.size == 0) continue; - std::cout << " " << std::dec << index << ":" << std::endl; - std::cout << " Offset: 0x" << std::hex << (uint64_t)info.offset << std::endl; - std::cout << " Size: 0x" << std::hex << (uint64_t)info.size << std::endl; - std::cout << " Format Type: " << nn::hac::ContentArchiveUtil::getFormatTypeAsString(info.format_type) << std::endl; - std::cout << " Hash Type: " << nn::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type) << std::endl; - std::cout << " Enc. Type: " << nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type) << std::endl; + fmt::print(" {:d}:\n", index); + fmt::print(" Offset: 0x{:x}\n", info.offset); + fmt::print(" Size: 0x{:x}\n", info.size); + fmt::print(" Format Type: {:s}\n", nn::hac::ContentArchiveUtil::getFormatTypeAsString(info.format_type)); + fmt::print(" Hash Type: {:s}\n", nn::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type)); + fmt::print(" Enc. Type: {:s}\n", nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type)); if (info.enc_type == nn::hac::nca::EncryptionType::AesCtr) { - fnd::aes::sAesIvCtr ctr; - fnd::aes::AesIncrementCounter(info.aes_ctr.iv, info.offset>>4, ctr.iv); - std::cout << " AesCtr Counter:" << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(ctr.iv, sizeof(fnd::aes::sAesIvCtr), true, ":") << std::endl; + nn::hac::detail::aes_iv_t aes_ctr; + //fnd::aes::AesIncrementCounter(info.aes_ctr.iv, info.offset>>4, ctr.iv); + fmt::print(" AesCtr Counter:\n"); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(aes_ctr.data(), aes_ctr.size(), true, ":")); } if (info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) { - fnd::LayeredIntegrityMetadata& hash_hdr = info.layered_intergrity_metadata; - std::cout << " HierarchicalIntegrity Header:" << std::endl; - for (size_t j = 0; j < hash_hdr.getHashLayerInfo().size(); j++) + auto hash_hdr = info.hashed_stream_info; + fmt::print(" HierarchicalIntegrity Header:\n"); + for (size_t j = 0; j < hash_hdr.hash_layer_info.size(); j++) { - std::cout << " Hash Layer " << std::dec << j << ":" << std::endl; - std::cout << " Offset: 0x" << std::hex << (uint64_t)hash_hdr.getHashLayerInfo()[j].offset << std::endl; - std::cout << " Size: 0x" << std::hex << (uint64_t)hash_hdr.getHashLayerInfo()[j].size << std::endl; - std::cout << " BlockSize: 0x" << std::hex << (uint32_t)hash_hdr.getHashLayerInfo()[j].block_size << std::endl; + fmt::print(" Hash Layer {:d}:\n", j); + fmt::print(" Offset: 0x{:x}\n", hash_hdr.hash_layer_info[j].offset); + fmt::print(" Size: 0x{:x}\n", hash_hdr.hash_layer_info[j].size); + fmt::print(" BlockSize: 0x{:x}\n", hash_hdr.hash_layer_info[j].block_size); } - std::cout << " Data Layer:" << std::endl; - std::cout << " Offset: 0x" << std::hex << (uint64_t)hash_hdr.getDataLayer().offset << std::endl; - std::cout << " Size: 0x" << std::hex << (uint64_t)hash_hdr.getDataLayer().size << std::endl; - std::cout << " BlockSize: 0x" << std::hex << (uint32_t)hash_hdr.getDataLayer().block_size << std::endl; - for (size_t j = 0; j < hash_hdr.getMasterHashList().size(); j++) + fmt::print(" Data Layer:\n"); + fmt::print(" Offset: 0x{:x}\n", hash_hdr.data_layer_info.offset); + fmt::print(" Size: 0x{:x}\n", hash_hdr.data_layer_info.size); + fmt::print(" BlockSize: 0x{:x}\n", hash_hdr.data_layer_info.block_size); + size_t master_hash_num = hash_hdr.master_hash_data.size() / sizeof(nn::hac::detail::sha256_hash_t); + nn::hac::detail::sha256_hash_t* master_hash_table = (nn::hac::detail::sha256_hash_t*)hash_hdr.master_hash_data.data(); + for (size_t j = 0; j < master_hash_num; j++) { - std::cout << " Master Hash " << std::dec << j << ":" << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(hash_hdr.getMasterHashList()[j].bytes, 0x10, true, ":") << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(hash_hdr.getMasterHashList()[j].bytes+0x10, 0x10, true, ":") << std::endl; + fmt::print(" Master Hash {:d}:\n", j); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(master_hash_table[j].data(), 0x10, true, ":")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(master_hash_table[j].data()+0x10, 0x10, true, ":")); } } else if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256) { - fnd::LayeredIntegrityMetadata& hash_hdr = info.layered_intergrity_metadata; - std::cout << " HierarchicalSha256 Header:" << std::endl; - std::cout << " Master Hash:" << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(hash_hdr.getMasterHashList()[0].bytes, 0x10, true, ":") << std::endl; - std::cout << " " << fnd::SimpleTextOutput::arrayToString(hash_hdr.getMasterHashList()[0].bytes+0x10, 0x10, true, ":") << std::endl; - std::cout << " HashBlockSize: 0x" << std::hex << (uint32_t)hash_hdr.getDataLayer().block_size << std::endl; - std::cout << " Hash Layer:" << std::endl; - std::cout << " Offset: 0x" << std::hex << (uint64_t)hash_hdr.getHashLayerInfo()[0].offset << std::endl; - std::cout << " Size: 0x" << std::hex << (uint64_t)hash_hdr.getHashLayerInfo()[0].size << std::endl; - std::cout << " Data Layer:" << std::endl; - std::cout << " Offset: 0x" << std::hex << (uint64_t)hash_hdr.getDataLayer().offset << std::endl; - std::cout << " Size: 0x" << std::hex << (uint64_t)hash_hdr.getDataLayer().size << std::endl; + auto hash_hdr = info.hashed_stream_info; + fmt::print(" HierarchicalSha256 Header:\n"); + fmt::print(" Master Hash:\n"); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.master_hash_data.data(), 0x10, true, ":")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.master_hash_data.data()+0x10, 0x10, true, ":")); + fmt::print(" HashBlockSize: 0x{:x}\n", hash_hdr.data_layer_info.block_size); + fmt::print(" Hash Layer:\n"); + fmt::print(" Offset: 0x{:x}\n", hash_hdr.hash_layer_info[0].offset); + fmt::print(" Size: 0x{:x}\n", hash_hdr.hash_layer_info[0].size); + fmt::print(" Data Layer:\n"); + fmt::print(" Offset: 0x{:x}\n", hash_hdr.data_layer_info.offset); + fmt::print(" Size: 0x{:x}\n", hash_hdr.data_layer_info.size); } } } @@ -559,17 +556,17 @@ void nstool::NcaProcess::processPartitions() struct sPartitionInfo& partition = mPartitions[index]; // if the reader is null, skip - if (*partition.reader == nullptr) + if (partition.fs_reader == nullptr) { - std::cout << "[WARNING] NCA Partition " << std::dec << index << " not readable."; + fmt::print("[WARNING] NCA Partition {:d} not readable.", index); if (partition.fail_reason.empty() == false) { - std::cout << " (" << partition.fail_reason << ")"; + fmt::print(" ({:s})", partition.fail_reason); } - std::cout << std::endl; + fmt::print("\n"); continue; } - + /* try { if (partition.format_type == nn::hac::nca::FormatType::PartitionFs) { @@ -612,6 +609,7 @@ void nstool::NcaProcess::processPartitions() } catch (const tc::Exception& e) { std::cout << "[WARNING] NCA Partition " << std::dec << index << " not readable (" << e.error() << ")." << std::endl; } + */ } } diff --git a/src/NcaProcess.h b/src/NcaProcess.h index 4a11c91..5fec415 100644 --- a/src/NcaProcess.h +++ b/src/NcaProcess.h @@ -1,8 +1,10 @@ #pragma once #include "types.h" #include "KeyBag.h" +#include "FsProcess.h" #include +#include namespace nstool { @@ -19,16 +21,17 @@ public: void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); - // nca specfic - void setPartition0ExtractPath(const tc::io::Path& path); - void setPartition1ExtractPath(const tc::io::Path& path); - void setPartition2ExtractPath(const tc::io::Path& path); - void setPartition3ExtractPath(const tc::io::Path& path); - void setListFs(bool list_fs); + // fs specific + void setShowFsTree(bool show_fs_tree); + void setFsRootLabel(const std::string& root_label); + void setExtractJobs(const std::vector& extract_jobs); + // post process() get FS out + const std::shared_ptr& getFileSystem() const; private: - const std::string kModuleName = "NcaProcess"; - const std::string kNpdmExefsPath = "main.npdm"; + const std::string kNpdmExefsPath = "/main.npdm"; + + std::string mModuleName; // user options std::shared_ptr mFile; @@ -36,13 +39,13 @@ private: CliOutputMode mCliOutputMode; bool mVerify; - std::array, nn::hac::nca::kPartitionNum> mPartitionPath; + // fs processing + std::shared_ptr mFileSystem; + FsProcess mFsProcess; - bool mListFs; - - // data + // nca data nn::hac::sContentArchiveHeaderBlock mHdrBlock; - fnd::sha::sSha256Hash mHdrHash; + nn::hac::detail::sha256_hash_t mHdrHash; nn::hac::ContentArchiveHeader mHdr; // crypto @@ -81,9 +84,12 @@ private: tc::Optional aes_ctr; } mContentKey; + // raw partition data struct sPartitionInfo { std::shared_ptr reader; + tc::io::VirtualFileSystem::FileSystemMeta fs_meta; + std::shared_ptr fs_reader; std::string fail_reason; int64_t offset; int64_t size; @@ -92,12 +98,14 @@ private: nn::hac::nca::FormatType format_type; nn::hac::nca::HashType hash_type; nn::hac::nca::EncryptionType enc_type; + nn::hac::HierarchicalValidatedStream::StreamInfo hashed_stream_info; //fnd::LayeredIntegrityMetadata layered_intergrity_metadata; nn::hac::detail::aes_iv_t aes_ctr; - } + }; std::array mPartitions; + void importHeader(); void generateNcaBodyEncryptionKeys(); void generatePartitionConfiguration(); diff --git a/src/main.cpp b/src/main.cpp index be71960..5b91ee6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,7 @@ #include "GameCardProcess.h" #include "PfsProcess.h" #include "RomfsProcess.h" -//#include "NcaProcess.h" +#include "NcaProcess.h" #include "MetaProcess.h" #include "CnmtProcess.h" #include "NsoProcess.h" @@ -70,7 +70,6 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - /* else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NCA) { nstool::NcaProcess obj; @@ -80,7 +79,7 @@ int umain(const std::vector& args, const std::vector& obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - + /* if (set.nca.part0_extract_path.isSet()) obj.setPartition0ExtractPath(set.nca.part0_extract_path.get()); if (set.nca.part1_extract_path.isSet()) @@ -90,10 +89,10 @@ int umain(const std::vector& args, const std::vector& if (set.nca.part3_extract_path.isSet()) obj.setPartition3ExtractPath(set.nca.part3_extract_path.get()); obj.setListFs(set.fs.show_fs_tree); + */ obj.process(); } - */ else if (set.infile.filetype == nstool::Settings::FILE_TYPE_META) { nstool::MetaProcess obj; From f04c53f21e24cee53769d2390770669ea17a55c9 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Oct 2021 14:13:11 +0800 Subject: [PATCH 061/119] Port NcaProcess to libtoolchain. --- deps/libnintendo-hac | 2 +- src/NcaProcess.cpp | 166 +++++++++++++++++++++++++------------------ src/NcaProcess.h | 14 ++-- src/main.cpp | 13 +--- 4 files changed, 107 insertions(+), 88 deletions(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 6fc9b94..af43764 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 6fc9b94f377d41efceef9713215f5c7be9042d9d +Subproject commit af437641b9d14381baf69c9399f4177cece7840c diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index a6ff3a2..4cc2a21 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -1,24 +1,17 @@ #include "NcaProcess.h" - -#include "PfsProcess.h" -#include "RomfsProcess.h" #include "MetaProcess.h" -//#include -//#include -//#include - -//#include -//#include -//#include -//#include +//mak#include +#include #include #include -#include -#include +#include +#include #include #include +#include + nstool::NcaProcess::NcaProcess() : mModuleName("nstool::NcaProcess"), @@ -138,15 +131,12 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys() // process key area sKeys::sKeyAreaKey kak; - //KeyBag::aes128_key_t key_area_enc_key; - const KeyBag::aes128_key_t* key_area = (const KeyBag::aes128_key_t*) mHdr.getKeyArea(); - - for (size_t i = 0; i < nn::hac::nca::kKeyAreaKeyNum; i++) + for (size_t i = 0; i < mHdr.getKeyArea().size(); i++) { - if (key_area[i] != zero_aesctr_key) + if (mHdr.getKeyArea()[i] != zero_aesctr_key) { kak.index = (byte_t)i; - kak.enc = key_area[i]; + kak.enc = mHdr.getKeyArea()[i]; kak.decrypted = false; // key[0-3] if (i < 4 && mKeyCfg.nca_key_area_encryption_key[keak_index].find(masterkey_rev) != mKeyCfg.nca_key_area_encryption_key[keak_index].end()) @@ -252,24 +242,16 @@ void nstool::NcaProcess::generatePartitionConfiguration() // setup AES-CTR nn::hac::ContentArchiveUtil::getNcaPartitionAesCtr(&fs_header, info.aes_ctr.data()); - // save partition config - if (tc::is_uint64_t_too_large_for_int64_t(partition.offset)) - { - throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Offset({:x}): Too large", partition.header_index, partition.offset)); - } - if (tc::is_uint64_t_too_large_for_int64_t(partition.size)) - { - throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Size({:x}): Too large", partition.header_index, partition.size)); - } - - info.reader = nullptr; - info.offset = int64_t(partition.offset); - info.size = int64_t(partition.size); + // save partition configinfo + info.offset = partition.offset; + info.size = partition.size; info.format_type = (nn::hac::nca::FormatType)fs_header.format_type; info.hash_type = (nn::hac::nca::HashType)fs_header.hash_type; info.enc_type = (nn::hac::nca::EncryptionType)fs_header.encryption_type; if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256) { + info.hierarchicalsha256_hdr.fromBytes(fs_header.hash_info.data(), fs_header.hash_info.size()); + /* nn::hac::HierarchicalSha256Header hdr; // import raw data @@ -290,10 +272,13 @@ void nstool::NcaProcess::generatePartitionConfiguration() } } info.hashed_stream_info.master_hash_data = tc::ByteData(hdr.getMasterHash().data(), hdr.getMasterHash().size()); - info.hashed_stream_info.align_hash_layer_to_block = false; + info.hashed_stream_info.align_partial_block_to_blocksize = false; + */ } else if (info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) { + info.hierarchicalintegrity_hdr.fromBytes(fs_header.hash_info.data(), fs_header.hash_info.size()); + /* // info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_INTEGRITY); nn::hac::HierarchicalIntegrityHeader hdr; @@ -318,14 +303,13 @@ void nstool::NcaProcess::generatePartitionConfiguration() { ((nn::hac::detail::sha256_hash_t*)info.hashed_stream_info.master_hash_data.data())[i] = hdr.getMasterHashList()[i]; } - info.hashed_stream_info.align_hash_layer_to_block = false; + info.hashed_stream_info.align_partial_block_to_blocksize = false; + */ } // create reader try { - - // create reader based on encryption type0 if (info.enc_type == nn::hac::nca::EncryptionType::None) { @@ -350,13 +334,15 @@ void nstool::NcaProcess::generatePartitionConfiguration() } // filter out unrecognised hash types, and hash based readers - if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256 || info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) - { - //info.reader = new fnd::LayeredIntegrityWrappedIFile(info.reader, info.layered_intergrity_metadata); - info.reader = std::make_shared(nn::hac::HierarchicalValidatedStream(info.reader, info.hashed_stream_info)); - } - else if (info.hash_type != nn::hac::nca::HashType::None) + switch (info.hash_type) { + case (nn::hac::nca::HashType::HierarchicalSha256): + info.reader = std::make_shared(nn::hac::HierarchicalSha256Stream(info.reader, info.hierarchicalsha256_hdr)); + break; + case (nn::hac::nca::HashType::HierarchicalIntegrity): + info.reader = std::make_shared(nn::hac::HierarchicalIntegrityStream(info.reader, info.hierarchicalintegrity_hdr)); + break; + default: throw tc::Exception(mModuleName, fmt::format("HashType({:s}): UNKNOWN", nn::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type))); } @@ -499,49 +485,57 @@ void nstool::NcaProcess::displayHeader() if (info.enc_type == nn::hac::nca::EncryptionType::AesCtr) { nn::hac::detail::aes_iv_t aes_ctr; - //fnd::aes::AesIncrementCounter(info.aes_ctr.iv, info.offset>>4, ctr.iv); + memcpy(aes_ctr.data(), info.aes_ctr.data(), aes_ctr.size()); + tc::crypto::detail::incr_counter<16>(aes_ctr.data(), info.offset>>4); fmt::print(" AesCtr Counter:\n"); fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(aes_ctr.data(), aes_ctr.size(), true, ":")); } if (info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) { - auto hash_hdr = info.hashed_stream_info; + auto hash_hdr = info.hierarchicalintegrity_hdr; fmt::print(" HierarchicalIntegrity Header:\n"); - for (size_t j = 0; j < hash_hdr.hash_layer_info.size(); j++) + for (size_t j = 0; j < hash_hdr.getLayerInfo().size(); j++) { - fmt::print(" Hash Layer {:d}:\n", j); - fmt::print(" Offset: 0x{:x}\n", hash_hdr.hash_layer_info[j].offset); - fmt::print(" Size: 0x{:x}\n", hash_hdr.hash_layer_info[j].size); - fmt::print(" BlockSize: 0x{:x}\n", hash_hdr.hash_layer_info[j].block_size); + if (j+1 == hash_hdr.getLayerInfo().size()) + { + fmt::print(" Data Layer:\n"); + } + else + { + fmt::print(" Hash Layer {:d}:\n", j); + } + fmt::print(" Offset: 0x{:x}\n", hash_hdr.getLayerInfo()[j].offset); + fmt::print(" Size: 0x{:x}\n", hash_hdr.getLayerInfo()[j].size); + fmt::print(" BlockSize: 0x{:x}\n", hash_hdr.getLayerInfo()[j].block_size); } - - fmt::print(" Data Layer:\n"); - fmt::print(" Offset: 0x{:x}\n", hash_hdr.data_layer_info.offset); - fmt::print(" Size: 0x{:x}\n", hash_hdr.data_layer_info.size); - fmt::print(" BlockSize: 0x{:x}\n", hash_hdr.data_layer_info.block_size); - size_t master_hash_num = hash_hdr.master_hash_data.size() / sizeof(nn::hac::detail::sha256_hash_t); - nn::hac::detail::sha256_hash_t* master_hash_table = (nn::hac::detail::sha256_hash_t*)hash_hdr.master_hash_data.data(); - for (size_t j = 0; j < master_hash_num; j++) + for (size_t j = 0; j < hash_hdr.getMasterHashList().size(); j++) { fmt::print(" Master Hash {:d}:\n", j); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(master_hash_table[j].data(), 0x10, true, ":")); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(master_hash_table[j].data()+0x10, 0x10, true, ":")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHashList()[j].data(), 0x10, true, ":")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHashList()[j].data()+0x10, 0x10, true, ":")); } } else if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256) { - auto hash_hdr = info.hashed_stream_info; + auto hash_hdr = info.hierarchicalsha256_hdr; fmt::print(" HierarchicalSha256 Header:\n"); fmt::print(" Master Hash:\n"); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.master_hash_data.data(), 0x10, true, ":")); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.master_hash_data.data()+0x10, 0x10, true, ":")); - fmt::print(" HashBlockSize: 0x{:x}\n", hash_hdr.data_layer_info.block_size); - fmt::print(" Hash Layer:\n"); - fmt::print(" Offset: 0x{:x}\n", hash_hdr.hash_layer_info[0].offset); - fmt::print(" Size: 0x{:x}\n", hash_hdr.hash_layer_info[0].size); - fmt::print(" Data Layer:\n"); - fmt::print(" Offset: 0x{:x}\n", hash_hdr.data_layer_info.offset); - fmt::print(" Size: 0x{:x}\n", hash_hdr.data_layer_info.size); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHash().data(), 0x10, true, ":")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHash().data()+0x10, 0x10, true, ":")); + fmt::print(" HashBlockSize: 0x{:x}\n", hash_hdr.getHashBlockSize()); + for (size_t j = 0; j < hash_hdr.getLayerInfo().size(); j++) + { + if (j+1 == hash_hdr.getLayerInfo().size()) + { + fmt::print(" Data Layer:\n"); + } + else + { + fmt::print(" Hash Layer {:d}:\n", j); + } + fmt::print(" Offset: 0x{:x}\n", hash_hdr.getLayerInfo()[j].offset); + fmt::print(" Size: 0x{:x}\n", hash_hdr.getLayerInfo()[j].size); + } } } } @@ -550,6 +544,8 @@ void nstool::NcaProcess::displayHeader() void nstool::NcaProcess::processPartitions() { + std::vector mount_points; + for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++) { uint32_t index = mHdr.getPartitionEntryList()[i].header_index; @@ -566,6 +562,21 @@ void nstool::NcaProcess::processPartitions() fmt::print("\n"); continue; } + + std::string mount_point_name; + /* + if (mHdr.getContentType() == nn::hac::nca::ContentType::Program) + { + mount_point_name = nn::hac::ContentArchiveUtil::getProgramContentParititionIndexAsString((nn::hac::nca::ProgramContentPartitionIndex)index); + } + else + */ + { + mount_point_name = fmt::format("{:d}", index); + } + + mount_points.push_back( { mount_point_name, partition.fs_meta } ); + /* try { if (partition.format_type == nn::hac::nca::FormatType::PartitionFs) @@ -611,11 +622,24 @@ void nstool::NcaProcess::processPartitions() } */ } + + tc::io::VirtualFileSystem::FileSystemMeta fs_meta = nn::hac::CombinedFsMetaGenerator(mount_points); + + std::shared_ptr nca_fs = std::make_shared(tc::io::VirtualFileSystem(fs_meta)); + + mFsProcess.setInputFileSystem(nca_fs); + mFsProcess.setFsFormatName("ContentArchive"); + mFsProcess.setFsProperties({ + fmt::format("DirNum: {:d}", fs_meta.dir_entries.size()-1), + fmt::format("FileNum: {:d}", fs_meta.file_entries.size()-1) + }); + mFsProcess.setFsRootLabel(getContentTypeForMountStr(mHdr.getContentType())); + mFsProcess.process(); } -const char* nstool::NcaProcess::getContentTypeForMountStr(nn::hac::nca::ContentType cont_type) const +std::string nstool::NcaProcess::getContentTypeForMountStr(nn::hac::nca::ContentType cont_type) const { - const char* str = nullptr; + std::string str; switch (cont_type) { diff --git a/src/NcaProcess.h b/src/NcaProcess.h index 5fec415..fe3303e 100644 --- a/src/NcaProcess.h +++ b/src/NcaProcess.h @@ -4,7 +4,8 @@ #include "FsProcess.h" #include -#include +#include +#include namespace nstool { @@ -98,14 +99,17 @@ private: nn::hac::nca::FormatType format_type; nn::hac::nca::HashType hash_type; nn::hac::nca::EncryptionType enc_type; - nn::hac::HierarchicalValidatedStream::StreamInfo hashed_stream_info; - //fnd::LayeredIntegrityMetadata layered_intergrity_metadata; + + // hash meta data + nn::hac::HierarchicalIntegrityHeader hierarchicalintegrity_hdr; + nn::hac::HierarchicalSha256Header hierarchicalsha256_hdr; + + // crypto metadata nn::hac::detail::aes_iv_t aes_ctr; }; std::array mPartitions; - void importHeader(); void generateNcaBodyEncryptionKeys(); void generatePartitionConfiguration(); @@ -113,7 +117,7 @@ private: void displayHeader(); void processPartitions(); - const char* getContentTypeForMountStr(nn::hac::nca::ContentType cont_type) const; + std::string getContentTypeForMountStr(nn::hac::nca::ContentType cont_type) const; }; } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 5b91ee6..317d97a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -79,17 +79,8 @@ int umain(const std::vector& args, const std::vector& obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify); - /* - if (set.nca.part0_extract_path.isSet()) - obj.setPartition0ExtractPath(set.nca.part0_extract_path.get()); - if (set.nca.part1_extract_path.isSet()) - obj.setPartition1ExtractPath(set.nca.part1_extract_path.get()); - if (set.nca.part2_extract_path.isSet()) - obj.setPartition2ExtractPath(set.nca.part2_extract_path.get()); - if (set.nca.part3_extract_path.isSet()) - obj.setPartition3ExtractPath(set.nca.part3_extract_path.get()); - obj.setListFs(set.fs.show_fs_tree); - */ + obj.setShowFsTree(set.fs.show_fs_tree); + obj.setExtractJobs(set.fs.extract_jobs); obj.process(); } From d4232a1f22322455e48c2ee3ddd6e9ed177c2888 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Oct 2021 14:13:44 +0800 Subject: [PATCH 062/119] Remove not_ported dir. --- src/not_ported/RomfsProcess.cpp | 341 -------------------------------- src/not_ported/RomfsProcess.h | 136 ------------- 2 files changed, 477 deletions(-) delete mode 100644 src/not_ported/RomfsProcess.cpp delete mode 100644 src/not_ported/RomfsProcess.h diff --git a/src/not_ported/RomfsProcess.cpp b/src/not_ported/RomfsProcess.cpp deleted file mode 100644 index 93207e6..0000000 --- a/src/not_ported/RomfsProcess.cpp +++ /dev/null @@ -1,341 +0,0 @@ -#include -#include -#include -#include -#include -#include "CompressedArchiveIFile.h" -#include "RomfsProcess.h" - -nstool::RomfsProcess::RomfsProcess() : - mFile(), - mCliOutputMode(true, false, false, false), - mVerify(false), - mExtractPath(), - mExtract(false), - mMountName(), - mListFs(false), - mDirNum(0), - mFileNum(0) -{ - mRootDir.name.clear(); - mRootDir.dir_list.clear(); - mRootDir.file_list.clear(); -} - -void nstool::RomfsProcess::process() -{ - resolveRomfs(); - - if (mCliOutputMode.show_basic_info) - { - displayHeader(); - if (mListFs || mCliOutputMode.show_extended_info) - displayFs(); - } - - if (mExtract) - extractFs(); -} - -void nstool::RomfsProcess::setInputFile(const std::shared_ptr& file) -{ - mFile = file; -} - -void nstool::RomfsProcess::setCliOutputMode(CliOutputMode type) -{ - mCliOutputMode = type; -} - -void nstool::RomfsProcess::setVerifyMode(bool verify) -{ - mVerify = verify; -} - -void nstool::RomfsProcess::setMountPointName(const std::string& mount_name) -{ - mMountName = mount_name; -} - -void nstool::RomfsProcess::setExtractPath(const std::string& path) -{ - mExtract = true; - mExtractPath = path; -} - -void nstool::RomfsProcess::setListFs(bool list_fs) -{ - mListFs = list_fs; -} - -const nstool::RomfsProcess::sDirectory& nstool::RomfsProcess::getRootDir() const -{ - return mRootDir; -} - -void nstool::RomfsProcess::printTab(size_t tab) const -{ - for (size_t i = 0; i < tab; i++) - { - std::cout << " "; - } -} - -void nstool::RomfsProcess::displayFile(const sFile& file, size_t tab) const -{ - printTab(tab); - std::cout << file.name; - if (mCliOutputMode.show_layout) - { - std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ")"; - } - std::cout << std::endl; -} - -void nstool::RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const -{ - if (dir.name.empty() == false) - { - printTab(tab); - std::cout << dir.name << std::endl; - } - - for (size_t i = 0; i < dir.dir_list.size(); i++) - { - displayDir(dir.dir_list[i], tab+1); - } - for (size_t i = 0; i < dir.file_list.size(); i++) - { - displayFile(dir.file_list[i], tab+1); - } -} - -void nstool::RomfsProcess::displayHeader() -{ - std::cout << "[RomFS]" << std::endl; - std::cout << " DirNum: " << std::dec << mDirNum << std::endl; - std::cout << " FileNum: " << std::dec << mFileNum << std::endl; - if (mMountName.empty() == false) - { - std::cout << " MountPoint: " << mMountName; - if (mMountName.at(mMountName.length()-1) != '/') - std::cout << "/"; - std::cout << std::endl; - } -} - -void nstool::RomfsProcess::displayFs() -{ - displayDir(mRootDir, 1); -} - -void nstool::RomfsProcess::extractDir(const std::string& path, const sDirectory& dir) -{ - std::string dir_path; - std::string file_path; - - // make dir path - fnd::io::appendToPath(dir_path, path); - if (dir.name.empty() == false) - fnd::io::appendToPath(dir_path, dir.name); - - // make directory - fnd::io::makeDirectory(dir_path); - - // extract files - fnd::SimpleFile outFile; - for (size_t i = 0; i < dir.file_list.size(); i++) - { - file_path.clear(); - fnd::io::appendToPath(file_path, dir_path); - fnd::io::appendToPath(file_path, dir.file_list[i].name); - - if (mCliOutputMode.show_basic_info) - std::cout << "extract=[" << file_path << "]" << std::endl; - - outFile.open(file_path, outFile.Create); - (*mFile)->seek(dir.file_list[i].offset); - for (size_t j = 0; j < ((dir.file_list[i].size / kCacheSize) + ((dir.file_list[i].size % kCacheSize) != 0)); j++) - { - (*mFile)->read(mCache.data(), _MIN(dir.file_list[i].size - (kCacheSize * j),kCacheSize)); - outFile.write(mCache.data(), _MIN(dir.file_list[i].size - (kCacheSize * j),kCacheSize)); - } - outFile.close(); - } - - for (size_t i = 0; i < dir.dir_list.size(); i++) - { - extractDir(dir_path, dir.dir_list[i]); - } -} - - -void nstool::RomfsProcess::extractFs() -{ - // allocate only when extractDir is invoked - mCache.alloc(kCacheSize); - extractDir(mExtractPath, mRootDir); -} - -bool nstool::RomfsProcess::validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const -{ - bool validLayout = true; - - if (hdr->header_size.get() != sizeof(nn::hac::sRomfsHeader)) - { - validLayout = false; - } - - uint64_t pos = hdr->sections[0].offset.get(); - for (size_t i = 0; i < nn::hac::romfs::SECTION_NUM; i++) - { - if (hdr->sections[i].offset.get() != pos) - { - validLayout = false; - } - pos += hdr->sections[i].size.get(); - } - - return validLayout; -} - -void nstool::RomfsProcess::importDirectory(uint32_t dir_offset, sDirectory& dir) -{ - nn::hac::sRomfsDirEntry* d_node = get_dir_node(dir_offset); - - /* - printf("[DIR-NODE]\n"); - printf(" parent=%08x\n", d_node->parent.get()); - printf(" sibling=%08x\n", d_node->sibling.get()); - printf(" child=%08x\n", d_node->child.get()); - printf(" file=%08x\n", d_node->file.get()); - printf(" hash=%08x\n", d_node->hash.get()); - printf(" name_size=%08x\n", d_node->name_size.get()); - printf(" name=%s\n", d_node->name); - */ - - for (uint32_t file_addr = d_node->file.get(); file_addr != nn::hac::romfs::kInvalidAddr; ) - { - nn::hac::sRomfsFileEntry* f_node = get_file_node(file_addr); - - /* - printf("[FILE-NODE]\n"); - printf(" parent=%08x\n", f_node->parent.get()); - printf(" sibling=%08x\n", f_node->sibling.get()); - printf(" offset=%08" PRIx64 "\n", f_node->offset.get()); - printf(" size=%08" PRIx64 "\n", f_node->size.get()); - printf(" hash=%08x\n", f_node->hash.get()); - printf(" name_size=%08x\n", f_node->name_size.get()); - printf(" name=%s\n", f_node->name); - */ - - dir.file_list.push_back({std::string(f_node->name(), f_node->name_size.get()), mHdr.data_offset.get() + f_node->offset.get(), f_node->size.get()}); - - file_addr = f_node->sibling.get(); - mFileNum++; - } - - for (uint32_t child_addr = d_node->child.get(); child_addr != nn::hac::romfs::kInvalidAddr; ) - { - nn::hac::sRomfsDirEntry* c_node = get_dir_node(child_addr); - - dir.dir_list.push_back({std::string(c_node->name(), c_node->name_size.get())}); - importDirectory(child_addr, dir.dir_list.atBack()); - - child_addr = c_node->sibling.get(); - mDirNum++; - } -} - -void nstool::RomfsProcess::resolveRomfs() -{ - if (*mFile == nullptr) - { - throw tc::Exception(kModuleName, "No file reader set."); - } - - // read header - (*mFile)->read((byte_t*)&mHdr, 0, sizeof(nn::hac::sRomfsHeader)); - - // logic check on the header layout - if (validateHeaderLayout(&mHdr) == false) - { - throw tc::Exception(kModuleName, "Invalid ROMFS Header"); - } - - // check for romfs compression - size_t physical_size = (*mFile)->size(); - size_t logical_size = mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].offset.get() + mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].size.get(); - - // if logical size is greater than the physical size, check for compression meta footer - if (logical_size > physical_size) - { - // initial and final entries - nn::hac::sCompressionEntry entry[2]; - - // read final compression entry - (*mFile)->read((byte_t*)&entry[1], physical_size - sizeof(nn::hac::sCompressionEntry), sizeof(nn::hac::sCompressionEntry)); - - // the final compression entry should be for the (final part, in the case of metadata > 0x10000) romfs footer, for which the logical offset is detailed in the romfs header - // the compression is always enabled for non-header compression entries - uint64_t romfs_metadata_begin_offset = mHdr.sections[nn::hac::romfs::DIR_HASHMAP_TABLE].offset.get(); - uint64_t romfs_metadata_end_offset = mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].offset.get() + mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].size.get(); - - if ((entry[1].virtual_offset.get() >= romfs_metadata_begin_offset && entry[1].virtual_offset.get() < romfs_metadata_end_offset) == false || \ - entry[1].compression_type != (byte_t)nn::hac::compression::CompressionType::Lz4) - { - throw tc::Exception(kModuleName, "RomFs appears corrupted (bad final compression entry virtual offset/compression type)"); - } - - // the first compression entry follows the physical placement of the final data chunk (specified in the final compression entry) - size_t first_entry_offset = align(entry[1].physical_offset.get() + entry[1].physical_size.get(), nn::hac::compression::kRomfsBlockAlign); - - // quick check to make sure the offset at least before the last entry offset - if (first_entry_offset >= (physical_size - sizeof(nn::hac::sCompressionEntry))) - { - throw tc::Exception(kModuleName, "RomFs appears corrupted (bad final compression entry physical offset/size)"); - } - - // read first compression entry - (*mFile)->read((byte_t*)&entry[0], first_entry_offset, sizeof(nn::hac::sCompressionEntry)); - - // validate first compression entry - // this should be the same for all compressed romfs - if (entry[0].virtual_offset.get() != 0x0 || \ - entry[0].physical_offset.get() != 0x0 || \ - entry[0].physical_size.get() != 0x200 || \ - entry[0].compression_type != (byte_t)nn::hac::compression::CompressionType::None) - { - throw tc::Exception(kModuleName, "RomFs appears corrupted (bad first compression entry)"); - } - - // wrap mFile in a class to transparantly decompress the image. - mFile = new CompressedArchiveIFile(mFile, first_entry_offset); - } - - // read directory nodes - mDirNodes.alloc(mHdr.sections[nn::hac::romfs::DIR_NODE_TABLE].size.get()); - (*mFile)->read(mDirNodes.data(), mHdr.sections[nn::hac::romfs::DIR_NODE_TABLE].offset.get(), mDirNodes.size()); - //printf("[RAW DIR NODES]\n"); - //fnd::SimpleTextOutput::hxdStyleDump(mDirNodes.data(), mDirNodes.size()); - - // read file nodes - mFileNodes.alloc(mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].size.get()); - (*mFile)->read(mFileNodes.data(), mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].offset.get(), mFileNodes.size()); - //printf("[RAW FILE NODES]\n"); - //fnd::SimpleTextOutput::hxdStyleDump(mFileNodes.data(), mFileNodes.size()); - - // A logic check on the root directory node - if ( get_dir_node(0)->parent.get() != 0 \ - || get_dir_node(0)->sibling.get() != nn::hac::romfs::kInvalidAddr \ - || get_dir_node(0)->hash.get() != nn::hac::romfs::kInvalidAddr \ - || get_dir_node(0)->name_size.get() != 0) - { - throw tc::Exception(kModuleName, "Invalid root directory node"); - } - - // import directory into internal structure - mDirNum = 0; - mFileNum = 0; - importDirectory(0, mRootDir); -} \ No newline at end of file diff --git a/src/not_ported/RomfsProcess.h b/src/not_ported/RomfsProcess.h deleted file mode 100644 index 35575c4..0000000 --- a/src/not_ported/RomfsProcess.h +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once -#include "types.h" - -#include - -namespace nstool { - -class RomfsProcess -{ -public: - struct sDirectory; - struct sFile; - - struct sDirectory - { - std::string name; - std::vector dir_list; - std::vector file_list; - - void operator=(const sDirectory& other) - { - name = other.name; - dir_list = other.dir_list; - file_list = other.file_list; - } - - bool operator==(const sDirectory& other) const - { - return (name == other.name) \ - && (dir_list == other.dir_list) \ - && (file_list == other.file_list); - } - - bool operator!=(const sDirectory& other) const - { - return !operator==(other); - } - - bool operator==(const std::string& other) const - { - return (name == other); - } - }; - - struct sFile - { - std::string name; - int64_t offset; - int64_t size; - - void operator=(const sFile& other) - { - name = other.name; - offset = other.offset; - size = other.size; - } - - bool operator==(const sFile& other) const - { - return (name == other.name) \ - && (offset == other.offset) \ - && (size == other.size); - } - - bool operator!=(const sFile& other) const - { - return !operator==(other); - } - - bool operator==(const std::string& other) const - { - return (name == other); - } - }; - - RomfsProcess(); - - void process(); - - // generic - void setInputFile(const std::shared_ptr& file); - void setCliOutputMode(CliOutputMode type); - void setVerifyMode(bool verify); - - // romfs specific - void setMountPointName(const std::string& mount_name); - void setExtractPath(const tc::io::Path& path); - void setListFs(bool list_fs); - - const sDirectory& getRootDir() const; -private: - const std::string kModuleName = "RomfsProcess"; - static const size_t kCacheSize = 0x10000; - - std::shared_ptr mFile; - CliOutputMode mCliOutputMode; - bool mShowBasicInfo; - bool mShowExtendedInfo; - bool mShowLayoutInfo; - bool mShowKeydata; - bool mVerbose; - bool mVerify; - - tc::Optional mExtractPath; - std::string mMountName; - bool mListFs; - - tc::ByteData mCache; - - size_t mDirNum; - size_t mFileNum; - nn::hac::sRomfsHeader mHdr; - tc::ByteData mDirNodes; - tc::ByteData mFileNodes; - sDirectory mRootDir; - - inline nn::hac::sRomfsDirEntry* get_dir_node(uint32_t offset) { return (nn::hac::sRomfsDirEntry*)(mDirNodes.data() + offset); } - inline nn::hac::sRomfsFileEntry* get_file_node(uint32_t offset) { return (nn::hac::sRomfsFileEntry*)(mFileNodes.data() + offset); } - - - void printTab(size_t tab) const; - void displayFile(const sFile& file, size_t tab) const; - void displayDir(const sDirectory& dir, size_t tab) const; - - void displayHeader(); - void displayFs(); - - void extractDir(const std::string& path, const sDirectory& dir); - void extractFs(); - - bool validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const; - void importDirectory(uint32_t dir_offset, sDirectory& dir); - void resolveRomfs(); -}; - -} \ No newline at end of file From ac89a8788ff6c6d40384e47e45dfd7cc799f9c84 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Oct 2021 14:28:35 +0800 Subject: [PATCH 063/119] Misc formatting issue. --- src/NcaProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index 4cc2a21..ccc042f 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -460,7 +460,7 @@ void nstool::NcaProcess::displayHeader() if (mContentKey.kak_list[i].decrypted) fmt::print("{:s}", tc::cli::FormatUtil::formatBytesAsString(mContentKey.kak_list[i].dec.data(), mContentKey.kak_list[i].dec.size(), true, ":")); else - fmt::print(" "); + fmt::print(" "); fmt::print(" |\n"); } From c6d8b9707b0bffec657e937ada5dd66ca504889e Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Oct 2021 15:41:45 +0800 Subject: [PATCH 064/119] Fix ivfc hash fail bug. --- deps/libnintendo-hac | 2 +- src/NcaProcess.cpp | 47 ++------------------------------------------ 2 files changed, 3 insertions(+), 46 deletions(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index af43764..d806d65 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit af437641b9d14381baf69c9399f4177cece7840c +Subproject commit d806d65bb7e86e3b24ebbc6ab4279ec2eb92c0bd diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index ccc042f..0c897a2 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -336,6 +336,8 @@ void nstool::NcaProcess::generatePartitionConfiguration() // filter out unrecognised hash types, and hash based readers switch (info.hash_type) { + case (nn::hac::nca::HashType::None): + break; case (nn::hac::nca::HashType::HierarchicalSha256): info.reader = std::make_shared(nn::hac::HierarchicalSha256Stream(info.reader, info.hierarchicalsha256_hdr)); break; @@ -576,51 +578,6 @@ void nstool::NcaProcess::processPartitions() } mount_points.push_back( { mount_point_name, partition.fs_meta } ); - - /* - try { - if (partition.format_type == nn::hac::nca::FormatType::PartitionFs) - { - PfsProcess pfs; - pfs.setInputFile(partition.reader); - pfs.setCliOutputMode(mCliOutputMode); - pfs.setListFs(mListFs); - if (mHdr.getContentType() == nn::hac::nca::ContentType::Program) - { - pfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + nn::hac::ContentArchiveUtil::getProgramContentParititionIndexAsString((nn::hac::nca::ProgramContentPartitionIndex)index)); - } - else - { - pfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/"); - } - - if (mPartitionPath[index].doExtract) - pfs.setExtractPath(mPartitionPath[index].path); - pfs.process(); - } - else if (partition.format_type == nn::hac::nca::FormatType::RomFs) - { - RomfsProcess romfs; - romfs.setInputFile(partition.reader); - romfs.setCliOutputMode(mCliOutputMode); - romfs.setListFs(mListFs); - if (mHdr.getContentType() == nn::hac::nca::ContentType::Program) - { - romfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + nn::hac::ContentArchiveUtil::getProgramContentParititionIndexAsString((nn::hac::nca::ProgramContentPartitionIndex)index)); - } - else - { - romfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/"); - } - - if (mPartitionPath[index].doExtract) - romfs.setExtractPath(mPartitionPath[index].path); - romfs.process(); - } - } catch (const tc::Exception& e) { - std::cout << "[WARNING] NCA Partition " << std::dec << index << " not readable (" << e.error() << ")." << std::endl; - } - */ } tc::io::VirtualFileSystem::FileSystemMeta fs_meta = nn::hac::CombinedFsMetaGenerator(mount_points); From 836a85f71e5818834c9694d34d9970ff707f4ac4 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Oct 2021 15:44:06 +0800 Subject: [PATCH 065/119] Misc --- deps/libnintendo-hac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index d806d65..5b3e894 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit d806d65bb7e86e3b24ebbc6ab4279ec2eb92c0bd +Subproject commit 5b3e894d3f0e46e481c113fa5c7adb00a533fa3d From 008c76a82d1b5bad07d649da20af0797af6fc319 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Oct 2021 16:00:17 +0800 Subject: [PATCH 066/119] Rename PkiCertProcess to EsCertProcess --- build/visualstudio/nstool/nstool.vcxproj | 4 +- .../nstool/nstool.vcxproj.filters | 48 +++++++-------- src/{PkiCertProcess.cpp => EsCertProcess.cpp} | 32 +++++----- src/{PkiCertProcess.h => EsCertProcess.h} | 4 +- src/Settings.cpp | 4 +- src/Settings.h | 2 +- src/main.cpp | 59 +------------------ 7 files changed, 50 insertions(+), 103 deletions(-) rename src/{PkiCertProcess.cpp => EsCertProcess.cpp} (86%) rename src/{PkiCertProcess.h => EsCertProcess.h} (95%) diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index 309584a..e9bac82 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -156,6 +156,7 @@ + @@ -168,7 +169,6 @@ - @@ -182,6 +182,7 @@ + @@ -195,7 +196,6 @@ - diff --git a/build/visualstudio/nstool/nstool.vcxproj.filters b/build/visualstudio/nstool/nstool.vcxproj.filters index f8362e7..9af3240 100644 --- a/build/visualstudio/nstool/nstool.vcxproj.filters +++ b/build/visualstudio/nstool/nstool.vcxproj.filters @@ -27,6 +27,9 @@ Header Files + + Header Files + Header Files @@ -63,9 +66,6 @@ Header Files - - Header Files - Header Files @@ -101,6 +101,9 @@ Source Files + + Source Files + Source Files @@ -110,15 +113,33 @@ Source Files + + Source Files + Source Files + + Source Files + Source Files + + Source Files + Source Files + + Source Files + + + Source Files + + + Source Files + Source Files @@ -140,26 +161,5 @@ Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - \ No newline at end of file diff --git a/src/PkiCertProcess.cpp b/src/EsCertProcess.cpp similarity index 86% rename from src/PkiCertProcess.cpp rename to src/EsCertProcess.cpp index 6ddb2b1..8671003 100644 --- a/src/PkiCertProcess.cpp +++ b/src/EsCertProcess.cpp @@ -1,18 +1,18 @@ -#include "PkiCertProcess.h" +#include "EsCertProcess.h" #include "PkiValidator.h" #include "util.h" #include -nstool::PkiCertProcess::PkiCertProcess() : - mModuleName("nstool::PkiCertProcess"), +nstool::EsCertProcess::EsCertProcess() : + mModuleName("nstool::EsCertProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false) { } -void nstool::PkiCertProcess::process() +void nstool::EsCertProcess::process() { importCerts(); @@ -23,27 +23,27 @@ void nstool::PkiCertProcess::process() displayCerts(); } -void nstool::PkiCertProcess::setInputFile(const std::shared_ptr& file) +void nstool::EsCertProcess::setInputFile(const std::shared_ptr& file) { mFile = file; } -void nstool::PkiCertProcess::setKeyCfg(const KeyBag& keycfg) +void nstool::EsCertProcess::setKeyCfg(const KeyBag& keycfg) { mKeyCfg = keycfg; } -void nstool::PkiCertProcess::setCliOutputMode(CliOutputMode mode) +void nstool::EsCertProcess::setCliOutputMode(CliOutputMode mode) { mCliOutputMode = mode; } -void nstool::PkiCertProcess::setVerifyMode(bool verify) +void nstool::EsCertProcess::setVerifyMode(bool verify) { mVerify = verify; } -void nstool::PkiCertProcess::importCerts() +void nstool::EsCertProcess::importCerts() { if (mFile == nullptr) { @@ -74,7 +74,7 @@ void nstool::PkiCertProcess::importCerts() } } -void nstool::PkiCertProcess::validateCerts() +void nstool::EsCertProcess::validateCerts() { PkiValidator pki; @@ -90,7 +90,7 @@ void nstool::PkiCertProcess::validateCerts() } } -void nstool::PkiCertProcess::displayCerts() +void nstool::EsCertProcess::displayCerts() { for (size_t i = 0; i < mCert.size(); i++) { @@ -98,9 +98,9 @@ void nstool::PkiCertProcess::displayCerts() } } -void nstool::PkiCertProcess::displayCert(const nn::pki::SignedData& cert) +void nstool::EsCertProcess::displayCert(const nn::pki::SignedData& cert) { - fmt::print("[NNPKI Certificate]\n"); + fmt::print("[ES Certificate]\n"); fmt::print(" SignType {:s}", getSignTypeStr(cert.getSignature().getSignType())); if (mCliOutputMode.show_extended_info) @@ -171,7 +171,7 @@ void nstool::PkiCertProcess::displayCert(const nn::pki::SignedData& args, const std::vector& obj.process(); } - else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PKI_CERT) + else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ES_CERT) { - nstool::PkiCertProcess obj; + nstool::EsCertProcess obj; obj.setInputFile(infile_stream); obj.setKeyCfg(set.opt.keybag); @@ -215,59 +215,6 @@ int umain(const std::vector& args, const std::vector& obj.process(); } - - switch (set.infile.filetype) - { - case nstool::Settings::FILE_TYPE_GAMECARD : - fmt::print("## FILE_TYPE_GAMECARD ##\n"); - break; - case nstool::Settings::FILE_TYPE_NSP : - fmt::print("## FILE_TYPE_NSP ##\n"); - break; - case nstool::Settings::FILE_TYPE_PARTITIONFS : - fmt::print("## FILE_TYPE_PARTITIONFS ##\n"); - break; - case nstool::Settings::FILE_TYPE_ROMFS : - fmt::print("## FILE_TYPE_ROMFS ##\n"); - break; - case nstool::Settings::FILE_TYPE_NCA : - fmt::print("## FILE_TYPE_NCA ##\n"); - break; - case nstool::Settings::FILE_TYPE_META : - fmt::print("## FILE_TYPE_META ##\n"); - break; - case nstool::Settings::FILE_TYPE_CNMT : - fmt::print("## FILE_TYPE_CNMT ##\n"); - break; - case nstool::Settings::FILE_TYPE_NSO : - fmt::print("## FILE_TYPE_NSO ##\n"); - break; - case nstool::Settings::FILE_TYPE_NRO : - fmt::print("## FILE_TYPE_NRO ##\n"); - break; - case nstool::Settings::FILE_TYPE_NACP : - fmt::print("## FILE_TYPE_NACP ##\n"); - break; - case nstool::Settings::FILE_TYPE_INI : - fmt::print("## FILE_TYPE_INI ##\n"); - break; - case nstool::Settings::FILE_TYPE_KIP : - fmt::print("## FILE_TYPE_KIP ##\n"); - break; - case nstool::Settings::FILE_TYPE_PKI_CERT : - fmt::print("## FILE_TYPE_PKI_CERT ##\n"); - break; - case nstool::Settings::FILE_TYPE_ES_TIK : - fmt::print("## FILE_TYPE_ES_TIK ##\n"); - break; - case nstool::Settings::FILE_TYPE_HB_ASSET : - fmt::print("## FILE_TYPE_HB_ASSET ##\n"); - break; - default: - fmt::print("## unknown({}) ##\n", (int)set.infile.filetype); - break; - } - } catch (tc::Exception& e) { From 289470d2a80205af27353f7565f80ee89723722c Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Oct 2021 16:01:09 +0800 Subject: [PATCH 067/119] Update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 38cf29b..b290e96 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ General purpose reading/extraction tool for Nintendo Switch file formats. ## Supported File Formats * Meta (.npdm) -* PartitionFS (and HashedPartitionFS) (includes raw .nsp) -* RomFS (and CompressedRomFS) +* PartitionFS (and Sha256PartitionFS) (includes raw .nsp) +* RomFS * NX GameCard Image (.xci) * Nintendo Content Archive (.nca) * Content Metadata (.cnmt) @@ -13,8 +13,8 @@ General purpose reading/extraction tool for Nintendo Switch file formats. * Kernel Initial Process List (.ini) * Kernel Initial Process (.kip) * Nintendo Application Control Property (.nacp) +* ES Certificate (.cert) * ES Ticket (v2 only) (.tik) -* PKI Certificate (.cert) # Usage ``` From 42de7f6bdc66ca3d5456014ffbe719e3e414a9a1 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Oct 2021 16:40:14 +0800 Subject: [PATCH 068/119] Begin SparseFs impl --- src/NcaProcess.cpp | 15 +++++++++++++++ src/NcaProcess.h | 10 +++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index 0c897a2..a45ff83 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -239,6 +239,21 @@ void nstool::NcaProcess::generatePartitionConfiguration() throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Version({:d}): UNSUPPORTED", partition.header_index, fs_header.version.unwrap())); } + // detect sparse + if (fs_header.sparse_info.generation.unwrap() != 0) + { + fmt::print("[IsSparse]\n"); + fmt::print(" bucket:\n"); + fmt::print(" offset: 0x{:x}\n", fs_header.sparse_info.bucket.offset.unwrap()); + fmt::print(" size: 0x{:x}\n", fs_header.sparse_info.bucket.offset.unwrap()); + fmt::print(" header:\n"); + fmt::print(" st_magic: {:s}\n", ((tc::bn::string<4>*)&fs_header.sparse_info.bucket.header.st_magic)->str()); + fmt::print(" version: 0x{:x}\n", fs_header.sparse_info.bucket.header.version.unwrap()); + fmt::print(" entry_count: {:d}\n", fs_header.sparse_info.bucket.header.entry_count.unwrap()); + fmt::print(" physical_offset: 0x{:x}\n", fs_header.sparse_info.physical_offset.unwrap()); + fmt::print(" generation: 0x{:x}\n", fs_header.sparse_info.generation.unwrap()); + } + // setup AES-CTR nn::hac::ContentArchiveUtil::getNcaPartitionAesCtr(&fs_header, info.aes_ctr.data()); diff --git a/src/NcaProcess.h b/src/NcaProcess.h index fe3303e..535a0d8 100644 --- a/src/NcaProcess.h +++ b/src/NcaProcess.h @@ -84,7 +84,12 @@ private: tc::Optional aes_ctr; } mContentKey; - + + struct SparseInfo + { + + }; + // raw partition data struct sPartitionInfo { @@ -106,6 +111,9 @@ private: // crypto metadata nn::hac::detail::aes_iv_t aes_ctr; + + // sparse metadata + SparseInfo sparse_info; }; std::array mPartitions; From fa3da57dcf967f27c87ff1cbc7d89a791e5a4a20 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Oct 2021 16:41:21 +0800 Subject: [PATCH 069/119] Misc --- src/NcaProcess.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index a45ff83..88c74a8 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -601,10 +601,6 @@ void nstool::NcaProcess::processPartitions() mFsProcess.setInputFileSystem(nca_fs); mFsProcess.setFsFormatName("ContentArchive"); - mFsProcess.setFsProperties({ - fmt::format("DirNum: {:d}", fs_meta.dir_entries.size()-1), - fmt::format("FileNum: {:d}", fs_meta.file_entries.size()-1) - }); mFsProcess.setFsRootLabel(getContentTypeForMountStr(mHdr.getContentType())); mFsProcess.process(); } From 122350b8e5b66b09f69c150a20f2804288f0f29b Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 19 Oct 2021 16:20:15 +0800 Subject: [PATCH 070/119] Fix bug where writeStreamToStream() would not overwrite an existing file. --- src/util.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util.cpp b/src/util.cpp index 0147dce..a3645ac 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -68,22 +68,22 @@ void nstool::processResFile(const std::shared_ptr& file, std::m void nstool::writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, tc::ByteData& cache) { - writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache); + writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::Create, tc::io::FileAccess::Write)), cache); } void nstool::writeSubStreamToFile(const std::shared_ptr& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size) { - writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache_size); + writeStreamToStream(std::make_shared(tc::io::SubStream(in_stream, offset, length)), std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::Create, tc::io::FileAccess::Write)), cache_size); } void nstool::writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, tc::ByteData& cache) { - writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache); + writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::Create, tc::io::FileAccess::Write)), cache); } void nstool::writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size) { - writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write)), cache_size); + writeStreamToStream(in_stream, std::make_shared(tc::io::FileStream(out_path, tc::io::FileMode::Create, tc::io::FileAccess::Write)), cache_size); } void nstool::writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, tc::ByteData& cache) From ac3d1d493ed4935e1b4d2907eaacc24d0b3316f9 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 19 Oct 2021 16:22:59 +0800 Subject: [PATCH 071/119] Fix title key decryption & continue SparseLayer research. --- deps/libnintendo-hac | 2 +- src/KeyBag.cpp | 11 ++++- src/NcaProcess.cpp | 95 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 5b3e894..372517d 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 5b3e894d3f0e46e481c113fa5c7adb00a533fa3d +Subproject commit 372517dc3df76fb7026af757760ded29ad8ee0fc diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 3938e5a..87152cf 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -568,16 +568,23 @@ void nstool::KeyBagInitializer::importTicket(const tc::io::Path& tik_path) byte_t common_key_index = tik.getBody().getCommonKeyId(); // work around for bad scene tickets where they don't set the commonkey id field (detect scene ticket with ffff.... signature) - if (common_key_index != rights_id[15] && tik.getSignature().getBytes()[0x00] == 0xff && tik.getSignature().getBytes()[0x01] == 0xff) + auto signature_bytes = tik.getSignature().getSignature(); + if (common_key_index != rights_id[15] && *((uint64_t*)signature_bytes.data()) == (uint64_t)0xffffffffffffffff) { common_key_index = rights_id[15]; } - if (etik_common_key.find(tik.getBody().getCommonKeyId()) == etik_common_key.end()) + + // convert key_generation + common_key_index = nn::hac::AesKeygen::getMasterKeyRevisionFromKeyGeneration(common_key_index); + + if (etik_common_key.find(common_key_index) == etik_common_key.end()) { fmt::print("[WARNING] Ticket \"{:s}\" will not be imported. Could not decrypt title key.\n", tc::cli::FormatUtil::formatBytesAsString(rights_id.data(), rights_id.size(), true, "")); return; } + fmt::print("[TIK] decrypt title key with index 0x{:x}\n", common_key_index); + // decrypt title key aes128_key_t dec_title_key; tc::crypto::DecryptAes128Ecb(dec_title_key.data(), enc_title_key.data(), sizeof(aes128_key_t), etik_common_key[common_key_index].data(), sizeof(aes128_key_t)); diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index 88c74a8..170d30d 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -1,7 +1,7 @@ #include "NcaProcess.h" #include "MetaProcess.h" +#include "util.h" -//mak#include #include #include @@ -126,7 +126,7 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys() memset(zero_aesctr_key.data(), 0, zero_aesctr_key.size()); // get key data from header - byte_t masterkey_rev = nn::hac::ContentArchiveUtil::getMasterKeyRevisionFromKeyGeneration(mHdr.getKeyGeneration()); + byte_t masterkey_rev = nn::hac::AesKeygen::getMasterKeyRevisionFromKeyGeneration(mHdr.getKeyGeneration()); byte_t keak_index = mHdr.getKeyAreaEncryptionKeyIndex(); // process key area @@ -232,26 +232,109 @@ void nstool::NcaProcess::generatePartitionConfiguration() { throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Hash: FAIL", partition.header_index)); } - if (fs_header.version.unwrap() != nn::hac::nca::kDefaultFsHeaderVersion) { throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Version({:d}): UNSUPPORTED", partition.header_index, fs_header.version.unwrap())); } - // detect sparse + // detect sparse layer if (fs_header.sparse_info.generation.unwrap() != 0) { - fmt::print("[IsSparse]\n"); + fmt::print("[IsSparse {:d}]\n", partition.header_index); + fmt::print(" > raw data\n"); fmt::print(" bucket:\n"); fmt::print(" offset: 0x{:x}\n", fs_header.sparse_info.bucket.offset.unwrap()); - fmt::print(" size: 0x{:x}\n", fs_header.sparse_info.bucket.offset.unwrap()); + fmt::print(" size: 0x{:x}\n", fs_header.sparse_info.bucket.size.unwrap()); fmt::print(" header:\n"); fmt::print(" st_magic: {:s}\n", ((tc::bn::string<4>*)&fs_header.sparse_info.bucket.header.st_magic)->str()); fmt::print(" version: 0x{:x}\n", fs_header.sparse_info.bucket.header.version.unwrap()); fmt::print(" entry_count: {:d}\n", fs_header.sparse_info.bucket.header.entry_count.unwrap()); fmt::print(" physical_offset: 0x{:x}\n", fs_header.sparse_info.physical_offset.unwrap()); fmt::print(" generation: 0x{:x}\n", fs_header.sparse_info.generation.unwrap()); + + fmt::print(" > processed data\n"); + fmt::print(" file_size: 0x{:x}\n", mFile->length()); + int64_t phys_offset = fs_header.sparse_info.physical_offset.unwrap(); + int64_t phys_size = fs_header.sparse_info.bucket.offset.unwrap() + fs_header.sparse_info.bucket.size.unwrap(); + fmt::print(" phys_offset: 0x{:x}\n", phys_offset); + fmt::print(" phys_size: 0x{:x}\n", phys_size); + + nn::hac::detail::aes_iv_t sparse_iv; + + nn::hac::ContentArchiveUtil::getNcaPartitionAesCtr(uint32_t(fs_header.sparse_info.generation.unwrap()) << 16, fs_header.secure_value.unwrap(), sparse_iv.data()); + + if (phys_size != 0 && mContentKey.aes_ctr.isSet()) + { + std::shared_ptr sparse_layer; + + sparse_layer = mFile; + sparse_layer = std::make_shared(tc::crypto::Aes128CtrEncryptedStream(sparse_layer, mContentKey.aes_ctr.get(), sparse_iv)); + sparse_layer = std::make_shared(tc::io::SubStream(sparse_layer, phys_offset, phys_size)); + + tc::ByteData sparse_bktr = tc::ByteData(fs_header.sparse_info.bucket.size.unwrap()); + sparse_layer->seek(fs_header.sparse_info.bucket.offset.unwrap(), tc::io::SeekOrigin::Begin); + sparse_layer->read(sparse_bktr.data(), sparse_bktr.size()); + + //writeSubStreamToFile(sparse_layer, fs_header.sparse_info.bucket.offset.unwrap(), fs_header.sparse_info.bucket.size.unwrap(), tc::io::Path("./data/sparse_bktr.bin")); + +#pragma pack(push,1) + struct sRelocationHeader + { + tc::bn::pad<4> padding; + tc::bn::le32 bucket_num; + tc::bn::le64 virtual_image_size; + std::array, 2046> bucket_base_virtual_offset; + }; + static_assert(sizeof(sRelocationHeader) == 0x4000, "sRelocationHeader size."); + + struct sRelocationBucketHeader + { + tc::bn::pad<4> padding; + tc::bn::le32 bucket_num; + tc::bn::le64 bucket_end_offset; + }; + static_assert(sizeof(sRelocationBucketHeader) == 0x10, "sRelocationBucketHeader size."); + + struct sRelocationBucketEntry + { + tc::bn::le64 patch_addr; + tc::bn::le64 base_addr; + tc::bn::le32 storage_index; // 0 = base, 1 = patch + }; + static_assert(sizeof(sRelocationBucketEntry) == 0x14, "sRelocationBucketEntry size."); + + struct sRelocationBucket + { + sRelocationBucketHeader head; + std::array entry; + tc::bn::pad<8> padding; + }; + static_assert(sizeof(sRelocationBucket) == 0x4000, "sRelocationBucket size."); +#pragma pack(pop) + + sRelocationHeader* header = (sRelocationHeader*)sparse_bktr.data(); + sRelocationBucket* bucket = (sRelocationBucket*)(sparse_bktr.data() + sizeof(sRelocationHeader)); + fmt::print("[RelocationHeader]\n"); + fmt::print(" head:\n"); + fmt::print(" bucket_num: {:d}\n", header->bucket_num.unwrap()); + fmt::print(" virtual_image_size: 0x{:016x}\n", header->virtual_image_size.unwrap()); + for (size_t bucket_idx = 0; bucket_idx < header->bucket_num.unwrap(); bucket_idx++) + { + fmt::print(" bucket {:d}:\n", bucket_idx); + fmt::print(" start_offset: 0x{:016x}\n", header->bucket_base_virtual_offset[bucket_idx].unwrap()); + fmt::print(" end_offset: 0x{:016x}\n", bucket[bucket_idx].head.bucket_end_offset.unwrap()); + fmt::print(" entry_num: {:d}\n", bucket[bucket_idx].head.bucket_num.unwrap()); + for (size_t entry_idx = 0; entry_idx < bucket[bucket_idx].head.bucket_num.unwrap(); entry_idx++) + { + fmt::print(" entry {:d}\n", entry_idx); + fmt::print(" patch_addr: 0x{:016x}\n", bucket[bucket_idx].entry[entry_idx].patch_addr.unwrap()); + fmt::print(" base_addr: 0x{:016x}{:s}\n", bucket[bucket_idx].entry[entry_idx].base_addr.unwrap(), (bucket[bucket_idx].entry[entry_idx].storage_index.unwrap() == 0 && bucket[bucket_idx].entry[entry_idx].base_addr.unwrap() == 0) ? " NEEDED A VALID OFFSET" : ""); + fmt::print(" storage_index: {:s}\n", bucket[bucket_idx].entry[entry_idx].storage_index.unwrap() == 0 ? "BASE" : "PATCH"); + } + + } + } } // setup AES-CTR From 48afdbbad57f51d39e130c94adde418f02393de7 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 19 Oct 2021 17:17:56 +0800 Subject: [PATCH 072/119] Update libtoolchain --- deps/libnintendo-hac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 372517d..b0f4749 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 372517dc3df76fb7026af757760ded29ad8ee0fc +Subproject commit b0f474934f1c91bf0cefdb636c4c2e1d10ae8323 From 620d0609d6ea769c760759cf8705774815d119af Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 22 Oct 2021 04:11:36 +0800 Subject: [PATCH 073/119] Show ticket property mask even when empty with verbose output. --- src/EsTikProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EsTikProcess.cpp b/src/EsTikProcess.cpp index e0b1ed9..db6b2ca 100644 --- a/src/EsTikProcess.cpp +++ b/src/EsTikProcess.cpp @@ -132,7 +132,7 @@ void nstool::EsTikProcess::displayTicket() } fmt::print(" Version: {:s} (v{:d})\n", getTitleVersionStr(body.getTicketVersion()), body.getTicketVersion()); fmt::print(" License Type: {:s}\n", getLicenseTypeStr(body.getLicenseType())); - if (body.getPropertyFlags().size() > 0) + if (body.getPropertyFlags().size() > 0 || mCliOutputMode.show_extended_info) { nn::es::sTicketBody_v2* raw_body = (nn::es::sTicketBody_v2*)body.getBytes().data(); fmt::print(" PropertyMask: 0x{:04x}\n", ((tc::bn::le16*)&raw_body->property_mask)->unwrap()); From be0f5c29e01ed787a47019acd5bb27dcacaed7c8 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 22 Oct 2021 04:12:03 +0800 Subject: [PATCH 074/119] Better detect, handle and warn when a custom ticket is malformed. --- src/KeyBag.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 87152cf..0f97ed9 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -568,10 +568,12 @@ void nstool::KeyBagInitializer::importTicket(const tc::io::Path& tik_path) byte_t common_key_index = tik.getBody().getCommonKeyId(); // work around for bad scene tickets where they don't set the commonkey id field (detect scene ticket with ffff.... signature) - auto signature_bytes = tik.getSignature().getSignature(); - if (common_key_index != rights_id[15] && *((uint64_t*)signature_bytes.data()) == (uint64_t)0xffffffffffffffff) + if (common_key_index == 0 && *((uint64_t*)tik.getSignature().getSignature().data()) == (uint64_t)0xffffffffffffffff) { - common_key_index = rights_id[15]; + fmt::print("[WARNING] Ticket \"{:s}\" is fake-signed, and NCA decryption may fail if ticket was incorrectly generated.", tc::cli::FormatUtil::formatBytesAsString(rights_id.data(), rights_id.size(), true, "")); + // the keygeneration was included in the rights_id from keygeneration 0x03 and onwards, so in those cases we can copy from there + if (rights_id[15] >= 0x03) + common_key_index = rights_id[15]; } // convert key_generation From ebdf0211f40d7f34b8a5f2358f65de23ed3399cc Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 22 Oct 2021 20:56:39 +0800 Subject: [PATCH 075/119] Pre-load common ES Certificates/PublicKeys. --- deps/libnintendo-hac | 2 +- src/KeyBag.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index b0f4749..23924e7 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit b0f474934f1c91bf0cefdb636c4c2e1d10ae8323 +Subproject commit 23924e7b646c3eb66526ad9094b47b7ba1c2d1ea diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 0f97ed9..ba8e7b4 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -879,6 +879,82 @@ void nstool::KeyBagInitializer::importKnownKeys(bool isDev) }, }; + struct sBroadOnRsaKeyAndCert + { + std::string issuer; + nn::pki::sign::SignatureAlgo key_type; + tc::ByteData modulus; + tc::ByteData certificate; + }; + + static const std::vector kProdBroadOnRsaKeyAndCert = + { + { + "Root", + nn::pki::sign::SIGN_ALGO_RSA4096, + tc::cli::FormatUtil::hexStringToBytes("F8246C58BAE7500301FBB7C2EBE0010571DA922378F0514EC0031DD0D21ED3D07EFC852069B5DE9BB951A8BC90A244926D379295AE9436AAA6A302510C7B1DEDD5FB20869D7F3016F6BE65D383A16DB3321B95351890B17002937EE193F57E99A2474E9D3824C7AEE38541F567E7518C7A0E38E7EBAF41191BCFF17B42A6B4EDE6CE8DE7318F7F5204B3990E226745AFD485B24493008B08C7F6B7E56B02B3E8FE0C9D859CB8B68223B8AB27EE5F6538078B2DB91E2A153E85818072A23B6DD93281054F6FB0F6F5AD283ECA0B7AF35455E03DA7B68326F3EC834AF314048AC6DF20D28508673CAB62A2C7BC131A533E0B66806B1C30664B372331BDC4B0CAD8D11EE7BBD9285548AAEC1F66E821B3C8A0476900C5E688E80CCE3C61D69CBBA137C6604F7A72DD8C7B3E3D51290DAA6A597B081F9D3633A3467A356109ACA7DD7D2E2FB2C1AEB8E20F4892D8B9F8B46F4E3C11F4F47D8B757DFEFEA3899C33595C5EFDEBCBABE8413E3A9A803C69356EB2B2AD5CC4C858455EF5F7B30644B47C64068CDF809F76025A2DB446E03D7CF62F34E702457B02A4CF5D9DD53CA53A7CA629788C67CA08BFECCA43A957AD16C94E1CD875CA107DCE7E0118F0DF6BFEE51DDBD991C26E60CD4858AA592C820075F29F526C917C6FE5403EA7D4A50CEC3B7384DE886E82D2EB4D4E42B5F2B149A81EA7CE7144DC2994CFC44E1F91CBD495"), + tc::ByteData() + }, + { + "Root-CA00000003", + nn::pki::sign::SIGN_ALGO_RSA2048, + tc::cli::FormatUtil::hexStringToBytes("B279C9E2EEE121C6EAF44FF639F88F078B4B77ED9F9560B0358281B50E55AB721115A177703C7A30FE3AE9EF1C60BC1D974676B23A68CC04B198525BC968F11DE2DB50E4D9E7F071E562DAE2092233E9D363F61DD7C19FF3A4A91E8F6553D471DD7B84B9F1B8CE7335F0F5540563A1EAB83963E09BE901011F99546361287020E9CC0DAB487F140D6626A1836D27111F2068DE4772149151CF69C61BA60EF9D949A0F71F5499F2D39AD28C7005348293C431FFBD33F6BCA60DC7195EA2BCC56D200BAF6D06D09C41DB8DE9C720154CA4832B69C08C69CD3B073A0063602F462D338061A5EA6C915CD5623579C3EB64CE44EF586D14BAAA8834019B3EEBEED379"), + tc::cli::FormatUtil::hexStringToBytes("00010003704138EFBBBDA16A987DD901326D1C9459484C88A2861B91A312587AE70EF6237EC50E1032DC39DDE89A96A8E859D76A98A6E7E36A0CFE352CA893058234FF833FCB3B03811E9F0DC0D9A52F8045B4B2F9411B67A51C44B5EF8CE77BD6D56BA75734A1856DE6D4BED6D3A242C7C8791B3422375E5C779ABF072F7695EFA0F75BCB83789FC30E3FE4CC8392207840638949C7F688565F649B74D63D8D58FFADDA571E9554426B1318FC468983D4C8A5628B06B6FC5D507C13E7A18AC1511EB6D62EA5448F83501447A9AFB3ECC2903C9DD52F922AC9ACDBEF58C6021848D96E208732D3D1D9D9EA440D91621C7A99DB8843C59C1F2E2C7D9B577D512C166D6F7E1AAD4A774A37447E78FE2021E14A95D112A068ADA019F463C7A55685AABB6888B9246483D18B9C806F474918331782344A4B8531334B26303263D9D2EB4F4BB99602B352F6AE4046C69A5E7E8E4A18EF9BC0A2DED61310417012FD824CC116CFB7C4C1F7EC7177A17446CBDE96F3EDD88FCD052F0B888A45FDAF2B631354F40D16E5FA9C2C4EDA98E798D15E6046DC5363F3096B2C607A9D8DD55B1502A6AC7D3CC8D8C575998E7D796910C804C495235057E91ECD2637C9C1845151AC6B9A0490AE3EC6F47740A0DB0BA36D075956CEE7354EA3E9A4F2720B26550C7D394324BC0CB7E9317D8A8661F42191FF10B08256CE3FD25B745E5194906B4D61CB4C2E000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001434130303030303030330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007BE8EF6CB279C9E2EEE121C6EAF44FF639F88F078B4B77ED9F9560B0358281B50E55AB721115A177703C7A30FE3AE9EF1C60BC1D974676B23A68CC04B198525BC968F11DE2DB50E4D9E7F071E562DAE2092233E9D363F61DD7C19FF3A4A91E8F6553D471DD7B84B9F1B8CE7335F0F5540563A1EAB83963E09BE901011F99546361287020E9CC0DAB487F140D6626A1836D27111F2068DE4772149151CF69C61BA60EF9D949A0F71F5499F2D39AD28C7005348293C431FFBD33F6BCA60DC7195EA2BCC56D200BAF6D06D09C41DB8DE9C720154CA4832B69C08C69CD3B073A0063602F462D338061A5EA6C915CD5623579C3EB64CE44EF586D14BAAA8834019B3EEBEED3790001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + }, + { + "Root-CA00000003-XS00000020", + nn::pki::sign::SIGN_ALGO_RSA2048, + tc::cli::FormatUtil::hexStringToBytes("D21D3CE67C1069DA049D5E5310E76B907E18EEC80B337C4723E339573F4C664907DB2F0832D03DF5EA5F160A4AF24100D71AFAC2E3AE75AFA1228012A9A21616597DF71EAFCB65941470D1B40F5EF83A597E179FCB5B57C2EE17DA3BC3769864CB47856767229D67328141FC9AB1DF149E0C5C15AEB80BC58FC71BE18966642D68308B506934B8EF779F78E4DDF30A0DCF93FCAFBFA131A8839FD641949F47EE25CEECF814D55B0BE6E5677C1EFFEC6F29871EF29AA3ED9197B0D83852E050908031EF1ABBB5AFC8B3DD937A076FF6761AB362405C3F7D86A3B17A6170A659C16008950F7F5E06A5DE3E5998895EFA7DEEA060BE9575668F78AB1907B3BA1B7D"), + tc::cli::FormatUtil::hexStringToBytes("00010004969FE8288DA6B9DD52C7BD63642A4A9AE5F053ECCB93613FDA37992087BD9199DA5E6797618D77098133FD5B05CD8288139E2E975CD2608003878CDAF020F51A0E5B7692780845561B31C61808E8A47C3462224D94F736E9A14E56ACBF71B7F11BBDEE38DDB846D6BD8F0AB4E4948C5434EAF9BF26529B7EB83671D3CE60A6D7A850DBE6801EC52A7B7A3E5A27BC675BA3C53377CFC372EBCE02062F59F37003AA23AE35D4880E0E4B69F982FB1BAC806C2F75BA29587F2815FD7783998C354D52B19E3FAD9FBEF444C48579288DB0978116AFC82CE54DACB9ED7E1BFD50938F22F85EECF3A4F426AE5FEB15B72F022FB36ECCE9314DAD131429BFC9675F58EE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F742D4341303030303030303300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015853303030303030323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000D21D3CE67C1069DA049D5E5310E76B907E18EEC80B337C4723E339573F4C664907DB2F0832D03DF5EA5F160A4AF24100D71AFAC2E3AE75AFA1228012A9A21616597DF71EAFCB65941470D1B40F5EF83A597E179FCB5B57C2EE17DA3BC3769864CB47856767229D67328141FC9AB1DF149E0C5C15AEB80BC58FC71BE18966642D68308B506934B8EF779F78E4DDF30A0DCF93FCAFBFA131A8839FD641949F47EE25CEECF814D55B0BE6E5677C1EFFEC6F29871EF29AA3ED9197B0D83852E050908031EF1ABBB5AFC8B3DD937A076FF6761AB362405C3F7D86A3B17A6170A659C16008950F7F5E06A5DE3E5998895EFA7DEEA060BE9575668F78AB1907B3BA1B7D0001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + }, + { + "Root-CA00000003-XS00000022", + nn::pki::sign::SIGN_ALGO_RSA2048, + tc::cli::FormatUtil::hexStringToBytes("E29775264ADC30C0FDE2DEFCFBBAC406A3B1C2ADDC2644C4C3E46DAD11E4F1ED7BC8B6E8CDBBE87E01020DF807520CE8A8F88BB53BE82CAFF4A1AF7FC5CD69EB34E24BEA74781203B611D09EBF56F82E3F21A494549F407FABA948545B34B64F487E1F27B031A83D337CE40496A3034AC6A2DB9E9353A1967EC7A07A4B3672B5B3F7934FF166D90EC131D8A75DC925ED629F8381A586589A82790489D4BF170F29F8E4BE50811ECBA9564D5517362D8DE777B01FC0A0AD8DED692CEAFD028A468CD6A6BF18A2767801AF8BB954EDB4DA3CED78337F3E9B6A1602B94752C85A53993678FDFA0300A4200CA752FA0D94E5A4B74B726FB61876146E49EC148508F5"), + tc::cli::FormatUtil::hexStringToBytes("0001000458C7FFCDB0A1758925286C3C5029942683A8ED614A4E5C24EECE90548F99A0E59429C3D7D9CA37910CC1F5974E3AA2D7438627FBE456C9C830E5BF5B4440013CD41AA6F0AE02307E3EDEF3EDE0E6404A87234786FED37878F4BF4D964C2290C66AF167AC94869DC2F8378FA3FE0B764F776F9CE479B9E7A4EAE77FCA924B84035C9C06075F5F23BBF3A202AC1AF6A640C62BEEE4603925534783642663238A803D5FC87C5BEA58E09B0669CE9273509A87FACEDA94333FC6264D095708C18F9A50963F0E02A0771AEBC7174D4A89BE158C6CF407BD11658363E924F2808B15C7FE00267565CBD386E576540C906F9B2FFB3AFC64BA1198FA711A48F107E6A9AD000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F742D4341303030303030303300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015853303030303030323200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E29775264ADC30C0FDE2DEFCFBBAC406A3B1C2ADDC2644C4C3E46DAD11E4F1ED7BC8B6E8CDBBE87E01020DF807520CE8A8F88BB53BE82CAFF4A1AF7FC5CD69EB34E24BEA74781203B611D09EBF56F82E3F21A494549F407FABA948545B34B64F487E1F27B031A83D337CE40496A3034AC6A2DB9E9353A1967EC7A07A4B3672B5B3F7934FF166D90EC131D8A75DC925ED629F8381A586589A82790489D4BF170F29F8E4BE50811ECBA9564D5517362D8DE777B01FC0A0AD8DED692CEAFD028A468CD6A6BF18A2767801AF8BB954EDB4DA3CED78337F3E9B6A1602B94752C85A53993678FDFA0300A4200CA752FA0D94E5A4B74B726FB61876146E49EC148508F50001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + }, + { + "Root-CA00000003-XS00000024", + nn::pki::sign::SIGN_ALGO_RSA2048, + tc::cli::FormatUtil::hexStringToBytes("C694562BBDC74E83069876608EAD1EE7B1BC715036E9A6BDF58A6D22FD660ABD9CF360AA893D7746C92C5B9D20E480EAD1387786739956E060EBA79BD6ED7C06B2FCC6DBC1734ED12999AFEE5396FEE722FE3B96846D903F00A32671AC0CEDAAAB0C933C950304650A76152ED1B69547031793755599939B42F075CDF137F8C16D35870DE90D1BACBDB4B746F814AED0D3CB7F7C1B789A1FDF9947717B641BA972A6460D3258F5561CAA1D8E20085E8E3B9EBC92C9B6DCBB872A2AE02D74577F65EFA5AAFF29FD52EAA400D1408B2815C5C96CD2B54DF0F6708A755FB1DFA694A7794C4A0422F1BF8FAFE9E6870578133D50FD3D5356506CFCEC94EF79D6B639"), + tc::cli::FormatUtil::hexStringToBytes("000100046EEED0424FF97D659DA43F85DF01E0997BA1FD1593997145BDE95703AABBDB7EFDE315F8CFE8AEC0F710FFF002938AEF8CEF7EBF90F725B6AC4EDA66DCF85132435E974DDBBE8F8C725CD114CE38C83404DFC0123BB4137C676C5C86EB4E211B7C35A03AB0C7E7637F1BF2486FDF0A5CCE5B047483841B5EA6BDC09AE770951ACA79EFB5497C46750585CF6C3FE4159ACE1CDFAB1BA8C00AB2446B57383F2F063344052E48E1557BD60BF973B252AC5AB4F5A12323AFCC1B06718775F3EDA4DD580A47A04D8063844235DC35D21A4FDED5A44EA401519920B73D15C66FDDF9F8286CF9122795EB9D5EA95C6E51603F6E62AA1631549EBE9E272B4BC3AEBC0D89000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F742D4341303030303030303300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015853303030303030323400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C694562BBDC74E83069876608EAD1EE7B1BC715036E9A6BDF58A6D22FD660ABD9CF360AA893D7746C92C5B9D20E480EAD1387786739956E060EBA79BD6ED7C06B2FCC6DBC1734ED12999AFEE5396FEE722FE3B96846D903F00A32671AC0CEDAAAB0C933C950304650A76152ED1B69547031793755599939B42F075CDF137F8C16D35870DE90D1BACBDB4B746F814AED0D3CB7F7C1B789A1FDF9947717B641BA972A6460D3258F5561CAA1D8E20085E8E3B9EBC92C9B6DCBB872A2AE02D74577F65EFA5AAFF29FD52EAA400D1408B2815C5C96CD2B54DF0F6708A755FB1DFA694A7794C4A0422F1BF8FAFE9E6870578133D50FD3D5356506CFCEC94EF79D6B6390001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + } + }; + + static const std::vector kDevBroadOnRsaKeyAndCert = + { + { + "Root", + nn::pki::sign::SIGN_ALGO_RSA4096, + tc::cli::FormatUtil::hexStringToBytes("D01FE100D43556B24B56DAE971B5A5D384B93003BE1BBF28A2305B060645467D5B0251D2561A274F9E9F9CEC646150AB3D2AE3366866ACA4BAE81AE3D79AA6B04A8BCBA7E6FB648945EBDFDB85BA091FD7D114B5A3A780E3A22E6ECD87B5A4C6F910E4032208814B0CEEA1A17DF739695F617EF63528DB949637A056037F7B32413895C0A8F1982E1565E38EEDC22E590EE2677B8609F48C2E303FBC405CAC18042F822084E4936803DA7F41349248562B8EE12F78F803246330BC7BE7EE724AF458A472E7AB46A1A7C10C2F18FA07C3DDD89806A11C9CC130B247A33C8D47DE67F29E5577B11C43493D5BBA7634A7E4E71531B7DF5981FE24A114554CBD8F005CE1DB35085CCFC77806B6DE254068A26CB5492D4580438FE1E5A9ED75C5ED451DCE789439CCC3BA28A2312A1B8719EF0F73B713950C02591A7462A607F37C0AA7A18FA943A36D752A5F4192F0136100AA9CB41BBE14BEB1F9FC692FDFA09446DE5A9DDE2CA5F68C1C0C21429287CB2DAAA3D263752F73E09FAF4479D2817429F69800AFDE6B592DC19882BDF581CCABF2CB91029EF35C4CFDBBFF49C1FA1B2FE31DE7A560ECB47EBCFE32425B956F81B69917487E3B789151DB2E78B1FD2EBE7E626B3EA165B4FB00CCB751AF507329C4A3939EA6DD9C50A0E7386B0145796B41AF61F78555944F3BC22DC3BD0D00F8798A42B1AAA08320659AC7395AB4F329"), + tc::ByteData() + }, + { + "Root-CA00000004", + nn::pki::sign::SIGN_ALGO_RSA2048, + tc::cli::FormatUtil::hexStringToBytes("C9CC2DC4DF2930E4DF3F8C70A078948775AD5E9AA604C5B4D8EAFF2AA1D214676564EFCA28CC00154554A1A3EA1379E9E6CAACED1593FE88D89AC6B8ACCCAB6E207CEB7CCA29809E2980440662B7D4382A15DA43085745A9AAE59AA05BDB32F66869A2DD4295386C87ECDD3508A2CF60D01E23EC2FE698F470D6001549A2F06759131E534C7006057DEF1D18A83F0AC79CFE80FF5A91F2BED4A0837061190A0329902165403C9A908FB615739F3CE33BF1BAEA16C25BCED7963FACC9D24D9C0AD76FC020B2C4B84C10A741A2CC7D9BAC3AACCCA3529BAC316A9AA75D2A26C7D7D288CBA466C5FE5F454AE679744A90A15772DB3B0E47A49AF031D16DBEAB332B"), + tc::cli::FormatUtil::hexStringToBytes("000100031949429D1E58A62E7E8B56D1B76AE302FD8B97491F778745F75388C4DD0BEB1DF122FB9642151497764A53CF78151845E42CA8FDE486FD2A4F53F8A1BA008A7485FF73B3BF7E3C980729D0656B693219ADE835EB5FFFFCCB7CBB5E307FE0688B888EF2D2053FB7E791E985FD15EF10D79CCA88D6BB15E8E4714A98EE09BF7B8AF053232B6450E6D5FDFFC20A6D1EA6A23812E1014525D56D4082703B86986959A73CD1A14364D2C2DAEA96B095F76C46E4FF4155465E70EF1ED31053D97011E010CC93E7914013687FA3A802996D1E557B1CCC7A7E8F5865C1742E28E26DEF38A93AB5D82D43ECCCBF0BEF22E1FD57E2864333582FEDEABC012F986DDFC3E9447973470308455BDC57AA170B84427F73A29B48F6DA135F66C745C142A84AFB0E6A5EED85D7B9719936F8CE2B621F395F40DC03BEF8854C1117FF0C128641CC7843B97B4346DB226F6026ACB56C278B8E0EA79A2D65EF798E1078AD80ED4B9604D2F08B2CD64A23A3DB270833B402F80851F35BED3EE4577C6660FBF16D9413E09C917A49D42C6DA375BC27F0230DB98F8973AB027B522CD57EC03D25E8B3FC3494C97FB108FE18C68A4336E46C26B6F280D27E34BE287C3E4687BC9D776B76D928D1B6352EC0347D7294AA9360268D26F5F652064AF240D7D00C7C5EA3C32DE62D9B5C4B4CAB6FD7BD371D57C2166095910E4AD8E9ED181EF761936153892D77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F74000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014341303030303030303400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081122A46C9CC2DC4DF2930E4DF3F8C70A078948775AD5E9AA604C5B4D8EAFF2AA1D214676564EFCA28CC00154554A1A3EA1379E9E6CAACED1593FE88D89AC6B8ACCCAB6E207CEB7CCA29809E2980440662B7D4382A15DA43085745A9AAE59AA05BDB32F66869A2DD4295386C87ECDD3508A2CF60D01E23EC2FE698F470D6001549A2F06759131E534C7006057DEF1D18A83F0AC79CFE80FF5A91F2BED4A0837061190A0329902165403C9A908FB615739F3CE33BF1BAEA16C25BCED7963FACC9D24D9C0AD76FC020B2C4B84C10A741A2CC7D9BAC3AACCCA3529BAC316A9AA75D2A26C7D7D288CBA466C5FE5F454AE679744A90A15772DB3B0E47A49AF031D16DBEAB332B0001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + }, + { + "Root-CA00000004-XS00000020", + nn::pki::sign::SIGN_ALGO_RSA2048, + tc::cli::FormatUtil::hexStringToBytes("BA278428765D879A7F215404C6EE4E0A0D3F66C33BC7F8A32FD898E52CB7B63443CED8B00527D89DEDC6BBF60AD1C5C9923021DD555F9BAD4BE0C0C406D3702915E5B34AC2D2ABE503C32A3A23B43834C9157B9A0AF2E4E9C03BEDE2B4C115F5353DFC66BD04A6C079970E38CBDD5A50A2B98FF2D765FCF83381E9E0E849C3573578378FF65951613E95F75EE8EF26183A40AAE4A76D7384EA478D2CDCE80FBA0321A6BF8D69983C3AA7AE5443B72BFE410BF1323CBD88C3560EA13D17D38A2E34041DE7AAF389DE4375225CA87EE349C760C7D99BE7E737C6261CC74E25AE46527AC9619F939057088D7435A6DE7B25AB82DD5410F2579CAEF1491A909D302B"), + tc::cli::FormatUtil::hexStringToBytes("00010004603483F7F4EF3C863FF7B46821E9D83983EFB0BA29C45B10DFB2E8A268AE06084DFEB0B9680B6D40D6DF82CAE7D651E28F31364F5AB567DD9DCCFCBB5EFF8F1CEA92489C046E8BED2AF0D7F9CA0BD50F683633D681B08D1AF417D7B7F8762AD88E3AF0D1C45DAC5407B9D8F24F167AEDC7C5DBCF4E0E176EC341D4F9BA8ADFE749CF37F45688653886566A9045ED89AD353DD289E32631867BDFD7FC67738BDF0E1E858F3BF9DEE25B02206F0B7FFB61F866DA4D862D77DDBDA9668BB61CBDEFACB2583CE5C9FAB22FC90EF8EB06954743E5A32A37A3513C6C795BC8E0C65E2F170B435A8C447765381C4050427F88E9828E6E94144D28441C7F9C7F6D92FE5B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F742D4341303030303030303400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015853303030303030323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BA278428765D879A7F215404C6EE4E0A0D3F66C33BC7F8A32FD898E52CB7B63443CED8B00527D89DEDC6BBF60AD1C5C9923021DD555F9BAD4BE0C0C406D3702915E5B34AC2D2ABE503C32A3A23B43834C9157B9A0AF2E4E9C03BEDE2B4C115F5353DFC66BD04A6C079970E38CBDD5A50A2B98FF2D765FCF83381E9E0E849C3573578378FF65951613E95F75EE8EF26183A40AAE4A76D7384EA478D2CDCE80FBA0321A6BF8D69983C3AA7AE5443B72BFE410BF1323CBD88C3560EA13D17D38A2E34041DE7AAF389DE4375225CA87EE349C760C7D99BE7E737C6261CC74E25AE46527AC9619F939057088D7435A6DE7B25AB82DD5410F2579CAEF1491A909D302B0001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + }, + { + "Root-CA00000004-XS00000021", + nn::pki::sign::SIGN_ALGO_RSA2048, + tc::cli::FormatUtil::hexStringToBytes("CA303087732E88FB2736A4644B6A84DF02393786EBE3DBE914F92224126AB3C38D7CA67322B7D3107B9AC0E00AED9AF13DEDDC62BCB8AC889A47B07196B45BC97AC5DD333CC63E872E30CDB52BD35E055FBA56C2521AFC8BBFC8B06390522E99699750B457C8647A42D6318BE5EAA192ADA35DA7023FC348215613141919821FD5599B93497893CA58E7A88C8D6E6E9EB0A57567BF8C12EB25241BB6082BED7A696658A58DDB66E7CC2562F2EA061E5373E6A5397BF6299F7EA9065B0047AA408F24A5D6F6E4050B99DA86831F663792E08D96F182400182D9BECB917AE315A238CA89741B6AEAF52E238590CDA60FC5B74DC638F9D6FCAF95D2CA7AE005A7B1"), + tc::cli::FormatUtil::hexStringToBytes("000100041282394569ED63B5C4E54747B31E3B32C68B0EB90712E3C04662101CA77A551A0394CC36CB28E369DFBBB9FD78C9C503C3B1F184729C2B05B4D1691581A877AF82F8EC3D70E95B27F459039B65D8221D4864CCB282FF721EBDD6C46863F8C95591C1BF36D0AC118C0027FA3E2D434821A5FEF123C65F17885321F9A520C22B9EE6FC533EEF582706A95DD0CEABACE87B0169E136336E2E9CC0399637CE4CC77B6DA418671A277614F80A0A909569018F624DE0C5E1A67DB134C63F1FFE30BB64C201FDD8DAD42F4C88B247C1C6133CC554DE6B15684320D6AA1E09CB59EAD15F37140C9DD4BE69D87CC90DD8F733C946FBA6C576C70533601AF4E8294A3B5DD4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F742D4341303030303030303400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015853303030303030323100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000CA303087732E88FB2736A4644B6A84DF02393786EBE3DBE914F92224126AB3C38D7CA67322B7D3107B9AC0E00AED9AF13DEDDC62BCB8AC889A47B07196B45BC97AC5DD333CC63E872E30CDB52BD35E055FBA56C2521AFC8BBFC8B06390522E99699750B457C8647A42D6318BE5EAA192ADA35DA7023FC348215613141919821FD5599B93497893CA58E7A88C8D6E6E9EB0A57567BF8C12EB25241BB6082BED7A696658A58DDB66E7CC2562F2EA061E5373E6A5397BF6299F7EA9065B0047AA408F24A5D6F6E4050B99DA86831F663792E08D96F182400182D9BECB917AE315A238CA89741B6AEAF52E238590CDA60FC5B74DC638F9D6FCAF95D2CA7AE005A7B10001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + }, + { + "Root-CA00000004-XS00000024", + nn::pki::sign::SIGN_ALGO_RSA2048, + tc::cli::FormatUtil::hexStringToBytes("E5E262EA591EEB885DE5A3CA451CE9E4FEA0E89DAA9BBCD9EC6C23E3FEFCE31BE7C7217DAC3E1F5A8331EF3F0C738E9310E765E71AAAB92C6E18D36071E7F93446E06E2581A28483AE9EB9C8217C1D68A21FD098E186C71B6A1E1E2626C80B69FFD562EAD515C77D616554F6B74B34DEEB0DE905F44631F56A6FF12B47AC0190578267559592FAB173146071ECFBB014951512E45299A21C0B8B3377E55DCD95625130E533877BF11157585D43DB00F61DFE77CFA44F5F29F44945DBF4214F37214921C16ED7D74455B2AE7C8CA15DFBD20A4D5E6501965B78C2C542727D7A91C071C56403738F07E858239CD150DB304BA5ACD8751145A9A91D3E97085AD217"), + tc::cli::FormatUtil::hexStringToBytes("000100049EA82934BD64EDCED4D506BB68BF1F217BAD912BEF134B687C8E590F2A9355D8EDD9BE00D85735A83820F69978B3E2876FE07BBB7F356B25CDCC98EEEA17BFA9EF649769E921BFDA7867D0570D2F0A7A223E7ACD8D9AE1F304F8EEC6C96B11805436CF686FBD05969E4C0D19A63EF89DA00899222D2CF006776FF027118E96CB027114AABBA268CB1775022B25EAF045C9E4A328A55FF85CFFFFFEAEFB78F1CD184ED9042D23A06F0161E5FBADE92638A483A1A3680DC9994A03D02F1D76EB0EE793843D31CCC172A3993C7344B2C5E634F2B89C47F85FD8B74D21745AEBCFD55E32FC03F6BCD40421499261F8AB93AC8A07E6EF5436036283A3B86192890A78000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F742D4341303030303030303400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015853303030303030323400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E5E262EA591EEB885DE5A3CA451CE9E4FEA0E89DAA9BBCD9EC6C23E3FEFCE31BE7C7217DAC3E1F5A8331EF3F0C738E9310E765E71AAAB92C6E18D36071E7F93446E06E2581A28483AE9EB9C8217C1D68A21FD098E186C71B6A1E1E2626C80B69FFD562EAD515C77D616554F6B74B34DEEB0DE905F44631F56A6FF12B47AC0190578267559592FAB173146071ECFBB014951512E45299A21C0B8B3377E55DCD95625130E533877BF11157585D43DB00F61DFE77CFA44F5F29F44945DBF4214F37214921C16ED7D74455B2AE7C8CA15DFBD20A4D5E6501965B78C2C542727D7A91C071C56403738F07E858239CD150DB304BA5ACD8751145A9A91D3E97085AD2170001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + } + }; + if (isDev) { if (xci_header_sign_key.isNull()) @@ -901,6 +977,12 @@ void nstool::KeyBagInitializer::importKnownKeys(bool isDev) if (acid_sign_key.find(itr->generation) == acid_sign_key.end()) acid_sign_key[itr->generation] = tc::crypto::RsaPublicKey(itr->modulus.data(), itr->modulus.size()); } + + for (auto itr = kDevBroadOnRsaKeyAndCert.begin(); itr != kDevBroadOnRsaKeyAndCert.end(); itr++) + { + if (broadon_signer.find(itr->issuer) == broadon_signer.end()) + broadon_signer[itr->issuer] = {itr->certificate, itr->key_type, tc::crypto::RsaPublicKey(itr->modulus.data(), itr->modulus.size())}; + } } else { @@ -924,5 +1006,11 @@ void nstool::KeyBagInitializer::importKnownKeys(bool isDev) if (acid_sign_key.find(itr->generation) == acid_sign_key.end()) acid_sign_key[itr->generation] = tc::crypto::RsaPublicKey(itr->modulus.data(), itr->modulus.size()); } + + for (auto itr = kProdBroadOnRsaKeyAndCert.begin(); itr != kProdBroadOnRsaKeyAndCert.end(); itr++) + { + if (broadon_signer.find(itr->issuer) == broadon_signer.end()) + broadon_signer[itr->issuer] = {itr->certificate, itr->key_type, tc::crypto::RsaPublicKey(itr->modulus.data(), itr->modulus.size())}; + } } } \ No newline at end of file From b827cea8285a6cef230ad138a78fc4c4c74bd265 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 23 Oct 2021 08:44:21 +0800 Subject: [PATCH 076/119] Fix bug where NCA header key was not imported. --- src/KeyBag.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index ba8e7b4..5717e40 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -266,7 +266,7 @@ void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_pa { // nca header key //fmt::print("{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kKeyStr); - //_SAVE_AES128XTSKEY(fmt::format("{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kKeyStr), nca_header_key); + _SAVE_AES128XTSKEY(fmt::format("{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kKeyStr), nca_header_key); // nca header sign0 key (generations) for (size_t keygen_rev = 0; keygen_rev < kKeyGenerationNum; keygen_rev++) From c307be9749ac0c33e95378ae28a9c69d09f89ccc Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 23 Oct 2021 11:38:33 +0800 Subject: [PATCH 077/119] Misc --- src/FsProcess.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/FsProcess.cpp b/src/FsProcess.cpp index f192589..13da38c 100644 --- a/src/FsProcess.cpp +++ b/src/FsProcess.cpp @@ -39,8 +39,7 @@ void nstool::FsProcess::process() { printFs(); } - - + if (mExtractJobs.empty() == false) { extractFs(); From 18a77e89d001ba47e45c901cd5d083a511d914a3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 23 Oct 2021 11:38:44 +0800 Subject: [PATCH 078/119] Update readme. --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b290e96..92da944 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,16 @@ General purpose reading/extraction tool for Nintendo Switch file formats. * Meta (.npdm) * PartitionFS (and Sha256PartitionFS) (includes raw .nsp) * RomFS -* NX GameCard Image (.xci) -* Nintendo Content Archive (.nca) -* Content Metadata (.cnmt) -* Nintendo Software Object (.nso) -* Nintendo Relocatable Software Object (.nro) -* Kernel Initial Process List (.ini) -* Kernel Initial Process (.kip) -* Nintendo Application Control Property (.nacp) -* ES Certificate (.cert) -* ES Ticket (v2 only) (.tik) +* N**X** Game**C**ard **I**mage (.xci) +* **N**intendo **C**ontent **A**rchive (.nca) +* **C**o**n**tent **M**e**t**adata (.cnmt) +* **N**intendo **S**hared **O**bject (.nso) +* **N**intendo **R**elocatable **O**bject (.nro) +* **Ini**tial Process List (.ini) +* **K**ernel **I**nitial **P**rocess (.kip) +* **N**intendo **A**pplication **C**ontrol **P**roperty (.nacp) +* ES **Cert**ificate (.cert) +* ES **Ti**c**k**et (v2 only) (.tik) # Usage ``` From f6b669bb8d9e1971012be1e4378f59aa32a2eed5 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 23 Oct 2021 11:43:07 +0800 Subject: [PATCH 079/119] Misc --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 92da944..cd108fd 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,13 @@ Usage: nstool [options... ] --part2 Extract "partition 2" to directory. --part3 Extract "partition 3" to directory. - NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object) + NSO (Nintendo Shared Object), NRO (Nintendo Relocatable Object) nstool [--listapi --listsym] [--insttype ] --listapi Print SDK API List. --listsym Print Code Symbols. --insttype Specify instruction type [64bit|32bit] (64bit is assumed). - INI (Initial Process List Blob) + INI (Initial Process List) nstool [--kipdir ] --kipdir Extract embedded KIPs to directory. From 315dd38b20b21d4876f10ce54f1a6d3f6bc34964 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 23 Oct 2021 11:51:06 +0800 Subject: [PATCH 080/119] Update help text. --- README.md | 2 +- src/Settings.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cd108fd..2a3ae78 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Usage: nstool [options... ] --normal Extract "normal" partition to directory. --secure Extract "secure" partition to directory. - PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package) + PFS0/HFS0 (PartitionFs), RomFs, NSP (Nintendo Submission Package) nstool [--listfs] [--fsdir ] --listfs Print file system. --fsdir Extract file system to directory. diff --git a/src/Settings.cpp b/src/Settings.cpp index df46259..873aa92 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -707,7 +707,7 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --logo Extract \"logo\" partition to directory.\n"); fmt::print(" --normal Extract \"normal\" partition to directory.\n"); fmt::print(" --secure Extract \"secure\" partition to directory.\n"); - fmt::print("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n"); + fmt::print("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Nintendo Submission Package)\n"); fmt::print(" {:s} [--listfs] [--fsdir ] \n", BIN_NAME); fmt::print(" --listfs Print file system.\n"); fmt::print(" --fsdir Extract file system to directory.\n"); @@ -722,7 +722,7 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --part1 Extract \"partition 1\" to directory.\n"); fmt::print(" --part2 Extract \"partition 2\" to directory.\n"); fmt::print(" --part3 Extract \"partition 3\" to directory.\n"); - fmt::print("\n NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)\n"); + fmt::print("\n NSO (Nintendo Shared Object), NRO (Nintendo Relocatable Object)\n"); fmt::print(" {:s} [--listapi --listsym] [--insttype ] \n", BIN_NAME); fmt::print(" --listapi Print SDK API List.\n"); fmt::print(" --listsym Print Code Symbols.\n"); From a8437dfc8ec57a255013cc3df027638cfcd5ce70 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 23 Oct 2021 11:52:07 +0800 Subject: [PATCH 081/119] Misc --- src/Settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Settings.cpp b/src/Settings.cpp index 873aa92..df72210 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -727,7 +727,7 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --listapi Print SDK API List.\n"); fmt::print(" --listsym Print Code Symbols.\n"); fmt::print(" --insttype Specify instruction type [64bit|32bit] (64bit is assumed).\n"); - fmt::print("\n INI (Initial Process List Blob)\n"); + fmt::print("\n INI (Initial Process List)\n"); fmt::print(" {:s} [--kipdir ] \n", BIN_NAME); fmt::print(" --kipdir Extract embedded KIPs to directory.\n"); fmt::print("\n ASET (Homebrew Asset Blob)\n"); From 6d6f824c3acb1e3fd5c722c92b811f2100f29fee Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 27 Oct 2021 19:12:24 +0800 Subject: [PATCH 082/119] Remove sparse test code for now. --- deps/libnintendo-hac | 2 +- src/NcaProcess.cpp | 97 +------------------------------------------- 2 files changed, 3 insertions(+), 96 deletions(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index b0f4749..dcbee47 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit b0f474934f1c91bf0cefdb636c4c2e1d10ae8323 +Subproject commit dcbee472b6bd79474277d542eba5585938df01ac diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index 170d30d..77e4e20 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -238,103 +238,10 @@ void nstool::NcaProcess::generatePartitionConfiguration() throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Version({:d}): UNSUPPORTED", partition.header_index, fs_header.version.unwrap())); } - // detect sparse layer + // detect compacted partition (sparse layer) if (fs_header.sparse_info.generation.unwrap() != 0) { - fmt::print("[IsSparse {:d}]\n", partition.header_index); - fmt::print(" > raw data\n"); - fmt::print(" bucket:\n"); - fmt::print(" offset: 0x{:x}\n", fs_header.sparse_info.bucket.offset.unwrap()); - fmt::print(" size: 0x{:x}\n", fs_header.sparse_info.bucket.size.unwrap()); - fmt::print(" header:\n"); - fmt::print(" st_magic: {:s}\n", ((tc::bn::string<4>*)&fs_header.sparse_info.bucket.header.st_magic)->str()); - fmt::print(" version: 0x{:x}\n", fs_header.sparse_info.bucket.header.version.unwrap()); - fmt::print(" entry_count: {:d}\n", fs_header.sparse_info.bucket.header.entry_count.unwrap()); - fmt::print(" physical_offset: 0x{:x}\n", fs_header.sparse_info.physical_offset.unwrap()); - fmt::print(" generation: 0x{:x}\n", fs_header.sparse_info.generation.unwrap()); - - fmt::print(" > processed data\n"); - fmt::print(" file_size: 0x{:x}\n", mFile->length()); - int64_t phys_offset = fs_header.sparse_info.physical_offset.unwrap(); - int64_t phys_size = fs_header.sparse_info.bucket.offset.unwrap() + fs_header.sparse_info.bucket.size.unwrap(); - fmt::print(" phys_offset: 0x{:x}\n", phys_offset); - fmt::print(" phys_size: 0x{:x}\n", phys_size); - - nn::hac::detail::aes_iv_t sparse_iv; - - nn::hac::ContentArchiveUtil::getNcaPartitionAesCtr(uint32_t(fs_header.sparse_info.generation.unwrap()) << 16, fs_header.secure_value.unwrap(), sparse_iv.data()); - - if (phys_size != 0 && mContentKey.aes_ctr.isSet()) - { - std::shared_ptr sparse_layer; - - sparse_layer = mFile; - sparse_layer = std::make_shared(tc::crypto::Aes128CtrEncryptedStream(sparse_layer, mContentKey.aes_ctr.get(), sparse_iv)); - sparse_layer = std::make_shared(tc::io::SubStream(sparse_layer, phys_offset, phys_size)); - - tc::ByteData sparse_bktr = tc::ByteData(fs_header.sparse_info.bucket.size.unwrap()); - sparse_layer->seek(fs_header.sparse_info.bucket.offset.unwrap(), tc::io::SeekOrigin::Begin); - sparse_layer->read(sparse_bktr.data(), sparse_bktr.size()); - - //writeSubStreamToFile(sparse_layer, fs_header.sparse_info.bucket.offset.unwrap(), fs_header.sparse_info.bucket.size.unwrap(), tc::io::Path("./data/sparse_bktr.bin")); - -#pragma pack(push,1) - struct sRelocationHeader - { - tc::bn::pad<4> padding; - tc::bn::le32 bucket_num; - tc::bn::le64 virtual_image_size; - std::array, 2046> bucket_base_virtual_offset; - }; - static_assert(sizeof(sRelocationHeader) == 0x4000, "sRelocationHeader size."); - - struct sRelocationBucketHeader - { - tc::bn::pad<4> padding; - tc::bn::le32 bucket_num; - tc::bn::le64 bucket_end_offset; - }; - static_assert(sizeof(sRelocationBucketHeader) == 0x10, "sRelocationBucketHeader size."); - - struct sRelocationBucketEntry - { - tc::bn::le64 patch_addr; - tc::bn::le64 base_addr; - tc::bn::le32 storage_index; // 0 = base, 1 = patch - }; - static_assert(sizeof(sRelocationBucketEntry) == 0x14, "sRelocationBucketEntry size."); - - struct sRelocationBucket - { - sRelocationBucketHeader head; - std::array entry; - tc::bn::pad<8> padding; - }; - static_assert(sizeof(sRelocationBucket) == 0x4000, "sRelocationBucket size."); -#pragma pack(pop) - - sRelocationHeader* header = (sRelocationHeader*)sparse_bktr.data(); - sRelocationBucket* bucket = (sRelocationBucket*)(sparse_bktr.data() + sizeof(sRelocationHeader)); - fmt::print("[RelocationHeader]\n"); - fmt::print(" head:\n"); - fmt::print(" bucket_num: {:d}\n", header->bucket_num.unwrap()); - fmt::print(" virtual_image_size: 0x{:016x}\n", header->virtual_image_size.unwrap()); - for (size_t bucket_idx = 0; bucket_idx < header->bucket_num.unwrap(); bucket_idx++) - { - fmt::print(" bucket {:d}:\n", bucket_idx); - fmt::print(" start_offset: 0x{:016x}\n", header->bucket_base_virtual_offset[bucket_idx].unwrap()); - fmt::print(" end_offset: 0x{:016x}\n", bucket[bucket_idx].head.bucket_end_offset.unwrap()); - fmt::print(" entry_num: {:d}\n", bucket[bucket_idx].head.bucket_num.unwrap()); - for (size_t entry_idx = 0; entry_idx < bucket[bucket_idx].head.bucket_num.unwrap(); entry_idx++) - { - fmt::print(" entry {:d}\n", entry_idx); - fmt::print(" patch_addr: 0x{:016x}\n", bucket[bucket_idx].entry[entry_idx].patch_addr.unwrap()); - fmt::print(" base_addr: 0x{:016x}{:s}\n", bucket[bucket_idx].entry[entry_idx].base_addr.unwrap(), (bucket[bucket_idx].entry[entry_idx].storage_index.unwrap() == 0 && bucket[bucket_idx].entry[entry_idx].base_addr.unwrap() == 0) ? " NEEDED A VALID OFFSET" : ""); - fmt::print(" storage_index: {:s}\n", bucket[bucket_idx].entry[entry_idx].storage_index.unwrap() == 0 ? "BASE" : "PATCH"); - } - - } - } + // TODO } // setup AES-CTR From 635930f8502ccdb354b225a9541dc6595e352000 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 28 Oct 2021 15:19:39 +0800 Subject: [PATCH 083/119] Remove debug print. --- src/KeyBag.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 5717e40..7f44738 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -585,8 +585,6 @@ void nstool::KeyBagInitializer::importTicket(const tc::io::Path& tik_path) return; } - fmt::print("[TIK] decrypt title key with index 0x{:x}\n", common_key_index); - // decrypt title key aes128_key_t dec_title_key; tc::crypto::DecryptAes128Ecb(dec_title_key.data(), enc_title_key.data(), sizeof(aes128_key_t), etik_common_key[common_key_index].data(), sizeof(aes128_key_t)); From 9d97e9667375a7193ab4dc94ff320beacec58420 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 29 Oct 2021 16:09:24 +0800 Subject: [PATCH 084/119] Added missing new-line. --- src/KeyBag.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp index 7f44738..e8320ee 100644 --- a/src/KeyBag.cpp +++ b/src/KeyBag.cpp @@ -570,7 +570,7 @@ void nstool::KeyBagInitializer::importTicket(const tc::io::Path& tik_path) // work around for bad scene tickets where they don't set the commonkey id field (detect scene ticket with ffff.... signature) if (common_key_index == 0 && *((uint64_t*)tik.getSignature().getSignature().data()) == (uint64_t)0xffffffffffffffff) { - fmt::print("[WARNING] Ticket \"{:s}\" is fake-signed, and NCA decryption may fail if ticket was incorrectly generated.", tc::cli::FormatUtil::formatBytesAsString(rights_id.data(), rights_id.size(), true, "")); + fmt::print("[WARNING] Ticket \"{:s}\" is fake-signed, and NCA decryption may fail if ticket was incorrectly generated.\n", tc::cli::FormatUtil::formatBytesAsString(rights_id.data(), rights_id.size(), true, "")); // the keygeneration was included in the rights_id from keygeneration 0x03 and onwards, so in those cases we can copy from there if (rights_id[15] >= 0x03) common_key_index = rights_id[15]; From 7ce606db2a7b2d9f02eeffce62f5a1a960092ba0 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 5 Nov 2021 13:53:07 +0800 Subject: [PATCH 085/119] Update dependencies. --- deps/libnintendo-hac | 2 +- deps/libtoolchain | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index dcbee47..46df669 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit dcbee472b6bd79474277d542eba5585938df01ac +Subproject commit 46df66957d73bb76f42ec65c64edaaf0c64cd1b9 diff --git a/deps/libtoolchain b/deps/libtoolchain index 20321bf..80d2ee3 160000 --- a/deps/libtoolchain +++ b/deps/libtoolchain @@ -1 +1 @@ -Subproject commit 20321bf5033fe97b8f1a0e0727105d0798f60165 +Subproject commit 80d2ee3e0b226dfcc0ba6a164375c3754ab3fcc5 From e8597a8e86c0a7fb75c65944ccf10ef27cacf477 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 5 Nov 2021 13:57:35 +0800 Subject: [PATCH 086/119] [NcaProcess] Identify Sparse partitions and explain they are not supported. --- src/NcaProcess.cpp | 110 +++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 74 deletions(-) diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index 77e4e20..22a829d 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -12,7 +12,6 @@ #include #include - nstool::NcaProcess::NcaProcess() : mModuleName("nstool::NcaProcess"), mFile(), @@ -238,12 +237,6 @@ void nstool::NcaProcess::generatePartitionConfiguration() throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Version({:d}): UNSUPPORTED", partition.header_index, fs_header.version.unwrap())); } - // detect compacted partition (sparse layer) - if (fs_header.sparse_info.generation.unwrap() != 0) - { - // TODO - } - // setup AES-CTR nn::hac::ContentArchiveUtil::getNcaPartitionAesCtr(&fs_header, info.aes_ctr.data()); @@ -256,86 +249,55 @@ void nstool::NcaProcess::generatePartitionConfiguration() if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256) { info.hierarchicalsha256_hdr.fromBytes(fs_header.hash_info.data(), fs_header.hash_info.size()); - /* - nn::hac::HierarchicalSha256Header hdr; - - // import raw data - hdr.fromBytes(fs_header.hash_info.data(), fs_header.hash_info.size()); - for (size_t i = 0; i < hdr.getLayerInfo().size(); i++) - { - nn::hac::HierarchicalValidatedStream::StreamInfo::LayerInfo layer; - layer.offset = hdr.getLayerInfo()[i].offset; - layer.size = hdr.getLayerInfo()[i].size; - layer.block_size = hdr.getHashBlockSize(); - if (i + 1 == hdr.getLayerInfo().size()) - { - info.hashed_stream_info.data_layer_info = layer; - } - else - { - info.hashed_stream_info.hash_layer_info.push_back(layer); - } - } - info.hashed_stream_info.master_hash_data = tc::ByteData(hdr.getMasterHash().data(), hdr.getMasterHash().size()); - info.hashed_stream_info.align_partial_block_to_blocksize = false; - */ } else if (info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) { info.hierarchicalintegrity_hdr.fromBytes(fs_header.hash_info.data(), fs_header.hash_info.size()); - /* - // info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_INTEGRITY); - nn::hac::HierarchicalIntegrityHeader hdr; - - hdr.fromBytes(fs_header.hash_info.data(), fs_header.hash_info.size()); - for (size_t i = 0; i < hdr.getLayerInfo().size(); i++) - { - nn::hac::HierarchicalValidatedStream::StreamInfo::LayerInfo layer; - layer.offset = hdr.getLayerInfo()[i].offset; - layer.size = hdr.getLayerInfo()[i].size; - layer.block_size = (1 << hdr.getLayerInfo()[i].block_size); - if (i + 1 == hdr.getLayerInfo().size()) - { - info.hashed_stream_info.data_layer_info = layer; - } - else - { - info.hashed_stream_info.hash_layer_info.push_back(layer); - } - } - info.hashed_stream_info.master_hash_data = tc::ByteData(hdr.getMasterHashList().size() * sizeof(nn::hac::detail::sha256_hash_t)); - for (size_t i = 0; i < hdr.getMasterHashList().size(); i++) - { - ((nn::hac::detail::sha256_hash_t*)info.hashed_stream_info.master_hash_data.data())[i] = hdr.getMasterHashList()[i]; - } - info.hashed_stream_info.align_partial_block_to_blocksize = false; - */ } // create reader try { - // create reader based on encryption type0 - if (info.enc_type == nn::hac::nca::EncryptionType::None) + // handle partition encryption and partition compaction (sparse layer) + if (fs_header.sparse_info.generation.unwrap() != 0) { - info.reader = std::make_shared(tc::io::SubStream(mFile, info.offset, info.size)); - } - else if (info.enc_type == nn::hac::nca::EncryptionType::AesCtr) - { - if (mContentKey.aes_ctr.isNull()) - throw tc::Exception(mModuleName, "AES-CTR Key was not determined"); - //info.reader = new fnd::OffsetAdjustedIFile(new fnd::AesCtrWrappedIFile(mFile, mContentKey.aes_ctr.get(), info.aes_ctr), info.offset, info.size); - info.reader = std::make_shared(tc::crypto::Aes128CtrEncryptedStream(mFile, mContentKey.aes_ctr.get(), info.aes_ctr)); - info.reader = std::make_shared(tc::io::SubStream(info.reader, info.offset, info.size)); - - } - else if (info.enc_type == nn::hac::nca::EncryptionType::AesXts || info.enc_type == nn::hac::nca::EncryptionType::AesCtrEx) - { - throw tc::Exception(mModuleName, fmt::format("EncryptionType({:s}): UNSUPPORTED", nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type))); + throw tc::Exception("SparseStorage: Not currently supported."); } else { - throw tc::Exception(mModuleName, fmt::format("EncryptionType({:s}): UNKNOWN", nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type))); + // create raw partition + info.reader = std::make_shared(tc::io::SubStream(mFile, info.offset, info.size)); + + // handle encryption if required reader based on encryption type + if (info.enc_type == nn::hac::nca::EncryptionType::None) + { + // no encryption so do nothing + //info.reader = info.reader; + } + else if (info.enc_type == nn::hac::nca::EncryptionType::AesCtr) + { + if (mContentKey.aes_ctr.isNull()) + throw tc::Exception(mModuleName, "AES-CTR Key was not determined"); + + // get partition key + nn::hac::detail::aes128_key_t partition_key = mContentKey.aes_ctr.get(); + + // get partition counter + nn::hac::detail::aes_iv_t partition_ctr = info.aes_ctr; + tc::crypto::detail::incr_counter<16>(partition_ctr.data(), info.offset>>4); + + // create decryption stream + info.reader = std::make_shared(tc::crypto::Aes128CtrEncryptedStream(info.reader, partition_key, partition_ctr)); + + } + else if (info.enc_type == nn::hac::nca::EncryptionType::AesXts || info.enc_type == nn::hac::nca::EncryptionType::AesCtrEx) + { + throw tc::Exception(mModuleName, fmt::format("EncryptionType({:s}): UNSUPPORTED", nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type))); + } + else + { + throw tc::Exception(mModuleName, fmt::format("EncryptionType({:s}): UNKNOWN", nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type))); + } } // filter out unrecognised hash types, and hash based readers From 818885e0e8ce4f7212fbdaf168152deff4ac5a8f Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 5 Nov 2021 14:10:16 +0800 Subject: [PATCH 087/119] Misc --- deps/libmbedtls | 2 +- deps/libtoolchain | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/libmbedtls b/deps/libmbedtls index 597bb57..b3a3fad 160000 --- a/deps/libmbedtls +++ b/deps/libmbedtls @@ -1 +1 @@ -Subproject commit 597bb57271177d776682104dc7c8ea8b62670d1d +Subproject commit b3a3fade84185460d4802f5584d10c9a4dc2dd02 diff --git a/deps/libtoolchain b/deps/libtoolchain index 80d2ee3..feb5d43 160000 --- a/deps/libtoolchain +++ b/deps/libtoolchain @@ -1 +1 @@ -Subproject commit 80d2ee3e0b226dfcc0ba6a164375c3754ab3fcc5 +Subproject commit feb5d43cca502429af4e4a92633407dcb2dde80b From 5829c8078e9afacc925322c788b6ef031f6edbe3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 5 Nov 2021 14:11:01 +0800 Subject: [PATCH 088/119] Update KIP description in help text --- README.md | 24 ++++++++++++------------ src/Settings.cpp | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2a3ae78..0890c74 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,16 @@ General purpose reading/extraction tool for Nintendo Switch file formats. * Meta (.npdm) * PartitionFS (and Sha256PartitionFS) (includes raw .nsp) * RomFS -* N**X** Game**C**ard **I**mage (.xci) -* **N**intendo **C**ontent **A**rchive (.nca) -* **C**o**n**tent **M**e**t**adata (.cnmt) -* **N**intendo **S**hared **O**bject (.nso) -* **N**intendo **R**elocatable **O**bject (.nro) -* **Ini**tial Process List (.ini) -* **K**ernel **I**nitial **P**rocess (.kip) -* **N**intendo **A**pplication **C**ontrol **P**roperty (.nacp) -* ES **Cert**ificate (.cert) -* ES **Ti**c**k**et (v2 only) (.tik) +* NX GameCard Image (.xci) +* Nintendo Content Archive (.nca) +* Content Metadata (.cnmt) +* Nintendo Shared Object (`NSO0`) (.nso) +* Nintendo Relocatable Object (`NRO0`) (.nro) +* Initial Program Bundle (`INI1`) (.ini) +* Initial Program (`KIP1`) (.kip) +* Nintendo Application Control Property (.nacp) +* ES Certificate (.cert) +* ES Ticket (v2 only) (.tik) # Usage ``` @@ -62,9 +62,9 @@ Usage: nstool [options... ] --listsym Print Code Symbols. --insttype Specify instruction type [64bit|32bit] (64bit is assumed). - INI (Initial Process List) + INI1 (Initial Program Bundle) nstool [--kipdir ] - --kipdir Extract embedded KIPs to directory. + --kipdir Extract embedded Initial Programs to directory. ASET (Homebrew Asset Blob) nstool [--listfs] [--icon --nacp --fsdir ] diff --git a/src/Settings.cpp b/src/Settings.cpp index df72210..a3d5613 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -727,9 +727,9 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --listapi Print SDK API List.\n"); fmt::print(" --listsym Print Code Symbols.\n"); fmt::print(" --insttype Specify instruction type [64bit|32bit] (64bit is assumed).\n"); - fmt::print("\n INI (Initial Process List)\n"); + fmt::print("\n INI (Initial Program Bundle)\n"); fmt::print(" {:s} [--kipdir ] \n", BIN_NAME); - fmt::print(" --kipdir Extract embedded KIPs to directory.\n"); + fmt::print(" --kipdir Extract embedded Inital Programs to directory.\n"); fmt::print("\n ASET (Homebrew Asset Blob)\n"); fmt::print(" {:s} [--listfs] [--icon --nacp --fsdir ] \n", BIN_NAME); fmt::print(" --listfs Print filesystem in embedded RomFS partition.\n"); From a81a105730ba296b4b1214a0b389a748166db32c Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 5 Nov 2021 14:19:49 +0800 Subject: [PATCH 089/119] Update libnintendo-hac --- deps/libnintendo-hac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 46df669..5490672 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 46df66957d73bb76f42ec65c64edaaf0c64cd1b9 +Subproject commit 5490672abaff623c553df2823a6fcc688ac5e401 From c9242f6ee6d36d8e243ae3c06c67dcb380a3c792 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 5 Nov 2021 14:30:45 +0800 Subject: [PATCH 090/119] Remove libfnd --- .gitmodules | 3 --- deps/libfnd | 1 - 2 files changed, 4 deletions(-) delete mode 160000 deps/libfnd diff --git a/.gitmodules b/.gitmodules index 61d11ae..b3939ec 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "deps/liblz4"] path = deps/liblz4 url = https://github.com/jakcron/liblz4.git -[submodule "deps/libfnd"] - path = deps/libfnd - url = https://github.com/jakcron/libfnd.git [submodule "deps/libnintendo-pki"] path = deps/libnintendo-pki url = https://github.com/jakcron/libnintendo-pki.git diff --git a/deps/libfnd b/deps/libfnd deleted file mode 160000 index 27705ae..0000000 --- a/deps/libfnd +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 27705aeb2394dbe21f580cc742792537e3a0cbed From ebb3433b4f7de8530cd534977d39800b21b8fc06 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 5 Nov 2021 14:37:30 +0800 Subject: [PATCH 091/119] Remove libfnd from visual studio project files. --- build/visualstudio/nstool.sln | 25 ------------------------ build/visualstudio/nstool/nstool.vcxproj | 3 --- 2 files changed, 28 deletions(-) diff --git a/build/visualstudio/nstool.sln b/build/visualstudio/nstool.sln index 202b2b9..8180974 100644 --- a/build/visualstudio/nstool.sln +++ b/build/visualstudio/nstool.sln @@ -7,34 +7,18 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nstool", "nstool\nstool.vcx EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deps", "deps", "{05929EAE-4471-4E8E-A6F3-793A81623D7F}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfnd", "..\..\deps\libfnd\build\visualstudio\libfnd\libfnd.vcxproj", "{4E578016-34BA-4A1E-B8EC-37A48780B6CA}" - ProjectSection(ProjectDependencies) = postProject - {E741ADED-7900-4E07-8DB0-D008C336C3FB} = {E741ADED-7900-4E07-8DB0-D008C336C3FB} - {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblz4", "..\..\deps\liblz4\build\visualstudio\liblz4\liblz4.vcxproj", "{E741ADED-7900-4E07-8DB0-D008C336C3FB}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-es", "..\..\deps\libnintendo-es\build\visualstudio\libnintendo-es\libnintendo-es.vcxproj", "{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}" - ProjectSection(ProjectDependencies) = postProject - {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac", "..\..\deps\libnintendo-hac\build\visualstudio\libnintendo-hac\libnintendo-hac.vcxproj", "{8885C125-83FB-4F73-A93A-C712B1434D54}" - ProjectSection(ProjectDependencies) = postProject - {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac-hb", "..\..\deps\libnintendo-hac-hb\build\visualstudio\libnintendo-hac-hb\libnintendo-hac-hb.vcxproj", "{24D001B4-D439-4967-9371-DC3E0523EB19}" ProjectSection(ProjectDependencies) = postProject - {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} {8885C125-83FB-4F73-A93A-C712B1434D54} = {8885C125-83FB-4F73-A93A-C712B1434D54} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-pki", "..\..\deps\libnintendo-pki\build\visualstudio\libnintendo-pki\libnintendo-pki.vcxproj", "{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}" - ProjectSection(ProjectDependencies) = postProject - {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA} - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmbedtls", "..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj", "{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}" EndProject @@ -58,14 +42,6 @@ Global {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.Build.0 = Release|x64 {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.ActiveCfg = Release|Win32 {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.Build.0 = Release|Win32 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.ActiveCfg = Debug|x64 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.Build.0 = Debug|x64 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.ActiveCfg = Debug|Win32 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.Build.0 = Debug|Win32 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.ActiveCfg = Release|x64 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.Build.0 = Release|x64 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.ActiveCfg = Release|Win32 - {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.Build.0 = Release|Win32 {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.ActiveCfg = Debug|x64 {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.Build.0 = Debug|x64 {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.ActiveCfg = Debug|Win32 @@ -135,7 +111,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} {E741ADED-7900-4E07-8DB0-D008C336C3FB} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} {8616D6C9-C8DE-4C3F-AFC2-625636664C2B} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} {8885C125-83FB-4F73-A93A-C712B1434D54} = {05929EAE-4471-4E8E-A6F3-793A81623D7F} diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj index e9bac82..e5e67e1 100644 --- a/build/visualstudio/nstool/nstool.vcxproj +++ b/build/visualstudio/nstool/nstool.vcxproj @@ -126,9 +126,6 @@ {f4b0540e-0aae-4006-944b-356944ef61fa} - - {4e578016-34ba-4a1e-b8ec-37a48780b6ca} - {e741aded-7900-4e07-8db0-d008c336c3fb} From a594486101186b6f9420558d41f7df72394c6e5a Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 5 Nov 2021 15:23:18 +0800 Subject: [PATCH 092/119] Update dependencies to stable branches. --- deps/libnintendo-es | 2 +- deps/libnintendo-hac | 2 +- deps/libnintendo-hac-hb | 2 +- deps/libnintendo-pki | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deps/libnintendo-es b/deps/libnintendo-es index bcbbf13..e2aa92f 160000 --- a/deps/libnintendo-es +++ b/deps/libnintendo-es @@ -1 +1 @@ -Subproject commit bcbbf13670eaabfa21697887227d1aabea794f9d +Subproject commit e2aa92fd2d59571f4f55d789eb6e5e2c2f0ab6c7 diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 5490672..9e246f7 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 5490672abaff623c553df2823a6fcc688ac5e401 +Subproject commit 9e246f7b121e526b8b42fdd81adf558f76b515eb diff --git a/deps/libnintendo-hac-hb b/deps/libnintendo-hac-hb index 1956c9b..321243a 160000 --- a/deps/libnintendo-hac-hb +++ b/deps/libnintendo-hac-hb @@ -1 +1 @@ -Subproject commit 1956c9be9a01d90d84e2691c76d707781694f3be +Subproject commit 321243a4ccf07e61460f86e90ae635f8b94c6474 diff --git a/deps/libnintendo-pki b/deps/libnintendo-pki index b8a1066..5a763cb 160000 --- a/deps/libnintendo-pki +++ b/deps/libnintendo-pki @@ -1 +1 @@ -Subproject commit b8a10663a99f7b5fedf11139549e8bf1b289c0e0 +Subproject commit 5a763cb9ba89de94fe39b4f0aecd1cc963dad0f0 From 141477207b49bd85711a624e20c6d46b10e4cc64 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 5 Nov 2021 15:23:58 +0800 Subject: [PATCH 093/119] Misc --- .gitmodules | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.gitmodules b/.gitmodules index b3939ec..d7defa6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,15 @@ [submodule "deps/liblz4"] path = deps/liblz4 url = https://github.com/jakcron/liblz4.git +[submodule "deps/libmbedtls"] + path = deps/libmbedtls + url = https://github.com/jakcron/libmbedtls +[submodule "deps/libtoolchain"] + path = deps/libtoolchain + url = https://github.com/jakcron/libtoolchain.git +[submodule "deps/libfmt"] + path = deps/libfmt + url = https://github.com/jakcron/libfmt.git [submodule "deps/libnintendo-pki"] path = deps/libnintendo-pki url = https://github.com/jakcron/libnintendo-pki.git @@ -13,12 +22,3 @@ [submodule "deps/libnintendo-hac-hb"] path = deps/libnintendo-hac-hb url = https://github.com/jakcron/libnintendo-hac-hb.git -[submodule "deps/libmbedtls"] - path = deps/libmbedtls - url = https://github.com/jakcron/libmbedtls -[submodule "deps/libtoolchain"] - path = deps/libtoolchain - url = https://github.com/jakcron/libtoolchain.git -[submodule "deps/libfmt"] - path = deps/libfmt - url = https://github.com/jakcron/libfmt.git From be59f92a2cc761d6139491c963020c8149910d6e Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 5 Nov 2021 15:31:34 +0800 Subject: [PATCH 094/119] Change formatting of hex data to exclude ":" between bytes. --- src/EsTikProcess.cpp | 4 ++-- src/GameCardProcess.cpp | 8 ++++---- src/NcaProcess.cpp | 16 ++++++++-------- src/Settings.cpp | 20 ++++++++++---------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/EsTikProcess.cpp b/src/EsTikProcess.cpp index db6b2ca..575fb22 100644 --- a/src/EsTikProcess.cpp +++ b/src/EsTikProcess.cpp @@ -119,12 +119,12 @@ void nstool::EsTikProcess::displayTicket() if (body.getTitleKeyEncType() == nn::es::ticket::RSA2048) { fmt::print(" Data:\n"); - fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(body.getEncTitleKey(), 0x100, true, ":", 0x10, 6, false)); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(body.getEncTitleKey(), 0x100, true, "", 0x10, 6, false)); } else if (body.getTitleKeyEncType() == nn::es::ticket::AES128_CBC) { fmt::print(" Data:\n"); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getEncTitleKey(), 0x10, true, ":")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getEncTitleKey(), 0x10, true, "")); } else { diff --git a/src/GameCardProcess.cpp b/src/GameCardProcess.cpp index d16a85b..739dd2d 100644 --- a/src/GameCardProcess.cpp +++ b/src/GameCardProcess.cpp @@ -153,12 +153,12 @@ void nstool::GameCardProcess::displayHeader() fmt::print(" TitleKeyDecIndex: {:d}\n", mHdr.getTitleKeyDecIndex()); fmt::print(" InitialData:\n"); fmt::print(" Hash:\n"); - fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getInitialDataHash().data(), mHdr.getInitialDataHash().size(), true, ":", 0x10, 6, false)); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getInitialDataHash().data(), mHdr.getInitialDataHash().size(), true, "", 0x10, 6, false)); } if (mCliOutputMode.show_extended_info) { fmt::print(" Extended Header AesCbc IV:\n"); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getAesCbcIv().data(), mHdr.getAesCbcIv().size(), true, ":")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getAesCbcIv().data(), mHdr.getAesCbcIv().size(), true, "")); } fmt::print(" SelSec: 0x{:x}\n", mHdr.getSelSec()); fmt::print(" SelT1Key: 0x{:x}\n", mHdr.getSelT1Key()); @@ -191,7 +191,7 @@ void nstool::GameCardProcess::displayHeader() if (mCliOutputMode.show_extended_info) { fmt::print(" Hash:\n"); - fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getPartitionFsHash().data(), mHdr.getPartitionFsHash().size(), true, ":", 0x10, 6, false)); + fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getPartitionFsHash().data(), mHdr.getPartitionFsHash().size(), true, "", 0x10, 6, false)); } } @@ -211,7 +211,7 @@ void nstool::GameCardProcess::displayHeader() fmt::print(" Update Partition Info:\n"); fmt::print(" CUP Version: {:s} (v{:d})\n", nn::hac::ContentMetaUtil::getVersionAsString(mHdr.getUppVersion()), mHdr.getUppVersion()); fmt::print(" CUP TitleId: 0x{:016x}\n", mHdr.getUppId()); - fmt::print(" CUP Digest: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getUppHash().data(), mHdr.getUppHash().size(), true, ":")); + fmt::print(" CUP Digest: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getUppHash().data(), mHdr.getUppHash().size(), true, "")); } } diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index 22a829d..6cfda7a 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -208,7 +208,7 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys() if (mContentKey.aes_ctr.isSet()) { fmt::print("[NCA Content Key]\n"); - fmt::print(" AES-CTR Key: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mContentKey.aes_ctr.get().data(), mContentKey.aes_ctr.get().size(), true, ":")); + fmt::print(" AES-CTR Key: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mContentKey.aes_ctr.get().data(), mContentKey.aes_ctr.get().size(), true, "")); } } } @@ -423,11 +423,11 @@ void nstool::NcaProcess::displayHeader() fmt::print(" |-----|-------------------------------------------------|-------------------------------------------------|\n"); for (size_t i = 0; i < mContentKey.kak_list.size(); i++) { - fmt::print(" | {:3d} | {:s} | ", mContentKey.kak_list[i].index, tc::cli::FormatUtil::formatBytesAsString(mContentKey.kak_list[i].enc.data(), mContentKey.kak_list[i].enc.size(), true, ":")); + fmt::print(" | {:3d} | {:s} | ", mContentKey.kak_list[i].index, tc::cli::FormatUtil::formatBytesAsString(mContentKey.kak_list[i].enc.data(), mContentKey.kak_list[i].enc.size(), true, "")); if (mContentKey.kak_list[i].decrypted) - fmt::print("{:s}", tc::cli::FormatUtil::formatBytesAsString(mContentKey.kak_list[i].dec.data(), mContentKey.kak_list[i].dec.size(), true, ":")); + fmt::print("{:s}", tc::cli::FormatUtil::formatBytesAsString(mContentKey.kak_list[i].dec.data(), mContentKey.kak_list[i].dec.size(), true, "")); else fmt::print(" "); @@ -457,7 +457,7 @@ void nstool::NcaProcess::displayHeader() memcpy(aes_ctr.data(), info.aes_ctr.data(), aes_ctr.size()); tc::crypto::detail::incr_counter<16>(aes_ctr.data(), info.offset>>4); fmt::print(" AesCtr Counter:\n"); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(aes_ctr.data(), aes_ctr.size(), true, ":")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(aes_ctr.data(), aes_ctr.size(), true, "")); } if (info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) { @@ -480,8 +480,8 @@ void nstool::NcaProcess::displayHeader() for (size_t j = 0; j < hash_hdr.getMasterHashList().size(); j++) { fmt::print(" Master Hash {:d}:\n", j); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHashList()[j].data(), 0x10, true, ":")); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHashList()[j].data()+0x10, 0x10, true, ":")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHashList()[j].data(), 0x10, true, "")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHashList()[j].data()+0x10, 0x10, true, "")); } } else if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256) @@ -489,8 +489,8 @@ void nstool::NcaProcess::displayHeader() auto hash_hdr = info.hierarchicalsha256_hdr; fmt::print(" HierarchicalSha256 Header:\n"); fmt::print(" Master Hash:\n"); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHash().data(), 0x10, true, ":")); - fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHash().data()+0x10, 0x10, true, ":")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHash().data(), 0x10, true, "")); + fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.getMasterHash().data()+0x10, 0x10, true, "")); fmt::print(" HashBlockSize: 0x{:x}\n", hash_hdr.getHashBlockSize()); for (size_t j = 0; j < hash_hdr.getLayerInfo().size(); j++) { diff --git a/src/Settings.cpp b/src/Settings.cpp index a3d5613..6114501 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -753,22 +753,22 @@ void nstool::SettingsInitializer::dump_keys() const if (opt.keybag.nca_header_key.isSet()) { fmt::print(" Header-EncryptionKey:\n"); - fmt::print(" Key0: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(opt.keybag.nca_header_key.get()[0].data(), opt.keybag.nca_header_key.get()[0].size(), true, ":")); - fmt::print(" Key1: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(opt.keybag.nca_header_key.get()[1].data(), opt.keybag.nca_header_key.get()[1].size(), true, ":")); + fmt::print(" Key0: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(opt.keybag.nca_header_key.get()[0].data(), opt.keybag.nca_header_key.get()[0].size(), true, "")); + fmt::print(" Key1: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(opt.keybag.nca_header_key.get()[1].data(), opt.keybag.nca_header_key.get()[1].size(), true, "")); } std::vector kaek_label = {"Application", "Ocean", "System"}; for (size_t kaek_index = 0; kaek_index < opt.keybag.nca_key_area_encryption_key.size(); kaek_index++) { for (auto itr = opt.keybag.nca_key_area_encryption_key[kaek_index].begin(); itr != opt.keybag.nca_key_area_encryption_key[kaek_index].end(); itr++) { - fmt::print(" KeyAreaEncryptionKey-{:s}-{:02x}:\n {:s}\n", kaek_label[kaek_index], itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + fmt::print(" KeyAreaEncryptionKey-{:s}-{:02x}:\n {:s}\n", kaek_label[kaek_index], itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, "")); } } for (size_t kaek_index = 0; kaek_index < opt.keybag.nca_key_area_encryption_key_hw.size(); kaek_index++) { for (auto itr = opt.keybag.nca_key_area_encryption_key_hw[kaek_index].begin(); itr != opt.keybag.nca_key_area_encryption_key_hw[kaek_index].end(); itr++) { - fmt::print(" KeyAreaEncryptionKeyHw-{:s}-{:02x}:\n {:s}\n", kaek_label[kaek_index], itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + fmt::print(" KeyAreaEncryptionKeyHw-{:s}-{:02x}:\n {:s}\n", kaek_label[kaek_index], itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, "")); } } fmt::print(" NRR Keys:\n"); @@ -783,7 +783,7 @@ void nstool::SettingsInitializer::dump_keys() const } for (auto itr = opt.keybag.xci_header_key.begin(); itr != opt.keybag.xci_header_key.end(); itr++) { - fmt::print(" ExtendedHeader-EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + fmt::print(" ExtendedHeader-EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, "")); } if (opt.keybag.xci_cert_sign_key.isSet()) { @@ -793,7 +793,7 @@ void nstool::SettingsInitializer::dump_keys() const fmt::print(" Package1 Keys:\n"); for (auto itr = opt.keybag.pkg1_key.begin(); itr != opt.keybag.pkg1_key.end(); itr++) { - fmt::print(" EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + fmt::print(" EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, "")); } fmt::print(" Package2 Keys:\n"); @@ -803,13 +803,13 @@ void nstool::SettingsInitializer::dump_keys() const } for (auto itr = opt.keybag.pkg2_key.begin(); itr != opt.keybag.pkg2_key.end(); itr++) { - fmt::print(" EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + fmt::print(" EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, "")); } fmt::print(" ETicket Keys:\n"); for (auto itr = opt.keybag.etik_common_key.begin(); itr != opt.keybag.etik_common_key.end(); itr++) { - fmt::print(" CommonKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + fmt::print(" CommonKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, "")); } fmt::print(" BroadOn Signer Profiles:\n"); @@ -858,7 +858,7 @@ void nstool::SettingsInitializer::dump_rsa_key(const KeyBag::rsa_key_t& key, con if (expanded_key_data) { fmt::print("{:s} Modulus:\n", indent_str); - fmt::print("{:s} {:s}", indent_str, tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(key.n.data(), key.n.size(), true, ":", 0x10, indent + 4, false)); + fmt::print("{:s} {:s}", indent_str, tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(key.n.data(), key.n.size(), true, "", 0x10, indent + 4, false)); } else { @@ -870,7 +870,7 @@ void nstool::SettingsInitializer::dump_rsa_key(const KeyBag::rsa_key_t& key, con if (expanded_key_data) { fmt::print("{:s} Private Exponent:\n", indent_str); - fmt::print("{:s} {:s}", indent_str, tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(key.d.data(), key.d.size(), true, ":", 0x10, indent + 4, false)); + fmt::print("{:s} {:s}", indent_str, tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(key.d.data(), key.d.size(), true, "", 0x10, indent + 4, false)); } else { From d1e1d5229b3296d2cff99a076aaf57e72ba3a65a Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 9 Nov 2021 16:30:11 +0800 Subject: [PATCH 095/119] Clarified help text. --- README.md | 41 +++++++++++++++++++++-------------------- src/Settings.cpp | 41 ++++++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 0890c74..9dcf8dc 100644 --- a/README.md +++ b/README.md @@ -31,30 +31,31 @@ Usage: nstool [options... ] --showlayout Show layout metadata. -v, --verbose Verbose output. - XCI (GameCard Image) - nstool [--listfs] [--update --logo --normal --secure ] <.xci file> - --listfs Print file system in embedded partitions. - --update Extract "update" partition to directory. - --logo Extract "logo" partition to directory. - --normal Extract "normal" partition to directory. - --secure Extract "secure" partition to directory. - PFS0/HFS0 (PartitionFs), RomFs, NSP (Nintendo Submission Package) - nstool [--listfs] [--fsdir ] + nstool [--listfs] [--fsdir [] ] --listfs Print file system. - --fsdir Extract file system to directory. + -x, --fsdir Extract file system to directory. + + XCI (GameCard Image) + nstool [--listfs] [--fsdir [] ] [--update --logo --normal --secure ] <.xci file> + --listfs Print file system. + -x, --fsdir Extract file system to directory. + --update Extract "update" partition to directory. (Alias for "-x /update ") + --logo Extract "logo" partition to directory. (Alias for "-x /logo ") + --normal Extract "normal" partition to directory. (Alias for "-x /normal ") + --secure Extract "secure" partition to directory. (Alias for "-x /secure ") NCA (Nintendo Content Archive) - nstool [--listfs] [--bodykey --titlekey ] [--part0 ...] <.nca file> - --listfs Print file system in embedded partitions. + nstool [--listfs] [--bodykey --titlekey ] [--fsdir [] ] [--part0 ...] <.nca file> + --listfs Print file system. + -x, --fsdir Extract file system to directory. --titlekey Specify title key extracted from ticket. - --bodykey Specify body encryption key. --tik Specify ticket to source title key. --cert Specify certificate chain to verify ticket. - --part0 Extract "partition 0" to directory. - --part1 Extract "partition 1" to directory. - --part2 Extract "partition 2" to directory. - --part3 Extract "partition 3" to directory. + --part0 Extract "partition 0" to directory. (Alias for "-x /0 ") + --part1 Extract "partition 1" to directory. (Alias for "-x /1 ") + --part2 Extract "partition 2" to directory. (Alias for "-x /2 ") + --part3 Extract "partition 3" to directory. (Alias for "-x /3 ") NSO (Nintendo Shared Object), NRO (Nintendo Relocatable Object) nstool [--listapi --listsym] [--insttype ] @@ -62,16 +63,16 @@ Usage: nstool [options... ] --listsym Print Code Symbols. --insttype Specify instruction type [64bit|32bit] (64bit is assumed). - INI1 (Initial Program Bundle) + INI (Initial Program Bundle) nstool [--kipdir ] - --kipdir Extract embedded Initial Programs to directory. + --kipdir Extract embedded Inital Programs to directory. ASET (Homebrew Asset Blob) nstool [--listfs] [--icon --nacp --fsdir ] --listfs Print filesystem in embedded RomFS partition. --icon Extract icon partition to file. --nacp Extract NACP partition to file. - --fsdir Extract RomFS partition to directory. + -x, --fsdir Extract RomFS partition to directory. ``` # External Keys diff --git a/src/Settings.cpp b/src/Settings.cpp index 6114501..a826a6c 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -404,6 +404,11 @@ public: throw tc::ArgumentOutOfRangeException(fmt::format("Option \"{:s}\" requires a parameter.", option)); } + std::string custom_path_str; + tc::io::PathUtil::pathToUnixUTF8(mCustomPath, custom_path_str); + + fmt::print("[WARNING] \"{:s} {:s}\" is deprecated. Consider using \"-x {:s} {:s}\" instead.\n", option, params[0], custom_path_str, params[0]); + mJobs.push_back({mCustomPath, tc::io::Path(params[0])}); } private: @@ -700,28 +705,30 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --showkeys Show keys generated.\n"); fmt::print(" --showlayout Show layout metadata.\n"); fmt::print(" -v, --verbose Verbose output.\n"); - fmt::print("\n XCI (GameCard Image)\n"); - fmt::print(" {:s} [--listfs] [--update --logo --normal --secure ] <.xci file>\n", BIN_NAME); - fmt::print(" --listfs Print file system in embedded partitions.\n"); - fmt::print(" --update Extract \"update\" partition to directory.\n"); - fmt::print(" --logo Extract \"logo\" partition to directory.\n"); - fmt::print(" --normal Extract \"normal\" partition to directory.\n"); - fmt::print(" --secure Extract \"secure\" partition to directory.\n"); fmt::print("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Nintendo Submission Package)\n"); - fmt::print(" {:s} [--listfs] [--fsdir ] \n", BIN_NAME); + fmt::print(" {:s} [--listfs] [--fsdir [] ] \n", BIN_NAME); fmt::print(" --listfs Print file system.\n"); - fmt::print(" --fsdir Extract file system to directory.\n"); + fmt::print(" -x, --fsdir Extract file system to directory.\n"); + fmt::print("\n XCI (GameCard Image)\n"); + fmt::print(" {:s} [--listfs] [--fsdir [] ] [--update --logo --normal --secure ] <.xci file>\n", BIN_NAME); + fmt::print(" --listfs Print file system.\n"); + fmt::print(" -x, --fsdir Extract file system to directory.\n"); + fmt::print(" --update Extract \"update\" partition to directory. (Alias for \"-x /update \")\n"); + fmt::print(" --logo Extract \"logo\" partition to directory. (Alias for \"-x /logo \")\n"); + fmt::print(" --normal Extract \"normal\" partition to directory. (Alias for \"-x /normal \")\n"); + fmt::print(" --secure Extract \"secure\" partition to directory. (Alias for \"-x /secure \")\n"); fmt::print("\n NCA (Nintendo Content Archive)\n"); - fmt::print(" {:s} [--listfs] [--bodykey --titlekey ] [--part0 ...] <.nca file>\n", BIN_NAME); - fmt::print(" --listfs Print file system in embedded partitions.\n"); + fmt::print(" {:s} [--listfs] [--bodykey --titlekey ] [--fsdir [] ] [--part0 ...] <.nca file>\n", BIN_NAME); + fmt::print(" --listfs Print file system.\n"); + fmt::print(" -x, --fsdir Extract file system to directory.\n"); fmt::print(" --titlekey Specify title key extracted from ticket.\n"); - fmt::print(" --bodykey Specify body encryption key.\n"); + //fmt::print(" --bodykey Specify body encryption key.\n"); fmt::print(" --tik Specify ticket to source title key.\n"); fmt::print(" --cert Specify certificate chain to verify ticket.\n"); - fmt::print(" --part0 Extract \"partition 0\" to directory.\n"); - fmt::print(" --part1 Extract \"partition 1\" to directory.\n"); - fmt::print(" --part2 Extract \"partition 2\" to directory.\n"); - fmt::print(" --part3 Extract \"partition 3\" to directory.\n"); + fmt::print(" --part0 Extract \"partition 0\" to directory. (Alias for \"-x /0 \")\n"); + fmt::print(" --part1 Extract \"partition 1\" to directory. (Alias for \"-x /1 \")\n"); + fmt::print(" --part2 Extract \"partition 2\" to directory. (Alias for \"-x /2 \")\n"); + fmt::print(" --part3 Extract \"partition 3\" to directory. (Alias for \"-x /3 \")\n"); fmt::print("\n NSO (Nintendo Shared Object), NRO (Nintendo Relocatable Object)\n"); fmt::print(" {:s} [--listapi --listsym] [--insttype ] \n", BIN_NAME); fmt::print(" --listapi Print SDK API List.\n"); @@ -735,7 +742,7 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --listfs Print filesystem in embedded RomFS partition.\n"); fmt::print(" --icon Extract icon partition to file.\n"); fmt::print(" --nacp Extract NACP partition to file.\n"); - fmt::print(" --fsdir Extract RomFS partition to directory.\n"); + fmt::print(" -x, --fsdir Extract RomFS partition to directory.\n"); } void nstool::SettingsInitializer::dump_keys() const From e813d7a7a6ff64bb135e7b7c96319d4f413fd92a Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 9 Nov 2021 16:38:42 +0800 Subject: [PATCH 096/119] Update SWITCH_KEYS.md to what can be imported. --- SWITCH_KEYS.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/SWITCH_KEYS.md b/SWITCH_KEYS.md index 2bb3efd..1ec2440 100644 --- a/SWITCH_KEYS.md +++ b/SWITCH_KEYS.md @@ -10,10 +10,8 @@ Some switch files formats feature encryption and or cryptographic signatures. In This keyset file can be provided via the command line (refer to usage for details). - - ## Format -The following keys are recognised (## represents a hexadecimal number between 00 and 1F): +The following keys are recognised (## represents a hexadecimal number between 00 and FF): ``` ; Key Sources @@ -47,18 +45,24 @@ pki_root_sign_key_private : RSA4096 Private Exponent (0x200 bytes) nca_header_key : AES128-XTS Key (0x20 bytes) nca_header_sign_key_##_modulus : RSA2048 Modulus (0x100 bytes) nca_header_sign_key_##_private : RSA2048 Private Exponent (0x100 bytes) -nca_body_keak_application_## : AES128 Key (0x10 bytes) -nca_body_keak_ocean_## : AES128 Key (0x10 bytes) -nca_body_keak_system_## : AES128 Key (0x10 bytes) +nca_key_area_key_application_## : AES128 Key (0x10 bytes) +nca_key_area_key_ocean_## : AES128 Key (0x10 bytes) +nca_key_area_key_system_## : AES128 Key (0x10 bytes) +nca_key_area_key_hw_application_## : AES128 Key (0x10 bytes) +nca_key_area_key_hw_ocean_## : AES128 Key (0x10 bytes) +nca_key_area_key_hw_system_## : AES128 Key (0x10 bytes) ; NRR Keys nrr_certificate_sign_key_##_modulus : RSA2048 Modulus (0x100 bytes) nrr_certificate_sign_key_##_private : RSA2048 Private Exponent (0x100 bytes) ; XCI Keys -xci_header_key : AES128 Key (0x10 bytes) +xci_header_key_## : AES128 Key (0x10 bytes) xci_header_sign_key_modulus : RSA2048 Modulus (0x100 bytes) xci_header_sign_key_private : RSA2048 Private Exponent (0x100 bytes) +xci_initial_data_kek_## : AES128 Key (0x10 bytes) +xci_cert_sign_key_modulus : RSA2048 Modulus (0x100 bytes) +xci_cert_sign_key_private : RSA2048 Private Exponent (0x100 bytes) ; ACID Keys acid_sign_key_##_modulus : RSA2048 Modulus (0x100 bytes) From 293180f76ab045d839a32fcf3a73333563790e63 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 9 Nov 2021 16:40:36 +0800 Subject: [PATCH 097/119] Clarify BUILDING.md --- BUILDING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index ba5df5c..2d94f09 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -12,9 +12,9 @@ * `make deps` - Compile locally included dependency libraries * `make clean_deps` - Remove compiled library binaries and object files -## Native Win32 - Visual Studio +## Native Windows - Visual Studio ### Requirements -* [Visual Studio Community](https://visualstudio.microsoft.com/vs/community/) 2015 or 2017 +* [Visual Studio Community](https://visualstudio.microsoft.com/vs/community/) 2015 / 2017 / 2019 ### Compiling NSTool * Open `build/visualstudio/nstool.sln` in Visual Studio From c327f605e6fe3be5c1b19a24cd482cfdabb03a6c Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 9 Nov 2021 16:56:57 +0800 Subject: [PATCH 098/119] Update build_master.yml --- .github/workflows/build_master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_master.yml b/.github/workflows/build_master.yml index 2cf9ff8..db07cda 100644 --- a/.github/workflows/build_master.yml +++ b/.github/workflows/build_master.yml @@ -46,7 +46,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.0.0 + uses: microsoft/setup-msbuild@v1.1 - name: Clone submodules run: git submodule init && git submodule update - name: Compile ${{ matrix.prog }} From 187cbca7e3580159545d6d36f200b77731e86d6c Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 9 Nov 2021 19:07:19 +0800 Subject: [PATCH 099/119] Misc typo. --- src/AssetProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index b384db7..fc6073f 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -3,7 +3,7 @@ #include "util.h" nstool::AssetProcess::AssetProcess() : - mModuleName("nstool::AssetProcess::"), + mModuleName("nstool::AssetProcess"), mFile(), mCliOutputMode(true, false, false, false), mVerify(false) From 1b27126a913dbfe98aef3f3427880ee648d3dc94 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 9 Nov 2021 20:23:20 +0800 Subject: [PATCH 100/119] Revise help text again --- README.md | 33 ++++++++++++++++--------------- src/Settings.cpp | 51 +++++++++++++++++++++++++++++------------------- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 9dcf8dc..f89713b 100644 --- a/README.md +++ b/README.md @@ -32,30 +32,31 @@ Usage: nstool [options... ] -v, --verbose Verbose output. PFS0/HFS0 (PartitionFs), RomFs, NSP (Nintendo Submission Package) - nstool [--listfs] [--fsdir [] ] - --listfs Print file system. - -x, --fsdir Extract file system to directory. + nstool [--fstree] [-x [] ] + --fstree Print filesystem tree. + -x, --extract Extract a file or directory to local filesystem. XCI (GameCard Image) - nstool [--listfs] [--fsdir [] ] [--update --logo --normal --secure ] <.xci file> - --listfs Print file system. - -x, --fsdir Extract file system to directory. + nstool [--fstree] [-x [] ] <.xci file> + --fstree Print filesystem tree. + -x, --extract Extract a file or directory to local filesystem. --update Extract "update" partition to directory. (Alias for "-x /update ") --logo Extract "logo" partition to directory. (Alias for "-x /logo ") --normal Extract "normal" partition to directory. (Alias for "-x /normal ") --secure Extract "secure" partition to directory. (Alias for "-x /secure ") NCA (Nintendo Content Archive) - nstool [--listfs] [--bodykey --titlekey ] [--fsdir [] ] [--part0 ...] <.nca file> - --listfs Print file system. - -x, --fsdir Extract file system to directory. + nstool [--fstree] [-x [] ] [--bodykey --titlekey -tik ] <.nca file> + --fstree Print filesystem tree. + -x, --extract Extract a file or directory to local filesystem. --titlekey Specify title key extracted from ticket. + --bodykey Specify body encryption key. --tik Specify ticket to source title key. --cert Specify certificate chain to verify ticket. - --part0 Extract "partition 0" to directory. (Alias for "-x /0 ") - --part1 Extract "partition 1" to directory. (Alias for "-x /1 ") - --part2 Extract "partition 2" to directory. (Alias for "-x /2 ") - --part3 Extract "partition 3" to directory. (Alias for "-x /3 ") + --part0 Extract partition "0" to directory. (Alias for "-x /0 ") + --part1 Extract partition "1" to directory. (Alias for "-x /1 ") + --part2 Extract partition "2" to directory. (Alias for "-x /2 ") + --part3 Extract partition "3" to directory. (Alias for "-x /3 ") NSO (Nintendo Shared Object), NRO (Nintendo Relocatable Object) nstool [--listapi --listsym] [--insttype ] @@ -68,11 +69,11 @@ Usage: nstool [options... ] --kipdir Extract embedded Inital Programs to directory. ASET (Homebrew Asset Blob) - nstool [--listfs] [--icon --nacp --fsdir ] - --listfs Print filesystem in embedded RomFS partition. + nstool [--fstree] [-x [] ] [--icon --nacp ] + --fstree Print RomFS filesystem tree. + -x, --extract Extract a file or directory from RomFS to local filesystem. --icon Extract icon partition to file. --nacp Extract NACP partition to file. - -x, --fsdir Extract RomFS partition to directory. ``` # External Keys diff --git a/src/Settings.cpp b/src/Settings.cpp index a826a6c..7603e45 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -407,7 +407,17 @@ public: std::string custom_path_str; tc::io::PathUtil::pathToUnixUTF8(mCustomPath, custom_path_str); - fmt::print("[WARNING] \"{:s} {:s}\" is deprecated. Consider using \"-x {:s} {:s}\" instead.\n", option, params[0], custom_path_str, params[0]); + fmt::print("[WARNING] \"{:s} {:s}\" is deprecated. ", option, params[0]); + // if custom path is root path, use the shortened version of -x + if (mCustomPath == tc::io::Path("/")) + { + fmt::print("Consider using \"-x {:s}\" instead.\n", params[0]); + } + else + { + fmt::print("Consider using \"-x {:s} {:s}\" instead.\n", custom_path_str, params[0]); + } + mJobs.push_back({mCustomPath, tc::io::Path(params[0])}); } @@ -550,8 +560,9 @@ void nstool::SettingsInitializer::parse_args(const std::vector& arg opts.registerOptionHandler(std::shared_ptr(new InstructionTypeOptionHandler(code.is_64bit_instruction, { "--insttype" }))); // fs options - opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(fs.show_fs_tree, { "--listfs" }))); - opts.registerOptionHandler(std::shared_ptr(new ExtractDataPathOptionHandler(fs.extract_jobs, { "--fsdir", "-x", "--extract" }))); + opts.registerOptionHandler(std::shared_ptr(new FlagOptionHandler(fs.show_fs_tree, { "--fstree", "--listfs" }))); + opts.registerOptionHandler(std::shared_ptr(new ExtractDataPathOptionHandler(fs.extract_jobs, { "-x", "--extract" }))); + opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--fsdir" }, tc::io::Path("/")))); // xci options opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--update" }, tc::io::Path("/update/")))); @@ -706,29 +717,29 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --showlayout Show layout metadata.\n"); fmt::print(" -v, --verbose Verbose output.\n"); fmt::print("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Nintendo Submission Package)\n"); - fmt::print(" {:s} [--listfs] [--fsdir [] ] \n", BIN_NAME); - fmt::print(" --listfs Print file system.\n"); - fmt::print(" -x, --fsdir Extract file system to directory.\n"); + fmt::print(" {:s} [--fstree] [-x [] ] \n", BIN_NAME); + fmt::print(" --fstree Print filesystem tree.\n"); + fmt::print(" -x, --extract Extract a file or directory to local filesystem.\n"); fmt::print("\n XCI (GameCard Image)\n"); - fmt::print(" {:s} [--listfs] [--fsdir [] ] [--update --logo --normal --secure ] <.xci file>\n", BIN_NAME); - fmt::print(" --listfs Print file system.\n"); - fmt::print(" -x, --fsdir Extract file system to directory.\n"); + fmt::print(" {:s} [--fstree] [-x [] ] <.xci file>\n", BIN_NAME); + fmt::print(" --fstree Print filesystem tree.\n"); + fmt::print(" -x, --extract Extract a file or directory to local filesystem.\n"); fmt::print(" --update Extract \"update\" partition to directory. (Alias for \"-x /update \")\n"); fmt::print(" --logo Extract \"logo\" partition to directory. (Alias for \"-x /logo \")\n"); fmt::print(" --normal Extract \"normal\" partition to directory. (Alias for \"-x /normal \")\n"); fmt::print(" --secure Extract \"secure\" partition to directory. (Alias for \"-x /secure \")\n"); fmt::print("\n NCA (Nintendo Content Archive)\n"); - fmt::print(" {:s} [--listfs] [--bodykey --titlekey ] [--fsdir [] ] [--part0 ...] <.nca file>\n", BIN_NAME); - fmt::print(" --listfs Print file system.\n"); - fmt::print(" -x, --fsdir Extract file system to directory.\n"); + fmt::print(" {:s} [--fstree] [-x [] ] [--bodykey --titlekey -tik ] <.nca file>\n", BIN_NAME); + fmt::print(" --fstree Print filesystem tree.\n"); + fmt::print(" -x, --extract Extract a file or directory to local filesystem.\n"); fmt::print(" --titlekey Specify title key extracted from ticket.\n"); - //fmt::print(" --bodykey Specify body encryption key.\n"); + fmt::print(" --bodykey Specify body encryption key.\n"); fmt::print(" --tik Specify ticket to source title key.\n"); fmt::print(" --cert Specify certificate chain to verify ticket.\n"); - fmt::print(" --part0 Extract \"partition 0\" to directory. (Alias for \"-x /0 \")\n"); - fmt::print(" --part1 Extract \"partition 1\" to directory. (Alias for \"-x /1 \")\n"); - fmt::print(" --part2 Extract \"partition 2\" to directory. (Alias for \"-x /2 \")\n"); - fmt::print(" --part3 Extract \"partition 3\" to directory. (Alias for \"-x /3 \")\n"); + fmt::print(" --part0 Extract partition \"0\" to directory. (Alias for \"-x /0 \")\n"); + fmt::print(" --part1 Extract partition \"1\" to directory. (Alias for \"-x /1 \")\n"); + fmt::print(" --part2 Extract partition \"2\" to directory. (Alias for \"-x /2 \")\n"); + fmt::print(" --part3 Extract partition \"3\" to directory. (Alias for \"-x /3 \")\n"); fmt::print("\n NSO (Nintendo Shared Object), NRO (Nintendo Relocatable Object)\n"); fmt::print(" {:s} [--listapi --listsym] [--insttype ] \n", BIN_NAME); fmt::print(" --listapi Print SDK API List.\n"); @@ -738,11 +749,11 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" {:s} [--kipdir ] \n", BIN_NAME); fmt::print(" --kipdir Extract embedded Inital Programs to directory.\n"); fmt::print("\n ASET (Homebrew Asset Blob)\n"); - fmt::print(" {:s} [--listfs] [--icon --nacp --fsdir ] \n", BIN_NAME); - fmt::print(" --listfs Print filesystem in embedded RomFS partition.\n"); + fmt::print(" {:s} [--fstree] [-x [] ] [--icon --nacp ] \n", BIN_NAME); + fmt::print(" --fstree Print RomFS filesystem tree.\n"); + fmt::print(" -x, --extract Extract a file or directory from RomFS to local filesystem.\n"); fmt::print(" --icon Extract icon partition to file.\n"); fmt::print(" --nacp Extract NACP partition to file.\n"); - fmt::print(" -x, --fsdir Extract RomFS partition to directory.\n"); } void nstool::SettingsInitializer::dump_keys() const From 0832c98cc0ea435ae92e1f90f6537bb241e8874a Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 9 Nov 2021 20:44:13 +0800 Subject: [PATCH 101/119] Misc. --- src/NsoProcess.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NsoProcess.h b/src/NsoProcess.h index 5856e7a..766e421 100644 --- a/src/NsoProcess.h +++ b/src/NsoProcess.h @@ -43,7 +43,6 @@ private: void processRoMeta(); size_t decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity); - }; } \ No newline at end of file From e74dadba820323a0ab54d78ebc23a879ee456a41 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 10 Nov 2021 15:12:01 +0800 Subject: [PATCH 102/119] Update makefile. --- makefile | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/makefile b/makefile index f8c302a..cebe800 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,6 @@ # C++/C Recursive Project Makefile # (c) Jack -# Version 4 +# Version 6 (20211110) # Project Name PROJECT_NAME = nstool @@ -31,8 +31,8 @@ PROJECT_SONAME = $(PROJECT_NAME).so.$(PROJECT_SO_VER_MAJOR) PROJECT_SO_FILENAME = $(PROJECT_SONAME).$(PROJECT_SO_VER_MINOR).$(PROJECT_SO_VER_PATCH) # Project Dependencies -PROJECT_DEPEND = mbedtls lz4 toolchain fmt nintendo-hac nintendo-hac-hb nintendo-es nintendo-pki -PROJECT_DEPEND_LOCAL_DIR = libmbedtls liblz4 libtoolchain libfmt libnintendo-hac libnintendo-hac-hb libnintendo-es libnintendo-pki +PROJECT_DEPEND = nintendo-hac-hb nintendo-hac nintendo-es nintendo-pki toolchain fmt lz4 mbedtls +PROJECT_DEPEND_LOCAL_DIR = libnintendo-hac-hb libnintendo-hac libnintendo-es libnintendo-pki libtoolchain libfmt liblz4 libmbedtls # Generate compiler flags for including project include path ifneq ($(PROJECT_INCLUDE_PATH),) @@ -64,12 +64,26 @@ ifeq ($(PROJECT_PLATFORM),) endif endif +# Detect Architecture +ifeq ($(PROJECT_PLATFORM_ARCH),) + ifeq ($(PROJECT_PLATFORM), WIN32) + export PROJECT_PLATFORM_ARCH = x86_64 + else ifeq ($(PROJECT_PLATFORM), GNU) + export PROJECT_PLATFORM_ARCH = $(shell uname -m) + else ifeq ($(PROJECT_PLATFORM), MACOS) + export PROJECT_PLATFORM_ARCH = $(shell uname -m) + else + export PROJECT_PLATFORM_ARCH = x86_64 + endif +endif + # Generate platform specific compiler flags ifeq ($(PROJECT_PLATFORM), WIN32) # Windows Flags/Libs CC = x86_64-w64-mingw32-gcc CXX = x86_64-w64-mingw32-g++ WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-but-set-variable + ARCHFLAGS = INC += LIB += -static ARFLAGS = cr -o @@ -78,6 +92,7 @@ else ifeq ($(PROJECT_PLATFORM), GNU) #CC = #CXX = WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-but-set-variable + ARCHFLAGS = INC += LIB += ARFLAGS = cr -o @@ -86,18 +101,19 @@ else ifeq ($(PROJECT_PLATFORM), MACOS) #CC = #CXX = WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-private-field + ARCHFLAGS = -arch $(PROJECT_PLATFORM_ARCH) INC += LIB += ARFLAGS = rc endif # Compiler Flags -CXXFLAGS = -std=c++11 $(INC) $(WARNFLAGS) -fPIC -CFLAGS = -std=c11 $(INC) $(WARNFLAGS) -fPIC +CXXFLAGS = -std=c++11 $(INC) $(WARNFLAGS) $(ARCHFLAGS) -fPIC +CFLAGS = -std=c11 $(INC) $(WARNFLAGS) $(ARCHFLAGS) -fPIC # Object Files -SRC_OBJ = $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c))) -TESTSRC_OBJ = $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c))) +SRC_OBJ = $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cc,.o,$(wildcard $(dir)/*.cc))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c))) +TESTSRC_OBJ = $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cc,.o,$(wildcard $(dir)/*.cc))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c))) # all is the default, user should specify what the default should do # - 'static_lib' for building static library @@ -118,6 +134,10 @@ clean: clean_object_files remove_binary_dir @echo CXX $< @$(CXX) $(CXXFLAGS) -c $< -o $@ +%.o: %.cc + @echo CXX $< + @$(CXX) $(CXXFLAGS) -c $< -o $@ + # Binary Directory .PHONY: create_binary_dir create_binary_dir: From 4430f0642295acce4c3220bec6b7e4f76685cd28 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 10 Nov 2021 15:12:20 +0800 Subject: [PATCH 103/119] Address Ubuntu compiler error/warn. --- src/Settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Settings.cpp b/src/Settings.cpp index 7603e45..29326dd 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -599,7 +599,7 @@ void nstool::SettingsInitializer::determine_filetype() auto raw_data = file.pullData(0, 0x5000); #define _TYPE_PTR(st) ((st*)(raw_data.data())) -#define _ASSERT_FILE_SIZE(sz) (file.length() >= (sz)) +#define _ASSERT_FILE_SIZE(sz) (file.length() >= tc::io::IOUtil::castSizeToInt64(sz)) // do easy tests From d53d8ce786fd50c6751e0c9775f2782d2fecab3d Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 10 Nov 2021 15:12:28 +0800 Subject: [PATCH 104/119] Update dependencies. --- deps/libfmt | 2 +- deps/liblz4 | 2 +- deps/libmbedtls | 2 +- deps/libnintendo-es | 2 +- deps/libnintendo-hac | 2 +- deps/libnintendo-hac-hb | 2 +- deps/libnintendo-pki | 2 +- deps/libtoolchain | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deps/libfmt b/deps/libfmt index e91b9aa..81f1d18 160000 --- a/deps/libfmt +++ b/deps/libfmt @@ -1 +1 @@ -Subproject commit e91b9aa49f2970ec3c05a1b9307d3aa4a625ba76 +Subproject commit 81f1d186de87209642e562493bf83c1763eb3e5e diff --git a/deps/liblz4 b/deps/liblz4 index 555e430..50e8cef 160000 --- a/deps/liblz4 +++ b/deps/liblz4 @@ -1 +1 @@ -Subproject commit 555e43086b348a640179ddf2ecd86d8503c12b85 +Subproject commit 50e8cef9587eaabbdcac594cc1951f22152c04ff diff --git a/deps/libmbedtls b/deps/libmbedtls index b3a3fad..ccbdff4 160000 --- a/deps/libmbedtls +++ b/deps/libmbedtls @@ -1 +1 @@ -Subproject commit b3a3fade84185460d4802f5584d10c9a4dc2dd02 +Subproject commit ccbdff42ed7baf65e9e0ff4cdd8e49561f72435b diff --git a/deps/libnintendo-es b/deps/libnintendo-es index e2aa92f..92c0383 160000 --- a/deps/libnintendo-es +++ b/deps/libnintendo-es @@ -1 +1 @@ -Subproject commit e2aa92fd2d59571f4f55d789eb6e5e2c2f0ab6c7 +Subproject commit 92c03837df601d388d09e07914be120f10ccccfa diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 9e246f7..2c94f3c 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 9e246f7b121e526b8b42fdd81adf558f76b515eb +Subproject commit 2c94f3c51f0c4266eca0c7e23316e511567c28fc diff --git a/deps/libnintendo-hac-hb b/deps/libnintendo-hac-hb index 321243a..1589998 160000 --- a/deps/libnintendo-hac-hb +++ b/deps/libnintendo-hac-hb @@ -1 +1 @@ -Subproject commit 321243a4ccf07e61460f86e90ae635f8b94c6474 +Subproject commit 15899984b54a89d2aa2a61c360d911fec3f812ba diff --git a/deps/libnintendo-pki b/deps/libnintendo-pki index 5a763cb..7ba4b27 160000 --- a/deps/libnintendo-pki +++ b/deps/libnintendo-pki @@ -1 +1 @@ -Subproject commit 5a763cb9ba89de94fe39b4f0aecd1cc963dad0f0 +Subproject commit 7ba4b27e685e058c80fb05238b6c3e97e0f22c7d diff --git a/deps/libtoolchain b/deps/libtoolchain index feb5d43..63f70b6 160000 --- a/deps/libtoolchain +++ b/deps/libtoolchain @@ -1 +1 @@ -Subproject commit feb5d43cca502429af4e4a92633407dcb2dde80b +Subproject commit 63f70b68b5c9c60e3c899844919c50d90f414376 From 0249957fad7b9d393ad4e6064f0af6d4f26b1656 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 10 Nov 2021 15:40:19 +0800 Subject: [PATCH 105/119] Update makefile. --- makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefile b/makefile index cebe800..33eb3d2 100644 --- a/makefile +++ b/makefile @@ -165,13 +165,13 @@ shared_lib: $(SRC_OBJ) create_binary_dir # Build Program program: $(SRC_OBJ) create_binary_dir @echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME) - @$(CXX) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)" + @$(CXX) $(ARCHFLAGS) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)" # Build Test Program test_program: $(TESTSRC_OBJ) $(SRC_OBJ) create_binary_dir ifneq ($(PROJECT_TESTSRC_PATH),) @echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME)_test - @$(CXX) $(TESTSRC_OBJ) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)_test" + @$(CXX) $(ARCHFLAGS) $(TESTSRC_OBJ) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)_test" endif # Documentation From d0e1229f6d1a429bb32f97d0a8fe1727f8f1e29a Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 10 Nov 2021 15:40:29 +0800 Subject: [PATCH 106/119] Update dependencies. --- deps/libfmt | 2 +- deps/liblz4 | 2 +- deps/libmbedtls | 2 +- deps/libnintendo-es | 2 +- deps/libnintendo-hac | 2 +- deps/libnintendo-hac-hb | 2 +- deps/libnintendo-pki | 2 +- deps/libtoolchain | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deps/libfmt b/deps/libfmt index 81f1d18..53d084c 160000 --- a/deps/libfmt +++ b/deps/libfmt @@ -1 +1 @@ -Subproject commit 81f1d186de87209642e562493bf83c1763eb3e5e +Subproject commit 53d084cc0c6ea61bbb535a873299b0ae5ff9a05d diff --git a/deps/liblz4 b/deps/liblz4 index 50e8cef..b40b464 160000 --- a/deps/liblz4 +++ b/deps/liblz4 @@ -1 +1 @@ -Subproject commit 50e8cef9587eaabbdcac594cc1951f22152c04ff +Subproject commit b40b46406e87a753328abfda3b53dfabd2408da2 diff --git a/deps/libmbedtls b/deps/libmbedtls index ccbdff4..e9e56bb 160000 --- a/deps/libmbedtls +++ b/deps/libmbedtls @@ -1 +1 @@ -Subproject commit ccbdff42ed7baf65e9e0ff4cdd8e49561f72435b +Subproject commit e9e56bb773111f831af8dd36ad74de73b0c31aa2 diff --git a/deps/libnintendo-es b/deps/libnintendo-es index 92c0383..654d281 160000 --- a/deps/libnintendo-es +++ b/deps/libnintendo-es @@ -1 +1 @@ -Subproject commit 92c03837df601d388d09e07914be120f10ccccfa +Subproject commit 654d281c485454d240bd54cd4c173a0281da4f2c diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index 2c94f3c..b633fa9 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit 2c94f3c51f0c4266eca0c7e23316e511567c28fc +Subproject commit b633fa9bfd522605631c072f15493d67a7beb09d diff --git a/deps/libnintendo-hac-hb b/deps/libnintendo-hac-hb index 1589998..1716a72 160000 --- a/deps/libnintendo-hac-hb +++ b/deps/libnintendo-hac-hb @@ -1 +1 @@ -Subproject commit 15899984b54a89d2aa2a61c360d911fec3f812ba +Subproject commit 1716a7266e8b4673d39df7ddad9f7e71465b47ca diff --git a/deps/libnintendo-pki b/deps/libnintendo-pki index 7ba4b27..07381ab 160000 --- a/deps/libnintendo-pki +++ b/deps/libnintendo-pki @@ -1 +1 @@ -Subproject commit 7ba4b27e685e058c80fb05238b6c3e97e0f22c7d +Subproject commit 07381abd256230eee140fc85c4c34e0ec8e17031 diff --git a/deps/libtoolchain b/deps/libtoolchain index 63f70b6..b2c991a 160000 --- a/deps/libtoolchain +++ b/deps/libtoolchain @@ -1 +1 @@ -Subproject commit 63f70b68b5c9c60e3c899844919c50d90f414376 +Subproject commit b2c991a4fdafa0798c183a2ceac496db17e7c559 From 20603d11774769339cd0e9684e33c218bd8e0b57 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 10 Nov 2021 15:41:02 +0800 Subject: [PATCH 107/119] Update build_master.yml for macOS arm64 and win x86 builds. --- .github/workflows/build_master.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build_master.yml b/.github/workflows/build_master.yml index db07cda..3f705e5 100644 --- a/.github/workflows/build_master.yml +++ b/.github/workflows/build_master.yml @@ -14,19 +14,24 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - dist: [ubuntu_x86_64, macos_x86_64] + dist: [ubuntu_x86_64, macos_x86_64, macos_arm64] prog: [nstool] include: - dist: ubuntu_x86_64 os: ubuntu-latest + arch: x86_64 - dist: macos_x86_64 os: macos-latest + arch: x86_64 + - dist: macos_arm64 + os: macos-latest + arch: arm64 steps: - uses: actions/checkout@v1 - name: Clone submodules run: git submodule init && git submodule update - name: Compile ${{ matrix.prog }} - run: make deps && make + run: make PROJECT_PLATFORM_ARCH=${{ matrix.dist_arch }} deps all - uses: actions/upload-artifact@v2 with: name: ${{ matrix.prog }}-${{ matrix.dist }} @@ -36,13 +41,19 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - dist: [win_x86_64] + dist: [win_x64, win_x86] prog: [nstool] include: - - dist: win_x86_64 + - dist: win_x64 os: windows-latest platform: x64 configuration: Release + build_path: x64\Release + - dist: win_x86 + os: windows-latest + platform: x86 + configuration: Release + build_path: Release steps: - uses: actions/checkout@v1 - name: Add msbuild to PATH @@ -54,5 +65,5 @@ jobs: - uses: actions/upload-artifact@v2 with: name: ${{ matrix.prog }}-${{ matrix.dist }} - path: .\build\visualstudio\${{ matrix.platform }}\${{ matrix.configuration }}\${{ matrix.prog }}.exe + path: .\build\visualstudio\${{ matrix.build_path }}\${{ matrix.prog }}.exe From 9222a7dcb72b68363c04435224b43568964f00aa Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 10 Nov 2021 15:42:38 +0800 Subject: [PATCH 108/119] Fix typo in build_master.yml --- .github/workflows/build_master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_master.yml b/.github/workflows/build_master.yml index 3f705e5..ff9020d 100644 --- a/.github/workflows/build_master.yml +++ b/.github/workflows/build_master.yml @@ -31,7 +31,7 @@ jobs: - name: Clone submodules run: git submodule init && git submodule update - name: Compile ${{ matrix.prog }} - run: make PROJECT_PLATFORM_ARCH=${{ matrix.dist_arch }} deps all + run: make PROJECT_PLATFORM_ARCH=${{ matrix.arch }} deps all - uses: actions/upload-artifact@v2 with: name: ${{ matrix.prog }}-${{ matrix.dist }} From c5f9b8a45c02232860552886539336ca1e6c0a2d Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 10 Nov 2021 16:08:33 +0800 Subject: [PATCH 109/119] Fix typo in readme --- README.md | 2 +- src/Settings.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f89713b..c4ecbf5 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Usage: nstool [options... ] INI (Initial Program Bundle) nstool [--kipdir ] - --kipdir Extract embedded Inital Programs to directory. + --kipdir Extract embedded Initial Programs to directory. ASET (Homebrew Asset Blob) nstool [--fstree] [-x [] ] [--icon --nacp ] diff --git a/src/Settings.cpp b/src/Settings.cpp index 29326dd..cea7e41 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -747,7 +747,7 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --insttype Specify instruction type [64bit|32bit] (64bit is assumed).\n"); fmt::print("\n INI (Initial Program Bundle)\n"); fmt::print(" {:s} [--kipdir ] \n", BIN_NAME); - fmt::print(" --kipdir Extract embedded Inital Programs to directory.\n"); + fmt::print(" --kipdir Extract embedded Initial Programs to directory.\n"); fmt::print("\n ASET (Homebrew Asset Blob)\n"); fmt::print(" {:s} [--fstree] [-x [] ] [--icon --nacp ] \n", BIN_NAME); fmt::print(" --fstree Print RomFS filesystem tree.\n"); From a7d5ae6375f80dbe204241aa236ddb45c6f432c3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 10 Nov 2021 16:22:06 +0800 Subject: [PATCH 110/119] Update dependencies. --- deps/libnintendo-es | 2 +- deps/libnintendo-hac | 2 +- deps/libnintendo-hac-hb | 2 +- deps/libnintendo-pki | 2 +- deps/libtoolchain | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deps/libnintendo-es b/deps/libnintendo-es index 654d281..af4d79a 160000 --- a/deps/libnintendo-es +++ b/deps/libnintendo-es @@ -1 +1 @@ -Subproject commit 654d281c485454d240bd54cd4c173a0281da4f2c +Subproject commit af4d79ad2aca410ce2d451456023b0a03904ceeb diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac index b633fa9..e7d93ce 160000 --- a/deps/libnintendo-hac +++ b/deps/libnintendo-hac @@ -1 +1 @@ -Subproject commit b633fa9bfd522605631c072f15493d67a7beb09d +Subproject commit e7d93cea7c7bac93661c9ef6500279db19c264a0 diff --git a/deps/libnintendo-hac-hb b/deps/libnintendo-hac-hb index 1716a72..42abe9b 160000 --- a/deps/libnintendo-hac-hb +++ b/deps/libnintendo-hac-hb @@ -1 +1 @@ -Subproject commit 1716a7266e8b4673d39df7ddad9f7e71465b47ca +Subproject commit 42abe9b9a90ef05ea13df6caf320ac4f50e34537 diff --git a/deps/libnintendo-pki b/deps/libnintendo-pki index 07381ab..11b9902 160000 --- a/deps/libnintendo-pki +++ b/deps/libnintendo-pki @@ -1 +1 @@ -Subproject commit 07381abd256230eee140fc85c4c34e0ec8e17031 +Subproject commit 11b99025b11862b0828a186bd462b0097e341da7 diff --git a/deps/libtoolchain b/deps/libtoolchain index b2c991a..5966167 160000 --- a/deps/libtoolchain +++ b/deps/libtoolchain @@ -1 +1 @@ -Subproject commit b2c991a4fdafa0798c183a2ceac496db17e7c559 +Subproject commit 5966167aa5065c0e582bfbca151e28db580e972f From 93fc2e29bc56f7316dd6a96bf20038a03101cf5f Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 13 Nov 2021 15:53:36 +0800 Subject: [PATCH 111/119] Update SWITCH_KEYS.md --- SWITCH_KEYS.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/SWITCH_KEYS.md b/SWITCH_KEYS.md index 1ec2440..75898cf 100644 --- a/SWITCH_KEYS.md +++ b/SWITCH_KEYS.md @@ -11,20 +11,20 @@ Some switch files formats feature encryption and or cryptographic signatures. In This keyset file can be provided via the command line (refer to usage for details). ## Format -The following keys are recognised (## represents a hexadecimal number between 00 and FF): +The following keys are recognised (## represents the key revision, a hexadecimal number between 00 and FF): ``` ; Key Sources -master_key_## : Master key, used to derive other keys. (0x10 bytes) -aes_kek_generation_source : Used to derive other aes-keks. (0x10 bytes) -aes_key_generation_source : Used to derive other aes-keys. (0x10 bytes) -package2_key_source : Used with master_key_## to derive package2_key_##. (0x10 bytes) -ticket_commonkey_source : Used with master_key_## to derive ticket_commonkey_##. (0x10 bytes) -nca_header_kek_source : Used with master_key_00, aes_kek_generation_source and aes_key_generation_source to generate nca_header_kek. (0x10 bytes) -nca_header_key_source : Used with nca_header_kek to generate nca_header_key. (0x20 bytes) -nca_body_keak_application_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_body_keak_application_##. (0x10 bytes) -nca_body_keak_ocean_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_body_keak_ocean_##. (0x10 bytes) -nca_body_keak_system_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_body_keak_system_##. (0x10 bytes) +master_key_## : Master key, used to derive other keys. (0x10 bytes) +aes_kek_generation_source : Used to derive other aes-keks. (0x10 bytes) +aes_key_generation_source : Used to derive other aes-keys. (0x10 bytes) +package2_key_source : Used with master_key_## to derive package2_key_##. (0x10 bytes) +ticket_commonkey_source : Used with master_key_## to derive ticket_commonkey_##. (0x10 bytes) +nca_header_kek_source : Used with master_key_00, aes_kek_generation_source and aes_key_generation_source to generate nca_header_kek. (0x10 bytes) +nca_header_key_source : Used with nca_header_kek to generate nca_header_key. (0x20 bytes) +nca_key_area_key_application_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_key_area_key_application_##. (0x10 bytes) +nca_key_area_key_ocean_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_key_area_key_ocean_##. (0x10 bytes) +nca_key_area_key_system_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_key_area_key_system_##. (0x10 bytes) ; Package1 keys package1_key_## : AES128 Key (0x10 bytes) From 0cb9608590a8741d6d8e81e90639a06c1ece10b4 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 13 Nov 2021 15:54:06 +0800 Subject: [PATCH 112/119] Change names for NCA content keys internally. --- src/Settings.cpp | 16 ++++++++-------- src/Settings.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Settings.cpp b/src/Settings.cpp index cea7e41..d0386c7 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -433,8 +433,8 @@ nstool::SettingsInitializer::SettingsInitializer(const std::vector& mShowLayout(false), mShowKeydata(false), mVerbose(false), - mTitleKey(), - mBodyKey(), + mNcaEncryptedContentKey(), + mNcaContentKey(), mTikPath(), mCertPath() { @@ -486,8 +486,8 @@ nstool::SettingsInitializer::SettingsInitializer(const std::vector& // generate keybag opt.keybag = KeyBagInitializer(opt.is_dev, mKeysetPath, mTikPath, mCertPath); - opt.keybag.fallback_enc_content_key = mTitleKey; - opt.keybag.fallback_content_key = mBodyKey; + opt.keybag.fallback_enc_content_key = mNcaEncryptedContentKey; + opt.keybag.fallback_content_key = mNcaContentKey; // dump keys if requires if (mShowKeydata) // but not opt.cli_output_mode.show_keydata, since this that enabled by toggling -v,--verbose, personally I don't think a summary of imported keydata should be included in verbose output. @@ -549,8 +549,8 @@ void nstool::SettingsInitializer::parse_args(const std::vector& arg // get user-provided keydata opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(mKeysetPath, {"-k", "--keyset"}))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamAesKeyOptionHandler(mTitleKey, {"--titlekey"}))); - opts.registerOptionHandler(std::shared_ptr(new SingleParamAesKeyOptionHandler(mBodyKey, {"--bodykey"}))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamAesKeyOptionHandler(mNcaEncryptedContentKey, {"--titlekey"}))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamAesKeyOptionHandler(mNcaContentKey, {"--contentkey", "--bodykey"}))); opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(mTikPath, {"--tik"}))); opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(mCertPath, {"--cert"}))); @@ -732,8 +732,8 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" {:s} [--fstree] [-x [] ] [--bodykey --titlekey -tik ] <.nca file>\n", BIN_NAME); fmt::print(" --fstree Print filesystem tree.\n"); fmt::print(" -x, --extract Extract a file or directory to local filesystem.\n"); - fmt::print(" --titlekey Specify title key extracted from ticket.\n"); - fmt::print(" --bodykey Specify body encryption key.\n"); + fmt::print(" --titlekey Specify (encrypted) title key extracted from ticket.\n"); + fmt::print(" --contentkey Specify content key.\n"); fmt::print(" --tik Specify ticket to source title key.\n"); fmt::print(" --cert Specify certificate chain to verify ticket.\n"); fmt::print(" --part0 Extract partition \"0\" to directory. (Alias for \"-x /0 \")\n"); diff --git a/src/Settings.h b/src/Settings.h index b29fa12..2406f54 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -133,8 +133,8 @@ private: bool mVerbose; tc::Optional mKeysetPath; - tc::Optional mTitleKey; - tc::Optional mBodyKey; + tc::Optional mNcaEncryptedContentKey; + tc::Optional mNcaContentKey; tc::Optional mTikPath; tc::Optional mCertPath; From 6a94885cee37ff9c933ee162729fe3496e69b469 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 13 Nov 2021 15:54:18 +0800 Subject: [PATCH 113/119] Begin Re-draft of README --- README.md | 247 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 184 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index c4ecbf5..7ca05d6 100644 --- a/README.md +++ b/README.md @@ -2,80 +2,201 @@ General purpose reading/extraction tool for Nintendo Switch file formats. ## Supported File Formats -* Meta (.npdm) -* PartitionFS (and Sha256PartitionFS) (includes raw .nsp) -* RomFS -* NX GameCard Image (.xci) +* PartitionFs (`PFS0`) (.pfs0) +* Sha256PartitionFs (`HFS0`) (.hfs0) +* RomFs (.romfs) +* Nintendo Submission Package (.nsp) * Nintendo Content Archive (.nca) +* NX GameCard Image (.xci) +* Meta (`META`) (.npdm) +* Nintendo Application Control Property (.nacp) * Content Metadata (.cnmt) +* ES Certificate (.cert) +* ES Ticket (v2 only) (.tik) * Nintendo Shared Object (`NSO0`) (.nso) * Nintendo Relocatable Object (`NRO0`) (.nro) * Initial Program Bundle (`INI1`) (.ini) * Initial Program (`KIP1`) (.kip) -* Nintendo Application Control Property (.nacp) -* ES Certificate (.cert) -* ES Ticket (v2 only) (.tik) + # Usage +## General usage +The default mode of NSTool is to show general information about a file. + +To display general information the usage is as follows: ``` -Usage: nstool [options... ] - - General Options: - -d, --dev Use devkit keyset. - -k, --keyset Specify keyset file. - -t, --type Specify input file type. [xci, pfs, romfs, nca, meta, cnmt, nso, nro, ini, kip, nacp, aset, cert, tik] - -y, --verify Verify file. - - Output Options: - --showkeys Show keys generated. - --showlayout Show layout metadata. - -v, --verbose Verbose output. - - PFS0/HFS0 (PartitionFs), RomFs, NSP (Nintendo Submission Package) - nstool [--fstree] [-x [] ] - --fstree Print filesystem tree. - -x, --extract Extract a file or directory to local filesystem. - - XCI (GameCard Image) - nstool [--fstree] [-x [] ] <.xci file> - --fstree Print filesystem tree. - -x, --extract Extract a file or directory to local filesystem. - --update Extract "update" partition to directory. (Alias for "-x /update ") - --logo Extract "logo" partition to directory. (Alias for "-x /logo ") - --normal Extract "normal" partition to directory. (Alias for "-x /normal ") - --secure Extract "secure" partition to directory. (Alias for "-x /secure ") - - NCA (Nintendo Content Archive) - nstool [--fstree] [-x [] ] [--bodykey --titlekey -tik ] <.nca file> - --fstree Print filesystem tree. - -x, --extract Extract a file or directory to local filesystem. - --titlekey Specify title key extracted from ticket. - --bodykey Specify body encryption key. - --tik Specify ticket to source title key. - --cert Specify certificate chain to verify ticket. - --part0 Extract partition "0" to directory. (Alias for "-x /0 ") - --part1 Extract partition "1" to directory. (Alias for "-x /1 ") - --part2 Extract partition "2" to directory. (Alias for "-x /2 ") - --part3 Extract partition "3" to directory. (Alias for "-x /3 ") - - NSO (Nintendo Shared Object), NRO (Nintendo Relocatable Object) - nstool [--listapi --listsym] [--insttype ] - --listapi Print SDK API List. - --listsym Print Code Symbols. - --insttype Specify instruction type [64bit|32bit] (64bit is assumed). - - INI (Initial Program Bundle) - nstool [--kipdir ] - --kipdir Extract embedded Initial Programs to directory. - - ASET (Homebrew Asset Blob) - nstool [--fstree] [-x [] ] [--icon --nacp ] - --fstree Print RomFS filesystem tree. - -x, --extract Extract a file or directory from RomFS to local filesystem. - --icon Extract icon partition to file. - --nacp Extract NACP partition to file. +nstool some_file.bin ``` +However not all information is shown in this mode; file-layout, key data and properties set to default values are omitted. + +## Alternative output modes +To output file-layout information, use the `--showlayout` option: +``` +nstool --showlayout some_file.bin +``` + +To output key data generation and selection, use the `--showkeys` option: +``` +nstool --showkeys some_file.bin +``` + +To output all information, enable the verbose output mode with the `-v` or `--verbose` option: +``` +nstool -v some_file.bin +``` + +## Specify File Type +NSTool will in most cases correctly identify the file type. However you can override this and manually specify the file type with the `-t` or `--intype` option: +``` +nstool -t cnmt some_file.bin +``` +In that example `cnmt` was selected, NSTool would process the file as `Content Metadata`. See below for a list of supported file type codes: +| Code | Description | +| ----------- | --------------- | +| gc, xci | NX GameCard Image | +| nsp | Nintendo Submission Package | +| pfs | PartitionFs | +| hfs | Sha256PartitionFs | +| romfs | RomFs | +| nca | Nintendo Content Archive | +| meta, npdm | Meta (.npdm) | +| cnmt | Content Metadata | +| nso | Nintendo Shared Object | +| nro | Nintendo Relocatable Object | +| ini | Initial Program Bundle | +| kip | Initial Program | +| nacp | Nintendo Application Control Property | +| cert | ES Certificate | +| tik | ES Ticket | +| aset, asset | Homebrew NRO Asset Binary | + +## Validate Input File +Some file types have signatures/hashes/fields that can be validated by NSTool, but this mode isn't enabled by default. + +To validate files with NSTool, enable the verify mode with the `-y` or `--verify` option: +``` +nstool -y some_file.bin +``` + +See the below table for file types that support optional validation: +| File Type | Validation | Comments | +| --------- | ---------- | -------- | +| ES Certificate | Signature | If certificate is part of a certificate chain it will validate it as part of that chain. `Root` signed certificates are verified with user supplied `Root` public key. | +| ES Ticket | Signature | If the user specifies a certificate chain with `--cert` option, the ticket will be verified against that certificate chain. | +| NX GameCard Image | XCI Header Signature, HFS0 Hashes | XCI header signature is verified with user supplied `XCI Header` public key. | +| META | AccessControlInfo fields, AccessControlInfoDesc signature | AccessControlInfo fields are validated against the AccessControlInfoDesc. AccessControlInfoDesc signature is verfied with the appropriate user supplied `ACID` public key. | +| NCA | Header Signature[0], Header Signature[1] | Header Signature[0] is verified with the appropriate user supplied `NCA Header` public key. Header Signature[1] is verified only in Program titles, by retrieving the with public key from the AccessControlInfoDesc stored in the `code` partition. | + +* As of Nintendo Switch Firmware 9.0.0, Nintendo retroactively added key generations for some public keys, including `NCA Header` and `ACID` public keys, so the various generations for these public keys will have to be supplied by the user +* As of NSTool v1.6.0 the public key(s) for `Root Certificate`, `XCI Header`, `ACID` and `NCA Header` are built-in, and will be used if the user does not supply the public key in a key file. + +## Encrypted Files +Some Nintendo Switch files are partially or completely encrypted. These require the user to supply the encryption keys to NSTool so that it can process them. +### NX GameCard Image +The `GameCard ExtendedHeader` is encrypted with one of 8 keys, specified by the `KekIndex` in the `GameCard Header`. +It isn't required to extract game data, it just contains metadata. + +Only two keys are currently defined: +| KeyIndex | Name | Description | +| ----------- | --------------- | ----------- | +| 00 | Production | Usually selected for prod images. Some dev images use this key index. | +| 01 | Development | Usually selected for dev images. This was changed from key index 00 at some point. | + +In order to read the XCI Extended Header, the header key(s) must be defined in `prod.keys`/`dev.keys`. + +Define these keys in both `prod.keys` and `dev.keys` (Prod and dev share the same keydata). +``` +xci_header_key_00 = <32 char AES128 key here> +xci_header_key_01 = <32 char AES128 key here> +``` + +### Nintendo Content Archive +Nintendo Content Archive (NCA) files have both an encrypted header and content. The encrypted header determines the layout/format/encryption method of the content, which contains the game data. + +In order to read the NCA header, the header key must be defined in `prod.keys`/`dev.keys`. + +This can be explicitly: +``` +nca_header_key = <64 char AES128-XTS key-data here> +``` +Or allow NSTool to derive it from key sources: +``` +master_key_00 = <32 char AES128 key-data here> +aes_kek_generation_source = <32 char AES128 key-data here> +aes_key_generation_source = <32 char AES128 key-data here> +nca_header_kek_source = <32 char AES128 key-data here> +nca_header_key_source = <64 char AES128 key-data here> +``` + +In order to read the NCA content, the content key must be determined. Unlike the header key which is fixed, each NCA will have a unique content key. + +Content keys are either: +1) "Internal", where they are encrypted the NCA Header KeyArea +2) "External", where they are encrypted in an external Ticket file (.tik) (external content keys are sometimes called title keys) + +#### Internal Content Key +Decrypting the content key from the NCA Header Key Area requires the appropriate `nca_key_area_key` to be defined in `prod.keys`/`dev.keys`. +However for security reasons Nintendo revises this key periodically, and within each key revision there are 3 separate keys for different categories of applications. + +It's best to define as many of these as possible, to reduce the number of times you need to edit the keyfiles. + +So for a given key revision these key area keys can be defined explicitly (`##` represents the key revision in hexadecimal): +``` +nca_key_area_key_application_## = <32 char AES128 key-data here> +nca_key_area_key_ocean_## = <32 char AES128 key-data here> +nca_key_area_key_system_## = <32 char AES128 key-data here> +``` +Or allow NSTool to derive them from key sources: (`##` represents the key revision in hexadecimal): +``` +master_key_## = <32 char AES128 key-data here> +aes_kek_generation_source = <32 char AES128 key-data here> +aes_key_generation_source = <32 char AES128 key-data here> +nca_key_area_key_application_source = <32 char AES128 key-data here> +nca_key_area_key_ocean_source = <32 char AES128 key-data here> +nca_key_area_key_system_source = <32 char AES128 key-data here> +``` + +#### External Content Key +For NCAs that use an external content key, the user must supplied the key to NSTool. + +Most NCAs that use an external content key will be bundled with a ticket file (*.tik) that contains the content key in an encrypted form. + +The ticket can be supplied by the user using the `--tik` option: +``` +nstool --tik <32 char rightsid>.tik <32 char contentid>.nca +``` +This however requires the the appropriate commonkey to be defined in `prod.keys`/`dev.keys` to decrypt the content key in the ticket. However for security reasons Nintendo revises this key periodically. So it's best to define as many of these as possible, to reduce the number of times you need to edit the keyfiles. + +So for a given key revision the common key can be defined explicitly (`##` represents the key revision in hexadecimal): +``` +ticket_commonkey_## = <32 char AES128 key-data here> +``` +Or allow NSTool to derive them from key sources: (`##` represents the key revision in hexadecimal): +``` +master_key_## = <32 char AES128 key-data here> +ticket_commonkey_source = <32 char AES128 key-data here> +``` + +Alternatively you can supply the raw encrypted content key (also called a title key) directly with the `--titlekey` option: +``` +nstool --titlekey <32 char AES128 key-data here> <32 char contentid>.nca +``` + +It is also possible to supply the decrypted content key directly with the `--contentkey` option: +``` +nstool --contentkey <32 char AES128 key-data here> <32 char contentid>.nca +``` + +##### Scene tickets +Please note that "Scene" tickets have been known to have errors. If you have issues using the `--tik` option, try passing the raw encrypted titlekey directly with the `--titlekey` option. The titlekey can be found by reading the ticket with NSTool: +``` +nstool <32 char rightsid>.tik +``` + +##### Dealing with ticket errors +If the ticket is personalised (encrypted with console unique RSA key), NSTool will not support it. You will need to use extract the title key with another tool and pass the encrypted title key directly with the `--titlekey` option. + # External Keys NSTool doesn't embed any keys that are copyright protected. However keys can be imported via various keyset files. From d1d8850818e704d474d184981a0c3e2db29f44e2 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 14 Nov 2021 07:11:34 +0800 Subject: [PATCH 114/119] More readme drafting --- README.md | 23 ++++++++++++++++------- SWITCH_KEYS.md | 5 ++++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7ca05d6..1db29e3 100644 --- a/README.md +++ b/README.md @@ -88,11 +88,22 @@ See the below table for file types that support optional validation: | META | AccessControlInfo fields, AccessControlInfoDesc signature | AccessControlInfo fields are validated against the AccessControlInfoDesc. AccessControlInfoDesc signature is verfied with the appropriate user supplied `ACID` public key. | | NCA | Header Signature[0], Header Signature[1] | Header Signature[0] is verified with the appropriate user supplied `NCA Header` public key. Header Signature[1] is verified only in Program titles, by retrieving the with public key from the AccessControlInfoDesc stored in the `code` partition. | -* As of Nintendo Switch Firmware 9.0.0, Nintendo retroactively added key generations for some public keys, including `NCA Header` and `ACID` public keys, so the various generations for these public keys will have to be supplied by the user +* As of Nintendo Switch Firmware 9.0.0, Nintendo retroactively added key generations for some public keys, including `NCA Header` and `ACID` public keys, so the various generations for these public keys will have to be supplied by the user. * As of NSTool v1.6.0 the public key(s) for `Root Certificate`, `XCI Header`, `ACID` and `NCA Header` are built-in, and will be used if the user does not supply the public key in a key file. +## DevKit Mode +Files generated for `Production` use different (for the most part) encryption/signing keys than files generated for `Development`. NSTool will select `Production` encryption/signing keys by default. +When handling files intended for developer consoles (e.g. systemupdaters, devtools, test builds, etc), you should enable developer mode with the `-d`, `--dev` option: +``` +nstool -d some_file.bin +``` + +## Extract Files +Some file types have some internal file system that can be displayed or extracted + ## Encrypted Files -Some Nintendo Switch files are partially or completely encrypted. These require the user to supply the encryption keys to NSTool so that it can process them. +Some Nintendo Switch files are partially or completely encrypted. These require the user to supply the encryption keys to NSTool so that it can process them. Follow the below advice for what keys are required and how to supply them to NSTool. + ### NX GameCard Image The `GameCard ExtendedHeader` is encrypted with one of 8 keys, specified by the `KekIndex` in the `GameCard Header`. It isn't required to extract game data, it just contains metadata. @@ -103,9 +114,7 @@ Only two keys are currently defined: | 00 | Production | Usually selected for prod images. Some dev images use this key index. | | 01 | Development | Usually selected for dev images. This was changed from key index 00 at some point. | -In order to read the XCI Extended Header, the header key(s) must be defined in `prod.keys`/`dev.keys`. - -Define these keys in both `prod.keys` and `dev.keys` (Prod and dev share the same keydata). +In order to read the XCI Extended Header, the header key(s) must be defined in `prod.keys`/`dev.keys` (Prod and dev share the same keydata). ``` xci_header_key_00 = <32 char AES128 key here> xci_header_key_01 = <32 char AES128 key here> @@ -188,13 +197,13 @@ It is also possible to supply the decrypted content key directly with the `--con nstool --contentkey <32 char AES128 key-data here> <32 char contentid>.nca ``` -##### Scene tickets +##### Scene Tickets Please note that "Scene" tickets have been known to have errors. If you have issues using the `--tik` option, try passing the raw encrypted titlekey directly with the `--titlekey` option. The titlekey can be found by reading the ticket with NSTool: ``` nstool <32 char rightsid>.tik ``` -##### Dealing with ticket errors +##### Personalised Tickets If the ticket is personalised (encrypted with console unique RSA key), NSTool will not support it. You will need to use extract the title key with another tool and pass the encrypted title key directly with the `--titlekey` option. # External Keys diff --git a/SWITCH_KEYS.md b/SWITCH_KEYS.md index 75898cf..24b8e90 100644 --- a/SWITCH_KEYS.md +++ b/SWITCH_KEYS.md @@ -8,7 +8,10 @@ If a keyset file is located in ___$HOME/.switch/___ it will be loaded automatica # General Keys (prod.keys and dev.keys) Some switch files formats feature encryption and or cryptographic signatures. In order to process these file formats, some keys are required. These keys can be supplied via a keyfile: ___prod.keys___ (or ___dev.keys___ for devkit variants). -This keyset file can be provided via the command line (refer to usage for details). +This keyset file can be provided via the command line, use the `-k` or `--keyset` option: +``` +nstool -k prod.keys some_file.bin +``` ## Format The following keys are recognised (## represents the key revision, a hexadecimal number between 00 and FF): From 1e2902202bb780927720c7b66d1f5589936d91b0 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 14 Nov 2021 07:14:58 +0800 Subject: [PATCH 115/119] Standardise formatting of RomFs and PartitionFs names. --- src/AssetProcess.cpp | 2 +- src/GameCardProcess.cpp | 2 +- src/PfsProcess.cpp | 2 +- src/RomfsProcess.cpp | 2 +- src/Settings.cpp | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp index fc6073f..6f52bde 100644 --- a/src/AssetProcess.cpp +++ b/src/AssetProcess.cpp @@ -133,7 +133,7 @@ void nstool::AssetProcess::displayHeader() fmt::print(" NACP:\n"); fmt::print(" Offset: 0x{:x}\n", mHdr.getNacpInfo().offset); fmt::print(" Size: 0x{:x}\n", mHdr.getNacpInfo().size); - fmt::print(" RomFS:\n"); + fmt::print(" RomFs:\n"); fmt::print(" Offset: 0x{:x}\n", mHdr.getRomfsInfo().offset); fmt::print(" Size: 0x{:x}\n", mHdr.getRomfsInfo().size); } diff --git a/src/GameCardProcess.cpp b/src/GameCardProcess.cpp index 739dd2d..929f720 100644 --- a/src/GameCardProcess.cpp +++ b/src/GameCardProcess.cpp @@ -271,7 +271,7 @@ void nstool::GameCardProcess::processRootPfs() FsProcess fs_proc; fs_proc.setInputFileSystem(gc_vfs); - fs_proc.setFsFormatName("PartitionFS"); + fs_proc.setFsFormatName("PartitionFs"); fs_proc.setFsProperties({ fmt::format("Type: Nested HFS0"), fmt::format("DirNum: {:d}", gc_vfs_meta.dir_entries.empty() ? 0 : gc_vfs_meta.dir_entries.size() - 1), // -1 to not include root directory diff --git a/src/PfsProcess.cpp b/src/PfsProcess.cpp index 6099951..c7be0ac 100644 --- a/src/PfsProcess.cpp +++ b/src/PfsProcess.cpp @@ -17,7 +17,7 @@ nstool::PfsProcess::PfsProcess() : mFileSystem(), mFsProcess() { - mFsProcess.setFsFormatName("PartitionFS"); + mFsProcess.setFsFormatName("PartitionFs"); } void nstool::PfsProcess::process() diff --git a/src/RomfsProcess.cpp b/src/RomfsProcess.cpp index 1c5bfe1..6fb3d7b 100644 --- a/src/RomfsProcess.cpp +++ b/src/RomfsProcess.cpp @@ -15,7 +15,7 @@ nstool::RomfsProcess::RomfsProcess() : mFileSystem(), mFsProcess() { - mFsProcess.setFsFormatName("RomFS"); + mFsProcess.setFsFormatName("RomFs"); } void nstool::RomfsProcess::process() diff --git a/src/Settings.cpp b/src/Settings.cpp index d0386c7..82487c6 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -750,8 +750,8 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --kipdir Extract embedded Initial Programs to directory.\n"); fmt::print("\n ASET (Homebrew Asset Blob)\n"); fmt::print(" {:s} [--fstree] [-x [] ] [--icon --nacp ] \n", BIN_NAME); - fmt::print(" --fstree Print RomFS filesystem tree.\n"); - fmt::print(" -x, --extract Extract a file or directory from RomFS to local filesystem.\n"); + fmt::print(" --fstree Print RomFs filesystem tree.\n"); + fmt::print(" -x, --extract Extract a file or directory from RomFs to local filesystem.\n"); fmt::print(" --icon Extract icon partition to file.\n"); fmt::print(" --nacp Extract NACP partition to file.\n"); } From 2ab44c8a8c3651294fd6e4ffbb9f3cce083bf6e4 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 14 Nov 2021 12:20:15 +0800 Subject: [PATCH 116/119] Move advice for encrypted files from README.md to SWITCH_KEYS.md --- README.md | 104 +----------------------------------------- SWITCH_KEYS.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 116 insertions(+), 109 deletions(-) diff --git a/README.md b/README.md index 1db29e3..d8e9d04 100644 --- a/README.md +++ b/README.md @@ -102,109 +102,9 @@ nstool -d some_file.bin Some file types have some internal file system that can be displayed or extracted ## Encrypted Files -Some Nintendo Switch files are partially or completely encrypted. These require the user to supply the encryption keys to NSTool so that it can process them. Follow the below advice for what keys are required and how to supply them to NSTool. +Some Nintendo Switch files are partially or completely encrypted. These require the user to supply the encryption keys to NSTool so that it can process them. -### NX GameCard Image -The `GameCard ExtendedHeader` is encrypted with one of 8 keys, specified by the `KekIndex` in the `GameCard Header`. -It isn't required to extract game data, it just contains metadata. - -Only two keys are currently defined: -| KeyIndex | Name | Description | -| ----------- | --------------- | ----------- | -| 00 | Production | Usually selected for prod images. Some dev images use this key index. | -| 01 | Development | Usually selected for dev images. This was changed from key index 00 at some point. | - -In order to read the XCI Extended Header, the header key(s) must be defined in `prod.keys`/`dev.keys` (Prod and dev share the same keydata). -``` -xci_header_key_00 = <32 char AES128 key here> -xci_header_key_01 = <32 char AES128 key here> -``` - -### Nintendo Content Archive -Nintendo Content Archive (NCA) files have both an encrypted header and content. The encrypted header determines the layout/format/encryption method of the content, which contains the game data. - -In order to read the NCA header, the header key must be defined in `prod.keys`/`dev.keys`. - -This can be explicitly: -``` -nca_header_key = <64 char AES128-XTS key-data here> -``` -Or allow NSTool to derive it from key sources: -``` -master_key_00 = <32 char AES128 key-data here> -aes_kek_generation_source = <32 char AES128 key-data here> -aes_key_generation_source = <32 char AES128 key-data here> -nca_header_kek_source = <32 char AES128 key-data here> -nca_header_key_source = <64 char AES128 key-data here> -``` - -In order to read the NCA content, the content key must be determined. Unlike the header key which is fixed, each NCA will have a unique content key. - -Content keys are either: -1) "Internal", where they are encrypted the NCA Header KeyArea -2) "External", where they are encrypted in an external Ticket file (.tik) (external content keys are sometimes called title keys) - -#### Internal Content Key -Decrypting the content key from the NCA Header Key Area requires the appropriate `nca_key_area_key` to be defined in `prod.keys`/`dev.keys`. -However for security reasons Nintendo revises this key periodically, and within each key revision there are 3 separate keys for different categories of applications. - -It's best to define as many of these as possible, to reduce the number of times you need to edit the keyfiles. - -So for a given key revision these key area keys can be defined explicitly (`##` represents the key revision in hexadecimal): -``` -nca_key_area_key_application_## = <32 char AES128 key-data here> -nca_key_area_key_ocean_## = <32 char AES128 key-data here> -nca_key_area_key_system_## = <32 char AES128 key-data here> -``` -Or allow NSTool to derive them from key sources: (`##` represents the key revision in hexadecimal): -``` -master_key_## = <32 char AES128 key-data here> -aes_kek_generation_source = <32 char AES128 key-data here> -aes_key_generation_source = <32 char AES128 key-data here> -nca_key_area_key_application_source = <32 char AES128 key-data here> -nca_key_area_key_ocean_source = <32 char AES128 key-data here> -nca_key_area_key_system_source = <32 char AES128 key-data here> -``` - -#### External Content Key -For NCAs that use an external content key, the user must supplied the key to NSTool. - -Most NCAs that use an external content key will be bundled with a ticket file (*.tik) that contains the content key in an encrypted form. - -The ticket can be supplied by the user using the `--tik` option: -``` -nstool --tik <32 char rightsid>.tik <32 char contentid>.nca -``` -This however requires the the appropriate commonkey to be defined in `prod.keys`/`dev.keys` to decrypt the content key in the ticket. However for security reasons Nintendo revises this key periodically. So it's best to define as many of these as possible, to reduce the number of times you need to edit the keyfiles. - -So for a given key revision the common key can be defined explicitly (`##` represents the key revision in hexadecimal): -``` -ticket_commonkey_## = <32 char AES128 key-data here> -``` -Or allow NSTool to derive them from key sources: (`##` represents the key revision in hexadecimal): -``` -master_key_## = <32 char AES128 key-data here> -ticket_commonkey_source = <32 char AES128 key-data here> -``` - -Alternatively you can supply the raw encrypted content key (also called a title key) directly with the `--titlekey` option: -``` -nstool --titlekey <32 char AES128 key-data here> <32 char contentid>.nca -``` - -It is also possible to supply the decrypted content key directly with the `--contentkey` option: -``` -nstool --contentkey <32 char AES128 key-data here> <32 char contentid>.nca -``` - -##### Scene Tickets -Please note that "Scene" tickets have been known to have errors. If you have issues using the `--tik` option, try passing the raw encrypted titlekey directly with the `--titlekey` option. The titlekey can be found by reading the ticket with NSTool: -``` -nstool <32 char rightsid>.tik -``` - -##### Personalised Tickets -If the ticket is personalised (encrypted with console unique RSA key), NSTool will not support it. You will need to use extract the title key with another tool and pass the encrypted title key directly with the `--titlekey` option. +See [SWITCH_KEYS.md](/SWITCH_KEYS.md) for more info. # External Keys NSTool doesn't embed any keys that are copyright protected. However keys can be imported via various keyset files. diff --git a/SWITCH_KEYS.md b/SWITCH_KEYS.md index 24b8e90..86af30a 100644 --- a/SWITCH_KEYS.md +++ b/SWITCH_KEYS.md @@ -87,12 +87,119 @@ acid_sign_key_private : alias for acid_sign_key_00_private titlekek_source : hactool alias for ticket_commonkey_source header_key_source : hactool alias for nca_header_key_source header_kek_source : hactool alias for nca_header_kek_source -key_area_key_application_source : hactool alias for nca_body_keak_application_source -key_area_key_ocean_source : hactool alias for nca_body_keak_ocean_source -key_area_key_system_source : hactool alias for nca_body_keak_system_source +key_area_key_application_source : hactool alias for nca_key_area_key_application_source +key_area_key_ocean_source : hactool alias for nca_key_area_key_ocean_source +key_area_key_system_source : hactool alias for nca_key_area_key_system_source titlekek_## : hactool alias for ticket_commonkey_## header_key : hactool alias for nca_header_key -key_area_key_application_## : hactool alias for nca_body_keak_application_## -key_area_key_ocean_## : hactool alias for nca_body_keak_ocean_## -key_area_key_system_## : hactool alias for nca_body_keak_system_## -``` \ No newline at end of file +key_area_key_application_## : hactool alias for nca_key_area_key_application_## +key_area_key_ocean_## : hactool alias for nca_key_area_key_ocean_## +key_area_key_system_## : hactool alias for nca_key_area_key_system_## +``` + +## Encrypted File Types +See below for advice on what keys are required to decrypt certain file types. + +### NX GameCard Image +The `GameCard ExtendedHeader` is encrypted with one of 8 keys, specified by the `KekIndex` in the `GameCard Header`. +It isn't required to extract game data, it just contains metadata. + +Only two keys are currently defined: +| KeyIndex | Name | Description | +| ----------- | --------------- | ----------- | +| 00 | Production | Usually selected for prod images. Some dev images use this key index. | +| 01 | Development | Usually selected for dev images. This was changed from key index 00 at some point. | + +Define the header key(s) in `prod.keys`/`dev.keys` (Prod and dev share the same keydata): +``` +xci_header_key_00 = <32 char AES128 key here> +xci_header_key_01 = <32 char AES128 key here> +``` + +### Nintendo Content Archive +Nintendo Content Archive (NCA) files have both an encrypted header and content. The encrypted header determines the layout/format/encryption method of the content, which contains the game data. + +Define the header key in `prod.keys`/`dev.keys`. +``` +nca_header_key = <64 char AES128-XTS key-data here> +``` +Or allow NSTool to derive it from key sources: +``` +master_key_00 = <32 char AES128 key-data here> +aes_kek_generation_source = <32 char AES128 key-data here> +aes_key_generation_source = <32 char AES128 key-data here> +nca_header_kek_source = <32 char AES128 key-data here> +nca_header_key_source = <64 char AES128 key-data here> +``` + +In order to read the NCA content, the content key must be determined. Unlike the header key which is fixed, each NCA will have a unique content key. + +Content keys are either: +1) "Internal", where they are encrypted the NCA Header KeyArea +2) "External", where they are encrypted in an external Ticket file (.tik) (external content keys are sometimes called title keys) + +#### Internal Content Key +Decrypting the content key from the NCA Header Key Area requires the appropriate `nca_key_area_key` to be defined in `prod.keys`/`dev.keys`. +However for security reasons Nintendo revises this key periodically, and within each key revision there are 3 separate keys for different categories of applications. + +It's best to define as many of these as possible, to reduce the number of times you need to edit the keyfiles. + +So for a given key revision these key area keys can be defined explicitly (`##` represents the key revision in hexadecimal): +``` +nca_key_area_key_application_## = <32 char AES128 key-data here> +nca_key_area_key_ocean_## = <32 char AES128 key-data here> +nca_key_area_key_system_## = <32 char AES128 key-data here> +``` +Or allow NSTool to derive them from key sources: (`##` represents the key revision in hexadecimal): +``` +master_key_## = <32 char AES128 key-data here> +aes_kek_generation_source = <32 char AES128 key-data here> +aes_key_generation_source = <32 char AES128 key-data here> +nca_key_area_key_application_source = <32 char AES128 key-data here> +nca_key_area_key_ocean_source = <32 char AES128 key-data here> +nca_key_area_key_system_source = <32 char AES128 key-data here> +``` + +#### External Content Key +For NCAs that use an external content key, the user must supplied the key to NSTool. + +Most NCAs that use an external content key will be bundled with a ticket file (*.tik) that contains the content key in an encrypted form. + +The ticket can be supplied by the user using the `--tik` option: +``` +nstool --tik <32 char rightsid>.tik <32 char contentid>.nca +``` +This however requires the the appropriate commonkey to be defined in `prod.keys`/`dev.keys` to decrypt the content key in the ticket. However for security reasons Nintendo revises this key periodically. + +It's best to define as many of these as possible, to reduce the number of times you need to edit the keyfiles. + +So for a given key revision the common key can be defined explicitly (`##` represents the key revision in hexadecimal): +``` +ticket_commonkey_## = <32 char AES128 key-data here> +``` +Or allow NSTool to derive them from key sources: (`##` represents the key revision in hexadecimal): +``` +master_key_## = <32 char AES128 key-data here> +ticket_commonkey_source = <32 char AES128 key-data here> +``` + +Alternatively you can supply the raw encrypted content key (also called a title key) directly with the `--titlekey` option: +``` +nstool --titlekey <32 char AES128 key-data here> <32 char contentid>.nca +``` + +It is also possible to supply the decrypted content key directly with the `--contentkey` option: +``` +nstool --contentkey <32 char AES128 key-data here> <32 char contentid>.nca +``` + +##### Scene Tickets +Please note that "Scene" tickets have been known to have errors. If you have issues using the `--tik` option, try passing the raw encrypted titlekey directly with the `--titlekey` option. The titlekey can be found by reading the ticket with NSTool: +``` +nstool <32 char rightsid>.tik +``` + +##### Personalised Tickets +If the ticket is personalised (encrypted with console unique RSA key), NSTool will not support it. You will need to use extract the title key with another tool and pass the encrypted title key directly with the `--titlekey` option. + +# Title \ No newline at end of file From a1dcf64f8a9d760e756e4817749dafc08c5e60fc Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 14 Nov 2021 12:53:57 +0800 Subject: [PATCH 117/119] Update README.md --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d8e9d04..64d5a03 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ General purpose reading/extraction tool for Nintendo Switch file formats. * PartitionFs (`PFS0`) (.pfs0) * Sha256PartitionFs (`HFS0`) (.hfs0) * RomFs (.romfs) -* Nintendo Submission Package (.nsp) * Nintendo Content Archive (.nca) +* Nintendo Submission Package (.nsp) * NX GameCard Image (.xci) * Meta (`META`) (.npdm) * Nintendo Application Control Property (.nacp) @@ -18,7 +18,6 @@ General purpose reading/extraction tool for Nintendo Switch file formats. * Initial Program Bundle (`INI1`) (.ini) * Initial Program (`KIP1`) (.kip) - # Usage ## General usage The default mode of NSTool is to show general information about a file. @@ -99,7 +98,50 @@ nstool -d some_file.bin ``` ## Extract Files -Some file types have some internal file system that can be displayed or extracted +Some file types have an internal file system. This can be displayed and extracted. + +To display the file system tree, use the file tree option `--fstree`: +``` +nstool --fstree some_file.bin +``` + +To extract the file system, use the extract option `-x`, `--extract`. Which has four modes. + +1) Extract the entire file system. + +This extracts the contents of the entire file system to `./extract_dir/`. `extract_dir` will be created if it doesn't exist. +``` +nstool -x ./extract_dir/ some_file.bin +``` + +2) Extract a sub directory. + +This extracts the contents of `/a/sub/directory/` to `./extract_dir/`. `extract_dir` will be created if it doesn't exist. +``` +nstool -x /a/sub/directory/ ./extract_dir/ some_file.bin +``` + +3) Extract a specific file to a directory. + +This extracts `/path/to/a/file.bin` to `./extract_dir/file.bin`. +``` +nstool -x /path/to/a/file.bin ./extract_dir/ some_file.bin +``` + +4) Extract a specific file with a custom name. + +This extracts `/path/to/a/file.bin` to `./extract_dir/different_name.bin`. +``` +nstool -x /path/to/a/file.bin ./extract_dir/different_name.bin some_file.bin +``` + +### Supported File Types +* PartitionFs +* Sha256PartitionFs +* RomFs (including RomFs embedded in Homebrew NRO) +* NCA +* NSP +* XCI ## Encrypted Files Some Nintendo Switch files are partially or completely encrypted. These require the user to supply the encryption keys to NSTool so that it can process them. From 2b5742d1ccd87fe5ac13be1cb442b07e2fc2c47d Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 14 Nov 2021 12:57:20 +0800 Subject: [PATCH 118/119] Misc. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64d5a03..38ee11c 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ This extracts the contents of `/a/sub/directory/` to `./extract_dir/`. `extract_ nstool -x /a/sub/directory/ ./extract_dir/ some_file.bin ``` -3) Extract a specific file to a directory. +3) Extract a specific file, preserving the original name. This extracts `/path/to/a/file.bin` to `./extract_dir/file.bin`. ``` From bf3e43d4fdc004d1c020a8fe21f1bdb4c546b54a Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 14 Nov 2021 13:02:37 +0800 Subject: [PATCH 119/119] Misc. --- SWITCH_KEYS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/SWITCH_KEYS.md b/SWITCH_KEYS.md index 86af30a..1834679 100644 --- a/SWITCH_KEYS.md +++ b/SWITCH_KEYS.md @@ -183,6 +183,7 @@ master_key_## = <32 char AES128 key-data here> ticket_commonkey_source = <32 char AES128 key-data here> ``` +##### Supply the external content key directly to NSTool Alternatively you can supply the raw encrypted content key (also called a title key) directly with the `--titlekey` option: ``` nstool --titlekey <32 char AES128 key-data here> <32 char contentid>.nca