internal static FlashingPayload[] GetOptimizedPayloads(FlashPart[] flashParts, UInt32 chunkSize)
        {
            List <FlashingPayload> flashingPayloads = new List <FlashingPayload>();

            if (flashParts == null)
            {
                return(flashingPayloads.ToArray());
            }

            Int64 TotalProcess1 = 0;

            for (Int32 j = 0; j < flashParts.Count(); j++)
            {
                FlashPart flashPart = flashParts[j];
                TotalProcess1 += flashPart.Stream.Length / chunkSize;
            }

            Int64    CurrentProcess1 = 0;
            DateTime startTime       = DateTime.Now;

            Logging.Log("Hashing resources...");

            using (SHA256 crypto = SHA256.Create())
            {
                for (UInt32 j = 0; j < flashParts.Count(); j++)
                {
                    FlashPart flashPart = flashParts[(Int32)j];
                    flashPart.Stream.Seek(0, SeekOrigin.Begin);
                    Int64 totalChunkCount = flashPart.Stream.Length / chunkSize;

                    for (UInt32 i = 0; i < totalChunkCount; i++)
                    {
                        byte[] buffer   = new byte[chunkSize];
                        Int64  position = flashPart.Stream.Position;
                        flashPart.Stream.Read(buffer, 0, (Int32)chunkSize);
                        byte[] hash = crypto.ComputeHash(buffer);

                        if (flashingPayloads.Any(x => ByteOperations.Compare(x.ChunkHashes.First(), hash)))
                        {
                            var payloadIndex = flashingPayloads.FindIndex(x => ByteOperations.Compare(x.ChunkHashes.First(), hash));
                            var locationList = flashingPayloads[payloadIndex].TargetLocations.ToList();
                            locationList.Add(((UInt32)flashPart.StartLocation / chunkSize) + i);
                            flashingPayloads[payloadIndex].TargetLocations = locationList.ToArray();
                        }
                        else
                        {
                            flashingPayloads.Add(new FlashingPayload(1, new byte[][] { hash }, new UInt32[] { ((UInt32)flashPart.StartLocation / chunkSize) + i }, new UInt32[] { j }, new Int64[] { position }));
                        }

                        CurrentProcess1++;
                        ShowProgress(CurrentProcess1, TotalProcess1, startTime);
                    }
                }
            }

            return(flashingPayloads.ToArray());
        }
