防止篡改程序代码的一个方法(转载)
软件加密是防止软件盗版的一种有效手段。加密又分为软加密和硬加密,软加密通常是在软件中通过校验注册码,绑定计算机特征码,软外壳保护等手段,防止软件被非法拷贝使用。硬加密是指使用加密锁厂商提供的加密锁硬件对软件进行加密,如坚石诚信公司的ET系列加密锁产品就是很好的软件加密保护产品。就安全强度上讲,硬加密的强度要高于软加密,但也存在一个使用问题,如果使用不好加密锁,其加密强度也会低于软加密。
加密锁就是一个加密的平台,根据锁的类型,这个平台上的功能有所不同。一般低端的加密锁是单片机芯片,提供读写数据和简单的对称或散列算法,如坚石诚信的ET99;高端加密锁采用智能卡芯片,不仅硬件不能被复制,还提供了锁内编程,RSA非对称加解密,双精度浮点运算,文件系统等丰富的功能,这种加密锁以坚石诚信的ET199为代表。
如何使用好加密锁实际上要看在加密锁提供的平台上加密方案设计的好坏。而加密方案的设计需要开发人员有丰富的加密经验,是日积月累形成的。但在很多情况下,项目时间紧,开发人员又缺少加密经验,所以很多加密方法只是检查加密锁是否存在,或者向锁内存一个固定的数据,然后软件运行时再读出比较。这样的表面加密虽然看起来也是软件运行时必须插入加密锁,但根本没有安全强度可言,非常容易被破解。破解者只需要使用一些常见的反汇编工具,如SoftICE,OllyDbg等就可以跟踪到比较点,修改一句汇编跳转指令,就可以绕过这个比较点,从而不检查加密锁是否存在或者使从锁内读出的数据进行比较时永远为真,从而让软件在没有锁的情况下也能运行。
在这里介绍一个简单的方法,增加破解者通过上述方法绕过比较点的难度。过程大致如下:
(1) 在程序中设置标志。这里使用的汇编代码,这样好在程序编译后的二进制文件中进行定位。如__asm mov al,99h编译后就2个字节,即0xb0 0x99。
__asm mov oldal,al
__asm mov al,99h
__asm mov al,98h
__asm mov al,97h
__asm mov al,96h
__asm mov al,oldal
(2) 定义一个宏。这个宏的作用就是将输入的函数段编译后的二进制字节与输入的种子进行异或计算,将结果反出。这里有一个for循环,是在从TESTCRC_func地址开始,小于10000字节的范围搜索定位标志,如果函数段编译后大于10000可以根据实际情况修改。另外最后的算法使用的是异或处理,这里可以采用更复杂的算法,将函数段的二进制与种子进行计算产生计算结果。
#define TESTCRC(TESTCRC_src, TESTCRC_func) \
{ int TESTCRC_iLen = 0; \
unsigned char* TESTCRC_pFun = (unsigned char*)TESTCRC_func; \
int TESTCRC_i = 0;\
for(TESTCRC_i=0; TESTCRC_i10000; TESTCRC_i++)\
if(TESTCRC_pFun[TESTCRC_i] == 0xb0 \
TESTCRC_pFun[TESTCRC_i+1] == 0x99 \
TESTCRC_pFun[TESTCRC_i+2] == 0xb0 \
TESTCRC_pFun[TESTCRC_i+3] == 0x98 \
TESTCRC_pFun[TESTCRC_i+4] == 0xb0 \
TESTCRC_pFun[TESTCRC_i+5] == 0x97 \
TESTCRC_pFun[TESTCRC_i+6] == 0xb0 \
TESTCRC_pFun[TESTCRC_i+7] == 0x96)\
TESTCRC_iLen = TESTCRC_i;\
break;\
for(TESTCRC_i=0; TESTCRC_iTESTCRC_iLen; TESTCRC_i++)\
TESTCRC_src ^= *((PDWORD)(TESTCRC_pFun+TESTCRC_i)); \
(3) 将(2)中的计算结果赋值到程序其他有赋值的地方。
下面是程序的整个演示过程
BYTE gTemp[100] = {0};
int test(int a)
BYTE oldal=0;
int i=a;
int j=4;
if(ji)
memset(gTemp, 0x88, 100);//不直接返回;
else
memset(gTemp, 0x00, 100);//不直接返回;
__asm mov oldal,al
__asm mov al,99h
__asm mov al,98h
__asm mov al,97h
__asm mov al,96h
__asm mov al,oldal
return 0;
int main(int argc, char* argv[])
DWORD dwSeed = 0x7776;
TESTCRC(dwSeed, test);
printf("%08x\n", dwSeed);
//假如这时经上面的变换后dwSeed的值为100。那么下面如果有很多赋值语句
//DWORD dwA =10;改为
DWORD dwA = dwSeed -90;
//DWORD dwB=188;改为
DWORD dwB= dwSeed+88;
return 0;
可以看到,示例程序中将test函数进行了保护,如果破解者要更改if(ji)的比较,这时test函数的二进制就发生了变化,那么TESTCRC(dwSeed, test);运算后的dwSeed也就不是100了,从而造成dwA不为10,dwB不为188,那么使用dwA和dwB的程序就会执行出错,如果涉及到地址的操作,程序就跑飞了。这样就能有效的防止破解者通过修改二进制屏蔽比较的破解手段。