From 72afaeb010dc1c388a2ba85650176211ca71d3d3 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 13 Oct 2024 22:10:59 -0400 Subject: [PATCH] Migrate more to split code --- NDecrypt.N3DS/CommonOperations.cs | 27 +++++ NDecrypt.N3DS/ThreeDSTool.cs | 157 ++++++++++++++++++++---------- 2 files changed, 133 insertions(+), 51 deletions(-) diff --git a/NDecrypt.N3DS/CommonOperations.cs b/NDecrypt.N3DS/CommonOperations.cs index dc10973..7923d40 100644 --- a/NDecrypt.N3DS/CommonOperations.cs +++ b/NDecrypt.N3DS/CommonOperations.cs @@ -325,6 +325,33 @@ namespace NDecrypt.N3DS public static uint GetExeFSSize(NCCHHeader header, uint mediaUnitSize) => header.ExeFSSizeInMediaUnits * mediaUnitSize; + /// + /// Get the size of a partition extended header + /// + /// Size of the partition extended header in bytes, 0 on error + public static uint GetExtendedHeaderSize(Cart cart, int index) + { + // Empty partitions array means no size is available + var partitions = cart.Partitions; + if (partitions == null) + return 0; + + // Invalid partition header means no size is available + var header = partitions[index]; + if (header == null) + return 0; + + // Return the adjusted size + return GetExtendedHeaderSize(header); + } + + /// + /// Get the size of a partition extended header + /// + /// Size of the partition extended header in bytes, 0 on error + public static uint GetExtendedHeaderSize(NCCHHeader header) + => header.ExtendedHeaderSizeInBytes; + /// /// Get the size of a partition RomFS /// diff --git a/NDecrypt.N3DS/ThreeDSTool.cs b/NDecrypt.N3DS/ThreeDSTool.cs index ab02574..5d3713c 100644 --- a/NDecrypt.N3DS/ThreeDSTool.cs +++ b/NDecrypt.N3DS/ThreeDSTool.cs @@ -150,12 +150,10 @@ namespace NDecrypt.N3DS // Determine the Keys to be used SetEncryptionKeys(cart, index, encrypt); - // Process the extended header - ProcessExtendedHeader(cart, index, encrypt, input, output); - // If we're encrypting, encrypt the filesystems and update the flags if (encrypt) { + EncryptExtendedHeader(cart, index, input, output); EncryptExeFS(cart, index, input, output); EncryptRomFS(cart, index, input, output); UpdateEncryptCryptoAndMasks(cart, index, output); @@ -164,6 +162,7 @@ namespace NDecrypt.N3DS // If we're decrypting, decrypt the filesystems and update the flags else { + DecryptExtendedHeader(cart, index, input, output); DecryptExeFS(cart, index, input, output); DecryptRomFS(cart, index, input, output); UpdateDecryptCryptoAndMasks(cart, index, output); @@ -205,54 +204,54 @@ namespace NDecrypt.N3DS KeysMap[index] = new PartitionKeys(_decryptArgs, rsaSignature, masks, method, _development); } - /// - /// Process the extended header, if it exists - /// - /// Cart representing the 3DS file - /// Index of the partition - /// Indicates if the file should be encrypted or decrypted - /// Stream representing the input - /// Stream representing the output - private bool ProcessExtendedHeader(Cart cart, int index, bool encrypt, Stream input, Stream output) - { - // Get required offsets - uint partitionOffsetMU = cart.Header!.PartitionsTable![index]!.Offset; - uint partitionOffset = partitionOffsetMU * cart.MediaUnitSize(); - - if (cart.Partitions![index]!.ExtendedHeaderSizeInBytes > 0) - { - // Seek to the extended header - input.Seek(partitionOffset + 0x200, SeekOrigin.Begin); - output.Seek(partitionOffset + 0x200, SeekOrigin.Begin); - - Console.WriteLine($"Partition {index} ExeFS: " + (encrypt ? "Encrypting" : "Decrypting") + ": ExHeader"); - - // Create the Plain AES cipher for this partition - var cipher = CreateAESCipher(KeysMap[index].NormalKey2C, cart.PlainIV(index), encrypt); - - // Process the extended header - byte[] readBytes = input.ReadBytes(Constants.CXTExtendedDataHeaderLength); - byte[] processedBytes = cipher.ProcessBytes(readBytes); - output.Write(processedBytes); - -#if NET6_0_OR_GREATER - // In .NET 6.0, this operation is not picked up by the reader, so we have to force it to reload its buffer - input.Seek(0, SeekOrigin.Begin); -#endif - output.Flush(); - return true; - } - else - { - Console.WriteLine($"Partition {index} ExeFS: No Extended Header... Skipping..."); - return false; - } - } - #endregion #region Decrypt + /// + /// Decrypt the extended header, if it exists + /// + /// Cart representing the 3DS file + /// Index of the partition + /// Stream representing the input + /// Stream representing the output + private bool DecryptExtendedHeader(Cart cart, int index, Stream input, Stream output) + { + // Get required offsets + uint partitionOffset = GetPartitionOffset(cart, index); + if (partitionOffset == 0) + { + Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping..."); + return false; + } + + uint extHeaderSize = GetExtendedHeaderSize(cart, index); + if (extHeaderSize == 0) + { + Console.WriteLine($"Partition {index} RomFS: No Extended Header... Skipping..."); + return false; + } + + // Seek to the extended header + input.Seek(partitionOffset + 0x200, SeekOrigin.Begin); + output.Seek(partitionOffset + 0x200, SeekOrigin.Begin); + + Console.WriteLine($"Partition {index} ExeFS: Decrypting: ExHeader"); + + // Create the Plain AES cipher for this partition + var cipher = CreateAESDecryptionCipher(KeysMap[index].NormalKey2C, cart.PlainIV(index)); + + // Process the extended header + PerformAESOperation(Constants.CXTExtendedDataHeaderLength, cipher, input, output, null); + +#if NET6_0_OR_GREATER + // In .NET 6.0, this operation is not picked up by the reader, so we have to force it to reload its buffer + input.Seek(0, SeekOrigin.Begin); +#endif + output.Flush(); + return true; + } + /// /// Decrypt the ExeFS, if it exists /// @@ -398,7 +397,7 @@ namespace NDecrypt.N3DS /// Stream representing the output private bool DecryptRomFS(Cart cart, int index, Stream input, Stream output) { - // Get required offsets + // Validate the RomFS uint romFsOffset = GetRomFSOffset(cart, index); if (romFsOffset == 0) { @@ -406,6 +405,13 @@ namespace NDecrypt.N3DS return false; } + uint romFsSize = GetRomFSSize(cart, index); + if (romFsSize == 0) + { + Console.WriteLine($"Partition {index} RomFS: No Data... Skipping..."); + return false; + } + // Seek to the RomFS input.Seek(romFsOffset, SeekOrigin.Begin); output.Seek(romFsOffset, SeekOrigin.Begin); @@ -414,7 +420,6 @@ namespace NDecrypt.N3DS var cipher = CreateAESDecryptionCipher(KeysMap[index].NormalKey, cart.RomFSIV(index)); // Setup and perform the decryption - uint romFsSize = GetRomFSSize(cart, index); PerformAESOperation(romFsSize, cipher, input, @@ -457,6 +462,50 @@ namespace NDecrypt.N3DS #region Encrypt + /// + /// Encrypt the extended header, if it exists + /// + /// Cart representing the 3DS file + /// Index of the partition + /// Stream representing the input + /// Stream representing the output + private bool EncryptExtendedHeader(Cart cart, int index, Stream input, Stream output) + { + // Get required offsets + uint partitionOffset = GetPartitionOffset(cart, index); + if (partitionOffset == 0) + { + Console.WriteLine($"Partition {index} ExeFS: No Data... Skipping..."); + return false; + } + + uint extHeaderSize = GetExtendedHeaderSize(cart, index); + if (extHeaderSize == 0) + { + Console.WriteLine($"Partition {index} RomFS: No Extended Header... Skipping..."); + return false; + } + + // Seek to the extended header + input.Seek(partitionOffset + 0x200, SeekOrigin.Begin); + output.Seek(partitionOffset + 0x200, SeekOrigin.Begin); + + Console.WriteLine($"Partition {index} ExeFS: Encrypting: ExHeader"); + + // Create the Plain AES cipher for this partition + var cipher = CreateAESEncryptionCipher(KeysMap[index].NormalKey2C, cart.PlainIV(index)); + + // Process the extended header + PerformAESOperation(Constants.CXTExtendedDataHeaderLength, cipher, input, output, null); + +#if NET6_0_OR_GREATER + // In .NET 6.0, this operation is not picked up by the reader, so we have to force it to reload its buffer + input.Seek(0, SeekOrigin.Begin); +#endif + output.Flush(); + return true; + } + /// /// Encrypt the ExeFS, if it exists /// @@ -603,7 +652,7 @@ namespace NDecrypt.N3DS /// Stream representing the output private bool EncryptRomFS(Cart cart, int index, Stream input, Stream output) { - // Get required offset + // Validate the RomFS uint romFsOffset = GetRomFSOffset(cart, index); if (romFsOffset == 0) { @@ -611,6 +660,13 @@ namespace NDecrypt.N3DS return false; } + uint romFsSize = GetRomFSSize(cart, index); + if (romFsSize == 0) + { + Console.WriteLine($"Partition {index} RomFS: No Data... Skipping..."); + return false; + } + // Seek to the RomFS input.Seek(romFsOffset, SeekOrigin.Begin); output.Seek(romFsOffset, SeekOrigin.Begin); @@ -635,7 +691,6 @@ namespace NDecrypt.N3DS var cipher = CreateAESEncryptionCipher(KeysMap[index].NormalKey, cart.RomFSIV(index)); // Setup and perform the decryption - uint romFsSize = GetRomFSSize(cart, index); PerformAESOperation(romFsSize, cipher, input,