| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System.Buffers.Binary; |
| | | 5 | | using System.Collections.Generic; |
| | | 6 | | using System.Collections.Immutable; |
| | | 7 | | using System.Diagnostics; |
| | | 8 | | using System.Reflection.Internal; |
| | | 9 | | using System.Reflection.Metadata; |
| | | 10 | | |
| | | 11 | | namespace System.Reflection.PortableExecutable |
| | | 12 | | { |
| | | 13 | | public abstract class PEBuilder |
| | | 14 | | { |
| | 0 | 15 | | public PEHeaderBuilder Header { get; } |
| | 0 | 16 | | public Func<IEnumerable<Blob>, BlobContentId> IdProvider { get; } |
| | 0 | 17 | | public bool IsDeterministic { get; } |
| | | 18 | | |
| | | 19 | | private readonly Lazy<ImmutableArray<Section>> _lazySections; |
| | | 20 | | private Blob _lazyChecksum; |
| | | 21 | | |
| | | 22 | | protected readonly struct Section |
| | | 23 | | { |
| | | 24 | | public readonly string Name; |
| | | 25 | | public readonly SectionCharacteristics Characteristics; |
| | | 26 | | |
| | | 27 | | public Section(string name, SectionCharacteristics characteristics) |
| | 0 | 28 | | { |
| | 0 | 29 | | if (name is null) |
| | 0 | 30 | | { |
| | 0 | 31 | | Throw.ArgumentNull(nameof(name)); |
| | | 32 | | } |
| | | 33 | | |
| | 0 | 34 | | Name = name; |
| | 0 | 35 | | Characteristics = characteristics; |
| | 0 | 36 | | } |
| | | 37 | | } |
| | | 38 | | |
| | | 39 | | private readonly struct SerializedSection |
| | | 40 | | { |
| | | 41 | | public readonly BlobBuilder Builder; |
| | | 42 | | |
| | | 43 | | public readonly string Name; |
| | | 44 | | public readonly SectionCharacteristics Characteristics; |
| | | 45 | | public readonly int RelativeVirtualAddress; |
| | | 46 | | public readonly int SizeOfRawData; |
| | | 47 | | public readonly int PointerToRawData; |
| | | 48 | | |
| | | 49 | | public SerializedSection(BlobBuilder builder, string name, SectionCharacteristics characteristics, int relat |
| | 0 | 50 | | { |
| | 0 | 51 | | Name = name; |
| | 0 | 52 | | Characteristics = characteristics; |
| | 0 | 53 | | Builder = builder; |
| | 0 | 54 | | RelativeVirtualAddress = relativeVirtualAddress; |
| | 0 | 55 | | SizeOfRawData = sizeOfRawData; |
| | 0 | 56 | | PointerToRawData = pointerToRawData; |
| | 0 | 57 | | } |
| | | 58 | | |
| | 0 | 59 | | public int VirtualSize => Builder.Count; |
| | | 60 | | } |
| | | 61 | | |
| | 0 | 62 | | protected PEBuilder(PEHeaderBuilder header, Func<IEnumerable<Blob>, BlobContentId>? deterministicIdProvider) |
| | 0 | 63 | | { |
| | 0 | 64 | | if (header is null) |
| | 0 | 65 | | { |
| | 0 | 66 | | Throw.ArgumentNull(nameof(header)); |
| | | 67 | | } |
| | | 68 | | |
| | 0 | 69 | | IdProvider = deterministicIdProvider ?? BlobContentId.GetTimeBasedProvider(); |
| | 0 | 70 | | IsDeterministic = deterministicIdProvider != null; |
| | 0 | 71 | | Header = header; |
| | 0 | 72 | | _lazySections = new Lazy<ImmutableArray<Section>>(CreateSections); |
| | 0 | 73 | | } |
| | | 74 | | |
| | | 75 | | protected ImmutableArray<Section> GetSections() |
| | 0 | 76 | | { |
| | 0 | 77 | | var sections = _lazySections.Value; |
| | 0 | 78 | | if (sections.IsDefault) |
| | 0 | 79 | | { |
| | 0 | 80 | | throw new InvalidOperationException(SR.Format(SR.MustNotReturnNull, nameof(CreateSections))); |
| | | 81 | | } |
| | | 82 | | |
| | 0 | 83 | | return sections; |
| | 0 | 84 | | } |
| | | 85 | | |
| | | 86 | | protected abstract ImmutableArray<Section> CreateSections(); |
| | | 87 | | |
| | | 88 | | protected abstract BlobBuilder SerializeSection(string name, SectionLocation location); |
| | | 89 | | |
| | | 90 | | protected internal abstract PEDirectoriesBuilder GetDirectories(); |
| | | 91 | | |
| | | 92 | | public BlobContentId Serialize(BlobBuilder builder) |
| | 0 | 93 | | { |
| | | 94 | | // Define and serialize sections in two steps. |
| | | 95 | | // We need to know about all sections before serializing them. |
| | 0 | 96 | | var serializedSections = SerializeSections(); |
| | | 97 | | |
| | | 98 | | // The positions and sizes of directories are calculated during section serialization. |
| | 0 | 99 | | var directories = GetDirectories(); |
| | | 100 | | |
| | | 101 | | Blob stampFixup; |
| | 0 | 102 | | WritePESignature(builder); |
| | 0 | 103 | | WriteCoffHeader(builder, serializedSections, out stampFixup); |
| | 0 | 104 | | WritePEHeader(builder, directories, serializedSections); |
| | 0 | 105 | | WriteSectionHeaders(builder, serializedSections); |
| | 0 | 106 | | builder.Align(Header.FileAlignment); |
| | | 107 | | |
| | 0 | 108 | | foreach (var section in serializedSections) |
| | 0 | 109 | | { |
| | 0 | 110 | | builder.LinkSuffix(section.Builder); |
| | 0 | 111 | | builder.Align(Header.FileAlignment); |
| | 0 | 112 | | } |
| | | 113 | | |
| | 0 | 114 | | var contentId = IdProvider(builder.GetBlobs()); |
| | | 115 | | |
| | | 116 | | // patch timestamp in COFF header: |
| | 0 | 117 | | var stampWriter = new BlobWriter(stampFixup); |
| | 0 | 118 | | stampWriter.WriteUInt32(contentId.Stamp); |
| | 0 | 119 | | Debug.Assert(stampWriter.RemainingBytes == 0); |
| | | 120 | | |
| | 0 | 121 | | return contentId; |
| | 0 | 122 | | } |
| | | 123 | | |
| | | 124 | | private ImmutableArray<SerializedSection> SerializeSections() |
| | 0 | 125 | | { |
| | 0 | 126 | | var sections = GetSections(); |
| | 0 | 127 | | var result = ImmutableArray.CreateBuilder<SerializedSection>(sections.Length); |
| | 0 | 128 | | int sizeOfPeHeaders = Header.ComputeSizeOfPEHeaders(sections.Length); |
| | | 129 | | |
| | 0 | 130 | | var nextRva = BitArithmetic.Align(sizeOfPeHeaders, Header.SectionAlignment); |
| | 0 | 131 | | var nextPointer = BitArithmetic.Align(sizeOfPeHeaders, Header.FileAlignment); |
| | | 132 | | |
| | 0 | 133 | | foreach (var section in sections) |
| | 0 | 134 | | { |
| | 0 | 135 | | var builder = SerializeSection(section.Name, new SectionLocation(nextRva, nextPointer)); |
| | | 136 | | |
| | 0 | 137 | | var serialized = new SerializedSection( |
| | 0 | 138 | | builder, |
| | 0 | 139 | | section.Name, |
| | 0 | 140 | | section.Characteristics, |
| | 0 | 141 | | relativeVirtualAddress: nextRva, |
| | 0 | 142 | | sizeOfRawData: BitArithmetic.Align(builder.Count, Header.FileAlignment), |
| | 0 | 143 | | pointerToRawData: nextPointer); |
| | | 144 | | |
| | 0 | 145 | | result.Add(serialized); |
| | | 146 | | |
| | 0 | 147 | | nextRva = BitArithmetic.Align(serialized.RelativeVirtualAddress + serialized.VirtualSize, Header.Section |
| | 0 | 148 | | nextPointer = serialized.PointerToRawData + serialized.SizeOfRawData; |
| | 0 | 149 | | } |
| | | 150 | | |
| | 0 | 151 | | return result.MoveToImmutable(); |
| | 0 | 152 | | } |
| | | 153 | | |
| | | 154 | | private static void WritePESignature(BlobBuilder builder) |
| | 0 | 155 | | { |
| | | 156 | | // MS-DOS stub (128 bytes) |
| | 0 | 157 | | builder.WriteBytes(DosHeader); |
| | | 158 | | |
| | | 159 | | // PE Signature "PE\0\0" |
| | 0 | 160 | | builder.WriteUInt32(PEHeaders.PESignature); |
| | 0 | 161 | | } |
| | | 162 | | |
| | | 163 | | internal static ReadOnlySpan<byte> DosHeader => |
| | 0 | 164 | | [ |
| | 0 | 165 | | 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, |
| | 0 | 166 | | 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, |
| | 0 | 167 | | 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| | 0 | 168 | | 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| | 0 | 169 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| | 0 | 170 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| | 0 | 171 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| | 0 | 172 | | 0x00, 0x00, 0x00, 0x00, |
| | 0 | 173 | | |
| | 0 | 174 | | 0x80, 0x00, 0x00, 0x00, // NT Header offset (0x80 == DosHeader.Length) |
| | 0 | 175 | | |
| | 0 | 176 | | 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, |
| | 0 | 177 | | 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, |
| | 0 | 178 | | 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, |
| | 0 | 179 | | 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, |
| | 0 | 180 | | 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, |
| | 0 | 181 | | 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, |
| | 0 | 182 | | 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, |
| | 0 | 183 | | 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
| | 0 | 184 | | ]; |
| | | 185 | | |
| | | 186 | | private void WriteCoffHeader(BlobBuilder builder, ImmutableArray<SerializedSection> sections, out Blob stampFixu |
| | 0 | 187 | | { |
| | | 188 | | // Machine |
| | 0 | 189 | | builder.WriteUInt16((ushort)(Header.Machine == 0 ? Machine.I386 : Header.Machine)); |
| | | 190 | | |
| | | 191 | | // NumberOfSections |
| | 0 | 192 | | builder.WriteUInt16((ushort)sections.Length); |
| | | 193 | | |
| | | 194 | | // TimeDateStamp: |
| | 0 | 195 | | stampFixup = builder.ReserveBytes(sizeof(uint)); |
| | | 196 | | |
| | | 197 | | // PointerToSymbolTable (TODO: not supported): |
| | | 198 | | // The file pointer to the COFF symbol table, or zero if no COFF symbol table is present. |
| | | 199 | | // This value should be zero for a PE image. |
| | 0 | 200 | | builder.WriteUInt32(0); |
| | | 201 | | |
| | | 202 | | // NumberOfSymbols (TODO: not supported): |
| | | 203 | | // The number of entries in the symbol table. This data can be used to locate the string table, |
| | | 204 | | // which immediately follows the symbol table. This value should be zero for a PE image. |
| | 0 | 205 | | builder.WriteUInt32(0); |
| | | 206 | | |
| | | 207 | | // SizeOfOptionalHeader: |
| | | 208 | | // The size of the optional header, which is required for executable files but not for object files. |
| | | 209 | | // This value should be zero for an object file (TODO). |
| | 0 | 210 | | builder.WriteUInt16((ushort)PEHeader.Size(Header.Is32Bit)); |
| | | 211 | | |
| | | 212 | | // Characteristics |
| | 0 | 213 | | builder.WriteUInt16((ushort)Header.ImageCharacteristics); |
| | 0 | 214 | | } |
| | | 215 | | |
| | | 216 | | private void WritePEHeader(BlobBuilder builder, PEDirectoriesBuilder directories, ImmutableArray<SerializedSecti |
| | 0 | 217 | | { |
| | 0 | 218 | | builder.WriteUInt16((ushort)(Header.Is32Bit ? PEMagic.PE32 : PEMagic.PE32Plus)); |
| | 0 | 219 | | builder.WriteByte(Header.MajorLinkerVersion); |
| | 0 | 220 | | builder.WriteByte(Header.MinorLinkerVersion); |
| | | 221 | | |
| | | 222 | | // SizeOfCode: |
| | 0 | 223 | | builder.WriteUInt32((uint)SumRawDataSizes(sections, SectionCharacteristics.ContainsCode)); |
| | | 224 | | |
| | | 225 | | // SizeOfInitializedData: |
| | 0 | 226 | | builder.WriteUInt32((uint)SumRawDataSizes(sections, SectionCharacteristics.ContainsInitializedData)); |
| | | 227 | | |
| | | 228 | | // SizeOfUninitializedData: |
| | 0 | 229 | | builder.WriteUInt32((uint)SumRawDataSizes(sections, SectionCharacteristics.ContainsUninitializedData)); |
| | | 230 | | |
| | | 231 | | // AddressOfEntryPoint: |
| | 0 | 232 | | builder.WriteUInt32((uint)directories.AddressOfEntryPoint); |
| | | 233 | | |
| | | 234 | | // BaseOfCode: |
| | 0 | 235 | | int codeSectionIndex = IndexOfSection(sections, SectionCharacteristics.ContainsCode); |
| | 0 | 236 | | builder.WriteUInt32((uint)(codeSectionIndex != -1 ? sections[codeSectionIndex].RelativeVirtualAddress : 0)); |
| | | 237 | | |
| | 0 | 238 | | if (Header.Is32Bit) |
| | 0 | 239 | | { |
| | | 240 | | // BaseOfData: |
| | 0 | 241 | | int dataSectionIndex = IndexOfSection(sections, SectionCharacteristics.ContainsInitializedData); |
| | 0 | 242 | | builder.WriteUInt32((uint)(dataSectionIndex != -1 ? sections[dataSectionIndex].RelativeVirtualAddress : |
| | | 243 | | |
| | 0 | 244 | | builder.WriteUInt32((uint)Header.ImageBase); |
| | 0 | 245 | | } |
| | | 246 | | else |
| | 0 | 247 | | { |
| | 0 | 248 | | builder.WriteUInt64(Header.ImageBase); |
| | 0 | 249 | | } |
| | | 250 | | |
| | | 251 | | // NT additional fields: |
| | 0 | 252 | | builder.WriteUInt32((uint)Header.SectionAlignment); |
| | 0 | 253 | | builder.WriteUInt32((uint)Header.FileAlignment); |
| | 0 | 254 | | builder.WriteUInt16(Header.MajorOperatingSystemVersion); |
| | 0 | 255 | | builder.WriteUInt16(Header.MinorOperatingSystemVersion); |
| | 0 | 256 | | builder.WriteUInt16(Header.MajorImageVersion); |
| | 0 | 257 | | builder.WriteUInt16(Header.MinorImageVersion); |
| | 0 | 258 | | builder.WriteUInt16(Header.MajorSubsystemVersion); |
| | 0 | 259 | | builder.WriteUInt16(Header.MinorSubsystemVersion); |
| | | 260 | | |
| | | 261 | | // Win32VersionValue (reserved, should be 0) |
| | 0 | 262 | | builder.WriteUInt32(0); |
| | | 263 | | |
| | | 264 | | // SizeOfImage: |
| | 0 | 265 | | var lastSection = sections[sections.Length - 1]; |
| | 0 | 266 | | builder.WriteUInt32((uint)BitArithmetic.Align(lastSection.RelativeVirtualAddress + lastSection.VirtualSize, |
| | | 267 | | |
| | | 268 | | // SizeOfHeaders: |
| | 0 | 269 | | builder.WriteUInt32((uint)BitArithmetic.Align(Header.ComputeSizeOfPEHeaders(sections.Length), Header.FileAli |
| | | 270 | | |
| | | 271 | | // Checksum: |
| | | 272 | | // Shall be zero for strong name signing, already zeroed by ReserveBytes. |
| | 0 | 273 | | _lazyChecksum = builder.ReserveBytes(sizeof(uint)); |
| | | 274 | | |
| | 0 | 275 | | builder.WriteUInt16((ushort)Header.Subsystem); |
| | 0 | 276 | | builder.WriteUInt16((ushort)Header.DllCharacteristics); |
| | | 277 | | |
| | 0 | 278 | | if (Header.Is32Bit) |
| | 0 | 279 | | { |
| | 0 | 280 | | builder.WriteUInt32((uint)Header.SizeOfStackReserve); |
| | 0 | 281 | | builder.WriteUInt32((uint)Header.SizeOfStackCommit); |
| | 0 | 282 | | builder.WriteUInt32((uint)Header.SizeOfHeapReserve); |
| | 0 | 283 | | builder.WriteUInt32((uint)Header.SizeOfHeapCommit); |
| | 0 | 284 | | } |
| | | 285 | | else |
| | 0 | 286 | | { |
| | 0 | 287 | | builder.WriteUInt64(Header.SizeOfStackReserve); |
| | 0 | 288 | | builder.WriteUInt64(Header.SizeOfStackCommit); |
| | 0 | 289 | | builder.WriteUInt64(Header.SizeOfHeapReserve); |
| | 0 | 290 | | builder.WriteUInt64(Header.SizeOfHeapCommit); |
| | 0 | 291 | | } |
| | | 292 | | |
| | | 293 | | // LoaderFlags |
| | 0 | 294 | | builder.WriteUInt32(0); |
| | | 295 | | |
| | | 296 | | // The number of data-directory entries in the remainder of the header. |
| | 0 | 297 | | builder.WriteUInt32(16); |
| | | 298 | | |
| | | 299 | | // directory entries: |
| | 0 | 300 | | builder.WriteUInt32((uint)directories.ExportTable.RelativeVirtualAddress); |
| | 0 | 301 | | builder.WriteUInt32((uint)directories.ExportTable.Size); |
| | 0 | 302 | | builder.WriteUInt32((uint)directories.ImportTable.RelativeVirtualAddress); |
| | 0 | 303 | | builder.WriteUInt32((uint)directories.ImportTable.Size); |
| | 0 | 304 | | builder.WriteUInt32((uint)directories.ResourceTable.RelativeVirtualAddress); |
| | 0 | 305 | | builder.WriteUInt32((uint)directories.ResourceTable.Size); |
| | 0 | 306 | | builder.WriteUInt32((uint)directories.ExceptionTable.RelativeVirtualAddress); |
| | 0 | 307 | | builder.WriteUInt32((uint)directories.ExceptionTable.Size); |
| | | 308 | | |
| | | 309 | | // Authenticode CertificateTable directory. Shall be zero before the PE is signed. |
| | 0 | 310 | | builder.WriteUInt32(0); |
| | 0 | 311 | | builder.WriteUInt32(0); |
| | | 312 | | |
| | 0 | 313 | | builder.WriteUInt32((uint)directories.BaseRelocationTable.RelativeVirtualAddress); |
| | 0 | 314 | | builder.WriteUInt32((uint)directories.BaseRelocationTable.Size); |
| | 0 | 315 | | builder.WriteUInt32((uint)directories.DebugTable.RelativeVirtualAddress); |
| | 0 | 316 | | builder.WriteUInt32((uint)directories.DebugTable.Size); |
| | 0 | 317 | | builder.WriteUInt32((uint)directories.CopyrightTable.RelativeVirtualAddress); |
| | 0 | 318 | | builder.WriteUInt32((uint)directories.CopyrightTable.Size); |
| | 0 | 319 | | builder.WriteUInt32((uint)directories.GlobalPointerTable.RelativeVirtualAddress); |
| | 0 | 320 | | builder.WriteUInt32((uint)directories.GlobalPointerTable.Size); |
| | 0 | 321 | | builder.WriteUInt32((uint)directories.ThreadLocalStorageTable.RelativeVirtualAddress); |
| | 0 | 322 | | builder.WriteUInt32((uint)directories.ThreadLocalStorageTable.Size); |
| | 0 | 323 | | builder.WriteUInt32((uint)directories.LoadConfigTable.RelativeVirtualAddress); |
| | 0 | 324 | | builder.WriteUInt32((uint)directories.LoadConfigTable.Size); |
| | 0 | 325 | | builder.WriteUInt32((uint)directories.BoundImportTable.RelativeVirtualAddress); |
| | 0 | 326 | | builder.WriteUInt32((uint)directories.BoundImportTable.Size); |
| | 0 | 327 | | builder.WriteUInt32((uint)directories.ImportAddressTable.RelativeVirtualAddress); |
| | 0 | 328 | | builder.WriteUInt32((uint)directories.ImportAddressTable.Size); |
| | 0 | 329 | | builder.WriteUInt32((uint)directories.DelayImportTable.RelativeVirtualAddress); |
| | 0 | 330 | | builder.WriteUInt32((uint)directories.DelayImportTable.Size); |
| | 0 | 331 | | builder.WriteUInt32((uint)directories.CorHeaderTable.RelativeVirtualAddress); |
| | 0 | 332 | | builder.WriteUInt32((uint)directories.CorHeaderTable.Size); |
| | | 333 | | |
| | | 334 | | // Reserved, should be 0 |
| | 0 | 335 | | builder.WriteUInt64(0); |
| | 0 | 336 | | } |
| | | 337 | | |
| | | 338 | | private static void WriteSectionHeaders(BlobBuilder builder, ImmutableArray<SerializedSection> serializedSection |
| | 0 | 339 | | { |
| | 0 | 340 | | foreach (var serializedSection in serializedSections) |
| | 0 | 341 | | { |
| | 0 | 342 | | WriteSectionHeader(builder, serializedSection); |
| | 0 | 343 | | } |
| | 0 | 344 | | } |
| | | 345 | | |
| | | 346 | | private static void WriteSectionHeader(BlobBuilder builder, SerializedSection serializedSection) |
| | 0 | 347 | | { |
| | 0 | 348 | | if (serializedSection.VirtualSize == 0) |
| | 0 | 349 | | { |
| | 0 | 350 | | return; |
| | | 351 | | } |
| | | 352 | | |
| | 0 | 353 | | for (int j = 0, m = serializedSection.Name.Length; j < 8; j++) |
| | 0 | 354 | | { |
| | 0 | 355 | | if (j < m) |
| | 0 | 356 | | { |
| | 0 | 357 | | builder.WriteByte((byte)serializedSection.Name[j]); |
| | 0 | 358 | | } |
| | | 359 | | else |
| | 0 | 360 | | { |
| | 0 | 361 | | builder.WriteByte(0); |
| | 0 | 362 | | } |
| | 0 | 363 | | } |
| | | 364 | | |
| | 0 | 365 | | builder.WriteUInt32((uint)serializedSection.VirtualSize); |
| | 0 | 366 | | builder.WriteUInt32((uint)serializedSection.RelativeVirtualAddress); |
| | 0 | 367 | | builder.WriteUInt32((uint)serializedSection.SizeOfRawData); |
| | 0 | 368 | | builder.WriteUInt32((uint)serializedSection.PointerToRawData); |
| | | 369 | | |
| | | 370 | | // PointerToRelocations (TODO: not supported): |
| | 0 | 371 | | builder.WriteUInt32(0); |
| | | 372 | | |
| | | 373 | | // PointerToLinenumbers (TODO: not supported): |
| | 0 | 374 | | builder.WriteUInt32(0); |
| | | 375 | | |
| | | 376 | | // NumberOfRelocations (TODO: not supported): |
| | 0 | 377 | | builder.WriteUInt16(0); |
| | | 378 | | |
| | | 379 | | // NumberOfLinenumbers (TODO: not supported): |
| | 0 | 380 | | builder.WriteUInt16(0); |
| | | 381 | | |
| | 0 | 382 | | builder.WriteUInt32((uint)serializedSection.Characteristics); |
| | 0 | 383 | | } |
| | | 384 | | |
| | | 385 | | private static int IndexOfSection(ImmutableArray<SerializedSection> sections, SectionCharacteristics characteris |
| | 0 | 386 | | { |
| | 0 | 387 | | for (int i = 0; i < sections.Length; i++) |
| | 0 | 388 | | { |
| | 0 | 389 | | if ((sections[i].Characteristics & characteristics) == characteristics) |
| | 0 | 390 | | { |
| | 0 | 391 | | return i; |
| | | 392 | | } |
| | 0 | 393 | | } |
| | | 394 | | |
| | 0 | 395 | | return -1; |
| | 0 | 396 | | } |
| | | 397 | | |
| | | 398 | | private static int SumRawDataSizes(ImmutableArray<SerializedSection> sections, SectionCharacteristics characteri |
| | 0 | 399 | | { |
| | 0 | 400 | | int result = 0; |
| | 0 | 401 | | for (int i = 0; i < sections.Length; i++) |
| | 0 | 402 | | { |
| | 0 | 403 | | if ((sections[i].Characteristics & characteristics) == characteristics) |
| | 0 | 404 | | { |
| | 0 | 405 | | result += sections[i].SizeOfRawData; |
| | 0 | 406 | | } |
| | 0 | 407 | | } |
| | | 408 | | |
| | 0 | 409 | | return result; |
| | 0 | 410 | | } |
| | | 411 | | |
| | | 412 | | // internal for testing |
| | | 413 | | internal static IEnumerable<Blob> GetContentToSign(BlobBuilder peImage, int peHeadersSize, int peHeaderAlignment |
| | 0 | 414 | | { |
| | | 415 | | // Signed content includes |
| | | 416 | | // - PE header without its alignment padding |
| | | 417 | | // - all sections including their alignment padding and excluding strong name signature blob |
| | | 418 | | |
| | | 419 | | // PE specification: |
| | | 420 | | // To calculate the PE image hash, Authenticode orders the sections that are specified in the section tabl |
| | | 421 | | // by address range, then hashes the resulting sequence of bytes, passing over the exclusion ranges. |
| | | 422 | | // |
| | | 423 | | // Note that sections are by construction ordered by their address, so there is no need to reorder. |
| | | 424 | | |
| | 0 | 425 | | int remainingHeaderToSign = peHeadersSize; |
| | 0 | 426 | | int remainingHeader = BitArithmetic.Align(peHeadersSize, peHeaderAlignment); |
| | 0 | 427 | | foreach (var blob in peImage.GetBlobs()) |
| | 0 | 428 | | { |
| | 0 | 429 | | int blobStart = blob.Start; |
| | 0 | 430 | | int blobLength = blob.Length; |
| | 0 | 431 | | while (blobLength > 0) |
| | 0 | 432 | | { |
| | 0 | 433 | | if (remainingHeader > 0) |
| | 0 | 434 | | { |
| | | 435 | | int length; |
| | | 436 | | |
| | 0 | 437 | | if (remainingHeaderToSign > 0) |
| | 0 | 438 | | { |
| | 0 | 439 | | length = Math.Min(remainingHeaderToSign, blobLength); |
| | 0 | 440 | | yield return new Blob(blob.Buffer, blobStart, length); |
| | 0 | 441 | | remainingHeaderToSign -= length; |
| | 0 | 442 | | } |
| | | 443 | | else |
| | 0 | 444 | | { |
| | 0 | 445 | | length = Math.Min(remainingHeader, blobLength); |
| | 0 | 446 | | } |
| | | 447 | | |
| | 0 | 448 | | remainingHeader -= length; |
| | 0 | 449 | | blobStart += length; |
| | 0 | 450 | | blobLength -= length; |
| | 0 | 451 | | } |
| | 0 | 452 | | else if (blob.Buffer == strongNameSignatureFixup.Buffer) |
| | 0 | 453 | | { |
| | 0 | 454 | | yield return GetPrefixBlob(new Blob(blob.Buffer, blobStart, blobLength), strongNameSignatureFixu |
| | 0 | 455 | | yield return GetSuffixBlob(new Blob(blob.Buffer, blobStart, blobLength), strongNameSignatureFixu |
| | 0 | 456 | | break; |
| | | 457 | | } |
| | | 458 | | else |
| | 0 | 459 | | { |
| | 0 | 460 | | yield return new Blob(blob.Buffer, blobStart, blobLength); |
| | 0 | 461 | | break; |
| | | 462 | | } |
| | 0 | 463 | | } |
| | 0 | 464 | | } |
| | 0 | 465 | | } |
| | | 466 | | |
| | | 467 | | // internal for testing |
| | 0 | 468 | | internal static Blob GetPrefixBlob(Blob container, Blob blob) => new Blob(container.Buffer, container.Start, blo |
| | 0 | 469 | | internal static Blob GetSuffixBlob(Blob container, Blob blob) => new Blob(container.Buffer, blob.Start + blob.Le |
| | | 470 | | |
| | | 471 | | internal void Sign(BlobBuilder peImage, Blob strongNameSignatureFixup, Func<IEnumerable<Blob>, byte[]> signature |
| | 0 | 472 | | { |
| | 0 | 473 | | Debug.Assert(peImage != null); |
| | 0 | 474 | | Debug.Assert(signatureProvider != null); |
| | | 475 | | |
| | 0 | 476 | | int peHeadersSize = Header.ComputeSizeOfPEHeaders(GetSections().Length); |
| | 0 | 477 | | byte[] signature = signatureProvider(GetContentToSign(peImage, peHeadersSize, Header.FileAlignment, strongNa |
| | | 478 | | |
| | | 479 | | // signature may be shorter (the rest of the reserved space is padding): |
| | 0 | 480 | | if (signature == null || signature.Length > strongNameSignatureFixup.Length) |
| | 0 | 481 | | { |
| | 0 | 482 | | throw new InvalidOperationException(SR.SignatureProviderReturnedInvalidSignature); |
| | | 483 | | } |
| | | 484 | | |
| | 0 | 485 | | var writer = new BlobWriter(strongNameSignatureFixup); |
| | 0 | 486 | | writer.WriteBytes(signature); |
| | | 487 | | |
| | | 488 | | // Calculate the checksum after the strong name signature has been written. |
| | 0 | 489 | | uint checksum = CalculateChecksum(peImage, _lazyChecksum); |
| | 0 | 490 | | new BlobWriter(_lazyChecksum).WriteUInt32(checksum); |
| | 0 | 491 | | } |
| | | 492 | | |
| | | 493 | | // internal for testing |
| | | 494 | | internal static uint CalculateChecksum(BlobBuilder peImage, Blob checksumFixup) |
| | 0 | 495 | | { |
| | 0 | 496 | | uint checksum = 0; |
| | 0 | 497 | | int pendingByte = -1; |
| | | 498 | | |
| | 0 | 499 | | foreach (Blob blob in peImage.GetBlobs()) |
| | 0 | 500 | | { |
| | 0 | 501 | | if (blob.Buffer == checksumFixup.Buffer) |
| | 0 | 502 | | { |
| | | 503 | | // The checksum field itself is excluded, so checksum the content before and |
| | | 504 | | // after the fixup as two separate segments. |
| | 0 | 505 | | AddToChecksum(GetPrefixBlob(blob, checksumFixup).GetBytes(), ref checksum, ref pendingByte); |
| | 0 | 506 | | AddToChecksum(GetSuffixBlob(blob, checksumFixup).GetBytes(), ref checksum, ref pendingByte); |
| | 0 | 507 | | } |
| | | 508 | | else |
| | 0 | 509 | | { |
| | 0 | 510 | | AddToChecksum(blob.GetBytes(), ref checksum, ref pendingByte); |
| | 0 | 511 | | } |
| | 0 | 512 | | } |
| | | 513 | | |
| | 0 | 514 | | if (pendingByte >= 0) |
| | 0 | 515 | | { |
| | 0 | 516 | | checksum = AggregateChecksum(checksum, (ushort)pendingByte); |
| | 0 | 517 | | } |
| | | 518 | | |
| | 0 | 519 | | return checksum + (uint)peImage.Count; |
| | 0 | 520 | | } |
| | | 521 | | |
| | | 522 | | private static void AddToChecksum(ReadOnlySpan<byte> segment, ref uint checksum, ref int pendingByte) |
| | 0 | 523 | | { |
| | 0 | 524 | | if (segment.IsEmpty) |
| | 0 | 525 | | { |
| | 0 | 526 | | return; |
| | | 527 | | } |
| | | 528 | | |
| | 0 | 529 | | if (pendingByte >= 0) |
| | 0 | 530 | | { |
| | | 531 | | // little-endian encoding: |
| | 0 | 532 | | checksum = AggregateChecksum(checksum, (ushort)(segment[0] << 8 | pendingByte)); |
| | 0 | 533 | | segment = segment.Slice(1); |
| | 0 | 534 | | } |
| | | 535 | | |
| | 0 | 536 | | while (segment.Length >= sizeof(ushort)) |
| | 0 | 537 | | { |
| | | 538 | | // little-endian encoding: |
| | 0 | 539 | | checksum = AggregateChecksum(checksum, BinaryPrimitives.ReadUInt16LittleEndian(segment)); |
| | 0 | 540 | | segment = segment.Slice(sizeof(ushort)); |
| | 0 | 541 | | } |
| | | 542 | | |
| | | 543 | | // Carry a trailing odd byte over to the next segment. |
| | 0 | 544 | | pendingByte = segment.IsEmpty ? -1 : segment[0]; |
| | 0 | 545 | | } |
| | | 546 | | |
| | | 547 | | private static uint AggregateChecksum(uint checksum, ushort value) |
| | 0 | 548 | | { |
| | 0 | 549 | | uint sum = checksum + value; |
| | 0 | 550 | | return (sum >> 16) + unchecked((ushort)sum); |
| | 0 | 551 | | } |
| | | 552 | | } |
| | | 553 | | } |
| | | 554 | | |