鉴于任何程序都运行cpu指令并可被反编译成汇编码,也就是说,理论上,程序都是在裸奔,所以天下无不可破之程序。所以这里讨论的防止,只是一定程度上的,比如提高破解难度,拖延时间,让破解者挫折感增加等等。
一. pb的现状
pb从5-12,其pbd文件,和dll文件格式的版本都为0600(pb5尚不知晓,还没找到pb5的程序),结构大致一样。所以pbkiller的作者只要还有时间,他可以在一到三个月就做出最新的版本。这个软件比起shudepb来说更灭绝人性,因为作者将其免费(最初好像也是要卖钱的,无奈,没有采用shudepb那样的网络计算,使得其被破解了。从这一点看也蛮有意思。),从而使得无数的好事者参与到破解的学习中。因为我们写的程序众多,高手只对商业价值高的软件感兴趣,而且以它为职业糊口,而初学者只是随便找个软件练手。我朋友有个软件被初学者拿来在里面放了一些新文件,其实根本没破解到,但是宣称为“破解版”。破解方法写到:输入随便的注册码,提示出错,然后就得到真正的注册码了。。。如果不出错,多试几次即可。结果呢?程序根本就不在输入注册码的地方判断正确以否,你说搞笑不搞笑。pbkiller目前最高版2.5.8只支持6-9,我看过,代码反编译比较漂亮。错误肯定存在,但已经很完美了。
“错误”只有对自己的项目反向时才会头痛,对破解者而言,他们只想看注册部分的算法。
shudepb是商业版,而且几千块一套。这一定程度对pb有利,毕竟初学者是不会去买一套没用的工具来学习破解的。而且demo版它限制代码的长度,非注册版看不全代码。还真是厚道。所以如果你不打算应对反编译软件的话,那请把代码写在100行之后(具体是不是100行限制,自己测试看看)。这是最简单办法(pb10以上)。
至于"pbd"还原器,原理是用pb本身的方法得到dw的反编译代码。这个不叫做破解。pbd中的dw本来就可以直接用,而pb又有函数可以得到语法。这就没什么新鲜的了。pb的本身函数是不会提供功能给你能反编译得到源码的。是绝对不可能的。
在网上搜索破解pb程序的帖子,通用的说法是:程序没法用OD来调,而且pbvm里也不好跟,都是要用到pbkiller这个利器,这在一定程度上说明,对付菜鸟,使得其反编译成功的几率小一些,就能更好的保护自己辛苦劳动的软件。
目前几种破解的方法:
a. 看程序算法和思路,直接破解算法,公布算法或者写注册机。(如果代码一览无遗,这个方法其实最简单,但是也有很多菜鸟看不懂pb代码,还在网上到处问。可笑)
b. 有人尝试搜索pbd中的关键代码,用ue修改pbd中的字节。(这个有难度,做的人不多,而且多数菜鸟只限于pbkiller会用而已,对pbd文件不了解。据我自己观察:对pbd有了解的话,很多地方可以用ue直接改。),比如修改一个字节可以把+变成-,把=变成<>,有点恐怖。
c. 对于外部dll的调用,有破解者提议写假的dll来爆破之。让其返回一个已经注册过的序号的依据,如磁盘号。有所谓的免狗程序应该屏蔽了对狗的调用或者搞了假的dll。所以有必要多处验证文件的md5值。只要他没法修改pbd文件。
以下是我猜想的一些做法,是否切实可行,待大家提意见,一个软件总是新出时容易被破解,如果一年都没被破解,以后版本升级,变来变去的,破解也就更少了。
我晓得一个朋友的软件,他一年连出十多个版本,新版一出,两三天之内就被破。他用的是delphi写的,他真可怜。二进制编译的软件及asm之后也许更容易破解吧,只要调试就搞定了。就像pb一样,破解得非常快。
pb程序其实也占几个优势:多是商业软件,商业用户还是有购买的习俗。练手者不太容易找pb程序来破解,因为共享软件很多。一般是你的用户用得还可以,又不想给钱,就会有破解的念头。pb做的都是管理软件,破解高手对商业价值不高的软件不感兴趣。再者pb程序难以调试,知道的人都不想弄。加之sql之类的,显得异常繁杂,远没有破解一个二进制编译的,通用的,使用量大的软件有意思。
代理商破解的可能性更大。
二.我的几个构想的方法(都只针对增加难度,让静态pbd反编译难度加大,让pj者知难而退)
1.能采用machinecode方式的尽量用机器编译,单一exe文件,用pbkiller和shudepb都无法看到代码,只能看到变量。pj知难而退。
2.我有几个项目,代码超级大,在当初用pb10的时候就没法编译成机器码。现在的pb11因为有string和blob的bug没法编译成机器码,而pb12刚出来,还没测试。不过代码太多似乎没法编译成单一machinecode的exe。那就只能用p-code。
所以第二点就是欺骗,在程序下放跟exe同名的假的pbd文件。比如程序为main.exe,放一个假的main.pbd给pj者去调试。当然不一定是假的pbd,你随便找一个pbd,用ue删除一些二进制数字即可,做的跟真的一样。
pbd可以放在在window系统目录下,或者path定义的任何地方。或者之前文章也提到,写他几十个注册函数,放在不同的pbd里,到底是哪一个,只有你晓得,搞死他。librarylist也可以做做文章。因为同名对象,都依照lib顺序来查找。
3.程序启动时抓一个时间,启动结束后,抓一个时间,如果中途有停顿,是否可以判定在被调试,我想你可以删除他电脑上任何东西。反正都是破坏,别让他悠闲的调试程序。我作为一个写代码的人,我认为有必要这么做。他要QJ你,自然也允许你反抗。某个杀毒软件当年就这样把破解版搞得吐血。
4.注册码输入时,不做任何提示和判断。只是写一个标志,程序启动时在title那里显示为“正式版”或者注册版,但是实际上,我可以延期验证或者随机验证。呵呵,这样也许把破解者都搞得没办法。因为他只要输入一次注册码,不管是什么字符,都成了正式版。而至于什么时间验证,除非他有那个耐心,否则也觉得破解麻烦。对破解者而言,抓个特征值是个小case,而要一行一行地看代码,估计吐血而死。
5.ini中的注册码调入内存和真实注册码对比时,我认为将其复杂化后放入一个数组或者blob中,注册码不是在程序启动时,而是在多时,多点,多处,重复的校验。我的想法,在100个地方校验,其程序破解的难度就可能让pj者早退却了。
而且,把注册码和系列号都跟程序的变量或者功能,或者数据返回条数等挂钩,做的非常隐蔽,不再是体现在一个if比较的地方。那就能大大减少破解的可能。因为我们知道,破解者无法修改pbd,而且不熟悉pb代码。如果搞得太复杂,可能他早就不干了。
6.我以后发行程序准备放100个或者1000个pbd在程序下,破解者要反编译就去反吧。看到100个pbd,就算我搞pb几年我也知难而退了。看到1000个pbd,立即倒地吐血而死。
7.如果主程序不能编译成单一machinecode的exe,则某些关键功能可以用run外部程序方式来运行,而外部程序用pb单独编写一个小的程序,小的程序是完全能编译成功的。
8.费解算法和写法。比如把一个数放入数组,然后在其他地方,其他时候有意无意去改变,或者复制,移动,再到某些时间去执行某些操作。或者引发一些限制。破解变成去看你的代码,我想没人愿意继续下去。
9.如果代理商破解你的软件,你就让客户对他产生不信任即可。买他的破解版就会被骗钱!做到这个足矣。而且你得一棍子把他敲死。
10.发若干个自己做的“破解版”,其实都是测试版,这样使用者找遍网上也下载不到真正的破解版。很多国内网站转载时都是煞有介事地称其为“破解版,绿色版,完全版”,其实都是测试版,搞笑!
11.暗桩:检测用户越过注册部分,不动声色地做限制。或者在多处多时深层次的地方,或者细节的地方做限制。比如数据汇总等于0.或者是其他单据隐蔽的错误等。
12.攻击:生成病毒特征体,致使杀毒软件介入。或者删除自己,或者重启,因为重启不可阻止,而且破坏调试过程。
13.程序运行完,调用一个外部exe删除所有文件,包括main.exe和所有pbd和dll(如果他做一个假的dll,就会被删除,而程序启动时释放一个新的文件出来,永远只使用自己的文件。这样他修改dll和pbd都没用。)
14.必须研究和使用pbkiller和shudepb这两个工具,研究它们的bug,利用bug写关键代码,至少在它bug没解决之前是可以保护的。比如pbkiller对dec{4}的解析不出来(*提示:如对不同精度的数做赋值,则丢失精度,或者做数学运算等等,丢失精度后转为字符串或者blob),对longlong也解析不出来。。。都可以利用之。这两个工具还有很多很多的bug。
*20090730今天分析发现一个pb的bug可以使得pbkiller无法得到任何代码:
-------------------------
//global function integer lf_count ()
int li_i0001
.....一直到
int li_i3260
int li_i3261
int li_i3262
int li_i3263
int li_i3264
return 1024
--------------------------
本想测试一下pbkiller对代码行数有没有限制,发现pb9在变量数超过3264行后,保存就会出错,但是重启IDE又能插入一个,可以保存,但是插入第二个变量就不行了,我用这种方法最大可以插入到:int li_i3270。现在我们先写3264个,编译和调用这个函数都没错。然后多申明一个:比如int li_i3265,采用增量编译后,主调用程序中返回1024(注意:不能进行全编译,否则报内存只读错误),这倒没什么价值,价值在于这样编译后的pbd,pbkiller无法反编译出任何代码。大家可以测试一下。如果用作关键点计算或者返回几个关键参数,倒是蛮好的!不过,工程project也只能用增量编译,否则IDE报错退出。
15.pbNI的运用。使得关键代码可以融入pbd,但是pbkiller又反不到,其他调式工具也单独调试不到。具体未测试过。见黄国畴的blog有实例。
16.编译后的文件假设包括一个main.exe和main.pbd,second.pbd,用ue打开main.exe,在最后部分RTL标志以上有一个dat*标志,找到其中的pbd列表,修改一下,如:mypic.jpg,然后把相应的pbd文件改改名字。效果也挺搞笑的。
17.做3-10个exe(不是主程序),其作用只是依次调用,比如1.exe调用2.exe,2.exe又去调3.exe.最后调用main.exe,各步骤的调用都带参数,并且这3-10个exe还执行部分文件操作和数据处理,或者部分认证和文件校验等,然后最后调用main.exe,而且带参数。这些参数隐蔽地作为一些地方的数据。也可以在3-10.exe执行过程中将一些数据写入sql中或者文件中。如此使用倍增难度的方式来为难pj者。
还是那句话,不要让pbkiller直接看到你的源码(给破解者一个假的pbd是个好办法),保护自己的软件一年以上。给客户一个必须要掏钱的理由!
只要能防止静态下反编译到你的pbd(一般破解者就是直接找跟主文件同名的pbd来编译看看是否能找到注册算法,根本不会在意你的pbd到底是不是正确的!!!也不会在意你对他的欺骗),你就有99.99%的希望保护到自己的软件,如果你的软件受众很少的话。
有兴趣可以参考《加密与解密》P197页的几个对软件保护的建议。(*见网上有,就摘抄过来):
本节将给出关于软件保护的一般性建议,这些都是无数人经验的总结。程序员在设计自己的保护方式时最好能够遵守这里给出的准则,这样会提高软件的保护强度。
(1)软件最终发行之前一定要将可执行程序进行加壳/压缩,使得解密者无法直接修改程序。如果时间允许并且有相应的技术能力,最好是设计自己的加壳/压缩方法。如果采用现成的加壳工具,最好不要选择流行的工具,因为这些工具已被广泛深入地加以研究,有了通用的脱壳/解压办法。另外,最好采用两种以上的不同的工具来对程序进行加壳/压缩,并尽可能地利用这些工具提供的反跟踪特性。
(2)增加对软件自身的完整性检查。这包括对磁盘文件和内存映像的检查,以防止有人未经允许修改程序以达到破解的目的。DLL和EXE之间可以互相检查完整性。
(3)不要采用一目了然的名字来命名函数和文件,如IsLicensedVersion()、key.dat等。所有与软件保护相关的字符串都不能以明文形式直接存放在可执行文件中,这些字符串最好是动态生成。
(4)尽可能少地给用户提示信息,因为这些蛛丝马迹都可能导致解密者直接深入到保护的核心。比如,当检测到破解企图之后,不要立即给用户提示信息,而是在系统的某个地方做一个记号,随机地过一段时间后使软件停止工作,或者装作正常工作但实际上却在所处理的数据中加入了一些垃圾。
(5)将注册码、安装时间记录在多个不同的地方。
(7)检查注册信息和时间的代码越分散越好。不要调用同一个函数或判断同一个全局标志,因为这样做的话只要修改了一个地方则全部都被破解了。
(8)不要依赖于GetLocalTime( )、GetSystemTime(
)这样众所周知的函数来获取系统时间,可以通过读取关键的系统文件的修改时间来得到系统时间的信息。
(9)如果有可能的话,可以采用联网检查注册码的方法,且数据在网上传输时要加密。
(10)除了加壳/压缩之外,还需要自己编程在软件中嵌入反跟踪的代码,以增加安全性。
(11)在检查注册信息的时候插入大量无用的运算以误导解密者,并在检查出错误的注册信息之后加入延时。
(12)给软件保护加入一定的随机性,比如除了启动时检查注册码之外,还可以在软件运行的某个时刻随机地检查注册码。随机值还可以很好地防止那些模拟工具,如软件狗模拟程序。
(13)如果采用注册码的保护方式,最好是一机一码,即注册码与机器特征相关,这样一台机器上的注册码就无法在另外一台机器上使用,可以防止有人散播注册码,并且机器号的算法不要太迷信硬盘序列号,因用相关工具可以修改其值。
(14)如果试用版与正式版是分开的两个版本,且试用版的软件没有某项功能,则不要仅仅使相关的菜单变灰,而是彻底删除相关的代码,使得编译后的程序中根本没有相关的功能代码。
(15)如果软件中包含驱动程序,则最好将保护判断加在驱动程序中。因为驱动程序在访问系统资源时受到的限制比普通应用程序少得多,这也给了软件设计者发挥的余地。
(16)如果采用keyfile的保护方式,则keyfile的尺寸不能太小,可将其结构设计得比较复杂,在程序中不同的地方对keyfile的不同部分进行复杂的运算和检查。
(17)自己设计的检查注册信息的算法不能过于简单,最好是采用比较成熟的密码学算法。可以在网上找到大量的源码。