0

I'm trying to understand DLL identification in the GAC by parsing the top-voted answer in this StackOverflow question: What is the GAC in .NET?

The writer walks us through the tree structure for C:\Windows\assembly\GAC_64\System.Data and concludes with . . .

Here you can see version 2.0.0.0__b77a5c561934e089 of System.Data.

A DLL is identified by 5 parts:

Name
Version
Architecture
Culture
Public Key

... and that's the part I'm unclear on. Once I've gotten to the leaves of the tree how do I extract those 5 parts of the DLL's identification, and is there any relation to the name of the assembly?

In the example I'm working with the assemblies are directories with names like:

v4.0_2.0.6178.1045__d45c8e156fba2841

... and inside that directory there's a DLL with a name like

Ab3d.DXEngine.dll

... so how do I extract the five parts of its identification?

EDIT:

Lifu Huang, below suggested that there was a way to access ILDASM from visual studio. There wasn't a built-in way from mine (VS 2013) but there was a way to ADD it, described here: http://dailydotnettips.com/2016/01/05/did-you-know-you-can-launch-ildasm-tool-from-inside-visual-studio-itself-how/ ...so I did that and when I tried to run ILDASM on a DLL in the GAC I got an error from ILDASM saying "Protected module - cannot disassemble"

Community
  • 1
  • 1
user316117
  • 7,971
  • 20
  • 83
  • 158
  • Hmm, those assemblies don't seem to belong there. The v4.0 GAC is stored in c:\windows\microsoft.net\assembly. Review the install procedure. – Hans Passant Jan 13 '17 at 18:23
  • I didn't put them there. But my question is, how do I easily extract the five parts mentioned in the SO post? – user316117 Jan 13 '17 at 18:25

3 Answers3

0

how do I extract the five parts of its identification?

You can find them in the CLR header and the metadata of an assembly.

Actually every managed module (dll or exe) consists of four parts:

  • PE32 or PE32+ header (Information used by Windows)
  • CLR header (Information used by CLR)
  • Metadata (Here is a blog post introducing this topic). There are mainly three types of metadata tables:

    • Assembly Manifest (global information about the assembly)
    • Definition Tables (information about things defined in this module, e.g. ModuleDef, TypeDef, MethodDef, PropertyDef)
    • Reference Tables (information about thing referred by this module, e.g. AssemblyRef, ModuleRef, TypeRef, MemberRef)
  • IL Code (IL code generated from the source code)

