0

When I compile a .net program I can tell it to compile for /platform:anycpu or /platform:x86 etc. Is there someway I can determine what platform value a .net exe/dll has been compiled with?

See my answer below for a program that determines the platform flag. Or use corflags.exe.

dan gibson
  • 3,605
  • 3
  • 39
  • 57

3 Answers3

4

Have a look at CorFlags.exe. It allows you to view the PE and 32BIT flags of .NET assemblies.

Edit: this page has a bit more info on how to interpret the output of CorFlags.

Marty
  • 7,464
  • 1
  • 31
  • 52
2

This may be able to help for executables:

http://www.mitec.cz/exe.html

If you need to programmatically check against .net dlls, then there are these posts:

How to find if a native DLL file is compiled as x64 or x86?

Check if unmanaged DLL is 32-bit or 64-bit?

Community
  • 1
  • 1
Luke Hutton
  • 10,612
  • 6
  • 33
  • 58
1

Using the information from the answers from Luke and Marty I was able to do this. Here is the console program I wrote that checks that an assembly is compiled with the specified platform. It's messy because I haven't cleaned it up, but it works.

using System;
using System.IO;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace MyApp
{
    static class App
    {
        [STAThread]
        static void Main(string[] args)
        {
            if (args.GetUpperBound(0) != 1)
            {
                Console.WriteLine("Usage:");
                Console.WriteLine("  checkPlatform [anycpu|x86|x64] filename");
                return;
            }
            CheckPlatform(args[0], args[1]);
        }

        public static bool CheckPlatform(string targetPlatform, string filename)
        {
            //see http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
            //offset to PE header is always at 0x3C
            //PE header starts with "PE\0\0" =  0x50 0x45 0x00 0x00
            //followed by 2-byte machine type field (see document above for enum)
            FileStream fs = new FileStream(filename, FileMode.Open, System.IO.FileAccess.Read);
            BinaryReader br = new BinaryReader(fs);
            fs.Seek(0x3c, SeekOrigin.Begin);
            Int32 peOffset = br.ReadInt32();
            fs.Seek(peOffset, SeekOrigin.Begin);
            UInt32 peHead = br.ReadUInt32();

            if(peHead!=0x00004550) // "PE\0\0", little-endian
                throw new Exception("Can't find PE header");
            int machineType = (int) br.ReadUInt16(); // The number that identifies the type of target machine.
            int numberOfSections = (int)br.ReadUInt16(); // The number of sections. This indicates the size of the section table, which immediately follows the headers.
            int timeDateStamp = (int)br.ReadUInt32(); // The low 32 bits of the number of seconds since 00:00 January 1, 1970 (a C run-time time_t value), that indicates when the file was created.
            int pointerToSymbolTable = (int)br.ReadUInt32(); // The file offset of the COFF symbol table, or zero if no COFF symbol table is present. This value should be zero for an image because COFF debugging information is deprecated.
            int numberOfSymbols = (int)br.ReadUInt32(); // The number of entries in the symbol table. This data can be used to locate the string table, which immediately follows the symbol table. This value should be zero for an image because COFF debugging information is deprecated.
            int sizeOfOptionalHeader = (int)br.ReadUInt16(); // The size of the optional header, which is required for executable files but not for object files. This value should be zero for an object file. For a description of the header format, see section 3.4, “Optional Header (Image Only).”
            int characteristics = (int)br.ReadUInt16(); // The flags that indicate the attributes of the file. For specific flag values, see section 3.3.2, “Characteristics.”
            int sectionoffset = (int)fs.Position + sizeOfOptionalHeader;
            int magic = (int)br.ReadUInt16();

        /*
Now we are at the end of the PE Header and from here, the
            PE Optional Headers starts...
    To go directly to the datadictionary, we'll increase the
    stream’s current position to with 96 (0x60). 96 because,
            28 for Standard fields
            68 for NT-specific fields
From here DataDictionary starts...and its of total 128 bytes. DataDictionay has 16 directories in total,
doing simple maths 128/16 = 8.
So each directory is of 8 bytes.
            In this 8 bytes, 4 bytes is of RVA and 4 bytes of Size.

btw, the 15th directory consist of CLR header! if its 0, its not a CLR file :)
            */
            ushort dataDictionaryStart;
            uint[] dataDictionaryRVA = new uint[16];
            uint[] dataDictionarySize = new uint[16];

            dataDictionaryStart = Convert.ToUInt16 (Convert.ToUInt16(fs.Position) + 0x60);
            fs.Position = dataDictionaryStart - 2;
            for (int i = 0; i < 15; i++)
            {
                dataDictionaryRVA[i] = br.ReadUInt32();
                dataDictionarySize[i] = br.ReadUInt32();
            }
            if (dataDictionaryRVA[14] == 0)
            {
                MessageBox.Show("This is NOT a valid CLR File!!");
                return false;
            }

            fs.Position = sectionoffset;

            int[] SVirtualAddress = new int[numberOfSections];
            int[] SSizeOfRawData = new int[numberOfSections];
            int[] SPointerToRawData = new int[numberOfSections];

            for ( int i = 0 ; i <numberOfSections; i++)
            {
                br.ReadBytes(12);
                SVirtualAddress[i] = br.ReadInt32();
                SSizeOfRawData[i] = br.ReadInt32();
                SPointerToRawData[i] = br.ReadInt32();
                br.ReadBytes(16);
            }

            fs.Position = ConvertRVA(numberOfSections, SVirtualAddress, SSizeOfRawData, SPointerToRawData, dataDictionaryRVA[14]);
            int size = br.ReadInt32();
            int majorruntimeversion = br.ReadInt16();
            int minorruntimeversion = br.ReadInt16();
            int metadatarva = br.ReadInt32();
            int metadatasize = br.ReadInt32();
            int corflags = br.ReadInt32();
            br.Close();
            fs.Close();

            bool is32bit = false;
            if ((characteristics & 0x0100) == (0x0100)) is32bit = true;
            string s = "";
            if (magic == 0x10b) s += "Magic=PE\r\n"; else
            if (magic == 0x20b) s += "Magic=PE+\r\n"; else
            s += "Magic=" + magic.ToString("X") + "\r\n";
            s += "machineType=" + machineType.ToString() + "\r\n";
            s += "is32bit=" + is32bit.ToString() + "\r\n";
            s += "numberOfSections=" + numberOfSections.ToString() + "\r\n";
            s += "sizeOfOptionalHeader=" + sizeOfOptionalHeader.ToString() + "\r\n";
            s += "Characteristics=" + characteristics.ToString() + "\r\n";
            s += "corflags=" + corflags.ToString("X") + "\r\n";

//·         anycpu: PE = PE32    and  32BIT = 0
//·         x86:      PE = PE32    and  32BIT = 1
//·         64-bit:  PE = PE32+  and  32BIT = 0
            if ((corflags & 2) == 2) is32bit = true; else is32bit = false;
            string platform = "Unknown";
            if (magic == 0x10b)
            {
                // PE32
                if (is32bit) platform = "x86"; else platform = "anycpu";
            } else
            if (magic == 0x20b)
            {
                // PE32+
                if (is32bit == false) platform = "x64";
            }

            if (platform.ToUpper() != targetPlatform.ToUpper())
            {
                MessageBox.Show(filename + " is " + platform + ", not " + targetPlatform + ".\r\n" + s);
                return false;
            }

            return true;
        }

        public static long ConvertRVA(int numberOfSections, int[] SVirtualAddress, int[] SSizeOfRawData, int[] SPointerToRawData, long rva)
        {
            int i;
            for ( i = 0 ; i <numberOfSections; i++)
            {
                if ( rva >= SVirtualAddress [i]  && ( rva <  SVirtualAddress[i] + SSizeOfRawData [i] ))
                break ;
            }
            return SPointerToRawData [i] + ( rva - SVirtualAddress[i] );
        }
    }
}
dan gibson
  • 3,605
  • 3
  • 39
  • 57