Esempio n. 1
0
        /// <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 = CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV, decryptArgs.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");
        }
Esempio n. 2
0
        /// <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 = AddToByteArray(ncchHeader.ExeFSIV, ctroffsetE);

            var exeFS = CreateAESCipher(ncchHeader.NormalKey2C, exefsIVWithOffset, decryptArgs.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: " + (decryptArgs.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: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {exefsSizeM + 1} / {exefsSizeM + 1} mb... Done!\r\n");
        }
Esempio n. 3
0
        /// <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();
        }
Esempio n. 4
0
        /// <summary>
        /// Process the extended header, if it exists
        /// </summary>
        /// <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(NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
        {
            // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value?
            uint mediaUnitSize = 0x200; // mediaUnitSize;

            if (ncchHeader.ExtendedHeaderSizeInBytes > 0)
            {
                reader.BaseStream.Seek((ncchHeader.Entry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin);
                writer.BaseStream.Seek((ncchHeader.Entry.Offset * mediaUnitSize) + 0x200, SeekOrigin.Begin);

                Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");

                var cipher = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.PlainIV, decryptArgs.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);
            }

            return(false);
        }
Esempio n. 5
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();
        }
Esempio n. 6
0
        public void ExtractNCCHFromFile(string NCCH_PATH, string outputDirectory, RichTextBox TB_Progress = null, ProgressBar PB_Show = null)
        {
            if (!Directory.Exists(outputDirectory))
            {
                Directory.CreateDirectory(outputDirectory);
            }

            byte[] headerBytes = new byte[0x200];
            using (FileStream fs = new FileStream(NCCH_PATH, FileMode.Open, FileAccess.Read))
            {
                fs.Read(headerBytes, 0, headerBytes.Length);
                Header = new NCCHHeader();
                Header.BuildHeaderFromBytes(headerBytes);

                logo = new byte[Header.LogoSize * MEDIA_UNIT_SIZE];
                fs.Seek(Convert.ToInt32(Header.LogoOffset * MEDIA_UNIT_SIZE), SeekOrigin.Begin);
                fs.Read(logo, 0, logo.Length);

                plainregion = new byte[Header.PlainRegionSize * MEDIA_UNIT_SIZE];
                fs.Seek(Convert.ToInt32(Header.PlainRegionOffset * MEDIA_UNIT_SIZE), SeekOrigin.Begin);
                fs.Read(plainregion, 0, plainregion.Length);
            }

            ExtractExheader(NCCH_PATH, outputDirectory, TB_Progress);
            ExtractExeFS(NCCH_PATH, outputDirectory, TB_Progress);
            ExtractRomFS(NCCH_PATH, outputDirectory, TB_Progress, PB_Show);
        }
Esempio n. 7
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);
     }
 }
Esempio n. 8
0
        /// <summary>
        /// Process the extended header, if it exists
        /// </summary>
        /// <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(NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
        {
            // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value?
            uint mediaUnitSize = 0x200; // mediaUnitSize;

            reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * 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 + mediaUnitSize) / 0x10);

                byte[] exefsIVWithOffsetForHeader = AddToByteArray(ncchHeader.ExeFSIV, (int)ctroffset);

                var firstCipher  = CreateAESCipher(ncchHeader.NormalKey, exefsIVWithOffsetForHeader, decryptArgs.Encrypt);
                var secondCipher = CreateAESCipher(ncchHeader.NormalKey2C, exefsIVWithOffsetForHeader, !decryptArgs.Encrypt);

                reader.BaseStream.Seek((((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * mediaUnitSize) + fileHeader.FileOffset, SeekOrigin.Begin);
                writer.BaseStream.Seek((((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) + 1) * 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: " + (decryptArgs.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: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": {fileHeader.ReadableFileName}... {datalenM + 1} / {datalenM + 1} mb... Done!\r\n");
            }
        }
Esempio n. 9
0
        /// <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: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");

            var exeFSFilenameTable = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.ExeFSIV, decryptArgs.Encrypt);

            writer.Write(exeFSFilenameTable.ProcessBytes(reader.ReadBytes((int)ncsdHeader.MediaUnitSize)));
            writer.Flush();
        }
