Beispiel #1
0
        public static List <Ticket> DumpTickets(Keyset keyset, IStorage savefile, string consoleName)
        {
            var           tickets      = new List <Ticket>();
            var           save         = new SaveDataFileSystem(keyset, savefile, IntegrityCheckLevel.ErrorOnInvalid, false);
            var           ticketList   = new BinaryReader(save.OpenFile("/ticket_list.bin", OpenMode.Read).AsStream());
            var           ticketFile   = new BinaryReader(save.OpenFile("/ticket.bin", OpenMode.Read).AsStream());
            DirectoryInfo ticketFolder = HACGUIKeyset.GetTicketsDirectory(consoleName);

            ticketFolder.Create();

            var titleId = ticketList.ReadUInt64();

            while (titleId != ulong.MaxValue)
            {
                ticketList.BaseStream.Position += 0x18;
                var    start            = ticketFile.BaseStream.Position;
                Ticket ticket           = new Ticket(ticketFile);
                Stream ticketFileStream = ticketFolder.GetFile(BitConverter.ToString(ticket.RightsId).Replace("-", "").ToLower() + ".tik").Create();
                byte[] data             = ticket.GetBytes();
                ticketFileStream.Write(data, 0, data.Length);
                ticketFileStream.Close();
                tickets.Add(ticket);
                ticketFile.BaseStream.Position = start + 0x400;
                titleId = ticketList.ReadUInt64();
            }

            return(tickets);
        }
Beispiel #2
0
        // ReSharper disable once UnusedMember.Local
        private static string PrintFatLayout(this SaveDataFileSystem save)
        {
            var sb = new StringBuilder();

            foreach (DirectoryEntry entry in save.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File))
            {
                save.SaveDataFileSystemCore.FileTable.TryOpenFile(entry.FullPath, out SaveFileInfo fileInfo);
                if (fileInfo.StartBlock < 0)
                {
                    continue;
                }

                IEnumerable <(int block, int length)> chain = save.SaveDataFileSystemCore.AllocationTable.DumpChain(fileInfo.StartBlock);

                sb.AppendLine(entry.FullPath);
                sb.AppendLine(PrintBlockChain(chain));
            }

            sb.AppendLine("Directory Table");
            sb.AppendLine(PrintBlockChain(save.SaveDataFileSystemCore.AllocationTable.DumpChain(0)));

            sb.AppendLine("File Table");
            sb.AppendLine(PrintBlockChain(save.SaveDataFileSystemCore.AllocationTable.DumpChain(1)));

            sb.AppendLine("Free blocks");
            sb.AppendLine(PrintBlockChain(save.SaveDataFileSystemCore.AllocationTable.DumpChain(-1)));

            return(sb.ToString());
        }
Beispiel #3
0
        private void OpenAllSaves()
        {
            if (SaveFs == null)
            {
                return;
            }

            foreach (DirectoryEntry fileEntry in SaveFs.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File))
            {
                SaveDataFileSystem save     = null;
                string             saveName = Path.GetFileNameWithoutExtension(fileEntry.Name);

                try
                {
                    IFile file = SaveFs.OpenFile(fileEntry.FullPath, OpenMode.Read);
                    save = new SaveDataFileSystem(Keyset, file.AsStorage(), IntegrityCheckLevel.None, true);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"{ex.Message} File: {fileEntry.FullPath}");
                }

                if (save != null && saveName != null)
                {
                    Saves[saveName] = save;
                }
            }
        }
Beispiel #4
0
        public Result Create(out IFileSystem fileSystem, out ISaveDataExtraDataAccessor extraDataAccessor,
                             IFileSystem sourceFileSystem, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
                             SaveDataType type, ITimeStampGenerator timeStampGenerator)
        {
            fileSystem        = default;
            extraDataAccessor = default;

            string saveDataPath = $"/{saveDataId:x16}";

            Result rc = sourceFileSystem.GetEntryType(out DirectoryEntryType entryType, saveDataPath);

            if (rc.IsFailure())
            {
                return(rc == ResultFs.PathNotFound ? ResultFs.TargetNotFound : rc);
            }

            switch (entryType)
            {
            case DirectoryEntryType.Directory:
                // Actual FS does this check
                // if (!allowDirectorySaveData) return ResultFs.InvalidSaveDataEntryType.Log();

                var subDirFs = new SubdirectoryFileSystem(sourceFileSystem, saveDataPath);

                rc = DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, subDirFs);
                if (rc.IsFailure())
                {
                    return(rc);
                }

                fileSystem = saveFs;

                // Todo: Dummy ISaveDataExtraDataAccessor

                return(Result.Success);

            case DirectoryEntryType.File:
                rc = sourceFileSystem.OpenFile(out IFile saveDataFile, saveDataPath, OpenMode.ReadWrite);
                if (rc.IsFailure())
                {
                    return(rc);
                }

                var saveDataStorage = new DisposingFileStorage(saveDataFile);
                fileSystem = new SaveDataFileSystem(Keyset, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid, false);

                // Todo: ISaveDataExtraDataAccessor

                return(Result.Success);

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
Beispiel #5
0
        private static List <Ticket> ReadTickets(Keyset keyset, Stream savefile)
        {
            var tickets    = new List <Ticket>();
            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());

            ulong titleId = ticketList.ReadUInt64();

            while (titleId != ulong.MaxValue)
            {
                ticketList.BaseStream.Position += 0x18;
                long start = ticketFile.BaseStream.Position;
                tickets.Add(new Ticket(ticketFile));
                ticketFile.BaseStream.Position = start + 0x400;
                titleId = ticketList.ReadUInt64();
            }

            return(tickets);
        }
