2018-07-14 22:36:47 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Management;
|
|
|
|
|
using System.Windows.Input;
|
|
|
|
|
using GalaSoft.MvvmLight;
|
|
|
|
|
using GalaSoft.MvvmLight.Command;
|
2018-08-31 17:47:11 +02:00
|
|
|
|
using LibHac;
|
|
|
|
|
using LibHac.Nand;
|
|
|
|
|
using LibHac.Savefile;
|
|
|
|
|
using LibHac.Streams;
|
2018-07-14 22:36:47 +02:00
|
|
|
|
|
|
|
|
|
namespace NandReaderGui.ViewModel
|
|
|
|
|
{
|
|
|
|
|
public class NandViewModel : ViewModelBase
|
|
|
|
|
{
|
|
|
|
|
public List<DiskInfo> Disks { get; } = new List<DiskInfo>();
|
|
|
|
|
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<Ticket>();
|
|
|
|
|
var system = nand.OpenSystemPartition();
|
|
|
|
|
|
2018-08-25 01:27:04 +02:00
|
|
|
|
var saveE1File = system.OpenFile("save\\80000000000000E1", FileMode.Open, FileAccess.Read);
|
|
|
|
|
tickets.AddRange(ReadTickets(saveE1File));
|
2018-07-14 22:36:47 +02:00
|
|
|
|
|
|
|
|
|
var saveE2 = system.OpenFile("save\\80000000000000E2", FileMode.Open, FileAccess.Read);
|
2018-08-25 01:27:04 +02:00
|
|
|
|
tickets.AddRange(ReadTickets(saveE2));
|
2018-07-14 22:36:47 +02:00
|
|
|
|
|
|
|
|
|
logger?.LogMessage($"Found {tickets.Count} tickets");
|
|
|
|
|
|
|
|
|
|
return tickets.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-25 01:27:04 +02:00
|
|
|
|
private static List<Ticket> ReadTickets(Stream savefile)
|
|
|
|
|
{
|
|
|
|
|
var tickets = new List<Ticket>();
|
|
|
|
|
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;
|
|
|
|
|
}
|
2018-07-14 22:36:47 +02:00
|
|
|
|
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})";
|
|
|
|
|
}
|
|
|
|
|
}
|