861 lines
24 KiB
C#
861 lines
24 KiB
C#
|
//
|
||
|
// Author:
|
||
|
// Jb Evain (jbevain@gmail.com)
|
||
|
//
|
||
|
// Copyright (c) 2008 - 2015 Jb Evain
|
||
|
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||
|
//
|
||
|
// Licensed under the MIT/X11 license.
|
||
|
//
|
||
|
|
||
|
using MonoFN.Cecil.Cil;
|
||
|
using MonoFN.Cecil.Metadata;
|
||
|
using System;
|
||
|
using System.IO;
|
||
|
using RVA = System.UInt32;
|
||
|
|
||
|
namespace MonoFN.Cecil.PE {
|
||
|
|
||
|
sealed class ImageWriter : BinaryStreamWriter {
|
||
|
|
||
|
readonly ModuleDefinition module;
|
||
|
readonly MetadataBuilder metadata;
|
||
|
readonly TextMap text_map;
|
||
|
readonly internal Disposable<Stream> stream;
|
||
|
|
||
|
readonly string runtime_version;
|
||
|
|
||
|
ImageDebugHeader debug_header;
|
||
|
|
||
|
ByteBuffer win32_resources;
|
||
|
|
||
|
const uint pe_header_size = 0x98u;
|
||
|
const uint section_header_size = 0x28u;
|
||
|
const uint file_alignment = 0x200;
|
||
|
const uint section_alignment = 0x2000;
|
||
|
const ulong image_base = 0x00400000;
|
||
|
|
||
|
internal const RVA text_rva = 0x2000;
|
||
|
|
||
|
readonly bool pe64;
|
||
|
readonly bool has_reloc;
|
||
|
|
||
|
internal Section text;
|
||
|
internal Section rsrc;
|
||
|
internal Section reloc;
|
||
|
|
||
|
ushort sections;
|
||
|
|
||
|
ImageWriter (ModuleDefinition module, string runtime_version, MetadataBuilder metadata, Disposable<Stream> stream, bool metadataOnly = false)
|
||
|
: base (stream.value)
|
||
|
{
|
||
|
this.module = module;
|
||
|
this.runtime_version = runtime_version;
|
||
|
this.text_map = metadata.text_map;
|
||
|
this.stream = stream;
|
||
|
this.metadata = metadata;
|
||
|
if (metadataOnly)
|
||
|
return;
|
||
|
|
||
|
this.pe64 = module.Architecture == TargetArchitecture.AMD64 || module.Architecture == TargetArchitecture.IA64 || module.Architecture == TargetArchitecture.ARM64;
|
||
|
this.has_reloc = module.Architecture == TargetArchitecture.I386;
|
||
|
this.GetDebugHeader ();
|
||
|
this.GetWin32Resources ();
|
||
|
this.BuildTextMap ();
|
||
|
this.sections = (ushort)(has_reloc ? 2 : 1); // text + reloc?
|
||
|
}
|
||
|
|
||
|
void GetDebugHeader ()
|
||
|
{
|
||
|
var symbol_writer = metadata.symbol_writer;
|
||
|
if (symbol_writer != null)
|
||
|
debug_header = symbol_writer.GetDebugHeader ();
|
||
|
|
||
|
if (module.HasDebugHeader) {
|
||
|
var header = module.GetDebugHeader ();
|
||
|
var deterministic = header.GetDeterministicEntry ();
|
||
|
if (deterministic == null)
|
||
|
return;
|
||
|
|
||
|
debug_header = debug_header.AddDeterministicEntry ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GetWin32Resources ()
|
||
|
{
|
||
|
if (!module.HasImage)
|
||
|
return;
|
||
|
|
||
|
DataDirectory win32_resources_directory = module.Image.Win32Resources;
|
||
|
var size = win32_resources_directory.Size;
|
||
|
|
||
|
if (size > 0) {
|
||
|
win32_resources = module.Image.GetReaderAt (win32_resources_directory.VirtualAddress, size, (s, reader) => new ByteBuffer (reader.ReadBytes ((int)s)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static ImageWriter CreateWriter (ModuleDefinition module, MetadataBuilder metadata, Disposable<Stream> stream)
|
||
|
{
|
||
|
var writer = new ImageWriter (module, module.runtime_version, metadata, stream);
|
||
|
writer.BuildSections ();
|
||
|
return writer;
|
||
|
}
|
||
|
|
||
|
public static ImageWriter CreateDebugWriter (ModuleDefinition module, MetadataBuilder metadata, Disposable<Stream> stream)
|
||
|
{
|
||
|
var writer = new ImageWriter (module, "PDB v1.0", metadata, stream, metadataOnly: true);
|
||
|
var length = metadata.text_map.GetLength ();
|
||
|
writer.text = new Section { SizeOfRawData = length, VirtualSize = length };
|
||
|
return writer;
|
||
|
}
|
||
|
|
||
|
void BuildSections ()
|
||
|
{
|
||
|
var has_win32_resources = win32_resources != null;
|
||
|
if (has_win32_resources)
|
||
|
sections++;
|
||
|
|
||
|
text = CreateSection (".text", text_map.GetLength (), null);
|
||
|
var previous = text;
|
||
|
|
||
|
if (has_win32_resources) {
|
||
|
rsrc = CreateSection (".rsrc", (uint)win32_resources.length, previous);
|
||
|
|
||
|
PatchWin32Resources (win32_resources);
|
||
|
previous = rsrc;
|
||
|
}
|
||
|
|
||
|
if (has_reloc)
|
||
|
reloc = CreateSection (".reloc", 12u, previous);
|
||
|
}
|
||
|
|
||
|
Section CreateSection (string name, uint size, Section previous)
|
||
|
{
|
||
|
return new Section {
|
||
|
Name = name,
|
||
|
VirtualAddress = previous != null
|
||
|
? previous.VirtualAddress + Align (previous.VirtualSize, section_alignment)
|
||
|
: text_rva,
|
||
|
VirtualSize = size,
|
||
|
PointerToRawData = previous != null
|
||
|
? previous.PointerToRawData + previous.SizeOfRawData
|
||
|
: Align (GetHeaderSize (), file_alignment),
|
||
|
SizeOfRawData = Align (size, file_alignment)
|
||
|
};
|
||
|
}
|
||
|
|
||
|
static uint Align (uint value, uint align)
|
||
|
{
|
||
|
align--;
|
||
|
return (value + align) & ~align;
|
||
|
}
|
||
|
|
||
|
void WriteDOSHeader ()
|
||
|
{
|
||
|
Write (new byte [] {
|
||
|
// dos header start
|
||
|
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,
|
||
|
// lfanew
|
||
|
0x80, 0x00, 0x00, 0x00,
|
||
|
// dos header end
|
||
|
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
|
||
|
});
|
||
|
}
|
||
|
|
||
|
ushort SizeOfOptionalHeader ()
|
||
|
{
|
||
|
return (ushort)(!pe64 ? 0xe0 : 0xf0);
|
||
|
}
|
||
|
|
||
|
void WritePEFileHeader ()
|
||
|
{
|
||
|
WriteUInt32 (0x00004550); // Magic
|
||
|
WriteUInt16 ((ushort)module.Architecture); // Machine
|
||
|
WriteUInt16 (sections); // NumberOfSections
|
||
|
WriteUInt32 (metadata.timestamp);
|
||
|
WriteUInt32 (0); // PointerToSymbolTable
|
||
|
WriteUInt32 (0); // NumberOfSymbols
|
||
|
WriteUInt16 (SizeOfOptionalHeader ()); // SizeOfOptionalHeader
|
||
|
|
||
|
const ushort LargeAddressAware = 0x0020;
|
||
|
|
||
|
// ExecutableImage | (!pe64 ? 32BitsMachine : LargeAddressAware)
|
||
|
var characteristics = (ushort)(0x0002 | (!pe64 ? 0x0100 : LargeAddressAware));
|
||
|
if (module.Kind == ModuleKind.Dll || module.Kind == ModuleKind.NetModule)
|
||
|
characteristics |= 0x2000;
|
||
|
|
||
|
if (module.Image != null && (module.Image.Characteristics & LargeAddressAware) != 0)
|
||
|
characteristics |= LargeAddressAware;
|
||
|
|
||
|
WriteUInt16 (characteristics); // Characteristics
|
||
|
}
|
||
|
|
||
|
Section LastSection ()
|
||
|
{
|
||
|
if (reloc != null)
|
||
|
return reloc;
|
||
|
|
||
|
if (rsrc != null)
|
||
|
return rsrc;
|
||
|
|
||
|
return text;
|
||
|
}
|
||
|
|
||
|
void WriteOptionalHeaders ()
|
||
|
{
|
||
|
WriteUInt16 ((ushort)(!pe64 ? 0x10b : 0x20b)); // Magic
|
||
|
WriteUInt16 (module.linker_version);
|
||
|
WriteUInt32 (text.SizeOfRawData); // CodeSize
|
||
|
WriteUInt32 ((reloc != null ? reloc.SizeOfRawData : 0)
|
||
|
+ (rsrc != null ? rsrc.SizeOfRawData : 0)); // InitializedDataSize
|
||
|
WriteUInt32 (0); // UninitializedDataSize
|
||
|
|
||
|
var startub_stub = text_map.GetRange (TextSegment.StartupStub);
|
||
|
WriteUInt32 (startub_stub.Length > 0 ? startub_stub.Start : 0); // EntryPointRVA
|
||
|
WriteUInt32 (text_rva); // BaseOfCode
|
||
|
|
||
|
if (!pe64) {
|
||
|
WriteUInt32 (0); // BaseOfData
|
||
|
WriteUInt32 ((uint)image_base); // ImageBase
|
||
|
} else {
|
||
|
WriteUInt64 (image_base); // ImageBase
|
||
|
}
|
||
|
|
||
|
WriteUInt32 (section_alignment); // SectionAlignment
|
||
|
WriteUInt32 (file_alignment); // FileAlignment
|
||
|
|
||
|
WriteUInt16 (4); // OSMajor
|
||
|
WriteUInt16 (0); // OSMinor
|
||
|
WriteUInt16 (0); // UserMajor
|
||
|
WriteUInt16 (0); // UserMinor
|
||
|
WriteUInt16 (module.subsystem_major); // SubSysMajor
|
||
|
WriteUInt16 (module.subsystem_minor); // SubSysMinor
|
||
|
WriteUInt32 (0); // Reserved
|
||
|
|
||
|
var last_section = LastSection ();
|
||
|
WriteUInt32 (last_section.VirtualAddress + Align (last_section.VirtualSize, section_alignment)); // ImageSize
|
||
|
WriteUInt32 (text.PointerToRawData); // HeaderSize
|
||
|
|
||
|
WriteUInt32 (0); // Checksum
|
||
|
WriteUInt16 (GetSubSystem ()); // SubSystem
|
||
|
WriteUInt16 ((ushort)module.Characteristics); // DLLFlags
|
||
|
|
||
|
if (!pe64) {
|
||
|
const uint stack_reserve = 0x100000;
|
||
|
const uint stack_commit = 0x1000;
|
||
|
const uint heap_reserve = 0x100000;
|
||
|
const uint heap_commit = 0x1000;
|
||
|
|
||
|
WriteUInt32 (stack_reserve);
|
||
|
WriteUInt32 (stack_commit);
|
||
|
WriteUInt32 (heap_reserve);
|
||
|
WriteUInt32 (heap_commit);
|
||
|
} else {
|
||
|
const ulong stack_reserve = 0x400000;
|
||
|
const ulong stack_commit = 0x4000;
|
||
|
const ulong heap_reserve = 0x100000;
|
||
|
const ulong heap_commit = 0x2000;
|
||
|
|
||
|
WriteUInt64 (stack_reserve);
|
||
|
WriteUInt64 (stack_commit);
|
||
|
WriteUInt64 (heap_reserve);
|
||
|
WriteUInt64 (heap_commit);
|
||
|
}
|
||
|
|
||
|
WriteUInt32 (0); // LoaderFlags
|
||
|
WriteUInt32 (16); // NumberOfDataDir
|
||
|
|
||
|
WriteZeroDataDirectory (); // ExportTable
|
||
|
WriteDataDirectory (text_map.GetDataDirectory (TextSegment.ImportDirectory)); // ImportTable
|
||
|
if (rsrc != null) { // ResourceTable
|
||
|
WriteUInt32 (rsrc.VirtualAddress);
|
||
|
WriteUInt32 (rsrc.VirtualSize);
|
||
|
} else
|
||
|
WriteZeroDataDirectory ();
|
||
|
|
||
|
WriteZeroDataDirectory (); // ExceptionTable
|
||
|
WriteZeroDataDirectory (); // CertificateTable
|
||
|
WriteUInt32 (reloc != null ? reloc.VirtualAddress : 0); // BaseRelocationTable
|
||
|
WriteUInt32 (reloc != null ? reloc.VirtualSize : 0);
|
||
|
|
||
|
if (text_map.GetLength (TextSegment.DebugDirectory) > 0) {
|
||
|
WriteUInt32 (text_map.GetRVA (TextSegment.DebugDirectory));
|
||
|
WriteUInt32 ((uint)(debug_header.Entries.Length * ImageDebugDirectory.Size));
|
||
|
} else
|
||
|
WriteZeroDataDirectory ();
|
||
|
|
||
|
WriteZeroDataDirectory (); // Copyright
|
||
|
WriteZeroDataDirectory (); // GlobalPtr
|
||
|
WriteZeroDataDirectory (); // TLSTable
|
||
|
WriteZeroDataDirectory (); // LoadConfigTable
|
||
|
WriteZeroDataDirectory (); // BoundImport
|
||
|
WriteDataDirectory (text_map.GetDataDirectory (TextSegment.ImportAddressTable)); // IAT
|
||
|
WriteZeroDataDirectory (); // DelayImportDesc
|
||
|
WriteDataDirectory (text_map.GetDataDirectory (TextSegment.CLIHeader)); // CLIHeader
|
||
|
WriteZeroDataDirectory (); // Reserved
|
||
|
}
|
||
|
|
||
|
void WriteZeroDataDirectory ()
|
||
|
{
|
||
|
WriteUInt32 (0);
|
||
|
WriteUInt32 (0);
|
||
|
}
|
||
|
|
||
|
ushort GetSubSystem ()
|
||
|
{
|
||
|
switch (module.Kind) {
|
||
|
case ModuleKind.Console:
|
||
|
case ModuleKind.Dll:
|
||
|
case ModuleKind.NetModule:
|
||
|
return 0x3;
|
||
|
case ModuleKind.Windows:
|
||
|
return 0x2;
|
||
|
default:
|
||
|
throw new ArgumentOutOfRangeException ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WriteSectionHeaders ()
|
||
|
{
|
||
|
WriteSection (text, 0x60000020);
|
||
|
|
||
|
if (rsrc != null)
|
||
|
WriteSection (rsrc, 0x40000040);
|
||
|
|
||
|
if (reloc != null)
|
||
|
WriteSection (reloc, 0x42000040);
|
||
|
}
|
||
|
|
||
|
void WriteSection (Section section, uint characteristics)
|
||
|
{
|
||
|
var name = new byte [8];
|
||
|
var sect_name = section.Name;
|
||
|
for (int i = 0; i < sect_name.Length; i++)
|
||
|
name [i] = (byte)sect_name [i];
|
||
|
|
||
|
WriteBytes (name);
|
||
|
WriteUInt32 (section.VirtualSize);
|
||
|
WriteUInt32 (section.VirtualAddress);
|
||
|
WriteUInt32 (section.SizeOfRawData);
|
||
|
WriteUInt32 (section.PointerToRawData);
|
||
|
WriteUInt32 (0); // PointerToRelocations
|
||
|
WriteUInt32 (0); // PointerToLineNumbers
|
||
|
WriteUInt16 (0); // NumberOfRelocations
|
||
|
WriteUInt16 (0); // NumberOfLineNumbers
|
||
|
WriteUInt32 (characteristics);
|
||
|
}
|
||
|
|
||
|
uint GetRVAFileOffset (Section section, RVA rva)
|
||
|
{
|
||
|
return section.PointerToRawData + rva - section.VirtualAddress;
|
||
|
}
|
||
|
|
||
|
void MoveTo (uint pointer)
|
||
|
{
|
||
|
BaseStream.Seek (pointer, SeekOrigin.Begin);
|
||
|
}
|
||
|
|
||
|
void MoveToRVA (Section section, RVA rva)
|
||
|
{
|
||
|
BaseStream.Seek (GetRVAFileOffset (section, rva), SeekOrigin.Begin);
|
||
|
}
|
||
|
|
||
|
void MoveToRVA (TextSegment segment)
|
||
|
{
|
||
|
MoveToRVA (text, text_map.GetRVA (segment));
|
||
|
}
|
||
|
|
||
|
void WriteRVA (RVA rva)
|
||
|
{
|
||
|
if (!pe64)
|
||
|
WriteUInt32 (rva);
|
||
|
else
|
||
|
WriteUInt64 (rva);
|
||
|
}
|
||
|
|
||
|
void PrepareSection (Section section)
|
||
|
{
|
||
|
MoveTo (section.PointerToRawData);
|
||
|
|
||
|
const int buffer_size = 4096;
|
||
|
|
||
|
if (section.SizeOfRawData <= buffer_size) {
|
||
|
Write (new byte [section.SizeOfRawData]);
|
||
|
MoveTo (section.PointerToRawData);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var written = 0;
|
||
|
var buffer = new byte [buffer_size];
|
||
|
while (written != section.SizeOfRawData) {
|
||
|
var write_size = System.Math.Min ((int)section.SizeOfRawData - written, buffer_size);
|
||
|
Write (buffer, 0, write_size);
|
||
|
written += write_size;
|
||
|
}
|
||
|
|
||
|
MoveTo (section.PointerToRawData);
|
||
|
}
|
||
|
|
||
|
void WriteText ()
|
||
|
{
|
||
|
PrepareSection (text);
|
||
|
|
||
|
// ImportAddressTable
|
||
|
|
||
|
if (has_reloc) {
|
||
|
WriteRVA (text_map.GetRVA (TextSegment.ImportHintNameTable));
|
||
|
WriteRVA (0);
|
||
|
}
|
||
|
|
||
|
// CLIHeader
|
||
|
|
||
|
WriteUInt32 (0x48);
|
||
|
WriteUInt16 (2);
|
||
|
WriteUInt16 ((ushort)((module.Runtime <= TargetRuntime.Net_1_1) ? 0 : 5));
|
||
|
|
||
|
WriteUInt32 (text_map.GetRVA (TextSegment.MetadataHeader));
|
||
|
WriteUInt32 (GetMetadataLength ());
|
||
|
WriteUInt32 ((uint)module.Attributes);
|
||
|
WriteUInt32 (metadata.entry_point.ToUInt32 ());
|
||
|
WriteDataDirectory (text_map.GetDataDirectory (TextSegment.Resources));
|
||
|
WriteDataDirectory (text_map.GetDataDirectory (TextSegment.StrongNameSignature));
|
||
|
WriteZeroDataDirectory (); // CodeManagerTable
|
||
|
WriteZeroDataDirectory (); // VTableFixups
|
||
|
WriteZeroDataDirectory (); // ExportAddressTableJumps
|
||
|
WriteZeroDataDirectory (); // ManagedNativeHeader
|
||
|
|
||
|
// Code
|
||
|
|
||
|
MoveToRVA (TextSegment.Code);
|
||
|
WriteBuffer (metadata.code);
|
||
|
|
||
|
// Resources
|
||
|
|
||
|
MoveToRVA (TextSegment.Resources);
|
||
|
WriteBuffer (metadata.resources);
|
||
|
|
||
|
// Data
|
||
|
|
||
|
if (metadata.data.length > 0) {
|
||
|
MoveToRVA (TextSegment.Data);
|
||
|
WriteBuffer (metadata.data);
|
||
|
}
|
||
|
|
||
|
// StrongNameSignature
|
||
|
// stays blank
|
||
|
|
||
|
// MetadataHeader
|
||
|
|
||
|
MoveToRVA (TextSegment.MetadataHeader);
|
||
|
WriteMetadataHeader ();
|
||
|
|
||
|
WriteMetadata ();
|
||
|
|
||
|
// DebugDirectory
|
||
|
if (text_map.GetLength (TextSegment.DebugDirectory) > 0) {
|
||
|
MoveToRVA (TextSegment.DebugDirectory);
|
||
|
WriteDebugDirectory ();
|
||
|
}
|
||
|
|
||
|
if (!has_reloc)
|
||
|
return;
|
||
|
|
||
|
// ImportDirectory
|
||
|
MoveToRVA (TextSegment.ImportDirectory);
|
||
|
WriteImportDirectory ();
|
||
|
|
||
|
// StartupStub
|
||
|
MoveToRVA (TextSegment.StartupStub);
|
||
|
WriteStartupStub ();
|
||
|
}
|
||
|
|
||
|
uint GetMetadataLength ()
|
||
|
{
|
||
|
return text_map.GetRVA (TextSegment.DebugDirectory) - text_map.GetRVA (TextSegment.MetadataHeader);
|
||
|
}
|
||
|
|
||
|
public void WriteMetadataHeader ()
|
||
|
{
|
||
|
WriteUInt32 (0x424a5342); // Signature
|
||
|
WriteUInt16 (1); // MajorVersion
|
||
|
WriteUInt16 (1); // MinorVersion
|
||
|
WriteUInt32 (0); // Reserved
|
||
|
|
||
|
var version = GetZeroTerminatedString (runtime_version);
|
||
|
WriteUInt32 ((uint)version.Length);
|
||
|
WriteBytes (version);
|
||
|
WriteUInt16 (0); // Flags
|
||
|
WriteUInt16 (GetStreamCount ());
|
||
|
|
||
|
uint offset = text_map.GetRVA (TextSegment.TableHeap) - text_map.GetRVA (TextSegment.MetadataHeader);
|
||
|
|
||
|
WriteStreamHeader (ref offset, TextSegment.TableHeap, "#~");
|
||
|
WriteStreamHeader (ref offset, TextSegment.StringHeap, "#Strings");
|
||
|
WriteStreamHeader (ref offset, TextSegment.UserStringHeap, "#US");
|
||
|
WriteStreamHeader (ref offset, TextSegment.GuidHeap, "#GUID");
|
||
|
WriteStreamHeader (ref offset, TextSegment.BlobHeap, "#Blob");
|
||
|
WriteStreamHeader (ref offset, TextSegment.PdbHeap, "#Pdb");
|
||
|
}
|
||
|
|
||
|
ushort GetStreamCount ()
|
||
|
{
|
||
|
return (ushort)(
|
||
|
1 // #~
|
||
|
+ 1 // #Strings
|
||
|
+ (metadata.user_string_heap.IsEmpty ? 0 : 1) // #US
|
||
|
+ (metadata.guid_heap.IsEmpty ? 0 : 1) // GUID
|
||
|
+ (metadata.blob_heap.IsEmpty ? 0 : 1)
|
||
|
+ (metadata.pdb_heap == null ? 0 : 1)); // #Blob
|
||
|
}
|
||
|
|
||
|
void WriteStreamHeader (ref uint offset, TextSegment heap, string name)
|
||
|
{
|
||
|
var length = (uint)text_map.GetLength (heap);
|
||
|
if (length == 0)
|
||
|
return;
|
||
|
|
||
|
WriteUInt32 (offset);
|
||
|
WriteUInt32 (length);
|
||
|
WriteBytes (GetZeroTerminatedString (name));
|
||
|
offset += length;
|
||
|
}
|
||
|
|
||
|
static int GetZeroTerminatedStringLength (string @string)
|
||
|
{
|
||
|
return (@string.Length + 1 + 3) & ~3;
|
||
|
}
|
||
|
|
||
|
static byte [] GetZeroTerminatedString (string @string)
|
||
|
{
|
||
|
return GetString (@string, GetZeroTerminatedStringLength (@string));
|
||
|
}
|
||
|
|
||
|
static byte [] GetSimpleString (string @string)
|
||
|
{
|
||
|
return GetString (@string, @string.Length);
|
||
|
}
|
||
|
|
||
|
static byte [] GetString (string @string, int length)
|
||
|
{
|
||
|
var bytes = new byte [length];
|
||
|
for (int i = 0; i < @string.Length; i++)
|
||
|
bytes [i] = (byte)@string [i];
|
||
|
|
||
|
return bytes;
|
||
|
}
|
||
|
|
||
|
public void WriteMetadata ()
|
||
|
{
|
||
|
WriteHeap (TextSegment.TableHeap, metadata.table_heap);
|
||
|
WriteHeap (TextSegment.StringHeap, metadata.string_heap);
|
||
|
WriteHeap (TextSegment.UserStringHeap, metadata.user_string_heap);
|
||
|
WriteHeap (TextSegment.GuidHeap, metadata.guid_heap);
|
||
|
WriteHeap (TextSegment.BlobHeap, metadata.blob_heap);
|
||
|
WriteHeap (TextSegment.PdbHeap, metadata.pdb_heap);
|
||
|
}
|
||
|
|
||
|
void WriteHeap (TextSegment heap, HeapBuffer buffer)
|
||
|
{
|
||
|
if (buffer == null || buffer.IsEmpty)
|
||
|
return;
|
||
|
|
||
|
MoveToRVA (heap);
|
||
|
WriteBuffer (buffer);
|
||
|
}
|
||
|
|
||
|
void WriteDebugDirectory ()
|
||
|
{
|
||
|
var data_start = (int)BaseStream.Position + (debug_header.Entries.Length * ImageDebugDirectory.Size);
|
||
|
|
||
|
for (var i = 0; i < debug_header.Entries.Length; i++) {
|
||
|
var entry = debug_header.Entries [i];
|
||
|
var directory = entry.Directory;
|
||
|
WriteInt32 (directory.Characteristics);
|
||
|
WriteInt32 (directory.TimeDateStamp);
|
||
|
WriteInt16 (directory.MajorVersion);
|
||
|
WriteInt16 (directory.MinorVersion);
|
||
|
WriteInt32 ((int)directory.Type);
|
||
|
WriteInt32 (directory.SizeOfData);
|
||
|
WriteInt32 (directory.AddressOfRawData);
|
||
|
WriteInt32 (data_start);
|
||
|
|
||
|
data_start += entry.Data.Length;
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < debug_header.Entries.Length; i++) {
|
||
|
var entry = debug_header.Entries [i];
|
||
|
WriteBytes (entry.Data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WriteImportDirectory ()
|
||
|
{
|
||
|
WriteUInt32 (text_map.GetRVA (TextSegment.ImportDirectory) + 40); // ImportLookupTable
|
||
|
WriteUInt32 (0); // DateTimeStamp
|
||
|
WriteUInt32 (0); // ForwarderChain
|
||
|
WriteUInt32 (text_map.GetRVA (TextSegment.ImportHintNameTable) + 14);
|
||
|
WriteUInt32 (text_map.GetRVA (TextSegment.ImportAddressTable));
|
||
|
Advance (20);
|
||
|
|
||
|
// ImportLookupTable
|
||
|
WriteUInt32 (text_map.GetRVA (TextSegment.ImportHintNameTable));
|
||
|
|
||
|
// ImportHintNameTable
|
||
|
MoveToRVA (TextSegment.ImportHintNameTable);
|
||
|
|
||
|
WriteUInt16 (0); // Hint
|
||
|
WriteBytes (GetRuntimeMain ());
|
||
|
WriteByte (0);
|
||
|
WriteBytes (GetSimpleString ("mscoree.dll"));
|
||
|
WriteUInt16 (0);
|
||
|
}
|
||
|
|
||
|
byte [] GetRuntimeMain ()
|
||
|
{
|
||
|
return module.Kind == ModuleKind.Dll || module.Kind == ModuleKind.NetModule
|
||
|
? GetSimpleString ("_CorDllMain")
|
||
|
: GetSimpleString ("_CorExeMain");
|
||
|
}
|
||
|
|
||
|
void WriteStartupStub ()
|
||
|
{
|
||
|
switch (module.Architecture) {
|
||
|
case TargetArchitecture.I386:
|
||
|
WriteUInt16 (0x25ff);
|
||
|
WriteUInt32 ((uint)image_base + text_map.GetRVA (TextSegment.ImportAddressTable));
|
||
|
return;
|
||
|
default:
|
||
|
throw new NotSupportedException ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WriteRsrc ()
|
||
|
{
|
||
|
PrepareSection (rsrc);
|
||
|
WriteBuffer (win32_resources);
|
||
|
}
|
||
|
|
||
|
void WriteReloc ()
|
||
|
{
|
||
|
PrepareSection (reloc);
|
||
|
|
||
|
var reloc_rva = text_map.GetRVA (TextSegment.StartupStub);
|
||
|
reloc_rva += module.Architecture == TargetArchitecture.IA64 ? 0x20u : 2;
|
||
|
var page_rva = reloc_rva & ~0xfffu;
|
||
|
|
||
|
WriteUInt32 (page_rva); // PageRVA
|
||
|
WriteUInt32 (0x000c); // Block Size
|
||
|
|
||
|
switch (module.Architecture) {
|
||
|
case TargetArchitecture.I386:
|
||
|
WriteUInt32 (0x3000 + reloc_rva - page_rva);
|
||
|
break;
|
||
|
default:
|
||
|
throw new NotSupportedException ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void WriteImage ()
|
||
|
{
|
||
|
WriteDOSHeader ();
|
||
|
WritePEFileHeader ();
|
||
|
WriteOptionalHeaders ();
|
||
|
WriteSectionHeaders ();
|
||
|
WriteText ();
|
||
|
if (rsrc != null)
|
||
|
WriteRsrc ();
|
||
|
if (reloc != null)
|
||
|
WriteReloc ();
|
||
|
Flush ();
|
||
|
}
|
||
|
|
||
|
void BuildTextMap ()
|
||
|
{
|
||
|
var map = text_map;
|
||
|
|
||
|
map.AddMap (TextSegment.Code, metadata.code.length, !pe64 ? 4 : 16);
|
||
|
map.AddMap (TextSegment.Resources, metadata.resources.length, 8);
|
||
|
map.AddMap (TextSegment.Data, metadata.data.length, 4);
|
||
|
if (metadata.data.length > 0)
|
||
|
metadata.table_heap.FixupData (map.GetRVA (TextSegment.Data));
|
||
|
map.AddMap (TextSegment.StrongNameSignature, GetStrongNameLength (), 4);
|
||
|
|
||
|
BuildMetadataTextMap ();
|
||
|
|
||
|
int debug_dir_len = 0;
|
||
|
if (debug_header != null && debug_header.HasEntries) {
|
||
|
var directories_len = debug_header.Entries.Length * ImageDebugDirectory.Size;
|
||
|
var data_address = (int)map.GetNextRVA (TextSegment.BlobHeap) + directories_len;
|
||
|
var data_len = 0;
|
||
|
|
||
|
for (var i = 0; i < debug_header.Entries.Length; i++) {
|
||
|
var entry = debug_header.Entries [i];
|
||
|
var directory = entry.Directory;
|
||
|
|
||
|
directory.AddressOfRawData = entry.Data.Length == 0 ? 0 : data_address;
|
||
|
entry.Directory = directory;
|
||
|
|
||
|
data_len += entry.Data.Length;
|
||
|
data_address += data_len;
|
||
|
}
|
||
|
|
||
|
debug_dir_len = directories_len + data_len;
|
||
|
}
|
||
|
|
||
|
map.AddMap (TextSegment.DebugDirectory, debug_dir_len, 4);
|
||
|
|
||
|
if (!has_reloc) {
|
||
|
var start = map.GetNextRVA (TextSegment.DebugDirectory);
|
||
|
map.AddMap (TextSegment.ImportDirectory, new Range (start, 0));
|
||
|
map.AddMap (TextSegment.ImportHintNameTable, new Range (start, 0));
|
||
|
map.AddMap (TextSegment.StartupStub, new Range (start, 0));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RVA import_dir_rva = map.GetNextRVA (TextSegment.DebugDirectory);
|
||
|
RVA import_hnt_rva = import_dir_rva + 48u;
|
||
|
import_hnt_rva = (import_hnt_rva + 15u) & ~15u;
|
||
|
uint import_dir_len = (import_hnt_rva - import_dir_rva) + 27u;
|
||
|
|
||
|
RVA startup_stub_rva = import_dir_rva + import_dir_len;
|
||
|
startup_stub_rva = module.Architecture == TargetArchitecture.IA64
|
||
|
? (startup_stub_rva + 15u) & ~15u
|
||
|
: 2 + ((startup_stub_rva + 3u) & ~3u);
|
||
|
|
||
|
map.AddMap (TextSegment.ImportDirectory, new Range (import_dir_rva, import_dir_len));
|
||
|
map.AddMap (TextSegment.ImportHintNameTable, new Range (import_hnt_rva, 0));
|
||
|
map.AddMap (TextSegment.StartupStub, new Range (startup_stub_rva, GetStartupStubLength ()));
|
||
|
}
|
||
|
|
||
|
public void BuildMetadataTextMap ()
|
||
|
{
|
||
|
var map = text_map;
|
||
|
|
||
|
map.AddMap (TextSegment.MetadataHeader, GetMetadataHeaderLength (module.RuntimeVersion));
|
||
|
map.AddMap (TextSegment.TableHeap, metadata.table_heap.length, 4);
|
||
|
map.AddMap (TextSegment.StringHeap, metadata.string_heap.length, 4);
|
||
|
map.AddMap (TextSegment.UserStringHeap, metadata.user_string_heap.IsEmpty ? 0 : metadata.user_string_heap.length, 4);
|
||
|
map.AddMap (TextSegment.GuidHeap, metadata.guid_heap.length, 4);
|
||
|
map.AddMap (TextSegment.BlobHeap, metadata.blob_heap.IsEmpty ? 0 : metadata.blob_heap.length, 4);
|
||
|
map.AddMap (TextSegment.PdbHeap, metadata.pdb_heap == null ? 0 : metadata.pdb_heap.length, 4);
|
||
|
}
|
||
|
|
||
|
uint GetStartupStubLength ()
|
||
|
{
|
||
|
switch (module.Architecture) {
|
||
|
case TargetArchitecture.I386:
|
||
|
return 6;
|
||
|
default:
|
||
|
throw new NotSupportedException ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int GetMetadataHeaderLength (string runtimeVersion)
|
||
|
{
|
||
|
return
|
||
|
// MetadataHeader
|
||
|
20 + GetZeroTerminatedStringLength (runtimeVersion)
|
||
|
// #~ header
|
||
|
+ 12
|
||
|
// #Strings header
|
||
|
+ 20
|
||
|
// #US header
|
||
|
+ (metadata.user_string_heap.IsEmpty ? 0 : 12)
|
||
|
// #GUID header
|
||
|
+ 16
|
||
|
// #Blob header
|
||
|
+ (metadata.blob_heap.IsEmpty ? 0 : 16)
|
||
|
//
|
||
|
+ (metadata.pdb_heap == null ? 0 : 16);
|
||
|
}
|
||
|
|
||
|
int GetStrongNameLength ()
|
||
|
{
|
||
|
if (module.kind == ModuleKind.NetModule || module.Assembly == null)
|
||
|
return 0;
|
||
|
|
||
|
var public_key = module.Assembly.Name.PublicKey;
|
||
|
if (public_key.IsNullOrEmpty ())
|
||
|
return 0;
|
||
|
|
||
|
// in fx 2.0 the key may be from 384 to 16384 bits
|
||
|
// so we must calculate the signature size based on
|
||
|
// the size of the public key (minus the 32 byte header)
|
||
|
int size = public_key.Length;
|
||
|
if (size > 32)
|
||
|
return size - 32;
|
||
|
|
||
|
// note: size == 16 for the ECMA "key" which is replaced
|
||
|
// by the runtime with a 1024 bits key (128 bytes)
|
||
|
|
||
|
return 128; // default strongname signature size
|
||
|
}
|
||
|
|
||
|
public DataDirectory GetStrongNameSignatureDirectory ()
|
||
|
{
|
||
|
return text_map.GetDataDirectory (TextSegment.StrongNameSignature);
|
||
|
}
|
||
|
|
||
|
public uint GetHeaderSize ()
|
||
|
{
|
||
|
return pe_header_size + SizeOfOptionalHeader () + (sections * section_header_size);
|
||
|
}
|
||
|
|
||
|
void PatchWin32Resources (ByteBuffer resources)
|
||
|
{
|
||
|
PatchResourceDirectoryTable (resources);
|
||
|
}
|
||
|
|
||
|
void PatchResourceDirectoryTable (ByteBuffer resources)
|
||
|
{
|
||
|
resources.Advance (12);
|
||
|
|
||
|
var entries = resources.ReadUInt16 () + resources.ReadUInt16 ();
|
||
|
|
||
|
for (int i = 0; i < entries; i++)
|
||
|
PatchResourceDirectoryEntry (resources);
|
||
|
}
|
||
|
|
||
|
void PatchResourceDirectoryEntry (ByteBuffer resources)
|
||
|
{
|
||
|
resources.Advance (4);
|
||
|
var child = resources.ReadUInt32 ();
|
||
|
|
||
|
var position = resources.position;
|
||
|
resources.position = (int)child & 0x7fffffff;
|
||
|
|
||
|
if ((child & 0x80000000) != 0)
|
||
|
PatchResourceDirectoryTable (resources);
|
||
|
else
|
||
|
PatchResourceDataEntry (resources);
|
||
|
|
||
|
resources.position = position;
|
||
|
}
|
||
|
|
||
|
void PatchResourceDataEntry (ByteBuffer resources)
|
||
|
{
|
||
|
var rva = resources.ReadUInt32 ();
|
||
|
resources.position -= 4;
|
||
|
|
||
|
resources.WriteUInt32 (rva - module.Image.Win32Resources.VirtualAddress + rsrc.VirtualAddress);
|
||
|
}
|
||
|
}
|
||
|
}
|