• 标 题:软件反破解技术经验总结:欢迎加入您的经验
  • 作 者:jike
  • 时 间:2004-3-29 周一, 上午11:06
  • 链 接:http://bbs.pediy.com

共享软件的开发是一项非常困难的工作,有了投入,很可能还没有回报,或投入产出比过大。
在国内,您还必须应对软件被破解的情况。

所以相应的一些软件保护技术就产生了。以下是本人的一些经验:


1.对软件中重要的数据进行加密变换,如何实现呢,要知道,反汇编工具,最大的好处是通过分析字符串找到
突破口,我们可以将软件中使用的字符串找出来,放到一个文件中,一行一个,
再写一个小工具将其加密,在软件压缩后,将变换后内容贴到软件后面。在delphi中用memorystream装载即可。
这样脱了软件壳,肯定无法正常工作
加密代码段,方法不详
哪位知道,请告知一二。

2。检查进程的父进程,如果不是explorer进程,就是被别的程序装载,在win98下容易实现。使用createtoolhelp32snapshort即可
这样还可对一些脱壳机起作用,使其无法脱壳
如果EXE文件打开方式被更改,也会出现这种情况

3.delphi软件的保护,基于delphi为核心的软件将被反编绎,但以mck为核心编绎的软件则在dede中至今还是很安全的。很多情况下可
以直接在mck工程中引用vcl但有些时候则无效。

4使用readprocessmemory,writeprocessmemory来防破解,软件运行后,可以随时检查软件中某些地址是否被爆破手PATCH了,对于设置时间过
期的,可以在软件中让叛断过期的代码永远不成立,这样可以迷惑一些破解新手,没时间限制吧,过了时间,软件可以用writeprocessmemory
改写此段代码,而实现时间限制

5在所有进程中检查是否有特定名称,如trw2000这类,但软件不断增加,难免误判,所以可以在进程中查找特征码方法,将网上一些精典工具
采集其中特征码,软件可以更安全。对于查病毒木马软件很适用,不需增加扫描引擎。

6利用时间差,如可用gettickcount之类,在软件装载起始处与结束处比较,超过了某一数值,就认为被调试,如果软件中要装载可变内容的数
据时,要将判断放到前面去。此法还要考虑兼容性,要取一个较大的值。

7.锁住键盘,鼠标,防止被调试,user32.dll中有个blockinput即可调用。
8.将功能,注册判断放到DLL中去,脱壳工具大多是对exe文件起作用,DLL则很少有脱壳器。
9.crc校验法,可在任意时候检查自身是否被修改。发现异常删除自已。不要来个提示什么的,那将引导对软件的逆向工程

10.对不同的操作系统进行不同的处理,判断代码具随机性等能有效地避免被'成功'的破解。
破解者不一定在所有的系统下去测试一个被破解的程序,这样将破解程序弄到另一个系统下将被删除。

11用createfilea,_lopen之类来打开sice,ntsice的句柄,检查调试器是否被驻留。
12用findwindow查找特定软件的标题,类名,关闭该软件或退出自已。
13软件中加载无效内容,让反汇编器失效。
{本人还未成功实现)
14,改写一些公开的加壳器源码来自加壳,必须注意系统兼容性

15....待续


最后,在决定对软件进行保护时,是否应当改进软件的功能,这样可能会有更多的用户购买。

  • 标 题:我的经验-如何用简单方法防止破解
  • 作 者:北极异型
  • 时 间: 2004-3-31 周三, 上午2:14
  • 链 接:http://bbs.pediy.com

在Debug的手册里可以看到Debug工具的局限. 第一个局限是只能下4个内存区域的断点,每个断点不能控制超过两个字节,这样内存断点不能控制超过16个字节的区域. 第二个局限是对多线程只能同时跟踪一个线程.

假设你的注册部分有300行,你可以分成30个inline函数调用或MACRO(一定要inline),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的暴力破解就经常会破坏正常程序数据.