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); }
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); } }
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)); }