// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.Diagnostics;
using dnlib.IO;
using dnlib.PE;
namespace dnlib.DotNet.Writer {
///
/// options
///
public sealed class PEHeadersOptions {
///
/// Default DLL characteristics
///
public const DllCharacteristics DefaultDllCharacteristics = dnlib.PE.DllCharacteristics.TerminalServerAware | dnlib.PE.DllCharacteristics.NoSeh | dnlib.PE.DllCharacteristics.NxCompat | dnlib.PE.DllCharacteristics.DynamicBase;
///
/// Default subsystem value
///
public const Subsystem DEFAULT_SUBSYSTEM = dnlib.PE.Subsystem.WindowsGui;
///
/// Default major linker version. Roslyn C# defaults to 0x30, and Roslyn VB defaults to 0x50.
///
public const byte DEFAULT_MAJOR_LINKER_VERSION = 11;
///
/// Default minor linker version
///
public const byte DEFAULT_MINOR_LINKER_VERSION = 0;
///
/// IMAGE_FILE_HEADER.Machine value
///
public Machine? Machine;
///
/// IMAGE_FILE_HEADER.TimeDateStamp value
///
public uint? TimeDateStamp;
///
/// IMAGE_FILE_HEADER.PointerToSymbolTable value
///
public uint? PointerToSymbolTable;
///
/// IMAGE_FILE_HEADER.NumberOfSymbols value
///
public uint? NumberOfSymbols;
///
/// IMAGE_FILE_HEADER.Characteristics value. bit
/// is ignored and set/cleared depending on whether it's a EXE or a DLL file.
///
public Characteristics? Characteristics;
///
/// IMAGE_OPTIONAL_HEADER.MajorLinkerVersion value
///
public byte? MajorLinkerVersion;
///
/// IMAGE_OPTIONAL_HEADER.MinorLinkerVersion value
///
public byte? MinorLinkerVersion;
///
/// IMAGE_OPTIONAL_HEADER.ImageBase value
///
public ulong? ImageBase;
///
/// IMAGE_OPTIONAL_HEADER.SectionAlignment value
///
public uint? SectionAlignment;
///
/// IMAGE_OPTIONAL_HEADER.FileAlignment value
///
public uint? FileAlignment;
///
/// IMAGE_OPTIONAL_HEADER.MajorOperatingSystemVersion value
///
public ushort? MajorOperatingSystemVersion;
///
/// IMAGE_OPTIONAL_HEADER.MinorOperatingSystemVersion value
///
public ushort? MinorOperatingSystemVersion;
///
/// IMAGE_OPTIONAL_HEADER.MajorImageVersion value
///
public ushort? MajorImageVersion;
///
/// IMAGE_OPTIONAL_HEADER.MinorImageVersion value
///
public ushort? MinorImageVersion;
///
/// IMAGE_OPTIONAL_HEADER.MajorSubsystemVersion value
///
public ushort? MajorSubsystemVersion;
///
/// IMAGE_OPTIONAL_HEADER.MinorSubsystemVersion value
///
public ushort? MinorSubsystemVersion;
///
/// IMAGE_OPTIONAL_HEADER.Win32VersionValue value
///
public uint? Win32VersionValue;
///
/// IMAGE_OPTIONAL_HEADER.Subsystem value
///
public Subsystem? Subsystem;
///
/// IMAGE_OPTIONAL_HEADER.DllCharacteristics value
///
public DllCharacteristics? DllCharacteristics;
///
/// IMAGE_OPTIONAL_HEADER.SizeOfStackReserve value
///
public ulong? SizeOfStackReserve;
///
/// IMAGE_OPTIONAL_HEADER.SizeOfStackCommit value
///
public ulong? SizeOfStackCommit;
///
/// IMAGE_OPTIONAL_HEADER.SizeOfHeapReserve value
///
public ulong? SizeOfHeapReserve;
///
/// IMAGE_OPTIONAL_HEADER.SizeOfHeapCommit value
///
public ulong? SizeOfHeapCommit;
///
/// IMAGE_OPTIONAL_HEADER.LoaderFlags value
///
public uint? LoaderFlags;
///
/// IMAGE_OPTIONAL_HEADER.NumberOfRvaAndSizes value
///
public uint? NumberOfRvaAndSizes;
///
/// Creates a new time date stamp using current time
///
/// A new time date stamp
public static uint CreateNewTimeDateStamp() => (uint)(DateTime.UtcNow - Epoch).TotalSeconds;
static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}
///
/// DOS and PE headers
///
public sealed class PEHeaders : IChunk {
IList sections;
readonly PEHeadersOptions options;
FileOffset offset;
RVA rva;
uint length;
readonly uint sectionAlignment;
readonly uint fileAlignment;
ulong imageBase;
long startOffset;
long checkSumOffset;
bool isExeFile;
// Copied from Partition II.25.2.1
static readonly byte[] dosHeader = new byte[0x80] {
0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD,
0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68,
0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72,
0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F,
0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E,
0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20,
0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
///
/// Gets/sets the native entry point
///
public StartupStub StartupStub { get; set; }
///
/// Gets/sets the COR20 header
///
public ImageCor20Header ImageCor20Header { get; set; }
///
/// Gets/sets the IAT
///
public ImportAddressTable ImportAddressTable { get; set; }
///
/// Gets/sets the
///
public ImportDirectory ImportDirectory { get; set; }
///
/// Gets/sets the Win32 resources
///
public Win32ResourcesChunk Win32Resources { get; set; }
///
/// Gets/sets the relocation directory
///
public RelocDirectory RelocDirectory { get; set; }
///
/// Gets/sets the debug directory
///
public DebugDirectory DebugDirectory { get; set; }
internal IChunk ExportDirectory { get; set; }
///
/// Gets the image base
///
public ulong ImageBase => imageBase;
///
/// Gets/sets a value indicating whether this is a EXE or a DLL file
///
public bool IsExeFile {
get => isExeFile;
set => isExeFile = value;
}
///
public FileOffset FileOffset => offset;
///
public RVA RVA => rva;
///
/// Gets the section alignment
///
public uint SectionAlignment => sectionAlignment;
///
/// Gets the file alignment
///
public uint FileAlignment => fileAlignment;
///
/// Gets/sets the s
///
public IList PESections {
get => sections;
set => sections = value;
}
///
/// Default constructor
///
public PEHeaders()
: this(new PEHeadersOptions()) {
}
///
/// Constructor
///
/// Options
public PEHeaders(PEHeadersOptions options) {
this.options = options ?? new PEHeadersOptions();
sectionAlignment = this.options.SectionAlignment ?? 0x2000;
fileAlignment = this.options.FileAlignment ?? 0x200;
}
///
public void SetOffset(FileOffset offset, RVA rva) {
this.offset = offset;
this.rva = rva;
length = (uint)dosHeader.Length;
length += 4 + 0x14;
length += Use32BitOptionalHeader() ? 0xE0U : 0xF0;
length += (uint)sections.Count * 0x28;
if (Use32BitOptionalHeader())
imageBase = options.ImageBase ?? (IsExeFile ? 0x00400000UL : 0x10000000);
else
imageBase = options.ImageBase ?? (IsExeFile ? 0x0000000140000000UL : 0x0000000180000000);
}
int SectionsCount {
get {
int count = 0;
foreach (var section in sections) {
if (section.GetVirtualSize() != 0)
count++;
}
return count;
}
}
///
public uint GetFileLength() => length;
///
public uint GetVirtualSize() => GetFileLength();
///
public uint CalculateAlignment() => 0;
IEnumerable GetSectionSizeInfos() {
foreach (var section in sections) {
uint virtSize = section.GetVirtualSize();
if (virtSize != 0)
yield return new SectionSizeInfo(virtSize, section.Characteristics);
}
}
///
public void WriteTo(DataWriter writer) {
startOffset = writer.Position;
// DOS header
writer.WriteBytes(dosHeader);
// PE magic
writer.WriteInt32(0x00004550);
// Image file header
writer.WriteUInt16((ushort)GetMachine());
writer.WriteUInt16((ushort)SectionsCount);
Debug.Assert(SectionsCount == sections.Count, "One or more sections are empty! The PE file could be bigger than it should be. Empty sections should be removed.");
writer.WriteUInt32(options.TimeDateStamp ?? PEHeadersOptions.CreateNewTimeDateStamp());
writer.WriteUInt32(options.PointerToSymbolTable ?? 0);
writer.WriteUInt32(options.NumberOfSymbols ?? 0);
writer.WriteUInt16((ushort)(Use32BitOptionalHeader() ? 0xE0U : 0xF0));
writer.WriteUInt16((ushort)GetCharacteristics());
var sectionSizes = new SectionSizes(fileAlignment, sectionAlignment, length, () => GetSectionSizeInfos());
// Image optional header
uint ep = StartupStub is null || !StartupStub.Enable ? 0 : (uint)StartupStub.EntryPointRVA;
if (Use32BitOptionalHeader()) {
writer.WriteUInt16((ushort)0x010B);
writer.WriteByte(options.MajorLinkerVersion ?? PEHeadersOptions.DEFAULT_MAJOR_LINKER_VERSION);
writer.WriteByte(options.MinorLinkerVersion ?? PEHeadersOptions.DEFAULT_MINOR_LINKER_VERSION);
writer.WriteUInt32(sectionSizes.SizeOfCode);
writer.WriteUInt32(sectionSizes.SizeOfInitdData);
writer.WriteUInt32(sectionSizes.SizeOfUninitdData);
writer.WriteUInt32(ep);
writer.WriteUInt32(sectionSizes.BaseOfCode);
writer.WriteUInt32(sectionSizes.BaseOfData);
writer.WriteUInt32((uint)imageBase);
writer.WriteUInt32(sectionAlignment);
writer.WriteUInt32(fileAlignment);
writer.WriteUInt16(options.MajorOperatingSystemVersion ?? 4);
writer.WriteUInt16(options.MinorOperatingSystemVersion ?? 0);
writer.WriteUInt16(options.MajorImageVersion ?? 0);
writer.WriteUInt16(options.MinorImageVersion ?? 0);
writer.WriteUInt16(options.MajorSubsystemVersion ?? 4);
writer.WriteUInt16(options.MinorSubsystemVersion ?? 0);
writer.WriteUInt32(options.Win32VersionValue ?? 0);
writer.WriteUInt32(sectionSizes.SizeOfImage);
writer.WriteUInt32(sectionSizes.SizeOfHeaders);
checkSumOffset = writer.Position;
writer.WriteInt32(0); // CheckSum
writer.WriteUInt16((ushort)(options.Subsystem ?? PEHeadersOptions.DEFAULT_SUBSYSTEM));
writer.WriteUInt16((ushort)(options.DllCharacteristics ?? PEHeadersOptions.DefaultDllCharacteristics));
writer.WriteUInt32((uint)(options.SizeOfStackReserve ?? 0x00100000));
writer.WriteUInt32((uint)(options.SizeOfStackCommit ?? 0x00001000));
writer.WriteUInt32((uint)(options.SizeOfHeapReserve ?? 0x00100000));
writer.WriteUInt32((uint)(options.SizeOfHeapCommit ?? 0x00001000));
writer.WriteUInt32(options.LoaderFlags ?? 0x00000000);
writer.WriteUInt32(options.NumberOfRvaAndSizes ?? 0x00000010);
}
else {
writer.WriteUInt16((ushort)0x020B);
writer.WriteByte(options.MajorLinkerVersion ?? PEHeadersOptions.DEFAULT_MAJOR_LINKER_VERSION);
writer.WriteByte(options.MinorLinkerVersion ?? PEHeadersOptions.DEFAULT_MINOR_LINKER_VERSION);
writer.WriteUInt32(sectionSizes.SizeOfCode);
writer.WriteUInt32(sectionSizes.SizeOfInitdData);
writer.WriteUInt32(sectionSizes.SizeOfUninitdData);
writer.WriteUInt32(ep);
writer.WriteUInt32(sectionSizes.BaseOfCode);
writer.WriteUInt64(imageBase);
writer.WriteUInt32(sectionAlignment);
writer.WriteUInt32(fileAlignment);
writer.WriteUInt16(options.MajorOperatingSystemVersion ?? 4);
writer.WriteUInt16(options.MinorOperatingSystemVersion ?? 0);
writer.WriteUInt16(options.MajorImageVersion ?? 0);
writer.WriteUInt16(options.MinorImageVersion ?? 0);
writer.WriteUInt16(options.MajorSubsystemVersion ?? 4);
writer.WriteUInt16(options.MinorSubsystemVersion ?? 0);
writer.WriteUInt32(options.Win32VersionValue ?? 0);
writer.WriteUInt32(sectionSizes.SizeOfImage);
writer.WriteUInt32(sectionSizes.SizeOfHeaders);
checkSumOffset = writer.Position;
writer.WriteInt32(0); // CheckSum
writer.WriteUInt16((ushort)(options.Subsystem ?? PEHeadersOptions.DEFAULT_SUBSYSTEM));
writer.WriteUInt16((ushort)(options.DllCharacteristics ?? PEHeadersOptions.DefaultDllCharacteristics));
writer.WriteUInt64(options.SizeOfStackReserve ?? 0x0000000000400000);
writer.WriteUInt64(options.SizeOfStackCommit ?? 0x0000000000004000);
writer.WriteUInt64(options.SizeOfHeapReserve ?? 0x0000000000100000);
writer.WriteUInt64(options.SizeOfHeapCommit ?? 0x0000000000002000);
writer.WriteUInt32(options.LoaderFlags ?? 0x00000000);
writer.WriteUInt32(options.NumberOfRvaAndSizes ?? 0x00000010);
}
writer.WriteDataDirectory(ExportDirectory);
writer.WriteDataDirectory(ImportDirectory);
writer.WriteDataDirectory(Win32Resources);
writer.WriteDataDirectory(null); // Exception table
writer.WriteDataDirectory(null); // Certificate table
writer.WriteDataDirectory(RelocDirectory);
writer.WriteDebugDirectory(DebugDirectory);
writer.WriteDataDirectory(null); // Architecture-specific data
writer.WriteDataDirectory(null); // Global pointer register RVA
writer.WriteDataDirectory(null); // Thread local storage
writer.WriteDataDirectory(null); // Load configuration table
writer.WriteDataDirectory(null); // Bound import table
writer.WriteDataDirectory(ImportAddressTable);
writer.WriteDataDirectory(null); // Delay import descriptor
writer.WriteDataDirectory(ImageCor20Header);
writer.WriteDataDirectory(null); // Reserved
// Sections
uint rva = Utils.AlignUp(sectionSizes.SizeOfHeaders, sectionAlignment);
int emptySections = 0;
foreach (var section in sections) {
if (section.GetVirtualSize() != 0)
rva += section.WriteHeaderTo(writer, fileAlignment, sectionAlignment, rva);
else
emptySections++;
}
if (emptySections != 0)
writer.Position += emptySections * 0x28;
}
///
/// Calculates the PE checksum and writes it to the checksum field
///
/// Writer
/// Length of PE file
public void WriteCheckSum(DataWriter writer, long length) {
writer.Position = startOffset;
uint checkSum = writer.InternalStream.CalculatePECheckSum(length, checkSumOffset);
writer.Position = checkSumOffset;
writer.WriteUInt32(checkSum);
}
Machine GetMachine() => options.Machine ?? Machine.I386;
bool Use32BitOptionalHeader() => !GetMachine().Is64Bit();
Characteristics GetCharacteristics() {
var chr = options.Characteristics ?? GetDefaultCharacteristics();
if (IsExeFile)
chr &= ~Characteristics.Dll;
else
chr |= Characteristics.Dll;
return chr;
}
Characteristics GetDefaultCharacteristics() {
if (Use32BitOptionalHeader())
return Characteristics.Bit32Machine | Characteristics.ExecutableImage;
return Characteristics.ExecutableImage | Characteristics.LargeAddressAware;
}
}
}