首页
社区
课程
招聘
[原创]破解WebUI.NET Framework验证原理
2005-8-17 14:18 10083

[原创]破解WebUI.NET Framework验证原理

2005-8-17 14:18
10083
破解WebUI.NET Framework验证原理

【软件名称】:WebGrid.NET Version 4.0
【软件类别】:国外软件
【整理时间】:2005-08-17
【下载地址】:http://www.evget.com/view/viewProductInfo.asp?productId=89&tabIndex=0
【软件简介】:显示与操作标准数据,允许您的用户自由地对数据进行操作,使用简单,并能有效节约您的开发时间,WebGrid.NET企业版针对基于Asp.net的Grid控件上,在执行上设置了新的标准,用户接口和效果,允许用户像使用类似于windows的用户一样方便地操作基于web环境的Webgrid,此工业版本是唯一的装备有众多强大内置功能和高级核心架构(比如OnTheFly?数据处理),丰富的包括WebCombo.NET?的编辑控件以及日历控件,全球化支持(包括中文简体,繁体支持),表格输出,以及其他更多的内容
典型客户
・  Energie NB Power Corp.
・  Microsoft Corporation.
・  Intel Corp.
・  Dell Corp.
・  BellSouth Corp.
・  DollarGeneral Corporation.
・  General Electric Corporation.
・  Siemens GmBH, Germany.
・  Inco Limited, Canada.
・  TempWorks, USA.

【保护方式】:License
【保护软件】:XenoCode
【编译语言】:ASP.NET
【调试环境】:WinXP、.NET Framework 1.1、Reflector、ildasm、sn、ultraedit
【破解日期】:2005-08-17
【破解目的】:研究.NET程序的破解流程
【作者声明】:初学Crack,只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!

听说WebGrid.NET 4.0采用了另外的加密技术,于是下载下来想见识一下,结果非常失望。除了把验证部分转移到了Framework的通用框架内以外,没有发现有什么改进的,加密水平还后退回WebCombo.NET的水平去了。

先是利用流程反混淆技术,把一堆函数反混淆出来,过程没有什么新意,参看以前我写的《揭开.NET程序保护的秘密》和《破解WebCombo.NET Version 2.0》,用ildasm反编译成il后修改转跳流程,然后用ilasm重新编译回去即可,唯一遇到一点点麻烦的是ilasm居然不认那个混淆后的resource。这也没关系,我们只是要分析验证原理,暂时不把resource编进去就是了。虽然是用XenoCode混淆的,可是连变量名字都没有处理过,非常容易就找到这两个函数

<ISNet.WebUI.ISNetControl>
private void CheckLicense()
{
      string text2 = "";
      string text3 = "";
      RegistryKey key3 = this.GetIntersoftRegKey(); // HKLM\Software\Intersoft Solutions
      text2 = this.Product.ProductName; // 具体的软件产品会重载此field
      text3 = this.Product.VersionNumber; // 具体的软件产品会重载此field
      if (key3 != null)
      {
            RegistryKey key1 = key3.OpenSubKey(text2);//从注册表读键值
            if (key1 != null)
            {
                  RegistryKey key2 = key1.OpenSubKey(text3);
                  if (key2 != null)
                  {
                        string text1 = key2.GetValue("SID") as string; //注册码
                        if ((text1 != "") && (text1 != null))
                        {
                              if (this.MatchSN(this.Product.dk, text1)) //关键,如果爆破只要nop掉这里即可
                              {
                                    this.LicenseType = LicType.Full;
                              }
                              else if (this.MatchSN(this.Product.dr, text1))
                              {
                                    this.LicenseType = LicType.RuntimeOnly;
                              }
                              else
                              {
                                    text1 = this.ReadWebConfigValue("RuntimeLicenseKey");
                                    if ((text1 != null) && this.MatchSN(this.Product.dr, text1))
                                    {
                                          this.LicenseType = LicType.RuntimeOnly;
                                    }
                              }
                        }
                  }
            }
      }
}

