Ejemplo n.º 1
0
        public void UntrimAndEncrypt(Keyset keyset)
        {
            FolderTools.ExtractTitlekeys(decryptedDir, keyset, Out);

            var decryptedFs = new LocalFileSystem(decryptedDir);
            var encryptedFs = new LocalFileSystem(encryptedDir);

            EncryptNCA.Encrypt(decryptedFs, encryptedFs, VerifyHashes, keyset, Out);

            var dirDecrypted = new DirectoryInfo(decryptedDir);

            foreach (var file in decryptedFs.EnumerateEntries()
                     .Where(item => item.Type == DirectoryEntryType.File && !item.Name.EndsWith(".tca")))
            {
                if (!file.Name.EndsWith(".nca"))
                {
                    using (var srcFile = decryptedFs.OpenFile(file.FullPath, OpenMode.Read))
                        using (var destFile = FolderTools.CreateAndOpen(file, encryptedFs, file.Name, file.Size))
                        {
                            srcFile.CopyTo(destFile);
                        }
                }

                decryptedFs.DeleteFile(file.FullPath);
            }

            UntrimDeltaNCA.Process(decryptedDir, encryptedFs, keyset, Out);
            EncryptNCA.Encrypt(decryptedFs, encryptedFs, VerifyHashes, keyset, Out);
        }
Ejemplo n.º 2
0
        private void CompressFunct()
        {
            var CompressionIO  = new byte[104857600];
            var blocksPerChunk = CompressionIO.Length / bs + (CompressionIO.Length % bs > 0 ? 1 : 0);
            var sourceFs       = new LocalFileSystem(inFolderPath);
            var destFs         = new LocalFileSystem(outFolderPath);

            foreach (var file in sourceFs.EnumerateEntries().Where(item => item.Type == DirectoryEntryType.File))
            {
                Out.Log($"{file.FullPath}\r\n");
                var outFileName = $"{file.Name}.nsz";
                using (var outputFileBase = FolderTools.CreateAndOpen(file, destFs, outFileName))
                    using (var outputFile = new FilePositionStorage(outputFileBase))
                        using (var inputFileBase = sourceFs.OpenFile(file.FullPath, OpenMode.Read))
                            using (var inputFile = new FilePositionStorage(inputFileBase))
                            {
                                amountOfBlocks = (int)Math.Ceiling((decimal)inputFile.GetSize() / bs);
                                sizeOfSize     = (int)Math.Ceiling(Math.Log(bs, 2) / 8);
                                var perBlockHeaderSize = sizeOfSize + 1;
                                var headerSize         = 0x15 + perBlockHeaderSize * amountOfBlocks;
                                outputFile.Seek(headerSize);
                                var nsZipMagic          = new byte[] { 0x6e, 0x73, 0x5a, 0x69, 0x70 };
                                var nsZipMagicRandomKey = new byte[5];
                                secureRNG.GetBytes(nsZipMagicRandomKey);
                                Util.XorArrays(nsZipMagic, nsZipMagicRandomKey);
                                var chunkIndex = 0;
                                nsZipHeader = new byte[headerSize];
                                Array.Copy(nsZipMagic, 0x00, nsZipHeader, 0x00, 0x05);
                                Array.Copy(nsZipMagicRandomKey, 0x00, nsZipHeader, 0x05, 0x05);
                                nsZipHeader[0x0A] = 0x00;         //Version
                                nsZipHeader[0x0B] = 0x01;         //Type
                                nsZipHeader[0x0C] = (byte)(bs >> 32);
                                nsZipHeader[0x0D] = (byte)(bs >> 24);
                                nsZipHeader[0x0E] = (byte)(bs >> 16);
                                nsZipHeader[0x0F] = (byte)(bs >> 8);
                                nsZipHeader[0x10] = (byte)bs;
                                nsZipHeader[0x11] = (byte)(amountOfBlocks >> 24);
                                nsZipHeader[0x12] = (byte)(amountOfBlocks >> 16);
                                nsZipHeader[0x13] = (byte)(amountOfBlocks >> 8);
                                nsZipHeader[0x14] = (byte)amountOfBlocks;
                                sha256Compressed  = new SHA256Cng();


                                long maxPos = inputFile.GetSize();
                                int  blocksLeft;
                                int  blocksInThisChunk;

                                do
                                {
                                    var outputLen = new int[blocksPerChunk];             //Filled with 0
                                    inputFile.Read(CompressionIO);

                                    blocksLeft        = amountOfBlocks - chunkIndex * blocksPerChunk;
                                    blocksInThisChunk = Math.Min(blocksPerChunk, blocksLeft);

                                    var opt = new ParallelOptions()
                                    {
                                        MaxDegreeOfParallelism = this.MaxDegreeOfParallelism
                                    };

                                    //for(int index = 0; index < blocksInThisChunk; ++index)
                                    Parallel.For(0, blocksInThisChunk, opt, index =>
                                    {
                                        var currentBlockID   = chunkIndex * blocksPerChunk + index;
                                        var startPosRelative = index * bs;

                                        //Don't directly cast bytesLeft to int or sectors over 2 GB will overflow into negative size
                                        long startPos  = (long)currentBlockID * (long)bs;
                                        long bytesLeft = maxPos - startPos;
                                        var blockSize  = bs < bytesLeft ? bs : (int)bytesLeft;

                                        Out.Print($"Block: {currentBlockID + 1}/{amountOfBlocks} ({opt.MaxDegreeOfParallelism})\r\n");

                                        CompressionAlgorithm compressionAlgorithm;
                                        outputLen[index] = CompressBlock(ref CompressionIO, startPosRelative, blockSize, out compressionAlgorithm);
                                        //Out.Log($"inputLen[{currentBlockID}]: {blockSize}\r\n");
                                        //Out.Log($"outputLen[{currentBlockID}]: {outputLen[index]} bytesLeft={bytesLeft}\r\n");

                                        var offset = currentBlockID * (sizeOfSize + 1);
                                        switch (compressionAlgorithm)
                                        {
                                        case CompressionAlgorithm.None:
                                            nsZipHeader[0x15 + offset] = 0x00;
                                            break;

                                        case CompressionAlgorithm.Zstandard:
                                            nsZipHeader[0x15 + offset] = 0x01;
                                            break;

                                        case CompressionAlgorithm.LZMA:
                                            nsZipHeader[0x15 + offset] = 0x02;
                                            break;

                                        default:
                                            throw new ArgumentOutOfRangeException();
                                        }
                                        for (var j = 0; j < sizeOfSize; ++j)
                                        {
                                            nsZipHeader[0x16 + offset + j] = (byte)(outputLen[index] >> ((sizeOfSize - j - 1) * 8));
                                        }
                                    });

                                    for (int index = 0; index < blocksInThisChunk; ++index)
                                    {
                                        var startPos = index * bs;
                                        sha256Compressed.TransformBlock(CompressionIO, startPos, outputLen[index], null, 0);
                                        var dataToWrite = CompressionIO.AsSpan().Slice(startPos, outputLen[index]);
                                        outputFile.Write(dataToWrite);
                                    }

                                    ++chunkIndex;
                                } while (blocksLeft - blocksInThisChunk > 0);

                                outputFile.Write(nsZipHeader, 0);
                                sha256Header = new SHA256Cng();
                                sha256Header.ComputeHash(nsZipHeader);
                                var sha256Hash = new byte[0x20];
                                Array.Copy(sha256Header.Hash, sha256Hash, 0x20);
                                sha256Compressed.TransformFinalBlock(new byte[0], 0, 0);
                                Util.XorArrays(sha256Hash, sha256Compressed.Hash);
                                //Console.WriteLine(sha256Header.Hash.ToHexString());
                                //Console.WriteLine(sha256Compressed.Hash.ToHexString());
                                outputFile.Seek(0, SeekOrigin.End);
                                outputFile.Write(sha256Hash.AsSpan().Slice(0, 0x10));
                            }
            }
        }
