From 32b430fafaa24738e65d08cca0bba6ade27db29d Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sat, 12 Oct 2024 00:15:00 -0400 Subject: [PATCH] Update libraries --- NDecrypt.Core/NDecrypt.Core.csproj | 4 +- NDecrypt.N3DS/CIATool.cs | 62 ++- NDecrypt.N3DS/NDecrypt.N3DS.csproj | 3 +- NDecrypt.N3DS/Serializer.cs | 781 --------------------------- NDecrypt.N3DS/ThreeDSTool.cs | 88 ++- NDecrypt.Nitro/DSTool.cs | 24 +- NDecrypt.Nitro/NDecrypt.Nitro.csproj | 3 +- NDecrypt.Nitro/Serializer.cs | 176 ------ 8 files changed, 174 insertions(+), 967 deletions(-) delete mode 100644 NDecrypt.N3DS/Serializer.cs delete mode 100644 NDecrypt.Nitro/Serializer.cs diff --git a/NDecrypt.Core/NDecrypt.Core.csproj b/NDecrypt.Core/NDecrypt.Core.csproj index 52804c3..675791d 100644 --- a/NDecrypt.Core/NDecrypt.Core.csproj +++ b/NDecrypt.Core/NDecrypt.Core.csproj @@ -1,4 +1,4 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/NDecrypt.N3DS/CIATool.cs b/NDecrypt.N3DS/CIATool.cs index 96f2633..9c780a2 100644 --- a/NDecrypt.N3DS/CIATool.cs +++ b/NDecrypt.N3DS/CIATool.cs @@ -5,6 +5,8 @@ using System.Numerics; using NDecrypt.Core; using SabreTools.Models.N3DS; using static NDecrypt.Core.Helper; +using CIADeserializer = SabreTools.Serialization.Deserializers.CIA; +using N3DSDeserializer = SabreTools.Serialization.Deserializers.N3DS; namespace NDecrypt.N3DS { @@ -71,9 +73,9 @@ namespace NDecrypt.N3DS // Open the read and write on the same file for inplace processing using var reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); using var writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)); - + // Deserialize the CIA information - var cia = Serializer.ReadCIA(reader); + var cia = ReadCIA(reader); if (cia == null) { Console.WriteLine("Error: Not a 3DS CIA!"); @@ -284,7 +286,7 @@ namespace NDecrypt.N3DS uint mediaUnitSize = 0x200; // mediaUnitSize; reader.BaseStream.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin); - var exefsHeader = Serializer.ReadExeFSHeader(reader); + var exefsHeader = ReadExeFSHeader(reader); // If the header failed to read, log and return if (exefsHeader?.FileHeaders == null) @@ -659,5 +661,59 @@ namespace NDecrypt.N3DS } #endregion + + #region Serialization + + /// + /// Read from a stream and get a CIA header, if possible + /// + /// BinaryReader representing the input stream + /// CIA header object, null on error + private static CIA? ReadCIA(BinaryReader reader) + { + try + { + return CIADeserializer.DeserializeStream(reader.BaseStream); + } + catch + { + return null; + } + } + + /// + /// Read from a stream and get an ExeFS header, if possible + /// + /// BinaryReader representing the input stream + /// ExeFS header object, null on error + private static ExeFSHeader? ReadExeFSHeader(BinaryReader reader) + { + var header = new ExeFSHeader(); + + try + { + header.FileHeaders = new ExeFSFileHeader[10]; + for (int i = 0; i < 10; i++) + { + header.FileHeaders[i] = N3DSDeserializer.ParseExeFSFileHeader(reader.BaseStream)!; + } + + header.Reserved = reader.ReadBytes(0x20); + + header.FileHashes = new byte[10][]; + for (int i = 0; i < 10; i++) + { + header.FileHashes[9 - i] = reader.ReadBytes(0x20); + } + + return header; + } + catch + { + return null; + } + } + + #endregion } } \ No newline at end of file diff --git a/NDecrypt.N3DS/NDecrypt.N3DS.csproj b/NDecrypt.N3DS/NDecrypt.N3DS.csproj index 22ca8cb..416fa9a 100644 --- a/NDecrypt.N3DS/NDecrypt.N3DS.csproj +++ b/NDecrypt.N3DS/NDecrypt.N3DS.csproj @@ -40,7 +40,8 @@ - + + diff --git a/NDecrypt.N3DS/Serializer.cs b/NDecrypt.N3DS/Serializer.cs deleted file mode 100644 index 2bb9ac6..0000000 --- a/NDecrypt.N3DS/Serializer.cs +++ /dev/null @@ -1,781 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using SabreTools.Models.N3DS; - -namespace NDecrypt.N3DS -{ - internal static class Serializer - { - #region Constants - - private const string NCCHMagicNumber = "NCCH"; - private const string NCSDMagicNumber = "NCSD"; - - #endregion - - #region Reading - - /// - /// Read from a stream and get N3DS cart image, if possible - /// - /// BinaryReader representing the input stream - /// True if development cart, false otherwise - /// N3DS cart image object, null on error - public static Cart? ReadCart(BinaryReader reader, bool development) - { - var cart = new Cart(); - - try - { - cart.Header = ReadNCSDHeader(reader); - if (cart.Header == null) - return null; - - if (cart.Header.PartitionsFSType == FilesystemType.Normal - || cart.Header.PartitionsFSType == FilesystemType.None) - { - cart.CardInfoHeader = ReadCardInfoHeader(reader); - if (cart.CardInfoHeader == null) - return null; - - if (development) - { - cart.DevelopmentCardInfoHeader = ReadDevelopmentCardInfoHeader(reader); - if (cart.DevelopmentCardInfoHeader == null) - return null; - } - } - - cart.Partitions = new NCCHHeader[8]; - for (int i = 0; i < 8; i++) - { - // Check the entry is valid - var tableEntry = cart.Header.PartitionsTable![i]; - if (tableEntry == null || tableEntry.Offset == 0 || tableEntry.Length == 0) - continue; - - // Seek to the beginning of the NCCH partition - long offset = tableEntry.Offset * cart.Header.ImageSizeInMediaUnits; - reader.BaseStream.Seek(offset, SeekOrigin.Begin); - - cart.Partitions[i] = ReadNCCHHeader(reader, readSignature: true); - } - - return cart; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get a CIA header, if possible - /// - /// BinaryReader representing the input stream - /// CIA header object, null on error - public static CIA? ReadCIA(BinaryReader reader) - { - var cia = new CIA(); - - try - { - var header = new CIAHeader(); - - header.HeaderSize = reader.ReadUInt32(); - header.Type = reader.ReadUInt16(); - header.Version = reader.ReadUInt16(); - header.CertificateChainSize = reader.ReadUInt32(); - header.TicketSize = reader.ReadUInt32(); - header.TMDFileSize = reader.ReadUInt32(); - header.MetaSize = reader.ReadUInt32(); - header.ContentSize = reader.ReadUInt64(); - header.ContentIndex = reader.ReadBytes(0x2000); - - cia.Header = header; - - var certificateChain = new Certificate[3]; - if (reader.BaseStream.Position % 64 != 0) - reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current); - - certificateChain[0] = ReadCertificate(reader)!; // CA - certificateChain[1] = ReadCertificate(reader)!; // Ticket - certificateChain[2] = ReadCertificate(reader)!; // TMD - if (reader.BaseStream.Position % 64 != 0) - reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current); - - cia.CertificateChain = certificateChain; - - cia.Ticket = ReadTicket(reader, header.TicketSize); - if (reader.BaseStream.Position % 64 != 0) - reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current); - - cia.TMDFileData = ReadTitleMetadata(reader, header.TMDFileSize); - if (reader.BaseStream.Position % 64 != 0) - reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current); - - long startingPosition = reader.BaseStream.Position; - var headers = new List(); - while ((ulong)reader.BaseStream.Position < (ulong)startingPosition + header.ContentSize) - { - long initPosition = reader.BaseStream.Position; - var ncchHeader = ReadNCCHHeader(reader, readSignature: true); - if (ncchHeader == null) - break; - - headers.Add(ncchHeader); - reader.BaseStream.Seek(initPosition + ncchHeader.ContentSizeInMediaUnits * 0x200, SeekOrigin.Begin); - } - - cia.Partitions = [.. headers]; - if (header.MetaSize > 0) - cia.MetaData = ReadMetaData(reader); - - return cia; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get an ExeFS header, if possible - /// - /// BinaryReader representing the input stream - /// ExeFS header object, null on error - public static ExeFSHeader? ReadExeFSHeader(BinaryReader reader) - { - var header = new ExeFSHeader(); - - try - { - header.FileHeaders = new ExeFSFileHeader[10]; - for (int i = 0; i < 10; i++) - { - header.FileHeaders[i] = ReadExeFSFileHeader(reader)!; - } - - header.Reserved = reader.ReadBytes(0x20); - - header.FileHashes = new byte[10][]; - for (int i = 0; i < 10; i++) - { - header.FileHashes[9 - i] = reader.ReadBytes(0x20); - } - - return header; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get an NCCH header, if possible - /// - /// BinaryReader representing the input stream - /// True if the RSA signature is read, false otherwise - /// NCCH header object, null on error - public static NCCHHeader? ReadNCCHHeader(BinaryReader reader, bool readSignature) - { - var header = new NCCHHeader(); - - try - { - if (readSignature) - header.RSA2048Signature = reader.ReadBytes(0x100); - - if (new string(reader.ReadChars(4)) != NCCHMagicNumber) - return null; - - header.ContentSizeInMediaUnits = reader.ReadUInt32(); - header.PartitionId = reader.ReadUInt64(); - header.MakerCode = reader.ReadUInt16(); - header.Version = reader.ReadUInt16(); - header.VerificationHash = reader.ReadUInt32(); - header.ProgramId = reader.ReadBytes(8); - header.Reserved1 = reader.ReadBytes(0x10); - header.LogoRegionHash = reader.ReadBytes(0x20); - byte[] productCodeBytes = reader.ReadBytes(0x10); - header.ProductCode = Encoding.ASCII.GetString(productCodeBytes); - header.ExtendedHeaderHash = reader.ReadBytes(0x20); - header.ExtendedHeaderSizeInBytes = reader.ReadUInt32(); - header.Reserved2 = reader.ReadUInt32(); - header.Flags = ReadNCCHHeaderFlags(reader); - header.PlainRegionOffsetInMediaUnits = reader.ReadUInt32(); - header.PlainRegionSizeInMediaUnits = reader.ReadUInt32(); - header.LogoRegionOffsetInMediaUnits = reader.ReadUInt32(); - header.LogoRegionSizeInMediaUnits = reader.ReadUInt32(); - header.ExeFSOffsetInMediaUnits = reader.ReadUInt32(); - header.ExeFSSizeInMediaUnits = reader.ReadUInt32(); - header.ExeFSHashRegionSizeInMediaUnits = reader.ReadUInt32(); - header.Reserved3 = reader.ReadUInt32(); - header.RomFSOffsetInMediaUnits = reader.ReadUInt32(); - header.RomFSSizeInMediaUnits = reader.ReadUInt32(); - header.RomFSHashRegionSizeInMediaUnits = reader.ReadUInt32(); - header.Reserved4 = reader.ReadUInt32(); - header.ExeFSSuperblockHash = reader.ReadBytes(0x20); - header.RomFSSuperblockHash = reader.ReadBytes(0x20); - - return header; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get an CardInfo header, if possible - /// - /// BinaryReader representing the input stream - /// CardInfo header object, null on error - private static CardInfoHeader? ReadCardInfoHeader(BinaryReader reader) - { - var header = new CardInfoHeader(); - - try - { - header.WritableAddressMediaUnits = reader.ReadUInt32(); - header.CardInfoBitmask = reader.ReadUInt32(); - header.Reserved3 = reader.ReadBytes(0x108); - header.TitleVersion = reader.ReadUInt16(); - header.CardRevision = reader.ReadUInt16(); - header.Reserved4 = reader.ReadBytes(0xCD6); - header.InitialData = ReadInitialData(reader); - - return header; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get certificate, if possible - /// - /// BinaryReader representing the input stream - /// Certificate object, null on error - private static Certificate? ReadCertificate(BinaryReader reader) - { - var ct = new Certificate(); - - try - { - ct.SignatureType = (SignatureType)reader.ReadUInt32(); - switch (ct.SignatureType) - { - case SignatureType.RSA_4096_SHA1: - case SignatureType.RSA_4096_SHA256: - ct.SignatureSize = 0x200; - ct.PaddingSize = 0x3C; - break; - case SignatureType.RSA_2048_SHA1: - case SignatureType.RSA_2048_SHA256: - ct.SignatureSize = 0x100; - ct.PaddingSize = 0x3C; - break; - case SignatureType.ECDSA_SHA1: - case SignatureType.ECDSA_SHA256: - ct.SignatureSize = 0x03C; - ct.PaddingSize = 0x40; - break; - default: - return null; - } - - ct.Signature = reader.ReadBytes(ct.SignatureSize); - reader.ReadBytes(ct.PaddingSize); // Padding - byte[] issuerBytes = reader.ReadBytes(0x40); - ct.Issuer = Encoding.ASCII.GetString(issuerBytes); - ct.KeyType = (PublicKeyType)reader.ReadUInt32(); - byte[] nameBytes = reader.ReadBytes(0x40); - ct.Name = Encoding.ASCII.GetString(nameBytes); - ct.ExpirationTime = reader.ReadUInt32(); - - switch (ct.KeyType) - { - case PublicKeyType.RSA_4096: - ct.RSAModulus = reader.ReadBytes(0x200); - ct.RSAPublicExponent = reader.ReadUInt32(); - ct.RSAPadding = reader.ReadBytes(0x34); - break; - case PublicKeyType.RSA_2048: - ct.RSAModulus = reader.ReadBytes(0x100); - ct.RSAPublicExponent = reader.ReadUInt32(); - ct.RSAPadding = reader.ReadBytes(0x34); - break; - case PublicKeyType.EllipticCurve: - ct.ECCPublicKey = reader.ReadBytes(0x3C); - ct.ECCPadding = reader.ReadBytes(0x3C); - break; - default: - return null; - } - - return ct; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get content chunk record, if possible - /// - /// BinaryReader representing the input stream - /// Content chunk record object, null on error - private static ContentChunkRecord? ReadContentChunkRecord(BinaryReader reader) - { - var ccr = new ContentChunkRecord(); - - try - { - ccr.ContentId = reader.ReadUInt32(); - ccr.ContentIndex = (ContentIndex)reader.ReadUInt16(); - ccr.ContentType = (TMDContentType)reader.ReadUInt16(); - ccr.ContentSize = reader.ReadUInt64(); - ccr.SHA256Hash = reader.ReadBytes(0x20); - return ccr; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get content info record, if possible - /// - /// BinaryReader representing the input stream - /// Content info record object, null on error - private static ContentInfoRecord? ReadContentInfoRecord(BinaryReader reader) - { - var cir = new ContentInfoRecord(); - - try - { - cir.ContentIndexOffset = reader.ReadUInt16(); - cir.ContentCommandCount = reader.ReadUInt16(); - cir.UnhashedContentRecordsSHA256Hash = reader.ReadBytes(0x20); - return cir; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get an DevelopmentCardInfo header, if possible - /// - /// BinaryReader representing the input stream - /// DevelopmentCardInfo object, null on error - private static DevelopmentCardInfoHeader? ReadDevelopmentCardInfoHeader(BinaryReader reader) - { - var header = new DevelopmentCardInfoHeader(); - - try - { - header.CardDeviceReserved1 = reader.ReadBytes(0x200); - header.TitleKey = reader.ReadBytes(0x10); - header.CardDeviceReserved2 = reader.ReadBytes(0xF0); - - return header; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get an ExeFS file header, if possible - /// - /// BinaryReader representing the input stream - /// ExeFS file header object, null on error - private static ExeFSFileHeader? ReadExeFSFileHeader(BinaryReader reader) - { - var header = new ExeFSFileHeader(); - - try - { - byte[] fileNameBytes = reader.ReadBytes(8); - header.FileName = Encoding.ASCII.GetString(fileNameBytes); - header.FileOffset = reader.ReadUInt32(); - header.FileSize = reader.ReadUInt32(); - return header; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get inital data, if possible - /// - /// BinaryReader representing the input stream - /// Initial data object, null on error - private static InitialData? ReadInitialData(BinaryReader reader) - { - var id = new InitialData(); - - try - { - id.CardSeedKeyY = reader.ReadBytes(0x10); - id.EncryptedCardSeed = reader.ReadBytes(0x10); - id.CardSeedAESMAC = reader.ReadBytes(0x10); - id.CardSeedNonce = reader.ReadBytes(0x0C); - id.Reserved = reader.ReadBytes(0xC4); - id.BackupHeader = ReadNCCHHeader(reader, readSignature: false); - - return id; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get the Metafile data, if possible - /// - /// BinaryReader representing the input stream - /// Metafile data object, null on error - private static MetaData? ReadMetaData(BinaryReader reader) - { - var metaData = new MetaData(); - - try - { - metaData.TitleIDDependencyList = reader.ReadBytes(0x180); - metaData.Reserved1 = reader.ReadBytes(0x180); - metaData.CoreVersion = reader.ReadUInt32(); - metaData.Reserved2 = reader.ReadBytes(0xFC); - metaData.IconData = reader.ReadBytes(0x36C0); - - return metaData; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get an NCCH header flags, if possible - /// - /// BinaryReader representing the input stream - /// NCCH header flags object, null on error - private static NCCHHeaderFlags? ReadNCCHHeaderFlags(BinaryReader reader) - { - var flags = new NCCHHeaderFlags(); - - try - { - flags.Reserved0 = reader.ReadByte(); - flags.Reserved1 = reader.ReadByte(); - flags.Reserved2 = reader.ReadByte(); - flags.CryptoMethod = (CryptoMethod)reader.ReadByte(); - flags.ContentPlatform = (ContentPlatform)reader.ReadByte(); - flags.MediaPlatformIndex = (ContentType)reader.ReadByte(); - flags.ContentUnitSize = reader.ReadByte(); - flags.BitMasks = (BitMasks)reader.ReadByte(); - return flags; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get an NCSD header, if possible - /// - /// BinaryReader representing the input stream - /// NCSD header object, null on error - private static NCSDHeader? ReadNCSDHeader(BinaryReader reader) - { - var header = new NCSDHeader(); - - try - { - header.RSA2048Signature = reader.ReadBytes(0x100); - - if (new string(reader.ReadChars(4)) != NCSDMagicNumber) - return null; - - header.ImageSizeInMediaUnits = reader.ReadUInt32(); - header.MediaId = reader.ReadBytes(8); - header.PartitionsFSType = (FilesystemType)reader.ReadUInt64(); - header.PartitionsCryptType = reader.ReadBytes(8); - - header.PartitionsTable = new PartitionTableEntry[8]; - for (int i = 0; i < 8; i++) - { - header.PartitionsTable[i] = ReadPartitionTableEntry(reader)!; - } - - if (header.PartitionsFSType == FilesystemType.Normal - || header.PartitionsFSType == FilesystemType.None) - { - header.ExheaderHash = reader.ReadBytes(0x20); - header.AdditionalHeaderSize = reader.ReadUInt32(); - header.SectorZeroOffset = reader.ReadUInt32(); - header.PartitionFlags = reader.ReadBytes(8); - - header.PartitionIdTable = new ulong[8]; - for (int i = 0; i < 8; i++) - { - header.PartitionIdTable[i] = reader.ReadUInt64(); - } - - header.Reserved1 = reader.ReadBytes(0x20); - header.Reserved2 = reader.ReadBytes(0xE); - header.FirmUpdateByte1 = reader.ReadByte(); - header.FirmUpdateByte2 = reader.ReadByte(); - } - else if (header.PartitionsFSType == FilesystemType.FIRM) - { - header.Unknown = reader.ReadBytes(0x5E); - header.EncryptedMBR = reader.ReadBytes(0x42); - } - - return header; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get partition table entry, if possible - /// - /// BinaryReader representing the input stream - /// Partition table entry object, null on error - private static PartitionTableEntry? ReadPartitionTableEntry(BinaryReader reader) - { - var entry = new PartitionTableEntry(); - - try - { - entry.Offset = reader.ReadUInt32(); - entry.Length = reader.ReadUInt32(); - return entry; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get ticket, if possible - /// - /// BinaryReader representing the input stream - /// Ticket size from the header - /// Ticket object, null on error - private static Ticket? ReadTicket(BinaryReader reader, uint ticketSize) - { - var tk = new Ticket(); - - try - { - long startingPosition = reader.BaseStream.Position; - - tk.SignatureType = (SignatureType)reader.ReadUInt32(); - switch (tk.SignatureType) - { - case SignatureType.RSA_4096_SHA1: - case SignatureType.RSA_4096_SHA256: - tk.SignatureSize = 0x200; - tk.PaddingSize = 0x3C; - break; - case SignatureType.RSA_2048_SHA1: - case SignatureType.RSA_2048_SHA256: - tk.SignatureSize = 0x100; - tk.PaddingSize = 0x3C; - break; - case SignatureType.ECDSA_SHA1: - case SignatureType.ECDSA_SHA256: - tk.SignatureSize = 0x03C; - tk.PaddingSize = 0x40; - break; - default: - return null; - } - - tk.Signature = reader.ReadBytes(tk.SignatureSize); - reader.ReadBytes(tk.PaddingSize); // Padding - byte[] issuerBytes = reader.ReadBytes(0x40); - tk.Issuer = Encoding.ASCII.GetString(issuerBytes); - tk.ECCPublicKey = reader.ReadBytes(0x3C); - tk.Version = reader.ReadByte(); - tk.CaCrlVersion = reader.ReadByte(); - tk.SignerCrlVersion = reader.ReadByte(); - tk.TitleKey = reader.ReadBytes(0x10); - tk.Reserved1 = reader.ReadByte(); - tk.TicketID = reader.ReadUInt64(); - tk.ConsoleID = reader.ReadUInt32(); - tk.TitleID = reader.ReadUInt64(); - tk.Reserved2 = reader.ReadBytes(2); - tk.TicketTitleVersion = reader.ReadUInt16(); - tk.Reserved3 = reader.ReadBytes(8); - tk.LicenseType = reader.ReadByte(); - tk.CommonKeyYIndex = reader.ReadByte(); - tk.Reserved4 = reader.ReadBytes(0x2A); - tk.eShopAccountID = reader.ReadUInt32(); - tk.Reserved5 = reader.ReadByte(); - tk.Audit = reader.ReadByte(); - tk.Reserved6 = reader.ReadBytes(0x42); - - tk.Limits = new uint[0x10]; - for (int i = 0; i < 0x10; i++) - { - tk.Limits[i] = reader.ReadUInt32(); - } - - reader.ReadBytes(4); // Seek to size in Content Index - tk.ContentIndexSize = BitConverter.ToUInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); - reader.BaseStream.Seek(-8, SeekOrigin.Current); - tk.ContentIndex = reader.ReadBytes((int)tk.ContentIndexSize); - if (reader.BaseStream.Position % 64 != 0) - reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current); - - if (ticketSize > (reader.BaseStream.Position - startingPosition) + (2 * 0x200)) - { - tk.CertificateChain = new Certificate[2]; - tk.CertificateChain[0] = ReadCertificate(reader)!; // Ticket - tk.CertificateChain[1] = ReadCertificate(reader)!; // CA - } - - return tk; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get ticket metadata, if possible - /// - /// BinaryReader representing the input stream - /// Metadata size from the header - /// Title metadata object, null on error - private static TitleMetadata? ReadTitleMetadata(BinaryReader reader, uint metadataSize) - { - var tm = new TitleMetadata(); - - try - { - long startingPosition = reader.BaseStream.Position; - - tm.SignatureType = (SignatureType)reader.ReadUInt32(); - switch (tm.SignatureType) - { - case SignatureType.RSA_4096_SHA1: - case SignatureType.RSA_4096_SHA256: - tm.SignatureSize = 0x200; - tm.PaddingSize = 0x3C; - break; - case SignatureType.RSA_2048_SHA1: - case SignatureType.RSA_2048_SHA256: - tm.SignatureSize = 0x100; - tm.PaddingSize = 0x3C; - break; - case SignatureType.ECDSA_SHA1: - case SignatureType.ECDSA_SHA256: - tm.SignatureSize = 0x03C; - tm.PaddingSize = 0x40; - break; - } - - tm.Signature = reader.ReadBytes(tm.SignatureSize); - tm.Padding1 = reader.ReadBytes(tm.PaddingSize); - byte[] issuerBytes = reader.ReadBytes(0x40); - tm.Issuer = Encoding.ASCII.GetString(issuerBytes); - tm.Version = reader.ReadByte(); - tm.CaCrlVersion = reader.ReadByte(); - tm.SignerCrlVersion = reader.ReadByte(); - tm.Reserved1 = reader.ReadByte(); - tm.SystemVersion = reader.ReadUInt64(); - tm.TitleID = reader.ReadUInt64(); - tm.TitleType = reader.ReadUInt32(); - tm.GroupID = reader.ReadUInt16(); - tm.SaveDataSize = reader.ReadUInt32(); - tm.SRLPrivateSaveDataSize = reader.ReadUInt32(); - tm.Reserved2 = reader.ReadBytes(4); - tm.SRLFlag = reader.ReadByte(); - tm.Reserved3 = reader.ReadBytes(0x31); - tm.AccessRights = reader.ReadUInt32(); - tm.TitleVersion = reader.ReadUInt16(); - tm.ContentCount = BitConverter.ToUInt16(reader.ReadBytes(2).Reverse().ToArray(), 0); - tm.BootContent = reader.ReadUInt16(); - tm.Padding2 = reader.ReadBytes(2); - tm.SHA256HashContentInfoRecords = reader.ReadBytes(0x20); - - tm.ContentInfoRecords = new ContentInfoRecord[64]; - for (int i = 0; i < 64; i++) - { - tm.ContentInfoRecords[i] = ReadContentInfoRecord(reader)!; - } - - tm.ContentChunkRecords = new ContentChunkRecord[tm.ContentCount]; - for (int i = 0; i < tm.ContentCount; i++) - { - tm.ContentChunkRecords[i] = ReadContentChunkRecord(reader)!; - } - - if (metadataSize > (reader.BaseStream.Position - startingPosition) + (2 * 0x200)) - { - tm.CertificateChain = new Certificate[2]; - tm.CertificateChain[0] = ReadCertificate(reader)!; // TMD - tm.CertificateChain[1] = ReadCertificate(reader)!; // CA - } - - return tm; - } - catch - { - return null; - } - } - - #endregion - - #region Writing - - /// - /// Write NCCH header flags to stream, if possible - /// - /// BinaryWriter representing the output stream - public static void Write(NCCHHeaderFlags flags, BinaryWriter writer) - { - try - { - writer.Write(flags.Reserved0); - writer.Write(flags.Reserved1); - writer.Write(flags.Reserved2); - writer.Write((byte)flags.CryptoMethod); - writer.Write((byte)flags.ContentPlatform); - writer.Write((byte)flags.MediaPlatformIndex); - writer.Write(flags.ContentUnitSize); - writer.Write((byte)flags.BitMasks); - - } - catch { } - } - - #endregion - } -} \ No newline at end of file diff --git a/NDecrypt.N3DS/ThreeDSTool.cs b/NDecrypt.N3DS/ThreeDSTool.cs index 8a78112..62b9e7e 100644 --- a/NDecrypt.N3DS/ThreeDSTool.cs +++ b/NDecrypt.N3DS/ThreeDSTool.cs @@ -5,6 +5,7 @@ using System.Numerics; using NDecrypt.Core; using SabreTools.Models.N3DS; using static NDecrypt.Core.Helper; +using N3DSDeserializer = SabreTools.Serialization.Deserializers.N3DS; namespace NDecrypt.N3DS { @@ -72,7 +73,7 @@ namespace NDecrypt.N3DS using var writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)); // Deserialize the cart information - var cart = Serializer.ReadCart(reader, decryptArgs.Development); + var cart = ReadCart(reader); if (cart?.Header == null || cart?.CardInfoHeader?.InitialData?.BackupHeader == null) { Console.WriteLine("Error: Not a 3DS cart image!"); @@ -303,7 +304,7 @@ namespace NDecrypt.N3DS uint exeFsOffset = (partitionOffsetMU + exeFsOffsetMU + 1) * cart.MediaUnitSize(); reader.BaseStream.Seek(exeFsHeaderOffset, SeekOrigin.Begin); - var exefsHeader = Serializer.ReadExeFSHeader(reader); + var exefsHeader = ReadExeFSHeader(reader); // If the header failed to read, log and return if (exefsHeader == null) @@ -744,5 +745,88 @@ namespace NDecrypt.N3DS } #endregion + + #region Serialization + + /// + /// Read from a stream and get N3DS cart image, if possible + /// + /// BinaryReader representing the input stream + /// N3DS cart image object, null on error + private static Cart? ReadCart(BinaryReader reader) + { + var cart = new Cart(); + + try + { + cart.Header = N3DSDeserializer.ParseNCSDHeader(reader.BaseStream); + if (cart.Header == null) + return null; + + if (cart.Header.PartitionsFSType == FilesystemType.Normal + || cart.Header.PartitionsFSType == FilesystemType.None) + { + cart.CardInfoHeader = N3DSDeserializer.ParseCardInfoHeader(reader.BaseStream); + if (cart.CardInfoHeader == null) + return null; + } + + cart.Partitions = new NCCHHeader[8]; + for (int i = 0; i < 8; i++) + { + // Check the entry is valid + var tableEntry = cart.Header.PartitionsTable![i]; + if (tableEntry == null || tableEntry.Offset == 0 || tableEntry.Length == 0) + continue; + + // Seek to the beginning of the NCCH partition + long offset = tableEntry.Offset * cart.Header.ImageSizeInMediaUnits; + reader.BaseStream.Seek(offset, SeekOrigin.Begin); + + cart.Partitions[i] = N3DSDeserializer.ParseNCCHHeader(reader.BaseStream, skipSignature: false); + } + + return cart; + } + catch + { + return null; + } + } + + /// + /// Read from a stream and get an ExeFS header, if possible + /// + /// BinaryReader representing the input stream + /// ExeFS header object, null on error + private static ExeFSHeader? ReadExeFSHeader(BinaryReader reader) + { + var header = new ExeFSHeader(); + + try + { + header.FileHeaders = new ExeFSFileHeader[10]; + for (int i = 0; i < 10; i++) + { + header.FileHeaders[i] = N3DSDeserializer.ParseExeFSFileHeader(reader.BaseStream)!; + } + + header.Reserved = reader.ReadBytes(0x20); + + header.FileHashes = new byte[10][]; + for (int i = 0; i < 10; i++) + { + header.FileHashes[9 - i] = reader.ReadBytes(0x20); + } + + return header; + } + catch + { + return null; + } + } + + #endregion } } diff --git a/NDecrypt.Nitro/DSTool.cs b/NDecrypt.Nitro/DSTool.cs index 68fca22..2307b16 100644 --- a/NDecrypt.Nitro/DSTool.cs +++ b/NDecrypt.Nitro/DSTool.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using NDecrypt.Core; using SabreTools.Models.Nitro; +using NitroDeserializer = SabreTools.Serialization.Deserializers.Nitro; namespace NDecrypt.Nitro { @@ -43,7 +44,7 @@ namespace NDecrypt.Nitro using var writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)); // Deserialize the cart information - Cart? cart = Serializer.ReadCart(reader); + Cart? cart = ReadCart(reader); if (cart == null) { Console.WriteLine("Error: Not a DS or DSi Rom!"); @@ -360,5 +361,26 @@ namespace NDecrypt.Nitro _cardHash[i + 18 + 1] = tmp2; } } + + #region Serialization + + /// + /// Read from a stream and get an NDS/NDSi Cart, if possible + /// + /// BinaryReader representing the input stream + /// NDS/NDSi Cart object, null on error + private static Cart? ReadCart(BinaryReader reader) + { + try + { + return NitroDeserializer.DeserializeStream(reader.BaseStream); + } + catch + { + return null; + } + } + + #endregion } } diff --git a/NDecrypt.Nitro/NDecrypt.Nitro.csproj b/NDecrypt.Nitro/NDecrypt.Nitro.csproj index 13bda8d..3024b21 100644 --- a/NDecrypt.Nitro/NDecrypt.Nitro.csproj +++ b/NDecrypt.Nitro/NDecrypt.Nitro.csproj @@ -40,7 +40,8 @@ - + + \ No newline at end of file diff --git a/NDecrypt.Nitro/Serializer.cs b/NDecrypt.Nitro/Serializer.cs deleted file mode 100644 index 7100277..0000000 --- a/NDecrypt.Nitro/Serializer.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System.IO; -using System.Text; -using SabreTools.Models.Nitro; - -namespace NDecrypt.Nitro -{ - internal static class Serializer - { - /// - /// Read from a stream and get an NDS/NDSi Cart, if possible - /// - /// BinaryReader representing the input stream - /// NDS/NDSi Cart object, null on error - public static Cart? ReadCart(BinaryReader reader) - { - var cart = new Cart(); - - try - { - cart.CommonHeader = ReadCommonHeader(reader); - if (cart.CommonHeader == null) - return null; - - // If we have a DSi compatible title - if (cart.CommonHeader.UnitCode == Unitcode.NDSPlusDSi - || cart.CommonHeader.UnitCode == Unitcode.DSi) - { - cart.ExtendedDSiHeader = ReadExtendedDSiHeader(reader); - if (cart.ExtendedDSiHeader == null) - return null; - } - - return cart; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get a common header, if possible - /// - /// BinaryReader representing the input stream - /// Common header object, null on error - private static CommonHeader? ReadCommonHeader(BinaryReader reader) - { - var header = new CommonHeader(); - - try - { - byte[] gameTitleBytes = reader.ReadBytes(0x0C); - header.GameTitle = Encoding.ASCII.GetString(gameTitleBytes); - header.GameCode = reader.ReadUInt32(); - byte[] makerCodeBytes = reader.ReadBytes(2); - header.MakerCode = Encoding.ASCII.GetString(makerCodeBytes); - header.UnitCode = (Unitcode)reader.ReadByte(); - header.EncryptionSeedSelect = reader.ReadByte(); - header.DeviceCapacity = reader.ReadByte(); - header.Reserved1 = reader.ReadBytes(7); - header.GameRevision = reader.ReadUInt16(); - header.RomVersion = reader.ReadByte(); - header.InternalFlags = reader.ReadByte(); - header.ARM9RomOffset = reader.ReadUInt32(); - header.ARM9EntryAddress = reader.ReadUInt32(); - header.ARM9LoadAddress = reader.ReadUInt32(); - header.ARM9Size = reader.ReadUInt32(); - header.ARM7RomOffset = reader.ReadUInt32(); - header.ARM7EntryAddress = reader.ReadUInt32(); - header.ARM7LoadAddress = reader.ReadUInt32(); - header.ARM7Size = reader.ReadUInt32(); - header.FileNameTableOffset = reader.ReadUInt32(); - header.FileNameTableLength = reader.ReadUInt32(); - header.FileAllocationTableOffset = reader.ReadUInt32(); - header.FileAllocationTableLength = reader.ReadUInt32(); - header.ARM9OverlayOffset = reader.ReadUInt32(); - header.ARM9OverlayLength = reader.ReadUInt32(); - header.ARM7OverlayOffset = reader.ReadUInt32(); - header.ARM7OverlayLength = reader.ReadUInt32(); - header.SecureDisable = reader.ReadBytes(8); - header.NTRRegionRomSize = reader.ReadUInt32(); - header.HeaderSize = reader.ReadUInt32(); - header.Reserved2 = reader.ReadBytes(56); - header.NintendoLogo = reader.ReadBytes(156); - header.NintendoLogoCRC = reader.ReadUInt16(); - header.DebuggerReserved = reader.ReadBytes(0x20); - - return header; - } - catch - { - return null; - } - } - - /// - /// Read from a stream and get an extended DSi header, if possible - /// - /// BinaryReader representing the input stream - /// Extended DSi header object, null on error - private static ExtendedDSiHeader? ReadExtendedDSiHeader(BinaryReader reader) - { - var header = new ExtendedDSiHeader(); - - try - { - header.GlobalMBK15Settings = new uint[5]; - for (int i = 0; i < 5; i++) - { - header.GlobalMBK15Settings[i] = reader.ReadUInt32(); - } - header.LocalMBK68SettingsARM9 = new uint[3]; - for (int i = 0; i < 3; i++) - { - header.LocalMBK68SettingsARM9[i] = reader.ReadUInt32(); - } - header.LocalMBK68SettingsARM7 = new uint[3]; - for (int i = 0; i < 3; i++) - { - header.LocalMBK68SettingsARM7[i] = reader.ReadUInt32(); - } - header.GlobalMBK9Setting = reader.ReadUInt32(); - header.RegionFlags = reader.ReadUInt32(); - header.AccessControl = reader.ReadUInt32(); - header.ARM7SCFGEXTMask = reader.ReadUInt32(); - header.ReservedFlags = reader.ReadUInt32(); - header.ARM9iRomOffset = reader.ReadUInt32(); - header.Reserved3 = reader.ReadUInt32(); - header.ARM9iLoadAddress = reader.ReadUInt32(); - header.ARM9iSize = reader.ReadUInt32(); - header.ARM7iRomOffset = reader.ReadUInt32(); - header.Reserved4 = reader.ReadUInt32(); - header.ARM7iLoadAddress = reader.ReadUInt32(); - header.ARM7iSize = reader.ReadUInt32(); - header.DigestNTRRegionOffset = reader.ReadUInt32(); - header.DigestNTRRegionLength = reader.ReadUInt32(); - header.DigestTWLRegionOffset = reader.ReadUInt32(); - header.DigestTWLRegionLength = reader.ReadUInt32(); - header.DigestSectorHashtableRegionOffset = reader.ReadUInt32(); - header.DigestSectorHashtableRegionLength = reader.ReadUInt32(); - header.DigestBlockHashtableRegionOffset = reader.ReadUInt32(); - header.DigestBlockHashtableRegionLength = reader.ReadUInt32(); - header.DigestSectorSize = reader.ReadUInt32(); - header.DigestBlockSectorCount = reader.ReadUInt32(); - header.IconBannerSize = reader.ReadUInt32(); - header.Unknown1 = reader.ReadUInt32(); - header.ModcryptArea1Offset = reader.ReadUInt32(); - header.ModcryptArea1Size = reader.ReadUInt32(); - header.ModcryptArea2Offset = reader.ReadUInt32(); - header.ModcryptArea2Size = reader.ReadUInt32(); - header.TitleID = reader.ReadBytes(8); - header.DSiWarePublicSavSize = reader.ReadUInt32(); - header.DSiWarePrivateSavSize = reader.ReadUInt32(); - header.ReservedZero = reader.ReadBytes(176); - header.Unknown2 = reader.ReadBytes(0x10); - header.ARM9WithSecureAreaSHA1HMACHash = reader.ReadBytes(20); - header.ARM7SHA1HMACHash = reader.ReadBytes(20); - header.DigestMasterSHA1HMACHash = reader.ReadBytes(20); - header.BannerSHA1HMACHash = reader.ReadBytes(20); - header.ARM9iDecryptedSHA1HMACHash = reader.ReadBytes(20); - header.ARM7iDecryptedSHA1HMACHash = reader.ReadBytes(20); - header.Reserved5 = reader.ReadBytes(40); - header.ARM9NoSecureAreaSHA1HMACHash = reader.ReadBytes(20); - header.Reserved6 = reader.ReadBytes(2636); - header.ReservedAndUnchecked = reader.ReadBytes(0x180); - header.RSASignature = reader.ReadBytes(0x80); - - return header; - } - catch - { - return null; - } - } - } -}