欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-02-29 15:38:34
在使用第三方类库时,经常会看到它自带的演示程序中,包含有这样的demo许可文件复制代码 代码如下:infragistics.win.misc.ultrabutton, in...

在使用第三方类库时,经常会看到它自带的演示程序中,包含有这样的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,开始源代码分析之旅。

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

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

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

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

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