mirror of
https://github.com/jakcron/nstool
synced 2024-11-22 13:39:28 +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": {
|
||||
"__functional_base": "cpp",
|
||||
"memory": "cpp",
|
||||
"tuple": "cpp",
|
||||
"utility": "cpp",
|
||||
"__string": "cpp",
|
||||
"__config": "cpp",
|
||||
"__nullptr": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"exception": "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_view": "cpp",
|
||||
"__split_buffer": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iterator": "cpp",
|
||||
"utility": "cpp",
|
||||
"vector": "cpp",
|
||||
"__functional_base_03": "cpp",
|
||||
"__tuple": "cpp",
|
||||
"limits": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"stdexcept": "cpp"
|
||||
"istream": "cpp",
|
||||
"atomic": "cpp",
|
||||
"ios": "cpp",
|
||||
"system_error": "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
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnx", "lib\libnx\nx.vcxproj", "{91BA9E79-8242-4F7D-B997-0DFEC95EA22B}"
|
||||
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}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
lib\makefile = lib\makefile
|
||||
EndProjectSection
|
||||
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}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
programs\makefile = programs\makefile
|
||||
|
@ -30,14 +26,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
README.md = README.md
|
||||
EndProjectSection
|
||||
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}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tiktool", "programs\tiktool\tiktool.vcxproj", "{2200B834-F15A-4C6E-9DDB-6012B9A5C246}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xcitool", "programs\xcitool\xcitool.vcxproj", "{007FF616-7B99-4CB3-84CD-39C47F64FC7E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
|
|
|
@ -3,9 +3,9 @@ Tools for NX (Nintendo Switch).
|
|||
|
||||
== Tools ==
|
||||
* ncatool - read/extract *.nca
|
||||
* npdmtool - read *.npdm
|
||||
* pfstool - read/extract PartitionFS (PFS0|HFS0) blobs (including *.nsp).
|
||||
* xcitool - read *.xci
|
||||
* nstool - read *.npdm, read/extract PartitionFS (PFS0|HFS0) blobs (including *.nsp), read *.xci
|
||||
* tiktool - read ticket fields.
|
||||
|
||||
== 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)
|
||||
ifeq ($(UNAME), Darwin)
|
||||
# MacOS Only Flags/Libs
|
||||
CFLAGS += -Wno-unused-private-field
|
||||
CXXFLAGS += -Wno-unused-private-field
|
||||
CFLAGS += -arch x86_64 -Wno-unused-private-field
|
||||
CXXFLAGS += -arch x86_64 -Wno-unused-private-field
|
||||
ARFLAGS = rc
|
||||
else
|
||||
# *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?"
|
||||
#endif
|
||||
|
||||
/*
|
||||
template <class T, static T (*F)(T)>
|
||||
class ISerialiseablePrimative {
|
||||
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, be_word> be_uint32_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"
|
||||
|
||||
main: build
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <fnd/SimpleTextOutput.h>
|
||||
#include <nx/NXCrypto.h>
|
||||
#include <nx/NcaHeader.h>
|
||||
#include <nx/NcaUtils.h>
|
||||
#include <nx/PfsHeader.h>
|
||||
#include <inttypes.h>
|
||||
#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] }
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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])
|
||||
{
|
||||
byte_t tweak[crypto::aes::kAesBlockSize];
|
||||
|
||||
// decrypt main header
|
||||
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));
|
||||
}
|
||||
crypto::aes::sAesXts128Key a;
|
||||
a.set(key[0],key[1]);
|
||||
nx::NcaUtils::decryptNcaHeader(header, header, a);
|
||||
}
|
||||
|
||||
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(" Hash Type: %s\n", kHashTypeStr[fsHdr->hash_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));
|
||||
printf(" PFS0 SuperBlock:\n");
|
||||
printf(" Master Hash: \n");
|
||||
printf(" HashBlockSize: 0x%x\n", pfs0->hash_block_size.get());
|
||||
printf(" Unknown: 0x%x\n", pfs0->unk_0x02.get());
|
||||
printf(" HashDataOffset: 0x%" PRIx64 "\n", pfs0->hash_data.offset.get());
|
||||
printf(" HashDataSize: 0x%" PRIx64 "\n", pfs0->hash_data.size.get());
|
||||
printf(" HashTargetOffset: 0x%" PRIx64 "\n", pfs0->hash_target.offset.get());
|
||||
printf(" HashTargetSize: 0x%" PRIx64 "\n", pfs0->hash_target.size.get());
|
||||
const nx::sHierarchicalSha256Header* hash_hdr = (const nx::sHierarchicalSha256Header*)(header + sectorToOffset(sector_index) + sizeof(nx::sNcaFsHeader));
|
||||
printf(" HashHierarchicalSha256 Header:\n");
|
||||
printf(" Master Hash: ");
|
||||
fnd::SimpleTextOutput::hexDump(hash_hdr->master_hash, 0x20);
|
||||
printf(" HashBlockSize: 0x%x\n", hash_hdr->hash_block_size.get());
|
||||
printf(" Unknown: 0x%x\n", hash_hdr->unk_0x02.get());
|
||||
printf(" HashDataOffset: 0x%" PRIx64 "\n", hash_hdr->hash_data.offset.get());
|
||||
printf(" HashDataSize: 0x%" PRIx64 "\n", hash_hdr->hash_data.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