mirror of
https://github.com/SabreTools/NDecrypt.git
synced 2026-02-06 13:54:47 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e90f7a76af | ||
|
|
9750ae5a1e | ||
|
|
fa518ed5a5 | ||
|
|
350acd7be4 | ||
|
|
dde0c96e6c | ||
|
|
36e6e803cc | ||
|
|
e669936e08 | ||
|
|
dc55511a84 | ||
|
|
b9b8c76e84 | ||
|
|
10fcd51e10 | ||
|
|
a0878d0bf4 | ||
|
|
ed55f76d1e | ||
|
|
a726fc26d9 | ||
|
|
414df7808e | ||
|
|
d61e03687b | ||
|
|
cccb4e6261 | ||
|
|
df7ff2b95a | ||
|
|
52a928638b | ||
|
|
5962fc6072 | ||
|
|
5807d4ddc2 | ||
|
|
6fa8867f93 | ||
|
|
ed45d2d14e | ||
|
|
69ecb0d379 | ||
|
|
5974e2371e | ||
|
|
1e5e3badbc | ||
|
|
ef02b1c33a | ||
|
|
32e8bab2a6 | ||
|
|
bd1c6f8b51 | ||
|
|
30da008cec | ||
|
|
aa7566c312 | ||
|
|
56a5f4951b | ||
|
|
4b71cd621f | ||
|
|
4042b1c216 | ||
|
|
26c7a34e98 | ||
|
|
dc68aa5046 | ||
|
|
ff486d8613 | ||
|
|
941f7d5191 | ||
|
|
b83293e8c9 | ||
|
|
0190f3d316 | ||
|
|
5c6b303d41 | ||
|
|
0800d93d22 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -258,4 +258,7 @@ paket-files/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyc
|
||||
|
||||
# VSCode
|
||||
/NDecrypt/Properties/launchSettings.json
|
||||
|
||||
27
.vscode/launch.json
vendored
Normal file
27
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (console)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/NDecrypt/bin/Debug/netcoreapp3.1/NDecrypt.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/NDecrypt",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
]
|
||||
}
|
||||
24
.vscode/tasks.json
vendored
Normal file
24
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
"build",
|
||||
// Ask dotnet build to generate full paths for file names.
|
||||
"/property:GenerateFullPaths=true",
|
||||
// Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2006
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "3DSDecrypt", "3DSDecrypt\3DSDecrypt.csproj", "{2E30006A-3C60-4576-A262-937B21C83C06}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2E30006A-3C60-4576-A262-937B21C83C06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2E30006A-3C60-4576-A262-937B21C83C06}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2E30006A-3C60-4576-A262-937B21C83C06}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2E30006A-3C60-4576-A262-937B21C83C06}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {5AB50D9B-BA18-4F96-804B-52E7E0845B37}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,75 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{2E30006A-3C60-4576-A262-937B21C83C06}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>_3DSDecrypt</RootNamespace>
|
||||
<AssemblyName>3DSDecrypt</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="BouncyCastle.Crypto, Version=1.8.4.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
|
||||
<HintPath>..\packages\BouncyCastle.1.8.4\lib\BouncyCastle.Crypto.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Data\Constants.cs" />
|
||||
<Compile Include="Headers\NCCHHeaderFlags.cs" />
|
||||
<Compile Include="ThreeDSTool.cs" />
|
||||
<Compile Include="Data\Enums.cs" />
|
||||
<Compile Include="Headers\AccessControlInfo.cs" />
|
||||
<Compile Include="Headers\ARM11KernelCapabilities.cs" />
|
||||
<Compile Include="Headers\ARM11LocalSystemCapabilities.cs" />
|
||||
<Compile Include="Headers\ARM9AccessControl.cs" />
|
||||
<Compile Include="Headers\CodeSetInfo.cs" />
|
||||
<Compile Include="Headers\CXIExtendedHeader.cs" />
|
||||
<Compile Include="Headers\ExeFSFileHeader.cs" />
|
||||
<Compile Include="Headers\ExeFSHeader.cs" />
|
||||
<Compile Include="Headers\NCCHHeader.cs" />
|
||||
<Compile Include="Headers\NCSDHeader.cs" />
|
||||
<Compile Include="Headers\PartitionTableEntry.cs" />
|
||||
<Compile Include="Headers\StorageInfo.cs" />
|
||||
<Compile Include="Headers\SystemControlInfo.cs" />
|
||||
<Compile Include="Headers\SystemInfo.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -1,66 +0,0 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace ThreeDS.Data
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
// Setup Keys and IVs
|
||||
public static byte[] PlainCounter = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static byte[] ExefsCounter = new byte[] { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static byte[] RomfsCounter = new byte[] { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
// Big Endian - 0x1F, 0xF9, 0xE9, 0xAA, 0xC5, 0xFE, 0x04, 0x08, 0x02, 0x45, 0x91, 0xDC, 0x5D, 0x52, 0x76, 0x8A
|
||||
// Little Endian - 0x8A, 0x76, 0x52, 0x5D, 0xDC, 0x91, 0x45, 0x02, 0x08, 0x04, 0xFE, 0xC5, 0xAA, 0xE9, 0xF9, 0x1F
|
||||
public static BigInteger AESHardwareConstant = new BigInteger(new byte[] { 0x8A, 0x76, 0x52, 0x5D, 0xDC, 0x91, 0x45, 0x02, 0x08, 0x04, 0xFE, 0xC5, 0xAA, 0xE9, 0xF9, 0x1F });
|
||||
|
||||
#region Retail keys
|
||||
|
||||
// KeyX 0x18 (New 3DS 9.3)
|
||||
// Big Endian - 0x82, 0xE9, 0xC9, 0xBE, 0xBF, 0xB8, 0xBD, 0xB8, 0x75, 0xEC, 0xC0, 0xA0, 0x7D, 0x47, 0x43, 0x74
|
||||
// Little Endian - 0x74, 0x43, 0x47, 0x7D, 0xA0, 0xC0, 0xEC, 0x75, 0xB8, 0xBD, 0xB8, 0xBF, 0xBE, 0xC9, 0xE9, 0x82
|
||||
public static BigInteger KeyX0x18 = new BigInteger(new byte[] { 0x74, 0x43, 0x47, 0x7D, 0xA0, 0xC0, 0xEC, 0x75, 0xB8, 0xBD, 0xB8, 0xBF, 0xBE, 0xC9, 0xE9, 0x82, 0x00 });
|
||||
|
||||
// KeyX 0x1B (New 3DS 9.6)
|
||||
// Big Endian - 0x45, 0xAD, 0x04, 0x95, 0x39, 0x92, 0xC7, 0xC8, 0x93, 0x72, 0x4A, 0x9A, 0x7B, 0xCE, 0x61, 0x82
|
||||
// Little Endian - 0x82, 0x61, 0xCE, 0x7B, 0x9A, 0x4A, 0x72, 0x93, 0xC8, 0xC7, 0x92, 0x39, 0x95, 0x04, 0xAD, 0x45
|
||||
public static BigInteger KeyX0x1B = new BigInteger(new byte[] { 0x82, 0x61, 0xCE, 0x7B, 0x9A, 0x4A, 0x72, 0x93, 0xC8, 0xC7, 0x92, 0x39, 0x95, 0x04, 0xAD, 0x45, 0x00 });
|
||||
|
||||
// KeyX 0x25 (> 7.x)
|
||||
// Big Endian - 0xCE, 0xE7, 0xD8, 0xAB, 0x30, 0xC0, 0x0D, 0xAE, 0x85, 0x0E, 0xF5, 0xE3, 0x82, 0xAC, 0x5A, 0xF3
|
||||
// Little Endian - 0xF3, 0x5A, 0xAC, 0x82, 0xE3, 0xF5, 0x0E, 0x85, 0xAE, 0x0D, 0xC0, 0x30, 0xAB, 0xD8, 0xE7, 0xCE
|
||||
public static BigInteger KeyX0x25 = new BigInteger(new byte[] { 0xF3, 0x5A, 0xAC, 0x82, 0xE3, 0xF5, 0x0E, 0x85, 0xAE, 0x0D, 0xC0, 0x30, 0xAB, 0xD8, 0xE7, 0xCE, 0x00 });
|
||||
|
||||
// Dev KeyX 0x2C (< 6.x)
|
||||
// Big Endian - 0xB9, 0x8E, 0x95, 0xCE, 0xCA, 0x3E, 0x4D, 0x17, 0x1F, 0x76, 0xA9, 0x4D, 0xE9, 0x34, 0xC0, 0x53
|
||||
// Little Endian - 0x53, 0xC0, 0x34, 0xE9, 0x4D, 0xA9, 0x76, 0x1F, 0x17, 0x4D, 0x3E, 0xCA, 0xCE, 0x95, 0x8E, 0xB9
|
||||
public static BigInteger KeyX0x2C = new BigInteger(new byte[] { 0x53, 0xC0, 0x34, 0xE9, 0x4D, 0xA9, 0x76, 0x1F, 0x17, 0x4D, 0x3E, 0xCA, 0xCE, 0x95, 0x8E, 0xB9, 0x00 });
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dev Keys (Must be converted to LE)
|
||||
|
||||
// Dev KeyX 0x18 (New 3DS 9.3)
|
||||
// Big Endian - 0x30, 0x4B, 0xF1, 0x46, 0x83, 0x72, 0xEE, 0x64, 0x11, 0x5E, 0xBD, 0x40, 0x93, 0xD8, 0x42, 0x76
|
||||
// Little Endian - 0x76, 0x42, 0xD8, 0x93, 0x40, 0xBD, 0x5E, 0x11, 0x64, 0xEE, 0x72, 0x83, 0x46, 0xF1, 0x4B, 0x30
|
||||
public static BigInteger DevKeyX0x18 = new BigInteger(new byte[] { 0x76, 0x42, 0xD8, 0x93, 0x40, 0xBD, 0x5E, 0x11, 0x64, 0xEE, 0x72, 0x83, 0x46, 0xF1, 0x4B, 0x30, 0x00 });
|
||||
|
||||
// Dev KeyX 0x1B New 3DS 9.6)
|
||||
// Big Endian - 0x6C, 0x8B, 0x29, 0x44, 0xA0, 0x72, 0x60, 0x35, 0xF9, 0x41, 0xDF, 0xC0, 0x18, 0x52, 0x4F, 0xB6
|
||||
// Little Endian - 0xB6, 0x4F, 0x52, 0x18, 0xC0, 0xDF, 0x41, 0xF9, 0x35, 0x60, 0x72, 0xA0, 0x44, 0x29, 0x8B, 0x6C
|
||||
public static BigInteger DevKeyX0x1B = new BigInteger(new byte[] { 0xB6, 0x4F, 0x52, 0x18, 0xC0, 0xDF, 0x41, 0xF9, 0x35, 0x60, 0x72, 0xA0, 0x44, 0x29, 0x8B, 0x6C, 0x00 });
|
||||
|
||||
// Dev KeyX 0x25 (> 7.x)
|
||||
// Big Endian - 0x81, 0x90, 0x7A, 0x4B, 0x6F, 0x1B, 0x47, 0x32, 0x3A, 0x67, 0x79, 0x74, 0xCE, 0x4A, 0xD7, 0x1B
|
||||
// Little Endian - 0x1B, 0xD7, 0x4A, 0xCE, 0x74, 0x79, 0x67, 0x3A, 0x32, 0x47, 0x1B, 0x6F, 0x4B, 0x7A, 0x90, 0x81
|
||||
public static BigInteger DevKeyX0x25 = new BigInteger(new byte[] { 0x1B, 0xD7, 0x4A, 0xCE, 0x74, 0x79, 0x67, 0x3A, 0x32, 0x47, 0x1B, 0x6F, 0x4B, 0x7A, 0x90, 0x81, 0x00 });
|
||||
|
||||
// Dev KeyX 0x2C (< 6.x)
|
||||
// Big Endian - 0x51, 0x02, 0x07, 0x51, 0x55, 0x07, 0xCB, 0xB1, 0x8E, 0x24, 0x3D, 0xCB, 0x85, 0xE2, 0x3A, 0x1D
|
||||
// Little Endian - 0x1D, 0x3A, 0xE2, 0x85, 0xCB, 0x3D, 0x24, 0x8E, 0xB1, 0xCB, 0x07, 0x55, 0x51, 0x07, 0x02, 0x51
|
||||
public static BigInteger DevKeyX0x2C = new BigInteger(new byte[] { 0x1D, 0x3A, 0xE2, 0x85, 0xCB, 0x3D, 0x24, 0x8E, 0xB1, 0xCB, 0x07, 0x55, 0x51, 0x07, 0x02, 0x51, 0x00 });
|
||||
|
||||
#endregion
|
||||
|
||||
public const int CXTExtendedDataHeaderLength = 0x800;
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace ThreeDS.Data
|
||||
{
|
||||
[Flags]
|
||||
public enum BitMasks : byte
|
||||
{
|
||||
FixedCryptoKey = 0x01,
|
||||
NoMountRomFs = 0x02,
|
||||
NoCrypto = 0x04,
|
||||
NewKeyYGenerator = 0x20,
|
||||
}
|
||||
|
||||
public enum ContentPlatform : byte
|
||||
{
|
||||
CTR = 0x01,
|
||||
Snake = 0x02, // New3DS
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ContentType : byte
|
||||
{
|
||||
Data = 0x01,
|
||||
Executable = 0x02,
|
||||
SystemUpdate = 0x04,
|
||||
Manual = 0x08,
|
||||
Child = 0x04 | 0x08,
|
||||
Trial = 0x10,
|
||||
}
|
||||
|
||||
public enum CryptoMethod : byte
|
||||
{
|
||||
Original = 0x00,
|
||||
Seven = 0x01,
|
||||
NineThree = 0x0A,
|
||||
NineSix = 0x0B,
|
||||
}
|
||||
|
||||
public enum FilesystemType : ulong
|
||||
{
|
||||
None = 0,
|
||||
Normal = 1,
|
||||
FIRM = 3,
|
||||
AGB_FIRMSave = 4,
|
||||
}
|
||||
|
||||
public enum MediaCardDeviceType : byte
|
||||
{
|
||||
NORFlash = 0x01,
|
||||
None = 0x02,
|
||||
BT = 0x03,
|
||||
}
|
||||
|
||||
public enum MediaPlatformIndex : byte
|
||||
{
|
||||
CTR = 0x01,
|
||||
}
|
||||
|
||||
public enum MediaTypeIndex : byte
|
||||
{
|
||||
InnerDevice = 0x00,
|
||||
Card1 = 0x01,
|
||||
Card2 = 0x02,
|
||||
ExtendedDevice = 0x03,
|
||||
}
|
||||
|
||||
public enum NCCHFlags
|
||||
{
|
||||
CryptoMethod = 0x03,
|
||||
ContentPlatform = 0x04,
|
||||
ContentTypeBitMask = 0x05,
|
||||
ContentUnitSize = 0x06,
|
||||
BitMasks = 0x07,
|
||||
}
|
||||
|
||||
public enum NCSDFlags
|
||||
{
|
||||
BackupWriteWaitTime = 0x00,
|
||||
MediaCardDevice3X = 0x03,
|
||||
MediaPlatformIndex = 0x04,
|
||||
MediaTypeIndex = 0x05,
|
||||
MediaUnitSize = 0x06,
|
||||
MediaCardDevice2X = 0x07,
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class ARM11KernelCapabilities
|
||||
{
|
||||
public byte[][] Descriptors = new byte[28][];
|
||||
public byte[] Reserved = new byte[0x10];
|
||||
|
||||
public static ARM11KernelCapabilities Read(BinaryReader reader)
|
||||
{
|
||||
ARM11KernelCapabilities kc = new ARM11KernelCapabilities();
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < 28; i++)
|
||||
kc.Descriptors[i] = reader.ReadBytes(4);
|
||||
|
||||
kc.Reserved = reader.ReadBytes(0x10);
|
||||
return kc;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class ARM11LocalSystemCapabilities
|
||||
{
|
||||
public byte[] ProgramID = new byte[8];
|
||||
public uint CoreVersion;
|
||||
public byte Flag1;
|
||||
public byte Flag2;
|
||||
public byte Flag0;
|
||||
public byte Priority;
|
||||
public byte[][] ResourceLimitDescriptors = new byte[16][];
|
||||
public StorageInfo StorageInfo;
|
||||
public byte[][] ServiceAccessControl = new byte[32][];
|
||||
public byte[][] ExtendedServiceAccessControl = new byte[2][];
|
||||
public byte[] Reserved = new byte[0xF];
|
||||
public byte ResourceLimitCategory;
|
||||
|
||||
public static ARM11LocalSystemCapabilities Read(BinaryReader reader)
|
||||
{
|
||||
ARM11LocalSystemCapabilities lsc = new ARM11LocalSystemCapabilities();
|
||||
|
||||
try
|
||||
{
|
||||
lsc.ProgramID = reader.ReadBytes(8);
|
||||
lsc.CoreVersion = reader.ReadUInt32();
|
||||
lsc.Flag1 = reader.ReadByte();
|
||||
lsc.Flag2 = reader.ReadByte();
|
||||
lsc.Flag0 = reader.ReadByte();
|
||||
lsc.Priority = reader.ReadByte();
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
lsc.ResourceLimitDescriptors[i] = reader.ReadBytes(2);
|
||||
|
||||
lsc.StorageInfo = StorageInfo.Read(reader);
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
lsc.ServiceAccessControl[i] = reader.ReadBytes(8);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
lsc.ExtendedServiceAccessControl[i] = reader.ReadBytes(8);
|
||||
|
||||
lsc.Reserved = reader.ReadBytes(0xF);
|
||||
lsc.ResourceLimitCategory = reader.ReadByte();
|
||||
return lsc;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class ARM9AccessControl
|
||||
{
|
||||
public byte[] Descriptors = new byte[15];
|
||||
public byte DescriptorVersion;
|
||||
|
||||
public static ARM9AccessControl Read(BinaryReader reader)
|
||||
{
|
||||
ARM9AccessControl ac = new ARM9AccessControl();
|
||||
|
||||
try
|
||||
{
|
||||
ac.Descriptors = reader.ReadBytes(15);
|
||||
ac.DescriptorVersion = reader.ReadByte();
|
||||
return ac;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class AccessControlInfo
|
||||
{
|
||||
public ARM11LocalSystemCapabilities ARM11LocalSystemCapabilities;
|
||||
public ARM11KernelCapabilities ARM11KernelCapabilities;
|
||||
public ARM9AccessControl ARM9AccessControl;
|
||||
|
||||
public static AccessControlInfo Read(BinaryReader reader)
|
||||
{
|
||||
AccessControlInfo aci = new AccessControlInfo();
|
||||
|
||||
try
|
||||
{
|
||||
aci.ARM11LocalSystemCapabilities = ARM11LocalSystemCapabilities.Read(reader);
|
||||
aci.ARM11KernelCapabilities = ARM11KernelCapabilities.Read(reader);
|
||||
aci.ARM9AccessControl = ARM9AccessControl.Read(reader);
|
||||
return aci;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class CXIExtendedHeader
|
||||
{
|
||||
public SystemControlInfo SCI;
|
||||
public AccessControlInfo ACI;
|
||||
public byte[] AccessDescSignature = new byte[0x100];
|
||||
public byte[] NCCHHDRPublicKey = new byte[0x100];
|
||||
public AccessControlInfo ACIForLimitations;
|
||||
|
||||
public static CXIExtendedHeader Read(BinaryReader reader)
|
||||
{
|
||||
CXIExtendedHeader header = new CXIExtendedHeader();
|
||||
|
||||
try
|
||||
{
|
||||
header.SCI = SystemControlInfo.Read(reader);
|
||||
header.ACI = AccessControlInfo.Read(reader);
|
||||
header.AccessDescSignature = reader.ReadBytes(0x100);
|
||||
header.NCCHHDRPublicKey = reader.ReadBytes(0x100);
|
||||
header.ACIForLimitations = AccessControlInfo.Read(reader);
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class CodeSetInfo
|
||||
{
|
||||
public byte[] Address = new byte[0x04];
|
||||
public uint PhysicalRegionSizeInPages;
|
||||
public uint SizeInBytes;
|
||||
|
||||
public static CodeSetInfo Read(BinaryReader reader)
|
||||
{
|
||||
CodeSetInfo csi = new CodeSetInfo();
|
||||
|
||||
try
|
||||
{
|
||||
csi.Address = reader.ReadBytes(4);
|
||||
csi.PhysicalRegionSizeInPages = reader.ReadUInt32();
|
||||
csi.SizeInBytes = reader.ReadUInt32();
|
||||
return csi;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class ExeFSFileHeader
|
||||
{
|
||||
private const string codeSegment = ".code\0\0\0";
|
||||
|
||||
public string FileName;
|
||||
public bool IsCodeBinary { get { return FileName == codeSegment; } }
|
||||
public uint FileOffset;
|
||||
public uint FileSize;
|
||||
public byte[] FileHash = new byte[0x20];
|
||||
|
||||
public static ExeFSFileHeader Read(BinaryReader reader)
|
||||
{
|
||||
ExeFSFileHeader header = new ExeFSFileHeader();
|
||||
|
||||
try
|
||||
{
|
||||
header.FileName = new string(reader.ReadChars(8));
|
||||
header.FileOffset = reader.ReadUInt32();
|
||||
header.FileSize = reader.ReadUInt32();
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class ExeFSHeader
|
||||
{
|
||||
public ExeFSFileHeader[] FileHeaders = new ExeFSFileHeader[10];
|
||||
public byte[] Reserved = new byte[0x20];
|
||||
|
||||
public static ExeFSHeader Read(BinaryReader reader)
|
||||
{
|
||||
ExeFSHeader header = new ExeFSHeader();
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
header.FileHeaders[i] = ExeFSFileHeader.Read(reader);
|
||||
|
||||
header.Reserved = reader.ReadBytes(0x20);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
byte[] fileHash = reader.ReadBytes(0x20);
|
||||
header.FileHeaders[9 - i].FileHash = fileHash;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ThreeDS.Data;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class NCCHHeader
|
||||
{
|
||||
private const string NCCHMagicNumber = "NCCH";
|
||||
|
||||
public byte[] RSA2048Signature = new byte[0x100];
|
||||
public uint ContentSizeInMediaUnits;
|
||||
public uint ContentSizeInBytes { get { return ContentSizeInMediaUnits * 0x200; } }
|
||||
public byte[] PartitionId = new byte[8];
|
||||
public byte[] MakerCode = new byte[2];
|
||||
public byte[] Version = new byte[2];
|
||||
public byte[] VerificationHash = new byte[4];
|
||||
public byte[] ProgramId = new byte[4];
|
||||
public byte[] Reserved1 = new byte[0x10];
|
||||
public byte[] LogoRegionHash = new byte[0x20];
|
||||
public byte[] ProductCode = new byte[0x10];
|
||||
public byte[] ExtendedHeaderHash = new byte[0x20];
|
||||
public uint ExtendedHeaderSizeInBytes;
|
||||
public byte[] Reserved2 = new byte[4];
|
||||
public NCCHHeaderFlags Flags;
|
||||
public uint PlainRegionOffsetInMediaUnits;
|
||||
public uint PlainRegionOffsetInBytes { get { return PlainRegionOffsetInMediaUnits * 0x200; } }
|
||||
public uint PlainRegionSizeInMediaUnits;
|
||||
public uint PlainRegionSizeInBytes { get { return PlainRegionSizeInMediaUnits * 0x200; } }
|
||||
public uint LogoRegionOffsetInMediaUnits;
|
||||
public uint LogoRegionOffsetInBytes { get { return LogoRegionOffsetInMediaUnits * 0x200; } }
|
||||
public uint LogoRegionSizeInMediaUnits;
|
||||
public uint LogoRegionSizeInBytes { get { return LogoRegionSizeInMediaUnits * 0x200; } }
|
||||
public uint ExeFSOffsetInMediaUnits;
|
||||
public uint ExeFSOffsetInBytes { get { return ExeFSOffsetInMediaUnits * 0x200; } }
|
||||
public uint ExeFSSizeInMediaUnits;
|
||||
public uint ExeFSSizeInBytes { get { return ExeFSSizeInMediaUnits * 0x200; } }
|
||||
public uint ExeFSHashRegionOffsetInMediaUnits;
|
||||
public uint ExeFSHashRegionOffsetInBytes { get { return ExeFSHashRegionOffsetInMediaUnits * 0x200; } }
|
||||
public uint ExeFSHashRegionSizeInMediaUnits;
|
||||
public uint ExeFSHashRegionSizeInBytes { get { return ExeFSHashRegionSizeInMediaUnits * 0x200; } }
|
||||
public uint RomFSOffsetInMediaUnits;
|
||||
public uint RomFSOffsetInBytes { get { return RomFSOffsetInMediaUnits * 0x200; } }
|
||||
public uint RomFSSizeInMediaUnits;
|
||||
public uint RomFSSizeInBytes { get { return RomFSSizeInMediaUnits * 0x200; } }
|
||||
public uint RomFSHashRegionOffsetInMediaUnits;
|
||||
public uint RomFSHashRegionOffsetInBytes { get { return RomFSHashRegionOffsetInMediaUnits * 0x200; } }
|
||||
public uint RomFSHashRegionSizeInMediaUnits;
|
||||
public uint RomFSHashRegionSizeInBytes { get { return RomFSHashRegionSizeInMediaUnits * 0x200; } }
|
||||
public byte[] ExeFSSuperblockHash = new byte[0x20];
|
||||
public byte[] RomFSSuperblockHash = new byte[0x20];
|
||||
|
||||
public static NCCHHeader Read(BinaryReader reader)
|
||||
{
|
||||
NCCHHeader header = new NCCHHeader();
|
||||
|
||||
try
|
||||
{
|
||||
header.RSA2048Signature = reader.ReadBytes(0x100);
|
||||
|
||||
if (new string(reader.ReadChars(4)) != NCCHMagicNumber)
|
||||
return null;
|
||||
|
||||
header.ContentSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.PartitionId = reader.ReadBytes(8).Reverse().ToArray();
|
||||
header.MakerCode = reader.ReadBytes(2);
|
||||
header.Version = reader.ReadBytes(2);
|
||||
header.VerificationHash = reader.ReadBytes(4);
|
||||
header.ProgramId = reader.ReadBytes(8);
|
||||
header.Reserved1 = reader.ReadBytes(0x10);
|
||||
header.LogoRegionHash = reader.ReadBytes(0x20);
|
||||
header.ProductCode = reader.ReadBytes(0x10);
|
||||
header.ExtendedHeaderHash = reader.ReadBytes(0x20);
|
||||
header.ExtendedHeaderSizeInBytes = reader.ReadUInt32();
|
||||
header.Reserved2 = reader.ReadBytes(4);
|
||||
header.Flags = NCCHHeaderFlags.Read(reader);
|
||||
header.PlainRegionOffsetInMediaUnits = reader.ReadUInt32();
|
||||
header.PlainRegionSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.LogoRegionOffsetInMediaUnits = reader.ReadUInt32();
|
||||
header.LogoRegionSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.ExeFSOffsetInMediaUnits = reader.ReadUInt32();
|
||||
header.ExeFSSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.ExeFSHashRegionOffsetInMediaUnits = reader.ReadUInt32();
|
||||
header.ExeFSHashRegionSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.RomFSOffsetInMediaUnits = reader.ReadUInt32();
|
||||
header.RomFSSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.RomFSHashRegionOffsetInMediaUnits = reader.ReadUInt32();
|
||||
header.RomFSHashRegionSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.ExeFSSuperblockHash = reader.ReadBytes(0x20);
|
||||
header.RomFSSuperblockHash = reader.ReadBytes(0x20);
|
||||
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using ThreeDS.Data;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class NCCHHeaderFlags
|
||||
{
|
||||
public byte Reserved0;
|
||||
public byte Reserved1;
|
||||
public byte Reserved2;
|
||||
public CryptoMethod CryptoMethod;
|
||||
public ContentPlatform ContentPlatform;
|
||||
public ContentType MediaPlatformIndex;
|
||||
public byte ContentUnitSize;
|
||||
public uint ContentUnitSizeInBytes { get { return (uint)(0x200 * Math.Pow(2, this.ContentUnitSize)); } }
|
||||
public BitMasks BitMasks;
|
||||
|
||||
public static NCCHHeaderFlags Read(BinaryReader reader)
|
||||
{
|
||||
NCCHHeaderFlags flags = new NCCHHeaderFlags();
|
||||
|
||||
try
|
||||
{
|
||||
flags.Reserved0 = reader.ReadByte();
|
||||
flags.Reserved1 = reader.ReadByte();
|
||||
flags.Reserved2 = reader.ReadByte();
|
||||
flags.CryptoMethod = (CryptoMethod)reader.ReadByte();
|
||||
flags.ContentPlatform = (ContentPlatform)reader.ReadByte();
|
||||
flags.MediaPlatformIndex = (ContentType)reader.ReadByte();
|
||||
flags.ContentUnitSize = reader.ReadByte();
|
||||
flags.BitMasks = (BitMasks)reader.ReadByte();
|
||||
return flags;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
try
|
||||
{
|
||||
writer.Write(this.Reserved0);
|
||||
writer.Write(this.Reserved1);
|
||||
writer.Write(this.Reserved2);
|
||||
writer.Write((byte)this.CryptoMethod);
|
||||
writer.Write((byte)this.ContentPlatform);
|
||||
writer.Write((byte)this.MediaPlatformIndex);
|
||||
writer.Write((byte)this.ContentUnitSize);
|
||||
writer.Write((byte)this.BitMasks);
|
||||
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using ThreeDS.Data;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class NCSDHeader
|
||||
{
|
||||
private const string NCSDMagicNumber = "NCSD";
|
||||
|
||||
// Common to all NCSD files
|
||||
public byte[] RSA2048Signature = new byte[0x100];
|
||||
public uint ImageSizeInMediaUnits;
|
||||
public uint ImageSizeInBytes { get { return ImageSizeInMediaUnits * 0x200; } }
|
||||
public byte[] MediaId = new byte[8];
|
||||
public FilesystemType PartitionsFSType;
|
||||
public byte[] PartitionsCryptType = new byte[8];
|
||||
public PartitionTableEntry[] PartitionsTable = new PartitionTableEntry[8];
|
||||
|
||||
// For carts
|
||||
public byte[] ExheaderHash = new byte[0x20];
|
||||
public uint AdditionalHeaderSize;
|
||||
public uint SectorZeroOffset;
|
||||
private byte[] partitionFlags = new byte[8];
|
||||
public byte BackupWriteWaitTime { get { return partitionFlags[(int)NCSDFlags.BackupWriteWaitTime]; } }
|
||||
public MediaCardDeviceType MediaCardDevice3X { get { return (MediaCardDeviceType)partitionFlags[(int)NCSDFlags.MediaCardDevice3X]; } }
|
||||
public MediaPlatformIndex MediaPlatformIndex { get { return (MediaPlatformIndex)partitionFlags[(int)NCSDFlags.MediaPlatformIndex]; } }
|
||||
public MediaTypeIndex MediaTypeIndex { get { return (MediaTypeIndex)partitionFlags[(int)NCSDFlags.MediaTypeIndex]; } }
|
||||
public uint SectorSize { get { return (uint)(0x200 * Math.Pow(2, partitionFlags[(int)NCSDFlags.MediaUnitSize])); } }
|
||||
public MediaCardDeviceType MediaCardDevice2X { get { return (MediaCardDeviceType)partitionFlags[(int)NCSDFlags.MediaCardDevice2X]; } }
|
||||
public byte[][] PartitionIdTable = new byte[8][];
|
||||
public byte[] ReservedBlock1 = new byte[0x20];
|
||||
public byte[] ReservedBlock2 = new byte[0xE];
|
||||
public byte FirmUpdateByte1;
|
||||
public byte FIrmUpdateByte2;
|
||||
|
||||
// For NAND
|
||||
public byte[] Unknown = new byte[0x5E];
|
||||
public byte[] EncryptedMBR = new byte[0x42];
|
||||
|
||||
public static NCSDHeader Read(BinaryReader reader)
|
||||
{
|
||||
NCSDHeader header = new NCSDHeader();
|
||||
|
||||
try
|
||||
{
|
||||
header.RSA2048Signature = reader.ReadBytes(0x100);
|
||||
|
||||
if (new string(reader.ReadChars(4)) != NCSDMagicNumber)
|
||||
return null;
|
||||
|
||||
header.ImageSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.MediaId = reader.ReadBytes(8);
|
||||
header.PartitionsFSType = (FilesystemType)reader.ReadUInt64();
|
||||
header.PartitionsCryptType = reader.ReadBytes(8);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
header.PartitionsTable[i] = PartitionTableEntry.Read(reader);
|
||||
|
||||
if (header.PartitionsFSType == FilesystemType.Normal
|
||||
|| header.PartitionsFSType == FilesystemType.None)
|
||||
{
|
||||
header.ExheaderHash = reader.ReadBytes(0x20);
|
||||
header.AdditionalHeaderSize = reader.ReadUInt32();
|
||||
header.SectorZeroOffset = reader.ReadUInt32();
|
||||
header.partitionFlags = reader.ReadBytes(8);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
header.PartitionIdTable[i] = reader.ReadBytes(8);
|
||||
|
||||
header.ReservedBlock1 = reader.ReadBytes(0x20);
|
||||
header.ReservedBlock2 = reader.ReadBytes(0xE);
|
||||
header.FirmUpdateByte1 = reader.ReadByte();
|
||||
header.FIrmUpdateByte2 = reader.ReadByte();
|
||||
}
|
||||
else if (header.PartitionsFSType == FilesystemType.FIRM)
|
||||
{
|
||||
header.Unknown = reader.ReadBytes(0x5E);
|
||||
header.EncryptedMBR = reader.ReadBytes(0x42);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class StorageInfo
|
||||
{
|
||||
public byte[] ExtdataID = new byte[8];
|
||||
public byte[] SystemSavedataIDs = new byte[8];
|
||||
public byte[] StorageAccessibleUniqueIDs = new byte[8];
|
||||
public byte[] FilesystemAccessInfo = new byte[7];
|
||||
public byte OtherAttributes;
|
||||
|
||||
public static StorageInfo Read(BinaryReader reader)
|
||||
{
|
||||
StorageInfo si = new StorageInfo();
|
||||
|
||||
try
|
||||
{
|
||||
si.ExtdataID = reader.ReadBytes(8);
|
||||
si.SystemSavedataIDs = reader.ReadBytes(8);
|
||||
si.StorageAccessibleUniqueIDs = reader.ReadBytes(8);
|
||||
si.FilesystemAccessInfo = reader.ReadBytes(7);
|
||||
si.OtherAttributes = reader.ReadByte();
|
||||
return si;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class SystemControlInfo
|
||||
{
|
||||
public char[] ApplicationTitle = new char[8];
|
||||
public byte[] Reserved1 = new byte[5];
|
||||
public byte Flag;
|
||||
public byte[] RemasterVersion = new byte[2];
|
||||
public CodeSetInfo TextCodesetInfo;
|
||||
public uint StackSize;
|
||||
public CodeSetInfo ReadOnlyCodeSetInfo;
|
||||
public byte[] Reserved2 = new byte[4];
|
||||
public CodeSetInfo DataCodeSetInfo;
|
||||
public uint BSSSize;
|
||||
public byte[][] DependencyModuleList = new byte[48][];
|
||||
public SystemInfo SystemInfo;
|
||||
|
||||
public static SystemControlInfo Read(BinaryReader reader)
|
||||
{
|
||||
SystemControlInfo sci = new SystemControlInfo();
|
||||
|
||||
try
|
||||
{
|
||||
sci.ApplicationTitle = reader.ReadChars(8);
|
||||
sci.Reserved1 = reader.ReadBytes(5);
|
||||
sci.Flag = reader.ReadByte();
|
||||
sci.RemasterVersion = reader.ReadBytes(2);
|
||||
sci.TextCodesetInfo = CodeSetInfo.Read(reader);
|
||||
sci.StackSize = reader.ReadUInt32();
|
||||
sci.ReadOnlyCodeSetInfo = CodeSetInfo.Read(reader);
|
||||
sci.Reserved2 = reader.ReadBytes(4);
|
||||
sci.DataCodeSetInfo = CodeSetInfo.Read(reader);
|
||||
sci.BSSSize = reader.ReadUInt32();
|
||||
|
||||
for (int i = 0; i < 48; i++)
|
||||
sci.DependencyModuleList[i] = reader.ReadBytes(8);
|
||||
|
||||
sci.SystemInfo = SystemInfo.Read(reader);
|
||||
return sci;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
{
|
||||
public class SystemInfo
|
||||
{
|
||||
public ulong SaveDataSize;
|
||||
public byte[] JumpID = new byte[8];
|
||||
public byte[] Reserved = new byte[0x30];
|
||||
|
||||
public static SystemInfo Read(BinaryReader reader)
|
||||
{
|
||||
SystemInfo si = new SystemInfo();
|
||||
|
||||
try
|
||||
{
|
||||
si.SaveDataSize = reader.ReadUInt64();
|
||||
si.JumpID = reader.ReadBytes(8);
|
||||
si.Reserved = reader.ReadBytes(0x30);
|
||||
return si;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS
|
||||
{
|
||||
class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 2 || (args[0] != "encrypt" && args[0] != "decrypt"))
|
||||
{
|
||||
Console.WriteLine("Usage: 3dsdecrypt.exe (decrypt|encrypt) [-dev] <file|dir> ...");
|
||||
return;
|
||||
}
|
||||
|
||||
bool development = false;
|
||||
int start = 1;
|
||||
if (args[1] == "-dev")
|
||||
{
|
||||
development = true;
|
||||
start = 2;
|
||||
}
|
||||
|
||||
for (int i = start; i < args.Length; i++)
|
||||
{
|
||||
if (File.Exists(args[i]))
|
||||
{
|
||||
ThreeDSTool tool = new ThreeDSTool(args[i], development);
|
||||
if (args[0] == "decrypt")
|
||||
tool.Decrypt();
|
||||
else if (args[0] == "encrypt")
|
||||
tool.Encrypt();
|
||||
}
|
||||
else if (Directory.Exists(args[i]))
|
||||
{
|
||||
foreach (string file in Directory.EnumerateFiles(args[i], "*", SearchOption.AllDirectories))
|
||||
{
|
||||
ThreeDSTool tool = new ThreeDSTool(file, development);
|
||||
if (args[0] == "decrypt")
|
||||
tool.Decrypt();
|
||||
else if (args[0] == "encrypt")
|
||||
tool.Encrypt();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("3DSDecrypt")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("3DSDecrypt")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("2e30006a-3c60-4576-a262-937b21c83c06")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,623 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Security;
|
||||
using ThreeDS.Data;
|
||||
using ThreeDS.Headers;
|
||||
|
||||
namespace ThreeDS
|
||||
{
|
||||
public class ThreeDSTool
|
||||
{
|
||||
private readonly string filename;
|
||||
private readonly bool development;
|
||||
|
||||
public ThreeDSTool(string filename, bool development)
|
||||
{
|
||||
this.filename = filename;
|
||||
this.development = development;
|
||||
}
|
||||
|
||||
public void Decrypt()
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
return;
|
||||
|
||||
Console.WriteLine(filename);
|
||||
|
||||
using (BinaryReader f = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
using (BinaryWriter g = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
|
||||
{
|
||||
NCSDHeader header = NCSDHeader.Read(f);
|
||||
if (header == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a 3DS Rom!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over all 8 NCCH partitions
|
||||
for (int p = 0; p < 8; p++)
|
||||
{
|
||||
if (!header.PartitionsTable[p].IsValid())
|
||||
{
|
||||
Console.WriteLine("Partition {0} Not found... Skipping...", p);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Seek to the beginning of the NCCH partition
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize), SeekOrigin.Begin);
|
||||
|
||||
NCCHHeader partitionHeader = NCCHHeader.Read(f);
|
||||
if (partitionHeader == null)
|
||||
{
|
||||
Console.WriteLine("Partition {0} Unable to read NCCH header", p);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the 'NoCrypto' bit is set
|
||||
if ((partitionHeader.Flags.BitMasks & BitMasks.NoCrypto) != 0)
|
||||
{
|
||||
Console.WriteLine("Partition {0:d}: Already Decrypted?...", p);
|
||||
continue;
|
||||
}
|
||||
|
||||
// PartitionID is used as IV joined with the content type.
|
||||
byte[] plainIV = partitionHeader.PartitionId.Concat(Constants.PlainCounter).ToArray(); // Get the IV for plain sector (TitleID + Plain Counter)
|
||||
byte[] exefsIV = partitionHeader.PartitionId.Concat(Constants.ExefsCounter).ToArray(); // Get the IV for ExeFS (TitleID + ExeFS Counter)
|
||||
byte[] romfsIV = partitionHeader.PartitionId.Concat(Constants.RomfsCounter).ToArray(); // Get the IV for RomFS (TitleID + RomFS Counter)
|
||||
|
||||
BigInteger KeyX = 0;
|
||||
BigInteger KeyX2C = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C);
|
||||
BigInteger KeyY = new BigInteger(partitionHeader.RSA2048Signature.Take(16).Reverse().ToArray()); // KeyY is the first 16 bytes of the partition RSA-2048 SHA-256 signature
|
||||
|
||||
BigInteger NormalKey = 0;
|
||||
BigInteger NormalKey2C = RotateLeft((RotateLeft(KeyX2C, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128);
|
||||
|
||||
// Determine the Keys to be used
|
||||
if ((partitionHeader.Flags.BitMasks & BitMasks.FixedCryptoKey) != 0)
|
||||
{
|
||||
NormalKey = 0x00;
|
||||
NormalKey2C = 0x00;
|
||||
if (p == 0)
|
||||
Console.WriteLine("Encryption Method: Zero Key");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (partitionHeader.Flags.CryptoMethod == CryptoMethod.Original)
|
||||
{
|
||||
KeyX = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C);
|
||||
if (p == 0)
|
||||
Console.WriteLine("Encryption Method: Key 0x2C");
|
||||
}
|
||||
else if (partitionHeader.Flags.CryptoMethod == CryptoMethod.Seven)
|
||||
{
|
||||
KeyX = (development ? Constants.KeyX0x25 : Constants.KeyX0x25);
|
||||
if (p == 0)
|
||||
Console.WriteLine("Encryption Method: Key 0x25");
|
||||
}
|
||||
else if (partitionHeader.Flags.CryptoMethod == CryptoMethod.NineThree)
|
||||
{
|
||||
KeyX = (development ? Constants.DevKeyX0x18 : Constants.KeyX0x18);
|
||||
if (p == 0)
|
||||
Console.WriteLine("Encryption Method: Key 0x18");
|
||||
}
|
||||
else if (partitionHeader.Flags.CryptoMethod == CryptoMethod.NineSix)
|
||||
{
|
||||
KeyX = (development ? Constants.DevKeyX0x1B : Constants.KeyX0x1B);
|
||||
if (p == 0)
|
||||
Console.WriteLine("Encryption Method: Key 0x1B");
|
||||
}
|
||||
|
||||
NormalKey = RotateLeft((RotateLeft(KeyX, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128);
|
||||
}
|
||||
|
||||
// Decrypted extended header, if it exists
|
||||
if (partitionHeader.ExtendedHeaderSizeInBytes > 0)
|
||||
{
|
||||
// Seek to the partition start and skip first part of the header
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x200, SeekOrigin.Begin);
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x200, SeekOrigin.Begin);
|
||||
|
||||
var str = BitConverter.ToString(plainIV).Replace("-", "");
|
||||
|
||||
var exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), plainIV));
|
||||
|
||||
Console.WriteLine("Partition {0} ExeFS: Decrypting: ExHeader", p);
|
||||
|
||||
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(Constants.CXTExtendedDataHeaderLength)));
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
// Decrypt the ExeFS, if it exists
|
||||
if (partitionHeader.ExeFSSizeInBytes > 0)
|
||||
{
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
|
||||
|
||||
var exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIV));
|
||||
|
||||
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes((int)header.SectorSize)));
|
||||
g.Flush();
|
||||
|
||||
Console.WriteLine("Partition {0} ExeFS: Decrypting: ExeFS Filename Table", p);
|
||||
|
||||
if (partitionHeader.Flags.CryptoMethod != CryptoMethod.Original)
|
||||
{
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
|
||||
ExeFSHeader exefsHeader = ExeFSHeader.Read(f);
|
||||
if (exefsHeader != null)
|
||||
{
|
||||
foreach (ExeFSFileHeader fileHeader in exefsHeader.FileHeaders)
|
||||
{
|
||||
if (!fileHeader.IsCodeBinary)
|
||||
continue;
|
||||
|
||||
uint datalenM = ((fileHeader.FileSize) / (1024 * 1024));
|
||||
uint datalenB = ((fileHeader.FileSize) % (1024 * 1024));
|
||||
uint ctroffset = ((fileHeader.FileOffset + header.SectorSize) / 0x10);
|
||||
|
||||
byte[] exefsIVWithOffsetForHeader = AddToByteArray(exefsIV, (int)ctroffset);
|
||||
|
||||
var exefsctrmode = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
exefsctrmode.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), exefsIVWithOffsetForHeader));
|
||||
|
||||
exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
exefsctrmode2C.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIVWithOffsetForHeader));
|
||||
|
||||
f.BaseStream.Seek((((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.SectorSize) + fileHeader.FileOffset, SeekOrigin.Begin);
|
||||
g.BaseStream.Seek((((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.SectorSize) + fileHeader.FileOffset, SeekOrigin.Begin);
|
||||
|
||||
if (datalenM > 0)
|
||||
{
|
||||
for (int i = 0; i < datalenM; i++)
|
||||
{
|
||||
g.Write(exefsctrmode2C.ProcessBytes(exefsctrmode.ProcessBytes(f.ReadBytes(1024 * 1024))));
|
||||
g.Flush();
|
||||
Console.Write("\rPartition {0} ExeFS: Decrypting: {1}... {2} / {3} mb...", p, fileHeader.FileName, i, datalenM + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (datalenB > 0)
|
||||
{
|
||||
g.Write(exefsctrmode2C.ProcessBytes(exefsctrmode.ProcessBytes(f.ReadBytes((int)datalenB))));
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
Console.Write("\rPartition {0} ExeFS: Decrypting: {1}... {2} / {3} mb... Done!\r\n", p, fileHeader.FileName, datalenM + 1, datalenM + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decrypt exefs
|
||||
int exefsSizeM = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.SectorSize) / (1024 * 1024);
|
||||
int exefsSizeB = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.SectorSize) % (1024 * 1024);
|
||||
int ctroffsetE = (int)(header.SectorSize / 0x10);
|
||||
|
||||
byte[] exefsIVWithOffset = AddToByteArray(exefsIV, ctroffsetE);
|
||||
|
||||
exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIVWithOffset));
|
||||
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.SectorSize, SeekOrigin.Begin);
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.SectorSize, SeekOrigin.Begin);
|
||||
if (exefsSizeM > 0)
|
||||
{
|
||||
for (int i = 0; i < exefsSizeM; i++)
|
||||
{
|
||||
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(1024 * 1024)));
|
||||
g.Flush();
|
||||
Console.Write("\rPartition {0} ExeFS: Decrypting: {1} / {2} mb", p, i, exefsSizeM + 1);
|
||||
}
|
||||
}
|
||||
if (exefsSizeB > 0)
|
||||
{
|
||||
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(exefsSizeB)));
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
Console.Write("\rPartition {0} ExeFS: Decrypting: {1} / {2} mb... Done!\r\n", p, exefsSizeM + 1, exefsSizeM + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Partition {0} ExeFS: No Data... Skipping...", p);
|
||||
}
|
||||
|
||||
if (partitionHeader.RomFSOffsetInMediaUnits != 0)
|
||||
{
|
||||
int romfsSizeM = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) / (1024 * 1024);
|
||||
int romfsSizeB = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) % (1024 * 1024);
|
||||
|
||||
var romfsctrmode = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
romfsctrmode.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), romfsIV));
|
||||
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
|
||||
if (romfsSizeM > 0)
|
||||
{
|
||||
for (int i = 0; i < romfsSizeM; i++)
|
||||
{
|
||||
g.Write(romfsctrmode.ProcessBytes(f.ReadBytes(1024 * 1024)));
|
||||
g.Flush();
|
||||
Console.Write("\rPartition {0} RomFS: Decrypting: {1} / {2} mb", p, i, romfsSizeM + 1);
|
||||
}
|
||||
}
|
||||
if (romfsSizeB > 0)
|
||||
{
|
||||
g.Write(romfsctrmode.ProcessBytes(f.ReadBytes(romfsSizeB)));
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
Console.Write("\rPartition {0} RomFS: Decrypting: {1} / {2} mb... Done!\r\n", p, romfsSizeM + 1, romfsSizeM + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Partition {0} RomFS: No Data... Skipping...", p);
|
||||
}
|
||||
|
||||
// Write the new CryptoMethod
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18B, SeekOrigin.Begin);
|
||||
g.Write((byte)CryptoMethod.Original);
|
||||
g.Flush();
|
||||
|
||||
// Write the new BitMasks flag
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18F, SeekOrigin.Begin);
|
||||
BitMasks flag = partitionHeader.Flags.BitMasks;
|
||||
flag = flag & (BitMasks)((byte)(BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) ^ 0xFF);
|
||||
flag = (flag | BitMasks.NoCrypto);
|
||||
g.Write((byte)flag);
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
Console.WriteLine("Press Enter to Exit...");
|
||||
Console.Read();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes wrong header values
|
||||
/// </summary>
|
||||
public void Encrypt()
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
return;
|
||||
|
||||
Console.WriteLine(filename);
|
||||
|
||||
using (BinaryReader f = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
using (BinaryWriter g = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
|
||||
{
|
||||
NCSDHeader header = NCSDHeader.Read(f);
|
||||
if (header == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a 3DS Rom!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over all 8 NCCH partitions
|
||||
for (int p = 0; p < 8; p++)
|
||||
{
|
||||
if (!header.PartitionsTable[p].IsValid())
|
||||
{
|
||||
Console.WriteLine("Partition {0} Not found... Skipping...", p);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Seek to the beginning of the NCCH partition
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize), SeekOrigin.Begin);
|
||||
|
||||
NCCHHeader partitionHeader = NCCHHeader.Read(f);
|
||||
if (partitionHeader == null)
|
||||
{
|
||||
Console.WriteLine("Partition {0} Unable to read NCCH header", p);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the backup flags
|
||||
f.BaseStream.Seek(0x1188, SeekOrigin.Begin);
|
||||
NCCHHeaderFlags backupFlags = NCCHHeaderFlags.Read(f);
|
||||
|
||||
// Check if the 'NoCrypto' bit is not set
|
||||
if ((partitionHeader.Flags.BitMasks & BitMasks.NoCrypto) == 0)
|
||||
{
|
||||
Console.WriteLine("Partition {0:d}: Already Encrypted?...", p);
|
||||
continue;
|
||||
}
|
||||
|
||||
// PartitionID is used as IV joined with the content type.
|
||||
byte[] plainIV = partitionHeader.PartitionId.Concat(Constants.PlainCounter).ToArray(); // Get the IV for plain sector (TitleID + Plain Counter)
|
||||
byte[] exefsIV = partitionHeader.PartitionId.Concat(Constants.ExefsCounter).ToArray(); // Get the IV for ExeFS (TitleID + ExeFS Counter)
|
||||
byte[] romfsIV = partitionHeader.PartitionId.Concat(Constants.RomfsCounter).ToArray(); // Get the IV for RomFS (TitleID + RomFS Counter)
|
||||
|
||||
BigInteger KeyX = 0;
|
||||
BigInteger KeyX2C = Constants.KeyX0x2C;
|
||||
BigInteger KeyY = new BigInteger(partitionHeader.RSA2048Signature.Take(16).Reverse().ToArray()); // KeyY is the first 16 bytes of the partition RSA-2048 SHA-256 signature
|
||||
|
||||
BigInteger NormalKey = 0;
|
||||
BigInteger NormalKey2C = RotateLeft((RotateLeft(KeyX2C, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128);
|
||||
|
||||
// Determine the Keys to be used
|
||||
if ((backupFlags.BitMasks & BitMasks.FixedCryptoKey) != 0)
|
||||
{
|
||||
NormalKey = 0x00;
|
||||
NormalKey2C = 0x00;
|
||||
if (p == 0)
|
||||
Console.WriteLine("Encryption Method: Zero Key");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (backupFlags.CryptoMethod == CryptoMethod.Original)
|
||||
{
|
||||
KeyX = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C);
|
||||
if (p == 0)
|
||||
Console.WriteLine("Encryption Method: Key 0x2C");
|
||||
}
|
||||
else if (backupFlags.CryptoMethod == CryptoMethod.Seven)
|
||||
{
|
||||
KeyX = (development ? Constants.KeyX0x25 : Constants.KeyX0x25);
|
||||
if (p == 0)
|
||||
Console.WriteLine("Encryption Method: Key 0x25");
|
||||
}
|
||||
else if (backupFlags.CryptoMethod == CryptoMethod.NineThree)
|
||||
{
|
||||
KeyX = (development ? Constants.DevKeyX0x18 : Constants.KeyX0x18);
|
||||
if (p == 0)
|
||||
Console.WriteLine("Encryption Method: Key 0x18");
|
||||
}
|
||||
else if (backupFlags.CryptoMethod == CryptoMethod.NineSix)
|
||||
{
|
||||
KeyX = (development ? Constants.DevKeyX0x1B : Constants.KeyX0x1B);
|
||||
if (p == 0)
|
||||
Console.WriteLine("Encryption Method: Key 0x1B");
|
||||
}
|
||||
|
||||
NormalKey = RotateLeft((RotateLeft(KeyX, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128);
|
||||
}
|
||||
|
||||
// Encrypt extended header, if it exists
|
||||
if (partitionHeader.ExtendedHeaderSizeInBytes > 0)
|
||||
{
|
||||
// Seek to the partition start and skip first part of the header
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x200, SeekOrigin.Begin);
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x200, SeekOrigin.Begin);
|
||||
|
||||
var str = BitConverter.ToString(plainIV).Replace("-", "");
|
||||
|
||||
var exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
exefsctrmode2C.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), plainIV));
|
||||
|
||||
Console.WriteLine("Partition {0} ExeFS: Encrypting: ExHeader", p);
|
||||
|
||||
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(Constants.CXTExtendedDataHeaderLength)));
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
// Encrypt the ExeFS, if it exists
|
||||
if (partitionHeader.ExeFSSizeInBytes > 0)
|
||||
{
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
|
||||
|
||||
var exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
exefsctrmode2C.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIV));
|
||||
|
||||
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes((int)header.SectorSize)));
|
||||
g.Flush();
|
||||
|
||||
Console.WriteLine("Partition {0} ExeFS: Encrypting: ExeFS Filename Table", p);
|
||||
|
||||
if (backupFlags.CryptoMethod != CryptoMethod.Original)
|
||||
{
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
|
||||
ExeFSHeader exefsHeader = ExeFSHeader.Read(f);
|
||||
if (exefsHeader != null)
|
||||
{
|
||||
foreach (ExeFSFileHeader fileHeader in exefsHeader.FileHeaders)
|
||||
{
|
||||
if (!fileHeader.IsCodeBinary)
|
||||
continue;
|
||||
|
||||
uint datalenM = ((fileHeader.FileSize) / (1024 * 1024));
|
||||
uint datalenB = ((fileHeader.FileSize) % (1024 * 1024));
|
||||
uint ctroffset = ((fileHeader.FileOffset + header.SectorSize) / 0x10);
|
||||
|
||||
byte[] exefsIVWithOffsetForHeader = AddToByteArray(exefsIV, (int)ctroffset);
|
||||
|
||||
var exefsctrmode = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
exefsctrmode.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), exefsIVWithOffsetForHeader));
|
||||
|
||||
exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
exefsctrmode2C.Init(false, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIVWithOffsetForHeader));
|
||||
|
||||
f.BaseStream.Seek((((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.SectorSize) + fileHeader.FileOffset, SeekOrigin.Begin);
|
||||
g.BaseStream.Seek((((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits) + 1) * header.SectorSize) + fileHeader.FileOffset, SeekOrigin.Begin);
|
||||
|
||||
if (datalenM > 0)
|
||||
{
|
||||
for (int i = 0; i < datalenM; i++)
|
||||
{
|
||||
g.Write(exefsctrmode2C.ProcessBytes(exefsctrmode.ProcessBytes(f.ReadBytes(1024 * 1024))));
|
||||
g.Flush();
|
||||
Console.Write("\rPartition {0} ExeFS: Encrypting: {1}... {2} / {3} mb...", p, fileHeader.FileName, i, datalenM + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (datalenB > 0)
|
||||
{
|
||||
g.Write(exefsctrmode2C.ProcessBytes(exefsctrmode.ProcessBytes(f.ReadBytes((int)datalenB))));
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
Console.Write("\rPartition {0} ExeFS: Encrypting: {1}... {2} / {3} mb... Done!\r\n", p, fileHeader.FileName, datalenM + 1, datalenM + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decrypt exefs
|
||||
int exefsSizeM = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.SectorSize) / (1024 * 1024);
|
||||
int exefsSizeB = (int)((partitionHeader.ExeFSSizeInMediaUnits - 1) * header.SectorSize) % (1024 * 1024);
|
||||
int ctroffsetE = (int)(header.SectorSize / 0x10);
|
||||
|
||||
byte[] exefsIVWithOffset = AddToByteArray(exefsIV, ctroffsetE);
|
||||
|
||||
exefsctrmode2C = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
exefsctrmode2C.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey2C)), exefsIVWithOffset));
|
||||
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.SectorSize, SeekOrigin.Begin);
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.ExeFSOffsetInMediaUnits + 1) * header.SectorSize, SeekOrigin.Begin);
|
||||
if (exefsSizeM > 0)
|
||||
{
|
||||
for (int i = 0; i < exefsSizeM; i++)
|
||||
{
|
||||
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(1024 * 1024)));
|
||||
g.Flush();
|
||||
Console.Write("\rPartition {0} ExeFS: Encrypting: {1} / {2} mb", p, i, exefsSizeM + 1);
|
||||
}
|
||||
}
|
||||
if (exefsSizeB > 0)
|
||||
{
|
||||
g.Write(exefsctrmode2C.ProcessBytes(f.ReadBytes(exefsSizeB)));
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
Console.Write("\rPartition {0} ExeFS: Encrypting: {1} / {2} mb... Done!\r\n", p, exefsSizeM + 1, exefsSizeM + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Partition {0} ExeFS: No Data... Skipping...", p);
|
||||
}
|
||||
|
||||
if (partitionHeader.RomFSOffsetInMediaUnits != 0)
|
||||
{
|
||||
int romfsBlockSize = 16; // block size in mb
|
||||
int romfsSizeM = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) / (romfsBlockSize * (1024 * 1024));
|
||||
int romfsSizeB = (int)(partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) % (romfsBlockSize * (1024 * 1024));
|
||||
int romfsSizeTotalMb = (int)((partitionHeader.RomFSSizeInMediaUnits * header.SectorSize) / (1024 * 1024) + 1);
|
||||
|
||||
if (p > 0) // RomFS for partitions 1 and up always use Key0x2C
|
||||
{
|
||||
if ((backupFlags.BitMasks & BitMasks.FixedCryptoKey) != 0) // except if using zero-key
|
||||
{
|
||||
NormalKey = 0x00;
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyX = KeyX = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C);
|
||||
NormalKey = RotateLeft((RotateLeft(KeyX, 2, 128) ^ KeyY) + Constants.AESHardwareConstant, 87, 128);
|
||||
}
|
||||
}
|
||||
|
||||
var romfsctrmode = CipherUtilities.GetCipher("AES/CTR"); // ("AES/CTR/NoPadding")
|
||||
romfsctrmode.Init(true, new ParametersWithIV(new KeyParameter(TakeSixteen(NormalKey)), romfsIV));
|
||||
|
||||
f.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset + partitionHeader.RomFSOffsetInMediaUnits) * header.SectorSize, SeekOrigin.Begin);
|
||||
if (romfsSizeM > 0)
|
||||
{
|
||||
for (int i = 0; i < romfsSizeM; i++)
|
||||
{
|
||||
g.Write(romfsctrmode.ProcessBytes(f.ReadBytes(romfsBlockSize * 1024 * 1024)));
|
||||
g.Flush();
|
||||
Console.Write("\rPartition {0} RomFS: Encrypting: {1} / {2} mb", p, i * romfsBlockSize, romfsSizeTotalMb);
|
||||
}
|
||||
}
|
||||
if (romfsSizeB > 0)
|
||||
{
|
||||
g.Write(romfsctrmode.ProcessBytes(f.ReadBytes(romfsSizeB)));
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
Console.Write("\rPartition {0} RomFS: Encrypting: {1} / {2} mb... Done!\r\n", p, romfsSizeTotalMb, romfsSizeTotalMb);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Partition {0} RomFS: No Data... Skipping...", p);
|
||||
}
|
||||
|
||||
// Write the new CryptoMethod
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18B, SeekOrigin.Begin);
|
||||
if (p > 0)
|
||||
{
|
||||
g.Write((byte)CryptoMethod.Original); // For partitions 1 and up, set crypto-method to 0x00
|
||||
g.Flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
g.Write((byte)backupFlags.CryptoMethod); // If partition 0, restore crypto-method from backup flags
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
// Write the new BitMasks flag
|
||||
g.BaseStream.Seek((header.PartitionsTable[p].Offset * header.SectorSize) + 0x18F, SeekOrigin.Begin);
|
||||
BitMasks flag = partitionHeader.Flags.BitMasks;
|
||||
flag = (flag & ((BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF));
|
||||
flag = (flag | (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & backupFlags.BitMasks);
|
||||
g.Write((byte)flag);
|
||||
g.Flush();
|
||||
}
|
||||
|
||||
Console.WriteLine("Press Enter to Exit...");
|
||||
Console.Read();
|
||||
}
|
||||
}
|
||||
|
||||
private static BigInteger RotateLeft(BigInteger val, int r_bits, int max_bits)
|
||||
{
|
||||
return (val << r_bits % max_bits) & (BigInteger.Pow(2, max_bits) - 1) | ((val & (BigInteger.Pow(2, max_bits) - 1)) >> (max_bits - (r_bits % max_bits)));
|
||||
}
|
||||
|
||||
private static string ToBytes(int num)
|
||||
{
|
||||
string numstr = "";
|
||||
int tmp = num;
|
||||
|
||||
while (numstr.Length < 16)
|
||||
{
|
||||
numstr += (char)(tmp & 0xFF);
|
||||
tmp >>= 8;
|
||||
}
|
||||
|
||||
return numstr;
|
||||
}
|
||||
|
||||
private static byte[] AddToByteArray(byte[] input, int add)
|
||||
{
|
||||
int len = input.Length;
|
||||
var bigint = new BigInteger(input.Reverse().ToArray());
|
||||
bigint += add;
|
||||
var arr = bigint.ToByteArray().Reverse().ToArray();
|
||||
|
||||
if (arr.Length < len)
|
||||
{
|
||||
byte[] temp = new byte[len];
|
||||
for (int i = 0; i < (len - arr.Length); i++)
|
||||
temp[i] = 0x00;
|
||||
|
||||
Array.Copy(arr, 0, temp, len - arr.Length, arr.Length);
|
||||
arr = temp;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
private static byte[] TakeSixteen(BigInteger input)
|
||||
{
|
||||
var arr = input.ToByteArray().Take(16).Reverse().ToArray();
|
||||
|
||||
if (arr.Length < 16)
|
||||
{
|
||||
byte[] temp = new byte[16];
|
||||
for (int i = 0; i < (16 - arr.Length); i++)
|
||||
temp[i] = 0x00;
|
||||
|
||||
Array.Copy(arr, 0, temp, 16 - arr.Length, arr.Length);
|
||||
arr = temp;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="BouncyCastle" version="1.8.4" targetFramework="net461" />
|
||||
</packages>
|
||||
30
NDecrypt.sln
Normal file
30
NDecrypt.sln
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.28803.156
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{31CE0445-F693-4C9A-B6CD-499C38CFF7FE}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NDecrypt", "NDecrypt\NDecrypt.csproj", "{91C54370-5741-4742-B2E9-EC498551AD1C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{91C54370-5741-4742-B2E9-EC498551AD1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{91C54370-5741-4742-B2E9-EC498551AD1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{91C54370-5741-4742-B2E9-EC498551AD1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{91C54370-5741-4742-B2E9-EC498551AD1C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {5AB50D9B-BA18-4F96-804B-52E7E0845B37}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
368
NDecrypt/DSTool.cs
Normal file
368
NDecrypt/DSTool.cs
Normal file
@@ -0,0 +1,368 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NDecrypt.NDS;
|
||||
using NDecrypt.NDS.Headers;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
public class DSTool : ITool
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the input DS/DSi file
|
||||
/// </summary>
|
||||
private readonly string filename;
|
||||
|
||||
/// <summary>
|
||||
/// Flag to determine if encrypting or decrypting
|
||||
/// </summary>
|
||||
private readonly bool encrypt;
|
||||
|
||||
/// <summary>
|
||||
/// Flag to determine if forcing operations
|
||||
/// </summary>
|
||||
private readonly bool force;
|
||||
|
||||
#region Encryption process variables
|
||||
|
||||
private uint[] cardHash = new uint[0x412];
|
||||
private uint[] arg2 = new uint[3];
|
||||
|
||||
#endregion
|
||||
|
||||
public DSTool(string filename, bool encrypt, bool force)
|
||||
{
|
||||
this.filename = filename;
|
||||
this.encrypt = encrypt;
|
||||
this.force = force;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process an input file given the input values
|
||||
/// </summary>
|
||||
public bool ProcessFile()
|
||||
{
|
||||
// Make sure we have a file to process first
|
||||
Console.WriteLine(filename);
|
||||
if (!File.Exists(filename))
|
||||
return false;
|
||||
|
||||
// Open the read and write on the same file for inplace processing
|
||||
using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
using (BinaryWriter writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
|
||||
{
|
||||
NDSHeader header = NDSHeader.Read(reader);
|
||||
if (header == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a DS or DSi Rom!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Process the secure area
|
||||
ProcessSecureArea(header, reader, writer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process secure area in the DS/DSi file
|
||||
/// </summary>
|
||||
/// <param name="ndsHeader">NDS header representing the DS file</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void ProcessSecureArea(NDSHeader ndsHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
// If we're forcing the operation, tell the user
|
||||
if (force)
|
||||
{
|
||||
Console.WriteLine("File is not verified due to force flag being set.");
|
||||
}
|
||||
// If we're not forcing the operation, check to see if we should be proceeding
|
||||
else
|
||||
{
|
||||
bool? isDecrypted = CheckIfDecrypted(reader);
|
||||
if (isDecrypted == null)
|
||||
{
|
||||
Console.WriteLine("File has an empty secure area, cannot proceed");
|
||||
return;
|
||||
}
|
||||
else if (encrypt ^ isDecrypted.Value)
|
||||
{
|
||||
Console.WriteLine("File is already " + (encrypt ? "encrypted" : "decrypted"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessARM9(ndsHeader, reader, writer);
|
||||
|
||||
Console.WriteLine("File has been " + (encrypt ? "encrypted" : "decrypted"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the current file is already decrypted or not (or has an empty secure area)
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>True if the file has known values for a decrypted file, null if it's empty, false otherwise</returns>
|
||||
private bool? CheckIfDecrypted(BinaryReader reader)
|
||||
{
|
||||
reader.BaseStream.Seek(0x4000, SeekOrigin.Begin);
|
||||
uint firstValue = reader.ReadUInt32();
|
||||
uint secondValue = reader.ReadUInt32();
|
||||
|
||||
// Empty secure area standard
|
||||
if (firstValue == 0x00000000 && secondValue == 0x00000000)
|
||||
{
|
||||
Console.WriteLine("Empty secure area found. Cannot be encrypted or decrypted.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Improperly decrypted empty secure area (decrypt empty with woodsec)
|
||||
else if ((firstValue == 0xE386C397 && secondValue == 0x82775B7E)
|
||||
|| (firstValue == 0xF98415B8 && secondValue == 0x698068FC))
|
||||
{
|
||||
Console.WriteLine("Improperly decrypted empty secure area found. Should be encrypted to get proper value.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Improperly encrypted empty secure area (encrypt empty with woodsec)
|
||||
else if ((firstValue == 0x4BCE88BE && secondValue == 0xD3662DD1)
|
||||
|| (firstValue == 0x2543C534 && secondValue == 0xCC4BE38E))
|
||||
{
|
||||
Console.WriteLine("Improperly encrypted empty secure area found. Should be decrypted to get proper value.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Properly decrypted nonstandard value (mastering issue)
|
||||
else if ((firstValue == 0xD0D48B67 && secondValue == 0x39392F23) // Dragon Quest 5 (EU)
|
||||
|| (firstValue == 0x014A191A && secondValue == 0xA5C470B9) // Dragon Quest 5 (USA)
|
||||
|| (firstValue == 0x7829BC8D && secondValue == 0x9968EF44)) // Dragon Quest 5 (JP)
|
||||
{
|
||||
Console.WriteLine("Decrypted secure area for known, nonstandard value found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Properly decrypted prototype value
|
||||
else if (firstValue == 0xBA35F813 && secondValue == 0xB691AAE8)
|
||||
{
|
||||
Console.WriteLine("Decrypted secure area for prototype found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Properly encrypted prototype value
|
||||
else if (firstValue == 0xA71329EE && secondValue == 0x2A1D4C38)
|
||||
{
|
||||
Console.WriteLine("Encrypted secure area for prototype found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Standard decryption values
|
||||
return firstValue == 0xE7FFDEFF && secondValue == 0xE7FFDEFF;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the secure ARM9 region of the file, if possible
|
||||
/// </summary>
|
||||
/// <param name="ndsHeader">NDS header representing the DS file</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void ProcessARM9(NDSHeader ndsHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
// Seek to the beginning of the secure area
|
||||
reader.BaseStream.Seek(0x4000, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek(0x4000, SeekOrigin.Begin);
|
||||
|
||||
// Grab the first two blocks
|
||||
uint p0 = reader.ReadUInt32();
|
||||
uint p1 = reader.ReadUInt32();
|
||||
|
||||
// Perform the initialization steps
|
||||
Init1(ndsHeader);
|
||||
if (!encrypt) Decrypt(ref p1, ref p0);
|
||||
arg2[1] <<= 1;
|
||||
arg2[2] >>= 1;
|
||||
Init2();
|
||||
|
||||
// If we're decrypting, set the proper flags
|
||||
if (!encrypt)
|
||||
{
|
||||
Decrypt(ref p1, ref p0);
|
||||
if (p0 == Constants.MAGIC30 && p1 == Constants.MAGIC34)
|
||||
{
|
||||
p0 = 0xE7FFDEFF;
|
||||
p1 = 0xE7FFDEFF;
|
||||
}
|
||||
|
||||
writer.Write(p0);
|
||||
writer.Write(p1);
|
||||
}
|
||||
|
||||
// Ensure alignment
|
||||
reader.BaseStream.Seek(0x4008, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek(0x4008, SeekOrigin.Begin);
|
||||
|
||||
// Loop throgh the main encryption step
|
||||
uint size = 0x800 - 8;
|
||||
while (size > 0)
|
||||
{
|
||||
p0 = reader.ReadUInt32();
|
||||
p1 = reader.ReadUInt32();
|
||||
|
||||
if (encrypt)
|
||||
Encrypt(ref p1, ref p0);
|
||||
else
|
||||
Decrypt(ref p1, ref p0);
|
||||
|
||||
writer.Write(p0);
|
||||
writer.Write(p1);
|
||||
|
||||
size -= 8;
|
||||
}
|
||||
|
||||
// Replace the header explicitly if we're encrypting
|
||||
if (encrypt)
|
||||
{
|
||||
reader.BaseStream.Seek(0x4000, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek(0x4000, SeekOrigin.Begin);
|
||||
|
||||
p0 = reader.ReadUInt32();
|
||||
p1 = reader.ReadUInt32();
|
||||
|
||||
if (p0 == 0xE7FFDEFF && p1 == 0xE7FFDEFF)
|
||||
{
|
||||
p0 = Constants.MAGIC30;
|
||||
p1 = Constants.MAGIC34;
|
||||
}
|
||||
|
||||
Encrypt(ref p1, ref p0);
|
||||
Init1(ndsHeader);
|
||||
Encrypt(ref p1, ref p0);
|
||||
|
||||
writer.Write(p0);
|
||||
writer.Write(p1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// First common initialization step
|
||||
/// </summary>
|
||||
/// <param name="ndsHeader">NDS header representing the DS file</param>
|
||||
private void Init1(NDSHeader ndsHeader)
|
||||
{
|
||||
Buffer.BlockCopy(Constants.NDSEncryptionData, 0, cardHash, 0, 4 * (1024 + 18));
|
||||
arg2 = new uint[] { ndsHeader.Gamecode, ndsHeader.Gamecode >> 1, ndsHeader.Gamecode << 1 };
|
||||
Init2();
|
||||
Init2();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Second common initialization step
|
||||
/// </summary>
|
||||
private void Init2()
|
||||
{
|
||||
Encrypt(ref arg2[2], ref arg2[1]);
|
||||
Encrypt(ref arg2[1], ref arg2[0]);
|
||||
|
||||
byte[] allBytes = BitConverter.GetBytes(arg2[0])
|
||||
.Concat(BitConverter.GetBytes(arg2[1]))
|
||||
.Concat(BitConverter.GetBytes(arg2[2]))
|
||||
.ToArray();
|
||||
|
||||
UpdateHashtable(allBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a decryption step
|
||||
/// </summary>
|
||||
/// <param name="arg1">First unsigned value to use in decryption</param>
|
||||
/// <param name="arg2">Second unsigned value to use in decryption</param>
|
||||
private void Decrypt(ref uint arg1, ref uint arg2)
|
||||
{
|
||||
uint a = arg1;
|
||||
uint b = arg2;
|
||||
for (int i = 17; i > 1; i--)
|
||||
{
|
||||
uint c = cardHash[i] ^ a;
|
||||
a = b ^ Lookup(c);
|
||||
b = c;
|
||||
}
|
||||
|
||||
arg1 = b ^ cardHash[0];
|
||||
arg2 = a ^ cardHash[1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform an encryption step
|
||||
/// </summary>
|
||||
/// <param name="arg1">First unsigned value to use in encryption</param>
|
||||
/// <param name="arg2">Second unsigned value to use in encryption</param>
|
||||
private void Encrypt(ref uint arg1, ref uint arg2)
|
||||
{
|
||||
uint a = arg1;
|
||||
uint b = arg2;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
uint c = cardHash[i] ^ a;
|
||||
a = b ^ Lookup(c);
|
||||
b = c;
|
||||
}
|
||||
|
||||
arg2 = a ^ cardHash[16];
|
||||
arg1 = b ^ cardHash[17];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lookup the value from the hashtable
|
||||
/// </summary>
|
||||
/// <param name="v">Value to lookup in the hashtable</param>
|
||||
/// <returns>Processed value through the hashtable</returns>
|
||||
private uint Lookup(uint v)
|
||||
{
|
||||
uint a = (v >> 24) & 0xFF;
|
||||
uint b = (v >> 16) & 0xFF;
|
||||
uint c = (v >> 8) & 0xFF;
|
||||
uint d = (v >> 0) & 0xFF;
|
||||
|
||||
a = cardHash[a + 18 + 0];
|
||||
b = cardHash[b + 18 + 256];
|
||||
c = cardHash[c + 18 + 512];
|
||||
d = cardHash[d + 18 + 768];
|
||||
|
||||
return d + (c ^ (b + a));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the hashtable
|
||||
/// </summary>
|
||||
/// <param name="arg1">Value to update the hashtable with</param>
|
||||
private void UpdateHashtable(byte[] arg1)
|
||||
{
|
||||
for (int j = 0; j < 18; j++)
|
||||
{
|
||||
uint r3 = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
r3 <<= 8;
|
||||
r3 |= arg1[(j * 4 + i) & 7];
|
||||
}
|
||||
|
||||
cardHash[j] ^= r3;
|
||||
}
|
||||
|
||||
uint tmp1 = 0;
|
||||
uint tmp2 = 0;
|
||||
for (int i = 0; i < 18; i += 2)
|
||||
{
|
||||
Encrypt(ref tmp1, ref tmp2);
|
||||
cardHash[i + 0] = tmp1;
|
||||
cardHash[i + 1] = tmp2;
|
||||
}
|
||||
for (int i = 0; i < 0x400; i += 2)
|
||||
{
|
||||
Encrypt(ref tmp1, ref tmp2);
|
||||
cardHash[i + 18 + 0] = tmp1;
|
||||
cardHash[i + 18 + 1] = tmp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
NDecrypt/Helper.cs
Normal file
86
NDecrypt/Helper.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
public static class Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// Add an integer value to a number represented by a byte array
|
||||
/// </summary>
|
||||
/// <param name="input">Byte array to add to</param>
|
||||
/// <param name="add">Amount to add</param>
|
||||
/// <returns>Byte array representing the new value</returns>
|
||||
public static byte[] AddToByteArray(byte[] input, int add)
|
||||
{
|
||||
int len = input.Length;
|
||||
var bigint = new BigInteger(input.Reverse().ToArray());
|
||||
bigint += add;
|
||||
var arr = bigint.ToByteArray().Reverse().ToArray();
|
||||
|
||||
if (arr.Length < len)
|
||||
{
|
||||
byte[] temp = new byte[len];
|
||||
for (int i = 0; i < (len - arr.Length); i++)
|
||||
temp[i] = 0x00;
|
||||
|
||||
Array.Copy(arr, 0, temp, len - arr.Length, arr.Length);
|
||||
arr = temp;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create AES cipher and intialize
|
||||
/// </summary>
|
||||
/// <param name="key">BigInteger representation of 128-bit encryption key</param>
|
||||
/// <param name="iv">AES initial value for counter</param>
|
||||
/// <param name="encrypt">True if cipher is created for encryption, false otherwise</param>
|
||||
/// <returns>Initialized AES cipher</returns>
|
||||
public static IBufferedCipher CreateAESCipher(BigInteger key, byte[] iv, bool encrypt)
|
||||
{
|
||||
var cipher = CipherUtilities.GetCipher("AES/CTR");
|
||||
cipher.Init(encrypt, new ParametersWithIV(new KeyParameter(TakeSixteen(key)), iv));
|
||||
return cipher;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a rotate left on a BigInteger
|
||||
/// </summary>
|
||||
/// <param name="val">BigInteger value to rotate</param>
|
||||
/// <param name="r_bits">Number of bits to rotate</param>
|
||||
/// <param name="max_bits">Maximum number of bits to rotate on</param>
|
||||
/// <returns>Rotated BigInteger value</returns>
|
||||
public static BigInteger RotateLeft(BigInteger val, int r_bits, int max_bits)
|
||||
{
|
||||
return (val << r_bits % max_bits) & (BigInteger.Pow(2, max_bits) - 1) | ((val & (BigInteger.Pow(2, max_bits) - 1)) >> (max_bits - (r_bits % max_bits)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a 16-byte array representation of a BigInteger
|
||||
/// </summary>
|
||||
/// <param name="input">BigInteger value to convert</param>
|
||||
/// <returns>16-byte array representing the BigInteger</returns>
|
||||
private static byte[] TakeSixteen(BigInteger input)
|
||||
{
|
||||
var arr = input.ToByteArray().Take(16).Reverse().ToArray();
|
||||
|
||||
if (arr.Length < 16)
|
||||
{
|
||||
byte[] temp = new byte[16];
|
||||
for (int i = 0; i < (16 - arr.Length); i++)
|
||||
temp[i] = 0x00;
|
||||
|
||||
Array.Copy(arr, 0, temp, 16 - arr.Length, arr.Length);
|
||||
arr = temp;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
NDecrypt/ITool.cs
Normal file
13
NDecrypt/ITool.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
public interface ITool
|
||||
{
|
||||
bool ProcessFile();
|
||||
}
|
||||
}
|
||||
101
NDecrypt/N3DS/Constants.cs
Normal file
101
NDecrypt/N3DS/Constants.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace NDecrypt.N3DS
|
||||
{
|
||||
public class Constants
|
||||
{
|
||||
// Setup Keys and IVs
|
||||
public static byte[] PlainCounter = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static byte[] ExefsCounter = new byte[] { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public static byte[] RomfsCounter = new byte[] { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
public static BigInteger AESHardwareConstant { get; private set; }
|
||||
|
||||
#region Retail 3DS keys
|
||||
|
||||
// KeyX 0x18 (New 3DS 9.3)
|
||||
public static BigInteger KeyX0x18 { get; private set; }
|
||||
|
||||
// KeyX 0x1B (New 3DS 9.6)
|
||||
public static BigInteger KeyX0x1B { get; private set; }
|
||||
|
||||
// KeyX 0x25 (> 7.x)
|
||||
public static BigInteger KeyX0x25 { get; private set; }
|
||||
|
||||
// KeyX 0x2C (< 6.x)
|
||||
public static BigInteger KeyX0x2C { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dev 3DS Keys
|
||||
|
||||
// Dev KeyX 0x18 (New 3DS 9.3)
|
||||
public static BigInteger DevKeyX0x18 { get; private set; }
|
||||
|
||||
// Dev KeyX 0x1B New 3DS 9.6)
|
||||
public static BigInteger DevKeyX0x1B { get; private set; }
|
||||
|
||||
// Dev KeyX 0x25 (> 7.x)
|
||||
public static BigInteger DevKeyX0x25 { get; private set; }
|
||||
|
||||
// Dev KeyX 0x2C (< 6.x)
|
||||
public static BigInteger DevKeyX0x2C { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
public const int CXTExtendedDataHeaderLength = 0x800;
|
||||
|
||||
/// <summary>
|
||||
/// Represents if all of the keys have been initialized properly
|
||||
/// </summary>
|
||||
public static bool IsReady { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setup all of the necessary constants
|
||||
/// </summary>
|
||||
/// <remarks>keys.bin should be in little endian format</remarks>
|
||||
static Constants()
|
||||
{
|
||||
string keyfile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "keys.bin");
|
||||
if (!File.Exists(keyfile))
|
||||
{
|
||||
IsReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (BinaryReader reader = new BinaryReader(File.Open(keyfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
{
|
||||
// This is required to preserve sign for BigInteger
|
||||
byte[] signByte = new byte[] { 0x00 };
|
||||
|
||||
// Hardware constant
|
||||
AESHardwareConstant = new BigInteger(reader.ReadBytes(16));
|
||||
|
||||
// Retail keys
|
||||
KeyX0x18 = new BigInteger(reader.ReadBytes(16).Concat(signByte).ToArray());
|
||||
KeyX0x1B = new BigInteger(reader.ReadBytes(16).Concat(signByte).ToArray());
|
||||
KeyX0x25 = new BigInteger(reader.ReadBytes(16).Concat(signByte).ToArray());
|
||||
KeyX0x2C = new BigInteger(reader.ReadBytes(16).Concat(signByte).ToArray());
|
||||
|
||||
// Development keys
|
||||
DevKeyX0x18 = new BigInteger(reader.ReadBytes(16).Concat(signByte).ToArray());
|
||||
DevKeyX0x1B = new BigInteger(reader.ReadBytes(16).Concat(signByte).ToArray());
|
||||
DevKeyX0x25 = new BigInteger(reader.ReadBytes(16).Concat(signByte).ToArray());
|
||||
DevKeyX0x2C = new BigInteger(reader.ReadBytes(16).Concat(signByte).ToArray());
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
IsReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
IsReady = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
182
NDecrypt/N3DS/Enums.cs
Normal file
182
NDecrypt/N3DS/Enums.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
|
||||
namespace NDecrypt.N3DS
|
||||
{
|
||||
[Flags]
|
||||
public enum ARM9AccessControlDescriptors : byte
|
||||
{
|
||||
MountNandRoot = 0x01,
|
||||
MountNandroWriteAccess = 0x02,
|
||||
MountTwlnRoot = 0x04,
|
||||
MountWnandRoot = 0x08,
|
||||
MountCardSPI = 0x0F,
|
||||
UseSDIF3 = 0x10,
|
||||
CreateSeed = 0x20,
|
||||
UseCardSPI = 0x40,
|
||||
SDApplication = 0x80,
|
||||
MoundSdmcWriteAccess = 0xF0,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ARM11LSCFlag0 : byte
|
||||
{
|
||||
IdealProcessor = 0x01 | 0x02,
|
||||
AffinityMask = 0x04 | 0x08,
|
||||
|
||||
/// <summary>
|
||||
/// Value Description
|
||||
/// 0 Prod (64MB of usable application memory)
|
||||
/// 1 Undefined (unusable)
|
||||
/// 2 Dev1 (96MB of usable application memory)
|
||||
/// 3 Dev2 (80MB of usable application memory)
|
||||
/// 4 Dev3 (72MB of usable application memory)
|
||||
/// 5 Dev4 (32MB of usable application memory)
|
||||
/// 6-7 Undefined Same as Prod?
|
||||
/// </summary>
|
||||
Old3DSSystemMode = 0x0F | 0x10 | 0x20 | 0x40,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ARM11LSCFlag1 : byte
|
||||
{
|
||||
EnableL2Cache = 0x01,
|
||||
Cpuspeed_804MHz = 0x02,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ARM11LSCFlag2 : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Value Description
|
||||
/// 0 Legacy (use Old3DS system mode)
|
||||
/// 1 Prod (124MB of usable application memory)
|
||||
/// 2 Dev1 (178MB of usable application memory)
|
||||
/// 3 Dev2 (124MB of usable application memory)
|
||||
/// 4-7 Undefined Same as Prod?
|
||||
/// </summary>
|
||||
New3DSSystemMode = 0x01 | 0x02 | 0x04 | 0x08,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum BitMasks : byte
|
||||
{
|
||||
FixedCryptoKey = 0x01,
|
||||
NoMountRomFs = 0x02,
|
||||
NoCrypto = 0x04,
|
||||
NewKeyYGenerator = 0x20,
|
||||
}
|
||||
|
||||
public enum ContentPlatform : byte
|
||||
{
|
||||
CTR = 0x01,
|
||||
Snake = 0x02, // New3DS
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ContentType : byte
|
||||
{
|
||||
Data = 0x01,
|
||||
Executable = 0x02,
|
||||
SystemUpdate = 0x04,
|
||||
Manual = 0x08,
|
||||
Child = 0x04 | 0x08,
|
||||
Trial = 0x10,
|
||||
}
|
||||
|
||||
public enum CryptoMethod : byte
|
||||
{
|
||||
Original = 0x00,
|
||||
Seven = 0x01,
|
||||
NineThree = 0x0A,
|
||||
NineSix = 0x0B,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum FilesystemAccessInfo : ulong
|
||||
{
|
||||
CategorySystemApplication = 0x1,
|
||||
CategoryHardwareCheck = 0x2,
|
||||
CategoryFilesystemTool = 0x4,
|
||||
Debug = 0x8,
|
||||
TWLCardBackup = 0x10,
|
||||
TWLNANDData = 0x20,
|
||||
BOSS = 0x40,
|
||||
sdmcRoot = 0x80,
|
||||
Core = 0x100,
|
||||
nandRootroReadOnly = 0x200,
|
||||
nandRootrw = 0x400,
|
||||
nandrootroWriteAccess = 0x800,
|
||||
CategorySystemSettings = 0x1000,
|
||||
Cardboard = 0x2000,
|
||||
ExportImportIVS = 0x4000,
|
||||
sdmcRootWriteOnly = 0x8000,
|
||||
SwitchCleanup = 0x10000, // Introduced in 3.0.0?
|
||||
SavedataMove = 0x20000, // Introduced in 5.0.0
|
||||
Shop = 0x40000, // Introduced in 5.0.0
|
||||
Shell = 0x80000, // Introduced in 5.0.0
|
||||
CategoryHomeMenu = 0x100000, // Introduced in 6.0.0
|
||||
SeedDB = 0x200000, // Introduced in 9.6.0-X FIRM. Home Menu has this bit set starting with 9.6.0-X.
|
||||
}
|
||||
|
||||
public enum FilesystemType : ulong
|
||||
{
|
||||
None = 0,
|
||||
Normal = 1,
|
||||
FIRM = 3,
|
||||
AGB_FIRMSave = 4,
|
||||
}
|
||||
|
||||
public enum MediaCardDeviceType : byte
|
||||
{
|
||||
NORFlash = 0x01,
|
||||
None = 0x02,
|
||||
BT = 0x03,
|
||||
}
|
||||
|
||||
public enum MediaPlatformIndex : byte
|
||||
{
|
||||
CTR = 0x01,
|
||||
}
|
||||
|
||||
public enum MediaTypeIndex : byte
|
||||
{
|
||||
InnerDevice = 0x00,
|
||||
Card1 = 0x01,
|
||||
Card2 = 0x02,
|
||||
ExtendedDevice = 0x03,
|
||||
}
|
||||
|
||||
public enum NCCHFlags
|
||||
{
|
||||
CryptoMethod = 0x03,
|
||||
ContentPlatform = 0x04,
|
||||
ContentTypeBitMask = 0x05,
|
||||
ContentUnitSize = 0x06,
|
||||
BitMasks = 0x07,
|
||||
}
|
||||
|
||||
public enum NCSDFlags
|
||||
{
|
||||
BackupWriteWaitTime = 0x00,
|
||||
MediaCardDevice3X = 0x03,
|
||||
MediaPlatformIndex = 0x04,
|
||||
MediaTypeIndex = 0x05,
|
||||
MediaUnitSize = 0x06,
|
||||
MediaCardDevice2X = 0x07,
|
||||
}
|
||||
|
||||
public enum ResourceLimitCategory
|
||||
{
|
||||
APPLICATION = 0,
|
||||
SYS_APPLET = 1,
|
||||
LIB_APPLET = 2,
|
||||
OTHER = 3,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum StorageInfoOtherAttributes : byte
|
||||
{
|
||||
NotUseROMFS = 0x01,
|
||||
UseExtendedSavedataAccess = 0x02,
|
||||
}
|
||||
}
|
||||
65
NDecrypt/N3DS/Headers/ARM11KernelCapabilities.cs
Normal file
65
NDecrypt/N3DS/Headers/ARM11KernelCapabilities.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class ARM11KernelCapabilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Descriptors
|
||||
/// -------------------
|
||||
/// Pattern of bits 20-31 Type Fields
|
||||
/// 0b1110xxxxxxxx Interrupt info
|
||||
/// 0b11110xxxxxxx System call mask Bits 24-26: System call mask table index; Bits 0-23: mask
|
||||
/// 0b1111110xxxxx Kernel release version Bits 8-15: Major version; Bits 0-7: Minor version
|
||||
/// 0b11111110xxxx Handle table size Bits 0-18: size
|
||||
/// 0b111111110xxx Kernel flags
|
||||
/// 0b11111111100x Map address range Describes a memory mapping like the 0b111111111110 descriptor, but an entire range rather than a single page is mapped.Another 0b11111111100x descriptor must follow this one to denote the(exclusive) end of the address range to map.
|
||||
/// 0b111111111110 Map memory page Bits 0-19: page index to map(virtual address >> 12; the physical address is determined per-page according to Memory layout); Bit 20: Map read-only(otherwise read-write)
|
||||
///
|
||||
/// ARM11 Kernel Flags
|
||||
/// -------------------
|
||||
/// Bit Description
|
||||
/// 0 Allow debug
|
||||
/// 1 Force debug
|
||||
/// 2 Allow non-alphanum
|
||||
/// 3 Shared page writing
|
||||
/// 4 Privilege priority
|
||||
/// 5 Allow main() args
|
||||
/// 6 Shared device memory
|
||||
/// 7 Runnable on sleep
|
||||
/// 8-11 Memory type(1: application, 2: system, 3: base)
|
||||
/// 12 Special memory
|
||||
/// 13 Process has access to CPU core 2 (New3DS only)
|
||||
/// </summary>
|
||||
public byte[][] Descriptors { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get ARM11 kernel capabilities, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>ARM11 kernel capabilities object, null on error</returns>
|
||||
public static ARM11KernelCapabilities Read(BinaryReader reader)
|
||||
{
|
||||
ARM11KernelCapabilities kc = new ARM11KernelCapabilities();
|
||||
|
||||
try
|
||||
{
|
||||
kc.Descriptors = new byte[28][];
|
||||
for (int i = 0; i < 28; i++)
|
||||
kc.Descriptors[i] = reader.ReadBytes(4);
|
||||
|
||||
kc.Reserved = reader.ReadBytes(0x10);
|
||||
return kc;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
NDecrypt/N3DS/Headers/ARM11LocalSystemCapabilities.cs
Normal file
109
NDecrypt/N3DS/Headers/ARM11LocalSystemCapabilities.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class ARM11LocalSystemCapabilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Program ID
|
||||
/// </summary>
|
||||
public byte[] ProgramID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Core version (The Title ID low of the required FIRM)
|
||||
/// </summary>
|
||||
public uint CoreVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag1 (implemented starting from 8.0.0-18).
|
||||
/// </summary>
|
||||
public ARM11LSCFlag1 Flag1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag2 (implemented starting from 8.0.0-18).
|
||||
/// </summary>
|
||||
public ARM11LSCFlag2 Flag2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag0
|
||||
/// </summary>
|
||||
public ARM11LSCFlag0 Flag0 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Priority
|
||||
/// </summary>
|
||||
public byte Priority { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resource limit descriptors. The first byte here controls the maximum allowed CpuTime.
|
||||
/// </summary>
|
||||
public byte[][] ResourceLimitDescriptors { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Storage info
|
||||
/// </summary>
|
||||
public StorageInfo StorageInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Service access control
|
||||
/// </summary>
|
||||
public byte[][] ServiceAccessControl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Extended service access control, support for this was implemented with 9.3.0-X.
|
||||
/// </summary>
|
||||
public byte[][] ExtendedServiceAccessControl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resource limit category. (0 = APPLICATION, 1 = SYS_APPLET, 2 = LIB_APPLET, 3 = OTHER (sysmodules running under the BASE memregion))
|
||||
/// </summary>
|
||||
public ResourceLimitCategory ResourceLimitCategory { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get ARM11 local system capabilities, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>ARM11 local system capabilities object, null on error</returns>
|
||||
public static ARM11LocalSystemCapabilities Read(BinaryReader reader)
|
||||
{
|
||||
ARM11LocalSystemCapabilities lsc = new ARM11LocalSystemCapabilities();
|
||||
|
||||
try
|
||||
{
|
||||
lsc.ProgramID = reader.ReadBytes(8);
|
||||
lsc.CoreVersion = reader.ReadUInt32();
|
||||
lsc.Flag1 = (ARM11LSCFlag1)reader.ReadByte();
|
||||
lsc.Flag2 = (ARM11LSCFlag2)reader.ReadByte();
|
||||
lsc.Flag0 = (ARM11LSCFlag0)reader.ReadByte();
|
||||
lsc.Priority = reader.ReadByte();
|
||||
|
||||
lsc.ResourceLimitDescriptors = new byte[16][];
|
||||
for (int i = 0; i < 16; i++)
|
||||
lsc.ResourceLimitDescriptors[i] = reader.ReadBytes(2);
|
||||
|
||||
lsc.StorageInfo = StorageInfo.Read(reader);
|
||||
|
||||
lsc.ServiceAccessControl = new byte[32][];
|
||||
for (int i = 0; i < 32; i++)
|
||||
lsc.ServiceAccessControl[i] = reader.ReadBytes(8);
|
||||
|
||||
lsc.ExtendedServiceAccessControl = new byte[2][];
|
||||
for (int i = 0; i < 2; i++)
|
||||
lsc.ExtendedServiceAccessControl[i] = reader.ReadBytes(8);
|
||||
|
||||
lsc.Reserved = reader.ReadBytes(0xF);
|
||||
lsc.ResourceLimitCategory = (ResourceLimitCategory)reader.ReadByte();
|
||||
return lsc;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
NDecrypt/N3DS/Headers/ARM9AccessControl.cs
Normal file
40
NDecrypt/N3DS/Headers/ARM9AccessControl.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class ARM9AccessControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Descriptors
|
||||
/// </summary>
|
||||
public ARM9AccessControlDescriptors[] Descriptors { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 Descriptor Version. Originally this value had to be ≥ 2. Starting with 9.3.0-X this value has to be either value 2 or value 3.
|
||||
/// </summary>
|
||||
public byte DescriptorVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get ARM9 access control, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>ARM9 access control object, null on error</returns>
|
||||
public static ARM9AccessControl Read(BinaryReader reader)
|
||||
{
|
||||
ARM9AccessControl ac = new ARM9AccessControl();
|
||||
|
||||
try
|
||||
{
|
||||
ac.Descriptors = new ARM9AccessControlDescriptors[15];
|
||||
for (int i = 0; i < 15; i++)
|
||||
ac.Descriptors[i] = (ARM9AccessControlDescriptors)reader.ReadByte();
|
||||
ac.DescriptorVersion = reader.ReadByte();
|
||||
return ac;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
NDecrypt/N3DS/Headers/AccessControlInfo.cs
Normal file
44
NDecrypt/N3DS/Headers/AccessControlInfo.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class AccessControlInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// ARM11 local system capabilities
|
||||
/// </summary>
|
||||
public ARM11LocalSystemCapabilities ARM11LocalSystemCapabilities { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM11 kernel capabilities
|
||||
/// </summary>
|
||||
public ARM11KernelCapabilities ARM11KernelCapabilities { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 access control
|
||||
/// </summary>
|
||||
public ARM9AccessControl ARM9AccessControl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get access control info, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Access control info object, null on error</returns>
|
||||
public static AccessControlInfo Read(BinaryReader reader)
|
||||
{
|
||||
AccessControlInfo aci = new AccessControlInfo();
|
||||
|
||||
try
|
||||
{
|
||||
aci.ARM11LocalSystemCapabilities = ARM11LocalSystemCapabilities.Read(reader);
|
||||
aci.ARM11KernelCapabilities = ARM11KernelCapabilities.Read(reader);
|
||||
aci.ARM9AccessControl = ARM9AccessControl.Read(reader);
|
||||
return aci;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
NDecrypt/N3DS/Headers/CXIExtendedHeader.cs
Normal file
56
NDecrypt/N3DS/Headers/CXIExtendedHeader.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class CXIExtendedHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// SCI
|
||||
/// </summary>
|
||||
public SystemControlInfo SCI { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ACI
|
||||
/// </summary>
|
||||
public AccessControlInfo ACI { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// AccessDesc signature (RSA-2048-SHA256)
|
||||
/// </summary>
|
||||
public byte[] AccessDescSignature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// NCCH HDR RSA-2048 public key
|
||||
/// </summary>
|
||||
public byte[] NCCHHDRPublicKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ACI (for limitation of first ACI)
|
||||
/// </summary>
|
||||
public AccessControlInfo ACIForLimitations { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get a CXI extended header, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>CXI extended header object, null on error</returns>
|
||||
public static CXIExtendedHeader Read(BinaryReader reader)
|
||||
{
|
||||
CXIExtendedHeader header = new CXIExtendedHeader();
|
||||
|
||||
try
|
||||
{
|
||||
header.SCI = SystemControlInfo.Read(reader);
|
||||
header.ACI = AccessControlInfo.Read(reader);
|
||||
header.AccessDescSignature = reader.ReadBytes(0x100);
|
||||
header.NCCHHDRPublicKey = reader.ReadBytes(0x100);
|
||||
header.ACIForLimitations = AccessControlInfo.Read(reader);
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
NDecrypt/N3DS/Headers/CodeSetInfo.cs
Normal file
44
NDecrypt/N3DS/Headers/CodeSetInfo.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class CodeSetInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Address
|
||||
/// </summary>
|
||||
public byte[] Address { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Physical region size (in page-multiples)
|
||||
/// </summary>
|
||||
public uint PhysicalRegionSizeInPages { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size (in bytes)
|
||||
/// </summary>
|
||||
public uint SizeInBytes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get code set info, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Code set info object, null on error</returns>
|
||||
public static CodeSetInfo Read(BinaryReader reader)
|
||||
{
|
||||
CodeSetInfo csi = new CodeSetInfo();
|
||||
|
||||
try
|
||||
{
|
||||
csi.Address = reader.ReadBytes(4);
|
||||
csi.PhysicalRegionSizeInPages = reader.ReadUInt32();
|
||||
csi.SizeInBytes = reader.ReadUInt32();
|
||||
return csi;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
NDecrypt/N3DS/Headers/ExeFSFileHeader.cs
Normal file
56
NDecrypt/N3DS/Headers/ExeFSFileHeader.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class ExeFSFileHeader
|
||||
{
|
||||
// .code\0\0\0
|
||||
private readonly byte[] codeSegmentBytes = new byte[] { 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x00, 0x00, 0x00 };
|
||||
|
||||
/// <summary>
|
||||
/// File name
|
||||
/// </summary>
|
||||
public byte[] FileName { get; private set; }
|
||||
public string ReadableFileName { get { return Encoding.ASCII.GetString(FileName); } }
|
||||
public bool IsCodeBinary { get { return Enumerable.SequenceEqual(FileName, codeSegmentBytes); } }
|
||||
|
||||
/// <summary>
|
||||
/// File offset
|
||||
/// </summary>
|
||||
public uint FileOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File size
|
||||
/// </summary>
|
||||
public uint FileSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SHA256 hash calculated over the entire file contents
|
||||
/// </summary>
|
||||
public byte[] FileHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get an ExeFS file header, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>ExeFS file header object, null on error</returns>
|
||||
public static ExeFSFileHeader Read(BinaryReader reader)
|
||||
{
|
||||
ExeFSFileHeader header = new ExeFSFileHeader();
|
||||
|
||||
try
|
||||
{
|
||||
header.FileName = reader.ReadBytes(8);
|
||||
header.FileOffset = reader.ReadUInt32();
|
||||
header.FileSize = reader.ReadUInt32();
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
NDecrypt/N3DS/Headers/ExeFSHeader.cs
Normal file
45
NDecrypt/N3DS/Headers/ExeFSHeader.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class ExeFSHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// File headers (10 headers maximum, 16 bytes each)
|
||||
/// </summary>
|
||||
public ExeFSFileHeader[] FileHeaders { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get an ExeFS header, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>ExeFS header object, null on error</returns>
|
||||
public static ExeFSHeader Read(BinaryReader reader)
|
||||
{
|
||||
ExeFSHeader header = new ExeFSHeader();
|
||||
|
||||
try
|
||||
{
|
||||
header.FileHeaders = new ExeFSFileHeader[10];
|
||||
for (int i = 0; i < 10; i++)
|
||||
header.FileHeaders[i] = ExeFSFileHeader.Read(reader);
|
||||
|
||||
header.Reserved = reader.ReadBytes(0x20);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
header.FileHeaders[9 - i].FileHash = reader.ReadBytes(0x20);
|
||||
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
248
NDecrypt/N3DS/Headers/NCCHHeader.cs
Normal file
248
NDecrypt/N3DS/Headers/NCCHHeader.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class NCCHHeader
|
||||
{
|
||||
private const string NCCHMagicNumber = "NCCH";
|
||||
|
||||
/// <summary>
|
||||
/// Partition number for the current partition
|
||||
/// </summary>
|
||||
public int PartitionNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for the current partition
|
||||
/// </summary>
|
||||
public PartitionTableEntry Entry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// RSA-2048 signature of the NCCH header, using SHA-256.
|
||||
/// </summary>
|
||||
public byte[] RSA2048Signature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content size, in media units (1 media unit = 0x200 bytes)
|
||||
/// </summary>
|
||||
public uint ContentSizeInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Partition ID
|
||||
/// </summary>
|
||||
public byte[] PartitionId { get; private set; }
|
||||
public byte[] PlainIV { get { return PartitionId.Concat(Constants.PlainCounter).ToArray(); } }
|
||||
public byte[] ExeFSIV { get { return PartitionId.Concat(Constants.ExefsCounter).ToArray(); } }
|
||||
public byte[] RomFSIV { get { return PartitionId.Concat(Constants.RomfsCounter).ToArray(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Boot rom key
|
||||
/// </summary>
|
||||
public BigInteger KeyX { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// NCCH boot rom key
|
||||
/// </summary>
|
||||
public BigInteger KeyX2C { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Kernel9/Process9 key
|
||||
/// </summary>
|
||||
public BigInteger KeyY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normal AES key
|
||||
/// </summary>
|
||||
public BigInteger NormalKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// NCCH AES key
|
||||
/// </summary>
|
||||
public BigInteger NormalKey2C { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maker code
|
||||
/// </summary>
|
||||
public byte[] MakerCode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version
|
||||
/// </summary>
|
||||
public byte[] Version { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// When ncchflag[7] = 0x20 starting with FIRM 9.6.0-X, this is compared with the first output u32 from a
|
||||
/// SHA256 hash. The data used for that hash is 0x18-bytes: [0x10-long title-unique content lock seed]
|
||||
/// [programID from NCCH + 0x118]. This hash is only used for verification of the content lock seed, and
|
||||
/// is not the actual keyY.
|
||||
/// </summary>
|
||||
public byte[] VerificationHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Program ID
|
||||
/// </summary>
|
||||
public byte[] ProgramId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Logo Region SHA-256 hash. (For applications built with SDK 5+) (Supported from firmware: 5.0.0-11)
|
||||
/// </summary>
|
||||
public byte[] LogoRegionHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Product code
|
||||
/// </summary>
|
||||
public byte[] ProductCode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Extended header SHA-256 hash (SHA256 of 2x Alignment Size, beginning at 0x0 of ExHeader)
|
||||
/// </summary>
|
||||
public byte[] ExtendedHeaderHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Extended header size, in bytes
|
||||
/// </summary>
|
||||
public uint ExtendedHeaderSizeInBytes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flags
|
||||
/// </summary>
|
||||
public NCCHHeaderFlags Flags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Plain region offset, in media units
|
||||
/// </summary>
|
||||
public uint PlainRegionOffsetInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Plain region size, in media units
|
||||
/// </summary>
|
||||
public uint PlainRegionSizeInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Logo Region offset, in media units (For applications built with SDK 5+) (Supported from firmware: 5.0.0-11)
|
||||
/// </summary>
|
||||
public uint LogoRegionOffsetInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Logo Region size, in media units (For applications built with SDK 5+) (Supported from firmware: 5.0.0-11)
|
||||
/// </summary>
|
||||
public uint LogoRegionSizeInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ExeFS offset, in media units
|
||||
/// </summary>
|
||||
public uint ExeFSOffsetInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ExeFS size, in media units
|
||||
/// </summary>
|
||||
public uint ExeFSSizeInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ExeFS hash region size, in media units
|
||||
/// </summary>
|
||||
public uint ExeFSHashRegionSizeInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// RomFS offset, in media units
|
||||
/// </summary>
|
||||
public uint RomFSOffsetInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// RomFS size, in media units
|
||||
/// </summary>
|
||||
public uint RomFSSizeInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// RomFS hash region size, in media units
|
||||
/// </summary>
|
||||
public uint RomFSHashRegionSizeInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved4 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ExeFS superblock SHA-256 hash - (SHA-256 hash, starting at 0x0 of the ExeFS over the number of
|
||||
/// media units specified in the ExeFS hash region size)
|
||||
/// </summary>
|
||||
public byte[] ExeFSSuperblockHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// RomFS superblock SHA-256 hash - (SHA-256 hash, starting at 0x0 of the RomFS over the number
|
||||
/// of media units specified in the RomFS hash region size)
|
||||
/// </summary>
|
||||
public byte[] RomFSSuperblockHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get an NCCH header, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="readSignature">True if the RSA signature is read, false otherwise</param>
|
||||
/// <returns>NCCH header object, null on error</returns>
|
||||
public static NCCHHeader Read(BinaryReader reader, bool readSignature)
|
||||
{
|
||||
NCCHHeader header = new NCCHHeader();
|
||||
|
||||
try
|
||||
{
|
||||
if (readSignature)
|
||||
header.RSA2048Signature = reader.ReadBytes(0x100);
|
||||
|
||||
if (new string(reader.ReadChars(4)) != NCCHMagicNumber)
|
||||
return null;
|
||||
|
||||
header.ContentSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.PartitionId = reader.ReadBytes(8).Reverse().ToArray();
|
||||
header.MakerCode = reader.ReadBytes(2);
|
||||
header.Version = reader.ReadBytes(2);
|
||||
header.VerificationHash = reader.ReadBytes(4);
|
||||
header.ProgramId = reader.ReadBytes(8);
|
||||
header.Reserved1 = reader.ReadBytes(0x10);
|
||||
header.LogoRegionHash = reader.ReadBytes(0x20);
|
||||
header.ProductCode = reader.ReadBytes(0x10);
|
||||
header.ExtendedHeaderHash = reader.ReadBytes(0x20);
|
||||
header.ExtendedHeaderSizeInBytes = reader.ReadUInt32();
|
||||
header.Reserved2 = reader.ReadBytes(4);
|
||||
header.Flags = NCCHHeaderFlags.Read(reader);
|
||||
header.PlainRegionOffsetInMediaUnits = reader.ReadUInt32();
|
||||
header.PlainRegionSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.LogoRegionOffsetInMediaUnits = reader.ReadUInt32();
|
||||
header.LogoRegionSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.ExeFSOffsetInMediaUnits = reader.ReadUInt32();
|
||||
header.ExeFSSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.ExeFSHashRegionSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.Reserved3 = reader.ReadBytes(4);
|
||||
header.RomFSOffsetInMediaUnits = reader.ReadUInt32();
|
||||
header.RomFSSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.RomFSHashRegionSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.Reserved4 = reader.ReadBytes(4);
|
||||
header.ExeFSSuperblockHash = reader.ReadBytes(0x20);
|
||||
header.RomFSSuperblockHash = reader.ReadBytes(0x20);
|
||||
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
103
NDecrypt/N3DS/Headers/NCCHHeaderFlags.cs
Normal file
103
NDecrypt/N3DS/Headers/NCCHHeaderFlags.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class NCCHHeaderFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte Reserved0 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Crypto Method: When this is non-zero, a NCCH crypto method using two keyslots is used.
|
||||
/// </summary>
|
||||
public CryptoMethod CryptoMethod { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content Platform: 1 = CTR, 2 = snake (New 3DS).
|
||||
/// </summary>
|
||||
public ContentPlatform ContentPlatform { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content Type Bit-masks: Data = 0x1, Executable = 0x2, SystemUpdate = 0x4, Manual = 0x8,
|
||||
/// Child = (0x4|0x8), Trial = 0x10. When 'Data' is set, but not 'Executable', NCCH is a CFA.
|
||||
/// Otherwise when 'Executable' is set, NCCH is a CXI.
|
||||
/// </summary>
|
||||
public ContentType MediaPlatformIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content Unit Size i.e. u32 ContentUnitSize = 0x200*2^flags[6];
|
||||
/// </summary>
|
||||
public byte ContentUnitSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Bit-masks: FixedCryptoKey = 0x1, NoMountRomFs = 0x2, NoCrypto = 0x4, using a new keyY
|
||||
/// generator = 0x20(starting with FIRM 9.6.0-X).
|
||||
/// </summary>
|
||||
public BitMasks BitMasks { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get if the NoCrypto bit is set
|
||||
/// </summary>
|
||||
public bool PossblyDecrypted { get { return BitMasks.HasFlag(BitMasks.NoCrypto); } }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get an NCCH header flags, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>NCCH header flags object, null on error</returns>
|
||||
public static NCCHHeaderFlags Read(BinaryReader reader)
|
||||
{
|
||||
NCCHHeaderFlags flags = new NCCHHeaderFlags();
|
||||
|
||||
try
|
||||
{
|
||||
flags.Reserved0 = reader.ReadByte();
|
||||
flags.Reserved1 = reader.ReadByte();
|
||||
flags.Reserved2 = reader.ReadByte();
|
||||
flags.CryptoMethod = (CryptoMethod)reader.ReadByte();
|
||||
flags.ContentPlatform = (ContentPlatform)reader.ReadByte();
|
||||
flags.MediaPlatformIndex = (ContentType)reader.ReadByte();
|
||||
flags.ContentUnitSize = reader.ReadByte();
|
||||
flags.BitMasks = (BitMasks)reader.ReadByte();
|
||||
return flags;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write NCCH header flags to stream, if possible
|
||||
/// </summary>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
try
|
||||
{
|
||||
writer.Write(this.Reserved0);
|
||||
writer.Write(this.Reserved1);
|
||||
writer.Write(this.Reserved2);
|
||||
writer.Write((byte)this.CryptoMethod);
|
||||
writer.Write((byte)this.ContentPlatform);
|
||||
writer.Write((byte)this.MediaPlatformIndex);
|
||||
writer.Write((byte)this.ContentUnitSize);
|
||||
writer.Write((byte)this.BitMasks);
|
||||
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
325
NDecrypt/N3DS/Headers/NCSDHeader.cs
Normal file
325
NDecrypt/N3DS/Headers/NCSDHeader.cs
Normal file
@@ -0,0 +1,325 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class NCSDHeader
|
||||
{
|
||||
private const string NCSDMagicNumber = "NCSD";
|
||||
|
||||
#region Common to all NCSD files
|
||||
|
||||
/// <summary>
|
||||
/// RSA-2048 SHA-256 signature of the NCSD header
|
||||
/// </summary>
|
||||
public byte[] RSA2048Signature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the NCSD image, in media units (1 media unit = 0x200 bytes)
|
||||
/// </summary>
|
||||
public uint ImageSizeInMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Media ID
|
||||
/// </summary>
|
||||
public byte[] MediaId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Partitions FS type (0=None, 1=Normal, 3=FIRM, 4=AGB_FIRM save)
|
||||
/// </summary>
|
||||
public FilesystemType PartitionsFSType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Partitions crypt type (each byte corresponds to a partition in the partition table)
|
||||
/// </summary>
|
||||
public byte[] PartitionsCryptType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset & Length partition table, in media units
|
||||
/// </summary>
|
||||
public PartitionTableEntry[] PartitionsTable { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for Executable Content (CXI)
|
||||
/// </summary>
|
||||
public PartitionTableEntry ExecutableContent { get { return PartitionsTable[0]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for E-Manual (CFA)
|
||||
/// </summary>
|
||||
public PartitionTableEntry EManual { get { return PartitionsTable[1]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for Download Play Child container (CFA)
|
||||
/// </summary>
|
||||
public PartitionTableEntry DownloadPlayChildContainer { get { return PartitionsTable[2]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for New3DS Update Data (CFA)
|
||||
/// </summary>
|
||||
public PartitionTableEntry New3DSUpdateData { get { return PartitionsTable[6]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Partition table entry for Update Data (CFA)
|
||||
/// </summary>
|
||||
public PartitionTableEntry UpdateData { get { return PartitionsTable[7]; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region CTR Cart Image (CCI) Specific
|
||||
|
||||
/// <summary>
|
||||
/// Exheader SHA-256 hash
|
||||
/// </summary>
|
||||
public byte[] ExheaderHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional header size
|
||||
/// </summary>
|
||||
public uint AdditionalHeaderSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sector zero offset
|
||||
/// </summary>
|
||||
public uint SectorZeroOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Partition Flags
|
||||
/// </summary>
|
||||
public byte[] PartitionFlags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Backup Write Wait Time (The time to wait to write save to backup after the card is recognized (0-255
|
||||
/// seconds)).NATIVE_FIRM loads this flag from the gamecard NCSD header starting with 6.0.0-11.
|
||||
/// </summary>
|
||||
public byte BackupWriteWaitTime { get { return PartitionFlags[(int)NCSDFlags.BackupWriteWaitTime]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (SDK 3.X+)
|
||||
/// </summary>
|
||||
public MediaCardDeviceType MediaCardDevice3X { get { return (MediaCardDeviceType)PartitionFlags[(int)NCSDFlags.MediaCardDevice3X]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Media Platform Index (1 = CTR)
|
||||
/// </summary>
|
||||
public MediaPlatformIndex MediaPlatformIndex { get { return (MediaPlatformIndex)PartitionFlags[(int)NCSDFlags.MediaPlatformIndex]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Media Type Index (0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device)
|
||||
/// </summary>
|
||||
public MediaTypeIndex MediaTypeIndex { get { return (MediaTypeIndex)PartitionFlags[(int)NCSDFlags.MediaTypeIndex]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Media Unit Size i.e. u32 MediaUnitSize = 0x200*2^flags[6];
|
||||
/// </summary>
|
||||
public uint MediaUnitSize { get { return (uint)(0x200 * Math.Pow(2, PartitionFlags[(int)NCSDFlags.MediaUnitSize])); } }
|
||||
|
||||
/// <summary>
|
||||
/// Media Card Device (1 = NOR Flash, 2 = None, 3 = BT) (Only SDK 2.X)
|
||||
/// </summary>
|
||||
public MediaCardDeviceType MediaCardDevice2X { get { return (MediaCardDeviceType)PartitionFlags[(int)NCSDFlags.MediaCardDevice2X]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Partition ID table
|
||||
/// </summary>
|
||||
public byte[][] PartitionIdTable { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved?
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Support for this was implemented with 9.6.0-X FIRM. Bit0=1 enables using bits 1-2, it's unknown
|
||||
/// what these two bits are actually used for(the value of these two bits get compared with some other
|
||||
/// value during NCSD verification/loading). This appears to enable a new, likely hardware-based,
|
||||
/// antipiracy check on cartridges.
|
||||
/// </summary>
|
||||
public byte FirmUpdateByte1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Support for this was implemented with 9.6.0-X FIRM, see below regarding save crypto.
|
||||
/// </summary>
|
||||
public byte FirmUpdateByte2 { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Raw NAND Format Specific
|
||||
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
public byte[] Unknown { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Encrypted MBR partition-table, for the TWL partitions(key-data used for this keyslot is console-unique).
|
||||
/// </summary>
|
||||
public byte[] EncryptedMBR { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Card Info Header
|
||||
|
||||
/// <summary>
|
||||
/// CARD2: Writable Address In Media Units (For 'On-Chip' Savedata). CARD1: Always 0xFFFFFFFF.
|
||||
/// </summary>
|
||||
public byte[] CARD2WritableAddressMediaUnits { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Card Info Bitmask
|
||||
/// </summary>
|
||||
public byte[] CardInfoBytemask { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved1
|
||||
/// </summary>
|
||||
public byte[] Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Title version
|
||||
/// </summary>
|
||||
public ushort TitleVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Card revision
|
||||
/// </summary>
|
||||
public ushort CardRevision { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved2
|
||||
/// </summary>
|
||||
public byte[] Reserved4 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Card seed keyY (first u64 is Media ID (same as first NCCH partitionId))
|
||||
/// </summary>
|
||||
public byte[] CardSeedKeyY { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Encrypted card seed (AES-CCM, keyslot 0x3B for retail cards, see CTRCARD_SECSEED) /// </summary>
|
||||
public byte[] EncryptedCardSeed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Card seed AES-MAC
|
||||
/// </summary>
|
||||
public byte[] CardSeedAESMAC { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Card seed nonce
|
||||
/// </summary>
|
||||
public byte[] CardSeedNonce { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved3
|
||||
/// </summary>
|
||||
public byte[] Reserved5 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Copy of first NCCH header (excluding RSA signature)
|
||||
/// </summary>
|
||||
public NCCHHeader BackupHeader { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Development Card Info Header Extension
|
||||
|
||||
/// <summary>
|
||||
/// CardDeviceReserved1
|
||||
/// </summary>
|
||||
public byte[] CardDeviceReserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// TitleKey
|
||||
/// </summary>
|
||||
public byte[] TitleKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// CardDeviceReserved2
|
||||
/// </summary>
|
||||
public byte[] CardDeviceReserved2 { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get an NCSD header, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="development">True if development cart, false otherwise</param>
|
||||
/// <returns>NCSD header object, null on error</returns>
|
||||
public static NCSDHeader Read(BinaryReader reader, bool development)
|
||||
{
|
||||
NCSDHeader header = new NCSDHeader();
|
||||
|
||||
try
|
||||
{
|
||||
header.RSA2048Signature = reader.ReadBytes(0x100);
|
||||
|
||||
if (new string(reader.ReadChars(4)) != NCSDMagicNumber)
|
||||
return null;
|
||||
|
||||
header.ImageSizeInMediaUnits = reader.ReadUInt32();
|
||||
header.MediaId = reader.ReadBytes(8);
|
||||
header.PartitionsFSType = (FilesystemType)reader.ReadUInt64();
|
||||
header.PartitionsCryptType = reader.ReadBytes(8);
|
||||
|
||||
header.PartitionsTable = new PartitionTableEntry[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
header.PartitionsTable[i] = PartitionTableEntry.Read(reader);
|
||||
|
||||
if (header.PartitionsFSType == FilesystemType.Normal
|
||||
|| header.PartitionsFSType == FilesystemType.None)
|
||||
{
|
||||
header.ExheaderHash = reader.ReadBytes(0x20);
|
||||
header.AdditionalHeaderSize = reader.ReadUInt32();
|
||||
header.SectorZeroOffset = reader.ReadUInt32();
|
||||
header.PartitionFlags = reader.ReadBytes(8);
|
||||
|
||||
header.PartitionIdTable = new byte[8][];
|
||||
for (int i = 0; i < 8; i++)
|
||||
header.PartitionIdTable[i] = reader.ReadBytes(8);
|
||||
|
||||
header.Reserved1 = reader.ReadBytes(0x20);
|
||||
header.Reserved2 = reader.ReadBytes(0xE);
|
||||
header.FirmUpdateByte1 = reader.ReadByte();
|
||||
header.FirmUpdateByte2 = reader.ReadByte();
|
||||
|
||||
header.CARD2WritableAddressMediaUnits = reader.ReadBytes(4);
|
||||
header.CardInfoBytemask = reader.ReadBytes(4);
|
||||
header.Reserved3 = reader.ReadBytes(0x108);
|
||||
header.TitleVersion = reader.ReadUInt16();
|
||||
header.CardRevision = reader.ReadUInt16();
|
||||
header.Reserved4 = reader.ReadBytes(0xCEC); // Incorrectly documented as 0xCEE
|
||||
header.CardSeedKeyY = reader.ReadBytes(0x10);
|
||||
header.EncryptedCardSeed = reader.ReadBytes(0x10);
|
||||
header.CardSeedAESMAC = reader.ReadBytes(0x10);
|
||||
header.CardSeedNonce = reader.ReadBytes(0xC);
|
||||
header.Reserved5 = reader.ReadBytes(0xC4);
|
||||
header.BackupHeader = NCCHHeader.Read(reader, false);
|
||||
|
||||
if (development)
|
||||
{
|
||||
header.CardDeviceReserved1 = reader.ReadBytes(0x200);
|
||||
header.TitleKey = reader.ReadBytes(0x10);
|
||||
header.CardDeviceReserved2 = reader.ReadBytes(0xF0);
|
||||
}
|
||||
}
|
||||
else if (header.PartitionsFSType == FilesystemType.FIRM)
|
||||
{
|
||||
header.Unknown = reader.ReadBytes(0x5E);
|
||||
header.EncryptedMBR = reader.ReadBytes(0x42);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,24 @@
|
||||
using System.IO;
|
||||
|
||||
namespace ThreeDS.Headers
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class PartitionTableEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Offset
|
||||
/// </summary>
|
||||
public uint Offset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length
|
||||
/// </summary>
|
||||
public uint Length { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get partition table entry, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Partition table entry object, null on error</returns>
|
||||
public static PartitionTableEntry Read(BinaryReader reader)
|
||||
{
|
||||
PartitionTableEntry entry = new PartitionTableEntry();
|
||||
@@ -23,6 +35,10 @@ namespace ThreeDS.Headers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for a valid partition
|
||||
/// </summary>
|
||||
/// <returns>True if the offset and length are not 0, false otherwise</returns>
|
||||
public bool IsValid()
|
||||
{
|
||||
return Offset != 0 && Length != 0;
|
||||
127
NDecrypt/N3DS/Headers/RomFSHeader.cs
Normal file
127
NDecrypt/N3DS/Headers/RomFSHeader.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
// https://www.3dbrew.org/wiki/RomFS
|
||||
public class RomFSHeader
|
||||
{
|
||||
private const string RomFSMagicNumber = "IVFC";
|
||||
private const uint RomFSSecondMagicNumber = 0x10000;
|
||||
|
||||
/// <summary>
|
||||
/// Master hash size
|
||||
/// </summary>
|
||||
public uint MasterHashSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 1 logical offset
|
||||
/// </summary>
|
||||
public ulong Level1LogicalOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 1 hashdata size
|
||||
/// </summary>
|
||||
public ulong Level1HashdataSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 1 block size, in log2
|
||||
/// </summary>
|
||||
public uint Level1BlockSizeLog2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 2 logical offset
|
||||
/// </summary>
|
||||
public ulong Level2LogicalOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 2 hashdata size
|
||||
/// </summary>
|
||||
public ulong Level2HashdataSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 2 block size, in log2
|
||||
/// </summary>
|
||||
public uint Level2BlockSizeLog2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 3 logical offset
|
||||
/// </summary>
|
||||
public ulong Level3LogicalOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 3 hashdata size
|
||||
/// </summary>
|
||||
public ulong Level3HashdataSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Level 3 block size, in log2
|
||||
/// </summary>
|
||||
public uint Level3BlockSizeLog2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved4 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional info size.
|
||||
/// </summary>
|
||||
public uint OptionalInfoSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get a RomFS header, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>RomFS header object, null on error</returns>
|
||||
public static RomFSHeader Read(BinaryReader reader)
|
||||
{
|
||||
RomFSHeader header = new RomFSHeader();
|
||||
|
||||
try
|
||||
{
|
||||
if (new string(reader.ReadChars(4)) != RomFSMagicNumber)
|
||||
return null;
|
||||
|
||||
if (reader.ReadUInt32() != RomFSSecondMagicNumber)
|
||||
return null;
|
||||
|
||||
header.MasterHashSize = reader.ReadUInt32();
|
||||
header.Level1LogicalOffset = reader.ReadUInt64();
|
||||
header.Level1HashdataSize = reader.ReadUInt64();
|
||||
header.Level1BlockSizeLog2 = reader.ReadUInt32();
|
||||
header.Reserved1 = reader.ReadBytes(4);
|
||||
header.Level2LogicalOffset = reader.ReadUInt64();
|
||||
header.Level2HashdataSize = reader.ReadUInt64();
|
||||
header.Level2BlockSizeLog2 = reader.ReadUInt32();
|
||||
header.Reserved2 = reader.ReadBytes(4);
|
||||
header.Level3LogicalOffset = reader.ReadUInt64();
|
||||
header.Level3HashdataSize = reader.ReadUInt64();
|
||||
header.Level3BlockSizeLog2 = reader.ReadUInt32();
|
||||
header.Reserved3 = reader.ReadBytes(4);
|
||||
header.Reserved4 = reader.ReadBytes(4);
|
||||
header.OptionalInfoSize = reader.ReadUInt32();
|
||||
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
NDecrypt/N3DS/Headers/StorageInfo.cs
Normal file
56
NDecrypt/N3DS/Headers/StorageInfo.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class StorageInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Extdata ID
|
||||
/// </summary>
|
||||
public byte[] ExtdataID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// System savedata IDs
|
||||
/// </summary>
|
||||
public byte[] SystemSavedataIDs { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Storage accessible unique IDs
|
||||
/// </summary>
|
||||
public byte[] StorageAccessibleUniqueIDs { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Filesystem access info
|
||||
/// </summary>
|
||||
public byte[] FilesystemAccessInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Other attributes
|
||||
/// </summary>
|
||||
public StorageInfoOtherAttributes OtherAttributes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get storage info, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>Storage info object, null on error</returns>
|
||||
public static StorageInfo Read(BinaryReader reader)
|
||||
{
|
||||
StorageInfo si = new StorageInfo();
|
||||
|
||||
try
|
||||
{
|
||||
si.ExtdataID = reader.ReadBytes(8);
|
||||
si.SystemSavedataIDs = reader.ReadBytes(8);
|
||||
si.StorageAccessibleUniqueIDs = reader.ReadBytes(8);
|
||||
si.FilesystemAccessInfo = reader.ReadBytes(7);
|
||||
si.OtherAttributes = (StorageInfoOtherAttributes)reader.ReadByte();
|
||||
return si;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
102
NDecrypt/N3DS/Headers/SystemControlInfo.cs
Normal file
102
NDecrypt/N3DS/Headers/SystemControlInfo.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class SystemControlInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Application title (default is "CtrApp")
|
||||
/// </summary>
|
||||
public char[] ApplicationTitle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag (bit 0: CompressExefsCode, bit 1: SDApplication)
|
||||
/// </summary>
|
||||
public byte Flag { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Remaster version
|
||||
/// </summary>
|
||||
public byte[] RemasterVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Text code set info
|
||||
/// </summary>
|
||||
public CodeSetInfo TextCodesetInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stack size
|
||||
/// </summary>
|
||||
public uint StackSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read-only code set info
|
||||
/// </summary>
|
||||
public CodeSetInfo ReadOnlyCodeSetInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data code set info
|
||||
/// </summary>
|
||||
public CodeSetInfo DataCodeSetInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// BSS size
|
||||
/// </summary>
|
||||
public uint BSSSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dependency module (program ID) list
|
||||
/// </summary>
|
||||
public byte[][] DependencyModuleList { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SystemInfo
|
||||
/// </summary>
|
||||
public SystemInfo SystemInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get system control info, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>System control info object, null on error</returns>
|
||||
public static SystemControlInfo Read(BinaryReader reader)
|
||||
{
|
||||
SystemControlInfo sci = new SystemControlInfo();
|
||||
|
||||
try
|
||||
{
|
||||
sci.ApplicationTitle = reader.ReadChars(8);
|
||||
sci.Reserved1 = reader.ReadBytes(5);
|
||||
sci.Flag = reader.ReadByte();
|
||||
sci.RemasterVersion = reader.ReadBytes(2);
|
||||
sci.TextCodesetInfo = CodeSetInfo.Read(reader);
|
||||
sci.StackSize = reader.ReadUInt32();
|
||||
sci.ReadOnlyCodeSetInfo = CodeSetInfo.Read(reader);
|
||||
sci.Reserved2 = reader.ReadBytes(4);
|
||||
sci.DataCodeSetInfo = CodeSetInfo.Read(reader);
|
||||
sci.BSSSize = reader.ReadUInt32();
|
||||
|
||||
sci.DependencyModuleList = new byte[48][];
|
||||
for (int i = 0; i < 48; i++)
|
||||
sci.DependencyModuleList[i] = reader.ReadBytes(8);
|
||||
|
||||
sci.SystemInfo = SystemInfo.Read(reader);
|
||||
return sci;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
NDecrypt/N3DS/Headers/SystemInfo.cs
Normal file
44
NDecrypt/N3DS/Headers/SystemInfo.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.N3DS.Headers
|
||||
{
|
||||
public class SystemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// SaveData Size
|
||||
/// </summary>
|
||||
public ulong SaveDataSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Jump ID
|
||||
/// </summary>
|
||||
public byte[] JumpID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get system info, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>System info object, null on error</returns>
|
||||
public static SystemInfo Read(BinaryReader reader)
|
||||
{
|
||||
SystemInfo si = new SystemInfo();
|
||||
|
||||
try
|
||||
{
|
||||
si.SaveDataSize = reader.ReadUInt64();
|
||||
si.JumpID = reader.ReadBytes(8);
|
||||
si.Reserved = reader.ReadBytes(0x30);
|
||||
return si;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
277
NDecrypt/NDS/Constants.cs
Normal file
277
NDecrypt/NDS/Constants.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
namespace NDecrypt.NDS
|
||||
{
|
||||
public class Constants
|
||||
{
|
||||
public static byte[] NDSEncryptionData = new byte[]
|
||||
{
|
||||
0x99,0xD5,0x20,0x5F,0x57,0x44,0xF5,0xB9,0x6E,0x19,0xA4,0xD9,0x9E,0x6A,0x5A,0x94,
|
||||
0xD8,0xAE,0xF1,0xEB,0x41,0x75,0xE2,0x3A,0x93,0x82,0xD0,0x32,0x33,0xEE,0x31,0xD5,
|
||||
0xCC,0x57,0x61,0x9A,0x37,0x06,0xA2,0x1B,0x79,0x39,0x72,0xF5,0x55,0xAE,0xF6,0xBE,
|
||||
0x5F,0x1B,0x69,0xFB,0xE5,0x9D,0xF1,0xE9,0xCE,0x2C,0xD9,0xA1,0x5E,0x32,0x05,0xE6,
|
||||
0xFE,0xD3,0xFE,0xCF,0xD4,0x62,0x04,0x0D,0x8B,0xF5,0xEC,0xB7,0x2B,0x60,0x79,0xBB,
|
||||
0x12,0x95,0x31,0x0D,0x6E,0x3F,0xDA,0x2B,0x88,0x84,0xF0,0xF1,0x3D,0x12,0x7E,0x25,
|
||||
0x45,0x22,0xF1,0xBB,0x24,0x06,0x1A,0x06,0x11,0xAD,0xDF,0x28,0x8B,0x64,0x81,0x34,
|
||||
0x2B,0xEB,0x33,0x29,0x99,0xAA,0xF2,0xBD,0x9C,0x14,0x95,0x9D,0x9F,0xF7,0xF5,0x8C,
|
||||
0x72,0x97,0xA1,0x29,0x9D,0xD1,0x5F,0xCF,0x66,0x4D,0x07,0x1A,0xDE,0xD3,0x4A,0x4B,
|
||||
0x85,0xC9,0xA7,0xA3,0x17,0x95,0x05,0x3A,0x3D,0x49,0x0A,0xBF,0x0A,0x89,0x8B,0xA2,
|
||||
0x4A,0x82,0x49,0xDD,0x27,0x90,0xF1,0x0B,0xE9,0xEB,0x1C,0x6A,0x83,0x76,0x45,0x05,
|
||||
0xBA,0x81,0x70,0x61,0x17,0x3F,0x4B,0xDE,0xAE,0xCF,0xAB,0x39,0x57,0xF2,0x3A,0x56,
|
||||
0x48,0x11,0xAD,0x8A,0x40,0xE1,0x45,0x3F,0xFA,0x9B,0x02,0x54,0xCA,0xA6,0x93,0xFB,
|
||||
0xEF,0x4D,0xFE,0x6F,0xA3,0xD8,0x87,0x9C,0x08,0xBA,0xD5,0x48,0x6A,0x8D,0x2D,0xFD,
|
||||
0x6E,0x15,0xF8,0x74,0xBD,0xBE,0x52,0x8B,0x18,0x22,0x8A,0x9E,0xFB,0x74,0x37,0x07,
|
||||
0x1B,0x36,0x6C,0x4A,0x19,0xBA,0x42,0x62,0xB9,0x79,0x91,0x10,0x7B,0x67,0x65,0x96,
|
||||
0xFE,0x02,0x23,0xE8,0xEE,0x99,0x8C,0x77,0x3E,0x5C,0x86,0x64,0x4D,0x6D,0x78,0x86,
|
||||
0xA5,0x4F,0x65,0xE2,0x1E,0xB2,0xDF,0x5A,0x0A,0xD0,0x7E,0x08,0x14,0xB0,0x71,0xAC,
|
||||
0xBD,0xDB,0x83,0x1C,0xB9,0xD7,0xA1,0x62,0xCD,0xC6,0x63,0x7C,0x52,0x69,0xC3,0xE6,
|
||||
0xBF,0x75,0xCE,0x12,0x44,0x5D,0x21,0x04,0xFA,0xFB,0xD3,0x3C,0x38,0x11,0x63,0xD4,
|
||||
0x95,0x85,0x41,0x49,0x46,0x09,0xF2,0x08,0x43,0x11,0xDC,0x1F,0x76,0xC0,0x15,0x6D,
|
||||
0x1F,0x3C,0x63,0x70,0xEA,0x87,0x80,0x6C,0xC3,0xBD,0x63,0x8B,0xC2,0x37,0x21,0x37,
|
||||
0xDC,0xEE,0x09,0x23,0x2E,0x37,0x6A,0x4D,0x73,0x90,0xF7,0x50,0x30,0xAC,0x1C,0x92,
|
||||
0x04,0x10,0x23,0x91,0x4F,0xD2,0x07,0xAA,0x68,0x3E,0x4F,0x9A,0xC9,0x64,0x60,0x6A,
|
||||
0xC8,0x14,0x21,0xF3,0xD6,0x22,0x41,0x12,0x44,0x24,0xCF,0xE6,0x8A,0x56,0xDD,0x0D,
|
||||
0x53,0x4D,0xE1,0x85,0x1E,0x8C,0x52,0x5A,0x9C,0x19,0x84,0xC2,0x03,0x57,0xF1,0x6F,
|
||||
0xE3,0x00,0xBE,0x58,0xF6,0x4C,0xED,0xD5,0x21,0x64,0x9C,0x1F,0xBE,0x55,0x03,0x3C,
|
||||
0x4A,0xDC,0xFF,0xAA,0xC9,0xDA,0xE0,0x5D,0x5E,0xBF,0xE6,0xDE,0xF5,0xD8,0xB1,0xF8,
|
||||
0xFF,0x36,0xB3,0xB9,0x62,0x67,0x95,0xDB,0x31,0x5F,0x37,0xED,0x4C,0x70,0x67,0x99,
|
||||
0x90,0xB5,0x18,0x31,0x6C,0x3D,0x99,0x99,0xE4,0x42,0xDA,0xD3,0x25,0x42,0x13,0xA0,
|
||||
0xAE,0xD7,0x70,0x6C,0xB1,0x55,0xCF,0xC7,0xD7,0x46,0xD5,0x43,0x61,0x17,0x3D,0x44,
|
||||
0x28,0xE9,0x33,0x85,0xD5,0xD0,0xA2,0x93,0xAA,0x25,0x12,0x1F,0xFB,0xC5,0x0B,0x46,
|
||||
0xF5,0x97,0x76,0x56,0x45,0xA6,0xBE,0x87,0xB1,0x94,0x6B,0xE8,0xB1,0xFE,0x33,0x99,
|
||||
0xAE,0x1F,0x3E,0x6C,0x39,0x71,0x1D,0x09,0x00,0x90,0x37,0xE4,0x10,0x3E,0x75,0x74,
|
||||
0xFF,0x8C,0x83,0x3B,0xB0,0xF1,0xB0,0xF9,0x01,0x05,0x47,0x42,0x95,0xF1,0xD6,0xAC,
|
||||
0x7E,0x38,0xE6,0x9E,0x95,0x74,0x26,0x3F,0xB4,0x68,0x50,0x18,0xD0,0x43,0x30,0xB4,
|
||||
0x4C,0x4B,0xE3,0x68,0xBF,0xE5,0x4D,0xB6,0x95,0x8B,0x0A,0xA0,0x74,0x25,0x32,0x77,
|
||||
0xCF,0xA1,0xF7,0x2C,0xD8,0x71,0x13,0x5A,0xAB,0xEA,0xC9,0x51,0xE8,0x0D,0xEE,0xEF,
|
||||
0xE9,0x93,0x7E,0x19,0xA7,0x1E,0x43,0x38,0x81,0x16,0x2C,0xA1,0x48,0xE3,0x73,0xCC,
|
||||
0x29,0x21,0x6C,0xD3,0x5D,0xCE,0xA0,0xD9,0x61,0x71,0x43,0xA0,0x15,0x13,0xB5,0x64,
|
||||
0x92,0xCF,0x2A,0x19,0xDC,0xAD,0xB7,0xA5,0x9F,0x86,0x65,0xF8,0x1A,0x9F,0xE7,0xFB,
|
||||
0xF7,0xFD,0xB8,0x13,0x6C,0x27,0xDB,0x6F,0xDF,0x35,0x1C,0xF7,0x8D,0x2C,0x5B,0x9B,
|
||||
0x12,0xAB,0x38,0x64,0x06,0xCC,0xDE,0x31,0xE8,0x4E,0x75,0x11,0x64,0xE3,0xFA,0xEA,
|
||||
0xEB,0x34,0x54,0xC2,0xAD,0x3F,0x34,0xEB,0x93,0x2C,0x7D,0x26,0x36,0x9D,0x56,0xF3,
|
||||
0x5A,0xE1,0xF6,0xB3,0x98,0x63,0x4A,0x9E,0x32,0x83,0xE4,0x9A,0x84,0x60,0x7D,0x90,
|
||||
0x2E,0x13,0x0E,0xEE,0x93,0x4B,0x36,0xA2,0x85,0xEC,0x16,0x38,0xE8,0x88,0x06,0x02,
|
||||
0xBF,0xF0,0xA0,0x3A,0xED,0xD7,0x6A,0x9A,0x73,0xE1,0x57,0xCF,0xF8,0x44,0xB8,0xDC,
|
||||
0x2E,0x23,0x59,0xD1,0xDF,0x95,0x52,0x71,0x99,0x61,0xA0,0x4B,0xD5,0x7F,0x6E,0x78,
|
||||
0xBA,0xA9,0xC5,0x30,0xD3,0x40,0x86,0x32,0x9D,0x32,0x0C,0x9C,0x37,0xB7,0x02,0x2F,
|
||||
0xBA,0x54,0x98,0xA9,0xC4,0x13,0x04,0xC9,0x8D,0xBE,0xC8,0xE7,0x5D,0x97,0x50,0x2E,
|
||||
0x93,0xD6,0x22,0x59,0x0C,0x27,0xBC,0x22,0x92,0xE0,0xA7,0x20,0x0F,0x93,0x6F,0x7F,
|
||||
0x4C,0x9F,0xD3,0xB5,0xA6,0x2A,0x0B,0x74,0x67,0x49,0x7D,0x10,0x26,0xCB,0xD1,0xC5,
|
||||
0x86,0x71,0xE7,0x8C,0xA0,0x9C,0xE9,0x5B,0xB2,0x1A,0xF6,0x01,0xEE,0x8C,0x9E,0x5E,
|
||||
0x83,0xF2,0x1A,0xDB,0xE6,0xE5,0xEA,0x84,0x59,0x76,0xD2,0x7C,0xF6,0x8D,0xA5,0x49,
|
||||
0x36,0x48,0xC2,0x16,0x52,0xBB,0x83,0xA3,0x74,0xB9,0x07,0x0C,0x3B,0xFF,0x61,0x28,
|
||||
0xE1,0x61,0xE9,0xE4,0xEF,0x6E,0x15,0xAA,0x4E,0xBA,0xE8,0x5D,0x05,0x96,0xBB,0x32,
|
||||
0x56,0xB0,0xFB,0x72,0x52,0x0F,0x0E,0xC8,0x42,0x25,0x65,0x76,0x89,0xAF,0xF2,0xDE,
|
||||
0x10,0x27,0xF0,0x01,0x4B,0x74,0xA7,0x97,0x07,0xD5,0x26,0x54,0x54,0x09,0x1F,0x82,
|
||||
0x0A,0x86,0x7D,0x30,0x39,0x0E,0xB3,0x26,0x9B,0x0B,0x57,0xBB,0x36,0x06,0x31,0xAF,
|
||||
0xFD,0x79,0xFC,0xD9,0x30,0x10,0x2B,0x0C,0xB3,0xE1,0x9B,0xD7,0x7B,0xDC,0x5F,0xEF,
|
||||
0xD2,0xF8,0x13,0x45,0x4D,0x47,0x75,0xBD,0x46,0x96,0x3C,0x7E,0x75,0xF3,0x3E,0xB5,
|
||||
0x67,0xC5,0x9A,0x3B,0xB0,0x5B,0x29,0x6B,0xDE,0x80,0x5B,0xC8,0x15,0x05,0xB1,0x31,
|
||||
0xB6,0xCE,0x49,0xDD,0xAD,0x84,0xB5,0xAE,0x60,0xDC,0x67,0x31,0x34,0x30,0xFE,0x4E,
|
||||
0xBD,0x80,0x2F,0xA6,0xBF,0x63,0x39,0x21,0x86,0xD9,0x35,0x7F,0x16,0x68,0x22,0x05,
|
||||
0x54,0xE9,0x90,0x26,0x8C,0x07,0x6C,0x51,0xA4,0x31,0x55,0xD7,0x09,0x07,0xA8,0x3E,
|
||||
0x2E,0x53,0x66,0xC1,0xF8,0xF2,0x7B,0xC4,0xF2,0x58,0xCF,0xF1,0x87,0xC5,0xA2,0xE7,
|
||||
0x27,0x8F,0x30,0x87,0x58,0xA0,0x64,0x62,0x23,0x18,0xB9,0x88,0x7C,0xFA,0xCE,0xC4,
|
||||
0x98,0xAE,0xAD,0x17,0xCC,0x4A,0x5B,0xF3,0xE9,0x48,0xD5,0x56,0xD3,0x0D,0xF2,0xC8,
|
||||
0x92,0x73,0x8C,0xDB,0xD7,0x2F,0x56,0xAC,0x81,0xF9,0x92,0x69,0x4D,0xC6,0x32,0xF6,
|
||||
0xE6,0xC0,0x8D,0x21,0xE2,0x76,0x80,0x61,0x11,0xBC,0xDC,0x6C,0x93,0xAF,0x19,0x69,
|
||||
0x9B,0xD0,0xBF,0xB9,0x31,0x9F,0x02,0x67,0xA3,0x51,0xEE,0x83,0x06,0x22,0x7B,0x0C,
|
||||
0xAB,0x49,0x42,0x40,0xB8,0xD5,0x01,0x7D,0xCE,0x5E,0xF7,0x55,0x53,0x39,0xC5,0x99,
|
||||
0x46,0xD8,0x87,0x9F,0xBA,0xF7,0x64,0xB4,0xE3,0x9A,0xFA,0xA1,0x6D,0x90,0x68,0x10,
|
||||
0x30,0xCA,0x8A,0x54,0xA7,0x9F,0x60,0xC3,0x19,0xF5,0x6B,0x0D,0x7A,0x51,0x98,0xE6,
|
||||
0x98,0x43,0x51,0xB4,0xD6,0x35,0xE9,0x4F,0xC3,0xDF,0x0F,0x7B,0xD6,0x2F,0x5C,0xBD,
|
||||
0x3A,0x15,0x61,0x19,0xF1,0x4B,0xCB,0xAA,0xDC,0x6D,0x64,0xC9,0xD3,0xC6,0x1E,0x56,
|
||||
0xEF,0x38,0x4C,0x50,0x71,0x86,0x75,0xCC,0x0D,0x0D,0x4E,0xE9,0x28,0xF6,0x06,0x5D,
|
||||
0x70,0x1B,0xAA,0xD3,0x45,0xCF,0xA8,0x39,0xAC,0x95,0xA6,0x2E,0xB4,0xE4,0x22,0xD4,
|
||||
0x74,0xA8,0x37,0x5F,0x48,0x7A,0x04,0xCC,0xA5,0x4C,0x40,0xD8,0x28,0xB4,0x28,0x08,
|
||||
0x0D,0x1C,0x72,0x52,0x41,0xF0,0x7D,0x47,0x19,0x3A,0x53,0x4E,0x58,0x84,0x62,0x6B,
|
||||
0x93,0xB5,0x8A,0x81,0x21,0x4E,0x0D,0xDC,0xB4,0x3F,0xA2,0xC6,0xFC,0xC9,0x2B,0x40,
|
||||
0xDA,0x38,0x04,0xE9,0x5E,0x5A,0x86,0x6B,0x0C,0x22,0x25,0x85,0x68,0x11,0x8D,0x7C,
|
||||
0x92,0x1D,0x95,0x55,0x4D,0xAB,0x8E,0xBB,0xDA,0xA6,0xE6,0xB7,0x51,0xB6,0x32,0x5A,
|
||||
0x05,0x41,0xDD,0x05,0x2A,0x0A,0x56,0x50,0x91,0x17,0x47,0xCC,0xC9,0xE6,0x7E,0xB5,
|
||||
0x61,0x4A,0xDB,0x73,0x67,0x51,0xC8,0x33,0xF5,0xDA,0x6E,0x74,0x2E,0x54,0xC3,0x37,
|
||||
0x0D,0x6D,0xAF,0x08,0xE8,0x15,0x8A,0x5F,0xE2,0x59,0x21,0xCD,0xA8,0xDE,0x0C,0x06,
|
||||
0x5A,0x77,0x6B,0x5F,0xDB,0x18,0x65,0x3E,0xC8,0x50,0xDE,0x78,0xE0,0xB8,0x82,0xB3,
|
||||
0x5D,0x4E,0x72,0x32,0x07,0x4F,0xC1,0x34,0x23,0xBA,0x96,0xB7,0x67,0x4E,0xA4,0x28,
|
||||
0x1E,0x34,0x62,0xEB,0x2D,0x6A,0x70,0xE9,0x2F,0x42,0xC4,0x70,0x4E,0x5A,0x31,0x9C,
|
||||
0xF9,0x5B,0x47,0x28,0xAA,0xDA,0x71,0x6F,0x38,0x1F,0xB3,0x78,0xC4,0x92,0x6B,0x1C,
|
||||
0x9E,0xF6,0x35,0x9A,0xB7,0x4D,0x0E,0xBF,0xCC,0x18,0x29,0x41,0x03,0x48,0x35,0x5D,
|
||||
0x55,0xD0,0x2B,0xC6,0x29,0xAF,0x5C,0x60,0x74,0x69,0x8E,0x5E,0x9B,0x7C,0xD4,0xBD,
|
||||
0x7B,0x44,0x64,0x7D,0x3F,0x92,0x5D,0x69,0xB6,0x1F,0x00,0x4B,0xD4,0x83,0x35,0xCF,
|
||||
0x7E,0x64,0x4E,0x17,0xAE,0x8D,0xD5,0x2E,0x9A,0x28,0x12,0x4E,0x2E,0x2B,0x49,0x08,
|
||||
0x5C,0xAE,0xC6,0x46,0x85,0xAE,0x41,0x61,0x1E,0x6F,0x82,0xD2,0x51,0x37,0x16,0x1F,
|
||||
0x0B,0xF6,0x59,0xA4,0x9A,0xCA,0x5A,0xAF,0x0D,0xD4,0x33,0x8B,0x20,0x63,0xF1,0x84,
|
||||
0x80,0x5C,0xCB,0xCF,0x08,0xB4,0xB9,0xD3,0x16,0x05,0xBD,0x62,0x83,0x31,0x9B,0x56,
|
||||
0x51,0x98,0x9F,0xBA,0xB2,0x5B,0xAA,0xB2,0x22,0x6B,0x2C,0xB5,0xD4,0x48,0xFA,0x63,
|
||||
0x2B,0x5F,0x58,0xFA,0x61,0xFA,0x64,0x09,0xBB,0x38,0xE0,0xB8,0x9D,0x92,0x60,0xA8,
|
||||
0x0D,0x67,0x6F,0x0E,0x37,0xF5,0x0D,0x01,0x9F,0xC2,0x77,0xD4,0xFE,0xEC,0xF1,0x73,
|
||||
0x30,0x39,0xE0,0x7D,0xF5,0x61,0x98,0xE4,0x2C,0x28,0x55,0x04,0x56,0x55,0xDB,0x2F,
|
||||
0x6B,0xEC,0xE5,0x58,0x06,0xB6,0x64,0x80,0x6A,0x2A,0x1A,0x4E,0x5B,0x0F,0xD8,0xC4,
|
||||
0x0A,0x2E,0x52,0x19,0xD9,0x62,0xF5,0x30,0x48,0xBE,0x8C,0x7B,0x4F,0x38,0x9B,0xA2,
|
||||
0xC3,0xAF,0xC9,0xD3,0xC7,0xC1,0x62,0x41,0x86,0xB9,0x61,0x21,0x57,0x6F,0x99,0x4F,
|
||||
0xC1,0xBA,0xCE,0x7B,0xB5,0x3B,0x4D,0x5E,0x8A,0x8B,0x44,0x57,0x5F,0x13,0x5F,0x70,
|
||||
0x6D,0x5B,0x29,0x47,0xDC,0x38,0xE2,0xEC,0x04,0x55,0x65,0x12,0x2A,0xE8,0x17,0x43,
|
||||
0xE1,0x8E,0xDD,0x2A,0xB3,0xE2,0x94,0xF7,0x09,0x6E,0x5C,0xE6,0xEB,0x8A,0xF8,0x6D,
|
||||
0x89,0x49,0x54,0x48,0xF5,0x2F,0xAD,0xBF,0xEA,0x94,0x4B,0xCA,0xFC,0x39,0x87,0x82,
|
||||
0x5F,0x8A,0x01,0xF2,0x75,0xF2,0xE6,0x71,0xD6,0xD8,0x42,0xDE,0xF1,0x2D,0x1D,0x28,
|
||||
0xA6,0x88,0x7E,0xA3,0xA0,0x47,0x1D,0x30,0xD9,0xA3,0x71,0xDF,0x49,0x1C,0xCB,0x01,
|
||||
0xF8,0x36,0xB1,0xF2,0xF0,0x22,0x58,0x5D,0x45,0x6B,0xBD,0xA0,0xBB,0xB2,0x88,0x42,
|
||||
0xC7,0x8C,0x28,0xCE,0x93,0xE8,0x90,0x63,0x08,0x90,0x7C,0x89,0x3C,0xF5,0x7D,0xB7,
|
||||
0x04,0x2D,0x4F,0x55,0x51,0x16,0xFD,0x7E,0x79,0xE8,0xBE,0xC1,0xF2,0x12,0xD4,0xF8,
|
||||
0xB4,0x84,0x05,0x23,0xA0,0xCC,0xD2,0x2B,0xFD,0xE1,0xAB,0xAD,0x0D,0xD1,0x55,0x6C,
|
||||
0x23,0x41,0x94,0x4D,0x77,0x37,0x4F,0x05,0x28,0x0C,0xBF,0x17,0xB3,0x12,0x67,0x6C,
|
||||
0x8C,0xC3,0x5A,0xF7,0x41,0x84,0x2A,0x6D,0xD0,0x94,0x12,0x27,0x2C,0xB4,0xED,0x9C,
|
||||
0x4D,0xEC,0x47,0x82,0x97,0xD5,0x67,0xB9,0x1B,0x9D,0xC0,0x55,0x07,0x7E,0xE5,0x8E,
|
||||
0xE2,0xA8,0xE7,0x3E,0x12,0xE4,0x0E,0x3A,0x2A,0x45,0x55,0x34,0xA2,0xF9,0x2D,0x5A,
|
||||
0x1B,0xAB,0x52,0x7C,0x83,0x10,0x5F,0x55,0xD2,0xF1,0x5A,0x43,0x2B,0xC6,0xA7,0xA4,
|
||||
0x89,0x15,0x95,0xE8,0xB4,0x4B,0x9D,0xF8,0x75,0xE3,0x9F,0x60,0x78,0x5B,0xD6,0xE6,
|
||||
0x0D,0x44,0xE6,0x21,0x06,0xBD,0x47,0x22,0x53,0xA4,0x00,0xAD,0x8D,0x43,0x13,0x85,
|
||||
0x39,0xF7,0xAA,0xFC,0x38,0xAF,0x7B,0xED,0xFC,0xE4,0x2B,0x54,0x50,0x98,0x4C,0xFC,
|
||||
0x85,0x80,0xF7,0xDF,0x3C,0x80,0x22,0xE1,0x94,0xDA,0xDE,0x24,0xC6,0xB0,0x7A,0x39,
|
||||
0x38,0xDC,0x0F,0xA1,0xA7,0xF4,0xF9,0x6F,0x63,0x18,0x57,0x8B,0x84,0x41,0x2A,0x2E,
|
||||
0xD4,0x53,0xF2,0xD9,0x00,0x0F,0xD0,0xDD,0x99,0x6E,0x19,0xA6,0x0A,0xD0,0xEC,0x5B,
|
||||
0x58,0x24,0xAB,0xC0,0xCB,0x06,0x65,0xEC,0x1A,0x13,0x38,0x94,0x0A,0x67,0x03,0x2F,
|
||||
0x3F,0xF7,0xE3,0x77,0x44,0x77,0x33,0xC6,0x14,0x39,0xD0,0xE3,0xC0,0xA2,0x08,0x79,
|
||||
0xBB,0x40,0x99,0x57,0x41,0x0B,0x01,0x90,0xCD,0xE1,0xCC,0x48,0x67,0xDB,0xB3,0xAF,
|
||||
0x88,0x74,0xF3,0x4C,0x82,0x8F,0x72,0xB1,0xB5,0x23,0x29,0xC4,0x12,0x6C,0x19,0xFC,
|
||||
0x8E,0x46,0xA4,0x9C,0xC4,0x25,0x65,0x87,0xD3,0x6D,0xBE,0x8A,0x93,0x11,0x03,0x38,
|
||||
0xED,0x83,0x2B,0xF3,0x46,0xA4,0x93,0xEA,0x3B,0x53,0x85,0x1D,0xCE,0xD4,0xF1,0x08,
|
||||
0x83,0x27,0xED,0xFC,0x9B,0x1A,0x18,0xBC,0xF9,0x8B,0xAE,0xDC,0x24,0xAB,0x50,0x38,
|
||||
0xE9,0x72,0x4B,0x10,0x22,0x17,0x7B,0x46,0x5D,0xAB,0x59,0x64,0xF3,0x40,0xAE,0xF8,
|
||||
0xBB,0xE5,0xC8,0xF9,0x26,0x03,0x4E,0x55,0x7D,0xEB,0xEB,0xFE,0xF7,0x39,0xE6,0xE0,
|
||||
0x0A,0x11,0xBE,0x2E,0x28,0xFF,0x98,0xED,0xC0,0xC9,0x42,0x56,0x42,0xC3,0xFD,0x00,
|
||||
0xF6,0xAF,0x87,0xA2,0x5B,0x01,0x3F,0x32,0x92,0x47,0x95,0x9A,0x72,0xA5,0x32,0x3D,
|
||||
0xAE,0x6B,0xD0,0x9B,0x07,0xD2,0x49,0x92,0xE3,0x78,0x4A,0xFA,0xA1,0x06,0x7D,0xF2,
|
||||
0x41,0xCF,0x77,0x74,0x04,0x14,0xB2,0x0C,0x86,0x84,0x64,0x16,0xD5,0xBB,0x51,0xA1,
|
||||
0xE5,0x6F,0xF1,0xD1,0xF2,0xE2,0xF7,0x5F,0x58,0x20,0x4D,0xB8,0x57,0xC7,0xCF,0xDD,
|
||||
0xC5,0xD8,0xBE,0x76,0x3D,0xF6,0x5F,0x7E,0xE7,0x2A,0x8B,0x88,0x24,0x1B,0x38,0x3F,
|
||||
0x0E,0x41,0x23,0x77,0xF5,0xF0,0x4B,0xD4,0x0C,0x1F,0xFA,0xA4,0x0B,0x80,0x5F,0xCF,
|
||||
0x45,0xF6,0xE0,0xDA,0x2F,0x34,0x59,0x53,0xFB,0x20,0x3C,0x52,0x62,0x5E,0x35,0xB5,
|
||||
0x62,0xFE,0x8B,0x60,0x63,0xE3,0x86,0x5A,0x15,0x1A,0x6E,0xD1,0x47,0x45,0xBC,0x32,
|
||||
0xB4,0xEB,0x67,0x38,0xAB,0xE4,0x6E,0x33,0x3A,0xB5,0xED,0xA3,0xAD,0x67,0xE0,0x4E,
|
||||
0x41,0x95,0xEE,0x62,0x62,0x71,0x26,0x1D,0x31,0xEF,0x62,0x30,0xAF,0xD7,0x82,0xAC,
|
||||
0xC2,0xDC,0x05,0x04,0xF5,0x97,0x07,0xBF,0x11,0x59,0x23,0x07,0xC0,0x64,0x02,0xE8,
|
||||
0x97,0xE5,0x3E,0xAF,0x18,0xAC,0x59,0xA6,0x8B,0x4A,0x33,0x90,0x1C,0x6E,0x7C,0x9C,
|
||||
0x20,0x7E,0x4C,0x3C,0x3E,0x61,0x64,0xBB,0xC5,0x6B,0x7C,0x7E,0x3E,0x9F,0xC5,0x4C,
|
||||
0x9F,0xEA,0x73,0xF5,0xD7,0x89,0xC0,0x4C,0xF4,0xFB,0xF4,0x2D,0xEC,0x14,0x1B,0x51,
|
||||
0xD5,0xC1,0x12,0xC8,0x10,0xDF,0x0B,0x4A,0x8B,0x9C,0xBC,0x93,0x45,0x6A,0x3E,0x3E,
|
||||
0x7D,0xC1,0xA9,0xBA,0xCD,0xC1,0xB4,0x07,0xE4,0xE1,0x68,0x86,0x43,0xB2,0x6D,0x38,
|
||||
0xF3,0xFB,0x0C,0x5C,0x66,0x37,0x71,0xDE,0x56,0xEF,0x6E,0xA0,0x10,0x40,0x65,0xA7,
|
||||
0x98,0xF7,0xD0,0xBE,0x0E,0xC8,0x37,0x36,0xEC,0x10,0xCA,0x7C,0x9C,0xAB,0x84,0x1E,
|
||||
0x05,0x17,0x76,0x02,0x1C,0x4F,0x52,0xAA,0x5F,0xC1,0xC6,0xA0,0x56,0xB9,0xD8,0x04,
|
||||
0x84,0x44,0x4D,0xA7,0x59,0xD8,0xDE,0x60,0xE6,0x38,0x0E,0x05,0x8F,0x03,0xE1,0x3B,
|
||||
0x6D,0x81,0x04,0x33,0x6F,0x30,0x0B,0xCE,0x69,0x05,0x21,0x33,0xFB,0x26,0xBB,0x89,
|
||||
0x7D,0xB6,0xAE,0x87,0x7E,0x51,0x07,0xE0,0xAC,0xF7,0x96,0x0A,0x6B,0xF9,0xC4,0x5C,
|
||||
0x1D,0xE4,0x44,0x47,0xB8,0x5E,0xFA,0xE3,0x78,0x84,0x55,0x42,0x4B,0x48,0x5E,0xF7,
|
||||
0x7D,0x47,0x35,0x86,0x1D,0x2B,0x43,0x05,0x03,0xEC,0x8A,0xB8,0x1E,0x06,0x3C,0x76,
|
||||
0x0C,0x48,0x1A,0x43,0xA7,0xB7,0x8A,0xED,0x1E,0x13,0xC6,0x43,0xEE,0x10,0xEF,0xDB,
|
||||
0xEC,0xFB,0x3C,0x83,0xB2,0x95,0x44,0xEF,0xD8,0x54,0x51,0x4E,0x2D,0x11,0x44,0x1D,
|
||||
0xFB,0x36,0x59,0x1E,0x7A,0x34,0xC1,0xC3,0xCA,0x57,0x00,0x61,0xEA,0x67,0xA5,0x16,
|
||||
0x9B,0x55,0xD0,0x55,0xE1,0x7F,0xD9,0x36,0xD2,0x40,0x76,0xAE,0xDC,0x01,0xCE,0xB0,
|
||||
0x7A,0x83,0xD5,0xCB,0x20,0x98,0xEC,0x6B,0xC1,0x72,0x92,0x34,0xF3,0x82,0x57,0x37,
|
||||
0x62,0x8A,0x32,0x36,0x0C,0x90,0x43,0xAE,0xAE,0x5C,0x9B,0x78,0x8E,0x13,0x65,0x02,
|
||||
0xFD,0x68,0x71,0xC1,0xFE,0xB0,0x31,0xA0,0x24,0x82,0xB0,0xC3,0xB1,0x79,0x69,0xA7,
|
||||
0xF5,0xD2,0xEB,0xD0,0x82,0xC0,0x32,0xDC,0x9E,0xC7,0x26,0x3C,0x6D,0x8D,0x98,0xC1,
|
||||
0xBB,0x22,0xD4,0xD0,0x0F,0x33,0xEC,0x3E,0xB9,0xCC,0xE1,0xDC,0x6A,0x4C,0x77,0x36,
|
||||
0x14,0x1C,0xF9,0xBF,0x81,0x9F,0x28,0x5F,0x71,0x85,0x32,0x29,0x90,0x75,0x48,0xC4,
|
||||
0xB3,0x4A,0xCE,0xD8,0x44,0x8F,0x14,0x2F,0xFD,0x40,0x57,0xEF,0xAA,0x08,0x75,0xD9,
|
||||
0x46,0xD1,0xD6,0x6E,0x32,0x55,0x1F,0xC3,0x18,0xFE,0x84,0x1F,0xFC,0x84,0xD5,0xFF,
|
||||
0x71,0x5E,0x1B,0x48,0xC3,0x86,0x95,0x0E,0x28,0x08,0x27,0xD3,0x38,0x83,0x71,0x7B,
|
||||
0x4C,0x80,0x63,0x54,0x9A,0x56,0xB0,0xAC,0xCF,0x80,0xCA,0x31,0x09,0xEF,0xFE,0xF3,
|
||||
0xBE,0xAF,0x24,0x7E,0xA6,0xFE,0x53,0x3F,0xC2,0x8D,0x4A,0x33,0x68,0xD1,0x22,0xA6,
|
||||
0x66,0xAD,0x7B,0xEA,0xDE,0xB6,0x43,0xB0,0xA1,0x25,0x95,0x00,0xA3,0x3F,0x75,0x46,
|
||||
0x14,0x11,0x44,0xEC,0xD7,0x95,0xBC,0x92,0xF0,0x4F,0xA9,0x16,0x53,0x62,0x97,0x60,
|
||||
0x2A,0x0F,0x41,0xF1,0x71,0x24,0xBE,0xEE,0x94,0x7F,0x08,0xCD,0x60,0x93,0xB3,0x85,
|
||||
0x5B,0x07,0x00,0x3F,0xD8,0x0F,0x28,0x83,0x9A,0xD1,0x69,0x9F,0xD1,0xDA,0x2E,0xC3,
|
||||
0x90,0x01,0xA2,0xB9,0x6B,0x4E,0x2A,0x66,0x9D,0xDA,0xAE,0xA6,0xEA,0x2A,0xD3,0x68,
|
||||
0x2F,0x0C,0x0C,0x9C,0xD2,0x8C,0x4A,0xED,0xE2,0x9E,0x57,0x65,0x9D,0x09,0x87,0xA3,
|
||||
0xB4,0xC4,0x32,0x5D,0xC9,0xD4,0x32,0x2B,0xB1,0xE0,0x71,0x1E,0x64,0x4D,0xE6,0x90,
|
||||
0x71,0xE3,0x1E,0x40,0xED,0x7D,0xF3,0x84,0x0E,0xED,0xC8,0x78,0x76,0xAE,0xC0,0x71,
|
||||
0x27,0x72,0xBB,0x05,0xEA,0x02,0x64,0xFB,0xF3,0x48,0x6B,0xB5,0x42,0x93,0x3F,0xED,
|
||||
0x9F,0x13,0x53,0xD2,0xF7,0xFE,0x2A,0xEC,0x1D,0x47,0x25,0xDB,0x3C,0x91,0x86,0xC6,
|
||||
0x8E,0xF0,0x11,0xFD,0x23,0x74,0x36,0xF7,0xA4,0xF5,0x9E,0x7A,0x7E,0x53,0x50,0x44,
|
||||
0xD4,0x47,0xCA,0xD3,0xEB,0x38,0x6D,0xE6,0xD9,0x71,0x94,0x7F,0x4A,0xC6,0x69,0x4B,
|
||||
0x11,0xF4,0x52,0xEA,0x22,0xFE,0x8A,0xB0,0x36,0x67,0x8B,0x59,0xE8,0xE6,0x80,0x2A,
|
||||
0xEB,0x65,0x04,0x13,0xEE,0xEC,0xDC,0x9E,0x5F,0xB1,0xEC,0x05,0x6A,0x59,0xE6,0x9F,
|
||||
0x5E,0x59,0x6B,0x89,0xBF,0xF7,0x1A,0xCA,0x44,0xF9,0x5B,0x6A,0x71,0x85,0x03,0xE4,
|
||||
0x29,0x62,0xE0,0x70,0x6F,0x41,0xC4,0xCF,0xB2,0xB1,0xCC,0xE3,0x7E,0xA6,0x07,0xA8,
|
||||
0x87,0xE7,0x7F,0x84,0x93,0xDB,0x52,0x4B,0x6C,0xEC,0x7E,0xDD,0xD4,0x24,0x48,0x10,
|
||||
0x69,0x9F,0x04,0x60,0x74,0xE6,0x48,0x18,0xF3,0xE4,0x2C,0xB9,0x4F,0x2E,0x50,0x7A,
|
||||
0xDF,0xD4,0x54,0x69,0x2B,0x8B,0xA7,0xF3,0xCE,0xFF,0x1F,0xF3,0x3E,0x26,0x01,0x39,
|
||||
0x17,0x95,0x84,0x89,0xB0,0xF0,0x4C,0x4B,0x82,0x91,0x9F,0xC4,0x4B,0xAC,0x9D,0xA5,
|
||||
0x74,0xAF,0x17,0x25,0xC9,0xCA,0x32,0xD3,0xBC,0x89,0x8A,0x84,0x89,0xCC,0x0D,0xAE,
|
||||
0x7C,0xA2,0xDB,0x9C,0x6A,0x78,0x91,0xEE,0xEA,0x76,0x5D,0x4E,0x87,0x60,0xF5,0x69,
|
||||
0x15,0x67,0xD4,0x02,0xCF,0xAF,0x48,0x36,0x07,0xEA,0xBF,0x6F,0x66,0x2D,0x06,0x8F,
|
||||
0xC4,0x9A,0xFE,0xF9,0xF6,0x90,0x87,0x75,0xB8,0xF7,0xAD,0x0F,0x76,0x10,0x5A,0x3D,
|
||||
0x59,0xB0,0x2E,0xB3,0xC7,0x35,0x2C,0xCC,0x70,0x56,0x2B,0xCB,0xE3,0x37,0x96,0xC5,
|
||||
0x2F,0x46,0x1B,0x8A,0x22,0x46,0xC7,0x88,0xA7,0x26,0x32,0x98,0x61,0xDF,0x86,0x22,
|
||||
0x8A,0xF4,0x1C,0x2F,0x87,0xA1,0x09,0xAA,0xCC,0xA9,0xAE,0xD3,0xBD,0x00,0x45,0x1C,
|
||||
0x9A,0x54,0x87,0x86,0x52,0x87,0xEF,0xFF,0x1E,0x8F,0xA1,0x8F,0xC1,0x89,0x5C,0x35,
|
||||
0x1B,0xDA,0x2D,0x3A,0x2C,0x16,0xB2,0xC2,0xF1,0x56,0xE2,0x78,0xC1,0x6B,0x63,0x97,
|
||||
0xC5,0x56,0x8F,0xC9,0x32,0x7F,0x2C,0xAA,0xAF,0xA6,0xA8,0xAC,0x20,0x91,0x22,0x88,
|
||||
0xDE,0xE4,0x60,0x8B,0xF9,0x4B,0x42,0x25,0x1A,0xE3,0x7F,0x9C,0x2C,0x19,0x89,0x3A,
|
||||
0x7E,0x05,0xD4,0x36,0xCC,0x69,0x58,0xC2,0xC1,0x32,0x8B,0x2F,0x90,0x85,0xEB,0x7A,
|
||||
0x39,0x50,0xA5,0xA1,0x27,0x92,0xC5,0x66,0xB0,0x20,0x4F,0x58,0x7E,0x55,0x83,0x43,
|
||||
0x2B,0x45,0xE2,0x9C,0xE4,0xD8,0x12,0x90,0x2C,0x16,0x83,0x56,0x16,0x79,0x03,0xB3,
|
||||
0xAD,0x2D,0x61,0x18,0x1A,0x13,0x1F,0x37,0xE2,0xE1,0x9C,0x73,0x7B,0x80,0xD5,0xFD,
|
||||
0x2D,0x51,0x87,0xFC,0x7B,0xAA,0xD7,0x1F,0x2C,0x7A,0x8E,0xAF,0xF4,0x8D,0xBB,0xCD,
|
||||
0x95,0x11,0x7C,0x72,0x0B,0xEE,0x6F,0xE2,0xB9,0xAF,0xDE,0x37,0x83,0xDE,0x8C,0x8D,
|
||||
0x62,0x05,0x67,0xB7,0x96,0xC6,0x8D,0x56,0xB6,0x0D,0xD7,0x62,0xBA,0xD6,0x46,0x36,
|
||||
0xBD,0x8E,0xC8,0xE6,0xEA,0x2A,0x6C,0x10,0x14,0xFF,0x6B,0x5B,0xFA,0x82,0x3C,0x46,
|
||||
0xB1,0x30,0x43,0x46,0x51,0x8A,0x7D,0x9B,0x92,0x3E,0x83,0x79,0x5B,0x55,0x5D,0xB2,
|
||||
0x6C,0x5E,0xCE,0x90,0x62,0x8E,0x53,0x98,0xC9,0x0D,0x6D,0xE5,0x2D,0x57,0xCD,0xC5,
|
||||
0x81,0x57,0xBA,0xE1,0xE8,0xB8,0x8F,0x72,0xE5,0x4F,0x13,0xDC,0xEA,0x9D,0x71,0x15,
|
||||
0x10,0xB2,0x11,0x88,0xD5,0x09,0xD4,0x7F,0x5B,0x65,0x7F,0x2C,0x3B,0x38,0x4C,0x11,
|
||||
0x68,0x50,0x8D,0xFB,0x9E,0xB0,0x59,0xBF,0x94,0x80,0x89,0x4A,0xC5,0x1A,0x18,0x12,
|
||||
0x89,0x53,0xD1,0x4A,0x10,0x29,0xE8,0x8C,0x1C,0xEC,0xB6,0xEA,0x46,0xC7,0x17,0x8B,
|
||||
0x25,0x15,0x31,0xA8,0xA2,0x6B,0x43,0xB1,0x9D,0xE2,0xDB,0x0B,0x87,0x9B,0xB0,0x11,
|
||||
0x04,0x0E,0x71,0xD2,0x29,0x77,0x89,0x82,0x0A,0x66,0x41,0x7F,0x1D,0x0B,0x48,0xFF,
|
||||
0x72,0xBB,0x24,0xFD,0xC2,0x48,0xA1,0x9B,0xFE,0x7B,0x7F,0xCE,0x88,0xDB,0x86,0xD9,
|
||||
0x85,0x3B,0x1C,0xB0,0xDC,0xA8,0x33,0x07,0xBF,0x51,0x2E,0xE3,0x0E,0x9A,0x00,0x97,
|
||||
0x1E,0x06,0xC0,0x97,0x43,0x9D,0xD8,0xB6,0x45,0xC4,0x86,0x67,0x5F,0x00,0xF8,0x88,
|
||||
0x9A,0xA4,0x52,0x9E,0xC7,0xAA,0x8A,0x83,0x75,0xEC,0xC5,0x18,0xAE,0xCE,0xC3,0x2F,
|
||||
0x1A,0x2B,0xF9,0x18,0xFF,0xAE,0x1A,0xF5,0x53,0x0B,0xB5,0x33,0x51,0xA7,0xFD,0xE8,
|
||||
0xA8,0xE1,0xA2,0x64,0xB6,0x22,0x17,0x43,0x80,0xCC,0x0A,0xD8,0xAE,0x3B,0xBA,0x40,
|
||||
0xD7,0xD9,0x92,0x4A,0x89,0xDF,0x04,0x10,0xEE,0x9B,0x18,0x2B,0x6A,0x77,0x69,0x8A,
|
||||
0x68,0xF4,0xF9,0xB9,0xA2,0x21,0x15,0x6E,0xE6,0x1E,0x3B,0x03,0x62,0x30,0x9B,0x60,
|
||||
0x41,0x7E,0x25,0x9B,0x9E,0x8F,0xC5,0x52,0x10,0x08,0xF8,0xC2,0x69,0xA1,0x21,0x11,
|
||||
0x88,0x37,0x5E,0x79,0x35,0x66,0xFF,0x10,0x42,0x18,0x6E,0xED,0x97,0xB6,0x6B,0x1C,
|
||||
0x4E,0x36,0xE5,0x6D,0x7D,0xB4,0xE4,0xBF,0x20,0xB9,0xE0,0x05,0x3A,0x69,0xD5,0xB8,
|
||||
0xE3,0xD5,0xDC,0xE0,0xB9,0xAC,0x53,0x3E,0x07,0xA4,0x57,0xAD,0x77,0xFF,0x48,0x18,
|
||||
0x76,0x2A,0xAC,0x49,0x2A,0x8E,0x47,0x75,0x6D,0x9F,0x67,0x63,0x30,0x35,0x8C,0x39,
|
||||
0x05,0x39,0xD5,0x6F,0x64,0x3A,0x5B,0xAD,0xCA,0x0B,0xBB,0x82,0x52,0x99,0x45,0xB1,
|
||||
0x93,0x36,0x36,0x99,0xAF,0x13,0x20,0x44,0x36,0xD8,0x02,0x44,0x09,0x39,0x92,0x85,
|
||||
0xFF,0x4A,0x4A,0x97,0x87,0xA6,0x63,0xD7,0xC7,0xB5,0xB5,0x24,0xED,0x0F,0xB4,0x6F,
|
||||
0x0C,0x58,0x52,0x14,0xD9,0xA6,0x7B,0xD3,0x79,0xBC,0x38,0x58,0xA1,0xBD,0x3B,0x84,
|
||||
0x06,0xD8,0x1A,0x06,0xFD,0x6B,0xA8,0xEA,0x4B,0x69,0x28,0x04,0x37,0xAD,0x82,0x99,
|
||||
0xFB,0x0E,0x1B,0x85,0xBD,0xA8,0x5D,0x73,0xCD,0xDC,0x58,0x75,0x0A,0xBE,0x63,0x6C,
|
||||
0x48,0xE7,0x4C,0xE4,0x30,0x2B,0x04,0x60,0xB9,0x15,0xD8,0xDA,0x86,0x81,0x75,0x8F,
|
||||
0x96,0xD4,0x8D,0x1C,0x5D,0x70,0x85,0x7C,0x1C,0x67,0x7B,0xD5,0x08,0x67,0xA6,0xCE,
|
||||
0x4B,0x0A,0x66,0x70,0xB7,0xE5,0x63,0xD4,0x5B,0x8A,0x82,0xEA,0x10,0x67,0xCA,0xE2,
|
||||
0xF4,0xEF,0x17,0x85,0x2F,0x2A,0x5F,0x8A,0x97,0x82,0xF8,0x6A,0xD6,0x34,0x10,0xEA,
|
||||
0xEB,0xC9,0x5C,0x3C,0xE1,0x49,0xF8,0x46,0xEB,0xDE,0xBD,0xF6,0xA9,0x92,0xF1,0xAA,
|
||||
0xA6,0xA0,0x18,0xB0,0x3A,0xD3,0x0F,0x1F,0xF3,0x6F,0xFF,0x31,0x45,0x43,0x44,0xD3,
|
||||
0x50,0x9A,0xF7,0x88,0x09,0x96,0xC1,0xCE,0x76,0xCC,0xF2,0x2C,0x2C,0xBA,0xAD,0x82,
|
||||
0x77,0x8F,0x18,0x84,0xC0,0xD2,0x07,0x9C,0x36,0x90,0x83,0x4E,0x0B,0xA5,0x4F,0x43,
|
||||
0x3E,0x04,0xAB,0x78,0x4F,0xD6,0xFB,0x09,0x01,0x24,0x90,0xDA,0x6F,0x3C,0x3A,0x61,
|
||||
0x0D,0x7F,0x69,0x4A,0xEB,0x2B,0x30,0x02,0xB4,0xDB,0xE0,0x84,0xA9,0xEC,0xD7,0x35,
|
||||
0xBF,0x37,0x7D,0x85,0x58,0xCE,0xA9,0x4E,0xE4,0x80,0xC7,0xA8,0xD3,0x30,0x67,0x48,
|
||||
0xEB,0x29,0xAF,0x2F,0x74,0x6A,0xB4,0xA7,0x3F,0x0F,0x3F,0x92,0xAF,0xF3,0xCA,0xAC,
|
||||
0xAF,0x4B,0xD9,0x94,0xC0,0x43,0xCA,0x81,0x0D,0x2F,0x48,0xA1,0xB0,0x27,0xD5,0xD2,
|
||||
0xEF,0x4B,0x05,0x85,0xA3,0xDE,0x4D,0x93,0x30,0x3C,0xF0,0xBB,0x4A,0x8F,0x30,0x27,
|
||||
0x4C,0xEB,0xE3,0x3E,0x64,0xED,0x9A,0x2F,0x3B,0xF1,0x82,0xF0,0xBA,0xF4,0xCF,0x7F,
|
||||
0x40,0xCB,0xB0,0xE1,0x7F,0xBC,0xAA,0x57,0xD3,0xC9,0x74,0xF2,0xFA,0x43,0x0D,0x22,
|
||||
0xD0,0xF4,0x77,0x4E,0x93,0xD7,0x85,0x70,0x1F,0x99,0xBF,0xB6,0xDE,0x35,0xF1,0x30,
|
||||
0xA7,0x5E,0x71,0xF0,0x6B,0x01,0x2D,0x7B,0x64,0xF0,0x33,0x53,0x0A,0x39,0x88,0xF3,
|
||||
0x6B,0x3A,0xA6,0x6B,0x35,0xD2,0x2F,0x43,0xCD,0x02,0xFD,0xB5,0xE9,0xBC,0x5B,0xAA,
|
||||
0xD8,0xA4,0x19,0x7E,0x0E,0x5D,0x94,0x81,0x9E,0x6F,0x77,0xAD,0xD6,0x0E,0x74,0x93,
|
||||
0x96,0xE7,0xC4,0x18,0x5F,0xAD,0xF5,0x19,
|
||||
};
|
||||
|
||||
#region ARM9 decryption check values
|
||||
|
||||
public const uint MAGIC30 = 0x72636E65;
|
||||
public const uint MAGIC34 = 0x6A624F79;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
11
NDecrypt/NDS/Enums.cs
Normal file
11
NDecrypt/NDS/Enums.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace NDecrypt.NDS
|
||||
{
|
||||
public enum NDSUnitcode : byte
|
||||
{
|
||||
NDS = 0x00,
|
||||
NDSPlusDSi = 0x02,
|
||||
DSi = 0x03,
|
||||
}
|
||||
}
|
||||
578
NDecrypt/NDS/Headers/NDSHeader.cs
Normal file
578
NDecrypt/NDS/Headers/NDSHeader.cs
Normal file
@@ -0,0 +1,578 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NDecrypt.NDS.Headers
|
||||
{
|
||||
public class NDSHeader
|
||||
{
|
||||
#region Common
|
||||
|
||||
/// <summary>
|
||||
/// Game Title
|
||||
/// </summary>
|
||||
public char[] GameTitle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gamecode
|
||||
/// </summary>
|
||||
public uint Gamecode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Makercode
|
||||
/// </summary>
|
||||
public char[] Makercode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unitcode
|
||||
/// </summary>
|
||||
public NDSUnitcode Unitcode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Encryption seed select (device code. 0 = normal)
|
||||
/// </summary>
|
||||
public byte EncryptionSeedSelect { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Devicecapacity
|
||||
/// </summary>
|
||||
public byte Devicecapacity { get; private set; }
|
||||
public int DeviceCapacityInBytes { get { return (1 << Devicecapacity) * (1024 * 1024); } }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Game Revision (used by DSi titles)
|
||||
/// </summary>
|
||||
public ushort GameRevision { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ROM Version
|
||||
/// </summary>
|
||||
public byte RomVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal flags, (Bit2: Autostart)
|
||||
/// </summary>
|
||||
public byte InternalFlags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 rom offset
|
||||
/// </summary>
|
||||
public uint ARM9RomOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 entry address
|
||||
/// </summary>
|
||||
public uint ARM9EntryAddress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 load address
|
||||
/// </summary>
|
||||
public uint ARM9LoadAddress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 size
|
||||
/// </summary>
|
||||
public uint ARM9Size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7 rom offset
|
||||
/// </summary>
|
||||
public uint ARM7RomOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7 entry address
|
||||
/// </summary>
|
||||
public uint ARM7EntryAddress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7 load address
|
||||
/// </summary>
|
||||
public uint ARM7LoadAddress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7 size
|
||||
/// </summary>
|
||||
public uint ARM7Size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File Name Table (FNT) offset
|
||||
/// </summary>
|
||||
public uint FileNameTableOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File Name Table (FNT) length
|
||||
/// </summary>
|
||||
public uint FileNameTableLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File Allocation Table (FNT) offset
|
||||
/// </summary>
|
||||
public uint FileAllocationTableOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File Allocation Table (FNT) length
|
||||
/// </summary>
|
||||
public uint FileAllocationTableLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File Name Table (FNT) offset
|
||||
/// </summary>
|
||||
public uint ARM9OverlayOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File Name Table (FNT) length
|
||||
/// </summary>
|
||||
public uint ARM9OverlayLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File Name Table (FNT) offset
|
||||
/// </summary>
|
||||
public uint ARM7OverlayOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File Name Table (FNT) length
|
||||
/// </summary>
|
||||
public uint ARM7OverlayLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normal card control register settings (0x00416657 for OneTimePROM)
|
||||
/// </summary>
|
||||
public byte[] NormalCardControlRegisterSettings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Secure card control register settings (0x081808F8 for OneTimePROM)
|
||||
/// </summary>
|
||||
public byte[] SecureCardControlRegisterSettings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Icon Banner offset (NDSi same as NDS, but with new extra entries)
|
||||
/// </summary>
|
||||
public uint IconBannerOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Secure area (2K) CRC
|
||||
/// </summary>
|
||||
public ushort SecureAreaCRC { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Secure transfer timeout (0x0D7E for OneTimePROM)
|
||||
/// </summary>
|
||||
public ushort SecureTransferTimeout { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 autoload
|
||||
/// </summary>
|
||||
public byte[] ARM9Autoload { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7 autoload
|
||||
/// </summary>
|
||||
public byte[] ARM7Autoload { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Secure disable
|
||||
/// </summary>
|
||||
public byte[] SecureDisable { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// NTR region ROM size (excluding DSi area)
|
||||
/// </summary>
|
||||
public uint NTRRegionRomSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Header size
|
||||
/// </summary>
|
||||
public uint HeaderSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///Reserved (0x88, 0x8C, 0x90 = Unknown, used by DSi)
|
||||
/// </summary>
|
||||
public byte[] Reserved2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nintendo Logo
|
||||
/// </summary>
|
||||
public byte[] NintendoLogo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nintendo Logo CRC
|
||||
/// </summary>
|
||||
public ushort NintendoLogoCRC { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Header CRC
|
||||
/// </summary>
|
||||
public ushort HeaderCRC { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Debugger reserved
|
||||
/// </summary>
|
||||
public byte[] DebuggerReserved { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extended DSi
|
||||
|
||||
/// <summary>
|
||||
/// Global MBK1..MBK5 Settings
|
||||
/// </summary>
|
||||
public byte[] GlobalMBK15Settings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Local MBK6..MBK8 Settings for ARM9
|
||||
/// </summary>
|
||||
public byte[] LocalMBK68SettingsARM9 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Local MBK6..MBK8 Settings for ARM7
|
||||
/// </summary>
|
||||
public byte[] LocalMBK68SettingsARM7 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global MBK9 Setting
|
||||
/// </summary>
|
||||
public byte[] GlobalMBK9Setting { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public byte[] RegionFlags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Access control
|
||||
/// </summary>
|
||||
public byte[] AccessControl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7 SCFG EXT mask (controls which devices to enable)
|
||||
/// </summary>
|
||||
public byte[] ARM7SCFGEXTMask { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved/flags? When bit2 of byte 0x1bf is set, usage of banner.sav from the title data dir is enabled.(additional banner data)
|
||||
/// </summary>
|
||||
public byte[] ReservedFlags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9i rom offset
|
||||
/// </summary>
|
||||
public uint ARM9iRomOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9i load address
|
||||
/// </summary>
|
||||
public uint ARM9iLoadAddress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9i size;
|
||||
/// </summary>
|
||||
public uint ARM9iSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7i rom offset
|
||||
/// </summary>
|
||||
public uint ARM7iRomOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pointer to base address where various structures and parameters are passed to the title - what is that???
|
||||
/// </summary>
|
||||
public byte[] Reserved4 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7i load address
|
||||
/// </summary>
|
||||
public uint ARM7iLoadAddress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7i size;
|
||||
/// </summary>
|
||||
public uint ARM7iSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest NTR region offset
|
||||
/// </summary>
|
||||
public uint DigestNTRRegionOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest NTR region length
|
||||
/// </summary>
|
||||
public uint DigestNTRRegionLength { get; private set; }
|
||||
|
||||
// <summary>
|
||||
/// Digest TWL region offset
|
||||
/// </summary>
|
||||
public uint DigestTWLRegionOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest TWL region length
|
||||
/// </summary>
|
||||
public uint DigestTWLRegionLength { get; private set; }
|
||||
|
||||
// <summary>
|
||||
/// Digest Sector Hashtable region offset
|
||||
/// </summary>
|
||||
public uint DigestSectorHashtableRegionOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest Sector Hashtable region length
|
||||
/// </summary>
|
||||
public uint DigestSectorHashtableRegionLength { get; private set; }
|
||||
|
||||
// <summary>
|
||||
/// Digest Block Hashtable region offset
|
||||
/// </summary>
|
||||
public uint DigestBlockHashtableRegionOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest Block Hashtable region length
|
||||
/// </summary>
|
||||
public uint DigestBlockHashtableRegionLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest Sector size
|
||||
/// </summary>
|
||||
public uint DigestSectorSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digeset Block Sectorount
|
||||
/// </summary>
|
||||
public uint DigestBlockSectorCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Icon Banner Size (usually 0x23C0)
|
||||
/// </summary>
|
||||
public uint IconBannerSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unknown (used by DSi)
|
||||
/// </summary>
|
||||
public byte[] Unknown1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// NTR+TWL region ROM size (total size including DSi area)
|
||||
/// </summary>
|
||||
public uint NTRTWLRegionRomSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unknown (used by DSi)
|
||||
/// </summary>
|
||||
public byte[] Unknown2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Modcrypt area 1 offset
|
||||
/// </summary>
|
||||
public uint ModcryptArea1Offset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Modcrypt area 1 size
|
||||
/// </summary>
|
||||
public uint ModcryptArea1Size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Modcrypt area 2 offset
|
||||
/// </summary>
|
||||
public uint ModcryptArea2Offset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Modcrypt area 2 size
|
||||
/// </summary>
|
||||
public uint ModcryptArea2Size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Title ID
|
||||
/// </summary>
|
||||
public byte[] TitleID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// DSiWare: "public.sav" size
|
||||
/// </summary>
|
||||
public uint DSiWarePublicSavSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// DSiWare: "private.sav" size
|
||||
/// </summary>
|
||||
public uint DSiWarePrivateSavSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved (zero)
|
||||
/// </summary>
|
||||
public byte[] ReservedZero { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unknown (used by DSi)
|
||||
/// </summary>
|
||||
public byte[] Unknown3 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 (with encrypted secure area) SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] ARM9WithSecureAreaSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7 SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] ARM7SHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest master SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] DigestMasterSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Banner SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] BannerSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9i (decrypted) SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] ARM9iDecryptedSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM7i (decrypted) SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] ARM7iDecryptedSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved5 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ARM9 (without secure area) SHA1 HMAC hash
|
||||
/// </summary>
|
||||
public byte[] ARM9NoSecureAreaSHA1HMACHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved6 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved and unchecked region, always zero. Used for passing arguments in debug environment.
|
||||
/// </summary>
|
||||
public byte[] ReservedAndUnchecked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// RSA signature (the first 0xE00 bytes of the header are signed with an 1024-bit RSA signature).
|
||||
/// </summary>
|
||||
public byte[] RSASignature { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Read from a stream and get an NDS/NDSi header, if possible
|
||||
/// </summary>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <returns>NDS/NDSi header object, null on error</returns>
|
||||
public static NDSHeader Read(BinaryReader reader)
|
||||
{
|
||||
NDSHeader header = new NDSHeader();
|
||||
|
||||
try
|
||||
{
|
||||
header.GameTitle = reader.ReadChars(0xC);
|
||||
header.Gamecode = reader.ReadUInt32();
|
||||
header.Makercode = reader.ReadChars(2);
|
||||
header.Unitcode = (NDSUnitcode)reader.ReadByte();
|
||||
header.EncryptionSeedSelect = reader.ReadByte();
|
||||
header.Devicecapacity = reader.ReadByte();
|
||||
header.Reserved1 = reader.ReadBytes(7);
|
||||
header.GameRevision = reader.ReadUInt16();
|
||||
header.RomVersion = reader.ReadByte();
|
||||
header.InternalFlags = reader.ReadByte();
|
||||
header.ARM9RomOffset = reader.ReadUInt32();
|
||||
header.ARM9EntryAddress = reader.ReadUInt32();
|
||||
header.ARM9LoadAddress = reader.ReadUInt32();
|
||||
header.ARM9Size = reader.ReadUInt32();
|
||||
header.ARM7RomOffset = reader.ReadUInt32();
|
||||
header.ARM7EntryAddress = reader.ReadUInt32();
|
||||
header.ARM7LoadAddress = reader.ReadUInt32();
|
||||
header.ARM7Size = reader.ReadUInt32();
|
||||
header.FileNameTableOffset = reader.ReadUInt32();
|
||||
header.FileNameTableLength = reader.ReadUInt32();
|
||||
header.FileAllocationTableOffset = reader.ReadUInt32();
|
||||
header.FileAllocationTableLength = reader.ReadUInt32();
|
||||
header.ARM9OverlayOffset = reader.ReadUInt32();
|
||||
header.ARM9OverlayLength = reader.ReadUInt32();
|
||||
header.ARM7OverlayOffset = reader.ReadUInt32();
|
||||
header.ARM7OverlayLength = reader.ReadUInt32();
|
||||
header.SecureDisable = reader.ReadBytes(8);
|
||||
header.NTRRegionRomSize = reader.ReadUInt32();
|
||||
header.HeaderSize = reader.ReadUInt32();
|
||||
header.Reserved2 = reader.ReadBytes(56);
|
||||
header.NintendoLogo = reader.ReadBytes(156);
|
||||
header.NintendoLogoCRC = reader.ReadUInt16();
|
||||
header.DebuggerReserved = reader.ReadBytes(0x20);
|
||||
|
||||
// If we have a DSi compatible title
|
||||
if (header.Unitcode == NDSUnitcode.NDSPlusDSi
|
||||
|| header.Unitcode == NDSUnitcode.DSi)
|
||||
{
|
||||
header.GlobalMBK15Settings = reader.ReadBytes(20);
|
||||
header.LocalMBK68SettingsARM9 = reader.ReadBytes(12);
|
||||
header.LocalMBK68SettingsARM7 = reader.ReadBytes(12);
|
||||
header.GlobalMBK9Setting = reader.ReadBytes(4);
|
||||
header.RegionFlags = reader.ReadBytes(4);
|
||||
header.AccessControl = reader.ReadBytes(4);
|
||||
header.ARM7SCFGEXTMask = reader.ReadBytes(4);
|
||||
header.ReservedFlags = reader.ReadBytes(4);
|
||||
header.ARM9iRomOffset = reader.ReadUInt32();
|
||||
header.Reserved3 = reader.ReadBytes(4);
|
||||
header.ARM9iLoadAddress = reader.ReadUInt32();
|
||||
header.ARM9iSize = reader.ReadUInt32();
|
||||
header.ARM7iRomOffset = reader.ReadUInt32();
|
||||
header.Reserved4 = reader.ReadBytes(4);
|
||||
header.ARM7iLoadAddress = reader.ReadUInt32();
|
||||
header.ARM7iSize = reader.ReadUInt32();
|
||||
header.DigestNTRRegionOffset = reader.ReadUInt32();
|
||||
header.DigestNTRRegionLength = reader.ReadUInt32();
|
||||
header.DigestTWLRegionOffset = reader.ReadUInt32();
|
||||
header.DigestTWLRegionLength = reader.ReadUInt32();
|
||||
header.DigestSectorHashtableRegionOffset = reader.ReadUInt32();
|
||||
header.DigestSectorHashtableRegionLength = reader.ReadUInt32();
|
||||
header.DigestBlockHashtableRegionOffset = reader.ReadUInt32();
|
||||
header.DigestBlockHashtableRegionLength = reader.ReadUInt32();
|
||||
header.DigestSectorSize = reader.ReadUInt32();
|
||||
header.DigestBlockSectorCount = reader.ReadUInt32();
|
||||
header.IconBannerSize = reader.ReadUInt32();
|
||||
header.Unknown1 = reader.ReadBytes(4);
|
||||
header.ModcryptArea1Offset = reader.ReadUInt32();
|
||||
header.ModcryptArea1Size = reader.ReadUInt32();
|
||||
header.ModcryptArea2Offset = reader.ReadUInt32();
|
||||
header.ModcryptArea2Size = reader.ReadUInt32();
|
||||
header.TitleID = reader.ReadBytes(8);
|
||||
header.DSiWarePublicSavSize = reader.ReadUInt32();
|
||||
header.DSiWarePrivateSavSize = reader.ReadUInt32();
|
||||
header.ReservedZero = reader.ReadBytes(176);
|
||||
header.Unknown2 = reader.ReadBytes(0x10);
|
||||
header.ARM9WithSecureAreaSHA1HMACHash = reader.ReadBytes(20);
|
||||
header.ARM7SHA1HMACHash = reader.ReadBytes(20);
|
||||
header.DigestMasterSHA1HMACHash = reader.ReadBytes(20);
|
||||
header.BannerSHA1HMACHash = reader.ReadBytes(20);
|
||||
header.ARM9iDecryptedSHA1HMACHash = reader.ReadBytes(20);
|
||||
header.ARM7iDecryptedSHA1HMACHash = reader.ReadBytes(20);
|
||||
header.Reserved5 = reader.ReadBytes(40);
|
||||
header.ARM9NoSecureAreaSHA1HMACHash = reader.ReadBytes(20);
|
||||
header.Reserved6 = reader.ReadBytes(2636);
|
||||
header.ReservedAndUnchecked = reader.ReadBytes(0x180);
|
||||
header.RSASignature = reader.ReadBytes(0x80);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
NDecrypt/NDecrypt.csproj
Normal file
15
NDecrypt/NDecrypt.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win10-x64;win7-x86</RuntimeIdentifiers>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.NetCore" Version="1.8.8" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
142
NDecrypt/Program.cs
Normal file
142
NDecrypt/Program.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using NDecrypt.N3DS;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
DisplayHelp("Not enough arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
bool? encrypt;
|
||||
if (args[0] == "decrypt" || args[0] == "d")
|
||||
{
|
||||
encrypt = false;
|
||||
}
|
||||
else if (args[0] == "encrypt" || args[0] == "e")
|
||||
{
|
||||
encrypt = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayHelp($"Invalid operation: {args[0]}");
|
||||
return;
|
||||
}
|
||||
|
||||
bool development = false, force = false;
|
||||
int start = 1;
|
||||
for ( ; start < args.Length; start++)
|
||||
{
|
||||
if (args[start] == "-dev" || args[start] == "--development")
|
||||
development = true;
|
||||
|
||||
else if (args[start] == "-f" || args[start] == "--force")
|
||||
force = true;
|
||||
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure the constants are all set
|
||||
new Constants();
|
||||
if (!Constants.IsReady)
|
||||
{
|
||||
Console.WriteLine("Could not read keys from keys.bin. Please make sure the file exists and try again");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = start; i < args.Length; i++)
|
||||
{
|
||||
if (File.Exists(args[i]))
|
||||
{
|
||||
ITool tool = DeriveTool(args[i], encrypt.Value, development, force);
|
||||
if (tool?.ProcessFile() != true)
|
||||
Console.WriteLine("Processing failed!");
|
||||
}
|
||||
else if (Directory.Exists(args[i]))
|
||||
{
|
||||
foreach (string file in Directory.EnumerateFiles(args[i], "*", SearchOption.AllDirectories))
|
||||
{
|
||||
ITool tool = DeriveTool(file, encrypt.Value, development, force);
|
||||
if (tool?.ProcessFile() != true)
|
||||
Console.WriteLine("Processing failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("Press Enter to Exit...");
|
||||
Console.Read();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display a basic help text
|
||||
/// </summary>
|
||||
/// <param name="err">Additional error text to display, can be null to ignore</param>
|
||||
private static void DisplayHelp(string err = null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(err))
|
||||
Console.WriteLine($"Error: {err}");
|
||||
|
||||
Console.WriteLine("Usage: NDecrypt.exe (decrypt|encrypt) [-dev] [-f] <file|dir> ...");
|
||||
}
|
||||
|
||||
private enum RomType
|
||||
{
|
||||
NULL,
|
||||
NDS,
|
||||
NDSi,
|
||||
N3DS,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Derive the encryption tool to be used for the given file
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to derive the tool from</param>
|
||||
/// <param name="encrypt">True if we are encrypting the file, false otherwise</param>
|
||||
/// <param name="development">True if we are using development keys, false otherwise</param>
|
||||
/// <param name="force">True if operations should be forced, false otherwise</param>
|
||||
/// <returns></returns>
|
||||
private static ITool DeriveTool(string filename, bool encrypt, bool development, bool force)
|
||||
{
|
||||
RomType type = DetermineRomType(filename);
|
||||
switch(type)
|
||||
{
|
||||
case RomType.NDS:
|
||||
case RomType.NDSi:
|
||||
return new DSTool(filename, encrypt, force);
|
||||
case RomType.N3DS:
|
||||
return new ThreeDSTool(filename, development, encrypt, force);
|
||||
case RomType.NULL:
|
||||
default:
|
||||
Console.WriteLine($"Unrecognized file format for {filename}. Expected *.nds, *.srl, *.dsi, *.3ds");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the rom type from the filename extension
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to derive the type from</param>
|
||||
/// <returns>RomType value, if possible</returns>
|
||||
private static RomType DetermineRomType(string filename)
|
||||
{
|
||||
if (filename.EndsWith(".nds", StringComparison.OrdinalIgnoreCase)
|
||||
|| filename.EndsWith(".srl", StringComparison.OrdinalIgnoreCase))
|
||||
return RomType.NDS;
|
||||
|
||||
else if (filename.EndsWith(".dsi", StringComparison.OrdinalIgnoreCase))
|
||||
return RomType.NDSi;
|
||||
|
||||
else if (filename.EndsWith(".3ds", StringComparison.OrdinalIgnoreCase))
|
||||
return RomType.N3DS;
|
||||
|
||||
return RomType.NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
584
NDecrypt/ThreeDSTool.cs
Normal file
584
NDecrypt/ThreeDSTool.cs
Normal file
@@ -0,0 +1,584 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using NDecrypt.N3DS;
|
||||
using NDecrypt.N3DS.Headers;
|
||||
|
||||
namespace NDecrypt
|
||||
{
|
||||
public class ThreeDSTool : ITool
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the input 3DS file
|
||||
/// </summary>
|
||||
private readonly string filename;
|
||||
|
||||
/// <summary>
|
||||
/// Flag to detrmine if development keys should be used
|
||||
/// </summary>
|
||||
private readonly bool development;
|
||||
|
||||
/// <summary>
|
||||
/// Flag to determine if encrypting or decrypting
|
||||
/// </summary>
|
||||
private readonly bool encrypt;
|
||||
|
||||
/// <summary>
|
||||
/// Flag to determine if forcing operations
|
||||
/// </summary>
|
||||
private readonly bool force;
|
||||
|
||||
public ThreeDSTool(string filename, bool development, bool encrypt, bool force)
|
||||
{
|
||||
this.filename = filename;
|
||||
this.development = development;
|
||||
this.encrypt = encrypt;
|
||||
this.force = force;
|
||||
}
|
||||
|
||||
#region Common Methods
|
||||
|
||||
/// <summary>
|
||||
/// Process an input file given the input values
|
||||
/// </summary>
|
||||
public bool ProcessFile()
|
||||
{
|
||||
// Make sure we have a file to process first
|
||||
Console.WriteLine(filename);
|
||||
if (!File.Exists(filename))
|
||||
return false;
|
||||
|
||||
// Open the read and write on the same file for inplace processing
|
||||
using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
using (BinaryWriter writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)))
|
||||
{
|
||||
NCSDHeader header = NCSDHeader.Read(reader, development);
|
||||
if (header == null)
|
||||
{
|
||||
Console.WriteLine("Error: Not a 3DS Rom!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Process all 8 NCCH partitions
|
||||
ProcessAllPartitions(header, reader, writer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process all partitions in the partition table of an NCSD header
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void ProcessAllPartitions(NCSDHeader ncsdHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
// Iterate over all 8 NCCH partitions
|
||||
for (int p = 0; p < 8; p++)
|
||||
{
|
||||
NCCHHeader ncchHeader = GetPartitionHeader(ncsdHeader, reader, p);
|
||||
if (ncchHeader == null)
|
||||
continue;
|
||||
|
||||
ProcessPartition(ncsdHeader, ncchHeader, reader, writer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a specific partition header from the partition table
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="partitionNumber">Partition number to attempt to retrieve</param>
|
||||
/// <returns>NCCH header for the partition requested, null on error</returns>
|
||||
private NCCHHeader GetPartitionHeader(NCSDHeader ncsdHeader, BinaryReader reader, int partitionNumber)
|
||||
{
|
||||
if (!ncsdHeader.PartitionsTable[partitionNumber].IsValid())
|
||||
{
|
||||
Console.WriteLine($"Partition {partitionNumber} Not found... Skipping...");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Seek to the beginning of the NCCH partition
|
||||
reader.BaseStream.Seek((ncsdHeader.PartitionsTable[partitionNumber].Offset * ncsdHeader.MediaUnitSize), SeekOrigin.Begin);
|
||||
|
||||
NCCHHeader partitionHeader = NCCHHeader.Read(reader, true);
|
||||
if (partitionHeader == null)
|
||||
{
|
||||
Console.WriteLine($"Partition {partitionNumber} Unable to read NCCH header");
|
||||
return null;
|
||||
}
|
||||
|
||||
partitionHeader.PartitionNumber = partitionNumber;
|
||||
partitionHeader.Entry = ncsdHeader.PartitionsTable[partitionNumber];
|
||||
return partitionHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a single partition
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void ProcessPartition(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
// If we're forcing the operation, tell the user
|
||||
if (force)
|
||||
{
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} is not verified due to force flag being set.");
|
||||
}
|
||||
// If we're not forcing the operation, check if the 'NoCrypto' bit is set
|
||||
else if (ncchHeader.Flags.PossblyDecrypted ^ encrypt)
|
||||
{
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber}: Already " + (encrypt ? "Encrypted" : "Decrypted") + "?...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the Keys to be used
|
||||
SetEncryptionKeys(ncsdHeader, ncchHeader);
|
||||
|
||||
// Process the extended header
|
||||
ProcessExtendedHeader(ncsdHeader, ncchHeader, reader, writer);
|
||||
|
||||
// If we're encrypting, encrypt the filesystems and update the flags
|
||||
if (encrypt)
|
||||
{
|
||||
EncryptExeFS(ncsdHeader, ncchHeader, reader, writer);
|
||||
EncryptRomFS(ncsdHeader, ncchHeader, reader, writer);
|
||||
UpdateEncryptCryptoAndMasks(ncsdHeader, ncchHeader, writer);
|
||||
}
|
||||
|
||||
// If we're decrypting, decrypt the filesystems and update the flags
|
||||
else
|
||||
{
|
||||
DecryptExeFS(ncsdHeader, ncchHeader, reader, writer);
|
||||
DecryptRomFS(ncsdHeader, ncchHeader, reader, writer);
|
||||
UpdateDecryptCryptoAndMasks(ncsdHeader, ncchHeader, writer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the set of keys to be used for encryption or decryption
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
private void SetEncryptionKeys(NCSDHeader ncsdHeader, NCCHHeader ncchHeader)
|
||||
{
|
||||
ncchHeader.KeyX = 0;
|
||||
ncchHeader.KeyX2C = development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C;
|
||||
|
||||
// Backup headers can't have a KeyY value set
|
||||
if (ncchHeader.RSA2048Signature != null)
|
||||
ncchHeader.KeyY = new BigInteger(ncchHeader.RSA2048Signature.Take(16).Reverse().ToArray());
|
||||
else
|
||||
ncchHeader.KeyY = new BigInteger(0);
|
||||
|
||||
ncchHeader.NormalKey = 0;
|
||||
ncchHeader.NormalKey2C = Helper.RotateLeft((Helper.RotateLeft(ncchHeader.KeyX2C, 2, 128) ^ ncchHeader.KeyY) + Constants.AESHardwareConstant, 87, 128);
|
||||
|
||||
// Set the header to use based on mode
|
||||
BitMasks masks;
|
||||
CryptoMethod method;
|
||||
if (encrypt)
|
||||
{
|
||||
masks = ncsdHeader.BackupHeader.Flags.BitMasks;
|
||||
method = ncsdHeader.BackupHeader.Flags.CryptoMethod;
|
||||
}
|
||||
else
|
||||
{
|
||||
masks = ncchHeader.Flags.BitMasks;
|
||||
method = ncchHeader.Flags.CryptoMethod;
|
||||
}
|
||||
|
||||
if (masks.HasFlag(BitMasks.FixedCryptoKey))
|
||||
{
|
||||
ncchHeader.NormalKey = 0x00;
|
||||
ncchHeader.NormalKey2C = 0x00;
|
||||
Console.WriteLine("Encryption Method: Zero Key");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (method == CryptoMethod.Original)
|
||||
{
|
||||
ncchHeader.KeyX = development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C;
|
||||
Console.WriteLine("Encryption Method: Key 0x2C");
|
||||
}
|
||||
else if (method == CryptoMethod.Seven)
|
||||
{
|
||||
ncchHeader.KeyX = development ? Constants.DevKeyX0x25 : Constants.KeyX0x25;
|
||||
Console.WriteLine("Encryption Method: Key 0x25");
|
||||
}
|
||||
else if (method == CryptoMethod.NineThree)
|
||||
{
|
||||
ncchHeader.KeyX = development ? Constants.DevKeyX0x18 : Constants.KeyX0x18;
|
||||
Console.WriteLine("Encryption Method: Key 0x18");
|
||||
}
|
||||
else if (method == CryptoMethod.NineSix)
|
||||
{
|
||||
ncchHeader.KeyX = development ? Constants.DevKeyX0x1B : Constants.KeyX0x1B;
|
||||
Console.WriteLine("Encryption Method: Key 0x1B");
|
||||
}
|
||||
|
||||
ncchHeader.NormalKey = Helper.RotateLeft((Helper.RotateLeft(ncchHeader.KeyX, 2, 128) ^ ncchHeader.KeyY) + Constants.AESHardwareConstant, 87, 128);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the extended header, if it exists
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private bool ProcessExtendedHeader(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
if (ncchHeader.ExtendedHeaderSizeInBytes > 0)
|
||||
{
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x200, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x200, SeekOrigin.Begin);
|
||||
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");
|
||||
|
||||
var cipher = Helper.CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.PlainIV, encrypt);
|
||||
writer.Write(cipher.ProcessBytes(reader.ReadBytes(Constants.CXTExtendedDataHeaderLength)));
|
||||
writer.Flush();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: No Extended Header... Skipping...");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the extended header, if it exists
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void ProcessExeFSFileEntries(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
ExeFSHeader exefsHeader = ExeFSHeader.Read(reader);
|
||||
|
||||
// If the header failed to read, log and return
|
||||
if (exefsHeader == null)
|
||||
{
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS header could not be read. Skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (ExeFSFileHeader fileHeader in exefsHeader.FileHeaders)
|
||||
{
|
||||
// Only decrypt a file if it's a code binary
|
||||
if (!fileHeader.IsCodeBinary)
|
||||
continue;
|
||||
|
||||
uint datalenM = ((fileHeader.FileSize) / (1024 * 1024));
|
||||
uint datalenB = ((fileHeader.FileSize) % (1024 * 1024));
|
||||
uint ctroffset = ((fileHeader.FileOffset + ncsdHeader.MediaUnitSize) / 0x10);
|
||||
|
||||
byte[] exefsIVWithOffsetForHeader = Helper.AddToByteArray(ncchHeader.ExeFSIV, (int)ctroffset);
|
||||
|
||||
var firstCipher = Helper.CreateAESCipher(ncchHeader.NormalKey, exefsIVWithOffsetForHeader, encrypt);
|
||||
var secondCipher = Helper.CreateAESCipher(ncchHeader.NormalKey2C, exefsIVWithOffsetForHeader, !encrypt);
|
||||
|
||||
reader.BaseStream.Seek((((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * ncsdHeader.MediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * ncsdHeader.MediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin);
|
||||
|
||||
if (datalenM > 0)
|
||||
{
|
||||
for (int i = 0; i < datalenM; i++)
|
||||
{
|
||||
writer.Write(secondCipher.ProcessBytes(firstCipher.ProcessBytes(reader.ReadBytes(1024 * 1024))));
|
||||
writer.Flush();
|
||||
Console.Write($"\rPartition {ncchHeader.PartitionNumber} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.ReadableFileName}... {i} / {datalenM + 1} mb...");
|
||||
}
|
||||
}
|
||||
|
||||
if (datalenB > 0)
|
||||
{
|
||||
writer.Write(secondCipher.DoFinal(firstCipher.DoFinal(reader.ReadBytes((int)datalenB))));
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
Console.Write($"\rPartition {ncchHeader.PartitionNumber} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.ReadableFileName}... {datalenM + 1} / {datalenM + 1} mb... Done!\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the ExeFS Filename Table
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void ProcessExeFSFilenameTable(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");
|
||||
|
||||
var exeFSFilenameTable = Helper.CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.ExeFSIV, encrypt);
|
||||
writer.Write(exeFSFilenameTable.ProcessBytes(reader.ReadBytes((int)ncsdHeader.MediaUnitSize)));
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the ExeFS, if it exists
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void ProcessExeFS(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
int exefsSizeM = (int)((long)((ncchHeader.ExeFSSizeInMediaUnits - 1) * ncsdHeader.MediaUnitSize) / (1024 * 1024));
|
||||
int exefsSizeB = (int)((long)((ncchHeader.ExeFSSizeInMediaUnits - 1) * ncsdHeader.MediaUnitSize) % (1024 * 1024));
|
||||
int ctroffsetE = (int)(ncsdHeader.MediaUnitSize / 0x10);
|
||||
|
||||
byte[] exefsIVWithOffset = Helper.AddToByteArray(ncchHeader.ExeFSIV, ctroffsetE);
|
||||
|
||||
var exeFS = Helper.CreateAESCipher(ncchHeader.NormalKey2C, exefsIVWithOffset, encrypt);
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits + 1) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
if (exefsSizeM > 0)
|
||||
{
|
||||
for (int i = 0; i < exefsSizeM; i++)
|
||||
{
|
||||
writer.Write(exeFS.ProcessBytes(reader.ReadBytes(1024 * 1024)));
|
||||
writer.Flush();
|
||||
Console.Write($"\rPartition {ncchHeader.PartitionNumber} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {i} / {exefsSizeM + 1} mb");
|
||||
}
|
||||
}
|
||||
if (exefsSizeB > 0)
|
||||
{
|
||||
writer.Write(exeFS.DoFinal(reader.ReadBytes(exefsSizeB)));
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
Console.Write($"\rPartition {ncchHeader.PartitionNumber} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + $": {exefsSizeM + 1} / {exefsSizeM + 1} mb... Done!\r\n");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Decrypt
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt the ExeFS, if it exists
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void DecryptExeFS(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
// If the ExeFS size is 0, we log and return
|
||||
if (ncchHeader.ExeFSSizeInMediaUnits == 0)
|
||||
{
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: No Data... Skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Decrypt the filename table
|
||||
ProcessExeFSFilenameTable(ncsdHeader, ncchHeader, reader, writer);
|
||||
|
||||
// For all but the original crypto method, process each of the files in the table
|
||||
if (ncchHeader.Flags.CryptoMethod != CryptoMethod.Original)
|
||||
ProcessExeFSFileEntries(ncsdHeader, ncchHeader, reader, writer);
|
||||
|
||||
// Decrypt the rest of the ExeFS
|
||||
ProcessExeFS(ncsdHeader, ncchHeader, reader, writer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt the RomFS, if it exists
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
/// TODO: See how much can be extracted into a common method with Encrypt
|
||||
private void DecryptRomFS(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
// If the RomFS offset is 0, we log and return
|
||||
if (ncchHeader.RomFSOffsetInMediaUnits == 0)
|
||||
{
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} RomFS: No Data... Skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
long romfsSizeM = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * ncsdHeader.MediaUnitSize) / (1024 * 1024));
|
||||
int romfsSizeB = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * ncsdHeader.MediaUnitSize) % (1024 * 1024));
|
||||
|
||||
var cipher = Helper.CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV, encrypt);
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
if (romfsSizeM > 0)
|
||||
{
|
||||
for (int i = 0; i < romfsSizeM; i++)
|
||||
{
|
||||
writer.Write(cipher.ProcessBytes(reader.ReadBytes(1024 * 1024)));
|
||||
writer.Flush();
|
||||
Console.Write($"\rPartition {ncchHeader.PartitionNumber} RomFS: Decrypting: {i} / {romfsSizeM + 1} mb");
|
||||
}
|
||||
}
|
||||
if (romfsSizeB > 0)
|
||||
{
|
||||
writer.Write(cipher.DoFinal(reader.ReadBytes(romfsSizeB)));
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
Console.Write($"\rPartition {ncchHeader.PartitionNumber} RomFS: Decrypting: {romfsSizeM + 1} / {romfsSizeM + 1} mb... Done!\r\n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the CryptoMethod and BitMasks for the decrypted partition
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void UpdateDecryptCryptoAndMasks(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryWriter writer)
|
||||
{
|
||||
// Write the new CryptoMethod
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
writer.Write((byte)CryptoMethod.Original);
|
||||
writer.Flush();
|
||||
|
||||
// Write the new BitMasks flag
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x18F, SeekOrigin.Begin);
|
||||
BitMasks flag = ncchHeader.Flags.BitMasks;
|
||||
flag &= (BitMasks)((byte)(BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) ^ 0xFF);
|
||||
flag |= BitMasks.NoCrypto;
|
||||
writer.Write((byte)flag);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Encrypt
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt the ExeFS, if it exists
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void EncryptExeFS(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
// If the ExeFS size is 0, we log and return
|
||||
if (ncchHeader.ExeFSSizeInMediaUnits == 0)
|
||||
{
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: No Data... Skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
// For all but the original crypto method, process each of the files in the table
|
||||
if (ncsdHeader.BackupHeader.Flags.CryptoMethod != CryptoMethod.Original)
|
||||
ProcessExeFSFileEntries(ncsdHeader, ncchHeader, reader, writer);
|
||||
|
||||
// Encrypt the filename table
|
||||
ProcessExeFSFilenameTable(ncsdHeader, ncchHeader, reader, writer);
|
||||
|
||||
// Encrypt the rest of the ExeFS
|
||||
ProcessExeFS(ncsdHeader, ncchHeader, reader, writer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt the RomFS, if it exists
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="reader">BinaryReader representing the input stream</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
/// TODO: See how much can be extracted into a common method with Decrypt
|
||||
private void EncryptRomFS(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
|
||||
{
|
||||
// If the RomFS offset is 0, we log and return
|
||||
if (ncchHeader.RomFSOffsetInMediaUnits == 0)
|
||||
{
|
||||
Console.WriteLine($"Partition {ncchHeader.PartitionNumber} RomFS: No Data... Skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
long romfsSizeM = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * ncsdHeader.MediaUnitSize) / (1024 * 1024));
|
||||
int romfsSizeB = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * ncsdHeader.MediaUnitSize) % (1024 * 1024));
|
||||
|
||||
// Encrypting RomFS for partitions 1 and up always use Key0x2C
|
||||
if (ncchHeader.PartitionNumber > 0)
|
||||
{
|
||||
if (ncsdHeader.BackupHeader.Flags?.BitMasks.HasFlag(BitMasks.FixedCryptoKey) == true) // except if using zero-key
|
||||
{
|
||||
ncchHeader.NormalKey = 0x00;
|
||||
}
|
||||
else
|
||||
{
|
||||
ncchHeader.KeyX = (development ? Constants.DevKeyX0x2C : Constants.KeyX0x2C);
|
||||
ncchHeader.NormalKey = Helper.RotateLeft((Helper.RotateLeft(ncchHeader.KeyX, 2, 128) ^ ncchHeader.KeyY) + Constants.AESHardwareConstant, 87, 128);
|
||||
}
|
||||
}
|
||||
|
||||
var cipher = Helper.CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV, encrypt);
|
||||
|
||||
reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * ncsdHeader.MediaUnitSize, SeekOrigin.Begin);
|
||||
if (romfsSizeM > 0)
|
||||
{
|
||||
for (int i = 0; i < romfsSizeM; i++)
|
||||
{
|
||||
writer.Write(cipher.ProcessBytes(reader.ReadBytes(1024 * 1024)));
|
||||
writer.Flush();
|
||||
Console.Write($"\rPartition {ncchHeader.PartitionNumber} RomFS: Encrypting: {i} / {romfsSizeM + 1} mb");
|
||||
}
|
||||
}
|
||||
if (romfsSizeB > 0)
|
||||
{
|
||||
writer.Write(cipher.DoFinal(reader.ReadBytes(romfsSizeB)));
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
Console.Write($"\rPartition {ncchHeader.PartitionNumber} RomFS: Encrypting: {romfsSizeM + 1} / {romfsSizeM + 1} mb... Done!\r\n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the CryptoMethod and BitMasks for the encrypted partition
|
||||
/// </summary>
|
||||
/// <param name="ncsdHeader">NCSD header representing the 3DS file</param>
|
||||
/// <param name="ncchHeader">NCCH header representing the partition</param>
|
||||
/// <param name="writer">BinaryWriter representing the output stream</param>
|
||||
private void UpdateEncryptCryptoAndMasks(NCSDHeader ncsdHeader, NCCHHeader ncchHeader, BinaryWriter writer)
|
||||
{
|
||||
// Write the new CryptoMethod
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x18B, SeekOrigin.Begin);
|
||||
|
||||
// For partitions 1 and up, set crypto-method to 0x00
|
||||
if (ncchHeader.PartitionNumber > 0)
|
||||
writer.Write((byte)CryptoMethod.Original);
|
||||
|
||||
// If partition 0, restore crypto-method from backup flags
|
||||
else
|
||||
writer.Write((byte)ncsdHeader.BackupHeader.Flags.CryptoMethod);
|
||||
|
||||
writer.Flush();
|
||||
|
||||
// Write the new BitMasks flag
|
||||
writer.BaseStream.Seek((ncchHeader.Entry.Offset * ncsdHeader.MediaUnitSize) + 0x18F, SeekOrigin.Begin);
|
||||
BitMasks flag = ncchHeader.Flags.BitMasks;
|
||||
flag &= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF;
|
||||
flag |= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & ncsdHeader.BackupHeader.Flags.BitMasks;
|
||||
writer.Write((byte)flag);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
54
README.md
Normal file
54
README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# NDecrypt
|
||||
|
||||
A simple tool for simple people.
|
||||
|
||||
## What is this?
|
||||
|
||||
This is a code port of 3 different programs:
|
||||
|
||||
- `3ds_encrypt.py`
|
||||
- `3ds_decrypt.py`
|
||||
- `woodsec` (part of [wooddumper](https://github.com/TuxSH/wooddumper))
|
||||
|
||||
## No really, what is this?
|
||||
|
||||
This tool allows you to encrypt and decrypt your personally dumped NDS and N3DS roms with minimal hassle. The only caveat right now is that you need a `keys.bin` file for your personally obtained encryption keys.
|
||||
|
||||
## So how do I use this?
|
||||
|
||||
NDecrypt.exe <flag> [-dev] <file|dir> ...
|
||||
|
||||
Possible values for <flag>:
|
||||
encrypt, e - Encrypt the incoming files
|
||||
decrypt, d - Decrypt the incoming files
|
||||
|
||||
|
||||
**Note:** This overwrites the incoming files, so make backups if you're working on your original, personal dumps.
|
||||
|
||||
**Note:** Mixed folders or inputs are also accepted, you can decrypt or encrypt multiple files, regardless of their type. This being said, you can only do encrypt OR decrypt at one time.
|
||||
|
||||
## I feel like something is missing...
|
||||
|
||||
You are! In fact, you may be asking, "Hey, what was that `keys.bin` you mentioned??". I'm glad you asked. It's used only for N3DS files. Since some people don't like reading code, you need the 9 16-bit keys in little endian format (most common extraction methods produce big endian, so keep that in mind). It's recommended that you fill with 0x00 if you don't have access to a particular value so it doesn't mess up the read. They need to be in the following order:
|
||||
|
||||
- Hardware constant
|
||||
- KeyX0x10
|
||||
- KeyX0x1B
|
||||
- KeyX0x25
|
||||
- KeyX0x2C
|
||||
- DevKeyX0x10
|
||||
- DevKeyX0x1B
|
||||
- DevKeyX0x25
|
||||
- DevKeyX0x2C
|
||||
|
||||
The last 4 are only required if you use the `-dev` flag. Once again, don't ask for these, please. If you're missing a required key, then things won't work. Don't blame me, blame society. Or something.
|
||||
|
||||
## Anything else?
|
||||
|
||||
I'd like to thank the developers of the original programs for doing the actual hard work to figure things out. I'd also like to thank everyone who helped to test this against the original programs and made code suggestions.
|
||||
|
||||
Unofficially, this is entirely, 100% FOSS, no strings attached. I keep forgetting what license that is.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This program is **ONLY** for use with personally dumped files and keys and is not meant for enabling illegal activity. I do not condone using this program for anything other than personal use and research. If this program is used for anything other than that, I cannot be held liable for anything that happens.
|
||||
Reference in New Issue
Block a user