自已编写Resharper v1.0注册机的流水帐(2) -- 分析算法、痛苦
自已编写Resharper v1.0注册机的流水帐(1) -- 上阵、分析、碰壁
四、注册机
做注册机,就得搞清别人的算法,算法估计都在LicenseChecker类里面,现在看他的代码吧。。
首先是构造函数:
{
// 搞定public key
this.N = new BigInteger(publickey, 10);
this._username = username;
this._company = company;
try
{
// _code 就是最终的明文Lincense
this._code = new BigInteger(Convert.FromBase64String(license));
}
catch (FormatException)
{
this._code = LicenseChecker._zero;
return;
}
// 将username转换为Biginteger,并与1进行位或运算
byte[] buffer1 = LicenseChecker.GetBytesFromString(username);
BigInteger integer1 = new BigInteger(buffer1);
integer1 = BigInteger.op_BitwiseOr(integer1, BigInteger.op_Implicit(((int) 1)));
// 解码
this._code = this._code.modPow(integer1, this.N);
}
唉,代码还是太。。。看来Reflector还要改进。不过看懂还是没有问题的,
1. 取得PublicKey,并将其以十进制转换为BigInteger类型
2. 将License解码,并转换为BigInteger类型.
3. 将username也转换为BigInteger类型,并与1做位或运算,实际上它就是真正的公钥。
4. 将username的BigInteger、publicKey的BigInteger与License做模&&幂运算。
看到这里,我算是看懂了,又是BigInteger,又是publicKey,又是modPow运算,再明显不过了,这就是RSA算法嘛!!!太好玩了,兄弟们赶快去找公式,这个算法俺后面就不具体说了的。
其中BigInteger类是算法的重点,C#中没有现成的类型,用Resharper自带的吧,But由于重载了很多运算符,resharper反编译出来的代码几乎不可读,咋办?
俺想起了,JDK。。。在JDK 1.4中BigInteger是标准库中的,JDK又是开源的,拿过来抄抄嘛:)
但是一打开JDK中BigInteger源代码,天,那么多,还继承至一个Number,而且Java是不支持运算符重载的:(算了,俺很懒,还是网上找找有没有现成的吧。
Google就是牛,一下子就找到了,还是C#的。。。
http://www.codeproject.com/csharp/BigInteger.asp
直接拿过来用吧:)
回头再看,在LicenseChecker构造函数中实际上就是已经把License解码成为“明文”了,并保存在_code字段中了,这个_code代表的“明文”里面又有一些什么信息呢?接着看。
首先来看IsChecksumOK的源代码
{
get
{
if (this._code != LicenseChecker._zero)
{
return (((this._code.IntValue() & 65535) % 65521) == this.UserHash());
}
return false;
}
}
这段代码不用多解释,但是很重要,_code与65535进行位与运算,并被65521除余UserHash的值,这个UserHash在反编译的代码写的很清楚,虽然反编译的不咋的,但是改一改就可以直接拿来用了。
这段代码虽少,但是却是算法的关健,实际上就告诉你了,“明文”是啥样子的。
还有一个重要的属性是ExpirationDate,他的源代码如下:
{
get
{
DateTime time1;
// 以下两句是关健
int num1 = (BigInteger.op_RightShift(this._code, 56).IntValue() & 65535);
if (num1 == 65535)
{
return DateTime.MaxValue;
}
try
{
time1 = new DateTime((2003 + (num1 & 127)), ((num1 >> 7) & 15), (num1 >> 11));
return time1;
}
catch (Exception)
{
return DateTime.MinValue;
}
return time1;
}
}
这代码太有意思了,看了前两行就不用看了,实际上就是判断(_code >> 56 & 65535) = 65535,等于则返回最大的DateTime,这就是我们要的,我们就是要个无限大的注册时间嘛:)至于后面的带时间的注册信息,看都别看了。。哈哈。
另外还有几个属性的源代码也是要看一下的,不过,我后面写了一个Check程序验证了几个试用版的号码,发现Type与Version的值永远为0,所以像Type、Version这些属性应当是为以后版本准备的,本版本不重要,所以我这里就不多说了。
“明文”知道了,“密文”又是什么呢?呵呵,这还不简单,License就是加密后的密文嘛:)
搞到这里,突然想起来CheckerLicener参数中的PublicKey是哪来的还不知道,往上找,找到EnterLicenseForm,发现没有,它自已倒还有一个SetPublic方法,看来,这个PublicKey还是从外面传进来的。
到底是从哪里传来的呢?俺不知道,唉,不了解Vs.net插件是怎么运行的嘛。。。
不过,事到如此,不了解也不行了,马上上网找了几篇讲Vs.net编写插件的文章,一边看一边用,最后又来分析Resharper的结构,花了一个上午,总算是在JetBrains.Resharper.Services.dll下JetBrains.Resharper.Shell.Impl命名空间中的LicenseData类找到了它的身影。(唉,这么大一个LicenseData类,我以前怎么就没有看到:()
反编译,PublicKey就这样呈现在我们的面前
{
get
{
return "3439664563558343388897268028516178220150974083190422162869";
}
}
OK,PublicKey知道了,就要来找PrivateKey了。
回来看看RSA算法,我发现CheckLicense中的modPow运算是把PublicKey做N用的,而将Username做为公钥使用的。
// Iteger1是username | 1的结果,N是publicKey的bigint
this._code = this._code.modPow(integer1, this.N);
这就意味着,这个publickey,就是两个素数的积(即p * q),只要我算出这两个素数,就可以知道z(即(p-1) * (q-1)),从而就可以知道真正的密钥d了。
但是MyGod,这个publicKey足有60位啊!!!估计p、q的取值也在1*10的29次方至1*10的30次方之间,俺穷人的机器只有AMD 号称的XP 1700+啊。你要让我算多久啊。。。。
神啊救救我吧。。