Exemplo n.º 1
0
        private bool VerifyFileChecksum(byte[] Image, UInt32 Offset)
        {
            // This function only checks fixed checksum-values 0x55 and 0xAA.

            UInt16 FileHeaderSize = 0x18;
            UInt32 FileSize       = ByteOperations.ReadUInt24(Image, Offset + 0x14);

            byte[] Header = new byte[FileHeaderSize - 1];
            System.Buffer.BlockCopy(Image, (int)Offset, Header, 0, FileHeaderSize - 1);
            ByteOperations.WriteUInt16(Header, 0x10, 0); // Clear checksum
            byte CurrentHeaderChecksum    = ByteOperations.ReadUInt8(Image, Offset + 0x10);
            byte CalculatedHeaderChecksum = ByteOperations.CalculateChecksum8(Header, 0, (UInt32)FileHeaderSize - 1);

            if (CurrentHeaderChecksum != CalculatedHeaderChecksum)
            {
                return(false);
            }

            byte FileAttribs         = ByteOperations.ReadUInt8(Image, Offset + 0x13);
            byte CurrentFileChecksum = ByteOperations.ReadUInt8(Image, Offset + 0x11);

            if ((FileAttribs & 0x40) > 0)
            {
                // Calculate file checksum
                byte CalculatedFileChecksum = ByteOperations.CalculateChecksum8(Image, Offset + FileHeaderSize, FileSize - FileHeaderSize);
                if (CurrentFileChecksum != CalculatedFileChecksum)
                {
                    return(false);
                }
            }
            else
            {
                // Fixed file checksum
                if ((CurrentFileChecksum != 0xAA) && (CurrentFileChecksum != 0x55))
                {
                    return(false);
                }
            }

            return(true);
        }
Exemplo n.º 2
0
        private void CalculateFileChecksum(byte[] Image, UInt32 Offset)
        {
            UInt16 FileHeaderSize = 0x18;
            UInt32 FileSize       = ByteOperations.ReadUInt24(Image, Offset + 0x14);

            ByteOperations.WriteUInt16(Image, Offset + 0x10, 0); // Clear checksum
            byte NewChecksum = ByteOperations.CalculateChecksum8(Image, Offset, (UInt32)FileHeaderSize - 1);

            ByteOperations.WriteUInt8(Image, Offset + 0x10, NewChecksum); // File-Header checksum

            byte FileAttribs = ByteOperations.ReadUInt8(Image, Offset + 0x13);

            if ((FileAttribs & 0x40) > 0)
            {
                // Calculate file checksum
                byte CalculatedFileChecksum = ByteOperations.CalculateChecksum8(Image, Offset + FileHeaderSize, FileSize - FileHeaderSize);
                ByteOperations.WriteUInt8(Image, Offset + 0x11, CalculatedFileChecksum);
            }
            else
            {
                // Fixed file checksum
                ByteOperations.WriteUInt8(Image, Offset + 0x11, 0xAA);
            }
        }
Exemplo n.º 3
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));
        }
