GAC Nitty Gritty, Part 2

In the first post, we've gone through GAC, SNK, and how the CLR is loading the signed assemblies, in this post we'll examine the folder structure of the GAC, and see how the GAC is working behind the scenes, for more information please refer to the first part.

GAC Folder Structure

Global assembly cache is just a physical folder; just like any other folder, but when you browse to the default GAC location at : %WinDir%\assembly, the windows shell extension SHFusion.DLL shows you the customized GAC folder, the truth is that GAC is just a windows folder, if you hit your CTRL+R, and enter %WinDir%\assembly\gac , you will get the contents of the GAC folder, however browse one level up to %WinDir%\assembly, voila, now you can the folders under the GAC location, these are processed by SHFusion.DLL to show you the customized view of the GAC contents, now we'll examine the folder structure of the GAC



  • GAC : this folder contains .NET Framework 1.x assemblies.

  • GAC_32 : this folder contains assemblies to be run on 32bit operating systems, considering the operating system word size to be 32 bits.

  • GAC_64 : this folder contains assemblies to be run on 64bit operating systems, the word size is 64 bits, so the JIT compiler compiles these assemblies considering they will be running in 64bits environment, also note that if your OS is 32 bits so you will not have this folder in your GAC, but if your OS is 64 bits, so you will have both GAC_32 and GAC_64 folders, that is because Windows 64bit is able to running 32 bits applications through emulation or what's called WoW ( Windows on Windows ), for more information about WoW 32-bit emulator, please refer to this post.

  • GAC_MSIL : this folder contains assemblies that can run on either 32bit or 64bit platforms, they are compiled into MSIL, and in runtime, the JIT (Just In Time) Compiler, compiles them to the proper machine code ( with support for target word size ).

  • NativeImages_Vx_WordSize : whenever you compile your assemblies; they are compiled to MSIL code, and in runtime JIT compiler compiles the code to the machine language, this process is costly, so if you want to boost your application performance, you can use a .NET utility called NGen.exe, this utility compiles your MSIL assemblies to the machine language right away, saving you JIT compilation time, and this greatly boosts your application performance, taking into consideration that assemblies compiled using NGen are machine specific and not deployable, so first you have to deploy your assemblies, and then run NGen.exe on them, to generate the pre-compiled assemblies, so the native image of one assembly is the precompiled assembly, and they are saved in this folder, considering framework version ( Vx ), and word size 32bits or 64bits, ( WordSize ), so you will end up with more than one NativeImages folder according to your OS word size, and .NET framework versions you have installed, NGen is an interesting topic, to get more information about it, please refer to this MSDN column CLR Inside Out .

  • Temp folders : these are temporary folders.
Note that the Processor Architecture column in the GAC view, this reflects in which folder the assembly is saved.

So how .NET framework achieves versioning, let's take one example, if you open GAC_32 folder and then open Microsoft.SqlServer.Replication folder, you will find one folder named 9.0.242.0__89845dcd8080cc91, apparently this is the folder name is the assembly name ( without .DLL extension ) , and the sub folder follows this pattern AssemblyVersion__PublicKey, go one level down and open this folder you will find the assembly named Microsoft.SqlServer.Replication.dll


Select the file, and hit Alt+Enter ( for properties ), select Version tab, you will find the comments field under other version information -> Item Name, has the value "NT INTEL X86", this explains why the assembly is placed inside GAC_32.


Select Digital Signatures tab, select Microsoft Corporation from Signature List, have a look at Timestamp column, then click Details button, click the Advanced Tab


Voila, Digest algorithm is SHA1, Digest encryption algorithm is RSA, and the message digest is there as well, so let's recall what we've explained in last post, in runtime CLR has the public key and the assembly contents ( MSIL + metadata ), it uses the assembly contents to generate a hash value using SHA1 algorithm, then it decrypts the encrypted message digest to get the original hash value ( message digest ), then it compares the one it generated first, to the one that 's just decrypted, if they are equivalent so it loads the assembly ( or installs it in the GAC if the assembly is to be installed in the GAC ), otherwise the installation or loading of the assembly is discontinued.

Now, everything is clear and we know how CLR manages versioning, so for target="_blank" each and every assembly version you install in the GAC, one folder is created behind the scenes to hold this new assembly version.

GacUtil.exe

This post is not intended to teach you how to use the GacUtil.exe, however it is very simple to install and uninstall one assembly, for detailed information about the command line of Gacutil.exe please refer to MSDN, Global Assembly Cache Tool.

Tip: one of the differences between GAC in .NET 1.x and 2.0, is that when you use GacUtil /u AssemblyName.dll, it fails to uninstall the assembly, the right way to uninstall one assembly is to just pass the assembly name with no extension, and also it's worth mentioning that you can access the GAC through a set of APIs.

References and Related Readings
I hope I could make it clear, however if you have any comments, inquires, or you find any inconsistent information, please leave me a comment.

Labels:

GAC Nitty Gritty, Part 1

Intro

Global Assembly Cache, is one of the most important scapes of .NET framework, however there are so many tips and tricks, and guidelines that you should be aware of, unfortunately most of developers when asked about GAC, they barely would tell what it stands for, GAC is a major scape of .NET architecture, and I believe that understanding what .NET is so important as I have seen so many people around arguing about .NET inner workings and how insignificant to know, I believe that if you know how .NET is doing the magic for you, this would help you so much, everyone can write code, but how many people know the architecture of .NET, not so many people, however in this post I will try to deliver my knowledge and my experience with GAC, I have read so many articles, blog posts, forums discussions, and MSDN documentation, and at the end of this post I will mentioned related readings so you can follow and get more knowledge about GAC.

