开发一个编译器 深入解析.NET 许可证编译器 (Lc.exe) 的原理与源代码剖析
深入解析.NET 许可证编译器 (Lc.exe) 的原理与源代码剖析
许可证编译器 (Lc exe) 的作用是读取包含授权信息的文本文件 并产生一个可作为资源嵌入到公用语言运行库可执行文件中的 licenses 文件在使用第三方类库时 经常会看到它自带的演示程序中 包含有这样的Demo许可文件
复制代码 代码如下: Infragistics Win Misc UltraButton Infragistics Win Misc v Version= Culture=neutral PublicKeyToken=f b b b fdf Infragistics Win Misc UltraLabel Infragistics Win Misc v Version= Culture=neutral PublicKeyToken=f b b b fdf Infragistics Win Printing UltraPrintPreviewDialog Infragistics Win UltraWinPrintPreviewDialog v Version= Culture=neutral PublicKeyToken=f b b b fdf Infragistics Win UltraWinDataSource UltraDataSource Infragistics Win UltraWinDataSource v Version= Culture=neutral PublicKeyToken=f b b b fdf这个文件的格式是文本文件 但要按照它的格式要求来写
控件名称 程序集全名称
首先根据需要 写一个需要被授权的控件列表 格式如上所示 例如 HostApp exe 的应用程序要引用Samples DLL 中的授权控件 MyCompany Samples LicControl 则可以创建包含以下内容的 HostAppLic txt MyCompany Samples LicControl Samples DLL
再调用下面的命令创建名为 HostApp exe licenses 的 licenses 文件 lc /target:HostApp exe /plist:hostapplic txt /i:Samples DLL /outdir:c:bindir
生成将 licenses 文件作为资源嵌入在HostApp exe的资源中 如果生成的是 C# 应用程序 则应使用下面的命令生成应用程序
csc /res:HostApp exe licenses /out:HostApp exe * cs
NET Framework SDK目录中的LC EXE文件是由 NET语言编写的 它的功能就是为了根据许可文件的内容 生成资源文件 在编译的最后时刻 由CSC编译器把生成的资源文件嵌入到执行文件中
用 NET Reflector载入LC EXE 开始源代码分析之旅
程序的入口处先是分析命令行参数 根据参数的不同来执行指定的功能 先看一个完整的参数列表 代码是下面三行
复制代码 代码如下: if (!ProcessArgs(args)) { return num; }MSDN有完整的解释 拷贝到下面方便您参考 以减少因查找MSDN引起思路中断 /plist:filename 指定包含授权组件列表的文件名 这些授权组件要包括到 licenses 文件中 每个组件用它的全名引用 并且每行只有一个组件 命令行用户可为项目中的每个窗体指定一个单独的文件 Lc exe 接受多个输入文件并产生一个 licenses 文件 /h[elp] 显示该工具的命令语法和选项 /i:module 指定模块 这些模块包含文件 /plist 中列出的组件 若要指定多个模块 请使用多个 /i 标志 /nologo 取消显示 Microsoft 启动标题 /outdir:path 指定用来放置输出 licenses 文件的目录 /target:targetPE 指定为其生成 licenses 文件的可执行文件 /v 指定详细模式 显示编译进度信息 /? 显示该工具的命令语法和选项 ProcessArgs方法的关键作用是分析出组件列表 程序集列表 如下面的代码所示
复制代码 代码如下: if ((!flag && (str Length > )) && str Substring( ) ToUpper(CultureInfo InvariantCulture) Equals("TARGET:")) { targetPE = str Substring( ); flag = true; } if ((!flag && (str Length > )) && str Substring( ) ToUpper(CultureInfo InvariantCulture) Equals("PLIST:")) { string str = str Substring( ); if ((str != null) && (str Length > )) { if (pLists == null) { pLists = new ArrayList(); } pLists Add(str ); flag = true; } } if ((!flag && (str Length > )) && str Substring( ) ToUpper(CultureInfo InvariantCulture) Equals("I:")) { string str = str Substring( ); if (str Length > ) { if (assemblies == null) { assemblies = new ArrayList(); } assemblies Add(str ); } flag = true; }分 析出组件和程序集之后 再来ResolveEventHandler 委托的含义 如果运行库类加载程序无法解析对程序集 类型或资源的引用 则将引发相应的事件 从而使回调有机会通知运行库引用的程序集 类型或资源位于哪 个程序集中 ResolveEventHandler 负责返回解析类型 程序集或资源的程序集
复制代码 代码如下: ResolveEventHandler handler = new ResolveEventHandler(LicenseCompiler OnAssemblyResolve); AppDomain CurrentDomain AssemblyResolve += handler;对第一部参数分析出来的组件列表 依次循环 为它们产生授权许可
复制代码 代码如下: DesigntimeLicenseContext creationContext = new DesigntimeLicenseContext(); foreach (string str in pLists) { key = reader ReadLine(); hashtable[key] = Type GetType(key); LicenseManager CreateWithContext((Type) hashtable[key] creationContext); }最后 生成许可文件并保存到磁盘中 等待CSC编译器将它编译成资源文件 嵌入到程序集中
复制代码 代码如下: string path = null; if (outputDir != null) { path = outputDir + @"" + targetPE ToLower(CultureInfo InvariantCulture) + " licenses"; } else { path = targetPE ToLower(CultureInfo InvariantCulture) + " licenses"; } Stream o = null; try { o = File Create(path); DesigntimeLicenseContextSerializer Serialize(o targetPE ToUpper(CultureInfo InvariantCulture) creationContext); } finally { if (o != null) { o Flush(); o Close(); } }这种方式是 NET Framework推荐的保护组件的方式 与我们平时所讨论的输入序列号 RSA签名不同 来看一下 商业的组件是如何应用这种技术保护组件的