Esempio n. 10
0
        /// <summary>
        /// Encrypt the RomFS, if it exists
        /// </summary>
        /// <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(NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
        {
            // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value?
            uint mediaUnitSize = 0x200; // ncsdHeader.MediaUnitSize;

            // 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 * mediaUnitSize) / (1024 * 1024));
            int  romfsSizeB = (int)((long)(ncchHeader.RomFSSizeInMediaUnits * mediaUnitSize) % (1024 * 1024));

            // Encrypting RomFS for partitions 1 and up always use Key0x2C
            if (ncchHeader.PartitionNumber > 0)
            {
                // TODO: Determine how to figure out the original crypto method, if possible
                //if (ciaHeader.BackupHeader.Flags?.BitMasks.HasFlag(BitMasks.FixedCryptoKey) == true) // except if using zero-key
                //{
                //    ncchHeader.NormalKey = 0x00;
                //}
                //else
                //{
                ncchHeader.KeyX      = (decryptArgs.Development ? decryptArgs.DevKeyX0x2C : decryptArgs.KeyX0x2C);
                ncchHeader.NormalKey = RotateLeft((RotateLeft(ncchHeader.KeyX, 2, 128) ^ ncchHeader.KeyY) + decryptArgs.AESHardwareConstant, 87, 128);
                //}
            }

            var cipher = CreateAESCipher(ncchHeader.NormalKey, ncchHeader.RomFSIV, decryptArgs.Encrypt);

            reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
            writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.RomFSOffsetInMediaUnits) * 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");
        }
Esempio n. 11
0
        /// <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);
            }
        }
Esempio n. 12
0
        /// <summary>
        /// Process the ExeFS Filename Table
        /// </summary>
        /// <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(NCCHHeader ncchHeader, BinaryReader reader, BinaryWriter writer)
        {
            // TODO: Determine how to figure out the MediaUnitSize without an NCSD header. Is it a default value?
            uint mediaUnitSize = 0x200; // mediaUnitSize;

            reader.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
            writer.BaseStream.Seek((ncchHeader.Entry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);

            Console.WriteLine($"Partition {ncchHeader.PartitionNumber} ExeFS: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + $": ExeFS Filename Table");

            var exeFSFilenameTable = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.ExeFSIV, decryptArgs.Encrypt);

            writer.Write(exeFSFilenameTable.ProcessBytes(reader.ReadBytes((int)mediaUnitSize)));
            writer.Flush();
        }
Esempio n. 13
0
        /// <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();
        }
Esempio n. 14
0
        /// <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");
        }
Esempio n. 15
0
        static void Main(string[] args)
        {
            // string path = @"D:\Games\3DS\LEGO Star Wars III (USA).3ds";
            string path = args[0];
            // string path = @"D:\Games\3DS\Mario Kart 7 (USA).3ds";

            FileStream fs         = File.Open(path, FileMode.Open);
            NCSDHeader ncsdHeader = new NCSDHeader(IO.ReadData(fs, 512));

            System.Console.WriteLine(ncsdHeader);

            int partition = 1;

            if (args.Length > 1)
            {
                partition = int.Parse(args[1]);
            }
            else
            {
                System.Console.Write("Partition: ");
                partition = int.Parse(System.Console.ReadLine());
            }
            long offset = ncsdHeader.PartitionTable.partitions[partition].Offset;

            fs.Seek(offset, SeekOrigin.Begin);
            NCCHHeader ncchHeader = new NCCHHeader(IO.ReadData(fs, 512), offset);

            System.Console.WriteLine(ncchHeader);

            fs.Seek(ncchHeader.ExeFSOffset, SeekOrigin.Begin);
            ExeFSHeader exeFSHeader = new ExeFSHeader(IO.ReadData(fs, 512), ncchHeader.ExeFSOffset);

            System.Console.WriteLine($"\nExeFS Header\n{exeFSHeader}");

            fs.Seek(ncchHeader.RomFSOffset, SeekOrigin.Begin);
            IVFCHeader ivfcHeader = new IVFCHeader(IO.ReadData(fs, 92), ncchHeader.RomFSOffset);

            System.Console.WriteLine($"\nIVFC Header (RomFS)\n{ivfcHeader}");

            L3Partition l3Partition = new L3Partition(fs, ncchHeader.RomFSOffset + 0x1000);

            System.Console.WriteLine($"\nL3 Header (RomFS)\n{l3Partition.Header}");
            // System.Console.WriteLine($"\nL3 Partition Directory Metadata Table (RomFS)\n{l3Partition.DirectoryMetadataTable}");
            // System.Console.WriteLine($"\nL3 Partition Files Metadata Table (RomFS)\n{l3Partition.FileMetadataTable}");
            System.Console.WriteLine($"\nL3 Partition Structure (RomFS)\n{l3Partition}");
        }
Esempio n. 16
0
        /// <summary>
        /// Update the CryptoMethod and BitMasks for the decrypted partition
        /// </summary>
        /// <param name="ncchHeader">NCCH header representing the partition</param>
        /// <param name="writer">BinaryWriter representing the output stream</param>
        private void UpdateDecryptCryptoAndMasks(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);
            writer.Write((byte)CryptoMethod.Original);
            writer.Flush();

            // Write the new BitMasks flag
            writer.BaseStream.Seek((ncchHeader.Entry.Offset * 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();
        }
Esempio n. 17
0
        /// <summary>
        /// Encrypt the ExeFS, if it exists
        /// </summary>
        /// <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(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;
            }

            // TODO: Determine how to figure out the original crypto method, if possible
            // For all but the original crypto method, process each of the files in the table
            //if (ciaHeader.BackupHeader.Flags.CryptoMethod != CryptoMethod.Original)
            //    ProcessExeFSFileEntries(ncchHeader, reader, writer);

            // Encrypt the filename table
            ProcessExeFSFilenameTable(ncchHeader, reader, writer);

            // Encrypt the rest of the ExeFS
            ProcessExeFS(ncchHeader, reader, writer);
        }
Esempio n. 18
0
        /// <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: " + (decryptArgs.Encrypt ? "Encrypting" : "Decrypting") + ": ExHeader");

                var cipher = CreateAESCipher(ncchHeader.NormalKey2C, ncchHeader.PlainIV, decryptArgs.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);
            }
        }
Esempio n. 19
0
        /// <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);
        }
Esempio n. 20
0
        private byte[] getNcchAesCounter(NCCHHeader header, NCCHSection type) //Function based on code from ctrtool's source: https://github.com/Relys/Project_CTR
        {
            byte[] counter = new byte[16];
            if (header.formatVersion == 2 || header.formatVersion == 0)
            {
                for (int i = 0; i < 8; ++i)
                {
                    counter[i] = header.titleId[header.titleId.Length - 1 - i];
                }
                counter[8] = (byte)type;
            }
            else if (header.formatVersion == 1)
            {
                UInt32 x = 0;
                switch (type)
                {
                case NCCHSection.ExHeader:
                    x = 0x200;     //ExHeader is always 0x200 bytes into the NCCH
                    break;

                case NCCHSection.ExeFS:
                    x = header.exefsOffset * MEDIAUNITSIZE;
                    break;

                case NCCHSection.RomFS:
                    x = header.romfsOffset * MEDIAUNITSIZE;
                    break;
                }
                for (int i = 0; i < 8; ++i)
                {
                    counter[i] = header.titleId[i];
                }
                counter[12] = (byte)((x >> 24) & 0xFF);
                counter[13] = (byte)((x >> 16) & 0xFF);
                counter[14] = (byte)((x >> 8) & 0xFF);
                counter[15] = (byte)(x & 0xFF);
            }
            return(counter);
        }
