【CLR】详解CLR中的程序集

目录结构:

contents structure [+]

  1. 程序集的简介
  2. 为程序集分配强名称
    1. 如何指定程序集的版本资源信息
    2. 如何对程序集签名
  3. 全局程序集缓存
  4. 如何查看程序集的信息
  5. 强命名程序集防串改

1. 程序集的简介

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文件中能嵌入标准的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进行哈希处理,然后比较这两个结果值,如果这两个值不一致的话,说明有文件已经被篡改,将会阻止运行。

原文地址:https://www.cnblogs.com/HDK2016/p/9304598.html

时间: 2024-08-05 18:38:45

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

【转】详解C#中的反射

原帖链接点这里:详解C#中的反射 反射(Reflection) 2008年01月02日 星期三 11:21 两个现实中的例子: 1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的生理情况.这是如何做到的呢?B超是B型超声波,它可以透过肚皮通过向你体内发射B型超声波,当超声波遇到内脏壁的时候就会产生一定的“回音”反射,然后把“回音”进行处理就可以显示出内脏的情况了(我不是医生也不是声学专家,不知说得是否准确^_^). 2.地球内部结构:地球的内部结构大体可以分为三层:地壳.地

详解Android中那些酷炫返回方式的实现

Android手机都会有返回键,不管是实体键,还是虚拟键.Android用户主要也都是通过这个返回键操控页面返回方式的,不比IOS逼格甚高的只保留一个操作键.这种方式是最普遍的返回方式,还有一种也是比较常见的,那就是页面内部自己响应.绝大多数APP每个页面的设计图顶部左侧都会有一个返回键图标,偶尔也有奇葩的设计放在底部左侧,点击这个图标即finish掉当前页面.简单的介绍完了最常见的两种方式,下面为大家介绍两种更友好的交互方式. 拿大家比较常用的三款社交软件的交互来说.腾讯微博的返回方式除去上述

CSS学习笔记(9)--详解CSS中:nth-child的用法

详解CSS中:nth-child的用法 前端的哥们想必都接触过css中一个神奇的玩意,可以轻松选取你想要的标签并给与修改添加样式,是不是很给力,它就是":nth-child". 下面我将用几个典型的实例来给大家讲解:nth-child的实际用途: Tips:还用低版本的IE浏览器的哥们请绕过! :nth-child(2)选取第几个标签,"2可以是你想要的数字" .demo01 li:nth-child(2){background:#090} :nth-child(n

详解HTTP中get和post的区别

1.详解HTTP中GET和POST的区别 http://www.jellythink.com/archives/806 2.HTTP 方法:GET 对比 POST http://www.cnblogs.com/liu-ke/p/4198815.html

详解mysql中的Using与On的用法

多用才可以体会各个关键字的用法啊... 原文来自[http://bbs.php100.com/read-htm-tid-148469.html] 在用Join进行多表联合查询时,我们通常使用On来建立两个表的关系.其实还有一个更方便的关键字,那就是Using.那么这两个关键字在使用上有啥区别呢?往下看.假设有如下两张表:    mysql> select * from pets; +---------+---------+--------+-----------+ | pets_id | ani

详解Webwork中Action 调用的方法

详解Webwork中Action 调用的方法 从三方面介绍webwork action调用相关知识: 1.Webwork 获取和包装 web 参数 2.这部分框架类关系 3.DefaultActionProxyFactory.DefaultActionProxy.DefaultActionInvocation 终于要开始 webwork 核心业务类的总结,webwork 通过对客户端传递的 web 参数重新包装,进行执行业务 Action 类,并反馈执行结果,本篇源码分析对应下图 WebWork

实例详解 EJB 中的六大事务传播属性--转

前言 事务 (Transaction) 是访问并可能更新数据库中各种数据项的一个程序执行单元 (unit).在关系数据库中,一个事务可以是一条或一组 SQL 语句,甚至整个程序.它有通常被称为 ACID 的原子性(Atomicity).一致性(Consistency).隔离性(Isolation).持续性(Durability)四大特性: 原子性(Atomicity):一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做. 一致性(Consistency):事务必须是使数据库

一张图详解Linux中的top命令

一张图详解Linux中的top命令及每个参数的含义:

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

图文详解Unity3D中Material的Tiling和Offset是怎么回事 Tiling和Offset概述 Tiling表示UV坐标的缩放倍数,Offset表示UV坐标的起始位置. 这样说当然是隔靴搔痒. 下面用*.3ds文件作为模型,介绍Tiling和Offset到底是怎么回事. 3DS格式解析 比如我有这样一个tank_player.3ds模型.右侧的'select'处的图片就是贴图. *.3ds文件最基本的内容包括顶点列表Vertices.贴图坐标列表UVs.面列表Faces.其中Ve

详解Android中的屏幕方向

详解Android中的屏幕方向 屏幕方向 是对Activity而言的,所以你可以在AndroidManifest.xml 文件中,通过<activity> 标记的screenOrientation 属性进行设定,例如: <activity android:name=".SketchpadActivity" android:screenOrientation="landscape"/><!--让该Activity总是显示为横屏-->