using System; using System.Collections.Generic; using System.IO; using System.Management; using System.Windows.Input; using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using LibHac; using LibHac.Nand; using LibHac.Savefile; using LibHac.Streams; namespace NandReaderGui.ViewModel { public class NandViewModel : ViewModelBase { public List Disks { get; } = new List(); public ICommand OpenCommand { get; set; } public DiskInfo SelectedDisk { get; set; } public NandViewModel() { OpenCommand = new RelayCommand(Open); var query = new WqlObjectQuery("SELECT * FROM Win32_DiskDrive"); using (var searcher = new ManagementObjectSearcher(query)) { foreach (var drive in searcher.Get()) { if (drive.GetPropertyValue("Size") == null) continue; var info = new DiskInfo(); info.PhysicalName = (string)drive.GetPropertyValue("Name"); info.Name = (string)drive.GetPropertyValue("Caption"); info.Model = (string)drive.GetPropertyValue("Model"); info.Length = (long)((ulong)drive.GetPropertyValue("Size")); info.SectorSize = (int)((uint)drive.GetPropertyValue("BytesPerSector")); info.DisplaySize = Util.GetBytesReadable((long)((ulong)drive.GetPropertyValue("Size"))); Disks.Add(info); } } } public void Open() { var disk = SelectedDisk; var stream = new RandomAccessSectorStream(new SectorStream(new DeviceStream(disk.PhysicalName, disk.Length), disk.SectorSize * 100)); var keyset = OpenKeyset(); var nand = new Nand(stream, keyset); var prodinfo = nand.OpenProdInfo(); var calibration = new Calibration(prodinfo); keyset.eticket_ext_key_rsa = Crypto.DecryptRsaKey(calibration.EticketExtKeyRsa, keyset.eticket_rsa_kek); var tickets = GetTickets(nand); using (var outStream = new StreamWriter("titlekeys.txt")) { foreach (var ticket in tickets) { var key = ticket.GetTitleKey(keyset); outStream.WriteLine($"{ticket.RightsId.ToHexString()},{key.ToHexString()}"); } } } private static Ticket[] GetTickets(Nand nand, IProgressReport logger = null) { var tickets = new List(); var system = nand.OpenSystemPartition(); var saveE1File = system.OpenFile("save\\80000000000000E1", FileMode.Open, FileAccess.Read); tickets.AddRange(ReadTickets(saveE1File)); var saveE2 = system.OpenFile("save\\80000000000000E2", FileMode.Open, FileAccess.Read); tickets.AddRange(ReadTickets(saveE2)); logger?.LogMessage($"Found {tickets.Count} tickets"); return tickets.ToArray(); } private static List ReadTickets(Stream savefile) { var tickets = new List(); var save = new Savefile(savefile); var ticketList = new BinaryReader(save.OpenFile("ticket_list.bin")); var ticketFile = new BinaryReader(save.OpenFile("ticket.bin")); var titleId = ticketList.ReadUInt64(); while (titleId != ulong.MaxValue) { ticketList.BaseStream.Position += 0x18; var start = ticketFile.BaseStream.Position; tickets.Add(new Ticket(ticketFile)); ticketFile.BaseStream.Position = start + 0x400; titleId = ticketList.ReadUInt64(); } return tickets; } private static Keyset OpenKeyset() { var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var homeKeyFile = Path.Combine(home, ".switch", "prod.keys"); var homeTitleKeyFile = Path.Combine(home, ".switch", "title.keys"); var homeConsoleKeyFile = Path.Combine(home, ".switch", "console.keys"); string keyFile = null; string titleKeyFile = null; string consoleKeyFile = null; if (File.Exists(homeKeyFile)) { keyFile = homeKeyFile; } if (File.Exists(homeTitleKeyFile)) { titleKeyFile = homeTitleKeyFile; } if (File.Exists(homeConsoleKeyFile)) { consoleKeyFile = homeConsoleKeyFile; } return ExternalKeys.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile); } } public class DiskInfo { public string PhysicalName { get; set; } public string Name { get; set; } public string Model { get; set; } public long Length { get; set; } public int SectorSize { get; set; } public string DisplaySize { get; set; } public string Display => $"{Name} ({DisplaySize})"; } }