软件加壳技术及实现_第1页
软件加壳技术及实现_第2页
软件加壳技术及实现_第3页
软件加壳技术及实现_第4页
软件加壳技术及实现_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、软件加壳技术及实现摘 要 当今盗版软件的泛滥成灾几乎已经成为了我们中国民族软件的灾难,为了防止软件的非法复制、盗版,保护软件开发商的利益,就必须对软件进行加密保护。现在市面上有许多反盗版软件,但这类软件多是单机处理,并且只使用简单的加密手段,很容易被解密者破解。本文描述了一个通过Internet,集加密和电子注册于一身的完善的软件保护方案。该方案基于多种密码学意义上可靠的算法,如对称加密算法,散列算法,数字签名,密钥交换等等。通过对Windows下PE可执行文件的结构及载入机制进行深刻的剖析, 巧妙的使用这些密码学算法及多种反破解方案对PE文件进行加密保护。在该方案的实现中,使用CryptoA

2、PI中的数字签名算法RSA,加密算法RC2和RC4,散列算法SHA,同时自己编写了使用了MD5算法用于快速计算大量数据的摘要;网络接口使用WinSocket;编程语言选用汇编语言和C+混合编程方式;反破解方案有检测文件完整性、检测代码完整性、反跟踪、反反汇编、反Dump、代码变形等等。由于使用了可靠的密码学算法,使软件加密的强度大大提高;由于使用了Internet在线注册方式,用户使用也非常方便。关键词 加密 ;数字签名 ;散列;反跟踪 ;电子注册 Software Protection technique and its realizationLEI Peng( GuiLin Institu

3、te of Electronic Technology . The Department of Computing )Abstract The flooding of pirate software has been a calamity of our national software industry . In order to prevent software from pirate , and protect the profit of the software developer , they must encrypt their software to get a protecti

4、on . There are several software protection tools in the market currently , but these tools were standalone nine tenths , and they only used simple encryption algorithms , so they could be cracked easily by the crackers .This thesis describes a perfect software encryption and protection scheme which

5、integrate the encryption and electronic register . This scheme is based on multiple reliable cryptographic algorithms such as symmetric encryption algorithm , digital signature , hashing and key exchange . The PE file format (Portable Executable File Format) and its loading mechanism under Windows a

6、re dissected thoroughly in this thesis . Then these cryptographic algorithms and several anti-crack method are used gracefully to encrypt and protect the PE file .Within the realization of this scheme , the RSA digital signature algorithm , RC2 and RC4 encryption algorithm , SHA hasing algorithm etc

7、 in MicroSoft CryptoAPI are used . In order to increase the performace of caculate the digest of large number of data, MD5 hashing algorithm was rewritten . WinSocket API is used as the network interface . The blend of C+ and assembly are used for easily contoling the bottom layer of the system and

8、simplify the programming . The anti-crack method consits the integralization of the file checking , the integralization of the code checking , and anti-debug , anti-disassembly , anti-dump and code metamorphose etc .The reliable cyrpto algorithms guarantee the crypto strength . As a result of online

9、 register , the retail users and the software developers get convenience .Key words Encrypt ; Digital Signature ; Hashing ; Anti-Debug ; Electronic Register 目 录第一章 概述4第二章 密码学简介52.1 概念52.2 对称密码算法62.3 公开密码算法72.4 单向散列函数72.5 数字签名7第三章 Windows 环境下 PE 文件简介83.1 WIN32 与 PE 基本概念83.2 PE首部93.3 PE文件的导入表10第四章 当前流

10、行的一些软件保护技术124.1 序列号保护124.2 时间限制134.3 Key File 保护134.4 CD-check134.5 反跟踪技术(Anti-Debug)134.6 反反汇编技术(Anti- Disassmbly)134.7 软件狗144.8 Vbox 保护技术144.9 SalesAgent 保护技术144.10 SecuROM 保护技术144.11 软盘加密144.12 将软件与机器硬件信息结合154.13 加壳15第五章 该软件的设计思想155.1 传统保护的不足155.2 网络的流行155.3 我的方案155.4 该方案的可行性分析15第六章 该软件的整体构架、开发工具

11、及方法166.1 需求分析166.2 整体框架186.3 各取所长(汇编与 C/C+ 各取所长)186.4 C/C+ 与汇编语言混合编程时的互调协议186.5 该软件中各模块对语言特性的限制及解决方法206.6 C/C+ 和汇编语言的预编译22第七章 该软件的实现及技术细节227.1 CryptoAPI 简介237.2 几个公共函数和宏247.3 模块共用的结构体定义267.4 Shield 模块277.4.1 壳程序中API和库函数的处理287.4.2 壳程序主体297.4.4 运行中修改自身代码307.4.6 跳转到客户程序入口317.4.7 载入并销毁 Client 程序的 Import

