发信人: cantonleo(凡心)
整理人: luor_d(2004-03-30 16:41:05), 站内信件
|
想学LISP程序就睇下啦!
[zt]部分:
对AUTOLSP程序,由AUTOCAD解释执行,因此对源程序的保护能力十分有限,尽管AUTOCAD还支持一种LISP保护格式,但由于保护算法单一,早已失去了它保护源程序的功能,读者参阅《AutoCAD软件技术探秘》一书便知,因此LISP源程序的保护问题已使许多开发者陷入一个两难境地:要不推出他那几乎毫无防御能力的LISP程序,任凭他人轻易地获取凝结自己数年甚至数十年智慧和辛勤劳动的结晶;要不放弃AUTOLISP这个AUTOCAD中使用最方便最古老的开发工具,放弃你原有总数达数兆(例如一般建筑设计CAD系统)的成果而改用C语言编写ADS程序;选择前者,那么你的产品推出之时,便孕育了它“同父异母”的弟兄!若选择后者用C语言改写调试将额外地投入人力、物力、精力,且冒着被别人抢先推出产品而你却失去了占有市场的宝贵时机这一风险。
当然使用标准的AUTOLSP编密方法保护源程序只是在早期产品中才能见到,在近期的产品中广泛采用的是使用一个特殊的ACAD.EXE或ADS程序来装入一个经特别方法编密的LISP程序,然而能被AUTOCAD装入及识别的编密方法实在有限,让计算机来对不同的密匙、算法进行分析大部份能找出它的反保护算法,下例即是笔者对某一近期软件分析获得的反编密算法主循环片断:
.....
ch1=getc(fin);
while( !feof(fin))
{
.....
ch1=(unsigned char) i;
ch1=ch1^0x20; /* 请注意本语句是标准反保护算法中没有的*/
}
......
可以用一个小程序在标准输出流中对字符用一个特殊的密匙进行异或运算处理,并遍列0~0xFF共256个密匙,对每个密匙仅输出部分结果即可(如16个字符,你能快速地浏览),使用一个二进制文件观察工具(如HEX、PCTOOLS)看看是否含有合法的LISP程序片断,即找到了你的解密密匙。
让计算机进行穷举试探并不总能成功,但使用修改ACAD.EXE或使用ADS加解密装载LISP程序的方法并不能阻止探密者的步伐,对开发者来说也许忽视了这样一个严峻的事实:无论在磁盘上源程序经过何种复杂的加密算法处理,但一旦由AUTOCAD(或用户的ADS)装入内存,在计算机RAM中它却总是具有固定的格式--作为一个AUTOLISP表来存储!这是由系统决定的,谁也无法改变它,只不过你用“!”去显示它时你仅能看到PROTECTED的字样罢了,既然是LISP表我们当然能看到它,以下述测试函数TEST.LSP为例:
(DEFUN TEST1 ( A / B )
(SETQ B (1+ A))
(PRINC B)
)
用下述LISP表达式求值时我们可得:
!TEST1 ;返回PROTECTED
不用灰心,让我们继续测试:
(CAAR TEST1) ;返回A
(CAADR TEST1) ;返回SETQ
此时,我们见到的已不再是那讨厌的PROTECTED,而分别是A及SETQ,这正是该函数值参和第一个表达式中的关键词,至此聪明的读者一定有办法看到它的其余部分了;方法是:遍历整个表,打印它的每个原子及层次关系。请看下述程序(UP.LSP),它能将内存中指定的函数或所有LISP函数以源码的方式写入一个文件:
装入该程序后用(LSP_UN_PRO TEST1)可获得TEST1.LSP其内容和TESTP.LSP是相同的,而用ULSPALL命令则你能获得内存中所有的函数源程序。
由此我们可知:只要是用SETQ或DEFUN定义的表及函数,无论是保护格式或非保护格式,只要其还留在RAM中,均可通过上述函数获得其源代码,用此函数,我们已可以说LISP程序已无保密可言。
综合上述加密及反加密技术对于开发者来说不是无路可走,作者在此介绍几个关键技术和建议相信对学习者及开发者都会有所帮助:
1.使用别名,AUTOCAD对所有数据及程序都采用链表指针的存储结构,下例实验说明了别名的应用:
在AUTOCAD命令方式下输入:
(SETQ V1 PRINC V2 DEFUNV3 SETQ)
现在我们实际上已为系统函数PRINC、DEFUN及SETQ建立了别名分别为V1、V2及V3;即指针V1、V2、V3已指向上述三个系统函数的处理入口,此时看看我们的测试程序TEST2.LSP:
;TEST2.LSP
(V2 TEST2 ( A / B )
(V3 B (1+ A))
(V1 B)
)
此时即使你用上述方法获得TEST2的源程序,读者一时肯定无法确定TEST2.LSP到底执行什么功能,事实上它与TEST1.LSP具有相同的功能,分别用(TEST2 10)及(TEST1 10)求值便可验证。
因此开发者不妨为所有AUTOCAD标准函数建立它的别名,并将你程序中的所有标准函数用别名代替,这样即使解密者获得程序,他也无法读懂;当然建立别名必须与ADS联合来实现,至少你应多费一点脑筋,否则等于你直接把密匙给了对方;
2.不要在一开始就把别名建立好,应在该模块加载时建立,而不论该模块执行成功与否(如^C中断,这是一个后门,每个模块均应带出错处理函数*ERROR*)均应立即将其定义取消,否则别人会轻易地获得真名与别名的对照表,这会使你的工作前功尽弃;我们继续上述测试:
(= PRINC V1) ;返回T说明标准函数PRINC与V1指向同一地址;
可见将别名与标准函数逐个比较并输出约束于同一地址的函数定义,你便很容易获得答案,相信读者编写这个LISP程序不会有什么困难,对开发者来说保护这一密匙的重要性显然是不言而语的。
3.要保护密匙也不是一件容易的事,稍有不慎便功亏一篑,因此不让函数名及别名留在内存无疑是上策,自然我们会想到用LAMBDA将你的模块定义为无名函数,它与用DEFUN定义的函数不同,无名函数执行后不会留在内存中,这样解密者便无机可乘了(至少是“少机可乘”),退一步至少不要让真名和别名同时留在内存是上策,我们可用:
(SETQ V1 PRINC PRINC NIL);取消标准函数PRINC的定义,以V1代替。
使标准函数在内存中仅保留一个入口定义,运行后再执行下条语句:
(SETQ PRINC V1 V1 NIL);恢复PRINC的原定义,取消别名定义
4.将所有的LISP程序使用一种独特的数据加密及压缩算法处理成一个或数个文件,其文件扩展名不妨取.exp(或.exe)这样以迷惑解密者,他会以为这是一个ADS程序而止步,使用时经一个特别设计的装入程序悄悄地解密解压后装入运行;
5.不要将要装入的模块解压解密成一个磁盘上的文件,即使该文件具有隐藏的属性及运行后立即删除也是无用的,因为使用DOS命令UNDELETE及ATTRIB就能打破你的防线,更不用说使用PCTOOLS及NORTON这类工具软件了,即使你一定要这么做,那么在删除前请用笔者的方法:将一些垃圾写入该文件!
6.对大多数应用来说不需要使用ATOMS-FAMILY函数,该函数保留了所有已定义的AUTOLISP函数名列表,屏蔽它便使上述程序中ULSPALL这一功能无用武之地:
(SETQ ATOMS-FAMILY NIL)
7.尽可能地将关键函数用ADS改写,从理论上来说是无法将一个真正编译程序转换成源程序的,因此ADS函数越多,探密者的收获便越少;而对LISP程序的加密和解密是矛盾的两个方面,特殊的锁总有特殊的钥匙来开启,ADS程序能使你一劳永逸;至于反拷贝等技术已超出本文的主题,读者可参考其他书籍来处理;
最后,请读者谅解!笔者隐瞒了一些最新的解密技术,这是为了保护开发者的劳动成果;同时也隐藏了一些加密要点,因为介绍制作盾牌的同时也暴露了制作矛的方法,对于一个学习探索者来说,上述方法已足于让你从前人哪儿去了解和学到许多CAD程序的编制方法和系统技巧,而对于开发者来说,依作者介绍的方法为基础加上你的努力,在一定的时间内已能有效地保护你自己,从而从容地将你的LISP部分或全部程序最终过渡至ADS程序;如用上述介绍的方法去侵害软件著作者的合法权益,那定是违背了笔者意愿的,我们从事科学技术工作的人,均应好自为之;笔者愿与所有的开发者和学习者共同促进我国CAD事业的发展。
|
|