diff --git a/WPinternals/Models/NokiaFlashModel.cs b/WPinternals/Models/NokiaFlashModel.cs index ab186be..7548912 100644 --- a/WPinternals/Models/NokiaFlashModel.cs +++ b/WPinternals/Models/NokiaFlashModel.cs @@ -28,6 +28,10 @@ namespace WPinternals internal class NokiaFlashModel : NokiaPhoneModel { + private string _devicePath; + + private readonly PhoneInfo Info = new(); + internal event InterfaceChangedHandler InterfaceChanged = delegate { }; // @@ -48,10 +52,16 @@ namespace WPinternals /* NOKXCE */ private const string EchoSignature = $"{CommonExtendedMessageSignature}E"; - public NokiaFlashModel(string DevicePath) : base(DevicePath) { } + public NokiaFlashModel(string DevicePath) : base(DevicePath) + { + _devicePath = DevicePath; + } internal void SwitchToBootManagerContext(bool DisableTimeOut = true) { + PhoneInfo info = ReadPhoneInfoCommon(); + bool ModernFlashApp = info.FlashAppProtocolVersionMajor >= 2; + byte[] Request = new byte[7]; ByteOperations.WriteAsciiString(Request, 0, SwitchModeSignature + "B"); byte[] Response = ExecuteRawMethod(Request); @@ -65,10 +75,28 @@ namespace WPinternals { throw new NotSupportedException("SwitchToBootManagerContext: Error 0x" + Error.ToString("X4")); } + + if (ModernFlashApp) + { + DisableRebootTimeOut(); + + Info.App = FlashAppType.BootManager; + + // If current Info class was retrieved while in BootMgr mode, then we need to invalidate this data, because it is incomplete. + if (Info.PlatformID == null) + { + Info.State = PhoneInfoState.Empty; + } + + InterfaceChanged(PhoneInterfaces.Lumia_Bootloader, _devicePath); + } } internal void SwitchToPhoneInfoAppContext() { + PhoneInfo info = ReadPhoneInfoCommon(); + bool ModernFlashApp = info.FlashAppProtocolVersionMajor >= 2; + byte[] Request = new byte[7]; ByteOperations.WriteAsciiString(Request, 0, SwitchModeSignature + "P"); byte[] Response = ExecuteRawMethod(Request); @@ -82,6 +110,21 @@ namespace WPinternals { throw new NotSupportedException("SwitchToPhoneInfoAppContext: Error 0x" + Error.ToString("X4")); } + + if (ModernFlashApp) + { + DisableRebootTimeOut(); + + Info.App = FlashAppType.PhoneInfoApp; + + // If current Info class was retrieved while in BootMgr mode, then we need to invalidate this data, because it is incomplete. + if (Info.PlatformID == null) + { + Info.State = PhoneInfoState.Empty; + } + + InterfaceChanged(PhoneInterfaces.Lumia_PhoneInfo, _devicePath); + } } internal void SwitchToMmosContext() @@ -97,6 +140,9 @@ namespace WPinternals internal void SwitchToFlashAppContext() { + PhoneInfo info = ReadPhoneInfoCommon(); + bool ModernFlashApp = info.FlashAppProtocolVersionMajor >= 2; + byte[] Request = new byte[7]; ByteOperations.WriteAsciiString(Request, 0, SwitchModeSignature + "F"); // This will stop charging the phone byte[] Response = ExecuteRawMethod(Request); @@ -110,6 +156,21 @@ namespace WPinternals { throw new NotSupportedException("SwitchToFlashAppContext: Error 0x" + Error.ToString("X4")); } + + if (ModernFlashApp) + { + DisableRebootTimeOut(); + + Info.App = FlashAppType.FlashApp; + + // If current Info class was retrieved while in BootMgr mode, then we need to invalidate this data, because it is incomplete. + if (Info.PlatformID == null) + { + Info.State = PhoneInfoState.Empty; + } + + InterfaceChanged(PhoneInterfaces.Lumia_Flash, _devicePath); + } } @@ -136,6 +197,110 @@ namespace WPinternals return (FlashAppType)Response[5]; } + + internal PhoneInfo ReadPhoneInfoCommon() + { + // NOKH = Get Phone Info (IMEI and info from Product.dat) - Not available on some phones, like Lumia 640. + // NOKV = Info Query + + bool PhoneInfoLogged = Info.State != PhoneInfoState.Empty; + PhoneInfo Result = Info; + + if (Result.State == PhoneInfoState.Empty) + { + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, InfoQuerySignature); + byte[] Response = ExecuteRawMethod(Request); + if ((Response != null) && (ByteOperations.ReadAsciiString(Response, 0, 4) != "NOKU")) + { + Result.App = (FlashAppType)Response[5]; + + switch (Result.App) + { + case FlashAppType.BootManager: + Result.BootManagerProtocolVersionMajor = Response[6]; + Result.BootManagerProtocolVersionMinor = Response[7]; + Result.BootManagerVersionMajor = Response[8]; + Result.BootManagerVersionMinor = Response[9]; + break; + case FlashAppType.FlashApp: + Result.FlashAppProtocolVersionMajor = Response[6]; + Result.FlashAppProtocolVersionMinor = Response[7]; + Result.FlashAppVersionMajor = Response[8]; + Result.FlashAppVersionMinor = Response[9]; + break; + case FlashAppType.PhoneInfoApp: + Result.PhoneInfoAppProtocolVersionMajor = Response[6]; + Result.PhoneInfoAppProtocolVersionMinor = Response[7]; + Result.PhoneInfoAppVersionMajor = Response[8]; + Result.PhoneInfoAppVersionMinor = Response[9]; + break; + } + + byte SubblockCount = Response[10]; + int SubblockOffset = 11; + + for (int i = 0; i < SubblockCount; i++) + { + byte SubblockID = Response[SubblockOffset + 0x00]; + UInt16 SubblockLength = BigEndian.ToUInt16(Response, SubblockOffset + 0x01); + int SubblockPayloadOffset = SubblockOffset + 3; + byte SubblockVersion; + switch (SubblockID) + { + case 0x01: + Result.TransferSize = BigEndian.ToUInt32(Response, SubblockPayloadOffset); + break; + case 0x02: + Result.WriteBufferSize = BigEndian.ToUInt32(Response, SubblockPayloadOffset); + break; + case 0x03: + Result.EmmcSizeInSectors = BigEndian.ToUInt32(Response, SubblockPayloadOffset); + break; + case 0x05: + Result.PlatformID = ByteOperations.ReadAsciiString(Response, (uint)SubblockPayloadOffset, SubblockLength).Trim([' ', '\0']); + break; + case 0x0D: + Result.AsyncSupport = Response[SubblockPayloadOffset + 1] == 1; + break; + case 0x0F: + SubblockVersion = Response[SubblockPayloadOffset]; // 0x03 + Result.PlatformSecureBootEnabled = Response[SubblockPayloadOffset + 0x01] == 0x01; + Result.SecureFfuEnabled = Response[SubblockPayloadOffset + 0x02] == 0x01; + Result.JtagDisabled = Response[SubblockPayloadOffset + 0x03] == 0x01; + Result.RdcPresent = Response[SubblockPayloadOffset + 0x04] == 0x01; + Result.Authenticated = (Response[SubblockPayloadOffset + 0x05] == 0x01) || (Response[SubblockPayloadOffset + 0x05] == 0x02); + Result.UefiSecureBootEnabled = Response[SubblockPayloadOffset + 0x06] == 0x01; + Result.SecondaryHardwareKeyPresent = Response[SubblockPayloadOffset + 0x07] == 0x01; + break; + case 0x10: + SubblockVersion = Response[SubblockPayloadOffset]; // 0x01 + Result.SecureFfuSupportedProtocolMask = BigEndian.ToUInt16(Response, SubblockPayloadOffset + 0x01); + break; + case 0x1F: + Result.MmosOverUsbSupported = Response[SubblockPayloadOffset] == 1; + break; + case 0x20: + // CRC header info + break; + } + SubblockOffset += SubblockLength + 3; + } + } + + Result.State = PhoneInfoState.Basic; + } + + Result.IsBootloaderSecure = !(Info.Authenticated || Info.RdcPresent || !Info.SecureFfuEnabled); + + if (!PhoneInfoLogged) + { + Result.Log(LogType.FileOnly); + } + + return Result; + } + public void DisableRebootTimeOut() { byte[] Request = new byte[4]; diff --git a/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs b/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs index faf7785..48ebd8f 100644 --- a/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs +++ b/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs @@ -71,7 +71,7 @@ namespace WPinternals EffectivePhoneInfoSecurityStatus = Info.UefiSecureBootEnabled; - LogFile.Log("Effective PhoneInfo Security Status: " + EffectivePhoneInfoSecurityStatus.ToString()); + LogFile.Log("Effective Bootloader Security Status: " + EffectivePhoneInfoSecurityStatus.ToString()); } catch { diff --git a/WPinternals/ViewModels/SwitchModeViewModel.cs b/WPinternals/ViewModels/SwitchModeViewModel.cs index 91f190c..54aeead 100644 --- a/WPinternals/ViewModels/SwitchModeViewModel.cs +++ b/WPinternals/ViewModels/SwitchModeViewModel.cs @@ -318,18 +318,19 @@ namespace WPinternals }).Start(); break; case PhoneInterfaces.Lumia_Normal: - ((LumiaFlashAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ((LumiaFlashAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null); LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_Bootloader: - ((LumiaFlashAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ((LumiaFlashAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); ModeSwitchProgressWrapper("Rebooting phone to Bootloader mode...", null); LogFile.Log("Rebooting phone to Bootloader mode", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_PhoneInfo: + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModernFlashApp = ((LumiaFlashAppModel)CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2; if (ModernFlashApp) { @@ -339,7 +340,6 @@ namespace WPinternals { ((LumiaFlashAppModel)CurrentModel).SwitchToPhoneInfoAppContextLegacy(); } - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Phone Info mode...", null); LogFile.Log("Rebooting phone to Phone Info mode", LogType.FileAndConsole); break; @@ -347,9 +347,9 @@ namespace WPinternals SwitchFromFlashToLabelMode(); break; case PhoneInterfaces.Lumia_Flash: // attempt to boot from limited flash to full flash + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; byte[] RebootToFlashCommand = [0x4E, 0x4F, 0x4B, 0x53]; // NOKS ((LumiaFlashAppModel)CurrentModel).ExecuteRawVoidMethod(RebootToFlashCommand); - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Flash mode...", null); LogFile.Log("Rebooting phone to Flash mode", LogType.FileAndConsole); break; @@ -357,6 +357,7 @@ namespace WPinternals SwitchFromFlashToMassStorageMode(); break; case PhoneInterfaces.Qualcomm_Download: + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; byte[] RebootToQualcommDownloadCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x43, 0x42, 0x45]; // NOKXCBE // TODO RebootCommandResult = ((LumiaFlashAppModel)CurrentModel).ExecuteRawMethod(RebootToQualcommDownloadCommand); if (RebootCommandResult?.Length == 4) // This means fail: NOKU (unknow command) @@ -366,7 +367,6 @@ namespace WPinternals } else { - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Qualcomm Download mode...", null); LogFile.Log("Rebooting phone to Qualcomm Download mode", LogType.FileAndConsole); } @@ -380,28 +380,28 @@ namespace WPinternals switch (TargetMode) { case PhoneInterfaces.Lumia_Normal: - ((LumiaPhoneInfoAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ((LumiaPhoneInfoAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null); LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_Bootloader: + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModernFlashApp = ((LumiaPhoneInfoAppModel)CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2; if (ModernFlashApp) { ((LumiaPhoneInfoAppModel)CurrentModel).SwitchToBootManagerContext(); } - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Bootloader mode...", null); LogFile.Log("Rebooting phone to Bootloader mode", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_PhoneInfo: // attempt to boot from limited phone info to full phone info + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModernFlashApp = ((LumiaPhoneInfoAppModel)CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2; if (ModernFlashApp) { ((LumiaPhoneInfoAppModel)CurrentModel).SwitchToPhoneInfoAppContext(); } - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Phone Info mode...", null); LogFile.Log("Rebooting phone to Phone Info mode", LogType.FileAndConsole); break; @@ -409,6 +409,7 @@ namespace WPinternals SwitchFromPhoneInfoToLabelMode(); break; case PhoneInterfaces.Lumia_Flash: + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModernFlashApp = ((LumiaPhoneInfoAppModel)CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2; if (ModernFlashApp) { @@ -418,7 +419,6 @@ namespace WPinternals { ((LumiaPhoneInfoAppModel)CurrentModel).ContinueBoot(); } - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Flash mode...", null); LogFile.Log("Rebooting phone to Flash mode", LogType.FileAndConsole); break; @@ -426,6 +426,7 @@ namespace WPinternals SwitchFromPhoneInfoToMassStorageMode(); break; case PhoneInterfaces.Qualcomm_Download: + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; byte[] RebootToQualcommDownloadCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x43, 0x42, 0x45]; // NOKXCBE // TODO RebootCommandResult = ((LumiaPhoneInfoAppModel)CurrentModel).ExecuteRawMethod(RebootToQualcommDownloadCommand); if (RebootCommandResult?.Length == 4) // This means fail: NOKU (unknow command) @@ -435,7 +436,6 @@ namespace WPinternals } else { - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Qualcomm Download mode...", null); LogFile.Log("Rebooting phone to Qualcomm Download mode", LogType.FileAndConsole); } @@ -459,14 +459,14 @@ namespace WPinternals }).Start(); break; case PhoneInterfaces.Lumia_Normal: - ((LumiaBootManagerAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ((LumiaBootManagerAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null); LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_Bootloader: - ((LumiaBootManagerAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ((LumiaBootManagerAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); ModeSwitchProgressWrapper("Rebooting phone to Bootloader mode...", null); LogFile.Log("Rebooting phone to Bootloader mode", LogType.FileAndConsole); break; @@ -474,9 +474,9 @@ namespace WPinternals SwitchFromFlashToLabelMode(); break; case PhoneInterfaces.Lumia_Flash: // attempt to boot from limited flash to full flash + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; byte[] RebootToFlashCommand = [0x4E, 0x4F, 0x4B, 0x53]; // NOKS ((LumiaBootManagerAppModel)CurrentModel).ExecuteRawVoidMethod(RebootToFlashCommand); - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Flash mode...", null); LogFile.Log("Rebooting phone to Flash mode", LogType.FileAndConsole); break; @@ -484,6 +484,7 @@ namespace WPinternals SwitchFromFlashToMassStorageMode(); break; case PhoneInterfaces.Qualcomm_Download: + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; byte[] RebootToQualcommDownloadCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x43, 0x42, 0x45]; // NOKXCBE // TODO RebootCommandResult = ((LumiaBootManagerAppModel)CurrentModel).ExecuteRawMethod(RebootToQualcommDownloadCommand); if (RebootCommandResult?.Length == 4) // This means fail: NOKU (unknow command) @@ -493,7 +494,6 @@ namespace WPinternals } else { - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Qualcomm Download mode...", null); LogFile.Log("Rebooting phone to Qualcomm Download mode", LogType.FileAndConsole); } @@ -507,26 +507,26 @@ namespace WPinternals switch (TargetMode) { case PhoneInterfaces.Lumia_Normal: - ((MassStorage)CurrentModel).Reboot(); PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ((MassStorage)CurrentModel).Reboot(); ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null); LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_Label: - ((MassStorage)CurrentModel).Reboot(); PhoneNotifier.NewDeviceArrived += NewDeviceArrivedFromMassStorageMode; + ((MassStorage)CurrentModel).Reboot(); ModeSwitchProgressWrapper("Rebooting phone to Label mode...", null); LogFile.Log("Rebooting phone to Label mode...", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_Flash: - ((MassStorage)CurrentModel).Reboot(); PhoneNotifier.NewDeviceArrived += NewDeviceArrivedFromMassStorageMode; + ((MassStorage)CurrentModel).Reboot(); ModeSwitchProgressWrapper("Rebooting phone to Flash mode...", null); LogFile.Log("Rebooting phone to Flash mode...", LogType.FileAndConsole); break; case null: - ((MassStorage)CurrentModel).Reboot(); PhoneNotifier.NewDeviceArrived += NewDeviceArrivedFromMassStorageMode; + ((MassStorage)CurrentModel).Reboot(); ModeSwitchProgressWrapper("First rebooting phone to Flash mode...", null); LogFile.Log("First rebooting phone to Bootloader mode...", LogType.FileAndConsole); break; @@ -782,13 +782,14 @@ namespace WPinternals } else { + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + byte[] BootModeFlagCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00]; // NOKFW UBF byte[] RebootCommand = [0x4E, 0x4F, 0x4B, 0x52]; // NOKR BootModeFlagCommand[0x0F] = 0x59; ((LumiaFlashAppModel)CurrentModel).ExecuteRawMethod(BootModeFlagCommand); ((LumiaFlashAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Label mode", null); LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole); } @@ -860,13 +861,14 @@ namespace WPinternals } else { + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + byte[] BootModeFlagCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00]; // NOKFW UBF byte[] RebootCommand = [0x4E, 0x4F, 0x4B, 0x52]; // NOKR BootModeFlagCommand[0x0F] = 0x59; ((LumiaFlashAppModel)CurrentModel).ExecuteRawMethod(BootModeFlagCommand); ((LumiaFlashAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Label mode", null); LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole); } @@ -921,6 +923,7 @@ namespace WPinternals if (ResultCode == 0) { PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); ModeSwitchProgressWrapper(ProgressText, MassStorageWarning); LogFile.Log("Rebooting phone to Mass Storage mode"); @@ -934,6 +937,7 @@ namespace WPinternals else { PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ModeSwitchProgressWrapper(ProgressText, MassStorageWarning); LogFile.Log("Rebooting phone to Mass Storage mode"); } @@ -1127,6 +1131,7 @@ namespace WPinternals if (ResultCode == 0) { PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); ModeSwitchProgressWrapper(ProgressText, MassStorageWarning); LogFile.Log("Rebooting phone to Mass Storage mode"); @@ -1140,6 +1145,7 @@ namespace WPinternals else { PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ModeSwitchProgressWrapper(ProgressText, MassStorageWarning); LogFile.Log("Rebooting phone to Mass Storage mode"); }