|
以下是一些人的编程体会摘录,基本未改其中的内容,在此表示感谢!
附录1加密已形成密码学我引用《应用密码学》作者的话:
世界上有两种密码:一种是防止你的小妹妹看你的文件;另一种是防止奸人阅读你的文件资料。
如果把一封信锁在保险柜中,把保险柜藏在纽约的某个地方…,然后告诉你去看这封信。这并不是安全,而是隐藏。相反,如果把一封信锁在保险柜中,然后把保险柜及其设计规范和许多同样的保险柜给你,以便你和世界上最好的开保险柜的专家能够研究锁的装置。而你还是无法打开保险柜去读这封信,这样才是安全的。
意思是说,一个密码系统的安全性只在于密钥的保密性,而不在算法的保密性。
对纯数据的加密的确是这样。对于你不愿意让他看到这些数据(数据的明文)的人,用可靠的加密算法,只要破解者不知道被加密数据的密码,他就不可解读这些数据。
但是,软件的加密不同于数据的加密,它只能是“隐藏”。不管你愿意不愿意让他(合法用户,或 Cracker)看见这些数据(软件的明文),软件最终总要在机器上运行,对机器,它就必须是明文。既然机器可以“看见”这些明文,那么 Cracker,通过一些技术,也可以看到这些明文。
于是,从理论上,任何软件加密技术都可以破解。只是破解的难度不同而已。有的要让最高明的 Cracker 忙上几个月,有的可能不费吹灰之力,就被破解了。
所以,反盗版的任务(技术上的反盗版,而非行政上的反盗版)就是增加 Cracker 的破解难度。让他们花费在破解软件上的成本,比他破解这个软件的获利还要高。这样 Cracker 的破解变得毫无意义——谁会花比正版软件更多的钱去买盗版软件 ?
然而,要做到“难破解”,何尝容易? Sony 曾宣称的超强反盗版(Key 2 Audio音乐 CD反盗版),使用了很尖端的技术,然而最近却被一枝记号笔破解了,成为人们的饭后笑料!
所以,很多看上去很好的技术,可能在 Cracker 面前的确不堪一击。就像马其诺防线一样,Cracker 不从你的防线入手,而是“绕道”。这样,让你的反盗版技术在你做梦也想不到的地方被 Crack 了。
为什么会这样呢 ?归根到底是因为软件在机器上运行,并且软件和机器是分离的——这一点是关键,如果软件和硬件完全绑定,不能分离,是可以做到象 IDEA 之类几乎不可破解的系统的。这将在后面谈传统软件保护技术时详细说明。
对我的这个解决方案,我不能保证Crack高手在几天之内不能破解它,我只能说:“在这个软件中,我尽量堵住了当前破解者普遍使用的方法以及“我想得到”的可能的缺口。”但是我相信,倾注了我 三个月心血的反盗版软件,决不是一个“玩具式”的反盗版软件。
附录2《如何用简单方法防止破解》北极异型 在Debug的手册里可以看到Debug工具的局限:第一个局限是只能下4个内存区域的断点,每个断点不能控制超过两个字节,这样内存断点不能控制超过16个字节的区域;第二个局限是对多线程只能同时跟踪一个线程。 假设你的注册部分有300行,你可以分成30个子程序调用或重复的func1(),func2()... func30()。将他们随意放到程序的各个部分,一定不能放在一起(自己能找到就行了)。不要用Memcpy等常用系统调用拷贝注册码,尽可能自己写,像Memcpy很好写,性能差点无所谓。经过编译后inline函数展开,注册部分和其他代码混在一起,他要写出注册机就像大海里捞针,在几十万甚至上百万汇编代码里找出有用的注册部分。
利用Debug的第一个局限最重要的一点是:注册码也不要放在一起,假设你的注册码是12位,千万不要用一个12位的数组放注册码,你可以在程序的不同位置定义12个全局字符变量,每个放一位,这样注册码在内存就不连续了。最好再加密处理一下(简单的字符异或就可以),验证时再解密。也不要用连续内存保存验证用到的变量,尽量将用到的验证临时变量分散定义在程序的不同处,再在验证中,不断转移一些值到其他变量中,对付暴力和Loader会比较有效。
没有必要用复杂的加密算法,更容易成为追踪的目标。只要你将注册部分隐藏的足够好,也没有漏洞,你花1天写的加密算法,破解者可能会花100-1000倍的时间破解。大部分人都会放弃。
你将注册做在一起,就像将你的财宝放在现代保险箱里,虽然非常坚固难以解密,对于开锁高手两分钟就打开了。
而古代海盗用的方法是将财宝埋在海岛上,这样没有藏宝图,对所有高手和低手都只有一条路,拿一把铁撬挖,可能要挖一生。程序有那么多代码,反编译出来可能超过百万行,你将注册部分藏在里面,藏的好就如同将财宝埋在海岛里。那些所谓的Crackme只是给高手玩儿的现代保险箱而已,用原始的方法可以达到同样效果。
1.读完注册码后不要立刻检查注册码,因为读注册码肯定用到系统调用,系统调用附近很容易下断点。先放到内存作为全局变量,然后你可以在程序的任何部分,任何时候,读注册码,读内存是没有任何系统调用的。
2.两种办法处理在内存的注册码,
一种方法是在检查前分散隐藏注册码,这样他们无法从内存中搜索到注册码出现的位置,因为他们会在注册码出现位置附近下断点。
还有一种方法是正好相反,你可以在读到注册码后,多次将注册码在内存(用Malloc分配多处)的各个位置做大量拷贝,这最多浪费一些内存。16Bits的注册码做10000份拷贝也只用160K内存。如果不用Malloc分配这些内存,那么最好和其他程序使用的全局变量交互混在一起(这样即使破解者用HEX 编辑器打开并搜索你的程序中使用那部分内存的代码,也无法将正常程序数据和注册码区分出来). 你不要立刻检查注册码,10000份拷贝你只要以后随机找一份用就行了,破解的人不知道你正在用的是那一个. 同时你可以不断生成一些假的读取内存注册码的调用干扰破解者。这种方法对程序的性能影响微不足到,只是浪费一点内存。因为Debug对内存下断点的局限,这种情况他要下断点,累死的就是破解的人了。
3.程序多处做CRC校验文件大小检查,发现不对就退出。一定要多处检查,不能只检查一次。
4.用inline函数将注册部分分成许多小块,分散到程序各处运行。最好能放在不同的线程中运行. 检查结束不要给任何提示,在程序中的内存中做一个标志即可,提示信息要延迟一段时间出现,不要让破解者通过提示信息找到标志位置或检查结束位置,否则前功尽弃。当然也可以象上面那样做10000个标记,随机用一个,这时你不用担心破解者知道标记的位置.而且标记最好不要用0或1,可以用一个貌似随机的值作为注册成功的标记,如何产生这个随机值只有你自己知道。
什么算法无所谓,最终目的就是让破解者的作用发挥不出来。和古代海岛藏宝一样,你只要把检查痕迹很好擦掉分散,破解高手的用处就发挥不出来。破解者玩的Crackme就是保险箱,如果是开锁高手,目标明确总能打开,不行还可以用炸药。但到海盗的藏宝地点,任何开锁高手都没有用。
这种方法用的好的结果是你容易创造检查注册码的方法,自我创造发挥的余地很大,而对破解者会很麻烦,他搞不清楚你在干什么,你只要让破解者感觉象是蒙着眼睛走迷宫或者在猜没有谜题的谜语就行了.而且对你来说更换算法也很容易,因为可能根本没有算法,只有谜语和迷宫。要充分利用程序反汇编后代码多这个特点,除了读注册码的时刻,其他时候根本没有系统调用,除了内存,根本无处下断点.而你使用复杂的加密算法通常的结果是你麻烦,破解者很容易,因为各种加密算法都已经研究透了,而且用复杂的加密算法的调用或系统调用立刻暴露你检查注册码的位置,为暴力破解提供了方便。
我和很多人一样,也是不会汇编语言,而且程序一出来就被0day破解,而且几乎每一个版本它们都要破解.苦于不会汇编,也不太了解破解流程,只好去看SOFTICE的手册,想了这么一个简单的方法. 0day虽然它们经验丰富也能破解,而且似乎也逐渐了解和改进对我的程序的破解. 但许多破解是坏的,根本不能用. 而且就是被破解后,只要将这些inline调用和全局内存变量的位置稍微变一变(大概只需要2-3个小时),它们下一次的破解又是坏的. 而且我还没有完全使用上面的方法,例如我还没有做10000份的内存拷贝或10000份的标记. 因为只是简单的用inline和将注册信息与其他全局变量混在一起,现在就已经够了,0day的暴力破解就经常会破坏正常程序数据.
|
|