9 Commits
0.2.1 ... 0.2.2

Author SHA1 Message Date
Matt Nadareski
7415a21d5c Try/catch blocks around file processing 2020-12-17 11:30:34 -08:00
Matt Nadareski
f349389994 keys.bin is only needed for 3DS, better docs 2020-12-17 11:08:53 -08:00
Matt Nadareski
a046bd4152 Remove forced pause 2020-12-17 10:39:30 -08:00
Matt Nadareski
51a8f0c9df Report new compatibility information 2020-12-17 10:37:34 -08:00
Matt Nadareski
0449c16a01 Yet another empty one 2020-12-16 19:39:33 -08:00
Matt Nadareski
7638d7dbf8 One more decrypted empty secure area 2020-12-16 16:27:39 -08:00
Matt Nadareski
387bf46e5a Fix incorrect matching criteria 2020-12-16 16:07:45 -08:00
Matt Nadareski
6d257dc1e3 Add iQue extension for NDS 2020-12-16 13:15:49 -08:00
Matt Nadareski
91d816e359 Handle PoP cases, add compatibility to README 2020-12-16 11:57:55 -08:00
5 changed files with 105 additions and 55 deletions

View File

@@ -47,22 +47,31 @@ namespace NDecrypt
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)))
try
{
NDSHeader header = NDSHeader.Read(reader);
if (header == null)
// 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)))
{
Console.WriteLine("Error: Not a DS or DSi Rom!");
return false;
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);
}
// Process the secure area
ProcessSecureArea(header, reader, writer);
return true;
}
catch
{
Console.WriteLine($"An error has occurred. {filename} may be corrupted if it was partially processed.");
Console.WriteLine("Please check that the file was a valid DS or DSi file and try again.");
return false;
}
return true;
}
/// <summary>
@@ -119,7 +128,10 @@ namespace NDecrypt
// Improperly decrypted empty secure area (decrypt empty with woodsec)
else if ((firstValue == 0xE386C397 && secondValue == 0x82775B7E)
|| (firstValue == 0xF98415B8 && secondValue == 0x698068FC))
|| (firstValue == 0xF98415B8 && secondValue == 0x698068FC)
|| (firstValue == 0xA71329EE && secondValue == 0x2A1D4C38)
|| (firstValue == 0xC44DCC48 && secondValue == 0x38B6F8CB)
|| (firstValue == 0x3A9323B5 && secondValue == 0xC0387241))
{
Console.WriteLine("Improperly decrypted empty secure area found. Should be encrypted to get proper value.");
return true;
@@ -136,7 +148,9 @@ namespace NDecrypt
// 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)
|| (firstValue == 0x7829BC8D && secondValue == 0x9968EF44) // Dragon Quest 5 (JP)
|| (firstValue == 0xC4A15AB8 && secondValue == 0xD2E667C8) // Prince of Persia (EU)
|| (firstValue == 0xD5E97D20 && secondValue == 0x21B2A159)) // Prince of Persia (USA)
{
Console.WriteLine("Decrypted secure area for known, nonstandard value found.");
return true;
@@ -149,13 +163,6 @@ namespace NDecrypt
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;
}

View File

