mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-14 03:16:40 +10:00
509 lines
23 KiB
C#
509 lines
23 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace WPinternals
|
|
{
|
|
internal class LumiaV3FlashRomViewModel : ContextViewModel
|
|
{
|
|
private static void RoundUpToChunks(Stream stream, UInt32 chunkSize)
|
|
{
|
|
Int64 Size = stream.Length;
|
|
if ((Size % chunkSize) > 0)
|
|
{
|
|
Int64 padding = (UInt32)(((Size / chunkSize) + 1) * chunkSize) - Size;
|
|
stream.Write(new byte[padding], 0, (Int32)padding);
|
|
}
|
|
}
|
|
|
|
internal class ImageHeader
|
|
{
|
|
public UInt32 Size = 24;
|
|
public string Signature = "ImageFlash ";
|
|
public UInt32 ManifestLength;
|
|
public UInt32 ChunkSize = 128;
|
|
}
|
|
|
|
public class StoreHeader
|
|
{
|
|
public UInt32 UpdateType = 0; // Full update
|
|
|
|
public UInt16 MajorVersion = 1;
|
|
public UInt16 MinorVersion = 0;
|
|
public UInt16 FullFlashMajorVersion = 2;
|
|
public UInt16 FullFlashMinorVersion = 0;
|
|
|
|
// Size is 0xC0
|
|
public string PlatformId;
|
|
|
|
public UInt32 BlockSizeInBytes = 131072;
|
|
public UInt32 WriteDescriptorCount;
|
|
public UInt32 WriteDescriptorLength;
|
|
public UInt32 ValidateDescriptorCount = 0;
|
|
public UInt32 ValidateDescriptorLength = 0;
|
|
public UInt32 InitialTableIndex = 0;
|
|
public UInt32 InitialTableCount = 0;
|
|
public UInt32 FlashOnlyTableIndex = 0; // Should be the index of the critical partitions, but for now we don't implement that
|
|
public UInt32 FlashOnlyTableCount = 1;
|
|
public UInt32 FinalTableIndex; //= WriteDescriptorCount - FinalTableCount;
|
|
public UInt32 FinalTableCount = 0;
|
|
}
|
|
|
|
public class SecurityHeader
|
|
{
|
|
public UInt32 Size = 32;
|
|
public string Signature = "SignedImage ";
|
|
public UInt32 ChunkSizeInKb = 128;
|
|
public UInt32 HashAlgorithm = 32780; // SHA256 algorithm id
|
|
public UInt32 CatalogSize;
|
|
public UInt32 HashTableSize;
|
|
}
|
|
|
|
internal class ManifestIni
|
|
{
|
|
internal static string BuildUpManifest(FullFlash fullFlash, Store store)
|
|
{
|
|
string Manifest = "[FullFlash]\r\n";
|
|
if (!string.IsNullOrEmpty(fullFlash.AntiTheftVersion))
|
|
{
|
|
Manifest += "AntiTheftVersion = " + fullFlash.AntiTheftVersion + "\r\n";
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(fullFlash.OSVersion))
|
|
{
|
|
Manifest += "OSVersion = " + fullFlash.OSVersion + "\r\n";
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(fullFlash.Description))
|
|
{
|
|
Manifest += "Description = " + fullFlash.Description + "\r\n";
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(fullFlash.Version))
|
|
{
|
|
Manifest += "Version = " + fullFlash.Version + "\r\n";
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(fullFlash.DevicePlatformId0))
|
|
{
|
|
Manifest += "DevicePlatformId0 = " + fullFlash.DevicePlatformId0 + "\r\n";
|
|
}
|
|
|
|
Manifest += "\r\n[Store]\r\n";
|
|
Manifest += "SectorSize = " + store.SectorSize + "\r\n";
|
|
Manifest += "MinSectorCount = " + store.MinSectorCount + "\r\n";
|
|
Manifest += "\r\n";
|
|
|
|
return Manifest;
|
|
}
|
|
}
|
|
|
|
internal class FullFlash
|
|
{
|
|
public string AntiTheftVersion = "1.1"; // Allow flashing on all devices
|
|
public string OSVersion;
|
|
public string Description = "Update on: " + DateTime.Now.ToString("u") + "::\r\n";
|
|
public string Version = "2.0";
|
|
public string DevicePlatformId0;
|
|
}
|
|
|
|
internal class Store
|
|
{
|
|
public UInt32 SectorSize;
|
|
public UInt32 MinSectorCount;
|
|
}
|
|
|
|
// V3 exploit
|
|
// Magic
|
|
//
|
|
private static byte[] GenerateCatalogFile(byte[] hashData)
|
|
{
|
|
byte[] catalog_first_part = new byte[] { 0x30, 0x82, 0x01, 0x44, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02, 0xA0, 0x82, 0x01, 0x35, 0x30, 0x82, 0x01, 0x31, 0x02, 0x01, 0x01, 0x31, 0x00, 0x30, 0x82, 0x01, 0x26, 0x06, 0x09, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0A, 0x01, 0xA0, 0x82, 0x01, 0x17, 0x30, 0x82, 0x01, 0x13, 0x30, 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0C, 0x01, 0x01, 0x04, 0x10, 0xA8, 0xCA, 0xD9, 0x7D, 0xBF, 0x6D, 0x67, 0x4D, 0xB1, 0x4D, 0x62, 0xFB, 0xE6, 0x26, 0x22, 0xD4, 0x17, 0x0D, 0x32, 0x30, 0x30, 0x31, 0x31, 0x30, 0x31, 0x32, 0x31, 0x32, 0x32, 0x37, 0x5A, 0x30, 0x0E, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0C, 0x01, 0x02, 0x05, 0x00, 0x30, 0x81, 0xD1, 0x30, 0x81, 0xCE, 0x04, 0x1E, 0x48, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x54, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x2E, 0x00, 0x62, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x62, 0x00, 0x00, 0x00, 0x31, 0x81, 0xAB, 0x30, 0x45, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04, 0x31, 0x37, 0x30, 0x35, 0x30, 0x10, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x19, 0xA2, 0x02, 0x80, 0x00, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14 };
|
|
byte[] catalog_second_part = new byte[] { 0x30, 0x62, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0C, 0x02, 0x02, 0x31, 0x54, 0x30, 0x52, 0x1E, 0x4C, 0x00, 0x7B, 0x00, 0x44, 0x00, 0x45, 0x00, 0x33, 0x00, 0x35, 0x00, 0x31, 0x00, 0x41, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2D, 0x00, 0x38, 0x00, 0x45, 0x00, 0x35, 0x00, 0x39, 0x00, 0x2D, 0x00, 0x31, 0x00, 0x31, 0x00, 0x44, 0x00, 0x30, 0x00, 0x2D, 0x00, 0x38, 0x00, 0x43, 0x00, 0x34, 0x00, 0x37, 0x00, 0x2D, 0x00, 0x30, 0x00, 0x30, 0x00, 0x43, 0x00, 0x30, 0x00, 0x34, 0x00, 0x46, 0x00, 0x43, 0x00, 0x32, 0x00, 0x39, 0x00, 0x35, 0x00, 0x45, 0x00, 0x45, 0x00, 0x7D, 0x02, 0x02, 0x02, 0x00, 0x31, 0x00 };
|
|
|
|
byte[] hash = new SHA1Managed().ComputeHash(hashData);
|
|
|
|
byte[] catalog = new byte[catalog_first_part.Length + hash.Length + catalog_second_part.Length];
|
|
Buffer.BlockCopy(catalog_first_part, 0, catalog, 0, catalog_first_part.Length);
|
|
Buffer.BlockCopy(hash, 0, catalog, catalog_first_part.Length, hash.Length);
|
|
Buffer.BlockCopy(catalog_second_part, 0, catalog, catalog_first_part.Length + hash.Length, catalog_second_part.Length);
|
|
|
|
return catalog;
|
|
}
|
|
|
|
// 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) => { };
|
|
}
|
|
|
|
const uint chunkSize = 131072u;
|
|
const 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 = Array.Empty<LumiaV2UnlockBootViewModel.FlashingPayload>();
|
|
if (FlashParts != null)
|
|
{
|
|
payloads = LumiaV2UnlockBootViewModel.GetNonOptimizedPayloads(FlashParts, chunkSizes, Info.WriteBufferSize / chunkSize, SetWorkingStatus, UpdateWorkingStatus).OrderBy(x => x.TargetLocations.Length).ToArray();
|
|
}
|
|
|
|
MemoryStream Headerstream1 = new();
|
|
|
|
// ==============================
|
|
// Header 1 start
|
|
|
|
ImageHeader image = new();
|
|
FullFlash ffimage = new();
|
|
Store simage = new();
|
|
|
|
// 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();
|
|
|
|
// ==============================
|
|
// Header 2 start
|
|
|
|
StoreHeader store = new();
|
|
|
|
store.WriteDescriptorCount = (UInt32)payloads.Length;
|
|
store.FinalTableIndex = (UInt32)payloads.Length - 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(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[0] > PlatEnd)
|
|
{
|
|
break;
|
|
}
|
|
|
|
store.FlashOnlyTableIndex++;
|
|
}
|
|
|
|
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.Length); // 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();
|
|
|
|
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(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();
|
|
|
|
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.Length, 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.Length; i+= numberOfPayloadsToSendAtOnce)
|
|
{
|
|
if (i + numberOfPayloadsToSendAtOnce - 1 >= payloads.Length)
|
|
{
|
|
numberOfPayloadsToSendAtOnce = payloads.Length - 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[0];
|
|
FlashPart flashPart = FlashParts[(Int32)StreamIndex];
|
|
|
|
if (flashPart.ProgressText != null)
|
|
{
|
|
ProgressText = flashPart.ProgressText;
|
|
}
|
|
|
|
Stream Stream = flashPart.Stream;
|
|
Stream.Seek(payload.StreamLocations[0], 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.Length).ToString()));
|
|
}
|
|
else
|
|
{
|
|
Model.SendFfuPayloadV1(payloadBuffer, Int32.Parse((counter * 100 / (UInt64)payloads.Length).ToString()));
|
|
}
|
|
|
|
UpdateWorkingStatus(ProgressText, null, counter, WPinternalsStatus.Flashing);
|
|
}
|
|
|
|
Model.ResetPhone();
|
|
|
|
await Notifier.WaitForRemoval();
|
|
|
|
ExitSuccess("Flash succeeded!", null);
|
|
}
|
|
catch
|
|
{
|
|
throw new WPinternalsException("Custom flash failed");
|
|
}
|
|
}
|
|
}
|
|
}
|