软件暴力破解的原理和应对方法

软件暴力破解的原理和应对方法



共享软件是目前世界上软件业比较热门的话题,国内更是如此。成千上万的中国程序员以极 大的热情投入到这个领域来,都憧憬着用辛勤的劳动获得丰厚的回报;但,实际并非如此,绝大多数的人都铩羽而归。值得注意的是:除了软件设计和技术上的原因外,最大的原因就是共享软件被破解( Crack )了……

面对破解

一个做共享软件的作者,面对的是已经形成团伙的众多破解高手,国内的什么 CCG 、 BCG ,国外的 eGis 、 King 、 Core 、 TNT 、 DAMN 和 TMG ,皆为水平一流的破解组织。全球盗版软件不少于 80% 都是由他们的破解的,技术实力连大软件公司都不可小视。

看到这里,你是否已经灰心了 ? 别怕,虽然目前我们理论上无法完全避免被破解,但如果能够有效地拖延被破解的时间,并充分打击破解者的自信心,是可以让破解者无法忍受这种折磨从而最终放弃的。

破解,通常的做法有两种——暴力破解(爆破)和写注册机。笔者就自己积累的经验来依次讲解每种破解方法的原理和应对方法,某些关键的例程讲解( Delphi 代码),使用 C++ 和 VB 的朋友可以自己稍微修改一下。希望这能对一些新手有些帮助,能够更有效地保护自己的劳动成果。

认识暴力破解

暴力破解简称“爆破”,这是破解手段中最常见的,也是最简单的破解方法。该法最适合于对付没有 CRC 校验的软件,破解新手乐于采用。

大凡共享软件,验证是否注册大多采用 if 条件语句来进行判断,即使你采用了什么 RSA 或 ECC 等强力加密算法,也免不了使用 if 条件语句。这里就是共享软件最为危险的地方,也是爆破手孜孜不倦所寻求的目标。
例如,你的注册函数类似如下:

{利用 RSA 进行注册码的数字签名验证}
if RSAVerify ( MD5 ( Key ), MD5 ( Code ), e , n ) then
ShowMessage ( " 注册成功 !" )
else
ShowMessage ( " 注册失败 !" );

{这里 Key 是用户输入的注册码,是由你发送给注册用户的, Code 是根据用户输入的用户名自动计算出来的注册码, e 是 RSA 算法的公匙,而 n 是 RSA 算法的模数。}




 第一次过招

上例注册函数即使使用了强劲的 RSA 算法进行注册码验证,但依然很容易被破解,我们只要把这里修改为:

将逻辑判断改为否 
if not RSAVerify ( MD5 ) Key ), MD5 ( Code ), e , n ) then
ShowMessage ( " 注册成功 !" )
else
ShowMessage ( " 注册失败 !" );

这时戏剧性的结果会产生:随便输入任何注册码都可以注册通过,相反输入正确的注

册码却无法通过注册。

要破解这样的软件就必须先反汇编或者跟踪你的程序,找到判断注册码的 cmp 、 test 等汇编指令后的关键跳转指令处,通常是 je 、 jz 之类的汇编指令,把它们修改为 jne 或 jnz 即可,这样常常只需要修改一个字节就可以完美破解了。

目前大部分共享软件都是用以上方法进行判断,这也是为什么网上被破解的软件铺天盖地的主要原因。因为这样破解实在是太简单了……

第二次过招

其实只要把软件的关键代码嵌入到注册码或者注册文件中就可以充分防止破解。

最简单的方法就是把关键代码(你的软件功能限制部分最关键而且最简单的一个函数)做成一个小 DLL (动态链接库),用强力对称算法加密(密匙可以是主程序某一固定不变的部分或壳的特征 Hash 值)后生成一个注册文件( License 文件,这格式只有你知道),或者 Base64 编码后生成一个注册表文件,用户可以双击导入注册表内。

校验流程如下:已注册用户验证注册码时,先验证有没有文件,没有文件则自然受限制的功能无法使用。如果有注册文件,解密后即生成一个小临时文件。如果主程序被脱壳或者被修改(爆破),自然 Hash 值密码不符,解密出来的肯定都是垃圾码,没有一点用处。只有没有被修改的主程序才能正确地解码,而且当然只有解密正确的文件才是一个真正的 DLL 文件,才能被 GetProcAddress 函数找到欲调用的关键函数地址。这样只有已注册用户才可以享受到你的软件的全部功能了。如此一来, Cracker 破解你的软件就变得很困难了。

首先,他如果没有注册文件,即使他把主程序脱壳了,由于受限制的部分和注册文件是关联的,他也根本无法修补完整。




第二,即使他得到了你的注册文件,由于是加密文件,他也无法直接利用,这样就逼迫他去拆解你的算法,这可是他们最不愿意碰到的事情啊 ! 如果到了这一步,只有真正对加密算法有研究的 Cracker 高手才会继续破解下去。

第三,你是可以用些小技巧来使他的破解工作更加繁锁。这里我推荐大家使用 DSA 公开密匙加密算法,它和 RSA 一样,可以进行数字签名( RSA 还可以加密, DSA 则只能进行数字签名)。笔者这里选用它的原因就是它有一项非常实用的特性:随机数填充机制。即 DSA 每次签名都要使用一个随机数 K ,正因为有这个 K 的存在,即使是相同的用户名和机器识别码,由 DSA 加密过的每份注册文件都不会相同。这对 Cracker 拆解你的注册文件来说是一个极大的障碍。

第四,即使他得到了解密后的 DLL 文件,他也需要大幅度地修改主程序或者把你的 DLL 部分的关键

代码拆出来添到主可执行文件中。这就看他对 PE 文件格式理解得如何了。即使这样,如果你的程序中有大量的 Hash 校验和死机代码,你就耐心等着我们可爱的 Cracker 同志吐血吧……:)

最后还要记住:用完这个 DLL 临时文件后立即从内存中卸载此 DLL 并删掉,而且注意在解密之前探测一下,系统中有没有 FileMon 这个威胁极大的探测器:

{探测 FileMon }
function DetectFileMon : Boolean ;
begin
if CreateFile ( PChar ( "\\.\FILEVXD" ),
GENERIC_READ or GENERIC_WRITE ,
FILE_SHARE_READ or FILE_SHARE_WRITE 
nil ,
OPEN_EXISTING ,
FILE_ATTRIBUTE_NORMAL ,
0 <> INVALID_HANDLE_VALUE then
Result : = True // 如果有,就 Down 机 !
else
Result : = False ;
end ;

当然,你可以保护得更好一些:可以不采用临时 DLL ,而把解密后的关键代码用 WriteProcessMemory 这个 API 函数写入到主可执行文件自己进程被提交( Committed )的内存页面的指定位置去。这样由于磁盘上没有解密后的临时文件,破解更加困难。事实上,目前世界上最强劲的专业保护软件 Amadillo 就是用的这种方法。而且这种方法可以充分防止被调试器 Dump 。但实现起来比较困难,尤其是在 WinNT 5 以后的操作系统中。

由于这种方法将注册文件和受限制代码惟一关联,爆破手拿到你的软件也只有干瞪眼。建议大家都给共享软件加上功能限制,这样比时间和次数限制更加安全。

相关文档
最新文档