Skip to content

Instantly share code, notes, and snippets.

@tnzk
Created May 8, 2012 08:00
Show Gist options
  • Select an option

  • Save tnzk/2633405 to your computer and use it in GitHub Desktop.

Select an option

Save tnzk/2633405 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
namespace unification
{
// Reads in the header information of the Portable Executable format.
// Provides information such as the date the assembly was compiled.
public class PeHeaderReader {
#region File Header Structures
public struct IMAGE_DOS_HEADER { // DOS .EXE header
public UInt16 e_magic; // Magic number
public UInt16 e_cblp; // Bytes on last page of file
public UInt16 e_cp; // Pages in file
public UInt16 e_crlc; // Relocations
public UInt16 e_cparhdr; // Size of header in paragraphs
public UInt16 e_minalloc; // Minimum extra paragraphs needed
public UInt16 e_maxalloc; // Maximum extra paragraphs needed
public UInt16 e_ss; // Initial (relative) SS value
public UInt16 e_sp; // Initial SP value
public UInt16 e_csum; // Checksum
public UInt16 e_ip; // Initial IP value
public UInt16 e_cs; // Initial (relative) CS value
public UInt16 e_lfarlc; // File address of relocation table
public UInt16 e_ovno; // Overlay number
public UInt16 e_res_0; // Reserved words
public UInt16 e_res_1; // Reserved words
public UInt16 e_res_2; // Reserved words
public UInt16 e_res_3; // Reserved words
public UInt16 e_oemid; // OEM identifier (for e_oeminfo)
public UInt16 e_oeminfo; // OEM information; e_oemid specific
public UInt16 e_res2_0; // Reserved words
public UInt16 e_res2_1; // Reserved words
public UInt16 e_res2_2; // Reserved words
public UInt16 e_res2_3; // Reserved words
public UInt16 e_res2_4; // Reserved words
public UInt16 e_res2_5; // Reserved words
public UInt16 e_res2_6; // Reserved words
public UInt16 e_res2_7; // Reserved words
public UInt16 e_res2_8; // Reserved words
public UInt16 e_res2_9; // Reserved words
public UInt32 e_lfanew; // File address of new exe header
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct IMAGE_DATA_DIRECTORY {
public Int32 RVA;
public Int32 Size;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct IMAGE_OPTIONAL_HEADER32 {
public UInt16 Magic;
public Byte MajorLinkerVersion;
public Byte MinorLinkerVersion;
public UInt32 SizeOfCode;
public UInt32 SizeOfInitializedData;
public UInt32 SizeOfUninitializedData;
public UInt32 AddressOfEntryPoint;
public UInt32 BaseOfCode;
public UInt32 BaseOfData;
public UInt32 ImageBase;
public UInt32 SectionAlignment;
public UInt32 FileAlignment;
public UInt16 MajorOperatingSystemVersion;
public UInt16 MinorOperatingSystemVersion;
public UInt16 MajorImageVersion;
public UInt16 MinorImageVersion;
public UInt16 MajorSubsystemVersion;
public UInt16 MinorSubsystemVersion;
public UInt32 Win32VersionValue;
public UInt32 SizeOfImage;
public UInt32 SizeOfHeaders;
public UInt32 CheckSum;
public UInt16 Subsystem;
public UInt16 DllCharacteristics;
public UInt32 SizeOfStackReserve;
public UInt32 SizeOfStackCommit;
public UInt32 SizeOfHeapReserve;
public UInt32 SizeOfHeapCommit;
public UInt32 LoaderFlags;
public UInt32 NumberOfRvaAndSizes;
public IMAGE_DATA_DIRECTORY DirectoryExportTable;
public IMAGE_DATA_DIRECTORY DirectoryImportTable;
public IMAGE_DATA_DIRECTORY DirectoryResourceTable;
public IMAGE_DATA_DIRECTORY DirectoryExceptioniTable;
public IMAGE_DATA_DIRECTORY DirectoryCertificateTable;
public IMAGE_DATA_DIRECTORY DirectoryBaseRelocationTable;
public IMAGE_DATA_DIRECTORY DirectoryDebug;
public IMAGE_DATA_DIRECTORY DirectoryArchitecture;
public IMAGE_DATA_DIRECTORY DirectoryGlobalPtr;
public IMAGE_DATA_DIRECTORY DirectoryTLSTable;
public IMAGE_DATA_DIRECTORY DirectoryLoadConfigTable;
public IMAGE_DATA_DIRECTORY DirectoryBoundTable;
public IMAGE_DATA_DIRECTORY DirectoryImportAddressTable;
public IMAGE_DATA_DIRECTORY DirectoryDelayImportDescroptor;
public IMAGE_DATA_DIRECTORY DirectoryCOMDescroptor;
public IMAGE_DATA_DIRECTORY DirectoryReserved;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct IMAGE_OPTIONAL_HEADER64 {
public UInt16 Magic;
public Byte MajorLinkerVersion;
public Byte MinorLinkerVersion;
public UInt32 SizeOfCode;
public UInt32 SizeOfInitializedData;
public UInt32 SizeOfUninitializedData;
public UInt32 AddressOfEntryPoint;
public UInt32 BaseOfCode;
public UInt64 ImageBase;
public UInt32 SectionAlignment;
public UInt32 FileAlignment;
public UInt16 MajorOperatingSystemVersion;
public UInt16 MinorOperatingSystemVersion;
public UInt16 MajorImageVersion;
public UInt16 MinorImageVersion;
public UInt16 MajorSubsystemVersion;
public UInt16 MinorSubsystemVersion;
public UInt32 Win32VersionValue;
public UInt32 SizeOfImage;
public UInt32 SizeOfHeaders;
public UInt32 CheckSum;
public UInt16 Subsystem;
public UInt16 DllCharacteristics;
public UInt64 SizeOfStackReserve;
public UInt64 SizeOfStackCommit;
public UInt64 SizeOfHeapReserve;
public UInt64 SizeOfHeapCommit;
public UInt32 LoaderFlags;
public UInt32 NumberOfRvaAndSizes;
// FIXME
public IMAGE_DATA_DIRECTORY DirectoryExportTable;
public IMAGE_DATA_DIRECTORY DirectoryImportTable;
public IMAGE_DATA_DIRECTORY DirectoryResourceTable;
public IMAGE_DATA_DIRECTORY DirectoryExceptioniTable;
public IMAGE_DATA_DIRECTORY DirectoryCertificateTable;
public IMAGE_DATA_DIRECTORY DirectoryBaseRelocationTable;
public IMAGE_DATA_DIRECTORY DirectoryDebug;
public IMAGE_DATA_DIRECTORY DirectoryArchitecture;
public IMAGE_DATA_DIRECTORY DirectoryGlobalPtr;
public IMAGE_DATA_DIRECTORY DirectoryTLSTable;
public IMAGE_DATA_DIRECTORY DirectoryLoadConfigTable;
public IMAGE_DATA_DIRECTORY DirectoryBoundTable;
public IMAGE_DATA_DIRECTORY DirectoryImportAddressTable;
public IMAGE_DATA_DIRECTORY DirectoryCOMDescroptor;
public IMAGE_DATA_DIRECTORY DirectoryDelayImportDescroptor;
public IMAGE_DATA_DIRECTORY DirectoryReserved;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct IMAGE_FILE_HEADER {
public UInt16 Machine;
public UInt16 NumberOfSections;
public UInt32 TimeDateStamp;
public UInt32 PointerToSymbolTable;
public UInt32 NumberOfSymbols;
public UInt16 SizeOfOptionalHeader;
public UInt16 Characteristics;
}
#endregion File Header Structures
#region Section Structures
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct IMAGE_SECTION_HEADER {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)]
public string Name;
public UInt32 VirtualSize;
public UInt32 VirtualAddress;
public UInt32 SizeOfRawData;
public UInt32 PointerToRawData;
public UInt32 PointerToRelocations;
public UInt32 PointerToLinenumbers;
public UInt16 NumberOfRelocations;
public UInt16 NumberOfLinenumbers;
public UInt32 Characteristics;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct IMAGE_IMPORT_DESCRIPTOR {
public UInt32 OriginalFirstThunk;
public UInt32 TimeDateStamp;
public UInt32 ForwarderChain;
public UInt32 Name;
public UInt32 FirstThunk;
}
#endregion
#region Private Fields
private IMAGE_DOS_HEADER dosHeader;
private IMAGE_FILE_HEADER fileHeader;
private IMAGE_OPTIONAL_HEADER32 optionalHeader32;
private IMAGE_OPTIONAL_HEADER64 optionalHeader64;
private List<IMAGE_SECTION_HEADER> sectionHeaders;
private List<IMAGE_IMPORT_DESCRIPTOR> importDescroptors;
public List<string> importedModules;
#endregion Private Fields
#region Public Methods
public PeHeaderReader(string filePath) {
// Read in the DLL or EXE and get the timestamp
using (FileStream stream = new FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) {
BinaryReader reader = new BinaryReader(stream);
dosHeader = FromBinaryReader<IMAGE_DOS_HEADER>(reader);
// Add 4 bytes to the offset
stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin);
UInt32 ntHeadersSignature = reader.ReadUInt32();
fileHeader = FromBinaryReader<IMAGE_FILE_HEADER>(reader);
if (this.Is32BitHeader) {
optionalHeader32 = FromBinaryReader<IMAGE_OPTIONAL_HEADER32>(reader);
}
else {
optionalHeader64 = FromBinaryReader<IMAGE_OPTIONAL_HEADER64>(reader);
}
sectionHeaders = new List<IMAGE_SECTION_HEADER>();
IMAGE_SECTION_HEADER idata;
for( int i = 0; i < fileHeader.NumberOfSections; i++) {
sectionHeaders.Add(FromBinaryReader<IMAGE_SECTION_HEADER>(reader));
}
// Obtain import descripors
long offset = RVAtoOffset((this.Is32BitHeader ? optionalHeader32.DirectoryImportTable.RVA : optionalHeader64.DirectoryImportTable.RVA));
stream.Seek( offset, SeekOrigin.Begin);
importDescroptors = new List<IMAGE_IMPORT_DESCRIPTOR>();
while(true) {
IMAGE_IMPORT_DESCRIPTOR imp = FromBinaryReader<IMAGE_IMPORT_DESCRIPTOR>(reader);
if (imp.Name == 0) break;
importDescroptors.Add(imp);
}
importedModules = new List<string>();
foreach (IMAGE_IMPORT_DESCRIPTOR imp in importDescroptors) {
importedModules.Add(GetCString(stream, RVAtoOffset(imp.Name)));
}
}
}
private long RVAtoOffset(long rva)
{
IMAGE_SECTION_HEADER sechead = new IMAGE_SECTION_HEADER();
bool found = false;
foreach (IMAGE_SECTION_HEADER sh in sectionHeaders) {
if (sh.VirtualAddress <= rva && rva <= sh.VirtualAddress + sh.VirtualSize) {
sechead = sh;
found = true;
}
}
return rva - sechead.VirtualAddress + sechead.PointerToRawData;
}
private string GetCString(Stream stream, long start)
{
BinaryReader reader = new BinaryReader(stream);
stream.Seek(start, SeekOrigin.Begin);
List<byte> result = new List<byte>();
while (true) {
byte b = reader.ReadByte();
if (b == 0) break;
result.Add(b);
}
return Encoding.ASCII.GetString(result.ToArray());
}
// Gets the header of the .NET assembly that called this function
public static PeHeaderReader GetCallingAssemblyHeader() {
string pathCallingAssembly = System.Reflection.Assembly.GetCallingAssembly().Location;
// Get the path to the calling assembly, which is the path to the
// DLL or EXE that we want the time of
string filePath = System.Reflection.Assembly.GetCallingAssembly().Location;
// Get and return the timestamp
return new PeHeaderReader(filePath);
}
// Reads in a block from a file and converts it to the struct
// type specified by the template parameter
public static T FromBinaryReader<T>(BinaryReader reader) {
// Read in a byte array
byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T)));
// Pin the managed memory while, copy it out the data, then unpin it
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return theStructure;
}
#endregion Public Methods
#region Properties
// Gets if the file header is 32 bit or not
public bool Is32BitHeader {
get {
UInt16 IMAGE_FILE_32BIT_MACHINE = 0x0100;
return (IMAGE_FILE_32BIT_MACHINE & FileHeader.Characteristics) == IMAGE_FILE_32BIT_MACHINE;
}
}
// Gets the file header
public IMAGE_FILE_HEADER FileHeader {
get {
return fileHeader;
}
}
// Gets the optional header
public IMAGE_OPTIONAL_HEADER32 OptionalHeader32 {
get {
return optionalHeader32;
}
}
// Gets the optional header
public IMAGE_OPTIONAL_HEADER64 OptionalHeader64 {
get {
return optionalHeader64;
}
}
// Gets the lsit of section headers
public List<IMAGE_SECTION_HEADER> SectionHeaders
{
get
{
return sectionHeaders;
}
}
// Gets the lsit of section headers
public List<IMAGE_IMPORT_DESCRIPTOR> ImporetDescriptors
{
get
{
return importDescroptors;
}
}
// Gets the timestamp from the file header
public DateTime TimeStamp {
get {
// Timestamp is a date offset from 1970
DateTime returnValue = new DateTime(1970, 1, 1, 0, 0, 0);
// Add in the number of seconds since 1970/1/1
returnValue = returnValue.AddSeconds(fileHeader.TimeDateStamp);
// Adjust to local timezone
returnValue += TimeZone.CurrentTimeZone.GetUtcOffset(returnValue);
return returnValue;
}
}
#endregion Properties
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment