示例#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");
        }
示例#2
0
文件: NCSD.cs 项目: tom-overton/pk3DS
        public NCSDHeader CreateHeaderFromBytes(byte[] data)
        {
            Data = data;
            var header = new NCSDHeader
            {
                Signature       = new byte[0x100],
                Magic           = BitConverter.ToUInt32(data, 0x100),
                MediaSize       = BitConverter.ToUInt32(data, 0x104),
                TitleId         = BitConverter.ToUInt64(data, 0x108),
                OffsetSizeTable = new NCCH_Meta[8]
            };

            Array.Copy(data, header.Signature, 0x100);
            for (int i = 0; i < 8; i++)
            {
                header.OffsetSizeTable[i] = new NCCH_Meta
                {
                    Offset = BitConverter.ToUInt32(data, 0x120 + (8 * i)),
                    Size   = BitConverter.ToUInt32(data, 0x124 + (8 * i))
                };
            }
            header.flags = new byte[8];
            Array.Copy(data, 0x188, header.flags, 0, 8);
            header.NCCHIdTable = new ulong[8];
            for (int i = 0; i < 8; i++)
            {
                header.NCCHIdTable[i] = BitConverter.ToUInt64(data, 0x190 + (8 * i));
            }
            return(header);
        }
示例#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();
        }
示例#4
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)))
                    {
                        NCSDHeader header = NCSDHeader.Read(reader, decryptArgs.Development);
                        if (header == null)
                        {
                            Console.WriteLine("Error: Not a 3DS cart image!");
                            return(false);
                        }

                        // 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 cart image and try again.");
                return(false);
            }
        }
示例#5
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");
        }
示例#6
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();
        }
示例#7
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 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 = 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) * 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: " + (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");
            }
        }
示例#8
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);
            }
        }
示例#9
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();
        }
示例#10
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      = (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) * 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");
        }
示例#11
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}");
        }
示例#12
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);
            }
        }
示例#13
0
        /// <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);
        }
示例#14
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);
        }
示例#15
0
文件: NCSD.cs 项目: tom-overton/pk3DS
        public void ExtractFilesFromNCSD(string NCSD_PATH, string outputDirectory, RichTextBox TB_Progress = null, ProgressBar PB_Show = null)
        {
            if (!Directory.Exists(outputDirectory))
            {
                Directory.CreateDirectory(outputDirectory);
            }

            UpdateTB(TB_Progress, "Extracting game data (CXI) from .3DS file...");
            byte[] headerBytes = new byte[0x200];
            using (FileStream fs = new FileStream(NCSD_PATH, FileMode.Open, FileAccess.Read))
            {
                fs.Read(headerBytes, 0, headerBytes.Length);
            }

            Header = CreateHeaderFromBytes(headerBytes);
            string ncchPath = ExtractCXIfromNCSD(NCSD_PATH, outputDirectory, Header.OffsetSizeTable[0].Size, PB_Show);

            UpdateTB(TB_Progress, "CXI extracted, extracting files from CXI...");
            NCCH ncch = new NCCH();

            ncch.ExtractNCCHFromFile(ncchPath, outputDirectory, TB_Progress, PB_Show);
            File.Delete(ncchPath);
        }
示例#16
0
        private List <NCCHInfoEntry> parseNCSD(BinaryReader reader)
        {
            Console.WriteLine("Parsing NCSD in file \"{0}\"", (reader.BaseStream as FileStream).Name);
            reader.BaseStream.Position = 0;

            byte[]     headerbytes = reader.ReadBytes(Marshal.SizeOf(typeof(NCSDHeader)));
            NCSDHeader header      = StructMarshaling.BytesToStruct <NCSDHeader>(headerbytes);

            string titleID = ByteArrayTo3DSIdentifier(header.titleId);

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

            for (uint i = 0; i <= header.partitionTable.GetUpperBound(0); ++i)
            {
                PartitionEntry currententry = header.partitionTable[i];
                if (currententry.offset != 0)
                {
                    List <NCCHInfoEntry> newentries = parseNCCH(reader, currententry.offset * MEDIAUNITSIZE, i, titleID, false);
                    entries.AddRange(newentries);
                }
            }

            return(entries);
        }
示例#17
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);
            }
        }
示例#18
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);
            }
        }