Exemplo n.º 1
0
        private void CalculateVolumeChecksum(byte[] Image, UInt32 Offset)
        {
            UInt16 VolumeHeaderSize = ByteOperations.ReadUInt16(Image, Offset + 0x30);

            ByteOperations.WriteUInt16(Image, Offset + 0x32, 0); // Clear checksum
            UInt16 NewChecksum = ByteOperations.CalculateChecksum16(Image, Offset, VolumeHeaderSize);

            ByteOperations.WriteUInt16(Image, Offset + 0x32, NewChecksum);
        }
Exemplo n.º 2
0
        private bool VerifyVolumeChecksum(byte[] Image, UInt32 Offset)
        {
            UInt16 VolumeHeaderSize = ByteOperations.ReadUInt16(Image, Offset + 0x30);

            byte[] Header = new byte[VolumeHeaderSize];
            System.Buffer.BlockCopy(Image, (int)Offset, Header, 0, VolumeHeaderSize);
            ByteOperations.WriteUInt16(Header, 0x32, 0); // Clear checksum
            UInt16 CurrentChecksum = ByteOperations.ReadUInt16(Image, Offset + 0x32);
            UInt16 NewChecksum     = ByteOperations.CalculateChecksum16(Header, 0, VolumeHeaderSize);

            return(CurrentChecksum == NewChecksum);
        }
Exemplo n.º 3
0
        internal QualcommPartition(byte[] Binary, uint Offset = 0)
        {
#if DEBUG
            System.Diagnostics.Debug.Print("Loader: " + Converter.ConvertHexToString(new SHA256Managed().ComputeHash(Binary, 0, Binary.Length), ""));
#endif

            this.Binary = Binary;

            byte[] LongHeaderPattern = new byte[] { 0xD1, 0xDC, 0x4B, 0x84, 0x34, 0x10, 0xD7, 0x73, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
            byte[] LongHeaderMask    = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

            if (ByteOperations.FindPattern(Binary, Offset, 4, new byte[] { 0x7F, 0x45, 0x4C, 0x46 }, new byte[] { 0x00, 0x00, 0x00, 0x00 }, null) == 0)
            {
                // This is an ELF image
                // First program header is a reference to the elf-header
                // Second program header is a reference to the signed hash-table
                HeaderType = QualcommPartitionHeaderType.Short;
                UInt32 ProgramHeaderOffset;
                UInt16 ProgramHeaderEntrySize;
                UInt32 HashTableProgramHeaderOffset;
                if (Binary[Offset + 0x04] == 1)
                {
                    // 32-bit elf image
                    ProgramHeaderOffset          = Offset + ByteOperations.ReadUInt32(Binary, Offset + 0x1c);
                    ProgramHeaderEntrySize       = ByteOperations.ReadUInt16(Binary, Offset + 0x2a);
                    HashTableProgramHeaderOffset = ProgramHeaderOffset + ProgramHeaderEntrySize;
                    ImageOffset  = Offset + ByteOperations.ReadUInt32(Binary, HashTableProgramHeaderOffset + 0x04);
                    HeaderOffset = ImageOffset + 8;
                }
                else if (Binary[Offset + 0x04] == 2)
                {
                    // 64-bit elf image
                    ProgramHeaderOffset          = Offset + ByteOperations.ReadUInt32(Binary, Offset + 0x20);
                    ProgramHeaderEntrySize       = ByteOperations.ReadUInt16(Binary, Offset + 0x36);
                    HashTableProgramHeaderOffset = ProgramHeaderOffset + ProgramHeaderEntrySize;
                    ImageOffset  = Offset + (UInt32)ByteOperations.ReadUInt64(Binary, HashTableProgramHeaderOffset + 0x08);
                    HeaderOffset = ImageOffset + 8;
                }
                else
                {
                    throw new WPinternalsException("Invalid programmer");
                }
            }
            else if (ByteOperations.FindPattern(Binary, Offset, (uint)LongHeaderPattern.Length, LongHeaderPattern, LongHeaderMask, null) == null)
            {
                HeaderType   = QualcommPartitionHeaderType.Short;
                ImageOffset  = Offset;
                HeaderOffset = ImageOffset + 8;
            }
            else
            {
                HeaderType   = QualcommPartitionHeaderType.Long;
                ImageOffset  = Offset;
                HeaderOffset = ImageOffset + (uint)LongHeaderPattern.Length;
            }

            if (ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X00) != 0)
            {
                ImageOffset = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X00);
            }
            else if (HeaderType == QualcommPartitionHeaderType.Short)
            {
                ImageOffset += 0x28;
            }
            else
            {
                ImageOffset += 0x50;
            }

            ImageAddress        = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X04);
            ImageSize           = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X08);
            CodeSize            = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X0C);
            SignatureAddress    = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X10);
            SignatureSize       = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X14);
            SignatureOffset     = SignatureAddress - ImageAddress + ImageOffset;
            CertificatesAddress = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X18);
            CertificatesSize    = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X1C);
            CertificatesOffset  = CertificatesAddress - ImageAddress + ImageOffset;

            uint CurrentCertificateOffset = CertificatesOffset;
            uint CertificateSize          = 0;
            while (CurrentCertificateOffset < (CertificatesOffset + CertificatesSize))
            {
                if ((Binary[CurrentCertificateOffset] == 0x30) && (Binary[CurrentCertificateOffset + 1] == 0x82))
                {
                    CertificateSize = (uint)(Binary[CurrentCertificateOffset + 2] * 0x100) + Binary[CurrentCertificateOffset + 3] + 4; // Big endian!

                    if ((CurrentCertificateOffset + CertificateSize) == (CertificatesOffset + CertificatesSize))
                    {
                        // This is the last certificate. So this is the root key.
                        RootKeyHash = new SHA256Managed().ComputeHash(Binary, (int)CurrentCertificateOffset, (int)CertificateSize);

#if DEBUG
                        System.Diagnostics.Debug.Print("RKH: " + Converter.ConvertHexToString(RootKeyHash, ""));
#endif
                    }
#if DEBUG
                    else
                    {
                        System.Diagnostics.Debug.Print("Cert: " + Converter.ConvertHexToString(new SHA256Managed().ComputeHash(Binary, (int)CurrentCertificateOffset, (int)CertificateSize), ""));
                    }
#endif
                    CurrentCertificateOffset += CertificateSize;
                }
                else
                {
                    if ((RootKeyHash == null) && (CurrentCertificateOffset > CertificatesOffset))
                    {
                        CurrentCertificateOffset -= CertificateSize;

                        // This is the last certificate. So this is the root key.
                        RootKeyHash = new SHA256Managed().ComputeHash(Binary, (int)CurrentCertificateOffset, (int)CertificateSize);

#if DEBUG
                        System.Diagnostics.Debug.Print("RKH: " + Converter.ConvertHexToString(RootKeyHash, ""));
#endif
                    }
                    break;
                }
            }
        }
Exemplo n.º 4
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.º 5
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);
        }