Rupreet's Weblog

Looking deep inside under the hood

  Home  |   Contact  |   Syndication    |   Login
  14 Posts | 0 Stories | 46 Comments | 58 Trackbacks

News

Archives

Post Categories

            There are some scenarios where need to check programmatically if the given file is a .NET assembly or not. How do we do that? One way is to use reflection and try to load that file (assembly). If the assembly gets loaded, and doesn’t throw any exception, then yes, it’s a valid .NET assembly. If it’s not a valid file, then it’ll throw a “BadImageFormatException”. The idea of checking weather a file is assembly or not by loading it and checking if exception is thrown or not; doesn’t seem to be too clean way at least to me. So I was trying some other way with which I can get to know if the file is an assembly or not. Here is the solution I got;

 

            .NET assemblies are regular Win32 PE files. Operating System doesn’t differentiate between .NET assemblies and Win32 executable binaries; for it, they are same normal PE files. So how does system get to know if this file is a managed assembly and if yes, how to load CLR? How OS loads CLR is a separate discussion, I’ll just talk about how to check this header for validating the file if it’s a managed assembly or not. Well if we go through the ECMA Specifications Partition II – Metadata which is shipped along with .NET SDK, we see that we have a separate CLI Header in the PE Format. It is the 15th data directory in the PE Optional Headers. So, in simple terms, if we have value in this data directory, then it means that this is a valid .NET assembly, otherwise it is not.

 

Here is the code sample that does the same:

 

private void GetHeaders()

{

    uint peHeader;

    uint peHeaderSignature;

    ushort machine;

    ushort sections;

    uint timestamp;

    uint pSymbolTable;

    uint noOfSymbol;

    ushort optionalHeaderSize;

    ushort characteristics;

    ushort dataDictionaryStart;

    uint[] dataDictionaryRVA = new uint[16] ;

    uint[] dataDictionarySize = new uint[16];

 

 

    Stream fs = new FileStream(@"D:\Test.exe", FileMode.Open,

FileAccess.Read);

          BinaryReader reader = new BinaryReader(fs);

 

          //PE Header starts @ 0x3C (60). Its a 4 byte header.

          fs.Position = 0x3C;

              

          peHeader = reader.ReadUInt32();

 

          //Moving to PE Header start location...

          fs.Position = peHeader;

          peHeaderSignature = reader.ReadUInt32();

               

    //We can also show all these value, but we will be       

          //limiting to the CLI header test.

               

    machine = reader.ReadUInt16();

          sections = reader.ReadUInt16();

          timestamp = reader.ReadUInt32();

          pSymbolTable = reader.ReadUInt32();

          noOfSymbol = reader.ReadUInt32(); 

          optionalHeaderSize = reader.ReadUInt16();

          characteristics = reader.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 :)

            */

dataDictionaryStart = Convert.ToUInt16 (Convert.ToUInt16

(fs.Position) + 0x60);               

fs.Position = dataDictionaryStart;

            for (int i = 0; i < 15; i++)

            {

                dataDictionaryRVA[i] = reader.ReadUInt32();

                dataDictionarySize[i] = reader.ReadUInt32(); 

            }

            if (dataDictionaryRVA[14] == 0)

            {

                Console.WriteLine("This is NOT a valid CLR File!!");

            }

            else

            {

                Console.WriteLine("This is a valid CLR File..");

            }

            fs.Close();

}

 

To simplify more, you can directly move your stream position to the 15th Data Directory location and check the first 8 bytes (4 bytes for RVA and other 4 bytes for Size).

posted on Wednesday, November 2, 2005 11:09 AM

Feedback

# re: To Check if the given file is .NET assembly or not? 11/2/2005 12:31 PM arun
Nice write-up!

# re: How to determine whether a file is a .NET Assembly or not? 11/7/2005 4:03 PM Navdeep Bhardwaj
Great Article

# re: How to determine whether a file is a .NET Assembly or not? 12/19/2005 2:17 PM Manish Tandon
Good One!!!

# re: How to determine whether a file is a .NET Assembly or not? 12/19/2005 2:17 PM Manish Tandon
Good One!!!

# re: How to determine whether a file is a .NET Assembly or not? 12/19/2005 2:17 PM Manish Tandon
Good One!!!

# re: How to determine whether a file is a .NET Assembly or not? 12/19/2005 2:17 PM Manish Tandon
Good One!!!

# re: How to determine whether a file is a .NET Assembly or not? 12/19/2005 2:17 PM Manish Tandon
Good One!!!

# re: How to determine whether a file is a .NET Assembly or not? 1/13/2006 6:50 AM Craig Anderson
Nicely done. In addition, I would check the "magic" field of the PE header (the first 2 bytes) to ensure it is a 32-bit image file (0x010B). The data directory table for 64-bit PE headers start at offset 112 - as opposed to 96 for 32-bit headers.

# re: How to determine whether a file is a .NET Assembly or not? 1/13/2006 8:12 PM Rupreet Singh Gujral
Yes, there can be many checks added to this code. I just tried to exemplify how we can validate an assembly.

# re: How to determine whether a file is a .NET Assembly or not? 6/12/2006 1:44 PM add
Good Article, So Add some good comments.
In a M$ compiler(I don't what it exactly is, because of Reverse S E ), the startup code includes a function which use is to check whether it is a .Net assembly or not.
.text:01002E25 IsPEdotNetAssembly proc near ; CODE XREF: start+9Fp
.text:01002E25 cmp word ptr ds:1000000h, 5A4Dh
.text:01002E2E jnz short loc_1002E57
.text:01002E30 mov eax, ds:100003Ch
.text:01002E35 lea eax, [eax+1000000h]
.text:01002E3B cmp dword ptr [eax], 4550h
.text:01002E41 jnz short loc_1002E57
.text:01002E43 movzx ecx, word ptr [eax+18h]
.text:01002E47 cmp ecx, 10Bh
.text:01002E4D jz short loc_1002E6D
.text:01002E4F cmp ecx, 20Bh
.text:01002E55 jz short loc_1002E5A
.text:01002E57
.text:01002E57 loc_1002E57: ; CODE XREF: IsPEdotNetAssembly+9j
.text:01002E57 ; IsPEdotNetAssembly+1Cj ...
.text:01002E57 xor eax, eax
.text:01002E59 retn
.text:01002E5A ; ??????????????????????????????????????
.text:01002E5A
.text:01002E5A loc_1002E5A: ; CODE XREF: IsPEdotNetAssembly+30j
.text:01002E5A cmp dword ptr [eax+84h], 0Eh
.text:01002E61 jbe short loc_1002E57
.text:01002E63 xor ecx, ecx
.text:01002E65 cmp [eax+0F8h], ecx
.text:01002E6B jmp short loc_1002E7B
.text:01002E6D ; ??????????????????????????????????????
.text:01002E6D
.text:01002E6D loc_1002E6D: ; CODE XREF: IsPEdotNetAssembly+28j
.text:01002E6D cmp dword ptr [eax+74h], 0Eh
.text:01002E71 jbe short loc_1002E57
.text:01002E73 xor ecx, ecx
.text:01002E75 cmp [eax+0E8h], ecx
.text:01002E7B
.text:01002E7B loc_1002E7B: ; CODE XREF: IsPEdotNetAssembly+46j
.text:01002E7B setnz cl
.text:01002E7E mov eax, ecx
.text:01002E80 retn
.text:01002E80 IsPEdotNetAssembly endp
.text:01002E80

Enjoy it!!! Because the codes can teach you more exactly and more carefully.

# re: How to determine whether a file is a .NET Assembly or not? 6/12/2006 1:47 PM Add
However, a good job!!!thanks.

# re: How to determine whether a file is a .NET Assembly or not? 6/26/2007 1:06 PM Kumar Guddu
good one

# re: How to determine whether a file is a .NET Assembly or not? 12/5/2007 5:36 AM Russell
Nice work Rupreet. I've adapted your sample into a helper class and posted the code to my blog: http://blog.ruski.co.za/post/2007/12/Determine-if-a-file-is-a-NET-assembly.aspx

# re: How to determine whether a file is a .NET Assembly or not? 12/19/2007 7:43 AM irnbru
didnt you mean CLR Header instead of CLI Header??

# re: How to determine whether a file is a .NET Assembly or not? 12/21/2007 4:52 PM Amr A.
This article is very informative and there is a lot to know just from looking at the code however I would be very thankful if someone post how to infect a .Net assembly, I read before that there is already a virus called Donut that successfully infected .Net assemblies.

I'm not asking for such knowledge to develop viruses or to use such knowledge in anything harmful however I'm curious about how viral codes get into an assembly regardless if it is a managed or unmanaged assembly.

# re: How to determine whether a file is a .NET Assembly or not? 2/4/2009 6:18 PM James
This example is not reliable, and ignores recommendations in Microsoft's PE specification to check the values of size fields so you don't read the wrong data.

It will also fail with PE32+ image file formats, as the location of the data dictionary depends on the image file format.

Poor example.

# re: How to determine whether a file is a .NET Assembly or not? 8/20/2010 1:21 AM GangL
James, until you have something better, please shut up.

# re: How to determine whether a file is a .NET Assembly or not? 4/24/2012 6:18 PM html5 media player
Thanks for sharing your info. I really appreciate your efforts and I will be waiting for your further write ups thanks once again.

# re: How to determine whether a file is a .NET Assembly or not? 12/4/2012 8:55 PM Internationale Spedition
Thanks for this post. I try to use this knowledge in my work

# re: How to determine whether a file is a .NET Assembly or not? 3/3/2014 2:55 PM Marco Hentschel
I hope I've found the shortest version ...
private bool IsDotNetAssembly(string assemblyPath)
{
Stream fs = null;
try
{
fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read);
//PE Header starts @ 0x3C (60). Its a 4 byte header.
fs.Position = 0x3C;
//Moving to PE Header start locaition...
fs.Read(buffer, 0, 4);
fs.Position = BitConverter.ToUInt32(buffer, 0) + 232;
fs.Read(buffer, 0, 4);
return 0 != buffer[0] || 0 != buffer[1] || 0 != buffer[2] || 0 != buffer[3];
}
finally
{
if (null != fs)
{
fs.Close();
}
}
}

Post A Comment
Title:
Name:
Email:
Comment:
Verification: