Using dumpbin on my managed PE file I can see it contains a checksum in the optional header values.
I would like to be able to get that checksum so that I can store it and make sure no one is replacing PE files produced by our build machine. I am not concerned if this checksum if cryptographically secure since we are only using this to determine if some one has mistakenly put PE files in the wrong location and we are not guarding against deliberate attacks. I am not sure how to go about getting the checksum from the PE file though. Does C# have a managed API for getting the checksum of a PE file?
If this diagram is correct I could just use a FileStream
and inspect the correct bytes of the PE file but I would prefer to use the .NET framework if at all possible to get this information.
Here is an image of the field from the command dumpbin /HEADERS MyDLL.dll
. I put a yellow square around the field I am trying to retrieve.
EDIT1: I was complicating things when I said I could use an unsafe C# project to read the header. As @xanatos pointed out I can just use a filestream to read the bytes of the header.
EDIT2: I removed my questions about if this was a PE32(+) file because I was able to determine it is just a PE32 file.
So here is the console application I built to get the checksum from the PE files I am working with.
using System;
using System.IO;
using System.Text;
namespace ConsoleApp1
{
class Program
{
public const int PeHeaderOffset = 0x003C;
public const int CheckSumOffset = 0x0058;
static void Main(string[] args)
{
while (true)
{
Console.Write("Path to PE file: ");
string path = Console.ReadLine();
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
byte[] peHeaderPointer = new byte[4];
byte[] checkSum = new byte[4];
peHeaderPointer = ReadPeHeaderPointer(fileStream);
int checkSumOffSet = BitConverter.ToInt32(peHeaderPointer, 0);
checkSum = ReadChecksum(fileStream, checkSumOffSet);
string hex = ByteArrayToHexString(checkSum);
Console.WriteLine($"Checksum: {hex}");
Console.ReadLine();
}
}
}
//This will not reverse the bytes because the BitConvert.ToInt32 is already reading it in the correct order.
public static byte[] ReadPeHeaderPointer(FileStream fileStream)
{
byte[] bytes = new byte[4];
fileStream.Seek(PeHeaderOffset, SeekOrigin.Begin);
fileStream.Read(bytes, 0, 4);
return bytes;
}
//This reverses the bytes to that this tool will match dumpbin /headers and dotPeek
public static byte[] ReadChecksum(FileStream fileStream, int offSet)
{
byte[] bytes = new byte[4];
fileStream.Seek(offSet + CheckSumOffset, SeekOrigin.Begin);
fileStream.Read(bytes, 0, 4);
bytes = ReverseBytes(bytes);
return bytes;
}
//The PE file seems to be written to the file system in Big Endian
//I need to read them in Small Endian to match dumpbin and dotPeek
public static byte[] ReverseBytes(byte[] bytes)
{
byte[] tempBytes = new byte[4];
tempBytes[0] = bytes[3];
tempBytes[1] = bytes[2];
tempBytes[2] = bytes[1];
tempBytes[3] = bytes[0];
return tempBytes;
}
public static string ByteArrayToHexString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString().ToUpper();
}
}
}
I am not completely certain why BitConverter is using Little Endian but the dll is written in Big Endian. That is why some of the byte arrays are being reversed and some of them are not being reversed.