@@ -51,14 +51,18 @@ namespace NDecrypt.N3DS
/// <summary>
/// Represents if all of the keys have been initialized properly
/// </summary>
public static bool IsReady { get; private set; }
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()
public static void Init()
{
// If we're already attempted to set the constants, don't try to again
if (IsReady != null)
return;
string keyfile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "keys.bin");
if (!File.Exists(keyfile))
{

View File

@@ -1,6 +1,5 @@
using System;
using System.IO;
using NDecrypt.N3DS;
namespace NDecrypt
{
@@ -43,14 +42,6 @@ namespace NDecrypt
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]))
@@ -68,10 +59,11 @@ namespace NDecrypt
Console.WriteLine("Processing failed!");
}
}
else
{
Console.WriteLine($"{args[i]} is not a file or folder. Please check your spelling and formatting and try again.");
}
}
Console.WriteLine("Press Enter to Exit...");
Console.Read();
}
/// <summary>
@@ -83,7 +75,18 @@ namespace NDecrypt
if (!string.IsNullOrWhiteSpace(err))
Console.WriteLine($"Error: {err}");
Console.WriteLine("Usage: NDecrypt.exe (decrypt|encrypt) [-dev] [-f] <file|dir> ...");
Console.WriteLine(@"Usage: NDecrypt.exe <opeation> [flags] <path> ...
Possible values for <operation>:
e, encrypt - Encrypt the input files
d, decrypt - Decrypt the input files
Possible values for [flags] (one or more can be used):
-dev, --development - Enable using development keys, if available
-f, --force - Force operation by avoiding sanity checks
<path> can be any file or folder that contains uncompressed items.
More than one path can be specified at a time.");
}
private enum RomType
@@ -126,8 +129,9 @@ namespace NDecrypt
/// <returns>RomType value, if possible</returns>
private static RomType DetermineRomType(string filename)
{
if (filename.EndsWith(".nds", StringComparison.OrdinalIgnoreCase)
|| filename.EndsWith(".srl", StringComparison.OrdinalIgnoreCase))
if (filename.EndsWith(".nds", StringComparison.OrdinalIgnoreCase) // Standard carts
|| filename.EndsWith(".srl", StringComparison.OrdinalIgnoreCase) // Development carts/images
|| filename.EndsWith(".ids", StringComparison.OrdinalIgnoreCase)) // iQue DS Carts
return RomType.NDS;
else if (filename.EndsWith(".dsi", StringComparison.OrdinalIgnoreCase))

View File

@@ -44,27 +44,44 @@ namespace NDecrypt
/// </summary>
public bool ProcessFile()
{
// Ensure the constants are all set
Constants.Init();
if (Constants.IsReady != true)
{
Console.WriteLine("Could not read keys from keys.bin. Please make sure the file exists and try again.");
return false;
}
// 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)))
try
{
NCSDHeader header = NCSDHeader.Read(reader, development);
if (header == null)
// 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)))
{
Console.WriteLine("Error: Not a 3DS Rom!");
return false;
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);
}
// Process all 8 NCCH partitions
ProcessAllPartitions(header, reader, writer);
return true;
}
catch
{
Console.WriteLine($"An error has occurred. {filename} may be corrupted if it was partially processed.");
Console.WriteLine("Please check that the file was a valid 3DS or New 3DS file and try again.");
return false;
}
return true;
}
/// <summary>

View File

@@ -16,20 +16,27 @@ This tool allows you to encrypt and decrypt your personally dumped NDS and N3DS
## So how do I use this?
NDecrypt.exe <flag> [-dev] <file|dir> ...
NDecrypt.exe <operation> [flags] <path> ...
Possible values for <flag>:
encrypt, e - Encrypt the incoming files
decrypt, d - Decrypt the incoming files
Possible values for <operation>:
e, encrypt - Encrypt the input files
d, decrypt - Decrypt the input files
Possible values for [flags] (one or more can be used):
-dev, --development - Enable using development keys, if available
-f, --force - Force operation by avoiding sanity checks
<path> can be any file or folder that contains uncompressed items.
More than one path can be specified at a time.
**Note:** This overwrites the incoming files, so make backups if you're working on your original, personal dumps.
**Note:** This overwrites the input 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:
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 Nintendo 3DS and New 3DS 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
@@ -41,7 +48,18 @@ You are! In fact, you may be asking, "Hey, what was that `keys.bin` you mentione
- 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.
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. And yes, I'll fix this being required across the board at some point.
## But does it work?
As much as I'd like to think that this program is entirely without flaws, numbers need to speak for themselves sometimes. Here's a list of the supported sets and their current compatibility percentages with woodsec and the Python scripts (as of 2020-12-16):
- **Nintendo DS** - >99% compatible (Both encryption and decryption)
- **Nintendo DSi** - 100% compatible (Both encryption and decryption)
- **Nintendo 3DS** - 100% compatible (Decryption only, encryption is currently being tested)
- **Nintendo New 3DS** - 100% compatible (Both encryption and decryption)
Please note the above numbers are based on the current, documented values, not necessarily what is correct. For example, in the Nintendo DS set, there are issues with prototype and unlicensed cartridges, all of which have strange, potentially hacky values in their secure areas.
## Anything else?