private static int WriteEmbeddedPortablePdbData(BlobBuilder builder, BlobBuilder debugMetadata) { int start = builder.Count; // header (signature, decompressed size): builder.WriteUInt32(PortablePdbVersions.DebugDirectoryEmbeddedSignature); builder.WriteInt32(debugMetadata.Count); // compressed data: var compressed = new MemoryStream(); using (var deflate = new DeflateStream(compressed, CompressionLevel.Optimal, leaveOpen: true)) { foreach (var blob in debugMetadata.GetBlobs()) { var segment = blob.GetBytes(); deflate.Write(segment.Array, segment.Offset, segment.Count); } } // TODO: avoid multiple copies: builder.WriteBytes(compressed.ToArray()); return builder.Count - start; }
public BlobContentId Serialize(BlobBuilder builder) { // Define and serialize sections in two steps. // We need to know about all sections before serializing them. var serializedSections = SerializeSections(); // The positions and sizes of directories are calculated during section serialization. var directories = GetDirectories(); Blob stampFixup; WritePESignature(builder); WriteCoffHeader(builder, serializedSections, out stampFixup); WritePEHeader(builder, directories, serializedSections); WriteSectionHeaders(builder, serializedSections); builder.Align(Header.FileAlignment); foreach (var section in serializedSections) { builder.LinkSuffix(section.Builder); builder.Align(Header.FileAlignment); } var contentId = IdProvider(builder.GetBlobs()); // patch timestamp in COFF header: var stampWriter = new BlobWriter(stampFixup); stampWriter.WriteUInt32(contentId.Stamp); Debug.Assert(stampWriter.RemainingBytes == 0); return contentId; }
private IEnumerable<Blob> GetContentToSign(BlobBuilder peImage) { // Signed content includes // - PE header without its alignment padding // - all sections including their alignment padding and excluding strong name signature blob int remainingHeader = Header.ComputeSizeOfPeHeaders(GetSections().Length); foreach (var blob in peImage.GetBlobs()) { if (remainingHeader > 0) { int length = Math.Min(remainingHeader, blob.Length); yield return new Blob(blob.Buffer, blob.Start, length); remainingHeader -= length; } else if (blob.Buffer == _lazyStrongNameSignature.Buffer) { yield return new Blob(blob.Buffer, blob.Start, _lazyStrongNameSignature.Start - blob.Start); yield return new Blob(blob.Buffer, _lazyStrongNameSignature.Start + _lazyStrongNameSignature.Length, blob.Length - _lazyStrongNameSignature.Length); } else { yield return new Blob(blob.Buffer, blob.Start, blob.Length); } } }
// internal for testing internal static IEnumerable<Blob> GetContentToSign(BlobBuilder peImage, int peHeadersSize, int peHeaderAlignment, Blob strongNameSignatureFixup) { // Signed content includes // - PE header without its alignment padding // - all sections including their alignment padding and excluding strong name signature blob // PE specification: // To calculate the PE image hash, Authenticode orders the sections that are specified in the section table // by address range, then hashes the resulting sequence of bytes, passing over the exclusion ranges. // // Note that sections are by construction ordered by their address, so there is no need to reorder. int remainingHeaderToSign = peHeadersSize; int remainingHeader = BitArithmetic.Align(peHeadersSize, peHeaderAlignment); foreach (var blob in peImage.GetBlobs()) { int blobStart = blob.Start; int blobLength = blob.Length; while (blobLength > 0) { if (remainingHeader > 0) { int length; if (remainingHeaderToSign > 0) { length = Math.Min(remainingHeaderToSign, blobLength); yield return new Blob(blob.Buffer, blobStart, length); remainingHeaderToSign -= length; } else { length = Math.Min(remainingHeader, blobLength); } remainingHeader -= length; blobStart += length; blobLength -= length; } else if (blob.Buffer == strongNameSignatureFixup.Buffer) { yield return GetPrefixBlob(new Blob(blob.Buffer, blobStart, blobLength), strongNameSignatureFixup); yield return GetSuffixBlob(new Blob(blob.Buffer, blobStart, blobLength), strongNameSignatureFixup); break; } else { yield return new Blob(blob.Buffer, blobStart, blobLength); break; } } } }
// internal for testing internal static IEnumerable<Blob> GetContentToChecksum(BlobBuilder peImage, Blob checksumFixup) { foreach (var blob in peImage.GetBlobs()) { if (blob.Buffer == checksumFixup.Buffer) { yield return GetPrefixBlob(blob, checksumFixup); yield return GetSuffixBlob(blob, checksumFixup); } else { yield return blob; } } }
private static bool TestChecksumAndAuthenticodeSignature(Stream peStream, byte[] privateKeyOpt = null) { var peHeaders = new PEHeaders(peStream); bool is32bit = peHeaders.PEHeader.Magic == PEMagic.PE32; uint expectedChecksum = peHeaders.PEHeader.CheckSum; int peHeadersSize = peHeaders.PEHeaderStartOffset + PEHeader.Size(is32bit) + SectionHeader.Size * peHeaders.SectionHeaders.Length; peStream.Position = 0; if (expectedChecksum == 0) { // not signed return false; } int peSize = (int)peStream.Length; var peImage = new BlobBuilder(peSize); Assert.Equal(peSize, peImage.TryWriteBytes(peStream, peSize)); var buffer = peImage.GetBlobs().Single().Buffer; var checksumBlob = new Blob(buffer, peHeaders.PEHeaderStartOffset + PEHeader.OffsetOfChecksum, sizeof(uint)); uint checksum = PEBuilder.CalculateChecksum(peImage, checksumBlob); Assert.Equal(expectedChecksum, checksum); // validate signature: if (privateKeyOpt != null) { // signature is calculated with checksum zeroed: new BlobWriter(checksumBlob).WriteUInt32(0); int snOffset; Assert.True(peHeaders.TryGetDirectoryOffset(peHeaders.CorHeader.StrongNameSignatureDirectory, out snOffset)); var snBlob = new Blob(buffer, snOffset, peHeaders.CorHeader.StrongNameSignatureDirectory.Size); var expectedSignature = snBlob.GetBytes().ToArray(); var signature = SigningUtilities.CalculateRsaSignature(PEBuilder.GetContentToSign(peImage, peHeadersSize, peHeaders.PEHeader.FileAlignment, snBlob), privateKeyOpt); AssertEx.Equal(expectedSignature, signature); } return true; }
private static IEnumerable<string> GetBlobRanges(BlobBuilder builder, IEnumerable<Blob> blobs) { var blobIndex = new Dictionary<byte[], int>(); int i = 0; foreach (var blob in builder.GetBlobs()) { blobIndex.Add(blob.Buffer, i++); } foreach (var blob in blobs) { yield return $"{blobIndex[blob.Buffer]}: [{blob.Start}, {blob.Start + blob.Length})"; } }
public void GetContentToSign_HeaderAndFixupInDistinctBlobs() { var builder = new BlobBuilder(16); builder.WriteBytes(0, 16); builder.WriteBytes(1, 16); builder.WriteBytes(2, 16); builder.WriteBytes(3, 16); var snFixup = builder.ReserveBytes(16); builder.WriteBytes(5, 16); builder.WriteBytes(6, 1); Assert.Equal(7, builder.GetBlobs().Count()); AssertEx.Equal( new[] { "0: [0, 1)", "0: [4, 16)", "1: [0, 16)", "2: [0, 16)", "3: [0, 16)", "4: [0, 0)", "4: [16, 16)", "5: [0, 16)", "6: [0, 1)" }, GetBlobRanges(builder, PEBuilder.GetContentToSign(builder, peHeadersSize: 1, peHeaderAlignment: 4, strongNameSignatureFixup: snFixup))); }
public void GetContentToSign_MultiBlobHeader() { var builder = new BlobBuilder(16); builder.WriteBytes(0, 16); builder.WriteBytes(1, 16); builder.WriteBytes(2, 16); builder.WriteBytes(3, 16); builder.WriteBytes(4, 2); var snFixup = builder.ReserveBytes(1); builder.WriteBytes(4, 13); builder.WriteBytes(5, 10); Assert.Equal(6, builder.GetBlobs().Count()); AssertEx.Equal( new[] { "0: [0, 16)", "1: [0, 16)", "2: [0, 1)", "4: [0, 2)", "4: [3, 16)", "5: [0, 10)" }, GetBlobRanges(builder, PEBuilder.GetContentToSign(builder, peHeadersSize: 33, peHeaderAlignment: 64, strongNameSignatureFixup: snFixup))); }
public void GetContentToSign_AllInOneBlob() { var builder = new BlobBuilder(16); builder.WriteBytes(1, 5); var snFixup = builder.ReserveBytes(5); builder.WriteBytes(2, 6); Assert.Equal(1, builder.GetBlobs().Count()); AssertEx.Equal( new[] { "0: [0, 2)", "0: [4, 5)", "0: [10, 16)" }, GetBlobRanges(builder, PEBuilder.GetContentToSign(builder, peHeadersSize: 2, peHeaderAlignment: 4, strongNameSignatureFixup: snFixup))); }