private bool MatchSN(byte[] ?, string ?)//比较序列号的核心call
{
      byte[] buffer1 = Encoding.ASCII.GetBytes(?);
      bool flag1 = false;
      bool flag2 = false;
      bool flag3 = false;
      int num1 = 0;
      byte[] buffer2 = ?;
      int num4 = 0;
Label_001B:
      if (num4 < buffer2.Length)        //实际是循环:for(num4=0;num4<buffer2.Length;num4++)
      {
            byte num2 = buffer2[num4];//读入num2和num3进行比较
            byte num3 = 0;
            try
            {
                  num3 = (byte) buffer1.GetValue(num1);
            }
            catch
            {
                  flag3 = true;
                  goto Label_014B;
            }
            if (num2 != 0xbc)//下面一大段是核心判断,后面解释
            {
                  if (num2 == 190)
                  {
                        if ((num3 >= 0x41) && (num3 <= 0x4b))
                        {
                              goto Label_0054;
                        }
                        flag3 = true;
                  }
                  else
                  {
                        if (num2 == 0xb8)
                        {
                              flag2 = true;
                              goto Label_005A;
                        }
                        if (num2 != 0x4a)
                        {
                              if (num2 != 0x7e)
                              {
                                    if (num2 != 90)
                                    {
                                          if (num2 == 0x42)
                                          {
                                                if ((num3 >= 0x30) && (num3 <= 0x35))
                                                {
                                                      goto Label_0054;
                                                }
                                                flag3 = true;
                                          }
                                          else
                                          {
                                                if (num2 != 0x80)
                                                {
                                                      if (!flag2)
                                                      {
                                                            flag3 = true;
                                                            goto Label_014B;
                                                      }
                                                      if (num3 != (num2 / 2))
                                                      {
                                                            flag3 = true;
                                                            goto Label_014B;
                                                      }
                                                      flag2 = false;
                                                      goto Label_0054;
                                                }
                                                if ((num3 >= 0x36) && (num3 <= 0x39))
                                                {
                                                      goto Label_0054;
                                                }
                                                flag3 = true;
                                          }
                                    }
                                    else
                                    {
                                          if (num3 == (num2 / 2))
                                          {
                                                goto Label_0054;
                                          }
                                          flag3 = true;
                                    }
                              }
                              else
                              {
                                    if ((num3 >= 0x41) && (num3 <= 90))
                                    {
                                          goto Label_0054;
                                    }
                                    flag3 = true;
                              }
                        }
                        else
                        {
                              if ((num3 >= 0x30) && (num3 <= 0x39))
                              {
                                    goto Label_0054;
                              }
                              flag3 = true;
                        }
                  }
            }
            else
            {
                  if ((num3 >= 0x4c) && (num3 <= 90))
                  {
                        goto Label_0054;
                  }
                  flag3 = true;
            }
      }
      goto Label_014B;
Label_0054:
      num1++;
Label_005A:        //实际是循环:for(num4=0;num4<buffer2.Length;num4++)
      num4++;
      goto Label_001B;
Label_014B:
      if (!flag3)
      {
            flag1 = true;
      }
      return flag1;
}

看起来有点眼花,实际是因为Reflector对switch语句的支持不太好造成的。按照里面的逻辑列一张表就清楚了:

num2                num3

190                [0x41,0x4b]
0xbc                [0x4c,90]
0xb8                flag2 = true,当前num3不变(num2在这里的作用是一个额外的转义字符)
0x42                [0x30,0x35]
90                45
0x7e                [0x41,90]
0x4a                [0x30,0x39]
0x80                [0x36,0x39]

default:        flag2 == true && num3 == num2 / 2, flag2 = false

再看看Product.dk是在哪里初始化的,用Callee graph查到WebGrid.NET当中
<ISNet.WebUI.WebGrid.WebGrid>
.method family hidebysig specialname virtual instance [ISNet.WebUI]ISNet.WebUI.ProductInfo get_Product() cil managed
{
      // Code Size: 292 byte(s)

      L_006b: ldc.i4.s 16
      L_006d: newarr unsigned int8
      L_0072: dup
      L_0073: ldtoken ?/? ?::?
      L_0078: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray([mscorlib]System.Array, [mscorlib]System.RuntimeFieldHandle)
      L_007d: stfld unsigned int8[] [ISNet.WebUI]ISNet.WebUI.ProductInfo::dk
}
找那串乱码的定义
.field assembly static ?/? ? = ((BC 42 4A 4A 42 5A 42 BC BE 7E 5A 7E BC BE BE BC))
对照上面的表,就可以手动构造一个sn了。如0x4c, 0x30, 0x30, 0x30, 0x30, 45, 0x30, 0x4c, 0x41, 0x41, 45, 0x41, 0x4c, 0x41, 0x41, 0x4c,即N0000-0NAA-ANAAN。Keygen也不难写,甚至可以写一个WebUI Framework产品万能注册机,直接从exe里面读取这串东西,呵呵。这里就省略了。

想不明白WebUI Framework的注册码验证算法为什么不再用Remotesoft Protector了。莫非出现版权问题?不过这对偶等穷人是好事。

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞7
打赏
分享
最新回复 (6)
雪    币: 442
活跃值: (1211)
能力值: ( LV12,RANK:1130 )
在线值:
发帖
回帖
粉丝
baby2008 28 2005-8-17 15:10
2
0
又出手了,学习
雪    币: 200
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
JasonNET 2005-8-17 16:04
3
0
显然 Remotesoft Protector 还是存在某些缺陷,导致 WinGrid4.0放弃了它。。。

还是看我 MaxtoCode 的吧,嘿嘿
雪    币: 234
活跃值: (370)
能力值: ( LV9,RANK:530 )
在线值:
发帖
回帖
粉丝
lnn1123 13 2005-8-17 16:27
4
0
看来楼主精通JAVA。NET啊,强!
雪    币: 267
活跃值: (44)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
hrbx 8 2005-8-17 22:29
5
0
强,支持,学习中
雪    币: 61
活跃值: (160)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
pendan2001 4 2005-8-18 15:37
6
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
opx 2005-8-21 11:14
7
0
好东西,好好学习一下
游客
登录 | 注册 方可回帖
返回