2018-08-31 18:19:14 +02:00
|
|
|
|
// ReSharper disable UnusedVariable UnusedMember.Local
|
|
|
|
|
using System;
|
2018-07-10 01:34:46 +02:00
|
|
|
|
using System.Collections.Generic;
|
2018-07-09 18:49:59 +02:00
|
|
|
|
using System.IO;
|
2018-08-31 17:47:11 +02:00
|
|
|
|
using LibHac;
|
2018-11-19 05:20:34 +01:00
|
|
|
|
using LibHac.IO;
|
|
|
|
|
using LibHac.IO.Save;
|
2018-08-31 17:47:11 +02:00
|
|
|
|
using LibHac.Nand;
|
2018-07-09 18:49:59 +02:00
|
|
|
|
|
|
|
|
|
namespace NandReader
|
|
|
|
|
{
|
|
|
|
|
public static class Program
|
|
|
|
|
{
|
|
|
|
|
public static void Main(string[] args)
|
|
|
|
|
{
|
2018-07-10 01:34:46 +02:00
|
|
|
|
if (args.Length != 1)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Usage: NandReader raw_nand_dump_file");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-07-13 17:25:39 +02:00
|
|
|
|
GetTitleKeys(args[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void GetTitleKeys(string nandFile)
|
|
|
|
|
{
|
|
|
|
|
using (var logger = new ProgressBar())
|
|
|
|
|
using (var stream = new FileStream(nandFile, FileMode.Open, FileAccess.Read))
|
|
|
|
|
{
|
2018-10-03 00:25:58 +02:00
|
|
|
|
Keyset keyset = OpenKeyset();
|
2018-07-13 17:25:39 +02:00
|
|
|
|
var nand = new Nand(stream, keyset);
|
2018-10-03 00:25:58 +02:00
|
|
|
|
Stream prodinfo = nand.OpenProdInfo();
|
2018-07-13 17:25:39 +02:00
|
|
|
|
var calibration = new Calibration(prodinfo);
|
|
|
|
|
|
2018-09-13 03:27:46 +02:00
|
|
|
|
keyset.EticketExtKeyRsa = Crypto.DecryptRsaKey(calibration.EticketExtKeyRsa, keyset.EticketRsaKek);
|
2018-10-03 00:25:58 +02:00
|
|
|
|
Ticket[] tickets = GetTickets(keyset, nand, logger);
|
2018-07-13 17:25:39 +02:00
|
|
|
|
|
2018-10-03 00:25:58 +02:00
|
|
|
|
foreach (Ticket ticket in tickets)
|
2018-07-13 17:25:39 +02:00
|
|
|
|
{
|
2018-10-03 00:25:58 +02:00
|
|
|
|
byte[] key = ticket.GetTitleKey(keyset);
|
2018-07-13 17:25:39 +02:00
|
|
|
|
logger.LogMessage($"{ticket.RightsId.ToHexString()},{key.ToHexString()}");
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-12 03:03:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void ReadSwitchFs(string nandFile)
|
|
|
|
|
{
|
|
|
|
|
using (var logger = new ProgressBar())
|
|
|
|
|
using (var stream = new FileStream(nandFile, FileMode.Open, FileAccess.Read))
|
|
|
|
|
{
|
2018-10-03 00:25:58 +02:00
|
|
|
|
Keyset keyset = OpenKeyset();
|
2018-07-12 03:03:09 +02:00
|
|
|
|
var nand = new Nand(stream, keyset);
|
2019-01-11 22:26:27 +01:00
|
|
|
|
FatFileSystemProvider user = nand.OpenSystemPartition();
|
2019-01-13 22:06:15 +01:00
|
|
|
|
SwitchFs sdfs = SwitchFs.OpenNandPartition(keyset, user);
|
2018-07-12 03:03:09 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void ReadCalibration(string nandFile)
|
|
|
|
|
{
|
|
|
|
|
using (var logger = new ProgressBar())
|
|
|
|
|
using (var stream = new FileStream(nandFile, FileMode.Open, FileAccess.Read))
|
|
|
|
|
{
|
2018-10-03 00:25:58 +02:00
|
|
|
|
Keyset keyset = OpenKeyset();
|
2018-07-12 03:03:09 +02:00
|
|
|
|
var nand = new Nand(stream, keyset);
|
2018-10-03 00:25:58 +02:00
|
|
|
|
Stream prodinfo = nand.OpenProdInfo();
|
2018-07-12 03:03:09 +02:00
|
|
|
|
var calibration = new Calibration(prodinfo);
|
|
|
|
|
}
|
2018-07-09 18:49:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-10 01:34:46 +02:00
|
|
|
|
private static void DumpTickets(string nandFile)
|
2018-07-09 18:49:59 +02:00
|
|
|
|
{
|
|
|
|
|
using (var logger = new ProgressBar())
|
2018-07-10 01:34:46 +02:00
|
|
|
|
using (var stream = new FileStream(nandFile, FileMode.Open, FileAccess.Read))
|
2018-07-09 18:49:59 +02:00
|
|
|
|
{
|
2018-10-03 00:25:58 +02:00
|
|
|
|
Keyset keyset = OpenKeyset();
|
2018-07-10 01:34:46 +02:00
|
|
|
|
var nand = new Nand(stream, keyset);
|
2018-10-03 00:25:58 +02:00
|
|
|
|
Ticket[] tickets = GetTickets(keyset, nand, logger);
|
2018-07-10 01:34:46 +02:00
|
|
|
|
|
|
|
|
|
Directory.CreateDirectory("tickets");
|
2018-10-03 00:25:58 +02:00
|
|
|
|
foreach (Ticket ticket in tickets)
|
2018-07-10 01:34:46 +02:00
|
|
|
|
{
|
2018-10-03 00:25:58 +02:00
|
|
|
|
string filename = Path.Combine("tickets", $"{ticket.RightsId.ToHexString()}.tik");
|
2018-07-10 01:34:46 +02:00
|
|
|
|
File.WriteAllBytes(filename, ticket.File);
|
|
|
|
|
}
|
2018-07-09 18:49:59 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-16 05:10:42 +02:00
|
|
|
|
private static Ticket[] GetTickets(Keyset keyset, Nand nand, IProgressReport logger = null)
|
2018-07-13 17:25:39 +02:00
|
|
|
|
{
|
|
|
|
|
var tickets = new List<Ticket>();
|
2019-01-11 22:26:27 +01:00
|
|
|
|
FatFileSystemProvider system = nand.OpenSystemPartition();
|
2018-07-13 17:25:39 +02:00
|
|
|
|
|
2019-01-11 22:26:27 +01:00
|
|
|
|
IFile saveE1File = system.OpenFile("/save/80000000000000E1", OpenMode.Read);
|
|
|
|
|
tickets.AddRange(ReadTickets(keyset, saveE1File.AsStream()));
|
2018-07-13 17:25:39 +02:00
|
|
|
|
|
2019-01-11 22:26:27 +01:00
|
|
|
|
IFile saveE2 = system.OpenFile("/save/80000000000000E2", OpenMode.Read);
|
|
|
|
|
tickets.AddRange(ReadTickets(keyset, saveE2.AsStream()));
|
2018-07-13 17:25:39 +02:00
|
|
|
|
|
|
|
|
|
logger?.LogMessage($"Found {tickets.Count} tickets");
|
|
|
|
|
|
|
|
|
|
return tickets.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-16 05:10:42 +02:00
|
|
|
|
private static List<Ticket> ReadTickets(Keyset keyset, Stream savefile)
|
2018-08-25 01:27:04 +02:00
|
|
|
|
{
|
|
|
|
|
var tickets = new List<Ticket>();
|
2019-01-05 05:00:56 +01:00
|
|
|
|
var save = new SaveDataFileSystem(keyset, savefile.AsStorage(), IntegrityCheckLevel.None, true);
|
|
|
|
|
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin", OpenMode.Read).AsStream());
|
|
|
|
|
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin", OpenMode.Read).AsStream());
|
2018-08-25 01:27:04 +02:00
|
|
|
|
|
2018-10-03 00:25:58 +02:00
|
|
|
|
ulong titleId = ticketList.ReadUInt64();
|
2018-08-25 01:27:04 +02:00
|
|
|
|
while (titleId != ulong.MaxValue)
|
|
|
|
|
{
|
|
|
|
|
ticketList.BaseStream.Position += 0x18;
|
2018-10-03 00:25:58 +02:00
|
|
|
|
long start = ticketFile.BaseStream.Position;
|
2018-08-25 01:27:04 +02:00
|
|
|
|
tickets.Add(new Ticket(ticketFile));
|
|
|
|
|
ticketFile.BaseStream.Position = start + 0x400;
|
|
|
|
|
titleId = ticketList.ReadUInt64();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tickets;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 18:49:59 +02:00
|
|
|
|
private static Keyset OpenKeyset()
|
|
|
|
|
{
|
2018-10-03 00:25:58 +02:00
|
|
|
|
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
|
|
|
|
string homeKeyFile = Path.Combine(home, ".switch", "prod.keys");
|
|
|
|
|
string homeTitleKeyFile = Path.Combine(home, ".switch", "title.keys");
|
|
|
|
|
string homeConsoleKeyFile = Path.Combine(home, ".switch", "console.keys");
|
2018-07-09 18:49:59 +02:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-13 17:25:39 +02:00
|
|
|
|
|