【文章标题】: ANTS Profiler(for .net)的分析、调试及破解
【文章作者】: tankaiha
【作者主页】: vxer.cn
【软件名称】: ANTS Profiler
【下载地址】: 自己搜索下载
【保护方式】: 混淆+强命名+RSA
【使用工具】: Reflector,Pebrowse,snremove,UltraEdit
【操作平台】: .Net v1.1
【作者声明】: 第一次用破文生成器,8错!
--------------------------------------------------------------------------------
【详细过程】
 
    ANTS Profiler的主要功能如下:
  "  Code profile .NET applications 
  "  Profile application memory use 
  "  Profile both .NET desktop applications and ASP.NET web applications 
  "  Optimize your code 
      不废话了,直接来过程。
      先运行一下,提示14天试用期,输入序列号。如果输入错误的序列号则提示Please enter a valid serial number"。用Reflector打开试试,运气好,发现软件混淆强度较弱(只有少部分使用了不可打印字符),再查发现有强命名。
      目录下有两个文件很可疑,RedGate.Licensing.Client.dll和RedGate.Licensing.Helper.dll(发现这两个文件一是看目录下的文件名,二是调试时会发现有这两个模块,见下节)。同样,用Reflector载入。搜索字符串"valid serial",很容易就来到了判断序列号的地方。(晕,关键的东东不混淆,而且敏感字符串以明文出现,好久没遇到这么爽的.Net程序了。)
  

代码:
     try             {                   string text1 = this.?.Text.ToUpper().Trim();                   if (text1 == "I NEED MORE TIME")                   {                         if (this.?.?())                         {                               this.?((?.?) 6);                               return;                         }                         MessageBox.Show("Your trial could not be extended", "Trial extension failure", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);                         return;                   }                   this.?.SerialNumber = this.?.Text;             }             catch (?)             {                   MessageBox.Show("Please enter a valid serial number", "Invalid serial number", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);                   return;             }   



    虽然有不少不可打印字符,但并不影响分析。从代码中看出输入错误的序列号会抛出异常。下面是找序列号的计算代码。(这里还有个字符串"I NEED MORE TIME",这是延长试用期的,结合注册表可以不破解直接无限使用,已经发过帖子了。)
    找序列号的比较代码有两种方法,一种是在PeBrowse中下断,动态调试找到地方,当时我用的这个方法,这里只简单的说一下。用PeBrowse载入(对于含有不可打印字符的程序,ildasm后的源代码往往不能用ilasm再编译,用WinDbg不方便,还是直接调试方便),在相应的dll处对所有的方法下断(因为暂时不知道是哪个方法计算序列号),见下图,红色圈中的就是前文提到的可疑dll。不再多说了,大家自已试,因为这个程序静态完全可以跟出来。

 

    第二是Reflector中直接分析。注意这句:
  this.?.SerialNumber = this.?.Text;
  点击"SericalNumber",来到它的定义处
  

代码:
  public string SerialNumber   {         get         {               return this.?;         }         set         {               value = value.Trim();               if (!Licence.?(value, this.?))               {                     throw new ?();               }               this.? = value;         }   }   


  这里明显调用的是set。代码的意义非常明显,如果License判断序列号是错的,就throw一个异常,这正好被刚才的catch捕捉到,显示invalid serial的窗口。那我们点击Licence.XX(某不可见字符,呵呵),终于,找到了:
  

代码:
  private static bool ?(string text5, int num1)   {         text5 = text5.ToUpper().Trim();         Regex regex1 = new Regex(@"[A-Z]{2}-[0-9A-Z]{1}-[0-9A-Z]{1}-\d{5}-[0-9A-F]{4}");         Regex regex2 = new Regex(@"\d{3}-\d{3}-\d{6}-[0-9A-F]{4}");         if (regex1.IsMatch(text5))         {               string text1 = text5.Substring(0, 12);               string text2 = string.Format("{0:X4}", Licence.?(text1));               if (!text5.EndsWith(text2))               {                     return false;               }         }         else         {               if (!regex2.IsMatch(text5))               {                     return false;               }               string text3 = text5.Substring(0, 14);               string text4 = string.Format("{0:X4}", Licence.?(text3));               if (!text5.EndsWith(text4))               {                     return false;               }               if (Convert.ToInt32(text5.Substring(0, 3)) != num1)               {                     return false;               }         }         return true;   }   


  还是正则表达式。这里不多说了,不明白的可以看编程文档。分析一下就知道序列号的格式,[A-Z]{2}-[0-9A-Z]{1}-[0-9A-Z]{1}-\d{5}-[0-9A-F]{4},而且前14位计算的值等于末尾4位。这里给出一个,以便我们继续分析:AA-0-0-12345-5C3B。
      输入序列号后,可以继续了,发现原来还要激活,不然还是试用14天。选择用Email激活(网络激活不好搞),然后会让你输入激活的代码。随便输入一些,提示"The activation response is not in the correct format"。就以这为关键词搜,来到这里。
  

