Exemplo n.º 1
0
        internal FFU(string Path)
        {
            this.Path = Path;

            try
            {
                OpenFile();

                // Read Security Header
                byte[] ShortSecurityHeader = new byte[0x20];
                FFUFile.Read(ShortSecurityHeader, 0, 0x20);
                if (ByteOperations.ReadAsciiString(ShortSecurityHeader, 0x04, 0x0C) != "SignedImage ")
                {
                    throw new BadImageFormatException();
                }

                ChunkSize = ByteOperations.ReadInt32(ShortSecurityHeader, 0x10) * 1024;
                UInt32 SecurityHeaderSize = ByteOperations.ReadUInt32(ShortSecurityHeader, 0x00);
                UInt32 CatalogSize        = ByteOperations.ReadUInt32(ShortSecurityHeader, 0x18);
                UInt32 HashTableSize      = ByteOperations.ReadUInt32(ShortSecurityHeader, 0x1C);
                SecurityHeader = new byte[RoundUpToChunks(SecurityHeaderSize + CatalogSize + HashTableSize)];
                FFUFile.Seek(0, SeekOrigin.Begin);
                FFUFile.Read(SecurityHeader, 0, SecurityHeader.Length);

                // Read Image Header
                byte[] ShortImageHeader = new byte[0x1C];
                FFUFile.Read(ShortImageHeader, 0, 0x1C);
                if (ByteOperations.ReadAsciiString(ShortImageHeader, 0x04, 0x0C) != "ImageFlash  ")
                {
                    throw new BadImageFormatException();
                }

                UInt32 ImageHeaderSize = ByteOperations.ReadUInt32(ShortImageHeader, 0x00);
                UInt32 ManifestSize    = ByteOperations.ReadUInt32(ShortImageHeader, 0x10);
                ImageHeader = new byte[RoundUpToChunks(ImageHeaderSize + ManifestSize)];
                FFUFile.Seek(SecurityHeader.Length, SeekOrigin.Begin);
                FFUFile.Read(ImageHeader, 0, ImageHeader.Length);

                // Read Store Header
                byte[] ShortStoreHeader = new byte[248];
                FFUFile.Read(ShortStoreHeader, 0, 248);
                PlatformID = ByteOperations.ReadAsciiString(ShortStoreHeader, 0x0C, 192).TrimEnd(new char[] { (char)0, ' ' });
                int    WriteDescriptorCount     = ByteOperations.ReadInt32(ShortStoreHeader, 208);
                UInt32 WriteDescriptorLength    = ByteOperations.ReadUInt32(ShortStoreHeader, 212);
                UInt32 ValidateDescriptorLength = ByteOperations.ReadUInt32(ShortStoreHeader, 220);
                StoreHeader = new byte[RoundUpToChunks(248 + WriteDescriptorLength + ValidateDescriptorLength)];
                FFUFile.Seek(SecurityHeader.Length + ImageHeader.Length, SeekOrigin.Begin);
                FFUFile.Read(StoreHeader, 0, StoreHeader.Length);

                // Parse Chunk Indexes
                int    HighestChunkIndex = 0;
                UInt32 LocationCount;
                int    ChunkIndex;
                int    ChunkCount;
                int    DiskAccessMethod;
                UInt32 WriteDescriptorEntryOffset = 248 + ValidateDescriptorLength;
                int    FFUChunkIndex = 0;
                for (int i = 0; i < WriteDescriptorCount; i++)
                {
                    LocationCount = ByteOperations.ReadUInt32(StoreHeader, WriteDescriptorEntryOffset + 0x00);
                    ChunkCount    = ByteOperations.ReadInt32(StoreHeader, WriteDescriptorEntryOffset + 0x04);

                    for (int j = 0; j < LocationCount; j++)
                    {
                        DiskAccessMethod = ByteOperations.ReadInt32(StoreHeader, (UInt32)(WriteDescriptorEntryOffset + 0x08 + (j * 0x08)));
                        ChunkIndex       = ByteOperations.ReadInt32(StoreHeader, (UInt32)(WriteDescriptorEntryOffset + 0x0C + (j * 0x08)));

                        if (DiskAccessMethod == 0 && (ChunkIndex + ChunkCount - 1) > HighestChunkIndex) // 0 = From begin, 2 = From end. We ignore chunks at end of disk. These contain secondairy GPT.
                        {
                            HighestChunkIndex = ChunkIndex + ChunkCount - 1;
                        }
                    }
                    WriteDescriptorEntryOffset += 8 + (LocationCount * 0x08);
                    FFUChunkIndex += ChunkCount;
                }
                ChunkIndexes = new int?[HighestChunkIndex + 1];
                WriteDescriptorEntryOffset = 248 + ValidateDescriptorLength;
                FFUChunkIndex = 0;
                for (int i = 0; i < WriteDescriptorCount; i++)
                {
                    LocationCount = ByteOperations.ReadUInt32(StoreHeader, WriteDescriptorEntryOffset + 0x00);
                    ChunkCount    = ByteOperations.ReadInt32(StoreHeader, WriteDescriptorEntryOffset + 0x04);

                    for (int j = 0; j < LocationCount; j++)
                    {
                        DiskAccessMethod = ByteOperations.ReadInt32(StoreHeader, (UInt32)(WriteDescriptorEntryOffset + 0x08 + (j * 0x08)));
                        ChunkIndex       = ByteOperations.ReadInt32(StoreHeader, (UInt32)(WriteDescriptorEntryOffset + 0x0C + (j * 0x08)));

                        if (DiskAccessMethod == 0) // 0 = From begin, 2 = From end. We ignore chunks at end of disk. These contain secondairy GPT.
                        {
                            for (int k = 0; k < ChunkCount; k++)
                            {
                                ChunkIndexes[ChunkIndex + k] = FFUChunkIndex + k;
                            }
                        }
                    }
                    WriteDescriptorEntryOffset += 8 + (LocationCount * 0x08);
                    FFUChunkIndex += ChunkCount;
                }

                byte[] GPTBuffer = GetSectors(0x01, 0x21);
                GPT = new GPT(GPTBuffer);

                HeaderSize = (UInt64)(SecurityHeader.Length + ImageHeader.Length + StoreHeader.Length);

                TotalChunkCount = (UInt64)FFUChunkIndex;
                PayloadSize     = TotalChunkCount * (UInt64)ChunkSize;
                TotalSize       = HeaderSize + PayloadSize;

                if (TotalSize != (UInt64)FFUFile.Length)
                {
                    throw new WPinternalsException("Bad FFU file", "Bad FFU file: " + Path + "." + Environment.NewLine + "Expected size: " + TotalSize.ToString() + ". Actual size: " + FFUFile.Length + ".");
                }
            }
            catch (WPinternalsException)
            {
                throw;
            }
            catch (Exception Ex)
            {
                throw new WPinternalsException("Bad FFU file", "Bad FFU file: " + Path + "." + Environment.NewLine + Ex.Message, Ex);
            }
            finally
            {
                CloseFile();
            }
        }