GAC, Why ?

"GAC is built specifically to be a machine-wide repository for system and custom assemblies, with support for versioning", this is the ultimate objective for GAC, one location for global assemblies, so each and every .NET assembly can access the global assemblies stored in the GAC, plus you can have versioned assembly inside the GAC, and this is how .NET solved the DLL Hell classical problem , so inside the GAC you can have more than one assembly with the same name but with different versions, and in the next lines we will explain throughly how .NET achieves versioning for GAC assemblies, so to recap; GAC is a repository in which we keep global versioned assemblies!, one more thing is worth mentioning is that default machine polity grants "full trust" to all assemblies installed into the GAC, and this is a very cool feature I remember that I developed a custom ASP.NET 2.0 WebPart that I wanted to use at Sharepoint Server 2007, when I was adding the WebPart assembly in the default web application bin folder, I had to write policy file to grant my assembly permission to be used from inside MOSS 2007, whilst instaling the assembly into the GAC, it just works and you don't care about any code access security for your assembly.

GAC, How ?

To get your assembly installed into the GAC, you have to digitally sign it by generating a strong name key by launching Visual Studio 2005 Command Prompt, enter "sn -k filename.snk", this writes the file into the current location from which you are running the command, and if you build a class library project from VS.NET you can right click the project from solution explorer and click Properties, select Signing, check the Sign the assembly, from the dropdown list you can select either to create a new strong name key through VS.NET or browse to the one you've created before using the VS 2005 command prompt.

SNK, Why and How ?

You might wonder about the need for generating the strong name key file and assigning it to the assembly whilst building it, at the first place any assembly to be placed into the GAC, it has to be built with snk file, but note that it's always good to sign your assemblies regardless of being installed into the GAC, so even if you don't intend to install your assembly into the GAC; you can still sign it using a strong name key, snk gives a unique indentity to your assembly just like the GUID was a unique identifier for COM objects, also when you digitally sign your assemblies using strong name key is preventing spoofing or tampering your assemblies, that may be hacked by any malicious source, for example if you have BizTalk Server 2006 installed on your machine, check your GAC at %windir%\assembly, you will find out that almost all BizTalk Server assemblies hold public key token ( more about this later ), as 31bf3856ad364e35, this tells the .NET CLR of your machine that these assemblies are coming from the source they are coming from ( I know it's somehow difficult to grasp ), now we'll examine the contents of one digitally signed assembly strong name; the strong name of one assembly consists of the following :

  • Public Key Token: also know as the public key ( example: 31bf3856ad364e35 ).
  • Assembly Name: it's the assembly name after suppressing ".dll", ( example: Microsoft.BizTalk.Messaging).
  • Assembly version: standard pattern is, Major.Minor.Build.Revision, ( example: 3.0.1.0 ).
  • Culture : culture identity value, ( example : Neutral ).
  • Digital Signature: this is hash value, more about that later, encrypted using the private key.
The process of the signing and verification can be depicted using the following process diagram:



Compilation Time
  1. When you create snk file using the SNK.exe, the public and private keys are written into the file, and one more thing to notice, that you can protect your snk file using a password.
  2. After binding your assembly with the strong name key, and when you compile your assembly, the compiler generates a "Hash Value", using the assembly IL code and meta data as an input and the algorithm used for hashing is SHA-1 Hash algorithm.
  3. Compiler reads the private key from the snk file, and takes the hash value generated in step 2 as an input for encryption, compiler uses RSA Public Key Algorithm to generate the digital signature that will be shipped with the assembly, while the private key is never included in the assembly metadata, it is always kept in a secure place.

Run Time

  1. Whenever the CLR loads an assembly that's digitally signed, regardless of being in the GAC or not, CLR reads the digital signature and tries to verify it, then it compares the digital signature included in the assembly, to the one that it evaluates at runtime if they are equivalent the CLR continues loading the assembly, otherwise the assembly is not loaded, first step CLR carries out is to generate a hash value from assembly contents, IL + metadata, this is done using the same hashing algorithm used by the compiler it's SHA-1.
  2. CLR decrypts original hash using the public token/key and same encryption algorithm used before, RSA Public Key algorithm, remember the digital signature is just an encrypted hash value using the private key.
  3. CL compares original hash value ( from step 2 ) and current hash value ( from step 1 ), if they are the same, CLR loads the assembly, otherwise it's not loading the assembly, and if the hash values are not equal, that means the assembly contents have been changed or they are not signed using the correct snk, that's coming from specific publisher/source.
Note that the assembly that is strongly named undergoes the runtime CLR signature verification every time it is getting called by another assembly , on the other hands this process is done only once for GAC assemblies while installing the assembly into the GAC, and then the CLR doesn't check it every time it's getting called, considering it's coming from a trusted source, because you wouldn't install an assembly into the GAC if you don't have administrative rights on the target machine.

Inside GAC

GAC is located at %windir%\assembly if you try to browse to this location, windows shell extension will load and show the contents of the GAC, the shell is named SHFusion.dll, and it's worth mentioning that the first codename for GAC versioning was Fusion, however after you browse to the GAC location you get the assemblies listed as shown below:


I will stop at this point and the next post I will go deeper into the GAC, and explain how the .NET framework is achieving the versioning behind the scenes.

Disclaimer: In part 2, I will gather and mention references and related readings.

Labels: