Wintle·旧居

个人blog移至:http://www.wintle.cn,欢迎光临。
posts - 27, comments - 148, trackbacks - 3, articles - 0
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

如何建立有效的.Net软件注册保护机制

Posted on 2004-08-25 16:28  Wintle  阅读(...)  评论(...编辑  收藏

这是我在做CodePlus软件的注册模块时遇到的实际问题,现在还没有很好的解决。

.Net程序很容易被反编译出来,也可以通过混淆器来进行一些掩盖,也可以通过强名称来保证不被修改。这真是一个矛与盾的问题。

那么到底怎么最大限度的保证软件不被修改、注册器不容易被编写。可能真是一个头痛的问题。

一般的思路大致是:

一、在注册机制上采用非对称加密结合数字签名的方法(这个过程较为复杂,也是我目前没有弄出来的地方,不过我会努力解决这个问题的,也希望得到高手们的指点)

二、一定要强名称。关于如何使用强名称,这个比较容易:一般的步骤是:

     1、在vs.net提供的dos命令窗口里 用 sn.exe -k 来生成一个钥匙对。并将之放在项目根目录下。 如c:\>sn -k c:\YourPrj.snk 然后将YourPrj.snk copy到你的project目录下。

     2、修改AssemblyInfo.cs,

               [assembly: AssemblyDelaySign(false)]
               [assembly: AssemblyKeyFile("..\\..\\YourPrj.snk")]
               [assembly: AssemblyKeyName("..\\..\\YourPrj.snk")]

      3、rebuild it.这个时候就已经被强名称保护了。任何对生成的exe或dll进行的修改(无论是IL,或者二进制),都会导致程序将不再运行。

三、通过混淆器对生成的exe或dll进行混淆。推荐采用http://www.remotesoft.com/salamander/obfuscator/download.html 可以对强名称保护下的exe或dll进行混淆再重签名。也就是说,可以得到混淆和强名称的双重保护。当然,这里也就会提出一个问题。就是既然remotesoft的obfuscator可以对强名称保护下的文件进行修改(混淆就是大修改了)再重签到强名称,虽然它要求YourPrj.snk要保留在project的根目录下。但我想进行一下替换,应该也是有可能的。因此,从另一角度来思考,这二种方式其实到最后,对于高手来说,都不会是什么难以对付的问题。其保护功能也就很有限了。

再说关于非对称加密进行注册验证机制,由于对rsa算法的使用还不是太明白,所以一直没有测试成功。昨天在索克论坛上看到一则东西:

http://www.sorke.com/bbs/Announce/Announce.asp?BoardID=100&ID=6060

里面讲到XC#的验证代码是公开的。(这下子明白了,混淆和强名称直接都不用了。)但是我在想,要破解xc#的那位朋友,为什么不把这个验证代码去掉,然后再编译呢?不解了。或者xc#的程序是已经生成好了的,虽然提供部分源代码,也只是给你看看,而不是让你有重新编译的机会吧。那么,这个里面强名称肯定是用到了的。由于没有下下来的研究,不是太明白。先把那段有用的代码copy过来先,这是xc#进行验证的算法:

const string RsaKey  = "qZj4mbr2CONBW+ABCBddSSDTfKFSzDQc9LltZ3Xzl1UrrE0iwrgQQ/NYNr3h760/JsBb5eTV
+owfTAAdjKzayIEjnTu1W2XMiDSfWfPcDaEpnoG3cWY1BhpTsUz8XxapVSHpRYovaaeA/1SY
fb0h7xbku1M4M9LgGdUwlab+iMc=AQAB";

static bool IsValidPair(string name, string key, string rsaKey)
{
    try
    {
        byte[] b = System.Convert.FromBase64String(key);
        using (RSA rsa = new RSACryptoServiceProvider())
        {
            rsa.FromXmlString(rsaKey);
            RSAPKCS1SignatureDeformatter f = new RSAPKCS1SignatureDeformatter(rsa);

            f.SetHashAlgorithm("SHA1");
            return (f.VerifySignature(new SHA1Managed().ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(name)), b));
        }
    }
    catch { return false; }
}

再引用那位强人对这段代码的分析:

我仔细检查代码后才发现,检查的代码关键是RSAPKCS1SignatureDeformatter类,它是System.Security.Cryptography名称空间中的一个类,专门负责验证RSA加密签名的。
RSA就是不对称加密的算法。就是通过私钥加密的只能通过公钥解密。上面的静态变量RsaKey明显就是公钥。就是说必须提供他通过带有私钥的Key生成器生成的验证码,才可以使Hash验证通过。就是说,没有任何办法通过公钥获得验证码。

那么生成注册码的办法没有用,是否可以通过修改IL代码的方式破解呢?只要把返回值直接改成true,那么无论是不是验证通过都可以使用了。经过检查,这个办法还是不行。
因为这个dll程序不是由XC#主程序自己使用,而是要被vs.net调用的,所以只能注册为全局程序集。我发现验证逻辑实现的程序集XHCS.VisualStudio.dll已经被安装到了GAC中。
大家都知道,安装到GAC的所有程序集必须要加入强名称验证。如果我修改了dll的内容,那么这个强名称必须被删除,这个程序集也没有办法在GAC中了,导致vs.net无法使用。

好了,现在我想到的是,基于这个代码,是否可以用在我们自己的系统里面,那么,在加密签名这块要怎么来写,尝试用:

   RSACryptoServiceProvider rsp = new RSACryptoServiceProvider();
   rsp.FromXmlString(skey); //这里读进了公钥和私钥
   bytes=enc.GetBytes(this.rtxtUserGiveSN.Text);
   bytes = rsp.SignData(bytes,"sha1");

这样的代码来做,但失败了,还没有找到原因。说是给对象设置了空值。

再想想,如果有谁对这方面有经验,希望指导一下。