How to Determine if a file is a .Net assembly (in Delphi and C#)


Given a file, we would like to check if it is a valid .Net assembly file.
How would you go about it?

1. A couple of words about the PE file format


.Net assemblies are valid PE files.   A PE file consists of:

      *     MS-DOS header  
      *     Stub Program
      *     PE file signature
      *     PE file header (This is where we position our stream)
      *     PE optional header
      *     Section headers   (This is where the RVA15 is)
      *     Section bodies


The PE file header is where we position our file stream at byte offset 60. The 32 bits at this position are the magical number whose value determine if this is a 32 bit (value = 0x010B) or 64 bit (value=0x020B) PE image.   This is important since there is a different offset to the data dictionary for these different types of images. 32 bit images have 0x60 offset to dictionary while 64bit images have a 32 bit offset to the data dictionary. The RVA dictionary is a sequence of 16 pairs of 32 bit. Each RVA entry is 8 bytes. Skipping to RVA15 means skipping 14*8 = 112 = 0x70

A PE file is   considered a .Net assembly   when RVA15 contains a non zero value. RVA 15 points to the CLI header.
For Further details about the CLI header:
http://dotnet.di.unipi.it/EcmaSpec/PartitionII/cont24.html
Microsoft PE file format:
http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx


2. IsDotNetAssembly - Delphi version

function IsDotNetAssembly(FileName:  string):boolean;
var
fs: TFileStream;
peHeader: LongWord;
peMagicNumber: Word; //contains if it is 32bit or 64bit image
RVA15Value: LongWord;
DictionaryOffset: LongWord;
begin

result := false;
fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);

try
fs.Position := $3C; //PE Header start offset.
fs.ReadBuffer(peHeader,sizeOf(peHeadeR));


fs.Position := peHeader + $18;
fs.ReadBuffer(peMagicNumber, sizeOf(peMagicNumber));

case peMagicNumber of
$010B: DictionaryOffset := $60; //32 bit Image
$020B: DictionaryOffset := $70; //64 bit Image
else
raise Exception.Create('Invalid Image Format');
end;

//Position to RVA 15 of the DataDictionary.
fs.Position := peHeader + $18 + DictionaryOffset + $70;

//Read the value.
fs.ReadBuffer(RVA15Value,sizeOf(RVA15Value));

//If this value is non zero this is a clr assembly
result := RVA15Value <> 0;

finally
fs.free;
end;
end;

3. IsDotNetAssembly C# Implementation  

Just for the fun of it, here is the C# version.

private bool IsDotNetAssembly(string fileName)
{
        using (FileStream fs =
                new
FileStream(fileName, FileMode.Open, FileAccess.Read))
        {

                try
                {
                        using (BinaryReader binReader = new BinaryReader(fs))
                        {
                       
try
                              {


                         
            fs.Position = 0x3C; //PE Header start offset
                               
      uint headerOffset = binReader.ReadUInt32();  

                                      fs.Position = headerOffset + 0x18;
       
                              UInt16 magicNumber = binReader.ReadUInt16();

                                      int dictionaryOffset;
                       
              switch (magicNumber)
                               
        {
                                              case 0x010B: dictionaryOffset = 0x60; break;
       
                                      case 0x020B: dictionaryOffset = 0x70; break;
               
                              default:
                       
                              throw new Exception("Invalid Image Format");
                               
        }

                                      //position to RVA 15
       
                              fs.Position = headerOffset + 0x18 +

dictionaryOffset + 0x70;


               
                      //Read the value
                               
      uint rva15value = binReader.ReadUInt32();
                               
      return rva15value != 0;

                                finally
                                {
                                    binReader.Close();
                        }
                       
}
                }

                finally
                {
                        fs.Close();
               
}
        }

}

Comments are closed