From 605b19aedea1d4c9b310168049814822263a82de Mon Sep 17 00:00:00 2001 From: Gustave Monce Date: Sat, 12 Oct 2024 21:22:07 +0200 Subject: [PATCH] Fixes --- .../UEFI/BootMgr/LumiaBootManagerAppModel.cs | 2 +- .../ViewModels/LumiaUnlockBootViewModel.cs | 33 ++- WPinternals/ViewModels/SwitchModeViewModel.cs | 218 +++++++++++++++++- 3 files changed, 243 insertions(+), 10 deletions(-) diff --git a/WPinternals/Models/Lumia/UEFI/BootMgr/LumiaBootManagerAppModel.cs b/WPinternals/Models/Lumia/UEFI/BootMgr/LumiaBootManagerAppModel.cs index 28ce865..a3c6949 100644 --- a/WPinternals/Models/Lumia/UEFI/BootMgr/LumiaBootManagerAppModel.cs +++ b/WPinternals/Models/Lumia/UEFI/BootMgr/LumiaBootManagerAppModel.cs @@ -374,7 +374,7 @@ internal void ResetPhoneToFlashMode() if (ModernFlashApp) { - DisableRebootTimeOut(); + //DisableRebootTimeOut(); info.App = FlashAppType.FlashApp; diff --git a/WPinternals/ViewModels/LumiaUnlockBootViewModel.cs b/WPinternals/ViewModels/LumiaUnlockBootViewModel.cs index 5188103..3bf4a43 100644 --- a/WPinternals/ViewModels/LumiaUnlockBootViewModel.cs +++ b/WPinternals/ViewModels/LumiaUnlockBootViewModel.cs @@ -40,7 +40,8 @@ namespace WPinternals internal enum MachineState { Default, - LumiaUnlockBoot + LumiaUnlockBoot, + LumiaGetGPT }; internal class LumiaUnlockBootViewModel : ContextViewModel @@ -95,12 +96,7 @@ internal override void EvaluateViewState() return; } - if (State == MachineState.LumiaUnlockBoot) - { - return; - } - - if (IsSwitchingInterface) + if ((State == MachineState.LumiaGetGPT) || (State == MachineState.LumiaUnlockBoot)) { return; } @@ -261,7 +257,17 @@ void ReturnFunction(string FFUPath, string LoadersPath, string SBL3Path, string { bool AlreadyUnlocked = false; LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; + + MachineState OriginalState = State; + State = MachineState.LumiaGetGPT; // Stop handling arrival notifications in this screen + bool OriginalIsSwitchingInterface = IsSwitchingInterface; + IsSwitchingInterface = true; // Stop handling arrival notifications in MainViewModel + GPT GPT = await LumiaUnlockBootloaderViewModel.ReadGPTFromFlashOrBootMgr(PhoneNotifier); + + IsSwitchingInterface = OriginalIsSwitchingInterface; + State = OriginalState; + if ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null)) { AlreadyUnlocked = true; @@ -305,6 +311,11 @@ void ReturnFunction(string FFUPath, string LoadersPath, string SBL3Path, string Task.Run(async () => { + MachineState OriginalState = State; + State = MachineState.LumiaGetGPT; // Stop handling arrival notifications in this screen + bool OriginalIsSwitchingInterface = IsSwitchingInterface; + IsSwitchingInterface = true; // Stop handling arrival notifications in MainViewModel + bool ModernFlashApp = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2; if (ModernFlashApp) { @@ -350,6 +361,9 @@ void ReturnFunction(string FFUPath, string LoadersPath, string SBL3Path, string throw new WPinternalsException("Unexpected Mode"); } + IsSwitchingInterface = OriginalIsSwitchingInterface; + State = OriginalState; + if (DoUnlock) { ActivateSubContext(new BootUnlockResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, true, FlashInfo.PlatformID, PhoneInfo.Type)); @@ -569,8 +583,10 @@ private async void StorePaths() LumiaFlashAppModel Model = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; LumiaFlashAppPhoneInfo FlashInfo = Model.ReadPhoneInfo(); + MachineState OriginalState = State; + State = MachineState.LumiaGetGPT; // Stop handling arrival notifications in this screen bool OriginalIsSwitchingInterface = IsSwitchingInterface; - IsSwitchingInterface = true; + IsSwitchingInterface = true; // Stop handling arrival notifications in MainViewModel bool ModernFlashApp = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2; if (ModernFlashApp) @@ -616,6 +632,7 @@ private async void StorePaths() } IsSwitchingInterface = OriginalIsSwitchingInterface; + State = OriginalState; if (EDEPath == null) { diff --git a/WPinternals/ViewModels/SwitchModeViewModel.cs b/WPinternals/ViewModels/SwitchModeViewModel.cs index 1fca35f..ed91e62 100644 --- a/WPinternals/ViewModels/SwitchModeViewModel.cs +++ b/WPinternals/ViewModels/SwitchModeViewModel.cs @@ -451,7 +451,7 @@ internal void StartSwitch() LogFile.Log("Rebooting phone to Flash mode", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_MassStorage: - SwitchFromFlashToMassStorageMode(); + SwitchFromBootloaderToMassStorageMode(); break; case PhoneInterfaces.Qualcomm_Download: PhoneNotifier.NewDeviceArrived += NewDeviceArrived; @@ -1139,6 +1139,222 @@ private async Task ReadGPTFromFlashOrBootMgr() return await LumiaUnlockBootloaderViewModel.ReadGPTFromFlashOrBootMgr(PhoneNotifier); } + private void SwitchFromBootloaderToMassStorageMode(bool Continuation = false) + { + string ProgressText = Continuation ? "And now rebooting phone to Mass Storage mode..." : "Rebooting phone to Mass Storage mode..."; + + LumiaBootManagerAppModel FlashModel = (LumiaBootManagerAppModel)PhoneNotifier.CurrentModel; + LumiaBootManagerPhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: false); + + bool IsModernFlashApp = Info.BootManagerProtocolVersionMajor >= 2; + + MassStorageWarning = null; + if (!IsModernFlashApp) + { + 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."; + } + } + + new Thread(async () => + { + bool IsUnlockedNew = false; + + GPT GPT = null; + + if (IsModernFlashApp) + { + GPT = await ReadGPTFromFlashOrBootMgr(); + IsUnlockedNew = (GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null) || (GPT.GetPartition("BACKUP_BS_NV") != null); + } + + if (CurrentMode == PhoneInterfaces.Lumia_Bootloader) + { + ((LumiaBootManagerAppModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext(); + + if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + { + await PhoneNotifier.WaitForArrival(); + } + } + + if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + { + throw new WPinternalsException("Unexpected Mode"); + } + + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; + LumiaFlashAppPhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: false); + + bool IsOriginalEngineeringLumia = !Info.IsBootloaderSecure && !IsUnlockedNew; + + if (!IsModernFlashApp || IsOriginalEngineeringLumia) + { + IsSwitchingInterface = true; + try + { + ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ResetToMassStorageMode(); + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + + ModeSwitchProgressWrapper(ProgressText, MassStorageWarning); + LogFile.Log("Rebooting phone to Mass Storage mode"); + } + catch (NotSupportedException) // This means fail: NOKU (unknown command) + { + try + { + ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).WriteParam("UBF", [(byte)'M']); + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + + ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ResetPhone(); + ModeSwitchProgressWrapper(ProgressText, MassStorageWarning); + LogFile.Log("Rebooting phone to Mass Storage mode"); + } + catch (NotSupportedException) + { + ModeSwitchErrorWrapper("Failed to switch to Mass Storage mode"); + IsSwitchingInterface = false; + } + } + } + else if (IsUnlockedNew) + { + 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); + System.Reflection.Assembly 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 (Stream stream = assembly.GetManifestResourceStream("WPinternals.SBMSM")) + { + using DecompressedStream dec = new(stream); + using MemoryStream SB = new(); // 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. + + 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 Parts = []; + FlashPart Part = new() + { + StartSector = (uint)Target.FirstSector, + 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"); + } + else + { + ModeSwitchErrorWrapper("Bootloader was not unlocked. First unlock bootloader before you try to switch to Mass Storage Mode."); + } + }).Start(); + } + private void SwitchFromFlashToMassStorageMode(bool Continuation = false) { string ProgressText = Continuation ? "And now rebooting phone to Mass Storage mode..." : "Rebooting phone to Mass Storage mode...";