Lifu Huang
  • 11,930
  • 14
  • 55
  • 77
  • Thanks, but could you be a little more specific? Is there an easy way to see these? Hopefully something less labor-and-typing intensive than constructing an _ildasm_ command with a full path and options from a developer command-line prompt. – user316117 Jan 13 '17 at 18:24
  • 1
    @user316117 I remember visual studio SDK comes with a ildasm UI, which might ease the pain of constructing command. – Lifu Huang Jan 13 '17 at 18:27
  • Thanks, I tried that - **see my edit to my question** - and I ran ILDASM and got an error. This can't be that hard- people must do it every day - how is it done? – user316117 Jan 13 '17 at 18:44
  • @user316117, it might because of many reasons, do you add `SuppressIldasmAttribute` to you assembly (see https://blogs.msdn.microsoft.com/vijaysk/2007/12/05/preventing-ildasm-from-disassembling-your-assembly/)? Btw, this should really be another question :) – Lifu Huang Jan 13 '17 at 18:49
  • It's not my assembly - it's installed on this PC. Are you saying that the Name, Version, and Architecture cannot be read/seen for some assemblies because of some attribute the creator added? – user316117 Jan 13 '17 at 19:00
  • @user316117. Yes and no, `SuppressIldasmAttribute` is just a way to protect program from reverse engineering by **Unauthenticated third party**, not including CLR itself, so it is just you who cannot read these information, but CLR can. But on the other hand, AFAIK, that attribute is only effective for ILDASM, which means you can still use other third-party disassembly tool to see the information. – Lifu Huang Jan 13 '17 at 19:04
0

You can read the metadata programmatically if you know the format. Here is some code that does it. Link

    public static CorFlagsReader ReadAssemblyMetadata(Stream stream)
    {
        if (stream == null)
        {
            throw new ArgumentNullException("stream");
        }

        long length = stream.Length;
        if (length < 0x40)
            return null;

        BinaryReader reader = new BinaryReader(stream);

        // Read the pointer to the PE header.
        stream.Position = 0x3c;
        uint peHeaderPtr = reader.ReadUInt32();
        if (peHeaderPtr == 0)
            peHeaderPtr = 0x80;

        // Ensure there is at least enough room for the following structures:
        //     24 byte PE Signature & Header
        //     28 byte Standard Fields         (24 bytes for PE32+)
        //     68 byte NT Fields               (88 bytes for PE32+)
        // >= 128 byte Data Dictionary Table
        if (peHeaderPtr > length - 256)
            return null;

        // Check the PE signature.  Should equal 'PE\0\0'.
        stream.Position = peHeaderPtr;
        uint peSignature = reader.ReadUInt32();
        if (peSignature != 0x00004550)
            return null;

        // Read PE header fields.
        ushort machine = reader.ReadUInt16();
        ushort numberOfSections = reader.ReadUInt16();
        uint timeStamp = reader.ReadUInt32();
        uint symbolTablePtr = reader.ReadUInt32();
        uint numberOfSymbols = reader.ReadUInt32();
        ushort optionalHeaderSize = reader.ReadUInt16();
        ushort characteristics = reader.ReadUInt16();

        // Read PE magic number from Standard Fields to determine format.
        PEFormat peFormat = (PEFormat)reader.ReadUInt16();
        if (peFormat != PEFormat.PE32 && peFormat != PEFormat.PE32Plus)
            return null;

        // Read the 15th Data Dictionary RVA field which contains the CLI header RVA.
        // When this is non-zero then the file contains CLI data otherwise not.
        stream.Position = peHeaderPtr + (peFormat == PEFormat.PE32 ? 232 : 248);
        uint cliHeaderRva = reader.ReadUInt32();
        if (cliHeaderRva == 0)
            return new CorFlagsReader(0,0,0, peFormat);

        // Read section headers.  Each one is 40 bytes.
        //    8 byte Name
        //    4 byte Virtual Size
        //    4 byte Virtual Address
        //    4 byte Data Size
        //    4 byte Data Pointer
        //  ... total of 40 bytes
        uint sectionTablePtr = peHeaderPtr + 24 + optionalHeaderSize;
        Section[] sections = new Section[numberOfSections];
        for (int i = 0; i < numberOfSections; i++)
        {
            stream.Position = sectionTablePtr + i * 40 + 8;

            Section section = new Section();
            section.VirtualSize = reader.ReadUInt32();
            section.VirtualAddress = reader.ReadUInt32();
            reader.ReadUInt32();
            section.Pointer = reader.ReadUInt32();

            sections[i] = section;
        }

        // Read parts of the CLI header.
        uint cliHeaderPtr = ResolveRva(sections, cliHeaderRva);
        if (cliHeaderPtr == 0)
            return null;

        stream.Position = cliHeaderPtr + 4;
        ushort majorRuntimeVersion = reader.ReadUInt16();
        ushort minorRuntimeVersion = reader.ReadUInt16();
        uint metadataRva = reader.ReadUInt32();
        uint metadataSize = reader.ReadUInt32();
        CorFlags corflags = (CorFlags)reader.ReadUInt32();

        // Done.
        return new CorFlagsReader(majorRuntimeVersion, minorRuntimeVersion, corflags, peFormat);
    }
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • Thanks but the reason why I linked to the original question and referenced the top-ranked answer was because he seemed to be showing us stuff from the command-line and then concluded by talking about those 5 attributes, and that answer got 100 upvotes so I assumed everyone but me could see the connection. I was hoping I wouldn't have to write code to see basic stuff like the name and version number of a GAC DLL. If I want to use a GAC DLL as a reference in a program don't I have to know the version number and architecture? Do people have to write programs to do this? – user316117 Jan 13 '17 at 22:10
  • Oh well if you just want the name and version, see my other answer that I am about to post. – John Wu Jan 13 '17 at 22:31
0

Much of the information you need is readily available from System.Reflection AssemblyName object.

This code will get the strong name, processor architecture, version, and public key token for System.Web.dll:

var n = System.Reflection.AssemblyName.GetAssemblyName(@"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\System.web.dll");
Console.WriteLine(String.Format("{0} {1} {2} {3}", n.Name, n.Version, n.ProcessorArchitecture, BitConverter.ToString(n.GetPublicKeyToken())));

Output:

System.Web 4.0.0.0 Amd64 B0-3F-5F-7F-11-D5-0A-3A

If you don't want to write a program just for this, you could also use a PowerShell script to do the same thing:

[reflection.assemblyname]::GetAssemblyName("${pwd}\System.Web.dll") | fl
Community
  • 1
  • 1
John Wu
  • 50,556
  • 8
  • 44
  • 80