Beispiel #6
0
        private static string Print(this SaveDataFileSystem save)
        {
            int colLen = 25;
            var sb     = new StringBuilder();

            sb.AppendLine();

            sb.AppendLine("Savefile:");
            PrintItem(sb, colLen, $"CMAC Signature{save.Header.SignatureValidity.GetValidityString()}:", save.Header.Cmac);
            PrintItem(sb, colLen, "Title ID:", $"{save.Header.ExtraData.TitleId:x16}");
            PrintItem(sb, colLen, "User ID:", save.Header.ExtraData.UserId);
            PrintItem(sb, colLen, "Save ID:", $"{save.Header.ExtraData.SaveId:x16}");
            PrintItem(sb, colLen, "Save Type:", $"{save.Header.ExtraData.Type}");
            PrintItem(sb, colLen, "Owner ID:", $"{save.Header.ExtraData.SaveOwnerId:x16}");
            PrintItem(sb, colLen, "Timestamp:", $"{DateTimeOffset.FromUnixTimeSeconds(save.Header.ExtraData.Timestamp):yyyy-MM-dd HH:mm:ss} UTC");
            PrintItem(sb, colLen, "Save Data Size:", $"0x{save.Header.ExtraData.DataSize:x16} ({Util.GetBytesReadable(save.Header.ExtraData.DataSize)})");
            PrintItem(sb, colLen, "Journal Size:", $"0x{save.Header.ExtraData.JournalSize:x16} ({Util.GetBytesReadable(save.Header.ExtraData.JournalSize)})");
            PrintItem(sb, colLen, $"Header Hash{save.Header.HeaderHashValidity.GetValidityString()}:", save.Header.Layout.Hash);
            PrintItem(sb, colLen, "Number of Files:", save.GetEntryCount(OpenDirectoryMode.Files));

            PrintIvfcHash(sb, colLen, 4, save.Header.Ivfc, IntegrityStorageType.Save);

            return(sb.ToString());
        }
Beispiel #7
0
        public static void Process(Context ctx)
        {
            using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.ReadWrite))
            {
                var save = new SaveDataFileSystem(ctx.Keyset, file, ctx.Options.IntegrityLevel, true);

                if (ctx.Options.Validate)
                {
                    save.Verify(ctx.Logger);
                }

                if (ctx.Options.OutDir != null)
                {
                    save.SaveDataFileSystemCore.Extract(ctx.Options.OutDir, ctx.Logger);
                }

                if (ctx.Options.DebugOutDir != null)
                {
                    // todo
                    string dir = ctx.Options.DebugOutDir;
                    Directory.CreateDirectory(dir);

                    FsLayout layout = save.Header.Layout;

                    string mainRemapDir = Path.Combine(dir, "main_remap");
                    Directory.CreateDirectory(mainRemapDir);

                    save.DataRemapStorage.GetBaseStorage().WriteAllBytes(Path.Combine(mainRemapDir, "Data"));
                    save.DataRemapStorage.GetHeaderStorage().WriteAllBytes(Path.Combine(mainRemapDir, "Header"));
                    save.DataRemapStorage.GetMapEntryStorage().WriteAllBytes(Path.Combine(mainRemapDir, "Map entries"));

                    string metadataRemapDir = Path.Combine(dir, "metadata_remap");
                    Directory.CreateDirectory(metadataRemapDir);

                    save.MetaRemapStorage.GetBaseStorage().WriteAllBytes(Path.Combine(metadataRemapDir, "Data"));
                    save.MetaRemapStorage.GetHeaderStorage().WriteAllBytes(Path.Combine(metadataRemapDir, "Header"));
                    save.MetaRemapStorage.GetMapEntryStorage().WriteAllBytes(Path.Combine(metadataRemapDir, "Map entries"));

                    string journalDir = Path.Combine(dir, "journal");
                    Directory.CreateDirectory(journalDir);

                    save.JournalStorage.GetBaseStorage().WriteAllBytes(Path.Combine(journalDir, "Data"));
                    save.JournalStorage.GetHeaderStorage().WriteAllBytes(Path.Combine(journalDir, "Header"));
                    save.JournalStorage.Map.GetHeaderStorage().WriteAllBytes(Path.Combine(journalDir, "Map_header"));
                    save.JournalStorage.Map.GetMapStorage().WriteAllBytes(Path.Combine(journalDir, "Map"));
                    save.JournalStorage.Map.GetModifiedPhysicalBlocksStorage().WriteAllBytes(Path.Combine(journalDir, "ModifiedPhysicalBlocks"));
                    save.JournalStorage.Map.GetModifiedVirtualBlocksStorage().WriteAllBytes(Path.Combine(journalDir, "ModifiedVirtualBlocks"));
                    save.JournalStorage.Map.GetFreeBlocksStorage().WriteAllBytes(Path.Combine(journalDir, "FreeBlocks"));

                    string saveDir = Path.Combine(dir, "save");
                    Directory.CreateDirectory(saveDir);

                    save.SaveDataFileSystemCore.GetHeaderStorage().WriteAllBytes(Path.Combine(saveDir, "Save_Header"));
                    save.SaveDataFileSystemCore.GetBaseStorage().WriteAllBytes(Path.Combine(saveDir, "Save_Data"));
                    save.SaveDataFileSystemCore.AllocationTable.GetHeaderStorage().WriteAllBytes(Path.Combine(saveDir, "FAT_header"));
                    save.SaveDataFileSystemCore.AllocationTable.GetBaseStorage().WriteAllBytes(Path.Combine(saveDir, "FAT_Data"));

                    save.Header.DataIvfcMaster.WriteAllBytes(Path.Combine(saveDir, "Save_MasterHash"));

                    IStorage saveLayer1Hash = save.MetaRemapStorage.Slice(layout.IvfcL1Offset, layout.IvfcL1Size);
                    IStorage saveLayer2Hash = save.MetaRemapStorage.Slice(layout.IvfcL2Offset, layout.IvfcL2Size);
                    IStorage saveLayer3Hash = save.MetaRemapStorage.Slice(layout.IvfcL3Offset, layout.IvfcL3Size);

                    saveLayer1Hash.WriteAllBytes(Path.Combine(saveDir, "Save_Layer1Hash"), ctx.Logger);
                    saveLayer2Hash.WriteAllBytes(Path.Combine(saveDir, "Save_Layer2Hash"), ctx.Logger);
                    saveLayer3Hash.WriteAllBytes(Path.Combine(saveDir, "Save_Layer3Hash"), ctx.Logger);

                    string duplexDir = Path.Combine(dir, "duplex");
                    Directory.CreateDirectory(duplexDir);

                    save.Header.DuplexMasterBitmapA.WriteAllBytes(Path.Combine(duplexDir, "MasterBitmapA"));
                    save.Header.DuplexMasterBitmapB.WriteAllBytes(Path.Combine(duplexDir, "MasterBitmapB"));

                    IStorage duplexL1A   = save.DataRemapStorage.Slice(layout.DuplexL1OffsetA, layout.DuplexL1Size);
                    IStorage duplexL1B   = save.DataRemapStorage.Slice(layout.DuplexL1OffsetB, layout.DuplexL1Size);
                    IStorage duplexDataA = save.DataRemapStorage.Slice(layout.DuplexDataOffsetA, layout.DuplexDataSize);
                    IStorage duplexDataB = save.DataRemapStorage.Slice(layout.DuplexDataOffsetB, layout.DuplexDataSize);

                    duplexL1A.WriteAllBytes(Path.Combine(duplexDir, "L1BitmapA"), ctx.Logger);
                    duplexL1B.WriteAllBytes(Path.Combine(duplexDir, "L1BitmapB"), ctx.Logger);
                    duplexDataA.WriteAllBytes(Path.Combine(duplexDir, "DataA"), ctx.Logger);
                    duplexDataB.WriteAllBytes(Path.Combine(duplexDir, "DataB"), ctx.Logger);
                }

                if (ctx.Options.ReplaceFileDest != null && ctx.Options.ReplaceFileSource != null)
                {
                    string destFilename = ctx.Options.ReplaceFileDest;
                    if (!destFilename.StartsWith("/"))
                    {
                        destFilename = '/' + destFilename;
                    }

                    using (IFile inFile = new LocalFile(ctx.Options.ReplaceFileSource, OpenMode.Read))
                    {
                        using (IFile outFile = save.OpenFile(destFilename, OpenMode.ReadWrite))
                        {
                            if (inFile.GetSize() != outFile.GetSize())
                            {
                                ctx.Logger.LogMessage($"Replacement file must be the same size as the original file. ({outFile.GetSize()} bytes)");
                                return;
                            }

                            inFile.CopyTo(outFile, ctx.Logger);

                            ctx.Logger.LogMessage($"Replaced file {destFilename}");
                        }
                    }

                    if (save.Commit(ctx.Keyset))
                    {
                        ctx.Logger.LogMessage("Successfully signed save file");
                    }
                    else
                    {
                        ctx.Logger.LogMessage("ERROR: Unable to sign save file. Do you have all the required keys?");
                    }

                    return;
                }

                if (ctx.Options.SignSave)
                {
                    if (save.Commit(ctx.Keyset))
                    {
                        ctx.Logger.LogMessage("Successfully signed save file");
                    }
                    else
                    {
                        ctx.Logger.LogMessage("Unable to sign save file. Do you have all the required keys?");
                    }

                    return;
                }

                if (ctx.Options.ListFiles)
                {
                    IDirectory dir = save.SaveDataFileSystemCore.OpenDirectory("/", OpenDirectoryMode.All);
                    foreach (DirectoryEntry entry in dir.EnumerateEntries())
                    {
                        ctx.Logger.LogMessage(entry.FullPath);
                    }
                }

                ctx.Logger.LogMessage(save.Print());
            }
        }
