// Potentially blocking UI. Threadsafe. internal override void EvaluateViewState() { if (!IsActive) { return; } if (State == MachineState.LumiaUnlockBoot) { return; } lock (EvaluateViewStateLockObject) { switch (PhoneNotifier.CurrentInterface) { case PhoneInterfaces.Lumia_Normal: case PhoneInterfaces.Lumia_Label: IsSwitchingInterface = false; if (DoUnlock) { // Display View to switch to Flash mode LogFile.Log("Start unlock. Phone needs to switch to Flash-mode"); ActivateSubContext(new MessageViewModel("In order to start unlocking the bootloader, the phone needs to be switched to Flash-mode.", SwitchToFlashMode, Exit)); } else { // Display View to switch to Flash mode LogFile.Log("Start boot restore. Phone needs to switch to Flash-mode"); ActivateSubContext(new MessageViewModel("In order to start restoring the bootloader, the phone needs to be switched to Flash-mode.", SwitchToFlashMode, Exit)); } break; case PhoneInterfaces.Lumia_Flash: // Display View with device info and request for resources // Click on "Continue" will start processing all resources // Processing may fail with error message // Or processing will succeed and user will again be asked to continue with Bricking-procedure (to switch to emergency mode) // This code is not always invoked by OnArrival event. // So this is not always in a thread from the threadpool. // So we need to avoid UI blocking code here. IsSwitchingInterface = false; int TestPos = 0; try // In case phone reboots during the time that status is being read { // Some phones, like Lumia 928 verizon, do not support the Terminal interface! // To read the RootKeyHash we use ReadParam("RRKH"), instead of GetTerminalResponse().RootKeyHash. RootKeyHash = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadParam("RRKH"); TestPos = 1; UefiSecurityStatusResponse SecurityStatus = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadSecurityStatus(); if (SecurityStatus != null) { IsBootLoaderUnlocked = (SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus); } TestPos = 2; PhoneInfo Info = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(); if (SecurityStatus == null) { IsBootLoaderUnlocked = !Info.IsBootloaderSecure; } if (RootKeyHash == null) { RootKeyHash = Info.RKH; if (RootKeyHash == null) { RootKeyHash = new byte[32]; } } TestPos = 3; if (Info.FlashAppProtocolVersionMajor < 2) { // This action is executed after the resources are selected by the user. Action <string, string, string, string, string, string, bool> ReturnFunction = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) => { // Stop responding to device arrival here, because all connections are handled by subfunctions, not here. IsSwitchingInterface = true; State = MachineState.LumiaUnlockBoot; // This is a callback on the UI thread // Resources are confirmed by user this.FFUPath = FFUPath; this.LoadersPath = LoadersPath; this.SBL3Path = SBL3Path; this.SupportedFFUPath = SupportedFFUPath; StorePaths(); LogFile.Log("Processing resources:"); LogFile.Log("FFU: " + FFUPath); LogFile.Log("Loaders: " + LoadersPath); if (SBL3Path == null) { LogFile.Log("No SBL3 specified"); } else { LogFile.Log("SBL3: " + SBL3Path); } ActivateSubContext(new BusyViewModel("Processing resources...")); if (DoUnlock) { Task.Run(async() => { await LumiaUnlockBootloaderViewModel.LumiaV1UnlockFirmware(PhoneNotifier, FFUPath, LoadersPath, SBL3Path, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); }); } else { Task.Run(async() => { await LumiaUnlockBootloaderViewModel.LumiaV1RelockFirmware(PhoneNotifier, FFUPath, LoadersPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); }); } }; if (DoUnlock) { ActivateSubContext(new BootUnlockResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, false)); } else { ActivateSubContext(new BootRestoreResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, false)); } } else { bool AlreadyUnlocked = false; if (DoUnlock) { NokiaFlashModel FlashModel = (NokiaFlashModel)PhoneNotifier.CurrentModel; GPT GPT = FlashModel.ReadGPT(); if ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null)) { ExitMessage("Phone is already unlocked", null); return; //AlreadyUnlocked = true; } } TestPos = 4; // Stop responding to device arrival here, because all connections are handled by subfunctions, not here. IsSwitchingInterface = true; // This action is executed after the resources are selected by the user. Action <string, string, string, string, string, string, bool> ReturnFunction = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) => { IsSwitchingInterface = true; State = MachineState.LumiaUnlockBoot; if (DoUnlock) { // This is a callback on the UI thread // Resources are confirmed by user this.ProfileFFUPath = ProfileFFUPath; this.EDEPath = EDEPath; this.SupportedFFUPath = SupportedFFUPath; StorePaths(); if (DoFixBoot) { LogFile.Log("Fix Boot"); } else { LogFile.Log("Unlock Bootloader"); } LogFile.Log("Processing resources:"); LogFile.Log("Profile FFU: " + ProfileFFUPath); LogFile.Log("EDE file: " + EDEPath); if (SupportedFFUPath != null) { LogFile.Log("Donor-FFU with supported OS version: " + SupportedFFUPath); } Task.Run(async() => { if (DoFixBoot) { await LumiaV2UnlockBootViewModel.LumiaV2FixBoot(PhoneNotifier, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); } else if (!AlreadyUnlocked) { await LumiaUnlockBootloaderViewModel.LumiaV2UnlockUEFI(PhoneNotifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); } else { await LumiaUnlockBootloaderViewModel.LumiaV2UnlockUEFI(PhoneNotifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage, true); } }); } else { Task.Run(async() => { FFU ProfileFFU = null; List <FFUEntry> FFUs = App.Config.FFURepository.Where(e => (Info.PlatformID.StartsWith(e.PlatformID, StringComparison.OrdinalIgnoreCase) && e.Exists())).ToList(); if (FFUs.Count() > 0) { ProfileFFU = new FFU(FFUs[0].Path); } else { throw new WPinternalsException("Profile FFU missing", "No profile FFU has been found in the repository for your device. You can add a profile FFU within the download section of the tool or by using the command line."); } LogFile.Log("Profile FFU: " + ProfileFFU.Path); await LumiaUnlockBootloaderViewModel.LumiaV2RelockUEFI(PhoneNotifier, ProfileFFU.Path, true, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); }); } }; TestPos = 5; if (DoUnlock) { ActivateSubContext(new BootUnlockResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, true, Info.PlatformID, Info.Type)); } else { ActivateSubContext(new BootRestoreResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, true, Info.PlatformID, Info.Type)); } } } catch (Exception Ex) { LogFile.LogException(Ex, LogType.FileAndConsole, TestPos.ToString()); } break; case PhoneInterfaces.Qualcomm_Download: IsSwitchingInterface = false; // If resources are not confirmed yet, then display view with device info and request for resources. QualcommDownload Download = new QualcommDownload((QualcommSerial)PhoneNotifier.CurrentModel); byte[] QualcommRootKeyHash; try { QualcommRootKeyHash = Download.GetRKH(); } catch (BadConnectionException) { // This is a Spec B device break; } if (RootKeyHash == null) { RootKeyHash = QualcommRootKeyHash; } else if (!StructuralComparisons.StructuralEqualityComparer.Equals(RootKeyHash, QualcommRootKeyHash)) { LogFile.Log("Error: Root Key Hash in Qualcomm Emergency mode does not match!"); ActivateSubContext(new MessageViewModel("Error: Root Key Hash in Qualcomm Emergency mode does not match!", Callback)); return; } // This action is executed after the user selected the resources. Action <string, string, string, string, string, string, bool> ReturnFunctionD = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) => { IsSwitchingInterface = true; State = MachineState.LumiaUnlockBoot; // This is a callback on the UI thread // Resources are confirmed by user this.FFUPath = FFUPath; this.LoadersPath = LoadersPath; this.SBL3Path = SBL3Path; StorePaths(); LogFile.Log("Processing resources:"); LogFile.Log("FFU: " + FFUPath); LogFile.Log("Loaders: " + LoadersPath); if (SBL3Path == null) { LogFile.Log("No SBL3 specified"); } else { LogFile.Log("SBL3: " + SBL3Path); } ActivateSubContext(new BusyViewModel("Processing resources...")); if (DoUnlock) { Task.Run(async() => { await LumiaUnlockBootloaderViewModel.LumiaV1UnlockFirmware(PhoneNotifier, FFUPath, LoadersPath, SBL3Path, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); }); } else { Task.Run(async() => { await LumiaUnlockBootloaderViewModel.LumiaV1RelockFirmware(PhoneNotifier, FFUPath, LoadersPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); }); } }; if (DoUnlock) { ActivateSubContext(new BootUnlockResourcesViewModel("Qualcomm Emergency Download mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunctionD, Abort, IsBootLoaderUnlocked, false)); } else { ActivateSubContext(new BootRestoreResourcesViewModel("Qualcomm Emergency Download mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunctionD, Abort, IsBootLoaderUnlocked, false)); } break; case PhoneInterfaces.Qualcomm_Flash: { IsSwitchingInterface = true; State = MachineState.LumiaUnlockBoot; ActivateSubContext(new BusyViewModel("Recovering resources...")); LogFile.Log("Phone was unexpectedly detected in this mode while resources were not loaded yet."); LogFile.Log("WPInternals tool probably crashed in previous session."); LogFile.Log("Trying to recover resources from the registry."); FFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "FFUPath", null); SupportedFFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SupportedFFUPath", null); LoadersPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "LoadersPath", null); if (DoUnlock) { SBL3Path = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SBL3Path", null); } else { SBL3Path = null; } if (DoUnlock) { Task.Run(async() => { await LumiaUnlockBootloaderViewModel.LumiaV1UnlockFirmware(PhoneNotifier, FFUPath, LoadersPath, SBL3Path, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); }); } else { Task.Run(async() => { await LumiaUnlockBootloaderViewModel.LumiaV1RelockFirmware(PhoneNotifier, FFUPath, LoadersPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); }); } break; } default: // Show View "Waiting for connection" IsSwitchingInterface = false; ActivateSubContext(null); break; } } }
internal void FlashFFUTask(string FFUPath) { new Thread(() => { bool Result = true; NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel; PhoneInfo Info = Phone.ReadPhoneInfo(false); #region Remove bootloader changes // If necessary remove bootloader changes // In case the NV vars were redirected, and a stock FFU is flashed, then the IsFlashing flag will be cleared in the redirected NV vars // And after a reboot the original NV vars are active again, but the IsFlashing flag is still set from when the bootloader was unlocked // So we will first restore the GPT, so the original vars are active again. // Then IsFlashing is true and the phone boots forcibly to FlashApp again. // Then we start normal FFU flasing and at the end the IsFlashing flag is cleared in the original vars. if (Info.FlashAppProtocolVersionMajor >= 2) { byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(Phone, 0x20000); // TODO: Get proper profile FFU and get ChunkSizeInBytes GPT GPT = new GPT(GPTChunk); FlashPart Part; List <FlashPart> FlashParts = new List <FlashPart>(); Partition NvBackupPartition = GPT.GetPartition("BACKUP_BS_NV"); if (NvBackupPartition != null) { // This must be a left over of a half unlocked bootloader Partition NvPartition = GPT.GetPartition("UEFI_BS_NV"); NvBackupPartition.Name = "UEFI_BS_NV"; NvBackupPartition.PartitionGuid = NvPartition.PartitionGuid; NvBackupPartition.PartitionTypeGuid = NvPartition.PartitionTypeGuid; GPT.Partitions.Remove(NvPartition); GPT.Rebuild(); Part = new FlashPart(); Part.StartSector = 0; Part.Stream = new MemoryStream(GPTChunk); FlashParts.Add(Part); } bool ClearFlashingStatus = true; // We should only clear NV if there was no backup NV to be restored and the current NV contains the SB unlock. if ((NvBackupPartition == null) && !Info.UefiSecureBootEnabled) { // ClearNV Part = new FlashPart(); Partition Target = GPT.GetPartition("UEFI_BS_NV"); Part.StartSector = (UInt32)Target.FirstSector; Part.Stream = new MemoryStream(new byte[0x40000]); FlashParts.Add(Part); ClearFlashingStatus = false; } if (FlashParts.Count > 0) { ActivateSubContext(new BusyViewModel("Restoring bootloader...")); WPinternalsStatus LastStatus = WPinternalsStatus.Undefined; LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(PhoneNotifier, FFUPath, false, false, FlashParts, true, ClearFlashingStatusAtEnd: ClearFlashingStatus, SetWorkingStatus: (m, s, v, a, st) => { if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset)) { SetWorkingStatus(m, s, v, a, st); } else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset)) { SetWorkingStatus("Restoring bootloader...", null, null, Status: WPinternalsStatus.Flashing); } LastStatus = st; }, UpdateWorkingStatus: (m, s, v, st) => { if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset)) { UpdateWorkingStatus(m, s, v, st); } else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset)) { SetWorkingStatus("Restoring bootloader...", null, null, Status: WPinternalsStatus.Flashing); } LastStatus = st; } ).Wait(); if ((PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (PhoneNotifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)) { PhoneNotifier.WaitForArrival().Wait(); } if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) { ((NokiaFlashModel)PhoneNotifier.CurrentModel).SwitchToFlashAppContext(); } } } #endregion Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel; ActivateSubContext(new BusyViewModel("Initializing flash...")); string ErrorSubMessage = null; try { FFU FFU = new FFU(FFUPath); BusyViewModel Busy = new BusyViewModel("Flashing original FFU...", MaxProgressValue: FFU.TotalChunkCount, UIContext: UIContext); ActivateSubContext(Busy); byte Options = 0; if (!Info.IsBootloaderSecure) { Options = (byte)((FlashOptions)Options | FlashOptions.SkipSignatureCheck); } Phone.FlashFFU(FFU, Busy.ProgressUpdater, true, Options); } catch (Exception Ex) { LogFile.LogException(Ex); if (Ex is WPinternalsException) { ErrorSubMessage = ((WPinternalsException)Ex).SubMessage; } Result = false; } if (!Result) { ExitFailure("Flash failed!", ErrorSubMessage); return; } ExitSuccess("Flash successful!", null); }).Start(); }
// V3 exploit // Magic // internal async static Task LumiaV3CustomFlash(PhoneNotifierViewModel Notifier, List <FlashPart> FlashParts, bool CheckSectorAlignment = true, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null) { if (SetWorkingStatus == null) { SetWorkingStatus = (m, s, v, a, st) => { } } ; if (UpdateWorkingStatus == null) { UpdateWorkingStatus = (m, s, v, st) => { } } ; if (ExitSuccess == null) { ExitSuccess = (m, s) => { } } ; if (ExitFailure == null) { ExitFailure = (m, s) => { } } ; uint chunkSize = 131072u; int chunkSizes = 131072; if (FlashParts != null) { foreach (FlashPart Part in FlashParts) { if (Part.Stream == null) { throw new ArgumentException("Stream is null"); } if (!Part.Stream.CanSeek) { throw new ArgumentException("Streams must be seekable"); } if (((Part.StartSector * 0x200) % chunkSize) != 0) { throw new ArgumentException("Invalid StartSector alignment"); } if (CheckSectorAlignment) { if ((Part.Stream.Length % chunkSize) != 0) { throw new ArgumentException("Invalid Data length"); } } } } try { NokiaFlashModel Model = (NokiaFlashModel)Notifier.CurrentModel; PhoneInfo Info = Model.ReadPhoneInfo(); if ((Info.SecureFfuSupportedProtocolMask & ((ushort)FfuProtocol.ProtocolSyncV2)) == 0) // Exploit needs protocol v2 -> This check is not conclusive, because old phones also report support for this protocol, although it is really not supported. { throw new WPinternalsException("Flash failed!", "Protocols not supported. The phone reports that it does not support the Protocol Sync V2."); } if (Info.FlashAppProtocolVersionMajor < 2) // Old phones do not support the hack. These phones have Flash protocol 1.x. { throw new WPinternalsException("Flash failed!", "Protocols not supported. The phone reports that Flash App communication protocol is lower than 2. Reported version by the phone: " + Info.FlashAppProtocolVersionMajor + "."); } if (Info.UefiSecureBootEnabled) { throw new WPinternalsException("Flash failed!", "UEFI Secureboot must be disabled for the Flash V3 exploit to work."); } // The payloads must be ordered by the number of locations // // FlashApp processes payloads like this: // - First payloads which are with one location, those can be sent in bulk // - Then payloads with more than one location, those should not be sent in bulk // // If you do not order payloads like this, you will get an error, most likely hash mismatch // LumiaV2UnlockBootViewModel.FlashingPayload[] payloads = new LumiaV2UnlockBootViewModel.FlashingPayload[0]; if (FlashParts != null) { payloads = LumiaV2UnlockBootViewModel.GetNonOptimizedPayloads(FlashParts, chunkSizes, (uint)(Info.WriteBufferSize / chunkSize), SetWorkingStatus, UpdateWorkingStatus).OrderBy(x => x.TargetLocations.Count()).ToArray(); } MemoryStream Headerstream1 = new MemoryStream(); // ============================== // Header 1 start ImageHeader image = new ImageHeader(); FullFlash ffimage = new FullFlash(); Store simage = new Store(); // Todo make this read the image itself ffimage.OSVersion = "10.0.11111.0"; ffimage.DevicePlatformId0 = Info.PlatformID; ffimage.AntiTheftVersion = "1.1"; simage.SectorSize = 512; simage.MinSectorCount = Info.EmmcSizeInSectors; //Logging.Log("Generating image manifest..."); string manifest = ManifestIni.BuildUpManifest(ffimage, simage);//, partitions); byte[] TextBytes = System.Text.Encoding.ASCII.GetBytes(manifest); image.ManifestLength = (UInt32)TextBytes.Length; byte[] ImageHeaderBuffer = new byte[0x18]; ByteOperations.WriteUInt32(ImageHeaderBuffer, 0, image.Size); ByteOperations.WriteAsciiString(ImageHeaderBuffer, 0x04, image.Signature); ByteOperations.WriteUInt32(ImageHeaderBuffer, 0x10, image.ManifestLength); ByteOperations.WriteUInt32(ImageHeaderBuffer, 0x14, image.ChunkSize); Headerstream1.Write(ImageHeaderBuffer, 0, 0x18); Headerstream1.Write(TextBytes, 0, TextBytes.Length); RoundUpToChunks(Headerstream1, chunkSize); // Header 1 stop + round // ============================== MemoryStream Headerstream2 = new MemoryStream(); // ============================== // Header 2 start StoreHeader store = new StoreHeader(); store.WriteDescriptorCount = (UInt32)payloads.Count(); store.FinalTableIndex = (UInt32)payloads.Count() - store.FinalTableCount; store.PlatformId = Info.PlatformID; foreach (LumiaV2UnlockBootViewModel.FlashingPayload payload in payloads) { store.WriteDescriptorLength += payload.GetStoreHeaderSize(); } byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(Model, 0x20000); GPT GPT = new GPT(GPTChunk); UInt64 PlatEnd = 0; if (GPT.Partitions.Any(x => x.Name == "PLAT")) { PlatEnd = GPT.GetPartition("PLAT").LastSector; } foreach (LumiaV2UnlockBootViewModel.FlashingPayload payload in payloads) { if (payload.TargetLocations.First() > PlatEnd) { break; } store.FlashOnlyTableIndex += 1; } byte[] StoreHeaderBuffer = new byte[0xF8]; ByteOperations.WriteUInt32(StoreHeaderBuffer, 0, store.UpdateType); ByteOperations.WriteUInt16(StoreHeaderBuffer, 0x04, store.MajorVersion); ByteOperations.WriteUInt16(StoreHeaderBuffer, 0x06, store.MinorVersion); ByteOperations.WriteUInt16(StoreHeaderBuffer, 0x08, store.FullFlashMajorVersion); ByteOperations.WriteUInt16(StoreHeaderBuffer, 0x0A, store.FullFlashMinorVersion); ByteOperations.WriteAsciiString(StoreHeaderBuffer, 0x0C, store.PlatformId); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xCC, store.BlockSizeInBytes); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xD0, store.WriteDescriptorCount); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xD4, store.WriteDescriptorLength); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xD8, store.ValidateDescriptorCount); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xDC, store.ValidateDescriptorLength); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xE0, store.InitialTableIndex); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xE4, store.InitialTableCount); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xE8, store.FlashOnlyTableIndex); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xEC, store.FlashOnlyTableCount); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xF0, store.FinalTableIndex); ByteOperations.WriteUInt32(StoreHeaderBuffer, 0xF4, store.FinalTableCount); Headerstream2.Write(StoreHeaderBuffer, 0, 0xF8); byte[] descriptorsBuffer = new byte[store.WriteDescriptorLength]; UInt32 NewWriteDescriptorOffset = 0; foreach (LumiaV2UnlockBootViewModel.FlashingPayload payload in payloads) { ByteOperations.WriteUInt32(descriptorsBuffer, NewWriteDescriptorOffset + 0x00, (UInt32)payload.TargetLocations.Count()); // Location count ByteOperations.WriteUInt32(descriptorsBuffer, NewWriteDescriptorOffset + 0x04, payload.ChunkCount); // Chunk count NewWriteDescriptorOffset += 0x08; foreach (UInt32 location in payload.TargetLocations) { ByteOperations.WriteUInt32(descriptorsBuffer, NewWriteDescriptorOffset + 0x00, 0x00000000); // Disk access method (0 = Begin, 2 = End) ByteOperations.WriteUInt32(descriptorsBuffer, NewWriteDescriptorOffset + 0x04, location); // Chunk index NewWriteDescriptorOffset += 0x08; } } Headerstream2.Write(descriptorsBuffer, 0, (Int32)store.WriteDescriptorLength); RoundUpToChunks(Headerstream2, chunkSize); // Header 2 stop + round // ============================== SecurityHeader security = new SecurityHeader(); Headerstream1.Seek(0, SeekOrigin.Begin); Headerstream2.Seek(0, SeekOrigin.Begin); security.HashTableSize = 0x20 * (UInt32)((Headerstream1.Length + Headerstream2.Length) / chunkSize); foreach (LumiaV2UnlockBootViewModel.FlashingPayload payload in payloads) { security.HashTableSize += payload.GetSecurityHeaderSize(); } byte[] HashTable = new byte[security.HashTableSize]; BinaryWriter bw = new BinaryWriter(new MemoryStream(HashTable)); SHA256 crypto = SHA256.Create(); for (Int32 i = 0; i < Headerstream1.Length / chunkSize; i++) { byte[] buffer = new byte[chunkSize]; Headerstream1.Read(buffer, 0, (Int32)chunkSize); byte[] hash = crypto.ComputeHash(buffer); bw.Write(hash, 0, hash.Length); } for (Int32 i = 0; i < Headerstream2.Length / chunkSize; i++) { byte[] buffer = new byte[chunkSize]; Headerstream2.Read(buffer, 0, (Int32)chunkSize); byte[] hash = crypto.ComputeHash(buffer); bw.Write(hash, 0, hash.Length); } foreach (LumiaV2UnlockBootViewModel.FlashingPayload payload in payloads) { bw.Write(payload.ChunkHashes[0], 0, payload.ChunkHashes[0].Length); } bw.Close(); //Logging.Log("Generating image catalog..."); byte[] catalog = GenerateCatalogFile(HashTable); security.CatalogSize = (UInt32)catalog.Length; byte[] SecurityHeaderBuffer = new byte[0x20]; ByteOperations.WriteUInt32(SecurityHeaderBuffer, 0, security.Size); ByteOperations.WriteAsciiString(SecurityHeaderBuffer, 0x04, security.Signature); ByteOperations.WriteUInt32(SecurityHeaderBuffer, 0x10, security.ChunkSizeInKb); ByteOperations.WriteUInt32(SecurityHeaderBuffer, 0x14, security.HashAlgorithm); ByteOperations.WriteUInt32(SecurityHeaderBuffer, 0x18, security.CatalogSize); ByteOperations.WriteUInt32(SecurityHeaderBuffer, 0x1C, security.HashTableSize); MemoryStream retstream = new MemoryStream(); retstream.Write(SecurityHeaderBuffer, 0, 0x20); retstream.Write(catalog, 0, (Int32)security.CatalogSize); retstream.Write(HashTable, 0, (Int32)security.HashTableSize); RoundUpToChunks(retstream, chunkSize); Headerstream1.Seek(0, SeekOrigin.Begin); Headerstream2.Seek(0, SeekOrigin.Begin); byte[] buff = new byte[Headerstream1.Length]; Headerstream1.Read(buff, 0, (Int32)Headerstream1.Length); Headerstream1.Close(); retstream.Write(buff, 0, buff.Length); buff = new byte[Headerstream2.Length]; Headerstream2.Read(buff, 0, (Int32)Headerstream2.Length); Headerstream2.Close(); retstream.Write(buff, 0, buff.Length); // -------- // Go back to the beginning retstream.Seek(0, SeekOrigin.Begin); byte[] FfuHeader = new byte[retstream.Length]; await retstream.ReadAsync(FfuHeader, 0, (Int32)retstream.Length); retstream.Close(); Byte Options = 0; if (!Info.IsBootloaderSecure) { Options = (Byte)((FlashOptions)Options | FlashOptions.SkipSignatureCheck); } LogFile.Log("Flash in progress...", LogType.ConsoleOnly); SetWorkingStatus("Flashing...", null, (UInt64?)payloads.Count(), Status: WPinternalsStatus.Flashing); Model.SendFfuHeaderV1(FfuHeader, Options); UInt64 counter = 0; Int32 numberOfPayloadsToSendAtOnce = 1; if ((Info.SecureFfuSupportedProtocolMask & (UInt16)FfuProtocol.ProtocolSyncV2) != 0) { numberOfPayloadsToSendAtOnce = (Int32)Math.Round((Double)Info.WriteBufferSize / chunkSize); } byte[] payloadBuffer; for (Int32 i = 0; i < payloads.Count(); i += numberOfPayloadsToSendAtOnce) { if (i + numberOfPayloadsToSendAtOnce - 1 >= payloads.Count()) { numberOfPayloadsToSendAtOnce = payloads.Count() - i; } payloadBuffer = new byte[numberOfPayloadsToSendAtOnce * chunkSizes]; string ProgressText = "Flashing resources"; for (Int32 j = 0; j < numberOfPayloadsToSendAtOnce; j++) { LumiaV2UnlockBootViewModel.FlashingPayload payload = payloads[i + j]; UInt32 StreamIndex = payload.StreamIndexes.First(); FlashPart flashPart = FlashParts[(Int32)StreamIndex]; if (flashPart.ProgressText != null) { ProgressText = flashPart.ProgressText; } Stream Stream = flashPart.Stream; Stream.Seek(payload.StreamLocations.First(), SeekOrigin.Begin); Stream.Read(payloadBuffer, j * chunkSizes, chunkSizes); counter++; } if ((Info.SecureFfuSupportedProtocolMask & (ushort)FfuProtocol.ProtocolSyncV2) != 0) { Model.SendFfuPayloadV2(payloadBuffer, Int32.Parse((counter * 100 / (UInt64)payloads.Count()).ToString())); } else { Model.SendFfuPayloadV1(payloadBuffer, Int32.Parse((counter * 100 / (UInt64)payloads.Count()).ToString())); } UpdateWorkingStatus(ProgressText, null, counter, WPinternalsStatus.Flashing); } Model.ResetPhone(); await Notifier.WaitForRemoval(); ExitSuccess("Flash succeeded!", null); } catch { throw new WPinternalsException("Custom flash failed"); } } } }