Ejemplo n.º 3
0
        static void Main(string[] args)
        {
            Console.WriteLine("Nintendo Switch NSP Verifier v1.00");
            Console.WriteLine("Copyright 2018 CaitSith2");
            Console.WriteLine("");

            _path = args.Length >= 1
                ? string.Join(" ", args)
                : Environment.CurrentDirectory;

            if (new[] { "--help", "-h" }.Any(x => x.Equals(_path, StringComparison.InvariantCultureIgnoreCase)))
            {
                Console.WriteLine("Usage: NSPVerify [path to NSP directory]");
                Console.WriteLine("");
                Console.WriteLine("If the tool is run without specifying a path, it will look for NSPs in the current directory and ALL sub-directories of current directory");
                return;
            }



            if (!Directory.Exists(_path))
            {
                Console.WriteLine("ERROR: Specified directory does not exist.  specify --help for usage information.");
                return;
            }

            IFileSystem fs = new LocalFileSystem(_path);

            var keys = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch", "prod.keys");

            if (File.Exists("keys.txt"))
            {
                keys = "keys.txt";
            }

            if (!File.Exists(keys))
            {
                Console.WriteLine($"Cannot verify NSPs without keys.txt. Either put it in the same directory as this tool,");
                Console.WriteLine($"or place it in \"{Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch")}\" named as prod.keys");
                PressAnyKey();
                return;
            }

            var keyset        = ExternalKeys.ReadKeyFile(keys);
            var badlist       = new List <string>();
            var exceptionlist = new List <string>();

            var files = new List <DirectoryEntry>();

            files.AddRange(fs.EnumerateEntries("*.nsp", SearchOptions.RecurseSubdirectories));
            files.AddRange(fs.EnumerateEntries("*.nsx", SearchOptions.RecurseSubdirectories));

            if (files.Count == 0)
            {
                Console.WriteLine("Error: No NSP/NSX files in specified directory");
                PressAnyKey();
                return;
            }

            foreach (DirectoryEntry dentry in files)
            {
                var file             = dentry.FullPath;
                var filename         = Path.GetFileName(file);
                var relativefilename = Util.GetRelativePath(file, "/");
                Console.Write($"Checking {filename}: ");
                try
                {
                    bool ok = true;
                    using (var nspfile = fs.OpenFile(file, OpenMode.Read))
                    {
                        var nspdata  = new PartitionFileSystem(new FileStorage(nspfile));
                        var cnmtfile = nspdata.Files.FirstOrDefault(x => x.Name.ToLowerInvariant().EndsWith(".cnmt.nca"));
                        if (cnmtfile == null)
                        {
                            Console.WriteLine($"\rChecking {filename}: No cnmt.nca file present");
                            badlist.Add(relativefilename);
                            continue;
                        }

                        var  cnmtdata = nspdata.OpenFile(cnmtfile, OpenMode.Read);
                        Cnmt cnmt;
                        using (var sr = new BinaryReader(cnmtdata.AsStream()))
                        {
                            var cnmthash = SHA256.Create().ComputeHash(sr.ReadBytes((int)cnmtdata.GetSize()));
                            if (!cnmtfile.Name.ToLowerInvariant().Contains(cnmthash.Take(16).ToArray().ToHexString()))
                            {
                                //Put failure here
                                Console.WriteLine($"\rChecking {filename}: cnmt.nca file is corrupted");
                                badlist.Add(relativefilename);
                                cnmtdata.Dispose();
                                continue;
                            }

                            sr.BaseStream.Position = 0;
                            var cnmtnca    = new Nca(keyset, new FileStorage(cnmtdata));
                            var section    = cnmtnca.OpenStorage(0, IntegrityCheckLevel.ErrorOnInvalid);
                            var sectionpfs = new PartitionFileSystem(section);
                            cnmt = new Cnmt(sectionpfs.OpenFile(sectionpfs.Files[0], OpenMode.Read).AsStream());
                        }

                        foreach (var entry in cnmt.ContentEntries)
                        {
                            var entryfile = nspdata.Files.FirstOrDefault(x => x.Name.ToLowerInvariant().EndsWith(entry.NcaId.ToHexString() + ".nca"));
                            if (entryfile == null)
                            {
                                if (entry.Type != CnmtContentType.DeltaFragment)
                                {
                                    //Put failure here
                                    Console.WriteLine($"\rChecking {filename}: one of the entries required by the cnmt.nca is missing.");
                                    badlist.Add(relativefilename);
                                    break;
                                }

                                continue;
                            }

                            using (var entrynca = nspdata.OpenFile(entryfile, OpenMode.Read))
                            {
                                var hash = SHA256.Create();

                                using (var sr = new BinaryReader(entrynca.AsStream()))
                                {
                                    while (entrynca.GetSize() != sr.BaseStream.Position)
                                    {
                                        var entryncadata = sr.ReadBytes(0x100000);
                                        hash.TransformBlock(entryncadata, 0, entryncadata.Length, entryncadata, 0);
                                        Console.Write($"\rChecking {filename}: {((sr.BaseStream.Position * 100.0) / entrynca.GetSize()):0.0}%");
                                    }

                                    hash.TransformFinalBlock(new byte[0], 0, 0);
                                }

                                if (hash.Hash.ToHexString().Equals(entry.Hash.ToHexString()))
                                {
                                    Console.Write($"\rChecking {filename}: {100:0.0}%");
                                    continue;
                                }

                                //Put failure here
                                Console.WriteLine($"\rChecking {filename}: one of the entries required by the cnmt.nca is corrupted");
                                badlist.Add(relativefilename);
                                ok = false;
                                break;
                            }
                        }

                        if (ok)
                        {
                            Console.WriteLine($"\rChecking {filename}: OK        ");
                        }
                        //Put Success here
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.WriteLine(ex.StackTrace);
                    exceptionlist.Add($"{relativefilename}{Environment.NewLine}Exception: \"{ex.GetType()}\" {ex.Message}{Environment.NewLine}Stack Trace: {ex.StackTrace}{Environment.NewLine}");
                }
            }

            badlist.Insert(0, badlist.Count == 0
                ? "None of the files are corrupted. :)"
                : "The following NSP/NSX files are corrupted:");

            exceptionlist.Insert(0, exceptionlist.Count == 0
                ? "No exceptions to log. :)"
                : "Exceptions caused while parsing the following NSP/NSX files:");

            try
            {
                File.WriteAllText("Corrupted NSPs.txt", string.Join(Environment.NewLine, badlist));
                File.WriteAllText("Exception Log.txt", string.Join(Environment.NewLine, exceptionlist));
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Could not write the output files \"Corrupted NSPs.txt\" and \"Exception Log.txt\" due to the following exception.");
                Console.WriteLine($"Exception: \"{ex.GetType()}\" {ex.Message}");
                Console.WriteLine($"Stack Trace: {ex.StackTrace}{Environment.NewLine}");

                Console.WriteLine(string.Join(Environment.NewLine, badlist));
                Console.WriteLine();
                Console.WriteLine(string.Join(Environment.NewLine, exceptionlist));
                Console.WriteLine();
            }

            Console.WriteLine("Done.");
            PressAnyKey();
        }