Esempio n. 21
0
        /// <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, readSignature: 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);
        }
Esempio n. 22
0
        /// <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 (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(ncsdHeader, ncchHeader);

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

            // If we're encrypting, encrypt the filesystems and update the flags
            if (decryptArgs.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);
            }
        }
Esempio n. 23
0
 //Function based on code from ctrtool's source: https://github.com/Relys/Project_CTR
 private byte[] getNcchAesCounter(NCCHHeader header, NCCHSection type)
 {
     byte[] counter = new byte[16];
     if(header.formatVersion == 2 || header.formatVersion == 0)
     {
         for(int i = 0; i < 8; ++i)
         {
             counter[i] = header.titleId[header.titleId.Length - 1 - i];
         }
         counter[8] = (byte) type;
     }
     else if(header.formatVersion == 1)
     {
         UInt32 x = 0;
         switch(type)
         {
             case NCCHSection.ExHeader:
                 x = 0x200; //ExHeader is always 0x200 bytes into the NCCH
                 break;
             case NCCHSection.ExeFS:
                 x = header.exefsOffset * MEDIAUNITSIZE;
                 break;
             case NCCHSection.RomFS:
                 x = header.romfsOffset * MEDIAUNITSIZE;
                 break;
         }
         for(int i = 0; i < 8; ++i)
         {
             counter[i] = header.titleId[i];
         }
         counter[12] = (byte) ((x >> 24) & 0xFF);
         counter[13] = (byte) ((x >> 16) & 0xFF);
         counter[14] = (byte) ((x >> 8) & 0xFF);
         counter[15] = (byte) (x & 0xFF);
     }
     return counter;
 }
Esempio n. 24
0
        private NCCHInfoEntry parseNCCHSection(NCCHHeader header, NCCHSection type, byte[] keyY, bool uses7xCrypto, bool usesSeedCrypto, bool print, string indent)
        {
            NCCHInfoEntry entry = new NCCHInfoEntry();
            entry.keyY = keyY;
            entry.reserved = 0x00000000;
            entry.uses7xcrypto = (UInt32) (uses7xCrypto ? 0x00000001 : 0x00000000);
            entry.uses9xcrypto = (UInt32) (usesSeedCrypto ? 0x00000001 : 0x00000000);
            entry.outputname = new byte[112];

            string sectionname;
            uint offset;
            uint sectionsize;

            switch(type)
            {
                case NCCHSection.ExHeader:
                    sectionname = "ExHeader";
                    offset = 0x200; //Always 0x200
                    sectionsize = header.exhdrSize * MEDIAUNITSIZE;
                    break;
                case NCCHSection.ExeFS:
                    sectionname = "ExeFS";
                    offset = header.exefsOffset * MEDIAUNITSIZE;
                    sectionsize = header.exefsSize * MEDIAUNITSIZE;
                    break;
                case NCCHSection.RomFS:
                    sectionname = "RomFS";
                    offset = header.romfsOffset * MEDIAUNITSIZE;
                    sectionsize = header.romfsSize * MEDIAUNITSIZE;
                    break;
                default:
                    Console.Error.WriteLine("Illegal NCCH Section type provided!");
                    Environment.Exit(1);
                    return entry; //Needed to compile
            }

            entry.counter = getNcchAesCounter(header, type);
            entry.titleID = new byte[8];
            Array.Copy(header.programId, 0, entry.titleID, 0, 8);

            //Compute section size in MB, rounding up to the next MB
            uint sectionMb = sectionsize / BYTES_PER_MEGABYTE; //Rounding down in this step
            uint remainder = sectionsize % BYTES_PER_MEGABYTE;
            if(remainder != 0)
            {
                ++sectionMb;
            }
            entry.size = sectionMb;

            if(print)
            {
                Console.WriteLine(String.Format(indent + "{0} offset:  {1:X8}", sectionname, offset));
                Console.WriteLine(String.Format(indent + "{0} counter: {1}", sectionname, ToHexString(entry.counter)));
                Console.WriteLine(String.Format(indent + "{0} Megabytes(rounded up): {1}", sectionname, sectionMb));
            }

            //TODO
            //return struct.pack('<16s16sIIIIQ', str(counter), str(keyY), sectionMb, 0, usesSeedCrypto, uses7xCrypto, titleId)

            return entry;
        }
