internal NokiaFlashViewModel(NokiaPhoneModel CurrentModel, Action <PhoneInterfaces> RequestModeSwitch, Action SwitchToGettingStarted)
     : base()
 {
     this.CurrentModel           = (NokiaFlashModel)CurrentModel;
     this.RequestModeSwitch      = RequestModeSwitch;
     this.SwitchToGettingStarted = SwitchToGettingStarted;
 }
Exemple #2
0
        internal override void EvaluateViewState()
        {
            if (!IsActive)
            {
                return;
            }

            if ((Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash))
            {
                NokiaFlashModel LumiaFlashModel = (NokiaFlashModel)Notifier.CurrentModel;
                PhoneInfo       Info            = LumiaFlashModel.ReadPhoneInfo();
                ProductType  = Info.Type;
                OperatorCode = "";
                ProductCode  = Info.ProductCode;
            }
            else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Normal)
            {
                NokiaPhoneModel LumiaNormalModel = (NokiaPhoneModel)Notifier.CurrentModel;
                OperatorCode = LumiaNormalModel.ExecuteJsonMethodAsString("ReadOperatorName", "OperatorName");                             // Example: 000-NL
                string TempProductType = LumiaNormalModel.ExecuteJsonMethodAsString("ReadManufacturerModelName", "ManufacturerModelName"); // RM-821_eu_denmark_251
                if (TempProductType.IndexOf('_') >= 0)
                {
                    TempProductType = TempProductType.Substring(0, TempProductType.IndexOf('_'));
                }
                ProductType = TempProductType;
                ProductCode = LumiaNormalModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode"); // 059Q9D7
            }
        }
        internal void FlashMMOSTask(string MMOSPath)
        {
            NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;

            if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader)
            {
                Phone.SwitchToFlashAppContext();
            }

            new Thread(() =>
            {
                bool Result = true;

                ActivateSubContext(new BusyViewModel("Initializing flash..."));

                string ErrorSubMessage = null;

                try
                {
                    FileInfo info = new(MMOSPath);
                    uint length   = uint.Parse(info.Length.ToString());
                    const int maximumbuffersize = 0x00240000;
                    uint totalcounts            = (uint)Math.Truncate((decimal)length / maximumbuffersize);
                    BusyViewModel Busy          = new("Flashing Test Mode package...", MaxProgressValue: totalcounts, UIContext: UIContext);
                    ActivateSubContext(Busy);

                    Phone.FlashMMOS(MMOSPath, Busy.ProgressUpdater);

                    ActivateSubContext(new BusyViewModel("And now booting phone to MMOS...", "If the phone stays on the lightning cog screen for a while, you may need to unplug and replug the phone to continue the boot process."));

                    PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
                }
                catch (Exception Ex)
                {
                    LogFile.LogException(Ex);
                    if (Ex is WPinternalsException)
                    {
                        ErrorSubMessage = ((WPinternalsException)Ex).SubMessage;
                    }

                    Result = false;
                }

                if (!Result)
                {
                    ExitFailure("Flash failed!", ErrorSubMessage);
                    return;
                }
            }).Start();
        }
        private void SwitchFromFlashToMassStorageMode(bool Continuation = false)
        {
            string ProgressText;

            if (Continuation)
            {
                ProgressText = "And now rebooting phone to Mass Storage mode...";
            }
            else
            {
                ProgressText = "Rebooting phone to Mass Storage mode...";
            }

            NokiaFlashModel FlashModel = (NokiaFlashModel)CurrentModel;

            if (CurrentMode == PhoneInterfaces.Lumia_Bootloader)
            {
                try
                {
                    FlashModel.SwitchToFlashAppContext();
                }
                catch { }
            }
            PhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: false);

            MassStorageWarning = null;
            if (Info.FlashAppProtocolVersionMajor < 2)
            {
                MassStorageWarning = "Switching to Mass Storage mode should take about 10 seconds. The phone should be unlocked using an Engineering SBL3 to enable Mass Storage mode. When you unlocked the bootloader, but you did not use an Engineering SBL3, an attempt to boot to Mass Storage mode may result in an unresponsive state. Installing drivers for this interface may also cause to hang the PC. So when this switch is taking too long, you should reboot both the PC and the phone. To reboot the phone, you have to perform a soft-reset. Press and hold the volume-down-button and the power-button at the same time for at least 10 seconds. This will trigger a power-cycle and the phone will reboot. Once fully booted, the phone may show strange behavior, like complaining about mail-accounts, showing old text-messages, inability to load https-websites, etc. This is expected behavior, because the time-settings of the phone are incorrect. Just wait a few seconds for the phone to get a data-connection and have the date/time synced. After that the strange behavior will stop automatically and normal operation is resumed.";
            }
            else
            {
                MassStorageWarning = "When the screen of the phone is black for a while, it could be that the phone is already in Mass Storage Mode, but there is no drive-letter assigned. To resolve this issue, open Device Manager and manually assign a drive-letter to the MainOS partition of your phone, or open a command-prompt and type: diskpart automount enable.";
                if (App.IsPnPEventLogMissing)
                {
                    MassStorageWarning += " It is also possible that the phone is in Mass Storage mode, but the Mass Storage driver on this PC failed. Your PC does not have an eventlog to detect this misbehaviour. But in this case you will see a device with an exclamation mark in Device Manager and then you need to manually reset the phone by pressing and holding the power-button for at least 10 seconds until it vibrates and reboots. After that Windows Phone Internals will revert the changes. After the phone has rebooted to the OS, you can retry to unlock the bootloader.";
                }
            }

            bool IsOldLumia    = (Info.FlashAppProtocolVersionMajor < 2);
            bool IsNewLumia    = (Info.FlashAppProtocolVersionMajor >= 2);
            bool IsUnlockedNew = false;

            if (IsNewLumia)
            {
                GPT GPT = FlashModel.ReadGPT();
                IsUnlockedNew = ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null));
            }
            bool IsOriginalEngineeringLumia = ((!Info.SecureFfuEnabled || Info.Authenticated || Info.RdcPresent) && !IsUnlockedNew);

            if (IsOldLumia || IsOriginalEngineeringLumia)
            {
                byte[] BootModeFlagCommand        = new byte[] { 0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; // NOKFW UBF
                byte[] RebootCommand              = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 };
                byte[] RebootToMassStorageCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x4D };                                                                         // NOKM
                IsSwitchingInterface = true;
                byte[] RebootCommandResult = ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(RebootToMassStorageCommand);
                if ((RebootCommandResult != null) && (RebootCommandResult.Length == 4)) // This means fail: NOKU (unknow command)
                {
                    BootModeFlagCommand[0x0F] = 0x4D;
                    byte[] BootFlagResult = ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(BootModeFlagCommand);
                    UInt16 ResultCode     = BitConverter.ToUInt16(BootFlagResult, 6);
                    if (ResultCode == 0)
                    {
                        PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
                        ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand);
                        ModeSwitchProgressWrapper(ProgressText, MassStorageWarning);
                        LogFile.Log("Rebooting phone to Mass Storage mode");
                    }
                    else
                    {
                        ModeSwitchErrorWrapper("Failed to switch to Mass Storage mode");
                        IsSwitchingInterface = false;
                    }
                }
                else
                {
                    PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
                    ModeSwitchProgressWrapper(ProgressText, MassStorageWarning);
                    LogFile.Log("Rebooting phone to Mass Storage mode");
                }
            }
            else if (IsUnlockedNew)
            {
                new Thread(async() =>
                {
                    LogFile.BeginAction("SwitchToMassStorageMode");

                    try
                    {
                        // Implementation of writing a partition with SecureBoot variable to the phone
                        ModeSwitchProgressWrapper(ProgressText, MassStorageWarning);
                        LogFile.Log("Preparing phone for Mass Storage Mode", LogType.FileAndConsole);
                        var assembly = System.Reflection.Assembly.GetExecutingAssembly();

                        // Magic!
                        // The SBMSM resource is a compressed version of a raw NV-variable-partition.
                        // In this partition the SecureBoot variable is disabled and an extra variable is added which triggers Mass Storage Mode on next reboot.
                        // It overwrites the variable in a different NV-partition than where this variable is stored usually.
                        // This normally leads to endless-loops when the NV-variables are enumerated.
                        // But the partition contains an extra hack to break out the endless loops.
                        using (var stream = assembly.GetManifestResourceStream("WPinternals.SBMSM"))
                        {
                            using (DecompressedStream dec = new DecompressedStream(stream))
                            {
                                using (System.IO.MemoryStream SB = new System.IO.MemoryStream()) // Must be a seekable stream!
                                {
                                    dec.CopyTo(SB);

                                    // We don't need to check for the BACKUP_BS_NV partition here,
                                    // because the SecureBoot flag is disabled here.
                                    // So either the NV was already backupped or already overwritten.

                                    GPT GPT          = FlashModel.ReadGPT();
                                    Partition Target = GPT.GetPartition("UEFI_BS_NV");

                                    // We've been reading the GPT, so we let the phone reset once more to be sure that memory maps are the same
                                    WPinternalsStatus LastStatus = WPinternalsStatus.Undefined;
                                    List <FlashPart> Parts       = new List <FlashPart>();
                                    FlashPart Part   = new FlashPart();
                                    Part.StartSector = (uint)Target.FirstSector;
                                    Part.Stream      = SB;
                                    Parts.Add(Part);
                                    await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(PhoneNotifier, null, false, false, Parts, DoResetFirst: true, ClearFlashingStatusAtEnd: false, ShowProgress: false,
                                                                                        SetWorkingStatus: (m, s, v, a, st) =>
                                    {
                                        if (SetWorkingStatus != null)
                                        {
                                            if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset))
                                            {
                                                SetWorkingStatus(m, s, v, a, st);
                                            }
                                            else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset))
                                            {
                                                SetWorkingStatus(ProgressText, MassStorageWarning);
                                            }
                                            LastStatus = st;
                                        }
                                    },
                                                                                        UpdateWorkingStatus: (m, s, v, st) =>
                                    {
                                        if (UpdateWorkingStatus != null)
                                        {
                                            if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset))
                                            {
                                                UpdateWorkingStatus(m, s, v, st);
                                            }
                                            else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset))
                                            {
                                                SetWorkingStatus(ProgressText, MassStorageWarning);
                                            }
                                            LastStatus = st;
                                        }
                                    });
                                }
                            }
                        }

                        if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_BadMassStorage)
                        {
                            throw new WPinternalsException("Phone is in Mass Storage mode, but the driver on PC failed to start");
                        }

                        // Wait for bootloader
                        if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_MassStorage)
                        {
                            LogFile.Log("Waiting for Mass Storage Mode (1)...", LogType.FileOnly);
                            await PhoneNotifier.WaitForArrival();
                        }

                        if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_BadMassStorage)
                        {
                            throw new WPinternalsException("Phone is in Mass Storage mode, but the driver on PC failed to start");
                        }

                        // Wait for mass storage mode
                        if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_MassStorage)
                        {
                            LogFile.Log("Waiting for Mass Storage Mode (2)...", LogType.FileOnly);
                            await PhoneNotifier.WaitForArrival();
                        }

                        if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_BadMassStorage)
                        {
                            throw new WPinternalsException("Phone is in Mass Storage mode, but the driver on PC failed to start");
                        }

                        MassStorage Storage = null;
                        if (PhoneNotifier.CurrentModel is MassStorage)
                        {
                            Storage = (MassStorage)PhoneNotifier.CurrentModel;
                        }

                        if (Storage == null)
                        {
                            ModeSwitchErrorWrapper("Failed to switch to Mass Storage Mode");
                        }
                        else
                        {
                            ModeSwitchSuccessWrapper();
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        ModeSwitchErrorWrapper(Ex.Message);
                    }

                    LogFile.EndAction("SwitchToMassStorageMode");
                }).Start();
            }
            else
            {
                ModeSwitchErrorWrapper("Bootloader was not unlocked. First unlock bootloader before you try to switch to Mass Storage Mode.");
            }
        }
