Lost刚看完了,距离上一篇 解析Global.asax和AutoEventWireup属性 已经有段时间了,我今天再来写篇文章吧,介绍下ca控件的相关情况先。
ComponentArt是全球最知名的用户界面控件开发商之一,其.NET平台下的用户界面控件及数据可视化技术处于世界领先水平,他们一直为全球各个国家和地区的软件开发者提供优质而全面的程序界面技术。自2002年起,CompnentArt一直在进行ASP.NET平台Web用户界面技术的开发和研究,直至今日,他们的旗舰产品ComponentArt Web.UI已经被广泛应用到各种ASP.NET和ASP.NET AJAX应用程序中,其优良的品质和美妙的外观获得众多软件开发人员的信赖。(摘自某控件网的开发商介绍,当然该公司旗下还拥有一个非常优秀的图表控件ComponentArt Charting,那先进的图表制作向导, 高级的数据结构和灵活的图表模型我就不多说了。嘎嘎~)
作为一名Asp.Net开发人员不会不知道ComponentArt Web.UI了,这可以说是最早一批我们接触到的asp.net第三方控件了,伴随著Asp.Net的一路走来,ComponentArt Web.UI控件也从1.5到3.0、2006.1到2008.2紧跟.net的步伐,ComponentArt Web.UI也不乏成功应用,著名.net平台web2.0软件提供商Telligent旗下的CommunityServer就采用了ComponentArt Web.UI控件,而CommunityServer的用户包括微软、myspace、金蝶等软件大亨,还有人气非常高的微软MVP社区MSMVPS,大家肯定经常访问吧。
昨天下午,无意中发现11月6日ComponentArt Web.UI发布了2008.2.1204,立即下载下来,想看看有什么更新,可是苦找了n久都没发现一个有用的注册码(ComponentArt Web.UI每发布一次,ComponentArt.Licensing.Manager.exe算法都和以前不一致了),如是就想到一个前辈的破解方法(时间久了已经不知道是哪位前辈出的招了),破解如下:
1.从官方下载一个ComponentArt Web.UI 2008.1,从网上找一个注册码(3JKX6-YJW6X-GJJDP),下载ComponentArt Web.UI 2008.2版本(网上有三个,这个就看你的需要了)。
2.安装ComponentArt Web.UI 2008.1,只需要选择安装Licensing.Manager就可以了,安装后拷贝出ComponentArt.Licensing.Manager.exe程序,现在已经不需要2008.1了,可以卸载了。
3.安装ComponentArt Web.UI 2008.2,提取成功后,就可以在系统登录用户的临时目录(默认在“ C:\Documents and Settings\登录名\Local Settings\Temp”目录,为了较少c盘读写次数和系统垃圾我在环境变量里把用户TMP和TEMP变量更换到" d:\temp"目录下了,所以如果你也更改了,请不要忘记到你自定义目录里去),找一个名称类似于这种“ {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}”Guid类型的目录(可能有多个,请仔细查找),如果里面有dotnetinstaller.exe、corecomp.ini和另外一个Guid类型目录,则进入到该Guid目录里,用第一步得到的ComponentArt.Licensing.Manager.exe文件替换掉该目录下的文件。然后一路Next到License Keys选项,选择“Enter License Now”,点击Next;4.这时就会运行刚才替换到的ComponentArt.Licensing.Manager.exe文件,输入第一步的注册码,确定后,完成安装,注意要选中SourceCode选项,因为这就是我们要研究的(如果直接选择“Complete”选项则会默认安装所有)。
这样就完成ComponentArt Web.UI 2008.2的代码安装了,下面来简单说下破解原理,实际这是在安装中来了一个欺骗法,从运行ComponentArt Web.UI的安装程序就知道,CA控件的安装程序采用了InstallShield,这可是软件安装、配置软件包和升级解决方案领域内公认的标准,强大灵活而又简单易用哦,可惜的是现在还不支持直接调用.net(ComponentArt.Licensing.Manager.exe是.net程序,可以用Reflector一看就知道了,不过被混淆了,所以直接反编译ComponentArt.Licensing.Manager.exe里的算法,做注册机就不是很好实现了),所以在点击Enter License Now后的下一步时,安装程序会运行一个.net程序ComponentArt.Licensing.Manager.exe,该程序根据相应算法执行完注册码验证后把结果写入注册表后,WebUI的安装程序无法执行注册表内容的验证,而只是验证注册表是否用相应信息,然后进行源代码的安装。我们就是根据这个原理在安装途中进行掉包,让它验证注册码后将相应信息写入注册表,从而达到进行欺骗的目的。
由于操作过程很简单我就没有贴图了,当然如果你不想动手,或者想要操作过程中有什么问题,抑或需要相关文件的,直接和我联系、贴上邮箱都可以。
安装成功后到达安装目录里,就可以看到源代码文件夹了,里面有项目文件,大家现在就可以来研究研究CA控件的精髓了,不过个人觉得CA控件(当然并不局限于CA控件,应该可以说是ASP.net控件, web控件),最难的是美工和js(高水平的,现在工作的电脑那叫一个慢,这不,也凸显出js的重要了,打开几个网页就垮了,特别是国内的XXXX,我现在不敢访问了);而asp.net控件开发啃本书籍或研究下微软asp.net关于控件开发的源码、熟悉一下页面流程基本就掌握了,毕竟微软的服务还是很周到的。CA控件的JS写的非常好,对象设计的非常棒(js对象差不多和.net对象一致,极大的方便了编程),而且帮助文档特别好,果然是跟着微软走的。
看看CA控件安装后的源码,我们看看CA控件的License授权是怎么实现的吧?CA控件用的是微软.net的License方式,该方式对所有组件和控件(包括 Windows 窗体控件和 ASP.NET 服务器控件)都应用相同的授权模型,它与 Microsoft ActiveX 控件的授权完全兼容,这个就不多说了,具体可以查看msdn。通过查看CA控件的源码我们发现了Licensing文件夹下的RedistributeLicenseProvider.cs文件,里面代码如下:
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)
{
// 设计时不考虑是否能得到License 直接返回一个实例化的
return new ComponentArt.Licensing.Providers.RedistributableLicense(this, "The App");
}
else
{
string strFoundAppKey;
// 运行时则要符合一定条件
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;
}
}
}
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()
{
}
}
RedistributableLicenseProvider类继承LicenseProvider,重写了GetLicense()方法,获得一个继承于License的RedistributableLicense类实例(该文件的注释说在Global.asax的Application_Start方法里为Application["ComponentArtWebUI_AppKey"]赋上和strAppKey 相等的值),在控件类里通过LicenseProviderAttribute标记该类指定RedistributableLicenseProvider来对该控件进行授权。
而控件类是通过WebControl(是所有CA控件的基类)来确定是否可以授予License的,WebControl主要代码如下:
internal System.ComponentModel.License License = null;
//该方法进行判断是否被授予许可证
public virtual bool IsLicensed()
{
if (License != null) return true;
try
{
//确定是否可以为当前实例授予许可证,如果不能则会抛出异常
//LicenseManager.Validate()会调用RedistributableLicenseProvider类的GetLicense()来得到License,如果返回null 则会抛出异常
License = LicenseManager.Validate(this.GetType(), this);
return true;
}
catch
{
return false;
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
//如果被授予License 调用ComponentArtPreRender()
if (IsLicensed()) ComponentArtPreRender(e);
}
protected override void Render(HtmlTextWriter output)
{
// 如果未被授予License 调用RenderRedistributableWarning()不进行控件的呈现操作
if (!IsLicensed())
{
RenderRedistributableWarning(output);
return;
}
ComponentArtRender(output);
}
protected void RenderRedistributableWarning(HtmlTextWriter output)
{
output.Write("<div style=\"background-color:#3F3F3F;border:1px;border-style:solid;border-bottom-color:black;border-right-color:black;border-left-color:lightslategray;border-top-color:lightslategray;color:cornsilk;padding:2px;font-family:verdana;font-size:11px;\">");
string productName = this.GetType().ToString().Replace("ComponentArt.Web.UI.", "");
output.Write("<b>ComponentArt " + productName + "</b> :: ");
output.Write("Unlicensed version. <br><br>");
output.Write("This version is licensed for single application use only. <br><br>");
output.Write("You can download the free trial version <a href=http://www.ComponentArt.com/ target=_blank><font color=silver>here</font></a>.");
output.Write("</div>");
}
具体过程和呈现逻辑我在注释中都说得很清楚了,下面我们看看安装包安装后自带的Componentart Web.UI.dll的license是如何进行管理的,通过.NET Reflector我们发现ComponentArt.Licensing.Providers命名空间下面虽然存在了RedistributableLicenseProvider类和RedistributableLicense类,但在控件类上却没有应用这两个类,而用的是RegistryFileLicenseProvider类和License类(该类继承于System.ComponentModel.License类)进行授权操作,还有一个TokenEncoding类是用来对Token进行解密的(我们这里就不进行讨论了,如果想做CA控件的注册机可以仔细分析这个类的代码了,只是该代码进行混淆了,但基本能看清楚)。RegistryFileLicenseProvider类代码如下:
public class RegistryFileLicenseProvider : LicenseProvider
{
private string licenseFileName = "ComponentArt.Web.UI.lic";//License文件名
private string registryRoot = @"SOFTWARE\ComponentArt\Web.UI for ASP.NET\2008.2";//注册表路径
private string registryTokensNode = "Tokens";//注册表项
//是否得到Token
//通过解密Token字符串数组得出TokenItem结构,判断TokenItem结构的LicenseGuid和要被授权的Type的Guid是否相等
private bool findLicenseToken(string[] tokens, string key, Type type)
{
if (tokens == null)
{
return false;
}
bool flag = false;
foreach (string str in tokens)
{
TokenEncoding.TokenItem item = TokenEncoding.TokenDecrypt(str);
if (item.LicenseGuid.ToUpper() == type.GUID.ToString().ToUpper())
{
flag = true;
key = item.LicenseKey;
}
}
return flag;
}
public override System.ComponentModel.License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
{
//运行时不检查
if (context.UsageMode == LicenseUsageMode.Designtime)
{
return new ComponentArt.Licensing.Providers.License(this, "");
}
string key = "";
string[] tokens = this.getTokenStringArrayFromRegistry();
bool flag = this.findLicenseToken(tokens, key, type);
if (!flag)
{
//如果注册表项没有正确得到Token 则从License文件继续操作
tokens = this.getTokenStringArrayFromFile();
flag = this.findLicenseToken(tokens, key, type);
}
if (flag)
{
//正确得到Token 返回License
return new ComponentArt.Licensing.Providers.License(this, key);
}
return null;
}
//从License文件获取Token字符串数组
private string[] getTokenStringArrayFromFile()
{
}
//从注册表获取Token字符串数组
private string[] getTokenStringArrayFromRegistry()
{
}
}
具体的授权原理和上述分析一致,那我们再看下WebControl是如何来确定是否可以授予许可证的,WebControl主要代码如下:
internal System.ComponentModel.License License = null;
private LicenseDice licenseDiceResult = LicenseDice.NotThrown;
//该方法进行判断是否显示是demo版本警告
private bool displayDemoWarning()
{
//回传则不显示 否则打乱了人家操作 可不够UE的
if (((this.Context != null) && (this.Page != null)) && (this.Context.Request != null))
{
foreach (string str in this.Context.Request.Params.AllKeys)
{
if (((str != null) && str.StartsWith("Cart_")) && (str.IndexOf("_Callback") > 0))
{
return false;
}
}
}
if (this.licenseDiceResult == LicenseDice.NotThrown)
{
Random random = new Random();
//不是每次都判断 概率
if (random.Next(30) == 3)
{
if (!this.IsLicensed())
{
//没有授予License 显示demo
this.licenseDiceResult = LicenseDice.NotLicensed;
return true;
}
this.licenseDiceResult = LicenseDice.Licensed;
return false;
}
this.licenseDiceResult = LicenseDice.Licensed;
return false;
}
if (this.licenseDiceResult == LicenseDice.Licensed)
{
return false;
}
return true;
}
//该方法进行判断是否被授予许可证
public virtual bool IsLicensed()
{
if (this.License != null)
{
return true;
}
try
{
//确定是否可以为当前实例授予许可证,如果不能则会抛出异常
//LicenseManager.Validate()会调用RedistributableLicenseProvider类的GetLicense()来得到License,如果返回null 则会抛出异常
this.License = LicenseManager.Validate(base.GetType(), this);
return true;
}
catch
{
return false;
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if (!this.displayDemoWarning())
{
this.ComponentArtPreRender(e);
}
}
protected override void Render(HtmlTextWriter output)
{
//判断是否显示demo输出
if (this.displayDemoWarning())
{
this.RenderDemoWarning(output);
}
else
{
this.ComponentArtRender(output);
if (this.IsBrowserSearchEngine() && this.RenderSearchEngineStamp)
{
this.RenderCrawlerStamp(output);
}
}
}
protected void RenderDemoWarning(HtmlTextWriter output)
{
output.Write("<div style=\"background-color:#3F3F3F;border:1px;border-style:solid;border-bottom-color:black;border-right-color:black;border-left-color:lightslategray;border-top-color:lightslategray;color:cornsilk;padding:2px;font-family:verdana;font-size:11px;\">");
output.Write("<b>ComponentArt " + base.GetType().ToString().Replace("ComponentArt.Web.UI.", "") + "</b> :: ");
output.Write("Demo version. <br><br>");
output.Write("Please reload the page to continue with your evaluation. <br><br>");
output.Write("You can purchase the full version <a href=http://www.ComponentArt.com/ target=_blank><font color=silver>here</font></a>.");
output.Write("</div>");
}
private enum LicenseDice
{
NotThrown,
Licensed,
NotLicensed
}
这样我们就弄清楚了CA控件未注册版和注册版的License授予的实现细节以及一些呈现逻辑,知道原理后你就会轻而易举的定制你自己的License了,或者去掉License,其实仔细一想就会发现,就是通过上面的方法只能是破解安装了不带License检查的CA源码,但是实际上你写到注册表上的信息并不能通过官方dll的License检查,因为我们写入到注册表上的信息是错的,首先版本就不对,但我有点不明白的是安装程序怎么不去检查检查注册表中的版本信息呢?这样做确实有点让人想不明白?破解的也太简单了吧?不过我们头说的好,企业只负责做这个功能,而不管别人能不能破解了,剩下的就是法律的事情了,他们有法律来保护他们的知识产权,确实很有道理。说到底,可能也是企业觉得大公司用它的东西,不敢不给他钱,小公司就算给他打知名度了,所以就不能限制得太死了,就算这样,也比微软的windows策略好些吧,呵呵。
话说到InstallShield,InstallScript至今也不能直接调用.net的dll或者程序,这可不能不说是一种遗憾啊,为了能调用.net,只能把.net程序注册成Com组件然后才能用InstallScript进行调用。不过到现在随着飞信和Silverlight的应用,技术方面已经不是什么问题了,难道依旧是这个版权问题,仍然不得而知了。
PS:本文破解仅供研究、学习,请不要用于商业目的,对此本人不负任何责任。如果用于商业开发,请购买正版软件。