目录结构:

contents structure [+]

1. 程序集的简介

程序集是一个.dll或者一个.exe文件,里面包含了很多类的定义和资源。比如我们使用的System.dll,System.Core.dll...文件。

CLR支持两种程序集:弱命名程序集和强命名程序集。弱命名和强命名程序集的结构完全相同。两者的区别在于:强命名程序集使用发布者的公钥/私钥进行了签名。这对秘钥允许对程序集进行唯一性的标识、保护和版本控制,并允许程序部署到用户机器的任何地方。

程序集可采用两种方式部署:私有或全局。私有部署的程序集是指部署到应用程序基目录或者某个子目录下面。全局部署是指部署到一些公认位置的程序集。弱命名程序集只能私有部署,强命名程序集既可以私有部署也可以全局部署。
如下:

  全局部署 私有部署
强命名程序集 是 是
弱命名程序集 否 是

2. 为程序集分配强名称

要由多个应用程序访问的程序集必须放到公认的目录。另外,检测到对程序集的引用时,CLR必须能自动检查该目录。

CLR支持对程序集进行唯一性标识的机制,这就是“强命名程序集”。强命名程序集共有4个重要特征,它们共同对程序集进行唯一性的标识:文件名(不计扩展名)、版本号、语言文化和公钥。由于公钥数字很大,所以经常使用从公钥派生的小哈希值,称为公钥标记(public key token)。例如下面4个程序集标识字符串显示了4个完全不同的程序集文件:

"MyTypes,Version=1.0.8123.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"
"MyTypes,Version=1.0.8123.0,Culture="en-Us",PublicKeyToken=b77a5c561934e089"
"MyTypes,Version=2.0.8123.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"
"MyTypes,Version=1.0.8123.0,Culture=neutral,PublicKeyToken=a0371q001382p23a"

2.1 如何指定程序集的版本资源信息

在PE(Portable Executable 可执行文件)文件中能嵌入标准的Win32版本资源。在生成程序集时,应该使用特性来设置各种版本资源字段,每种语言生成程序集版本资源的特性代码的都不一样,下面介绍使用VB语言生成PE文件时如何指定版本资源信息。

首先在Microsoft Visual Studio上新建一个VB的类库项目。在项目的本地文件夹下,可以找到AssemblyInfo.vb的文件

然后打开这个文件,可以看到其中的内容如下(笔者经过了修改,并且只贴出了部分代码):

<Assembly: AssemblyTitle("名称")>
<Assembly: AssemblyDescription("描述")>
<Assembly: AssemblyCompany("公司")>
<Assembly: AssemblyProduct("产品")>
<Assembly: AssemblyCopyright("Copyright © Microsoft 2018")>
<Assembly: AssemblyTrademark("商标")>
<Assembly: ComVisible(False)>
<Assembly: AssemblyVersion("1.0.0.0")>
<Assembly: AssemblyFileVersion("1.0.0.0")>

然后在Visual Studio中重新生成文件,找到生成的PE文件,右键“属性”->"详细信息",就可以看到如下图所示:

通过上面的方式指定程序集有点繁琐,好在Visual Studio为我们提供了便利,只需要在Visual Studio中右击项目,选择“属性”->"应用程序"->"程序集信息",就可以进行修改了。

2.2 如何对程序集签名

在上面我们已经知道了,弱命名程序集经过签名(使用公钥和私钥签名)就会成为强命名程序集,下面介绍如何签名程序集。

首先使用SN.exe来获得秘钥,SN.exe程序一般在C:\Program Files (x86)\Microsoft SDKs\Windows的子目录下面,下面使用SN.exe来创建一个包含公钥、私钥的文件。

sn -k MyCompany.snk

创建的MyCompany.snk是包含了一对公钥和秘钥的文件。公钥的数字很大,如果愿意的话,可以再次使用sn.exe来查看完整的公钥值和公钥标记值。只需要执行两次sn.exe。
第一次使用-p创建只含公钥的文件

sn -p MyCompany.snk MyCompany.PublicKey sha256

第二次使用-tp显示公钥标记和公钥本身。

sn -tp MyCompany.PublicKey 

然后会得到类似如下的输出:

Microsoft(R) .NET Framework 强名称实用工具 版本 4.0.30319.17929
版权所有(C) Microsoft Corporation。保留所有权利。 公钥(哈希算法: sha256):
002400000c800000940000000602000000240000525341310004000001000100f70211026bf0c5
04ec93bd52e3c7c14373e18f65d385e7151fc2de3559b50668cc8f4d5eae739745ead0d0e16036
d2aa033b8ec9366e92cf2d90a5a0d02ae00ee7a915df4e1eeb01d74a473063b741c0473c345254
211060134f626c30e3bb1057e43fd56ee04810713ba05101a32d591278d6a0497d14db70e488a3
1a731cbe 公钥标记为 47ea4c468f699f0b