Beispiel #8
0
        public static string GetName(SaveDataFileSystem save)
        {
            ulong id = save.Header.ExtraData.SaveId;

            id -= 0x8000000000000000;
            switch (id)
            {
            case 0x0:
                return("saveDataIxrDb");

            case 0x10:
                return("account");

            case 0x11:
                return("idgen");

            case 0x20:
                return("data");

            case 0x30:
            case 0x31:
                return("mii");

            case 0x40:
                return("apprecdb");

            case 0x41:
                return("nsaccache");

            case 0x43:
                return("ns_appman");

            case 0x44:
                return("ns_sysup");

            case 0x45:
                return("vmdb");

            case 0x46:
                return("dtlman");

            case 0x47:
                return("nx_exfat");

            case 0x48:
                return("ns_systemseed");

            case 0x49:
                return("ns_ssversion");

            case 0x50:
                return("SystemSettings");

            case 0x51:
                return("FwdbgSettingsS");

            case 0x52:
                return("PrivateSettings");

            case 0x53:
                return("DeviceSettings");

            case 0x54:
                return("ApplnSettings");

            case 0x60:
                return("SslSave");

            case 0x70:
                return("nim_sys");

            case 0x71:
                return("nim_net");

            case 0x72:
                return("nim_tmp");

            case 0x73:
                return("nim_dac");

            case 0x74:
                return("nim_delta");

            case 0x75:
                return("nim_vac");

            case 0x76:
                return("nim_local");

            case 0x77:
                return("nim_lsys");

            case 0x78:
                return("nim_eca_dbg");

            case 0x80:
                return("friends");

            case 0x81:
                return("friends-sys");

            case 0x82:
                return("friends-image");

            case 0x90:
                return("news");

            case 0x91:
                return("news-sys");

            case 0x92:
                return("news-dl");

            case 0xA0:
                return("prepo-sys");

            case 0xA1:
                return("prepo");

            case 0xA2:
                return("prepo-ap");

            case 0xB0:
                return("nsdsave");

            case 0xC1:
                return("bcat-sys");

            case 0xC2:
                return("bcat-dl");

            case 0xD1:
                return("save");

            case 0xE0:
                return("escertificate");

            case 0xE1:
                return("escommon");

            case 0xE2:
                return("espersonalized");

            case 0xE3:
                return("esmetarecord");

            case 0xE4:
                return("eselicense");

            case 0xF0:
                return("pdm");

            case 0x100:
                return("pctlss");

            case 0x110:
                return("npns_save");

            case 0x130:
                return("state");

            case 0x131:
                return("context");

            case 0x140:
                return("TM");

            default:
                return(null);
            }
        }
