zoukankan      html  css  js  c++  java
  • 解析.NET 许可证编译器 (Lc.exe) 的原理与源代码剖析

    许可证编译器 (Lc.exe) 的作用是读取包含授权信息的文本文件,并产生一个可作为资源嵌入到公用语言运行库可执行文件中的 .licenses 文件。

    在使用第三方类库时,经常会看到它自带的演示程序中,包含有这样的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:indir

    生成将 .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)与控件本身的逻辑完全分离,分工协作保护组件的知识产权。

  • 相关阅读:
    Mysql模糊查询 select count(*) from sys_invitation where from_id like '%1006%';
    java 结束程序进程 代码
    [解决问题]selenium.remote.UnreachableBrowserException 异常分析并解决问题
    【解决问题】failed: java.lang.RuntimeException: org.openqa.selenium.WebDriverException: Unexpected error launching Internet Explorer.
    linux下nginx部署以及配置详解
    Linux系统下安装jdk及环境配置(两种方法)
    linux服务器同时运行两个或多个tomcat
    linux配置环境jdk
    mysql修改后启动my.cnf报错Starting MySQL... ERROR! The server quit without updating PID file (/var/lib/mysql/localhost.localdomain.pid).
    mysql执行出错:Table 'k_user' is read only
  • 原文地址:https://www.cnblogs.com/JamesLi2015/p/3179197.html
Copyright © 2011-2022 走看看