Exemple #2
0
        internal static (FlashPart[], ulong, List <GPT.Partition> partitions) GetImageSlices(Stream stream, UInt32 chunkSize, string[] excluded)
        {
            byte[] GPTBuffer = new byte[chunkSize];
            stream.Read(GPTBuffer, 0, (Int32)chunkSize);

            UInt32 sectorsInAChunk = chunkSize / 512u;

            GPT GPT = new GPT(GPTBuffer);

            List <GPT.Partition> Partitions = GPT.Partitions;
            bool isUnlocked      = GPT.GetPartition("IS_UNLOCKED") != null;
            bool isUnlockedSpecA = GPT.GetPartition("HACK") != null && GPT.GetPartition("BACKUP_BS_NV") != null;

            if (isUnlocked)
            {
                Logging.Log("The phone is an unlocked Spec B phone, UEFI_BS_NV will be kept in the FFU image for the unlock to work");
            }

            if (isUnlockedSpecA)
            {
                Logging.Log("The phone is an UEFI unlocked Spec A phone, UEFI_BS_NV will be kept in the FFU image for the unlock to work");
            }

            List <FlashPart> flashParts = new List <FlashPart>();

            Logging.Log("Partitions with a * appended are ignored partitions");
            Logging.Log("");

            bool previouswasexcluded = true;

            FlashPart currentFlashPart = null;

            ulong PlatEnd = 0;

            foreach (GPT.Partition partition in Partitions.OrderBy(x => x.FirstSector))
            {
                if (partition.Name == "PLAT")
                {
                    PlatEnd = partition.LastSector / sectorsInAChunk;
                }

                bool isExcluded = false;

                if (excluded.Any(x => x == partition.Name))
                {
                    isExcluded = true;
                    if (isUnlocked && partition.Name == "UEFI_BS_NV")
                    {
                        isExcluded = false;
                    }
                    if (isUnlockedSpecA && partition.Name == "UEFI_BS_NV")
                    {
                        isExcluded = false;
                    }
                }

                string outputstring = (isExcluded ? "*" : "") + partition.Name + new String(' ', 50);
                outputstring = outputstring.Insert(25, " - " + partition.FirstSector);
                outputstring = outputstring.Insert(40, " - " + partition.LastSector);
                Logging.Log(outputstring, isExcluded ? Logging.LoggingLevel.Warning : Logging.LoggingLevel.Information);

                if (isExcluded)
                {
                    previouswasexcluded = true;

                    if (currentFlashPart != null)
                    {
                        if ((currentFlashPart.StartLocation / 512) % 256 != 0)
                        {
                            Logging.Log("- The stream doesn't start on a chunk boundary, a chunk is 256 sectors", Logging.LoggingLevel.Error);
                            throw new Exception();
                        }

                        if (((currentFlashPart.Stream.Length / 512) % sectorsInAChunk) != 0)
                        {
                            Logging.Log("- The stream doesn't finish on a chunk boundary, a chunk is 256 sectors", Logging.LoggingLevel.Error);
                            throw new Exception();
                        }

                        flashParts.Add(currentFlashPart);
                        currentFlashPart = null;
                    }

                    continue;
                }

                if (previouswasexcluded)
                {
                    currentFlashPart = new FlashPart(stream, partition.FirstSector * 512);
                }

                previouswasexcluded     = false;
                currentFlashPart.Stream = new PartialStream(stream, (Int64)currentFlashPart.StartLocation, (Int64)(partition.LastSector + 1) * 512);
            }

            if (!previouswasexcluded)
            {
                if (currentFlashPart != null)
                {
                    currentFlashPart.Stream = new PartialStream(stream, (Int64)currentFlashPart.StartLocation, stream.Length);

                    if ((currentFlashPart.StartLocation / 512) % 256 != 0)
                    {
                        Logging.Log("- The stream doesn't start on a chunk boundary, a chunk is 256 sectors", Logging.LoggingLevel.Error);
                        throw new Exception();
                    }

                    if (((currentFlashPart.Stream.Length / 512) % sectorsInAChunk) != 0)
                    {
                        Logging.Log("- The stream doesn't finish on a chunk boundary, a chunk is 256 sectors", Logging.LoggingLevel.Error);
                        throw new Exception();
                    }

                    flashParts.Add(currentFlashPart);
                }
            }

            Logging.Log("");
            Logging.Log("Plat end: " + PlatEnd);

            Logging.Log("");
            Logging.Log("Inserting GPT back into the FFU image");
            flashParts.Insert(0, new FlashPart(new MemoryStream(GPTBuffer), 0));

            return(flashParts.OrderBy(x => x.StartLocation).ToArray(), PlatEnd, Partitions);
        }
