深入解析.NET 许可证编译器 (Lc.exe) 的原理与源代码剖析

 更新时间:2021年9月22日 10:14  点击:1667

在使用第三方类库时,经常会看到它自带的演示程序中,包含有这样的Demo许可文件

复制代码 代码如下:

Infragistics.Win.Misc.UltraButton, Infragistics2.Win.Misc.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31
Infragistics.Win.Misc.UltraLabel, Infragistics2.Win.Misc.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31
Infragistics.Win.Printing.UltraPrintPreviewDialog, Infragistics2.Win.UltraWinPrintPreviewDialog.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31
Infragistics.Win.UltraWinDataSource.UltraDataSource, Infragistics2.Win.UltraWinDataSource.v11.1, Version=11.1.20111.2009, Culture=neutral, PublicKeyToken=f8b58b62b52fdf31

这个文件的格式是文本文件,但要按照它的格式要求来写:

控件名称, 程序集全名称

首先根据需要,写一个需要被授权的控件列表,格式如上所示。例如,HostApp.exe 的应用程序要引用Samples.DLL 中的授权控件 MyCompany.Samples.LicControl1,则可以创建包含以下内容的 HostAppLic.txt。 MyCompany.Samples.LicControl1, Samples.DLL。

再调用下面的命令创建名为 HostApp.exe.licenses 的 .licenses 文件。 lc /target:HostApp.exe /complist: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,开始源代码分析之旅。

image

程序的入口处先是分析命令行参数,根据参数的不同来执行指定的功能。先看一个完整的参数列表。代码是下面三行
复制代码 代码如下:

if (!ProcessArgs(args))
 {
     return num;
 }

image

MSDN有完整的解释,拷贝到下面方便您参考,以减少因查找MSDN引起思路中断。
/complist:filename   指定包含授权组件列表的文件名,这些授权组件要包括到 .licenses 文件中。每个组件用它的全名引用,并且每行只有一个组件。命令行用户可为项目中的每个窗体指定一个单独的文件。Lc.exe 接受多个输入文件并产生一个 .licenses 文件。
/h[elp]     显示该工具的命令语法和选项。
/i:module   指定模块,这些模块包含文件 /complist 中列出的组件。若要指定多个模块,请使用多个 /i 标志。
/nologo  取消显示 Microsoft 启动标题。
/outdir:path  指定用来放置输出 .licenses 文件的目录。
/target:targetPE   指定为其生成 .licenses 文件的可执行文件。
/v   指定详细模式;显示编译进度信息。
/?  显示该工具的命令语法和选项。
ProcessArgs方法的关键作用是分析出组件列表,程序集列表,如下面的代码所示
复制代码 代码如下:

  if ((!flag3 && (str2.Length > 7)) && str2.Substring(0, 7).ToUpper(CultureInfo.InvariantCulture).Equals("TARGET:"))
 {
       targetPE = str2.Substring(7);
       flag3 = true;
 }
if ((!flag3 && (str2.Length > 8)) && str2.Substring(0, 9).ToUpper(CultureInfo.InvariantCulture).Equals("COMPLIST:"))
 {
      string str3 = str2.Substring(9);
      if ((str3 != null) && (str3.Length > 1))
        {
                    if (compLists == null)
                    {
                        compLists = new ArrayList();
                    }
                    compLists.Add(str3);
                    flag3 = true;
       }
}
if ((!flag3 && (str2.Length > 2)) && str2.Substring(0, 2).ToUpper(CultureInfo.InvariantCulture).Equals("I:"))
 {
       string str4 = str2.Substring(2);
       if (str4.Length > 0)
        {
                    if (assemblies == null)
                    {
                        assemblies = new ArrayList();
                    }
                    assemblies.Add(str4);
        }
        flag3 = true;
}

分析出组件和程序集之后,再来ResolveEventHandler 委托的含义。如果运行库类加载程序无法解析对程序集、类型或资源的引用,则将引发相应的事件,从而使回调有机会通知运行库引用的程序集、类型或资源位于哪个程序集中。ResolveEventHandler 负责返回解析类型、程序集或资源的程序集。
复制代码 代码如下:

ResolveEventHandler handler = new ResolveEventHandler(LicenseCompiler.OnAssemblyResolve);
AppDomain.CurrentDomain.AssemblyResolve += handler;

对第一部参数分析出来的组件列表,依次循环,为它们产生授权许可
复制代码 代码如下:

DesigntimeLicenseContext creationContext = new DesigntimeLicenseContext();
foreach (string str in compLists)
{
   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 components must dispose of the licenses they grant.
        * Insert code here to dispose of the license. */
    }
}

