public void Decrypt(DirectoryInfo outputDirectory, bool fullyDecryptNcchContents) { foreach (ContentChunkRecord ccr in this.TMD.ContentChunkRecords) { if (!File.Exists($"{this.ContentDir.FullName}/{ccr.ID.ToString("x8")}")) { continue; } using (FileStream inputFs = File.OpenRead($"{this.ContentDir.FullName}/{ccr.ID.ToString("x8")}")) { using (Aes aes = Aes.Create()) { aes.Key = this.Cryptor.NormalKey[(int)Keyslot.DecryptedTitleKey]; byte[] cindex = BitConverter.GetBytes(ccr.ContentIndex); aes.IV = (BitConverter.IsLittleEndian ? cindex.FReverse() : cindex).PadRight(0x00, 0x10); aes.Padding = PaddingMode.Zeros; aes.Mode = CipherMode.CBC; using (CryptoStream cs = new CryptoStream(File.Create($"{outputDirectory.FullName}/{ccr.ID.ToString("x8")}"), aes.CreateDecryptor(), CryptoStreamMode.Write)) { inputFs.CopyTo(cs); cs.FlushFinalBlock(); } } } if (fullyDecryptNcchContents) { FileStream decryptedLayer1Fs = File.Open($"{outputDirectory.FullName}/{ccr.ID.ToString("x8")}", FileMode.Open, FileAccess.ReadWrite); using (NCCH ncch = new NCCH(decryptedLayer1Fs, this.Cryptor, this.SeedDb)) { using (FileStream decryptedLayer2Fs = File.Create($"{outputDirectory.FullName}/{ccr.ID.ToString("x8")}_decrypted.{(ncch.Info.Flags.IsExecutable ? "cxi" : "cfa")}")) { ncch.Decrypt(decryptedLayer2Fs); } } } } File.Copy($"{this.ContentDir.FullName}/tmd", $"{outputDirectory.FullName}/tmd"); }
public void Decrypt(FileStream outputStream, bool trim = true) { if (this.Cryptor == null || this.SeedDatabase == null) { throw new ArgumentException("Cannot decrypt without seed database or crypto engine."); } long size; if (!trim) { outputStream.SetLength(this.Info.ImageSize); } else { size = this.Info.Partitions[0].Offset; foreach (NCSDPartition partition in this.Info.Partitions) { size += partition.Size; } outputStream.SetLength(size); } outputStream.Seek(0, SeekOrigin.Begin); MemoryMappedFile outputFile = Tools.LoadFileMapped(outputStream); using (MemoryMappedViewStream srcHeaderViewStream = this.NCSDMemoryMappedFile.CreateViewStream(0x0, this.Info.Partitions[0].Offset)) { using (MemoryMappedViewStream destHeaderViewStream = outputFile.CreateViewStream(0x0, this.Info.Partitions[0].Offset)) { srcHeaderViewStream.CopyTo(destHeaderViewStream); } } foreach (NCSDPartition partition in this.Info.Partitions) { using (MemoryMappedViewStream destPartitionViewStream = outputFile.CreateViewStream(partition.Offset, partition.Size)) { NCCH ncch = new NCCH(this.NCSDMemoryMappedFile, partition.Offset, this.Cryptor, this.SeedDatabase); ncch.Decrypt(outputFile); } } if (!trim) { NCSDPartition lastPartition = this.Info.Partitions.Last(); long lastPosition = lastPartition.Offset + lastPartition.Size; using (MemoryMappedViewStream viewStream = outputFile.CreateViewStream(lastPosition, this.Info.ImageSize - lastPosition)) { while (viewStream.Position / 64 < viewStream.Length / 64) { byte[] buffer = new byte[64]; for (int i = 0; i < buffer.Length; i++) { buffer[i] = 0xFF; } viewStream.Write(buffer, 0, buffer.Length); } } } }