Exemple #3
0
        internal static FlashingPayload[] GetOptimizedPayloads(FlashPart[] flashParts, UInt32 chunkSize, ulong PlatEnd)
        {
            List <FlashingPayload> flashingPayloads = new List <FlashingPayload>();

            if (flashParts == null)
            {
                return(flashingPayloads.ToArray());
            }

            Int64 TotalProcess1 = 0;

            for (Int32 j = 0; j < flashParts.Count(); j++)
            {
                FlashPart flashPart = flashParts[j];
                TotalProcess1 += flashPart.Stream.Length / chunkSize;
            }

            Int64    CurrentProcess1 = 0;
            DateTime startTime       = DateTime.Now;

            Logging.Log("Hashing resources...");

            using (SHA256 crypto = SHA256.Create())
            {
                for (UInt32 j = 0; j < flashParts.Count(); j++)
                {
                    FlashPart flashPart = flashParts[(Int32)j];

                    flashPart.Stream.Seek(0, SeekOrigin.Begin);
                    Int64 totalChunkCount = flashPart.Stream.Length / chunkSize;

                    for (UInt32 i = 0; i < totalChunkCount; i++)
                    {
                        byte[] buffer   = new byte[chunkSize];
                        Int64  position = flashPart.Stream.Position;
                        flashPart.Stream.Read(buffer, 0, (Int32)chunkSize);
                        byte[] hash = crypto.ComputeHash(buffer);

                        byte[] emptyness = new byte[] { 0xFA, 0x43, 0x23, 0x9B, 0xCE, 0xE7, 0xB9, 0x7C, 0xA6, 0x2F, 0x00, 0x7C, 0xC6, 0x84, 0x87, 0x56, 0x0A, 0x39, 0xE1, 0x9F, 0x74, 0xF3, 0xDD, 0xE7, 0x48, 0x6D, 0xB3, 0xF9, 0x8D, 0xF8, 0xE4, 0x71 };

                        if ((((UInt32)flashPart.StartLocation / chunkSize) + i) < PlatEnd || !ByteOperations.Compare(emptyness, hash))
                        {
                            flashingPayloads.Add(new FlashingPayload(1, new byte[][] { hash }, new UInt32[] { ((UInt32)flashPart.StartLocation / chunkSize) + i }, new UInt32[] { j }, new Int64[] { position }));
                        }

                        /*if (flashingPayloads.Any(x => ByteOperations.Compare(x.ChunkHashes.First(), hash)))
                         * {
                         *  var payloadIndex = flashingPayloads.FindIndex(x => ByteOperations.Compare(x.ChunkHashes.First(), hash));
                         *  var locationList = flashingPayloads[payloadIndex].TargetLocations.ToList();
                         *  locationList.Add(((UInt32)flashPart.StartLocation / chunkSize) + i);
                         *  flashingPayloads[payloadIndex].TargetLocations = locationList.ToArray();
                         * }
                         * else
                         * {
                         *  flashingPayloads.Add(new FlashingPayload(1, new byte[][] { hash }, new UInt32[] { ((UInt32)flashPart.StartLocation / chunkSize) + i }, new UInt32[] { j }, new Int64[] { position }));
                         * }*/

                        CurrentProcess1++;
                        ShowProgress(CurrentProcess1, TotalProcess1, startTime, (((UInt32)flashPart.StartLocation / chunkSize) + i) < PlatEnd);
                    }
                }
            }

            return(flashingPayloads.ToArray());
        }
