计算机科学与技术专业毕业设计-PE格式程序加壳软件.doc_第1页
计算机科学与技术专业毕业设计-PE格式程序加壳软件.doc_第2页
计算机科学与技术专业毕业设计-PE格式程序加壳软件.doc_第3页
计算机科学与技术专业毕业设计-PE格式程序加壳软件.doc_第4页
计算机科学与技术专业毕业设计-PE格式程序加壳软件.doc_第5页
免费预览已结束,剩余68页可下载查看

下载本文档

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

文档简介

仲恺农业工程学院毕 业 设 计PE格式程序加壳软件姓 名 吴智文 院(系) 信息科学与技术学院专业班级 计算机科学与技术091班学 号 200910214117指导教师 张垒职 称 讲师论文答辩日期 2013年5月18日仲恺农业工程学院教务处制学生承诺书本人声明所呈交的论文软件加壳工具是本人在导师的指导下进行的,除了文中特别加以标注和致谢的地方外,论文中不包含其他人已经发表或撰写过的研究成果,不包含本人或其他用途使用过的成果。相关文献的引用已在论文中作了明确的说明。申请学位论文与资料若有不实之处,本人承担一切相关责任。特此声明。 论文作者签名: 年月 日摘 要加壳是保护文件的常用手段,其实是利用特殊的算法,对EXE、DLL文件里的资源进行压缩、加密。加壳过的程序可以直接运行,但是不能查看源代码.要经过脱壳才可以查看源代码。加壳的另一种常用的方式是在二进制的程序中植入一段代码,在运行的时候优先取得程序的控制权,做一些额外的工作。大多数病毒就是基于此原理。加壳的程序经常想尽办法阻止对程序的反汇编分析或者动态分析,以达到它不可告人的目的。这种技术也常用来保护软件版权,防止被软件破解。关键词:加壳软件保护软件破解目 录1 绪论11.1 加壳程序研究的背景11.2 软件保护现状与发展趋势11.3 研究的目的和意义22 软件保护32.1 软件版权现状32.2 常见软件保护方法32.2.1 序列号方式32.2.2 警告(NAG)窗口42.2.3 时间限制42.2.4 Key File保护42.2.5 功能限制的程序42.2.6 CD-check43 软件加壳脱壳介绍53.1 什么是壳53.2 壳的种类63.2.1 压缩壳63.2.2 加密壳63.3 脱壳技术64 PE文件结构74.1 简介74.2 PE文件结构布局74.3 重要的PE结构84.3.1 输入表8(图4.2)104.3.2 输出表115 常用逆向分析技术介绍135.1 动态分析技术135.2 静态分析技术145.3 结构化异常处理146 加壳程序的编写176.1 壳的一般装载过程176.2 PE文件数据读入,处理196.2.1 判断PE文件196.2.2,装入内存,初始化数据196.3 保存输入表206.3.1 输入表与脱壳206.4 原程序输入表的处理216.5 增加壳所需的节226.6 写入shellcode代码246.7 写入外壳所需数据247 shellcode的编写267.1 什么是外壳267.2 内联汇编277.2.1 简介277.2.2 外壳与内联汇编287.3 shellcode中变量存储297.3.1 局部变量与全局变量297.3.2 shellcode中的变量317.3.3 shellcode重定位327.3.4 字符串处理337.4 函数调用约定367.4.1 函数调用367.4.1 _cdecl调用约定377.4.2 _stdcall调用约定377.4.3 统一一种调用约定377.5 shellcode的提取397.6 取得API函数地址447.6.1 如何取得API447.6.2 取得Kernel32基址457.6.2 取得GetProcAddress函数地址467.7 恢复输入表497.8 重定位537.9 转交控制权548 反调试技术的加入568.1 反调试56总 结62参考文献63英文摘要64致 谢65681 绪论1.1 加壳程序研究的背景在自然界中,我想大家对壳这东西应该都不会陌生了。自然界中植物用它来保护种子,动物用它来保护身体等等。同样,在一些计算机软件里也有一段专门负责保护软件不被非法修改或反编译的程序。它们一般都是先于程序运行,拿到控制权,然后完成它们保护软件的任务。就像动植物的壳一般都是在身体外面一样理所当然(但后来也出现了所谓的“壳中带籽”的壳)。由于这段程序和自然界的壳在功能上有很多相同的地方,基于命名的规则,大家就把这样的程序称为“壳”了。就像计算机病毒和自然界的病毒一样,其实都是命名上的方法罢了。 从功能上抽象,软件的壳和自然界中的壳相差无几。无非是保护、隐蔽壳内的东西。而从技术的角度出发,壳是一段执行于原始程序前的代码。原始程序的代码在加壳的过程中可能被压缩、加密。当加壳后的文件执行时,壳这段代码先于原始程序运行,他把压缩、加密后的代码还原成原始程序代码,然后再把执行权交还给原始代码。 软件的壳分为加密壳、压缩壳、伪装壳、多层壳等类,目的都是为了隐藏程序真正的OEP(入口点,防止被破解)。1.2 软件保护现状与发展趋势软件加壳是作者写完软件后,为了保护自己的代码或维护软件产权等利益所常用到的手段。目前有很多加壳工具,当然有盾,自然就有矛,只要我们收集全常用脱壳工具,那就不怕他加壳了。软件脱壳有手动脱和自动脱壳之分,下面我们先介绍自动脱壳,因为手动脱壳需要运用汇编语言,要跟踪断点等,不适合初学者,但我们在后边将稍作介绍。 加壳一般属于软件加密,现在越来越多的软件经过压缩处理,给汉化带来许多不便,软件汉化爱好者也不得不学习掌握这种技能。现在脱壳一般分手动和自动两种,手动就是用TRW2000、TR、SOFTICE等调试工具对付,对脱壳者有一定水平要求,涉及到很多汇编语言和软件调试方面的知识。而自动就是用专门的脱壳工具来脱,最常用某种压缩软件都有他人写的反压缩工具对应,有些压缩工具自身能解压,如UPX;有些不提供这功能,如:ASPACK,就需要UNASPACK对付,好处是简单,缺点是版本更新了就没用了。另外脱壳就是用专门的脱壳工具来对付,最流行的是PROCDUMP v1.62 ,可对付目前各种压缩软件的压缩档。在这里介绍的是一些通用的方法和工具,希望对大家有帮助。我们知道文件的加密方式,就可以使用不同的工具、不同的方法进行脱壳。下面是我们常常会碰到的加壳方式及简单的脱壳措施,供大家参考: 脱壳的基本原则就是单步跟踪,只能往前,不能往后。脱壳的一般流程是:查壳-寻找OEP-Dump-修复 找OEP的一般思路如下: 先看壳是加密壳还是压缩壳,压缩壳相对来说容易些,一般是没有异常,找到对应的popad后就能到入口,跳到入口的方式一般为。 我们知道文件被一些压缩加壳软件加密,下一步我们就要分析加密软件的名称、版本。因为不同软件甚至不同版本加的壳,脱壳处理的方法都不相同。 1.3 研究的目的和意义研究加壳,可以使软件作者的劳动成果不会轻易地被软件破解者破解。作者编好软件后,编译成exe可执行文件。如果作者有以下需求 1.有一些版权信息需要保护起来,不想让别人随便改动,如作者的姓名,即为了保护软件不被破解,通常都是采用加壳来进行保护。 2. 需要把程序搞的小一点,从而方便使用。于是,需要用到一些软件,它们能将exe可执行文件压缩, 3. 在黑客界给木马等软件加壳脱壳以躲避杀毒软件。实现上述功能,只有利用加壳程序对软件进行加壳,以保护软件版权,保护软件作者的权益。2 软件保护2.1 软件版权现状计算机软件作者在付出劳动,创作计算机软件。计算机软件为软件使用都创造了价值,软件作者理应能得到应用的回报。但随着盗版,软件破解,非法下载等对软件权益的侵犯。使软件权益的保护得到了人们的关注。在美国,美国版权局于1964年就已开始接受程序的登记,国会于1974年设立了专门委员会,研究同计算机有关的作品生成、复制、使用等问题,并于1976年和1980年两次修改版权法,明确了由版权法保护计算机软件。随后,匈牙利于1983年,澳大利亚及印度于1984年先后把计算机软件列为版权法的保护客体。至于我国,在加入WTO之后,国务院关于鼓励软件产业和集成电路产业若干政策的通知、中华人民共和国著作权法、计算机软件软件保护条例等有关计算机软件保护的政策、法律法规的陆续出台或修订及公布实施,但在国,软件消费都比较喜欢用盗版,因为便宜,甚至免费。这也是因为我国消费者对软件版权意识的淡泊。还有一点是由于我国对软件侵权,保护等方面的法律建设较于其他国家较落后,使得软件破解等技术特别发达,软件保护刻不容缓。2.2 常见软件保护方法2.2.1 序列号方式软件验证序列号的过程,其实就是验证用户名和序列号之间的数学映射关系。这个映射关系是由软件的设计者制定的,所以各个软件生成序列号的算法是不同的。这是最常见的一种保护方式。2.2.2 警告(NAG)窗口 Nag的本义是烦人的意思。Nag窗口是软件设计者用来不时提醒用户购买正式版本的窗口。2.2.3 时间限制这类保护的软件一般都有时间段的限制,例如试用30天等。当过了共享软件的试用期后,就不予运行。只有向软件作者付费注册之后才能得到一个无时间限制的注册版本。2.2.4 Key File保护 Key File(注册文件)是一种利用文件来注册软件的保护方式。Key File一般是一个小文件,可以是纯文本文件,也可以是包含不可显示字符的二进制文件,其内容是一些加密过或未加密的数据,其中可能有用户名、注册码等信息。文件格式则由软件作者自己定义。试用版软件没有注册文件,当用户向作者付费注册之后,会收到作者寄来的注册文件,其中可能包含用户的个人信息。用户只要将该文件放入指定的目录,就可以让软件成为正式版。该文件一般是放在软件的安装目录中或系统目录下。软件每次启动时,从该文件中读取数据,然后利用某种算法进行处理,根据处理的结果判断是否为正确的注册文件,如果正确则以注册版模式来运行。2.2.5 功能限制的程序 这种程序一般是DEMO版或菜单中部分选项是灰色。有些DEMO版本的部分功能里面根本就没有。而有些程序功能全有,只要注册后就正常了。2.2.6 CD-check 最简单也最常见的光盘保护就是程序在启动时判断光驱中的光盘上是否存在特定的文件,如果不存在则认为用户没有正版光盘,拒绝运行。在程序运行的过程当中一般不再检查光盘的存在与否。3 软件加壳脱壳介绍3.1 什么是壳自然界中,植物用壳来保护种子,动物用壳来保护身体等。同样,在一些计算机软件里也有一段专门负责保护软件不被非法修改或反编译的程序。它们附加在原程序上通过windows加载器载入内存后,先于原始程序执行,得到控制权。执行过程中对原始程序进行解密,还原,还原完成后再把控制权交还给原始程序,执行原因来的代码的部分。加上外壳后,原始程序代码在磁盘文件中一般是以加密后的形式存在的。只在执行时在内容存中还原,这样就可以比较有效地防止破解者对程序文件的非法 修改,同时也可防止程序被静态反编译。由于这段程序和自然界的壳在功能上有很多相同的地方,基于命名的规则,就把这样的程序称为“壳”了。(图3.1)加壳软件一般都有良好的操作界面,使用也比较简单。除了一些商业壳,还有一些个人开发的壳,种类较多。壳对软件提供了良好保护的同时,也带来了兼容性的问题。选择一款壳保护软件后,要在不同硬件和系统上多测试。由于壳能保护自身代码,因此许多木马或者病毒都用壳来保护和隐藏自己。对于一些流行的壳,杀毒引擎能对目标软件脱壳,再进行病毒检查。而大多数私人壳,杀毒软件不会专门开发解压引擎,而是直接把壳当成木马或者病毒处理。有加壳就一定有脱壳。一般的脱壳软件多是专门针对某加壳软件而编写的,虽然针对性强,效果好,但收集麻烦。3.2 壳的种类3.2.1 压缩壳不同的外壳所侧重方面不一样,有的侧重于压缩,有的则侧重于加密。压缩壳的特点是减小软件体积,加密保护不是重点。常见的压缩壳有:UPX, ASPack, PECompact等.3.2.2 加密壳加密壳和类多,一些加密壳单纯保护程序,另一些壳还提供额外的功能.侧重于软件的保护,使软件不容易被分析,反汇编.常见的加密壳有:ASProtect,Armadillo,EXECryptor,Themida等3.3 脱壳技术任何事物都有两面性,有加壳,就必有脱壳.加壳与脱壳有着紧密的联系.一些脱壳技术是针对加壳而产生的,脱壳的进步,又迫使加壳软件不断创新发展.脱壳一般分手动和自动两种,手动就是用TRW2000、TR、SOFTICE等调试工具对付,对脱壳者有一定水平要求,涉及到很多汇编语言和软件调试方面的知识。而自动就是用专门的脱壳工具来脱,最常用某种压缩软件都有他人写的反压缩工具对应,有些压缩工具自身能解压,如UPX;有些不提供这功能,如:ASPACK,就需要UNASPACK对付,好处是简单,缺点是版本更新了就没用了。另外脱壳就是用专门的脱壳工具来对付,最流行的是PROCDUMP v1.62 ,可对付目前各种压缩软件的压缩档。知道文件的加密方式,就可以使用不同的工具、不同的方法进行脱壳。4 PE文件结构4.1 简介PE文件被称为可移植的执行体是Portable Execute的全称,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件(可能是间接被执行,如DLL)认识可执行文件的结构非常重要,在DOS下是这样,在Windows系统下更是如此。了解了这种结构后就可以对可执行程序进行加密、加壳和修改等,一些黑客也利用了这些技术。对于一切的加壳脱壳,都是对PE文件进行加壳脱壳。所以了解PE文件对加壳及脱壳是必不可少的。4.2 PE文件结构布局PE文件的数据结构在磁盘中和内存中一样这个是它的最大特点。加载一个可执行文件到内存主要是把PE文件中的某个部分映射到地址空间中。那么不仅要问编写好的代码怎样变成了可执行文件,这个过程就是编译器的主要工作,它将其编译称为目标文件OBJ,这样连接器就将OBJ文件和涉及到的库文件以及资源文件连接起来,生成最后的EXE文件保存在磁盘中,当我们运行其文件时Windows加载器就负责把EXE文件按照PE文件格式映射到内存的线性地址空间,最终表现为一个进程。当然这只是一个简单的介绍,这里面还涉及到操作系统的很多内容,这里我们关系与PE文件格式相关的内容有:相对虚拟地址、导入表、重定位表等内容。(图4.1)4.3 重要的PE结构4.3.1 输入表输入表是PE文件结构中不可或缺的部分,输入表也称之为“导入表”。要想了解输入表,首先还得先从DLL文件入手。日常生活中我们会看见一些大型软件有很多的DLL格式的文件,它们是“动态链接库文件”,这些文件中有很多的导入函数,这些函数不会直接被执行,当一个程序(EXE)运行时,导入函数是被程序调用执行的,其执行的代码是不在主程序(EXE)中的一小部分函数,其真正的代码却在DLL文件中。这时我们就会想,那么EXE主程序是如何找到这些需要导入的函数呢,这就要归结于“输入表”了,输入表就相当于EXE文件与DLL文件沟通的钥匙,形象的可以比喻成两个城市之间交流的高速公路,所有的导入函数信息都会写入输入表中,在PE文件映射到内存后,windows将相应的DLL文件装入,EXE文件通过“输入表”找到相应的DLL中的导入函数,从而完成程序的正常运行,这一动态连接的过程都是由“输入表”参与的。输入表的结构:输入表是以一个IMAGE_IMPORT_DESCRIPTOR(IID)数组开始,一个程序 要调用几个dll就会有几个IID项,即每个IID对应于一个dll IID结构: IMAGE_IMPORT_DESCRIPTOR struct union DWORD Characteristics ; ;00h DWORD originalFirstThunk; ; TimeDateStamp DWORD ;04h ForwarderChain DWORD ;08h Name DWORD ;0Ch FirstThunk DWORD ;10h IMAGE_IMPORT_DESCRIPTOR ends 当pe文件被装载器装入之后,OriginalFirstThunk还指向原来的数组(INT 输入名字表:Import Name Table),而FirstThunk则用调用的输入函数在内存的虚拟地址来代替表中的内容,即指向的是一张指向输入函数地址的表,称为:IAT(输入地址表:Import Address Table) IMAGE_THUNK_DATA结构: typedef struct IMAGE_THUNK_DATA union PBYTE ForwarderString; /当该结构双字最高位为1时,表示函数以序号方式输入,此时双字低位为函数序号 PDWORD Function; /当最高位为0时,表示函数以字符串类型的函数名方式输入,此时整个双字的值是 DWORD ordinal; /一个RVA,指向一个MAGE_IMPORT_BY_NAME结构(如下定义) PIMAGE_IMPORT_BY_NAME AddressOfData; / ul; IMAGE_IMPORT_BY_NAME 结构: MAGE_IMPORT_BY_NAME STRUCT Hint WORD ? /指示本函数在所驻留dll中的输出表中的序号(不是必须的) Name BYTE ? /含有输入函数的函数名,一个ASCII码字符串,以NULL结尾(可变尺寸) MAGE_IMPORT_BY_NAME ENDS 输入表的结构如下图所示(图4.2)4.3.2 输出表当PE文件被执行的时候,windows装载器将文件装入内存并将输入表中登记的DLL文件一并装入,再根据DLL文件中的函数输出信息对被执行文件的IAT表进行修正。在这些包含输出函数的DLL文件中,输出信息被保存在输出表中,通过输出表,DLL文件向系统提供输出函数的名称,序号和入口地址等信息。以便windows装载器通过这些信息来完成动态链接的过程。一般来说,exe文件不存在输出表,DLL文件大部分都包含输出表,不过这不是绝对的。一个Windows程序,它所实现的所有功能最终几乎都是调用系统DLL提供的API函数。通过我先前文章手写的Hello World程序可以看出,要使用任何一个DLL所提供的函数,我们需要将它导入,也就是用到了导入表。然而对于那些提供了被导出的函数的DLL程序来说,他们必须使用导出表将函数导出,之后别的程序才可以使用。无论是系统提供的标准DLL还是个人编写的DLL,只要想提供自己的函数给别人使用就必须建立导出表。一般使用任何开发环境编写具有导出功能的程序,导出表都是由链接器自动建立的。程序员只需指定被导出的函数名称或序号即可。以下是输出表的定义:typedef struct _IMAGE_EXPORT_DIRECTORY DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; / RVA from base of image DWORD AddressOfNames; / RVA from base of image DWORD AddressOfNameOrdinals; / RVA from base of image IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;第一个成员(Characteristics)是个DWORD类型,占四个字节。通常该值为零。 第二个成员(TimeDateStamp)DWORD类型,表示输出表创建的时间,该时间是自1970年1月1日至今的秒数。这个值通常不会影响程序执行,可以是零。 第三个成员(MajorVersion)是个WORD类型,表示主版本号,这个值通常不会影响程序执行,可以是零。 第四个成员(MinorVersion)是个WORD类型,表示次版本号这个值通常不会影响程序执行,可以是零。 第五个成员(Name)是个DWORD类型,指向模块的真实名称字符串的RVA值。该值是必须的,不能为零,通常PE装载器加载导入库的时候就是用这个内部名字而无论文件名是什么,一般情况下它和文件名相同。可以看到本程序中该值为0x2e0a6。将其转换为文件偏移,因为改程序的内存对齐粒度和文件对齐粒度都是0x1000,是相同的。所以RVA值转换成文件偏移也是相同的,是0x2e0a6。在文件找到偏移为0x2e0a6的地址处,可以看到该地址存放的字符串为:DLL.dll。由此得知此文件的导出模块名为DLL.dll。 第六个成员(Base)是个DWORD类型,是基数,实际上它等于所有函数导出序号中最小的值,该值也是导出地址表中第一个地址对应函数的导出序号。默认情况下导出序号从一开始递增,所以最小序号为一。但是也有特殊的情况,如下所述。 第七个成员(NumberOfFunctions)是个DWORD类型,表示模块中导出函数/符号个数,这里是0x00000002,由此得知此模块导出了两个函数(注意:这里不一定是真实导出函数的个数。实际上,该值等于所有函数中导出序号最小的值依次递增一到最大的值所经历的数的个数。)因为默认情况下导出序号从一开始递增,每次递增一,所以所经历的个数与导出函数个数相等。例如某模块导出了五个函数,而导出序号从一开始依次递增,每次递增一,递增到最大的是五,那么从一到五经历的的个数也是五。但是可以通过修改DEF文件为某个函数指定特定的导出序号,如果指定的结果并不是按照从一开始逐个递增一,那么将导致导出函数个数与递增个数不相等(也就是这里的值)5 常用逆向分析技术介绍5.1 动态分析技术动态分析技术是指利用调试器如(OLLYDBG)运行软件进行动态的调试的技术。动态分析一般可以动态地分析出软件的运行情况。动态调试技术在软件逆向工程领域也是一个很热门的概念,它是与静态分析技术相对而言的。静态分析技术是指破解者利用反汇编工具将二进制的可执行文件翻译成汇编代码,通过对代码的分析来破解软件;而动态调试则是指破解者利用调试器跟踪软件的运行,寻求破解的途径。在Win32环境下,几乎每一种高级语言都提供了调试器,供程序员侦错、跟踪自己的程序。Windows平台的调试器主要分为两大类:1 用户模式(user-mode)调试器:它们都基于win32 Debugging API,有使用方便的界面,主要用于调试用户模式下的应用程序。这类调试器包括Visual C+调试器、WinDBG、BoundChecker、Borland C+ Builder调试器、NTSD等。2 内核模式(kernel-mode)调试器:内核调试器位于CPU和操作系统之间,一旦启动,操作系统也会中止运行,主要用于调试驱动程序或用户模式调试器不易调试的程序。这类调试器包括WDEB386、WinDBG和softice等。其中WinDBG和softice也可以调试用户模式代码。实际上,目前最流行的调试工具是德国人出的Ollydbg,它对SEH异常处理,VXD,MMX等指令集都有很好的支持.Ollydbg,简称OD。一个新的动态追踪工具,将IDA与SoftICE结合起来的思想,Ring 3级调试器,非常容易上手,己代替SoftICE成为当今最为流行的调试解密工具了.同时还支持插件扩展功能,是目前最强大的调试工具.5.2 静态分析技术用高级语言编写的程序有两种形式,一种被编译成机器语言在CPU上执行,如果VC+, Pascal等。由于机器语言与汇编语言几乎是对应的,因此可以将机器语言转化成汇编语言,这个过程称为反汇编(Disassembler)。例如,在X86系统中,机器码“EBH”对诮的汇编语句是“JMP SHORT XX”。另一种高级语言是边解释边执行的,称为解释性语言,如果VB,Visual FoxPro等,这类语言的编译后程序可以被还原因成高级语言的原始结构,这个过程称为反编译(Decompiler).所谓静态分析,即从反汇编,反编译手段获得程序汇编代码或者源代码,然后从清单上分析程序流程,了解模块完成的功能。软件静态分析,是以输入软件二进制代码流,输出分析结果的分析方法。不用运行程序,即可得到分析的结果。静态分析都要借助一些强大的反汇编分析工具如:IDA Pro, W32Dasm, Hiew等静态分析工具。通过这些静态分析工具的帮助,逆向人员可以不用运行程序就能知道程序要做什么操作,可防止恶意软件运行破坏计算机。静态分析从反汇编出来的程序清单上分析程序流程,了解模块完成的功能。静态分析先分析程序的文件类型,如果程序使用加壳保护,则要先进行脱壳。通过静态分析工具加载程序进行分析。5.3 结构化异常处理 当某一线程发生异常时,程序的控制权会立即进入Ring0异常处理程序,这是属于操作系统的部分, 如果发生的异常是如页异常之类的异常,Ring0处理程序可以处理完它后重新回到程序中执行,而被中断 过的进程可能根本就不知道发生过异常。 但事情并不总是如此,有时进程会发生一些始料不及的异常,例如访问不存在的内存,被0除等, 这些异常Ring0处理程序不知该如何处理它,而进程本身也可能想自己处理这些情况,这是就要用到结构化异常处理(SEH)。在C/C+中也有异常处理的语句如_try,_catch等,这些语句的实现也与SEH紧密联 系。 当系统遇到一个它不知道如何处理的异常时,它就查找异常处理链表,注意每个线程都有它自己的异 常处理链表。异常链表以FS:0所指向的位置为链表头。 异常处理开始时,系统把一些与当前线程和与异常有关的内容传给链头所指向的处理程序;处理程序 由用户编写或编译器生成,它的返回值可以是告诉系统:异常处理以完成,可以继续执行程序,或未处理 异常,可由链表的下一个处理程序处理等,可以一次传递下去。(图5.1)6 加壳程序的编写6.1 壳的一般装载过程加壳过的EXE文件是可执行文件,它可以同正常的EXE文件一样执行。用户执行的实际上是外壳程序,这个外壳程序负责把用户原来的程序在内存中解压缩,并把控制权交还给解开后的真正程序,这一切工作都是在内存中运行的,整个过程对用户是透明的。 壳和病毒在某些方面比较类似,都需要比原程序代码更早的获得控制权。壳修改了原程序的执行文件的组织结构,从而能够比原程序的代码提前获得控制权,并且不会影响原程序的正常运行。这里简单说说一般壳的装载过程。(参考了Ljtt以前写过的一篇文章)1)获取壳自己所需要使用的API地址 如果用PE编辑工具查看加壳后的文件,会发现未加壳的文件和加壳后的文件的输入表不一样,加壳后的输入表一般所引入的DLL和API函数很少,甚至只有Kernel32.dll以及GetProcAddress这个API函数。 壳实际上还需要其他的API函数来完成它的工作,为了隐藏这些API,它一般只在壳的代码中用显式链接方式动态加载这些API函数2) 解密原程序的各个区块(Section)的数据 壳出于保护原程序代码和数据的目的,一般都会加密原程序文件的各个区块。在程序执行时外壳将会对这些区块数据解密,以让程序能正常运行。 壳一般按区块加密的,那么在解密时也按区块解密,并且把解密的区块数据按照区块的定义放在合适的内存位置。 如果加壳时用到了压缩技术,那么在解密之前还有一道工序,当然是解压缩。这也是一些壳的特色之一,比如说原来的程序文件未加壳时12M大小,加壳后反而只有几百K。 3)重定位 文件执行时将被映像到指定内存地址中,这个初始内存地址称为基地址(ImageBase)。当然这只是程序文件中声明的,程序运行时能够保证系统一定满足其要求吗? 对于EXE的程序文件来说,Windows系统会尽量满足。例如某EXE文件的基地址为0x400000,而运行时Windows系统提供给程序的基地址也同样是0x400000。在这种情况下就不需要进行地址“重定位”了。由于不需要对EXE文件进行“重定位”,所以加壳软件把原程序文件中用于保存重定位信息的区块干脆也删除了,这样使得加壳后的文件更加小巧。有些工具提供“Wipe Reloc”的功能,其实就是这个作用。 不过对于DLL的动态链接库文件来说,Windows系统没有办法保证每一次DLL运行时提供相同的基地址。这样“重定位”就很重要了,此时壳中也需要提供进行“重定位”的代码,否则原程序中的代码是无法正常运行起来的。从这点来说,加壳的DLL比加壳的EXE更难修正。 4)HOOK-API 程序文件中的输入表的作用是让Windows系统在程序运行时提供API的实际地址给程序使用。在程序的第一行代码执行之前,Windows系统就完成了这个工作。 壳一般都修改了原程序文件的输入表,然后自己模仿Windows系统的工作来填充输入表中相关的数据。在填充过程中,外壳就可填充HOOK-API的代码的地址,这样就可间接地获得程序的控制权。 5)跳转到程序原入口点(OEP) 从这个时候起壳就把控制权交还给原程序了,一般的壳在这里会有明显的一个“分界线”。但现在的猛壳己没这界限了,壳里有肉,肉里有壳。(图6.1)6.2 PE文件数据读入,处理6.2.1 判断PE文件首先要判断要加壳的文件是否是PE文件,如果不是,不处理。判断输入文件是否是PE文件,可通过先检验文件头部第一个字的值是否等于IMAGE_DOS_SIGNATURE,也就是字符串“MZ”,因为所有PE文件头两个字节都是它。然后再根据e_lfanew字段找到PE头,校验是否指向一个字符串“PE”。如果这两个条件都成立的话,就可以认为它是一个PE文件了。6.2.2,装入内存,初始化数据读取入文件到内存,取得PE文件相关信息,本程序采用仿照Windows装载器载入PE的方式,根据各个区块的RVA分别读入内存。(图6.2)首先取得PE文件头的SizeOfImage字段的值,然后申请相应大小的内存,再根据文件的节表中的虚拟地址和虚拟内存大小的值一个一个分别读入。这种方式有一个好处,就是要使用数据时只需要根据数据的RVA加上基址就可以找到他的地址了。6.3 保存输入表6.3.1 输入表与脱壳脱壳肯定要有的一步就是输入表的还原。在脱壳中输入表处理是很关键的一个环节,因此要求脱壳者对PE格式中的输入表概念非常清楚。在磁盘文件中,PE文件的输入表结构如下图所示:(图6.3) PE文件运行时,Windows系统加载器首先搜索OriginalFirstThunk,如果存在,装载程序迭代搜索数组中的每个指针,找到每个IMAGE_IMPORT_BY_NAME结构所指向的输入函数的地址,然后用函数入口地址来替代由FirstThunk指向的 IMAGE_THUNK_DATA 数组里的元素值(即用真实的函数地址填充到IAT里)。因当PE文件装载内存后准备执行时,上图己转换成这种情况了:(图6.4)此时输入表中其它部分就不重要了,程序依靠IAT提供的函数地址就可正常运行(图8.2 红圈部分)。如果程序加壳了,那壳自己模仿Windows装载器的工作来填充IAT中相关的数据,此时内存中就一张IAT表,输入表的其他部分是不存的(当然不是绝对的,也有不少壳,如Aspack等,内存中会出现完整的输入表结构)6.4 原程序输入表的处理 通过输入表,破解者可以知道程序都用到了哪些API,通过加密输入表,可以增加破解者破解难度.在如今常见的那些带加密功能的外壳中,破坏原程序的输入表几乎是必有的功能,而且是各出奇招,尽可能让脱壳者无法修复.要破坏一个程序的输入表一般要有两步,第一步是在加密时做的,它破坏程序中的输入表,将其转换成另一个形式存储.本加壳程序就是使用这种方式保存输入表.为了转存输入表,定义如下数据结构来存储:在这里,用到一个全局的链表来存储输入表,然后在存储到文件时,按照一定的组积格式存入文件中.首先要读取输入表里的数据,存放到上面的那两个数据结构里.(图6.5)(图6.6)6.5 增加壳所需的节由于加壳程序要修改目标PE文件,且为了方便写入的外壳能更有效地组织,需要增加两个节。 一个节用来写入shellcode,即在被加壳文件中首先运行的外壳代码。另一个节是外壳所用到的数据节。把外壳需要用到的数据写进被加壳文件中,如原程序真正的程序入口点,外壳所用的输入表等。先要在PE头增加两个节头。(图6.7)增加了两个节后,用一些PE查看工具即可查看到所增加的两个节。名叫.mypack和.mpdata(图6.8)6.6 写入shellcode代码把节的存储空间准备好后,就可以写入shellcode。至于shellcode的编写,论文后面会作详细介绍。(图6.9)6.7 写入外壳所需数据这里的数据是外壳所用到,包括OEP,外壳所用输入表,等。以下是该数据结构:(图6.10)接这个数据结构后面的,是原程序的原始输入表,用自定义的格式,写入到文件里。(图6.11)7 shellcode的编写7.1 什么是外壳外壳实际上是一段shellcode,被存放到某段内存中。CPU可以执行这段shellcode。加壳后的程序,外壳会先于原程序取得控制权,在进行了外壳的解密等操作后,会将把控制权还给原程序。对于用户来讲,外壳好像什么都没有做一样,对用户是完全透明的。但对想要破解软件者来讲,就是一道必须面对的坑。ShellCode可以在内存中任何一个位置独立运行,他不需要外界对其进行任何操作,不需要装载器对其进行装载、重定位、填充IAT这样操作。 因此很多工作都需要自己去做。所以,在编写时需要一定的前置知识: 1.编译器的实现原理.高级语言源代码是怎么变成Opcode的,如何处理局部或全局变量,文件作用域内代码是如何排布的 2.PE文件结构的知识.尤其是EAT方面的,因为在 ShellCode执行过程中调用的API都需要你自己获取到。Shellcode是一段高技巧的软件代码,为了小而精,一般直接写为16进制的操作码,当然编写者一般采用C或汇编编写,然后通过汇编程序成为16进制的操作码。Shellcode能完成特殊任务的自包含的二进制代码,根据不同的任务可能是发出一条系统调用或建立一个高权限,Shellcode还可用于漏洞入侵,通过把一段二进制码送入后并执行,就可以获得目标机器的控制权,之后的事情是属于爱好者学习技术,还是黑客的行为,就看攻击者的一念之差。但这里,本程序的shellcode只是完成外壳应该要做的事情:取得所需API,还原输入表,重定位程序,跳转程序原始OEP。这些都是一般外壳要做的事。(图7.1)7.2 内联汇编7.2.1 简介内联汇编,就是在C/C+语言的代码中插入汇编语言编写程序。这种编程方式更加高效,更加底层。一个内联汇编在 C 和 C+ 可以嵌入汇编语言指令源程序,而无需额外的程序集和链接步骤。 一个内联汇编编译为编译器 您不需要一个单独的汇编程序 (如 Microsoft Macro Assembler (masm)。由于这个内联汇编不需要单独的程序集和链接步骤,与一个单独的汇编方便。 内联程序集代码可以使用任何 C 或 C+ 变量或中的函数名,因此,其集成的程序的 C 和 C+ 代码非常容易。 并且,由于程序集代码可以与 C 和 C+ 语句组合,可以执行会相当麻烦或不可以在一个 C 或 C+ 的任务。_asm 关键字调用一个内联汇编,并且可以显示,每当 c. 或 C+ 语句合法。 它不能单独出现。 必须由程序集指令执行它,请在大括号中的命令的一组,或者,至少, null 对大括号。 该术语 “_asm 块是”此处称为命令的说明或组,在大括号。下面的代码是简单的 _asm 块对大括号。 (代码是一个自定义 prolog 序列。)/ asm_overview.cpp/ processor: x86void _declspec(naked) main() / Naked functions must provide their own prolog. _asm push ebp mov ebp, esp sub esp, _LOCAL_SIZE / . and epilog _asm pop ebp ret 或者,可以放置在每个程序集指令前面的 _asm :_asm push ebp _asm mov ebp, esp _asm sub esp, _LOCAL_SIZE7.2.2 外壳与内联汇编由于外壳的特殊性,外壳的编写使用汇编比较方便。高级语言如C/C+等对一些比较底层的操作做不到汇编那么灵活,自如。但使用内联汇编也要注意更多问题,如返回值,栈平衡等。内联汇编可以帮助我们充分利用计算能力。然而,大多数程序员很少有机会实际使用该特性。事实上,内联汇编只为特定的要求提供服务,在涉及先进的高层编程语言时尤其如此。 由于shellcode所需的操作比较特别,所以本加壳程序还是采用内联汇编作为shellcode编写方法。虽然也可以用别的编译器如microsoft的masn。但这对shellcode的提取和整合到程序都比较麻烦。所以还是采用内联汇编。如图为本程序所写的shellcode。(图7.2)7.3 shellcode中变量存储7.3.1 局部变量与全局变量从物理上讲,堆栈是就是一段连续分配的内存空间。在一个程序中,会声明各种变量。静态全局变量是位于数据段并且在程序开始运行的时候被加载。而程序的动态的局部变量则分配在堆栈里面。从操作上来讲,堆栈是一个先入后出的队列。他的生长方向与内存的生长方向正好相反。我们规定内存的生长方向为向上,则栈的生长方向为向下。压栈的操作push=ESP-4,出栈的操作是pop=ESP+4.换句话说,堆栈中老的值,其内存地址,反而比新的值要大。请牢牢记住这一点,因为这是堆栈溢出的基本理论依据。在一次函数调用中,堆栈中将被依次压入:参数,返回地址,EBP。如果函数有局部变量,接下来,就在堆栈中开辟相应的空间以构造变量。函数执行结束,这些局部变量的内容将被丢失。但是不被清除。在函数返回的时候,弹出EBP,恢复堆栈到函数调用的地址,弹出返回地址到EIP以继续执行程序。这就是正常C函数调用所用到的变量与栈的关系。(图7.3)在shellcode中,不能使用全局变量。正常通过编译器编写的程序,可以有全局变量,全局变量都是存储在数据段中,全局变量的地址在链接完成后就已经是确定的了。但是shellcode执行的地址却是不确定的,他无法使用全局变量。7.3.2 shellcode中的变量在shellcode中不可能使用全局变量,因为在不同的被加壳PE文件内部,是不可能知道或者存在全局变量的地址。在这里可以使用局部变量的方式,使用EBP指针来定位局部变量。Shellcode使用C语言对局部变量的处理方式,来使用变量。(图7.4)通过这

温馨提示

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

评论

0/150

提交评论