12、Table327.4.8 自毁壳程序代码337.4.9 编译方法337.5 Merge 模块347.6 Register 模块357.7 Server 模块367.8 软件授权协议的实现367.9 Client的代码(数据)的加密/解密流程图示38第八章 演示及效果38第九章 限制、不足与展望399.1 使用该软件的限制39第十章 结束语39第一章 概述我引用应用密码学作者Bruce Schneier的话:世界上有两种密码:一种是防止你的小妹妹看你的文件;另一种是防止当局者阅读你的文件资料。如果把一封信锁在保险柜中,把保险柜藏在纽约的某个地方,然后告诉你去看这封信。这并不是安全,而是隐藏。相反

13、,如果把一封信锁在保险柜中,然后把保险柜及其设计规范和许多同样的保险柜给你,以便你和世界上最好的开保险柜的专家能够研究锁的装置。而你还是无法打开保险柜去读这封信,这样才是安全的。意思是说,一个密码系统的安全性只在于密钥的保密性,而不在算法的保密性。对纯数据的加密的确是这样。对于你不愿意让他看到这些数据(数据的明文)的人,用可靠的加密算法,只要破解者不知道被加密数据的密码,他就不可解读这些数据。但是,软件的加密不同于数据的加密,它只能是隐藏。不管你愿意不愿意让他(合法用户,或 Cracker)看见这些数据(软件的明文),软件最终总要在机器上运行,对机器,它就必须是明文。既然机器可以看见这些明文,

14、那么 Cracker,通过一些技术,也可以看到这些明文。于是,从理论上,任何软件加密技术都可以破解。只是破解的难度不同而已。有的要让最高明的 Cracker 忙上几个月,有的可能不费吹灰之力,就被破解了。所以,反盗版的任务(技术上的反盗版,而非行政上的反盗版)就是增加 Cracker 的破解难度。让他们花费在破解软件上的成本,比他破解这个软件的获利还要高。这样 Cracker 的破解变得毫无意义-谁会花比正版软件更多的钱去买盗版软件 然而,要做到难破解,何尝容易 Sony 曾宣称的超强反盗版(Key 2 Audio音乐 CD反盗版),使用了很尖端的技术,然而最近却被一枝记号笔破解了,成为人们的

15、饭后笑料!所以,很多看上去很好的技术,可能在 Cracker 面前的确不堪一击。就像马其诺防线一样,Cracker 不从你的防线入手,而是绕道。这样,让你的反盗版技术在你做梦也想不到的地方被 Crack 了。为什么会这样呢 归根到底是因为软件在机器上运行,并且软件和机器是分离的-这一点是关键,如果软件和硬件完全绑定,不能分离,是可以做到象 IDEA 之类几乎不可破解的系统的。这将在后面谈传统软件保护技术时详细说明。对我的这个解决方案,我不能保证Crack高手在几天之内不能破解它,我只能说:在这个软件中,我尽量堵住了当前破解者普遍使用的方法以及我想得到的可能的缺口。但是我相信,倾注了我三个月心血

