コード例 #1
0
        private void ReloadKeySet()
        {
            string keyFile        = null;
            string titleKeyFile   = null;
            string consoleKeyFile = null;

            string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

            LoadSetAtPath(Path.Combine(home, ".switch"));
            LoadSetAtPath(GetSystemPath());

            void LoadSetAtPath(string basePath)
            {
                string localKeyFile        = Path.Combine(basePath, "prod.keys");
                string localTitleKeyFile   = Path.Combine(basePath, "title.keys");
                string localConsoleKeyFile = Path.Combine(basePath, "console.keys");

                if (File.Exists(localKeyFile))
                {
                    keyFile = localKeyFile;
                }

                if (File.Exists(localTitleKeyFile))
                {
                    titleKeyFile = localTitleKeyFile;
                }

                if (File.Exists(localConsoleKeyFile))
                {
                    consoleKeyFile = localConsoleKeyFile;
                }
            }

            KeySet = ExternalKeyReader.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile);
        }
コード例 #2
0
        public static void generateJson(string firmware, string pathkey, string firmver, string fwint, string output)
        {
            Keyset        keyset = ExternalKeyReader.ReadKeyFile(pathkey);
            StringBuilder sb     = new StringBuilder();
            StringWriter  sw     = new StringWriter(sb);
            int           files  = Directory.GetFiles(firmware).Length;

            using (JsonWriter writer = new JsonTextWriter(sw))
            {
                writer.Formatting = Formatting.Indented;

                writer.WriteStartObject();
                writer.WritePropertyName("fw_info");
                writer.WriteStartObject();
                writer.WritePropertyName("version");
                writer.WriteValue(firmver);
                writer.WritePropertyName("IsExfat");
                writer.WriteValue(true);
                writer.WritePropertyName("files");
                writer.WriteValue(files);
                writer.WriteEnd();
                writer.WritePropertyName("titleids");
                writer.WriteStartArray();
                // escribir todos los titleid
                ListTitleid(firmware, keyset, writer);
                writer.WriteEndArray();
                writer.WritePropertyName("programid");
                writer.WriteStartObject();
                Listnca(firmware, fwint, keyset, writer);
                writer.WriteEndObject();
                writer.WriteEnd();
            }
            File.WriteAllText(Path.Combine(output, fwint), sb.ToString());
        }
コード例 #3
0
        public HandlerArgs(string[] raw_args)
        {
            Handler[] Handlers = new [] {
                // Operation modes
                new Handler(Aliases: new[] { "--get-info", "--info", "-i" }, Fn: () => OperationMode     = Mode.GetInfo),
                new Handler(Aliases: new[] { "--get-latest", "--latest", "-l" }, Fn: () => OperationMode = Mode.GetLatest),
                new Handler(Aliases: new[] { "--help", "-h" }, Fn: () => OperationMode = Mode.Help),
                // Args
                new Handler(Aliases: new[] { "--cert", "-c" }, Fn: x => CertPath                        = x),
                new Handler(Aliases: new[] { "--keyset", "-k" }, Fn: x => KeysetPath                    = x),
                new Handler(Aliases: new[] { "--out-path", "--out", "-o" }, Fn: x => OutPath            = x),
                new Handler(Aliases: new[] { "--device-id", "-did" }, Fn: x => DeviceID                 = x),
                new Handler(Aliases: new[] { "--environment", "-env" }, Fn: x => Env                    = x),
                new Handler(Aliases: new[] { "--server", "-s" }, Fn: x => Server                        = x),
                new Handler(Aliases: new[] { "--platform", "-p" }, Fn: x => Platform                    = x),
                new Handler(Aliases: new[] { "--firmware-version", "-fwver" }, Fn: x => FirmwareVersion = x),
                new Handler(Aliases: new[] { "--jobs", "-j" }, Fn: x => MaxJobs = Math.Min(int.Parse(x), 1)),
                new Handler(Aliases: new[] { "--titles" }, Fn: x => TitleFilter = x.Split(',')),
                new Handler(Aliases: new[] { "-vf" }, Fn: x => FileVerbose      = x),
                // Flags
                new Handler(Aliases: new[] { "-v" }, Fn: () => ConsoleVerbose       = true),
                new Handler(Aliases: new[] { "--tencent", "-t" }, Fn: () => Tencent = true),
                new Handler(Aliases: new[] { "--ignore-warnings",
                                             "--no-confirm", "-q" }, Fn: () => IgnoreWarnings = true),
                new Handler(Aliases: new[] { "--only-meta" }, Fn: () => OnlyMeta = true)
            };

            for (int i = 0; i < raw_args.Length; ++i)
            {
                var handler = Handlers.FirstOrDefault(x => x.Aliases.Contains(raw_args[i]));

                if (handler is null)
                {
                    Console.WriteLine($"Warning: unknown arg {raw_args[i]}");
                }
                else
                {
                    handler.Invoke(handler.RequiresArg ? raw_args[++i] : null !);
                }
            }

            if (OperationMode is null)
            {
                return;
            }

            CertPath   = Path.GetFullPath(CertPath);
            KeysetPath = Path.GetFullPath(KeysetPath);
            if (!String.IsNullOrEmpty(OutPath))
            {
                OutPath = Path.GetFullPath(OutPath);
            }

            // keysets are only needed for a full download
            if (OperationMode == Mode.GetLatest)
            {
                Keyset = ExternalKeyReader.ReadKeyFile(KeysetPath);
            }
        }
