r.a.d.treeview2.5破解手记

       自从开始使用DotNet做网站以来便顺手开发一个自己的技术论坛,倒没有别的目的,只是想检验一下自己对DotNet的掌握程度。整个论坛的帖子作为叶片绑定在Web树控件上,树控件使用的是微软的Microsoft.Web.UI.WebControls组件中的TreeView,由于Microsoft.Web.UI.WebControls的资源是指向IIS主目录的,当服务商未装载Microsoft.Web.UI.WebControls组件时,这棵树便成了一堆干柴,无奈之下,便下载了Microsoft.Web.UI.WebControls的源码啃了一阵,将资源指向了站点的虚拟目录,编译成自己的版本。论坛终于在局域网中建起来了,我给他起名就叫 “好大一棵树论坛”。朋友给我找了个免费空间让我测试,我便用那只56K的老猫灌起水来,运行当然是正常,但速度实在是不理想,闪烁问题虽然可以通过关闭回传来避免,但这是以牺牲论坛的实时性和其他重要功能为代价的。改用宽带再试,效果果然好了许多,但我的论坛便也就被同事戏称为“好大一棵树论坛 DotNet宽带版”了。由于一时找不到轻巧好用的Web树控件,这棵大树也就一直锁在深闺。

        前几日在CSDN看到大家在对微软的TreeView大贬一通的同时,极力推崇r.a.d.treeview ,这便又一次勾起了我对那个论坛改换“树种”的想法。到r.a.d.treeview的老家http://www.telerik.com 上看了一下,果然响应速度和视觉效果都十分可心,便顺手下载了一个最新的2.5版。装入本机后,其演示画面如下:

        由于是30天的免费试用版,我把计算机的日期向后调整了一年,结果随机的出现了如下过期提示。

    由于两天前刚探究过DotNetTextBox1.3的授权方式(也顺便写了一篇破解手记,可惜这里不便发表),便直接用Reflector打开了RadTreeView.DLL,并直奔控件重载的渲染方法Render(

反汇编得出如下代码:

 

 

 VB代码

 对应的IL汇编代码

 

Protected Overrides Sub Render(ByVal output As HtmlTextWriter)

      Me.licenseMessage = String.Empty

      If ( Not Me . licenseMessage Is String.Empty) Then

            output.Write(Me.licenseMessage)

      Else

            Me.Page.VerifyRenderingInServerForm(Me)

            Dim text1 As String = String.Format("", Me.UniqueID, "trigger")

            Dim objArray1 As Object() = New Object() { String.Concat(Me.UniqueID, "_expanded"), String.Concat(Me.UniqueID, "_expanded"), Me.UniqueID, "trigger" }

            Dim text2 As String = String.Format("", objArray1)

            objArray1 = New Object() { String.Concat(Me.UniqueID, "_checked"), String.Concat(Me.UniqueID, "_checked"), Me.UniqueID, "trigger" }

            Dim text3 As String = String.Format("", objArray1)

            objArray1 = New Object() { String.Concat(Me.UniqueID, "_selected"), String.Concat(Me.UniqueID, "_selected"), Me.UniqueID, "trigger" }

            Dim text4 As String = String.Format("", objArray1)

            output.Write(text1)

            output.Write(text2)

            output.Write(text3)

            output.Write(text4)

      End If

End Sub
 
.method family virtual instance void Render([System.Web]System.Web.UI.HtmlTextWriter output) cil managed
{
      // Code Size: 344 byte(s)
      .maxstack 5
      .locals (
            string text1,
            string text2,
            string text3,
            string text4,
            object[] objArray1)
      L_0000: ldarg.0 
      L_0001: ldsfld string [mscorlib]System.String::Empty
      L_0006: stfld string WebControlLibrary1.WebCustomControl1::licenseMessage
      L_000b: ldarg.0 
      L_000c: ldfld string WebControlLibrary1.WebCustomControl1::licenseMessage
      L_0011: ldsfld string [mscorlib]System.String::Empty
      L_0016: beq.s L_0029
      L_0018: ldarg.1 
      L_0019: ldarg.0 
     ……中间无关代码略   
       
       
      L_0150: ldarg.1 
      L_0151: ldloc.3 
      L_0152: callvirt instance void [System.Web]System.Web.UI.HtmlTextWriter::Write(string)
      L_0157: ret 
}

RadTreeView.DLL的Render方法的反汇编代码相对照,在WebControlLibrary1得出如下对应关系

 

                        VB                     

 对应的IL汇编代码

 Me.licenseMessage = String.Empty 
    L_0000: ldarg.0 
      L_0001: ldsfld string [mscorlib]System.String::Empty
      L_0006: stfld string Telerik.WebControls.RadTreeView::licenseMessage

现在,可以添加IL代码了。打开Visual Studio .NET 2003 命令提示

 

键入ILDASM 打开RadTreeView.DLL

 

然后转储IL反汇编代码,保存RadTreeView.IL,附属文件也一并出现在文件夹中

 

用记事本打开RadTreeView.IL文件,现取消强名称验证。在文件头部找到如下代码将其删除:

 

.publickey =(00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00   // .$..............

         00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00   // .$..RSA1........

         CD 62 12 05 0E 7C CD 6F 51 AF 2C 41 FD CC 65 44   // .b...|.oQ.,A..eD

         AC E3 CF 79 6A 19 49 C5 80 C3 FF 52 7C AC 91 1D   // ...yj.I....R|...

         9B E0 5F AD 28 47 CE F4 E7 E5 EC 87 9F C9 4B E4   // .._.(G........K.

         9E 31 C7 97 C2 B8 39 25 C4 ED F6 AA 83 FA 78 A3   // .1....9%......x.

         5A 47 C0 F4 7B 44 A8 F9 3F D1 44 A9 B7 96 BF 74   // ZG..{D..?.D....t

         9E 8D FC B3 99 82 11 52 A9 5C 7A 37 EB A3 82 B6   // .......R./z7....

         9D A5 8B 7A 1C 87 DA 5C ED 0B 7A 72 BA B1 3F 12   // ...z.../..zr..?.

         52 C6 2F 50 DD 35 44 06 E6 F3 B0 4B AF F4 19 BD ) // R./P.5D....K.... 

查找Render方法,可见到其代码片断如下:

.method family hidebysig virtual instance void

            Render(class [System.Web]System.Web.UI.HtmlTextWriter output) cil managed

    {

      // 代码大小       313 (0x139)

      .maxstack  4

      .locals init (class Telerik.WebControls.RadTreeViewHtmlRenderer V_0,

               string V_1,

               string V_2,

               string V_3,

               string V_4)

      IL_0000:  ldarg.0

      IL_0001:  ldfld      string Telerik.WebControls.RadTreeView::licenseMessage

      IL_0006:  ldsfld     string [mscorlib]System.String::Empty

      IL_000b:  call       bool [mscorlib]System.String::op_Inequality(string,

                                                                       string)

      IL_0010:  brfalse.s  IL_001f

      ……中间无关代码略

      IL_0124:  callvirt   instance void [mscorlib]System.IO.TextWriter::Write(string)

      IL_0129:  ldarg.1

      IL_012a:  ldloc.3

      IL_012b:  callvirt   instance void [mscorlib]System.IO.TextWriter::Write(string)

      IL_0130:  ldarg.1

      IL_0131:  ldloc.s    V_4

      IL_0133:  callvirt   instance void [mscorlib]System.IO.TextWriter::Write(string)

      IL_0138:  ret

    } // end of method RadTreeView::Render 

将“仿真”的IL代码插入Render方法的最前面,见如下加粗代码。

.method family hidebysig virtual instance void

            Render(class [System.Web]System.Web.UI.HtmlTextWriter output) cil managed

    {

      // 代码大小       313 (0x139)

      .maxstack  4

      .locals init (class Telerik.WebControls.RadTreeViewHtmlRenderer V_0,

               string V_1,

               string V_2,

               string V_3,

               string V_4)

 

L_0000: ldarg.0 
         
         
      L_0001: ldsfld string [mscorlib]System.String::Empty
        
        

 

      IL_0000:  ldarg.0

      IL_0001:  ldfld      string Telerik.WebControls.RadTreeView::licenseMessage

      IL_0006:  ldsfld     string [mscorlib]System.String::Empty

      IL_000b:  call       bool [mscorlib]System.String::op_Inequality(string,

                                                                       string)

      IL_0010:  brfalse.s  IL_001f

      ……中间无关代码略

      IL_0133:  callvirt   instance void [mscorlib]System.IO.TextWriter::Write(string)

      IL_0138:  ret

    } // end of method RadTreeView::Render 

 

保存RadTreeView.IL准备使用ilasm重新编译,为应对编译出现反复,用记事本再做一个批处理文件Myasm.bat保存在同一目录中,内容如下:

ilasm /dll /resource: RadTreeView.res /output: RadTreeView.dll RadTreeView.IL

Visual Studio .NET 2003 命令提示行运行Myasm,几秒钟后,新的RadTreeView.dll出现了。

为检查添加的IL指令的正确性,用Reflector打开破解后的RadTreeView.dll,反编译Render方法为VB代码,可惜的一幕出现了,Me.licenseMessage = String.Empty

指令准确地出现在我们预定的位置上,可见“仿真”的IL代码是正确的。见下图:

 

进入Microsoft Visual   .NET 开发环境,将新的RadTreeView.dll添加到工具箱。仿照示例代码建立一测试页面WebForm1.aspx,将RadTreeView所需的资源TreeViewImages目录考入测试站点的虚拟目录中。

 <%@ Register TagPrefix="radt" Namespace="Telerik.WebControls" Assembly="RadTreeView" %>

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="WebForm1.aspx.vb" Inherits="WebTest.WebForm1"%>

   

       

       

       

       

       

   

   

       

           

                ContentFile="~/Examples/Advanced/ContextMenu/tree.xml" ImagesBaseDir="~/TreeViewImages/Round3d"

             

                ContextMenuContentFile="~/Examples/Advanced/ContextMenu/ContextMenus.xml">

               

               

           

   

进行必要的设置后编译测试项目,在本机浏览器中用localhost方式进行检测,可见页面上的Web树工作正常,各节点展缩自如,不再出现过期警告。截屏如下:



    在本机和其他机器上使用域名或
IP地址访问测试页,则发现尽管也不再出现过期警告,但Web树不再响应鼠标点击。浏览器状态行还出现页面有错误,缺少对象的警告。看来,破解并未最终完成。

仔细查看RadTreeView帮助文档,发现该控件与域名和IP地址进行了捆绑。当时使用域名和IP地址访问时,如未通过验证则显示过期警告,并不再发送控件脚本至客户端。之前的工作仅屏蔽了过期警告,却未解决有关的脚本问题。

看来必须找到检测许可证的方法或函数,还是用Reflector,在Telerik.WebControlsRadTreeView节点上有一个CheckLicenseKeys函数,从其函数名便可以基本确定它就是我们要找的东西,如下图:

反汇编出CheckLicenseKeys()的代码如下


        
        
         
          
        
        Private Function CheckLicenseKeys() As String
      Dim text1 As String = String.Empty
      Dim num1 As Integer = -1
      Dim text2 As String = ""
      Dim text3 As String = Me.Page.Request.ServerVariables.Get("SERVER_NAME").ToLower
      Dim text4 As String = Me.Page.Request.ServerVariables.Get("LOCAL_ADDR")
      If (((Me.Company Is String.Empty) AndAlso (Me.LicenseKey Is String.Empty)) AndAlso
 (
         
         
          
          
           
           Not
          
          
          
           
          
          
           
           Me
           
           .
          
          
         
         LicenseFile Is String.Empty)) Then
            Dim text5 As String = Me.Page.Request.MapPath(Me.LicenseFile.Replace("~/", Me.applicationPath))
            Dim document1 As XmlDocument = New XmlDocument
            Try 
                  document1.Load(text5)
                  Dim reader1 As XmlTextReader = New 
XmlTextReader(Assembly.GetExecutingAssembly.GetManifestResourceStream
("Telerik.WebControls.TreeViewResources.SchemaLicense.xsd"))
                  If Class2.Function4(document1, reader1) Then
                        Dim node1 As XmlNode
                        For Each node1 In document1.SelectNodes("//license[@control = 'treeview']")
                              Dim num2 As Integer = Class2.Function2(node1.Attributes.ItemOf("company").Value, 
node1.Attributes.ItemOf("licenseKey").Value, text3, text4)
                              If (num2 > num1) Then
                                    num1 = num2
                              End If
                              If (num1 = 1) Then
                                    goto Label_016A
                              End If
                        Next
                  End If
            Label_016A:
                  reader1.Close
            Catch exception1 As  XmlException
            End Try
      Else
            num1 = Class2.Function2(Me.Company, Me.LicenseKey, text3, text4)
      End If
      If ((num1 <= 0) AndAlso ((text3 Is "localhost") OrElse (text4 Is "127.0.0.1"))) Then
            num1 = 1
            text2 = "
           
           
Copyright ©
telerik 2003. To remove this message, please, obtain a < SPAN>
href=""http://www.telerik.com/clientnet"">30-day trial key.
"
      End If
      If (num1 > 0) Then
            If (text2.Length <= 0) Then
                  Return text1
            End If
            Dim time1 As DateTime = DateTime.Now
            Dim random1 As Random = New Random(time1.Millisecond)
            Dim num3 As Integer = random1.Next(19)
            If (num3 <> 7) Then
                  Return text1
            End If
            Return text2
      End If
      Me.Controls.Clear
      If (num1 < 0) Then
            Return "r.a.d.
treeview v.2.1. You have not provided valid license key or company, 
or you are trying to access the control by domain name, IP, or server name. Please,
 use http://localhost instead or obtain a 
30-day trial key."
      End If
      Return "r.a.d.treeview
 v.2.1. Your trial key has expired.
 To extend your key contact sales@telerik.com."
End Function
 

在记事本中打开RadTreeView.IL并搜索CheckLicenseKeys,发现整个控件仅在OnPreRender方法一处调用了CheckLicenseKeys函数,OnPreRender方法的VB代码为:

 

                                                                                         VB代码

对应的IL汇编代码 

Protected Overrides Sub OnPreRender(ByVal e As EventArgs)

      MyBase.OnPreRender(e)

      Me.licenseMessage = Me.CheckLicenseKeys

      If (Me.licenseMessage Is String.Empty) Then

            If (Me.IsEmpty AndAlso ( Not Me . ContentFile Is String.Empty)) Then

                  Me.LoadContentFile(Me.ContentFile)

            End If

            If ((Me.ContextMenus.Count = 0) AndAlso ( Not Me . ContextMenuContentFile Is String.Empty)) Then

                  Dim document1 As XmlDocument = New XmlDocument

                  document1.Load(Me.Context.Server.MapPath(Me.ContextMenuContentFile.Replace("~/", Me.applicationPath)))

                  Me.LoadContextMenusXmlString(document1.OuterXml)

            End If

            If Not Me.IsEmpty Then

                  Me.Page.RegisterArrayDeclaration("tlrkTreeViews", Me.ID)

                  Me.Page.GetPostBackEventReference(Me, "dummy")

                  If ( Not Me . CssFile Is String.Empty) Then

                        Me.Page.RegisterClientScriptBlock(String.Concat(Me.ID, "css"), String.Concat(" Me.CssFile.Replace("~/", Me.applicationPath), ">"))

                  End If

                  Me.RegisterStylesAsCss

                  If Not Me.Page.IsClientScriptBlockRegistered("RadTreeViewJavaScript") Then

                        If ( Not Me . PathToJavaScript Is String.Empty) Then

                              Me.PathToJavaScript = Me.PathToJavaScript.Replace("~/", Me.applicationPath)

                              Me.Page.RegisterClientScriptBlock("RadTreeViewJavaScript", String.Concat(" <script language=javascript src='"Me.PathToJavaScript.Replace("~/", Me.applicationPath), "'></script> " & ChrW(10)))

                        Else

                              Dim reader1 As StreamReader = New StreamReader(Assembly.GetExecutingAssembly.GetManifestResourceStream("Telerik.WebControls.TreeViewResources.RadTreeView_Client.js"))

                              Me.Page.RegisterClientScriptBlock("RadTreeViewJavaScript", String.Concat(" <script language=javascript>", reader1.ReadToEnd, "</script> " & ChrW(10)))

                        End If

                  End If

            End If

      End If

End Sub   

.method family hidebysig virtual instance 
void OnPreRender([mscorlib]System.EventArgs e) cil managed
{
      // Code Size: 488 byte(s)
      .maxstack 7
      .locals (
            [System.Xml]System.Xml.XmlDocument document1,
            [mscorlib]System.IO.StreamReader reader1)
      L_0000: ldarg.0 
      L_0001: ldarg.1 
      L_0002: call instance void [System.Web]System.Web.UI.
WebControls.WebControl::OnPreRender([mscorlib]System.EventArgs)
      L_0007: ldarg.0 
      L_0008: ldarg.0 
      L_0009: call instance string Telerik.WebControls.RadTreeView::
CheckLicenseKeys()
      L_0013: ldarg.0 
      L_0019: ldsfld string [mscorlib]System.String::Empty
      L_001e: call bool string::op_Inequality(string, string)
      L_0023: brfalse.s L_0026
      L_0025: ret 
      L_0026: ldarg.0 
      L_0027: call instance bool Telerik.WebControls.RadTreeView::
get_IsEmpty()
      L_002c: brfalse.s L_004c
      L_002e: ldarg.0 
      L_002f: call instance string Telerik.WebControls.RadTreeView::
get_ContentFile()
      L_0034: ldsfld string [mscorlib]System.String::Empty
      L_0039: call bool string::op_Inequality(string, string)
      L_003e: brfalse.s L_004c
      L_0040: ldarg.0 
……中间无关代码略   
           
           
      L_01c8: ldstr "RadTreeViewJavaScript"
      L_01cd: ldstr "<script language=javascript>"
      L_01d2: ldloc.1 
      L_01d3: callvirt instance string 
[mscorlib]System.IO.TextReader::ReadToEnd()
      L_01d8: ldstr "</script>/n"
      L_01dd: call string string::Concat(string, string, string)
      L_01e2: callvirt instance void [System.Web]System.Web.UI.Page::
RegisterClientScriptBlock(string, string)
      L_01e7: ret 
}

 

看来,licenseMessage的值时从CheckLicenseKeys函数或取的,只要返回的字符串是空值便会将控件完整的渲染到客户端。这样一来,我们可以不必仔细研究CheckLicenseKey的代码了,反正我们又不是要制作授权码生成器。如果判断没有错误的话,现在唯一需要做的就是将OnPreRender方法中的  Me.licenseMessage = Me.CheckLicenseKeys 改为   Me.licenseMessage = String.Empty结合前面分析Render方法的实的经验,我们得出了如下的代码对应关系。

 

 VB代码

 对应的IL汇编代码

 

Me.licenseMessage = String.Empty

 
L_0000: ldarg.0 
          
          
L_0001: ldsfld string [mscorlib]
System.String::Empty
        
        
L_0006: stfld string Telerik.WebControls.RadTreeView::licenseMessage
 Me.licenseMessage = Me.CheckLicenseKeys  
L_0007: ldarg.0 
L_0008: ldarg.0 
L_0009: call instance string Telerik.WebControls
.RadTreeView::CheckLicenseKeys()

L_000e: stfld string Telerik.WebControls.RadTreeView::

licenseMessage

现在,再次用记事本打开RadTreeView.IL ,找到OnPreRender方法, 根据上面的代码对应关系,将Me.licenseMessage = Me.CheckLicenseKeys IL代码改为   Me.licenseMessage = String.Empty IL代码后存盘,并再次编译,测试结果表明,破解成功。

     趁热打铁,最后就是解决示例文档与破解空间的完美结合问题。

        Visual Studio .NETC#新建一web项目r.a.d.treeview2.5x,并按下图将程序及名称和默认命名空间均设为tree

 

    添加项目对新的RadTreeView.IL的引用后,将r.a.d.treeview2.5原示例站点中的除bin目录的全部目录及文件复制后经过Visual Studio .NET的解决方案资源管理器粘贴到r.a.d.treeview2.5x项目中,设计器会弹出如下对话框,选择“是”,建立一个关联的类文件。

Reflector打开原示例站点bin目录中的tree.dll,反汇编出Tree.WebForm1Page_LoadC#代码

 

private void Page_Load(object sender, EventArgs e)

{

      string text2;

      int num1 = 1;

      string text1 = "Welcome";

      if ((text2 = base.Request.QueryString.Get("Tab")) == null)

      {

            goto Label_009D;

      }

      text2 = string.IsInterned(text2);

      if (text2 != "Quickstart")

      {

            if (text2 == "Examples")

            {

                  goto Label_0077;

            }

            if (text2 == "Help")

            {

                  goto Label_0081;

            }

            if (text2 == "Support")

            {

                  goto Label_008B;

            }

            if (text2 == "Purchase")

            {

                  goto Label_0095;

            }

            goto Label_009D;

      }

      num1 = 2;

      text1 = "Quickstart";

      goto Label_009D;

Label_0077:

      num1 = 3;

      text1 = "Examples";

      goto Label_009D;

Label_0081:

      num1 = 4;

      text1 = "Help";

      goto Label_009D;

Label_008B:

      num1 = 5;

      text1 = "Support";

      goto Label_009D;

Label_0095:

      num1 = 6;

      text1 = "Purchase";

Label_009D:

      this.leftNavigation.Controls.Add(base.LoadControl(string.Format("~/Examples/{0}Navigation.ascx", text1)));

      this.content.Controls.Add(base.LoadControl(string.Format("~/Examples/{0}.ascx", text1)));

      this.tabsImage.Src = string.Format("Img/tab{0}.gif", num1);

}

 

将上述代码填入Default.aspx.csprivate void Page_Load(object sender, System.EventArgs e)中。

生成这个示例项目后,在浏览器地址栏键入http://您的IP/r.a.d.treeview2.5x/,一个完美的r.a.d.treeview2.5破解版便诞生了。

看看她的全貌吧:

我是边破解边写这篇文档的,用了整整一天的时间。回过头来看,如果不走弯路,两个小时解决战斗是完全有可能的。

(另外,由于篇幅问题,在由WORD向这个WEB在线编辑器贴文章的时候,感觉这里采用的WEB在线编辑器对于图文并茂的文章作者时间十分痛苦的事,是在不如我破解的DotNetTextBox1.3。设置的编辑幅面太小,版面调整太困难,还不能会表格,急死人了。还有其他事,谁有什么好的控件请推荐给我,发到我的邮箱aspdotnet@yeah.net即可)

三君DotNet工作室   君仁

2004年9月17日星期五23时于  大连 旅顺

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值