Exemplo n.º 4
0
        internal byte[] Rebuild()
        {
            // The new binary will include the Qualcomm header, but not the signature and certificates, because they won't match anyway.
            byte[] NewBinary = new byte[Binary.Length];
            Buffer.BlockCopy(Binary, 0, NewBinary, 0, (int)CompressedSubImageOffset);

            ByteOperations.WriteUInt32(NewBinary, 0x10, ByteOperations.ReadUInt32(NewBinary, 0x14)); // Complete image size - does not include signature and certs anymore
            ByteOperations.WriteUInt32(NewBinary, 0x18, 0x00000000);                                 // Address of signature
            ByteOperations.WriteUInt32(NewBinary, 0x1C, 0x00000000);                                 // Signature length
            ByteOperations.WriteUInt32(NewBinary, 0x20, 0x00000000);                                 // Address of certificate
            ByteOperations.WriteUInt32(NewBinary, 0x24, 0x00000000);                                 // Certificate length

            // Compress volume
            byte[] NewCompressedImage = LZMA.Compress(DecompressedImage, 0, (UInt32)DecompressedImage.Length);

            // Replace compressed volume and add correct padding
            // First copy new image
            Buffer.BlockCopy(NewCompressedImage, 0, NewBinary, (int)CompressedSubImageOffset, NewCompressedImage.Length);

            // Determine padding
            UInt32 OldSectionPadding = ByteOperations.Align(0, CompressedSubImageSize, 4) - CompressedSubImageSize;
            UInt32 NewSectionPadding = ByteOperations.Align(0, (UInt32)NewCompressedImage.Length, 4) - (UInt32)NewCompressedImage.Length;
            UInt32 OldFileSize       = ByteOperations.ReadUInt24(Binary, FileHeaderOffset + 0x14);

            // Filesize includes fileheader. But it does not include the padding-bytes. Not even the padding bytes of the last section.
            UInt32 NewFileSize;

            if ((CompressedSubImageOffset + CompressedSubImageSize + OldSectionPadding) >= (FileHeaderOffset + OldFileSize))
            {
                // Compressed image is the last section of this file
                NewFileSize = CompressedSubImageOffset - FileHeaderOffset + (UInt32)NewCompressedImage.Length;
            }
            else
            {
                // Compressed image is NOT the last section of this file
                NewFileSize = OldFileSize - CompressedSubImageSize - OldSectionPadding + (UInt32)NewCompressedImage.Length + NewSectionPadding;
            }

            // Add section padding
            for (int i = 0; i < NewSectionPadding; i++)
            {
                NewBinary[CompressedSubImageOffset + NewCompressedImage.Length + i] = PaddingByteValue;
            }

            // If there are more bytes after the section padding of the compressed image, then copy the trailing sections
            if (((Int32)FileHeaderOffset + OldFileSize - CompressedSubImageOffset - CompressedSubImageSize - OldSectionPadding) > 0)
            {
                Buffer.BlockCopy(Binary, (int)(CompressedSubImageOffset + CompressedSubImageSize + OldSectionPadding), NewBinary,
                                 (int)(CompressedSubImageOffset + NewCompressedImage.Length + NewSectionPadding),
                                 (int)(FileHeaderOffset + OldFileSize - CompressedSubImageOffset - CompressedSubImageSize - OldSectionPadding));
            }

            // Add file padding
            // Filesize does not include last section padding or file padding
            UInt32 OldFilePadding = ByteOperations.Align(0, OldFileSize, 8) - OldFileSize;
            UInt32 NewFilePadding = ByteOperations.Align(0, NewFileSize, 8) - NewFileSize;

            for (int i = 0; i < NewFilePadding; i++)
            {
                NewBinary[FileHeaderOffset + NewFileSize + i] = PaddingByteValue;
            }

            if (NewCompressedImage.Length > CompressedSubImageSize)
            {
                Buffer.BlockCopy(Binary, (int)(FileHeaderOffset + OldFileSize + OldFilePadding), NewBinary, (int)(FileHeaderOffset + NewFileSize + NewFilePadding),
                                 (int)(VolumeHeaderOffset + VolumeSize - FileHeaderOffset - NewFileSize - NewFilePadding));
            }
            else
            {
                Buffer.BlockCopy(Binary, (int)(FileHeaderOffset + OldFileSize + OldFilePadding), NewBinary, (int)(FileHeaderOffset + NewFileSize + NewFilePadding),
                                 (int)(VolumeHeaderOffset + VolumeSize - FileHeaderOffset - OldFileSize - OldFilePadding));
                for (int i = (int)(VolumeHeaderOffset + VolumeSize - OldFileSize - OldFilePadding + NewFileSize + NewFilePadding); i < VolumeHeaderOffset + VolumeSize; i++)
                {
                    NewBinary[i] = PaddingByteValue;
                }
            }
            CompressedSubImageSize = (UInt32)NewCompressedImage.Length;

            // Fix section
            ByteOperations.WriteUInt24(NewBinary, SectionHeaderOffset, CompressedSubImageSize + ByteOperations.ReadUInt16(Binary, SectionHeaderOffset + 0x14));

            // Fix file
            ByteOperations.WriteUInt24(NewBinary, FileHeaderOffset + 0x14, NewFileSize);
            CalculateFileChecksum(NewBinary, FileHeaderOffset);

            // Fix volume (volume size is fixed)
            CalculateVolumeChecksum(NewBinary, VolumeHeaderOffset);

            Binary = NewBinary;
            return(Binary);
        }