Beispiel #9
0
        public static long GetOwner(SaveDataFileSystem save)
        {
            ulong id = save.Header.ExtraData.SaveId;

            id -= 0x8000000000000000;
            if (id == 0)
            {
                return(0x0100000000000000); // fs
            }
            if (id < 0x20)
            {
                return(0x010000000000001E); // account
            }
            if (id == 0x20)
            {
                return(0x0100000000000020); // nfc
            }
            if (id < 0x50 || id == 0xF0)
            {
                return(0x010000000000001F); // ns
            }
            if (id < 0x60)
            {
                return(0x0100000000000009); // settings
            }
            if (id == 0x60)
            {
                return(0x0100000000000024); // ssl
            }
            if (id < 0x80)
            {
                return(0x0100000000000025); // nim
            }
            if (id < 0x90)
            {
                return(0x010000000000000E); // friends
            }
            if (id == 0xB0)
            {
                return(0x0100000000000012); // bsdsockets
            }
            if (id < 0xD1)
            {
                return(0x010000000000000C); // bcat
            }
            if (id == 0xD1)
            {
                return(0x010000000000002B); // erpt
            }
            if (id < 0x100)
            {
                return(0x0100000000000033); // es
            }
            if (id == 0x100)
            {
                return(0x010000000000002E); // pctl
            }
            if (id == 0x110)
            {
                return(0x010000000000002F); // npns
            }
            if (id == 0x122)
            {
                return(-1); // unknown
            }
            if (id < 0x140)
            {
                return(0x010000000000003A); // migration
            }
            switch (id)
            {
            case 0x140:
                return(0x0100000000000022);    // capsrv

            case 0x150:
                return(0x010000000000003E);    // olsc

            case 0x180:
                return(0x0100000000000039);    // sdb

            case 0x1010:
                return(0x0100000000001000);    // qlaunch

            case 0x1020:
                return(0x0100000000001008);    // swkbd
            }
            if (id < 0x1040)
            {
                return(-1); // unknown
            }
            if (id < 0x1060)
            {
                return(0x0100000000001009); // miiEdit
            }
            if (id < 0x1070)
            {
                return(0x010000000000100B); // shop
            }
            if (id < 0x1090)
            {
                return(0x010000000000100A); // web
            }
            switch (id)
            {
            case 0x1091:
                return(0x0100000000001010);    // loginShare

            case 0x10B0:
                return(0x0100000000001007);    // playerSelect

            case 0x10C0:
                return(0x0100000000001013);    // myPage
            }

            return(-1);
        }
        public Result Create(out IFileSystem fileSystem,
                             out ReferenceCountedDisposable <ISaveDataExtraDataAccessor> extraDataAccessor, IFileSystem sourceFileSystem,
                             ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac, SaveDataType type,
                             ITimeStampGenerator timeStampGenerator)
        {
            UnsafeHelpers.SkipParamInit(out fileSystem, out extraDataAccessor);

            var saveDataPath = $"/{saveDataId:x16}".ToU8String();

            Result rc = sourceFileSystem.GetEntryType(out DirectoryEntryType entryType, saveDataPath);

            if (rc.IsFailure())
            {
                return(ResultFs.PathNotFound.Includes(rc) ? ResultFs.TargetNotFound.LogConverted(rc) : rc);
            }

            switch (entryType)
            {
            case DirectoryEntryType.Directory:
                if (!allowDirectorySaveData)
                {
                    return(ResultFs.InvalidSaveDataEntryType.Log());
                }

                rc = SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem subDirFs, sourceFileSystem,
                                                      saveDataPath);
                if (rc.IsFailure())
                {
                    return(rc);
                }

                bool isPersistentSaveData = type != SaveDataType.Temporary;
                bool isUserSaveData       = type == SaveDataType.Account || type == SaveDataType.Device;

                rc = DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, subDirFs,
                                                           isPersistentSaveData, isUserSaveData);
                if (rc.IsFailure())
                {
                    return(rc);
                }

                fileSystem = saveFs;

                // Todo: Dummy ISaveDataExtraDataAccessor

                return(Result.Success);

            case DirectoryEntryType.File:
                rc = sourceFileSystem.OpenFile(out IFile saveDataFile, saveDataPath, OpenMode.ReadWrite);
                if (rc.IsFailure())
                {
                    return(rc);
                }

                var saveDataStorage = new DisposingFileStorage(saveDataFile);
                fileSystem = new SaveDataFileSystem(KeySet, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid,
                                                    false);

                // Todo: ISaveDataExtraDataAccessor

                return(Result.Success);

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
Beispiel #11
0
 public SaveElement(KeyValuePair <string, SaveDataFileSystem> kv)
 {
     Key  = kv.Key;
     Save = kv.Value;
 }
Beispiel #12
0
        public override Task CreateTask()
        {
            return(new Task(() =>
            {
                FatFileSystemProvider system = NANDService.NAND.OpenSystemPartition();
                string accountSaveFileName = "/save/8000000000000010";
                if (system.FileExists(accountSaveFileName))
                {
                    system.OpenFile(out IFile accountSaveFile, accountSaveFileName.ToU8Span(), OpenMode.Read);
                    SaveDataFileSystem accountSaveFilesystem = new SaveDataFileSystem(HACGUIKeyset.Keyset, accountSaveFile.AsStorage(), IntegrityCheckLevel.ErrorOnInvalid, false);

                    HACGUIKeyset.AccountsFolderInfo.Create(); // make sure folder exists

                    accountSaveFilesystem.OpenDirectory(out IDirectory avatorsDirectory, "/su/avators/".ToU8Span(), OpenDirectoryMode.File);

                    DirectoryEntry[] files = new DirectoryEntry[0x100];

                    avatorsDirectory.Read(out long entriesLength, files.AsSpan());

                    if (accountSaveFilesystem.FileExists("/su/avators/profiles.dat"))
                    {
                        DirectoryEntry profileEntry = files.First(e => StringUtils.Utf8ZToString(e.Name) == "profiles.dat");

                        if (profileEntry.Size == 0x650)
                        {
                            accountSaveFilesystem.OpenFile(out IFile profileFile, "/su/avators/profiles.dat".ToU8Span(), OpenMode.Read);
                            Stream profileData = profileFile.AsStream();
                            profileData.Position += 0x10; // skip header
                            for (int i = 0; i < 8; i++)
                            {
                                byte[] data = new byte[0xC8];
                                profileData.Read(data, 0, data.Length);

                                byte[] uidBytes = new byte[0x10];
                                byte[] nameBytes = new byte[32];

                                Array.Copy(data, uidBytes, uidBytes.Length);
                                Array.Copy(data, 0x28, nameBytes, 0, nameBytes.Length);

                                char[] nameChars = Encoding.UTF8.GetChars(nameBytes);
                                int length = Array.IndexOf(nameChars, '\0');
                                string name = new string(nameChars.Take(length).ToArray());

                                Guid uid = Guid.Parse(uidBytes.ToHexString()); // ignores endianness, which is what i want
                                if (!string.IsNullOrEmpty(name))
                                {
                                    Preferences.Current.UserIds[uid.ToString()] = name;
                                }
                            }
                            Preferences.Current.Write();
                        }
                        else
                        {
                            MessageBox.Show("Invalid profiles.dat size! Something seems to be corrupt...");
                        }
                    }

                    foreach (DirectoryEntry entry in files.Where(e => StringUtils.Utf8ZToString(e.Name) != "profiles.dat" && e.Type == DirectoryEntryType.File))
                    {
                        FileInfo localFile = HACGUIKeyset.AccountsFolderInfo.GetFile(StringUtils.Utf8ZToString(entry.Name));
                        accountSaveFilesystem.OpenFile(out IFile saveFile, ("/su/avators/" + StringUtils.Utf8ZToString(entry.Name)).ToU8Span(), OpenMode.Read);
                        using (Stream localStream = localFile.Open(FileMode.Create)) {
                            saveFile.GetSize(out long size);
                            saveFile.AsStorage().CopyToStream(localStream, size);
                        }
                    }
                }
            }));
        }