在创建好公钥/私钥标记对后,就可以利用具体语言的具体命令来创建强命名程序集了。

3. 全局程序集缓存(GAC)

我们已经知道了如何创建强命名程序集了,强命名程序集可以私有部署,也可以全局部署(部署到GAC中)。

这里先介绍一下全局程序集缓存(Global Assembly Cache,GAC)的概念:由多个应用程序访问的程序集必须放到公认的目录,而且CLR在检测到该程序集的引用时,必须知道检查该目录,这个公认的位置就是GAC。

GAC一般在如下的位置:

%SystemRoot%\Microsoft.NET\Assembly

GAC的目录是结构化的:其中包含许多子目录,子目录名称用算法生成。永远不要将程序集手动复制到GAC目录,相反应该用工具来完成。

开发和测试时在GAC中安装命名程序集最常用的工具是GACUtil.exe,该工具一般在 C:\Program Files (x86)\Microsoft SDKs\Windows 的子目录下面。

4. 如何查看程序集的信息

我们已经知道了一个程序集由四部分标识,分别为:文件名(不计扩展名)、版本号、语言文化和公钥标记(公钥很大,一般不使用)。弱命名程序集没有公钥(自然也没有公钥标记),若命名程序集经过公钥/私钥对签名后,就称为了强命名程序集。

当我们知道一个程序集后,如果获得这些标识呢?在程序集中,没有直接给我们相关的信息,这些信息都是分散的,需要我们去分别查找。

使用ILDASM反编译一个程序集,点击“视图”->“元信息”->“显示!”,然后查找“Assembly”的定义部分(Assembly一般位于文档最底部),如图:

其中Name代表文件名;Version代表版本号;Locale代表语言文化,如果空,则表明是中性语言(neutral);Public Key则表示是公钥。

现在我们知道了程序集全部信息,图片中的public Key是完整的公钥而不是公钥标记。可以按照下面的过程得到公钥标记,

利用sn.exe程序的 -T参数
比如:

sn -T System.Data.dll

然后就可以看到如下的输出:

Microsoft(R) .NET Framework 强名称实用工具 版本 4.0.30319.17929
版权所有(C) Microsoft Corporation。保留所有权利。 公钥标记为 b77a5c561934e089

除此之外,若强命名程序集安装在GAC中的话,直接通过文件夹名称的后缀就可以看出:

5. 强命名程序集防篡改

用私钥对程序集进行签名,并将公钥和签名嵌入程序集,CLR就可验证程序集未被修改或损坏。程序集安装到GAC时候,系统对包含清单的那个文件内容进行哈希处理,将哈希值与PE文件中嵌入的RSA数字签名进行比较(在用公钥解除了签名之后)。如果两个值完全一致,表明文件内容未被篡改。此外,系统还对程序集的其他文件的内容进行哈希处理,并将哈希值与清单文件的FileDef表中存储的哈希值进行比较。任何一个哈希值不匹配,表明至少有一个文件被篡改,程序集将无法安装到GAC。

应用程序需要绑定到程序集时,CLR根据被引用程序集的属性(名称、版本、语言文化和公钥)在GAC中定位查找该程序集。如果被引用的程序集不在GAC中,CLR会查找引用程序的基目录,然后查找应用程序配置文件中的任何私有路径,如果还找不到就会抛出System.IO.FileNotFoundException成一行。

如果强命名程序集文件从GAC之外的位置加载(通过应用程序的基目录,或者通过配置文件中的codeBase元素),CLR会在程序集加载后比较哈希值。也就是说,每次应用程序执行并加载程序集时,都会对文件进行哈希处理,以牺牲性能为代价,保证程序集文件中的内容没有被篡改。

下面这张图可以帮助理解该流程:

通过这张图片可以清楚的看出,MyLibrary.dll文件进行强命名签名时,会把MyLibrary.dll文件进行哈希值处理,并且将结果值用秘钥进行处理,再将结果值嵌入到CLR头部中。把公钥嵌入到元数据中。

当MyLibrary.dll被加载时,程序会用清单中的公钥对CLR头中的数字签名进行解密操作,和再次对MyLibrary.dll进行哈希处理,然后比较这两个结果值,如果这两个值不一致的话,说明有文件已经被篡改,将会阻止运行。

