0

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.

enter image description here

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.

Max Young
  • 1,522
  • 1
  • 16
  • 42
  • To open a binary file (an exe or dll) you don't need "unsafe" code... You just need to open it with `FileStream`. – xanatos May 04 '17 at 12:16
  • You know I never really thought about that, that should just be a byte stream. – Max Young May 04 '17 at 12:21
  • You can use this Windows API: [ImageNtHeader](https://msdn.microsoft.com/en-us/library/windows/desktop/ms680212(v=vs.85).aspx). It will return a pointer to the `IMAGE_NT_HEADERS` and inside the `OptionalHeader` there is the `Checksum`. – xanatos May 04 '17 at 12:24
  • @xanatos do you happen to know if .NET uses that API any where? If you can post an example of how to read the header using a `FileStream` I would be more than happy to accept that as an answer. Although I would prefer to use an API that returned that as a property if I have to read it out that way that's what I will do. – Max Young May 04 '17 at 12:25
  • @xanatos Thank you for the suggestion about the `FileStream` though. Really saved me from over complicating this. – Max Young May 04 '17 at 12:31
  • It is not a cryptographically secure hash, and can be very easily forged, so isn't useful at all to detect spoofing. It doesn't do anything beyond providing an extra check that a PDB file matches the executable file. At least consider giving your assemblies a strong name. Project > Properties > Signing tab. And lock up the private key carefully so that it is not as easily spoofed. – Hans Passant May 04 '17 at 12:35
  • @HansPassant I am not really using it for spoofing I am using it just to make sure no one has accidentally published to the wrong location. I should have added my requirements are not for security reasons. We are locking down the folders that the builds get published to as well but my managers wanted to go one more step. I ask about whether they were concerned with it being crytographically secure since some one suggested just getting an MD5 and it wasnt a concern. – Max Young May 04 '17 at 12:36
  • The value of the checksum has nothing whatsoever to do with the "location". Very hard to guess what problem you are really trying to solve. – Hans Passant May 04 '17 at 12:39
  • I am making a build on our build machine and I am going to store the checksum. Then when we go to promote that build I am going to check the checksum again to make sure no one has mistakenly replaced the PE files between testing and promotion. We are not concerned about some one maliciously replacing files we are only concerned with some one manually publishing to the wrong location on accident. – Max Young May 04 '17 at 12:41
  • I could just hash the PE files myself but I figured if a checksum was already built into them why not use that. – Max Young May 04 '17 at 12:43

1 Answers1

0

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.

Max Young
  • 1,522
  • 1
  • 16
  • 42