public Xb2FileSystem(string sdPath) { SwitchFs sdFs = OpenSdCard(sdPath); Application xb2App = sdFs.Applications[0x0100E95004038000]; IFileSystem mainFs = xb2App.Patch.MainNca.OpenSectionFileSystem(1, IntegrityCheckLevel.ErrorOnInvalid); IFile mainArh = mainFs.OpenFile("/bf2.arh", OpenMode.Read); IFile mainArd = mainFs.OpenFile("/bf2.ard", OpenMode.Read); var mainArchiveFs = new ArchiveFileSystem(mainArh, mainArd); var fsList = new List <IFileSystem>(); fsList.Add(xb2App.Patch.MainNca.OpenSectionFileSystem(1, IntegrityCheckLevel.ErrorOnInvalid)); fsList.Add(mainArchiveFs); foreach (Title aoc in xb2App.AddOnContent.OrderBy(x => x.Id)) { IFileSystem aocFs = aoc.MainNca.OpenSectionFileSystem(0, IntegrityCheckLevel.ErrorOnInvalid); fsList.Add(aocFs); if (aoc.Id == 0x0100E95004039001) { IFile aocArh = aocFs.OpenFile("/aoc1.arh", OpenMode.Read); IFile aocArd = aocFs.OpenFile("/aoc1.ard", OpenMode.Read); var aocArchiveFs = new ArchiveFileSystem(aocArh, aocArd); fsList.Add(aocArchiveFs); } } fsList.Reverse(); BaseFs = new LayeredFileSystem(fsList); }
private static SwitchFs OpenSdCard(string path, IProgressReport logger = null) { SwitchFs switchFs; Keyset keyset = OpenKeyset(); var baseFs = new LocalFileSystem(path); if (Directory.Exists(Path.Combine(path, "Nintendo", "Contents", "registered"))) { logger?.LogMessage("Treating path as SD card storage"); switchFs = SwitchFs.OpenSdCard(keyset, baseFs); } else if (Directory.Exists(Path.Combine(path, "Contents", "registered"))) { logger?.LogMessage("Treating path as NAND storage"); switchFs = SwitchFs.OpenNandPartition(keyset, baseFs); } else { logger?.LogMessage("Treating path as a directory of loose NCAs"); switchFs = SwitchFs.OpenNcaDirectory(keyset, baseFs); } return(switchFs); }
public SwitchFs LoadFileSystem(Func <SwitchFs> fs) { FS = fs(); Ready(this, null); return(FS); }
public static void Listnca(string sdfs, string fwint, Keyset keyset, JsonWriter writer) { SwitchFs switchFs; LocalFileSystem baseFs = new LocalFileSystem(sdfs); switchFs = SwitchFs.OpenNcaDirectory(keyset, baseFs); string lasttitleid = ""; string lastncaid = ""; string type; foreach (SwitchFsNca nca in switchFs.Ncas.Values.OrderBy(x => x.Nca.Header.TitleId)) { if (nca.Nca.Header.TitleId.ToString("X16") != lasttitleid) { writer.WritePropertyName(nca.Nca.Header.TitleId.ToString("X16")); writer.WriteStartObject(); writer.WritePropertyName(nca.Nca.Header.ContentType.ToString()); writer.WriteValue(nca.NcaId); // solo contiene META if (int.Parse(fwint) >= 738197944) { if ("0100000000000825" == nca.Nca.Header.TitleId.ToString("X16") || "0100000000000029" == nca.Nca.Header.TitleId.ToString("X16") || "0100000000000816" == nca.Nca.Header.TitleId.ToString("X16")) { writer.WriteEnd(); } } else if ("010000000000001B" == nca.Nca.Header.TitleId.ToString("X16") || "0100000000000029" == nca.Nca.Header.TitleId.ToString("X16") || "0100000000000816" == nca.Nca.Header.TitleId.ToString("X16")) { writer.WriteEnd(); } lastncaid = nca.NcaId; lasttitleid = nca.Nca.Header.TitleId.ToString("X16"); } if (lastncaid != nca.NcaId && lasttitleid == nca.Nca.Header.TitleId.ToString()) { if (nca.Nca.Header.ContentType.ToString() == "Data" || nca.Nca.Header.ContentType.ToString() == "PublicData") { type = "Program"; } else { type = nca.Nca.Header.ContentType.ToString(); } writer.WritePropertyName(type); writer.WriteValue(nca.NcaId); writer.WriteEnd(); lastncaid = nca.NcaId; lasttitleid = nca.Nca.Header.TitleId.ToString("X16"); } else if (lastncaid != nca.NcaId && lasttitleid != nca.Nca.Header.TitleId.ToString()) { writer.WritePropertyName(nca.Nca.Header.ContentType.ToString()); writer.WriteValue(nca.NcaId); writer.WriteEnd(); lastncaid = nca.NcaId; lasttitleid = nca.Nca.Header.TitleId.ToString("X16"); } } }
static void ListTitles(SwitchFs sdfs) { foreach (Title title in sdfs.Titles.Values.OrderBy(x => x.Id)) { Console.WriteLine($"{title.Name} {title.Control?.DisplayVersion}"); Console.WriteLine($"{title.Id:X16} v{title.Version.Version} ({title.Version}) {title.Metadata.Type}"); foreach (CnmtContentEntry content in title.Metadata.ContentEntries) { Console.WriteLine( $" {content.NcaId.ToHexString()}.nca {content.Type} {Util.GetBytesReadable(content.Size)}"); } foreach (Nca nca in title.Ncas) { Console.WriteLine($" {nca.HasRightsId} {nca.NcaId} {nca.Header.ContentType}"); foreach (NcaSection sect in nca.Sections.Where(x => x != null)) { Console.WriteLine($" {sect.SectionNum} {sect.Type} {sect.Header.EncryptionType} {sect.MasterHashValidity}"); } } Console.WriteLine(""); } }
private static void CheckForNcaFolders(Context ctx, SwitchFs switchFs) { #if NETCOREAPP // Skip this until Linux gets FAT attribute support if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return; } #endif IFileSystem fs = switchFs.ContentFs; DirectoryEntry[] ncaDirs = fs.EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories) .Where(x => x.Type == DirectoryEntryType.Directory) .Where(x => fs.FileExists($"{x.FullPath}/00")) .ToArray(); if (ncaDirs.Length > 0) { ctx.Logger.LogMessage("Warning: NCA folders without the archive flag were found. Fixing..."); } foreach (DirectoryEntry file in ncaDirs) { fs.SetConcatenationFileAttribute(file.FullPath); ctx.Logger.LogMessage($"{file.FullPath}"); } }
public void LoadFileSystem(Func <SwitchFs> fsf, TitleSource source) { SwitchFs fs = fsf(); Ready?.Invoke(source); IndexedFilesystems[source].Add(fs); }
private static void ValidateSwitchFs(Context ctx, SwitchFs switchFs) { if (ctx.Options.TitleId != 0) { ulong id = ctx.Options.TitleId; if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } ValidateTitle(ctx, title, ""); return; } foreach (Application app in switchFs.Applications.Values) { ctx.Logger.LogMessage($"Checking {app.Name}..."); Title mainTitle = app.Patch ?? app.Main; if (mainTitle != null) { ValidateTitle(ctx, mainTitle, "Main title"); } foreach (Title title in app.AddOnContent) { ValidateTitle(ctx, title, "Add-on content"); } } }
private static void SaveTitle(Context ctx, SwitchFs switchFs) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to save title"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } string saveDir = Path.Combine(ctx.Options.OutDir, $"{title.Id:X16}v{title.Version.Version}"); Directory.CreateDirectory(saveDir); foreach (SwitchFsNca nca in title.Ncas) { Stream stream = nca.Nca.BaseStorage.AsStream(); string outFile = Path.Combine(saveDir, nca.Filename); ctx.Logger.LogMessage(nca.Filename); using (var outStream = new FileStream(outFile, FileMode.Create, FileAccess.ReadWrite)) { stream.CopyStream(outStream, stream.Length, ctx.Logger); } } }
private static void ExportSdSaves(Context ctx, SwitchFs switchFs) { foreach (KeyValuePair <string, SaveDataFileSystem> save in switchFs.Saves) { string outDir = Path.Combine(ctx.Options.SaveOutDir, save.Key); save.Value.Extract(outDir, ctx.Logger); } }
public SwitchFS(string switchFsDir, IProgressReport logger = null) { FileSystemDir = switchFsDir; Keyset = GetKeyset(logger); var localFs = new LocalFileSystem(switchFsDir); SwitchFs = SwitchFs.OpenSdCard(Keyset, localFs); }
private static void ReadSwitchFs(string nandFile) { using (var logger = new ProgressBar()) using (var stream = new FileStream(nandFile, FileMode.Open, FileAccess.Read)) { Keyset keyset = OpenKeyset(); var nand = new Nand(stream, keyset); FatFileSystemProvider user = nand.OpenSystemPartition(); SwitchFs sdfs = SwitchFs.OpenNandPartition(keyset, user); } }
static string ListNcas(SwitchFs sdfs) { var table = new TableBuilder("NCA ID", "Type", "Title ID"); foreach (SwitchFsNca nca in sdfs.Ncas.Values.OrderBy(x => x.NcaId)) { table.AddRow(nca.NcaId, nca.Nca.Header.ContentType.Print(), nca.Nca.Header.TitleId.ToString("X16")); } return(table.Print()); }
public static void CreateNsp(Context ctx, SwitchFs switchFs) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to save title"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } var builder = new PartitionFileSystemBuilder(); foreach (SwitchFsNca nca in title.Ncas) { builder.AddFile(nca.Filename, nca.Nca.BaseStorage.AsFile(OpenMode.Read)); } var ticket = new Ticket { SignatureType = TicketSigType.Rsa2048Sha256, Signature = new byte[0x200], Issuer = "Root-CA00000003-XS00000020", FormatVersion = 2, RightsId = title.MainNca.Nca.Header.RightsId.ToArray(), TitleKeyBlock = title.MainNca.Nca.GetDecryptedTitleKey(), CryptoType = title.MainNca.Nca.Header.KeyGeneration, SectHeaderOffset = 0x2C0 }; byte[] ticketBytes = ticket.GetBytes(); builder.AddFile($"{ticket.RightsId.ToHexString()}.tik", new MemoryStream(ticketBytes).AsIFile(OpenMode.ReadWrite)); Assembly thisAssembly = Assembly.GetExecutingAssembly(); Stream cert = thisAssembly.GetManifestResourceStream("hactoolnet.CA00000003_XS00000020"); builder.AddFile($"{ticket.RightsId.ToHexString()}.cert", cert.AsIFile(OpenMode.Read)); using (var outStream = new FileStream(ctx.Options.NspOut, FileMode.Create, FileAccess.ReadWrite)) { IStorage builtPfs = builder.Build(PartitionFileSystemType.Standard); builtPfs.GetSize(out long pfsSize).ThrowIfFailure(); builtPfs.CopyToStream(outStream, pfsSize, ctx.Logger); } }
public static void CreateNsp(Context ctx, SwitchFs switchFs) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to save title"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } var builder = new Pfs0Builder(); foreach (Nca nca in title.Ncas) { builder.AddFile(nca.Filename, nca.GetStorage().AsStream()); } var ticket = new Ticket { SignatureType = TicketSigType.Rsa2048Sha256, Signature = new byte[0x200], Issuer = "Root-CA00000003-XS00000020", FormatVersion = 2, RightsId = title.MainNca.Header.RightsId, TitleKeyBlock = title.MainNca.TitleKey, CryptoType = title.MainNca.Header.CryptoType2, SectHeaderOffset = 0x2C0 }; byte[] ticketBytes = ticket.GetBytes(); builder.AddFile($"{ticket.RightsId.ToHexString()}.tik", new MemoryStream(ticketBytes)); Assembly thisAssembly = Assembly.GetExecutingAssembly(); Stream cert = thisAssembly.GetManifestResourceStream("hactoolnet.CA00000003_XS00000020"); builder.AddFile($"{ticket.RightsId.ToHexString()}.cert", cert); using (var outStream = new FileStream(ctx.Options.NspOut, FileMode.Create, FileAccess.ReadWrite)) { builder.Build(outStream, ctx.Logger); } }
static string ListTitles(SwitchFs sdfs) { var table = new TableBuilder("Title ID", "Version", "", "Type", "Size", "Display Version", "Name"); foreach (Title title in sdfs.Titles.Values.OrderBy(x => x.Id)) { table.AddRow($"{title.Id:X16}", $"v{title.Version?.Version}", title.Version?.ToString(), title.Metadata?.Type.Print(), Utilities.GetBytesReadable(title.GetSize()), title.Control.Value.DisplayVersion.ToString(), title.Name); } return(table.Print()); }
static string ListApplications(SwitchFs sdfs) { var sb = new StringBuilder(); foreach (Application app in sdfs.Applications.Values.OrderBy(x => x.Name)) { if (app.Main != null) { sb.AppendLine($"{app.Name} v{app.DisplayVersion} ({app.Main.Id.ToString("X16")})"); sb.AppendLine($"Software: {Utilities.GetBytesReadable(app.Main.GetSize())}"); } else { sb.AppendLine($"{app.Name} v{app.DisplayVersion}"); } if (app.Patch != null) { sb.AppendLine($"Update Data: {Utilities.GetBytesReadable(app.Patch.GetSize())}"); } if (app.AddOnContent.Count > 0) { sb.AppendLine($"DLC: {Utilities.GetBytesReadable(app.AddOnContent.Sum(x => x.GetSize()))}"); } ref ApplicationControlProperty nacp = ref app.Nacp.Value; long userTotalSaveDataSize = nacp.UserAccountSaveDataSize + nacp.UserAccountSaveDataJournalSize; long deviceTotalSaveDataSize = nacp.DeviceSaveDataSize + nacp.DeviceSaveDataJournalSize; if (userTotalSaveDataSize > 0) { sb.AppendLine($"User save: {Utilities.GetBytesReadable(userTotalSaveDataSize)}"); } if (deviceTotalSaveDataSize > 0) { sb.AppendLine($"System save: {Utilities.GetBytesReadable(deviceTotalSaveDataSize)}"); } if (nacp.BcatDeliveryCacheStorageSize > 0) { sb.AppendLine($"BCAT save: {Utilities.GetBytesReadable(nacp.BcatDeliveryCacheStorageSize)}"); } sb.AppendLine(); }
public static void ListTitleid(string sdfs, Keyset keyset, JsonWriter writer) { SwitchFs switchFs; LocalFileSystem baseFs = new LocalFileSystem(sdfs); switchFs = SwitchFs.OpenNcaDirectory(keyset, baseFs); string lasttitleid = ""; foreach (SwitchFsNca nca in switchFs.Ncas.Values.OrderBy(x => x.Nca.Header.TitleId)) { if (nca.Nca.Header.TitleId.ToString("X16") == lasttitleid) { continue; } lasttitleid = nca.Nca.Header.TitleId.ToString("X16"); writer.WriteValue(nca.Nca.Header.TitleId.ToString("X16")); } }
public static void MatchupBaseNca(this IEnumerable <SwitchFsNca> ncas) { PseudoFileSystem ps = ncas.MakeFs(); SwitchFs fs = SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, ps); foreach (KeyValuePair <ulong, LibHac.Application> kv in fs.Applications) { ulong tid = kv.Key; LibHac.Application app = kv.Value; if (app.Patch != null && app.Main != null) { foreach (SwitchFsNca nca in app.Patch.Ncas) { ContentType type = nca.Nca.Header.ContentType; SwitchFsNca baseNca = app.Main.Ncas.Where(n => n.Nca.Header.ContentType == type).FirstOrDefault(); if (baseNca != null) { bool hasPatch = false; for (int i = 0; i < 4; i++) { Nca n = nca.Nca; if (n.CanOpenSection(i)) { NcaFsHeader section = n.Header.GetFsHeader(i); if (section.IsPatchSection()) { hasPatch = true; break; } } } if (hasPatch) { ncas.Where(n => n.Filename == nca.Filename.Replace("/", "")).First().BaseNca = baseNca.Nca; // set original NCA, not new parsed one } } } } } }
static string ListApplications(SwitchFs sdfs) { var sb = new StringBuilder(); foreach (Application app in sdfs.Applications.Values.OrderBy(x => x.Name)) { sb.AppendLine($"{app.Name} v{app.DisplayVersion}"); if (app.Main != null) { sb.AppendLine($"Software: {Util.GetBytesReadable(app.Main.GetSize())}"); } if (app.Patch != null) { sb.AppendLine($"Update Data: {Util.GetBytesReadable(app.Patch.GetSize())}"); } if (app.AddOnContent.Count > 0) { sb.AppendLine($"DLC: {Util.GetBytesReadable(app.AddOnContent.Sum(x => x.GetSize()))}"); } if (app.Nacp?.UserTotalSaveDataSize > 0) { sb.AppendLine($"User save: {Util.GetBytesReadable(app.Nacp.UserTotalSaveDataSize)}"); } if (app.Nacp?.DeviceTotalSaveDataSize > 0) { sb.AppendLine($"System save: {Util.GetBytesReadable(app.Nacp.DeviceTotalSaveDataSize)}"); } if (app.Nacp?.BcatDeliveryCacheStorageSize > 0) { sb.AppendLine($"BCAT save: {Util.GetBytesReadable(app.Nacp.BcatDeliveryCacheStorageSize)}"); } sb.AppendLine(); } return(sb.ToString()); }
public static List <FsTitle> processSd(string path) { try { using (var fs = new SwitchFs(keyset, new FileSystem(path), true)) { log?.WriteLine("{0} of {1} NCA processed", fs?.Titles?.Select(title => title.Value.Ncas.Count)?.Sum(), fs?.Ncas?.Count); List <Application> applications = fs?.Applications?.Values?.ToList() ?? new List <Application>(); log?.WriteLine("Found {0} applications", applications?.Count); List <FsTitle> fsTitles = new List <FsTitle>(); foreach (Application application in applications) { if (application.Main != null) { if (application.Main.MetaNca != null || application.Patch?.MetaNca == null) { fsTitles.Add(application.Main); } } if (application.Patch?.MetaNca != null) { fsTitles.Add(application.Patch); } fsTitles.AddRange(application.AddOnContent); } return(fsTitles.OrderBy(fsTitle => fsTitle.Id).ToList()); } } catch (DirectoryNotFoundException) { return(null); } }
private void OnNandFound() { Nand nand = NANDService.NAND; Stream NANDSource = NANDService.NANDSource; NANDSource.Seek(0x804000, SeekOrigin.Begin); // BCPKG2-1-Normal-Main offset + length of BootConfig FileStream pkg2stream = HACGUIKeyset.TempPkg2FileInfo.Create(); NANDSource.CopyToNew(pkg2stream, 0x7FC000); // rest of BCPPKG2-Normal-Main partition pkg2stream.Seek(0, SeekOrigin.Begin); byte[] pkg2raw = new byte[pkg2stream.Length]; pkg2stream.Read(pkg2raw, 0, pkg2raw.Length); Package2 pkg2 = new Package2(HACGUIKeyset.Keyset, new MemoryStream(pkg2raw)); HACGUIKeyset.RootTempPkg2FolderInfo.Create(); FileStream kernelstream = HACGUIKeyset.TempKernelFileInfo.Create(); FileStream INI1stream = HACGUIKeyset.TempINI1FileInfo.Create(); pkg2.OpenKernel().CopyTo(kernelstream); pkg2.OpenIni1().CopyTo(INI1stream); kernelstream.Close(); INI1stream.Close(); Ini1 INI1 = new Ini1(pkg2.OpenIni1()); List <HashSearchEntry> hashes = new List <HashSearchEntry>(); Dictionary <byte[], byte[]> keys = new Dictionary <byte[], byte[]>(); HACGUIKeyset.RootTempINI1Folder.Create(); foreach (Kip kip in INI1.Kips) { Stream rodatastream, datastream; switch (kip.Header.Name) { case "FS": hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeyApplicationSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeyOceanSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.KeyAreaKeySystemSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.HeaderKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.SaveMacKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.SaveMacKeySourceHash, 0x10)); rodatastream = new MemoryStream(kip.DecompressSection(1)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeyApplicationSourceHash], HACGUIKeyset.Keyset.KeyAreaKeyApplicationSource, 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeyOceanSourceHash], HACGUIKeyset.Keyset.KeyAreaKeyOceanSource, 0x10); Array.Copy(keys[NintendoKeys.KeyAreaKeySystemSourceHash], HACGUIKeyset.Keyset.KeyAreaKeySystemSource, 0x10); Array.Copy(keys[NintendoKeys.HeaderKekSourceHash], HACGUIKeyset.Keyset.HeaderKekSource, 0x10); Array.Copy(keys[NintendoKeys.SaveMacKekSourceHash], HACGUIKeyset.Keyset.SaveMacKekSource, 0x10); Array.Copy(keys[NintendoKeys.SaveMacKeySourceHash], HACGUIKeyset.Keyset.SaveMacKeySource, 0x10); hashes.Clear(); rodatastream.Seek(0, SeekOrigin.Begin); bool sdWarn = false; hashes.Add(new HashSearchEntry(NintendoKeys.SDCardKekSourceHash, 0x10)); try { keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.SDCardKekSourceHash], HACGUIKeyset.Keyset.SdCardKekSource, 0x10); } catch (EndOfStreamException) { MessageBox.Show("Failed to find SD card kek source! The NAND is probably from 1.0.0."); sdWarn = true; } if (!sdWarn) // don't try to find the rest of the keys if the other one couldn't be found { hashes.Clear(); rodatastream.Seek(0, SeekOrigin.Begin); hashes.Add(new HashSearchEntry(NintendoKeys.SDCardSaveKeySourceHash, 0x20)); hashes.Add(new HashSearchEntry(NintendoKeys.SDCardNcaKeySourceHash, 0x20)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x20); Array.Copy(keys[NintendoKeys.SDCardSaveKeySourceHash], HACGUIKeyset.Keyset.SdCardKeySources[0], 0x20); Array.Copy(keys[NintendoKeys.SDCardNcaKeySourceHash], HACGUIKeyset.Keyset.SdCardKeySources[1], 0x20); } hashes.Clear(); rodatastream.Close(); hashes.Add(new HashSearchEntry(NintendoKeys.HeaderKeySourceHash, 0x20)); datastream = new MemoryStream(kip.DecompressSection(2)); keys = datastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x20); Array.Copy(keys[NintendoKeys.HeaderKeySourceHash], HACGUIKeyset.Keyset.HeaderKeySource, 0x20); datastream.Close(); hashes.Clear(); break; case "spl": hashes.Add(new HashSearchEntry(NintendoKeys.AesKeyGenerationSourceHash, 0x10)); rodatastream = new MemoryStream(kip.DecompressSection(1)); keys = rodatastream.FindKeyViaHash(hashes, new SHA256Managed(), 0x10); Array.Copy(keys[NintendoKeys.AesKeyGenerationSourceHash], HACGUIKeyset.Keyset.AesKeyGenerationSource, 0x10); rodatastream.Close(); hashes.Clear(); break; } FileStream kipstream = HACGUIKeyset.RootTempINI1Folder.GetFile(kip.Header.Name + ".kip").Create(); kip.OpenRawFile().CopyTo(kipstream); kipstream.Close(); } pkg2stream.Close(); INI1stream.Close(); HACGUIKeyset.Keyset.DeriveKeys(); SwitchFs fs = new SwitchFs(HACGUIKeyset.Keyset, NANDService.NAND.OpenSystemPartition()); foreach (KeyValuePair <string, Nca> kv in fs.Ncas) { Nca nca = kv.Value; switch (nca.Header.TitleId) { case 0x0100000000000033: // es switch (nca.Header.ContentType) { case ContentType.Program: NcaSection exefsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0); Stream pfsStream = nca.OpenSection(exefsSection.SectionNum, false, false); Pfs pfs = new Pfs(pfsStream); Nso nso = new Nso(pfs.OpenFile("main")); NsoSection section = nso.Sections[1]; Stream data = new MemoryStream(section.DecompressSection()); hashes.Clear(); hashes.Add(new HashSearchEntry(NintendoKeys.EticketRsaKekSourceHash, 0x10)); hashes.Add(new HashSearchEntry(NintendoKeys.EticketRsaKekekSourceHash, 0x10)); keys = data.FindKeyViaHash(hashes, new SHA256Managed(), 0x10, data.Length); byte[] EticketRsaKekSource = new byte[0x10]; byte[] EticketRsaKekekSource = new byte[0x10]; Array.Copy(keys[NintendoKeys.EticketRsaKekSourceHash], EticketRsaKekSource, 0x10); Array.Copy(keys[NintendoKeys.EticketRsaKekekSourceHash], EticketRsaKekekSource, 0x10); byte[] RsaOaepKekGenerationSource; XOR(NintendoKeys.KekMasks[0], NintendoKeys.KekSeeds[3], out RsaOaepKekGenerationSource); byte[] key1 = new byte[0x10]; Crypto.DecryptEcb(HACGUIKeyset.Keyset.MasterKeys[0], RsaOaepKekGenerationSource, key1, 0x10); byte[] key2 = new byte[0x10]; Crypto.DecryptEcb(key1, EticketRsaKekekSource, key2, 0x10); byte[] key3 = new byte[0x10]; Crypto.DecryptEcb(key2, EticketRsaKekSource, HACGUIKeyset.Keyset.EticketRsaKek, 0x10); break; } break; } } Stream prodinfo = nand.OpenProdInfo(); Stream prodinfoFile = HACGUIKeyset.TempPRODINFOFileInfo.Create(); prodinfo.CopyTo(prodinfoFile); prodinfo.Close(); prodinfoFile.Seek(0, SeekOrigin.Begin); Calibration cal0 = new Calibration(prodinfoFile); HACGUIKeyset.Keyset.EticketExtKeyRsa = Crypto.DecryptRsaKey(cal0.EticketExtKeyRsa, HACGUIKeyset.Keyset.EticketRsaKek); prodinfoFile.Close(); List <Ticket> tickets = new List <Ticket>(); NandPartition system = nand.OpenSystemPartition(); Stream e1Stream = system.OpenFile("save\\80000000000000E1", FileMode.Open, FileAccess.Read); tickets.AddRange(ReadTickets(HACGUIKeyset.Keyset, e1Stream)); Stream e2Stream = system.OpenFile("save\\80000000000000E2", FileMode.Open, FileAccess.Read); tickets.AddRange(ReadTickets(HACGUIKeyset.Keyset, e2Stream)); Stream nsAppmanStream = system.OpenFile("save\\8000000000000043", FileMode.Open, FileAccess.Read); Savefile save = new Savefile(HACGUIKeyset.Keyset, nsAppmanStream, false); Stream privateStream = save.OpenFile("/private"); byte[] sdSeed = new byte[0x10]; privateStream.Read(sdSeed, 0, 0x10); // Seek doesn't work so i just read twice privateStream.Read(sdSeed, 0, 0x10); HACGUIKeyset.Keyset.SetSdSeed(sdSeed); foreach (Ticket ticket in tickets) { HACGUIKeyset.Keyset.TitleKeys[ticket.RightsId] = new byte[0x10]; Array.Copy(ticket.TitleKeyBlock, HACGUIKeyset.Keyset.TitleKeys[ticket.RightsId], 0x10); } NANDService.Stop(); HACGUIKeyset.ProductionKeysFileInfo.Create().WriteString(HACGUIKeyset.PrintCommonKeys(HACGUIKeyset.Keyset, true)); HACGUIKeyset.ExtraKeysFileInfo.Create().WriteString(HACGUIKeyset.PrintCommonWithoutFriendlyKeys(HACGUIKeyset.Keyset)); HACGUIKeyset.ConsoleKeysFileInfo.Create().WriteString(ExternalKeys.PrintUniqueKeys(HACGUIKeyset.Keyset)); HACGUIKeyset.GetConsoleKeysFileInfoByName(PickConsole.ConsoleName).Create().WriteString(ExternalKeys.PrintUniqueKeys(HACGUIKeyset.Keyset)); HACGUIKeyset.TitleKeysFileInfo.Create().WriteString(ExternalKeys.PrintTitleKeys(HACGUIKeyset.Keyset)); }
private void ImportGameDataClicked(object sender, RoutedEventArgs arg) { string filter = @" Any game data (*.xci,*.nca,*.nsp)|*.xci;*.nca;*.nsp| NX Card Image (*.xci)|*.xci| Nintendo Content Archive (*.nca)|*.nca| Nintendo Installable Package (*.nsp)|*.nsp ".FilterMultilineString(); FileInfo[] files = RequestOpenFilesFromUser(".*", filter, "Select game data..."); if (files == null) { return; } TaskManagerPage.Current.Queue.Submit(new RunTask("Processing imported game data...", new Task(() => { IEnumerable <FileInfo> ncas = files.Where((f) => { try { new Nca(HACGUIKeyset.Keyset, new LocalFile(f.FullName, OpenMode.Read).AsStorage()); return(true); } catch (Exception) { return(false); } }); IEnumerable <FileInfo> xcis = files.Except(ncas).Where((f) => { try { new Xci(HACGUIKeyset.Keyset, new LocalFile(f.FullName, OpenMode.Read).AsStorage()); return(true); } catch (Exception) { return(false); } }); IEnumerable <FileInfo> nsp = files.Except(ncas).Except(xcis).Where((f) => { try { new PartitionFileSystem(new LocalFile(f.FullName, OpenMode.Read).AsStorage()); return(true); } catch (Exception) { return(false); } }); List <SwitchFs> switchFilesystems = new List <SwitchFs>(); if (ncas.Any()) { switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, ncas.MakeFs())); } foreach (FileInfo file in xcis) { Xci xci = new Xci(HACGUIKeyset.Keyset, new LocalFile(file.FullName, OpenMode.Read).AsStorage()); switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, xci.OpenPartition(XciPartitionType.Secure))); } bool foundTicket = true; foreach (FileInfo file in nsp) { PartitionFileSystem fs = new PartitionFileSystem(new LocalFile(file.FullName, OpenMode.Read).AsStorage()); foreach (DirectoryEntry d in fs.EnumerateEntries().Where(e => e.Type == DirectoryEntryType.File && e.Name.EndsWith(".tik"))) { using (IFile tikFile = fs.OpenFile(d.FullPath, OpenMode.Read)) { Ticket t = new Ticket(new BinaryReader(tikFile.AsStream())); try { HACGUIKeyset.Keyset.TitleKeys[t.RightsId] = t.GetTitleKey(HACGUIKeyset.Keyset); foundTicket = true; } catch (Exception e) { MessageBox.Show("Failed to import .tik file included in NSP."); } } } switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, fs)); } if (foundTicket) { TaskManagerPage.Current.Queue.Submit(new SaveKeysetTask(Preferences.Current.DefaultConsoleName)); } foreach (SwitchFs fs in switchFilesystems) { DeviceService.FsView.LoadFileSystemAsync("Opening imported data...", () => fs, FSView.TitleSource.Imported, false); } }))); }
public static void Start() { if (!Started) { Started = true; NANDSystemTitleView = new FSView(TitleSource.NAND); NANDUserTitleView = new FSView(TitleSource.NAND); SDTitleView = new FSView(TitleSource.SD); Applications = new Dictionary <ulong, Application>(); Titles = new Dictionary <ulong, Title>(); Saves = new Dictionary <string, SaveDataFileSystem>(); SDService.OnSDPluggedIn += (drive) => { SDTitleView.Ready += (_, __) => { StatusService.SDStatus = StatusService.Status.OK; }; SDTitleView.LoadFileSystemAsync("Opening SD filesystem...", () => SwitchFs.OpenSdCard(HACGUIKeyset.Keyset, new LocalFileSystem(drive.RootDirectory.FullName)), true); Update(); StatusService.SDStatus = StatusService.Status.Progress; }; SDService.OnSDRemoved += (drive) => { StatusService.SDStatus = StatusService.Status.Incorrect; SDTitleView.FS = null; Update(); }; NANDService.OnNANDPluggedIn += () => { void onComplete() { StatusService.NANDStatus = StatusService.Status.OK; Update(); }; int count = 0; NANDSystemTitleView.Ready += (_, __) => { count++; if (count >= 2) { onComplete(); } }; NANDUserTitleView.Ready += (_, __) => { count++; if (count >= 2) { onComplete(); } }; NANDUserTitleView.LoadFileSystemAsync("Opening NAND user filesystem...", () => SwitchFs.OpenNandPartition(HACGUIKeyset.Keyset, NANDService.NAND.OpenUserPartition()), false); NANDSystemTitleView.LoadFileSystemAsync("Opening NAND system filesystem...", () => SwitchFs.OpenNandPartition(HACGUIKeyset.Keyset, NANDService.NAND.OpenSystemPartition()), true); TaskManagerPage.Current.Queue.Submit(new DecryptTicketsTask()); StatusService.NANDStatus = StatusService.Status.Progress; }; NANDService.OnNANDRemoved += () => { StatusService.NANDStatus = StatusService.Status.Incorrect; NANDSystemTitleView.FS = null; NANDUserTitleView.FS = null; Update(); }; SDService.Start(); NANDService.Start(); Update(); } }
public static void Process(Context ctx) { var switchFs = new SwitchFs(ctx.Keyset, new FileSystem(ctx.Options.InFile)); if (ctx.Options.ListTitles) { ListTitles(switchFs); } if (ctx.Options.ListApps) { ctx.Logger.LogMessage(ListApplications(switchFs)); } if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to dump ExeFS"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } if (title.MainNca == null) { ctx.Logger.LogMessage($"Could not find main data for title {id:X16}"); return; } NcaSection section = title.MainNca.Sections[(int)ProgramPartitionType.Code]; if (section == null) { ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no ExeFS section"); return; } if (ctx.Options.ExefsOutDir != null) { title.MainNca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOut != null) { title.MainNca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to dump RomFS"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } if (title.MainNca == null) { ctx.Logger.LogMessage($"Could not find main data for title {id:X16}"); return; } NcaSection section = title.MainNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr); if (section == null) { ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no RomFS section"); return; } if (ctx.Options.RomfsOutDir != null) { var romfs = new Romfs(title.MainNca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel)); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } if (ctx.Options.RomfsOut != null) { title.MainNca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.OutDir != null) { SaveTitle(ctx, switchFs); } if (ctx.Options.NspOut != null) { ProcessNsp.CreateNsp(ctx, switchFs); } if (ctx.Options.SaveOutDir != null) { ExportSdSaves(ctx, switchFs); } if (ctx.Options.Validate) { ValidateSwitchFs(ctx, switchFs); } }
public static void Process(Context ctx) { SwitchFs switchFs; var baseFs = new LocalFileSystem(ctx.Options.InFile); if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Nintendo", "Contents", "registered"))) { ctx.Logger.LogMessage("Treating path as SD card storage"); switchFs = SwitchFs.OpenSdCard(ctx.Keyset, baseFs); } else if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Contents", "registered"))) { ctx.Logger.LogMessage("Treating path as NAND storage"); switchFs = SwitchFs.OpenNandPartition(ctx.Keyset, baseFs); } else { ctx.Logger.LogMessage("Treating path as a directory of loose NCAs"); switchFs = SwitchFs.OpenNcaDirectory(ctx.Keyset, baseFs); } if (ctx.Options.ListNcas) { ctx.Logger.LogMessage(ListNcas(switchFs)); } if (ctx.Options.ListTitles) { ctx.Logger.LogMessage(ListTitles(switchFs)); } if (ctx.Options.ListApps) { ctx.Logger.LogMessage(ListApplications(switchFs)); } if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to dump ExeFS"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } if (title.MainNca == null) { ctx.Logger.LogMessage($"Could not find main data for title {id:X16}"); return; } NcaSection section = title.MainNca.Sections[(int)ProgramPartitionType.Code]; if (section == null) { ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no ExeFS section"); return; } if (ctx.Options.ExefsOutDir != null) { title.MainNca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); } if (ctx.Options.ExefsOut != null) { title.MainNca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to dump RomFS"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } if (title.MainNca == null) { ctx.Logger.LogMessage($"Could not find main data for title {id:X16}"); return; } NcaSection section = title.MainNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr); if (section == null) { ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no RomFS section"); return; } if (ctx.Options.RomfsOutDir != null) { var romfs = new RomFsFileSystem(title.MainNca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel, true)); romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger); } if (ctx.Options.RomfsOut != null) { title.MainNca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); } } if (ctx.Options.OutDir != null) { SaveTitle(ctx, switchFs); } if (ctx.Options.NspOut != null) { ProcessNsp.CreateNsp(ctx, switchFs); } if (ctx.Options.SaveOutDir != null) { ExportSdSaves(ctx, switchFs); } if (ctx.Options.Validate) { ValidateSwitchFs(ctx, switchFs); } }
private void OnNandFound() { Nand nand = NANDService.NAND; // stream package2 to memory IStorage pkg2nand = nand.OpenPackage2(0); // 0 -> BCPKG2-1-Normal-Main byte[] pkg2raw = new byte[0x7FC000]; // maximum size of pkg2 pkg2nand.Read(0x4000, pkg2raw); MemoryStorage pkg2memory = new MemoryStorage(pkg2raw); HACGUIKeyset.RootTempFolderInfo.Create(); // copy to file for end user using (FileStream pkg2file = HACGUIKeyset.TempPkg2FileInfo.Create()) pkg2memory.CopyToStream(pkg2file); Package2 pkg2 = new Package2(HACGUIKeyset.Keyset, pkg2memory); HACGUIKeyset.RootTempPkg2FolderInfo.Create(); // make sure it exists using (FileStream kernelstream = HACGUIKeyset.TempKernelFileInfo.Create()) pkg2.OpenKernel().CopyToStream(kernelstream); using (FileStream INI1stream = HACGUIKeyset.TempINI1FileInfo.Create()) pkg2.OpenIni1().CopyToStream(INI1stream); Ini1 INI1 = new Ini1(pkg2.OpenIni1()); List <HashSearchEntry> hashes = new List <HashSearchEntry>(); HACGUIKeyset.RootTempINI1FolderInfo.Create(); foreach (Kip kip in INI1.Kips) { using (Stream rodatastream = new MemoryStream(kip.DecompressSection(1))) switch (kip.Header.Name) { case "FS": hashes.Add(new HashSearchEntry( NintendoKeys.KeyAreaKeyApplicationSourceHash, () => HACGUIKeyset.Keyset.KeyAreaKeyApplicationSource, 0x10)); hashes.Add(new HashSearchEntry( NintendoKeys.KeyAreaKeyOceanSourceHash, () => HACGUIKeyset.Keyset.KeyAreaKeyOceanSource, 0x10)); hashes.Add(new HashSearchEntry( NintendoKeys.KeyAreaKeySystemSourceHash, () => HACGUIKeyset.Keyset.KeyAreaKeySystemSource, 0x10)); hashes.Add(new HashSearchEntry( NintendoKeys.HeaderKekSourceHash, () => HACGUIKeyset.Keyset.HeaderKekSource, 0x10)); hashes.Add(new HashSearchEntry( NintendoKeys.SaveMacKekSourceHash, () => HACGUIKeyset.Keyset.SaveMacKekSource, 0x10)); hashes.Add(new HashSearchEntry( NintendoKeys.SaveMacKeySourceHash, () => HACGUIKeyset.Keyset.SaveMacKeySource, 0x10)); rodatastream.FindKeysViaHash(hashes, new SHA256Managed(), 0x10); hashes.Clear(); rodatastream.Seek(0, SeekOrigin.Begin); bool sdWarn = false; hashes.Add(new HashSearchEntry(NintendoKeys.SDCardKekSourceHash, () => HACGUIKeyset.Keyset.SdCardKekSource, 0x10)); try { rodatastream.FindKeysViaHash(hashes, new SHA256Managed(), 0x10); } catch (EndOfStreamException) { MessageBox.Show("Failed to find SD card kek source! The NAND is probably from 1.0.0."); sdWarn = true; } if (!sdWarn) // don't try to find the rest of the keys if the other one couldn't be found { hashes.Clear(); rodatastream.Seek(0, SeekOrigin.Begin); hashes.Add(new HashSearchEntry( NintendoKeys.SDCardSaveKeySourceHash, () => HACGUIKeyset.Keyset.SdCardKeySources[0], 0x20)); hashes.Add(new HashSearchEntry( NintendoKeys.SDCardNcaKeySourceHash, () => HACGUIKeyset.Keyset.SdCardKeySources[1], 0x20)); rodatastream.FindKeysViaHash(hashes, new SHA256Managed(), 0x20); } hashes.Clear(); hashes.Add(new HashSearchEntry( NintendoKeys.HeaderKeySourceHash, () => HACGUIKeyset.Keyset.HeaderKeySource, 0x20)); using (Stream datastream = new MemoryStream(kip.DecompressSection(2))) datastream.FindKeysViaHash(hashes, new SHA256Managed(), 0x20); hashes.Clear(); break; case "spl": hashes.Clear(); hashes.Add(new HashSearchEntry( NintendoKeys.AesKeyGenerationSourceHash, () => HACGUIKeyset.Keyset.AesKeyGenerationSource, 0x10)); rodatastream.FindKeysViaHash(hashes, new SHA256Managed(), 0x10); break; } using (FileStream kipstream = HACGUIKeyset.RootTempINI1FolderInfo.GetFile(kip.Header.Name + ".kip").Create()) kip.OpenRawFile().CopyToStream(kipstream); } HACGUIKeyset.Keyset.DeriveKeys(); SwitchFs fs = SwitchFs.OpenNandPartition(HACGUIKeyset.Keyset, nand.OpenSystemPartition()); NintendoKeys.KekSeeds[1].XOR(NintendoKeys.KekMasks[0], out byte[] RsaPrivateKekGenerationSource); NintendoKeys.KekSeeds[3].XOR(NintendoKeys.KekMasks[0], out byte[] RsaOaepKekGenerationSource); foreach (Nca nca in fs.Ncas.Values.Select(n => n.Nca)) { ulong titleId = nca.Header.TitleId; if (!new ulong[] { // check if title ID is one that needs to be processed before opening it 0x0100000000000033, // es 0x0100000000000024, // ssl }.Contains(titleId)) { continue; } // mainly to check if the NCA can be decrypted if (!nca.CanOpenSection(0)) { continue; } if (nca.Header.ContentType != NcaContentType.Program) { continue; } IFileSystem pfs = nca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid); pfs.OpenFile(out IFile nsoFile, "main".ToU8Span(), OpenMode.Read); Nso nso = new Nso(new FileStorage(nsoFile)); NsoSection section = nso.Sections[1]; Stream data = new MemoryStream(section.DecompressSection()); byte[] key1; byte[] key2; switch (titleId) { case 0x0100000000000033: // es hashes.Clear(); byte[] EticketRsaKekSource = new byte[0x10]; byte[] EticketRsaKekekSource = new byte[0x10]; hashes.Add(new HashSearchEntry( NintendoKeys.EticketRsaKekSourceHash, () => EticketRsaKekSource, 0x10)); hashes.Add(new HashSearchEntry( NintendoKeys.EticketRsaKekekSourceHash, () => EticketRsaKekekSource, 0x10)); data.FindKeysViaHash(hashes, new SHA256Managed(), 0x10, data.Length); key1 = new byte[0x10]; new AesEcbDecryptor(HACGUIKeyset.Keyset.MasterKeys[0]).Transform(RsaOaepKekGenerationSource, key1); key2 = new byte[0x10]; new AesEcbDecryptor(key1).Transform(EticketRsaKekekSource, key2); new AesEcbDecryptor(key2).Transform(EticketRsaKekSource, HACGUIKeyset.Keyset.EticketRsaKek); break; case 0x0100000000000024: // ssl hashes.Clear(); byte[] SslAesKeyX = new byte[0x10]; byte[] SslRsaKeyY = new byte[0x10]; hashes.Add(new HashSearchEntry( NintendoKeys.SslAesKeyXHash, () => SslAesKeyX, 0x10)); hashes.Add(new HashSearchEntry( NintendoKeys.SslRsaKeyYHash, () => SslRsaKeyY, 0x10)); data.FindKeysViaHash(hashes, new SHA256Managed(), 0x10, data.Length); key1 = new byte[0x10]; new AesEcbDecryptor(HACGUIKeyset.Keyset.MasterKeys[0]).Transform(RsaPrivateKekGenerationSource, key1); key2 = new byte[0x10]; new AesEcbDecryptor(key1).Transform(SslAesKeyX, key2); new AesEcbDecryptor(key2).Transform(SslRsaKeyY, HACGUIKeyset.Keyset.SslRsaKek); break; } } // save PRODINFO to file, then derive eticket_ext_key_rsa if (!TryDumpCert(nand: nand)) { MessageBox.Show($"Failed to parse decrypted certificate. If you are using Incognito, select your PRODINFO backup now."); Dispatcher.Invoke(() => // dispatcher is required, otherwise a deadlock occurs. probably some threading issue { while (true) { FileInfo info = RequestOpenFileFromUser(".bin", "PRODINFO backup (.bin)|*.bin", "Select a valid PRODINFO backup...", "PRODINFO.bin"); if (info != null) { if (TryDumpCert(info)) { break; } } else { MessageBox.Show("Failed to parse provided PRODINFO. You must have a valid PRODINFO backup."); } } }); } // get tickets new DecryptTicketsTask(PickConsolePage.ConsoleName).CreateTask().RunSynchronously(); FatFileSystemProvider system = NANDService.NAND.OpenSystemPartition(); system.OpenFile(out IFile nsAppmanFile, "save\\8000000000000043".ToU8Span(), OpenMode.Read); IStorage nsAppmanStorage = nsAppmanFile.AsStorage(); SaveDataFileSystem nsAppmanSave = new SaveDataFileSystem(HACGUIKeyset.Keyset, nsAppmanStorage, IntegrityCheckLevel.ErrorOnInvalid, false); nsAppmanSave.OpenFile(out IFile privateFile, "/private".ToU8Span(), OpenMode.Read); byte[] sdIdenitifer = new byte[0x10]; byte[] sdSeed = new byte[0x10]; using (nsAppmanFile) using (nsAppmanSave) using (privateFile) { IStorage privateStorage = privateFile.AsStorage(); privateStorage.Read(0, sdIdenitifer); // stored on SD and NAND, used to uniquely idenitfy the SD/NAND privateStorage.Read(0x10, sdSeed); } HACGUIKeyset.Keyset.SetSdSeed(sdSeed); Preferences.Current.SdIdentifiers[sdIdenitifer.ToHexString()] = sdSeed.ToHexString(); NANDService.Stop(); DirectoryInfo oldKeysDirectory = HACGUIKeyset.RootFolderInfo.GetDirectory("keys"); if (oldKeysDirectory.Exists) { oldKeysDirectory.Delete(true); // fix old versions after restructure of directory } // write all keys to file new SaveKeysetTask(PickConsolePage.ConsoleName).CreateTask().RunSynchronously(); Preferences.Current.DefaultConsoleName = PickConsolePage.ConsoleName; Preferences.Current.Write(); }
public static void Process(Context ctx) { SwitchFs switchFs; var baseFs = new LocalFileSystem(ctx.Options.InFile); if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Nintendo", "Contents", "registered"))) { ctx.Logger.LogMessage("Treating path as SD card storage"); switchFs = SwitchFs.OpenSdCard(ctx.Keyset, baseFs); CheckForNcaFolders(ctx, switchFs); } else if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Contents", "registered"))) { ctx.Logger.LogMessage("Treating path as NAND storage"); switchFs = SwitchFs.OpenNandPartition(ctx.Keyset, baseFs); CheckForNcaFolders(ctx, switchFs); } else { ctx.Logger.LogMessage("Treating path as a directory of loose NCAs"); switchFs = SwitchFs.OpenNcaDirectory(ctx.Keyset, baseFs); } if (ctx.Options.ListNcas) { ctx.Logger.LogMessage(ListNcas(switchFs)); } if (ctx.Options.ListTitles) { ctx.Logger.LogMessage(ListTitles(switchFs)); } if (ctx.Options.ListApps) { ctx.Logger.LogMessage(ListApplications(switchFs)); } if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to dump ExeFS"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } if (title.MainNca == null) { ctx.Logger.LogMessage($"Could not find main data for title {id:X16}"); return; } if (!title.MainNca.Nca.SectionExists(NcaSectionType.Code)) { ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no ExeFS section"); return; } if (ctx.Options.ExefsOutDir != null) { IFileSystem fs = title.MainNca.OpenFileSystem(NcaSectionType.Code, ctx.Options.IntegrityLevel); fs.Extract(ctx.Options.ExefsOutDir, ctx.Logger); } if (ctx.Options.ExefsOut != null) { title.MainNca.OpenStorage(NcaSectionType.Code, ctx.Options.IntegrityLevel).WriteAllBytes(ctx.Options.ExefsOut, ctx.Logger); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null) { ulong id = ctx.Options.TitleId; if (id == 0) { ctx.Logger.LogMessage("Title ID must be specified to dump RomFS"); return; } if (!switchFs.Titles.TryGetValue(id, out Title title)) { ctx.Logger.LogMessage($"Could not find title {id:X16}"); return; } if (title.MainNca == null) { ctx.Logger.LogMessage($"Could not find main data for title {id:X16}"); return; } if (!title.MainNca.Nca.SectionExists(NcaSectionType.Data)) { ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no RomFS section"); return; } ProcessRomfs.Process(ctx, title.MainNca.OpenStorage(NcaSectionType.Data, ctx.Options.IntegrityLevel)); } if (ctx.Options.OutDir != null) { SaveTitle(ctx, switchFs); } if (ctx.Options.NspOut != null) { ProcessPfs.CreateNsp(ctx, switchFs); } if (ctx.Options.SaveOutDir != null) { ExportSdSaves(ctx, switchFs); } if (ctx.Options.Validate) { ValidateSwitchFs(ctx, switchFs); } }
private void ImportGameDataClicked(object sender, RoutedEventArgs e) { string filter = @" Any game data (*.xci,*.nca,*.nsp)|*.xci;*.nca;*.nsp| NX Card Image (*.xci)|*.xci| Nintendo Content Archive (*.nca)|*.nca| Nintendo Installable Package (*.nsp)|*.nsp ".FilterMultilineString(); FileInfo[] files = RequestOpenFilesFromUser(".*", filter, "Select game data..."); if (files == null) { return; } TaskManagerPage.Current.Queue.Submit(new RunTask("Processing imported game data...", new Task(() => { IEnumerable <FileInfo> ncas = files.Where((f) => { try { new Nca(HACGUIKeyset.Keyset, new LocalFile(f.FullName, OpenMode.Read).AsStorage()); return(true); } catch (Exception) { return(false); } }); IEnumerable <FileInfo> xcis = files.Except(ncas).Where((f) => { try { new Xci(HACGUIKeyset.Keyset, new LocalFile(f.FullName, OpenMode.Read).AsStorage()); return(true); } catch (Exception) { return(false); } }); IEnumerable <FileInfo> nsp = files.Except(ncas).Except(xcis).Where((f) => { try { new PartitionFileSystem(new LocalFile(f.FullName, OpenMode.Read).AsStorage()); return(true); } catch (Exception) { return(false); } }); List <SwitchFs> switchFilesystems = new List <SwitchFs>(); PseudoFileSystem ncaFs = new PseudoFileSystem(); foreach (FileInfo file in ncas) { LocalFileSystem fs = new LocalFileSystem(file.Directory.FullName); // clean up filename so it only ends with .nca, then map to actual name string s = file.Name; while (s.EndsWith(".nca")) { s = s.Substring(0, s.IndexOf(".nca")); } ncaFs.Add($"/{s}.nca", $"/{file.Name}", fs); } if (ncas.Any()) { switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, ncaFs)); } foreach (FileInfo file in xcis) { Xci xci = new Xci(HACGUIKeyset.Keyset, new LocalFile(file.FullName, OpenMode.Read).AsStorage()); switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, xci.OpenPartition(XciPartitionType.Secure))); } foreach (FileInfo file in nsp) { PartitionFileSystem fs = new PartitionFileSystem(new LocalFile(file.FullName, OpenMode.Read).AsStorage()); switchFilesystems.Add(SwitchFs.OpenNcaDirectory(HACGUIKeyset.Keyset, fs)); } foreach (SwitchFs fs in switchFilesystems) { DeviceService.FsView.LoadFileSystemAsync("Opening imported data...", () => fs, FSView.TitleSource.Imported, false); } }))); }
public static void Start() { if (!Started) { Started = true; FsView = new FSView(); int count = 0; FsView.Ready += (source) => { if (source == TitleSource.SD) { StatusService.SDStatus = StatusService.Status.OK; } else if (source == TitleSource.NAND) { count++; if (count == 2) { StatusService.NANDStatus = StatusService.Status.OK; Update(); count = 0; } return; } Update(); }; SDService.OnSDPluggedIn += (_) => { FsView.LoadFileSystemAsync("Opening SD filesystem...", () => SwitchFs.OpenSdCard(HACGUIKeyset.Keyset, new LocalFileSystem(SDService.SDEffectiveRoot.FullName)), TitleSource.SD, true); StatusService.SDStatus = StatusService.Status.Progress; }; SDService.OnSDRemoved += (drive) => { StatusService.SDStatus = StatusService.Status.Incorrect; FsView.IndexedFilesystems[TitleSource.SD].Clear(); Update(); }; NANDService.OnNANDPluggedIn += () => { FsView.LoadFileSystemAsync("Opening NAND user filesystem...", () => SwitchFs.OpenNandPartition(HACGUIKeyset.Keyset, NANDService.NAND.OpenUserPartition()), TitleSource.NAND, false); FsView.LoadFileSystemAsync("Opening NAND system filesystem...", () => SwitchFs.OpenNandPartition(HACGUIKeyset.Keyset, NANDService.NAND.OpenSystemPartition()), TitleSource.NAND, true); TaskManagerPage.Current.Queue.Submit(new DecryptTicketsTask(Preferences.Current.DefaultConsoleName)); TaskManagerPage.Current.Queue.Submit(new SaveKeysetTask(Preferences.Current.DefaultConsoleName)); // TODO TaskManagerPage.Current.Queue.Submit(new CopyAccountDataTask()); StatusService.NANDStatus = StatusService.Status.Progress; }; NANDService.OnNANDRemoved += () => { StatusService.NANDStatus = StatusService.Status.Incorrect; FsView.IndexedFilesystems[TitleSource.NAND].Clear(); Update(); }; SDService.Start(); NANDService.Start(); } }