Exemple #5
0
 internal NokiaModeFlashViewModel(NokiaPhoneModel CurrentModel, Action <PhoneInterfaces?> RequestModeSwitch)
     : base()
 {
     this.CurrentModel      = (NokiaFlashModel)CurrentModel;
     this.RequestModeSwitch = RequestModeSwitch;
 }
        internal void FlashPartitionsTask(string EFIESPPath, string MainOSPath, string DataPath)
        {
            new Thread(() =>
            {
                bool Result = true;

                ActivateSubContext(new BusyViewModel("Initializing flash..."));

                NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;

                GPT GPT = Phone.ReadGPT();

                ulong TotalSizeSectors     = 0;
                int PartitionCount         = 0;
                ulong MainOSOldSectorCount = 0;
                ulong MainOSNewSectorCount = 0;
                ulong DataOldSectorCount   = 0;
                ulong DataNewSectorCount   = 0;
                ulong FirstMainOSSector    = 0;

                try
                {
                    if (EFIESPPath != null)
                    {
                        using (Stream Stream = new DecompressedStream(File.Open(EFIESPPath, FileMode.Open)))
                        {
                            ulong StreamLengthInSectors = (ulong)Stream.Length / 0x200;
                            TotalSizeSectors           += StreamLengthInSectors;
                            PartitionCount++;
                            Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, "EFIESP", true) == 0).FirstOrDefault();
                            if (StreamLengthInSectors > Partition.SizeInSectors)
                            {
                                LogFile.Log("Flash failed! Size of partition 'EFIESP' is too big.");
                                ExitFailure("Flash failed!", "Size of partition 'EFIESP' is too big.");
                                return;
                            }
                        }
                    }

                    if (MainOSPath != null)
                    {
                        using (Stream Stream = new DecompressedStream(File.Open(MainOSPath, FileMode.Open)))
                        {
                            ulong StreamLengthInSectors = (ulong)Stream.Length / 0x200;
                            TotalSizeSectors           += StreamLengthInSectors;
                            PartitionCount++;
                            Partition Partition  = GPT.Partitions.Where(p => string.Compare(p.Name, "MainOS", true) == 0).FirstOrDefault();
                            MainOSOldSectorCount = Partition.SizeInSectors;
                            MainOSNewSectorCount = StreamLengthInSectors;
                            FirstMainOSSector    = Partition.FirstSector;
                        }
                    }

                    if (DataPath != null)
                    {
                        using (Stream Stream = new DecompressedStream(File.Open(DataPath, FileMode.Open)))
                        {
                            ulong StreamLengthInSectors = (ulong)Stream.Length / 0x200;
                            TotalSizeSectors           += StreamLengthInSectors;
                            PartitionCount++;
                            Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, "Data", true) == 0).FirstOrDefault();
                            DataOldSectorCount  = Partition.SizeInSectors;
                            DataNewSectorCount  = StreamLengthInSectors;
                        }
                    }
                }
                catch (Exception Ex)
                {
                    LogFile.LogException(Ex);
                    Result = false;
                }

                if ((MainOSNewSectorCount > 0) && (DataNewSectorCount > 0))
                {
                    if ((MainOSNewSectorCount > MainOSOldSectorCount) || (DataNewSectorCount > DataOldSectorCount))
                    {
                        UInt64 OSSpace = GPT.LastUsableSector - FirstMainOSSector + 1;
                        if ((MainOSNewSectorCount + DataNewSectorCount) <= OSSpace)
                        {
                            // MainOS and Data partitions need to be re-aligned!
                            Partition MainOSPartition  = GPT.Partitions.Where(p => string.Compare(p.Name, "MainOS", true) == 0).Single();
                            Partition DataPartition    = GPT.Partitions.Where(p => string.Compare(p.Name, "Data", true) == 0).Single();
                            MainOSPartition.LastSector = MainOSPartition.FirstSector + MainOSNewSectorCount - 1;
                            DataPartition.FirstSector  = MainOSPartition.LastSector + 1;
                            DataPartition.LastSector   = DataPartition.FirstSector + DataNewSectorCount - 1;
                            Phone.WriteGPT(GPT);
                        }
                        else
                        {
                            LogFile.Log("Flash failed! Size of partitions 'MainOS' and 'Data' together are too big.");
                            ExitFailure("Flash failed!", "Sizes of partitions 'MainOS' and 'Data' together are too big.");
                            return;
                        }
                    }
                }
                else if ((MainOSNewSectorCount > 0) && (MainOSNewSectorCount > MainOSOldSectorCount))
                {
                    LogFile.Log("Flash failed! Size of partition 'MainOS' is too big.");
                    ExitFailure("Flash failed!", "Size of partition 'MainOS' is too big.");
                    return;
                }
                else if ((DataNewSectorCount > 0) && (DataNewSectorCount > DataOldSectorCount))
                {
                    LogFile.Log("Flash failed! Size of partition 'Data' is too big.");
                    ExitFailure("Flash failed!", "Size of partition 'Data' together is too big.");
                    return;
                }

                BusyViewModel Busy      = new BusyViewModel("Flashing...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext);
                ProgressUpdater Updater = Busy.ProgressUpdater;
                ActivateSubContext(Busy);

                int i = 0;
                if (Result)
                {
                    try
                    {
                        if (EFIESPPath != null)
                        {
                            i++;
                            Busy.Message = "Flashing partition EFIESP (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                            Phone.FlashRawPartition(EFIESPPath, "EFIESP", Updater);
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        Result = false;
                    }
                }

                if (Result)
                {
                    try
                    {
                        if (MainOSPath != null)
                        {
                            i++;
                            Busy.Message = "Flashing partition MainOS (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                            Phone.FlashRawPartition(MainOSPath, "MainOS", Updater);
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        Result = false;
                    }
                }

                if (Result)
                {
                    try
                    {
                        if (DataPath != null)
                        {
                            i++;
                            Busy.Message = "Flashing partition Data (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                            Phone.FlashRawPartition(DataPath, "Data", Updater);
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        Result = false;
                    }
                }

                if (!Result)
                {
                    ExitFailure("Flash failed!", null);
                    return;
                }

                ExitSuccess("Flash successful! Make sure you disable Windows Update on the phone!", null);
            }).Start();
        }
        internal void FlashFFUTask(string FFUPath)
        {
            new Thread(() =>
            {
                bool Result = true;

                NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;
                PhoneInfo Info        = Phone.ReadPhoneInfo(false);

                #region Remove bootloader changes

                // If necessary remove bootloader changes
                // In case the NV vars were redirected, and a stock FFU is flashed, then the IsFlashing flag will be cleared in the redirected NV vars
                // And after a reboot the original NV vars are active again, but the IsFlashing flag is still set from when the bootloader was unlocked
                // So we will first restore the GPT, so the original vars are active again.
                // Then IsFlashing is true and the phone boots forcibly to FlashApp again.
                // Then we start normal FFU flasing and at the end the IsFlashing flag is cleared in the original vars.

                if (Info.FlashAppProtocolVersionMajor >= 2)
                {
                    byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(Phone, 0x20000); // TODO: Get proper profile FFU and get ChunkSizeInBytes
                    GPT GPT         = new GPT(GPTChunk);
                    FlashPart Part;
                    List <FlashPart> FlashParts = new List <FlashPart>();

                    Partition NvBackupPartition = GPT.GetPartition("BACKUP_BS_NV");
                    if (NvBackupPartition != null)
                    {
                        // This must be a left over of a half unlocked bootloader
                        Partition NvPartition               = GPT.GetPartition("UEFI_BS_NV");
                        NvBackupPartition.Name              = "UEFI_BS_NV";
                        NvBackupPartition.PartitionGuid     = NvPartition.PartitionGuid;
                        NvBackupPartition.PartitionTypeGuid = NvPartition.PartitionTypeGuid;
                        GPT.Partitions.Remove(NvPartition);

                        GPT.Rebuild();
                        Part             = new FlashPart();
                        Part.StartSector = 0;
                        Part.Stream      = new MemoryStream(GPTChunk);
                        FlashParts.Add(Part);
                    }

                    bool ClearFlashingStatus = true;

                    // We should only clear NV if there was no backup NV to be restored and the current NV contains the SB unlock.
                    if ((NvBackupPartition == null) && !Info.UefiSecureBootEnabled)
                    {
                        // ClearNV
                        Part             = new FlashPart();
                        Partition Target = GPT.GetPartition("UEFI_BS_NV");
                        Part.StartSector = (UInt32)Target.FirstSector;
                        Part.Stream      = new MemoryStream(new byte[0x40000]);
                        FlashParts.Add(Part);

                        ClearFlashingStatus = false;
                    }

                    if (FlashParts.Count > 0)
                    {
                        ActivateSubContext(new BusyViewModel("Restoring bootloader..."));
                        WPinternalsStatus LastStatus = WPinternalsStatus.Undefined;
                        LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(PhoneNotifier, FFUPath, false, false, FlashParts, true, ClearFlashingStatusAtEnd: ClearFlashingStatus,
                                                                      SetWorkingStatus: (m, s, v, a, st) =>
                        {
                            if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset))
                            {
                                SetWorkingStatus(m, s, v, a, st);
                            }
                            else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset))
                            {
                                SetWorkingStatus("Restoring bootloader...", null, null, Status: WPinternalsStatus.Flashing);
                            }
                            LastStatus = st;
                        },
                                                                      UpdateWorkingStatus: (m, s, v, st) =>
                        {
                            if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset))
                            {
                                UpdateWorkingStatus(m, s, v, st);
                            }
                            else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset))
                            {
                                SetWorkingStatus("Restoring bootloader...", null, null, Status: WPinternalsStatus.Flashing);
                            }
                            LastStatus = st;
                        }
                                                                      ).Wait();

                        if ((PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash))
                        {
                            PhoneNotifier.WaitForArrival().Wait();
                        }

                        if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader)
                        {
                            ((NokiaFlashModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext();
                        }
                    }
                }

                #endregion

                Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;

                ActivateSubContext(new BusyViewModel("Initializing flash..."));

                string ErrorSubMessage = null;

                try
                {
                    FFU FFU            = new FFU(FFUPath);
                    BusyViewModel Busy = new BusyViewModel("Flashing original FFU...", MaxProgressValue: FFU.TotalChunkCount, UIContext: UIContext);
                    ActivateSubContext(Busy);
                    byte Options = 0;
                    if (!Info.IsBootloaderSecure)
                    {
                        Options = (byte)((FlashOptions)Options | FlashOptions.SkipSignatureCheck);
                    }
                    Phone.FlashFFU(FFU, Busy.ProgressUpdater, true, Options);
                }
                catch (Exception Ex)
                {
                    LogFile.LogException(Ex);
                    if (Ex is WPinternalsException)
                    {
                        ErrorSubMessage = ((WPinternalsException)Ex).SubMessage;
                    }
                    Result = false;
                }


                if (!Result)
                {
                    ExitFailure("Flash failed!", ErrorSubMessage);
                    return;
                }

                ExitSuccess("Flash successful!", null);
            }).Start();
        }
        internal void FlashArchiveTask(string ArchivePath)
        {
            new Thread(() =>
            {
                ActivateSubContext(new BusyViewModel("Initializing flash..."));

                NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;

                ulong TotalSizeSectors     = 0;
                int PartitionCount         = 0;
                ulong MainOSOldSectorCount = 0;
                ulong MainOSNewSectorCount = 0;
                ulong DataOldSectorCount   = 0;
                ulong DataNewSectorCount   = 0;
                ulong FirstMainOSSector    = 0;
                bool GPTChanged            = false;

                try
                {
                    GPT GPT = Phone.ReadGPT();

                    using (FileStream FileStream = new FileStream(ArchivePath, FileMode.Open))
                    {
                        using (ZipArchive Archive = new ZipArchive(FileStream, ZipArchiveMode.Read))
                        {
                            foreach (ZipArchiveEntry Entry in Archive.Entries)
                            {
                                // Determine if there is a partition layout present
                                ZipArchiveEntry PartitionEntry = Archive.GetEntry("Partitions.xml");
                                if (PartitionEntry == null)
                                {
                                    GPT.MergePartitions(null, false, Archive);
                                    GPTChanged |= GPT.HasChanged;
                                }
                                else
                                {
                                    using (Stream ZipStream = PartitionEntry.Open())
                                    {
                                        using (StreamReader ZipReader = new StreamReader(ZipStream))
                                        {
                                            string PartitionXml = ZipReader.ReadToEnd();
                                            GPT.MergePartitions(PartitionXml, false, Archive);
                                            GPTChanged |= GPT.HasChanged;
                                        }
                                    }
                                }

                                // First determine if we need a new GPT!
                                if (!Entry.FullName.Contains("/")) // No subfolders
                                {
                                    string PartitionName = System.IO.Path.GetFileNameWithoutExtension(Entry.Name);
                                    int P = PartitionName.IndexOf('.');
                                    if (P >= 0)
                                    {
                                        PartitionName = PartitionName.Substring(0, P); // Example: Data.bin.gz -> Data
                                    }
                                    Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, PartitionName, true) == 0).FirstOrDefault();
                                    if (Partition != null)
                                    {
                                        DecompressedStream DecompressedStream = new DecompressedStream(Entry.Open());
                                        ulong StreamLengthInSectors           = (ulong)Entry.Length / 0x200;
                                        try
                                        {
                                            StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200;
                                        }
                                        catch { }

                                        TotalSizeSectors += StreamLengthInSectors;
                                        PartitionCount++;

                                        if (string.Compare(PartitionName, "MainOS", true) == 0)
                                        {
                                            MainOSOldSectorCount = Partition.SizeInSectors;
                                            MainOSNewSectorCount = StreamLengthInSectors;
                                            FirstMainOSSector    = Partition.FirstSector;
                                        }
                                        else if (string.Compare(PartitionName, "Data", true) == 0)
                                        {
                                            DataOldSectorCount = Partition.SizeInSectors;
                                            DataNewSectorCount = StreamLengthInSectors;
                                        }
                                        else if (StreamLengthInSectors > Partition.SizeInSectors)
                                        {
                                            LogFile.Log("Flash failed! Size of partition '" + PartitionName + "' is too big.");
                                            ExitFailure("Flash failed!", "Size of partition '" + PartitionName + "' is too big.");
                                            return;
                                        }
                                    }
                                }
                            }

                            if ((MainOSNewSectorCount > 0) && (DataNewSectorCount > 0))
                            {
                                if ((MainOSNewSectorCount > MainOSOldSectorCount) || (DataNewSectorCount > DataOldSectorCount))
                                {
                                    UInt64 OSSpace = GPT.LastUsableSector - FirstMainOSSector + 1;
                                    if ((MainOSNewSectorCount + DataNewSectorCount) <= OSSpace)
                                    {
                                        // MainOS and Data partitions need to be re-aligned!
                                        Partition MainOSPartition  = GPT.Partitions.Where(p => string.Compare(p.Name, "MainOS", true) == 0).Single();
                                        Partition DataPartition    = GPT.Partitions.Where(p => string.Compare(p.Name, "Data", true) == 0).Single();
                                        MainOSPartition.LastSector = MainOSPartition.FirstSector + MainOSNewSectorCount - 1;
                                        DataPartition.FirstSector  = MainOSPartition.LastSector + 1;
                                        DataPartition.LastSector   = DataPartition.FirstSector + DataNewSectorCount - 1;

                                        GPTChanged = true;
                                    }
                                    else
                                    {
                                        LogFile.Log("Flash failed! Size of partitions 'MainOS' and 'Data' together are too big.");
                                        ExitFailure("Flash failed!", "Sizes of partitions 'MainOS' and 'Data' together are too big.");
                                        return;
                                    }
                                }
                            }
                            else if ((MainOSNewSectorCount > 0) && (MainOSNewSectorCount > MainOSOldSectorCount))
                            {
                                LogFile.Log("Flash failed! Size of partition 'MainOS' is too big.");
                                ExitFailure("Flash failed!", "Size of partition 'MainOS' is too big.");
                                return;
                            }
                            else if ((DataNewSectorCount > 0) && (DataNewSectorCount > DataOldSectorCount))
                            {
                                LogFile.Log("Flash failed! Size of partition 'Data' is too big.");
                                ExitFailure("Flash failed!", "Size of partition 'Data' is too big.");
                                return;
                            }

                            if (GPTChanged)
                            {
                                Phone.WriteGPT(GPT);
                            }

                            if (PartitionCount > 0)
                            {
                                BusyViewModel Busy      = new BusyViewModel("Flashing...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext);
                                ProgressUpdater Updater = Busy.ProgressUpdater;
                                ActivateSubContext(Busy);

                                int i = 0;

                                foreach (ZipArchiveEntry Entry in Archive.Entries)
                                {
                                    // "MainOS.bin.gz" => "MainOS"
                                    string PartitionName = Entry.Name;
                                    int Pos = PartitionName.IndexOf('.');
                                    if (Pos >= 0)
                                    {
                                        PartitionName = PartitionName.Substring(0, Pos);
                                    }

                                    Partition Partition = GPT.Partitions.Where(p => string.Compare(p.Name, PartitionName, true) == 0).FirstOrDefault();
                                    if (Partition != null)
                                    {
                                        Stream DecompressedStream   = new DecompressedStream(Entry.Open());
                                        ulong StreamLengthInSectors = (ulong)Entry.Length / 0x200;
                                        try
                                        {
                                            StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200;
                                        }
                                        catch { }

                                        if (StreamLengthInSectors <= Partition.SizeInSectors)
                                        {
                                            i++;
                                            Busy.Message = "Flashing partition " + Partition.Name + " (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                                            Phone.FlashRawPartition(DecompressedStream, Partition.Name, Updater);
                                        }
                                        DecompressedStream.Close();
                                    }
                                }
                            }
                            else
                            {
                                LogFile.Log("Flash failed! No valid partitions found in the archive.");
                                ExitFailure("Flash failed!", "No valid partitions found in the archive");
                                return;
                            }
                        }
                    }
                }
                catch (Exception Ex)
                {
                    LogFile.LogException(Ex);
                    if (Ex is WPinternalsException)
                    {
                        ExitFailure("Flash failed!", ((WPinternalsException)Ex).SubMessage);
                    }
                    else
                    {
                        ExitFailure("Flash failed!", null);
                    }
                    return;
                }

                ExitSuccess("Flash successful! Make sure you disable Windows Update on the phone!", null);
            }).Start();
        }
        internal void RestoreTask(string EFIESPPath, string MainOSPath, string DataPath)
        {
            new Thread(() =>
            {
                bool Result = true;

                ActivateSubContext(new BusyViewModel("Initializing restore..."));

                ulong TotalSizeSectors = 0;
                int PartitionCount     = 0;
                try
                {
                    if (EFIESPPath != null)
                    {
                        TotalSizeSectors += (ulong)new FileInfo(EFIESPPath).Length / 0x200;
                        PartitionCount++;
                    }

                    if (MainOSPath != null)
                    {
                        TotalSizeSectors += (ulong)new FileInfo(MainOSPath).Length / 0x200;
                        PartitionCount++;
                    }

                    if (DataPath != null)
                    {
                        TotalSizeSectors += (ulong)new FileInfo(DataPath).Length / 0x200;
                        PartitionCount++;
                    }
                }
                catch (Exception Ex)
                {
                    LogFile.LogException(Ex);
                    Result = false;
                }

                NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel;

                BusyViewModel Busy      = new BusyViewModel("Restoring...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext);
                ProgressUpdater Updater = Busy.ProgressUpdater;
                ActivateSubContext(Busy);

                int i = 0;
                if (Result)
                {
                    try
                    {
                        if (EFIESPPath != null)
                        {
                            i++;
                            Busy.Message = "Restoring partition EFIESP (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                            Phone.FlashRawPartition(EFIESPPath, "EFIESP", Updater);
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        Result = false;
                    }
                }

                if (Result)
                {
                    try
                    {
                        if (MainOSPath != null)
                        {
                            i++;
                            Busy.Message = "Restoring partition MainOS (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                            Phone.FlashRawPartition(EFIESPPath, "MainOS", Updater);
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        Result = false;
                    }
                }

                if (Result)
                {
                    try
                    {
                        if (DataPath != null)
                        {
                            i++;
                            Busy.Message = "Restoring partition Data (" + i.ToString() + @"/" + PartitionCount.ToString() + ")";
                            Phone.FlashRawPartition(EFIESPPath, "Data", Updater);
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        Result = false;
                    }
                }

                if (!Result)
                {
                    ActivateSubContext(new MessageViewModel("Failed to restore!", Exit));
                    return;
                }

                ActivateSubContext(new MessageViewModel("Successfully restored!", Exit));
            }).Start();
        }
        private void SwitchFromFlashToLabelMode(bool Continuation = false)
        {
            string ProgressText;

            if (Continuation)
            {
                ProgressText = "And now preparing to boot the phone to Label mode...";
            }
            else
            {
                ProgressText = "Preparing to boot the phone to Label mode...";
            }

            NokiaFlashModel FlashModel = (NokiaFlashModel)CurrentModel;

            if (CurrentMode == PhoneInterfaces.Lumia_Bootloader)
            {
                try
                {
                    FlashModel.SwitchToFlashAppContext();
                }
                catch { }
            }
            PhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: true);

            if (Info.MmosOverUsbSupported)
            {
                new Thread(() =>
                {
                    LogFile.BeginAction("SwitchToLabelMode");

                    try
                    {
                        ModeSwitchProgressWrapper(ProgressText, null);
                        string TempFolder = Environment.GetEnvironmentVariable("TEMP") + @"\WPInternals";
                        if (Info.Type == "RM-1152")
                        {
                            Info.Type = "RM-1151";
                        }
                        string ENOSWPackage = LumiaDownloadModel.SearchENOSW(Info.Type, Info.Firmware);
                        SetWorkingStatus("Downloading " + Info.Type + " Test Mode package...", MaxProgressValue: 100);
                        DownloadEntry downloadEntry = new DownloadEntry(ENOSWPackage, TempFolder, null, null, null);

                        downloadEntry.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) =>
                        {
                            if (e.PropertyName == "Progress")
                            {
                                int progress = (sender as DownloadEntry).Progress;
                                ulong progressret;
                                ulong.TryParse(progress.ToString(), out progressret);
                                UpdateWorkingStatus(null, CurrentProgressValue: progressret);

                                if (progress == 100)
                                {
                                    ModeSwitchProgressWrapper("Initializing Flash...", null);

                                    string MMOSPath = TempFolder + "\\" + (sender as DownloadEntry).Name;

                                    PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
                                    FileInfo info         = new FileInfo(MMOSPath);
                                    uint length           = uint.Parse(info.Length.ToString());
                                    int maximumbuffersize = 0x00240000;
                                    uint totalcounts      = (uint)Math.Truncate((decimal)length / maximumbuffersize);

                                    SetWorkingStatus("Flashing Test Mode package...", MaxProgressValue: 100);

                                    ProgressUpdater progressUpdater = new ProgressUpdater(totalcounts + 1, (int i, TimeSpan? time) => UpdateWorkingStatus(null, CurrentProgressValue: (ulong)i));
                                    FlashModel.FlashMMOS(MMOSPath, progressUpdater);

                                    SetWorkingStatus("And now booting phone to MMOS...", "If the phone stays on the lightning cog screen for a while, you may need to unplug and replug the phone to continue the boot process.");
                                }
                            }
                        };
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex);
                        ModeSwitchErrorWrapper(Ex.Message);
                    }

                    LogFile.EndAction("SwitchToLabelMode");
                }).Start();
            }
            else
            {
                byte[] BootModeFlagCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; // NOKFW UBF
                byte[] RebootCommand       = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 };                                                                         // NOKR

                BootModeFlagCommand[0x0F] = 0x59;
                ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(BootModeFlagCommand);
                ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand);
                PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
                ModeSwitchProgressWrapper("Rebooting phone to Label mode", null);
                LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole);
            }
        }