Beispiel #13
0
        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();
        }
Beispiel #14
0
        void Start(string keys, string fwPath, bool noExfat, bool verbose, bool showNcaIndex, bool fixHashes)
        {
            Config.keyset      = ExternalKeyReader.ReadKeyFile(keys);
            Config.fwPath      = fwPath;
            Config.noExfat     = noExfat;
            Config.normalBisId = (noExfat) ? "0100000000000819" : "010000000000081B";
            Config.safeBisId   = (noExfat) ? "010000000000081A" : "010000000000081C";
            Config.verbose     = verbose;
            Config.fixHashes   = fixHashes;

            int convertCount = 0;

            foreach (var foldername in Directory.GetDirectories(fwPath, "*.nca"))
            {
                convertCount++;
                File.Move($"{foldername}/00", $"{fwPath}/temp");
                Directory.Delete(foldername);
                File.Move($"{fwPath}/temp", foldername);
            }

            if (convertCount > 0)
            {
                Console.WriteLine($"Converted folder ncas to files (count: {convertCount})");
            }

            Console.WriteLine("Indexing nca files...");

            NcaIndexer ncaIndex = new NcaIndexer();

            VersionExtractor versionExtractor = new VersionExtractor(ncaIndex.FindNca("0100000000000809", NcaContentType.Meta));

            string destFolder = $"NX-{versionExtractor.Version}";

            if (!noExfat)
            {
                destFolder += "_exFAT";
            }

            if (showNcaIndex)
            {
                ShowNcaIndex(ref ncaIndex, destFolder);
                return;
            }

            Console.WriteLine("\nEmmcHaccGen will now generate firmware files using the following settings:\n" +
                              $"fw: {versionExtractor.Version}\n" +
                              $"Exfat Support: {!noExfat}\n" +
                              $"Key path: {keys}\n" +
                              $"Destination folder: {destFolder}\n");

            if (verbose)
            {
                Console.WriteLine($"BisIds:\nNormal: {Config.normalBisId}\nSafe:   {Config.safeBisId}\n");
            }

            // Folder creation
            Console.WriteLine("\nCreating folders..");

            if (Directory.Exists(destFolder))
            {
                Console.Write("Destenation folder already exists. Delete the old folder?\nY/N: ");
                string input = Console.ReadLine();

                if (input[0].ToString().ToLower() != "y")
                {
                    return;
                }

                Console.WriteLine($"Deleting {destFolder}");
                Directory.Delete(destFolder, true);
            }

            foreach (string folder in FOLDERSTRUCTURE)
            {
                Directory.CreateDirectory($"{destFolder}{folder}");
            }

            // Bis creation
            Console.WriteLine("\nGenerating bis..");
            BisAssembler     bisAssembler     = new BisAssembler(ref ncaIndex, destFolder);
            BisFileAssembler bisFileAssembler = new BisFileAssembler($"{versionExtractor.Version}{((!noExfat) ? "_exFAT" : "")}", ref bisAssembler, $"{destFolder}/boot.bis");

            // Copy fw files
            Console.WriteLine("\nCopying files...");
            foreach (var file in Directory.EnumerateFiles(fwPath))
            {
                File.Copy(file, $"{destFolder}/SYSTEM/Contents/registered/{file.Split(new char[] { '/', '\\' }).Last().Replace(".cnmt.nca ", ".nca ")}", true);
            }

            // Archive bit setting
            Console.WriteLine("\nSetting archive bits..");
            SetArchiveRecursively($"{destFolder}/SYSTEM");
            SetArchiveRecursively($"{destFolder}/USER");

            //Imkv generation
            Console.WriteLine("\nGenerating imkvdb..");
            Imkv imkvdb = new Imkv(ref ncaIndex);

            if (verbose)
            {
                imkvdb.DumpToFile($"{destFolder}/data.arc");
            }

            File.Copy("save.stub", $"{destFolder}/SYSTEM/save/8000000000000120", true);

            using (IStorage outfile = new LocalStorage($"{destFolder}/SYSTEM/save/8000000000000120", FileAccess.ReadWrite))
            {
                var save = new SaveDataFileSystem(Config.keyset, outfile, IntegrityCheckLevel.ErrorOnInvalid, true);
                save.OpenFile(out IFile file, new U8Span("/meta/imkvdb.arc"), OpenMode.AllowAppend | OpenMode.ReadWrite);
                using (file)
                {
                    file.Write(0, imkvdb.bytes.ToArray(), WriteOption.Flush).ThrowIfFailure();
                }
                save.Commit(Config.keyset).ThrowIfFailure();
            }
            Console.WriteLine($"Wrote save with an imvkdb size of 0x{imkvdb.bytes.Count:X4}");
        }
