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();
}
}
}