Esempio n. 25
0
        private List <NCCHInfoEntry> parseNCCH(BinaryReader reader, uint offset = 0, uint idx = 0, string titleID = "", bool standalone = true)
        {
            string indent = standalone ? "  " : "    ";

            if (standalone)
            {
                Console.WriteLine("Parsing NCCH in file " + (reader.BaseStream as FileStream).Name);
            }
            else
            {
                Console.WriteLine("  Parsing {0} NCCH", GetPartitionName(idx));
            }

            //Read the NCCH header
            reader.BaseStream.Position = offset;
            byte[]     headerbytes = reader.ReadBytes(Marshal.SizeOf(typeof(NCCHHeader)));
            NCCHHeader header      = StructMarshaling.BytesToStruct <NCCHHeader>(headerbytes);

            if (titleID.Equals(""))
            {
                titleID = ByteArrayTo3DSIdentifier(header.titleId);
            }

            //Find keyY
            byte[] keyY = new byte[16];
            Array.Copy(header.signature, 0, keyY, 0, keyY.Length);

            //Print info
            if (!standalone)
            {
                Console.WriteLine(indent + "NCCH offset: {0:X8}", offset);
            }
            Console.WriteLine(indent + "Product code: " + Encoding.ASCII.GetString(header.productCode).TrimEnd((char)0x00));
            if (!standalone)
            {
                Console.WriteLine(indent + "Partition number: {0}", idx);
            }
            Console.WriteLine(indent + "KeyY: {0}", ToHexString(keyY));
            Console.WriteLine(indent + "Title ID: {0}", ByteArrayTo3DSIdentifier(header.titleId));
            Console.WriteLine(indent + "Format version: {0}", header.formatVersion);

            bool uses7xCrypto   = (header.flags[3] != 0x00);
            bool usesSeedCrypto = (header.flags[3] == 0x20);

            if (uses7xCrypto)
            {
                Console.WriteLine(indent + "Uses 7.x NCCH crypto");
            }
            if (usesSeedCrypto)
            {
                Console.WriteLine(indent + "Uses 9.x SEED crypto");
            }

            Console.WriteLine();

            List <NCCHInfoEntry> entries = new List <NCCHInfoEntry>();
            NCCHInfoEntry        entry;

            if (header.exhdrSize != 0)
            {
                entry = parseNCCHSection(header, NCCHSection.ExHeader, keyY, false, false, true, indent);
                byte[] namebytes = Encoding.UTF8.GetBytes(genOutName(titleID, GetPartitionName(idx), "exheader"));
                Array.Copy(namebytes, 0, entry.outputname, 0, namebytes.Length);
                entries.Add(entry);
                Console.WriteLine();
            }

            if (header.exefsSize != 0)
            {
                //We need generate two xorpads for exefs if it uses 7.x crypto, since only a part of it uses the new crypto.
                entry = parseNCCHSection(header, NCCHSection.ExeFS, keyY, false, false, true, indent);
                byte[] namebytes = Encoding.UTF8.GetBytes(genOutName(titleID, GetPartitionName(idx), "exefs_norm"));
                Array.Copy(namebytes, 0, entry.outputname, 0, namebytes.Length);
                entries.Add(entry);
                if (uses7xCrypto)
                {
                    entry     = parseNCCHSection(header, NCCHSection.ExeFS, keyY, uses7xCrypto, usesSeedCrypto, false, indent);
                    namebytes = Encoding.UTF8.GetBytes(genOutName(titleID, GetPartitionName(idx), "exefs_7x"));
                    Array.Copy(namebytes, 0, entry.outputname, 0, namebytes.Length);
                    entries.Add(entry);
                }
                Console.WriteLine();
            }

            if (header.romfsSize != 0)
            {
                entry = parseNCCHSection(header, NCCHSection.RomFS, keyY, uses7xCrypto, usesSeedCrypto, true, indent);
                byte[] namebytes = Encoding.UTF8.GetBytes(genOutName(titleID, GetPartitionName(idx), "romfs"));
                Array.Copy(namebytes, 0, entry.outputname, 0, namebytes.Length);
                entries.Add(entry);
                Console.WriteLine();
            }

            Console.WriteLine();

            return(entries);
        }