コード例 #4
0
ファイル: Keys.cs プロジェクト: friedkeenan/nxfw-tool
 public static void TryLoadKeys()
 {
     if (File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/.switch/prod.keys"))
     {
         Console.WriteLine($"Using {Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/.switch/prod.keys");
         ExternalKeyReader.ReadKeyFile(Keyset, Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/.switch/prod.keys");
     }
     else
     {
         Console.WriteLine("Couldn't load any keys!");
     }
 }
コード例 #5
0
        public bool CopyKeyset()
        {
            ExternalKeyReader.ReadKeyFile(HACGUIKeyset.Keyset, KeysetFile.FullName);
            HACGUIKeyset.Keyset.DeriveKeys(); // derive from keyblobs

            if (HACGUIKeyset.Keyset.HeaderKey.IsEmpty())
            {
                MessageBox.Show("It seems that you are missing some keys...\nLockpick_RCM requires that Atmosphere's sept be on the SD card to derive every key.\nPlease add it and try again.");
                return(false);
            }
            else
            {
                return(true);
            }
        }
コード例 #6
0
        private static void OpenKeyset(Context ctx)
        {
            string keyFileName = ctx.Options.UseDevKeys ? "dev.keys" : "prod.keys";

#if CORERT_NO_REFLECTION
            string home = HomeFolder.GetFolderPath(Environment.SpecialFolder.UserProfile);
#else
            string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
#endif
            string homeKeyFile        = Path.Combine(home, ".switch", keyFileName);
            string homeTitleKeyFile   = Path.Combine(home, ".switch", "title.keys");
            string homeConsoleKeyFile = Path.Combine(home, ".switch", "console.keys");
            string keyFile            = ctx.Options.Keyfile;
            string titleKeyFile       = ctx.Options.TitleKeyFile;
            string consoleKeyFile     = ctx.Options.ConsoleKeyFile;

            if (keyFile == null && File.Exists(homeKeyFile))
            {
                keyFile = homeKeyFile;
            }

            if (titleKeyFile == null && File.Exists(homeTitleKeyFile))
            {
                titleKeyFile = homeTitleKeyFile;
            }

            if (consoleKeyFile == null && File.Exists(homeConsoleKeyFile))
            {
                consoleKeyFile = homeConsoleKeyFile;
            }

            ctx.Keyset = ExternalKeyReader.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile, ctx.Logger, ctx.Options.UseDevKeys);
            if (ctx.Options.SdSeed != null)
            {
                ctx.Keyset.SetSdSeed(ctx.Options.SdSeed.ToBytes());
            }

            if (ctx.Options.InFileType == FileType.Keygen && ctx.Options.OutDir != null)
            {
                string dir = ctx.Options.OutDir;
                Directory.CreateDirectory(dir);

                File.WriteAllText(Path.Combine(dir, keyFileName), ExternalKeyReader.PrintCommonKeys(ctx.Keyset));
                File.WriteAllText(Path.Combine(dir, "console.keys"), ExternalKeyReader.PrintUniqueKeys(ctx.Keyset));
                File.WriteAllText(Path.Combine(dir, "title.keys"), ExternalKeyReader.PrintTitleKeys(ctx.Keyset));
            }
        }
コード例 #7
0
ファイル: Program.cs プロジェクト: Thealexbarney/LibHacTools
        private static void OpenKeyset(Context ctx)
        {
            string home               = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
            string homeKeyFile        = Path.Combine(home, ".switch", "prod.keys");
            string homeDevKeyFile     = Path.Combine(home, ".switch", "dev.keys");
            string homeTitleKeyFile   = Path.Combine(home, ".switch", "title.keys");
            string homeConsoleKeyFile = Path.Combine(home, ".switch", "console.keys");
            string keyFile            = ctx.Options.Keyfile;
            string titleKeyFile       = ctx.Options.TitleKeyFile;
            string consoleKeyFile     = ctx.Options.ConsoleKeyFile;

            if (keyFile == null && File.Exists(homeKeyFile))
            {
                keyFile = homeKeyFile;
            }

            if (titleKeyFile == null && File.Exists(homeTitleKeyFile))
            {
                titleKeyFile = homeTitleKeyFile;
            }

            if (consoleKeyFile == null && File.Exists(homeConsoleKeyFile))
            {
                consoleKeyFile = homeConsoleKeyFile;
            }

            ctx.Keyset = ExternalKeyReader.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile, ctx.Logger);
            if (ctx.Options.SdSeed != null)
            {
                ctx.Keyset.SetSdSeed(ctx.Options.SdSeed.ToBytes());
            }

            if (File.Exists(homeDevKeyFile))
            {
                ctx.DevKeyset = ExternalKeyReader.ReadKeyFile(homeDevKeyFile, titleKeyFile, consoleKeyFile, ctx.Logger, true);
                if (ctx.Options.SdSeed != null)
                {
                    ctx.DevKeyset.SetSdSeed(ctx.Options.SdSeed.ToBytes());
                }
            }
        }
コード例 #8
0
        public void ReloadKeySet()
        {
            KeySet ??= KeySet.CreateDefaultKeySet();

            string keyFile        = null;
            string titleKeyFile   = null;
            string consoleKeyFile = null;

            if (AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile)
            {
                LoadSetAtPath(AppDataManager.KeysDirPathUser);
            }

            LoadSetAtPath(AppDataManager.KeysDirPath);

            void LoadSetAtPath(string basePath)
            {
                string localKeyFile        = Path.Combine(basePath, "prod.keys");
                string localTitleKeyFile   = Path.Combine(basePath, "title.keys");
                string localConsoleKeyFile = Path.Combine(basePath, "console.keys");

                if (File.Exists(localKeyFile))
                {
                    keyFile = localKeyFile;
                }

                if (File.Exists(localTitleKeyFile))
                {
                    titleKeyFile = localTitleKeyFile;
                }

                if (File.Exists(localConsoleKeyFile))
                {
                    consoleKeyFile = localConsoleKeyFile;
                }
            }

            ExternalKeyReader.ReadKeyFile(KeySet, keyFile, titleKeyFile, consoleKeyFile, null);
        }
コード例 #9
0
ファイル: VirtualFileSystem.cs プロジェクト: ski982/Ryujinx-1
        private void ReloadKeySet()
        {
            string keyFile        = null;
            string titleKeyFile   = null;
            string consoleKeyFile = null;

            if (!AppDataManager.IsCustomBasePath)
            {
                LoadSetAtPath(AppDataManager.KeysDirPathAlt);
            }

            LoadSetAtPath(AppDataManager.KeysDirPath);

            void LoadSetAtPath(string basePath)
            {
                string localKeyFile        = Path.Combine(basePath, "prod.keys");
                string localTitleKeyFile   = Path.Combine(basePath, "title.keys");
                string localConsoleKeyFile = Path.Combine(basePath, "console.keys");

                if (File.Exists(localKeyFile))
                {
                    keyFile = localKeyFile;
                }

                if (File.Exists(localTitleKeyFile))
                {
                    titleKeyFile = localTitleKeyFile;
                }

                if (File.Exists(localConsoleKeyFile))
                {
                    consoleKeyFile = localConsoleKeyFile;
                }
            }

            KeySet = ExternalKeyReader.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile);
        }