Beispiel #15
0
        internal static void ExportSaveDebug(Context ctx, string dir, SaveDataFileSystem save)
        {
            Directory.CreateDirectory(dir);

            FsLayout layout = save.Header.Layout;

            string mainRemapDir = Path.Combine(dir, "main_remap");

            Directory.CreateDirectory(mainRemapDir);

            save.DataRemapStorage.GetBaseStorage().WriteAllBytes(Path.Combine(mainRemapDir, "Data"));
            save.DataRemapStorage.GetHeaderStorage().WriteAllBytes(Path.Combine(mainRemapDir, "Header"));
            save.DataRemapStorage.GetMapEntryStorage().WriteAllBytes(Path.Combine(mainRemapDir, "Map entries"));

            string metadataRemapDir = Path.Combine(dir, "metadata_remap");

            Directory.CreateDirectory(metadataRemapDir);

            save.MetaRemapStorage.GetBaseStorage().WriteAllBytes(Path.Combine(metadataRemapDir, "Data"));
            save.MetaRemapStorage.GetHeaderStorage().WriteAllBytes(Path.Combine(metadataRemapDir, "Header"));
            save.MetaRemapStorage.GetMapEntryStorage().WriteAllBytes(Path.Combine(metadataRemapDir, "Map entries"));

            string journalDir = Path.Combine(dir, "journal");

            Directory.CreateDirectory(journalDir);

            save.JournalStorage.GetBaseStorage().WriteAllBytes(Path.Combine(journalDir, "Data"));
            save.JournalStorage.GetHeaderStorage().WriteAllBytes(Path.Combine(journalDir, "Header"));
            save.JournalStorage.Map.GetHeaderStorage().WriteAllBytes(Path.Combine(journalDir, "Map_header"));
            save.JournalStorage.Map.GetMapStorage().WriteAllBytes(Path.Combine(journalDir, "Map"));
            save.JournalStorage.Map.GetModifiedPhysicalBlocksStorage()
            .WriteAllBytes(Path.Combine(journalDir, "ModifiedPhysicalBlocks"));
            save.JournalStorage.Map.GetModifiedVirtualBlocksStorage()
            .WriteAllBytes(Path.Combine(journalDir, "ModifiedVirtualBlocks"));
            save.JournalStorage.Map.GetFreeBlocksStorage().WriteAllBytes(Path.Combine(journalDir, "FreeBlocks"));

            string saveDir = Path.Combine(dir, "save");

            Directory.CreateDirectory(saveDir);

            save.SaveDataFileSystemCore.GetHeaderStorage().WriteAllBytes(Path.Combine(saveDir, "Save_Header"));
            save.SaveDataFileSystemCore.GetBaseStorage().WriteAllBytes(Path.Combine(saveDir, "Save_Data"));
            save.SaveDataFileSystemCore.AllocationTable.GetHeaderStorage().WriteAllBytes(Path.Combine(saveDir, "FAT_header"));
            save.SaveDataFileSystemCore.AllocationTable.GetBaseStorage().WriteAllBytes(Path.Combine(saveDir, "FAT_Data"));

            save.Header.DataIvfcMaster.WriteAllBytes(Path.Combine(saveDir, "Save_MasterHash"));

            IStorage saveLayer1Hash = save.MetaRemapStorage.Slice(layout.IvfcL1Offset, layout.IvfcL1Size);
            IStorage saveLayer2Hash = save.MetaRemapStorage.Slice(layout.IvfcL2Offset, layout.IvfcL2Size);
            IStorage saveLayer3Hash = save.MetaRemapStorage.Slice(layout.IvfcL3Offset, layout.IvfcL3Size);

            saveLayer1Hash.WriteAllBytes(Path.Combine(saveDir, "Save_Layer1Hash"), ctx.Logger);
            saveLayer2Hash.WriteAllBytes(Path.Combine(saveDir, "Save_Layer2Hash"), ctx.Logger);
            saveLayer3Hash.WriteAllBytes(Path.Combine(saveDir, "Save_Layer3Hash"), ctx.Logger);

            if (layout.Version >= 0x50000)
            {
                save.Header.FatIvfcMaster.WriteAllBytes(Path.Combine(saveDir, "Fat_MasterHash"));

                IStorage fatLayer1Hash = save.MetaRemapStorage.Slice(layout.FatIvfcL1Offset, layout.FatIvfcL1Size);
                IStorage fatLayer2Hash = save.MetaRemapStorage.Slice(layout.FatIvfcL2Offset, layout.FatIvfcL1Size);

                fatLayer1Hash.WriteAllBytes(Path.Combine(saveDir, "Fat_Layer1Hash"), ctx.Logger);
                fatLayer2Hash.WriteAllBytes(Path.Combine(saveDir, "Fat_Layer2Hash"), ctx.Logger);
            }

            string duplexDir = Path.Combine(dir, "duplex");

            Directory.CreateDirectory(duplexDir);

            save.Header.DuplexMasterBitmapA.WriteAllBytes(Path.Combine(duplexDir, "MasterBitmapA"));
            save.Header.DuplexMasterBitmapB.WriteAllBytes(Path.Combine(duplexDir, "MasterBitmapB"));

            IStorage duplexL1A   = save.DataRemapStorage.Slice(layout.DuplexL1OffsetA, layout.DuplexL1Size);
            IStorage duplexL1B   = save.DataRemapStorage.Slice(layout.DuplexL1OffsetB, layout.DuplexL1Size);
            IStorage duplexDataA = save.DataRemapStorage.Slice(layout.DuplexDataOffsetA, layout.DuplexDataSize);
            IStorage duplexDataB = save.DataRemapStorage.Slice(layout.DuplexDataOffsetB, layout.DuplexDataSize);

            duplexL1A.WriteAllBytes(Path.Combine(duplexDir, "L1BitmapA"), ctx.Logger);
            duplexL1B.WriteAllBytes(Path.Combine(duplexDir, "L1BitmapB"), ctx.Logger);
            duplexDataA.WriteAllBytes(Path.Combine(duplexDir, "DataA"), ctx.Logger);
            duplexDataB.WriteAllBytes(Path.Combine(duplexDir, "DataB"), ctx.Logger);
        }
Beispiel #16
0
        public static void Process(Context ctx)
        {
            using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.ReadWrite))
            {
                bool signNeeded = ctx.Options.SignSave;

                var save = new SaveDataFileSystem(ctx.Keyset, file, ctx.Options.IntegrityLevel, true);

                if (ctx.Options.Validate)
                {
                    save.Verify(ctx.Logger);
                }

                if (ctx.Options.OutDir != null)
                {
                    save.SaveDataFileSystemCore.Extract(ctx.Options.OutDir, ctx.Logger);
                }

                if (ctx.Options.DebugOutDir != null)
                {
                    string dir = ctx.Options.DebugOutDir;

                    ExportSaveDebug(ctx, dir, save);
                }

                try
                {
                    if (ctx.Options.ReplaceFileDest != null && ctx.Options.ReplaceFileSource != null)
                    {
                        string destFilename = ctx.Options.ReplaceFileDest;
                        if (!destFilename.StartsWith("/"))
                        {
                            destFilename = '/' + destFilename;
                        }

                        using (IFile inFile = new LocalFile(ctx.Options.ReplaceFileSource, OpenMode.Read))
                        {
                            using (IFile outFile = save.OpenFile(destFilename, OpenMode.ReadWrite))
                            {
                                if (inFile.GetSize() != outFile.GetSize())
                                {
                                    outFile.SetSize(inFile.GetSize());
                                }

                                inFile.CopyTo(outFile, ctx.Logger);

                                ctx.Logger.LogMessage($"Replaced file {destFilename}");
                            }
                        }

                        signNeeded = true;
                    }

                    if (ctx.Options.RepackSource != null)
                    {
                        var source = new LocalFileSystem(ctx.Options.RepackSource);

                        save.CleanDirectoryRecursively("/");
                        save.Commit(ctx.Keyset);
                        source.CopyFileSystem(save);

                        signNeeded = true;
                    }
                }
                finally
                {
                    save.Commit(ctx.Keyset);
                }

                if (ctx.Options.TrimSave)
                {
                    save.FsTrim();
                    signNeeded = true;
                    ctx.Logger.LogMessage("Trimmed save file");
                }

                if (signNeeded)
                {
                    if (save.Commit(ctx.Keyset))
                    {
                        ctx.Logger.LogMessage("Successfully signed save file");
                    }
                    else
                    {
                        ctx.Logger.LogMessage("Unable to sign save file. Do you have all the required keys?");
                    }

                    return;
                }

                if (ctx.Options.ListFiles)
                {
                    foreach (DirectoryEntry entry in save.EnumerateEntries())
                    {
                        ctx.Logger.LogMessage(entry.FullPath);
                    }
                }

                ctx.Logger.LogMessage(save.Print());
                //ctx.Logger.LogMessage(PrintFatLayout(save.SaveDataFileSystemCore));
            }
        }