Exemplo n.º 2
0
        internal UEFI(byte[] UefiBinary)
        {
            Binary = UefiBinary;

            string VolumeHeaderMagic;
            UInt32?Offset = ByteOperations.FindAscii(Binary, "_FVH");

            if (Offset == null)
            {
                throw new BadImageFormatException();
            }
            else
            {
                VolumeHeaderOffset = (UInt32)Offset - 0x28;
            }

            if (!VerifyVolumeChecksum(Binary, VolumeHeaderOffset))
            {
                throw new BadImageFormatException();
            }

            VolumeSize       = ByteOperations.ReadUInt32(Binary, VolumeHeaderOffset + 0x20);                                              // TODO: This is actually a QWORD
            VolumeHeaderSize = ByteOperations.ReadUInt16(Binary, VolumeHeaderOffset + 0x30);
            PaddingByteValue = (ByteOperations.ReadUInt32(Binary, VolumeHeaderOffset + 0x2C) & 0x00000800) > 0 ? (byte)0xFF : (byte)0x00; // EFI_FVB_ERASE_POLARITY = 0x00000800

            // In the volume look for a file of type EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE (0x0B)

            FileHeaderOffset = VolumeHeaderOffset + VolumeHeaderSize;
            bool   VolumeFound = false;
            int    FileType;
            UInt32 FileSize;

            do
            {
                if (!VerifyFileChecksum(Binary, FileHeaderOffset))
                {
                    throw new BadImageFormatException();
                }

                FileType = ByteOperations.ReadUInt8(Binary, FileHeaderOffset + 0x12);
                FileSize = ByteOperations.ReadUInt24(Binary, FileHeaderOffset + 0x14);

                if (FileType == 0x0B) // EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE
                {
                    VolumeFound = true;
                }
                else
                {
                    FileHeaderOffset += FileSize;

                    // FileHeaderOffset in Volume-body must be Align 8
                    // In the file-header-attributes the file-alignment relative to the start of the volume is always set to 1,
                    // so that alignment can be ignored.
                    FileHeaderOffset = ByteOperations.Align(VolumeHeaderOffset + VolumeHeaderSize, FileHeaderOffset, 8);
                }
            }while (!VolumeFound && (FileHeaderOffset < (VolumeHeaderOffset + VolumeSize)));

            if (!VolumeFound)
            {
                throw new BadImageFormatException();
            }

            // Look in file for section of type EFI_SECTION_GUID_DEFINED (0x02)

            SectionHeaderOffset = FileHeaderOffset + 0x18;
            int    SectionType;
            UInt32 SectionSize;
            UInt16 SectionHeaderSize = 0;

            bool DecompressedVolumeFound = false;

            do
            {
                SectionType = ByteOperations.ReadUInt8(Binary, SectionHeaderOffset + 0x03);
                SectionSize = ByteOperations.ReadUInt24(Binary, SectionHeaderOffset + 0x00);

                if (SectionType == 0x02) // EFI_SECTION_GUID_DEFINED
                {
                    SectionHeaderSize       = ByteOperations.ReadUInt16(Binary, SectionHeaderOffset + 0x14);
                    DecompressedVolumeFound = true;
                }
                else
                {
                    SectionHeaderOffset += SectionSize;

                    // SectionHeaderOffset in File-body must be Align 4
                    SectionHeaderOffset = ByteOperations.Align(FileHeaderOffset + 0x18, SectionHeaderOffset, 4);
                }
            }while (!DecompressedVolumeFound && (SectionHeaderOffset < (FileHeaderOffset + FileSize)));

            if (!DecompressedVolumeFound)
            {
                throw new BadImageFormatException();
            }

            // Decompress subvolume
            CompressedSubImageOffset = SectionHeaderOffset + SectionHeaderSize;
            CompressedSubImageSize   = SectionSize - SectionHeaderSize;

            // DECOMPRESS HERE
            DecompressedImage = LZMA.Decompress(Binary, CompressedSubImageOffset, CompressedSubImageSize);

            // Extracted volume contains Sections at its root level

            DecompressedVolumeSectionHeaderOffset = 0;
            DecompressedVolumeFound = false;
            do
            {
                SectionType       = ByteOperations.ReadUInt8(DecompressedImage, DecompressedVolumeSectionHeaderOffset + 0x03);
                SectionSize       = ByteOperations.ReadUInt24(DecompressedImage, DecompressedVolumeSectionHeaderOffset + 0x00);
                SectionHeaderSize = ByteOperations.ReadUInt16(DecompressedImage, DecompressedVolumeSectionHeaderOffset + 0x14);

                if (SectionType == 0x17) // EFI_SECTION_FIRMWARE_VOLUME_IMAGE
                {
                    DecompressedVolumeFound = true;
                }
                else
                {
                    DecompressedVolumeSectionHeaderOffset += SectionSize;

                    // SectionHeaderOffset in File-body must be Align 4
                    DecompressedVolumeSectionHeaderOffset = ByteOperations.Align(FileHeaderOffset + 0x18, DecompressedVolumeSectionHeaderOffset, 4);
                }
            }while (!DecompressedVolumeFound && (DecompressedVolumeSectionHeaderOffset < DecompressedImage.Length));

            if (!DecompressedVolumeFound)
            {
                throw new BadImageFormatException();
            }

            DecompressedVolumeHeaderOffset = DecompressedVolumeSectionHeaderOffset + 4;

            // PARSE COMPRESSED VOLUME
            VolumeHeaderMagic = ByteOperations.ReadAsciiString(DecompressedImage, DecompressedVolumeHeaderOffset + 0x28, 0x04);
            if (VolumeHeaderMagic != "_FVH")
            {
                throw new BadImageFormatException();
            }

            if (!VerifyVolumeChecksum(DecompressedImage, DecompressedVolumeHeaderOffset))
            {
                throw new BadImageFormatException();
            }

            Int32  DecompressedVolumeSize       = ByteOperations.ReadInt32(DecompressedImage, DecompressedVolumeHeaderOffset + 0x20); // TODO: This is actually a QWORD
            UInt16 DecompressedVolumeHeaderSize = ByteOperations.ReadUInt16(DecompressedImage, DecompressedVolumeHeaderOffset + 0x30);

            // The files in this decompressed volume are the real EFI's.
            UInt32 DecompressedFileHeaderOffset = DecompressedVolumeHeaderOffset + DecompressedVolumeHeaderSize;
            EFI    CurrentEFI;

            do
            {
                if ((DecompressedFileHeaderOffset + 0x18) >= (DecompressedVolumeHeaderOffset + DecompressedVolumeSize))
                {
                    break;
                }

                bool ContentFound = false;
                for (int i = 0; i < 0x18; i++)
                {
                    if (DecompressedImage[DecompressedFileHeaderOffset + i] != PaddingByteValue)
                    {
                        ContentFound = true;
                        break;
                    }
                }
                if (!ContentFound)
                {
                    break;
                }

                FileSize = ByteOperations.ReadUInt24(DecompressedImage, DecompressedFileHeaderOffset + 0x14);

                if ((DecompressedFileHeaderOffset + FileSize) >= (DecompressedVolumeHeaderOffset + DecompressedVolumeSize))
                {
                    break;
                }

                if (!VerifyFileChecksum(DecompressedImage, DecompressedFileHeaderOffset))
                {
                    throw new BadImageFormatException();
                }

                CurrentEFI = new EFI();

                CurrentEFI.Type = ByteOperations.ReadUInt8(DecompressedImage, DecompressedFileHeaderOffset + 0x12);
                byte[] FileGuidBytes = new byte[0x10];
                System.Buffer.BlockCopy(DecompressedImage, (int)DecompressedFileHeaderOffset + 0x00, FileGuidBytes, 0, 0x10);
                CurrentEFI.Guid = new Guid(FileGuidBytes);

                // Parse sections of the EFI
                CurrentEFI.FileOffset = DecompressedFileHeaderOffset;
                UInt32 DecompressedSectionHeaderOffset = DecompressedFileHeaderOffset + 0x18;
                do
                {
                    SectionType = ByteOperations.ReadUInt8(DecompressedImage, DecompressedSectionHeaderOffset + 0x03);
                    SectionSize = ByteOperations.ReadUInt24(DecompressedImage, DecompressedSectionHeaderOffset + 0x00);

                    // SectionTypes that are relevant here:
                    // 0x10 = PE File
                    // 0x19 = RAW
                    // 0x15 = Description
                    // Not all section headers in the UEFI specs are 4 bytes long,
                    // but the sections that are used in Windows Phone EFI's all have a header of 4 bytes.
                    if (SectionType == 0x15)
                    {
                        CurrentEFI.Name = ByteOperations.ReadUnicodeString(DecompressedImage, DecompressedSectionHeaderOffset + 0x04, SectionSize - 0x04).TrimEnd(new char[] { (char)0, ' ' });
                    }
                    else if ((SectionType == 0x10) || (SectionType == 0x19))
                    {
                        CurrentEFI.SectionOffset = DecompressedSectionHeaderOffset;
                        CurrentEFI.BinaryOffset  = DecompressedSectionHeaderOffset + 0x04;
                        CurrentEFI.Size          = SectionSize - 0x04;
                    }

                    DecompressedSectionHeaderOffset += SectionSize;

                    // SectionHeaderOffset in File-body must be Align 4
                    DecompressedSectionHeaderOffset = ByteOperations.Align(DecompressedFileHeaderOffset + 0x18, DecompressedSectionHeaderOffset, 4);
                }while (DecompressedSectionHeaderOffset < (DecompressedFileHeaderOffset + FileSize));

                DecompressedFileHeaderOffset += FileSize;

                // FileHeaderOffset in Volume-body must be Align 8
                // In the file-header-attributes the file-alignment relative to the start of the volume is always set to 1,
                // so that alignment can be ignored.
                DecompressedFileHeaderOffset = ByteOperations.Align(DecompressedVolumeHeaderOffset + DecompressedVolumeHeaderSize, DecompressedFileHeaderOffset, 8);

                EFIs.Add(CurrentEFI);
            }while (DecompressedFileHeaderOffset < (DecompressedVolumeHeaderOffset + DecompressedVolumeSize));
        }