From dc68aa5046b8246c8e4f948bdca3599202ff44cb Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 7 Apr 2019 22:45:27 -0700 Subject: [PATCH] Summary overhaul --- 3DSDecrypt/3DSDecrypt.csproj | 1 + 3DSDecrypt/Data/Enums.cs | 97 +++++++++ 3DSDecrypt/Headers/ARM11KernelCapabilities.cs | 41 +++- .../Headers/ARM11LocalSystemCapabilities.cs | 88 ++++++-- 3DSDecrypt/Headers/ARM9AccessControl.cs | 21 +- 3DSDecrypt/Headers/AccessControlInfo.cs | 22 +- 3DSDecrypt/Headers/CXIExtendedHeader.cs | 34 +++- 3DSDecrypt/Headers/CodeSetInfo.cs | 22 +- 3DSDecrypt/Headers/ExeFSFileHeader.cs | 28 ++- 3DSDecrypt/Headers/ExeFSHeader.cs | 22 +- 3DSDecrypt/Headers/NCCHHeader.cs | 192 ++++++++++++++---- 3DSDecrypt/Headers/NCCHHeaderFlags.cs | 60 +++++- 3DSDecrypt/Headers/NCSDHeader.cs | 129 +++++++++--- 3DSDecrypt/Headers/PartitionTableEntry.cs | 16 ++ 3DSDecrypt/Headers/RomFSHeader.cs | 127 ++++++++++++ 3DSDecrypt/Headers/StorageInfo.cs | 37 +++- 3DSDecrypt/Headers/SystemControlInfo.cs | 77 +++++-- 3DSDecrypt/Headers/SystemInfo.cs | 22 +- 3DSDecrypt/ThreeDSTool.cs | 2 +- 19 files changed, 900 insertions(+), 138 deletions(-) create mode 100644 3DSDecrypt/Headers/RomFSHeader.cs diff --git a/3DSDecrypt/3DSDecrypt.csproj b/3DSDecrypt/3DSDecrypt.csproj index 9a7ed25..fb1bdd9 100644 --- a/3DSDecrypt/3DSDecrypt.csproj +++ b/3DSDecrypt/3DSDecrypt.csproj @@ -49,6 +49,7 @@ + diff --git a/3DSDecrypt/Data/Enums.cs b/3DSDecrypt/Data/Enums.cs index 7f2c66e..d7af04b 100644 --- a/3DSDecrypt/Data/Enums.cs +++ b/3DSDecrypt/Data/Enums.cs @@ -2,6 +2,61 @@ namespace ThreeDS.Data { + [Flags] + public enum ARM9AccessControlDescriptors : byte + { + MountNandRoot = 0x01, + MountNandroWriteAccess = 0x02, + MountTwlnRoot = 0x04, + MountWnandRoot = 0x08, + MountCardSPI = 0x0F, + UseSDIF3 = 0x10, + CreateSeed = 0x20, + UseCardSPI = 0x40, + SDApplication = 0x80, + MoundSdmcWriteAccess = 0xF0, + } + + [Flags] + public enum ARM11LSCFlag0 : byte + { + IdealProcessor = 0x01 | 0x02, + AffinityMask = 0x04 | 0x08, + + /// + /// Value Description + /// 0 Prod (64MB of usable application memory) + /// 1 Undefined (unusable) + /// 2 Dev1 (96MB of usable application memory) + /// 3 Dev2 (80MB of usable application memory) + /// 4 Dev3 (72MB of usable application memory) + /// 5 Dev4 (32MB of usable application memory) + /// 6-7 Undefined Same as Prod? + /// + Old3DSSystemMode = 0x0F | 0x10 | 0x20 | 0x40, + } + + [Flags] + public enum ARM11LSCFlag1 : byte + { + EnableL2Cache = 0x01, + Cpuspeed_804MHz = 0x02, + } + + [Flags] + public enum ARM11LSCFlag2 : byte + { + /// + /// Value Description + /// 0 Legacy (use Old3DS system mode) + /// 1 Prod (124MB of usable application memory) + /// 2 Dev1 (178MB of usable application memory) + /// 3 Dev2 (124MB of usable application memory) + /// 4-7 Undefined Same as Prod? + /// + New3DSSystemMode = 0x01 | 0x02 | 0x04 | 0x08, + } + [Flags] public enum BitMasks : byte { @@ -36,6 +91,33 @@ namespace ThreeDS.Data NineSix = 0x0B, } + [Flags] + public enum FilesystemAccessInfo : ulong + { + CategorySystemApplication = 0x1, + CategoryHardwareCheck = 0x2, + CategoryFilesystemTool = 0x4, + Debug = 0x8, + TWLCardBackup = 0x10, + TWLNANDData = 0x20, + BOSS = 0x40, + sdmcRoot = 0x80, + Core = 0x100, + nandRootroReadOnly = 0x200, + nandRootrw = 0x400, + nandrootroWriteAccess = 0x800, + CategorySystemSettings = 0x1000, + Cardboard = 0x2000, + ExportImportIVS = 0x4000, + sdmcRootWriteOnly = 0x8000, + SwitchCleanup = 0x10000, // Introduced in 3.0.0? + SavedataMove = 0x20000, // Introduced in 5.0.0 + Shop = 0x40000, // Introduced in 5.0.0 + Shell = 0x80000, // Introduced in 5.0.0 + CategoryHomeMenu = 0x100000, // Introduced in 6.0.0 + SeedDB = 0x200000, // Introduced in 9.6.0-X FIRM. Home Menu has this bit set starting with 9.6.0-X. + } + public enum FilesystemType : ulong { None = 0, @@ -82,4 +164,19 @@ namespace ThreeDS.Data MediaUnitSize = 0x06, MediaCardDevice2X = 0x07, } + + public enum ResourceLimitCategory + { + APPLICATION = 0, + SYS_APPLET = 1, + LIB_APPLET = 2, + OTHER = 3, + } + + [Flags] + public enum StorageInfoOtherAttributes : byte + { + NotUseROMFS = 0x01, + UseExtendedSavedataAccess = 0x02, + } } diff --git a/3DSDecrypt/Headers/ARM11KernelCapabilities.cs b/3DSDecrypt/Headers/ARM11KernelCapabilities.cs index c00cb33..b28e782 100644 --- a/3DSDecrypt/Headers/ARM11KernelCapabilities.cs +++ b/3DSDecrypt/Headers/ARM11KernelCapabilities.cs @@ -4,15 +4,52 @@ namespace ThreeDS.Headers { public class ARM11KernelCapabilities { - public byte[][] Descriptors = new byte[28][]; - public byte[] Reserved = new byte[0x10]; + /// + /// Descriptors + /// ------------------- + /// Pattern of bits 20-31 Type Fields + /// 0b1110xxxxxxxx Interrupt info + /// 0b11110xxxxxxx System call mask Bits 24-26: System call mask table index; Bits 0-23: mask + /// 0b1111110xxxxx Kernel release version Bits 8-15: Major version; Bits 0-7: Minor version + /// 0b11111110xxxx Handle table size Bits 0-18: size + /// 0b111111110xxx Kernel flags + /// 0b11111111100x Map address range Describes a memory mapping like the 0b111111111110 descriptor, but an entire range rather than a single page is mapped.Another 0b11111111100x descriptor must follow this one to denote the(exclusive) end of the address range to map. + /// 0b111111111110 Map memory page Bits 0-19: page index to map(virtual address >> 12; the physical address is determined per-page according to Memory layout); Bit 20: Map read-only(otherwise read-write) + /// + /// ARM11 Kernel Flags + /// ------------------- + /// Bit Description + /// 0 Allow debug + /// 1 Force debug + /// 2 Allow non-alphanum + /// 3 Shared page writing + /// 4 Privilege priority + /// 5 Allow main() args + /// 6 Shared device memory + /// 7 Runnable on sleep + /// 8-11 Memory type(1: application, 2: system, 3: base) + /// 12 Special memory + /// 13 Process has access to CPU core 2 (New3DS only) + /// + public byte[][] Descriptors { get; private set; } + /// + /// Reserved + /// + public byte[] Reserved { get; private set; } + + /// + /// Read from a stream and get ARM11 kernel capabilities, if possible + /// + /// BinaryReader representing the input stream + /// ARM11 kernel capabilities object, null on error public static ARM11KernelCapabilities Read(BinaryReader reader) { ARM11KernelCapabilities kc = new ARM11KernelCapabilities(); try { + kc.Descriptors = new byte[28][]; for (int i = 0; i < 28; i++) kc.Descriptors[i] = reader.ReadBytes(4); diff --git a/3DSDecrypt/Headers/ARM11LocalSystemCapabilities.cs b/3DSDecrypt/Headers/ARM11LocalSystemCapabilities.cs index 49f9e8e..37d6e6a 100644 --- a/3DSDecrypt/Headers/ARM11LocalSystemCapabilities.cs +++ b/3DSDecrypt/Headers/ARM11LocalSystemCapabilities.cs @@ -1,22 +1,75 @@ using System.IO; +using ThreeDS.Data; namespace ThreeDS.Headers { public class ARM11LocalSystemCapabilities { - public byte[] ProgramID = new byte[8]; - public uint CoreVersion; - public byte Flag1; - public byte Flag2; - public byte Flag0; - public byte Priority; - public byte[][] ResourceLimitDescriptors = new byte[16][]; - public StorageInfo StorageInfo; - public byte[][] ServiceAccessControl = new byte[32][]; - public byte[][] ExtendedServiceAccessControl = new byte[2][]; - public byte[] Reserved = new byte[0xF]; - public byte ResourceLimitCategory; + /// + /// Program ID + /// + public byte[] ProgramID { get; private set; } + /// + /// Core version (The Title ID low of the required FIRM) + /// + public uint CoreVersion { get; private set; } + + /// + /// Flag1 (implemented starting from 8.0.0-18). + /// + public ARM11LSCFlag1 Flag1 { get; private set; } + + /// + /// Flag2 (implemented starting from 8.0.0-18). + /// + public ARM11LSCFlag2 Flag2 { get; private set; } + + /// + /// Flag0 + /// + public ARM11LSCFlag0 Flag0 { get; private set; } + + /// + /// Priority + /// + public byte Priority { get; private set; } + + /// + /// Resource limit descriptors. The first byte here controls the maximum allowed CpuTime. + /// + public byte[][] ResourceLimitDescriptors { get; private set; } + + /// + /// Storage info + /// + public StorageInfo StorageInfo { get; private set; } + + /// + /// Service access control + /// + public byte[][] ServiceAccessControl { get; private set; } + + /// + /// Extended service access control, support for this was implemented with 9.3.0-X. + /// + public byte[][] ExtendedServiceAccessControl { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved { get; private set; } + + /// + /// Resource limit category. (0 = APPLICATION, 1 = SYS_APPLET, 2 = LIB_APPLET, 3 = OTHER (sysmodules running under the BASE memregion)) + /// + public ResourceLimitCategory ResourceLimitCategory { get; private set; } + + /// + /// Read from a stream and get ARM11 local system capabilities, if possible + /// + /// BinaryReader representing the input stream + /// ARM11 local system capabilities object, null on error public static ARM11LocalSystemCapabilities Read(BinaryReader reader) { ARM11LocalSystemCapabilities lsc = new ARM11LocalSystemCapabilities(); @@ -25,24 +78,27 @@ namespace ThreeDS.Headers { lsc.ProgramID = reader.ReadBytes(8); lsc.CoreVersion = reader.ReadUInt32(); - lsc.Flag1 = reader.ReadByte(); - lsc.Flag2 = reader.ReadByte(); - lsc.Flag0 = reader.ReadByte(); + lsc.Flag1 = (ARM11LSCFlag1)reader.ReadByte(); + lsc.Flag2 = (ARM11LSCFlag2)reader.ReadByte(); + lsc.Flag0 = (ARM11LSCFlag0)reader.ReadByte(); lsc.Priority = reader.ReadByte(); + lsc.ResourceLimitDescriptors = new byte[16][]; for (int i = 0; i < 16; i++) lsc.ResourceLimitDescriptors[i] = reader.ReadBytes(2); lsc.StorageInfo = StorageInfo.Read(reader); + lsc.ServiceAccessControl = new byte[32][]; for (int i = 0; i < 32; i++) lsc.ServiceAccessControl[i] = reader.ReadBytes(8); + lsc.ExtendedServiceAccessControl = new byte[2][]; for (int i = 0; i < 2; i++) lsc.ExtendedServiceAccessControl[i] = reader.ReadBytes(8); lsc.Reserved = reader.ReadBytes(0xF); - lsc.ResourceLimitCategory = reader.ReadByte(); + lsc.ResourceLimitCategory = (ResourceLimitCategory)reader.ReadByte(); return lsc; } catch diff --git a/3DSDecrypt/Headers/ARM9AccessControl.cs b/3DSDecrypt/Headers/ARM9AccessControl.cs index ef19c5f..75e7452 100644 --- a/3DSDecrypt/Headers/ARM9AccessControl.cs +++ b/3DSDecrypt/Headers/ARM9AccessControl.cs @@ -1,19 +1,34 @@ using System.IO; +using ThreeDS.Data; namespace ThreeDS.Headers { public class ARM9AccessControl { - public byte[] Descriptors = new byte[15]; - public byte DescriptorVersion; + /// + /// Descriptors + /// + public ARM9AccessControlDescriptors[] Descriptors { get; private set; } + /// + /// ARM9 Descriptor Version. Originally this value had to be ≥ 2. Starting with 9.3.0-X this value has to be either value 2 or value 3. + /// + public byte DescriptorVersion { get; private set; } + + /// + /// Read from a stream and get ARM9 access control, if possible + /// + /// BinaryReader representing the input stream + /// ARM9 access control object, null on error public static ARM9AccessControl Read(BinaryReader reader) { ARM9AccessControl ac = new ARM9AccessControl(); try { - ac.Descriptors = reader.ReadBytes(15); + ac.Descriptors = new ARM9AccessControlDescriptors[15]; + for (int i = 0; i < 15; i++) + ac.Descriptors[i] = (ARM9AccessControlDescriptors)reader.ReadByte(); ac.DescriptorVersion = reader.ReadByte(); return ac; } diff --git a/3DSDecrypt/Headers/AccessControlInfo.cs b/3DSDecrypt/Headers/AccessControlInfo.cs index ae0f09f..5adbc26 100644 --- a/3DSDecrypt/Headers/AccessControlInfo.cs +++ b/3DSDecrypt/Headers/AccessControlInfo.cs @@ -4,10 +4,26 @@ namespace ThreeDS.Headers { public class AccessControlInfo { - public ARM11LocalSystemCapabilities ARM11LocalSystemCapabilities; - public ARM11KernelCapabilities ARM11KernelCapabilities; - public ARM9AccessControl ARM9AccessControl; + /// + /// ARM11 local system capabilities + /// + public ARM11LocalSystemCapabilities ARM11LocalSystemCapabilities { get; private set; } + /// + /// ARM11 kernel capabilities + /// + public ARM11KernelCapabilities ARM11KernelCapabilities { get; private set; } + + /// + /// ARM9 access control + /// + public ARM9AccessControl ARM9AccessControl { get; private set; } + + /// + /// Read from a stream and get access control info, if possible + /// + /// BinaryReader representing the input stream + /// Access control info object, null on error public static AccessControlInfo Read(BinaryReader reader) { AccessControlInfo aci = new AccessControlInfo(); diff --git a/3DSDecrypt/Headers/CXIExtendedHeader.cs b/3DSDecrypt/Headers/CXIExtendedHeader.cs index 391eb63..28d778f 100644 --- a/3DSDecrypt/Headers/CXIExtendedHeader.cs +++ b/3DSDecrypt/Headers/CXIExtendedHeader.cs @@ -4,12 +4,36 @@ namespace ThreeDS.Headers { public class CXIExtendedHeader { - public SystemControlInfo SCI; - public AccessControlInfo ACI; - public byte[] AccessDescSignature = new byte[0x100]; - public byte[] NCCHHDRPublicKey = new byte[0x100]; - public AccessControlInfo ACIForLimitations; + /// + /// SCI + /// + public SystemControlInfo SCI { get; private set; } + /// + /// ACI + /// + public AccessControlInfo ACI { get; private set; } + + /// + /// AccessDesc signature (RSA-2048-SHA256) + /// + public byte[] AccessDescSignature { get; private set; } + + /// + /// NCCH HDR RSA-2048 public key + /// + public byte[] NCCHHDRPublicKey { get; private set; } + + /// + /// ACI (for limitation of first ACI) + /// + public AccessControlInfo ACIForLimitations { get; private set; } + + /// + /// Read from a stream and get a CXI extended header, if possible + /// + /// BinaryReader representing the input stream + /// CXI extended header object, null on error public static CXIExtendedHeader Read(BinaryReader reader) { CXIExtendedHeader header = new CXIExtendedHeader(); diff --git a/3DSDecrypt/Headers/CodeSetInfo.cs b/3DSDecrypt/Headers/CodeSetInfo.cs index 5726c01..89b7aec 100644 --- a/3DSDecrypt/Headers/CodeSetInfo.cs +++ b/3DSDecrypt/Headers/CodeSetInfo.cs @@ -4,10 +4,26 @@ namespace ThreeDS.Headers { public class CodeSetInfo { - public byte[] Address = new byte[0x04]; - public uint PhysicalRegionSizeInPages; - public uint SizeInBytes; + /// + /// Address + /// + public byte[] Address { get; private set; } + /// + /// Physical region size (in page-multiples) + /// + public uint PhysicalRegionSizeInPages { get; private set; } + + /// + /// Size (in bytes) + /// + public uint SizeInBytes { get; private set; } + + /// + /// Read from a stream and get code set info, if possible + /// + /// BinaryReader representing the input stream + /// Code set info object, null on error public static CodeSetInfo Read(BinaryReader reader) { CodeSetInfo csi = new CodeSetInfo(); diff --git a/3DSDecrypt/Headers/ExeFSFileHeader.cs b/3DSDecrypt/Headers/ExeFSFileHeader.cs index 75b594f..2d3b892 100644 --- a/3DSDecrypt/Headers/ExeFSFileHeader.cs +++ b/3DSDecrypt/Headers/ExeFSFileHeader.cs @@ -9,13 +9,33 @@ namespace ThreeDS.Headers private const string codeSegment = ".code\0\0\0"; private readonly byte[] codeSegmentBytes = new byte[] { 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x00, 0x00, 0x00 }; - public byte[] FileName = new byte[8]; + /// + /// File name + /// + public byte[] FileName { get; private set; } public string ReadableFileName { get { return Encoding.ASCII.GetString(FileName); } } public bool IsCodeBinary { get { return Enumerable.SequenceEqual(FileName, codeSegmentBytes); } } - public uint FileOffset; - public uint FileSize; - public byte[] FileHash = new byte[0x20]; + /// + /// File offset + /// + public uint FileOffset { get; private set; } + + /// + /// File size + /// + public uint FileSize { get; private set; } + + /// + /// SHA256 hash calculated over the entire file contents + /// + public byte[] FileHash { get; set; } + + /// + /// Read from a stream and get an ExeFS file header, if possible + /// + /// BinaryReader representing the input stream + /// ExeFS file header object, null on error public static ExeFSFileHeader Read(BinaryReader reader) { ExeFSFileHeader header = new ExeFSFileHeader(); diff --git a/3DSDecrypt/Headers/ExeFSHeader.cs b/3DSDecrypt/Headers/ExeFSHeader.cs index a448275..5b775f5 100644 --- a/3DSDecrypt/Headers/ExeFSHeader.cs +++ b/3DSDecrypt/Headers/ExeFSHeader.cs @@ -4,25 +4,35 @@ namespace ThreeDS.Headers { public class ExeFSHeader { - public ExeFSFileHeader[] FileHeaders = new ExeFSFileHeader[10]; - public byte[] Reserved = new byte[0x20]; + /// + /// File headers (10 headers maximum, 16 bytes each) + /// + public ExeFSFileHeader[] FileHeaders { get; private set; } + /// + /// Reserved + /// + public byte[] Reserved { get; private set; } + + /// + /// 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 Read(BinaryReader reader) { ExeFSHeader header = new ExeFSHeader(); try { + header.FileHeaders = new ExeFSFileHeader[10]; for (int i = 0; i < 10; i++) header.FileHeaders[i] = ExeFSFileHeader.Read(reader); header.Reserved = reader.ReadBytes(0x20); for (int i = 0; i < 10; i++) - { - byte[] fileHash = reader.ReadBytes(0x20); - header.FileHeaders[9 - i].FileHash = fileHash; - } + header.FileHeaders[9 - i].FileHash = reader.ReadBytes(0x20); return header; } diff --git a/3DSDecrypt/Headers/NCCHHeader.cs b/3DSDecrypt/Headers/NCCHHeader.cs index aa91466..bb0b35e 100644 --- a/3DSDecrypt/Headers/NCCHHeader.cs +++ b/3DSDecrypt/Headers/NCCHHeader.cs @@ -8,51 +8,159 @@ namespace ThreeDS.Headers { private const string NCCHMagicNumber = "NCCH"; + /// + /// RSA-2048 signature of the NCCH header, using SHA-256. + /// public byte[] RSA2048Signature = new byte[0x100]; - public uint ContentSizeInMediaUnits; - public uint ContentSizeInBytes { get { return ContentSizeInMediaUnits * 0x200; } } - public byte[] PartitionId = new byte[8]; + + /// + /// Content size, in media units (1 media unit = 0x200 bytes) + /// + public uint ContentSizeInMediaUnits { get; private set; } + + /// + /// Partition ID + /// + public byte[] PartitionId { get; private set; } public byte[] PlainIV { get { return PartitionId.Concat(Constants.PlainCounter).ToArray(); } } public byte[] ExeFSIV { get { return PartitionId.Concat(Constants.ExefsCounter).ToArray(); } } public byte[] RomFSIV { get { return PartitionId.Concat(Constants.RomfsCounter).ToArray(); } } - public byte[] MakerCode = new byte[2]; - public byte[] Version = new byte[2]; - public byte[] VerificationHash = new byte[4]; - public byte[] ProgramId = new byte[4]; - public byte[] Reserved1 = new byte[0x10]; - public byte[] LogoRegionHash = new byte[0x20]; - public byte[] ProductCode = new byte[0x10]; - public byte[] ExtendedHeaderHash = new byte[0x20]; - public uint ExtendedHeaderSizeInBytes; - public byte[] Reserved2 = new byte[4]; - public NCCHHeaderFlags Flags; - public uint PlainRegionOffsetInMediaUnits; - public uint PlainRegionOffsetInBytes { get { return PlainRegionOffsetInMediaUnits * 0x200; } } - public uint PlainRegionSizeInMediaUnits; - public uint PlainRegionSizeInBytes { get { return PlainRegionSizeInMediaUnits * 0x200; } } - public uint LogoRegionOffsetInMediaUnits; - public uint LogoRegionOffsetInBytes { get { return LogoRegionOffsetInMediaUnits * 0x200; } } - public uint LogoRegionSizeInMediaUnits; - public uint LogoRegionSizeInBytes { get { return LogoRegionSizeInMediaUnits * 0x200; } } - public uint ExeFSOffsetInMediaUnits; - public uint ExeFSOffsetInBytes { get { return ExeFSOffsetInMediaUnits * 0x200; } } - public uint ExeFSSizeInMediaUnits; - public uint ExeFSSizeInBytes { get { return ExeFSSizeInMediaUnits * 0x200; } } - public uint ExeFSHashRegionOffsetInMediaUnits; - public uint ExeFSHashRegionOffsetInBytes { get { return ExeFSHashRegionOffsetInMediaUnits * 0x200; } } - public uint ExeFSHashRegionSizeInMediaUnits; - public uint ExeFSHashRegionSizeInBytes { get { return ExeFSHashRegionSizeInMediaUnits * 0x200; } } - public uint RomFSOffsetInMediaUnits; - public uint RomFSOffsetInBytes { get { return RomFSOffsetInMediaUnits * 0x200; } } - public uint RomFSSizeInMediaUnits; - public uint RomFSSizeInBytes { get { return RomFSSizeInMediaUnits * 0x200; } } - public uint RomFSHashRegionOffsetInMediaUnits; - public uint RomFSHashRegionOffsetInBytes { get { return RomFSHashRegionOffsetInMediaUnits * 0x200; } } - public uint RomFSHashRegionSizeInMediaUnits; - public uint RomFSHashRegionSizeInBytes { get { return RomFSHashRegionSizeInMediaUnits * 0x200; } } - public byte[] ExeFSSuperblockHash = new byte[0x20]; - public byte[] RomFSSuperblockHash = new byte[0x20]; + /// + /// Maker code + /// + public byte[] MakerCode { get; private set; } + + /// + /// Version + /// + public byte[] Version { get; private set; } + + /// + /// When ncchflag[7] = 0x20 starting with FIRM 9.6.0-X, this is compared with the first output u32 from a + /// SHA256 hash. The data used for that hash is 0x18-bytes: [0x10-long title-unique content lock seed] + /// [programID from NCCH + 0x118]. This hash is only used for verification of the content lock seed, and + /// is not the actual keyY. + /// + public byte[] VerificationHash { get; private set; } + + /// + /// Program ID + /// + public byte[] ProgramId { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved1 { get; private set; } + + /// + /// Logo Region SHA-256 hash. (For applications built with SDK 5+) (Supported from firmware: 5.0.0-11) + /// + public byte[] LogoRegionHash { get; private set; } + + /// + /// Product code + /// + public byte[] ProductCode { get; private set; } + + /// + /// Extended header SHA-256 hash (SHA256 of 2x Alignment Size, beginning at 0x0 of ExHeader) + /// + public byte[] ExtendedHeaderHash { get; private set; } + + /// + /// Extended header size, in bytes + /// + public uint ExtendedHeaderSizeInBytes { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved2 { get; private set; } + + /// + /// Flags + /// + public NCCHHeaderFlags Flags { get; private set; } + + /// + /// Plain region offset, in media units + /// + public uint PlainRegionOffsetInMediaUnits { get; private set; } + + /// + /// Plain region size, in media units + /// + public uint PlainRegionSizeInMediaUnits { get; private set; } + + /// + /// Logo Region offset, in media units (For applications built with SDK 5+) (Supported from firmware: 5.0.0-11) + /// + public uint LogoRegionOffsetInMediaUnits { get; private set; } + + /// + /// Logo Region size, in media units (For applications built with SDK 5+) (Supported from firmware: 5.0.0-11) + /// + public uint LogoRegionSizeInMediaUnits { get; private set; } + + /// + /// ExeFS offset, in media units + /// + public uint ExeFSOffsetInMediaUnits { get; private set; } + + /// + /// ExeFS size, in media units + /// + public uint ExeFSSizeInMediaUnits { get; private set; } + + /// + /// ExeFS hash region size, in media units + /// + public uint ExeFSHashRegionSizeInMediaUnits { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved3 { get; private set; } + + /// + /// RomFS offset, in media units + /// + public uint RomFSOffsetInMediaUnits { get; private set; } + + /// + /// RomFS size, in media units + /// + public uint RomFSSizeInMediaUnits { get; private set; } + + /// + /// RomFS hash region size, in media units + /// + public uint RomFSHashRegionSizeInMediaUnits { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved4 { get; private set; } + + /// + /// ExeFS superblock SHA-256 hash - (SHA-256 hash, starting at 0x0 of the ExeFS over the number of + /// media units specified in the ExeFS hash region size) + /// + public byte[] ExeFSSuperblockHash { get; private set; } + + /// + /// RomFS superblock SHA-256 hash - (SHA-256 hash, starting at 0x0 of the RomFS over the number + /// of media units specified in the RomFS hash region size) + /// + public byte[] RomFSSuperblockHash { get; private set; } + + /// + /// Read from a stream and get an NCCH header, if possible + /// + /// BinaryReader representing the input stream + /// NCCH header object, null on error public static NCCHHeader Read(BinaryReader reader) { NCCHHeader header = new NCCHHeader(); @@ -83,12 +191,12 @@ namespace ThreeDS.Headers header.LogoRegionSizeInMediaUnits = reader.ReadUInt32(); header.ExeFSOffsetInMediaUnits = reader.ReadUInt32(); header.ExeFSSizeInMediaUnits = reader.ReadUInt32(); - header.ExeFSHashRegionOffsetInMediaUnits = reader.ReadUInt32(); header.ExeFSHashRegionSizeInMediaUnits = reader.ReadUInt32(); + header.Reserved3 = reader.ReadBytes(4); header.RomFSOffsetInMediaUnits = reader.ReadUInt32(); header.RomFSSizeInMediaUnits = reader.ReadUInt32(); - header.RomFSHashRegionOffsetInMediaUnits = reader.ReadUInt32(); header.RomFSHashRegionSizeInMediaUnits = reader.ReadUInt32(); + header.Reserved4 = reader.ReadBytes(4); header.ExeFSSuperblockHash = reader.ReadBytes(0x20); header.RomFSSuperblockHash = reader.ReadBytes(0x20); diff --git a/3DSDecrypt/Headers/NCCHHeaderFlags.cs b/3DSDecrypt/Headers/NCCHHeaderFlags.cs index 017f147..fab9956 100644 --- a/3DSDecrypt/Headers/NCCHHeaderFlags.cs +++ b/3DSDecrypt/Headers/NCCHHeaderFlags.cs @@ -6,16 +6,54 @@ namespace ThreeDS.Headers { public class NCCHHeaderFlags { - public byte Reserved0; - public byte Reserved1; - public byte Reserved2; - public CryptoMethod CryptoMethod; - public ContentPlatform ContentPlatform; - public ContentType MediaPlatformIndex; - public byte ContentUnitSize; - public uint ContentUnitSizeInBytes { get { return (uint)(0x200 * Math.Pow(2, this.ContentUnitSize)); } } - public BitMasks BitMasks; + /// + /// Reserved + /// + public byte Reserved0 { get; private set; } + /// + /// Reserved + /// + public byte Reserved1 { get; private set; } + + /// + /// Reserved + /// + public byte Reserved2 { get; private set; } + + /// + /// Crypto Method: When this is non-zero, a NCCH crypto method using two keyslots is used. + /// + public CryptoMethod CryptoMethod { get; private set; } + + /// + /// Content Platform: 1 = CTR, 2 = snake (New 3DS). + /// + public ContentPlatform ContentPlatform { get; private set; } + + /// + /// Content Type Bit-masks: Data = 0x1, Executable = 0x2, SystemUpdate = 0x4, Manual = 0x8, + /// Child = (0x4|0x8), Trial = 0x10. When 'Data' is set, but not 'Executable', NCCH is a CFA. + /// Otherwise when 'Executable' is set, NCCH is a CXI. + /// + public ContentType MediaPlatformIndex { get; private set; } + + /// + /// Content Unit Size i.e. u32 ContentUnitSize = 0x200*2^flags[6]; + /// + public byte ContentUnitSize { get; private set; } + + /// + /// Bit-masks: FixedCryptoKey = 0x1, NoMountRomFs = 0x2, NoCrypto = 0x4, using a new keyY + /// generator = 0x20(starting with FIRM 9.6.0-X). + /// + public BitMasks BitMasks { get; private set; } + + /// + /// Read from a stream and get an NCCH header flags, if possible + /// + /// BinaryReader representing the input stream + /// NCCH header flags object, null on error public static NCCHHeaderFlags Read(BinaryReader reader) { NCCHHeaderFlags flags = new NCCHHeaderFlags(); @@ -38,6 +76,10 @@ namespace ThreeDS.Headers } } + /// + /// Write NCCH header flags to stream, if possible + /// + /// BinaryWriter representing the output stream public void Write(BinaryWriter writer) { try diff --git a/3DSDecrypt/Headers/NCSDHeader.cs b/3DSDecrypt/Headers/NCSDHeader.cs index 4db2d6f..71dcc74 100644 --- a/3DSDecrypt/Headers/NCSDHeader.cs +++ b/3DSDecrypt/Headers/NCSDHeader.cs @@ -8,36 +8,117 @@ namespace ThreeDS.Headers { private const string NCSDMagicNumber = "NCSD"; - // Common to all NCSD files - public byte[] RSA2048Signature = new byte[0x100]; - public uint ImageSizeInMediaUnits; - public uint ImageSizeInBytes { get { return ImageSizeInMediaUnits * 0x200; } } - public byte[] MediaId = new byte[8]; - public FilesystemType PartitionsFSType; - public byte[] PartitionsCryptType = new byte[8]; - public PartitionTableEntry[] PartitionsTable = new PartitionTableEntry[8]; + #region Common to all NCSD files - // For carts - public byte[] ExheaderHash = new byte[0x20]; - public uint AdditionalHeaderSize; - public uint SectorZeroOffset; - private byte[] partitionFlags = new byte[8]; + /// + /// RSA-2048 SHA-256 signature of the NCSD header + /// + public byte[] RSA2048Signature { get; private set; } + + /// + /// Size of the NCSD image, in media units (1 media unit = 0x200 bytes) + /// + public uint ImageSizeInMediaUnits { get; private set; } + + /// + /// Media ID + /// + public byte[] MediaId { get; private set; } + + /// + /// Partitions FS type (0=None, 1=Normal, 3=FIRM, 4=AGB_FIRM save) + /// + public FilesystemType PartitionsFSType { get; private set; } + + /// + /// Partitions crypt type (each byte corresponds to a partition in the partition table) + /// + public byte[] PartitionsCryptType { get; private set; } + + /// + /// Offset & Length partition table, in media units + /// + public PartitionTableEntry[] PartitionsTable { get; private set; } + + #endregion + + #region For carts + + /// + /// Exheader SHA-256 hash + /// + public byte[] ExheaderHash { get; private set; } + + /// + /// Additional header size + /// + public uint AdditionalHeaderSize { get; private set; } + + /// + /// Sector zero offset + /// + public uint SectorZeroOffset { get; private set; } + + /// + /// Partition Flags + /// + private byte[] partitionFlags; public byte BackupWriteWaitTime { get { return partitionFlags[(int)NCSDFlags.BackupWriteWaitTime]; } } public MediaCardDeviceType MediaCardDevice3X { get { return (MediaCardDeviceType)partitionFlags[(int)NCSDFlags.MediaCardDevice3X]; } } public MediaPlatformIndex MediaPlatformIndex { get { return (MediaPlatformIndex)partitionFlags[(int)NCSDFlags.MediaPlatformIndex]; } } public MediaTypeIndex MediaTypeIndex { get { return (MediaTypeIndex)partitionFlags[(int)NCSDFlags.MediaTypeIndex]; } } public uint SectorSize { get { return (uint)(0x200 * Math.Pow(2, partitionFlags[(int)NCSDFlags.MediaUnitSize])); } } public MediaCardDeviceType MediaCardDevice2X { get { return (MediaCardDeviceType)partitionFlags[(int)NCSDFlags.MediaCardDevice2X]; } } - public byte[][] PartitionIdTable = new byte[8][]; - public byte[] ReservedBlock1 = new byte[0x20]; - public byte[] ReservedBlock2 = new byte[0xE]; - public byte FirmUpdateByte1; - public byte FIrmUpdateByte2; - // For NAND - public byte[] Unknown = new byte[0x5E]; - public byte[] EncryptedMBR = new byte[0x42]; + /// + /// Partition ID table + /// + public byte[][] PartitionIdTable { get; private set; } + /// + /// Reserved + /// + public byte[] Reserved1 { get; private set; } + + /// + /// Reserved? + /// + public byte[] Reserved2 { get; private set; } + + /// + /// Support for this was implemented with 9.6.0-X FIRM. Bit0=1 enables using bits 1-2, it's unknown + /// what these two bits are actually used for(the value of these two bits get compared with some other + /// value during NCSD verification/loading). This appears to enable a new, likely hardware-based, + /// antipiracy check on cartridges. + /// + public byte FirmUpdateByte1 { get; private set; } + + /// + /// Support for this was implemented with 9.6.0-X FIRM, see below regarding save crypto. + /// + public byte FIrmUpdateByte2 { get; private set; } + + #endregion + + #region For NAND + + /// + /// Unknown + /// + public byte[] Unknown { get; private set; } + + /// + /// Encrypted MBR partition-table, for the TWL partitions(key-data used for this keyslot is console-unique). + /// + public byte[] EncryptedMBR { get; private set; } + + #endregion + + /// + /// Read from a stream and get an NCSD header, if possible + /// + /// BinaryReader representing the input stream + /// NCSD header object, null on error public static NCSDHeader Read(BinaryReader reader) { NCSDHeader header = new NCSDHeader(); @@ -54,6 +135,7 @@ namespace ThreeDS.Headers 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] = PartitionTableEntry.Read(reader); @@ -65,11 +147,12 @@ namespace ThreeDS.Headers header.SectorZeroOffset = reader.ReadUInt32(); header.partitionFlags = reader.ReadBytes(8); + header.PartitionIdTable = new byte[8][]; for (int i = 0; i < 8; i++) header.PartitionIdTable[i] = reader.ReadBytes(8); - header.ReservedBlock1 = reader.ReadBytes(0x20); - header.ReservedBlock2 = reader.ReadBytes(0xE); + header.Reserved1 = reader.ReadBytes(0x20); + header.Reserved2 = reader.ReadBytes(0xE); header.FirmUpdateByte1 = reader.ReadByte(); header.FIrmUpdateByte2 = reader.ReadByte(); } diff --git a/3DSDecrypt/Headers/PartitionTableEntry.cs b/3DSDecrypt/Headers/PartitionTableEntry.cs index a116d31..50f8597 100644 --- a/3DSDecrypt/Headers/PartitionTableEntry.cs +++ b/3DSDecrypt/Headers/PartitionTableEntry.cs @@ -4,9 +4,21 @@ namespace ThreeDS.Headers { public class PartitionTableEntry { + /// + /// Offset + /// public uint Offset { get; set; } + + /// + /// Length + /// public uint Length { get; set; } + /// + /// Read from a stream and get partition table entry, if possible + /// + /// BinaryReader representing the input stream + /// Partition table entry object, null on error public static PartitionTableEntry Read(BinaryReader reader) { PartitionTableEntry entry = new PartitionTableEntry(); @@ -23,6 +35,10 @@ namespace ThreeDS.Headers } } + /// + /// Check for a valid partition + /// + /// True if the offset and length are not 0, false otherwise public bool IsValid() { return Offset != 0 && Length != 0; diff --git a/3DSDecrypt/Headers/RomFSHeader.cs b/3DSDecrypt/Headers/RomFSHeader.cs new file mode 100644 index 0000000..5bd7c78 --- /dev/null +++ b/3DSDecrypt/Headers/RomFSHeader.cs @@ -0,0 +1,127 @@ +using System.IO; + +namespace ThreeDS.Headers +{ + // https://www.3dbrew.org/wiki/RomFS + public class RomFSHeader + { + private const string RomFSMagicNumber = "IVFC"; + private const uint RomFSSecondMagicNumber = 0x10000; + + /// + /// Master hash size + /// + public uint MasterHashSize { get; private set; } + + /// + /// Level 1 logical offset + /// + public ulong Level1LogicalOffset { get; private set; } + + /// + /// Level 1 hashdata size + /// + public ulong Level1HashdataSize { get; private set; } + + /// + /// Level 1 block size, in log2 + /// + public uint Level1BlockSizeLog2 { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved1 { get; private set; } + + /// + /// Level 2 logical offset + /// + public ulong Level2LogicalOffset { get; private set; } + + /// + /// Level 2 hashdata size + /// + public ulong Level2HashdataSize { get; private set; } + + /// + /// Level 2 block size, in log2 + /// + public uint Level2BlockSizeLog2 { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved2 { get; private set; } + + /// + /// Level 3 logical offset + /// + public ulong Level3LogicalOffset { get; private set; } + + /// + /// Level 3 hashdata size + /// + public ulong Level3HashdataSize { get; private set; } + + /// + /// Level 3 block size, in log2 + /// + public uint Level3BlockSizeLog2 { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved3 { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved4 { get; private set; } + + /// + /// Optional info size. + /// + public uint OptionalInfoSize { get; private set; } + + /// + /// Read from a stream and get a RomFS header, if possible + /// + /// BinaryReader representing the input stream + /// RomFS header object, null on error + public static RomFSHeader Read(BinaryReader reader) + { + RomFSHeader header = new RomFSHeader(); + + try + { + if (new string(reader.ReadChars(4)) != RomFSMagicNumber) + return null; + + if (reader.ReadUInt32() != RomFSSecondMagicNumber) + return null; + + header.MasterHashSize = reader.ReadUInt32(); + header.Level1LogicalOffset = reader.ReadUInt64(); + header.Level1HashdataSize = reader.ReadUInt64(); + header.Level1BlockSizeLog2 = reader.ReadUInt32(); + header.Reserved1 = reader.ReadBytes(4); + header.Level2LogicalOffset = reader.ReadUInt64(); + header.Level2HashdataSize = reader.ReadUInt64(); + header.Level2BlockSizeLog2 = reader.ReadUInt32(); + header.Reserved2 = reader.ReadBytes(4); + header.Level3LogicalOffset = reader.ReadUInt64(); + header.Level3HashdataSize = reader.ReadUInt64(); + header.Level3BlockSizeLog2 = reader.ReadUInt32(); + header.Reserved3 = reader.ReadBytes(4); + header.Reserved4 = reader.ReadBytes(4); + header.OptionalInfoSize = reader.ReadUInt32(); + + return header; + } + catch + { + return null; + } + } + } +} diff --git a/3DSDecrypt/Headers/StorageInfo.cs b/3DSDecrypt/Headers/StorageInfo.cs index 41ca796..4ce52c9 100644 --- a/3DSDecrypt/Headers/StorageInfo.cs +++ b/3DSDecrypt/Headers/StorageInfo.cs @@ -1,15 +1,40 @@ using System.IO; +using ThreeDS.Data; namespace ThreeDS.Headers { public class StorageInfo { - public byte[] ExtdataID = new byte[8]; - public byte[] SystemSavedataIDs = new byte[8]; - public byte[] StorageAccessibleUniqueIDs = new byte[8]; - public byte[] FilesystemAccessInfo = new byte[7]; - public byte OtherAttributes; + /// + /// Extdata ID + /// + public byte[] ExtdataID { get; private set; } + /// + /// System savedata IDs + /// + public byte[] SystemSavedataIDs { get; private set; } + + /// + /// Storage accessible unique IDs + /// + public byte[] StorageAccessibleUniqueIDs { get; private set; } + + /// + /// Filesystem access info + /// + public byte[] FilesystemAccessInfo { get; private set; } + + /// + /// Other attributes + /// + public StorageInfoOtherAttributes OtherAttributes { get; private set; } + + /// + /// Read from a stream and get storage info, if possible + /// + /// BinaryReader representing the input stream + /// Storage info object, null on error public static StorageInfo Read(BinaryReader reader) { StorageInfo si = new StorageInfo(); @@ -20,7 +45,7 @@ namespace ThreeDS.Headers si.SystemSavedataIDs = reader.ReadBytes(8); si.StorageAccessibleUniqueIDs = reader.ReadBytes(8); si.FilesystemAccessInfo = reader.ReadBytes(7); - si.OtherAttributes = reader.ReadByte(); + si.OtherAttributes = (StorageInfoOtherAttributes)reader.ReadByte(); return si; } catch diff --git a/3DSDecrypt/Headers/SystemControlInfo.cs b/3DSDecrypt/Headers/SystemControlInfo.cs index 3f15103..70ea2f7 100644 --- a/3DSDecrypt/Headers/SystemControlInfo.cs +++ b/3DSDecrypt/Headers/SystemControlInfo.cs @@ -4,19 +4,71 @@ namespace ThreeDS.Headers { public class SystemControlInfo { - public char[] ApplicationTitle = new char[8]; - public byte[] Reserved1 = new byte[5]; - public byte Flag; - public byte[] RemasterVersion = new byte[2]; - public CodeSetInfo TextCodesetInfo; - public uint StackSize; - public CodeSetInfo ReadOnlyCodeSetInfo; - public byte[] Reserved2 = new byte[4]; - public CodeSetInfo DataCodeSetInfo; - public uint BSSSize; - public byte[][] DependencyModuleList = new byte[48][]; - public SystemInfo SystemInfo; + /// + /// Application title (default is "CtrApp") + /// + public char[] ApplicationTitle { get; private set; } + /// + /// Reserved + /// + public byte[] Reserved1 { get; private set; } + + /// + /// Flag (bit 0: CompressExefsCode, bit 1: SDApplication) + /// + public byte Flag { get; private set; } + + /// + /// Remaster version + /// + public byte[] RemasterVersion { get; private set; } + + /// + /// Text code set info + /// + public CodeSetInfo TextCodesetInfo { get; private set; } + + /// + /// Stack size + /// + public uint StackSize { get; private set; } + + /// + /// Read-only code set info + /// + public CodeSetInfo ReadOnlyCodeSetInfo { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved2 { get; private set; } + + /// + /// Data code set info + /// + public CodeSetInfo DataCodeSetInfo { get; private set; } + + /// + /// BSS size + /// + public uint BSSSize { get; private set; } + + /// + /// Dependency module (program ID) list + /// + public byte[][] DependencyModuleList { get; private set; } + + /// + /// SystemInfo + /// + public SystemInfo SystemInfo { get; private set; } + + /// + /// Read from a stream and get system control info, if possible + /// + /// BinaryReader representing the input stream + /// System control info object, null on error public static SystemControlInfo Read(BinaryReader reader) { SystemControlInfo sci = new SystemControlInfo(); @@ -34,6 +86,7 @@ namespace ThreeDS.Headers sci.DataCodeSetInfo = CodeSetInfo.Read(reader); sci.BSSSize = reader.ReadUInt32(); + sci.DependencyModuleList = new byte[48][]; for (int i = 0; i < 48; i++) sci.DependencyModuleList[i] = reader.ReadBytes(8); diff --git a/3DSDecrypt/Headers/SystemInfo.cs b/3DSDecrypt/Headers/SystemInfo.cs index 4af472c..669545e 100644 --- a/3DSDecrypt/Headers/SystemInfo.cs +++ b/3DSDecrypt/Headers/SystemInfo.cs @@ -4,10 +4,26 @@ namespace ThreeDS.Headers { public class SystemInfo { - public ulong SaveDataSize; - public byte[] JumpID = new byte[8]; - public byte[] Reserved = new byte[0x30]; + /// + /// SaveData Size + /// + public ulong SaveDataSize { get; private set; } + /// + /// Jump ID + /// + public byte[] JumpID { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved { get; private set; } + + /// + /// Read from a stream and get system info, if possible + /// + /// BinaryReader representing the input stream + /// System info object, null on error public static SystemInfo Read(BinaryReader reader) { SystemInfo si = new SystemInfo(); diff --git a/3DSDecrypt/ThreeDSTool.cs b/3DSDecrypt/ThreeDSTool.cs index 6d34465..7351e04 100644 --- a/3DSDecrypt/ThreeDSTool.cs +++ b/3DSDecrypt/ThreeDSTool.cs @@ -378,7 +378,7 @@ namespace ThreeDS /// True if we want to encrypt the extended header, false otherwise private void ProcessExeFS(BinaryReader reader, BinaryWriter writer, NCSDHeader header, int partitionNumber, NCCHHeader partitionHeader, bool encrypt) { - if (partitionHeader.ExeFSSizeInBytes > 0) + if (partitionHeader.ExeFSSizeInMediaUnits > 0) { // If we're decrypting, we need to decrypt the filename table first if (!encrypt)