Esempio n. 26
0
        private NCCHInfoEntry parseNCCHSection(NCCHHeader header, NCCHSection type, byte[] keyY, bool uses7xCrypto, bool usesSeedCrypto, bool print, string indent)
        {
            NCCHInfoEntry entry = new NCCHInfoEntry();

            entry.keyY         = keyY;
            entry.reserved     = 0x00000000;
            entry.uses7xcrypto = (UInt32)(uses7xCrypto ? 0x00000001 : 0x00000000);
            entry.uses9xcrypto = (UInt32)(usesSeedCrypto ? 0x00000001 : 0x00000000);
            entry.outputname   = new byte[112];

            string sectionname;
            uint   offset;
            uint   sectionsize;

            switch (type)
            {
            case NCCHSection.ExHeader:
                sectionname = "ExHeader";
                offset      = 0x200; //Always 0x200
                sectionsize = header.exhdrSize * MEDIAUNITSIZE;
                break;

            case NCCHSection.ExeFS:
                sectionname = "ExeFS";
                offset      = header.exefsOffset * MEDIAUNITSIZE;
                sectionsize = header.exefsSize * MEDIAUNITSIZE;
                break;

            case NCCHSection.RomFS:
                sectionname = "RomFS";
                offset      = header.romfsOffset * MEDIAUNITSIZE;
                sectionsize = header.romfsSize * MEDIAUNITSIZE;
                break;

            default:
                Console.Error.WriteLine("Illegal NCCH Section type provided!");
                Environment.Exit(1);
                return(entry);    //Needed to compile
            }

            entry.counter = getNcchAesCounter(header, type);
            entry.titleID = new byte[8];
            Array.Copy(header.programId, 0, entry.titleID, 0, 8);

            //Compute section size in MB, rounding up to the next MB
            uint sectionMb = sectionsize / BYTES_PER_MEGABYTE; //Rounding down in this step
            uint remainder = sectionsize % BYTES_PER_MEGABYTE;

            if (remainder != 0)
            {
                ++sectionMb;
            }
            entry.size = sectionMb;

            if (print)
            {
                Console.WriteLine(String.Format(indent + "{0} offset:  {1:X8}", sectionname, offset));
                Console.WriteLine(String.Format(indent + "{0} counter: {1}", sectionname, ToHexString(entry.counter)));
                Console.WriteLine(String.Format(indent + "{0} Megabytes(rounded up): {1}", sectionname, sectionMb));
            }


            //TODO
            //return struct.pack('<16s16sIIIIQ', str(counter), str(keyY), sectionMb, 0, usesSeedCrypto, uses7xCrypto, titleId)

            return(entry);
        }
Esempio n. 27
0
        /// <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 = 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);

            // Set the header to use based on mode
            BitMasks     masks;
            CryptoMethod method;

            if (decryptArgs.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 = 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);
            }
        }