// // Copyright (c) Microsoft Corporation. All rights reserved. // using System; using System.IO; using System.Runtime.InteropServices; namespace Bartok.MSIL { public class PELoader { // Constructor Methods public PELoader(Stream stream) : this(stream, "") { } public PELoader(Stream stream, String path) { this.path = path; this.stream = stream; this.loadPEHeaders(); } // Access Methods internal Stream getStream() { return this.stream; } internal int getEntryPoint() { return this.comHeader.entryPointToken; } internal int getMetaDataOffset() { return this.VaToOffset(this.comHeader.metaData.virtualAddress); } internal int getResourceOffset() { return this.VaToOffsetSafe(this.comHeader.resources.virtualAddress); } internal int getRelocationOffset() { int limit = ntHeader.numberOfSections; for (int i = 0; i < limit; i++) { SectionHeader section = this.sectionArray[i]; if (section.name.StartsWith(".reloc")) { return this.VaToOffset(section.virtualAddress); } } return 0; } internal int getVtableFixupOffset() { return this.VaToOffsetSafe(this.comHeader.vtableFixups.virtualAddress); } internal int getDelayIATOffset() { return this.VaToOffsetSafe(this.ntHeader.dataDirectory[13].virtualAddress); } internal int getImageBase() { return ntHeader.imageBase; } internal int getResourceSize() { return this.comHeader.resources.size; } internal int getVtableFixupSize() { return this.comHeader.vtableFixups.size; } internal int getDelayIATSize() { return this.ntHeader.dataDirectory[13].size; } internal bool IsExecutableImage() { return ((this.ntHeader.characteristics & NTHeader.IMAGE_FILE_EXECUTABLE_IMAGE) != 0) && ((this.ntHeader.characteristics & NTHeader.IMAGE_FILE_DLL) == 0); } // Output Routines internal void DumpHeader(StreamWriter outputStream) { outputStream.WriteLine("// PE Header:"); outputStream.WriteLine(); this.ntHeader.DumpLimitedToStream(outputStream, "// "); this.DumpIAT(outputStream, "Import directory", ref ntHeader.dataDirectory[1]); // this.DumpIAT(outputStream, "Import Address Table", ref ntHeader.dataDirectory[12]); // this.DumpIAT(outputStream, "Delay Load Import Address Table", ref ntHeader.dataDirectory[13]); outputStream.WriteLine("// CLR Header:"); outputStream.WriteLine("// "+this.comHeader.cb.ToString("x8")+" Header size"); outputStream.WriteLine("// "+this.comHeader.majorRuntimeVersion.ToString("x8")+" Major runtime version"); outputStream.WriteLine("// "+this.comHeader.minorRuntimeVersion.ToString("x8")+" Minor runtime version"); outputStream.WriteLine("// "+this.comHeader.flags.ToString("x8")+" Flags"); outputStream.WriteLine("// "+this.comHeader.entryPointToken.ToString("x8")+" Entrypoint token"); outputStream.WriteLine("// "+this.comHeader.metaData.virtualAddress.ToString("x8")+" ["+this.comHeader.metaData.size.ToString("x8")+"] address [size] of Metadata directory"); outputStream.WriteLine("// "+this.comHeader.resources.virtualAddress.ToString("x8")+" ["+this.comHeader.resources.size.ToString("x8")+"] address [size] of Resources directory"); outputStream.WriteLine("// "+this.comHeader.strongNameSignature.virtualAddress.ToString("x8")+" ["+this.comHeader.strongNameSignature.size.ToString("x8")+"] address [size] of Strong name signature"); outputStream.WriteLine("// "+this.comHeader.codeManagerTable.virtualAddress.ToString("x8")+" ["+this.comHeader.codeManagerTable.size.ToString("x8")+"] address [size] of CodeManager table"); outputStream.WriteLine("// "+this.comHeader.vtableFixups.virtualAddress.ToString("x8")+" ["+this.comHeader.vtableFixups.size.ToString("x8")+"] address [size] of VTableFixups directory"); outputStream.WriteLine("// "+this.comHeader.exportAddressTableJumps.virtualAddress.ToString("x8")+" ["+this.comHeader.exportAddressTableJumps.size.ToString("x8")+"] address [size] of Export address table"); outputStream.WriteLine("// "+this.comHeader.managedNativeHeader.virtualAddress.ToString("x8")+" ["+this.comHeader.managedNativeHeader.size.ToString("x8")+"] address [size] of Precompile header"); } internal void DumpCodeManager(StreamWriter outputStream) { outputStream.WriteLine("// Code Manager Table"); if (this.comHeader.codeManagerTable.size == 0) { outputStream.WriteLine("// default"); return; } // BUGBUG throw new NotYetImplemented("DumpCodeManager"); } internal void DumpVTables(StreamWriter outputStream) { outputStream.WriteLine("// VTableFixup Directory:"); if (comHeader.vtableFixups.virtualAddress == 0) { outputStream.WriteLine("// No data."); return; } // BUGBUG throw new NotYetImplemented("DumpVTables"); } internal void DumpEATTable(StreamWriter outputStream) { outputStream.WriteLine("// Export Address Table Jumps:"); if (comHeader.exportAddressTableJumps.virtualAddress == 0) { outputStream.WriteLine("// No data."); return; } // BUGBUG throw new NotYetImplemented("DumpEATTable"); } internal void DumpStatistics(StreamWriter outputStream) { throw new NotYetImplemented("DumpStatistics"); } public override String ToString() { return "PELoader("+path+")"; } // Private Helper Methods private void loadPEHeaders() { // Ensure that we are at the beginning of the stream. this.stream.Seek(0L, SeekOrigin.Begin); DOSHeader dosHeader = new DOSHeader(this.stream); // Move on to the NT header this.stream.Seek(dosHeader.lfanew, SeekOrigin.Begin); BinaryReader reader = new BinaryReader(this.stream); this.ntHeader = new NTHeader(reader); // Load the sections int sectionCount = ntHeader.numberOfSections; SectionHeader[] sectionArray = new SectionHeader[sectionCount]; this.sectionArray = sectionArray; for (int i = 0; i < sectionCount; i++) { this.sectionArray[i] = new SectionHeader(reader); int startAddr = sectionArray[i].virtualAddress; int endAddr = sectionArray[i].virtualAddress+sectionArray[i].sizeOfRawData; int delta = sectionArray[i].pointerToRawData-sectionArray[i].virtualAddress; } // Load the COM/COR20 header DirectoryEntry entry = this.ntHeader.dataDirectory[COMHeader.ENTRYINDEX]; int comOffset = this.VaToOffset(entry.virtualAddress); this.stream.Seek(comOffset, SeekOrigin.Begin); this.comHeader = new COMHeader(this.stream); } internal int VaToOffset(int virtualAddress) { int limit = ntHeader.numberOfSections; for (int i = 0; i < limit; i++) { SectionHeader section = this.sectionArray[i]; if (virtualAddress >= section.virtualAddress && virtualAddress < (section.virtualAddress + section.sizeOfRawData)) { return (virtualAddress - section.virtualAddress + section.pointerToRawData); } } throw new IllegalPEFormatException("Unknown VA "+virtualAddress); } internal int VaToOffsetSafe(int virtualAddress) { int limit = ntHeader.numberOfSections; for (int i = 0; i < limit; i++) { SectionHeader section = this.sectionArray[i]; if (virtualAddress >= section.virtualAddress && virtualAddress < (section.virtualAddress + section.sizeOfRawData)) { return (virtualAddress - section.virtualAddress + section.pointerToRawData); } } return -1; } private void DumpIAT(StreamWriter outputStream, String title, ref DirectoryEntry entry) { outputStream.WriteLine("// "+title); if (entry.size == 0) { outputStream.WriteLine("// No data."); outputStream.WriteLine(); return; } if (entry.size < ImportDescriptor.SIZE) { outputStream.WriteLine("Not enough data for IMAGE_IMPORT_DESCRIPTOR"); return; } int importOffset = this.VaToOffset(entry.virtualAddress); while (true) { this.stream.Seek(importOffset, SeekOrigin.Begin); ImportDescriptor importDescriptor = new ImportDescriptor(this.stream); if (importDescriptor.firstChunk == 0) { return; } String name = null; if (importDescriptor.name != 0) { int nameOffset = this.VaToOffset(importDescriptor.name); name = this.readString(nameOffset); } outputStream.WriteLine("// "+name); importDescriptor.DumpToStream(outputStream, "// "); outputStream.WriteLine("//"); int importTableOffset = this.VaToOffset(importDescriptor.firstChunk); while (true) { this.stream.Seek(importTableOffset, SeekOrigin.Begin); BinaryReader intReader = new BinaryReader(this.stream); int importTableID = intReader.ReadInt32(); if (importTableID == 0) break; outputStream.WriteLine("importTableID is "+importTableID.ToString("x")); outputStream.Flush(); int nameStringOffset = this.VaToOffset(importTableID & 0x7fffffff); this.stream.Seek(nameStringOffset, SeekOrigin.Begin); intReader = new BinaryReader(this.stream); if ((importTableID & 0x8000000) != 0) { outputStream.WriteLine("// "+intReader.ReadInt16().ToString("x8")+" by ordinal "+(importTableID & 0x7ffffff)); } else { outputStream.WriteLine("// "+intReader.ReadInt16().ToString("x8")+" "+this.readString(nameStringOffset+2)); } importTableOffset += 4; } outputStream.WriteLine(); importOffset += ImportDescriptor.SIZE; } } internal Section[] loadSections() { Section[] sections = new Section[sectionArray.Length]; for (int i = 0; i < sectionArray.Length; i++) { Section section = new Section(sectionArray[i]); section.LoadSection(this); sections[i] = section; } return sections; } public String readString(int offset) { this.stream.Seek(offset, SeekOrigin.Begin); int length = 8; int startIndex = 0; byte[] buffer = new byte[length]; while (true) { int count = this.stream.Read(buffer, startIndex, length - startIndex); if (count == 0) { throw new Exception("Got 0 bytes from Read of file"); } int limit = startIndex + count; for (int i = startIndex; i < limit; i++) { if (buffer[i] == 0) { char[] chars = new char[i]; for (int j = 0; j < i; j++) { chars[j] = (char) buffer[j]; } return new String(chars); } } byte[] newBuffer = new byte[2*length]; for (int i = 0; i < length; i++) { newBuffer[i] = buffer[i]; } length = 2*length; startIndex += count; buffer = newBuffer; } } // Private State private String path; private Stream stream; private NTHeader ntHeader; private COMHeader comHeader; private SectionHeader[] sectionArray; // Nested classes internal class DOSHeader { // Corresponds to the WinNT IMAGE_DOS_HEADER data structure // How to read from stream internal DOSHeader(Stream stream): this(new BinaryReader(stream)) { } internal DOSHeader(BinaryReader reader) { // We could just read the magic and lfanew fields, but // let's read everything for now this.magic = reader.ReadInt16(); this.cblp = reader.ReadInt16(); this.cp = reader.ReadInt16(); this.crlc = reader.ReadInt16(); this.cparhdr = reader.ReadInt16(); this.minalloc = reader.ReadInt16(); this.maxalloc = reader.ReadInt16(); this.ss = reader.ReadInt16(); this.sp = reader.ReadInt16(); this.csum = reader.ReadInt16(); this.ip = reader.ReadInt16(); this.cs = reader.ReadInt16(); this.lfarlc = reader.ReadInt16(); this.ovno = reader.ReadInt16(); this.res_0 = reader.ReadInt16(); this.res_1 = reader.ReadInt16(); this.res_2 = reader.ReadInt16(); this.res_3 = reader.ReadInt16(); this.oemid = reader.ReadInt16(); this.oeminfo = reader.ReadInt16(); this.res2_0 = reader.ReadInt16(); this.res2_1 = reader.ReadInt16(); this.res2_2 = reader.ReadInt16(); this.res2_3 = reader.ReadInt16(); this.res2_4 = reader.ReadInt16(); this.res2_5 = reader.ReadInt16(); this.res2_6 = reader.ReadInt16(); this.res2_7 = reader.ReadInt16(); this.res2_8 = reader.ReadInt16(); this.res2_9 = reader.ReadInt16(); this.lfanew = reader.ReadInt32(); // Verify that we have a correct DOS header and a valid // pointer to the NT header if (this.magic != DOSHeader.IMAGE_DOS_SIGNATURE || this.lfanew <= 0) { throw new IllegalPEFormatException("DOS header problems"); } } // State internal short magic; // Magic number internal short cblp; // Bytes on last page of file internal short cp; // Pages in file internal short crlc; // Relocations internal short cparhdr; // Size of header in paragraphs internal short minalloc; // Minimum extra paragraphs needed internal short maxalloc; // Maximum extra paragraphs needed internal short ss; // Initial (relative) SS value internal short sp; // Initial SP value internal short csum; // Checksum internal short ip; // Initial IP value internal short cs; // Initial (relative) CS value internal short lfarlc; // File address of relocation table internal short ovno; // Overlay number internal short res_0; // Reserved words internal short res_1; internal short res_2; internal short res_3; internal short oemid; // OEM identifier (for e_oeminfo) internal short oeminfo; // OEM information; e_oemid specific internal short res2_0; // Reserved words internal short res2_1; internal short res2_2; internal short res2_3; internal short res2_4; internal short res2_5; internal short res2_6; internal short res2_7; internal short res2_8; internal short res2_9; internal int lfanew; // File address of new exe header internal const short IMAGE_DOS_SIGNATURE = 0x5A4D; } internal class NTHeader { // Corresponds to the WinNT IMAGE_NT_HEADERS data structure // How to read from stream internal NTHeader(Stream stream): this(new BinaryReader(stream)) { } internal NTHeader(BinaryReader reader) { // We could read a selection of these, but read every // field for now. this.signature = reader.ReadInt32(); this.machine = reader.ReadInt16(); this.numberOfSections = reader.ReadInt16(); this.timeDateStamp = reader.ReadInt32(); this.pointerToSymbolTable = reader.ReadInt32(); this.numberOfSymbols = reader.ReadInt32(); this.sizeOfOptionalHeader = reader.ReadInt16(); this.characteristics = reader.ReadInt16(); this.magic = reader.ReadInt16(); this.majorLinkerVersion = reader.ReadByte(); this.minorLinkerVersion = reader.ReadByte(); this.sizeOfCode = reader.ReadInt32(); this.sizeOfInitializedData = reader.ReadInt32(); this.sizeOfUninitializedData = reader.ReadInt32(); this.addressOfEntryPoint = reader.ReadInt32(); this.baseOfCode = reader.ReadInt32(); this.baseOfData = reader.ReadInt32(); this.imageBase = reader.ReadInt32(); this.sectionAlignment = reader.ReadInt32(); this.fileAlignment = reader.ReadInt32(); this.majorOperatingSystemVersion = reader.ReadInt16(); this.minorOperatingSystemVersion = reader.ReadInt16(); this.majorImageVersion = reader.ReadInt16(); this.minorImageVersion = reader.ReadInt16(); this.majorSubsystemVersion = reader.ReadInt16(); this.minorSubsystemVersion = reader.ReadInt16(); this.win32VersionValue = reader.ReadInt32(); this.sizeOfImage = reader.ReadInt32(); this.sizeOfHeaders = reader.ReadInt32(); this.checkSum = reader.ReadInt32(); this.subsystem = reader.ReadInt16(); this.dllCharacteristics = reader.ReadInt16(); this.sizeOfStackReserve = reader.ReadInt32(); this.sizeOfStackCommit = reader.ReadInt32(); this.sizeOfHeapReserve = reader.ReadInt32(); this.sizeOfHeapCommit = reader.ReadInt32(); this.loaderFlags = reader.ReadInt32(); int count = reader.ReadInt32(); this.numberOfRvaAndSizes = count; DirectoryEntry[] directoryArray = new DirectoryEntry[count]; this.dataDirectory = directoryArray; for (int i = 0; i < count; i++) { directoryArray[i] = new DirectoryEntry(reader); } // Verify that we have a valid NT header if (this.signature != NTHeader.IMAGE_NT_SIGNATURE || this.sizeOfOptionalHeader != NTHeader.IMAGE_SIZEOF_NT_OPTIONAL32_HEADER || this.magic != NTHeader.IMAGE_NT_OPTIONAL_HDR32_MAGIC || this.numberOfRvaAndSizes != 16) { throw new IllegalPEFormatException("NT header problems"); } } // Output Routines internal void DumpLimitedToStream(StreamWriter outputStream, String prefix) { outputStream.WriteLine(prefix+ "Subsystem: "+ this.subsystem.ToString("x8")); outputStream.WriteLine(prefix+ "Native entry point address: "+ this.addressOfEntryPoint.ToString("x8")); outputStream.WriteLine(prefix+ "Image base: "+ this.imageBase.ToString("x8")); outputStream.WriteLine(prefix+ "Section alignment: "+ this.sectionAlignment.ToString("x8")); outputStream.WriteLine(prefix+ "File alignment: "+ this.fileAlignment.ToString("x8")); outputStream.WriteLine(prefix+ "Stack reserve size: "+ this.sizeOfStackReserve.ToString("x8")); outputStream.WriteLine(prefix+ "Stack commit size: "+ this.sizeOfStackCommit.ToString("x8")); outputStream.WriteLine(prefix+ "Directories: "+ this.numberOfRvaAndSizes.ToString("x8")); String indentedPrefix = prefix+" "; this.dataDirectory[0].DumpToStream(outputStream, indentedPrefix, " of Export directory "); this.dataDirectory[1].DumpToStream(outputStream, indentedPrefix, " of Import directory"); this.dataDirectory[2].DumpToStream(outputStream, indentedPrefix, " of Resource directory"); this.dataDirectory[3].DumpToStream(outputStream, indentedPrefix, " of Exception directory"); this.dataDirectory[4].DumpToStream(outputStream, indentedPrefix, " of Security directory"); this.dataDirectory[5].DumpToStream(outputStream, indentedPrefix, " of Base Relocation Table"); this.dataDirectory[6].DumpToStream(outputStream, indentedPrefix, " of Debug directory"); this.dataDirectory[7].DumpToStream(outputStream, indentedPrefix, " of Architecture specific"); this.dataDirectory[8].DumpToStream(outputStream, indentedPrefix, " of Global pointer directory"); this.dataDirectory[9].DumpToStream(outputStream, indentedPrefix, " of TLS directory "); this.dataDirectory[10].DumpToStream(outputStream, indentedPrefix, " of Load config directory"); this.dataDirectory[11].DumpToStream(outputStream, indentedPrefix, " of Bound import directory"); this.dataDirectory[12].DumpToStream(outputStream, indentedPrefix, " of Import Address Table"); this.dataDirectory[13].DumpToStream(outputStream, indentedPrefix, " of Delay Load IAT"); this.dataDirectory[14].DumpToStream(outputStream, indentedPrefix, " of CLR Header"); } // State internal int signature; // IMAGE_FILE_HEADER internal short machine; internal short numberOfSections; internal int timeDateStamp; internal int pointerToSymbolTable; internal int numberOfSymbols; internal short sizeOfOptionalHeader; internal short characteristics; // IMAGE_OPTIONAL_HEADER32 internal short magic; internal Byte majorLinkerVersion; internal Byte minorLinkerVersion; internal int sizeOfCode; internal int sizeOfInitializedData; internal int sizeOfUninitializedData; internal int addressOfEntryPoint; internal int baseOfCode; internal int baseOfData; internal int imageBase; internal int sectionAlignment; internal int fileAlignment; internal short majorOperatingSystemVersion; internal short minorOperatingSystemVersion; internal short majorImageVersion; internal short minorImageVersion; internal short majorSubsystemVersion; internal short minorSubsystemVersion; internal int win32VersionValue; internal int sizeOfImage; internal int sizeOfHeaders; internal int checkSum; internal short subsystem; internal short dllCharacteristics; internal int sizeOfStackReserve; internal int sizeOfStackCommit; internal int sizeOfHeapReserve; internal int sizeOfHeapCommit; internal int loaderFlags; internal int numberOfRvaAndSizes; // IMAGE_DATA_DIRECTORY internal DirectoryEntry[] dataDirectory; internal const int IMAGE_NT_SIGNATURE = 0x00004550; // PE00 internal const short IMAGE_SIZEOF_NT_OPTIONAL32_HEADER = 224; internal const short IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x010b; // File is executable (i.e. no unresolved external references). internal const short IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002; // File is a DLL. internal const short IMAGE_FILE_DLL = 0x2000; } internal struct DirectoryEntry { // How to create from a stream internal DirectoryEntry(Stream stream): this(new BinaryReader(stream)) { } internal DirectoryEntry(BinaryReader reader) { this.virtualAddress = reader.ReadInt32(); this.size = reader.ReadInt32(); } // Output Routines internal void DumpToStream(StreamWriter outputStream, String prefix, String suffix) { outputStream.WriteLine(prefix+ this.virtualAddress.ToString("x8")+ " ["+ this.size.ToString("x8")+ "] address [size]"+ suffix); } // State internal int virtualAddress; internal int size; } public class Section { public Section(SectionHeader header) { this.header = header; } public void LoadSection(PELoader peLoader) { Stream fileStream = peLoader.getStream(); fileStream.Seek(peLoader.VaToOffset(header.virtualAddress), SeekOrigin.Begin); BinaryReader reader = new BinaryReader(fileStream); this.rawData = reader.ReadBytes(header.sizeOfRawData); } public SectionHeader header; byte[] rawData; } public class SectionHeader { // How to create from a stream public SectionHeader(Stream stream): this(new BinaryReader(stream)) { } public SectionHeader(BinaryReader reader) { byte[] bytes = new byte[8]; reader.Read(bytes, 0, 8); char[] chars = new char[8]; for (int j = 0; j < 8; j++) { chars[j] = (char) bytes[j]; } this.name = new String(chars); this.virtualSize = reader.ReadInt32(); this.virtualAddress = reader.ReadInt32(); this.sizeOfRawData = reader.ReadInt32(); this.pointerToRawData = reader.ReadInt32(); this.pointerToRelocations = reader.ReadInt32(); this.pointerToLinenumbers = reader.ReadInt32(); this.numberOfRelocations = reader.ReadInt16(); this.numberOfLinenumbers = reader.ReadInt16(); this.characteristics = reader.ReadInt32(); } // State public String name; public int virtualSize; public int virtualAddress; public int sizeOfRawData; public int pointerToRawData; public int pointerToRelocations; public int pointerToLinenumbers; public short numberOfRelocations; public short numberOfLinenumbers; public int characteristics; } internal class COMHeader { // Corresponds to the WinNT IMAGE_COR20_HEADER data structure // How to create from a stream internal COMHeader(Stream stream): this(new BinaryReader(stream)) { } internal COMHeader(BinaryReader reader) { this.cb = reader.ReadInt32(); this.majorRuntimeVersion = reader.ReadInt16(); this.minorRuntimeVersion = reader.ReadInt16(); this.metaData = new DirectoryEntry(reader); this.flags = reader.ReadInt32(); this.entryPointToken = reader.ReadInt32(); this.resources = new DirectoryEntry(reader); this.strongNameSignature = new DirectoryEntry(reader); this.codeManagerTable = new DirectoryEntry(reader); this.vtableFixups = new DirectoryEntry(reader); this.exportAddressTableJumps = new DirectoryEntry(reader); this.managedNativeHeader = new DirectoryEntry(reader); // Verify that we have a valid header if (this.majorRuntimeVersion == 1 || this.majorRuntimeVersion > 2) { throw new IllegalPEFormatException("COM header problems"); } } // State // Header Versioning internal int cb; internal short majorRuntimeVersion; internal short minorRuntimeVersion; // Symbol table and startup information internal DirectoryEntry metaData; internal int flags; internal int entryPointToken; // Binding information internal DirectoryEntry resources; internal DirectoryEntry strongNameSignature; // Regular fixup and binding information internal DirectoryEntry codeManagerTable; internal DirectoryEntry vtableFixups; internal DirectoryEntry exportAddressTableJumps; // Managed Native Code internal DirectoryEntry managedNativeHeader; // Index of the COM header in the array of directories internal const int ENTRYINDEX = 14; } internal class ImportDescriptor { // How to create from a stream internal ImportDescriptor(Stream stream): this(new BinaryReader(stream)) { } internal ImportDescriptor(BinaryReader reader) { this.characteristics = reader.ReadInt32(); this.timeDateStamp = reader.ReadInt32(); this.forwarderChain = reader.ReadInt32(); this.name = reader.ReadInt32(); this.firstChunk = reader.ReadInt32(); } internal void DumpToStream(StreamWriter outputStream, String prefix) { outputStream.WriteLine(prefix+ this.firstChunk.ToString("x8")+ " Import Address Table"); outputStream.WriteLine(prefix+ this.name.ToString("x8")+ " Import Name Table"); outputStream.WriteLine(prefix+ this.timeDateStamp.ToString("x8")+ " time date stamp"); outputStream.WriteLine(prefix+ this.forwarderChain.ToString("x8")+ " index of first forwarder reference"); } // State internal int characteristics; internal int timeDateStamp; internal int forwarderChain; internal int name; internal int firstChunk; // Size of this structure in stream internal const int SIZE = 20; } internal class IllegalPEFormatException: Exception { // Constructor Methods internal IllegalPEFormatException(String reason): base(reason) { } } internal class NotYetImplemented: Exception { // Constructor Methods internal NotYetImplemented(String reason): base(reason) { } } } }