Exemplo n.º 5
0
        internal void ReplaceFile(string Name, byte[] Binary)
        {
            EFI File = EFIs.Where(f => (string.Compare(Name, f.Name, true) == 0) || (string.Compare(Name, f.Guid.ToString(), true) == 0)).FirstOrDefault();

            if (File == null)
            {
                throw new ArgumentOutOfRangeException();
            }

            UInt32 OldBinarySize = File.Size;
            UInt32 NewBinarySize = (UInt32)Binary.Length;

            UInt32 OldSectionPadding = ByteOperations.Align(0, OldBinarySize, 4) - OldBinarySize;
            UInt32 NewSectionPadding = ByteOperations.Align(0, NewBinarySize, 4) - NewBinarySize;

            UInt32 OldFileSize = ByteOperations.ReadUInt24(DecompressedImage, File.FileOffset + 0x14);
            UInt32 NewFileSize = OldFileSize - OldBinarySize - OldSectionPadding + NewBinarySize + NewSectionPadding;

            UInt32 OldFilePadding = ByteOperations.Align(0, OldFileSize, 8) - OldFileSize;
            UInt32 NewFilePadding = ByteOperations.Align(0, NewFileSize, 8) - NewFileSize;

            if ((OldBinarySize + OldSectionPadding) != (NewBinarySize + NewSectionPadding))
            {
                byte[] NewImage = new byte[DecompressedImage.Length - OldFileSize - OldFilePadding + NewFileSize + NewFilePadding]; // Also preserve space for File-alignement here

                // Copy Volume-head and File-head
                Buffer.BlockCopy(DecompressedImage, 0, NewImage, 0, (int)File.BinaryOffset);

                // Copy new binary
                Buffer.BlockCopy(Binary, 0, NewImage, (int)File.BinaryOffset, Binary.Length);

                // Insert section-padding
                for (int i = 0; i < NewSectionPadding; i++)
                {
                    NewImage[File.BinaryOffset + NewBinarySize + i] = PaddingByteValue;
                }

                // Copy file-tail
                Buffer.BlockCopy(
                    DecompressedImage,
                    (int)(File.BinaryOffset + OldBinarySize + OldSectionPadding),
                    NewImage,
                    (int)(File.BinaryOffset + NewBinarySize + NewSectionPadding),
                    (int)(File.FileOffset + OldFileSize - File.BinaryOffset - OldBinarySize - OldSectionPadding));

                // Insert file-padding
                for (int i = 0; i < NewFilePadding; i++)
                {
                    NewImage[File.BinaryOffset + NewFileSize + i] = PaddingByteValue;
                }

                // Copy volume-tail
                Buffer.BlockCopy(
                    DecompressedImage,
                    (int)(File.FileOffset + OldFileSize + OldFilePadding),
                    NewImage,
                    (int)(File.FileOffset + NewFileSize + NewFilePadding),
                    (int)(DecompressedImage.Length - File.FileOffset - OldFileSize - OldFilePadding));

                Int32 NewOffset = (int)(NewFileSize + NewFilePadding) - (int)(OldFileSize - OldFilePadding);

                // Fix section-size
                ByteOperations.WriteUInt24(NewImage, File.SectionOffset, (UInt32)(ByteOperations.ReadUInt24(NewImage, File.SectionOffset) + NewOffset));

                // Fix file-size
                ByteOperations.WriteUInt24(NewImage, File.FileOffset + 0x14, (UInt32)(ByteOperations.ReadUInt24(NewImage, File.FileOffset + 0x14) + NewOffset));

                // Fix volume-size - TODO: This is actually a QWORD
                ByteOperations.WriteUInt32(NewImage, DecompressedVolumeHeaderOffset + 0x20, (UInt32)(ByteOperations.ReadUInt32(NewImage, DecompressedVolumeHeaderOffset + 0x20) + NewOffset));

                // Fix section-size
                ByteOperations.WriteUInt24(NewImage, DecompressedVolumeSectionHeaderOffset, (UInt32)(ByteOperations.ReadUInt24(NewImage, DecompressedVolumeSectionHeaderOffset) + NewOffset));

                DecompressedImage = NewImage;

                // Modify all sizes in EFI's
                foreach (EFI CurrentFile in EFIs)
                {
                    if (CurrentFile.FileOffset > File.FileOffset)
                    {
                        CurrentFile.FileOffset    = (UInt32)(CurrentFile.FileOffset + NewOffset);
                        CurrentFile.SectionOffset = (UInt32)(CurrentFile.SectionOffset + NewOffset);
                        CurrentFile.BinaryOffset  = (UInt32)(CurrentFile.BinaryOffset + NewOffset);
                    }
                }
            }
            else
            {
                Buffer.BlockCopy(Binary, 0, DecompressedImage, (int)File.BinaryOffset, Binary.Length);
                for (int i = 0; i < NewSectionPadding; i++)
                {
                    DecompressedImage[File.BinaryOffset + Binary.Length + i] = PaddingByteValue;
                }
            }

            // Calculate File-checksum
            CalculateFileChecksum(DecompressedImage, File.FileOffset);

            // Calculate Volume-checksum
            CalculateVolumeChecksum(DecompressedImage, DecompressedVolumeHeaderOffset);
        }