16、的反盗版软件,决不是一个玩具式的反盗版软件。第二章 密码学简介2.1 概念(1) 发送者和接收者假设发送者想发送消息给接收者,且想安全地发送信息:她想确信偷听者不能阅读发送的消息。(2) 消息和加密消息被称为明文。用某种方法伪装消息以隐藏它的内容的过程称为加密,加了密的消息称为密文,而把密文转变为明文的过程称为解密。 明文用M(消息)或P(明文)表示,它可能是比特流(文本文件、位图、数字化的语音流或数字化的视频图像)。至于涉及到计算机,P是简单的二进制数据。明文可被传送或存储,无论在哪种情况,M指待加密的消息。密文用C表示,它也是二进制数据,有时和M一样大,有时稍大(通过压缩和加密的结合,C有

17、可能比P小些。然而,单单加密通常达不到这一点)。加密函数E作用于M得到密文C,用数学表示为:E(M)=C.相反地,解密函数D作用于C产生MD(C)=M.先加密后再解密消息,原始的明文将恢复出来,下面的等式必须成立:D(E(M)=M(3) 鉴别、完整性和抗抵赖除了提供机密性外,密码学通常有其它的作用:. (a) 鉴别消息的接收者应该能够确认消息的来源;入侵者不可能伪装成他人。 (b) 完整性检验消息的接收者应该能够验证在传送过程中消息没有被修改;入侵者不可能用假消息代替合法消息。 (c) 抗抵赖发送者事后不可能虚假地否认他发送的消息。(4) 算法和密钥密码算法也叫密码,是用于加密和解密的数学函数

18、。(通常情况下,有两个相关的函数:一个用作加密,另一个用作解密)如果算法的保密性是基于保持算法的秘密,这种算法称为受限制的算法。受限制的算法具有历史意义,但按现在的标准,它们的保密性已远远不够。大的或经常变换的用户组织不能使用它们,因为每有一个用户离开这个组织,其它的用户就必须改换另外不同的算法。如果有人无意暴露了这个秘密,所有人都必须改变他们的算法。更糟的是,受限制的密码算法不可能进行质量控制或标准化。每个用户组织必须有他们自己的唯一算法。这样的组织不可能采用流行的硬件或软件产品。但窃听者却可以买到这些流行产品并学习算法,于是用户不得不自己编写算法并予以实现,如果这个组织中没有好的密码学家,

19、那么他们就无法知道他们是否拥有安全的算法。尽管有这些主要缺陷,受限制的算法对低密级的应用来说还是很流行的,用户或者没有认识到或者不在乎他们系统中内在的问题。现代密码学用密钥解决了这个问题,密钥用K表示。K可以是很多数值里的任意值。密钥K的可能值的范围叫做密钥空间。加密和解密运算都使用这个密钥(即运算都依赖于密钥,并用K作为下标表示),这样,加/解密函数现在变成:EK(M)=CDK(C)=M.这些函数具有下面的特性:DK(EK(M)=M. 有些算法使用不同的加密密钥和解密密钥,也就是说加密密钥K1与相应的解密密钥K2不同,在这种情况下:EK1(M)=CDK2(C)=MDK2 (EK1(M)=M所

20、有这些算法的安全性都基于密钥的安全性;而不是基于算法的细节的安全性。这就意味着算法可以公开,也可以被分析,可以大量生产使用算法的产品,即使偷听者知道你的算法也没有关系;如果他不知道你使用的具体密钥,他就不可能阅读你的消息。 密码系统由算法、以及所有可能的明文、密文和密钥组成的。基于密钥的算法通常有两类:对称算法和公开密钥算法。下面将分别介绍:2.2 对称密码算法对称算法有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,反过来也成立。在大多数对称算法中,加/解密密钥是相同的。这些算法也叫秘密密钥算法或单密钥算法,它要求发送者和接收者在安全通信之前,商定一个密钥。对称算法的安全性依赖于密

21、钥,泄漏密钥就意味着任何人都能对消息进行加/解密。只要通信需要保密,密钥就必须保密。对称算法的加密和解密表示为:EK(M)=CDK(C)=M对称算法可分为两类。一次只对明文中的单个比特(有时对字节)运算的算法称为序列算法或序列密码。另一类算法是对明文的一组比特亚行运算,这些比特组称为分组,相应的算法称为分组算法或分组密码。现代计算机密码算法的典型分组长度为64比特-这个长度大到足以防止分析破译,但又小到足以方便使用(在计算机出现前,算法普遍地每次只对明文的一个字符运算,可认为是序列密码对字符序列的运算)。对称算法的加密和解密表示为:对称加密算法2.3 公开密码算法公开密钥算法(也叫非对称算法)

22、是这样设计的:用作加密的密钥不同于用作解密的密钥,而且解密密钥不能根据加密密钥计算出来(至少在合理假定的长时间内)。之所以叫做公开密钥算法,是因为加密密钥能够公开,即陌生者能用加密密钥加密信息,但只有用相应的解密密钥才能解密信息。在这些系统中,加密密钥叫做公开密钥(简称公钥),解密密钥叫做私人密钥(简称私钥)。私人密钥有时也叫秘密密钥。为了避免与对称算法混淆,此处不用秘密密钥这个名字。用公开密钥K加密表示为EK(M)=C.虽然公开密钥和私人密钥是不同的,但用相应的私人密钥解密可表示为:DK(C)=M 有时消息用私人密钥加密而用公开密钥解密,这用于数字签名(后面将详细介绍),尽管可能产生混淆,但

23、这些运算可分别表示为:EK(M)=CDK(C)=M当前的公开密码算法的速度,比起对称密码算法,要慢的多,这使得公开密码算法在大数据量的加密中应用有限。2.4 单向散列函数单向散列函数 H(M) 作用于一个任意长度的消息 M,它返回一个固定长度的散列值 h,其中 h 的长度为 m 。输入为任意长度且输出为固定长度的函数有很多种,但单向散列函数还有使其单向的其它特性:(1) 给定 M ,很容易计算 h ;(2) 给定 h ,根据 H(M) = h 计算 M 很难 ;(3) 给定 M ,要找到另一个消息 M 并满足 H(M) = H(M) 很难。在许多应用中,仅有单向性是不够的,还需要称之为抗碰撞的

24、条件:要找出两个随机的消息 M 和 M,使 H(M) = H(M) 满足很难。由于散列函数的这些特性,由于公开密码算法的计算速度往往很慢,所以,在一些密码协议中,它可以作为一个消息 M 的摘要,代替原始消息 M,让发送者为 H(M) 签名而不是对 M 签名 。如 SHA 散列算法用于数字签名协议 DSA中。2.5 数字签名提到数字签名就离不开公开密码系统和散列技术。有几种公钥算法能用作数字签名。在一些算法中,例如RSA,公钥或者私钥都可用作加密。用你的私钥加密文件,你就拥有安全的数字签名。在其它情况下,如DSA,算法便区分开来了数字签名算法不能用于加密。这种思想首先由Diffie和Hellma

25、n提出 。基本协议是简单的 :(1) A 用她的私钥对文件加密,从而对文件签名。(2) A 将签名的文件传给B。(3) B用A的公钥解密文件,从而验证签名。这个协议中,只需要证明A的公钥的确是她的。如果B不能完成第(3)步,那么他知道签名是无效的。这个协议也满足以下特征:(1) 签名是可信的。当B用A的公钥验证信息时,他知道是由A签名的。(2) 签名是不可伪造的。只有A知道她的私钥。(3) 签名是不可重用的。签名是文件的函数,并且不可能转换成另外的文件。(4) 被签名的文件是不可改变的。如果文件有任何改变,文件就不可能用A的公钥验证。(5) 签名是不可抵赖的。B不用A的帮助就能验证A的签名。在

26、实际应用中,因为公共密码算法的速度太慢,签名者往往是对消息的散列签名而不是对消息本身签名。这样做并不会降低签名的可信性。本章仅对密码学进行了一些简要的介绍,更多的请参阅参考文献1。第三章 Windows 环境下 PE 文件简介3.1 WIN32 与 PE 基本概念只要用过电脑的人都知道什么是 Windows,Windows95 已经是过时的昨日黄花了,Windows98 也已推出将近四年了。2000 年又推出了Windows2000,今年又推出了 WindowsXP,微软的操作系统更新速度是如此的快,以至于昨天还在使用的东西,在今天看来就已经过时了。Windows98 以后,微软传言不在推出

27、9x 内核的操作系统,但是2000 年下半年却正式推出了 WindowsMillennium,简称 Win.Me 。然而从 WindowsXP 的推出,可以断言,微软不会在升级 Win9x 操作系统了。Windows2000 和 WindowsXP 都是基于 NT 内核的。所有这些操作系统都使用一种可移植可执行文件格式(Portable Executable File Format),简称PE文件格式。下面简短介绍一下 PE 文件的一些概念。详细内容请参阅参考文献14。Windows NT 继承了 VAX,VMS和 UNIX的传统。许多 Windows NT 的创始人在进入微软前都在这些平台上

28、进行设计和编码。当他们开始设计 Windows NT 时,很自然的,为了最小化工程的启动时间,他们会使用以前写好的并且已经测试过的工具。用这些工具生成并且工作的可执行文件和 OBJ 文件格式叫做 COFF(Common Object File Format 的首字母缩写)。COFF 的年龄不超过八年。COFF 本身是一个很好的起点,但是需要扩展到一个现代操作系统如 Windows 95 和 Windows NT 就要进行一些更新。其结果就是产生了(PE格式)可移植可执行文件格式。它被称为可移植的是因为在所有平台(如x86,Alpha,MIPS等等)上实现的WindowsNT 都使用相同的可执行

29、文件格式。当然了,也有许多不同的东西如二进制代码的CPU指令。重要的是操作系统的装入器和程序设计工具不需要为任何一种CPU完全重写就能达到目的。关于PE文件最重要的是,磁盘上的可执行文件和它被WINDOWS载入内存之后(PE文件载入内存之后称为PE映像)是非常相像的(如图 3-1)。WINDOWS载入器不必为从磁盘上载入一个文件而辛辛苦苦创建一个进程。载入器使用内存映射文件机制把文件中相似的块映射到虚拟空间中。构造式的进行分析,一个PE文件类似一个预制的屋子。它本质上开始于这样一个空间,这个空间后面有几个把它连到其余空间的机件(就是说,把它联系到它的DLL上,等等)。这对PE格式的DLL式一样

30、容易应用的。一旦这个模块被载入,Windows 就可以有效的把它和其它内存映射文件同等对待。 PE文件和PE映像的布局很相似,对Win32来讲,模块所使用的所有代码,数据,资源,导入表,和其它需要的模块数据结构都在一个连续的内存块中。在这种形势下,你只需要知道载入器把可执行文件映射到了什么地方就可以了。通过作为映像的一部分的指针,你可以很容易的找到这个模块所有不同的块。另一个你需要知道的概念是相对虚拟地址(RVA)。PE文件中的许多域都用术语RVA来指定。一个RVA只是一些项目相对于文件映射到内存的偏移。比如说,载入器把一个文件映射到虚拟地址0x10000开始的内存块。如果映像中一个实际的表的

31、首址是0x10464,那么它的RVA就是0x464。(虚拟地址 0x10464)(基地址 0x10000)RVA 0x00464为了把一个RVA转化成一个有用的指针,只需要把RVA值加到模块的基地址上即可。基地址是EXE和DLL内存映射文件的基址,这个基址在Win32中这是一个很重要的概念。为了方便起见,WindowsNT 和 Windows9x用模块的基址作为这个模块的实例句柄(HINSTANCE)。可以对任何DLL调用GetModuleHandle(dllname)得到一个指针去访问它的组件。如果 dllname 为 NULL,则得到执行体自己的模块句柄。这是非常有用的,如通常编译器产生的

32、启动代码将取得这个句柄并将它作为一个参数hInstance传给WinMain 。3.2 PE首部和其它可执行文件格式一样,PE文件在众所周知的地方有一些定义文件其余部分面貌的域。首部就包含这样象代码和数据的位置和尺寸的地方,操作系统要对它进行干预,比如初始堆栈大小,和其它重要的块的信息。和微软其它执行体的格式相比,PE格式的执行体的主要的首部不是在文件的最开始。典型的PE文件最开始的数百个字节被DOS残留部分占用。这个残留部分是一个打印如这个程序不能在DOS下运行!这类信息的小程序。所以,你在一个不支持Win32的系统中运行这个程序,会得到这类错误信息。当载入器把一个Win32程序映射到内存,

33、这个映射文件的第一个字节对应于DOS残留部分的第一个字节,这是无疑的。于是,和你启动的任一个基于Win32 的程序一起,都有一个基于DOS的程序连带被载入。和微软的其它可执行格式一样,你可以通过查找它的起始偏移来得到真实首部,这个偏移放在DOS残留首部中。WINNT.H头文件包含了DOS残留程序的数据结构定义(注),使得很容易找到PE首部的起始位置。e_lfanew 域是PE真实首部的偏移。为了得到PE首部在内存中的指针,只需要把这个值加到映像的基址上即可。/ 忽略类型转化和指针转化pNTHeader = dosHeader + dosHeader-e_lfanew;注:为了不失简洁,这里未列

34、出这些结构体的完整定义就直接引用,这里直接引用的结构体其定义都在winnt.h中,建议读者在读本章时参考Winnt.h 。一旦你有了PE主首部的指针,游戏就可以开始了!PE主首部是一个IMAGE_NT_HEADERS的结构,在WINNT.H中定义。这个结构由一个双字(DWORD)和两个子结构组成,布局如下:DWORD Signature; / 标志域IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER OptionalHeader;标志域用ASCII表示就是PE00。标志域之后的是结构 IMAGE_FILE_HEADER 。这个域只包含这个文件

35、最基本的信息。这个结构看上去并未从它的原始COFF实现更改过。除了是PE首部的一部分,它还表现在微软Win32编译器生成的COFF OBJ 文件的最开始部分。这个部分的详细说明请参阅参考文献14。3.3 PE文件的导入表因为导入表在该软件的设计中很关键,后面壳程序导入表的构建,对客户程序导入表的载入等,都牵涉到导入表 。所以,在这里有必要说明一下,更详细的说明请参阅参考文献141516。导入表,简单的说,导入表的作用相当于 DOS 的系统中断功能。两者都是操作系统 API 。只不过DOS中断不需要操作系统在载入每个执行体时填入 API 的实际地址,并且,导入表还可以导入除操作系统 API 之外

36、的其它模块中的函数。在一个PE文件中,当你调用另一模块中的一个函数时(比如在USER32.DLL中的GetMessage ),编译器产生的CALL 指令并不把控制直接转移到在DLL中的这个函数。代替的,CALL 指令把把控制转移到一个也在 .text 中的 JMP DWORD PTR XXXXXXXX指令处。 这个 JMP 指令通过一个在导入表中的DWORD变量间接的转移控制。 导入表的这个DWORD包含操作系统函数入口的实际地址。为什么DLL调用用这种方式来实现呢原来,通过一个位置传送所有的对一个给定的DLL函数的调用,载入器不需要改变每个调用DLL的指令。所有的PE载入器必须做的是把目标函

37、数的正确地址放到导入表的一个 DWORD 中。不需要改变任何call指令本身。如果你想通过函数指针调用一个函数,事情也会如你所预料的一样。但是,如果你想读取 GetMessage 开始的字节,你将不能如愿。后面讲到反 API 断点时会详细说明。 前面描述了函数调用怎样到一个外部DLL中而不直接调用这个DLL 。代替的,在执行体中的 .text 块中(如果你用Borland C+ 就是 .icode 块),CALL指令到达一个 JMP DWORD PTR XXXXXXXX 指令处。JMP指令寻找的地址把控制转移到实际的目标地址。PE文件的导入表会包含一些必要的信息,这些信息是载入器用来确定目标函

38、数的地址以及在执行体映像中修正他们的。导入表开始于一个IMAGE_IMPORT_DESCRIPTOR数组。每个DLL都有一个PE文件隐含链接上的IMAGE_IMPORT_DESCRIPTOR 。没有指定这个数组中结构的数目的域。代替的,这个数组的最后一个元素是一个全NULL的IMAGE_IMPORT_DESCRIPTOR 。DWORD Characteristics 在一个时刻,这可能已是一个标志集。然而,微软改变了它的涵义并不再糊涂地升级WINNT.H 。这个域实际上是一个指向指针数组的偏移(RVA)。其中每个指针都指向一个IMAGE_IMPORT_BY_NAME结构。现在这个域的涵义是 O

39、riginalFirstThunk 。DWORD TimeDateStamp 表示这个文件的创建时间。 DWORD ForwarderChain 这个域联系到前向链。前向链包括一个DLL函数向另一个DLL转送引用。比如,在WindowsNT中,NTDLL.DLL就出现了的一些前向的它向KERNEL32.DLL导出的函数。应用程序可能以为它调用的是NTDLL.DLL中的函数,但它最终调用的是KERNEL32.DLL中的函数。这个域还包含一个FirstThunk数组的索引(即刻描述)。用这个域索引的函数会前向引用到另一个DLL 。不幸的是,函数怎样前向引用的格式没有文档,并且前向函数的例子也很难找

40、。DWORD Name 这是导入DLL的名字,指向以NULL结尾的ASCII字符串。通用例子是KERNEL32.DLL和USER32.DLL 。PIMAGE_THUNK_DATA FirstThunk 这个域是指向IMAGE_THUNK_DATA联合的偏移(RVA)。几乎在任何情况下,这个域都解释为一个指向的IMAGE_IMPORT_BY_NAME结构的指针。如果这个域不是这些指针中的一个,那它就被当作一个将从这个被导入的DLL的导出序数值。如果你实际上可以从序数导入一个函数而不是从名字导入,从文档看,这是很含糊的。IMAGE_IMPORT_DESCRIPTOR 的一个重要部分是导入的DLL的

41、名字和两个IMAGE_IMPORT_BY_NAME指针数组。在EXE文件中,这两个数组(由Characteristics域和FirstThunk域指向)是相互平行的,都是以NULL指针作为数组的最后一个元素。两个数组中的指针都指向 IMAGE_IMPORT_BY_NAME 结构。 PE文件导入表中的每一个函数有一个 IMAGE_IMPORT_BY_NAME 结构。IMAGE_IMPORT_BY_NAME结构非常简单,看上去是这样: WORD Hint; BYTE Name;第一个域是导入函数的导出序数的最佳猜测值。和NE文件不同,这个值不是必须正确的。于是,载入器指示把它当作一个进行二分查找的

42、建议开始值。下一个是导入函数的名字的ASCIIZ字符串。为什么有两个平行的指针数组指向结构IMAGE_IMPORT_BY_NAME 第一个数组(由Characteristics域指向的)单独的留下来,并不被修改。经常被称作提名表。第二个数组(由FirstThunk域指向的)将被PE载入器覆盖。载入器在这个数组中迭代每个指针,并查找每个IMAGE_IMPORT_BY_NAME结构指向的函数的地址。载入器然后用找到的函数地址覆盖这个指向IMAGE_IMPORT_BY_NAME结构的指针。JMP DWORD PTR XXXXXXXX 中的 XXXXXXXX 指向 FirstThunk 数组的一个条目

43、。因为由载入器覆盖的这个指针数组实际上保持所有导入函数的地址,叫做导入地址表。在优化上无止境的探索中,微软在WindowsNT中优化了系统DLL(KERNEL32.DLL等等)的thunk数组。在这个优化中,这个数组中的指针不再指向IMAGE_IMPORT_BY_NAME结构,它们已经包含了导入函数的地址。换句话说,载入器不需要去查找函数的地址并用导入函数的地址覆盖thunk数组(译注)。译注:这就是 Bound Import,关于Bound Import,参考文献1516,有详细介绍。不过,在我的软件中,忽略了对Bound Import 的处理,这样会造成一些程序载入速度的减小。但使问题简化

44、了许多。因为导入地址表在一个可写的块中,拦截一个EXE或DLL对另一个DLL的调用就相对容易。只需要修改适当地导入地址条目去指向希望拦截的函数。不需要修改调用者或被调者的任何代码。注意微软产生的PE文件的导入表并不是完全被连接器同步的,这一点很有趣。所有对另一个DLL中的函数的调用的指令都在一个导入库中。当你连接一个DLL时,库管理器(LIB32.EXE或LIB.EXE)扫描将要被连接的OBJ文件并且创建一个导入库。这个导入库完全不同于16位NE文件连接器使用的导入库。32位库管理器产生的导入库有一个.text块和几个.idata$块。导入库中的.text块包含 JMP XXXX 指令,这条指

45、令的标号在OBJ文件的符号表中用一个符号名来存储。这个符号名对将要从DLL中导出的所有函数名讲都是唯一的(例如:_Dispatch_Message4)。导入库中的一个.idata$块包含一个从导入库中引用的地址,即导入库的 .text 中的指令:JMP XXXX中的XXXX 。另一个.idata$块有一个提示序号(hint ordinal)的空间。这两个域就组成了IMAGE_IMPORT_BY_NAME结构。当你晚期连接一个使用导入库的PE文件时,导入库的块被加到连接器需要处理的块的列表中,这个列表在你的OBJ文件中。一旦导入库中的这个xxxx的名字和和要导入的函数名相同,连接器就假定这条jm

46、p xxxx指令就是这个导入函数,并修正其中的xxxx ,使其指向这个.idata$中的一个存储导入函数地址的空间。导入库中的这条jmp xxxx指令在本质上就被当作这个导入函数本身了。除了提供一个导入函数的指令jmp xxxx,导入库还提供PE文件的.idata块(或称导入表)的片断。这些片断来自于库管理器放入导入库中的不同的.idata$块。简而言之,连接器实际上不知道出现在不同的OBJ文件中的导入函数和普通函数之间的不同。连接器只是按照它的内部规则去建立并结合块,于是,所有的事情就自然顺理成章了。本文有关导入表的内容,基本上就这么多,要得到更多的信息,请参阅参考文献141516。第四章

47、当前流行的一些软件保护技术4.1 序列号保护数学算法一项都是密码加密的核心,但在一般的软件加密中,它似乎并不太为人们关心,因为大多数时候软件加密本身实现的都是一种编程的技巧。但近几年来随着序列号加密程序的普及,数学算法在软件加密中的比重似乎是越来越大了。 看看在网络上大行其道的序列号加密的工作原理。当用户从网络上下载某个shareware-共享软件后,一般都有使用时间上的限制,当过了共享软件的试用期后,你必须到这个软件的公司去注册后方能继续使用。注册过程一般是用户把自己的私人信息(一般主要指名字)连同信用卡号码告诉给软件公司,软件公司会根据用户的信息计算出一个序列码,在用户得到这个序列码后,按

48、照注册需要的步骤在软件中输入注册信息和注册码,其注册信息的合法性由软件验证通过后,软件就会取消掉本身的各种限制,这种加密实现起来比较简单,不需要额外的成本,用户购买也非常方便,在互联网上的软件80%都是以这种方式来保护的。 软件验证序列号的合法性过程,其实就是验证用户名和序列号之间的换算关系是否正确的过程。其验证最基本的有两种,一种是按用户输入的姓名来生成注册码,再同用户输入的注册码比较,公式表示如下: 序列号 = F(用户名) 但这种方法等于在用户软件中再现了软件公司生成注册码的过程,实际上是非常不安全的,不论其换算过程多么复杂,解密者只需把你的换算过程从程序中提取出来就可以编制一个通用的注

49、册程序。 另外一种是通过注册码来验证用户名的正确性,公式表示如下: 用户名称 = F逆(序列号) (如ACDSEE) 这其实是软件公司注册码计算过程的反算法,如果正向算法与反向算法不是对称算法的话,对于解密者来说,的确有些困难,但这种算法相当不好设计。 于是有人考虑到以下的算法: F1(用户名称) = F2(序列号) F1、F2是两种完全不同的的算法,但用户名通过F1算法计算出的特征字等于序列号通过F2算法计算出的特征字,这种算法在设计上比较简单,保密性相对以上两种算法也要好的多。如果能够把F1、F2算法设计成不可逆算法的话,保密性相当的好;可一旦解密者找到其中之一的反算法的话,这种算法就不安

50、全了。一元算法的设计看来再如何努力也很难有太大的突破,那么二元呢 特定值 = F(用户名,序列号) 这个算法看上去相当不错,用户名称与序列号之间的关系不再那么清晰了,但同时也失去了用户名于序列号的一一对应关系,软件开发者必须自己维护用户名称与序列号之间的唯一性,但这似乎不是难以办到的事,建个数据库就可以了。当然也可以把用户名称和序列号分为几个部分来构造多元的算法。 特定值 = F(用户名1,用户名2,.序列号1,序列号2.) 现有的序列号加密算法大多是软件开发者自行设计的,大部分相当简单。而且有些算法作者虽然下了很大的功夫,效果却往往得不到它所希望的结果。4.2 时间限制有些程序的试用版每次运

51、行都有时间限制,例如运行10分钟或20分钟就停止工作,必须重新运行该程序才能正常工作。这些程序里面自然有个定时器来统计程序运行的时间。这种方法使用的较少。4.3 Key File 保护Key File(注册文件)是一种利用文件来注册软件的保护方式。Key File一般是一个小文件,可以是纯文本文件,也可以是包含不可显示字符的二进制文件,其内容是一些加密过或未加密的数据,其中可能有用户名、注册码等信息。文件格式则由软件作者自己定义。试用版软件没有注册文件,当用户向作者付费注册之后,会收到作者寄来的注册文件,其中可能包含用户的个人信息。用户只要将该文件放入指定的目录,就可以让软件成为正式版。该文件

52、一般是放在软件的安装目录中或系统目录下。软件每次启动时,从该文件中读取数据,然后利用某种算法进行处理,根据处理的结果判断是否为正确的注册文件,如果正确则以注册版模式来运行。这种保护方法使用也不多,但是,我个人认为,比时间限制要好。4.4 CD-check即光盘保护技术。程序在启动时判断光驱中的光盘上是否存在特定的文件,如果不存在则认为用户没有正版光盘,拒绝运行。在程序运行的过程当中一般不再检查光盘的存在与否。Windows下的具体实现一般是这样的:先用GetLogicalDriveStrings( )或GetLogicalDrives( )得到系统中安装的所有驱动器的列表,然后再用GetDri

53、veType( )检查每一个驱动器,如果是光驱则用CreateFileA( )或FindFirstFileA( )等函数检查特定的文件存在与否,并可能进一步地检查文件的属性、大小、内容等。4.5 反跟踪技术(Anti-Debug)好的软件保护都要和反跟踪技术结合在一起。如果没有反跟踪技术,软件等于直接裸露在 Cracker 面前。这里说的反跟踪,指的是反动态跟踪。即防止 Cracker 用 SoftICE 之类的调试器动态跟踪,分析软件。当前的这类软件还有如 TRW 、ICEDUMP 等等。反跟踪技术一般是具有针对性的,即针对某种调试器的反跟踪,而不能防止所有的调试器跟踪,如果有新的破解工具出

54、现,就需要相应的反跟踪技术 。这种技术一般是检测这些特定的调试器是否驻留内存,如果驻留内存,就认为被跟踪,从而拒绝执行,或进行一些惩罚性措施 。还有一些检测方法,如假设这些调试器在内存中,软件和这些调试器通信,如果结果合乎这些调试器的输出 。就认为被跟踪 。或者在内存中搜寻这些调试器的特征串,如果找到,就认为被跟踪 。有的甚至用中断钩子、SEH(Structural Exception Handle,即结构化异常处理)检测调试器。4.6 反反汇编技术(Anti- Disassmbly)即 Anti-Disassmbly 。可针对专门的反汇编软件设计的陷阱,让反汇编器陷入死循环,但这种方法没有通

55、用性。一般是使用花指令 。这种方法有通用性,即所有的反汇编器都可以用这种方法来抵挡 。这种方法主要是利用不同的机器指令包含的字节数并不相同,有的是单字节指令,有的是多字节指令。对于多字节指令来说,反汇编软件需要确定指令的第一个字节的起始位置,也就是操作码的位置,这样才能正确地反汇编这条指令,否则它就可能反汇编成另外一条指令了。并且,多字节,指令长度不定,使得反汇编器在错误译码一条指令后,接下来的许多条指令都会被错误译码 。所以,这种方法是很有效的 。实施方法:在程序中加入一些无用的字节来干扰反汇编软件的判断,从而使得它错误地确定指令的起始位置,那么也就达到了干扰反汇编器工作的目的。一般形式如下

56、:.jmp L1dd 012344578h ;这里是一些随机数,用来干扰反汇编器 ;对指令的译码L1:.4.7 软件狗软件狗是一种智能型加密工具。它是一个安装在并口、串口等接口上的硬件电路,同时有一套使用于各种语言的接口软件和工具软件。当被狗保护的软件运行时,程序向插在计算机上的软件狗发出查询命令,软件狗迅速计算查询并给出响应,正确的响应保证软件继续运行。如果没有软件狗,程序将不能运行,复杂的软硬件技术结合在一起防止软件盗版。真正有商业价值得软件一般都用软件狗来保护。平时常见的狗主要有洋狗(国外狗)和土狗(国产狗)。这里洋狗主要指美国的彩虹和以色列的HASP,土狗主要有金天地(现在与美国彩虹合

57、资,叫彩虹天地)、深思、尖石。总的说来,洋狗在软件接口、加壳、反跟踪等软方面没有土狗好,但在硬件上绝对无法破解(应当说破解难度非常大);而土狗在软的方面做的很好,但在硬件上不如洋狗,稍有单片机功力的人,都可以复制。4.8 Vbox 保护技术Vbox 是一个软件 。它是用来保护其它软件的 。凡被 Vbox 保护的软件,一旦过了试用期,就不能再使用了,删了重装也没用,除非删除整个操作系统再重装。4.9 SalesAgent 保护技术SalesAgent 保护的软件一般具有 x 天试用再购买的接口,是一种时间限制保护方式。才用这种保护方式的软件主要有 Macromedia Flash 4 、DreameWaver 等等。4.10 SecuROM 保护技

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论