I made this pretty basic PE header parser for my injection library before I discovered PeNet, an amazing library for parsing PE headers.
Anyway, I thought I may as well drop the code here in case anyone needs it someday before I throw it out.
Code:
private static List<object> GetPeHeaders(string dllPath)
{
var peHeaders = new List<object>();
// Open a file stream to the dll
using (var stream = new FileStream(dllPath, FileMode.Open, FileAccess.Read))
{
// Open a binary reader to the file stream
var reader = new BinaryReader(stream);
// Get the dos header
var dosHeader = ConvertBytesToStructure<ImageDosHeader>(reader);
// Skip the dos stub
reader.BaseStream.Seek(dosHeader.E_Lfanew, SeekOrigin.Begin);
// Get the nt header
var ntHeader = new ImageNtHeader
{
Signature = ConvertBytesToStructure<uint>(reader),
FileHeader = ConvertBytesToStructure<ImageFileHeader>(reader)
};
var sectionHeaders = new List<ImageSectionHeader>();
// If the dll is x86
if ((ntHeader.FileHeader.Characteristics & 0x0100) == 0x0100)
{
// Get the optional header
ntHeader.OptionalHeader = ConvertBytesToStructure<ImageOptionalHeader>(reader);
// Get the section headers
foreach (var i in Enumerable.Range(0, ntHeader.OptionalHeader.NumberOfRvaAndSizes))
{
if (ntHeader.OptionalHeader.DataDirectory[i].Size > 0)
{
sectionHeaders.Add(ConvertBytesToStructure<ImageSectionHeader>(reader));
}
}
}
// If the dll is x64
else
{
// Get the optional header
ntHeader.OptionalHeader64 = ConvertBytesToStructure<ImageOptionalHeader64>(reader);
// Get the section headers
foreach (var i in Enumerable.Range(0, ntHeader.OptionalHeader.NumberOfRvaAndSizes))
{
if (ntHeader.OptionalHeader.DataDirectory[i].Size > 0)
{
sectionHeaders.Add(ConvertBytesToStructure<ImageSectionHeader>(reader));
}
}
}
// Add the header objects to a list
peHeaders.Add(dosHeader);
peHeaders.Add(ntHeader);
peHeaders.Add(sectionHeaders);
}
return peHeaders;
}
internal static TStructure ConvertBytesToStructure<TStructure>(BinaryReader reader)
{
// Get the size of the structure
var structureSize = Marshal.SizeOf(typeof(TStructure));
// Read the bytes
var bytes = reader.ReadBytes(structureSize);
// Allocate memory for a buffer
var buffer = Marshal.AllocHGlobal(structureSize);
Marshal.Copy(bytes, 0, buffer, structureSize);
// Convert the bytes into a structure
var structure = (TStructure) Marshal.PtrToStructure(buffer, typeof(TStructure));
// Free the previously allocated memory
Marshal.FreeHGlobal(buffer);
return structure;
}
And here are the structures used
Code:
[StructLayout(LayoutKind.Sequential)]
internal struct ImageDosHeader
{
internal readonly ushort E_Magic;
private readonly ushort E_Cblp;
private readonly ushort E_Cp;
private readonly ushort E_Crlc;
private readonly ushort E_Cparhdr;
private readonly ushort E_Minalloc;
private readonly ushort E_Maxalloc;
private readonly ushort E_Ss;
private readonly ushort E_Sp;
private readonly ushort E_Csum;
private readonly ushort E_Ip;
private readonly ushort E_Cs;
private readonly ushort E_Lfarlc;
private readonly ushort E_Ovno;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private readonly ushort[] E_Res1;
private readonly ushort E_Oemid;
private readonly ushort E_Oeminfo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
private readonly ushort[] E_Res2;
internal readonly int E_Lfanew;
}
[StructLayout(LayoutKind.Sequential)]
public struct ImageFileHeader
{
public readonly ushort Machine;
public readonly ushort NumberOfSections;
public readonly uint TimeDateStamp;
public readonly uint PointerToSymbolTable;
public readonly uint NumberOfSymbols;
public readonly ushort SizeOfOptionalHeader;
public readonly ushort Characteristics;
}
[StructLayout(LayoutKind.Sequential)]
public struct ImageDataDirectory
{
public readonly uint VirtualAddress;
public readonly uint Size;
}
[StructLayout(LayoutKind.Sequential)]
public struct ImageOptionalHeader
{
public ushort Magic;
public byte MajorLinkerVersion;
public byte MinorLinkerVersion;
public uint SizeOfCode;
public uint SizeOfInitializedData;
public uint SizeOfUninitializedData;
public uint AddressOfEntryPoint;
public uint BaseOfCode;
public uint BaseOfData;
public uint ImageBase;
public uint SectionAlignment;
public uint FileAlignment;
public ushort MajorOperatin****temVersion;
public ushort MinorOperatin****temVersion;
public ushort MajorImageVersion;
public ushort MinorImageVersion;
public ushort MajorSubsystemVersion;
public ushort MinorSubsystemVersion;
public uint Win32VersionValue;
public int SizeOfImage;
public uint SizeOfHeaders;
public uint CheckSum;
public ushort Subsystem;
public ushort DllCharacteristics;
public uint SizeOfStackReserve;
public uint SizeOfStackCommit;
public uint SizeOfHeapReserve;
public uint SizeOfHeapCommit;
public uint LoaderFlags;
public int NumberOfRvaAndSizes;
public ImageDataDirectory ExportTable;
public ImageDataDirectory ImportTable;
public ImageDataDirectory ResourceTable;
public ImageDataDirectory ExceptionTable;
public ImageDataDirectory CertificateTable;
public ImageDataDirectory BaseRelocationTable;
public ImageDataDirectory Debug;
public ImageDataDirectory Architecture;
public ImageDataDirectory GlobalPtr;
public ImageDataDirectory TLSTable;
public ImageDataDirectory LoadConfigTable;
public ImageDataDirectory BoundImport;
public ImageDataDirectory IAT;
public ImageDataDirectory DelayImportDescriptor;
public ImageDataDirectory CLRRuntimeHeader;
public ImageDataDirectory Reserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct ImageOptionalHeader64
{
public readonly ushort Magic;
private readonly byte MajorLinkerVersion;
private readonly byte MinorLinkerVersion;
private readonly uint SizeOfCode;
public readonly uint SizeOfInitializedData;
private readonly uint SizeOfUninitializedData;
public readonly uint AddressOfEntryPoint;
private readonly uint BaseOfCode;
public readonly uint ImageBase;
private readonly uint SectionAlignment;
private readonly uint FileAlignment;
private readonly ushort MajorOperatin****temVersion;
private readonly ushort MinorOperatin****temVersion;
private readonly ushort MajorImageVersion;
private readonly ushort MinorImageVersion;
private readonly ushort MajorSubsystemVersion;
private readonly ushort MinorSubsystemVersion;
private readonly uint Win32VersionValue;
public readonly int SizeOfImage;
private readonly uint SizeOfHeaders;
private readonly uint CheckSum;
private readonly ushort Subsystem;
private readonly ushort DllCharacteristics;
private readonly uint SizeOfStackReserve;
private readonly uint SizeOfStackCommit;
private readonly uint SizeOfHeapReserve;
private readonly uint SizeOfHeapCommit;
private readonly uint LoaderFlags;
public readonly int NumberOfRvaAndSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
private readonly ImageDataDirectory[] DataDirectory;
}
[StructLayout(LayoutKind.Sequential)]
public struct ImageNtHeader
{
public uint Signature;
public ImageFileHeader FileHeader;
public ImageOptionalHeader OptionalHeader;
public ImageOptionalHeader64 OptionalHeader64;
}
[StructLayout(LayoutKind.Sequential)]
public struct ImageSectionHeader
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] Name;
public uint VirtualSize;
public uint VirtualAddress;
public uint SizeOfRawData;
public uint PointerToRawData;
public uint PointerToRelocations;
public uint PointerToLinenumbers;
public ushort NumberOfRelocations;
public ushort NumberOfLinenumbers;
public uint Characteristics;
}
[StructLayout(LayoutKind.Sequential)]
public struct ImageBaseRelocation
{
public uint VirtualAddress;
public uint SizeOfBlock;
}
[StructLayout(LayoutKind.Sequential)]
public struct ImageImportDescription
{
public uint OriginalFirstThunk;
public uint TimeDateStamp;
public uint ForwarderChain;
public uint Name;
public uint FirstThunk;
}