コード例 #10
0
        private static void OpenKeySet(Context ctx)
        {
#if CORERT_NO_REFLECTION
            string home = HomeFolder.GetFolderPath(Environment.SpecialFolder.UserProfile);
#else
            string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
#endif
            string homeTitleKeyFile   = Path.Combine(home, ".switch", "title.keys");
            string homeConsoleKeyFile = Path.Combine(home, ".switch", "console.keys");

            string prodKeyFile    = Path.Combine(home, ".switch", "prod.keys");
            string devKeyFile     = Path.Combine(home, ".switch", "dev.keys");
            string titleKeyFile   = ctx.Options.TitleKeyFile;
            string consoleKeyFile = ctx.Options.ConsoleKeyFile;

            // Check if the files from the command line exist
            if (titleKeyFile != null && !File.Exists(titleKeyFile))
            {
                titleKeyFile = null;
            }

            if (consoleKeyFile != null && !File.Exists(consoleKeyFile))
            {
                consoleKeyFile = null;
            }

            if (!File.Exists(prodKeyFile))
            {
                prodKeyFile = null;
            }

            if (!File.Exists(devKeyFile))
            {
                devKeyFile = null;
            }

            // Check the home directory if no existing key files were specified
            if (consoleKeyFile == null && File.Exists(homeConsoleKeyFile))
            {
                consoleKeyFile = homeConsoleKeyFile;
            }

            if (titleKeyFile == null && File.Exists(homeTitleKeyFile))
            {
                titleKeyFile = homeTitleKeyFile;
            }

            var keySet = KeySet.CreateDefaultKeySet();

            // If the user specifies a key file then only load that file into the mode they specified,
            // otherwise load both prod.keys and dev.keys.
            // Todo: Should we add a way that both dev-only key files and mixed prod/dev key files
            // can both be loaded when specifying a key file in dev mode?
            if (ctx.Options.Keyfile != null && File.Exists(ctx.Options.Keyfile))
            {
                keySet.SetMode(ctx.Options.KeyMode);
                ExternalKeyReader.ReadKeyFile(keySet, ctx.Options.Keyfile, titleKeyFile, consoleKeyFile, ctx.Logger);
            }
            else
            {
                ExternalKeyReader.ReadKeyFile(keySet, prodKeyFile, devKeyFile, titleKeyFile, consoleKeyFile, ctx.Logger);
            }

            keySet.SetMode(ctx.Options.KeyMode);

            if (ctx.Options.SdSeed != null)
            {
                keySet.SetSdSeed(ctx.Options.SdSeed.ToBytes());
            }

            ctx.KeySet = keySet;
        }
コード例 #11
0
ファイル: Program.cs プロジェクト: shadowninja108/NxCertDump
        static void Main(string[] args)
        {
            if (args.Length != 1)
            {
                Console.WriteLine("Pass the path to PRODINFO as the only argument");
                return;
            }

            FileInfo prodinfoPath = new FileInfo(args[0]);

            if (!prodinfoPath.Exists)
            {
                Console.WriteLine($"Provided path \"{prodinfoPath.FullName}\" doesn't exist.");
                return;
            }

            if (OutputPfxInfo.Exists)
            {
                Console.WriteLine($"Output file {OutputPfxInfo.Name} already exists.");
                return;
            }

            Keyset k;

            if (GlobalProdSwitchKeysInfo.Exists)
            {
                k = ExternalKeyReader.ReadKeyFile(GlobalProdSwitchKeysInfo.FullName);
            }
            else if (LocalProdSwitchKeysInfo.Exists)
            {
                k = ExternalKeyReader.ReadKeyFile(LocalProdSwitchKeysInfo.FullName);
            }
            else
            {
                Console.WriteLine("Keys couldn't be found. Add to ~/.switch or working directory.");
                return;
            }
            k.DeriveKeys();

            if (k.SslRsaKek.IsEmpty())
            {
                Console.WriteLine("You are missing SslRsaKek in your keys file.");
                return;
            }

            Calibration cal0;

            byte[] certBytes;
            using (Stream prodinfoFile = prodinfoPath.OpenRead())
            {
                prodinfoFile.Seek(0, SeekOrigin.Begin);
                cal0 = new Calibration(prodinfoFile);

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

                certBytes = new byte[certLength];
                prodinfoFile.Seek(0x0AE0, SeekOrigin.Begin);      // seek to cert (should be redundant?)
                prodinfoFile.Read(certBytes, 0, (int)certLength); // read actual cert
            }

            // extract enc private modulus
            byte[] counter        = cal0.SslExtKey.Take(0x10).ToArray();
            byte[] privateModulus = cal0.SslExtKey.Skip(0x10).ToArray();

            // decrypt private modulus
            new Aes128CtrTransform(k.SslRsaKek, counter).TransformBlock(privateModulus);

            // import raw cert
            var certificate = new X509CertificateParser().ReadCertificate(certBytes);
            // import private modulus
            var privateParameter = certificate.RecoverPrivateParameter(privateModulus);

            // build PFX and add cert
            var store     = new Pkcs12Store();
            var certEntry = new X509CertificateEntry(certificate);

            store.SetCertificateEntry(certificate.SubjectDN.ToString(), certEntry);

            // add private key params to PFX
            AsymmetricKeyEntry privateKeyEntry = new AsymmetricKeyEntry(privateParameter);

            store.SetKeyEntry($"{certificate.SubjectDN}_key", privateKeyEntry, new[] { certEntry });

            // output PFX with password
            using (Stream pfxStream = OutputPfxInfo.Create())
                store.Save(pfxStream, "switch".ToCharArray(), new SecureRandom());

            Console.WriteLine($"Wrote to {OutputPfxInfo.FullName}");
        }