复制代码 代码如下: using System; using System Web; using System Web UI; using System Web UI WebControls; using System ComponentModel; namespace ComponentArt Licensing Providers { #region RedistributableLicenseProvider public class RedistributableLicenseProvider : System ComponentModel LicenseProvider { const string strAppKey = "This edition of ComponentArt Web UI is licensed for XYZ application only "; public override System ComponentModel License GetLicense(LicenseContext context Type type object instance bool allowExceptions) { if (context UsageMode == LicenseUsageMode Designtime) { // We are not going to worry about design time Issue a license return new ComponentArt Licensing Providers RedistributableLicense(this "The App"); } else { string strFoundAppKey; // During runtime we only want this control to run in the application // that it was packaged with HttpContext ctx = HttpContext Current; strFoundAppKey = (string)ctx Application["ComponentArtWebUI_AppKey"]; if(strAppKey == strFoundAppKey) return new ComponentArt Licensing Providers RedistributableLicense(this "The App"); else return null; } } } #endregion #region RedistributableLicense Class public class RedistributableLicense : System ComponentModel License { private ComponentArt Licensing Providers RedistributableLicenseProvider owner; private string key; public RedistributableLicense(ComponentArt Licensing Providers RedistributableLicenseProvider owner string key) { this owner = owner; this key = key; } public override string LicenseKey { get { return key; } } public override void Dispose() { } } #endregion }
首 先要创建一个类型 继承于License类型 再创建一个继承于LicenseProvider的类型 用于颁发许可证 包含在设计时许可和运行时许可 从上面的例子中可以看到 设计时没有限制 可以运行 但是到运行时 你必须有序列号 它才会生成许可对象 而不是返回null给 NET Framework类型 整个验证过程由 NET完成 你只需要像下面这样 应用这个许可保护机制
复制代码 代码如下: [LicenseProvider(typeof(RedistributableLicenseProvider))] public class MyControl : Control { // Insert code here protected override void Dispose(bool disposing) { /* All ponents must dispose of the licenses they grant * Insert code here to dispose of the license */ } } lishixinzhi/Article/program/net/201311/14366