【CLR】详解CLR中的程序集的更多相关文章

  1. 【转】详解C#中的反射

    原帖链接点这里:详解C#中的反射   反射(Reflection) 2008年01月02日 星期三 11:21 两个现实中的例子: 1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内 ...

  2. jQuery:详解jQuery中的事件(二)

    上一篇讲到jQuery中的事件,深入学习了加载DOM和事件绑定的相关知识,这篇主要深入讨论jQuery事件中的合成事件.事件冒泡和事件移除等内容. 接上篇jQuery:详解jQuery中的事件(一) ...

  3. 图文详解Unity3D中Material的Tiling和Offset是怎么回事

    图文详解Unity3D中Material的Tiling和Offset是怎么回事 Tiling和Offset概述 Tiling表示UV坐标的缩放倍数,Offset表示UV坐标的起始位置. 这样说当然是隔 ...

  4. 详解Webwork中Action 调用的方法

    详解Webwork中Action 调用的方法 从三方面介绍webwork action调用相关知识: 1.Webwork 获取和包装 web 参数 2.这部分框架类关系 3.DefaultAction ...

  5. 【转】详解JavaScript中的this

    ref:http://blog.jobbole.com/39305/ 来源:foocoder 详解JavaScript中的this JavaScript中的this总是让人迷惑,应该是js众所周知的坑 ...

  6. 深入详解SQL中的Null

    深入详解SQL中的Null NULL 在计算机和编程世界中表示的是未知,不确定.虽然中文翻译为 “空”, 但此空(null)非彼空(empty). Null表示的是一种未知状态,未来状态,比如小明兜里 ...

  7. java 乱码详解_jsp中pageEncoding、charset=UTF -8"、request.setCharacterEncoding("UTF-8")

    http://blog.csdn.net/qinysong/article/details/1179480 java 乱码详解__jsp中pageEncoding.charset=UTF -8&quo ...

  8. 详解Objective-C中委托和协议

    Objective-C委托和协议本没有任何关系,协议如前所述,就是起到C++中纯虚类的作用,对于“委托”则和协议没有关系,只是我们经常利用协议还实现委托的机制,其实不用协议也完全可以实现委托. AD: ...

  9. 举例详解Python中的split()函数的使用方法

    这篇文章主要介绍了举例详解Python中的split()函数的使用方法,split()函数的使用是Python学习当中的基础知识,通常用于将字符串切片并转换为列表,需要的朋友可以参考下   函数:sp ...

  10. 详解Java中的clone方法

    详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...

随机推荐

  1. Python hashlib、hmac加密模块

    #用于加密的相关操作,3.x里代替了md5模块和sha模块,主要提供sha1,sha224,sha256,sha384,sha512,md5算法 #sha2为主流加密算法,md5加密方式不如sha2 ...

  2. windows下的python环境搭建(python2和python3不兼容,python2用的多)

    Windows平台下搭建python开发环境 以下为在 Window 平台上安装 Python 的简单步骤: 打开WEB浏览器访问http://www.python.org/download/ 在下载 ...

  3. 【Java】 剑指offer(51)数组中的逆序对

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成 ...

  4. ubuntu 18图形界面

    sh .start_mode.sh: 换操作方式(图形界面,终端界面): 桌面版: 界面中 ctrl + shift + t 打开终端 ctrl + shift + "+" 放大字 ...

  5. anaconda3下配置python-3.5+tensorflow-gpu-1.9.0人脸识别项目环境

    https://www.cnblogs.com/31415926535x/p/10620732.html 之前为了配置tensorflow-gpu的环境又是装cuda,又是装cudnn,还有tenso ...

  6. u3d 创建元件 预制件 U3d creates component prefabricated parts

    u3d 创建元件 预制件 U3d creates component prefabricated parts 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@ ...

  7. luogu P4688 [Ynoi2016]掉进兔子洞 bitset 莫队

    题目链接 luogu P4688 [Ynoi2016]掉进兔子洞 题解 莫队维护bitset区间交个数 代码 // luogu-judger-enable-o2 #include<cmath&g ...

  8. bzoj4337: BJOI2015 树的同构 树哈希判同构

    题目链接 bzoj4337: BJOI2015 树的同构 题解 树哈希的一种方法 对于每各节点的哈希值为hash[x] = hash[sonk[x]] * p[k]; p为素数表 代码 #includ ...

  9. 洛谷P1823 音乐会的等待

    To 洛谷.1823 音乐会的等待 题目描述 N个人正在排队进入一个音乐会.人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人.队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或 ...

  10. 洛谷.3224.[HNOI2012]永无乡(Splay启发式合并)

    题目链接 查找排名为k的数用平衡树 合并时用启发式合并,把size小的树上的所有节点插入到size大的树中,每个节点最多需要O(logn)时间 并查集维护连通关系即可 O(nlogn*insert t ...