diff --git a/WPinternals/CommandLine.cs b/WPinternals/CommandLine.cs index 6184b6a..c932dd6 100644 --- a/WPinternals/CommandLine.cs +++ b/WPinternals/CommandLine.cs @@ -83,7 +83,8 @@ namespace WPinternals { FFU FFU = null; PhoneNotifierViewModel Notifier; - NokiaFlashModel FlashModel; + LumiaFlashAppModel FlashModel; + LumiaBootManagerAppModel BootMgrModel; NokiaPhoneModel NormalModel; PhoneInfo Info; string ProductType; @@ -182,23 +183,14 @@ namespace WPinternals Notifier = new PhoneNotifierViewModel(); UIContext.Send(s => Notifier.Start(), null); - FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Bootloader); // This also works for Bootloader Spec A + BootMgrModel = (LumiaBootManagerAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Bootloader); // This also works for Bootloader Spec A - GPT GPT = FlashModel.ReadGPT(); // May throw NotSupportedException + GPT GPT = BootMgrModel.ReadGPT(); // May throw NotSupportedException foreach (Partition Partition in GPT.Partitions) { LogFile.Log(Partition.Name.PadRight(20) + "0x" + Partition.FirstSector.ToString("X8") + " - 0x" + Partition.LastSector.ToString("X8") + " " + Partition.Volume, LogType.ConsoleOnly); } - if (FlashModel.ReadPhoneInfo(false).FlashAppProtocolVersionMajor >= 2) - { - FlashModel.SwitchToFlashAppContext(); - } - else - { - await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); - } - Notifier.Stop(); } catch (Exception Ex) @@ -221,15 +213,14 @@ namespace WPinternals { Notifier = new PhoneNotifierViewModel(); UIContext.Send(s => Notifier.Start(), null); - FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); - GPT GPT = FlashModel.ReadGPT(); // May throw NotSupportedException + BootMgrModel = (LumiaBootManagerAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Bootloader); + GPT GPT = BootMgrModel.ReadGPT(); // May throw NotSupportedException string DirPath = Path.GetDirectoryName(args[2]); if (!string.IsNullOrEmpty(DirPath) && !Directory.Exists(DirPath)) { Directory.CreateDirectory(DirPath); } GPT.WritePartitions(args[2]); - FlashModel.SwitchToFlashAppContext(); Notifier.Stop(); } catch (Exception Ex) @@ -281,14 +272,13 @@ namespace WPinternals { Notifier = new PhoneNotifierViewModel(); UIContext.Send(s => Notifier.Start(), null); - FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); - byte[] GptChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); + BootMgrModel = (LumiaBootManagerAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Bootloader); + byte[] GptChunk = BootMgrModel.GetGptChunk(0x20000); GPT GPT = new(GptChunk); string Xml = File.ReadAllText(args[2]); GPT.MergePartitions(Xml, false); GPT.Rebuild(); await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(Notifier, null, false, false, 0, GptChunk, true, true); - FlashModel.SwitchToFlashAppContext(); Notifier.Stop(); } catch (Exception Ex) @@ -552,7 +542,7 @@ namespace WPinternals try { UIContext.Send(s => Notifier.Start(), null); - FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); Info = FlashModel.ReadPhoneInfo(); Info.Log(LogType.ConsoleOnly); @@ -701,7 +691,7 @@ namespace WPinternals LogFile.Log("Command: Show phone info", LogType.FileAndConsole); Notifier = new PhoneNotifierViewModel(); UIContext.Send(s => Notifier.Start(), null); - FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); Info = FlashModel.ReadPhoneInfo(); Info.Log(LogType.ConsoleOnly); Notifier.Stop(); @@ -713,7 +703,7 @@ namespace WPinternals LogFile.Log("Command: Unlock Bootloader", LogType.FileAndConsole); Notifier = new PhoneNotifierViewModel(); UIContext.Send(s => Notifier.Start(), null); - FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); Info = FlashModel.ReadPhoneInfo(); Info.Log(LogType.ConsoleOnly); @@ -786,7 +776,7 @@ namespace WPinternals LogFile.Log("Custom ROM: " + CustomRomPath, LogType.FileAndConsole); Notifier = new PhoneNotifierViewModel(); UIContext.Send(s => Notifier.Start(), null); - FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); Info = FlashModel.ReadPhoneInfo(); Info.Log(LogType.ConsoleOnly); LogFile.Log("Preparing to flash Custom ROM", LogType.FileAndConsole); @@ -817,7 +807,7 @@ namespace WPinternals LogFile.Log("FFU file: " + FFUPath, LogType.FileAndConsole); Notifier = new PhoneNotifierViewModel(); UIContext.Send(s => Notifier.Start(), null); - FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); Info = FlashModel.ReadPhoneInfo(); Info.Log(LogType.ConsoleOnly); LogFile.Log("Flashing FFU...", LogType.FileAndConsole); @@ -1283,9 +1273,15 @@ namespace WPinternals NormalModel = (NokiaPhoneModel)Notifier.CurrentModel; ProductCode = NormalModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode"); } - else if ((Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) || (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash)) + else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + BootMgrModel = (LumiaBootManagerAppModel)Notifier.CurrentModel; + Info = BootMgrModel.ReadPhoneInfo(); + ProductCode = Info.ProductCode; + } + else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash) + { + FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; Info = FlashModel.ReadPhoneInfo(); ProductCode = Info.ProductCode; } @@ -1448,9 +1444,15 @@ namespace WPinternals ProductType = ProductType.Substring(0, ProductType.IndexOf('_')); } } - else if ((Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) || (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash)) + else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + BootMgrModel = (LumiaBootManagerAppModel)Notifier.CurrentModel; + Info = BootMgrModel.ReadPhoneInfo(); + ProductType = Info.Type; + } + else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash) + { + FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; Info = FlashModel.ReadPhoneInfo(); ProductType = Info.Type; } @@ -1559,9 +1561,15 @@ namespace WPinternals NormalModel = (NokiaPhoneModel)Notifier.CurrentModel; ProductCode = NormalModel.ExecuteJsonMethodAsString("ReadProductCode", "ProductCode"); } - else if ((Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) || (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash)) + else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + BootMgrModel = (LumiaBootManagerAppModel)Notifier.CurrentModel; + Info = BootMgrModel.ReadPhoneInfo(); + ProductCode = Info.ProductCode; + } + else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash) + { + FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; Info = FlashModel.ReadPhoneInfo(); ProductCode = Info.ProductCode; } diff --git a/WPinternals/Models/FlashAppType.cs b/WPinternals/Models/FlashAppType.cs new file mode 100644 index 0000000..5ed09a7 --- /dev/null +++ b/WPinternals/Models/FlashAppType.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +namespace WPinternals +{ + internal enum FlashAppType + { + BootManager = 1, + FlashApp = 2, + PhoneInfoApp = 3 + }; +} diff --git a/WPinternals/Models/FlashVersion.cs b/WPinternals/Models/FlashVersion.cs new file mode 100644 index 0000000..58494e8 --- /dev/null +++ b/WPinternals/Models/FlashVersion.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +namespace WPinternals +{ + internal class FlashVersion + { + public int ApplicationMajor; + public int ApplicationMinor; + public int ProtocolMajor; + public int ProtocolMinor; + } +} diff --git a/WPinternals/Models/LumiaBootManagerAppModel.cs b/WPinternals/Models/LumiaBootManagerAppModel.cs new file mode 100644 index 0000000..bd17075 --- /dev/null +++ b/WPinternals/Models/LumiaBootManagerAppModel.cs @@ -0,0 +1,392 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.IO; +using System.Linq; + +namespace WPinternals +{ + internal class LumiaBootManagerAppModel : NokiaFlashModel + { + private readonly PhoneInfo Info = new(); + + internal enum SecureBootKeyType : byte + { + Retail = 0, + Engineering = 1 + } + + // + // Not valid commands + // + /* NOK */ + private const string Signature = "NOK"; + /* NOKX */ + private const string ExtendedMessageSignature = $"{Signature}X"; + /* NOKXB */ + private const string LumiaBootManagerExtendedMessageSignature = $"{ExtendedMessageSignature}B"; + + // + // Normal commands + // + /* NOKA */ + private const string ContinueBootSignature = $"{Signature}A"; + /* NOKB */ + private const string RPMBSignature = $"{Signature}B"; + /* NOKC */ + private const string BatteryStatusSignature = $"{Signature}C"; + /* NOKD */ + private const string DisableTimeoutsSignature = $"{Signature}D"; + /* NOKI */ + private const string HelloSignature = $"{Signature}I"; + /* NOKM */ + private const string RebootToMassStorageSignature = $"{Signature}M"; + /* NOKP */ + private const string RebootToPhoneInfoAppSignature = $"{Signature}P"; + /* NOKR */ + private const string RebootSignature = $"{Signature}R"; + /* NOKS */ + private const string RebootToFlashAppSignature = $"{Signature}S"; + /* NOKT */ + private const string GetGPTSignature = $"{Signature}T"; + /* NOKV */ + private const string InfoQuerySignature = $"{Signature}V"; + /* NOKW */ + private const string WriteBootFlagFileSignature = $"{Signature}W"; + /* NOKY */ + private const string MMOSStartCommandSignature = $"{Signature}Y"; + /* NOKZ */ + private const string ShutdownSignature = $"{Signature}Z"; + + // + // Lumia Boot Manager extended commands + // + /* NOKXBD */ + private const string PlatformSecureBootEnableSignature = $"{LumiaBootManagerExtendedMessageSignature}D"; + /* NOKXBH */ + private const string WriteRootCertificateHashSignature = $"{LumiaBootManagerExtendedMessageSignature}H"; + /* NOKXBK */ + private const string UEFIKeysProvisionSignature = $"{LumiaBootManagerExtendedMessageSignature}K"; + /* NOKXBR */ + private const string ReadManufacturingStateSignature = $"{LumiaBootManagerExtendedMessageSignature}R"; + /* NOKXBU */ + private const string FlushVariablesSignature = $"{LumiaBootManagerExtendedMessageSignature}U"; + /* NOKXBW */ + private const string WriteManufacturingStateSignature = $"{LumiaBootManagerExtendedMessageSignature}W"; + + public LumiaBootManagerAppModel(string DevicePath) : base(DevicePath) + { + } + + internal void ContinueBoot() + { + LogFile.Log("Continue boot..."); + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, ContinueBootSignature); + ExecuteRawVoidMethod(Request); + } + + internal PhoneInfo ReadPhoneInfo(bool ExtendedInfo = true) + { + // 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 0x04: + if (Result.App == FlashAppType.BootManager) + { + Result.FlashAppProtocolVersionMajor = Response[SubblockPayloadOffset + 0x00]; + Result.FlashAppProtocolVersionMinor = Response[SubblockPayloadOffset + 0x01]; + Result.FlashAppVersionMajor = Response[SubblockPayloadOffset + 0x02]; + Result.FlashAppVersionMinor = Response[SubblockPayloadOffset + 0x03]; + } + else if (Result.App == FlashAppType.FlashApp) + { + Result.SdCardSizeInSectors = 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; + } + + internal GPT ReadGPT() + { + // If this function is used with a locked BootMgr v1, + // then the mode-switching should be done outside this function, + // because the context-switches that are used here are not supported on BootMgr v1. + + // Only works in BootLoader-mode or on unlocked bootloaders in Flash-mode!! + + byte[] Request = new byte[0x04]; + const string Header = GetGPTSignature; + + System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + + byte[] Buffer = ExecuteRawMethod(Request); + if ((Buffer == null) || (Buffer.Length < 0x4408)) + { + throw new InvalidOperationException("Unable to read GPT!"); + } + + UInt16 Error = (UInt16)((Buffer[6] << 8) + Buffer[7]); + if (Error > 0) + { + throw new NotSupportedException("ReadGPT: Error 0x" + Error.ToString("X4")); + } + + byte[] GPTBuffer = new byte[Buffer.Length - 0x208]; + System.Buffer.BlockCopy(Buffer, 0x208, GPTBuffer, 0, 0x4200); + + return new GPT(GPTBuffer); // NOKT message header and MBR are ignored + } + + internal byte[] GetGptChunk(UInt32 Size) // TODO! + { + // This function is also used to generate a dummy chunk to flash for testing. + // The dummy chunk will contain the GPT, so it can be flashed to the first sectors for testing. + byte[] GPTChunk = new byte[Size]; + + byte[] Request = new byte[0x04]; + const string Header = "NOKT"; + + System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + + byte[] Buffer = ExecuteRawMethod(Request); + if ((Buffer == null) || (Buffer.Length < 0x4408)) + { + throw new InvalidOperationException("Unable to read GPT!"); + } + + UInt16 Error = (UInt16)((Buffer[6] << 8) + Buffer[7]); + if (Error > 0) + { + throw new NotSupportedException("ReadGPT: Error 0x" + Error.ToString("X4")); + } + + System.Buffer.BlockCopy(Buffer, 8, GPTChunk, 0, 0x4400); + + return GPTChunk; + } + + internal void ProvisionSecureBootKeys(SecureBootKeyType KeyType) // Only for Flashmode, not BootManager mode. + { + byte[] Request = new byte[8]; + ByteOperations.WriteAsciiString(Request, 0, UEFIKeysProvisionSignature); + Request[6] = 0; // Options + Request[7] = (byte)KeyType; + byte[] Response = ExecuteRawMethod(Request); + UInt32 Status = ByteOperations.ReadUInt32(Response, 6); + if (Status != 0) + { + ThrowFlashError((int)Status); + } + } + + private void ThrowFlashError(int ErrorCode) + { + string SubMessage = ErrorCode switch + { + 0x0008 => "Unsupported protocol / Invalid options", + 0x000F => "Invalid sub block count", + 0x0010 => "Invalid sub block length", + 0x0012 => "Authentication required", + 0x000E => "Invalid sub block type", + 0x0013 => "Failed async message", + 0x1000 => "Invalid header type", + 0x1001 => "FFU header contain unknown extra data", + 0x0001 => "Couldn't allocate memory", + 0x1106 => "Security header validation failed", + 0x1105 => "Invalid hash table size", + 0x1104 => "Invalid catalog size", + 0x1103 => "Invalid chunk size", + 0x1102 => "Unsupported algorithm", + 0x1101 => "Invalid struct size", + 0x1100 => "Invalid signature", + 0x1202 => "Invalid struct size", + 0x1203 => "Unsupported algorithm", + 0x1204 => "Invalid chunk size", + 0x1005 => "Data not aligned correctly", + 0x0009 => "Locate protocol failed", + 0x1003 => "Hash mismatch", + 0x1006 => "Couldn't find hash from security header for index", + 0x1004 => "Security header import missing / All FFU headers have not been imported", + 0x1304 => "Invalid platform ID", + 0x1307 => "Invalid write descriptor info", + 0x1306 => "Invalid write descriptor info", + 0x1305 => "Invalid block size", + 0x1303 => "Unsupported FFU version", + 0x1302 => "Unsupported struct version", + 0x1301 => "Invalid update type", + 0x100B => "Too much payload data, all data has already been written", + 0x1008 => "Internal error", + 0x1007 => "Payload data does not contain all data", + 0x0004 => "Flash write failed", + 0x000D => "Flash verify failed", + 0x0002 => "Flash read failed", + _ => "Unknown error", + }; + WPinternalsException Ex = new("Flash failed!"); + Ex.SubMessage = "Error 0x" + ErrorCode.ToString("X4") + ": " + SubMessage; + + throw Ex; + } + + public void Shutdown() + { + byte[] Request = new byte[4]; + const string Header = ShutdownSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + ExecuteRawMethod(Request); + } + + internal void ResetPhone() + { + LogFile.Log("Rebooting phone", LogType.FileAndConsole); + try + { + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, RebootSignature); + ExecuteRawVoidMethod(Request); + } + catch + { + LogFile.Log("Sending reset-request failed", LogType.FileOnly); + LogFile.Log("Assuming automatic reset already in progress", LogType.FileOnly); + } + } + + internal void ResetPhoneToFlashMode() + { + // This only works when the phone is in BootMgr mode. If it is already in FlashApp, it will not reboot. It only makes the phone unresponsive. + LogFile.Log("Rebooting phone to Flash mode..."); + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, RebootToFlashAppSignature); + ExecuteRawVoidMethod(Request); + } + + internal void SwitchToPhoneInfoAppContextLegacy() + { + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, RebootToPhoneInfoAppSignature); + ExecuteRawVoidMethod(Request); + } + + internal void RebootToFlashApp() + { + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, "NOKS"); // This will let the phone charge + ExecuteRawVoidMethod(Request); // On phone with bootloader Spec A this triggers a reboot, so DisableRebootTimeOut() cannot be called immediately. + } + } +} diff --git a/WPinternals/Models/LumiaFlashAppModel.cs b/WPinternals/Models/LumiaFlashAppModel.cs new file mode 100644 index 0000000..6784ff5 --- /dev/null +++ b/WPinternals/Models/LumiaFlashAppModel.cs @@ -0,0 +1,1192 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.IO; +using System.Linq; + +namespace WPinternals +{ + internal enum SecureBootKeyType : byte + { + Retail = 0, + Engineering = 1 + } + + [Flags] + internal enum Fuse + { + SecureBoot = 1, + FfuVerify = 2, + Jtag = 4, + Shk = 8, + Simlock = 16, + ProductionDone = 32, + Rkh = 64, + PublicId = 128, + Dak = 256, + SecGen = 512, + OemId = 1024, + FastBoot = 2048, + SpdmSecMode = 4096, + RpmWdog = 8192, + Ssm = 16384 + } + + internal enum FfuProtocol + { + ProtocolSyncV1 = 1, + ProtocolAsyncV1 = 2, + ProtocolSyncV2 = 4, + ProtocolAsyncV2 = 8, + ProtocolAsyncV3 = 16 + } + + [Flags] + internal enum FlashOptions : byte + { + SkipWrite = 1, + SkipHashCheck = 2, + SkipIdCheck = 4, + SkipSignatureCheck = 8 + } + + internal class LumiaFlashAppModel : NokiaFlashModel + { + private readonly PhoneInfo Info = new(); + private UefiSecurityStatusResponse _SecurityStatus = null; + + // + // Not valid commands + // + /* NOK */ + private const string Signature = "NOK"; + /* NOKX */ + private const string ExtendedMessageSignature = $"{Signature}X"; + /* NOKXF */ + private const string LumiaFlashAppExtendedMessageSignature = $"{ExtendedMessageSignature}F"; + + // + // Normal commands + // + /* NOKB */ // TODO + private const string RPMBSignature = $"{Signature}B"; + /* NOKD */ + private const string DisableTimeoutsSignature = $"{Signature}D"; + /* NOKE */ + private const string EnableSecureFFUConfigSignature = $"{Signature}E"; + /* NOKF */ + private const string FlashSignature = $"{Signature}F"; + /* NOKG */ + private const string FactoryResetSignature = $"{Signature}G"; + /* NOKI */ + private const string HelloSignature = $"{Signature}I"; + /* NOKJ */ + private const string JTAGDisableSignature = $"{Signature}J"; + /* NOKK */ + private const string KickWatchdogSignature = $"{Signature}K"; + /* NOKL */ + private const string LoadFlashAppSignature = $"{Signature}L"; + /* NOKM */ + private const string RebootToMassStorageSignature = $"{Signature}M"; + /* NOKN */ + private const string AuthenticateSignature = $"{Signature}N"; + /* NOKP */ + private const string RebootToPhoneInfoAppSignature = $"{Signature}P"; + /* NOKR */ + private const string RebootSignature = $"{Signature}R"; + /* NOKT */ + private const string GetGPTSignature = $"{Signature}T"; + /* NOKV */ + private const string InfoQuerySignature = $"{Signature}V"; + /* NOKZ */ + private const string ShutdownSignature = $"{Signature}Z"; + + // + // Lumia Flash App extended commands + // + /* NOKXFB */ + private const string BackupSignature = $"{LumiaFlashAppExtendedMessageSignature}B"; + /* NOKXFC */ + private const string CertificateSignature = $"{LumiaFlashAppExtendedMessageSignature}C"; + /* NOKXFD */ + private const string PlatformSecureBootEnableSignature = $"{LumiaFlashAppExtendedMessageSignature}D"; + /* NOKXFE */ + private const string FlashEraseSignature = $"{LumiaFlashAppExtendedMessageSignature}E"; + /* NOKXFF */ + private const string AsyncFlashModeSignature = $"{LumiaFlashAppExtendedMessageSignature}F"; + /* NOKXFG */ + private const string CreateGoldenBackupSignature = $"{LumiaFlashAppExtendedMessageSignature}G"; + /* NOKXFH */ + private const string WriteRootCertificateHashSignature = $"{LumiaFlashAppExtendedMessageSignature}H"; + /* NOKXFK */ + private const string UEFIKeysProvisionSignature = $"{LumiaFlashAppExtendedMessageSignature}K"; + /* NOKXFL */ + private const string LoadSignature = $"{LumiaFlashAppExtendedMessageSignature}L"; + /* NOKXFP */ + private const string FlashPartitionEraseSignature = $"{LumiaFlashAppExtendedMessageSignature}P"; + /* NOKXFR */ + private const string ReadParamSignature = $"{LumiaFlashAppExtendedMessageSignature}R"; + /* NOKXFS */ + private const string SecureFlashSignature = $"{LumiaFlashAppExtendedMessageSignature}S"; + /* NOKXFT */ + private const string TerminalChallengeSignature = $"{LumiaFlashAppExtendedMessageSignature}T"; + /* NOKXFW */ + private const string WriteParamSignature = $"{LumiaFlashAppExtendedMessageSignature}W"; + /* NOKXFY */ + private const string MMOSStartCommandSignature = $"{LumiaFlashAppExtendedMessageSignature}Y"; + + public LumiaFlashAppModel(string DevicePath) : base(DevicePath) + { + } + + internal PhoneInfo ReadPhoneInfo(bool ExtendedInfo = true) + { + // 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 0x04: + if (Result.App == FlashAppType.BootManager) + { + Result.FlashAppProtocolVersionMajor = Response[SubblockPayloadOffset + 0x00]; + Result.FlashAppProtocolVersionMinor = Response[SubblockPayloadOffset + 0x01]; + Result.FlashAppVersionMajor = Response[SubblockPayloadOffset + 0x02]; + Result.FlashAppVersionMinor = Response[SubblockPayloadOffset + 0x03]; + } + else if (Result.App == FlashAppType.FlashApp) + { + Result.SdCardSizeInSectors = 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; + } + + if (ExtendedInfo && (Result.State == PhoneInfoState.Basic)) + { + if (Result.App == FlashAppType.FlashApp) + { + Result.Firmware = ReadStringParam("FVER"); + Result.RKH = ReadParam("RRKH"); + } + + Result.State = PhoneInfoState.Extended; + } + + Result.IsBootloaderSecure = !(Info.Authenticated || Info.RdcPresent || !Info.SecureFfuEnabled); + + if (!PhoneInfoLogged) + { + Result.Log(LogType.FileOnly); + } + + return Result; + } + + internal GPT ReadGPT() + { + // If this function is used with a locked BootMgr v1, + // then the mode-switching should be done outside this function, + // because the context-switches that are used here are not supported on BootMgr v1. + + // Only works in BootLoader-mode or on unlocked bootloaders in Flash-mode!! + + PhoneInfo Info = ReadPhoneInfo(ExtendedInfo: false); + FlashAppType OriginalAppType = Info.App; + bool Switch = Info.IsBootloaderSecure; + if (Switch) + { + throw new InvalidOperationException("Bootloader is not unlocked!"); + } + + byte[] Request = new byte[0x04]; + const string Header = GetGPTSignature; + + System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + + byte[] Buffer = ExecuteRawMethod(Request); + if ((Buffer == null) || (Buffer.Length < 0x4408)) + { + throw new InvalidOperationException("Unable to read GPT!"); + } + + UInt16 Error = (UInt16)((Buffer[6] << 8) + Buffer[7]); + if (Error > 0) + { + throw new NotSupportedException("ReadGPT: Error 0x" + Error.ToString("X4")); + } + + byte[] GPTBuffer = new byte[Buffer.Length - 0x208]; + System.Buffer.BlockCopy(Buffer, 0x208, GPTBuffer, 0, 0x4200); + + return new GPT(GPTBuffer); // NOKT message header and MBR are ignored + } + + internal byte[] GetGptChunk(UInt32 Size) // TODO! + { + // This function is also used to generate a dummy chunk to flash for testing. + // The dummy chunk will contain the GPT, so it can be flashed to the first sectors for testing. + byte[] GPTChunk = new byte[Size]; + + PhoneInfo Info = ReadPhoneInfo(ExtendedInfo: false); + FlashAppType OriginalAppType = Info.App; + bool Switch = Info.IsBootloaderSecure; + if (Switch) + { + throw new InvalidOperationException("Bootloader is not unlocked!"); + } + + byte[] Request = new byte[0x04]; + const string Header = "NOKT"; + + System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + + byte[] Buffer = ExecuteRawMethod(Request); + if ((Buffer == null) || (Buffer.Length < 0x4408)) + { + throw new InvalidOperationException("Unable to read GPT!"); + } + + UInt16 Error = (UInt16)((Buffer[6] << 8) + Buffer[7]); + if (Error > 0) + { + throw new NotSupportedException("ReadGPT: Error 0x" + Error.ToString("X4")); + } + + System.Buffer.BlockCopy(Buffer, 8, GPTChunk, 0, 0x4400); + + return GPTChunk; + } + + public byte[] ReadParam(string Param) + { + byte[] Request = new byte[0x0B]; + const string Header = ReadParamSignature; + + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length); + + byte[] Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0x10)) + { + return null; + } + + byte[] Result = new byte[Response[0x10]]; + Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]); + return Result; + } + + public string ReadStringParam(string Param) + { + byte[] Bytes = ReadParam(Param); + if (Bytes == null) + { + return null; + } + + return System.Text.Encoding.ASCII.GetString(Bytes).Trim(['\0']); + } + + public uint? ReadSecurityFlags() + { + byte[] Response = ReadParam("FCS"); + if ((Response == null) || (Response.Length != 4)) + { + return null; + } + + // This value is in big endian + return (UInt32)((Response[0] << 24) | (Response[1] << 16) | (Response[2] << 8) | Response[3]); + } + + public uint? ReadCurrentChargeLevel() + { + byte[] Response = ReadParam("CS"); + if ((Response == null) || (Response.Length != 8)) + { + return null; + } + + // This value is in big endian + return (UInt32)((Response[0] << 24) | (Response[1] << 16) | (Response[2] << 8) | Response[3]) + 1; + } + + public int? ReadCurrentChargeCurrent() + { + byte[] Response = ReadParam("CS"); + if ((Response == null) || (Response.Length != 8)) + { + return null; + } + + // This value is in big endian and needs to be XOR'd with 0xFFFFFFFF + return (Int32)(((Response[4] << 24) | (Response[5] << 16) | (Response[6] << 8) | Response[7]) ^ 0xFFFFFFFF) + 1; + } + + public UefiSecurityStatusResponse ReadSecurityStatus() + { + if (_SecurityStatus != null) + { + return _SecurityStatus; + } + + byte[] Response = ReadParam("SS"); + if (Response == null) + { + return null; + } + + UefiSecurityStatusResponse Result = new(); + + Result.IsTestDevice = Response[0]; + Result.PlatformSecureBootStatus = Convert.ToBoolean(Response[1]); + Result.SecureFfuEfuseStatus = Convert.ToBoolean(Response[2]); + Result.DebugStatus = Convert.ToBoolean(Response[3]); + Result.RdcStatus = Convert.ToBoolean(Response[4]); + Result.AuthenticationStatus = Convert.ToBoolean(Response[5]); + Result.UefiSecureBootStatus = Convert.ToBoolean(Response[6]); + Result.CryptoHardwareKey = Convert.ToBoolean(Response[7]); + + _SecurityStatus = Result; + + return Result; + } + + public FlashVersion GetFlashVersion() + { + byte[] Response = ReadParam("FAI"); + if ((Response == null) || (Response.Length < 6)) + { + return null; + } + + FlashVersion Result = new(); + + Result.ProtocolMajor = Response[1]; + Result.ProtocolMinor = Response[2]; + Result.ApplicationMajor = Response[3]; + Result.ApplicationMinor = Response[4]; + + return Result; + } + + internal UInt16 ReadSecureFfuSupportedProtocolMask() + { + return BigEndian.ToUInt16(ReadParam("SFPI"), 0); + } + + public TerminalResponse GetTerminalResponse() + { + byte[] AsskMask = [1, 0, 16, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64]; + byte[] Request = new byte[0xAC]; + const string Header = TerminalChallengeSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Request[7] = 1; + Buffer.BlockCopy(BigEndian.GetBytes(0x18, 4), 0, Request, 0x08, 4); // Subblocktype = 0x18 + Buffer.BlockCopy(BigEndian.GetBytes(0x9C, 4), 0, Request, 0x0C, 4); // Subblocklength = 0x9C + Buffer.BlockCopy(BigEndian.GetBytes(0x00, 4), 0, Request, 0x10, 4); // AsicIndex = 0x00 + Buffer.BlockCopy(AsskMask, 0, Request, 0x14, 0x10); + byte[] TerminalResponse = ExecuteRawMethod(Request); + if ((TerminalResponse?.Length > 0x20) && (BigEndian.ToUInt32(TerminalResponse, 0x14) == (TerminalResponse.Length - 0x18)) && (BitConverter.ToUInt32(TerminalResponse, 0x1C) == (TerminalResponse.Length - 0x20))) + { + // Parse Terminal Response from offset 0x18 + return Terminal.Parse(TerminalResponse, 0x18); + } + else + { + return null; + } + } + + public void SendFfuHeaderV1(byte[] FfuHeader, byte Options = 0) + { + byte[] Request = new byte[FfuHeader.Length + 0x20]; + + const string Header = SecureFlashSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(BigEndian.GetBytes(0x0001, 2), 0, Request, 0x06, 2); // Protocol version = 0x0001 + Request[0x08] = 0; // Progress = 0% + Request[0x0B] = 1; // Subblock count = 1 + Buffer.BlockCopy(BigEndian.GetBytes(0x0000000B, 4), 0, Request, 0x0C, 4); // Subblock type for header = 0x0B + Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length + 0x0C, 4), 0, Request, 0x10, 4); // Subblock length = length of header + 0x0C + Buffer.BlockCopy(BigEndian.GetBytes(0x00000000, 4), 0, Request, 0x14, 4); // Header type = 0 + Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length, 4), 0, Request, 0x18, 4); // Payload length = length of header + Request[0x1C] = Options; // Header options = 0 + + Buffer.BlockCopy(FfuHeader, 0, Request, 0x20, FfuHeader.Length); + + byte[] Response = ExecuteRawMethod(Request); + if (Response == null) + { + throw new BadConnectionException(); + } + + int ResultCode = (Response[6] << 8) + Response[7]; + if (ResultCode != 0) + { + ThrowFlashError(ResultCode); + } + } + + public void SendFfuHeaderV2(UInt32 TotalHeaderLength, UInt32 OffsetForThisPart, byte[] FfuHeader, byte Options = 0) + { + byte[] Request = new byte[FfuHeader.Length + 0x3C]; + + const string Header = SecureFlashSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolSyncV2, 2), 0, Request, 0x06, 2); // Protocol version = 0x0001 + Request[0x08] = 0; // Progress = 0% + Request[0x0B] = 1; // Subblock count = 1 + + Buffer.BlockCopy(BigEndian.GetBytes(0x00000021, 4), 0, Request, 0x0C, 4); // Subblock type for header v2 = 0x21 + Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length + 0x28, 4), 0, Request, 0x10, 4); // Subblock starts at 0x14, payload starts at 0x3C. + + Buffer.BlockCopy(BigEndian.GetBytes(0x00000000, 4), 0, Request, 0x14, 4); // Header type = 0 + Buffer.BlockCopy(BigEndian.GetBytes(TotalHeaderLength, 4), 0, Request, 0x18, 4); // Payload length = length of header + Request[0x1C] = Options; // Header options = 0 + + Buffer.BlockCopy(BigEndian.GetBytes(OffsetForThisPart, 4), 0, Request, 0x1D, 4); + Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length, 4), 0, Request, 0x21, 4); + Request[0x25] = 0; // No Erase + + Buffer.BlockCopy(FfuHeader, 0, Request, 0x3C, FfuHeader.Length); + + byte[] Response = ExecuteRawMethod(Request); + if (Response == null) + { + throw new BadConnectionException(); + } + + if (Response.Length == 4) + { + throw new WPinternalsException("Flash protocol v2 not supported", "The device reported that the Flash protocol v2 was not supported while sending the FFU header."); + } + + int ResultCode = (Response[6] << 8) + Response[7]; + if (ResultCode != 0) + { + ThrowFlashError(ResultCode); + } + } + + public void SendFfuPayloadV1(byte[] FfuChunk, int Progress = 0, byte Options = 0) + { + byte[] Request = new byte[FfuChunk.Length + 0x1C]; + + const string Header = SecureFlashSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolSyncV1, 2), 0, Request, 0x06, 2); // Protocol version = 0x0001 + Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100) + Request[0x0B] = 1; // Subblock count = 1 + + Buffer.BlockCopy(BigEndian.GetBytes(0x0000000C, 4), 0, Request, 0x0C, 4); // Subblock type for ChunkData = 0x0C + Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length + 0x08, 4), 0, Request, 0x10, 4); // Subblock length = length of chunk + 0x08 + + Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length, 4), 0, Request, 0x14, 4); // Payload length = length of chunk + Request[0x18] = Options; // Data options = 0 (1 = verify) + + Buffer.BlockCopy(FfuChunk, 0, Request, 0x1C, FfuChunk.Length); + + byte[] Response = ExecuteRawMethod(Request); + if (Response == null) + { + throw new BadConnectionException(); + } + + int ResultCode = (Response[6] << 8) + Response[7]; + if (ResultCode != 0) + { + ThrowFlashError(ResultCode); + } + } + + public void SendFfuPayloadV2(byte[] FfuChunk, int Progress = 0, byte Options = 0) + { + byte[] Request = new byte[FfuChunk.Length + 0x20]; + + const string Header = SecureFlashSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolSyncV2, 2), 0, Request, 0x06, 2); // Protocol + Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100) + Request[0x0B] = 1; // Subblock count = 1 + + Buffer.BlockCopy(BigEndian.GetBytes(0x0000001B, 4), 0, Request, 0x0C, 4); // Subblock type for Payload v2 = 0x1B + Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length + 0x0C, 4), 0, Request, 0x10, 4); // Subblock length = length of chunk + 0x08 + + Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length, 4), 0, Request, 0x14, 4); // Payload length = length of chunk + Request[0x18] = Options; // Data options = 0 (1 = verify) + + Buffer.BlockCopy(FfuChunk, 0, Request, 0x20, FfuChunk.Length); + + byte[] Response = ExecuteRawMethod(Request); + if (Response == null) + { + throw new BadConnectionException(); + } + + int ResultCode = (Response[6] << 8) + Response[7]; + if (ResultCode != 0) + { + ThrowFlashError(ResultCode); + } + } + + public void SendFfuPayloadV3(byte[] FfuChunk, UInt32 WriteDescriptorIndex, UInt32 CRC, int Progress = 0, byte Options = 0) + { + byte[] Request = new byte[FfuChunk.Length + 0x20]; + + const string Header = SecureFlashSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolAsyncV3, 2), 0, Request, 0x06, 2); // Protocol + Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100) + Request[0x0B] = 1; // Subblock count = 1 + + Buffer.BlockCopy(BigEndian.GetBytes(0x0000001D, 4), 0, Request, 0x0C, 4); // Subblock type for Payload v2 = 0x1B + Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length + 0x2C, 4), 0, Request, 0x10, 4); // Subblock length = length of chunk + 0x08 + + Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length, 4), 0, Request, 0x14, 4); // Payload length = length of chunk + Request[0x18] = Options; // Data options = 0 (1 = verify) + Buffer.BlockCopy(BigEndian.GetBytes(WriteDescriptorIndex, 4), 0, Request, 0x19, 4); // Payload length = length of chunk + Buffer.BlockCopy(BigEndian.GetBytes(CRC, 4), 0, Request, 0x1D, 4); // Payload length = length of chunk + + Buffer.BlockCopy(FfuChunk, 0, Request, 0x40, FfuChunk.Length); + + byte[] Response = ExecuteRawMethod(Request); + if (Response == null) + { + throw new BadConnectionException(); + } + + int ResultCode = (Response[6] << 8) + Response[7]; + if (ResultCode != 0) + { + ThrowFlashError(ResultCode); + } + } + + public void BackupPartitionToRam(string PartitionName) + { + PartitionName = PartitionName.ToUpper(); + if (new string[] { "MODEM_FSG", "MODEM_FS1", "MODEM_FS2", "SSD", "DPP" }.Any(s => s == PartitionName)) + { + byte[] Request = new byte[84]; + const string Header = BackupSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Request[0x07] = 1; // Subblock count = 1 + Request[0x08] = 6; // Subblock ID = 6 = Create Partition Backup to RAM + Buffer.BlockCopy(BigEndian.GetBytes(73, 2), 0, Request, 0x09, 2); // Subblock length = Length of data in subblock including fillers (subblock-ID-field and subblock-length-field are not counted for the subblock-length) + System.Text.Encoding.Unicode.GetBytes(PartitionName); + + byte[] PartitionBytes = System.Text.Encoding.Unicode.GetBytes(PartitionName); + Buffer.BlockCopy(PartitionBytes, 0, Request, 0x0C, PartitionBytes.Length); + Request[0x0C + PartitionBytes.Length + 0x00] = 0; // Trailing zero + Request[0x0C + PartitionBytes.Length + 0x01] = 0; + + byte[] Response = ExecuteRawMethod(Request); + int ResultCode = (Response[6] << 8) + Response[7]; + if (ResultCode != 0) + { + ThrowFlashError(ResultCode); + } + } + else + { + throw new WPinternalsException("Specified partition cannot be backupped to RAM", "Partition name: \"" + PartitionName + "\"."); + } + } + + public void LoadMmosBinary(UInt32 TotalLength, UInt32 Offset, bool SkipMmosSupportCheck, byte[] MmosPart) + { + byte[] Request = new byte[MmosPart.Length + 0x20]; + const string Header = LoadSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Request[0x07] = 1; // Subblock count = 1 + Buffer.BlockCopy(BigEndian.GetBytes(0x0000001E, 4), 0, Request, 0x08, 4); // Subblock ID = Load MMOS Binary = 0x1E + Buffer.BlockCopy(BigEndian.GetBytes(MmosPart.Length + 0x10, 4), 0, Request, 0x0C, 4); // Subblock length = Payload-length + 0x10 + Buffer.BlockCopy(BigEndian.GetBytes(TotalLength, 4), 0, Request, 0x10, 4); + Buffer.BlockCopy(BigEndian.GetBytes(Offset, 4), 0, Request, 0x14, 4); + if (SkipMmosSupportCheck) + { + Request[0x18] = 1; + } + + Buffer.BlockCopy(BigEndian.GetBytes(MmosPart.Length, 4), 0, Request, 0x1C, 4); + Buffer.BlockCopy(MmosPart, 0, Request, 0x20, MmosPart.Length); + + byte[] Response = ExecuteRawMethod(Request); + int ResultCode = (Response[6] << 8) + Response[7]; + if (ResultCode != 0) + { + ThrowFlashError(ResultCode); + } + } + + internal void ErasePartition(string PartitionName) + { + // Partition "Data" can always be erased. + // Other partitions can only be erased when a valid RDC certificate is present or full SX authentication was performed. + if (PartitionName.Length > 0x23) + { + throw new ArgumentException("PartitionName cannot exceed 0x23 chars!"); + } + + byte[] Request = new byte[0x50]; + const string Header = FlashPartitionEraseSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Request[0x06] = 1; // Protocol version must be 1 + Request[0x07] = 0; // Device type = 0 + + byte[] PartitionBytes = System.Text.Encoding.Unicode.GetBytes(PartitionName); + Buffer.BlockCopy(PartitionBytes, 0, Request, 8, PartitionBytes.Length); + Request[0x08 + PartitionBytes.Length + 0x00] = 0; // Trailing zero + Request[0x08 + PartitionBytes.Length + 0x01] = 0; + + ExecuteRawMethod(Request); + } + + internal void StartAsyncFlash() + { + byte[] Request = new byte[14]; + ByteOperations.WriteAsciiString(Request, 0, AsyncFlashModeSignature + "S"); + Request[8] = 1; // Protocol version must be 1 + Request[9] = 0; // Protocol type must be 0 + ExecuteRawMethod(Request); + } + + internal void EndAsyncFlash() + { + byte[] Request = new byte[7]; + ByteOperations.WriteAsciiString(Request, 0, AsyncFlashModeSignature + "E"); + ExecuteRawMethod(Request); + } + + internal void ProvisionSecureBootKeys(SecureBootKeyType KeyType) // Only for Flashmode, not BootManager mode. + { + byte[] Request = new byte[8]; + ByteOperations.WriteAsciiString(Request, 0, UEFIKeysProvisionSignature); + Request[6] = 0; // Options + Request[7] = (byte)KeyType; + byte[] Response = ExecuteRawMethod(Request); + UInt32 Status = ByteOperations.ReadUInt32(Response, 6); + if (Status != 0) + { + ThrowFlashError((int)Status); + } + } + + private void ThrowFlashError(int ErrorCode) + { + string SubMessage = ErrorCode switch + { + 0x0008 => "Unsupported protocol / Invalid options", + 0x000F => "Invalid sub block count", + 0x0010 => "Invalid sub block length", + 0x0012 => "Authentication required", + 0x000E => "Invalid sub block type", + 0x0013 => "Failed async message", + 0x1000 => "Invalid header type", + 0x1001 => "FFU header contain unknown extra data", + 0x0001 => "Couldn't allocate memory", + 0x1106 => "Security header validation failed", + 0x1105 => "Invalid hash table size", + 0x1104 => "Invalid catalog size", + 0x1103 => "Invalid chunk size", + 0x1102 => "Unsupported algorithm", + 0x1101 => "Invalid struct size", + 0x1100 => "Invalid signature", + 0x1202 => "Invalid struct size", + 0x1203 => "Unsupported algorithm", + 0x1204 => "Invalid chunk size", + 0x1005 => "Data not aligned correctly", + 0x0009 => "Locate protocol failed", + 0x1003 => "Hash mismatch", + 0x1006 => "Couldn't find hash from security header for index", + 0x1004 => "Security header import missing / All FFU headers have not been imported", + 0x1304 => "Invalid platform ID", + 0x1307 => "Invalid write descriptor info", + 0x1306 => "Invalid write descriptor info", + 0x1305 => "Invalid block size", + 0x1303 => "Unsupported FFU version", + 0x1302 => "Unsupported struct version", + 0x1301 => "Invalid update type", + 0x100B => "Too much payload data, all data has already been written", + 0x1008 => "Internal error", + 0x1007 => "Payload data does not contain all data", + 0x0004 => "Flash write failed", + 0x000D => "Flash verify failed", + 0x0002 => "Flash read failed", + _ => "Unknown error", + }; + WPinternalsException Ex = new("Flash failed!"); + Ex.SubMessage = "Error 0x" + ErrorCode.ToString("X4") + ": " + SubMessage; + + throw Ex; + } + + public bool? ReadFuseStatus(Fuse fuse) + { + uint? flags = ReadSecurityFlags(); + if (!flags.HasValue) + { + return null; + } + + var finalconfig = (Fuse)flags.Value; + return finalconfig.HasFlag(fuse); + } + + internal bool? IsBootLoaderUnlocked() + { + UefiSecurityStatusResponse SecurityStatus = ReadSecurityStatus(); + if (SecurityStatus != null) + { + return SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus; + } + + return null; + } + + public void FlashSectors(UInt32 StartSector, byte[] Data, int Progress = 0) + { + // Start sector is in UInt32, so max size of eMMC is 2 TB. + + byte[] Request = new byte[Data.Length + 0x40]; + + const string Header = FlashSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Request[0x05] = 0; // Device type = 0 + Buffer.BlockCopy(BigEndian.GetBytes(StartSector, 4), 0, Request, 0x0B, 4); // Start sector + Buffer.BlockCopy(BigEndian.GetBytes(Data.Length / 0x200, 4), 0, Request, 0x0F, 4); // Sector count + Request[0x13] = (byte)Progress; // Progress (0 - 100) + Request[0x18] = 0; // Do Verify + Request[0x19] = 0; // Is Test + + Buffer.BlockCopy(Data, 0, Request, 0x40, Data.Length); + + ExecuteRawMethod(Request); + } + + public void Shutdown() + { + byte[] Request = new byte[4]; + const string Header = ShutdownSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + ExecuteRawMethod(Request); + } + + internal void ResetPhone() + { + LogFile.Log("Rebooting phone", LogType.FileAndConsole); + try + { + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, RebootSignature); + ExecuteRawVoidMethod(Request); + } + catch + { + LogFile.Log("Sending reset-request failed", LogType.FileOnly); + LogFile.Log("Assuming automatic reset already in progress", LogType.FileOnly); + } + } + + internal void SwitchToPhoneInfoAppContextLegacy() + { + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, RebootToPhoneInfoAppSignature); + ExecuteRawVoidMethod(Request); + } + + internal void WriteGPT(GPT NewGPT) + { + bool? unlocked = IsBootLoaderUnlocked(); + if (unlocked == false) + { + throw new InvalidOperationException("Bootloader is not unlocked!"); + } + + byte[] Buffer = NewGPT.Rebuild(); + + UInt32? HeaderOffset = ByteOperations.FindAscii(Buffer, "EFI PART"); + if (HeaderOffset != 0) + { + throw new BadImageFormatException(); + } + + FlashSectors(1, Buffer); + } + + public void FlashMMOS(string MMOSPath, ProgressUpdater UpdaterPerChunk) + { + LogFile.BeginAction("FlashMMOS"); + + ProgressUpdater Progress = UpdaterPerChunk; + + PhoneInfo Info = ReadPhoneInfo(); + if (!Info.MmosOverUsbSupported) + { + throw new WPinternalsException("Flash failed!", "Protocols not supported. The device reports that loading Microsoft Manufacturing Operating System over USB is not supported."); + } + + FileInfo info = new(MMOSPath); + uint length = uint.Parse(info.Length.ToString()); + + int offset = 0; + const int maximumbuffersize = 0x00240000; + + uint totalcounts = (uint)Math.Truncate((decimal)length / maximumbuffersize); + + using (FileStream MMOSFile = new(MMOSPath, FileMode.Open, FileAccess.Read)) + { + for (int i = 1; i <= (uint)Math.Truncate((decimal)length / maximumbuffersize); i++) + { + Progress.IncreaseProgress(1); + byte[] data = new byte[maximumbuffersize]; + MMOSFile.Read(data, 0, maximumbuffersize); + + LoadMmosBinary(length, (uint)offset, false, data); + + offset += maximumbuffersize; + } + + if (length - offset != 0) + { + Progress.IncreaseProgress(1); + + byte[] data = new byte[length - offset]; + MMOSFile.Read(data, 0, (int)(length - offset)); + LoadMmosBinary(length, (uint)offset, false, data); + } + + SwitchToMmosContext(); + ResetPhone(); + } + + LogFile.EndAction("FlashMMOS"); + } + + public void FlashFFU(string FFUPath, bool ResetAfterwards = true, byte Options = 0) + { + FlashFFU(new FFU(FFUPath), ResetAfterwards, Options); + } + + public void FlashFFU(FFU FFU, bool ResetAfterwards = true, byte Options = 0) + { + FlashFFU(FFU, null, ResetAfterwards, Options); + } + + public void FlashFFU(FFU FFU, ProgressUpdater UpdaterPerChunk, bool ResetAfterwards = true, byte Options = 0) + { + LogFile.BeginAction("FlashFFU"); + + ProgressUpdater Progress = UpdaterPerChunk; + + PhoneInfo Info = ReadPhoneInfo(); + if ((Info.SecureFfuSupportedProtocolMask & ((ushort)FfuProtocol.ProtocolSyncV1 | (ushort)FfuProtocol.ProtocolSyncV2)) == 0) + { + throw new WPinternalsException("Flash failed!", "Protocols not supported. The device reports that both Protocol Sync v1 and Protocol Sync v2 are not supported for FFU flashing. Is this an old device?"); + } + + UInt64 CombinedFFUHeaderSize = FFU.HeaderSize; + byte[] FfuHeader = new byte[CombinedFFUHeaderSize]; + using (FileStream FfuFile = new(FFU.Path, FileMode.Open, FileAccess.Read)) + { + FfuFile.Read(FfuHeader, 0, (int)CombinedFFUHeaderSize); + SendFfuHeaderV1(FfuHeader, Options); + + UInt64 Position = CombinedFFUHeaderSize; + byte[] Payload; + int ChunkCount = 0; + + if ((Info.SecureFfuSupportedProtocolMask & (ushort)FfuProtocol.ProtocolSyncV2) == 0) + { + // Protocol v1 + Payload = new byte[FFU.ChunkSize]; + + while (Position < (UInt64)FfuFile.Length) + { + FfuFile.Read(Payload, 0, Payload.Length); + ChunkCount++; + SendFfuPayloadV1(Payload, (int)((double)ChunkCount * 100 / FFU.TotalChunkCount), 0); + Position += (ulong)Payload.Length; + + Progress?.IncreaseProgress(1); + } + } + else + { + // Protocol v2 + Payload = new byte[Info.WriteBufferSize]; + + while (Position < (UInt64)FfuFile.Length) + { + UInt32 PayloadSize = Info.WriteBufferSize; + if (((UInt64)FfuFile.Length - Position) < PayloadSize) + { + PayloadSize = (UInt32)((UInt64)FfuFile.Length - Position); + Payload = new byte[PayloadSize]; + } + + FfuFile.Read(Payload, 0, (int)PayloadSize); + ChunkCount += (int)(PayloadSize / FFU.ChunkSize); + SendFfuPayloadV2(Payload, (int)((double)ChunkCount * 100 / FFU.TotalChunkCount), 0); + Position += PayloadSize; + + Progress?.IncreaseProgress((ulong)(PayloadSize / FFU.ChunkSize)); + } + } + } + + if (ResetAfterwards) + { + ResetPhone(); + } + + LogFile.EndAction("FlashFFU"); + } + + internal void FlashRawPartition(string Path, string PartitionName) + { + FlashRawPartition(Path, null, PartitionName, null, null); + } + + internal void FlashRawPartition(string Path, string PartitionName, Action ProgressUpdateCallback) + { + FlashRawPartition(Path, null, PartitionName, ProgressUpdateCallback, null); + } + + internal void FlashRawPartition(string Path, string PartitionName, ProgressUpdater UpdaterPerSector) + { + FlashRawPartition(Path, null, PartitionName, null, UpdaterPerSector); + } + + internal void FlashRawPartition(Stream Stream, string PartitionName, ProgressUpdater UpdaterPerSector) + { + FlashRawPartition(null, Stream, PartitionName, null, UpdaterPerSector); + } + + internal void FlashRawPartition(byte[] Buffer, string PartitionName, ProgressUpdater UpdaterPerSector) + { + FlashRawPartition(null, new MemoryStream(Buffer, false), PartitionName, null, UpdaterPerSector); + } + + internal void FlashRawPartition(GPT GPT, string Path, string PartitionName) + { + FlashRawPartition(GPT, Path, null, PartitionName, null, null); + } + + internal void FlashRawPartition(GPT GPT, string Path, string PartitionName, Action ProgressUpdateCallback) + { + FlashRawPartition(GPT, Path, null, PartitionName, ProgressUpdateCallback, null); + } + + internal void FlashRawPartition(GPT GPT, string Path, string PartitionName, ProgressUpdater UpdaterPerSector) + { + FlashRawPartition(GPT, Path, null, PartitionName, null, UpdaterPerSector); + } + + internal void FlashRawPartition(GPT GPT, Stream Stream, string PartitionName, ProgressUpdater UpdaterPerSector) + { + FlashRawPartition(GPT, null, Stream, PartitionName, null, UpdaterPerSector); + } + + internal void FlashRawPartition(GPT GPT, byte[] Buffer, string PartitionName, ProgressUpdater UpdaterPerSector) + { + FlashRawPartition(GPT, null, new MemoryStream(Buffer, false), PartitionName, null, UpdaterPerSector); + } + + private void FlashRawPartition(string Path, Stream Stream, string PartitionName, Action ProgressUpdateCallback, ProgressUpdater UpdaterPerSector) + { + GPT GPT = ReadGPT(); + FlashRawPartition(Path, Stream, PartitionName, ProgressUpdateCallback, UpdaterPerSector); + } + + private void FlashRawPartition(GPT GPT, string Path, Stream Stream, string PartitionName, Action ProgressUpdateCallback, ProgressUpdater UpdaterPerSector) + { + bool? unlocked = IsBootLoaderUnlocked(); + if (unlocked == false) + { + throw new InvalidOperationException("Bootloader is not unlocked!"); + } + + Partition Partition = GPT.Partitions.First((p) => p.Name == PartitionName); + ulong PartitionSize = (Partition.LastSector - Partition.FirstSector + 1) * 0x200; + + Stream InputStream = null; + + if (Path != null) + { + InputStream = new DecompressedStream(File.Open(Path, FileMode.Open)); + } + else if (Stream != null) + { + InputStream = Stream is DecompressedStream ? Stream : new DecompressedStream(Stream); + } + + if (InputStream != null) + { + using (InputStream) + { + UInt64? InputStreamLength = null; + try + { + InputStreamLength = (UInt64)InputStream.Length; + } + catch (Exception ex) + { + LogFile.LogException(ex, LogType.FileOnly); + } + + if ((InputStreamLength != null) && ((UInt64)InputStream.Length > PartitionSize)) + { + throw new InvalidOperationException("Partition can not be flashed, because its size is too big!"); + } + + ProgressUpdater Progress = UpdaterPerSector; + if ((Progress == null) && (ProgressUpdateCallback != null) && (InputStreamLength != null)) + { + Progress = new ProgressUpdater((UInt64)(InputStreamLength / 0x200), ProgressUpdateCallback); + } + + int ProgressPercentage = 0; + + const int FlashBufferSize = 0x200000; // Flash 8 GB phone -> buffersize 0x200000 = 11:45 min, buffersize 0x20000 = 12:30 min + byte[] FlashBuffer = new byte[FlashBufferSize]; + int BytesRead; + UInt64 i = 0; + do + { + BytesRead = InputStream.Read(FlashBuffer, 0, FlashBufferSize); + + byte[] FlashBufferFinalSize; + if (BytesRead > 0) + { + if (BytesRead == FlashBufferSize) + { + FlashBufferFinalSize = FlashBuffer; + } + else + { + FlashBufferFinalSize = new byte[BytesRead]; + Buffer.BlockCopy(FlashBuffer, 0, FlashBufferFinalSize, 0, BytesRead); + } + + FlashSectors((UInt32)(Partition.FirstSector + (i / 0x200)), FlashBufferFinalSize, ProgressPercentage); + } + + if (Progress != null) + { + Progress.IncreaseProgress((UInt64)FlashBuffer.Length / 0x200); + ProgressPercentage = Progress.ProgressPercentage; + } + + i += FlashBufferSize; + } + while (BytesRead == FlashBufferSize); + } + } + } + } +} \ No newline at end of file diff --git a/WPinternals/Models/LumiaPhoneInfoAppModel.cs b/WPinternals/Models/LumiaPhoneInfoAppModel.cs new file mode 100644 index 0000000..6ffdf65 --- /dev/null +++ b/WPinternals/Models/LumiaPhoneInfoAppModel.cs @@ -0,0 +1,248 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.IO; +using System.Linq; + +namespace WPinternals +{ + internal class LumiaPhoneInfoAppModel : NokiaFlashModel + { + private readonly PhoneInfo Info = new(); + + // + // Not valid commands + // + /* NOK */ + private const string Signature = "NOK"; + /* NOKX */ + private const string ExtendedMessageSignature = $"{Signature}X"; + /* NOKXP */ + private const string PhoneInfoAppExtendedMessageSignature = $"{ExtendedMessageSignature}P"; + + // + // Normal commands + // + /* NOKA */ + private const string ContinueBootSignature = $"{Signature}A"; + /* NOKD */ + private const string DisableTimeoutsSignature = $"{Signature}D"; + /* NOKH */ + private const string GetPhoneInfoSignature = $"{Signature}H"; + /* NOKI */ + private const string HelloSignature = $"{Signature}I"; + /* NOKV */ + private const string InfoQuerySignature = $"{Signature}V"; + + // + // Phone Info App extended commands + // + /* NOKXPH */ + private const string GetVariableSignature = $"{PhoneInfoAppExtendedMessageSignature}H"; + + public LumiaPhoneInfoAppModel(string DevicePath) : base(DevicePath) + { + } + + internal void ContinueBoot() + { + LogFile.Log("Continue boot..."); + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, ContinueBootSignature); + ExecuteRawVoidMethod(Request); + } + + internal string GetPhoneInfo() + { + // NOKH = Get Phone Info (IMEI and info from Product.dat) - Not available on some phones, like Lumia 640. + // NOKV = Info Query + + if (Info.FlashAppProtocolVersionMajor >= 2) + { + return null; + } + + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, GetPhoneInfoSignature); + byte[] Response = ExecuteRawMethod(Request); + if ((Response == null) || (ByteOperations.ReadAsciiString(Response, 0, 4) == "NOKU")) + { + throw new NotSupportedException(); + } + + UInt16 Length = BigEndian.ToUInt16(Response, 0x04); + + string PhoneInfoData = ByteOperations.ReadAsciiString(Response, 0x8, Length); + + return PhoneInfoData; + } + + internal PhoneInfo ReadPhoneInfo(bool ExtendedInfo = true) + { + // 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 0x04: + if (Result.App == FlashAppType.BootManager) + { + Result.FlashAppProtocolVersionMajor = Response[SubblockPayloadOffset + 0x00]; + Result.FlashAppProtocolVersionMinor = Response[SubblockPayloadOffset + 0x01]; + Result.FlashAppVersionMajor = Response[SubblockPayloadOffset + 0x02]; + Result.FlashAppVersionMinor = Response[SubblockPayloadOffset + 0x03]; + } + else if (Result.App == FlashAppType.FlashApp) + { + Result.SdCardSizeInSectors = 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; + } + + if (ExtendedInfo && (Result.State == PhoneInfoState.Basic)) + { + try + { + Result.Type = ReadPhoneInfoVariable("TYPE"); + Result.ProductCode = ReadPhoneInfoVariable("CTR"); + Result.Imei = ReadPhoneInfoVariable("IMEI"); + } + catch (Exception ex) + { + LogFile.LogException(ex, LogType.FileOnly); + } + + Result.State = PhoneInfoState.Extended; + } + + Result.IsBootloaderSecure = !(Info.Authenticated || Info.RdcPresent || !Info.SecureFfuEnabled); + + if (!PhoneInfoLogged) + { + Result.Log(LogType.FileOnly); + } + + return Result; + } + + internal string ReadPhoneInfoVariable(string VariableName) + { + // This function assumes the phone is in Phone Info App context + + byte[] Request = new byte[16]; + ByteOperations.WriteAsciiString(Request, 0, GetVariableSignature + VariableName + "\0"); // BTR or CTR, CTR is public ProductCode + byte[] Response = ExecuteRawMethod(Request); + UInt16 Length = BigEndian.ToUInt16(Response, 6); + return ByteOperations.ReadAsciiString(Response, 8, Length).Trim([' ', '\0']); + } + + internal string ReadProductCode() + { + string Result = ReadPhoneInfoVariable("CTR"); + SwitchToFlashAppContext(); + return Result; + } + } +} diff --git a/WPinternals/Models/NokiaFlashModel.cs b/WPinternals/Models/NokiaFlashModel.cs index 48c53ed..ab186be 100644 --- a/WPinternals/Models/NokiaFlashModel.cs +++ b/WPinternals/Models/NokiaFlashModel.cs @@ -24,1239 +24,36 @@ using System.Linq; namespace WPinternals { - internal enum FfuProtocol - { - ProtocolSyncV1 = 1, - ProtocolAsyncV1 = 2, - ProtocolSyncV2 = 4, - ProtocolAsyncV2 = 8, - ProtocolAsyncV3 = 16 - } - - [Flags] - internal enum FlashOptions : byte - { - SkipWrite = 1, - SkipHashCheck = 2, - SkipIdCheck = 4, - SkipSignatureCheck = 8 - } - - internal delegate void InterfaceChangedHandler(PhoneInterfaces NewInterface); + internal delegate void InterfaceChangedHandler(PhoneInterfaces NewInterface, string DevicePath); internal class NokiaFlashModel : NokiaPhoneModel { - private UefiSecurityStatusResponse _SecurityStatus = null; - private readonly PhoneInfo Info = new(); - internal event InterfaceChangedHandler InterfaceChanged = delegate { }; + // + // Not valid commands + // + /* NOK */ + private const string Signature = "NOK"; + /* NOKX */ + private const string ExtendedMessageSignature = $"{Signature}X"; + /* NOKXC */ + private const string CommonExtendedMessageSignature = $"{ExtendedMessageSignature}C"; + + // + // Common extended commands + // + /* NOKXCB */ + private const string SwitchModeSignature = $"{CommonExtendedMessageSignature}B"; + /* NOKXCE */ + private const string EchoSignature = $"{CommonExtendedMessageSignature}E"; + public NokiaFlashModel(string DevicePath) : base(DevicePath) { } - public byte[] ReadParam(string Param) - { - byte[] Request = new byte[0x0B]; - const string Header = "NOKXFR"; - - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length); - - byte[] Response = ExecuteRawMethod(Request); - if ((Response == null) || (Response.Length < 0x10)) - { - return null; - } - - byte[] Result = new byte[Response[0x10]]; - Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]); - return Result; - } - - public string ReadStringParam(string Param) - { - byte[] Bytes = ReadParam(Param); - if (Bytes == null) - { - return null; - } - - return System.Text.Encoding.ASCII.GetString(Bytes).Trim(['\0']); - } - - [Flags] - internal enum Fuse - { - SecureBoot = 1, - FfuVerify = 2, - Jtag = 4, - Shk = 8, - Simlock = 16, - ProductionDone = 32, - Rkh = 64, - PublicId = 128, - Dak = 256, - SecGen = 512, - OemId = 1024, - FastBoot = 2048, - SpdmSecMode = 4096, - RpmWdog = 8192, - Ssm = 16384 - } - - public bool? ReadFuseStatus(Fuse fuse) - { - uint? flags = ReadSecurityFlags(); - if (!flags.HasValue) - { - return null; - } - - var finalconfig = (Fuse)flags.Value; - return finalconfig.HasFlag(fuse); - } - - public uint? ReadSecurityFlags() - { - byte[] Response = ReadParam("FCS"); - if ((Response == null) || (Response.Length != 4)) - { - return null; - } - - // This value is in big endian - return (UInt32)((Response[0] << 24) | (Response[1] << 16) | (Response[2] << 8) | Response[3]); - } - - public uint? ReadCurrentChargeLevel() - { - byte[] Response = ReadParam("CS"); - if ((Response == null) || (Response.Length != 8)) - { - return null; - } - - // This value is in big endian - return (UInt32)((Response[0] << 24) | (Response[1] << 16) | (Response[2] << 8) | Response[3]) + 1; - } - - public int? ReadCurrentChargeCurrent() - { - byte[] Response = ReadParam("CS"); - if ((Response == null) || (Response.Length != 8)) - { - return null; - } - - // This value is in big endian and needs to be XOR'd with 0xFFFFFFFF - return (Int32)(((Response[4] << 24) | (Response[5] << 16) | (Response[6] << 8) | Response[7]) ^ 0xFFFFFFFF) + 1; - } - - public UefiSecurityStatusResponse ReadSecurityStatus() - { - if (_SecurityStatus != null) - { - return _SecurityStatus; - } - - byte[] Response = ReadParam("SS"); - if (Response == null) - { - return null; - } - - UefiSecurityStatusResponse Result = new(); - - Result.IsTestDevice = Response[0]; - Result.PlatformSecureBootStatus = Convert.ToBoolean(Response[1]); - Result.SecureFfuEfuseStatus = Convert.ToBoolean(Response[2]); - Result.DebugStatus = Convert.ToBoolean(Response[3]); - Result.RdcStatus = Convert.ToBoolean(Response[4]); - Result.AuthenticationStatus = Convert.ToBoolean(Response[5]); - Result.UefiSecureBootStatus = Convert.ToBoolean(Response[6]); - Result.CryptoHardwareKey = Convert.ToBoolean(Response[7]); - - _SecurityStatus = Result; - - return Result; - } - - internal bool? IsBootLoaderUnlocked() - { - UefiSecurityStatusResponse SecurityStatus = ReadSecurityStatus(); - if (SecurityStatus != null) - { - return SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus; - } - - return null; - } - - public TerminalResponse GetTerminalResponse() - { - byte[] AsskMask = [1, 0, 16, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64]; - byte[] Request = new byte[0xAC]; - const string Header = "NOKXFT"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Request[7] = 1; - Buffer.BlockCopy(BigEndian.GetBytes(0x18, 4), 0, Request, 0x08, 4); // Subblocktype = 0x18 - Buffer.BlockCopy(BigEndian.GetBytes(0x9C, 4), 0, Request, 0x0C, 4); // Subblocklength = 0x9C - Buffer.BlockCopy(BigEndian.GetBytes(0x00, 4), 0, Request, 0x10, 4); // AsicIndex = 0x00 - Buffer.BlockCopy(AsskMask, 0, Request, 0x14, 0x10); - byte[] TerminalResponse = ExecuteRawMethod(Request); - if ((TerminalResponse?.Length > 0x20) && (BigEndian.ToUInt32(TerminalResponse, 0x14) == (TerminalResponse.Length - 0x18)) && (BitConverter.ToUInt32(TerminalResponse, 0x1C) == (TerminalResponse.Length - 0x20))) - { - // Parse Terminal Response from offset 0x18 - return Terminal.Parse(TerminalResponse, 0x18); - } - else - { - return null; - } - } - - public void SendFfuHeaderV1(byte[] FfuHeader, byte Options = 0) - { - byte[] Request = new byte[FfuHeader.Length + 0x20]; - - const string Header = "NOKXFS"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Buffer.BlockCopy(BigEndian.GetBytes(0x0001, 2), 0, Request, 0x06, 2); // Protocol version = 0x0001 - Request[0x08] = 0; // Progress = 0% - Request[0x0B] = 1; // Subblock count = 1 - Buffer.BlockCopy(BigEndian.GetBytes(0x0000000B, 4), 0, Request, 0x0C, 4); // Subblock type for header = 0x0B - Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length + 0x0C, 4), 0, Request, 0x10, 4); // Subblock length = length of header + 0x0C - Buffer.BlockCopy(BigEndian.GetBytes(0x00000000, 4), 0, Request, 0x14, 4); // Header type = 0 - Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length, 4), 0, Request, 0x18, 4); // Payload length = length of header - Request[0x1C] = Options; // Header options = 0 - - Buffer.BlockCopy(FfuHeader, 0, Request, 0x20, FfuHeader.Length); - - byte[] Response = ExecuteRawMethod(Request); - if (Response == null) - { - throw new BadConnectionException(); - } - - int ResultCode = (Response[6] << 8) + Response[7]; - if (ResultCode != 0) - { - ThrowFlashError(ResultCode); - } - } - - public void SendFfuHeaderV2(UInt32 TotalHeaderLength, UInt32 OffsetForThisPart, byte[] FfuHeader, byte Options = 0) - { - byte[] Request = new byte[FfuHeader.Length + 0x3C]; - - const string Header = "NOKXFS"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolSyncV2, 2), 0, Request, 0x06, 2); // Protocol version = 0x0001 - Request[0x08] = 0; // Progress = 0% - Request[0x0B] = 1; // Subblock count = 1 - - Buffer.BlockCopy(BigEndian.GetBytes(0x00000021, 4), 0, Request, 0x0C, 4); // Subblock type for header v2 = 0x21 - Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length + 0x28, 4), 0, Request, 0x10, 4); // Subblock starts at 0x14, payload starts at 0x3C. - - Buffer.BlockCopy(BigEndian.GetBytes(0x00000000, 4), 0, Request, 0x14, 4); // Header type = 0 - Buffer.BlockCopy(BigEndian.GetBytes(TotalHeaderLength, 4), 0, Request, 0x18, 4); // Payload length = length of header - Request[0x1C] = Options; // Header options = 0 - - Buffer.BlockCopy(BigEndian.GetBytes(OffsetForThisPart, 4), 0, Request, 0x1D, 4); - Buffer.BlockCopy(BigEndian.GetBytes(FfuHeader.Length, 4), 0, Request, 0x21, 4); - Request[0x25] = 0; // No Erase - - Buffer.BlockCopy(FfuHeader, 0, Request, 0x3C, FfuHeader.Length); - - byte[] Response = ExecuteRawMethod(Request); - if (Response == null) - { - throw new BadConnectionException(); - } - - if (Response.Length == 4) - { - throw new WPinternalsException("Flash protocol v2 not supported", "The device reported that the Flash protocol v2 was not supported while sending the FFU header."); - } - - int ResultCode = (Response[6] << 8) + Response[7]; - if (ResultCode != 0) - { - ThrowFlashError(ResultCode); - } - } - - public void SendFfuPayloadV1(byte[] FfuChunk, int Progress = 0, byte Options = 0) - { - byte[] Request = new byte[FfuChunk.Length + 0x1C]; - - const string Header = "NOKXFS"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolSyncV1, 2), 0, Request, 0x06, 2); // Protocol version = 0x0001 - Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100) - Request[0x0B] = 1; // Subblock count = 1 - - Buffer.BlockCopy(BigEndian.GetBytes(0x0000000C, 4), 0, Request, 0x0C, 4); // Subblock type for ChunkData = 0x0C - Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length + 0x08, 4), 0, Request, 0x10, 4); // Subblock length = length of chunk + 0x08 - - Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length, 4), 0, Request, 0x14, 4); // Payload length = length of chunk - Request[0x18] = Options; // Data options = 0 (1 = verify) - - Buffer.BlockCopy(FfuChunk, 0, Request, 0x1C, FfuChunk.Length); - - byte[] Response = ExecuteRawMethod(Request); - if (Response == null) - { - throw new BadConnectionException(); - } - - int ResultCode = (Response[6] << 8) + Response[7]; - if (ResultCode != 0) - { - ThrowFlashError(ResultCode); - } - } - - public void SendFfuPayloadV2(byte[] FfuChunk, int Progress = 0, byte Options = 0) - { - byte[] Request = new byte[FfuChunk.Length + 0x20]; - - const string Header = "NOKXFS"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolSyncV2, 2), 0, Request, 0x06, 2); // Protocol - Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100) - Request[0x0B] = 1; // Subblock count = 1 - - Buffer.BlockCopy(BigEndian.GetBytes(0x0000001B, 4), 0, Request, 0x0C, 4); // Subblock type for Payload v2 = 0x1B - Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length + 0x0C, 4), 0, Request, 0x10, 4); // Subblock length = length of chunk + 0x08 - - Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length, 4), 0, Request, 0x14, 4); // Payload length = length of chunk - Request[0x18] = Options; // Data options = 0 (1 = verify) - - Buffer.BlockCopy(FfuChunk, 0, Request, 0x20, FfuChunk.Length); - - byte[] Response = ExecuteRawMethod(Request); - if (Response == null) - { - throw new BadConnectionException(); - } - - int ResultCode = (Response[6] << 8) + Response[7]; - if (ResultCode != 0) - { - ThrowFlashError(ResultCode); - } - } - - public void SendFfuPayloadV3(byte[] FfuChunk, UInt32 WriteDescriptorIndex, UInt32 CRC, int Progress = 0, byte Options = 0) - { - byte[] Request = new byte[FfuChunk.Length + 0x20]; - - const string Header = "NOKXFS"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Buffer.BlockCopy(BigEndian.GetBytes((int)FfuProtocol.ProtocolAsyncV3, 2), 0, Request, 0x06, 2); // Protocol - Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100) - Request[0x0B] = 1; // Subblock count = 1 - - Buffer.BlockCopy(BigEndian.GetBytes(0x0000001D, 4), 0, Request, 0x0C, 4); // Subblock type for Payload v2 = 0x1B - Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length + 0x2C, 4), 0, Request, 0x10, 4); // Subblock length = length of chunk + 0x08 - - Buffer.BlockCopy(BigEndian.GetBytes(FfuChunk.Length, 4), 0, Request, 0x14, 4); // Payload length = length of chunk - Request[0x18] = Options; // Data options = 0 (1 = verify) - Buffer.BlockCopy(BigEndian.GetBytes(WriteDescriptorIndex, 4), 0, Request, 0x19, 4); // Payload length = length of chunk - Buffer.BlockCopy(BigEndian.GetBytes(CRC, 4), 0, Request, 0x1D, 4); // Payload length = length of chunk - - Buffer.BlockCopy(FfuChunk, 0, Request, 0x40, FfuChunk.Length); - - byte[] Response = ExecuteRawMethod(Request); - if (Response == null) - { - throw new BadConnectionException(); - } - - int ResultCode = (Response[6] << 8) + Response[7]; - if (ResultCode != 0) - { - ThrowFlashError(ResultCode); - } - } - - public void BackupPartitionToRam(string PartitionName) - { - PartitionName = PartitionName.ToUpper(); - if (new string[] { "MODEM_FSG", "MODEM_FS1", "MODEM_FS2", "SSD", "DPP" }.Any(s => s == PartitionName)) - { - byte[] Request = new byte[84]; - const string Header = "NOKXFB"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Request[0x07] = 1; // Subblock count = 1 - Request[0x08] = 6; // Subblock ID = 6 = Create Partition Backup to RAM - Buffer.BlockCopy(BigEndian.GetBytes(73, 2), 0, Request, 0x09, 2); // Subblock length = Length of data in subblock including fillers (subblock-ID-field and subblock-length-field are not counted for the subblock-length) - System.Text.Encoding.Unicode.GetBytes(PartitionName); - - byte[] PartitionBytes = System.Text.Encoding.Unicode.GetBytes(PartitionName); - Buffer.BlockCopy(PartitionBytes, 0, Request, 0x0C, PartitionBytes.Length); - Request[0x0C + PartitionBytes.Length + 0x00] = 0; // Trailing zero - Request[0x0C + PartitionBytes.Length + 0x01] = 0; - - byte[] Response = ExecuteRawMethod(Request); - int ResultCode = (Response[6] << 8) + Response[7]; - if (ResultCode != 0) - { - ThrowFlashError(ResultCode); - } - } - else - { - throw new WPinternalsException("Specified partition cannot be backupped to RAM", "Partition name: \"" + PartitionName + "\"."); - } - } - - public void LoadMmosBinary(UInt32 TotalLength, UInt32 Offset, bool SkipMmosSupportCheck, byte[] MmosPart) - { - byte[] Request = new byte[MmosPart.Length + 0x20]; - const string Header = "NOKXFL"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Request[0x07] = 1; // Subblock count = 1 - Buffer.BlockCopy(BigEndian.GetBytes(0x0000001E, 4), 0, Request, 0x08, 4); // Subblock ID = Load MMOS Binary = 0x1E - Buffer.BlockCopy(BigEndian.GetBytes(MmosPart.Length + 0x10, 4), 0, Request, 0x0C, 4); // Subblock length = Payload-length + 0x10 - Buffer.BlockCopy(BigEndian.GetBytes(TotalLength, 4), 0, Request, 0x10, 4); - Buffer.BlockCopy(BigEndian.GetBytes(Offset, 4), 0, Request, 0x14, 4); - if (SkipMmosSupportCheck) - { - Request[0x18] = 1; - } - - Buffer.BlockCopy(BigEndian.GetBytes(MmosPart.Length, 4), 0, Request, 0x1C, 4); - Buffer.BlockCopy(MmosPart, 0, Request, 0x20, MmosPart.Length); - - byte[] Response = ExecuteRawMethod(Request); - int ResultCode = (Response[6] << 8) + Response[7]; - if (ResultCode != 0) - { - ThrowFlashError(ResultCode); - } - } - - internal void SwitchToMmosContext() - { - byte[] Request = new byte[7]; - ByteOperations.WriteAsciiString(Request, 0, "NOKXCBA"); - ExecuteRawVoidMethod(Request); - - ResetDevice(); - - Dispose(true); - } - - private void ThrowFlashError(int ErrorCode) - { - string SubMessage = ErrorCode switch - { - 0x0008 => "Unsupported protocol / Invalid options", - 0x000F => "Invalid sub block count", - 0x0010 => "Invalid sub block length", - 0x0012 => "Authentication required", - 0x000E => "Invalid sub block type", - 0x0013 => "Failed async message", - 0x1000 => "Invalid header type", - 0x1001 => "FFU header contain unknown extra data", - 0x0001 => "Couldn't allocate memory", - 0x1106 => "Security header validation failed", - 0x1105 => "Invalid hash table size", - 0x1104 => "Invalid catalog size", - 0x1103 => "Invalid chunk size", - 0x1102 => "Unsupported algorithm", - 0x1101 => "Invalid struct size", - 0x1100 => "Invalid signature", - 0x1202 => "Invalid struct size", - 0x1203 => "Unsupported algorithm", - 0x1204 => "Invalid chunk size", - 0x1005 => "Data not aligned correctly", - 0x0009 => "Locate protocol failed", - 0x1003 => "Hash mismatch", - 0x1006 => "Couldn't find hash from security header for index", - 0x1004 => "Security header import missing / All FFU headers have not been imported", - 0x1304 => "Invalid platform ID", - 0x1307 => "Invalid write descriptor info", - 0x1306 => "Invalid write descriptor info", - 0x1305 => "Invalid block size", - 0x1303 => "Unsupported FFU version", - 0x1302 => "Unsupported struct version", - 0x1301 => "Invalid update type", - 0x100B => "Too much payload data, all data has already been written", - 0x1008 => "Internal error", - 0x1007 => "Payload data does not contain all data", - 0x0004 => "Flash write failed", - 0x000D => "Flash verify failed", - 0x0002 => "Flash read failed", - _ => "Unknown error", - }; - WPinternalsException Ex = new("Flash failed!"); - Ex.SubMessage = "Error 0x" + ErrorCode.ToString("X4") + ": " + SubMessage; - - throw Ex; - } - - public void FlashFFU(string FFUPath, bool ResetAfterwards = true, byte Options = 0) - { - FlashFFU(new FFU(FFUPath), ResetAfterwards, Options); - } - - public void FlashFFU(FFU FFU, bool ResetAfterwards = true, byte Options = 0) - { - FlashFFU(FFU, null, ResetAfterwards, Options); - } - - public void FlashFFU(FFU FFU, ProgressUpdater UpdaterPerChunk, bool ResetAfterwards = true, byte Options = 0) - { - LogFile.BeginAction("FlashFFU"); - - ProgressUpdater Progress = UpdaterPerChunk; - - PhoneInfo Info = ReadPhoneInfo(); - if ((Info.SecureFfuSupportedProtocolMask & ((ushort)FfuProtocol.ProtocolSyncV1 | (ushort)FfuProtocol.ProtocolSyncV2)) == 0) - { - throw new WPinternalsException("Flash failed!", "Protocols not supported. The device reports that both Protocol Sync v1 and Protocol Sync v2 are not supported for FFU flashing. Is this an old device?"); - } - - UInt64 CombinedFFUHeaderSize = FFU.HeaderSize; - byte[] FfuHeader = new byte[CombinedFFUHeaderSize]; - using (FileStream FfuFile = new(FFU.Path, FileMode.Open, FileAccess.Read)) - { - FfuFile.Read(FfuHeader, 0, (int)CombinedFFUHeaderSize); - SendFfuHeaderV1(FfuHeader, Options); - - UInt64 Position = CombinedFFUHeaderSize; - byte[] Payload; - int ChunkCount = 0; - - if ((Info.SecureFfuSupportedProtocolMask & (ushort)FfuProtocol.ProtocolSyncV2) == 0) - { - // Protocol v1 - Payload = new byte[FFU.ChunkSize]; - - while (Position < (UInt64)FfuFile.Length) - { - FfuFile.Read(Payload, 0, Payload.Length); - ChunkCount++; - SendFfuPayloadV1(Payload, (int)((double)ChunkCount * 100 / FFU.TotalChunkCount), 0); - Position += (ulong)Payload.Length; - - Progress?.IncreaseProgress(1); - } - } - else - { - // Protocol v2 - Payload = new byte[Info.WriteBufferSize]; - - while (Position < (UInt64)FfuFile.Length) - { - UInt32 PayloadSize = Info.WriteBufferSize; - if (((UInt64)FfuFile.Length - Position) < PayloadSize) - { - PayloadSize = (UInt32)((UInt64)FfuFile.Length - Position); - Payload = new byte[PayloadSize]; - } - - FfuFile.Read(Payload, 0, (int)PayloadSize); - ChunkCount += (int)(PayloadSize / FFU.ChunkSize); - SendFfuPayloadV2(Payload, (int)((double)ChunkCount * 100 / FFU.TotalChunkCount), 0); - Position += PayloadSize; - - Progress?.IncreaseProgress((ulong)(PayloadSize / FFU.ChunkSize)); - } - } - } - - if (ResetAfterwards) - { - ResetPhone(); - } - - LogFile.EndAction("FlashFFU"); - } - - public void FlashMMOS(string MMOSPath, ProgressUpdater UpdaterPerChunk) - { - LogFile.BeginAction("FlashMMOS"); - - ProgressUpdater Progress = UpdaterPerChunk; - - PhoneInfo Info = ReadPhoneInfo(); - if (!Info.MmosOverUsbSupported) - { - throw new WPinternalsException("Flash failed!", "Protocols not supported. The device reports that loading Microsoft Manufacturing Operating System over USB is not supported."); - } - - FileInfo info = new(MMOSPath); - uint length = uint.Parse(info.Length.ToString()); - - int offset = 0; - const int maximumbuffersize = 0x00240000; - - uint totalcounts = (uint)Math.Truncate((decimal)length / maximumbuffersize); - - using (FileStream MMOSFile = new(MMOSPath, FileMode.Open, FileAccess.Read)) - { - for (int i = 1; i <= (uint)Math.Truncate((decimal)length / maximumbuffersize); i++) - { - Progress.IncreaseProgress(1); - byte[] data = new byte[maximumbuffersize]; - MMOSFile.Read(data, 0, maximumbuffersize); - - LoadMmosBinary(length, (uint)offset, false, data); - - offset += maximumbuffersize; - } - - if (length - offset != 0) - { - Progress.IncreaseProgress(1); - - byte[] data = new byte[length - offset]; - MMOSFile.Read(data, 0, (int)(length - offset)); - LoadMmosBinary(length, (uint)offset, false, data); - } - - SwitchToMmosContext(); - ResetPhone(); - } - - LogFile.EndAction("FlashMMOS"); - } - - public void FlashSectors(UInt32 StartSector, byte[] Data, int Progress = 0) - { - // Start sector is in UInt32, so max size of eMMC is 2 TB. - - byte[] Request = new byte[Data.Length + 0x40]; - - const string Header = "NOKF"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Request[0x05] = 0; // Device type = 0 - Buffer.BlockCopy(BigEndian.GetBytes(StartSector, 4), 0, Request, 0x0B, 4); // Start sector - Buffer.BlockCopy(BigEndian.GetBytes(Data.Length / 0x200, 4), 0, Request, 0x0F, 4); // Sector count - Request[0x13] = (byte)Progress; // Progress (0 - 100) - Request[0x18] = 0; // Do Verify - Request[0x19] = 0; // Is Test - - Buffer.BlockCopy(Data, 0, Request, 0x40, Data.Length); - - ExecuteRawMethod(Request); - } - - public void DisableRebootTimeOut() - { - byte[] Request = new byte[4]; - const string Header = "NOKD"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - ExecuteRawMethod(Request); - } - - public void Shutdown() - { - byte[] Request = new byte[4]; - const string Header = "NOKZ"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - ExecuteRawMethod(Request); - } - - public FlashVersion GetFlashVersion() - { - byte[] Response = ReadParam("FAI"); - if ((Response == null) || (Response.Length < 6)) - { - return null; - } - - FlashVersion Result = new(); - - Result.ProtocolMajor = Response[1]; - Result.ProtocolMinor = Response[2]; - Result.ApplicationMajor = Response[3]; - Result.ApplicationMinor = Response[4]; - - return Result; - } - - internal GPT ReadGPT() - { - // If this function is used with a locked BootMgr v1, - // then the mode-switching should be done outside this function, - // because the context-switches that are used here are not supported on BootMgr v1. - - // Only works in BootLoader-mode or on unlocked bootloaders in Flash-mode!! - - PhoneInfo Info = ReadPhoneInfo(ExtendedInfo: false); - FlashAppType OriginalAppType = Info.App; - bool Switch = (Info.App != FlashAppType.BootManager) && Info.IsBootloaderSecure; - if (Switch) - { - SwitchToBootManagerContext(); - } - - byte[] Request = new byte[0x04]; - const string Header = "NOKT"; - - System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - - byte[] Buffer = ExecuteRawMethod(Request); - if ((Buffer == null) || (Buffer.Length < 0x4408)) - { - throw new InvalidOperationException("Unable to read GPT!"); - } - - UInt16 Error = (UInt16)((Buffer[6] << 8) + Buffer[7]); - if (Error > 0) - { - throw new NotSupportedException("ReadGPT: Error 0x" + Error.ToString("X4")); - } - - byte[] GPTBuffer = new byte[Buffer.Length - 0x208]; - System.Buffer.BlockCopy(Buffer, 0x208, GPTBuffer, 0, 0x4200); - - if (Switch) - { - if (OriginalAppType == FlashAppType.FlashApp) - { - SwitchToFlashAppContext(); - } - else - { - SwitchToPhoneInfoAppContext(); - } - } - - return new GPT(GPTBuffer); // NOKT message header and MBR are ignored - } - - internal void WriteGPT(GPT NewGPT) - { - bool? unlocked = IsBootLoaderUnlocked(); - if (unlocked == false) - { - throw new InvalidOperationException("Bootloader is not unlocked!"); - } - - byte[] Buffer = NewGPT.Rebuild(); - - UInt32? HeaderOffset = ByteOperations.FindAscii(Buffer, "EFI PART"); - if (HeaderOffset != 0) - { - throw new BadImageFormatException(); - } - - FlashSectors(1, Buffer); - } - - internal void FlashRawPartition(string Path, string PartitionName) - { - FlashRawPartition(Path, null, PartitionName, null, null); - } - - internal void FlashRawPartition(string Path, string PartitionName, Action ProgressUpdateCallback) - { - FlashRawPartition(Path, null, PartitionName, ProgressUpdateCallback, null); - } - - internal void FlashRawPartition(string Path, string PartitionName, ProgressUpdater UpdaterPerSector) - { - FlashRawPartition(Path, null, PartitionName, null, UpdaterPerSector); - } - - internal void FlashRawPartition(Stream Stream, string PartitionName, ProgressUpdater UpdaterPerSector) - { - FlashRawPartition(null, Stream, PartitionName, null, UpdaterPerSector); - } - - internal void FlashRawPartition(byte[] Buffer, string PartitionName, ProgressUpdater UpdaterPerSector) - { - FlashRawPartition(null, new MemoryStream(Buffer, false), PartitionName, null, UpdaterPerSector); - } - - private void FlashRawPartition(string Path, Stream Stream, string PartitionName, Action ProgressUpdateCallback, ProgressUpdater UpdaterPerSector) - { - bool? unlocked = IsBootLoaderUnlocked(); - if (unlocked == false) - { - throw new InvalidOperationException("Bootloader is not unlocked!"); - } - - GPT GPT = ReadGPT(); - - Partition Partition = GPT.Partitions.First((p) => p.Name == PartitionName); - ulong PartitionSize = (Partition.LastSector - Partition.FirstSector + 1) * 0x200; - - Stream InputStream = null; - - if (Path != null) - { - InputStream = new DecompressedStream(File.Open(Path, FileMode.Open)); - } - else if (Stream != null) - { - InputStream = Stream is DecompressedStream ? Stream : new DecompressedStream(Stream); - } - - if (InputStream != null) - { - using (InputStream) - { - UInt64? InputStreamLength = null; - try - { - InputStreamLength = (UInt64)InputStream.Length; - } - catch (Exception ex) - { - LogFile.LogException(ex, LogType.FileOnly); - } - - if ((InputStreamLength != null) && ((UInt64)InputStream.Length > PartitionSize)) - { - throw new InvalidOperationException("Partition can not be flashed, because its size is too big!"); - } - - ProgressUpdater Progress = UpdaterPerSector; - if ((Progress == null) && (ProgressUpdateCallback != null) && (InputStreamLength != null)) - { - Progress = new ProgressUpdater((UInt64)(InputStreamLength / 0x200), ProgressUpdateCallback); - } - - int ProgressPercentage = 0; - - const int FlashBufferSize = 0x200000; // Flash 8 GB phone -> buffersize 0x200000 = 11:45 min, buffersize 0x20000 = 12:30 min - byte[] FlashBuffer = new byte[FlashBufferSize]; - int BytesRead; - UInt64 i = 0; - do - { - BytesRead = InputStream.Read(FlashBuffer, 0, FlashBufferSize); - - byte[] FlashBufferFinalSize; - if (BytesRead > 0) - { - if (BytesRead == FlashBufferSize) - { - FlashBufferFinalSize = FlashBuffer; - } - else - { - FlashBufferFinalSize = new byte[BytesRead]; - Buffer.BlockCopy(FlashBuffer, 0, FlashBufferFinalSize, 0, BytesRead); - } - - FlashSectors((UInt32)(Partition.FirstSector + (i / 0x200)), FlashBufferFinalSize, ProgressPercentage); - } - - if (Progress != null) - { - Progress.IncreaseProgress((UInt64)FlashBuffer.Length / 0x200); - ProgressPercentage = Progress.ProgressPercentage; - } - - i += FlashBufferSize; - } - while (BytesRead == FlashBufferSize); - } - } - } - - internal void ErasePartition(string PartitionName) - { - // Partition "Data" can always be erased. - // Other partitions can only be erased when a valid RDC certificate is present or full SX authentication was performed. - if (PartitionName.Length > 0x23) - { - throw new ArgumentException("PartitionName cannot exceed 0x23 chars!"); - } - - byte[] Request = new byte[0x50]; - const string Header = "NOKXFP"; - Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - Request[0x06] = 1; // Protocol version must be 1 - Request[0x07] = 0; // Device type = 0 - - byte[] PartitionBytes = System.Text.Encoding.Unicode.GetBytes(PartitionName); - Buffer.BlockCopy(PartitionBytes, 0, Request, 8, PartitionBytes.Length); - Request[0x08 + PartitionBytes.Length + 0x00] = 0; // Trailing zero - Request[0x08 + PartitionBytes.Length + 0x01] = 0; - - ExecuteRawMethod(Request); - } - - internal FlashAppType GetFlashAppType() - { - byte[] Request = new byte[4]; - ByteOperations.WriteAsciiString(Request, 0, "NOKV"); - byte[] Response = ExecuteRawMethod(Request); - if ((Response == null) || (ByteOperations.ReadAsciiString(Response, 0, 4) == "NOKU")) - { - throw new NotSupportedException(); - } - - return (FlashAppType)Response[5]; - } - - internal PhoneInfo ReadPhoneInfo(bool ExtendedInfo = true) - { - // 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, "NOKV"); - 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 0x04: - if (Result.App == FlashAppType.BootManager) - { - Result.FlashAppProtocolVersionMajor = Response[SubblockPayloadOffset + 0x00]; - Result.FlashAppProtocolVersionMinor = Response[SubblockPayloadOffset + 0x01]; - Result.FlashAppVersionMajor = Response[SubblockPayloadOffset + 0x02]; - Result.FlashAppVersionMinor = Response[SubblockPayloadOffset + 0x03]; - } - else if (Result.App == FlashAppType.FlashApp) - { - Result.SdCardSizeInSectors = 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; - } - - if (ExtendedInfo && (Result.State == PhoneInfoState.Basic)) - { - FlashAppType OriginalType = Result.App; - - try - { - if (Result.BootManagerProtocolVersionMajor >= 2) - { - SwitchToPhoneInfoAppContext(); // May throw NotSupportedException - - Result.Type = ReadPhoneInfoVariable("TYPE"); - Result.ProductCode = ReadPhoneInfoVariable("CTR"); - Result.Imei = ReadPhoneInfoVariable("IMEI"); - - SwitchToFlashAppContext(); - DisableRebootTimeOut(); - } - else if (OriginalType == FlashAppType.PhoneInfoApp) - { - Result.Type = ReadPhoneInfoVariable("TYPE"); - Result.ProductCode = ReadPhoneInfoVariable("CTR"); - Result.Imei = ReadPhoneInfoVariable("IMEI"); - } - else - { - //SwitchToPhoneInfoAppContextLegacy(); - - //SwitchAwayToPhoneInfoAppContextLegacy(); - } - } - catch (Exception ex) - { - LogFile.LogException(ex, LogType.FileOnly); - } - - if (Result.App == FlashAppType.FlashApp) - { - Result.Firmware = ReadStringParam("FVER"); - Result.RKH = ReadParam("RRKH"); - } - - try - { - if (OriginalType == FlashAppType.BootManager) - { - SwitchToBootManagerContext(); - } - } - catch (Exception ex) - { - LogFile.LogException(ex, LogType.FileOnly); - } - - Result.State = PhoneInfoState.Extended; - } - - Result.IsBootloaderSecure = !(Info.Authenticated || Info.RdcPresent || !Info.SecureFfuEnabled); - - if (!PhoneInfoLogged) - { - Result.Log(LogType.FileOnly); - } - - return Result; - } - - internal void ResetPhone() - { - LogFile.Log("Rebooting phone", LogType.FileAndConsole); - try - { - byte[] Request = new byte[4]; - ByteOperations.WriteAsciiString(Request, 0, "NOKR"); - ExecuteRawVoidMethod(Request); - } - catch - { - LogFile.Log("Sending reset-request failed", LogType.FileOnly); - LogFile.Log("Assuming automatic reset already in progress", LogType.FileOnly); - } - } - - internal void ContinueBoot() - { - LogFile.Log("Continue boot..."); - byte[] Request = new byte[4]; - ByteOperations.WriteAsciiString(Request, 0, "NOKA"); - ExecuteRawVoidMethod(Request); - } - - internal void ResetPhoneToFlashMode() - { - // This only works when the phone is in BootMgr mode. If it is already in FlashApp, it will not reboot. It only makes the phone unresponsive. - LogFile.Log("Rebooting phone to Flash mode..."); - byte[] Request = new byte[4]; - ByteOperations.WriteAsciiString(Request, 0, "NOKS"); - ExecuteRawVoidMethod(Request); - } - - internal void Hello() - { - byte[] Request = new byte[4]; - ByteOperations.WriteAsciiString(Request, 0, "NOKI"); - byte[] Response = ExecuteRawMethod(Request); - if (Response == null) - { - throw new BadConnectionException(); - } - - if (ByteOperations.ReadAsciiString(Response, 0, 4) != "NOKI") - { - throw new WPinternalsException("Bad response from phone!", "The phone did not answer properly to the Hello message sent."); - } - } - - internal UInt16 ReadSecureFfuSupportedProtocolMask() - { - return BigEndian.ToUInt16(ReadParam("SFPI"), 0); - } - - internal void SwitchToPhoneInfoAppContext() - { - byte[] Request = new byte[7]; - ByteOperations.WriteAsciiString(Request, 0, "NOKXCBP"); - byte[] Response = ExecuteRawMethod(Request); - if (ByteOperations.ReadAsciiString(Response, 0, 4) == "NOKU") - { - throw new NotSupportedException(); - } - - UInt16 Error = (UInt16)((Response[6] << 8) + Response[7]); - if (Error > 0) - { - throw new NotSupportedException("SwitchToPhoneInfoAppContext: Error 0x" + Error.ToString("X4")); - } - - DisableRebootTimeOut(); - Info.App = FlashAppType.PhoneInfoApp; - InterfaceChanged(PhoneInterfaces.Lumia_Flash); - } - - internal void SwitchToPhoneInfoAppContextLegacy() - { - byte[] Request = new byte[4]; - ByteOperations.WriteAsciiString(Request, 0, "NOKP"); - ExecuteRawVoidMethod(Request); - - //DisableRebootTimeOut(); - Info.App = FlashAppType.PhoneInfoApp; - InterfaceChanged(PhoneInterfaces.Lumia_PhoneInfo); - } - - internal void SwitchAwayToPhoneInfoAppContextLegacy() - { - byte[] Request = new byte[4]; - ByteOperations.WriteAsciiString(Request, 0, "NOKA"); - ExecuteRawVoidMethod(Request); - - //DisableRebootTimeOut(); - Info.App = FlashAppType.FlashApp; - InterfaceChanged(PhoneInterfaces.Lumia_Flash); - } - - internal string GetPhoneInfo() - { - // NOKH = Get Phone Info (IMEI and info from Product.dat) - Not available on some phones, like Lumia 640. - // NOKV = Info Query - - if (Info.FlashAppProtocolVersionMajor >= 2) - { - return null; - } - - byte[] Request = new byte[4]; - ByteOperations.WriteAsciiString(Request, 0, "NOKH"); - byte[] Response = ExecuteRawMethod(Request); - if ((Response == null) || (ByteOperations.ReadAsciiString(Response, 0, 4) == "NOKU")) - { - throw new NotSupportedException(); - } - - UInt16 Length = BigEndian.ToUInt16(Response, 0x04); - - string PhoneInfoData = ByteOperations.ReadAsciiString(Response, 0x8, Length); - - return PhoneInfoData; - } - - internal void SwitchToFlashAppContext() - { - // SwitchToFlashAppContext() should only be used with BootMgr v2 - // For switching from BootMgr to FlashApp, it will use NOKS - // That will switch to a charging state, whereas a normal context switch will not start charging - // The implementation of NOKS in BootMgr mode has changed in BootMgr v2 - // It does not disconnect / reconnect anymore and the apptype is changed immediately - // NOKS still doesnt return a status - - byte[] Request; - - if (Info.State == PhoneInfoState.Empty) - { - ReadPhoneInfo(ExtendedInfo: false); - } - - if (Info.App == FlashAppType.BootManager) - { - if (Info.FlashAppProtocolVersionMajor < 2) - { - // A phone with Bootloader Spec A cannot be switched from BootMgr to FlashApp. - // NOKS will make the phone unresponsive and let you wait for a new arrival, but that would require a PhoneNotifier and that is not available in this context. - // And NOKXCBF is not supported at all. - return; - } - - Request = new byte[4]; - ByteOperations.WriteAsciiString(Request, 0, "NOKS"); // This will let the phone charge - ExecuteRawVoidMethod(Request); // On phone with bootloader Spec A this triggers a reboot, so DisableRebootTimeOut() cannot be called immediately. - } - else if (Info.App == FlashAppType.PhoneInfoApp) - { - Request = new byte[7]; - ByteOperations.WriteAsciiString(Request, 0, "NOKXCBF"); // This will stop charging the phone - byte[] Response = ExecuteRawMethod(Request); - if (ByteOperations.ReadAsciiString(Response, 0, 4) == "NOKU") - { - throw new NotSupportedException(); - } - - UInt16 Error = (UInt16)((Response[6] << 8) + Response[7]); - if (Error > 0) - { - throw new NotSupportedException("SwitchToFlashAppContext: Error 0x" + Error.ToString("X4")); - } - } - - 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); - } - internal void SwitchToBootManagerContext(bool DisableTimeOut = true) { byte[] Request = new byte[7]; - ByteOperations.WriteAsciiString(Request, 0, "NOKXCBB"); + ByteOperations.WriteAsciiString(Request, 0, SwitchModeSignature + "B"); byte[] Response = ExecuteRawMethod(Request); if (ByteOperations.ReadAsciiString(Response, 0, 4) == "NOKU") { @@ -1268,213 +65,99 @@ namespace WPinternals { throw new NotSupportedException("SwitchToBootManagerContext: Error 0x" + Error.ToString("X4")); } - - if (DisableTimeOut) - { - DisableRebootTimeOut(); - } - - Info.App = FlashAppType.BootManager; - InterfaceChanged(PhoneInterfaces.Lumia_Bootloader); } - internal string ReadPhoneInfoVariable(string VariableName) - { - // This function assumes the phone is in Phone Info App context - - byte[] Request = new byte[16]; - ByteOperations.WriteAsciiString(Request, 0, "NOKXPH" + VariableName + "\0"); // BTR or CTR, CTR is public ProductCode - byte[] Response = ExecuteRawMethod(Request); - UInt16 Length = BigEndian.ToUInt16(Response, 6); - return ByteOperations.ReadAsciiString(Response, 8, Length).Trim([' ', '\0']); - } - - internal string ReadProductCode() - { - SwitchToPhoneInfoAppContext(); - string Result = ReadPhoneInfoVariable("CTR"); - SwitchToFlashAppContext(); - return Result; - } - - internal void StartAsyncFlash() - { - byte[] Request = new byte[14]; - ByteOperations.WriteAsciiString(Request, 0, "NOKXFFS"); - Request[8] = 1; // Protocol version must be 1 - Request[9] = 0; // Protocol type must be 0 - ExecuteRawMethod(Request); - } - - internal void EndAsyncFlash() + internal void SwitchToPhoneInfoAppContext() { byte[] Request = new byte[7]; - ByteOperations.WriteAsciiString(Request, 0, "NOKXFFE"); + ByteOperations.WriteAsciiString(Request, 0, SwitchModeSignature + "P"); + byte[] Response = ExecuteRawMethod(Request); + if (ByteOperations.ReadAsciiString(Response, 0, 4) == "NOKU") + { + throw new NotSupportedException(); + } + + UInt16 Error = (UInt16)((Response[6] << 8) + Response[7]); + if (Error > 0) + { + throw new NotSupportedException("SwitchToPhoneInfoAppContext: Error 0x" + Error.ToString("X4")); + } + } + + internal void SwitchToMmosContext() + { + byte[] Request = new byte[7]; + ByteOperations.WriteAsciiString(Request, 0, SwitchModeSignature + "A"); + ExecuteRawVoidMethod(Request); + + ResetDevice(); + + Dispose(true); + } + + internal void SwitchToFlashAppContext() + { + byte[] Request = new byte[7]; + ByteOperations.WriteAsciiString(Request, 0, SwitchModeSignature + "F"); // This will stop charging the phone + byte[] Response = ExecuteRawMethod(Request); + if (ByteOperations.ReadAsciiString(Response, 0, 4) == "NOKU") + { + throw new NotSupportedException(); + } + + UInt16 Error = (UInt16)((Response[6] << 8) + Response[7]); + if (Error > 0) + { + throw new NotSupportedException("SwitchToFlashAppContext: Error 0x" + Error.ToString("X4")); + } + } + + + // + // Normal commands + // + /* NOKD */ + private const string DisableTimeoutsSignature = $"{Signature}D"; + /* NOKI */ + private const string HelloSignature = $"{Signature}I"; + /* NOKV */ + private const string InfoQuerySignature = $"{Signature}V"; + + internal FlashAppType GetFlashAppType() + { + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, InfoQuerySignature); + byte[] Response = ExecuteRawMethod(Request); + if ((Response == null) || (ByteOperations.ReadAsciiString(Response, 0, 4) == "NOKU")) + { + throw new NotSupportedException(); + } + + return (FlashAppType)Response[5]; + } + + public void DisableRebootTimeOut() + { + byte[] Request = new byte[4]; + const string Header = DisableTimeoutsSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); ExecuteRawMethod(Request); } - internal enum SecureBootKeyType : byte + internal void Hello() { - Retail = 0, - Engineering = 1 - } - - internal void ProvisionSecureBootKeys(SecureBootKeyType KeyType) // Only for Flashmode, not BootManager mode. - { - byte[] Request = new byte[8]; - ByteOperations.WriteAsciiString(Request, 0, "NOKXFK"); - Request[6] = 0; // Options - Request[7] = (byte)KeyType; + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, HelloSignature); byte[] Response = ExecuteRawMethod(Request); - UInt32 Status = ByteOperations.ReadUInt32(Response, 6); - if (Status != 0) + if (Response == null) { - ThrowFlashError((int)Status); + throw new BadConnectionException(); + } + + if (ByteOperations.ReadAsciiString(Response, 0, 4) != HelloSignature) + { + throw new WPinternalsException("Bad response from phone!", "The phone did not answer properly to the Hello message sent."); } } } - - internal enum FlashAppType - { - BootManager = 1, - FlashApp = 2, - PhoneInfoApp = 3 - }; - - internal enum PhoneInfoState - { - Empty, - Basic, - Extended - }; - - internal class PhoneInfo - { - public PhoneInfoState State = PhoneInfoState.Empty; - - public string Type; // Extended info - public string ProductCode; // Extended info - public string Imei; // Extended info - public string Firmware; // Extended info - public byte[] RKH; // Extended info - - public FlashAppType App; - - public byte FlashAppVersionMajor; - public byte FlashAppVersionMinor; - public byte FlashAppProtocolVersionMajor; - public byte FlashAppProtocolVersionMinor; - - public byte BootManagerVersionMajor; - public byte BootManagerVersionMinor; - public byte BootManagerProtocolVersionMajor; - public byte BootManagerProtocolVersionMinor; - - public byte PhoneInfoAppVersionMajor; - public byte PhoneInfoAppVersionMinor; - public byte PhoneInfoAppProtocolVersionMajor; - public byte PhoneInfoAppProtocolVersionMinor; - - public UInt32 TransferSize; - public bool MmosOverUsbSupported; - public UInt32 SdCardSizeInSectors; - public UInt32 WriteBufferSize; - public UInt32 EmmcSizeInSectors; - public string PlatformID; - public UInt16 SecureFfuSupportedProtocolMask; - public bool AsyncSupport; - - public bool PlatformSecureBootEnabled; - public bool SecureFfuEnabled; - public bool JtagDisabled; - public bool RdcPresent; - public bool Authenticated; - public bool UefiSecureBootEnabled; - public bool SecondaryHardwareKeyPresent; - - public bool IsBootloaderSecure; - - internal void Log(LogType Type) - { - if (State == PhoneInfoState.Extended) - { - if (this.Type != null) - { - LogFile.Log("Phone type: " + this.Type, Type); - } - - if (ProductCode != null) - { - LogFile.Log("Product code: " + ProductCode, Type); - } - - if (RKH != null) - { - LogFile.Log("Root key hash: " + Converter.ConvertHexToString(RKH, ""), Type); - } - - if (Firmware?.Length > 0) - { - LogFile.Log("Firmware version: " + Firmware, Type); - } - - if (Type != LogType.ConsoleOnly && (Imei != null)) - { - LogFile.Log("IMEI: " + Imei, LogType.FileOnly); - } - } - - switch (App) - { - case FlashAppType.BootManager: - LogFile.Log("Bootmanager: " + BootManagerVersionMajor + "." + BootManagerVersionMinor, Type); - LogFile.Log("Bootmanager protocol: " + BootManagerProtocolVersionMajor + "." + BootManagerProtocolVersionMinor, Type); - LogFile.Log("Flash app: " + FlashAppVersionMajor + "." + FlashAppVersionMinor, Type); - LogFile.Log("Flash protocol: " + FlashAppProtocolVersionMajor + "." + FlashAppProtocolVersionMinor, Type); - break; - case FlashAppType.FlashApp: - LogFile.Log("Flash app: " + FlashAppVersionMajor + "." + FlashAppVersionMinor, Type); - LogFile.Log("Flash protocol: " + FlashAppProtocolVersionMajor + "." + FlashAppProtocolVersionMinor, Type); - break; - case FlashAppType.PhoneInfoApp: - LogFile.Log("Phone info app: " + PhoneInfoAppVersionMajor + "." + PhoneInfoAppVersionMinor, Type); - LogFile.Log("Phone info protocol: " + PhoneInfoAppProtocolVersionMajor + "." + PhoneInfoAppProtocolVersionMinor, Type); - break; - } - - LogFile.Log("SecureBoot: " + ((!PlatformSecureBootEnabled || !UefiSecureBootEnabled) ? "Disabled" : "Enabled") + " (Platform Secure Boot: " + (PlatformSecureBootEnabled ? "Enabled" : "Disabled") + ", UEFI Secure Boot: " + (UefiSecureBootEnabled ? "Enabled" : "Disabled") + ")", Type); - - if ((Type == LogType.ConsoleOnly) || (Type == LogType.FileAndConsole)) - { - LogFile.Log("Flash app security: " + (!IsBootloaderSecure ? "Disabled" : "Enabled"), LogType.ConsoleOnly); - } - - if ((Type == LogType.FileOnly) || (Type == LogType.FileAndConsole)) - { - LogFile.Log("Flash app security: " + (!IsBootloaderSecure ? "Disabled" : "Enabled") + " (FFU security: " + (SecureFfuEnabled ? "Enabled" : "Disabled") + ", RDC: " + (RdcPresent ? "Present" : "Not found") + ", Authenticated: " + (Authenticated ? "True" : "False") + ")", LogType.FileOnly); - } - - LogFile.Log("JTAG: " + (JtagDisabled ? "Disabled" : "Enabled"), Type); - } - } - - internal class UefiSecurityStatusResponse - { - public byte IsTestDevice; - public bool PlatformSecureBootStatus; - public bool SecureFfuEfuseStatus; - public bool DebugStatus; - public bool RdcStatus; - public bool AuthenticationStatus; - public bool UefiSecureBootStatus; - public bool CryptoHardwareKey; - } - - internal class FlashVersion - { - public int ApplicationMajor; - public int ApplicationMinor; - public int ProtocolMajor; - public int ProtocolMinor; - } } diff --git a/WPinternals/Models/NokiaPhoneModel.cs b/WPinternals/Models/NokiaPhoneModel.cs index 1e9626e..2fab9e6 100644 --- a/WPinternals/Models/NokiaPhoneModel.cs +++ b/WPinternals/Models/NokiaPhoneModel.cs @@ -176,6 +176,11 @@ namespace WPinternals return null; } + if (Token.Value.ValueKind == JsonValueKind.String) + { + return Token.Value.GetString().Equals("true", StringComparison.InvariantCultureIgnoreCase); + } + return Token.Value.GetBoolean(); } diff --git a/WPinternals/Models/PhoneInfo.cs b/WPinternals/Models/PhoneInfo.cs new file mode 100644 index 0000000..d8c532a --- /dev/null +++ b/WPinternals/Models/PhoneInfo.cs @@ -0,0 +1,134 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; + +namespace WPinternals +{ + internal class PhoneInfo + { + public PhoneInfoState State = PhoneInfoState.Empty; + + public string Type; // Extended info + public string ProductCode; // Extended info + public string Imei; // Extended info + public string Firmware; // Extended info + public byte[] RKH; // Extended info + + public FlashAppType App; + + public byte FlashAppVersionMajor; + public byte FlashAppVersionMinor; + public byte FlashAppProtocolVersionMajor; + public byte FlashAppProtocolVersionMinor; + + public byte BootManagerVersionMajor; + public byte BootManagerVersionMinor; + public byte BootManagerProtocolVersionMajor; + public byte BootManagerProtocolVersionMinor; + + public byte PhoneInfoAppVersionMajor; + public byte PhoneInfoAppVersionMinor; + public byte PhoneInfoAppProtocolVersionMajor; + public byte PhoneInfoAppProtocolVersionMinor; + + public UInt32 TransferSize; + public bool MmosOverUsbSupported; + public UInt32 SdCardSizeInSectors; + public UInt32 WriteBufferSize; + public UInt32 EmmcSizeInSectors; + public string PlatformID; + public UInt16 SecureFfuSupportedProtocolMask; + public bool AsyncSupport; + + public bool PlatformSecureBootEnabled; + public bool SecureFfuEnabled; + public bool JtagDisabled; + public bool RdcPresent; + public bool Authenticated; + public bool UefiSecureBootEnabled; + public bool SecondaryHardwareKeyPresent; + + public bool IsBootloaderSecure; + + internal void Log(LogType Type) + { + if (State == PhoneInfoState.Extended) + { + if (this.Type != null) + { + LogFile.Log("Phone type: " + this.Type, Type); + } + + if (ProductCode != null) + { + LogFile.Log("Product code: " + ProductCode, Type); + } + + if (RKH != null) + { + LogFile.Log("Root key hash: " + Converter.ConvertHexToString(RKH, ""), Type); + } + + if (Firmware?.Length > 0) + { + LogFile.Log("Firmware version: " + Firmware, Type); + } + + if (Type != LogType.ConsoleOnly && (Imei != null)) + { + LogFile.Log("IMEI: " + Imei, LogType.FileOnly); + } + } + + switch (App) + { + case FlashAppType.BootManager: + LogFile.Log("Bootmanager: " + BootManagerVersionMajor + "." + BootManagerVersionMinor, Type); + LogFile.Log("Bootmanager protocol: " + BootManagerProtocolVersionMajor + "." + BootManagerProtocolVersionMinor, Type); + LogFile.Log("Flash app: " + FlashAppVersionMajor + "." + FlashAppVersionMinor, Type); + LogFile.Log("Flash protocol: " + FlashAppProtocolVersionMajor + "." + FlashAppProtocolVersionMinor, Type); + break; + case FlashAppType.FlashApp: + LogFile.Log("Flash app: " + FlashAppVersionMajor + "." + FlashAppVersionMinor, Type); + LogFile.Log("Flash protocol: " + FlashAppProtocolVersionMajor + "." + FlashAppProtocolVersionMinor, Type); + break; + case FlashAppType.PhoneInfoApp: + LogFile.Log("Phone info app: " + PhoneInfoAppVersionMajor + "." + PhoneInfoAppVersionMinor, Type); + LogFile.Log("Phone info protocol: " + PhoneInfoAppProtocolVersionMajor + "." + PhoneInfoAppProtocolVersionMinor, Type); + break; + } + + LogFile.Log("SecureBoot: " + ((!PlatformSecureBootEnabled || !UefiSecureBootEnabled) ? "Disabled" : "Enabled") + " (Platform Secure Boot: " + (PlatformSecureBootEnabled ? "Enabled" : "Disabled") + ", UEFI Secure Boot: " + (UefiSecureBootEnabled ? "Enabled" : "Disabled") + ")", Type); + + if ((Type == LogType.ConsoleOnly) || (Type == LogType.FileAndConsole)) + { + LogFile.Log("Flash app security: " + (!IsBootloaderSecure ? "Disabled" : "Enabled"), LogType.ConsoleOnly); + } + + if ((Type == LogType.FileOnly) || (Type == LogType.FileAndConsole)) + { + LogFile.Log("Flash app security: " + (!IsBootloaderSecure ? "Disabled" : "Enabled") + " (FFU security: " + (SecureFfuEnabled ? "Enabled" : "Disabled") + ", RDC: " + (RdcPresent ? "Present" : "Not found") + ", Authenticated: " + (Authenticated ? "True" : "False") + ")", LogType.FileOnly); + } + + LogFile.Log("JTAG: " + (JtagDisabled ? "Disabled" : "Enabled"), Type); + } + } +} diff --git a/WPinternals/Models/PhoneInfoState.cs b/WPinternals/Models/PhoneInfoState.cs new file mode 100644 index 0000000..ef03d93 --- /dev/null +++ b/WPinternals/Models/PhoneInfoState.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +namespace WPinternals +{ + internal enum PhoneInfoState + { + Empty, + Basic, + Extended + }; +} diff --git a/WPinternals/Models/UefiSecurityStatusResponse.cs b/WPinternals/Models/UefiSecurityStatusResponse.cs new file mode 100644 index 0000000..3c8b95a --- /dev/null +++ b/WPinternals/Models/UefiSecurityStatusResponse.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +namespace WPinternals +{ + internal class UefiSecurityStatusResponse + { + public byte IsTestDevice; + public bool PlatformSecureBootStatus; + public bool SecureFfuEfuseStatus; + public bool DebugStatus; + public bool RdcStatus; + public bool AuthenticationStatus; + public bool UefiSecureBootStatus; + public bool CryptoHardwareKey; + } +} diff --git a/WPinternals/TestCode.cs b/WPinternals/TestCode.cs index aa28243..89eec2e 100644 --- a/WPinternals/TestCode.cs +++ b/WPinternals/TestCode.cs @@ -258,7 +258,7 @@ namespace WPinternals { LogFile.Log("Phone needs to be switched to emergency mode.", LogType.FileAndConsole); await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); - PhoneInfo Info = ((NokiaFlashModel)Notifier.CurrentModel).ReadPhoneInfo(); + PhoneInfo Info = ((LumiaFlashAppModel)Notifier.CurrentModel).ReadPhoneInfo(); Info.Log(LogType.ConsoleOnly); await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Qualcomm_Download); if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Download) diff --git a/WPinternals/ViewModels/DownloadsViewModel.cs b/WPinternals/ViewModels/DownloadsViewModel.cs index 1efba03..07f9a76 100644 --- a/WPinternals/ViewModels/DownloadsViewModel.cs +++ b/WPinternals/ViewModels/DownloadsViewModel.cs @@ -469,7 +469,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash) { - NokiaFlashModel LumiaFlashModel = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel LumiaFlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; PhoneInfo Info = LumiaFlashModel.ReadPhoneInfo(); ProductType = Info.Type; OperatorCode = ""; diff --git a/WPinternals/ViewModels/LumiaFlashRomViewModel.cs b/WPinternals/ViewModels/LumiaFlashRomViewModel.cs index 66434af..3c0be57 100644 --- a/WPinternals/ViewModels/LumiaFlashRomViewModel.cs +++ b/WPinternals/ViewModels/LumiaFlashRomViewModel.cs @@ -73,7 +73,7 @@ namespace WPinternals await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_Flash, (msg, sub) => ActivateSubContext(new BusyViewModel(msg, sub))); - if (((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: false).FlashAppProtocolVersionMajor < 2) + if (((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: false).FlashAppProtocolVersionMajor < 2) { FlashPartitionsTask(EFIESPPath, MainOSPath, DataPath); } @@ -96,7 +96,7 @@ namespace WPinternals ActivateSubContext(new BusyViewModel("Initializing flash...")); - NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel; + LumiaFlashAppModel Phone = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; GPT GPT = Phone.ReadGPT(); @@ -268,7 +268,7 @@ namespace WPinternals await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_Flash, (msg, sub) => ActivateSubContext(new BusyViewModel(msg, sub))); - if (((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: false).FlashAppProtocolVersionMajor < 2) + if (((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(ExtendedInfo: false).FlashAppProtocolVersionMajor < 2) { FlashArchiveTask(ArchivePath); } @@ -289,7 +289,7 @@ namespace WPinternals { ActivateSubContext(new BusyViewModel("Initializing flash...")); - NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel; + LumiaFlashAppModel Phone = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; ulong TotalSizeSectors = 0; int PartitionCount = 0; @@ -505,7 +505,7 @@ namespace WPinternals { bool Result = true; - NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel; + LumiaFlashAppModel Phone = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; PhoneInfo Info = Phone.ReadPhoneInfo(false); #region Remove bootloader changes @@ -519,7 +519,7 @@ namespace WPinternals if (Info.FlashAppProtocolVersionMajor >= 2) { - byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(Phone, 0x20000); // TODO: Get proper profile FFU and get ChunkSizeInBytes + byte[] GPTChunk = Phone.GetGptChunk(0x20000); // TODO: Get proper profile FFU and get ChunkSizeInBytes GPT GPT = new(GPTChunk); FlashPart Part; List FlashParts = new(); @@ -598,14 +598,14 @@ namespace WPinternals if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext(); + ((LumiaBootManagerAppModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext(); } } } #endregion - Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel; + Phone = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; ActivateSubContext(new BusyViewModel("Initializing flash...")); @@ -664,12 +664,13 @@ namespace WPinternals internal void FlashMMOSTask(string MMOSPath) { - NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel; if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - Phone.SwitchToFlashAppContext(); + ((LumiaBootManagerAppModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext(); } + LumiaFlashAppModel Phone = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; + new Thread(() => { bool Result = true; diff --git a/WPinternals/ViewModels/LumiaInfoViewModel.cs b/WPinternals/ViewModels/LumiaInfoViewModel.cs index f3bf963..9af2636 100644 --- a/WPinternals/ViewModels/LumiaInfoViewModel.cs +++ b/WPinternals/ViewModels/LumiaInfoViewModel.cs @@ -66,18 +66,19 @@ namespace WPinternals switch (CurrentInterface) { case null: - case PhoneInterfaces.Lumia_Bootloader: ActivateSubContext(null); - //ActivateSubContext(new NokiaBootloaderViewModel((NokiaFlashModel)CurrentModel, ModeSwitchRequestCallback, SwitchToGettingStarted)); + break; + case PhoneInterfaces.Lumia_Bootloader: + ActivateSubContext(new NokiaBootloaderViewModel((LumiaBootManagerAppModel)CurrentModel, ModeSwitchRequestCallback, SwitchToGettingStarted)); break; case PhoneInterfaces.Lumia_PhoneInfo: - ActivateSubContext(new NokiaPhoneInfoViewModel((NokiaFlashModel)CurrentModel, ModeSwitchRequestCallback, SwitchToGettingStarted)); + ActivateSubContext(new NokiaPhoneInfoViewModel((LumiaPhoneInfoAppModel)CurrentModel, ModeSwitchRequestCallback, SwitchToGettingStarted)); break; case PhoneInterfaces.Lumia_Normal: ActivateSubContext(new NokiaNormalViewModel((NokiaPhoneModel)CurrentModel, ModeSwitchRequestCallback)); break; case PhoneInterfaces.Lumia_Flash: - ActivateSubContext(new NokiaFlashViewModel((NokiaFlashModel)CurrentModel, ModeSwitchRequestCallback, SwitchToGettingStarted)); + ActivateSubContext(new NokiaFlashViewModel((LumiaFlashAppModel)CurrentModel, ModeSwitchRequestCallback, SwitchToGettingStarted)); break; case PhoneInterfaces.Lumia_Label: ActivateSubContext(new NokiaLabelViewModel((NokiaPhoneModel)CurrentModel, ModeSwitchRequestCallback)); diff --git a/WPinternals/ViewModels/LumiaModeViewModel.cs b/WPinternals/ViewModels/LumiaModeViewModel.cs index 8c0e69c..38ce379 100644 --- a/WPinternals/ViewModels/LumiaModeViewModel.cs +++ b/WPinternals/ViewModels/LumiaModeViewModel.cs @@ -87,17 +87,16 @@ namespace WPinternals ActivateSubContext(null); break; case PhoneInterfaces.Lumia_Bootloader: - ActivateSubContext(null); - //ActivateSubContext(new NokiaModeBootloaderViewModel((NokiaFlashModel)CurrentModel, OnModeSwitchRequested)); + ActivateSubContext(new NokiaModeBootloaderViewModel((LumiaBootManagerAppModel)CurrentModel, OnModeSwitchRequested)); break; case PhoneInterfaces.Lumia_PhoneInfo: - ActivateSubContext(new NokiaModePhoneInfoViewModel((NokiaFlashModel)CurrentModel, OnModeSwitchRequested)); + ActivateSubContext(new NokiaModePhoneInfoViewModel((LumiaPhoneInfoAppModel)CurrentModel, OnModeSwitchRequested)); break; case PhoneInterfaces.Lumia_Normal: ActivateSubContext(new NokiaModeNormalViewModel((NokiaPhoneModel)CurrentModel, OnModeSwitchRequested)); break; case PhoneInterfaces.Lumia_Flash: - ActivateSubContext(new NokiaModeFlashViewModel((NokiaFlashModel)CurrentModel, OnModeSwitchRequested)); + ActivateSubContext(new NokiaModeFlashViewModel((LumiaFlashAppModel)CurrentModel, OnModeSwitchRequested)); break; case PhoneInterfaces.Lumia_Label: ActivateSubContext(new NokiaModeLabelViewModel((NokiaPhoneModel)CurrentModel, OnModeSwitchRequested)); diff --git a/WPinternals/ViewModels/LumiaUnlockBootViewModel.cs b/WPinternals/ViewModels/LumiaUnlockBootViewModel.cs index 3ba4ca8..afc55ba 100644 --- a/WPinternals/ViewModels/LumiaUnlockBootViewModel.cs +++ b/WPinternals/ViewModels/LumiaUnlockBootViewModel.cs @@ -133,11 +133,11 @@ namespace WPinternals { // 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"); + RootKeyHash = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadParam("RRKH"); TestPos = 1; - UefiSecurityStatusResponse SecurityStatus = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadSecurityStatus(); + UefiSecurityStatusResponse SecurityStatus = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadSecurityStatus(); if (SecurityStatus != null) { IsBootLoaderUnlocked = SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus; @@ -145,7 +145,7 @@ namespace WPinternals TestPos = 2; - PhoneInfo Info = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(); + PhoneInfo Info = ((LumiaFlashAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(); if (SecurityStatus == null) { IsBootLoaderUnlocked = !Info.IsBootloaderSecure; @@ -213,7 +213,7 @@ namespace WPinternals bool AlreadyUnlocked = false; if (DoUnlock) { - NokiaFlashModel FlashModel = (NokiaFlashModel)PhoneNotifier.CurrentModel; + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; GPT GPT = FlashModel.ReadGPT(); if ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null)) { @@ -510,7 +510,7 @@ namespace WPinternals if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Qualcomm_Download && PhoneNotifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) { - NokiaFlashModel Model = (NokiaFlashModel)PhoneNotifier.CurrentModel; + LumiaFlashAppModel Model = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; PhoneInfo Info = Model.ReadPhoneInfo(); if (EDEPath == null) diff --git a/WPinternals/ViewModels/LumiaUnlockBootloaderViewModel.cs b/WPinternals/ViewModels/LumiaUnlockBootloaderViewModel.cs index d175216..022f51f 100644 --- a/WPinternals/ViewModels/LumiaUnlockBootloaderViewModel.cs +++ b/WPinternals/ViewModels/LumiaUnlockBootloaderViewModel.cs @@ -34,7 +34,7 @@ namespace WPinternals // TODO: Add logging private static void PerformSoftBrick(PhoneNotifierViewModel Notifier, FFU FFU) { - NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; // Send FFU headers UInt64 CombinedFFUHeaderSize = FFU.HeaderSize; @@ -139,7 +139,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -149,7 +149,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -159,7 +159,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -205,7 +205,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -215,7 +215,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -225,7 +225,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -273,7 +273,7 @@ namespace WPinternals try { - if (Notifier.CurrentModel is NokiaFlashModel) + if (Notifier.CurrentModel is LumiaFlashAppModel) { await LumiaRelockUEFI(Notifier, FFUPath, true, SetWorkingStatus, UpdateWorkingStatus, null, (string Message, string SubMessage) => { @@ -320,9 +320,9 @@ namespace WPinternals throw new Exception("Error: Parsing FFU-file failed."); } - if (Notifier.CurrentModel is NokiaFlashModel) + if (Notifier.CurrentModel is LumiaFlashAppModel) { - FlashVersion FlashVersion = ((NokiaFlashModel)Notifier.CurrentModel).GetFlashVersion(); + FlashVersion FlashVersion = ((LumiaFlashAppModel)Notifier.CurrentModel).GetFlashVersion(); if (FlashVersion == null) { throw new Exception("Error: The version of the Flash Application on the phone could not be determined."); @@ -333,7 +333,7 @@ namespace WPinternals throw new Exception("Error: The version of the Flash Application on the phone is too old. Update your phone using Windows Updates or flash a newer ROM to your phone. Then try again."); } - UefiSecurityStatusResponse SecurityStatus = ((NokiaFlashModel)Notifier.CurrentModel).ReadSecurityStatus(); + UefiSecurityStatusResponse SecurityStatus = ((LumiaFlashAppModel)Notifier.CurrentModel).ReadSecurityStatus(); IsBootLoaderUnlocked = SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus; } @@ -350,9 +350,9 @@ namespace WPinternals #endif GPT NewGPT = null; - if (Notifier.CurrentModel is NokiaFlashModel) + if (Notifier.CurrentModel is LumiaFlashAppModel) { - ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + ((LumiaFlashAppModel)Notifier.CurrentModel).ResetPhone(); if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) { @@ -364,7 +364,7 @@ namespace WPinternals throw new WPinternalsException("Phone is in an unexpected mode.", "The phone should have been detected in bootloader mode. Instead it has been detected in " + Notifier.CurrentInterface.ToString() + " mode."); } - NewGPT = ((NokiaFlashModel)Notifier.CurrentModel).ReadGPT(); + NewGPT = ((LumiaBootManagerAppModel)Notifier.CurrentModel).ReadGPT(); await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); @@ -448,7 +448,7 @@ namespace WPinternals } else if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) { - RootKeyHash = ((NokiaFlashModel)Notifier.CurrentModel).ReadParam("RRKH"); + RootKeyHash = ((LumiaFlashAppModel)Notifier.CurrentModel).ReadParam("RRKH"); } if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) @@ -573,7 +573,7 @@ namespace WPinternals if (IsBootLoaderUnlocked) // Flash phone in Flash app { - NokiaFlashModel CurrentModel = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel CurrentModel = (LumiaFlashAppModel)Notifier.CurrentModel; LogFile.Log("Start flashing in Custom Flash mode"); UInt64 TotalSectorCount = (UInt64)0x21 + 1 + @@ -732,7 +732,7 @@ namespace WPinternals throw new WPinternalsException("Phone is in an unexpected mode.", "The phone should have been detected in flash mode. Instead it has been detected in " + Notifier.CurrentInterface.ToString() + " mode."); } - NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash && FlashModel.ReadParam("FS")[3] > 0) { ExitSuccess("Bootloader is restored", "NOTE: You need to flash a stock ROM because you recovered a phone from a bootloader unlock failure."); @@ -743,7 +743,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -753,7 +753,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -763,7 +763,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -843,9 +843,9 @@ namespace WPinternals throw new Exception("Error: Parsing FFU-file failed."); } - if (Notifier.CurrentModel is NokiaFlashModel) + if (Notifier.CurrentModel is LumiaFlashAppModel) { - FlashVersion FlashVersion = ((NokiaFlashModel)Notifier.CurrentModel).GetFlashVersion(); + FlashVersion FlashVersion = ((LumiaFlashAppModel)Notifier.CurrentModel).GetFlashVersion(); if (FlashVersion == null) { throw new Exception("Error: The version of the Flash Application on the phone could not be determined."); @@ -856,7 +856,7 @@ namespace WPinternals throw new Exception("Error: The version of the Flash Application on the phone is too old. Update your phone using Windows Updates or flash a newer ROM to your phone. Then try again."); } - UefiSecurityStatusResponse SecurityStatus = ((NokiaFlashModel)Notifier.CurrentModel).ReadSecurityStatus(); + UefiSecurityStatusResponse SecurityStatus = ((LumiaFlashAppModel)Notifier.CurrentModel).ReadSecurityStatus(); IsBootLoaderUnlocked = SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus; } @@ -904,9 +904,9 @@ namespace WPinternals #endif GPT NewGPT = null; - if (Notifier.CurrentModel is NokiaFlashModel) + if (Notifier.CurrentModel is LumiaFlashAppModel) { - ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + ((LumiaFlashAppModel)Notifier.CurrentModel).ResetPhone(); if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) { @@ -918,7 +918,7 @@ namespace WPinternals throw new WPinternalsException("Phone is in an unexpected mode.", "The phone should have been detected in bootloader mode. Instead it has been detected in " + Notifier.CurrentInterface.ToString() + " mode."); } - NewGPT = ((NokiaFlashModel)Notifier.CurrentModel).ReadGPT(); + NewGPT = ((LumiaBootManagerAppModel)Notifier.CurrentModel).ReadGPT(); await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); @@ -1013,7 +1013,7 @@ namespace WPinternals } else if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) { - RootKeyHash = ((NokiaFlashModel)Notifier.CurrentModel).ReadParam("RRKH"); + RootKeyHash = ((LumiaFlashAppModel)Notifier.CurrentModel).ReadParam("RRKH"); } if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) @@ -1252,7 +1252,7 @@ namespace WPinternals if (IsBootLoaderUnlocked) // Flash phone in Flash app { - NokiaFlashModel CurrentModel = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel CurrentModel = (LumiaFlashAppModel)Notifier.CurrentModel; LogFile.Log("Start flashing in Custom Flash mode"); UInt64 TotalSectorCount = (UInt64)0x21 + 1 + @@ -1423,7 +1423,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -1433,7 +1433,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -1443,7 +1443,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ContinueBoot(); } if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) @@ -1467,54 +1467,6 @@ namespace WPinternals } } - internal static byte[] GetGptChunk(NokiaFlashModel FlashModel, UInt32 Size) - { - // This function is also used to generate a dummy chunk to flash for testing. - // The dummy chunk will contain the GPT, so it can be flashed to the first sectors for testing. - byte[] GPTChunk = new byte[Size]; - - PhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: false); - FlashAppType OriginalAppType = Info.App; - bool Switch = (Info.App != FlashAppType.BootManager) && Info.IsBootloaderSecure; - if (Switch) - { - FlashModel.SwitchToBootManagerContext(); - } - - byte[] Request = new byte[0x04]; - const string Header = "NOKT"; - - System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - - byte[] Buffer = FlashModel.ExecuteRawMethod(Request); - if ((Buffer == null) || (Buffer.Length < 0x4408)) - { - throw new InvalidOperationException("Unable to read GPT!"); - } - - UInt16 Error = (UInt16)((Buffer[6] << 8) + Buffer[7]); - if (Error > 0) - { - throw new NotSupportedException("ReadGPT: Error 0x" + Error.ToString("X4")); - } - - System.Buffer.BlockCopy(Buffer, 8, GPTChunk, 0, 0x4400); - - if (Switch) - { - if (OriginalAppType == FlashAppType.FlashApp) - { - FlashModel.SwitchToFlashAppContext(); - } - else - { - FlashModel.SwitchToPhoneInfoAppContext(); - } - } - - return GPTChunk; - } - // Magic! // UEFI Secure Boot Hack for Spec A and Spec B devices // @@ -1547,7 +1499,7 @@ namespace WPinternals { GPT GPT = null; Partition Target = null; - NokiaFlashModel FlashModel = null; + LumiaFlashAppModel FlashModel = null; LogFile.Log("Command: Relock phone", LogType.FileAndConsole); @@ -1558,13 +1510,13 @@ namespace WPinternals byte[] EFIESPBackup = null; - PhoneInfo Info = ((NokiaFlashModel)Notifier.CurrentModel).ReadPhoneInfo(); + PhoneInfo Info = ((LumiaFlashAppModel)Notifier.CurrentModel).ReadPhoneInfo(); bool IsSpecB = Info.FlashAppProtocolVersionMajor >= 2; bool UndoEFIESPPadding = false; if (!IsSpecB) { - ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + ((LumiaFlashAppModel)Notifier.CurrentModel).ResetPhone(); if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) { @@ -1577,7 +1529,7 @@ namespace WPinternals } } - byte[] GPTChunk = GetGptChunk((NokiaFlashModel)Notifier.CurrentModel, 0x20000); + byte[] GPTChunk = ((LumiaBootManagerAppModel)Notifier.CurrentModel).GetGptChunk(0x20000); GPT = new GPT(GPTChunk); bool GPTChanged = false; Partition IsUnlockedPartitionSBL3 = GPT.GetPartition("IS_UNLOCKED_SBL3"); @@ -1719,7 +1671,7 @@ namespace WPinternals SetWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", null, Status: WPinternalsStatus.Initializing); - ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).SwitchToFlashAppContext(); List FlashParts = new(); @@ -1730,7 +1682,7 @@ namespace WPinternals FlashPart Part; - FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; // Remove IS_UNLOCKED flag in GPT Partition IsUnlockedPartition = GPT.GetPartition("IS_UNLOCKED"); @@ -1788,7 +1740,7 @@ namespace WPinternals // We should only clear NV if there was no backup NV to be restored and the current NV contains the SB unlock. bool NvCleared = false; - Info = ((NokiaFlashModel)Notifier.CurrentModel).ReadPhoneInfo(); + Info = ((LumiaFlashAppModel)Notifier.CurrentModel).ReadPhoneInfo(); if ((NvBackupPartition == null) && !Info.UefiSecureBootEnabled) { // ClearNV @@ -1857,7 +1809,7 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).SwitchToFlashAppContext(); } if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash) @@ -1886,7 +1838,7 @@ namespace WPinternals internal static async Task LumiaUnlockUEFI(PhoneNotifierViewModel Notifier, string ProfileFFUPath, string EDEPath, string SupportedFFUPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null, bool ExperimentalSpecBEFIESPUnlock = false, bool ExperimentalSpecAEFIESPUnlock = true, bool ReUnlockDevice = false) { LogFile.BeginAction("UnlockBootloader"); - NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; if (SetWorkingStatus == null) { @@ -1957,7 +1909,7 @@ namespace WPinternals LumiaPatchEFIESP(SupportedFFU, UnlockedEFIESP, IsSpecB); - byte[] GPTChunk = GetGptChunk(FlashModel, (UInt32)ProfileFFU.ChunkSize); + byte[] GPTChunk = FlashModel.GetGptChunk((UInt32)ProfileFFU.ChunkSize); byte[] GPTChunkBackup = new byte[GPTChunk.Length]; Buffer.BlockCopy(GPTChunk, 0, GPTChunkBackup, 0, GPTChunk.Length); GPT GPT = new(GPTChunk); @@ -2042,8 +1994,8 @@ namespace WPinternals if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { - FlashModel = (NokiaFlashModel)Notifier.CurrentModel; - FlashModel.SwitchToFlashAppContext(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).SwitchToFlashAppContext(); + FlashModel = ((LumiaFlashAppModel)Notifier.CurrentModel); } GPTChanged = false; @@ -2208,7 +2160,7 @@ namespace WPinternals if (!IsSpecB && !SBL3Eng) { - ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + ((LumiaFlashAppModel)Notifier.CurrentModel).ResetPhone(); LogFile.Log("Bootloader unlocked!", LogType.FileAndConsole); ExitSuccess("Bootloader unlocked successfully!", null); @@ -2344,7 +2296,7 @@ namespace WPinternals if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) { throw new WPinternalsException("Phone is in wrong mode", "The phone should have been detected in bootloader mode. Instead it has been detected in " + Notifier.CurrentInterface.ToString() + " mode."); - } ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); + } ((LumiaBootManagerAppModel)Notifier.CurrentModel).SwitchToFlashAppContext(); UInt32 OriginalEfiespFirstSector; if (!ReUnlockDevice) @@ -2504,7 +2456,7 @@ namespace WPinternals throw new WPinternalsException("Phone is in wrong mode", "The phone should have been detected in bootloader mode. Instead it has been detected in " + Notifier.CurrentInterface.ToString() + " mode."); } } - ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).SwitchToFlashAppContext(); Parts = LumiaGenerateEFIESPFlashPayload(UnlockedEFIESP, GPT, ProfileFFU, IsSpecB); @@ -2517,7 +2469,7 @@ namespace WPinternals if (!IsSpecB) { - ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + ((LumiaFlashAppModel)Notifier.CurrentModel).ResetPhone(); } } @@ -2637,7 +2589,7 @@ namespace WPinternals private static async Task LumiaFlashParts(PhoneNotifierViewModel Notifier, string FFUPath, bool PerformFullFlashFirst, bool SkipWrite, List Parts, bool DoResetFirst = true, bool ClearFlashingStatusAtEnd = true, bool CheckSectorAlignment = true, bool ShowProgress = true, bool Experimental = false, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null, string EDEPath = null) { - PhoneInfo Info = ((NokiaFlashModel)Notifier.CurrentModel).ReadPhoneInfo(); + PhoneInfo Info = ((LumiaFlashAppModel)Notifier.CurrentModel).ReadPhoneInfo(); bool IsSpecA = Info.FlashAppProtocolVersionMajor < 2; if (IsSpecA) @@ -2654,7 +2606,7 @@ namespace WPinternals { SetWorkingStatus("Initializing flash...", null, 100, Status: WPinternalsStatus.Initializing); - NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; UInt64 InputStreamLength = 0; UInt64 totalwritten = 0; diff --git a/WPinternals/ViewModels/LumiaV2UnlockBootViewModel.cs b/WPinternals/ViewModels/LumiaV2UnlockBootViewModel.cs index 1c3c202..202deab 100644 --- a/WPinternals/ViewModels/LumiaV2UnlockBootViewModel.cs +++ b/WPinternals/ViewModels/LumiaV2UnlockBootViewModel.cs @@ -57,7 +57,7 @@ namespace WPinternals { LogFile.Log("Find Flashing Profile", LogType.FileAndConsole); - NokiaFlashModel FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); PhoneInfo Info; if (DoResetFirst) @@ -125,14 +125,14 @@ namespace WPinternals LogFile.Log("Command: Enable testsigning", LogType.FileAndConsole); PhoneNotifierViewModel Notifier = new(); UIContext.Send(s => Notifier.Start(), null); - NokiaFlashModel FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); List Parts = new(); FlashPart Part; // Use GetGptChunk() here instead of ReadGPT(), because ReadGPT() skips the first sector. // We need the fist sector if we want to write back the GPT. - byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); + byte[] GPTChunk = FlashModel.GetGptChunk(0x20000); GPT GPT = new(GPTChunk); bool GPTChanged = false; @@ -228,7 +228,7 @@ namespace WPinternals return ((MassStorage)Notifier.CurrentModel).Drive; } - NokiaFlashModel FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); if (DoResetFirst) { // The phone will be reset before flashing, so we have the opportunity to get some more info from the phone @@ -260,12 +260,12 @@ namespace WPinternals LogFile.Log("Command: Clear NV", LogType.FileAndConsole); PhoneNotifierViewModel Notifier = new(); UIContext.Send(s => Notifier.Start(), null); - NokiaFlashModel FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); List Parts = new(); // Use GetGptChunk() here instead of ReadGPT(), because ReadGPT() skips the first sector. // We need the fist sector if we want to write back the GPT. - byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); + byte[] GPTChunk = FlashModel.GetGptChunk(0x20000); GPT GPT = new(GPTChunk); bool GPTChanged = false; Partition BACKUP_BS_NV = GPT.GetPartition("BACKUP_BS_NV"); @@ -336,13 +336,13 @@ namespace WPinternals PhoneNotifierViewModel Notifier = new(); UIContext.Send(s => Notifier.Start(), null); - NokiaFlashModel FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); PhoneInfo Info = FlashModel.ReadPhoneInfo(); // Use GetGptChunk() here instead of ReadGPT(), because ReadGPT() skips the first sector. // We need the fist sector if we want to write back the GPT. - byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); + byte[] GPTChunk = FlashModel.GetGptChunk(0x20000); GPT GPT = new(GPTChunk); Partition TargetPartition = GPT.GetPartition(PartitionName); @@ -445,7 +445,7 @@ namespace WPinternals PhoneNotifierViewModel Notifier = new(); UIContext.Send(s => Notifier.Start(), null); - NokiaFlashModel FlashModel = (NokiaFlashModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); PhoneInfo Info = FlashModel.ReadPhoneInfo(); @@ -483,10 +483,10 @@ namespace WPinternals internal async static Task LumiaV2CustomFlash(PhoneNotifierViewModel Notifier, string FFUPath, bool PerformFullFlashFirst, bool SkipWrite, List FlashParts, bool DoResetFirst = true, bool ClearFlashingStatusAtEnd = true, bool CheckSectorAlignment = true, bool ShowProgress = true, bool Experimental = false, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null, string ProgrammerPath = null) { - NokiaFlashModel Model = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel Model = (LumiaFlashAppModel)Notifier.CurrentModel; PhoneInfo Info = Model.ReadPhoneInfo(); - byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(Model, 131072u); + byte[] GPTChunk = Model.GetGptChunk(131072u); GPT GPT = new(GPTChunk); @@ -554,7 +554,7 @@ namespace WPinternals ExitFailure = (m, s) => { }; } - NokiaFlashModel Model = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel Model = (LumiaFlashAppModel)Notifier.CurrentModel; PhoneInfo Info = Model.ReadPhoneInfo(); string Type = Info.Type; @@ -673,7 +673,7 @@ namespace WPinternals MaximumAttempts = (int)(((MaximumGapFill / FFU.ChunkSize) + 1) * 8); } - byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(Model, (UInt32)FFU.ChunkSize); + byte[] GPTChunk = Model.GetGptChunk((UInt32)FFU.ChunkSize); // Start with a reset if (DoResetFirst) @@ -818,23 +818,12 @@ namespace WPinternals throw new WPinternalsException("Phone is in wrong mode", "The phone should have been detected in bootloader or flash mode. Instead it has been detected in " + Notifier.CurrentInterface.ToString() + " mode."); } - Model = (NokiaFlashModel)Notifier.CurrentModel; UpdateWorkingStatus("Initializing flash...", null, null); } - try - { - // This will succeed on new models - Model.SwitchToFlashAppContext(); - Model.DisableRebootTimeOut(); - } - catch - { - // This will succeed on old models - Model.ResetPhoneToFlashMode(); - await Notifier.WaitForArrival(); - Model = (NokiaFlashModel)Notifier.CurrentModel; - } + ((LumiaBootManagerAppModel)Notifier.CurrentModel).ResetPhoneToFlashMode(); + await Notifier.WaitForArrival(); + Model = (LumiaFlashAppModel)Notifier.CurrentModel; // The payloads must be ordered by the number of locations // @@ -1516,10 +1505,9 @@ namespace WPinternals break; } - Model = (NokiaFlashModel)Notifier.CurrentModel; - // In case we are on an Engineering phone which isn't stuck in flashmode and booted to BootMgrApp - Model.SwitchToFlashAppContext(); + ((LumiaBootManagerAppModel)Notifier.CurrentModel).SwitchToFlashAppContext(); + Model = (LumiaFlashAppModel)Notifier.CurrentModel; Model.DisableRebootTimeOut(); } @@ -1644,7 +1632,7 @@ namespace WPinternals } // Do the actual reset, which will result in a crash while cleaning up memory - ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + ((LumiaFlashAppModel)Notifier.CurrentModel).ResetPhone(); LogFile.Log("Phone performs hard exit", LogType.FileAndConsole); @@ -1782,7 +1770,7 @@ namespace WPinternals else { // If we didn't do a hard exit, we need to do a normal reboot - ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + ((LumiaFlashAppModel)Notifier.CurrentModel).ResetPhone(); } if (Success) @@ -1964,11 +1952,11 @@ namespace WPinternals { LogFile.BeginAction("FlashCustomROM"); - NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; // Use GetGptChunk() here instead of ReadGPT(), because ReadGPT() skips the first sector. // We need the fist sector if we want to write back the GPT. - byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); + byte[] GPTChunk = FlashModel.GetGptChunk(0x20000); GPT GPT = new(GPTChunk); Partition Target; @@ -2357,11 +2345,11 @@ namespace WPinternals // Assumes phone is in flash mode internal async static Task LumiaV2FlashPartitions(PhoneNotifierViewModel Notifier, string EFIESPPath, string MainOSPath, string DataPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null) { - NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)Notifier.CurrentModel; // Use GetGptChunk() here instead of ReadGPT(), because ReadGPT() skips the first sector. // We need the fist sector if we want to write back the GPT. - byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); + byte[] GPTChunk = FlashModel.GetGptChunk(0x20000); GPT GPT = new(GPTChunk); Partition Target; diff --git a/WPinternals/ViewModels/LumiaV3FlashRomViewModel.cs b/WPinternals/ViewModels/LumiaV3FlashRomViewModel.cs index 59cd759..d25601b 100644 --- a/WPinternals/ViewModels/LumiaV3FlashRomViewModel.cs +++ b/WPinternals/ViewModels/LumiaV3FlashRomViewModel.cs @@ -192,7 +192,7 @@ namespace WPinternals try { - NokiaFlashModel Model = (NokiaFlashModel)Notifier.CurrentModel; + LumiaFlashAppModel Model = (LumiaFlashAppModel)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. @@ -282,7 +282,7 @@ namespace WPinternals store.WriteDescriptorLength += payload.GetStoreHeaderSize(); } - byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(Model, 0x20000); + byte[] GPTChunk = Model.GetGptChunk(0x20000); GPT GPT = new(GPTChunk); UInt64 PlatEnd = 0; if (GPT.Partitions.Any(x => x.Name == "PLAT")) diff --git a/WPinternals/ViewModels/NokiaBootloaderViewModel.cs b/WPinternals/ViewModels/NokiaBootloaderViewModel.cs index 54a1e91..44846a3 100644 --- a/WPinternals/ViewModels/NokiaBootloaderViewModel.cs +++ b/WPinternals/ViewModels/NokiaBootloaderViewModel.cs @@ -30,7 +30,7 @@ namespace WPinternals internal class NokiaBootloaderViewModel : ContextViewModel { - private readonly NokiaFlashModel CurrentModel; + private readonly LumiaBootManagerAppModel CurrentModel; private readonly Action RequestModeSwitch; internal Action SwitchToGettingStarted; private readonly object LockDeviceInfo = new(); @@ -38,7 +38,7 @@ namespace WPinternals internal NokiaBootloaderViewModel(NokiaPhoneModel CurrentModel, Action RequestModeSwitch, Action SwitchToGettingStarted) : base() { - this.CurrentModel = (NokiaFlashModel)CurrentModel; + this.CurrentModel = (LumiaBootManagerAppModel)CurrentModel; this.RequestModeSwitch = RequestModeSwitch; this.SwitchToGettingStarted = SwitchToGettingStarted; } @@ -63,6 +63,9 @@ namespace WPinternals case "Normal": RequestModeSwitch(PhoneInterfaces.Lumia_Normal); break; + case "PhoneInfo": + RequestModeSwitch(PhoneInterfaces.Lumia_PhoneInfo); + break; case "Label": RequestModeSwitch(PhoneInterfaces.Lumia_Label); break; diff --git a/WPinternals/ViewModels/NokiaFlashViewModel.cs b/WPinternals/ViewModels/NokiaFlashViewModel.cs index bd521b5..a85c367 100644 --- a/WPinternals/ViewModels/NokiaFlashViewModel.cs +++ b/WPinternals/ViewModels/NokiaFlashViewModel.cs @@ -30,7 +30,7 @@ namespace WPinternals internal class NokiaFlashViewModel : ContextViewModel { - private readonly NokiaFlashModel CurrentModel; + private readonly LumiaFlashAppModel CurrentModel; private readonly Action RequestModeSwitch; internal Action SwitchToGettingStarted; private readonly object LockDeviceInfo = new(); @@ -39,7 +39,7 @@ namespace WPinternals internal NokiaFlashViewModel(NokiaPhoneModel CurrentModel, Action RequestModeSwitch, Action SwitchToGettingStarted) : base() { - this.CurrentModel = (NokiaFlashModel)CurrentModel; + this.CurrentModel = (LumiaFlashAppModel)CurrentModel; this.RequestModeSwitch = RequestModeSwitch; this.SwitchToGettingStarted = SwitchToGettingStarted; } @@ -95,21 +95,21 @@ namespace WPinternals SecurityFlags = (UInt32)CurrentModel.ReadSecurityFlags(); LogFile.Log("Security flags: 0x" + SecurityFlags.ToString("X8")); - FinalConfigDakStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Dak); - FinalConfigFastBootStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.FastBoot); - FinalConfigFfuVerifyStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.FfuVerify); - FinalConfigJtagStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Jtag); - FinalConfigOemIdStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.OemId); - FinalConfigProductionDoneStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.ProductionDone); - FinalConfigPublicIdStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.PublicId); - FinalConfigRkhStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Rkh); - FinalConfigRpmWdogStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.RpmWdog); - FinalConfigSecGenStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.SecGen); - FinalConfigSecureBootStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.SecureBoot); - FinalConfigShkStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Shk); - FinalConfigSimlockStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Simlock); - FinalConfigSpdmSecModeStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.SpdmSecMode); - FinalConfigSsmStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Ssm); + FinalConfigDakStatus = CurrentModel.ReadFuseStatus(Fuse.Dak); + FinalConfigFastBootStatus = CurrentModel.ReadFuseStatus(Fuse.FastBoot); + FinalConfigFfuVerifyStatus = CurrentModel.ReadFuseStatus(Fuse.FfuVerify); + FinalConfigJtagStatus = CurrentModel.ReadFuseStatus(Fuse.Jtag); + FinalConfigOemIdStatus = CurrentModel.ReadFuseStatus(Fuse.OemId); + FinalConfigProductionDoneStatus = CurrentModel.ReadFuseStatus(Fuse.ProductionDone); + FinalConfigPublicIdStatus = CurrentModel.ReadFuseStatus(Fuse.PublicId); + FinalConfigRkhStatus = CurrentModel.ReadFuseStatus(Fuse.Rkh); + FinalConfigRpmWdogStatus = CurrentModel.ReadFuseStatus(Fuse.RpmWdog); + FinalConfigSecGenStatus = CurrentModel.ReadFuseStatus(Fuse.SecGen); + FinalConfigSecureBootStatus = CurrentModel.ReadFuseStatus(Fuse.SecureBoot); + FinalConfigShkStatus = CurrentModel.ReadFuseStatus(Fuse.Shk); + FinalConfigSimlockStatus = CurrentModel.ReadFuseStatus(Fuse.Simlock); + FinalConfigSpdmSecModeStatus = CurrentModel.ReadFuseStatus(Fuse.SpdmSecMode); + FinalConfigSsmStatus = CurrentModel.ReadFuseStatus(Fuse.Ssm); } else { @@ -748,6 +748,9 @@ namespace WPinternals case "Normal": RequestModeSwitch(PhoneInterfaces.Lumia_Normal); break; + case "PhoneInfo": + RequestModeSwitch(PhoneInterfaces.Lumia_PhoneInfo); + break; case "Label": RequestModeSwitch(PhoneInterfaces.Lumia_Label); break; diff --git a/WPinternals/ViewModels/NokiaLabelViewModel.cs b/WPinternals/ViewModels/NokiaLabelViewModel.cs index 9fd127a..da833c1 100644 --- a/WPinternals/ViewModels/NokiaLabelViewModel.cs +++ b/WPinternals/ViewModels/NokiaLabelViewModel.cs @@ -292,6 +292,9 @@ namespace WPinternals case "Flash": RequestModeSwitch(PhoneInterfaces.Lumia_Flash); break; + case "PhoneInfo": + RequestModeSwitch(PhoneInterfaces.Lumia_PhoneInfo); + break; case "Label": RequestModeSwitch(PhoneInterfaces.Lumia_Label); break; diff --git a/WPinternals/ViewModels/NokiaModeBootloaderViewModel.cs b/WPinternals/ViewModels/NokiaModeBootloaderViewModel.cs index 554bd40..c6950ce 100644 --- a/WPinternals/ViewModels/NokiaModeBootloaderViewModel.cs +++ b/WPinternals/ViewModels/NokiaModeBootloaderViewModel.cs @@ -25,7 +25,7 @@ namespace WPinternals { internal class NokiaModeBootloaderViewModel : ContextViewModel { - private readonly NokiaFlashModel CurrentModel; + private readonly LumiaBootManagerAppModel CurrentModel; private readonly Action RequestModeSwitch; private readonly object LockDeviceInfo = new(); private bool DeviceInfoLoaded = false; @@ -33,7 +33,7 @@ namespace WPinternals internal NokiaModeBootloaderViewModel(NokiaPhoneModel CurrentModel, Action RequestModeSwitch) : base() { - this.CurrentModel = (NokiaFlashModel)CurrentModel; + this.CurrentModel = (LumiaBootManagerAppModel)CurrentModel; this.RequestModeSwitch = RequestModeSwitch; } @@ -69,19 +69,7 @@ namespace WPinternals { PhoneInfo Info = CurrentModel.ReadPhoneInfo(); - if (Info.FlashAppProtocolVersionMajor < 2) - { - UefiSecurityStatusResponse SecurityStatus = CurrentModel.ReadSecurityStatus(); - - if (SecurityStatus != null) - { - EffectiveBootloaderSecurityStatus = SecurityStatus.SecureFfuEfuseStatus && !SecurityStatus.AuthenticationStatus && !SecurityStatus.RdcStatus; - } - } - else - { - EffectiveBootloaderSecurityStatus = Info.UefiSecureBootEnabled; - } + EffectiveBootloaderSecurityStatus = Info.UefiSecureBootEnabled; LogFile.Log("Effective Bootloader Security Status: " + EffectiveBootloaderSecurityStatus.ToString()); } @@ -101,6 +89,9 @@ namespace WPinternals case "Normal": RequestModeSwitch(PhoneInterfaces.Lumia_Normal); break; + case "PhoneInfo": + RequestModeSwitch(PhoneInterfaces.Lumia_PhoneInfo); + break; case "Flash": RequestModeSwitch(PhoneInterfaces.Lumia_Flash); break; diff --git a/WPinternals/ViewModels/NokiaModeFlashViewModel.cs b/WPinternals/ViewModels/NokiaModeFlashViewModel.cs index f8b0ac6..01f1fa6 100644 --- a/WPinternals/ViewModels/NokiaModeFlashViewModel.cs +++ b/WPinternals/ViewModels/NokiaModeFlashViewModel.cs @@ -25,7 +25,7 @@ namespace WPinternals { internal class NokiaModeFlashViewModel : ContextViewModel { - private readonly NokiaFlashModel CurrentModel; + private readonly LumiaFlashAppModel CurrentModel; private readonly Action RequestModeSwitch; private readonly object LockDeviceInfo = new(); private bool DeviceInfoLoaded = false; @@ -33,7 +33,7 @@ namespace WPinternals internal NokiaModeFlashViewModel(NokiaPhoneModel CurrentModel, Action RequestModeSwitch) : base() { - this.CurrentModel = (NokiaFlashModel)CurrentModel; + this.CurrentModel = (LumiaFlashAppModel)CurrentModel; this.RequestModeSwitch = RequestModeSwitch; } @@ -103,6 +103,12 @@ namespace WPinternals case "Flash": RequestModeSwitch(PhoneInterfaces.Lumia_Flash); break; + case "PhoneInfo": + RequestModeSwitch(PhoneInterfaces.Lumia_PhoneInfo); + break; + case "BootMgr": + RequestModeSwitch(PhoneInterfaces.Lumia_Bootloader); + break; case "Label": RequestModeSwitch(PhoneInterfaces.Lumia_Label); break; diff --git a/WPinternals/ViewModels/NokiaModeLabelViewModel.cs b/WPinternals/ViewModels/NokiaModeLabelViewModel.cs index 8ad5834..0f5f41d 100644 --- a/WPinternals/ViewModels/NokiaModeLabelViewModel.cs +++ b/WPinternals/ViewModels/NokiaModeLabelViewModel.cs @@ -41,6 +41,12 @@ namespace WPinternals case "Normal": RequestModeSwitch(PhoneInterfaces.Lumia_Normal); break; + case "PhoneInfo": + RequestModeSwitch(PhoneInterfaces.Lumia_PhoneInfo); + break; + case "BootMgr": + RequestModeSwitch(PhoneInterfaces.Lumia_Bootloader); + break; case "Flash": RequestModeSwitch(PhoneInterfaces.Lumia_Flash); break; diff --git a/WPinternals/ViewModels/NokiaModeMassStorageViewModel.cs b/WPinternals/ViewModels/NokiaModeMassStorageViewModel.cs index b1f316b..0721797 100644 --- a/WPinternals/ViewModels/NokiaModeMassStorageViewModel.cs +++ b/WPinternals/ViewModels/NokiaModeMassStorageViewModel.cs @@ -63,6 +63,12 @@ namespace WPinternals case "Normal": RequestModeSwitch(PhoneInterfaces.Lumia_Normal); break; + case "PhoneInfo": + RequestModeSwitch(PhoneInterfaces.Lumia_PhoneInfo); + break; + case "BootMgr": + RequestModeSwitch(PhoneInterfaces.Lumia_Bootloader); + break; case "Label": RequestModeSwitch(PhoneInterfaces.Lumia_Label); break; diff --git a/WPinternals/ViewModels/NokiaModeNormalViewModel.cs b/WPinternals/ViewModels/NokiaModeNormalViewModel.cs index d3d88cb..554d6bc 100644 --- a/WPinternals/ViewModels/NokiaModeNormalViewModel.cs +++ b/WPinternals/ViewModels/NokiaModeNormalViewModel.cs @@ -41,6 +41,12 @@ namespace WPinternals case "Flash": RequestModeSwitch(PhoneInterfaces.Lumia_Flash); break; + case "PhoneInfo": + RequestModeSwitch(PhoneInterfaces.Lumia_PhoneInfo); + break; + case "BootMgr": + RequestModeSwitch(PhoneInterfaces.Lumia_Bootloader); + break; case "Label": RequestModeSwitch(PhoneInterfaces.Lumia_Label); break; diff --git a/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs b/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs index fb44bb4..faf7785 100644 --- a/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs +++ b/WPinternals/ViewModels/NokiaModePhoneInfoViewModel.cs @@ -25,7 +25,7 @@ namespace WPinternals { internal class NokiaModePhoneInfoViewModel : ContextViewModel { - private readonly NokiaFlashModel CurrentModel; + private readonly LumiaPhoneInfoAppModel CurrentModel; private readonly Action RequestModeSwitch; private readonly object LockDeviceInfo = new(); private bool DeviceInfoLoaded = false; @@ -33,7 +33,7 @@ namespace WPinternals internal NokiaModePhoneInfoViewModel(NokiaPhoneModel CurrentModel, Action RequestModeSwitch) : base() { - this.CurrentModel = (NokiaFlashModel)CurrentModel; + this.CurrentModel = (LumiaPhoneInfoAppModel)CurrentModel; this.RequestModeSwitch = RequestModeSwitch; } @@ -69,19 +69,7 @@ namespace WPinternals { PhoneInfo Info = CurrentModel.ReadPhoneInfo(); - if (Info.FlashAppProtocolVersionMajor < 2) - { - UefiSecurityStatusResponse SecurityStatus = CurrentModel.ReadSecurityStatus(); - - if (SecurityStatus != null) - { - EffectivePhoneInfoSecurityStatus = SecurityStatus.SecureFfuEfuseStatus && !SecurityStatus.AuthenticationStatus && !SecurityStatus.RdcStatus; - } - } - else - { - EffectivePhoneInfoSecurityStatus = Info.UefiSecureBootEnabled; - } + EffectivePhoneInfoSecurityStatus = Info.UefiSecureBootEnabled; LogFile.Log("Effective PhoneInfo Security Status: " + EffectivePhoneInfoSecurityStatus.ToString()); } @@ -104,6 +92,9 @@ namespace WPinternals case "Flash": RequestModeSwitch(PhoneInterfaces.Lumia_Flash); break; + case "BootMgr": + RequestModeSwitch(PhoneInterfaces.Lumia_Bootloader); + break; case "Label": RequestModeSwitch(PhoneInterfaces.Lumia_Label); break; diff --git a/WPinternals/ViewModels/NokiaNormalViewModel.cs b/WPinternals/ViewModels/NokiaNormalViewModel.cs index 09d4304..47c532f 100644 --- a/WPinternals/ViewModels/NokiaNormalViewModel.cs +++ b/WPinternals/ViewModels/NokiaNormalViewModel.cs @@ -384,6 +384,12 @@ namespace WPinternals case "Flash": RequestModeSwitch(PhoneInterfaces.Lumia_Flash); break; + case "PhoneInfo": + RequestModeSwitch(PhoneInterfaces.Lumia_PhoneInfo); + break; + case "BootMgr": + RequestModeSwitch(PhoneInterfaces.Lumia_Bootloader); + break; case "Label": RequestModeSwitch(PhoneInterfaces.Lumia_Label); break; diff --git a/WPinternals/ViewModels/NokiaPhoneInfoViewModel.cs b/WPinternals/ViewModels/NokiaPhoneInfoViewModel.cs index 4c9a1a6..a2a7e29 100644 --- a/WPinternals/ViewModels/NokiaPhoneInfoViewModel.cs +++ b/WPinternals/ViewModels/NokiaPhoneInfoViewModel.cs @@ -32,7 +32,7 @@ namespace WPinternals internal class NokiaPhoneInfoViewModel : ContextViewModel { - private readonly NokiaFlashModel CurrentModel; + private readonly LumiaPhoneInfoAppModel CurrentModel; private readonly Action RequestModeSwitch; internal Action SwitchToGettingStarted; private readonly object LockDeviceInfo = new(); @@ -41,7 +41,7 @@ namespace WPinternals internal NokiaPhoneInfoViewModel(NokiaPhoneModel CurrentModel, Action RequestModeSwitch, Action SwitchToGettingStarted) : base() { - this.CurrentModel = (NokiaFlashModel)CurrentModel; + this.CurrentModel = (LumiaPhoneInfoAppModel)CurrentModel; this.RequestModeSwitch = RequestModeSwitch; this.SwitchToGettingStarted = SwitchToGettingStarted; } @@ -192,6 +192,12 @@ namespace WPinternals case "Normal": RequestModeSwitch(PhoneInterfaces.Lumia_Normal); break; + case "Flash": + RequestModeSwitch(PhoneInterfaces.Lumia_Flash); + break; + case "BootMgr": + RequestModeSwitch(PhoneInterfaces.Lumia_Bootloader); + break; case "Label": RequestModeSwitch(PhoneInterfaces.Lumia_Label); break; diff --git a/WPinternals/ViewModels/PhoneNotifierViewModel.cs b/WPinternals/ViewModels/PhoneNotifierViewModel.cs index 906e92f..83f8ead 100644 --- a/WPinternals/ViewModels/PhoneNotifierViewModel.cs +++ b/WPinternals/ViewModels/PhoneNotifierViewModel.cs @@ -237,13 +237,12 @@ namespace WPinternals e.DevicePath.Contains("&PID_0A02", StringComparison.OrdinalIgnoreCase) || // VID_045E&PID_0A02 is for Lumia 950 e.DevicePath.Contains("&PID_05EE", StringComparison.OrdinalIgnoreCase)) // VID_0421&PID_05EE is for early RX100 { - CurrentModel = new NokiaFlashModel(e.DevicePath); - ((NokiaFlashModel)CurrentModel).InterfaceChanged += InterfaceChanged; - FlashAppType type = FlashAppType.FlashApp; try { - type = ((NokiaFlashModel)CurrentModel).GetFlashAppType(); + NokiaFlashModel tmpModel = new NokiaFlashModel(e.DevicePath); + type = tmpModel.GetFlashAppType(); + tmpModel.Dispose(); LogFile.Log("Flash App Type: " + type.ToString(), LogType.FileOnly); } catch @@ -255,6 +254,9 @@ namespace WPinternals { case FlashAppType.BootManager: { + CurrentModel = new LumiaBootManagerAppModel(e.DevicePath); + ((NokiaFlashModel)CurrentModel).InterfaceChanged += InterfaceChanged; + CurrentInterface = PhoneInterfaces.Lumia_Bootloader; LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); @@ -265,6 +267,9 @@ namespace WPinternals } case FlashAppType.FlashApp: { + CurrentModel = new LumiaFlashAppModel(e.DevicePath); + ((NokiaFlashModel)CurrentModel).InterfaceChanged += InterfaceChanged; + ((NokiaFlashModel)CurrentModel).DisableRebootTimeOut(); CurrentInterface = PhoneInterfaces.Lumia_Flash; LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); @@ -276,6 +281,9 @@ namespace WPinternals } case FlashAppType.PhoneInfoApp: { + CurrentModel = new LumiaPhoneInfoAppModel(e.DevicePath); + ((NokiaFlashModel)CurrentModel).InterfaceChanged += InterfaceChanged; + ((NokiaFlashModel)CurrentModel).DisableRebootTimeOut(); CurrentInterface = PhoneInterfaces.Lumia_PhoneInfo; LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); @@ -415,9 +423,80 @@ namespace WPinternals } } - private void InterfaceChanged(PhoneInterfaces NewInterface) + private void InterfaceChanged(PhoneInterfaces NewInterface, string DevicePath) { - CurrentInterface = NewInterface; + LastInterface = CurrentInterface; + CurrentInterface = null; + + if (CurrentModel != null) + { + CurrentModel.Dispose(); + CurrentModel = null; + LogFile.Log("Lumia disconnected", LogType.FileAndConsole); + } + + DeviceRemoved(); + + switch (NewInterface) + { + case PhoneInterfaces.Lumia_Bootloader: + { + CurrentModel = new LumiaBootManagerAppModel(DevicePath); + ((NokiaFlashModel)CurrentModel).InterfaceChanged += InterfaceChanged; + + CurrentInterface = PhoneInterfaces.Lumia_Bootloader; + LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly); + LogFile.Log("Device path: " + DevicePath, LogType.FileOnly); + LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); + LogFile.Log("Mode: Bootloader", LogType.FileAndConsole); + NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel)); + break; + } + case PhoneInterfaces.Lumia_Flash: + { + CurrentModel = new LumiaFlashAppModel(DevicePath); + ((NokiaFlashModel)CurrentModel).InterfaceChanged += InterfaceChanged; + + ((NokiaFlashModel)CurrentModel).DisableRebootTimeOut(); + CurrentInterface = PhoneInterfaces.Lumia_Flash; + LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly); + LogFile.Log("Device path: " + DevicePath, LogType.FileOnly); + LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); + LogFile.Log("Mode: Flash", LogType.FileAndConsole); + NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel)); + break; + } + case PhoneInterfaces.Lumia_PhoneInfo: + { + CurrentModel = new LumiaPhoneInfoAppModel(DevicePath); + ((NokiaFlashModel)CurrentModel).InterfaceChanged += InterfaceChanged; + + ((NokiaFlashModel)CurrentModel).DisableRebootTimeOut(); + CurrentInterface = PhoneInterfaces.Lumia_PhoneInfo; + LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly); + LogFile.Log("Device path: " + DevicePath, LogType.FileOnly); + LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); + LogFile.Log("Mode: Bootloader (Phone Info)", LogType.FileAndConsole); + NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel)); + break; + } + default: + { + LogFile.Log("Flash App Type could not be determined, assuming FlashApp", LogType.FileOnly); + + CurrentModel = new LumiaFlashAppModel(DevicePath); + ((NokiaFlashModel)CurrentModel).InterfaceChanged += InterfaceChanged; + + ((NokiaFlashModel)CurrentModel).DisableRebootTimeOut(); + CurrentInterface = PhoneInterfaces.Lumia_Flash; + LogFile.Log("Found device on interface: " + LumiaFlashInterfaceGuid.ToString(), LogType.FileOnly); + LogFile.Log("Device path: " + DevicePath, LogType.FileOnly); + LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); + LogFile.Log("Mode: Flash", LogType.FileAndConsole); + NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel)); + break; + } + } } private void LumiaNotifier_Removal(object sender, USBEvent e) diff --git a/WPinternals/ViewModels/RestoreViewModel.cs b/WPinternals/ViewModels/RestoreViewModel.cs index a5b34a6..dc5013e 100644 --- a/WPinternals/ViewModels/RestoreViewModel.cs +++ b/WPinternals/ViewModels/RestoreViewModel.cs @@ -113,7 +113,7 @@ namespace WPinternals Result = false; } - NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel; + LumiaFlashAppModel Phone = (LumiaFlashAppModel)PhoneNotifier.CurrentModel; BusyViewModel Busy = new("Restoring...", MaxProgressValue: TotalSizeSectors, UIContext: UIContext); ProgressUpdater Updater = Busy.ProgressUpdater; diff --git a/WPinternals/ViewModels/SwitchModeViewModel.cs b/WPinternals/ViewModels/SwitchModeViewModel.cs index b2303e9..91f190c 100644 --- a/WPinternals/ViewModels/SwitchModeViewModel.cs +++ b/WPinternals/ViewModels/SwitchModeViewModel.cs @@ -82,7 +82,7 @@ namespace WPinternals { if ((PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) && (TargetMode == PhoneInterfaces.Lumia_Flash)) { - PhoneInfo Info = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(false); + PhoneInfo Info = ((LumiaBootManagerAppModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(false); if (Info.BootManagerProtocolVersionMajor >= 2) { try @@ -95,7 +95,7 @@ namespace WPinternals // It does not disconnect / reconnect anymore and the apptype is changed immediately // NOKS still doesnt return a status // BootMgr v1 uses normal NOKS and waits for arrival of FlashApp - ((NokiaFlashModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext(); + ((LumiaBootManagerAppModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext(); // But this was called as a real switch, so we will raise an arrival event. PhoneNotifier.CurrentInterface = PhoneInterfaces.Lumia_Flash; @@ -233,6 +233,12 @@ namespace WPinternals { IsSwitching = true; + 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 + byte[] RebootCommandResult; + + bool ModernFlashApp; + // Make switch and set message or navigate to error switch (CurrentMode) { @@ -298,15 +304,11 @@ namespace WPinternals } break; case PhoneInterfaces.Lumia_Flash: - case PhoneInterfaces.Lumia_Bootloader: - 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 - byte[] RebootCommandResult; IsSwitchingInterface = true; switch (TargetMode) { case null: - ((NokiaFlashModel)CurrentModel).Shutdown(); + ((LumiaFlashAppModel)CurrentModel).Shutdown(); ModeSwitchProgressWrapper("Please disconnect your device. Waiting...", null); LogFile.Log("Please disconnect your device. Waiting...", LogType.FileAndConsole); new Thread(() => @@ -316,13 +318,154 @@ namespace WPinternals }).Start(); break; case PhoneInterfaces.Lumia_Normal: - ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); + ((LumiaFlashAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null); LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_Bootloader: - ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); + ((LumiaFlashAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ModeSwitchProgressWrapper("Rebooting phone to Bootloader mode...", null); + LogFile.Log("Rebooting phone to Bootloader mode", LogType.FileAndConsole); + break; + case PhoneInterfaces.Lumia_PhoneInfo: + ModernFlashApp = ((LumiaFlashAppModel)CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2; + if (ModernFlashApp) + { + ((LumiaFlashAppModel)CurrentModel).SwitchToPhoneInfoAppContext(); + } + else + { + ((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; + case PhoneInterfaces.Lumia_Label: + SwitchFromFlashToLabelMode(); + break; + case PhoneInterfaces.Lumia_Flash: // attempt to boot from limited flash to full flash + 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; + case PhoneInterfaces.Lumia_MassStorage: + SwitchFromFlashToMassStorageMode(); + break; + case PhoneInterfaces.Qualcomm_Download: + 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) + { + IsSwitchingInterface = false; + ModeSwitchErrorWrapper("Failed to switch to Qualcomm Download mode"); + } + else + { + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ModeSwitchProgressWrapper("Rebooting phone to Qualcomm Download mode...", null); + LogFile.Log("Rebooting phone to Qualcomm Download mode", LogType.FileAndConsole); + } + break; + default: + return; + } + break; + case PhoneInterfaces.Lumia_PhoneInfo: + IsSwitchingInterface = true; + switch (TargetMode) + { + case PhoneInterfaces.Lumia_Normal: + ((LumiaPhoneInfoAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null); + LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole); + break; + case PhoneInterfaces.Lumia_Bootloader: + 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 + 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; + case PhoneInterfaces.Lumia_Label: + SwitchFromPhoneInfoToLabelMode(); + break; + case PhoneInterfaces.Lumia_Flash: + ModernFlashApp = ((LumiaPhoneInfoAppModel)CurrentModel).ReadPhoneInfo().FlashAppProtocolVersionMajor >= 2; + if (ModernFlashApp) + { + ((LumiaPhoneInfoAppModel)CurrentModel).SwitchToFlashAppContext(); + } + else + { + ((LumiaPhoneInfoAppModel)CurrentModel).ContinueBoot(); + } + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ModeSwitchProgressWrapper("Rebooting phone to Flash mode...", null); + LogFile.Log("Rebooting phone to Flash mode", LogType.FileAndConsole); + break; + case PhoneInterfaces.Lumia_MassStorage: + SwitchFromPhoneInfoToMassStorageMode(); + break; + case PhoneInterfaces.Qualcomm_Download: + 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) + { + IsSwitchingInterface = false; + ModeSwitchErrorWrapper("Failed to switch to Qualcomm Download mode"); + } + else + { + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ModeSwitchProgressWrapper("Rebooting phone to Qualcomm Download mode...", null); + LogFile.Log("Rebooting phone to Qualcomm Download mode", LogType.FileAndConsole); + } + break; + default: + return; + } + break; + case PhoneInterfaces.Lumia_Bootloader: + IsSwitchingInterface = true; + switch (TargetMode) + { + case null: + ((LumiaBootManagerAppModel)CurrentModel).Shutdown(); + ModeSwitchProgressWrapper("Please disconnect your device. Waiting...", null); + LogFile.Log("Please disconnect your device. Waiting...", LogType.FileAndConsole); + new Thread(() => + { + PhoneNotifier.WaitForRemoval().Wait(); + ModeSwitchSuccessWrapper(); + }).Start(); + break; + case PhoneInterfaces.Lumia_Normal: + ((LumiaBootManagerAppModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + 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; ModeSwitchProgressWrapper("Rebooting phone to Bootloader mode...", null); LogFile.Log("Rebooting phone to Bootloader mode", LogType.FileAndConsole); @@ -332,7 +475,7 @@ namespace WPinternals break; case PhoneInterfaces.Lumia_Flash: // attempt to boot from limited flash to full flash byte[] RebootToFlashCommand = [0x4E, 0x4F, 0x4B, 0x53]; // NOKS - ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootToFlashCommand); + ((LumiaBootManagerAppModel)CurrentModel).ExecuteRawVoidMethod(RebootToFlashCommand); PhoneNotifier.NewDeviceArrived += NewDeviceArrived; ModeSwitchProgressWrapper("Rebooting phone to Flash mode...", null); LogFile.Log("Rebooting phone to Flash mode", LogType.FileAndConsole); @@ -341,8 +484,8 @@ namespace WPinternals SwitchFromFlashToMassStorageMode(); break; case PhoneInterfaces.Qualcomm_Download: - byte[] RebootToQualcommDownloadCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x43, 0x42, 0x45]; // NOKXCBE - RebootCommandResult = ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(RebootToQualcommDownloadCommand); + 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) { IsSwitchingInterface = false; @@ -423,6 +566,12 @@ namespace WPinternals case PhoneInterfaces.Lumia_Flash: ModeSwitchErrorWrapper("Failed to switch to Flash mode"); break; + case PhoneInterfaces.Lumia_Bootloader: + ModeSwitchErrorWrapper("Failed to switch to Boot Manager mode"); + break; + case PhoneInterfaces.Lumia_PhoneInfo: + ModeSwitchErrorWrapper("Failed to switch to Phone Info mode"); + break; case PhoneInterfaces.Lumia_Label: ModeSwitchErrorWrapper("Failed to switch to Label mode"); break; @@ -443,6 +592,12 @@ namespace WPinternals case PhoneInterfaces.Lumia_Flash: ModeSwitchErrorWrapper("Failed to switch to Flash mode"); break; + case PhoneInterfaces.Lumia_Bootloader: + ModeSwitchErrorWrapper("Failed to switch to Boot Manager mode"); + break; + case PhoneInterfaces.Lumia_PhoneInfo: + ModeSwitchErrorWrapper("Failed to switch to Phone Info mode"); + break; case PhoneInterfaces.Lumia_Label: ModeSwitchErrorWrapper("Failed to switch to Label mode"); break; @@ -471,7 +626,7 @@ namespace WPinternals // SwitchToFlashAppContext() will only switch context. Phone will not charge. // ResetPhoneToFlashMode() reboots to real flash app. Phone will charge. Works when in BootMgrApp, not when already in FlashApp. - ((NokiaFlashModel)CurrentModel).ResetPhoneToFlashMode(); + ((LumiaBootManagerAppModel)CurrentModel).ResetPhoneToFlashMode(); CurrentMode = PhoneInterfaces.Lumia_Flash; PhoneNotifier.NotifyArrival(); } @@ -511,7 +666,7 @@ namespace WPinternals else if ((CurrentMode == PhoneInterfaces.Lumia_Flash) && (TargetMode == PhoneInterfaces.Qualcomm_Download)) { byte[] RebootCommand = [0x4E, 0x4F, 0x4B, 0x52]; - byte[] RebootToQualcommDownloadCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x43, 0x42, 0x45]; // NOKXCBE + byte[] RebootToQualcommDownloadCommand = [0x4E, 0x4F, 0x4B, 0x58, 0x43, 0x42, 0x45]; // NOKXCBE // TODO IsSwitchingInterface = true; LogFile.Log("Sending command for rebooting to Emergency Download mode"); byte[] RebootCommandResult = ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(RebootToQualcommDownloadCommand); @@ -542,6 +697,12 @@ namespace WPinternals case PhoneInterfaces.Lumia_Flash: ModeSwitchErrorWrapper("Failed to switch to Flash mode"); break; + case PhoneInterfaces.Lumia_Bootloader: + ModeSwitchErrorWrapper("Failed to switch to Boot Manager mode"); + break; + case PhoneInterfaces.Lumia_PhoneInfo: + ModeSwitchErrorWrapper("Failed to switch to Phone Info mode"); + break; case PhoneInterfaces.Lumia_Label: ModeSwitchErrorWrapper("Failed to switch to Label mode"); break; @@ -552,21 +713,14 @@ namespace WPinternals } } - private void SwitchFromFlashToLabelMode(bool Continuation = false) + private async void SwitchFromPhoneInfoToLabelMode(bool Continuation = false) { string ProgressText = Continuation ? "And now preparing to boot the phone to Label mode..." : "Preparing to boot the phone to Label mode..."; - NokiaFlashModel FlashModel = (NokiaFlashModel)CurrentModel; - if (CurrentMode == PhoneInterfaces.Lumia_Bootloader) - { - try - { - FlashModel.SwitchToFlashAppContext(); - } - catch (Exception ex) - { - LogFile.LogException(ex, LogType.FileOnly); - } - } + + ((LumiaBootManagerAppModel)CurrentModel).SwitchToFlashAppContext(); + await PhoneNotifier.WaitForArrival(); + + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)CurrentModel; PhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: true); if (Info.MmosOverUsbSupported) @@ -632,29 +786,306 @@ namespace WPinternals byte[] RebootCommand = [0x4E, 0x4F, 0x4B, 0x52]; // NOKR BootModeFlagCommand[0x0F] = 0x59; - ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(BootModeFlagCommand); - ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); + ((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); } } + private void SwitchFromFlashToLabelMode(bool Continuation = false) + { + string ProgressText = Continuation ? "And now preparing to boot the phone to Label mode..." : "Preparing to boot the phone to Label mode..."; + + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)CurrentModel; + 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(ENOSWPackage, TempFolder, null, null, null); + + downloadEntry.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => + { + if (e.PropertyName == "Progress") + { + int progress = (sender as DownloadEntry)?.Progress ?? 0; + ulong.TryParse(progress.ToString(), out ulong 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(MMOSPath); + uint length = uint.Parse(info.Length.ToString()); + const int maximumbuffersize = 0x00240000; + uint totalcounts = (uint)Math.Truncate((decimal)length / maximumbuffersize); + + SetWorkingStatus("Flashing Test Mode package...", MaxProgressValue: 100); + + ProgressUpdater progressUpdater = new(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 = [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); + } + } + + private async void SwitchFromPhoneInfoToMassStorageMode(bool Continuation = false) + { + string ProgressText = Continuation ? "And now rebooting phone to Mass Storage mode..." : "Rebooting phone to Mass Storage mode..."; + + ((LumiaBootManagerAppModel)CurrentModel).SwitchToFlashAppContext(); + await PhoneNotifier.WaitForArrival(); + + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)CurrentModel; + 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) || (GPT.GetPartition("BACKUP_BS_NV") != null); + } + bool IsOriginalEngineeringLumia = !Info.IsBootloaderSecure && !IsUnlockedNew; + + if (IsOldLumia || IsOriginalEngineeringLumia) + { + 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]; + byte[] RebootToMassStorageCommand = [0x4E, 0x4F, 0x4B, 0x4D]; // NOKM + IsSwitchingInterface = true; + byte[] RebootCommandResult = ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(RebootToMassStorageCommand); + if (RebootCommandResult?.Length == 4) // This means fail: NOKU (unknown 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(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. + + 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 Parts = new(); + FlashPart Part = new(); + 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."); + } + } + private void SwitchFromFlashToMassStorageMode(bool Continuation = false) { string ProgressText = Continuation ? "And now rebooting phone to Mass Storage mode..." : "Rebooting phone to Mass Storage mode..."; - NokiaFlashModel FlashModel = (NokiaFlashModel)CurrentModel; if (CurrentMode == PhoneInterfaces.Lumia_Bootloader) { try { - FlashModel.SwitchToFlashAppContext(); + ((LumiaBootManagerAppModel)CurrentModel).SwitchToFlashAppContext(); } catch (Exception ex) { LogFile.LogException(ex, LogType.FileOnly); } } + LumiaFlashAppModel FlashModel = (LumiaFlashAppModel)CurrentModel; PhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: false); MassStorageWarning = null; diff --git a/WPinternals/Views/NokiaBootloaderView.xaml b/WPinternals/Views/NokiaBootloaderView.xaml index 47ec07a..30dc0c9 100644 --- a/WPinternals/Views/NokiaBootloaderView.xaml +++ b/WPinternals/Views/NokiaBootloaderView.xaml @@ -99,9 +99,38 @@ DEALINGS IN THE SOFTWARE. - - - diff --git a/WPinternals/Views/NokiaBootloaderView.xaml.cs b/WPinternals/Views/NokiaBootloaderView.xaml.cs index dd44404..e8193cd 100644 --- a/WPinternals/Views/NokiaBootloaderView.xaml.cs +++ b/WPinternals/Views/NokiaBootloaderView.xaml.cs @@ -18,9 +18,13 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Threading.Tasks; +using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; +using System.Windows.Media; +using System; namespace WPinternals { @@ -29,13 +33,41 @@ namespace WPinternals /// public partial class NokiaBootloaderView : UserControl { + private static PhoneNotifierViewModel PhoneNotifier; + private static SynchronizationContext UIContext; + public NokiaBootloaderView() { InitializeComponent(); + InterruptBoot = App.InterruptBoot; + UIContext = SynchronizationContext.Current; + // Setting these properties in XAML results in an error. Why? GifImage.GifSource = "/aerobusy.gif"; GifImage.AutoStart = true; + + Loaded += NokiaBootloaderView_Loaded; + Unloaded += NokiaBootloaderView_Unloaded; + } + + private void NokiaBootloaderView_Unloaded(object sender, RoutedEventArgs e) + { + PhoneNotifier.NewDeviceArrived -= PhoneNotifier_NewDeviceArrived; + } + + private void NokiaBootloaderView_Loaded(object sender, RoutedEventArgs e) + { + // Find the phone notifier + DependencyObject obj = (DependencyObject)sender; + while (!(obj is MainWindow)) + { + obj = VisualTreeHelper.GetParent(obj); + } + + PhoneNotifier = ((MainViewModel)((MainWindow)obj).DataContext).PhoneNotifier; + + PhoneNotifier.NewDeviceArrived += PhoneNotifier_NewDeviceArrived; } private void HandleHyperlinkClick(object sender, RoutedEventArgs args) @@ -43,10 +75,24 @@ namespace WPinternals Hyperlink link = args.Source as Hyperlink; if (link?.NavigateUri != null) { - if (link.NavigateUri.ToString() == "GettingStarted") + if (link.NavigateUri.ToString() == "Getting started") { - (this.DataContext as NokiaBootloaderViewModel)?.SwitchToGettingStarted(); - } (this.DataContext as NokiaBootloaderViewModel)?.RebootTo(link.NavigateUri.ToString()); + App.NavigateToGettingStarted(); + } + else if (link.NavigateUri.ToString() == "Unlock boot") + { + App.NavigateToUnlockBoot(); + } + else if (link.NavigateUri.ToString() == "Interrupt boot") + { + InterruptBoot = true; + } + else if (link.NavigateUri.ToString() == "Normal boot") + { + InterruptBoot = false; + } + + (this.DataContext as NokiaBootloaderViewModel)?.RebootTo(link.NavigateUri.ToString()); } } @@ -54,5 +100,41 @@ namespace WPinternals { (sender as FlowDocument)?.AddHandler(Hyperlink.ClickEvent, new RoutedEventHandler(HandleHyperlinkClick)); } + + public static readonly DependencyProperty InterruptBootProperty = + DependencyProperty.Register("InterruptBoot", typeof(Boolean), typeof(NokiaBootloaderView), new FrameworkPropertyMetadata(InterruptBootChanged)); + public bool InterruptBoot + { + get + { + return (bool)GetValue(InterruptBootProperty); + } + set + { + SetValue(InterruptBootProperty, value); + } + } + + internal static void InterruptBootChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + App.InterruptBoot = (bool)e.NewValue; + + if ((bool)e.NewValue && PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + { + App.InterruptBoot = false; + LogFile.Log("Found Lumia BootMgr and user forced to interrupt the boot process. Force to Flash-mode."); + Task.Run(() => SwitchModeViewModel.SwitchTo(PhoneNotifier, PhoneInterfaces.Lumia_Flash)); + } + } + + internal void PhoneNotifier_NewDeviceArrived(ArrivalEventArgs Args) + { + if (App.InterruptBoot && Args.NewInterface == PhoneInterfaces.Lumia_Bootloader) + { + App.InterruptBoot = false; + LogFile.Log("Found Lumia BootMgr and user forced to interrupt the boot process. Force to Flash-mode."); + Task.Run(() => SwitchModeViewModel.SwitchTo(PhoneNotifier, PhoneInterfaces.Lumia_Flash)); + } + } } } diff --git a/WPinternals/Views/NokiaModeFlashView.xaml b/WPinternals/Views/NokiaModeFlashView.xaml index fb56c3e..c03c4b7 100644 --- a/WPinternals/Views/NokiaModeFlashView.xaml +++ b/WPinternals/Views/NokiaModeFlashView.xaml @@ -58,6 +58,16 @@ DEALINGS IN THE SOFTWARE. + Switch to Phone-Info-mode + + + + + Switch to Boot-Manager-mode + + + + Switch to Label-mode diff --git a/WPinternals/Views/NokiaModePhoneInfoView.xaml b/WPinternals/Views/NokiaModePhoneInfoView.xaml index 22714ce..f910ee6 100644 --- a/WPinternals/Views/NokiaModePhoneInfoView.xaml +++ b/WPinternals/Views/NokiaModePhoneInfoView.xaml @@ -58,6 +58,16 @@ DEALINGS IN THE SOFTWARE. + Switch to Flash-mode + + + + + Switch to Boot-Manager-mode + + + + Switch to Label-mode diff --git a/WPinternals/Views/NokiaPhoneInfoView.xaml b/WPinternals/Views/NokiaPhoneInfoView.xaml index 3d1506f..1f62c00 100644 --- a/WPinternals/Views/NokiaPhoneInfoView.xaml +++ b/WPinternals/Views/NokiaPhoneInfoView.xaml @@ -82,12 +82,6 @@ DEALINGS IN THE SOFTWARE. - - - - - diff --git a/WPinternals/Views/NokiaPhoneInfoView.xaml.cs b/WPinternals/Views/NokiaPhoneInfoView.xaml.cs index aa9ea59..d2e1f07 100644 --- a/WPinternals/Views/NokiaPhoneInfoView.xaml.cs +++ b/WPinternals/Views/NokiaPhoneInfoView.xaml.cs @@ -32,10 +32,6 @@ namespace WPinternals public NokiaPhoneInfoView() { InitializeComponent(); - - // Setting these properties in XAML results in an error. Why? - GifImage.GifSource = "/aerobusy.gif"; - GifImage.AutoStart = true; } private void HandleHyperlinkClick(object sender, RoutedEventArgs args)