Exemple #1
0
        /// <summary>
        /// Process an input file given the input values
        /// </summary>
        public bool ProcessFile()
        {
            // Ensure the constants are all set
            if (decryptArgs.IsReady != true)
            {
                Console.WriteLine("Could not read keys. Please make sure the file exists and try again.");
                return(false);
            }

            try
            {
                // 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)))
                    {
                        CIAHeader header = CIAHeader.Read(reader);
                        if (header == null)
                        {
                            Console.WriteLine("Error: Not a 3DS CIA!");
                            return(false);
                        }

                        // Process all NCCH partitions
                        ProcessAllPartitions(header, reader, writer);
                    }

                return(false);
            }
            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 CIA file and try again.");
                return(false);
            }
        }
Exemple #2
0
        /// <summary>
        /// Update the CryptoMethod and BitMasks for the encrypted partition
        /// </summary>
        /// <param name="ciaHeader">CIA header representing the 3DS CIA file</param>
        /// <param name="ncchHeader">NCCH header representing the partition</param>
        /// <param name="writer">BinaryWriter representing the output stream</param>
        private void UpdateEncryptCryptoAndMasks(CIAHeader ciaHeader, NCCHHeader ncchHeader, BinaryWriter writer)
        {
            // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value?
            uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize;

            // Write the new CryptoMethod
            writer.BaseStream.Seek((ncchHeader.Entry.Offset * mediaUnitSize) + 0x18B, SeekOrigin.Begin);

            // For partitions 1 and up, set crypto-method to 0x00
            if (ncchHeader.PartitionNumber > 0)
            {
                writer.Write((byte)CryptoMethod.Original);
            }

            // TODO: Determine how to figure out the original crypto method, if possible
            // If partition 0, restore crypto-method from backup flags
            //else
            //    writer.Write((byte)ciaHeader.BackupHeader.Flags.CryptoMethod);

            writer.Flush();

            // Write the new BitMasks flag
            writer.BaseStream.Seek((ncchHeader.Entry.Offset * mediaUnitSize) + 0x18F, SeekOrigin.Begin);
            BitMasks flag = ncchHeader.Flags.BitMasks;

            flag &= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator | BitMasks.NoCrypto) ^ (BitMasks)0xFF;

            // TODO: Determine how to figure out the original crypto method, if possible
            //flag |= (BitMasks.FixedCryptoKey | BitMasks.NewKeyYGenerator) & ciaHeader.BackupHeader.Flags.BitMasks;
            writer.Write((byte)flag);
            writer.Flush();
        }
Exemple #3
0
 /// <summary>
 /// Process all partitions in the content file data of a CIA header
 /// </summary>
 /// <param name="ciaHeader">CIA header representing the 3DS CIA file</param>
 /// <param name="reader">BinaryReader representing the input stream</param>
 /// <param name="writer">BinaryWriter representing the output stream</param>
 private void ProcessAllPartitions(CIAHeader ciaHeader, BinaryReader reader, BinaryWriter writer)
 {
     // Iterate over all NCCH partitions
     for (int p = 0; p < ciaHeader.Partitions.Length; p++)
     {
         NCCHHeader ncchHeader = ciaHeader.Partitions[0];
         ProcessPartition(ciaHeader, ncchHeader, reader, writer);
     }
 }
Exemple #4
0
        /// <summary>
        /// Process a single partition
        /// </summary>
        /// <param name="ciaHeader">CIA header representing the 3DS CIA 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(CIAHeader ciaHeader, NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
        {
            // If we're forcing the operation, tell the user
            if (decryptArgs.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 ^ decryptArgs.Encrypt)
            {
                Console.WriteLine($"Partition {ncchHeader.PartitionNumber}: Already " + (decryptArgs.Encrypt ? "Encrypted" : "Decrypted") + "?...");
                return;
            }

            // Determine the Keys to be used
            SetEncryptionKeys(ciaHeader, ncchHeader);

            // Process the extended header
            ProcessExtendedHeader(ncchHeader, reader, writer);

            // If we're encrypting, encrypt the filesystems and update the flags
            if (decryptArgs.Encrypt)
            {
                EncryptExeFS(ncchHeader, reader, writer);
                EncryptRomFS(ncchHeader, reader, writer);
                UpdateEncryptCryptoAndMasks(ciaHeader, ncchHeader, writer);
            }

            // If we're decrypting, decrypt the filesystems and update the flags
            else
            {
                DecryptExeFS(ncchHeader, reader, writer);
                DecryptRomFS(ncchHeader, reader, writer);
                UpdateDecryptCryptoAndMasks(ncchHeader, writer);
            }
        }
Exemple #5
0
        /// <summary>
        /// Determine the set of keys to be used for encryption or decryption
        /// </summary>
        /// <param name="ciaHeader">NCSD header representing the 3DS CIA file</param>
        /// <param name="ncchHeader">NCCH header representing the partition</param>
        private void SetEncryptionKeys(CIAHeader ciaHeader, NCCHHeader ncchHeader)
        {
            ncchHeader.KeyX   = 0;
            ncchHeader.KeyX2C = decryptArgs.Development ? decryptArgs.DevKeyX0x2C : decryptArgs.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 = RotateLeft((RotateLeft(ncchHeader.KeyX2C, 2, 128) ^ ncchHeader.KeyY) + decryptArgs.AESHardwareConstant, 87, 128);

            // TODO: Figure out what sane defaults for these values are
            // Set the header to use based on mode
            BitMasks     masks  = BitMasks.NoCrypto;
            CryptoMethod method = CryptoMethod.Original;

            if (decryptArgs.Encrypt)
            {
                // TODO: Can we actually re-encrypt a CIA?
                //masks = ciaHeader.BackupHeader.Flags.BitMasks;
                //method = ciaHeader.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 = decryptArgs.Development ? decryptArgs.DevKeyX0x2C : decryptArgs.KeyX0x2C;
                    Console.WriteLine("Encryption Method: Key 0x2C");
                }
                else if (method == CryptoMethod.Seven)
                {
                    ncchHeader.KeyX = decryptArgs.Development ? decryptArgs.DevKeyX0x25 : decryptArgs.KeyX0x25;
                    Console.WriteLine("Encryption Method: Key 0x25");
                }
                else if (method == CryptoMethod.NineThree)
                {
                    ncchHeader.KeyX = decryptArgs.Development ? decryptArgs.DevKeyX0x18 : decryptArgs.KeyX0x18;
                    Console.WriteLine("Encryption Method: Key 0x18");
                }
                else if (method == CryptoMethod.NineSix)
                {
                    ncchHeader.KeyX = decryptArgs.Development ? decryptArgs.DevKeyX0x1B : decryptArgs.KeyX0x1B;
                    Console.WriteLine("Encryption Method: Key 0x1B");
                }

                ncchHeader.NormalKey = RotateLeft((RotateLeft(ncchHeader.KeyX, 2, 128) ^ ncchHeader.KeyY) + decryptArgs.AESHardwareConstant, 87, 128);
            }
        }