控件许可的验证代码(RedistributableLicenseProvider)与控件本身的逻辑完全分离,分工协作保护组件的知识产权。
[!--infotagslink--]

相关文章

  • ASP.NET购物车实现过程详解

    这篇文章主要为大家详细介绍了ASP.NET购物车的实现过程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
  • .NET Core下使用Kafka的方法步骤

    这篇文章主要介绍了.NET Core下使用Kafka的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 在ASP.NET 2.0中操作数据之七十二:调试存储过程

    在开发过程中,使用Visual Studio的断点调试功能可以很方便帮我们调试发现程序存在的错误,同样Visual Studio也支持对SQL Server里面的存储过程进行调试,下面就让我们看看具体的调试方法。...2021-09-22
  • Win10 IIS 安装.net 4.5的方法

    这篇文章主要介绍了Win10 IIS 安装及.net 4.5及Win10安装IIS并配置ASP.NET 4.0的方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • 详解.NET Core 3.0 里新的JSON API

    这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • .net数据库操作框架SqlSugar的简单入门

    这篇文章主要介绍了.net数据库操作框架SqlSugar的简单入门,帮助大家更好的理解和学习使用.net技术,感兴趣的朋友可以了解下...2021-09-22
  • ASP.NET Core根据环境变量支持多个 appsettings.json配置文件

    这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 记一次EFCore类型转换错误及解决方案

    这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.net core,感兴趣的朋友可以了解下...2021-09-22
  • .NET C#利用ZXing生成、识别二维码/条形码

    ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口。这篇文章主要给大家介绍了.NET C#利用ZXing生成、识别二维码/条形码的方法,文中给出了详细的示例代码,有需要的朋友们可以参考借鉴。...2020-06-25
  • 详解ASP.NET Core 中基于工厂的中间件激活的实现方法

    这篇文章主要介绍了ASP.NET Core 中基于工厂的中间件激活的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • C#使用Ado.Net更新和添加数据到Excel表格的方法

    这篇文章主要介绍了C#使用Ado.Net更新和添加数据到Excel表格的方法,较为详细的分析了OLEDB的原理与使用技巧,可实现较为方便的操作Excel数据,需要的朋友可以参考下...2020-06-25
  • asp.net通过消息队列处理高并发请求(以抢小米手机为例)

    这篇文章主要介绍了asp.net通过消息队列处理高并发请求(以抢小米手机为例),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • ASP.NET单选按钮控件RadioButton常用属性和方法介绍

    RadioButton又称单选按钮,其在工具箱中的图标为 ,单选按钮通常成组出现,用于提供两个或多个互斥选项,即在一组单选钮中只能选择一个...2021-09-22
  • ASP.NET 2.0中的数据操作:使用两个DropDownList过滤的主/从报表

    在前面的指南中我们研究了如何显示一个简单的主/从报表, 该报表使用DropDownList和GridView控件, DropDownList填充类别,GridView显示选定类别的产品. 这类报表用于显示具有...2016-05-19
  • 详解.NET Core 使用HttpClient SSL请求出错的解决办法

    这篇文章主要介绍了.NET Core 使用HttpClient SSL请求出错的解决办法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2021-09-22
  • Python调用.NET库的方法步骤

    这篇文章主要介绍了Python调用.NET库的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-05-09
  • ASP.NET中iframe框架点击左边页面链接 右边显示链接页面内容

    这篇文章主要介绍了ASP.NET中iframe框架点击左边页面链接,右边显示链接页面内容的实现代码,感兴趣的小伙伴们可以参考一下...2021-09-22
  • 创建一个完整的ASP.NET Web API项目

    ASP.NET Web API具有与ASP.NET MVC类似的编程方式,ASP.NET Web API不仅仅具有一个完全独立的消息处理管道,而且这个管道比为ASP.NET MVC设计的管道更为复杂,功能也更为强大。下面创建一个简单的Web API项目,需要的朋友可以参考下...2021-09-22
  • ASP.NET连接MySql数据库的2个方法及示例

    这篇文章主要介绍了ASP.NET连接MySql数据库的2个方法及示例,使用的是MySQL官方组件和ODBC.NET,需要的朋友可以参考下...2021-09-22
  • Asp.Net使用Bulk实现批量插入数据

    这篇文章主要介绍了Asp.Net使用Bulk实现批量插入数据的方法,对于进行asp.net数据库程序设计非常有借鉴价值,需要的朋友可以参考下...2021-09-22