mirror of
https://github.com/jakcron/nstool
synced 2024-11-26 07:29:29 +00:00
[programs] Remove xcitool, npdmtool, pfstool. Add nstool.
This commit is contained in:
parent
c42b498e19
commit
8ef9f478b0
33 changed files with 2253 additions and 1376 deletions
38
.vscode/settings.json
vendored
38
.vscode/settings.json
vendored
|
@ -1,20 +1,32 @@
|
||||||
{
|
{
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"__functional_base": "cpp",
|
"__config": "cpp",
|
||||||
"memory": "cpp",
|
"__nullptr": "cpp",
|
||||||
"tuple": "cpp",
|
"cstddef": "cpp",
|
||||||
"utility": "cpp",
|
"exception": "cpp",
|
||||||
"__string": "cpp",
|
"initializer_list": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"typeinfo": "cpp",
|
||||||
|
"__split_buffer": "cpp",
|
||||||
|
"__tree": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
"string": "cpp",
|
"string": "cpp",
|
||||||
"string_view": "cpp",
|
"string_view": "cpp",
|
||||||
"__split_buffer": "cpp",
|
"utility": "cpp",
|
||||||
"initializer_list": "cpp",
|
|
||||||
"iterator": "cpp",
|
|
||||||
"vector": "cpp",
|
"vector": "cpp",
|
||||||
"__functional_base_03": "cpp",
|
"istream": "cpp",
|
||||||
"__tuple": "cpp",
|
"atomic": "cpp",
|
||||||
"limits": "cpp",
|
"ios": "cpp",
|
||||||
"type_traits": "cpp",
|
"system_error": "cpp",
|
||||||
"stdexcept": "cpp"
|
"__functional_base": "cpp",
|
||||||
|
"locale": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"__locale": "cpp",
|
||||||
|
"cinttypes": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,15 +9,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcrypto", "lib\libcrypto\
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnx", "lib\libnx\nx.vcxproj", "{91BA9E79-8242-4F7D-B997-0DFEC95EA22B}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnx", "lib\libnx\nx.vcxproj", "{91BA9E79-8242-4F7D-B997-0DFEC95EA22B}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ncatool", "programs\ncatool\ncatool.vcxproj", "{7DA88C6F-4470-495D-995A-4F633F3370C1}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{170B4A09-1B67-4A62-93AB-116EBCFF4A8C}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{170B4A09-1B67-4A62-93AB-116EBCFF4A8C}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
lib\makefile = lib\makefile
|
lib\makefile = lib\makefile
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "npdmtool", "programs\npdmtool\npdmtool.vcxproj", "{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Programs", "Programs", "{E0863FCC-8E72-490D-BE1B-458F12CA8298}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Programs", "Programs", "{E0863FCC-8E72-490D-BE1B-458F12CA8298}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
programs\makefile = programs\makefile
|
programs\makefile = programs\makefile
|
||||||
|
@ -30,14 +26,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfstool", "programs\pfstool\pfstool.vcxproj", "{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}"
|
|
||||||
EndProject
|
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libes", "lib\libes\es.vcxproj", "{7BE99936-0D40-410D-944B-4513C2EFF8DC}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libes", "lib\libes\es.vcxproj", "{7BE99936-0D40-410D-944B-4513C2EFF8DC}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tiktool", "programs\tiktool\tiktool.vcxproj", "{2200B834-F15A-4C6E-9DDB-6012B9A5C246}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tiktool", "programs\tiktool\tiktool.vcxproj", "{2200B834-F15A-4C6E-9DDB-6012B9A5C246}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xcitool", "programs\xcitool\xcitool.vcxproj", "{007FF616-7B99-4CB3-84CD-39C47F64FC7E}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
|
|
|
@ -3,9 +3,9 @@ Tools for NX (Nintendo Switch).
|
||||||
|
|
||||||
== Tools ==
|
== Tools ==
|
||||||
* ncatool - read/extract *.nca
|
* ncatool - read/extract *.nca
|
||||||
* npdmtool - read *.npdm
|
* nstool - read *.npdm, read/extract PartitionFS (PFS0|HFS0) blobs (including *.nsp), read *.xci
|
||||||
* pfstool - read/extract PartitionFS (PFS0|HFS0) blobs (including *.nsp).
|
* tiktool - read ticket fields.
|
||||||
* xcitool - read *.xci
|
|
||||||
|
|
||||||
== Issues ==
|
== Issues ==
|
||||||
* [ncatool] nca section crypto not implemented
|
* [ncatool] nca section extraction not implemented
|
||||||
|
* [nstool] romfs support not implemented
|
||||||
|
|
|
@ -22,8 +22,8 @@ else
|
||||||
UNAME = $(shell uname -s)
|
UNAME = $(shell uname -s)
|
||||||
ifeq ($(UNAME), Darwin)
|
ifeq ($(UNAME), Darwin)
|
||||||
# MacOS Only Flags/Libs
|
# MacOS Only Flags/Libs
|
||||||
CFLAGS += -Wno-unused-private-field
|
CFLAGS += -arch x86_64 -Wno-unused-private-field
|
||||||
CXXFLAGS += -Wno-unused-private-field
|
CXXFLAGS += -arch x86_64 -Wno-unused-private-field
|
||||||
ARFLAGS = rc
|
ARFLAGS = rc
|
||||||
else
|
else
|
||||||
# *nix Only Flags/Libs
|
# *nix Only Flags/Libs
|
||||||
|
|
|
@ -37,6 +37,7 @@ static inline uint16_t le_hword(uint16_t a) { return __local_bswap16(a); }
|
||||||
#error "What's the endianness of the platform you're targeting?"
|
#error "What's the endianness of the platform you're targeting?"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
template <class T, static T (*F)(T)>
|
template <class T, static T (*F)(T)>
|
||||||
class ISerialiseablePrimative {
|
class ISerialiseablePrimative {
|
||||||
public:
|
public:
|
||||||
|
@ -53,4 +54,65 @@ typedef ISerialiseablePrimative<uint16_t, be_hword> be_uint16_t;
|
||||||
typedef ISerialiseablePrimative<uint32_t, le_word> le_uint32_t;
|
typedef ISerialiseablePrimative<uint32_t, le_word> le_uint32_t;
|
||||||
typedef ISerialiseablePrimative<uint32_t, be_word> be_uint32_t;
|
typedef ISerialiseablePrimative<uint32_t, be_word> be_uint32_t;
|
||||||
typedef ISerialiseablePrimative<uint64_t, le_dword> le_uint64_t;
|
typedef ISerialiseablePrimative<uint64_t, le_dword> le_uint64_t;
|
||||||
typedef ISerialiseablePrimative<uint64_t, be_dword> be_uint64_t;
|
typedef ISerialiseablePrimative<uint64_t, be_dword> be_uint64_t;
|
||||||
|
*/
|
||||||
|
|
||||||
|
class le_uint16_t {
|
||||||
|
public:
|
||||||
|
inline uint16_t get() const { return le_hword(mVar);}
|
||||||
|
inline void set(uint16_t var) { mVar = le_hword(var); }
|
||||||
|
inline uint16_t operator=(uint16_t var) { set(var); return get();}
|
||||||
|
inline uint16_t operator*() const { return get(); }
|
||||||
|
private:
|
||||||
|
uint16_t mVar;
|
||||||
|
};
|
||||||
|
|
||||||
|
class be_uint16_t {
|
||||||
|
public:
|
||||||
|
inline uint16_t get() const { return be_hword(mVar);}
|
||||||
|
inline void set(uint16_t var) { mVar = be_hword(var); }
|
||||||
|
inline uint16_t operator=(uint16_t var) { set(var); return get();}
|
||||||
|
inline uint16_t operator*() const { return get(); }
|
||||||
|
private:
|
||||||
|
uint16_t mVar;
|
||||||
|
};
|
||||||
|
|
||||||
|
class le_uint32_t {
|
||||||
|
public:
|
||||||
|
inline uint32_t get() const { return le_word(mVar);}
|
||||||
|
inline void set(uint32_t var) { mVar = le_word(var); }
|
||||||
|
inline uint32_t operator=(uint32_t var) { set(var); return get();}
|
||||||
|
inline uint32_t operator*() const { return get(); }
|
||||||
|
private:
|
||||||
|
uint32_t mVar;
|
||||||
|
};
|
||||||
|
|
||||||
|
class be_uint32_t {
|
||||||
|
public:
|
||||||
|
inline uint32_t get() const { return be_word(mVar);}
|
||||||
|
inline void set(uint32_t var) { mVar = be_word(var); }
|
||||||
|
inline uint32_t operator=(uint32_t var) { set(var); return get();}
|
||||||
|
inline uint32_t operator*() const { return get(); }
|
||||||
|
private:
|
||||||
|
uint32_t mVar;
|
||||||
|
};
|
||||||
|
|
||||||
|
class le_uint64_t {
|
||||||
|
public:
|
||||||
|
inline uint64_t get() const { return le_dword(mVar);}
|
||||||
|
inline void set(uint64_t var) { mVar = le_dword(var); }
|
||||||
|
inline uint64_t operator=(uint64_t var) { set(var); return get();}
|
||||||
|
inline uint64_t operator*() const { return get(); }
|
||||||
|
private:
|
||||||
|
uint64_t mVar;
|
||||||
|
};
|
||||||
|
|
||||||
|
class be_uint64_t {
|
||||||
|
public:
|
||||||
|
inline uint64_t get() const { return be_dword(mVar);}
|
||||||
|
inline void set(uint64_t var) { mVar = be_dword(var); }
|
||||||
|
inline uint64_t operator=(uint64_t var) { set(var); return get();}
|
||||||
|
inline uint64_t operator*() const { return get(); }
|
||||||
|
private:
|
||||||
|
uint64_t mVar;
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
PROGS = ncatool npdmtool pfstool tiktool xcitool
|
PROGS = nstool ncatool tiktool
|
||||||
BIN_DIR = "../bin"
|
BIN_DIR = "../bin"
|
||||||
|
|
||||||
main: build
|
main: build
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <fnd/SimpleTextOutput.h>
|
#include <fnd/SimpleTextOutput.h>
|
||||||
#include <nx/NXCrypto.h>
|
#include <nx/NXCrypto.h>
|
||||||
#include <nx/NcaHeader.h>
|
#include <nx/NcaHeader.h>
|
||||||
|
#include <nx/NcaUtils.h>
|
||||||
#include <nx/PfsHeader.h>
|
#include <nx/PfsHeader.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -77,7 +78,7 @@ static const byte_t* kNcaHeaderKey[2][2] =
|
||||||
{ crypto::aes::nx::prod::nca_header_key[0], crypto::aes::nx::prod::nca_header_key[1] }
|
{ crypto::aes::nx::prod::nca_header_key[0], crypto::aes::nx::prod::nca_header_key[1] }
|
||||||
};
|
};
|
||||||
|
|
||||||
inline size_t sectorToOffset(size_t sector_index) { return sector_index * nx::nca::kSectorSize; }
|
inline size_t sectorToOffset(size_t sector_index) { return nx::NcaUtils::sectorToOffset(sector_index); }
|
||||||
|
|
||||||
void initNcaCtr(byte_t ctr[crypto::aes::kAesBlockSize], uint32_t generation)
|
void initNcaCtr(byte_t ctr[crypto::aes::kAesBlockSize], uint32_t generation)
|
||||||
{
|
{
|
||||||
|
@ -90,23 +91,9 @@ void initNcaCtr(byte_t ctr[crypto::aes::kAesBlockSize], uint32_t generation)
|
||||||
|
|
||||||
void decryptNcaHeader(byte_t header[nx::nca::kHeaderSize], const byte_t* key[2])
|
void decryptNcaHeader(byte_t header[nx::nca::kHeaderSize], const byte_t* key[2])
|
||||||
{
|
{
|
||||||
byte_t tweak[crypto::aes::kAesBlockSize];
|
crypto::aes::sAesXts128Key a;
|
||||||
|
a.set(key[0],key[1]);
|
||||||
// decrypt main header
|
nx::NcaUtils::decryptNcaHeader(header, header, a);
|
||||||
byte_t raw_hdr[nx::nca::kSectorSize];
|
|
||||||
nx::NcaHeader hdr;
|
|
||||||
crypto::aes::AesXtsMakeTweak(tweak, 1);
|
|
||||||
crypto::aes::AesXtsDecryptSector(header + sectorToOffset(1), nx::nca::kSectorSize, key[0], key[1], tweak, raw_hdr);
|
|
||||||
hdr.importBinary(raw_hdr, nx::nca::kSectorSize);
|
|
||||||
|
|
||||||
bool useNca2SectorIndex = hdr.getFormatVersion() == nx::NcaHeader::NCA2_FORMAT;
|
|
||||||
|
|
||||||
// decrypt whole header
|
|
||||||
for (size_t i = 0; i < nx::nca::kHeaderSectorNum; i++)
|
|
||||||
{
|
|
||||||
crypto::aes::AesXtsMakeTweak(tweak, (i > 1 && useNca2SectorIndex)? 0 : i);
|
|
||||||
crypto::aes::AesXtsDecryptSector(header + sectorToOffset(i), nx::nca::kSectorSize, key[0], key[1], tweak, header + sectorToOffset(i));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool testNcaHeaderKey(const byte_t* header_src, const byte_t* key[2])
|
bool testNcaHeaderKey(const byte_t* header_src, const byte_t* key[2])
|
||||||
|
@ -198,21 +185,25 @@ void printHeader(const byte_t* header)
|
||||||
printf(" Format Type: %s\n", kFormatTypeStr[fsHdr->format_type].c_str());
|
printf(" Format Type: %s\n", kFormatTypeStr[fsHdr->format_type].c_str());
|
||||||
printf(" Hash Type: %s\n", kHashTypeStr[fsHdr->hash_type].c_str());
|
printf(" Hash Type: %s\n", kHashTypeStr[fsHdr->hash_type].c_str());
|
||||||
printf(" Enc. Type: %s\n", kEncryptionTypeStr[fsHdr->encryption_type].c_str());
|
printf(" Enc. Type: %s\n", kEncryptionTypeStr[fsHdr->encryption_type].c_str());
|
||||||
if (fsHdr->format_type == nx::nca::FORMAT_ROMFS)
|
if (fsHdr->hash_type == nx::nca::HASH_HIERARCHICAL_INTERGRITY)
|
||||||
{
|
{
|
||||||
|
const nx::sIvfcHeader* hash_hdr = (const nx::sIvfcHeader*)(header + sectorToOffset(sector_index) + sizeof(nx::sNcaFsHeader));
|
||||||
|
//printf(" HashHierarchicalIntegrity Header:\n");
|
||||||
|
//printf(" ")
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (fsHdr->format_type == nx::nca::FORMAT_PFS0)
|
else if (fsHdr->hash_type == nx::nca::HASH_HIERARCHICAL_SHA256)
|
||||||
{
|
{
|
||||||
const nx::sPfsSuperBlock* pfs0 = (const nx::sPfsSuperBlock*)(header + sectorToOffset(sector_index) + sizeof(nx::sNcaFsHeader));
|
const nx::sHierarchicalSha256Header* hash_hdr = (const nx::sHierarchicalSha256Header*)(header + sectorToOffset(sector_index) + sizeof(nx::sNcaFsHeader));
|
||||||
printf(" PFS0 SuperBlock:\n");
|
printf(" HashHierarchicalSha256 Header:\n");
|
||||||
printf(" Master Hash: \n");
|
printf(" Master Hash: ");
|
||||||
printf(" HashBlockSize: 0x%x\n", pfs0->hash_block_size.get());
|
fnd::SimpleTextOutput::hexDump(hash_hdr->master_hash, 0x20);
|
||||||
printf(" Unknown: 0x%x\n", pfs0->unk_0x02.get());
|
printf(" HashBlockSize: 0x%x\n", hash_hdr->hash_block_size.get());
|
||||||
printf(" HashDataOffset: 0x%" PRIx64 "\n", pfs0->hash_data.offset.get());
|
printf(" Unknown: 0x%x\n", hash_hdr->unk_0x02.get());
|
||||||
printf(" HashDataSize: 0x%" PRIx64 "\n", pfs0->hash_data.size.get());
|
printf(" HashDataOffset: 0x%" PRIx64 "\n", hash_hdr->hash_data.offset.get());
|
||||||
printf(" HashTargetOffset: 0x%" PRIx64 "\n", pfs0->hash_target.offset.get());
|
printf(" HashDataSize: 0x%" PRIx64 "\n", hash_hdr->hash_data.size.get());
|
||||||
printf(" HashTargetSize: 0x%" PRIx64 "\n", pfs0->hash_target.size.get());
|
printf(" HashTargetOffset: 0x%" PRIx64 "\n", hash_hdr->hash_target.offset.get());
|
||||||
|
printf(" HashTargetSize: 0x%" PRIx64 "\n", hash_hdr->hash_target.size.get());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,137 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
|
||||||
<ProjectConfiguration Include="Debug|Win32">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|Win32">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug|x64">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|x64">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<ProjectGuid>{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}</ProjectGuid>
|
|
||||||
<RootNamespace>npdmtool</RootNamespace>
|
|
||||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="Shared">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<PropertyGroup Label="UserMacros" />
|
|
||||||
<PropertyGroup />
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
<PreprocessorDefinitions>_MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
<PreprocessorDefinitions>_MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
<PreprocessorDefinitions>_MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
<PreprocessorDefinitions>_MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="source\main.cpp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\lib\libcrypto\crypto.vcxproj">
|
|
||||||
<Project>{6adbb60d-dba0-411d-bd2d-a355ef8e0fe1}</Project>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\..\lib\libfnd\fnd.vcxproj">
|
|
||||||
<Project>{4d27edb9-5110-44fe-8ce2-d46c5ad3c55b}</Project>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\..\lib\libnx\nx.vcxproj">
|
|
||||||
<Project>{91ba9e79-8242-4f7d-b997-0dfec95ea22b}</Project>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
|
@ -1,22 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup>
|
|
||||||
<Filter Include="Source Files">
|
|
||||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
|
||||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Header Files">
|
|
||||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
|
||||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Resource Files">
|
|
||||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
|
||||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
|
||||||
</Filter>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="source\main.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
|
@ -1,437 +0,0 @@
|
||||||
#include <cstdio>
|
|
||||||
#include <crypto/aes.h>
|
|
||||||
#include <fnd/io.h>
|
|
||||||
#include <fnd/MemoryBlob.h>
|
|
||||||
#include <nx/NXCrypto.h>
|
|
||||||
#include <nx/NpdmBinary.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
const std::string kInstructionType[2] = { "32Bit", "64Bit" };
|
|
||||||
const std::string kProcAddrSpace[4] = { "Unknown", "64Bit", "32Bit", "32Bit no reserved" };
|
|
||||||
const std::string kAciType[2] = { "ACI0", "ACID" };
|
|
||||||
const std::string kMiscFlag[15] = { "EnableDebug", "ForceDebug", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7", "bit8", "bit9", "bit10", "bit11", "bit12", "bit13", "bit14"};
|
|
||||||
const std::string kFsaFlag[64] =
|
|
||||||
{
|
|
||||||
"ApplicationInfo",
|
|
||||||
"BootModeControl",
|
|
||||||
"Calibration",
|
|
||||||
"SystemSaveData",
|
|
||||||
"GameCard",
|
|
||||||
"SaveDataBackUp",
|
|
||||||
"SaveDataManagement",
|
|
||||||
"BisAllRaw",
|
|
||||||
"GameCardRaw",
|
|
||||||
"GameCardPrivate",
|
|
||||||
"SetTime",
|
|
||||||
"ContentManager",
|
|
||||||
"ImageManager",
|
|
||||||
"CreateSaveData",
|
|
||||||
"SystemSaveDataManagement",
|
|
||||||
"BisFileSystem",
|
|
||||||
"SystemUpdate",
|
|
||||||
"SaveDataMeta",
|
|
||||||
"DeviceSaveData",
|
|
||||||
"SettingsControl",
|
|
||||||
"Bit20",
|
|
||||||
"Bit21",
|
|
||||||
"Bit22",
|
|
||||||
"Bit23",
|
|
||||||
"Bit24",
|
|
||||||
"Bit25",
|
|
||||||
"Bit26",
|
|
||||||
"Bit27",
|
|
||||||
"Bit28",
|
|
||||||
"Bit29",
|
|
||||||
"Bit30",
|
|
||||||
"Bit31",
|
|
||||||
"Bit32",
|
|
||||||
"Bit33",
|
|
||||||
"Bit34",
|
|
||||||
"Bit35",
|
|
||||||
"Bit36",
|
|
||||||
"Bit37",
|
|
||||||
"Bit38",
|
|
||||||
"Bit39",
|
|
||||||
"Bit40",
|
|
||||||
"Bit41",
|
|
||||||
"Bit42",
|
|
||||||
"Bit43",
|
|
||||||
"Bit44",
|
|
||||||
"Bit45",
|
|
||||||
"Bit46",
|
|
||||||
"Bit47",
|
|
||||||
"Bit48",
|
|
||||||
"Bit49",
|
|
||||||
"Bit50",
|
|
||||||
"Bit51",
|
|
||||||
"Bit52",
|
|
||||||
"Bit53",
|
|
||||||
"Bit54",
|
|
||||||
"Bit55",
|
|
||||||
"Bit56",
|
|
||||||
"Bit57",
|
|
||||||
"Bit58",
|
|
||||||
"Bit59",
|
|
||||||
"Bit60",
|
|
||||||
"Bit61",
|
|
||||||
"Debug",
|
|
||||||
"FullPermission"
|
|
||||||
};
|
|
||||||
const std::string kSysCall[0x80] =
|
|
||||||
{
|
|
||||||
"svc00",
|
|
||||||
"SetHeapSize",
|
|
||||||
"SetMemoryPermission",
|
|
||||||
"SetMemoryAttribute",
|
|
||||||
"MapMemory",
|
|
||||||
"UnmapMemory",
|
|
||||||
"QueryMemory",
|
|
||||||
"ExitProcess",
|
|
||||||
"CreateThread",
|
|
||||||
"StartThread",
|
|
||||||
"ExitThread",
|
|
||||||
"SleepThread",
|
|
||||||
"GetThreadPriority",
|
|
||||||
"SetThreadPriority",
|
|
||||||
"GetThreadCoreMask",
|
|
||||||
"SetThreadCoreMask",
|
|
||||||
"GetCurrentProcessorNumber",
|
|
||||||
"SignalEvent",
|
|
||||||
"ClearEvent",
|
|
||||||
"MapSharedMemory",
|
|
||||||
"UnmapSharedMemory",
|
|
||||||
"CreateTransferMemory",
|
|
||||||
"CloseHandle",
|
|
||||||
"ResetSignal",
|
|
||||||
"WaitSynchronization",
|
|
||||||
"CancelSynchronization",
|
|
||||||
"ArbitrateLock",
|
|
||||||
"ArbitrateUnlock",
|
|
||||||
"WaitProcessWideKeyAtomic",
|
|
||||||
"SignalProcessWideKey",
|
|
||||||
"GetSystemTick",
|
|
||||||
"ConnectToNamedPort",
|
|
||||||
"SendSyncRequestLight",
|
|
||||||
"SendSyncRequest",
|
|
||||||
"SendSyncRequestWithUserBuffer",
|
|
||||||
"SendAsyncRequestWithUserBuffer",
|
|
||||||
"GetProcessId",
|
|
||||||
"GetThreadId",
|
|
||||||
"Break",
|
|
||||||
"OutputDebugString",
|
|
||||||
"ReturnFromException",
|
|
||||||
"GetInfo",
|
|
||||||
"FlushEntireDataCache",
|
|
||||||
"FlushDataCache",
|
|
||||||
"MapPhysicalMemory",
|
|
||||||
"UnmapPhysicalMemory",
|
|
||||||
"svc2E",
|
|
||||||
"GetLastThreadInfo",
|
|
||||||
"GetResourceLimitLimitValue",
|
|
||||||
"GetResourceLimitCurrentValue",
|
|
||||||
"SetThreadActivity",
|
|
||||||
"GetThreadContext3",
|
|
||||||
"svc34",
|
|
||||||
"svc35",
|
|
||||||
"svc36",
|
|
||||||
"svc37",
|
|
||||||
"svc38",
|
|
||||||
"svc39",
|
|
||||||
"svc3A",
|
|
||||||
"svc3B",
|
|
||||||
"DumpInfo",
|
|
||||||
"svc3D",
|
|
||||||
"svc3E",
|
|
||||||
"svc3F",
|
|
||||||
"CreateSession",
|
|
||||||
"AcceptSession",
|
|
||||||
"ReplyAndReceiveLight",
|
|
||||||
"ReplyAndReceive",
|
|
||||||
"ReplyAndReceiveWithUserBuffer",
|
|
||||||
"CreateEvent",
|
|
||||||
"svc46",
|
|
||||||
"svc47",
|
|
||||||
"svc48",
|
|
||||||
"svc49",
|
|
||||||
"svc4A",
|
|
||||||
"svc4B",
|
|
||||||
"svc4C",
|
|
||||||
"SleepSystem",
|
|
||||||
"ReadWriteRegister",
|
|
||||||
"SetProcessActivity",
|
|
||||||
"CreateSharedMemory",
|
|
||||||
"MapTransferMemory",
|
|
||||||
"UnmapTransferMemory",
|
|
||||||
"CreateInterruptEvent",
|
|
||||||
"QueryPhysicalAddress",
|
|
||||||
"QueryIoMapping",
|
|
||||||
"CreateDeviceAddressSpace",
|
|
||||||
"AttachDeviceAddressSpace",
|
|
||||||
"DetachDeviceAddressSpace",
|
|
||||||
"MapDeviceAddressSpaceByForce",
|
|
||||||
"MapDeviceAddressSpaceAligned",
|
|
||||||
"MapDeviceAddressSpace",
|
|
||||||
"UnmapDeviceAddressSpace",
|
|
||||||
"InvalidateProcessDataCache",
|
|
||||||
"StoreProcessDataCache",
|
|
||||||
"FlushProcessDataCache",
|
|
||||||
"DebugActiveProcess",
|
|
||||||
"BreakDebugProcess",
|
|
||||||
"TerminateDebugProcess",
|
|
||||||
"GetDebugEvent",
|
|
||||||
"ContinueDebugEvent",
|
|
||||||
"GetProcessList",
|
|
||||||
"GetThreadList",
|
|
||||||
"GetDebugThreadContext",
|
|
||||||
"SetDebugThreadContext",
|
|
||||||
"QueryDebugProcessMemory",
|
|
||||||
"ReadDebugProcessMemory",
|
|
||||||
"WriteDebugProcessMemory",
|
|
||||||
"SetHardwareBreakPoint",
|
|
||||||
"GetDebugThreadParam",
|
|
||||||
"svc6E",
|
|
||||||
"svc6F",
|
|
||||||
"CreatePort",
|
|
||||||
"ManageNamedPort",
|
|
||||||
"ConnectToPort",
|
|
||||||
"SetProcessMemoryPermission",
|
|
||||||
"MapProcessMemory",
|
|
||||||
"UnmapProcessMemory",
|
|
||||||
"QueryProcessMemory",
|
|
||||||
"MapProcessCodeMemory",
|
|
||||||
"UnmapProcessCodeMemory",
|
|
||||||
"CreateProcess",
|
|
||||||
"StartProcess",
|
|
||||||
"TerminateProcess",
|
|
||||||
"GetProcessInfo",
|
|
||||||
"CreateResourceLimit",
|
|
||||||
"SetResourceLimitLimitValue",
|
|
||||||
"CallSecureMonitor"
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::string kMemMapPerm[2] = { "RW", "RO" };
|
|
||||||
const std::string kMemMapType[2] = { "Io", "Static" };
|
|
||||||
|
|
||||||
const std::string kAcidTarget[2] = { "Development", "Production" };
|
|
||||||
|
|
||||||
void displayNpdmHeader(const nx::NpdmHeader& hdr)
|
|
||||||
{
|
|
||||||
printf("[NPDM HEADER]\n");
|
|
||||||
printf(" Process Architecture Params:\n");
|
|
||||||
printf(" Ins. Type: %s\n", kInstructionType[hdr.getInstructionType()].c_str());
|
|
||||||
printf(" Addr Space: %s\n", kProcAddrSpace[hdr.getProcAddressSpaceType()].c_str());
|
|
||||||
printf(" Main Thread Params:\n");
|
|
||||||
printf(" Priority: %d\n", hdr.getMainThreadPriority());
|
|
||||||
printf(" CpuId: %d\n", hdr.getMainThreadCpuId());
|
|
||||||
printf(" StackSize: 0x%x\n", hdr.getMainThreadStackSize());
|
|
||||||
printf(" TitleInfo:\n");
|
|
||||||
printf(" Version: v%" PRIu32 "\n", hdr.getVersion());
|
|
||||||
printf(" Name: %s\n", hdr.getName().c_str());
|
|
||||||
if (hdr.getProductCode().length())
|
|
||||||
{
|
|
||||||
printf(" ProductCode: %s\n", hdr.getProductCode().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void displayAciHdr(const nx::AciHeader& aci)
|
|
||||||
{
|
|
||||||
printf("[Access Control Info]\n");
|
|
||||||
printf(" ACI Type: %s\n", kAciType[aci.getAciType()].c_str());
|
|
||||||
if (aci.getAciType() == nx::AciBinary::TYPE_ACI0)
|
|
||||||
{
|
|
||||||
printf(" ProgramID: %016" PRIx64 "\n", aci.getProgramId());
|
|
||||||
}
|
|
||||||
else if (aci.getAciType() == nx::AciBinary::TYPE_ACID)
|
|
||||||
{
|
|
||||||
|
|
||||||
printf(" ACID Size: %" PRIx64 "\n", aci.getAcidSize());
|
|
||||||
printf(" Target: %s\n", kAcidTarget[aci.isProduction()].c_str());
|
|
||||||
printf(" ProgramID Restriction\n");
|
|
||||||
printf(" Min: %016" PRIx64 "\n", aci.getProgramIdMin());
|
|
||||||
printf(" Max: %016" PRIx64 "\n", aci.getProgramIdMax());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void displayFac(const nx::FacBinary& fac)
|
|
||||||
{
|
|
||||||
printf("[FS Access Control]\n");
|
|
||||||
printf(" Format Version: %d\n", fac.getFormatVersion());
|
|
||||||
|
|
||||||
if (fac.getFsaRightsList().getSize())
|
|
||||||
{
|
|
||||||
printf(" FS Rights:\n");
|
|
||||||
for (size_t i = 0; i < fac.getFsaRightsList().getSize(); i++)
|
|
||||||
{
|
|
||||||
if (i % 10 == 0)
|
|
||||||
{
|
|
||||||
printf("%s ", i != 0 ? "\n" : "");
|
|
||||||
}
|
|
||||||
printf("%s%s", kFsaFlag[fac.getFsaRightsList()[i]].c_str(), fac.getFsaRightsList()[i] != fac.getFsaRightsList().atBack() ? ", " : "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf(" FS Rights: NONE\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fac.getContentOwnerIdList().getSize())
|
|
||||||
{
|
|
||||||
printf(" Content Owner IDs:\n");
|
|
||||||
for (size_t i = 0; i < fac.getContentOwnerIdList().getSize(); i++)
|
|
||||||
{
|
|
||||||
printf(" 0x%08x\n", fac.getContentOwnerIdList()[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fac.getSaveDataOwnerIdList().getSize())
|
|
||||||
{
|
|
||||||
printf(" Save Data Owner IDs:\n");
|
|
||||||
for (size_t i = 0; i < fac.getSaveDataOwnerIdList().getSize(); i++)
|
|
||||||
{
|
|
||||||
printf(" 0x%08x\n", fac.getSaveDataOwnerIdList()[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void displaySac(const nx::SacBinary& sac)
|
|
||||||
{
|
|
||||||
printf("[Service Access Control]\n");
|
|
||||||
printf(" Service List:\n");
|
|
||||||
for (size_t i = 0; i < sac.getServiceList().getSize(); i++)
|
|
||||||
{
|
|
||||||
if (i % 10 == 0)
|
|
||||||
{
|
|
||||||
printf("%s ", i != 0 ? "\n" : "");
|
|
||||||
}
|
|
||||||
printf("%s%s%s", sac.getServiceList()[i].getName().c_str(), sac.getServiceList()[i].isServer() ? "(isSrv)" : "", sac.getServiceList()[i] != sac.getServiceList().atBack() ? ", " : "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void displayKernelCap(const nx::KcBinary& kern)
|
|
||||||
{
|
|
||||||
printf("[Kernel Capabilities]\n");
|
|
||||||
if (kern.getThreadInfo().isSet())
|
|
||||||
{
|
|
||||||
nx::ThreadInfoHandler threadInfo = kern.getThreadInfo();
|
|
||||||
printf(" Thread Priority:\n");
|
|
||||||
printf(" Min: %d\n", threadInfo.getMinPriority());
|
|
||||||
printf(" Max: %d\n", threadInfo.getMaxPriority());
|
|
||||||
printf(" CpuId:\n");
|
|
||||||
printf(" Min: %d\n", threadInfo.getMinCpuId());
|
|
||||||
printf(" Max: %d\n", threadInfo.getMaxCpuId());
|
|
||||||
}
|
|
||||||
if (kern.getSystemCalls().isSet())
|
|
||||||
{
|
|
||||||
fnd::List<uint8_t> syscalls = kern.getSystemCalls().getSystemCalls();
|
|
||||||
printf(" SystemCalls:");
|
|
||||||
printf("\n ");
|
|
||||||
size_t lineLen = 0;
|
|
||||||
for (size_t i = 0; i < syscalls.getSize(); i++)
|
|
||||||
{
|
|
||||||
if (lineLen > 60)
|
|
||||||
{
|
|
||||||
lineLen = 0;
|
|
||||||
printf("\n ");
|
|
||||||
}
|
|
||||||
printf("%s%s", kSysCall[syscalls[i]].c_str(), syscalls[i] != syscalls.atBack() ? ", " : "\n");
|
|
||||||
lineLen += kSysCall[syscalls[i]].length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (kern.getMemoryMaps().isSet())
|
|
||||||
{
|
|
||||||
fnd::List<nx::MemoryMappingHandler::sMemoryMapping> maps = kern.getMemoryMaps().getMemoryMaps();
|
|
||||||
fnd::List<nx::MemoryMappingHandler::sMemoryMapping> ioMaps = kern.getMemoryMaps().getIoMemoryMaps();
|
|
||||||
|
|
||||||
printf(" MemoryMaps:\n");
|
|
||||||
for (size_t i = 0; i < maps.getSize(); i++)
|
|
||||||
{
|
|
||||||
printf(" 0x%016" PRIx64 " - 0x%016" PRIx64 " (perm=%s) (type=%s)\n", (uint64_t)maps[i].addr << 12, ((uint64_t)(maps[i].addr + maps[i].size) << 12) - 1, kMemMapPerm[maps[i].perm].c_str(), kMemMapType[maps[i].type].c_str());
|
|
||||||
}
|
|
||||||
//printf(" IoMaps:\n");
|
|
||||||
for (size_t i = 0; i < ioMaps.getSize(); i++)
|
|
||||||
{
|
|
||||||
printf(" 0x%016" PRIx64 " - 0x%016" PRIx64 " (perm=%s) (type=%s)\n", (uint64_t)ioMaps[i].addr << 12, ((uint64_t)(ioMaps[i].addr + ioMaps[i].size) << 12) - 1, kMemMapPerm[ioMaps[i].perm].c_str(), kMemMapType[ioMaps[i].type].c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (kern.getInterupts().isSet())
|
|
||||||
{
|
|
||||||
fnd::List<uint16_t> interupts = kern.getInterupts().getInteruptList();
|
|
||||||
printf(" Interupts Flags:\n");
|
|
||||||
for (uint32_t i = 0; i < interupts.getSize(); i++)
|
|
||||||
{
|
|
||||||
if (i % 10 == 0)
|
|
||||||
{
|
|
||||||
printf("%s ", i != 0 ? "\n" : "");
|
|
||||||
}
|
|
||||||
printf("0x%x%s", interupts[i], interupts[i] != interupts.atBack() ? ", " : "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (kern.getMiscParams().isSet())
|
|
||||||
{
|
|
||||||
printf(" ProgramType: %d\n", kern.getMiscParams().getProgramType());
|
|
||||||
}
|
|
||||||
if (kern.getKernelVersion().isSet())
|
|
||||||
{
|
|
||||||
printf(" Kernel Version: %d.%d\n", kern.getKernelVersion().getVerMajor(), kern.getKernelVersion().getVerMinor());
|
|
||||||
}
|
|
||||||
if (kern.getHandleTableSize().isSet())
|
|
||||||
{
|
|
||||||
printf(" Handle Table Size: 0x%x\n", kern.getHandleTableSize().getHandleTableSize());
|
|
||||||
}
|
|
||||||
if (kern.getMiscFlags().isSet())
|
|
||||||
{
|
|
||||||
fnd::List<nx::MiscFlagsHandler::Flags> flagList = kern.getMiscFlags().getFlagList();
|
|
||||||
|
|
||||||
printf(" Misc Flags:\n");
|
|
||||||
for (uint32_t i = 0; i < flagList.getSize(); i++)
|
|
||||||
{
|
|
||||||
if (i % 10 == 0)
|
|
||||||
{
|
|
||||||
printf("%s ", i != 0 ? "\n" : "");
|
|
||||||
}
|
|
||||||
printf("%s%s", kMiscFlag[flagList[i]].c_str(), flagList[i] != flagList.atBack() ? ", " : "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
if (argc < 2)
|
|
||||||
{
|
|
||||||
printf("usage: npdmtool <file>\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fnd::MemoryBlob file;
|
|
||||||
fnd::io::readFile(argv[1], file);
|
|
||||||
|
|
||||||
// import
|
|
||||||
nx::NpdmBinary npdm;
|
|
||||||
npdm.importBinary(file.getBytes(), file.getSize());
|
|
||||||
|
|
||||||
// npdm binary
|
|
||||||
displayNpdmHeader(npdm);
|
|
||||||
|
|
||||||
// aci binary
|
|
||||||
displayAciHdr(npdm.getAci());
|
|
||||||
displayFac(npdm.getAci().getFac());
|
|
||||||
displaySac(npdm.getAci().getSac());
|
|
||||||
displayKernelCap(npdm.getAci().getKc());
|
|
||||||
|
|
||||||
// acid binary
|
|
||||||
displayAciHdr(npdm.getAcid());
|
|
||||||
displayFac(npdm.getAcid().getFac());
|
|
||||||
displaySac(npdm.getAcid().getSac());
|
|
||||||
displayKernelCap(npdm.getAcid().getKc());
|
|
||||||
|
|
||||||
} catch (const fnd::Exception& e)
|
|
||||||
{
|
|
||||||
printf("%s\n", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
33
programs/nstool/source/NcaProcess.h
Normal file
33
programs/nstool/source/NcaProcess.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fnd/types.h>
|
||||||
|
#include <fnd/SimpleFile.h>
|
||||||
|
#include <nx/nca.h>
|
||||||
|
#include <nx/NcaHeader.h>
|
||||||
|
|
||||||
|
#include "nstool.h"
|
||||||
|
|
||||||
|
class NcaProcess
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NcaProcess();
|
||||||
|
~NcaProcess();
|
||||||
|
|
||||||
|
void process();
|
||||||
|
|
||||||
|
// generic
|
||||||
|
void setInputFile(fnd::IFile& reader);
|
||||||
|
void setInputFileOffset(size_t offset);
|
||||||
|
void setKeyset(const sKeyset* keyset);
|
||||||
|
void setCliOutputMode(CliOutputType type);
|
||||||
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
|
// nca specfic
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string kModuleName = "NcaProcess";
|
||||||
|
|
||||||
|
byte_t mRawHeader[nx::nca::kHeaderSize];
|
||||||
|
std::string mPath;
|
||||||
|
const sKeyset* mKeyset;
|
||||||
|
};
|
683
programs/nstool/source/NpdmProcess.cpp
Normal file
683
programs/nstool/source/NpdmProcess.cpp
Normal file
|
@ -0,0 +1,683 @@
|
||||||
|
#include "NpdmProcess.h"
|
||||||
|
#include <fnd/SimpleFile.h>
|
||||||
|
#include <fnd/MemoryBlob.h>
|
||||||
|
|
||||||
|
const std::string kInstructionType[2] = { "32Bit", "64Bit" };
|
||||||
|
const std::string kProcAddrSpace[4] = { "Unknown", "64Bit", "32Bit", "32Bit no reserved" };
|
||||||
|
const std::string kAciType[2] = { "ACI0", "ACID" };
|
||||||
|
const std::string kMiscFlag[15] = { "EnableDebug", "ForceDebug", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7", "bit8", "bit9", "bit10", "bit11", "bit12", "bit13", "bit14"};
|
||||||
|
const std::string kFsaFlag[64] =
|
||||||
|
{
|
||||||
|
"ApplicationInfo",
|
||||||
|
"BootModeControl",
|
||||||
|
"Calibration",
|
||||||
|
"SystemSaveData",
|
||||||
|
"GameCard",
|
||||||
|
"SaveDataBackUp",
|
||||||
|
"SaveDataManagement",
|
||||||
|
"BisAllRaw",
|
||||||
|
"GameCardRaw",
|
||||||
|
"GameCardPrivate",
|
||||||
|
"SetTime",
|
||||||
|
"ContentManager",
|
||||||
|
"ImageManager",
|
||||||
|
"CreateSaveData",
|
||||||
|
"SystemSaveDataManagement",
|
||||||
|
"BisFileSystem",
|
||||||
|
"SystemUpdate",
|
||||||
|
"SaveDataMeta",
|
||||||
|
"DeviceSaveData",
|
||||||
|
"SettingsControl",
|
||||||
|
"Bit20",
|
||||||
|
"Bit21",
|
||||||
|
"Bit22",
|
||||||
|
"Bit23",
|
||||||
|
"Bit24",
|
||||||
|
"Bit25",
|
||||||
|
"Bit26",
|
||||||
|
"Bit27",
|
||||||
|
"Bit28",
|
||||||
|
"Bit29",
|
||||||
|
"Bit30",
|
||||||
|
"Bit31",
|
||||||
|
"Bit32",
|
||||||
|
"Bit33",
|
||||||
|
"Bit34",
|
||||||
|
"Bit35",
|
||||||
|
"Bit36",
|
||||||
|
"Bit37",
|
||||||
|
"Bit38",
|
||||||
|
"Bit39",
|
||||||
|
"Bit40",
|
||||||
|
"Bit41",
|
||||||
|
"Bit42",
|
||||||
|
"Bit43",
|
||||||
|
"Bit44",
|
||||||
|
"Bit45",
|
||||||
|
"Bit46",
|
||||||
|
"Bit47",
|
||||||
|
"Bit48",
|
||||||
|
"Bit49",
|
||||||
|
"Bit50",
|
||||||
|
"Bit51",
|
||||||
|
"Bit52",
|
||||||
|
"Bit53",
|
||||||
|
"Bit54",
|
||||||
|
"Bit55",
|
||||||
|
"Bit56",
|
||||||
|
"Bit57",
|
||||||
|
"Bit58",
|
||||||
|
"Bit59",
|
||||||
|
"Bit60",
|
||||||
|
"Bit61",
|
||||||
|
"Debug",
|
||||||
|
"FullPermission"
|
||||||
|
};
|
||||||
|
const std::string kSysCall[0x80] =
|
||||||
|
{
|
||||||
|
"svc00",
|
||||||
|
"SetHeapSize",
|
||||||
|
"SetMemoryPermission",
|
||||||
|
"SetMemoryAttribute",
|
||||||
|
"MapMemory",
|
||||||
|
"UnmapMemory",
|
||||||
|
"QueryMemory",
|
||||||
|
"ExitProcess",
|
||||||
|
"CreateThread",
|
||||||
|
"StartThread",
|
||||||
|
"ExitThread",
|
||||||
|
"SleepThread",
|
||||||
|
"GetThreadPriority",
|
||||||
|
"SetThreadPriority",
|
||||||
|
"GetThreadCoreMask",
|
||||||
|
"SetThreadCoreMask",
|
||||||
|
"GetCurrentProcessorNumber",
|
||||||
|
"SignalEvent",
|
||||||
|
"ClearEvent",
|
||||||
|
"MapSharedMemory",
|
||||||
|
"UnmapSharedMemory",
|
||||||
|
"CreateTransferMemory",
|
||||||
|
"CloseHandle",
|
||||||
|
"ResetSignal",
|
||||||
|
"WaitSynchronization",
|
||||||
|
"CancelSynchronization",
|
||||||
|
"ArbitrateLock",
|
||||||
|
"ArbitrateUnlock",
|
||||||
|
"WaitProcessWideKeyAtomic",
|
||||||
|
"SignalProcessWideKey",
|
||||||
|
"GetSystemTick",
|
||||||
|
"ConnectToNamedPort",
|
||||||
|
"SendSyncRequestLight",
|
||||||
|
"SendSyncRequest",
|
||||||
|
"SendSyncRequestWithUserBuffer",
|
||||||
|
"SendAsyncRequestWithUserBuffer",
|
||||||
|
"GetProcessId",
|
||||||
|
"GetThreadId",
|
||||||
|
"Break",
|
||||||
|
"OutputDebugString",
|
||||||
|
"ReturnFromException",
|
||||||
|
"GetInfo",
|
||||||
|
"FlushEntireDataCache",
|
||||||
|
"FlushDataCache",
|
||||||
|
"MapPhysicalMemory",
|
||||||
|
"UnmapPhysicalMemory",
|
||||||
|
"svc2E",
|
||||||
|
"GetLastThreadInfo",
|
||||||
|
"GetResourceLimitLimitValue",
|
||||||
|
"GetResourceLimitCurrentValue",
|
||||||
|
"SetThreadActivity",
|
||||||
|
"GetThreadContext3",
|
||||||
|
"svc34",
|
||||||
|
"svc35",
|
||||||
|
"svc36",
|
||||||
|
"svc37",
|
||||||
|
"svc38",
|
||||||
|
"svc39",
|
||||||
|
"svc3A",
|
||||||
|
"svc3B",
|
||||||
|
"DumpInfo",
|
||||||
|
"svc3D",
|
||||||
|
"svc3E",
|
||||||
|
"svc3F",
|
||||||
|
"CreateSession",
|
||||||
|
"AcceptSession",
|
||||||
|
"ReplyAndReceiveLight",
|
||||||
|
"ReplyAndReceive",
|
||||||
|
"ReplyAndReceiveWithUserBuffer",
|
||||||
|
"CreateEvent",
|
||||||
|
"svc46",
|
||||||
|
"svc47",
|
||||||
|
"svc48",
|
||||||
|
"svc49",
|
||||||
|
"svc4A",
|
||||||
|
"svc4B",
|
||||||
|
"svc4C",
|
||||||
|
"SleepSystem",
|
||||||
|
"ReadWriteRegister",
|
||||||
|
"SetProcessActivity",
|
||||||
|
"CreateSharedMemory",
|
||||||
|
"MapTransferMemory",
|
||||||
|
"UnmapTransferMemory",
|
||||||
|
"CreateInterruptEvent",
|
||||||
|
"QueryPhysicalAddress",
|
||||||
|
"QueryIoMapping",
|
||||||
|
"CreateDeviceAddressSpace",
|
||||||
|
"AttachDeviceAddressSpace",
|
||||||
|
"DetachDeviceAddressSpace",
|
||||||
|
"MapDeviceAddressSpaceByForce",
|
||||||
|
"MapDeviceAddressSpaceAligned",
|
||||||
|
"MapDeviceAddressSpace",
|
||||||
|
"UnmapDeviceAddressSpace",
|
||||||
|
"InvalidateProcessDataCache",
|
||||||
|
"StoreProcessDataCache",
|
||||||
|
"FlushProcessDataCache",
|
||||||
|
"DebugActiveProcess",
|
||||||
|
"BreakDebugProcess",
|
||||||
|
"TerminateDebugProcess",
|
||||||
|
"GetDebugEvent",
|
||||||
|
"ContinueDebugEvent",
|
||||||
|
"GetProcessList",
|
||||||
|
"GetThreadList",
|
||||||
|
"GetDebugThreadContext",
|
||||||
|
"SetDebugThreadContext",
|
||||||
|
"QueryDebugProcessMemory",
|
||||||
|
"ReadDebugProcessMemory",
|
||||||
|
"WriteDebugProcessMemory",
|
||||||
|
"SetHardwareBreakPoint",
|
||||||
|
"GetDebugThreadParam",
|
||||||
|
"svc6E",
|
||||||
|
"svc6F",
|
||||||
|
"CreatePort",
|
||||||
|
"ManageNamedPort",
|
||||||
|
"ConnectToPort",
|
||||||
|
"SetProcessMemoryPermission",
|
||||||
|
"MapProcessMemory",
|
||||||
|
"UnmapProcessMemory",
|
||||||
|
"QueryProcessMemory",
|
||||||
|
"MapProcessCodeMemory",
|
||||||
|
"UnmapProcessCodeMemory",
|
||||||
|
"CreateProcess",
|
||||||
|
"StartProcess",
|
||||||
|
"TerminateProcess",
|
||||||
|
"GetProcessInfo",
|
||||||
|
"CreateResourceLimit",
|
||||||
|
"SetResourceLimitLimitValue",
|
||||||
|
"CallSecureMonitor"
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string kMemMapPerm[2] = { "RW", "RO" };
|
||||||
|
const std::string kMemMapType[2] = { "Io", "Static" };
|
||||||
|
|
||||||
|
const std::string kAcidTarget[2] = { "Development", "Production" };
|
||||||
|
|
||||||
|
void NpdmProcess::validateAcidSignature(const nx::AcidBinary& acid)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
acid.verifyBinary(mKeyset->acid_sign_key);
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
// this is minimal even though it's a warning because it's a validation method
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACID Signature: FAIL\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::validateAciFromAcid(const nx::AciBinary& aci, const nx::AcidBinary& acid)
|
||||||
|
{
|
||||||
|
// check Program ID
|
||||||
|
if (acid.getProgramIdMin() > 0 && aci.getProgramId() < acid.getProgramIdMin())
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI ProgramId: FAIL (Outside Legal Range)\n");
|
||||||
|
}
|
||||||
|
else if (acid.getProgramIdMax() > 0 && aci.getProgramId() > acid.getProgramIdMax())
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI ProgramId: FAIL (Outside Legal Range)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check FAC
|
||||||
|
if (aci.getFac().getFormatVersion() != acid.getFac().getFormatVersion())
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/FAC FormatVersion: FAIL (%d != %d (expected))\n", aci.getFac().getFormatVersion(),acid.getFac().getFormatVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < aci.getFac().getFsaRightsList().getSize(); i++)
|
||||||
|
{
|
||||||
|
bool fsaRightFound = false;
|
||||||
|
for (size_t j = 0; j < acid.getFac().getFsaRightsList().getSize() && fsaRightFound == false; j++)
|
||||||
|
{
|
||||||
|
if (aci.getFac().getFsaRightsList()[i] == acid.getFac().getFsaRightsList()[j])
|
||||||
|
fsaRightFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fsaRightFound == false)
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/FAC FsaRights: FAIL (%s not permitted)\n", kFsaFlag[aci.getFac().getFsaRightsList()[i]].c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < aci.getFac().getContentOwnerIdList().getSize(); i++)
|
||||||
|
{
|
||||||
|
bool rightFound = false;
|
||||||
|
for (size_t j = 0; j < acid.getFac().getContentOwnerIdList().getSize() && rightFound == false; j++)
|
||||||
|
{
|
||||||
|
if (aci.getFac().getContentOwnerIdList()[i] == acid.getFac().getContentOwnerIdList()[j])
|
||||||
|
rightFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightFound == false)
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/FAC ContentOwnerId: FAIL (%08x not permitted)\n", aci.getFac().getContentOwnerIdList()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < aci.getFac().getSaveDataOwnerIdList().getSize(); i++)
|
||||||
|
{
|
||||||
|
bool rightFound = false;
|
||||||
|
for (size_t j = 0; j < acid.getFac().getSaveDataOwnerIdList().getSize() && rightFound == false; j++)
|
||||||
|
{
|
||||||
|
if (aci.getFac().getSaveDataOwnerIdList()[i] == acid.getFac().getSaveDataOwnerIdList()[j])
|
||||||
|
rightFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightFound == false)
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/FAC ContentOwnerId: FAIL (%08x not permitted)\n", aci.getFac().getSaveDataOwnerIdList()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check SAC
|
||||||
|
for (size_t i = 0; i < aci.getSac().getServiceList().getSize(); i++)
|
||||||
|
{
|
||||||
|
bool rightFound = false;
|
||||||
|
for (size_t j = 0; j < acid.getSac().getServiceList().getSize() && rightFound == false; j++)
|
||||||
|
{
|
||||||
|
if (aci.getSac().getServiceList()[i] == acid.getSac().getServiceList()[j])
|
||||||
|
rightFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightFound == false)
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/SAC ServiceList: FAIL (%s%s not permitted)\n", aci.getSac().getServiceList()[i].getName().c_str(), aci.getSac().getServiceList()[i].isServer()? " (Server)" : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check KC
|
||||||
|
// check thread info
|
||||||
|
if (aci.getKc().getThreadInfo().getMaxCpuId() != acid.getKc().getThreadInfo().getMaxCpuId())
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC ThreadInfo/MaxCpuId: FAIL (%d not permitted)\n", aci.getKc().getThreadInfo().getMaxCpuId());
|
||||||
|
}
|
||||||
|
if (aci.getKc().getThreadInfo().getMinCpuId() != acid.getKc().getThreadInfo().getMinCpuId())
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC ThreadInfo/MinCpuId: FAIL (%d not permitted)\n", aci.getKc().getThreadInfo().getMinCpuId());
|
||||||
|
}
|
||||||
|
if (aci.getKc().getThreadInfo().getMaxPriority() != acid.getKc().getThreadInfo().getMaxPriority())
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC ThreadInfo/MaxPriority: FAIL (%d not permitted)\n", aci.getKc().getThreadInfo().getMaxPriority());
|
||||||
|
}
|
||||||
|
if (aci.getKc().getThreadInfo().getMinPriority() != acid.getKc().getThreadInfo().getMinPriority())
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC ThreadInfo/MinPriority: FAIL (%d not permitted)\n", aci.getKc().getThreadInfo().getMinPriority());
|
||||||
|
}
|
||||||
|
// check system calls
|
||||||
|
for (size_t i = 0; i < aci.getKc().getSystemCalls().getSystemCalls().getSize(); i++)
|
||||||
|
{
|
||||||
|
bool rightFound = false;
|
||||||
|
for (size_t j = 0; j < acid.getKc().getSystemCalls().getSystemCalls().getSize() && rightFound == false; j++)
|
||||||
|
{
|
||||||
|
if (aci.getKc().getSystemCalls().getSystemCalls()[i] == acid.getKc().getSystemCalls().getSystemCalls()[j])
|
||||||
|
rightFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightFound == false)
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC SystemCallList: FAIL (%s not permitted)\n", kSysCall[aci.getKc().getSystemCalls().getSystemCalls()[i]].c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check memory maps
|
||||||
|
for (size_t i = 0; i < aci.getKc().getMemoryMaps().getMemoryMaps().getSize(); i++)
|
||||||
|
{
|
||||||
|
bool rightFound = false;
|
||||||
|
for (size_t j = 0; j < acid.getKc().getMemoryMaps().getMemoryMaps().getSize() && rightFound == false; j++)
|
||||||
|
{
|
||||||
|
if (aci.getKc().getMemoryMaps().getMemoryMaps()[i] == acid.getKc().getMemoryMaps().getMemoryMaps()[j])
|
||||||
|
rightFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightFound == false)
|
||||||
|
{
|
||||||
|
const nx::MemoryMappingHandler::sMemoryMapping& map = aci.getKc().getMemoryMaps().getMemoryMaps()[i];
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC MemoryMap: FAIL (0x%016" PRIx64 " - 0x%016" PRIx64 " (perm=%s) (type=%s) not permitted)\n", (uint64_t)map.addr << 12, ((uint64_t)(map.addr + map.size) << 12) - 1, kMemMapPerm[map.perm].c_str(), kMemMapType[map.type].c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < aci.getKc().getMemoryMaps().getIoMemoryMaps().getSize(); i++)
|
||||||
|
{
|
||||||
|
bool rightFound = false;
|
||||||
|
for (size_t j = 0; j < acid.getKc().getMemoryMaps().getIoMemoryMaps().getSize() && rightFound == false; j++)
|
||||||
|
{
|
||||||
|
if (aci.getKc().getMemoryMaps().getIoMemoryMaps()[i] == acid.getKc().getMemoryMaps().getIoMemoryMaps()[j])
|
||||||
|
rightFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightFound == false)
|
||||||
|
{
|
||||||
|
const nx::MemoryMappingHandler::sMemoryMapping& map = aci.getKc().getMemoryMaps().getIoMemoryMaps()[i];
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC IoMemoryMap: FAIL (0x%016" PRIx64 " - 0x%016" PRIx64 " (perm=%s) (type=%s) not permitted)\n", (uint64_t)map.addr << 12, ((uint64_t)(map.addr + map.size) << 12) - 1, kMemMapPerm[map.perm].c_str(), kMemMapType[map.type].c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check interupts
|
||||||
|
for (size_t i = 0; i < aci.getKc().getInterupts().getInteruptList().getSize(); i++)
|
||||||
|
{
|
||||||
|
bool rightFound = false;
|
||||||
|
for (size_t j = 0; j < acid.getKc().getInterupts().getInteruptList().getSize() && rightFound == false; j++)
|
||||||
|
{
|
||||||
|
if (aci.getKc().getInterupts().getInteruptList()[i] == acid.getKc().getInterupts().getInteruptList()[j])
|
||||||
|
rightFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightFound == false)
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC InteruptsList: FAIL (0x%0x not permitted)\n", aci.getKc().getInterupts().getInteruptList()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check misc params
|
||||||
|
if (aci.getKc().getMiscParams().getProgramType() != acid.getKc().getMiscParams().getProgramType())
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC ProgramType: FAIL (%d not permitted)\n", aci.getKc().getMiscParams().getProgramType());
|
||||||
|
}
|
||||||
|
// check kernel version
|
||||||
|
uint32_t aciKernelVersion = (uint32_t)aci.getKc().getKernelVersion().getVerMajor() << 16 | (uint32_t)aci.getKc().getKernelVersion().getVerMinor();
|
||||||
|
uint32_t acidKernelVersion = (uint32_t)acid.getKc().getKernelVersion().getVerMajor() << 16 | (uint32_t)acid.getKc().getKernelVersion().getVerMinor();
|
||||||
|
if (aciKernelVersion < acidKernelVersion)
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC RequiredKernelVersion: FAIL (%d.%d not permitted)\n", aci.getKc().getKernelVersion().getVerMajor(), aci.getKc().getKernelVersion().getVerMinor());
|
||||||
|
}
|
||||||
|
// check handle table size
|
||||||
|
if (aci.getKc().getHandleTableSize().getHandleTableSize() > acid.getKc().getHandleTableSize().getHandleTableSize())
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC HandleTableSize: FAIL (0x%x too large)\n", aci.getKc().getHandleTableSize().getHandleTableSize());
|
||||||
|
}
|
||||||
|
// check misc flags
|
||||||
|
for (size_t i = 0; i < aci.getKc().getMiscFlags().getFlagList().getSize(); i++)
|
||||||
|
{
|
||||||
|
bool rightFound = false;
|
||||||
|
for (size_t j = 0; j < acid.getKc().getMiscFlags().getFlagList().getSize() && rightFound == false; j++)
|
||||||
|
{
|
||||||
|
if (aci.getKc().getMiscFlags().getFlagList()[i] == acid.getKc().getMiscFlags().getFlagList()[j])
|
||||||
|
rightFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightFound == false)
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] ACI/KC MiscFlag: FAIL (%s not permitted)\n", kMiscFlag[aci.getKc().getMiscFlags().getFlagList()[i]].c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::displayNpdmHeader(const nx::NpdmHeader& hdr)
|
||||||
|
{
|
||||||
|
printf("[NPDM HEADER]\n");
|
||||||
|
printf(" Process Architecture Params:\n");
|
||||||
|
printf(" Ins. Type: %s\n", kInstructionType[hdr.getInstructionType()].c_str());
|
||||||
|
printf(" Addr Space: %s\n", kProcAddrSpace[hdr.getProcAddressSpaceType()].c_str());
|
||||||
|
printf(" Main Thread Params:\n");
|
||||||
|
printf(" Priority: %d\n", hdr.getMainThreadPriority());
|
||||||
|
printf(" CpuId: %d\n", hdr.getMainThreadCpuId());
|
||||||
|
printf(" StackSize: 0x%x\n", hdr.getMainThreadStackSize());
|
||||||
|
printf(" TitleInfo:\n");
|
||||||
|
printf(" Version: v%" PRIu32 "\n", hdr.getVersion());
|
||||||
|
printf(" Name: %s\n", hdr.getName().c_str());
|
||||||
|
if (hdr.getProductCode().length())
|
||||||
|
{
|
||||||
|
printf(" ProductCode: %s\n", hdr.getProductCode().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::displayAciHdr(const nx::AciHeader& aci)
|
||||||
|
{
|
||||||
|
printf("[Access Control Info]\n");
|
||||||
|
printf(" ACI Type: %s\n", kAciType[aci.getAciType()].c_str());
|
||||||
|
if (aci.getAciType() == nx::AciBinary::TYPE_ACI0)
|
||||||
|
{
|
||||||
|
printf(" ProgramID: %016" PRIx64 "\n", aci.getProgramId());
|
||||||
|
}
|
||||||
|
else if (aci.getAciType() == nx::AciBinary::TYPE_ACID)
|
||||||
|
{
|
||||||
|
|
||||||
|
printf(" ACID Size: %" PRIx64 "\n", aci.getAcidSize());
|
||||||
|
printf(" Target: %s\n", kAcidTarget[aci.isProduction()].c_str());
|
||||||
|
printf(" ProgramID Restriction\n");
|
||||||
|
printf(" Min: %016" PRIx64 "\n", aci.getProgramIdMin());
|
||||||
|
printf(" Max: %016" PRIx64 "\n", aci.getProgramIdMax());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::displayFac(const nx::FacBinary& fac)
|
||||||
|
{
|
||||||
|
printf("[FS Access Control]\n");
|
||||||
|
printf(" Format Version: %d\n", fac.getFormatVersion());
|
||||||
|
|
||||||
|
if (fac.getFsaRightsList().getSize())
|
||||||
|
{
|
||||||
|
printf(" FS Rights:\n");
|
||||||
|
for (size_t i = 0; i < fac.getFsaRightsList().getSize(); i++)
|
||||||
|
{
|
||||||
|
if (i % 10 == 0)
|
||||||
|
{
|
||||||
|
printf("%s ", i != 0 ? "\n" : "");
|
||||||
|
}
|
||||||
|
printf("%s%s", kFsaFlag[fac.getFsaRightsList()[i]].c_str(), fac.getFsaRightsList()[i] != fac.getFsaRightsList().atBack() ? ", " : "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf(" FS Rights: NONE\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fac.getContentOwnerIdList().getSize())
|
||||||
|
{
|
||||||
|
printf(" Content Owner IDs:\n");
|
||||||
|
for (size_t i = 0; i < fac.getContentOwnerIdList().getSize(); i++)
|
||||||
|
{
|
||||||
|
printf(" 0x%08x\n", fac.getContentOwnerIdList()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fac.getSaveDataOwnerIdList().getSize())
|
||||||
|
{
|
||||||
|
printf(" Save Data Owner IDs:\n");
|
||||||
|
for (size_t i = 0; i < fac.getSaveDataOwnerIdList().getSize(); i++)
|
||||||
|
{
|
||||||
|
printf(" 0x%08x\n", fac.getSaveDataOwnerIdList()[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::displaySac(const nx::SacBinary& sac)
|
||||||
|
{
|
||||||
|
printf("[Service Access Control]\n");
|
||||||
|
printf(" Service List:\n");
|
||||||
|
for (size_t i = 0; i < sac.getServiceList().getSize(); i++)
|
||||||
|
{
|
||||||
|
if (i % 10 == 0)
|
||||||
|
{
|
||||||
|
printf("%s ", i != 0 ? "\n" : "");
|
||||||
|
}
|
||||||
|
printf("%s%s%s", sac.getServiceList()[i].getName().c_str(), sac.getServiceList()[i].isServer() ? "(isSrv)" : "", sac.getServiceList()[i] != sac.getServiceList().atBack() ? ", " : "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::displayKernelCap(const nx::KcBinary& kern)
|
||||||
|
{
|
||||||
|
printf("[Kernel Capabilities]\n");
|
||||||
|
if (kern.getThreadInfo().isSet())
|
||||||
|
{
|
||||||
|
nx::ThreadInfoHandler threadInfo = kern.getThreadInfo();
|
||||||
|
printf(" Thread Priority:\n");
|
||||||
|
printf(" Min: %d\n", threadInfo.getMinPriority());
|
||||||
|
printf(" Max: %d\n", threadInfo.getMaxPriority());
|
||||||
|
printf(" CpuId:\n");
|
||||||
|
printf(" Min: %d\n", threadInfo.getMinCpuId());
|
||||||
|
printf(" Max: %d\n", threadInfo.getMaxCpuId());
|
||||||
|
}
|
||||||
|
if (kern.getSystemCalls().isSet())
|
||||||
|
{
|
||||||
|
fnd::List<uint8_t> syscalls = kern.getSystemCalls().getSystemCalls();
|
||||||
|
printf(" SystemCalls:");
|
||||||
|
printf("\n ");
|
||||||
|
size_t lineLen = 0;
|
||||||
|
for (size_t i = 0; i < syscalls.getSize(); i++)
|
||||||
|
{
|
||||||
|
if (lineLen > 60)
|
||||||
|
{
|
||||||
|
lineLen = 0;
|
||||||
|
printf("\n ");
|
||||||
|
}
|
||||||
|
printf("%s%s", kSysCall[syscalls[i]].c_str(), syscalls[i] != syscalls.atBack() ? ", " : "\n");
|
||||||
|
lineLen += kSysCall[syscalls[i]].length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kern.getMemoryMaps().isSet())
|
||||||
|
{
|
||||||
|
fnd::List<nx::MemoryMappingHandler::sMemoryMapping> maps = kern.getMemoryMaps().getMemoryMaps();
|
||||||
|
fnd::List<nx::MemoryMappingHandler::sMemoryMapping> ioMaps = kern.getMemoryMaps().getIoMemoryMaps();
|
||||||
|
|
||||||
|
printf(" MemoryMaps:\n");
|
||||||
|
for (size_t i = 0; i < maps.getSize(); i++)
|
||||||
|
{
|
||||||
|
printf(" 0x%016" PRIx64 " - 0x%016" PRIx64 " (perm=%s) (type=%s)\n", (uint64_t)maps[i].addr << 12, ((uint64_t)(maps[i].addr + maps[i].size) << 12) - 1, kMemMapPerm[maps[i].perm].c_str(), kMemMapType[maps[i].type].c_str());
|
||||||
|
}
|
||||||
|
//printf(" IoMaps:\n");
|
||||||
|
for (size_t i = 0; i < ioMaps.getSize(); i++)
|
||||||
|
{
|
||||||
|
printf(" 0x%016" PRIx64 " - 0x%016" PRIx64 " (perm=%s) (type=%s)\n", (uint64_t)ioMaps[i].addr << 12, ((uint64_t)(ioMaps[i].addr + ioMaps[i].size) << 12) - 1, kMemMapPerm[ioMaps[i].perm].c_str(), kMemMapType[ioMaps[i].type].c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kern.getInterupts().isSet())
|
||||||
|
{
|
||||||
|
fnd::List<uint16_t> interupts = kern.getInterupts().getInteruptList();
|
||||||
|
printf(" Interupts Flags:\n");
|
||||||
|
for (uint32_t i = 0; i < interupts.getSize(); i++)
|
||||||
|
{
|
||||||
|
if (i % 10 == 0)
|
||||||
|
{
|
||||||
|
printf("%s ", i != 0 ? "\n" : "");
|
||||||
|
}
|
||||||
|
printf("0x%x%s", interupts[i], interupts[i] != interupts.atBack() ? ", " : "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kern.getMiscParams().isSet())
|
||||||
|
{
|
||||||
|
printf(" ProgramType: %d\n", kern.getMiscParams().getProgramType());
|
||||||
|
}
|
||||||
|
if (kern.getKernelVersion().isSet())
|
||||||
|
{
|
||||||
|
printf(" Kernel Version: %d.%d\n", kern.getKernelVersion().getVerMajor(), kern.getKernelVersion().getVerMinor());
|
||||||
|
}
|
||||||
|
if (kern.getHandleTableSize().isSet())
|
||||||
|
{
|
||||||
|
printf(" Handle Table Size: 0x%x\n", kern.getHandleTableSize().getHandleTableSize());
|
||||||
|
}
|
||||||
|
if (kern.getMiscFlags().isSet())
|
||||||
|
{
|
||||||
|
fnd::List<nx::MiscFlagsHandler::Flags> flagList = kern.getMiscFlags().getFlagList();
|
||||||
|
|
||||||
|
printf(" Misc Flags:\n");
|
||||||
|
for (uint32_t i = 0; i < flagList.getSize(); i++)
|
||||||
|
{
|
||||||
|
if (i % 10 == 0)
|
||||||
|
{
|
||||||
|
printf("%s ", i != 0 ? "\n" : "");
|
||||||
|
}
|
||||||
|
printf("%s%s", kMiscFlag[flagList[i]].c_str(), flagList[i] != flagList.atBack() ? ", " : "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NpdmProcess::NpdmProcess() :
|
||||||
|
mReader(nullptr),
|
||||||
|
mOffset(0),
|
||||||
|
mKeyset(nullptr),
|
||||||
|
mCliOutputType(OUTPUT_NORMAL),
|
||||||
|
mVerify(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::process()
|
||||||
|
{
|
||||||
|
fnd::MemoryBlob scratch;
|
||||||
|
scratch.alloc(mReader->size());
|
||||||
|
mReader->read(scratch.getBytes(), 0, scratch.getSize());
|
||||||
|
|
||||||
|
mNpdm.importBinary(scratch.getBytes(), scratch.getSize());
|
||||||
|
|
||||||
|
if (mVerify)
|
||||||
|
{
|
||||||
|
validateAcidSignature(mNpdm.getAcid());
|
||||||
|
validateAciFromAcid(mNpdm.getAci(), mNpdm.getAcid());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||||
|
{
|
||||||
|
// npdm binary
|
||||||
|
displayNpdmHeader(mNpdm);
|
||||||
|
|
||||||
|
// aci binary
|
||||||
|
displayAciHdr(mNpdm.getAci());
|
||||||
|
displayFac(mNpdm.getAci().getFac());
|
||||||
|
displaySac(mNpdm.getAci().getSac());
|
||||||
|
displayKernelCap(mNpdm.getAci().getKc());
|
||||||
|
|
||||||
|
// acid binary
|
||||||
|
displayAciHdr(mNpdm.getAcid());
|
||||||
|
displayFac(mNpdm.getAcid().getFac());
|
||||||
|
displaySac(mNpdm.getAcid().getSac());
|
||||||
|
displayKernelCap(mNpdm.getAcid().getKc());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::setInputFile(fnd::IFile& reader)
|
||||||
|
{
|
||||||
|
mReader = &reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::setInputFileOffset(size_t offset)
|
||||||
|
{
|
||||||
|
mOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::setKeyset(const sKeyset* keyset)
|
||||||
|
{
|
||||||
|
mKeyset = keyset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::setCliOutputMode(CliOutputType type)
|
||||||
|
{
|
||||||
|
mCliOutputType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NpdmProcess::setVerifyMode(bool verify)
|
||||||
|
{
|
||||||
|
mVerify = verify;
|
||||||
|
}
|
41
programs/nstool/source/NpdmProcess.h
Normal file
41
programs/nstool/source/NpdmProcess.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fnd/types.h>
|
||||||
|
#include <fnd/SimpleFile.h>
|
||||||
|
#include <nx/NpdmBinary.h>
|
||||||
|
|
||||||
|
#include "nstool.h"
|
||||||
|
|
||||||
|
class NpdmProcess
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NpdmProcess();
|
||||||
|
|
||||||
|
void process();
|
||||||
|
|
||||||
|
void setInputFile(fnd::IFile& reader);
|
||||||
|
void setInputFileOffset(size_t offset);
|
||||||
|
void setKeyset(const sKeyset* keyset);
|
||||||
|
void setCliOutputMode(CliOutputType type);
|
||||||
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string kModuleName = "NpdmProcess";
|
||||||
|
|
||||||
|
fnd::IFile* mReader;
|
||||||
|
size_t mOffset;
|
||||||
|
const sKeyset* mKeyset;
|
||||||
|
CliOutputType mCliOutputType;
|
||||||
|
bool mVerify;
|
||||||
|
|
||||||
|
nx::NpdmBinary mNpdm;
|
||||||
|
|
||||||
|
void validateAcidSignature(const nx::AcidBinary& acid);
|
||||||
|
void validateAciFromAcid(const nx::AciBinary& aci, const nx::AcidBinary& acid);
|
||||||
|
|
||||||
|
void displayNpdmHeader(const nx::NpdmHeader& hdr);
|
||||||
|
void displayAciHdr(const nx::AciHeader& aci);
|
||||||
|
void displayFac(const nx::FacBinary& fac);
|
||||||
|
void displaySac(const nx::SacBinary& sac);
|
||||||
|
void displayKernelCap(const nx::KcBinary& kern);
|
||||||
|
};
|
176
programs/nstool/source/PfsProcess.cpp
Normal file
176
programs/nstool/source/PfsProcess.cpp
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
#include "PfsProcess.h"
|
||||||
|
#include <fnd/io.h>
|
||||||
|
|
||||||
|
void PfsProcess::displayHeader()
|
||||||
|
{
|
||||||
|
printf("[PartitionFS]\n");
|
||||||
|
printf(" Type: %s\n", mPfs.getFsType() == mPfs.TYPE_PFS0? "PFS0" : "HFS0");
|
||||||
|
printf(" FileNum: %u\n", mPfs.getFileList().getSize());
|
||||||
|
if (mMountName.empty() == false)
|
||||||
|
printf(" MountPoint: %s%s\n", mMountName.c_str(), mMountName.at(mMountName.length()-1) != '/' ? "/" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::displayFs()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < mPfs.getFileList().getSize(); i++)
|
||||||
|
{
|
||||||
|
printf(" %s", mPfs.getFileList()[i].name.c_str());
|
||||||
|
if (mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
|
{
|
||||||
|
if (mPfs.getFsType() == mPfs.TYPE_PFS0)
|
||||||
|
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ")\n", mPfs.getFileList()[i].offset, mPfs.getFileList()[i].size);
|
||||||
|
else
|
||||||
|
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ", hash_protected_size=0x%" PRIx64 ")\n", mPfs.getFileList()[i].offset, mPfs.getFileList()[i].size, mPfs.getFileList()[i].hash_protected_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PfsProcess::determineHeaderSize(const nx::sPfsHeader* hdr)
|
||||||
|
{
|
||||||
|
size_t fileEntrySize = 0;
|
||||||
|
if (std::string(hdr->signature, 4) == nx::pfs::kPfsSig)
|
||||||
|
fileEntrySize = sizeof(nx::sPfsFile);
|
||||||
|
else
|
||||||
|
fileEntrySize = sizeof(nx::sHashedPfsFile);
|
||||||
|
|
||||||
|
return sizeof(nx::sPfsHeader) + hdr->file_num.get() * fileEntrySize + hdr->name_table_size.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::validateHfs()
|
||||||
|
{
|
||||||
|
fnd::MemoryBlob scratch;
|
||||||
|
crypto::sha::sSha256Hash hash;
|
||||||
|
const fnd::List<nx::PfsHeader::sFile>& file = mPfs.getFileList();
|
||||||
|
for (size_t i = 0; i < file.getSize(); i++)
|
||||||
|
{
|
||||||
|
scratch.alloc(file[i].hash_protected_size);
|
||||||
|
mReader->read(scratch.getBytes(), mOffset + file[i].offset, file[i].hash_protected_size);
|
||||||
|
crypto::sha::Sha256(scratch.getBytes(), scratch.getSize(), hash.bytes);
|
||||||
|
if (hash != file[i].hash)
|
||||||
|
{
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] HFS0 %s%s%s: FAIL (bad hash)\n", !mMountName.empty()? mMountName.c_str() : "", !mMountName.empty()? "/" : "", file[i].name.c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::extractFs()
|
||||||
|
{
|
||||||
|
// allocate scratch memory
|
||||||
|
fnd::MemoryBlob scratch;
|
||||||
|
scratch.alloc(kFileExportBlockSize);
|
||||||
|
|
||||||
|
// make extract dir
|
||||||
|
fnd::io::makeDirectory(mExtractPath);
|
||||||
|
|
||||||
|
fnd::SimpleFile outFile;
|
||||||
|
const fnd::List<nx::PfsHeader::sFile>& file = mPfs.getFileList();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < file.getSize(); i++)
|
||||||
|
{
|
||||||
|
outFile.open(mExtractPath + kPathSeparator + file[i].name, outFile.Create);
|
||||||
|
mReader->seek(mOffset + file[i].offset);
|
||||||
|
for (size_t j = 0; j < (file[i].size / kFileExportBlockSize); j++)
|
||||||
|
{
|
||||||
|
mReader->read(scratch.getBytes(), kFileExportBlockSize);
|
||||||
|
outFile.write(scratch.getBytes(), kFileExportBlockSize);
|
||||||
|
}
|
||||||
|
if (file[i].size % kFileExportBlockSize)
|
||||||
|
{
|
||||||
|
mReader->read(scratch.getBytes(), file[i].size % kFileExportBlockSize);
|
||||||
|
outFile.write(scratch.getBytes(), file[i].size % kFileExportBlockSize);
|
||||||
|
}
|
||||||
|
outFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PfsProcess::PfsProcess() :
|
||||||
|
mReader(nullptr),
|
||||||
|
mOffset(0),
|
||||||
|
mKeyset(nullptr),
|
||||||
|
mCliOutputType(OUTPUT_NORMAL),
|
||||||
|
mVerify(false),
|
||||||
|
mExtractPath(),
|
||||||
|
mExtract(false),
|
||||||
|
mMountName(),
|
||||||
|
mListFs(false),
|
||||||
|
mPfs()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::process()
|
||||||
|
{
|
||||||
|
fnd::MemoryBlob scratch;
|
||||||
|
|
||||||
|
// open minimum header to get full header size
|
||||||
|
scratch.alloc(sizeof(nx::sPfsHeader));
|
||||||
|
mReader->read(scratch.getBytes(), mOffset, scratch.getSize());
|
||||||
|
size_t pfsHeaderSize = determineHeaderSize(((nx::sPfsHeader*)scratch.getBytes()));
|
||||||
|
|
||||||
|
// open minimum header to get full header size
|
||||||
|
scratch.alloc(pfsHeaderSize);
|
||||||
|
mReader->read(scratch.getBytes(), mOffset, scratch.getSize());
|
||||||
|
mPfs.importBinary(scratch.getBytes(), scratch.getSize());
|
||||||
|
|
||||||
|
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||||
|
displayHeader();
|
||||||
|
if (mListFs || mCliOutputType >= OUTPUT_VERBOSE)
|
||||||
|
displayFs();
|
||||||
|
if (mPfs.getFsType() == mPfs.TYPE_HFS0 && mVerify)
|
||||||
|
validateHfs();
|
||||||
|
if (mExtract)
|
||||||
|
extractFs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::setInputFile(fnd::IFile& reader)
|
||||||
|
{
|
||||||
|
mReader = &reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::setInputFileOffset(size_t offset)
|
||||||
|
{
|
||||||
|
mOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::setKeyset(const sKeyset* keyset)
|
||||||
|
{
|
||||||
|
mKeyset = keyset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::setCliOutputMode(CliOutputType type)
|
||||||
|
{
|
||||||
|
mCliOutputType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::setVerifyMode(bool verify)
|
||||||
|
{
|
||||||
|
mVerify = verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::setMountPointName(const std::string& mount_name)
|
||||||
|
{
|
||||||
|
mMountName = mount_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::setExtractPath(const std::string& path)
|
||||||
|
{
|
||||||
|
mExtract = true;
|
||||||
|
mExtractPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfsProcess::setListFs(bool list_fs)
|
||||||
|
{
|
||||||
|
mListFs = list_fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nx::PfsHeader& PfsProcess::getPfsHeader() const
|
||||||
|
{
|
||||||
|
return mPfs;
|
||||||
|
}
|
53
programs/nstool/source/PfsProcess.h
Normal file
53
programs/nstool/source/PfsProcess.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fnd/types.h>
|
||||||
|
#include <fnd/SimpleFile.h>
|
||||||
|
#include <nx/PfsHeader.h>
|
||||||
|
|
||||||
|
#include "nstool.h"
|
||||||
|
|
||||||
|
class PfsProcess
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PfsProcess();
|
||||||
|
|
||||||
|
void process();
|
||||||
|
|
||||||
|
// generic
|
||||||
|
void setInputFile(fnd::IFile& reader);
|
||||||
|
void setInputFileOffset(size_t offset);
|
||||||
|
void setKeyset(const sKeyset* keyset);
|
||||||
|
void setCliOutputMode(CliOutputType type);
|
||||||
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
|
// pfs specific
|
||||||
|
void setMountPointName(const std::string& mount_name);
|
||||||
|
void setExtractPath(const std::string& path);
|
||||||
|
void setListFs(bool list_fs);
|
||||||
|
|
||||||
|
const nx::PfsHeader& getPfsHeader() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string kModuleName = "PfsProcess";
|
||||||
|
static const size_t kFileExportBlockSize = 0x1000000;
|
||||||
|
|
||||||
|
fnd::IFile* mReader;
|
||||||
|
size_t mOffset;
|
||||||
|
const sKeyset* mKeyset;
|
||||||
|
CliOutputType mCliOutputType;
|
||||||
|
bool mVerify;
|
||||||
|
|
||||||
|
|
||||||
|
std::string mExtractPath;
|
||||||
|
bool mExtract;
|
||||||
|
std::string mMountName;
|
||||||
|
bool mListFs;
|
||||||
|
|
||||||
|
nx::PfsHeader mPfs;
|
||||||
|
|
||||||
|
void displayHeader();
|
||||||
|
void displayFs();
|
||||||
|
size_t determineHeaderSize(const nx::sPfsHeader* hdr);
|
||||||
|
void validateHfs();
|
||||||
|
void extractFs();
|
||||||
|
};
|
0
programs/nstool/source/RomfsProcess.h
Normal file
0
programs/nstool/source/RomfsProcess.h
Normal file
537
programs/nstool/source/UserSettings.cpp
Normal file
537
programs/nstool/source/UserSettings.cpp
Normal file
|
@ -0,0 +1,537 @@
|
||||||
|
#include "UserSettings.h"
|
||||||
|
#include "version.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fnd/SimpleFile.h>
|
||||||
|
#include <fnd/SimpleTextOutput.h>
|
||||||
|
#include <fnd/MemoryBlob.h>
|
||||||
|
#include <fnd/ResourceFileReader.h>
|
||||||
|
#include <nx/NcaUtils.h>
|
||||||
|
#include <nx/AesKeygen.h>
|
||||||
|
#include <nx/xci.h>
|
||||||
|
#include <nx/pfs.h>
|
||||||
|
#include <nx/nca.h>
|
||||||
|
#include <nx/npdm.h>
|
||||||
|
#include <nx/romfs.h>
|
||||||
|
|
||||||
|
UserSettings::UserSettings()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void UserSettings::parseCmdArgs(int argc, char** argv)
|
||||||
|
{
|
||||||
|
sCmdArgs args;
|
||||||
|
populateCmdArgs(argc, argv, args);
|
||||||
|
populateKeyset(args);
|
||||||
|
populateUserSettings(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserSettings::showHelp()
|
||||||
|
{
|
||||||
|
printf("NSTool v%d.%d (C) %s\n", VER_MAJOR, VER_MINOR, AUTHORS);
|
||||||
|
printf("Built: %s %s\n\n", __TIME__, __DATE__);
|
||||||
|
|
||||||
|
printf("Usage: nstool [options... ] <file>\n");
|
||||||
|
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, npdm]\n");
|
||||||
|
printf(" -y, --verify Verify file\n");
|
||||||
|
printf(" -v, --verbose Verbose output\n");
|
||||||
|
printf(" -q, --quiet Minimal output\n");
|
||||||
|
printf("\n XCI (GameCard Image)\n");
|
||||||
|
printf(" nstool [--listfs] [--update <dir> --normal <dir> --secure <dir>] <.xci file>\n");
|
||||||
|
printf(" --listfs Print file system in embedded partitions\n");
|
||||||
|
printf(" --update Extract \"update\" 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(" nstool [--listfs] [--fsdir <dir>] <file>\n");
|
||||||
|
printf(" --listfs Print file system\n");
|
||||||
|
printf(" --fsdir Extract file system to directory\n");
|
||||||
|
/*
|
||||||
|
printf("\n NCA (Nintendo Content Archive)\n");
|
||||||
|
printf(" nstool [--listfs] [--bodykey <key> --titlekey <key>] [--part0 <dir> ...] <.nca file>\n");
|
||||||
|
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(" --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");
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string UserSettings::getInputPath() const
|
||||||
|
{
|
||||||
|
return mInputPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sKeyset& UserSettings::getKeyset() const
|
||||||
|
{
|
||||||
|
return mKeyset;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileType UserSettings::getFileType() const
|
||||||
|
{
|
||||||
|
return mFileType;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UserSettings::isVerifyFile() const
|
||||||
|
{
|
||||||
|
return mVerifyFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
CliOutputType UserSettings::getCliOutputType() const
|
||||||
|
{
|
||||||
|
return mOutputType;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UserSettings::isListFs() const
|
||||||
|
{
|
||||||
|
return mListFs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sOptional<std::string>& UserSettings::getUpdatePath() const
|
||||||
|
{
|
||||||
|
return mUpdatePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sOptional<std::string>& UserSettings::getNormalPath() const
|
||||||
|
{
|
||||||
|
return mNormalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sOptional<std::string>& UserSettings::getSecurePath() const
|
||||||
|
{
|
||||||
|
return mSecurePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sOptional<std::string>& UserSettings::getFsPath() const
|
||||||
|
{
|
||||||
|
return mFsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UserSettings::populateCmdArgs(int argc, char** argv, sCmdArgs& cmd_args)
|
||||||
|
{
|
||||||
|
// create vector of args
|
||||||
|
std::vector<std::string> args;
|
||||||
|
for (size_t i = 0; i < (size_t)argc; i++)
|
||||||
|
{
|
||||||
|
args.push_back(argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// show help text
|
||||||
|
if (args.size() < 2)
|
||||||
|
{
|
||||||
|
showHelp();
|
||||||
|
throw fnd::Exception(kModuleName, "Not enough arguments.");
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_args.clear();
|
||||||
|
cmd_args.input_path = args.back();
|
||||||
|
|
||||||
|
for (size_t i = 1; i < args.size(); i++)
|
||||||
|
{
|
||||||
|
if (args[i] == "-h" || args[i] == "--help")
|
||||||
|
{
|
||||||
|
showHelp();
|
||||||
|
throw fnd::Exception(kModuleName, "Nothing to do.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 1; i+1 < args.size(); i++)
|
||||||
|
{
|
||||||
|
bool hasParamter = args[i+1][0] != '-' && i+2 < args.size();
|
||||||
|
|
||||||
|
if (args[i] == "-d" || args[i] == "--dev")
|
||||||
|
{
|
||||||
|
if (hasParamter) throw fnd::Exception(kModuleName, args[i] + " does not take a parameter.");
|
||||||
|
cmd_args.devkit_keys = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "-y" || args[i] == "--verify")
|
||||||
|
{
|
||||||
|
if (hasParamter) throw fnd::Exception(kModuleName, args[i] + " does not take a parameter.");
|
||||||
|
cmd_args.verify_file = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "-v" || args[i] == "--verbose")
|
||||||
|
{
|
||||||
|
if (hasParamter) throw fnd::Exception(kModuleName, args[i] + " does not take a parameter.");
|
||||||
|
cmd_args.verbose_output = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "-q" || args[i] == "--quiet")
|
||||||
|
{
|
||||||
|
if (hasParamter) throw fnd::Exception(kModuleName, args[i] + " does not take a parameter.");
|
||||||
|
cmd_args.minimal_output = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "-k" || args[i] == "--keyset")
|
||||||
|
{
|
||||||
|
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||||
|
cmd_args.keyset_path = args[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "-t" || args[i] == "--type")
|
||||||
|
{
|
||||||
|
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||||
|
cmd_args.file_type = args[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "--listfs")
|
||||||
|
{
|
||||||
|
if (hasParamter) throw fnd::Exception(kModuleName, args[i] + " does not take a parameter.");
|
||||||
|
cmd_args.list_fs = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "--update")
|
||||||
|
{
|
||||||
|
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||||
|
cmd_args.update_path = args[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "--normal")
|
||||||
|
{
|
||||||
|
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||||
|
cmd_args.normal_path = args[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "--secure")
|
||||||
|
{
|
||||||
|
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||||
|
cmd_args.secure_path = args[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "--fsdir")
|
||||||
|
{
|
||||||
|
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||||
|
cmd_args.fs_path = args[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "--titlekey")
|
||||||
|
{
|
||||||
|
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||||
|
cmd_args.nca_titlekey = args[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "--bodykey")
|
||||||
|
{
|
||||||
|
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||||
|
cmd_args.nca_bodykey = args[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (args[i] == "-o")
|
||||||
|
{
|
||||||
|
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||||
|
cmd_args.output_path = args[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, args[i] + " is not recognised.");
|
||||||
|
}
|
||||||
|
|
||||||
|
i += hasParamter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserSettings::populateKeyset(sCmdArgs& args)
|
||||||
|
{
|
||||||
|
crypto::aes::sAes128Key zeros_aes_key;
|
||||||
|
crypto::aes::sAesXts128Key zeros_aes_xts_key;
|
||||||
|
memset((void*)&zeros_aes_key, 0, sizeof(crypto::aes::sAes128Key));
|
||||||
|
memset((void*)&zeros_aes_xts_key, 0, sizeof(crypto::aes::sAesXts128Key));
|
||||||
|
memset((void*)&mKeyset, 0, sizeof(sKeyset));
|
||||||
|
|
||||||
|
fnd::ResourceFileReader res;
|
||||||
|
if (args.keyset_path.isSet)
|
||||||
|
{
|
||||||
|
res.processFile(*args.keyset_path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// open other resource files in $HOME/.switch/prod.keys (or $HOME/.switch/dev.keys if -d/--dev is set).
|
||||||
|
char* home = nullptr;
|
||||||
|
if (home == nullptr) home = getenv("HOME");
|
||||||
|
if (home == nullptr) home = getenv("USERPROFILE");
|
||||||
|
if (home == nullptr) return;
|
||||||
|
|
||||||
|
const std::string kKeysetNameStr[2] = {"prod.keys", "dev.keys"};
|
||||||
|
|
||||||
|
std::string keyset_path = std::string(home) + std::string("/") + ".switch" + std::string("/") + kKeysetNameStr[args.devkit_keys.isSet ? *args.devkit_keys : 0];
|
||||||
|
//std::cout << keyset_path << std::endl;
|
||||||
|
res.processFile(keyset_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string kMasterKeyBase = "master_key_";
|
||||||
|
const std::string kPackage1KeyBase = "package1_key_";
|
||||||
|
const std::string kPackage2KeyBase = "package2_key_";
|
||||||
|
const std::string kTicketCommonKeyBase[2] = { "titlekek_", "ticket_commonkey_" };
|
||||||
|
const std::string kNcaKeyAreaKeyBase[3] = {"key_area_key_application_", "key_area_key_ocean_", "key_area_key_system_"};
|
||||||
|
const std::string kKeySource = "source";
|
||||||
|
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"};
|
||||||
|
|
||||||
|
const std::string kNcaHeaderKey[2] = {"header_key", "nca_header_key"};
|
||||||
|
const std::string kXciHeaderKey = "xci_header_key";
|
||||||
|
const std::string kKekGenSource = "aes_kek_generation_";
|
||||||
|
const std::string kKeyGenSource = "aes_key_generation_";
|
||||||
|
|
||||||
|
const std::string kNcaHeaderSignKeyBase = "nca_header_sign_key_";
|
||||||
|
const std::string kXciHeaderSignKeyBase = "xci_header_sign_key_";
|
||||||
|
const std::string kAcidSignKeyBase = "acid_sign_key_";
|
||||||
|
const std::string kPackage2SignKeyBase = "package2_sign_key_";
|
||||||
|
const std::string kRsaKeyComponent[2] = {"private", "modulus"};
|
||||||
|
|
||||||
|
// sources
|
||||||
|
crypto::aes::sAes128Key master_key[kMasterKeyNum] = { zeros_aes_key };
|
||||||
|
crypto::aes::sAes128Key package2_key_source = zeros_aes_key;
|
||||||
|
crypto::aes::sAes128Key ticket_titlekek_source = zeros_aes_key;
|
||||||
|
crypto::aes::sAes128Key key_area_key_source[3] = { zeros_aes_key, zeros_aes_key, zeros_aes_key };
|
||||||
|
crypto::aes::sAes128Key aes_kek_generation_source = zeros_aes_key;
|
||||||
|
crypto::aes::sAes128Key aes_key_generation_source = zeros_aes_key;
|
||||||
|
|
||||||
|
std::string key;
|
||||||
|
|
||||||
|
#define _SAVE_KEYDATA(key_name, array, len) \
|
||||||
|
key = res[(key_name)]; \
|
||||||
|
if (key.empty() == false) { \
|
||||||
|
decodeHexStringToBytes((key_name), key, (byte_t*)array, len); \
|
||||||
|
}
|
||||||
|
|
||||||
|
_SAVE_KEYDATA(kPackage2KeyBase + kKeySource, package2_key_source.key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kTicketCommonKeyBase[0] + kKeySource, ticket_titlekek_source.key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kTicketCommonKeyBase[1] + kKeySource, ticket_titlekek_source.key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kNcaKeyAreaKeyBase[0] + kKeySource, key_area_key_source[0].key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kNcaKeyAreaKeyBase[1] + kKeySource, key_area_key_source[1].key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kNcaKeyAreaKeyBase[2] + kKeySource, key_area_key_source[2].key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kKekGenSource + kKeySource, aes_kek_generation_source.key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kKeyGenSource + kKeySource, aes_key_generation_source.key, 0x10);
|
||||||
|
|
||||||
|
// Store Key Variants/Derivatives
|
||||||
|
for (size_t i = 0; i < kMasterKeyNum; i++)
|
||||||
|
{
|
||||||
|
_SAVE_KEYDATA(kMasterKeyBase + kKeyIndex[i], master_key[i].key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kPackage1KeyBase + kKeyIndex[i], mKeyset.package1_key[i].key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kPackage2KeyBase + kKeyIndex[i], mKeyset.package2_key[i].key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kTicketCommonKeyBase[0] + kKeyIndex[i], mKeyset.ticket.titlekey_kek[i].key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kTicketCommonKeyBase[1] + kKeyIndex[i], mKeyset.ticket.titlekey_kek[i].key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kNcaKeyAreaKeyBase[0] + kKeyIndex[i], mKeyset.nca.key_area_key[0][i].key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kNcaKeyAreaKeyBase[1] + kKeyIndex[i], mKeyset.nca.key_area_key[1][i].key, 0x10);
|
||||||
|
_SAVE_KEYDATA(kNcaKeyAreaKeyBase[2] + kKeyIndex[i], mKeyset.nca.key_area_key[2][i].key, 0x10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// store nca header key
|
||||||
|
_SAVE_KEYDATA(kNcaHeaderKey[0], mKeyset.nca.header_key.key[0], 0x20);
|
||||||
|
_SAVE_KEYDATA(kNcaHeaderKey[1], mKeyset.nca.header_key.key[0], 0x20);
|
||||||
|
// store xci header key
|
||||||
|
_SAVE_KEYDATA(kXciHeaderKey, mKeyset.xci.header_key.key, 0x10);
|
||||||
|
|
||||||
|
// store rsa keys
|
||||||
|
_SAVE_KEYDATA(kNcaHeaderSignKeyBase + kRsaKeyComponent[0], mKeyset.nca.header_sign_key.priv_exponent, 0x100);
|
||||||
|
_SAVE_KEYDATA(kNcaHeaderSignKeyBase + kRsaKeyComponent[1], mKeyset.nca.header_sign_key.modulus, 0x100);
|
||||||
|
|
||||||
|
_SAVE_KEYDATA(kXciHeaderSignKeyBase + kRsaKeyComponent[0], mKeyset.xci.header_sign_key.priv_exponent, 0x100);
|
||||||
|
_SAVE_KEYDATA(kXciHeaderSignKeyBase + kRsaKeyComponent[1], mKeyset.xci.header_sign_key.modulus, 0x100);
|
||||||
|
|
||||||
|
_SAVE_KEYDATA(kAcidSignKeyBase + kRsaKeyComponent[0], mKeyset.acid_sign_key.priv_exponent, 0x100);
|
||||||
|
_SAVE_KEYDATA(kAcidSignKeyBase + kRsaKeyComponent[1], mKeyset.acid_sign_key.modulus, 0x100);
|
||||||
|
|
||||||
|
_SAVE_KEYDATA(kPackage2SignKeyBase + kRsaKeyComponent[0], mKeyset.package2_sign_key.priv_exponent, 0x100);
|
||||||
|
_SAVE_KEYDATA(kPackage2SignKeyBase + kRsaKeyComponent[1], mKeyset.package2_sign_key.modulus, 0x100);
|
||||||
|
|
||||||
|
// save keydata from input args
|
||||||
|
if (args.nca_bodykey.isSet)
|
||||||
|
{
|
||||||
|
if (args.nca_bodykey.var.length() == (sizeof(crypto::aes::sAes128Key)*2))
|
||||||
|
{
|
||||||
|
decodeHexStringToBytes("--bodykey", args.nca_bodykey.var, mKeyset.nca.manual_body_key_aesctr.key, sizeof(crypto::aes::sAes128Key));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
decodeHexStringToBytes("--bodykey", args.nca_bodykey.var, mKeyset.nca.manual_body_key_aesxts.key[0], sizeof(crypto::aes::sAesXts128Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.nca_titlekey.isSet)
|
||||||
|
{
|
||||||
|
decodeHexStringToBytes("--titlekey", args.nca_titlekey.var, mKeyset.nca.manual_title_key.key, sizeof(crypto::aes::sAes128Key));
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef _SAVE_KEYDATA
|
||||||
|
|
||||||
|
// Derive keys
|
||||||
|
for (size_t i = 0; i < kMasterKeyNum; i++)
|
||||||
|
{
|
||||||
|
if (master_key[i] != zeros_aes_key)
|
||||||
|
{
|
||||||
|
if (aes_kek_generation_source != zeros_aes_key && aes_key_generation_source != zeros_aes_key)
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < nx::nca::kKeyAreaEncryptionKeyNum; j++)
|
||||||
|
{
|
||||||
|
if (key_area_key_source[j] != zeros_aes_key && mKeyset.nca.key_area_key[j][i] == zeros_aes_key)
|
||||||
|
{
|
||||||
|
nx::AesKeygen::generateKey(mKeyset.nca.key_area_key[j][i].key, aes_kek_generation_source.key, key_area_key_source[j].key, aes_key_generation_source.key, master_key[i].key);
|
||||||
|
//printf("nca keak %d/%02d ", j, i);
|
||||||
|
//fnd::SimpleTextOutput::hexDump(mKeyset.nca.key_area_key[j][i].key, 0x10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticket_titlekek_source != zeros_aes_key && mKeyset.ticket.titlekey_kek[i] == zeros_aes_key)
|
||||||
|
{
|
||||||
|
nx::AesKeygen::generateKey(mKeyset.ticket.titlekey_kek[i].key, ticket_titlekek_source.key, master_key[i].key);
|
||||||
|
//printf("ticket titlekek %02d ", i);
|
||||||
|
//fnd::SimpleTextOutput::hexDump(mKeyset.ticket.titlekey_kek[i].key, 0x10);
|
||||||
|
}
|
||||||
|
if (package2_key_source != zeros_aes_key && mKeyset.package2_key[i] == zeros_aes_key)
|
||||||
|
{
|
||||||
|
nx::AesKeygen::generateKey(mKeyset.package2_key[i].key, package2_key_source.key, master_key[i].key);
|
||||||
|
//printf("package2 key %02d ", i);
|
||||||
|
//fnd::SimpleTextOutput::hexDump(mKeyset.package2_key[i].key, 0x10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserSettings::populateUserSettings(sCmdArgs& args)
|
||||||
|
{
|
||||||
|
// check invalid input
|
||||||
|
if (args.input_path.isSet == false)
|
||||||
|
throw fnd::Exception(kModuleName, "No input file specified");
|
||||||
|
if (args.verbose_output.isSet && args.minimal_output.isSet)
|
||||||
|
throw fnd::Exception(kModuleName, "Options --verbose and --quiet cannot be used together.");
|
||||||
|
|
||||||
|
// save arguments
|
||||||
|
mInputPath = *args.input_path;
|
||||||
|
mVerifyFile = args.verify_file.isSet;
|
||||||
|
mListFs = args.list_fs.isSet;
|
||||||
|
mUpdatePath = args.update_path;
|
||||||
|
mNormalPath = args.normal_path;
|
||||||
|
mSecurePath = args.secure_path;
|
||||||
|
mFsPath = args.fs_path;
|
||||||
|
|
||||||
|
// determine output path
|
||||||
|
if (args.verbose_output.isSet)
|
||||||
|
mOutputType = OUTPUT_VERBOSE;
|
||||||
|
else if (args.minimal_output.isSet)
|
||||||
|
mOutputType = OUTPUT_MINIMAL;
|
||||||
|
else
|
||||||
|
mOutputType = OUTPUT_NORMAL;
|
||||||
|
|
||||||
|
// 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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UserSettings::decodeHexStringToBytes(const std::string& name, const std::string& str, byte_t* out, size_t out_len)
|
||||||
|
{
|
||||||
|
size_t size = str.size();
|
||||||
|
if ((size % 2) || ((size / 2) != out_len))
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "Key: \"" + name + "\" has incorrect length");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < out_len; i++)
|
||||||
|
{
|
||||||
|
out[i] = (charToByte(str[i * 2]) << 4) | charToByte(str[(i * 2) + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 == "xci")
|
||||||
|
type = FILE_XCI;
|
||||||
|
else if ( str == "partitionfs" \
|
||||||
|
|| str == "pfs" || str == "pfs0" \
|
||||||
|
|| str == "hfs" || str == "hfs0" \
|
||||||
|
|| str == "nsp")
|
||||||
|
type = FILE_PARTITIONFS;
|
||||||
|
else if (str == "romfs")
|
||||||
|
type = FILE_ROMFS;
|
||||||
|
else if (str == "nca")
|
||||||
|
type = FILE_NCA;
|
||||||
|
else if (str == "npdm")
|
||||||
|
type = FILE_NPDM;
|
||||||
|
else
|
||||||
|
type = FILE_INVALID;
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
||||||
|
{
|
||||||
|
static const size_t kMaxReadSize = 0x1000;
|
||||||
|
FileType file_type = FILE_INVALID;
|
||||||
|
fnd::SimpleFile file;
|
||||||
|
fnd::MemoryBlob blob;
|
||||||
|
|
||||||
|
// open file
|
||||||
|
file.open(path, file.Read);
|
||||||
|
|
||||||
|
// read file
|
||||||
|
blob.alloc(MIN(kMaxReadSize, file.size()));
|
||||||
|
file.read(blob.getBytes(), 0, blob.getSize());
|
||||||
|
// close file
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// prepare decrypted NCA data
|
||||||
|
byte_t nca_raw[nx::nca::kHeaderSize];
|
||||||
|
nx::sNcaHeader* nca_header = (nx::sNcaHeader*)(nca_raw + nx::NcaUtils::sectorToOffset(1));
|
||||||
|
|
||||||
|
if (blob.getSize() >= nx::nca::kHeaderSize)
|
||||||
|
{
|
||||||
|
nx::NcaUtils::decryptNcaHeader(blob.getBytes(), nca_raw, mKeyset.nca.header_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _QUICK_CAST resolves to a pointer of type 'st' located at blob.getBytes() + 'oft'
|
||||||
|
#define _QUICK_CAST(st, oft) ((st*)(blob.getBytes() + (oft)))
|
||||||
|
#define _ASSERT_SIZE(size) (blob.getSize() >= (size))
|
||||||
|
|
||||||
|
// test npdm
|
||||||
|
if (_ASSERT_SIZE(0x100 + sizeof(nx::sXciHeader)) && memcmp(_QUICK_CAST(nx::sXciHeader, 0x100)->signature, nx::xci::kXciSig.c_str(), 4) == 0)
|
||||||
|
file_type = FILE_XCI;
|
||||||
|
// test pfs0
|
||||||
|
else if (_ASSERT_SIZE(sizeof(nx::sPfsHeader)) && memcmp(_QUICK_CAST(nx::sPfsHeader, 0)->signature, nx::pfs::kPfsSig.c_str(), 4) == 0)
|
||||||
|
file_type = FILE_PARTITIONFS;
|
||||||
|
// test hfs0
|
||||||
|
else if (_ASSERT_SIZE(sizeof(nx::sPfsHeader)) && memcmp(_QUICK_CAST(nx::sPfsHeader, 0)->signature, nx::pfs::kHashedPfsSig.c_str(), 4) == 0)
|
||||||
|
file_type = FILE_PARTITIONFS;
|
||||||
|
// test romfs
|
||||||
|
else if (_ASSERT_SIZE(sizeof(nx::sRomfsHeader)) && _QUICK_CAST(nx::sRomfsHeader, 0)->header_size.get() == sizeof(nx::sRomfsHeader) && _QUICK_CAST(nx::sRomfsHeader, 0)->header_size.get() == _QUICK_CAST(nx::sRomfsHeader, 0)->sections[0].offset.get())
|
||||||
|
file_type = FILE_ROMFS;
|
||||||
|
// test nca2
|
||||||
|
else if (_ASSERT_SIZE(nx::nca::kHeaderSize) && memcmp(nca_header->signature, nx::nca::kNca2Sig.c_str(), 4) == 0)
|
||||||
|
file_type = FILE_NCA;
|
||||||
|
// test nca3
|
||||||
|
else if (_ASSERT_SIZE(nx::nca::kHeaderSize) && memcmp(nca_header->signature, nx::nca::kNca3Sig.c_str(), 4) == 0)
|
||||||
|
file_type = FILE_NCA;
|
||||||
|
// test npdm
|
||||||
|
else if (_ASSERT_SIZE(sizeof(nx::sNpdmHeader)) && memcmp(_QUICK_CAST(nx::sNpdmHeader, 0)->signature(), nx::npdm::kNpdmStructSig.c_str(), 4) == 0)
|
||||||
|
file_type = FILE_NPDM;
|
||||||
|
// else unrecognised
|
||||||
|
else
|
||||||
|
file_type = FILE_INVALID;
|
||||||
|
|
||||||
|
#undef _ASSERT_SIZE
|
||||||
|
#undef _QUICK_CAST
|
||||||
|
|
||||||
|
return file_type;
|
||||||
|
}
|
89
programs/nstool/source/UserSettings.h
Normal file
89
programs/nstool/source/UserSettings.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fnd/types.h>
|
||||||
|
#include "nstool.h"
|
||||||
|
|
||||||
|
class UserSettings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UserSettings();
|
||||||
|
|
||||||
|
void parseCmdArgs(int argc, char** argv);
|
||||||
|
void showHelp();
|
||||||
|
|
||||||
|
// generic options
|
||||||
|
const std::string getInputPath() const;
|
||||||
|
const sKeyset& getKeyset() const;
|
||||||
|
FileType getFileType() const;
|
||||||
|
bool isVerifyFile() const;
|
||||||
|
CliOutputType getCliOutputType() const;
|
||||||
|
|
||||||
|
// specialised toggles
|
||||||
|
bool isListFs() const;
|
||||||
|
|
||||||
|
// specialised paths
|
||||||
|
const sOptional<std::string>& getUpdatePath() const;
|
||||||
|
const sOptional<std::string>& getNormalPath() const;
|
||||||
|
const sOptional<std::string>& getSecurePath() const;
|
||||||
|
const sOptional<std::string>& getFsPath() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string kModuleName = "UserSettings";
|
||||||
|
|
||||||
|
struct sCmdArgs
|
||||||
|
{
|
||||||
|
sOptional<std::string> input_path;
|
||||||
|
sOptional<std::string> output_path;
|
||||||
|
sOptional<bool> devkit_keys;
|
||||||
|
sOptional<std::string> keyset_path;
|
||||||
|
sOptional<std::string> file_type;
|
||||||
|
sOptional<bool> verify_file;
|
||||||
|
sOptional<bool> verbose_output;
|
||||||
|
sOptional<bool> minimal_output;
|
||||||
|
sOptional<bool> list_fs;
|
||||||
|
sOptional<std::string> update_path;
|
||||||
|
sOptional<std::string> normal_path;
|
||||||
|
sOptional<std::string> secure_path;
|
||||||
|
sOptional<std::string> fs_path;
|
||||||
|
sOptional<std::string> nca_titlekey;
|
||||||
|
sOptional<std::string> nca_bodykey;
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
input_path.isSet = false;
|
||||||
|
output_path.isSet = false;
|
||||||
|
devkit_keys.isSet = false;
|
||||||
|
keyset_path.isSet = false;
|
||||||
|
file_type.isSet = false;
|
||||||
|
verify_file.isSet = false;
|
||||||
|
verbose_output.isSet = false;
|
||||||
|
minimal_output.isSet = false;
|
||||||
|
list_fs.isSet = false;
|
||||||
|
update_path.isSet = false;
|
||||||
|
normal_path.isSet = false;
|
||||||
|
secure_path.isSet = false;
|
||||||
|
fs_path.isSet = false;
|
||||||
|
nca_titlekey.isSet = false;
|
||||||
|
nca_bodykey.isSet = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string mInputPath;
|
||||||
|
FileType mFileType;
|
||||||
|
sKeyset mKeyset;
|
||||||
|
bool mVerifyFile;
|
||||||
|
CliOutputType mOutputType;
|
||||||
|
|
||||||
|
bool mListFs;
|
||||||
|
sOptional<std::string> mUpdatePath;
|
||||||
|
sOptional<std::string> mNormalPath;
|
||||||
|
sOptional<std::string> mSecurePath;
|
||||||
|
sOptional<std::string> mFsPath;
|
||||||
|
|
||||||
|
void populateCmdArgs(int argc, char** argv, sCmdArgs& cmd_args);
|
||||||
|
void populateKeyset(sCmdArgs& args);
|
||||||
|
void populateUserSettings(sCmdArgs& args);
|
||||||
|
void decodeHexStringToBytes(const std::string& name, const std::string& str, byte_t* out, size_t out_len);
|
||||||
|
FileType getFileTypeFromString(const std::string& type_str);
|
||||||
|
FileType determineFileTypeFromFile(const std::string& path);
|
||||||
|
};
|
271
programs/nstool/source/XciProcess.cpp
Normal file
271
programs/nstool/source/XciProcess.cpp
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
#include "XciProcess.h"
|
||||||
|
#include <fnd/SimpleTextOutput.h>
|
||||||
|
#include <nx/XciUtils.h>
|
||||||
|
|
||||||
|
inline const char* getBoolStr(bool isTrue)
|
||||||
|
{
|
||||||
|
return isTrue? "TRUE" : "FALSE";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char* getRomSizeStr(byte_t rom_size)
|
||||||
|
{
|
||||||
|
const char* str = "unknown";
|
||||||
|
switch (rom_size)
|
||||||
|
{
|
||||||
|
case (nx::xci::ROM_SIZE_1GB) :
|
||||||
|
str = "1GB";
|
||||||
|
break;
|
||||||
|
case (nx::xci::ROM_SIZE_2GB) :
|
||||||
|
str = "2GB";
|
||||||
|
break;
|
||||||
|
case (nx::xci::ROM_SIZE_4GB) :
|
||||||
|
str = "4GB";
|
||||||
|
break;
|
||||||
|
case (nx::xci::ROM_SIZE_8GB) :
|
||||||
|
str = "8GB";
|
||||||
|
break;
|
||||||
|
case (nx::xci::ROM_SIZE_16GB) :
|
||||||
|
str = "16GB";
|
||||||
|
break;
|
||||||
|
case (nx::xci::ROM_SIZE_32GB) :
|
||||||
|
str = "32GB";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char* getCardClockRate(uint32_t acc_ctrl_1)
|
||||||
|
{
|
||||||
|
const char* str = "unknown";
|
||||||
|
switch (acc_ctrl_1)
|
||||||
|
{
|
||||||
|
case (nx::xci::CLOCK_RATE_25) :
|
||||||
|
str = "20 MHz";
|
||||||
|
break;
|
||||||
|
case (nx::xci::CLOCK_RATE_50) :
|
||||||
|
str = "50 MHz";
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void XciProcess::displayHeader()
|
||||||
|
{
|
||||||
|
printf("[XCI HEADER]\n");
|
||||||
|
printf(" Magic: HEAD\n");
|
||||||
|
printf(" RomAreaStartPage: 0x%0x", mHdr.getRomAreaStartPage());
|
||||||
|
if (mHdr.getRomAreaStartPage() != -1)
|
||||||
|
printf(" (0x%" PRIx64 ")", nx::XciUtils::blockToAddr(mHdr.getRomAreaStartPage()));
|
||||||
|
printf("\n");
|
||||||
|
printf(" BackupAreaStartPage: 0x%0x", mHdr.getBackupAreaStartPage());
|
||||||
|
if (mHdr.getBackupAreaStartPage() != -1)
|
||||||
|
printf(" (0x%" PRIx64 ")", nx::XciUtils::blockToAddr(mHdr.getBackupAreaStartPage()));
|
||||||
|
printf("\n");
|
||||||
|
printf(" KekIndex: %d\n", mHdr.getKekIndex());
|
||||||
|
printf(" TitleKeyDecIndex: %d\n", mHdr.getTitleKeyDecIndex());
|
||||||
|
printf(" RomSize: 0x%x (%s)\n", mHdr.getRomSizeType(), getRomSizeStr(mHdr.getRomSizeType()));
|
||||||
|
printf(" CardHeaderVersion: %d\n", mHdr.getCardHeaderVersion());
|
||||||
|
printf(" Flags: 0x%x\n", mHdr.getFlags());
|
||||||
|
printf(" AutoBoot: %s\n", getBoolStr(_HAS_BIT(mHdr.getFlags(), nx::xci::FLAG_AUTOBOOT)));
|
||||||
|
printf(" HistoryErase: %s\n", getBoolStr(_HAS_BIT(mHdr.getFlags(), nx::xci::FLAG_HISTORY_ERASE)));
|
||||||
|
printf(" RepairTool: %s\n", getBoolStr(_HAS_BIT(mHdr.getFlags(), nx::xci::FLAG_REPAIR_TOOL)));
|
||||||
|
printf(" PackageId: 0x%" PRIx64 "\n", mHdr.getPackageId());
|
||||||
|
printf(" ValidDataEndPage: 0x%x", mHdr.getValidDataEndPage());
|
||||||
|
if (mHdr.getValidDataEndPage() != -1)
|
||||||
|
printf(" (0x%" PRIx64 ")", nx::XciUtils::blockToAddr(mHdr.getValidDataEndPage()));
|
||||||
|
printf("\n");
|
||||||
|
printf(" AesIv: ");
|
||||||
|
fnd::SimpleTextOutput::hexDump(mHdr.getAesCbcIv().iv, sizeof(mHdr.getAesCbcIv().iv));
|
||||||
|
printf(" PartitionFs:\n");
|
||||||
|
printf(" Offset: 0x%" PRIx64 "\n", mHdr.getPartitionFsAddress());
|
||||||
|
printf(" Size: 0x%" PRIx64 "\n", mHdr.getPartitionFsSize());
|
||||||
|
printf(" Hash: ");
|
||||||
|
fnd::SimpleTextOutput::hexDump(mHdr.getPartitionFsHash().bytes, sizeof(mHdr.getPartitionFsHash().bytes));
|
||||||
|
printf(" InitialData:\n");
|
||||||
|
printf(" Hash: ");
|
||||||
|
fnd::SimpleTextOutput::hexDump(mHdr.getInitialDataHash().bytes, sizeof(mHdr.getInitialDataHash().bytes));
|
||||||
|
printf(" SelSec: 0x%x\n", mHdr.getSelSec());
|
||||||
|
printf(" SelT1Key: 0x%x\n", mHdr.getSelT1Key());
|
||||||
|
printf(" SelKey: 0x%x\n", mHdr.getSelKey());
|
||||||
|
printf(" LimArea: 0x%x", mHdr.getLimAreaPage());
|
||||||
|
if (mHdr.getLimAreaPage() != -1)
|
||||||
|
printf(" (0x%" PRIx64 ")", nx::XciUtils::blockToAddr(mHdr.getLimAreaPage()));
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
|
||||||
|
printf(" FwVersion: v%d.%d\n", mHdr.getFwVerMajor(), mHdr.getFwVerMinor());
|
||||||
|
printf(" AccCtrl1: 0x%x\n", mHdr.getAccCtrl1());
|
||||||
|
printf(" CardClockRate: %s\n", getCardClockRate(mHdr.getAccCtrl1()));
|
||||||
|
printf(" Wait1TimeRead: 0x%x\n", mHdr.getWait1TimeRead());
|
||||||
|
printf(" Wait2TimeRead: 0x%x\n", mHdr.getWait2TimeRead());
|
||||||
|
printf(" Wait1TimeWrite: 0x%x\n", mHdr.getWait1TimeWrite());
|
||||||
|
printf(" Wait2TimeWrite: 0x%x\n", mHdr.getWait2TimeWrite());
|
||||||
|
printf(" FwMode: 0x%x\n", mHdr.getFwMode());
|
||||||
|
printf(" UppVersion: %d\n", mHdr.getUppVersion());
|
||||||
|
printf(" UppHash: ");
|
||||||
|
fnd::SimpleTextOutput::hexDump(mHdr.getUppHash(), 8);
|
||||||
|
printf(" UppId: %016" PRIx64 "\n", mHdr.getUppId());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XciProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash)
|
||||||
|
{
|
||||||
|
fnd::MemoryBlob scratch;
|
||||||
|
crypto::sha::sSha256Hash calc_hash;
|
||||||
|
scratch.alloc(len);
|
||||||
|
mReader->read(scratch.getBytes(), offset, len);
|
||||||
|
crypto::sha::Sha256(scratch.getBytes(), scratch.getSize(), calc_hash.bytes);
|
||||||
|
return calc_hash.compare(test_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::validateXciSignature()
|
||||||
|
{
|
||||||
|
crypto::sha::sSha256Hash calc_hash;
|
||||||
|
crypto::sha::Sha256((byte_t*)&mHdrPage.header, sizeof(nx::sXciHeader), calc_hash.bytes);
|
||||||
|
if (crypto::rsa::pkcs::rsaVerify(mKeyset->xci.header_sign_key, crypto::sha::HASH_SHA256, calc_hash.bytes, mHdrPage.signature) != 0)
|
||||||
|
{
|
||||||
|
// this is minimal even though it's a warning because it's a validation method
|
||||||
|
if (mCliOutputType >= OUTPUT_MINIMAL)
|
||||||
|
printf("[WARNING] XCI Header Signature: FAIL \n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::processRootPfs()
|
||||||
|
{
|
||||||
|
if (mVerify)
|
||||||
|
{
|
||||||
|
if (validateRegionOfFile(mOffset + mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().bytes) == false)
|
||||||
|
{
|
||||||
|
printf("[WARNING] XCI Root HFS0: FAIL (bad hash)\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mRootPfs.setInputFile(*mReader);
|
||||||
|
mRootPfs.setInputFileOffset(mOffset + mHdr.getPartitionFsAddress());
|
||||||
|
mRootPfs.setListFs(mListFs);
|
||||||
|
mRootPfs.setVerifyMode(mVerify);
|
||||||
|
mRootPfs.setCliOutputMode(mCliOutputType);
|
||||||
|
mRootPfs.setMountPointName(kXciMountPointName);
|
||||||
|
mRootPfs.process();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::processPartitionPfs()
|
||||||
|
{
|
||||||
|
const fnd::List<nx::PfsHeader::sFile>& rootPartitions = mRootPfs.getPfsHeader().getFileList();
|
||||||
|
for (size_t i = 0; i < rootPartitions.getSize(); i++)
|
||||||
|
{
|
||||||
|
PfsProcess tmp;
|
||||||
|
tmp.setInputFile(*mReader);
|
||||||
|
tmp.setInputFileOffset(mOffset + mHdr.getPartitionFsAddress() + rootPartitions[i].offset);
|
||||||
|
tmp.setListFs(mListFs);
|
||||||
|
tmp.setVerifyMode(mVerify);
|
||||||
|
tmp.setCliOutputMode(mCliOutputType);
|
||||||
|
tmp.setMountPointName(kXciMountPointName + rootPartitions[i].name);
|
||||||
|
if (mUpdatePath.doExtract && rootPartitions[i].name == "update")
|
||||||
|
tmp.setExtractPath(mUpdatePath.path);
|
||||||
|
if (mNormalPath.doExtract && rootPartitions[i].name == "normal")
|
||||||
|
tmp.setExtractPath(mNormalPath.path);
|
||||||
|
if (mSecurePath.doExtract && rootPartitions[i].name == "secure")
|
||||||
|
tmp.setExtractPath(mSecurePath.path);
|
||||||
|
tmp.process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XciProcess::XciProcess() :
|
||||||
|
mReader(nullptr),
|
||||||
|
mOffset(0),
|
||||||
|
mKeyset(nullptr),
|
||||||
|
mCliOutputType(OUTPUT_NORMAL),
|
||||||
|
mVerify(false),
|
||||||
|
mListFs(false),
|
||||||
|
mRootPfs(),
|
||||||
|
mUpdatePfs(),
|
||||||
|
mNormalPfs(),
|
||||||
|
mSecurePfs()
|
||||||
|
{
|
||||||
|
mUpdatePath.doExtract = false;
|
||||||
|
mNormalPath.doExtract = false;
|
||||||
|
mSecurePath.doExtract = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::process()
|
||||||
|
{
|
||||||
|
fnd::MemoryBlob scratch;
|
||||||
|
|
||||||
|
// read header page
|
||||||
|
mReader->read((byte_t*)&mHdrPage, mOffset, sizeof(nx::sXciHeaderPage));
|
||||||
|
|
||||||
|
// allocate memory for and decrypt sXciHeader
|
||||||
|
scratch.alloc(sizeof(nx::sXciHeader));
|
||||||
|
nx::XciUtils::decryptXciHeader((const byte_t*)&mHdrPage.header, scratch.getBytes(), mKeyset->xci.header_key.key);
|
||||||
|
|
||||||
|
// validate header signature
|
||||||
|
if (mVerify)
|
||||||
|
{
|
||||||
|
validateXciSignature();
|
||||||
|
}
|
||||||
|
|
||||||
|
// deserialise header
|
||||||
|
mHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
||||||
|
|
||||||
|
// display header
|
||||||
|
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||||
|
{
|
||||||
|
displayHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
// process root partition
|
||||||
|
processRootPfs();
|
||||||
|
|
||||||
|
// process partitions
|
||||||
|
processPartitionPfs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::setInputFile(fnd::IFile& reader)
|
||||||
|
{
|
||||||
|
mReader = &reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::setInputFileOffset(size_t offset)
|
||||||
|
{
|
||||||
|
mOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::setKeyset(const sKeyset* keyset)
|
||||||
|
{
|
||||||
|
mKeyset = keyset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::setCliOutputMode(CliOutputType type)
|
||||||
|
{
|
||||||
|
mCliOutputType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::setVerifyMode(bool verify)
|
||||||
|
{
|
||||||
|
mVerify = verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::setUpdateExtractPath(const std::string& path)
|
||||||
|
{
|
||||||
|
mUpdatePath.path = path;
|
||||||
|
mUpdatePath.doExtract = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::setNormalExtractPath(const std::string& path)
|
||||||
|
{
|
||||||
|
mNormalPath.path = path;
|
||||||
|
mNormalPath.doExtract = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::setSecureExtractPath(const std::string& path)
|
||||||
|
{
|
||||||
|
mSecurePath.path = path;
|
||||||
|
mSecurePath.doExtract = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XciProcess::setListFs(bool list_fs)
|
||||||
|
{
|
||||||
|
mListFs = list_fs;
|
||||||
|
}
|
60
programs/nstool/source/XciProcess.h
Normal file
60
programs/nstool/source/XciProcess.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fnd/types.h>
|
||||||
|
#include <fnd/SimpleFile.h>
|
||||||
|
#include <nx/XciHeader.h>
|
||||||
|
|
||||||
|
#include "nstool.h"
|
||||||
|
|
||||||
|
#include "PfsProcess.h"
|
||||||
|
|
||||||
|
|
||||||
|
class XciProcess
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
XciProcess();
|
||||||
|
|
||||||
|
void process();
|
||||||
|
|
||||||
|
// generic
|
||||||
|
void setInputFile(fnd::IFile& reader);
|
||||||
|
void setInputFileOffset(size_t offset);
|
||||||
|
void setKeyset(const sKeyset* keyset);
|
||||||
|
void setCliOutputMode(CliOutputType type);
|
||||||
|
void setVerifyMode(bool verify);
|
||||||
|
|
||||||
|
// xci specific
|
||||||
|
void setUpdateExtractPath(const std::string& path);
|
||||||
|
void setNormalExtractPath(const std::string& path);
|
||||||
|
void setSecureExtractPath(const std::string& path);
|
||||||
|
void setListFs(bool list_fs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string kModuleName = "XciProcess";
|
||||||
|
const std::string kXciMountPointName = "gamecard:/";
|
||||||
|
static const size_t kFileExportBlockSize = 0x1000000;
|
||||||
|
|
||||||
|
fnd::IFile* mReader;
|
||||||
|
size_t mOffset;
|
||||||
|
const sKeyset* mKeyset;
|
||||||
|
CliOutputType mCliOutputType;
|
||||||
|
bool mVerify;
|
||||||
|
|
||||||
|
struct sExtract
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
bool doExtract;
|
||||||
|
} mUpdatePath, mNormalPath, mSecurePath;
|
||||||
|
|
||||||
|
bool mListFs;
|
||||||
|
|
||||||
|
nx::sXciHeaderPage mHdrPage;
|
||||||
|
nx::XciHeader mHdr;
|
||||||
|
PfsProcess mRootPfs, mUpdatePfs, mNormalPfs, mSecurePfs;
|
||||||
|
|
||||||
|
void displayHeader();
|
||||||
|
bool validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash);
|
||||||
|
void validateXciSignature();
|
||||||
|
void processRootPfs();
|
||||||
|
void processPartitionPfs();
|
||||||
|
};
|
97
programs/nstool/source/main.cpp
Normal file
97
programs/nstool/source/main.cpp
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
#include <cstdio>
|
||||||
|
#include "UserSettings.h"
|
||||||
|
#include "XciProcess.h"
|
||||||
|
#include "PfsProcess.h"
|
||||||
|
//#include "RomfsProcess.h"
|
||||||
|
//#include "NcaProcess.h"
|
||||||
|
#include "NpdmProcess.h"
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
UserSettings user_set;
|
||||||
|
try {
|
||||||
|
user_set.parseCmdArgs(argc, argv);
|
||||||
|
|
||||||
|
fnd::SimpleFile inputFile;
|
||||||
|
inputFile.open(user_set.getInputPath(), inputFile.Read);
|
||||||
|
|
||||||
|
if (user_set.getFileType() == FILE_XCI)
|
||||||
|
{
|
||||||
|
XciProcess xci;
|
||||||
|
|
||||||
|
xci.setInputFile(inputFile);
|
||||||
|
|
||||||
|
xci.setKeyset(&user_set.getKeyset());
|
||||||
|
xci.setCliOutputMode(user_set.getCliOutputType());
|
||||||
|
xci.setVerifyMode(user_set.isVerifyFile());
|
||||||
|
|
||||||
|
if (user_set.getUpdatePath().isSet)
|
||||||
|
xci.setUpdateExtractPath(user_set.getUpdatePath().var);
|
||||||
|
if (user_set.getNormalPath().isSet)
|
||||||
|
xci.setNormalExtractPath(user_set.getNormalPath().var);
|
||||||
|
if (user_set.getSecurePath().isSet)
|
||||||
|
xci.setSecureExtractPath(user_set.getSecurePath().var);
|
||||||
|
xci.setListFs(user_set.isListFs());
|
||||||
|
|
||||||
|
xci.process();
|
||||||
|
}
|
||||||
|
else if (user_set.getFileType() == FILE_PARTITIONFS)
|
||||||
|
{
|
||||||
|
PfsProcess pfs;
|
||||||
|
|
||||||
|
pfs.setInputFile(inputFile);
|
||||||
|
pfs.setKeyset(&user_set.getKeyset());
|
||||||
|
pfs.setCliOutputMode(user_set.getCliOutputType());
|
||||||
|
pfs.setVerifyMode(user_set.isVerifyFile());
|
||||||
|
|
||||||
|
if (user_set.getFsPath().isSet)
|
||||||
|
pfs.setExtractPath(user_set.getFsPath().var);
|
||||||
|
pfs.setListFs(user_set.isListFs());
|
||||||
|
|
||||||
|
pfs.process();
|
||||||
|
}
|
||||||
|
else if (user_set.getFileType() == FILE_ROMFS)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
RomfsProcess romfs;
|
||||||
|
|
||||||
|
romfs.setRomfsPath(user_set.getInputPath());
|
||||||
|
romfs.setExtractPath(user_set.getFsPath());
|
||||||
|
romfs.setKeyset(user_set.getKeyset());
|
||||||
|
romfs.setCliOutputMode(user_set.getCliOutputType());
|
||||||
|
romfs.setVerifyMode(user_set.isVerifyFile());
|
||||||
|
|
||||||
|
romfs.process();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
else if (user_set.getFileType() == FILE_NCA)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
NcaProcess nca;
|
||||||
|
|
||||||
|
nca.setNcaPath(user_set.getInputPath());
|
||||||
|
nca.setKeyset(user_set.getKeyset());
|
||||||
|
nca.setCliOutputMode(user_set.getCliOutputType());
|
||||||
|
nca.setVerifyMode(user_set.isVerifyFile());
|
||||||
|
|
||||||
|
nca.process();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
else if (user_set.getFileType() == FILE_NPDM)
|
||||||
|
{
|
||||||
|
NpdmProcess npdm;
|
||||||
|
|
||||||
|
npdm.setInputFile(inputFile);
|
||||||
|
npdm.setKeyset(&user_set.getKeyset());
|
||||||
|
npdm.setCliOutputMode(user_set.getCliOutputType());
|
||||||
|
npdm.setVerifyMode(user_set.isVerifyFile());
|
||||||
|
|
||||||
|
npdm.process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const fnd::Exception& e) {
|
||||||
|
printf("\n\n%s\n", e.what());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
93
programs/nstool/source/nstool.h
Normal file
93
programs/nstool/source/nstool.h
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#pragma once
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fnd/types.h>
|
||||||
|
#include <crypto/aes.h>
|
||||||
|
#include <crypto/rsa.h>
|
||||||
|
#include <nx/nca.h>
|
||||||
|
|
||||||
|
static const size_t kMasterKeyNum = 0x20;
|
||||||
|
static const size_t kNcaKeakNum = nx::nca::kKeyAreaEncryptionKeyNum;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
const std::string kPathSeparator = "\\";
|
||||||
|
#else
|
||||||
|
const std::string kPathSeparator = "/";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum FileType
|
||||||
|
{
|
||||||
|
FILE_XCI,
|
||||||
|
FILE_PARTITIONFS,
|
||||||
|
FILE_ROMFS,
|
||||||
|
FILE_NCA,
|
||||||
|
FILE_NPDM,
|
||||||
|
FILE_INVALID = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CliOutputType
|
||||||
|
{
|
||||||
|
OUTPUT_MINIMAL,
|
||||||
|
OUTPUT_NORMAL,
|
||||||
|
OUTPUT_VERBOSE
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct sOptional
|
||||||
|
{
|
||||||
|
bool isSet;
|
||||||
|
T var;
|
||||||
|
inline const T& operator=(const T& other) { isSet = true; var = other; return var; }
|
||||||
|
inline const sOptional<T>& operator=(const sOptional<T>& other)
|
||||||
|
{
|
||||||
|
isSet = other.isSet;
|
||||||
|
if (isSet) {
|
||||||
|
var = other.var;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline T& operator*() { return var; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sKeyset
|
||||||
|
{
|
||||||
|
crypto::rsa::sRsa2048Key acid_sign_key;
|
||||||
|
|
||||||
|
crypto::aes::sAes128Key package1_key[kMasterKeyNum];
|
||||||
|
crypto::rsa::sRsa2048Key package2_sign_key;
|
||||||
|
crypto::aes::sAes128Key package2_key[kMasterKeyNum];
|
||||||
|
|
||||||
|
struct sNcaData
|
||||||
|
{
|
||||||
|
crypto::rsa::sRsa2048Key header_sign_key;
|
||||||
|
crypto::aes::sAesXts128Key header_key;
|
||||||
|
crypto::aes::sAes128Key key_area_key[kNcaKeakNum][kMasterKeyNum];
|
||||||
|
|
||||||
|
crypto::aes::sAes128Key manual_title_key;
|
||||||
|
crypto::aes::sAes128Key manual_body_key_aesctr;
|
||||||
|
crypto::aes::sAesXts128Key manual_body_key_aesxts;
|
||||||
|
} nca;
|
||||||
|
|
||||||
|
struct sXciData
|
||||||
|
{
|
||||||
|
crypto::rsa::sRsa2048Key header_sign_key;
|
||||||
|
crypto::aes::sAes128Key header_key;
|
||||||
|
} xci;
|
||||||
|
|
||||||
|
struct sTicketData
|
||||||
|
{
|
||||||
|
crypto::rsa::sRsa2048Key sign_key;
|
||||||
|
crypto::aes::sAes128Key titlekey_kek[kMasterKeyNum];
|
||||||
|
} ticket;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline byte_t charToByte(char chr)
|
||||||
|
{
|
||||||
|
if (chr >= 'a' && chr <= 'f')
|
||||||
|
return (chr - 'a') + 0xa;
|
||||||
|
else if (chr >= 'A' && chr <= 'F')
|
||||||
|
return (chr - 'A') + 0xa;
|
||||||
|
else if (chr >= '0' && chr <= '9')
|
||||||
|
return chr - '0';
|
||||||
|
return 0;
|
||||||
|
}
|
4
programs/nstool/source/version.h
Normal file
4
programs/nstool/source/version.h
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#pragma once
|
||||||
|
#define VER_MAJOR 0
|
||||||
|
#define VER_MINOR 1
|
||||||
|
#define AUTHORS "jakcron"
|
|
@ -1,47 +0,0 @@
|
||||||
# Sources
|
|
||||||
SRC_DIR = source
|
|
||||||
OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c)))
|
|
||||||
|
|
||||||
# External dependencies
|
|
||||||
DEPENDS = nx crypto fnd
|
|
||||||
LIB_DIR = ../../lib
|
|
||||||
LIBS = $(foreach dep,$(DEPENDS), -L"$(LIB_DIR)/lib$(dep)" -l$(dep))
|
|
||||||
INCS = $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include")
|
|
||||||
|
|
||||||
BIN_DIR = bin
|
|
||||||
OUTPUT = $(BIN_DIR)/$(shell basename $(CURDIR))
|
|
||||||
|
|
||||||
# Compiler Settings
|
|
||||||
CXXFLAGS = -std=c++11 $(INCS) -D__STDC_FORMAT_MACROS -Wall -Wno-unused-value
|
|
||||||
ifeq ($(OS),Windows_NT)
|
|
||||||
# Windows Only Flags/Libs
|
|
||||||
CC = x86_64-w64-mingw32-gcc
|
|
||||||
CXX = x86_64-w64-mingw32-g++
|
|
||||||
CFLAGS += -Wno-unused-but-set-variable
|
|
||||||
CXXFLAGS += -Wno-unused-but-set-variable
|
|
||||||
LIBS += -static
|
|
||||||
else
|
|
||||||
UNAME = $(shell uname -s)
|
|
||||||
ifeq ($(UNAME), Darwin)
|
|
||||||
# MacOS Only Flags/Libs
|
|
||||||
CFLAGS += -Wno-unused-private-field
|
|
||||||
CXXFLAGS += -Wno-unused-private-field
|
|
||||||
LIBS +=
|
|
||||||
else
|
|
||||||
# *nix Only Flags/Libs
|
|
||||||
CFLAGS += -Wno-unused-but-set-variable
|
|
||||||
CXXFLAGS += -Wno-unused-but-set-variable
|
|
||||||
LIBS +=
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: build
|
|
||||||
|
|
||||||
rebuild: clean build
|
|
||||||
|
|
||||||
build: $(OBJS)
|
|
||||||
mkdir -p $(BIN_DIR)
|
|
||||||
$(CXX) $(OBJS) $(LIBS) -o $(OUTPUT)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(OBJS) $(OUTPUT) $(BIN_DIR)
|
|
|
@ -1,140 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
|
||||||
<ProjectConfiguration Include="Debug|Win32">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|Win32">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug|x64">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|x64">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<ProjectGuid>{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}</ProjectGuid>
|
|
||||||
<RootNamespace>pfstool</RootNamespace>
|
|
||||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="Shared">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<PropertyGroup Label="UserMacros" />
|
|
||||||
<PropertyGroup />
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
<PreprocessorDefinitions>_MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
<PreprocessorDefinitions>_MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
<PreprocessorDefinitions>_MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
<PreprocessorDefinitions>_MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="source\main.cpp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="makefile" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\lib\libcrypto\crypto.vcxproj">
|
|
||||||
<Project>{6adbb60d-dba0-411d-bd2d-a355ef8e0fe1}</Project>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\..\lib\libfnd\fnd.vcxproj">
|
|
||||||
<Project>{4d27edb9-5110-44fe-8ce2-d46c5ad3c55b}</Project>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\..\lib\libnx\nx.vcxproj">
|
|
||||||
<Project>{91ba9e79-8242-4f7d-b997-0dfec95ea22b}</Project>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup>
|
|
||||||
<Filter Include="Source Files">
|
|
||||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
|
||||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Header Files">
|
|
||||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
|
||||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Resource Files">
|
|
||||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
|
||||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
|
||||||
</Filter>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="source\main.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="makefile" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
|
@ -1,77 +0,0 @@
|
||||||
#include <cstdio>
|
|
||||||
#include <crypto/aes.h>
|
|
||||||
#include <fnd/io.h>
|
|
||||||
#include <fnd/MemoryBlob.h>
|
|
||||||
#include <nx/NXCrypto.h>
|
|
||||||
#include <nx/PfsHeader.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <direct.h>
|
|
||||||
#else
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
std::string kFsTypeStr[]
|
|
||||||
{
|
|
||||||
"PFS0",
|
|
||||||
"HFS0"
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
if (argc < 2)
|
|
||||||
{
|
|
||||||
printf("usage: pfstool <file> [<output dir>]\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fnd::MemoryBlob file;
|
|
||||||
fnd::io::readFile(argv[1], file);
|
|
||||||
|
|
||||||
// import
|
|
||||||
nx::PfsHeader pfs;
|
|
||||||
pfs.importBinary(file.getBytes(), file.getSize());
|
|
||||||
|
|
||||||
if (argc == 3)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
_mkdir(argv[2]);
|
|
||||||
#else
|
|
||||||
mkdir(argv[2], S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("[PartitionFS]\n");
|
|
||||||
printf(" Type: %s\n", kFsTypeStr[pfs.getFsType()].c_str());
|
|
||||||
printf(" FileSystem: (%d files)\n", pfs.getFileList().getSize());
|
|
||||||
for (size_t i = 0; i < pfs.getFileList().getSize(); i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
printf(" %s", pfs.getFileList()[i].name.c_str());
|
|
||||||
if (pfs.getFsType() == pfs.TYPE_PFS0)
|
|
||||||
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ")\n", pfs.getFileList()[i].offset, pfs.getFileList()[i].size);
|
|
||||||
else
|
|
||||||
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ", hash_protected_size=0x%" PRIx64 ")\n", pfs.getFileList()[i].offset, pfs.getFileList()[i].size, pfs.getFileList()[i].hash_protected_size);
|
|
||||||
|
|
||||||
if (argc == 3)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
fnd::io::writeFile(std::string(argv[2]) + "\\" + pfs.getFileList()[i].name, file.getBytes() + pfs.getFileList()[i].offset, pfs.getFileList()[i].size);
|
|
||||||
#else
|
|
||||||
fnd::io::writeFile(std::string(argv[2]) + "/" + pfs.getFileList()[i].name, file.getBytes() + pfs.getFileList()[i].offset, pfs.getFileList()[i].size);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} catch (const fnd::Exception& e)
|
|
||||||
{
|
|
||||||
printf("%s\n", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
# Sources
|
|
||||||
SRC_DIR = source
|
|
||||||
OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c)))
|
|
||||||
|
|
||||||
# External dependencies
|
|
||||||
DEPENDS = nx crypto fnd
|
|
||||||
LIB_DIR = ../../lib
|
|
||||||
LIBS = $(foreach dep,$(DEPENDS), -L"$(LIB_DIR)/lib$(dep)" -l$(dep))
|
|
||||||
INCS = $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include")
|
|
||||||
|
|
||||||
BIN_DIR = bin
|
|
||||||
OUTPUT = $(BIN_DIR)/$(shell basename $(CURDIR))
|
|
||||||
|
|
||||||
# Compiler Settings
|
|
||||||
CXXFLAGS = -std=c++11 $(INCS) -D__STDC_FORMAT_MACROS -Wall -Wno-unused-value
|
|
||||||
ifeq ($(OS),Windows_NT)
|
|
||||||
# Windows Only Flags/Libs
|
|
||||||
CC = x86_64-w64-mingw32-gcc
|
|
||||||
CXX = x86_64-w64-mingw32-g++
|
|
||||||
CFLAGS += -Wno-unused-but-set-variable
|
|
||||||
CXXFLAGS += -Wno-unused-but-set-variable
|
|
||||||
LIBS += -static
|
|
||||||
else
|
|
||||||
UNAME = $(shell uname -s)
|
|
||||||
ifeq ($(UNAME), Darwin)
|
|
||||||
# MacOS Only Flags/Libs
|
|
||||||
CFLAGS += -Wno-unused-private-field
|
|
||||||
CXXFLAGS += -Wno-unused-private-field
|
|
||||||
LIBS +=
|
|
||||||
else
|
|
||||||
# *nix Only Flags/Libs
|
|
||||||
CFLAGS += -Wno-unused-but-set-variable
|
|
||||||
CXXFLAGS += -Wno-unused-but-set-variable
|
|
||||||
LIBS +=
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: build
|
|
||||||
|
|
||||||
rebuild: clean build
|
|
||||||
|
|
||||||
build: $(OBJS)
|
|
||||||
mkdir -p $(BIN_DIR)
|
|
||||||
$(CXX) $(OBJS) $(LIBS) -o $(OUTPUT)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(OBJS) $(OUTPUT) $(BIN_DIR)
|
|
|
@ -1,226 +0,0 @@
|
||||||
#include <cstdio>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <crypto/aes.h>
|
|
||||||
#include <crypto/rsa.h>
|
|
||||||
#include <fnd/io.h>
|
|
||||||
#include <fnd/MemoryBlob.h>
|
|
||||||
#include <fnd/SimpleTextOutput.h>
|
|
||||||
#include <nx/NXCrypto.h>
|
|
||||||
#include <nx/xci.h>
|
|
||||||
#include <nx/PfsHeader.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
struct sXciKeyData
|
|
||||||
{
|
|
||||||
crypto::aes::sAes128Key xci_header_encryption_key;
|
|
||||||
crypto::aes::sAes128Key initial_data_key;
|
|
||||||
crypto::rsa::sRsa2048Key xci_header_signer_key;
|
|
||||||
crypto::rsa::sRsa2048Key card_key_area_oeap_key;
|
|
||||||
};
|
|
||||||
|
|
||||||
void getTitleKeyFromInitialData(const byte_t* initialData, crypto::aes::sAes128Key& titleKey)
|
|
||||||
{
|
|
||||||
const sInitialData* data = (const sInitialData*)initialData;
|
|
||||||
crypto::aes::sAes128Key ccmKey;
|
|
||||||
crypto::aes::AesEcbDecrypt(data->key_source, 16, key_data.initial_data_key.key, ccmKey.key);
|
|
||||||
crypto::aes::AesCcmDecrypt(data->title_key_enc, 16, ccmKey.key, data->ccm_nonce, data->ccm_mac, titleKey.key);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
inline uint64_t blockToAddr(uint32_t block)
|
|
||||||
{
|
|
||||||
return ((uint64_t)block) << 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getBoolStr(bool isTrue)
|
|
||||||
{
|
|
||||||
return isTrue? "TRUE" : "FALSE";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getRomSizeStr(byte_t rom_size)
|
|
||||||
{
|
|
||||||
const char* str = "unknown";
|
|
||||||
switch (rom_size)
|
|
||||||
{
|
|
||||||
case (nx::xci::ROM_SIZE_1GB) :
|
|
||||||
str = "1GB";
|
|
||||||
break;
|
|
||||||
case (nx::xci::ROM_SIZE_2GB) :
|
|
||||||
str = "2GB";
|
|
||||||
break;
|
|
||||||
case (nx::xci::ROM_SIZE_4GB) :
|
|
||||||
str = "4GB";
|
|
||||||
break;
|
|
||||||
case (nx::xci::ROM_SIZE_8GB) :
|
|
||||||
str = "8GB";
|
|
||||||
break;
|
|
||||||
case (nx::xci::ROM_SIZE_16GB) :
|
|
||||||
str = "16GB";
|
|
||||||
break;
|
|
||||||
case (nx::xci::ROM_SIZE_32GB) :
|
|
||||||
str = "32GB";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getCardClockRate(uint32_t acc_ctrl_1)
|
|
||||||
{
|
|
||||||
const char* str = "unknown";
|
|
||||||
switch (acc_ctrl_1)
|
|
||||||
{
|
|
||||||
case (nx::xci::CLOCK_RATE_25) :
|
|
||||||
str = "20 MHz";
|
|
||||||
break;
|
|
||||||
case (nx::xci::CLOCK_RATE_50) :
|
|
||||||
str = "50 MHz";
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
void printXciHeader(const nx::sXciHeader& hdr, bool is_decrypted)
|
|
||||||
{
|
|
||||||
crypto::aes::sAesIvCtr iv;
|
|
||||||
for (size_t i = 0; i < sizeof(iv); i++)
|
|
||||||
{
|
|
||||||
iv.iv[15-i] = hdr.encryption_iv[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("[XCI HEADER]\n");
|
|
||||||
printf(" Magic: HEAD\n");
|
|
||||||
printf(" RomAreaStartPage: 0x%0x (0x%" PRIx64 ")\n", hdr.rom_area_start_page.get(), blockToAddr(hdr.rom_area_start_page.get()));
|
|
||||||
printf(" BackupAreaStartPage: 0x%0x\n", hdr.backup_area_start_page.get());
|
|
||||||
printf(" KeyFlag: 0x%x\n", hdr.key_flag);
|
|
||||||
printf(" KekIndex: %d\n", hdr.key_flag & 7);
|
|
||||||
printf(" TitleKeyDecIndex: %d\n", (hdr.key_flag >> 4) & 7);
|
|
||||||
printf(" RomSize: 0x%x (%s)\n", hdr.rom_size, getRomSizeStr(hdr.rom_size));
|
|
||||||
printf(" CardHeaderVersion: %d\n", hdr.card_header_version);
|
|
||||||
printf(" Flags: 0x%x\n", hdr.flags);
|
|
||||||
printf(" AutoBoot: %s\n", getBoolStr(_HAS_BIT(hdr.flags, nx::xci::FLAG_AUTOBOOT)));
|
|
||||||
printf(" HistoryErase: %s\n", getBoolStr(_HAS_BIT(hdr.flags, nx::xci::FLAG_HISTORY_ERASE)));
|
|
||||||
printf(" RepairTool: %s\n", getBoolStr(_HAS_BIT(hdr.flags, nx::xci::FLAG_REPAIR_TOOL)));
|
|
||||||
printf(" PackageId: 0x%" PRIx64 "\n", hdr.package_id.get());
|
|
||||||
printf(" ValidDataEndPage: 0x%x (0x%" PRIx64 ")\n", hdr.valid_data_end_page.get(), blockToAddr(hdr.valid_data_end_page.get()));
|
|
||||||
printf(" AesIv: ");
|
|
||||||
fnd::SimpleTextOutput::hexDump(iv.iv, sizeof(iv));
|
|
||||||
printf(" PartitionFs:\n");
|
|
||||||
printf(" Offset: 0x%" PRIx64 "\n", hdr.partition_fs_header_address.get());
|
|
||||||
printf(" Size: 0x%" PRIx64 "\n", hdr.partition_fs_header_size.get());
|
|
||||||
printf(" Hash: ");
|
|
||||||
fnd::SimpleTextOutput::hexDump(hdr.partition_fs_header_hash, 0x20);
|
|
||||||
printf(" InitialData:\n");
|
|
||||||
printf(" Hash: ");
|
|
||||||
fnd::SimpleTextOutput::hexDump(hdr.initial_data_hash, 0x20);
|
|
||||||
printf(" SelSec: 0x%x\n", hdr.sel_sec.get());
|
|
||||||
printf(" SelT1Key: 0x%x\n", hdr.sel_t1_key.get());
|
|
||||||
printf(" SelKey: 0x%x\n", hdr.sel_key.get());
|
|
||||||
printf(" LimArea: 0x%x (0x%" PRIx64 ")\n", hdr.lim_area.get(), blockToAddr(hdr.lim_area.get()));
|
|
||||||
|
|
||||||
if (is_decrypted == true)
|
|
||||||
{
|
|
||||||
printf(" FwVersion: v%d.%d\n", hdr.fw_version[nx::xci::FWVER_MAJOR].get(), hdr.fw_version[nx::xci::FWVER_MINOR].get());
|
|
||||||
printf(" AccCtrl1: 0x%x\n", hdr.acc_ctrl_1.get());
|
|
||||||
printf(" CardClockRate: %s\n", getCardClockRate(hdr.acc_ctrl_1.get()));
|
|
||||||
printf(" Wait1TimeRead: 0x%x\n", hdr.wait_1_time_read.get());
|
|
||||||
printf(" Wait2TimeRead: 0x%x\n", hdr.wait_2_time_read.get());
|
|
||||||
printf(" Wait1TimeWrite: 0x%x\n", hdr.wait_1_time_write.get());
|
|
||||||
printf(" Wait2TimeWrite: 0x%x\n", hdr.wait_2_time_write.get());
|
|
||||||
printf(" FwMode: 0x%x\n", hdr.fw_mode.get());
|
|
||||||
printf(" UppVersion: %d\n", hdr.upp_version.get());
|
|
||||||
printf(" UppHash: ");
|
|
||||||
fnd::SimpleTextOutput::hexDump(hdr.upp_hash, 8);
|
|
||||||
printf(" UppId: %016" PRIx64 "\n", hdr.upp_id.get());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void printXciPartitionFs(const nx::PfsHeader& pfs, size_t partition_base_offset, const std::string& partition_name)
|
|
||||||
{
|
|
||||||
printf("[PartitionFS]\n");
|
|
||||||
printf(" Type: %s\n", pfs.getFsType() == pfs.TYPE_PFS0 ? "PFS0" : "HFS0");
|
|
||||||
|
|
||||||
if (partition_name.empty())
|
|
||||||
{
|
|
||||||
printf(" FileSystem: (%d files)\n", pfs.getFileList().getSize());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf(" %s/\n", partition_name.c_str());
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < pfs.getFileList().getSize(); i++)
|
|
||||||
{
|
|
||||||
printf(" %s", pfs.getFileList()[i].name.c_str());
|
|
||||||
if (pfs.getFsType() == pfs.TYPE_PFS0)
|
|
||||||
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ")\n", (partition_base_offset + pfs.getFileList()[i].offset), pfs.getFileList()[i].size);
|
|
||||||
else
|
|
||||||
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ", hash_protected_size=0x%" PRIx64 ")\n", (partition_base_offset + pfs.getFileList()[i].offset), pfs.getFileList()[i].size, pfs.getFileList()[i].hash_protected_size);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void decryptXciHeader(const byte_t* src, byte_t* dst)
|
|
||||||
{
|
|
||||||
const byte_t* src_iv = ((const nx::sXciHeader*)src)->encryption_iv;
|
|
||||||
byte_t iv[crypto::aes::kAesBlockSize];
|
|
||||||
|
|
||||||
for (size_t i = 0; i < crypto::aes::kAesBlockSize; i++)
|
|
||||||
{
|
|
||||||
iv[i] = src_iv[15 - i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy plain
|
|
||||||
memcpy(dst, src, nx::xci::kHeaderEncOffset);
|
|
||||||
|
|
||||||
// decrypt encrypted data
|
|
||||||
crypto::aes::AesCbcDecrypt(src + nx::xci::kHeaderEncOffset, nx::xci::kHeaderEncSize, crypto::aes::nx::prod::xci_header_key, iv, dst + nx::xci::kHeaderEncOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
if (argc < 2)
|
|
||||||
{
|
|
||||||
printf("usage: %s <xci file>\n", argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fnd::MemoryBlob tmp;
|
|
||||||
fnd::io::readFile(argv[1], 0x100, sizeof(nx::sXciHeader), tmp);
|
|
||||||
decryptXciHeader(tmp.getBytes(), tmp.getBytes());
|
|
||||||
|
|
||||||
nx::sXciHeader hdr;
|
|
||||||
memcpy((void*)&hdr, tmp.getBytes(), sizeof(nx::sXciHeader));
|
|
||||||
printXciHeader(hdr, true);
|
|
||||||
|
|
||||||
crypto::sha::sSha256Hash testHash;
|
|
||||||
|
|
||||||
// read root PFS
|
|
||||||
fnd::io::readFile(argv[1], hdr.partition_fs_header_address.get(), hdr.partition_fs_header_size.get(), tmp);
|
|
||||||
crypto::sha::Sha256(tmp.getBytes(), tmp.getSize(), testHash.bytes);
|
|
||||||
if (testHash.compare(hdr.partition_fs_header_hash) == false)
|
|
||||||
{
|
|
||||||
throw fnd::Exception("xcitool", "Bad root partition hash");
|
|
||||||
}
|
|
||||||
nx::PfsHeader rootPfs;
|
|
||||||
rootPfs.importBinary(tmp.getBytes(), tmp.getSize());
|
|
||||||
printXciPartitionFs(rootPfs, hdr.partition_fs_header_address.get(), "xci:");
|
|
||||||
|
|
||||||
// read sub PFS
|
|
||||||
for (size_t i = 0; i < rootPfs.getFileList().getSize(); i++)
|
|
||||||
{
|
|
||||||
fnd::io::readFile(argv[1], hdr.partition_fs_header_address.get() + rootPfs.getFileList()[i].offset, rootPfs.getFileList()[i].hash_protected_size, tmp);
|
|
||||||
crypto::sha::Sha256(tmp.getBytes(), tmp.getSize(), testHash.bytes);
|
|
||||||
if (testHash.compare(rootPfs.getFileList()[i].hash) == false)
|
|
||||||
{
|
|
||||||
throw fnd::Exception("xcitool", "Bad partition hash");
|
|
||||||
}
|
|
||||||
nx::PfsHeader pfs;
|
|
||||||
pfs.importBinary(tmp.getBytes(), tmp.getSize());
|
|
||||||
printXciPartitionFs(pfs, hdr.partition_fs_header_address.get() + rootPfs.getFileList()[i].offset, "xci:/" + rootPfs.getFileList()[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
|
||||||
<ProjectConfiguration Include="Debug|Win32">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|Win32">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug|x64">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|x64">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<VCProjectVersion>15.0</VCProjectVersion>
|
|
||||||
<ProjectGuid>{007FF616-7B99-4CB3-84CD-39C47F64FC7E}</ProjectGuid>
|
|
||||||
<RootNamespace>xcitool</RootNamespace>
|
|
||||||
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="Shared">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<PropertyGroup Label="UserMacros" />
|
|
||||||
<PropertyGroup />
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;</AdditionalIncludeDirectories>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="makefile" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="source\main.cpp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup>
|
|
||||||
<Filter Include="Source Files">
|
|
||||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
|
||||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Header Files">
|
|
||||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
|
||||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Resource Files">
|
|
||||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
|
||||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
|
||||||
</Filter>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="makefile" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="source\main.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup />
|
|
||||||
</Project>
|
|
Loading…
Reference in a new issue