Exemple #11
0
        // V3 exploit
        // Magic
        //
        internal async static Task LumiaV3CustomFlash(PhoneNotifierViewModel Notifier, List <FlashPart> FlashParts, bool CheckSectorAlignment = true, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null)
        {
            if (SetWorkingStatus == null)
            {
                SetWorkingStatus = (m, s, v, a, st) => { }
            }
            ;
            if (UpdateWorkingStatus == null)
            {
                UpdateWorkingStatus = (m, s, v, st) => { }
            }
            ;
            if (ExitSuccess == null)
            {
                ExitSuccess = (m, s) => { }
            }
            ;
            if (ExitFailure == null)
            {
                ExitFailure = (m, s) => { }
            }
            ;

            uint chunkSize  = 131072u;
            int  chunkSizes = 131072;

            if (FlashParts != null)
            {
                foreach (FlashPart Part in FlashParts)
                {
                    if (Part.Stream == null)
                    {
                        throw new ArgumentException("Stream is null");
                    }
                    if (!Part.Stream.CanSeek)
                    {
                        throw new ArgumentException("Streams must be seekable");
                    }
                    if (((Part.StartSector * 0x200) % chunkSize) != 0)
                    {
                        throw new ArgumentException("Invalid StartSector alignment");
                    }
                    if (CheckSectorAlignment)
                    {
                        if ((Part.Stream.Length % chunkSize) != 0)
                        {
                            throw new ArgumentException("Invalid Data length");
                        }
                    }
                }
            }

            try
            {
                NokiaFlashModel Model = (NokiaFlashModel)Notifier.CurrentModel;
                PhoneInfo       Info  = Model.ReadPhoneInfo();

                if ((Info.SecureFfuSupportedProtocolMask & ((ushort)FfuProtocol.ProtocolSyncV2)) == 0) // Exploit needs protocol v2 -> This check is not conclusive, because old phones also report support for this protocol, although it is really not supported.
                {
                    throw new WPinternalsException("Flash failed!", "Protocols not supported. The phone reports that it does not support the Protocol Sync V2.");
                }
                if (Info.FlashAppProtocolVersionMajor < 2) // Old phones do not support the hack. These phones have Flash protocol 1.x.
                {
                    throw new WPinternalsException("Flash failed!", "Protocols not supported. The phone reports that Flash App communication protocol is lower than 2. Reported version by the phone: " + Info.FlashAppProtocolVersionMajor + ".");
                }

                if (Info.UefiSecureBootEnabled)
                {
                    throw new WPinternalsException("Flash failed!", "UEFI Secureboot must be disabled for the Flash V3 exploit to work.");
                }

                // The payloads must be ordered by the number of locations
                //
                // FlashApp processes payloads like this:
                // - First payloads which are with one location, those can be sent in bulk
                // - Then payloads with more than one location, those should not be sent in bulk
                //
                // If you do not order payloads like this, you will get an error, most likely hash mismatch
                //
                LumiaV2UnlockBootViewModel.FlashingPayload[] payloads = new LumiaV2UnlockBootViewModel.FlashingPayload[0];
                if (FlashParts != null)
                {
                    payloads = LumiaV2UnlockBootViewModel.GetNonOptimizedPayloads(FlashParts, chunkSizes, (uint)(Info.WriteBufferSize / chunkSize), SetWorkingStatus, UpdateWorkingStatus).OrderBy(x => x.TargetLocations.Count()).ToArray();
                }

                MemoryStream Headerstream1 = new MemoryStream();

                // ==============================
                // Header 1 start

                ImageHeader image   = new ImageHeader();
                FullFlash   ffimage = new FullFlash();
                Store       simage  = new Store();

                // Todo make this read the image itself
                ffimage.OSVersion         = "10.0.11111.0";
                ffimage.DevicePlatformId0 = Info.PlatformID;
                ffimage.AntiTheftVersion  = "1.1";

                simage.SectorSize     = 512;
                simage.MinSectorCount = Info.EmmcSizeInSectors;

                //Logging.Log("Generating image manifest...");
                string manifest = ManifestIni.BuildUpManifest(ffimage, simage);//, partitions);

                byte[] TextBytes = System.Text.Encoding.ASCII.GetBytes(manifest);

                image.ManifestLength = (UInt32)TextBytes.Length;

                byte[] ImageHeaderBuffer = new byte[0x18];

                ByteOperations.WriteUInt32(ImageHeaderBuffer, 0, image.Size);
                ByteOperations.WriteAsciiString(ImageHeaderBuffer, 0x04, image.Signature);
                ByteOperations.WriteUInt32(ImageHeaderBuffer, 0x10, image.ManifestLength);
                ByteOperations.WriteUInt32(ImageHeaderBuffer, 0x14, image.ChunkSize);

                Headerstream1.Write(ImageHeaderBuffer, 0, 0x18);
                Headerstream1.Write(TextBytes, 0, TextBytes.Length);

                RoundUpToChunks(Headerstream1, chunkSize);

                // Header 1 stop + round
                // ==============================

                MemoryStream Headerstream2 = new MemoryStream();

                // ==============================
                // Header 2 start

                StoreHeader store = new StoreHeader();

                store.WriteDescriptorCount = (UInt32)payloads.Count();
                store.FinalTableIndex      = (UInt32)payloads.Count() - store.FinalTableCount;
                store.PlatformId           = Info.PlatformID;

                foreach (LumiaV2UnlockBootViewModel.FlashingPayload payload in payloads)
                {
                    store.WriteDescriptorLength += payload.GetStoreHeaderSize();
                }

                foreach (LumiaV2UnlockBootViewModel.FlashingPayload payload in payloads)
                {
                    /*if (payload.TargetLocations.First() > PlatEnd)
                     *  break;*/
                    store.FlashOnlyTableIndex += 1;
                }

                byte[] StoreHeaderBuffer = new byte[0xF8];
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0, store.UpdateType);
                ByteOperations.WriteUInt16(StoreHeaderBuffer, 0x04, store.MajorVersion);
                ByteOperations.WriteUInt16(StoreHeaderBuffer, 0x06, store.MinorVersion);
                ByteOperations.WriteUInt16(StoreHeaderBuffer, 0x08, store.FullFlashMajorVersion);
                ByteOperations.WriteUInt16(StoreHeaderBuffer, 0x0A, store.FullFlashMinorVersion);
                ByteOperations.WriteAsciiString(StoreHeaderBuffer, 0x0C, store.PlatformId);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xCC, store.BlockSizeInBytes);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xD0, store.WriteDescriptorCount);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xD4, store.WriteDescriptorLength);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xD8, store.ValidateDescriptorCount);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xDC, store.ValidateDescriptorLength);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xE0, store.InitialTableIndex);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xE4, store.InitialTableCount);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xE8, store.FlashOnlyTableIndex);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xEC, store.FlashOnlyTableCount);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xF0, store.FinalTableIndex);
                ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xF4, store.FinalTableCount);
                Headerstream2.Write(StoreHeaderBuffer, 0, 0xF8);

                byte[] descriptorsBuffer = new byte[store.WriteDescriptorLength];

                UInt32 NewWriteDescriptorOffset = 0;
                foreach (LumiaV2UnlockBootViewModel.FlashingPayload payload in payloads)
                {
                    ByteOperations.WriteUInt32(descriptorsBuffer, NewWriteDescriptorOffset + 0x00, (UInt32)payload.TargetLocations.Count()); // Location count
                    ByteOperations.WriteUInt32(descriptorsBuffer, NewWriteDescriptorOffset + 0x04, payload.ChunkCount);                      // Chunk count
                    NewWriteDescriptorOffset += 0x08;

                    foreach (UInt32 location in payload.TargetLocations)
                    {
                        ByteOperations.WriteUInt32(descriptorsBuffer, NewWriteDescriptorOffset + 0x00, 0x00000000);                          // Disk access method (0 = Begin, 2 = End)
                        ByteOperations.WriteUInt32(descriptorsBuffer, NewWriteDescriptorOffset + 0x04, location);                            // Chunk index
                        NewWriteDescriptorOffset += 0x08;
                    }
                }

                Headerstream2.Write(descriptorsBuffer, 0, (Int32)store.WriteDescriptorLength);

                RoundUpToChunks(Headerstream2, chunkSize);

                // Header 2 stop + round
                // ==============================

                SecurityHeader security = new SecurityHeader();

                Headerstream1.Seek(0, SeekOrigin.Begin);
                Headerstream2.Seek(0, SeekOrigin.Begin);

                security.HashTableSize = 0x20 * (UInt32)((Headerstream1.Length + Headerstream2.Length) / chunkSize);

                foreach (LumiaV2UnlockBootViewModel.FlashingPayload payload in payloads)
                {
                    security.HashTableSize += payload.GetSecurityHeaderSize();
                }

                byte[]       HashTable = new byte[security.HashTableSize];
                BinaryWriter bw        = new BinaryWriter(new MemoryStream(HashTable));

                SHA256 crypto = SHA256.Create();
                for (int i = 0; i < Headerstream1.Length / chunkSize; i++)
                {
                    byte[] buffer = new byte[chunkSize];
                    Headerstream1.Read(buffer, 0, (Int32)chunkSize);
                    byte[] hash = crypto.ComputeHash(buffer);
                    bw.Write(hash, 0, hash.Length);
                }

                for (int i = 0; i < Headerstream2.Length / chunkSize; i++)
                {
                    byte[] buffer = new byte[chunkSize];
                    Headerstream2.Read(buffer, 0, (Int32)chunkSize);
                    byte[] hash = crypto.ComputeHash(buffer);
                    bw.Write(hash, 0, hash.Length);
                }

                foreach (LumiaV2UnlockBootViewModel.FlashingPayload payload in payloads)
                {
                    bw.Write(payload.ChunkHashes[0], 0, payload.ChunkHashes[0].Length);
                }

                bw.Close();

                //Logging.Log("Generating image catalog...");
                byte[] catalog = GenerateCatalogFile(HashTable);

                security.CatalogSize = (UInt32)catalog.Length;

                byte[] SecurityHeaderBuffer = new byte[0x20];

                ByteOperations.WriteUInt32(SecurityHeaderBuffer, 0, security.Size);
                ByteOperations.WriteAsciiString(SecurityHeaderBuffer, 0x04, security.Signature);
                ByteOperations.WriteUInt32(SecurityHeaderBuffer, 0x10, security.ChunkSizeInKb);
                ByteOperations.WriteUInt32(SecurityHeaderBuffer, 0x14, security.HashAlgorithm);
                ByteOperations.WriteUInt32(SecurityHeaderBuffer, 0x18, security.CatalogSize);
                ByteOperations.WriteUInt32(SecurityHeaderBuffer, 0x1C, security.HashTableSize);

                MemoryStream retstream = new MemoryStream();

                retstream.Write(SecurityHeaderBuffer, 0, 0x20);

                retstream.Write(catalog, 0, (Int32)security.CatalogSize);
                retstream.Write(HashTable, 0, (Int32)security.HashTableSize);

                RoundUpToChunks(retstream, chunkSize);

                Headerstream1.Seek(0, SeekOrigin.Begin);
                Headerstream2.Seek(0, SeekOrigin.Begin);

                byte[] buff = new byte[Headerstream1.Length];
                Headerstream1.Read(buff, 0, (Int32)Headerstream1.Length);

                Headerstream1.Close();

                retstream.Write(buff, 0, buff.Length);

                buff = new byte[Headerstream2.Length];
                Headerstream2.Read(buff, 0, (Int32)Headerstream2.Length);

                Headerstream2.Close();

                retstream.Write(buff, 0, buff.Length);

                // --------

                // Go back to the beginning
                retstream.Seek(0, SeekOrigin.Begin);

                byte[] FfuHeader = new byte[retstream.Length];
                await retstream.ReadAsync(FfuHeader, 0, (int)retstream.Length);

                retstream.Close();

                byte Options = 0;
                if (!Info.IsBootloaderSecure)
                {
                    Options = (byte)((FlashOptions)Options | FlashOptions.SkipSignatureCheck);
                }

                LogFile.Log("Flash in progress...", LogType.ConsoleOnly);
                SetWorkingStatus("Flashing...", null, (UInt64?)payloads.Count(), Status: WPinternalsStatus.Flashing);

                Model.SendFfuHeaderV1(FfuHeader, Options);

                UInt64 counter = 0;

                int    numberOfPayloadsToSendAtOnce = (int)Math.Round((double)Info.WriteBufferSize / chunkSize);
                byte[] payloadBuffer;

                for (int i = 0; i < payloads.Count(); i += numberOfPayloadsToSendAtOnce)
                {
                    if (i + numberOfPayloadsToSendAtOnce - 1 >= payloads.Count())
                    {
                        numberOfPayloadsToSendAtOnce = payloads.Count() - i;
                    }

                    payloadBuffer = new byte[numberOfPayloadsToSendAtOnce * chunkSizes];

                    string ProgressText = "Flashing resources";

                    for (int j = 0; j < numberOfPayloadsToSendAtOnce; j++)
                    {
                        LumiaV2UnlockBootViewModel.FlashingPayload payload = payloads[i + j];

                        UInt32    StreamIndex = payload.StreamIndexes.First();
                        FlashPart flashPart   = FlashParts[(int)StreamIndex];

                        if (flashPart.ProgressText != null)
                        {
                            ProgressText = flashPart.ProgressText;
                        }

                        Stream Stream = flashPart.Stream;
                        Stream.Seek(payload.StreamLocations.First(), SeekOrigin.Begin);
                        Stream.Read(payloadBuffer, j * chunkSizes, chunkSizes);
                        counter++;
                    }

                    Model.SendFfuPayloadV2(payloadBuffer, int.Parse((counter * 100 / (ulong)payloads.Count()).ToString()));
                    UpdateWorkingStatus(ProgressText, null, counter, WPinternalsStatus.Flashing);
                }

                Model.ResetPhone();

                await Notifier.WaitForRemoval();

                ExitSuccess("Flash succeeded!", null);
            }
            catch
            {
                throw new WPinternalsException("Custom flash failed");
            }
        }
    }
}
        // Potentially blocking UI. Threadsafe.
        internal override void EvaluateViewState()
        {
            if (!IsActive)
            {
                return;
            }

            if (State == MachineState.LumiaUnlockBoot)
            {
                return;
            }

            lock (EvaluateViewStateLockObject)
            {
                switch (PhoneNotifier.CurrentInterface)
                {
                case PhoneInterfaces.Lumia_Normal:
                case PhoneInterfaces.Lumia_Label:
                    IsSwitchingInterface = false;
                    if (DoUnlock)
                    {
                        // Display View to switch to Flash mode
                        LogFile.Log("Start unlock. Phone needs to switch to Flash-mode");
                        ActivateSubContext(new MessageViewModel("In order to start unlocking the bootloader, the phone needs to be switched to Flash-mode.", SwitchToFlashMode, Exit));
                    }
                    else
                    {
                        // Display View to switch to Flash mode
                        LogFile.Log("Start boot restore. Phone needs to switch to Flash-mode");
                        ActivateSubContext(new MessageViewModel("In order to start restoring the bootloader, the phone needs to be switched to Flash-mode.", SwitchToFlashMode, Exit));
                    }
                    break;

                case PhoneInterfaces.Lumia_Flash:
                    // Display View with device info and request for resources
                    // Click on "Continue" will start processing all resources
                    // Processing may fail with error message
                    // Or processing will succeed and user will again be asked to continue with Bricking-procedure (to switch to emergency mode)

                    // This code is not always invoked by OnArrival event.
                    // So this is not always in a thread from the threadpool.
                    // So we need to avoid UI blocking code here.

                    IsSwitchingInterface = false;

                    int TestPos = 0;

                    try     // In case phone reboots during the time that status is being read
                    {
                        // Some phones, like Lumia 928 verizon, do not support the Terminal interface!
                        // To read the RootKeyHash we use ReadParam("RRKH"), instead of GetTerminalResponse().RootKeyHash.
                        RootKeyHash = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadParam("RRKH");

                        TestPos = 1;

                        UefiSecurityStatusResponse SecurityStatus = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadSecurityStatus();
                        if (SecurityStatus != null)
                        {
                            IsBootLoaderUnlocked = (SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus);
                        }

                        TestPos = 2;

                        PhoneInfo Info = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo();
                        if (SecurityStatus == null)
                        {
                            IsBootLoaderUnlocked = !Info.IsBootloaderSecure;
                        }

                        if (RootKeyHash == null)
                        {
                            RootKeyHash = Info.RKH;

                            if (RootKeyHash == null)
                            {
                                RootKeyHash = new byte[32];
                            }
                        }

                        TestPos = 3;

                        if (Info.FlashAppProtocolVersionMajor < 2)
                        {
                            // This action is executed after the resources are selected by the user.
                            Action <string, string, string, string, string, string, bool> ReturnFunction = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) =>
                            {
                                // Stop responding to device arrival here, because all connections are handled by subfunctions, not here.
                                IsSwitchingInterface = true;
                                State = MachineState.LumiaUnlockBoot;

                                // This is a callback on the UI thread
                                // Resources are confirmed by user
                                this.FFUPath          = FFUPath;
                                this.LoadersPath      = LoadersPath;
                                this.SBL3Path         = SBL3Path;
                                this.SupportedFFUPath = SupportedFFUPath;
                                StorePaths();

                                LogFile.Log("Processing resources:");
                                LogFile.Log("FFU: " + FFUPath);
                                LogFile.Log("Loaders: " + LoadersPath);
                                if (SBL3Path == null)
                                {
                                    LogFile.Log("No SBL3 specified");
                                }
                                else
                                {
                                    LogFile.Log("SBL3: " + SBL3Path);
                                }

                                ActivateSubContext(new BusyViewModel("Processing resources..."));

                                if (DoUnlock)
                                {
                                    Task.Run(async() =>
                                    {
                                        await LumiaUnlockBootloaderViewModel.LumiaV1UnlockFirmware(PhoneNotifier, FFUPath, LoadersPath, SBL3Path, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
                                    });
                                }
                                else
                                {
                                    Task.Run(async() =>
                                    {
                                        await LumiaUnlockBootloaderViewModel.LumiaV1RelockFirmware(PhoneNotifier, FFUPath, LoadersPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
                                    });
                                }
                            };

                            if (DoUnlock)
                            {
                                ActivateSubContext(new BootUnlockResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, false));
                            }
                            else
                            {
                                ActivateSubContext(new BootRestoreResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, false));
                            }
                        }
                        else
                        {
                            bool AlreadyUnlocked = false;
                            if (DoUnlock)
                            {
                                NokiaFlashModel FlashModel = (NokiaFlashModel)PhoneNotifier.CurrentModel;
                                GPT             GPT        = FlashModel.ReadGPT();
                                if ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null))
                                {
                                    ExitMessage("Phone is already unlocked", null);
                                    return;
                                    //AlreadyUnlocked = true;
                                }
                            }

                            TestPos = 4;

                            // Stop responding to device arrival here, because all connections are handled by subfunctions, not here.
                            IsSwitchingInterface = true;

                            // This action is executed after the resources are selected by the user.
                            Action <string, string, string, string, string, string, bool> ReturnFunction = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) =>
                            {
                                IsSwitchingInterface = true;
                                State = MachineState.LumiaUnlockBoot;
                                if (DoUnlock)
                                {
                                    // This is a callback on the UI thread
                                    // Resources are confirmed by user
                                    this.ProfileFFUPath   = ProfileFFUPath;
                                    this.EDEPath          = EDEPath;
                                    this.SupportedFFUPath = SupportedFFUPath;
                                    StorePaths();

                                    if (DoFixBoot)
                                    {
                                        LogFile.Log("Fix Boot");
                                    }
                                    else
                                    {
                                        LogFile.Log("Unlock Bootloader");
                                    }

                                    LogFile.Log("Processing resources:");
                                    LogFile.Log("Profile FFU: " + ProfileFFUPath);
                                    LogFile.Log("EDE file: " + EDEPath);
                                    if (SupportedFFUPath != null)
                                    {
                                        LogFile.Log("Donor-FFU with supported OS version: " + SupportedFFUPath);
                                    }

                                    Task.Run(async() =>
                                    {
                                        if (DoFixBoot)
                                        {
                                            await LumiaV2UnlockBootViewModel.LumiaV2FixBoot(PhoneNotifier, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
                                        }
                                        else if (!AlreadyUnlocked)
                                        {
                                            await LumiaUnlockBootloaderViewModel.LumiaV2UnlockUEFI(PhoneNotifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
                                        }
                                        else
                                        {
                                            await LumiaUnlockBootloaderViewModel.LumiaV2UnlockUEFI(PhoneNotifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage, true);
                                        }
                                    });
                                }
                                else
                                {
                                    Task.Run(async() =>
                                    {
                                        FFU ProfileFFU = null;

                                        List <FFUEntry> FFUs = App.Config.FFURepository.Where(e => (Info.PlatformID.StartsWith(e.PlatformID, StringComparison.OrdinalIgnoreCase) && e.Exists())).ToList();
                                        if (FFUs.Count() > 0)
                                        {
                                            ProfileFFU = new FFU(FFUs[0].Path);
                                        }
                                        else
                                        {
                                            throw new WPinternalsException("Profile FFU missing", "No profile FFU has been found in the repository for your device. You can add a profile FFU within the download section of the tool or by using the command line.");
                                        }

                                        LogFile.Log("Profile FFU: " + ProfileFFU.Path);

                                        await LumiaUnlockBootloaderViewModel.LumiaV2RelockUEFI(PhoneNotifier, ProfileFFU.Path, true, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
                                    });
                                }
                            };

                            TestPos = 5;

                            if (DoUnlock)
                            {
                                ActivateSubContext(new BootUnlockResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, true, Info.PlatformID, Info.Type));
                            }
                            else
                            {
                                ActivateSubContext(new BootRestoreResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, true, Info.PlatformID, Info.Type));
                            }
                        }
                    }
                    catch (Exception Ex)
                    {
                        LogFile.LogException(Ex, LogType.FileAndConsole, TestPos.ToString());
                    }
                    break;

                case PhoneInterfaces.Qualcomm_Download:
                    IsSwitchingInterface = false;

                    // If resources are not confirmed yet, then display view with device info and request for resources.
                    QualcommDownload Download = new QualcommDownload((QualcommSerial)PhoneNotifier.CurrentModel);
                    byte[]           QualcommRootKeyHash;

                    try
                    {
                        QualcommRootKeyHash = Download.GetRKH();
                    }
                    catch (BadConnectionException)
                    {
                        // This is a Spec B device
                        break;
                    }

                    if (RootKeyHash == null)
                    {
                        RootKeyHash = QualcommRootKeyHash;
                    }
                    else if (!StructuralComparisons.StructuralEqualityComparer.Equals(RootKeyHash, QualcommRootKeyHash))
                    {
                        LogFile.Log("Error: Root Key Hash in Qualcomm Emergency mode does not match!");
                        ActivateSubContext(new MessageViewModel("Error: Root Key Hash in Qualcomm Emergency mode does not match!", Callback));
                        return;
                    }

                    // This action is executed after the user selected the resources.
                    Action <string, string, string, string, string, string, bool> ReturnFunctionD = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) =>
                    {
                        IsSwitchingInterface = true;
                        State = MachineState.LumiaUnlockBoot;
                        // This is a callback on the UI thread
                        // Resources are confirmed by user
                        this.FFUPath     = FFUPath;
                        this.LoadersPath = LoadersPath;
                        this.SBL3Path    = SBL3Path;
                        StorePaths();

                        LogFile.Log("Processing resources:");
                        LogFile.Log("FFU: " + FFUPath);
                        LogFile.Log("Loaders: " + LoadersPath);
                        if (SBL3Path == null)
                        {
                            LogFile.Log("No SBL3 specified");
                        }
                        else
                        {
                            LogFile.Log("SBL3: " + SBL3Path);
                        }

                        ActivateSubContext(new BusyViewModel("Processing resources..."));

                        if (DoUnlock)
                        {
                            Task.Run(async() =>
                            {
                                await LumiaUnlockBootloaderViewModel.LumiaV1UnlockFirmware(PhoneNotifier, FFUPath, LoadersPath, SBL3Path, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
                            });
                        }
                        else
                        {
                            Task.Run(async() =>
                            {
                                await LumiaUnlockBootloaderViewModel.LumiaV1RelockFirmware(PhoneNotifier, FFUPath, LoadersPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
                            });
                        }
                    };

                    if (DoUnlock)
                    {
                        ActivateSubContext(new BootUnlockResourcesViewModel("Qualcomm Emergency Download mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunctionD, Abort, IsBootLoaderUnlocked, false));
                    }
                    else
                    {
                        ActivateSubContext(new BootRestoreResourcesViewModel("Qualcomm Emergency Download mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunctionD, Abort, IsBootLoaderUnlocked, false));
                    }

                    break;

                case PhoneInterfaces.Qualcomm_Flash:
                {
                    IsSwitchingInterface = true;
                    State = MachineState.LumiaUnlockBoot;
                    ActivateSubContext(new BusyViewModel("Recovering resources..."));

                    LogFile.Log("Phone was unexpectedly detected in this mode while resources were not loaded yet.");
                    LogFile.Log("WPInternals tool probably crashed in previous session.");
                    LogFile.Log("Trying to recover resources from the registry.");

                    FFUPath          = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "FFUPath", null);
                    SupportedFFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SupportedFFUPath", null);
                    LoadersPath      = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "LoadersPath", null);
                    if (DoUnlock)
                    {
                        SBL3Path = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SBL3Path", null);
                    }
                    else
                    {
                        SBL3Path = null;
                    }

                    if (DoUnlock)
                    {
                        Task.Run(async() =>
                            {
                                await LumiaUnlockBootloaderViewModel.LumiaV1UnlockFirmware(PhoneNotifier, FFUPath, LoadersPath, SBL3Path, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
                            });
                    }
                    else
                    {
                        Task.Run(async() =>
                            {
                                await LumiaUnlockBootloaderViewModel.LumiaV1RelockFirmware(PhoneNotifier, FFUPath, LoadersPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage);
                            });
                    }
                    break;
                }

                default:
                    // Show View "Waiting for connection"
                    IsSwitchingInterface = false;
                    ActivateSubContext(null);
                    break;
                }
            }
        }
        private void StorePaths()
        {
            RegistryKey Key = Registry.CurrentUser.OpenSubKey(@"Software\WPInternals", true);

            if (Key == null)
            {
                Key = Registry.CurrentUser.CreateSubKey(@"Software\WPInternals");
            }

            if (FFUPath == null)
            {
                if (Key.GetValue("FFUPath") != null)
                {
                    Key.DeleteValue("FFUPath");
                }
            }
            else
            {
                Key.SetValue("FFUPath", FFUPath);
            }

            if (LoadersPath == null)
            {
                if (Key.GetValue("LoadersPath") != null)
                {
                    Key.DeleteValue("LoadersPath");
                }
            }
            else
            {
                Key.SetValue("LoadersPath", LoadersPath);
            }

            if (DoUnlock)
            {
                if (SBL3Path == null)
                {
                    if (Key.GetValue("SBL3Path") != null)
                    {
                        Key.DeleteValue("SBL3Path");
                    }
                }
                else
                {
                    Key.SetValue("SBL3Path", SBL3Path);
                }
            }

            if (ProfileFFUPath == null)
            {
                if (Key.GetValue("ProfileFFUPath") != null)
                {
                    Key.DeleteValue("ProfileFFUPath");
                }
            }
            else
            {
                Key.SetValue("ProfileFFUPath", ProfileFFUPath);

                App.Config.AddFfuToRepository(ProfileFFUPath);
            }

            if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Qualcomm_Download && PhoneNotifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash)
            {
                NokiaFlashModel Model = (NokiaFlashModel)PhoneNotifier.CurrentModel;
                PhoneInfo       Info  = Model.ReadPhoneInfo();

                if (EDEPath == null)
                {
                    if (Key.GetValue("EDEPath") != null)
                    {
                        Key.DeleteValue("EDEPath");
                    }
                }
                else
                {
                    Key.SetValue("EDEPath", EDEPath);

                    App.Config.AddEmergencyToRepository(Info.Type, EDEPath, null);
                }
            }

            if (SupportedFFUPath != null)
            {
                Key.SetValue("SupportedFFUPath", SupportedFFUPath);

                App.Config.AddFfuToRepository(SupportedFFUPath);
            }

            Key.Close();
        }