コード例 #12
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}");
        }
コード例 #13
0
        public MainPage()
        {
            InitializeComponent();

            Loaded += (_, __) =>
            {
                void rcmRefresh(bool b)
                {
                    foreach (MenuItem item in
                             RCMContextMenu.Items.Cast <MenuItem>().Where(i => i.Tag as string == "RequiresRCM"))
                    {
                        item.IsEnabled = b;
                    }
                };
                rcmRefresh(InjectService.LibusbKInstalled);

                void nandRefresh(bool b)
                {
                    foreach (MenuItem item in
                             NANDContextMenu.Items.Cast <MenuItem>().Where(i => i.Tag as string == "RequiresNAND"))
                    {
                        item.IsEnabled = b;
                    }
                };
                nandRefresh(false);

                InjectService.DeviceInserted += () =>
                {
                    if (InjectService.LibusbKInstalled)
                    {
                        Dispatcher.Invoke(() => rcmRefresh(true));
                    }
                };

                InjectService.DeviceRemoved += () =>
                {
                    Dispatcher.Invoke(() => rcmRefresh(false));
                };

                NANDService.OnNANDPluggedIn += () =>
                {
                    Dispatcher.Invoke(() => nandRefresh(true));
                };

                NANDService.OnNANDRemoved += () =>
                {
                    Dispatcher.Invoke(() => nandRefresh(false));
                };

                SDService.OnSDPluggedIn += (effectiveRoot) =>
                {
                    DirectoryInfo root      = SDService.SDRoot;
                    DirectoryInfo switchDir = root.GetDirectory("switch");
                    if (switchDir.Exists)
                    {
                        FileInfo prodKeysInfo = switchDir.GetFile("prod.keys");
                        if (prodKeysInfo.Exists)
                        {
                            try
                            {
                                ExternalKeyReader.ReadKeyFile(HACGUIKeyset.Keyset, prodKeysInfo.FullName);
                                new SaveKeysetTask(null).CreateTask().RunSynchronously();
                            }
                            catch (Exception e)
                            {
                                MessageBox.Show($"An error occured when attempting to import keys from SD card. It could be corrupted.\nError: {e.Message}");
                            }
                        }
                    }
                };

                // init this first as other pages may request tasks on init
                TaskManagerView          = new TaskManagerPage();
                TaskManagerFrame.Content = TaskManagerView;

                StatusService.Bar = StatusBar;
                StatusService.CurrentTaskBlock = CurrentTaskBlock;
                CurrentTaskBlock.Background    = StatusBar.Background;
                TaskManagerView.Queue.Submit(new RunTask("Opening/Deriving keys...", new Task(() => HACGUIKeyset.Keyset.LoadAll())));

                DeviceService.Start();

                TitleManagerView          = new MainTitleManagerPage();
                TitleManagerFrame.Content = TitleManagerView;
                SaveManagerView           = new SaveManagerPage(0);
                SaveManagerFrame.Content  = SaveManagerView;

                StatusService.Start();

                if (Native.IsAdministrator)
                {
                    AdminButton.IsEnabled = false;
                }
            };
        }