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