反编译工具:.NET Reflector

前言:
.NET的IL给我们反编译提供了巨大方便,Reflector更是.NET领域的最伟大的发明,反编译出来的程序几乎和源文件一摸一样,任何初学者只需要稍微懂得C#语言就可以分析注册码算法。因此,.NET几乎是没有秘密的,.NET的软件作者,如果想保护自己的成果的话,记住用混淆器啊:D

本文主要面向初学者(我也是其中一个,呵呵),注重演示分析类代码的过程,比较适合新手练手之用。
本文只作科研和学习用途。

过程:
首先找找有那些可疑文件,分别用.NET Reflector打开看看。最后找到bin/Web*****.dll,发Lealsoft.*****.Common里面有几个出现License字眼的类名,于是我们就仔细往里面看看

public class ********License : ServerLicense
{
      // Methods
      public ********License(Type type, string key, long startTicks, long ticks, string username, long userIndex, long clientCount);

      // Properties
      public long ClientCount { get; }
      public bool IsExpired { get; }
      public long StartTicks { get; }
      public long Ticks { get; }
      public long UserIndex { get; }
      public string Username { get; }

      // Fields
      private long _clientCount;
      private long _startTicks;
      private long _ticks;
      private long _userIndex;
      private string _username;
}

看来只是一个用于保存资料的类,我们需要的License认证算法不在里面,不过我们由此也知道一个License包含有哪些信息。

public class ServerLicenseProvider : LicenseProvider
{
      // Methods
      static ServerLicenseProvider();
      public ServerLicenseProvider();
      protected virtual ServerLicense CreateEmptyLicense(Type type);
      protected virtual ServerLicense CreateLicense(Type type, string key);
      public override License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions);
      protected virtual string GetLicenseData(Type type);
      protected virtual Stream GetLicenseDataStream(Type type);
      protected virtual bool ValidateLicense(ServerLicense license, out string errorMessage);
      protected virtual bool ValidateLicenseData(Type type, string licenseData);

      // Fields
      private static readonly ServerLicenseCollector LicenseCollector;

      // Nested Types
      private sealed class ServerLicenseCollector
      {
            // Methods
            public ServerLicenseCollector();
            public void AddLicense(Type objectType, ServerLicense license);
            public ServerLicense GetLicense(Type objectType);
            public void RemoveLicense(Type objectType);

            // Fields
            private IDictionary _collectedLicenses;
      }
}

看起来有点像了,于是我们找ValidateLicense的实现来看看
protected virtual bool ValidateLicense(ServerLicense license, out string errorMessage)
{
      errorMessage = null;
      ********License license1 = (********License) license;
      if (license1.IsExpired)
      {
            errorMessage = "The License has expired.";
            return false;
      }
      return true;
}

很失望,居然只是按照IsExpired字段来判断,跟具体算法依然无关。那IsExpired是怎么判断的呢?我们找********License.IsExpired属性:
public bool IsExpired
{
      get
      {
            if (DateTime.Today.Ticks <= this._ticks)
            {
                  return (DateTime.Now.Ticks < this._startTicks);
            }
            return true;
      }
}
 
原来是看当前时间的Ticks是否在License指定的时间范围内。ok,我们继续看另外一个Validate的函数

protected virtual bool ValidateLicenseData(Type type, string licenseData)
{
      bool flag1 = false;
      char[] chArray1 = new char[1] { ':' } ;
      string[] textArray1 = licenseData.Split(chArray1);
      if (textArray1.Length == 6)
      {
            return (string.Compare("********1Licensed", textArray1[0], true, CultureInfo.InvariantCulture) == 0);
      }
      return flag1;
}

看起来是验证licenseData的格式,它是用":"分隔各字段的,并且第一个字段需要为********1Licensed