Beispiel #17
0
        public static void Process(Context ctx)
        {
            var accessNeeded = FileAccess.Read;

            if (ctx.Options.SignSave || ctx.Options.ReplaceFileDest != null && ctx.Options.ReplaceFileSource != null ||
                ctx.Options.RepackSource != null || ctx.Options.TrimSave)
            {
                accessNeeded = FileAccess.ReadWrite;
            }

            using (var file = new LocalStorage(ctx.Options.InFile, accessNeeded))
            {
                bool signNeeded = ctx.Options.SignSave;

                var save            = new SaveDataFileSystem(ctx.KeySet, file, ctx.Options.IntegrityLevel, true);
                FileSystemClient fs = ctx.FsClient;

                fs.Register("save".ToU8Span(), save);

                if (ctx.Options.Validate)
                {
                    save.Verify(ctx.Logger);
                }

                if (ctx.Options.OutDir != null)
                {
                    fs.Register("output".ToU8Span(), new LocalFileSystem(ctx.Options.OutDir));

                    FsUtils.CopyDirectoryWithProgress(fs, "save:/".ToU8Span(), "output:/".ToU8Span(), logger: ctx.Logger).ThrowIfFailure();

                    fs.Unmount("output".ToU8Span());
                }

                if (ctx.Options.DebugOutDir != null)
                {
                    string dir = ctx.Options.DebugOutDir;

                    ExportSaveDebug(ctx, dir, save);
                }

                try
                {
                    if (ctx.Options.ReplaceFileDest != null && ctx.Options.ReplaceFileSource != null)
                    {
                        string destFilename = ctx.Options.ReplaceFileDest;
                        if (!destFilename.StartsWith("/"))
                        {
                            destFilename = '/' + destFilename;
                        }

                        using (IFile inFile = new LocalFile(ctx.Options.ReplaceFileSource, OpenMode.Read))
                        {
                            save.OpenFile(out IFile outFile, destFilename.ToU8String(), OpenMode.ReadWrite).ThrowIfFailure();

                            using (outFile)
                            {
                                inFile.GetSize(out long inFileSize).ThrowIfFailure();
                                outFile.GetSize(out long outFileSize).ThrowIfFailure();

                                if (inFileSize != outFileSize)
                                {
                                    outFile.SetSize(inFileSize).ThrowIfFailure();
                                }

                                inFile.CopyTo(outFile, ctx.Logger);

                                ctx.Logger.LogMessage($"Replaced file {destFilename}");
                            }
                        }

                        signNeeded = true;
                    }

                    if (ctx.Options.RepackSource != null)
                    {
                        fs.Register("input".ToU8Span(), new LocalFileSystem(ctx.Options.RepackSource));

                        fs.CleanDirectoryRecursively("save:/".ToU8Span());
                        fs.Commit("save".ToU8Span());

                        FsUtils.CopyDirectoryWithProgress(fs, "input:/".ToU8Span(), "save:/".ToU8Span(), logger: ctx.Logger).ThrowIfFailure();

                        fs.Commit("save".ToU8Span());
                        fs.Unmount("input".ToU8Span());

                        signNeeded = true;
                    }
                }
                finally
                {
                    if (signNeeded)
                    {
                        if (save.Commit(ctx.KeySet).IsSuccess())
                        {
                            ctx.Logger.LogMessage(
                                $"Successfully signed save file with key {ctx.KeySet.DeviceUniqueSaveMacKeys[0].ToString()}");
                        }
                        else
                        {
                            ctx.Logger.LogMessage("Unable to sign save file. Do you have all the required keys?");
                        }

                        signNeeded = false;
                    }
                }

                if (ctx.Options.TrimSave)
                {
                    save.FsTrim();
                    signNeeded = true;
                    ctx.Logger.LogMessage("Trimmed save file");
                }

                if (signNeeded)
                {
                    if (save.Commit(ctx.KeySet).IsSuccess())
                    {
                        ctx.Logger.LogMessage(
                            $"Successfully signed save file with key {ctx.KeySet.DeviceUniqueSaveMacKeys[0].ToString()}");
                    }
                    else
                    {
                        ctx.Logger.LogMessage("Unable to sign save file. Do you have all the required keys?");
                    }

                    fs.Unmount("save".ToU8Span());
                    return;
                }

                if (ctx.Options.ListFiles)
                {
                    foreach (DirectoryEntryEx entry in save.EnumerateEntries())
                    {
                        ctx.Logger.LogMessage(entry.FullPath);
                    }
                }

                ctx.Logger.LogMessage(save.Print(ctx.KeySet));
                //ctx.Logger.LogMessage(PrintFatLayout(save.SaveDataFileSystemCore));

                fs.Unmount("save".ToU8Span());
            }
        }