代码:
  private bool ?(XmlDocument document1, ref Licence.? ?Ref1)   {         XmlNodeList list1 = document1.GetElementsByTagName("data");         XmlNodeList list2 = document1.GetElementsByTagName("signature");         if ((list1.Count != 1) || (list2.Count != 1))         {               ?Ref1.? = "The activation response is not in the correct format";               return false;         }         string text1 = list1[0].OuterXml;         string text2 = list2[0].InnerXml;         if (text2.Length != 0)         {               ?Ref1.? = "The activation response does not contain a digital signature";               return false;         }         RSACryptoServiceProvider provider1 = new RSACryptoServiceProvider();         string text3 = "<RSAKeyValue><Modulus>zLizNmLUd4VlIWee1GXgn/KxEwcghPASQ+NUzZhbY2fTGzpW64T6yEOdHlIbhX1DX6yAz2gMZKfnpQL2aFqxh5ACFV9dONSTzuQzkqeXwFEARsMxGP3eTQSWMpwVhEcraSn1zOqMb3CRDeQpgasq0lv4HRFhbwalOifKarjEL/8=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";         provider1.FromXmlString(text3);         byte[] buffer1 = Convert.FromBase64String(text2);         byte[] buffer2 = Encoding.UTF8.GetBytes(text1);         if (provider1.VerifyData(buffer2, new SHA1Managed(), buffer1))         {               ?Ref1.? = "The activation response contains an incorrect digital signature";               return false;         }         ?Ref1.? = false;   


  
      晕,有RSA,我功力不行,只能爆破了。不过这里爆破范围和爆破点的选择还是很有技巧的,详见下文。大概分析一下代码,看来激活信息是XML格式,大至如下:
  <activationresponse>
  <data>
      XXXX
  </data>
  <signature>
      YYYY
  </signature>
  </activationresponse>
      而且YYYY必须的字符数必须是4的倍数,为啥?因为Convert.FromBase64String(text2),不信你可以试试。我们就按上面的样式输入,又有新提示:The activation response does not match the registration properties。看来这里还不是最终验证的地方,还得找。
      这里有个使用Reflector的小技巧,操作见图:先分析这个方法被哪些别的方法使用了,又使用了哪些别的方法。
   
   

    这里我们找被使用的关系,然后点Go To Member,来到下面的代码处。这里才是关键呵。
    
  
  

代码:
  private bool ?(XmlDocument document1)   {         Licence.? ?1 = new Licence.?();         if (!this.?(document1, ref ?1))         {               throw new ?(?1.?);         }         if (((?1.? != this.?) || (?1.? != this.?)) || (?1.? != this.?))         {               throw new ?("The activation response does not match the registration properties");         }         if (!?1.?)         {               if (?1.? != this.?)               {                     throw new ?("The activation response does not match the registration properties");               }               if (Licence.?(?1.?, this.?))               {                     this.? = true;                     return true;               }               throw new ?("The activation response is for a different machine");         }         if (?1.? != this.?)         {               throw new ?("The activation response is for a different session");         }         this.?(true);         return false;   }   


      我们要在这里进行爆破,但怎么个爆破法呢?注意,该段代码中只有一个正确的返回点,就是
  this.? = true;
  return true;
  我们的方法是让第一句Licence.? ?1 = new Licence.?();执行完后,不进行任何判断,直接执行上面的两句代码。对了,前面还有一处,就是RSA判断处的provider1.VerifyData也要改掉,这样才能进行到下面。
  修改方法,在结合ILDASM的反汇编代码,在UltraEdit中定位。
  第一处:
  IL_00a4:  /* 2D   | 0E               */ brtrue.s   IL_00b4
  将2D改为2C(brfalse.s)。物理偏移在0x3118h处。
  第二处:
      IL_0000:  /* 73   | (06)00003B       */ newobj     instance void RedGate.Licensing.Client.Licence/*02000008*//'?'/*02000009*/::.ctor() /* 0600003B */
      IL_0005:  /* 0A   |                  */ stloc.0
  
  在物理偏移的0x2cb4处。紧接着这两句修改。原文件为
  00002cbah: 02 03 12 00 28 29 00 00                         ; ....()..
  改为
  00002cbah: 02 17 7D 38 00 00 04 17                         ; ..}8....
  即MSIL代码
  ldarg.0
  ldc.i4.1
  stfld      bool RedGate.Licensing.Client.Licence::'?' 
  ret
  保存修改,运行。按格式输入注册码和激活信息,显示激活成功,不再提示有使用时间了。序列号生成器偶就不写了,很简单的算法。不知道什么时候才能搞定RSA。
 

  附件里是patch过的RedGate.Licensing.Client.DLL,仅供参考!
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2006年08月24日 0:39:39