protected virtual Stream GetLicenseDataStream(Type type)
{
      string text3 = type.Assembly.GetName().Name;
      type.Assembly.GetName().Version.ToString();
      string text1 = "~/licenses/********.lic";
      string text2 = null;
      try
      {
            text2 = HttpContext.Current.Server.MapPath(text1);
            if (!File.Exists(text2))
            {
                  text2 = null;
            }
      }
      catch
      {
      }
      if (text2 != null)
      {
            return new FileStream(text2, FileMode.Open, FileAccess.Read, FileShare.Read);
      }
      return null;
}

恩,看到没有,里面找到license的保存位置了。我们生成的license也应该放在这里

protected virtual ServerLicense CreateLicense(Type type, string key)
{
      char[] chArray1 = new char[1] { ':' } ;
      string[] textArray1 = key.Split(chArray1);
      return new ********License(type, key, long.Parse(textArray1[1], CultureInfo.InvariantCulture), long.Parse(textArray1[2], CultureInfo.InvariantCulture), textArray1[3], long.Parse(textArray1[4], CultureInfo.InvariantCulture), long.Parse(textArray1[5], CultureInfo.InvariantCulture));
}
 
从这里可以看到license的保存格式,原来合法的license只是简单用":"把几个license参数分隔开。对照********License的构造函数,我们就明白各字段的含义了。也可以构造出一个伪license。我随便构造一个,
string data = "********1Licensed:632454807859509835:735608407859509835:henry:0:10000";
注意一下那两个ticks字段,需要把当前的ticks时间包含起来才行,否则IsExpired会判断为true的

我们还没有找到验证的核心代码,继续找……什么?找完了ServerLicenseProvider了?
看来我们要的东西不在这里,那会在哪里呢?
看看Lealsoft.********.Common命名空间,似乎还有个EncryptedLicenseProvider的类,向里面看看

protected override Stream GetLicenseDataStream(Type type)
{
      Stream stream1 = base.GetLicenseDataStream(type);
      if (stream1 == null)
      {
            return null;
      }
      DESCryptoServiceProvider provider1 = new DESCryptoServiceProvider();
      provider1.Key = EncryptedLicenseProvider.encryptionKeyBytes;
      provider1.IV = EncryptedLicenseProvider.encryptionKeyBytes;
      ICryptoTransform transform1 = provider1.CreateDecryptor();
      return new CryptoStream(stream1, transform1, CryptoStreamMode.Read);
}

像是算法了吧。没错了,这就是解密算法了,这么短?确实是啊,你没有看到它调用了base.GetLicenseDataStream吗?而它的基类就是我们刚刚分析过的ServerLicenseProvider。
好,我们下一步搞清楚Key和IV的值。去看看构造函数:

static EncryptedLicenseProvider()
{
      EncryptedLicenseProvider.encryptionKeyBytes = new byte[8] { 0x35, 70, 0x42, 50, 0x38, 0x31, 70, 0x36 } ;
}

yeah,private key也找到了,于是我们就可以实现keygen了(其实是license generator)。就是实现DES加密而已。不熟.NET框架?查MSDN去吧:D

=============== keygen ===============
      byte[] encryptionBytes = new byte[8] { 0x35, 70, 0x42, 50, 0x38, 0x31, 70, 0x36 } ;
      DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
      provider.Key = encryptionBytes;
      provider.IV = encryptionBytes;
      ICryptoTransform transform1 = provider.CreateEncryptor();

      string data = "********1Licensed:632454807859509835:735608407859509835:henry:0:10000";
      //Console.WriteLine(System.DateTime.Now.Ticks);  //看看现在的ticks
      byte[] message = Encoding.UTF8.GetBytes(data);
      FileStream fs = new FileStream("c:\\********.lic", FileMode.Create,
        FileAccess.Write, FileShare.Write);
      CryptoStream cs = new CryptoStream(fs, transform1, CryptoStreamMode.Write);
      cs.Write(message, 0, message.Length);
      cs.Flush();
      cs.Close();
      fs.Close();
===============================================