c#

位置:IT落伍者 >> c# >> 浏览文章

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


发布日期:2020年04月26日
 
深入解析.NET 许可证编译器 (Lc.exe) 的原理与源代码剖析
许可证编译器 (Lcexe) 的作用是读取包含授权信息的文本文件并产生一个可作为资源嵌入到公用语言运行库可执行文件中的 licenses 文件

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

复制代码 代码如下:
InfragisticsWinMiscUltraButton  InfragisticsWinMiscv Version= Culture=neutral  PublicKeyToken=fbbbfdf
InfragisticsWinMiscUltraLabel  InfragisticsWinMiscv Version= Culture=neutral  PublicKeyToken=fbbbfdf
InfragisticsWinPrintingUltraPrintPreviewDialog  InfragisticsWinUltraWinPrintPreviewDialogv  Version= Culture=neutral  PublicKeyToken=fbbbfdf
InfragisticsWinUltraWinDataSourceUltraDataSource  InfragisticsWinUltraWinDataSourcev Version=  Culture=neutral PublicKeyToken=fbbbfdf


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

控件名称 程序集全名称

首先根据需要写一个需要被授权的控件列表格式如上所示例如HostAppexe 的应用程序要引用SamplesDLL 中的授权控件  MyCompanySamplesLicControl则可以创建包含以下内容的 HostAppLictxt  MyCompanySamplesLicControl SamplesDLL

再调用下面的命令创建名为 HostAppexelicenses 的 licenses 文件 lc  /target:HostAppexe /complist:hostapplictxt /i:SamplesDLL  /outdir:c:bindir

生成将 licenses 文件作为资源嵌入在HostAppexe的资源中如果生成的是 C# 应用程序则应使用下面的命令生成应用程序

csc /res:HostAppexelicenses /out:HostAppexe *cs

NET Framework SDK目录中的LCEXE文件是由NET语言编写的它的功能就是为了根据许可文件的内容生成资源文件在编译的最后时刻由CSC编译器把生成的资源文件嵌入到执行文件中

NET Reflector载入LCEXE开始源代码分析之旅



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

复制代码 代码如下:
if (!ProcessArgs(args))
{
return num;
}




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

复制代码 代码如下:
  if ((!flag && (strLength > )) &&  strSubstring(  )ToUpper(CultureInfoInvariantCulture)Equals("TARGET:"))
{
targetPE = strSubstring();
flag = true;
}
if  ((!flag && (strLength > )) && strSubstring(  )ToUpper(CultureInfoInvariantCulture)Equals("COMPLIST:"))
{
string str = strSubstring();
if ((str != null) && (strLength > ))
{
if (compLists == null)
{
compLists = new ArrayList();
}
compListsAdd(str);
flag = true;
}
}
if ((!flag && (strLength > )) && strSubstring( )ToUpper(CultureInfoInvariantCulture)Equals("I:"))
{
string str = strSubstring();
if (strLength > )
{
if (assemblies == null)
{
assemblies = new ArrayList();
}
assembliesAdd(str);
}
flag = true;
}


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

复制代码 代码如下:
ResolveEventHandler handler = new ResolveEventHandler(LicenseCompilerOnAssemblyResolve);
AppDomainCurrentDomainAssemblyResolve += handler;


对第一部参数分析出来的组件列表依次循环为它们产生授权许可

复制代码 代码如下:
DesigntimeLicenseContext creationContext = new DesigntimeLicenseContext();
foreach (string str in compLists)
{
  key = readerReadLine(); hashtable[key] = TypeGetType(key);  LicenseManagerCreateWithContext((Type) hashtable[key]  creationContext);
}


最后生成许可文件并保存到磁盘中等待CSC编译器将它编译成资源文件嵌入到程序集中

复制代码 代码如下:
string path = null;
if (outputDir != null)
{
path = outputDir + @"" + targetPEToLower(CultureInfoInvariantCulture) + "licenses";
}
else
{
path = targetPEToLower(CultureInfoInvariantCulture) + "licenses";
}
Stream o = null;
try
{
o = FileCreate(path);
DesigntimeLicenseContextSerializerSerialize(o targetPEToUpper(CultureInfoInvariantCulture) creationContext);
}
finally
{
if (o != null)
{
oFlush();
oClose();
}
}


这种方式是NET Framework推荐的保护组件的方式与我们平时所讨论的输入序列号RSA签名不同
来看一下商业的组件是如何应用这种技术保护组件的

复制代码 代码如下:
using System;
using SystemWeb;
using SystemWebUI;
using SystemWebUIWebControls;
using SystemComponentModel;
namespace ComponentArtLicensingProviders
{
#region RedistributableLicenseProvider
public class RedistributableLicenseProvider : SystemComponentModelLicenseProvider
{
const string strAppKey = "This edition of ComponentArt WebUI is licensed for XYZ application only";

  public override SystemComponentModelLicense GetLicense(LicenseContext  context Type type object instance bool allowExceptions)
{
if (contextUsageMode == LicenseUsageModeDesigntime)
{
// We are not going to worry about design time Issue a license
return new ComponentArtLicensingProvidersRedistributableLicense(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 = HttpContextCurrent;
strFoundAppKey = (string)ctxApplication["ComponentArtWebUI_AppKey"];
if(strAppKey == strFoundAppKey)
return new ComponentArtLicensingProvidersRedistributableLicense(this "The App");
else
return null;
}
}
}
#endregion
#region RedistributableLicense Class
public class RedistributableLicense : SystemComponentModelLicense
{
private ComponentArtLicensingProvidersRedistributableLicenseProvider owner;
private string key;
public RedistributableLicense(ComponentArtLicensingProvidersRedistributableLicenseProvider owner string key)
{
thisowner = owner;
thiskey = 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)与控件本身的逻辑完全分离分工协作保护组件的知识产权

               

上一篇:C# 如何获取指定目录包含的文件和子目录

下一篇:c#.Net史上经典面试题目