Exemple #4
0
        private static void GenerateFFU(string ImageFile, string FFUFile, string PlatformId, UInt32 chunkSize, string AntiTheftVersion, string Osversion, string[] excluded, UInt32 BlankSectorBufferSize)
        {
            Logging.Log("Input image: " + ImageFile);
            Logging.Log("Destination image: " + FFUFile);
            Logging.Log("Platform ID: " + PlatformId);
            Logging.Log("");

            Stream stream;

            if (ImageFile.ToLower().Contains(@"\\.\physicaldrive"))
            {
                stream = new DeviceStream(ImageFile, FileAccess.Read);
            }
            else
            {
                stream = new FileStream(ImageFile, FileMode.Open);
            }

            (FlashPart[] flashParts, ulong PlatEnd, List <GPT.Partition> partitions) = ImageSplitter.GetImageSlices(stream, chunkSize, excluded);

            IOrderedEnumerable <FlashingPayload> payloads = FlashingPayloadGenerator.GetOptimizedPayloads(flashParts, chunkSize, BlankSectorBufferSize).OrderBy(x => x.TargetLocations.First()); // , PlatEnd

            Logging.Log("");
            Logging.Log("Building image headers...");

            string     header1       = Path.GetTempFileName();
            FileStream Headerstream1 = new FileStream(header1, FileMode.OpenOrCreate);

            // ==============================
            // Header 1 start

            ImageHeader image   = new ImageHeader();
            FullFlash   ffimage = new FullFlash();
            Store       simage  = new Store();

            // Todo make this read the image itself
            ffimage.OSVersion         = Osversion;
            ffimage.DevicePlatformId0 = PlatformId;
            ffimage.AntiTheftVersion  = AntiTheftVersion;

            simage.SectorSize     = 512;
            simage.MinSectorCount = (UInt32)(stream.Length / 512);

            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
            // ==============================

            string     header2       = Path.GetTempFileName();
            FileStream Headerstream2 = new FileStream(header2, FileMode.OpenOrCreate);

            // ==============================
            // Header 2 start

            StoreHeader store = new StoreHeader();

            store.WriteDescriptorCount = (UInt32)payloads.Count();
            store.FinalTableIndex      = (UInt32)payloads.Count() - store.FinalTableCount;
            store.PlatformId           = PlatformId;

            foreach (FlashingPayload payload in payloads)
            {
                store.WriteDescriptorLength += payload.GetStoreHeaderSize();
            }

            foreach (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 (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 (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 (int 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 (int 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 (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);

            FileStream retstream = new FileStream(FFUFile, FileMode.CreateNew);

            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();
            File.Delete(header1);

            retstream.Write(buff, 0, buff.Length);

            buff = new byte[Headerstream2.Length];
            Headerstream2.Read(buff, 0, (Int32)Headerstream2.Length);

            Headerstream2.Close();
            File.Delete(header2);

            retstream.Write(buff, 0, buff.Length);

            Logging.Log("Writing payloads...");
            UInt64 counter = 0;

            DateTime startTime = DateTime.Now;

            foreach (FlashingPayload payload in payloads)
            {
                UInt32    StreamIndex = payload.StreamIndexes.First();
                FlashPart flashPart   = flashParts[StreamIndex];
                Stream    Stream      = flashPart.Stream;
                Stream.Seek(payload.StreamLocations.First(), SeekOrigin.Begin);
                byte[] buffer = new byte[chunkSize];
                Stream.Read(buffer, 0, (Int32)chunkSize);
                retstream.Write(buffer, 0, (Int32)chunkSize);
                counter++;
                ShowProgress((UInt64)payloads.Count() * chunkSize, startTime, counter * chunkSize, counter * chunkSize, payload.TargetLocations.First() * chunkSize < PlatEnd);
            }

            retstream.Close();
            Logging.Log("");
        }
Exemple #5
0
        internal static FlashingPayload[] GetOptimizedPayloads(FlashPart[] flashParts, UInt32 chunkSize, UInt32 BlankSectorBufferSize)
        {
            List <FlashingPayload> flashingPayloads = new List <FlashingPayload>();

            if (flashParts == null)
            {
                return(flashingPayloads.ToArray());
            }

            Int64 TotalProcess1 = 0;

            for (Int32 j = 0; j < flashParts.Count(); j++)
            {
                FlashPart flashPart = flashParts[j];
                TotalProcess1 += flashPart.Stream.Length / chunkSize;
            }

            Int64    CurrentProcess1 = 0;
            DateTime startTime       = DateTime.Now;

            Logging.Log("Hashing resources...");

            ulong maxblank   = BlankSectorBufferSize;
            bool  blankphase = false;
            ulong blankcount = 0;
            List <FlashingPayload> blankbuffer = new List <FlashingPayload>();

            using (SHA256 crypto = SHA256.Create())
            {
                for (UInt32 j = 0; j < flashParts.Count(); j++)
                {
                    FlashPart flashPart = flashParts[(Int32)j];

                    flashPart.Stream.Seek(0, SeekOrigin.Begin);
                    Int64 totalChunkCount = flashPart.Stream.Length / chunkSize;

                    for (UInt32 i = 0; i < totalChunkCount; i++)
                    {
                        byte[] buffer   = new byte[chunkSize];
                        Int64  position = flashPart.Stream.Position;
                        flashPart.Stream.Read(buffer, 0, (Int32)chunkSize);
                        byte[] hash = crypto.ComputeHash(buffer);

                        byte[] emptyness = new byte[] { 0xFA, 0x43, 0x23, 0x9B, 0xCE, 0xE7, 0xB9, 0x7C, 0xA6, 0x2F, 0x00, 0x7C, 0xC6, 0x84, 0x87, 0x56, 0x0A, 0x39, 0xE1, 0x9F, 0x74, 0xF3, 0xDD, 0xE7, 0x48, 0x6D, 0xB3, 0xF9, 0x8D, 0xF8, 0xE4, 0x71 };

                        if (!ByteOperations.Compare(emptyness, hash))
                        {
                            flashingPayloads.Add(new FlashingPayload(1, new byte[][] { hash }, new UInt32[] { ((UInt32)flashPart.StartLocation / chunkSize) + i }, new UInt32[] { j }, new Int64[] { position }));

                            if (blankphase && blankcount < maxblank)
                            {
                                foreach (var blankpay in blankbuffer)
                                {
                                    flashingPayloads.Add(blankpay);
                                }
                            }

                            blankphase = false;
                            blankcount = 0;
                            blankbuffer.Clear();
                        }
                        else if (blankcount < maxblank)
                        {
                            blankphase = true;
                            blankcount++;
                            blankbuffer.Add(new FlashingPayload(1, new byte[][] { hash }, new UInt32[] { ((UInt32)flashPart.StartLocation / chunkSize) + i }, new UInt32[] { j }, new Int64[] { position }));
                        }
                        else if (blankcount >= maxblank && blankbuffer.Count > 0)
                        {
                            foreach (var blankpay in blankbuffer)
                            {
                                flashingPayloads.Add(blankpay);
                            }
                            blankbuffer.Clear();
                        }

                        CurrentProcess1++;
                        ShowProgress(CurrentProcess1, TotalProcess1, startTime, blankphase);
                    }
                }
            }

            return(flashingPayloads.ToArray());
        }
Exemple #6
0
        private static void GenerateFFU(string ImageFile, string FFUFile, string PlatformId, UInt32 chunkSize, string AntiTheftVersion, string Osversion, string[] excluded, UInt32 BlankSectorBufferSize)
        {
            Logging.Log("Input image: " + ImageFile);
            Logging.Log("Destination image: " + FFUFile);
            Logging.Log("Platform ID: " + PlatformId);
            Logging.Log("");

            Stream      stream;
            VirtualDisk destDisk = null;

            if (ImageFile.ToLower().Contains(@"\\.\physicaldrive"))
            {
                stream = new DeviceStream(ImageFile, FileAccess.Read);
            }
            else if (File.Exists(ImageFile) && Path.GetExtension(ImageFile).ToLowerInvariant() == ".vhd")
            {
                DiscUtils.Setup.SetupHelper.RegisterAssembly(typeof(DiscUtils.Vhd.Disk).Assembly);
                DiscUtils.Setup.SetupHelper.RegisterAssembly(typeof(DiscUtils.Vhdx.Disk).Assembly);
                destDisk = VirtualDisk.OpenDisk(ImageFile, FileAccess.Read);
                stream   = destDisk.Content;
            }
            else if (File.Exists(ImageFile) && Path.GetExtension(ImageFile).ToLowerInvariant() == ".vhdx")
            {
                DiscUtils.Setup.SetupHelper.RegisterAssembly(typeof(DiscUtils.Vhd.Disk).Assembly);
                DiscUtils.Setup.SetupHelper.RegisterAssembly(typeof(DiscUtils.Vhdx.Disk).Assembly);
                destDisk = VirtualDisk.OpenDisk(ImageFile, FileAccess.Read);
                stream   = destDisk.Content;
            }
            else if (File.Exists(ImageFile))
            {
                stream = new FileStream(ImageFile, FileMode.Open);
            }
            else
            {
                Logging.Log("Unknown input specified");
                return;
            }

            (FlashPart[] flashParts, ulong PlatEnd, List <GPT.Partition> partitions) = ImageSplitter.GetImageSlices(stream, chunkSize, excluded);

            IOrderedEnumerable <FlashingPayload> payloads = FlashingPayloadGenerator.GetOptimizedPayloads(flashParts, chunkSize, BlankSectorBufferSize).OrderBy(x => x.TargetLocations.First());

            Logging.Log("");
            Logging.Log("Building image headers...");

            string     header1       = Path.GetTempFileName();
            FileStream Headerstream1 = new FileStream(header1, FileMode.OpenOrCreate);

            // ==============================
            // Header 1 start

            ImageHeader image   = new ImageHeader();
            FullFlash   ffimage = new FullFlash();
            Store       simage  = new Store();

            // Todo make this read the image itself
            ffimage.OSVersion         = Osversion;
            ffimage.DevicePlatformId0 = PlatformId;
            ffimage.AntiTheftVersion  = AntiTheftVersion;

            simage.SectorSize     = 512;
            simage.MinSectorCount = (UInt32)(stream.Length / 512);

            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
            // ==============================

            string     header2       = Path.GetTempFileName();
            FileStream Headerstream2 = new FileStream(header2, FileMode.OpenOrCreate);

            // ==============================
            // Header 2 start

            StoreHeader store = new StoreHeader();

            store.WriteDescriptorCount = (UInt32)payloads.Count();
            store.FinalTableIndex      = (UInt32)payloads.Count() - store.FinalTableCount;
            store.PlatformId           = PlatformId;

            byte[] WriteDescriptorBuffer = GetResultingBuffer(payloads);
            store.WriteDescriptorLength = (UInt32)WriteDescriptorBuffer.Length;

            foreach (FlashingPayload payload in payloads)
            {
                if (payload.TargetLocations.First() > PlatEnd)
                {
                    break;
                }
                store.FlashOnlyTableIndex += 1;
            }

            Headerstream2.Write(store.GetResultingBuffer(), 0, 0xF8);
            Headerstream2.Write(WriteDescriptorBuffer, 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 (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 (int 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 (int 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 (FlashingPayload payload in payloads)
            {
                foreach (var chunkHash in payload.ChunkHashes)
                {
                    bw.Write(chunkHash, 0, chunkHash.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);

            FileStream retstream = new FileStream(FFUFile, FileMode.CreateNew);

            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();
            File.Delete(header1);

            retstream.Write(buff, 0, buff.Length);

            buff = new byte[Headerstream2.Length];
            Headerstream2.Read(buff, 0, (Int32)Headerstream2.Length);

            Headerstream2.Close();
            File.Delete(header2);

            retstream.Write(buff, 0, buff.Length);

            Logging.Log("Writing payloads...");
            UInt64 counter = 0;

            DateTime startTime = DateTime.Now;

            foreach (FlashingPayload payload in payloads)
            {
                for (int i = 0; i < payload.StreamIndexes.Length; i++)
                {
                    UInt32    StreamIndex = payload.StreamIndexes[i];
                    FlashPart flashPart   = flashParts[StreamIndex];
                    Stream    Stream      = flashPart.Stream;
                    Stream.Seek(payload.StreamLocations[i], SeekOrigin.Begin);

                    byte[] buffer = new byte[chunkSize];
                    Stream.Read(buffer, 0, (Int32)chunkSize);
                    retstream.Write(buffer, 0, (Int32)chunkSize);
                    counter++;

                    ShowProgress((UInt64)payloads.Count() * chunkSize, startTime, counter * chunkSize, counter * chunkSize, payload.TargetLocations[i] * chunkSize < PlatEnd);
                }
            }

            retstream.Close();
            if (destDisk != null)
            {
                destDisk.Dispose();
            }
            Logging.Log("");
        }