Beispiel #18
0
        private void OnNandFound()
        {
            Nand nand = NANDService.NAND;

            FileStream pkg2file = HACGUIKeyset.TempPkg2FileInfo.Create();
            IStorage   pkg2nand = nand.OpenPackage2(0);

            byte[] pkg2raw = new byte[0x7FC000];
            pkg2nand.Read(pkg2raw, 0x4000);
            MemoryStorage pkg2memory = new MemoryStorage(pkg2raw);

            pkg2memory.CopyToStream(pkg2file);

            Package2 pkg2 = new Package2(HACGUIKeyset.Keyset, pkg2memory);

            HACGUIKeyset.RootTempPkg2FolderInfo.Create();
            FileStream kernelstream = HACGUIKeyset.TempKernelFileInfo.Create();
            FileStream INI1stream   = HACGUIKeyset.TempINI1FileInfo.Create();

            pkg2.OpenKernel().CopyToStream(kernelstream);
            pkg2.OpenIni1().CopyToStream(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().CopyToStream(kipstream);
                kipstream.Close();
            }

            pkg2file.Close();
            INI1stream.Close();

            HACGUIKeyset.Keyset.DeriveKeys();

            SwitchFs fs = SwitchFs.OpenNandPartition(HACGUIKeyset.Keyset, NANDService.NAND.OpenSystemPartition());

            foreach (KeyValuePair <string, Nca> kv in fs.Ncas)
            {
                Nca nca = kv.Value;

                if (nca.CanOpenSection(0)) // mainly a check if the NCA can be decrypted
                {
                    switch (nca.Header.TitleId)
                    {
                    case 0x0100000000000033:     // es
                        switch (nca.Header.ContentType)
                        {
                        case ContentType.Program:
                            NcaSection          exefsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0);
                            IStorage            pfsStorage   = nca.OpenSection(exefsSection.SectionNum, false, IntegrityCheckLevel.ErrorOnInvalid, false);
                            PartitionFileSystem pfs          = new PartitionFileSystem(pfsStorage);
                            Nso        nso     = new Nso(new FileStorage(pfs.OpenFile("main", OpenMode.Read)));
                            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);
                            Crypto.DecryptEcb(key2, EticketRsaKekSource, HACGUIKeyset.Keyset.EticketRsaKek, 0x10);
                            break;
                        }
                        break;

                    case 0x0100000000000024:     // ssl
                        switch (nca.Header.ContentType)
                        {
                        case ContentType.Program:
                            NcaSection          exefsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0);
                            IStorage            pfsStorage   = nca.OpenSection(exefsSection.SectionNum, false, IntegrityCheckLevel.ErrorOnInvalid, false);
                            PartitionFileSystem pfs          = new PartitionFileSystem(pfsStorage);
                            Nso        nso     = new Nso(new FileStorage(pfs.OpenFile("main", OpenMode.Read)));
                            NsoSection section = nso.Sections[1];
                            Stream     data    = new MemoryStream(section.DecompressSection());
                            hashes.Clear();

                            hashes.Add(new HashSearchEntry(NintendoKeys.SslAesKeyXHash, 0x10));
                            hashes.Add(new HashSearchEntry(NintendoKeys.SslRsaKeyYHash, 0x10));
                            keys = data.FindKeyViaHash(hashes, new SHA256Managed(), 0x10, data.Length);
                            byte[] SslAesKeyX = new byte[0x10];
                            byte[] SslRsaKeyY = new byte[0x10];
                            Array.Copy(keys[NintendoKeys.SslAesKeyXHash], SslAesKeyX, 0x10);
                            Array.Copy(keys[NintendoKeys.SslRsaKeyYHash], SslRsaKeyY, 0x10);

                            byte[] RsaPrivateKekGenerationSource;
                            XOR(NintendoKeys.KekMasks[0], NintendoKeys.KekSeeds[1], out RsaPrivateKekGenerationSource);

                            byte[] key1 = new byte[0x10];
                            Crypto.DecryptEcb(HACGUIKeyset.Keyset.MasterKeys[0], RsaPrivateKekGenerationSource, key1, 0x10);
                            byte[] key2 = new byte[0x10];
                            Crypto.DecryptEcb(key1, SslAesKeyX, key2, 0x10);
                            Crypto.DecryptEcb(key2, SslRsaKeyY, HACGUIKeyset.Keyset.SslRsaKek, 0x10);
                            break;
                        }
                        break;
                    }
                }
            }

            // save PRODINFO to file, then derive eticket_ext_key_rsa
            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);

            // get client certificate
            prodinfo.Seek(0x0AD0, SeekOrigin.Begin);
            byte[] buffer;
            buffer = new byte[0x4];
            prodinfo.Read(buffer, 0, buffer.Length); // read cert length
            uint certLength = BitConverter.ToUInt32(buffer, 0);

            buffer = new byte[certLength];
            prodinfo.Seek(0x0AE0, SeekOrigin.Begin); // should be redundant?
            prodinfo.Read(buffer, 0, buffer.Length); // read actual cert

            byte[] counter = cal0.SslExtKey.Take(0x10).ToArray();
            byte[] key     = cal0.SslExtKey.Skip(0x10).ToArray();                               // bit strange structure but it works

            new Aes128CtrTransform(HACGUIKeyset.Keyset.SslRsaKek, counter).TransformBlock(key); // decrypt private key

            X509Certificate2 certificate = new X509Certificate2();

            certificate.Import(buffer);
            certificate.ImportPrivateKey(key);

            byte[] pfx       = certificate.Export(X509ContentType.Pkcs12, "switch");
            Stream pfxStream = HACGUIKeyset.GetClientCertificateByName(PickConsolePage.ConsoleName).Create();

            pfxStream.Write(pfx, 0, pfx.Length);
            pfxStream.Close();
            prodinfoFile.Close();

            // get tickets
            List <Ticket>         tickets    = new List <Ticket>();
            FatFileSystemProvider system     = nand.OpenSystemPartition();
            const string          e1FileName = "save\\80000000000000E1";
            const string          e2FileName = "save\\80000000000000E2";

            if (system.FileExists(e1FileName))
            {
                IFile    e1File    = system.OpenFile(e1FileName, OpenMode.Read);
                IStorage e1Storage = new FileStorage(e1File);
                tickets.AddRange(DumpTickets(HACGUIKeyset.Keyset, e1Storage, PickConsolePage.ConsoleName));
            }

            if (system.FileExists(e2FileName))
            {
                IFile    e2File    = system.OpenFile(e2FileName, OpenMode.Read);
                IStorage e2Storage = new FileStorage(e2File);
                tickets.AddRange(DumpTickets(HACGUIKeyset.Keyset, e2Storage, PickConsolePage.ConsoleName));
            }

            IStorage           nsAppmanStorage = new FileStorage(system.OpenFile("save\\8000000000000043", OpenMode.Read));
            SaveDataFileSystem save            = new SaveDataFileSystem(HACGUIKeyset.Keyset, nsAppmanStorage, IntegrityCheckLevel.ErrorOnInvalid, false);
            IStorage           privateStorage  = new FileStorage(save.OpenFile("/private", OpenMode.Read));

            byte[] sdSeed = new byte[0x10];
            privateStorage.Read(sdSeed, 0x10);
            HACGUIKeyset.Keyset.SetSdSeed(sdSeed);

            foreach (Ticket ticket in tickets)
            {
                HACGUIKeyset.Keyset.TitleKeys[ticket.RightsId] = new byte[0x10];
                Array.Copy(ticket.GetTitleKey(HACGUIKeyset.Keyset), HACGUIKeyset.Keyset.TitleKeys[ticket.RightsId], 0x10);
            }

            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();
        }