1、7 恶意代码攻防技术17 恶意代码攻防技术u 随着计算机、网络、信息等技术的飞速发展,人类社会已经进入信息化时代。信息化带给人们极大便捷的同时,也带来了隐私泄露等信息安全问题。其中,恶意代码对信息安全构成的威胁最为严重。u 恶意代码是指故意编制或设置的、对网络或系统会产生威胁或潜在威胁的计算机代码。随着网络技术高速发展,恶意代码传播方式也在迅速地演化,从引导区传播,到电子邮件传播、网络传播,发作和流行时间越来越短,威胁也越来越大。本章将对恶意代码的攻防进行拓展介绍。210 物联网智能设备攻防技术u 主要内容p 7.1 恶意代码概述u 恶意代码行为u 恶意代码免杀技术p 7.2 逆向工程基础u
2、软件分析技术u 逆向分析技术u 代码保护方法u 加壳与脱壳的技术37.1 恶意代码概述4恶意代码行为u 1 1常见的恶意代码行为u 常见的两种恶意代码行为是下载器和启动器。恶意代码被下载器从互联网上下载后,在本地运行。启动器包含立即运行或之后的某个时间运行的恶意代码。下载器有多种主流方式,后门就是其中之一。启动器的实施则常采用反向ShellShell的方式。p 1 1)后门(BackDoorBackDoor)p 后门是一种常见的恶意代码,为攻击者提供远程攻击受害主机的途径。后门一般拥有多种功能,如操作注册表、列举窗口、创建目录、搜索文件等。p 2 2)反向shellshellp 反向shell
3、shell是指从被感染机器上发起连接,提供了攻击者shellshell访问被感染机器的权限,使攻击者可以像在本地系统上一样执行指令。59.1多Agent系统简述第九章 多Agent系统of 316弱定义:Agent一般用以说明一个软硬件系统。它具有自治性,社会性,反映性,能动性等特性。9.1.1概念1.Agent强定义:Agent不仅具有以上的特性,而且具有知识、信念、义务、意图等人类才具有的特性。强定义更加强调人格化概念的Agent的心智要素。一般而言,可以认为Agent是一个能够感知外界环境并具有自主行为能力的以实现其设计目标的自治系统。它运行于复杂和不断变化的动态环境中,能有效地利用环境
4、中各种可以利用的数据、知识、信息和计算资源,准确理解用户的真实意图,为用户提供迅捷、准确和满意的服务。9.1多Agent系统简述第九章 多Agent系统of 317关于MAS,百度百科给出的定义是由多个Agent组成的集合,其多个Agent成员之间相互协调,相互服务,共同完成一个任务。它的目标是将大而复杂的系统建设成小的、彼此互相通信和协调的,易于管理的系统。2.多Agent系统(MAS)MAS作为解决复杂系统的一个有效方法,能够利用并行分布式处理技术和模块化设计思想,把复杂系统划分成相对独立的Agent子系统,通过Agent之间的共同协作来完成对复杂问题的求解。在一个MAS中,各Agent成
5、员之间是独立自主的,其自身的目标和行为不受其它Agent成员的限制,可以采用不同的设计方法和计算机语言开发而成,没有全局数据,也没有全局控制,是一种开放的系统,Agent加入和离开都是自由的。9.1多Agent系统简述第九章 多Agent系统of 318MAS的体系结构是指MAS中Agent间的信息关系和控制关系,以及问题求解能力的分布模式,通过定义Agent之间的权威关系,为Agent提供了一种交互框架。9.1.2 MAS的结构MAS的体系结构可以有三种形式:集中式、分布式和混合式。(1)集中式结构(2)分布式结构(3)混合式结构9.1多Agent系统简述第九章 多Agent系统of 319
6、MAS用于解决实际问题,其特性因为应用的不同领域而有所不同,主要有以下一些特点:9.1.3特点及分类1.特点(1)由于Agent可以是不同的个人或组织采用不同的设计方法和计算机语言开发而成,因而可能是完全异质和分布的;(2)在MAS中,每个Agent是自治的,各自按照自己的方式异步地运行自己的进程,解决给定的子问题,自主地推理和规划并选择适当的策略,并以特定的方式影响环境;(3)MAS支持分布式应用,具有良好的模块性、易于扩展、设计灵活简单。在其实现过程中,不追求单个庞大复杂的体系,而是按面向对象的方法构造多层次,多元化的Agent以降低各个Agent问题求解的复杂性从而降低整个系统的复杂性;
7、(4)MAS是一个集成系统,各Agen之间互相通信彼此协调,并行地求解问题,能有效地提高问题求解的能力;(5)多Agent技术打破了人工智能领域仅仅使用一个专家系统的限制,在MAS环境,各领域的不同专家可能协作求解某一个专家无法解决或无法很好解决的问题,提高系统解决问题的能力。9.1多Agent系统简述第九章 多Agent系统of 31109.1.3特点及分类2.分类(1)根据Agent的自主性分类1)由控制Agent和被控Agent构成的系统:Agent之间存在较强的控制关系,每个Agent或对其他Agent具有控制作用,或受控于对它具有权威的Agent。在这类系统中,被控Agent的行为受
8、到约束,自主程度较低。2)自主Agent构成的系统:Agent自主地决策,产生计划,采取行动。Agent之间具有松散的社会性联系。Agent通过与外界的交互,了解外部世界的变化,并从经验中学习增强其求解问题的能力以及与相识者建立良好的协作关系。在这类系统中,自主Agent之间的协作关系是互惠互利的关系,当目标发生冲突时,通过协商来解决。3)灵活Agent(即半自主的Agent)构成的系统:Agent进行决策时,某些问题在一定程度上需要受控于其他Agent,大部分情况下要求Agent完全自主地工作。在这类系统中,Agent之间通常是松散耦合,具有一定的组织结构,通过承诺和组织约束相互联系。9.1
9、多Agent系统简述第九章 多Agent系统of 31119.1.3特点及分类2.分类(2)根据对动态性的适应方法分类 1)系统拓扑结构不变,即Agent数目、Agent之间的社会关系等都不变。Agent内部结构固定,基本技能不变,通过重构求解问题的方式来适应环境;Agent通过自重组来适应环境,例如修改调整自己的知识结构、目标、选择等。2)系统拓扑结构改变Agent数目不变,每个Agent的微结构稳定,可以修改Agent间的关系和组织形式;可增减Agent数目,可以动态创建和删除Agent。9.1多Agent系统简述第九章 多Agent系统of 31129.1.3特点及分类2.分类(3)根据
10、系统功能结构分类 1)同构型系统,即每个Agent功能结构相同;2)异构型系统,Agent的结构、功能、目标都可以不同,由通信协议保证Agent间协调与合作的实现。(4)根据Agent关于世界知识的存储分类 1)反应式多Agent系统;2)黑板模式的多Agent系统;3)分布存储的多Agent系统。(5)根据控制结构分类 1)集中控制:由一个中心Agent负责整个系统的控制、协调工作;2)层次控制:每个Agent控制处于其下层的Agent的行为,同时又受控于其上层的其他Agent;3)网络控制:由信息传递构成的控制结构,且该控制结构是可以动态改变的,可以实现灵活控制。恶意代码行为13u 2 2
11、僵尸网络u 僵尸网络是被感染主机(僵尸主机)的一个集合。僵尸网络由单一的实体控制,通常由一个称为僵尸控制器的计算机作为服务器。僵尸网络会尽可能多地感染计算机,进而传播恶意代码或蠕虫,也可以用于执行分布式拒绝服务攻击(DDoSDDoS)。u 3 3登录凭证窃取u 攻击者窃取登录凭证主要使用以下3 3种攻击方式。p(1 1)记录击键。p(2 2)转储系统中存放的信息。p(3 3)等待用户登录以窃取相应的凭证,如访问网站时产生的cookiecookie。u 4 4提权攻击u 一个恶意代码通常要执行提升权限攻击来获得对系统所有的访问权限。恶意代码免杀技术u 1 1隐藏用户态RootkitRootkit
12、u 恶意代码通常会隐藏其运行进程或依附进程。RootkitRootkit是众多隐藏技术的主流之一,其工具的表现形式各异,但是,大部分RootkitRootkit通常通过修改系统调用、系统内核进行工作。这样可以将恶意代码的源程序、过程数据、运行进程、网络连接,以及其他相关资源隐藏起来,使得反病毒程序和安全人员难以发现它们。u 一些RootkitRootkit会修改用户态的应用程序或系统内核,这样做的目的在于直接让其运行于内核态下,绕过用户态的保护程序,甚至成功躲避内核态的保护机制。与运行在用户态相比,运行在内核态的RootkitRootkit对操作系统的破坏力更大。u 运行在用户态的Rootki
13、tRootkit通常基于HookHook技术,通过对HookHook的定位和行为的分析,实现破坏系统的最终目标。14恶意代码免杀技术u 2 2数据加密u 恶意代码使用了加密技术隐藏其恶意行为。作为恶意代码的分析人员,想要了解恶意代码,就要掌握数据的加/解密技术。u 数据加密时,攻击者往往选择便于编码实现,同时又能提供足够破译复杂性的加密算法。古典密码算法通过字符集转换、字符的替代、覆盖等技术实现信息的隐藏。它更适用于有限的空间,其性能开销相对较小。恶意代码仅采用简单加密算法来阻止安全人员进行基本的分析。u 恶意代码通过加密网络通信和程序内部逻辑实现其行为隐藏的目的。p(1 1)将窃取的信息转储
14、到一个文件,并使用加密技术将其隐藏。p(2 2)转储需要使用的字符串,使用前再对其解密。p(3 3)隐藏配置信息,如指令和控制服务器的IPIP地址。157.2 逆向工程基础16逆向工程基础17u 逆向工程,即对一项目标产品进行逆向分析及研究的技术过程,从而演绎并得出该产品的处理流程、组织结构、功能、性能规格等设计要素,以制作出功能相近,但又不完全一样的产品。u 逆向工程源于商业及军事领域中的硬件分析,其主要目的是在无法轻易获得必要的生产信息的情况下,直接从成品分析,推导其设计原理。静态分析技术u 所谓静态分析,即从反汇编、反编译手段获得程序的汇编代码或源代码,然后从程序清单中分析程序流程,了解
15、模块完成的功能。p(1 1)文件类型分析。p 静态分析的第一步是分析文件类型,即了解编程语言、编译工具及程序是否被某种保护程序处理过,为下一步分析做准备。常见的分析工具有Detect it EasyDetect it Easy、PEiDPEiD、FileInfoFileInfo等。p(2 2)静态反汇编。p 静态反汇编的常用工具包括IDA proIDA pro、HopperHopper、Binary NinjaBinary Ninja等。其中,IDA proIDA pro是逆向工程的必备工具;Binary NinjaBinary Ninja提供了强大的脚本、反汇编功能;HopperHopper
16、特别适用于OS XOS X系统的逆向工程。18静态分析技术p(3 3)反汇编引擎。p 反汇编引擎的作用是把机器码解析成可以汇编的指令。开发反汇编引擎须要对相应架构的汇编指令编码有深入的理解。利用网络开源的工具可以自己开发一款反汇编分析的工具。u 2 2)静态分析工具IDA IDA p IDAIDA功能非常强大。操作者可以通过和IDAIDA的交互,指导IDAIDA更好地进行反汇编。IDAIDA会按照用户的指令找到可疑之处,用户的工作是通知IDAIDA怎么去做,如人工指定编译类型、重新定义变量名、数据结构、数据类型等。p 同时,IDAIDA支持多种类型的文件,从常见的PEPE、ELFELF(Lin
17、uxLinux平台的可执行文件),到嵌入式的ATmegaATmega等。另外,IDAIDA拥有丰富的插件社区,软件功能也得到了进一步的强化,例如:Hex-RayHex-Ray是IDAIDA知名的插件,可以将汇编代码直接翻译成C C语言。19动态分析技术u 动态分析技术中最核心的工具是调试器,可分为用户模式和内核模式两种类型。用户模式调试器是指用来调试用户模式应用程序的调试器,如OllyDbgOllyDbg、WinDbgWinDbg等。内核模式调试器是指能调试操作系统内核的调试器。内核模式调试器处于CPUCPU和操作系统之间,工作于Ring 0Ring 0级别中,如SoftICESoftICE等
18、。u OllyDbgOllyDbg调试器p OllyDbgOllyDbg(简称ODOD)是一款具有可视化界面的用户模式调试器,可以WindowsWindows上运行。ODOD结合了动态调试和静态分析功能,采用GUIGUI界面,容易上手,对异常处理的跟踪相当灵活。ODOD还能识别数千个C C语言和WindowsWindows操作系统的常用函数,并将其注释。ODOD会自动分析函数过程、循环语句、代码中的字符串等。此外,ODOD的执行脚本和插件接口设计经过爱好者的修改、完善、扩充后,其功能变得愈发强大。20逆向分析技术u 主要内容p 函数的识别p 函数的参数p 函数返回值21函数的识别u 程序调用函
19、数的代码保存了一个返回地址,并连同参数一起传给函数了。编译器大多使用callcall和retnretn指令来调用函数和返回调用位置。callcall指令与jmpjmp等跳转指令类似,不同的是,callcall指令会保存返回信息,即将其之后的指令地址压入堆栈顶部,当遇到retnretn指令时就返回这个地址。于是可以定位callcall或retret指令结束的标志来识别函数,callcall指令的操作数就是所调用函数的首地址。u 也有一些情况,函数调用时,直接使用寄存器传递函数地址或动态计算函数地址,例如:p call 4call 4*eax+10heax+10h22函数的识别u 下面来看一个例子
20、:u 上述C C程序编译后的addadd函数和mainmain函数如右图所示,可以看到,调用addadd函数时使用了callcall指令,返回使用了retnretn指令。23函数的参数u 函数通过堆栈、寄存器、全局变量这3 3种方式进行隐含参数传递。如果参数通过堆栈传递,就要定义参数在堆栈中的顺序,并约定函数被调用后,谁来平衡堆栈。如果参数是通过寄存器传递的,就要确定参数存放在哪个寄存器中。u(1 1)利用堆栈传递参数。u 堆栈是一种“后进先出”的存储区,栈顶指针ESPESP指向堆栈中第一个数据项。调用函数时,将参数依次压入栈中,然后调用函数,函数被调用后,从堆栈中获取数据,进行相应操作。函数
21、计算结束以后,则恢复堆栈平衡(有时由调用函数的程序恢复)。24参数入堆栈顺序25u 在参数传递中,有两个很重要的问题:当参数大于一个时,参数压入堆栈的顺序应该如何确定?由谁来平衡堆栈?这些都需要调用约定(Calling ConventionCalling Convention)来确定。不同的编译选项有不同的调用约定,如表所示。u C/C+C/C+和MFCMFC程序默认使用的调用约定是cdeclcdecl。u stdcallstdcall是Win32 APIWin32 API函数采用的调用约定。参数入堆栈顺序27u 为了了解不同类型调用约定的处理方式,可以来看下面的例子。假设调用的函数是test
22、1 test1(par1,par2,par3)(par1,par2,par3),按cdeclcdecl、PASCALPASCAL和stdcallstdcall的调用约定,其汇编代码如表。u 可以清楚地看到,cdeclcdecl类型和stdcallstdcall类型是先把右边参数压入堆栈,而PASCALPASCAL类型则相反。在堆栈平衡上,cdeclcdecl类型是调用者用“add esp,0cadd esp,0c”指令把1212字节参数空间清除,而PASCALPASCAL类型和stdcallstdcall类型则是由子程序负责清除的。堆栈建立过程28p 假设函数中有两个参数,执行函数前堆栈指针的
23、espesp为K K;p 根据stdcallstdcall调用约定,先将参数par2par2压进栈,此时espesp为K-04hK-04h;p 再将参数par1par1压进栈,此时espesp为K-08hK-08h;p 参数进栈结束后,程序开始执行callcall指令,callcall指令把返回地址压入堆栈,这时候espesp为K-0ChK-0Ch;u 因为espesp是堆栈指针,所以一般使用ebpebp来存取堆栈。堆栈建立情况如下。p 使用ebpebp存取参数,但为了在返回时恢复ebpebp的值,调用“push ebppush ebp”指令保存ebpebp的值,此时espesp为K-10hK
24、-10h;堆栈建立过程29p 再执行“mov ebp,espmov ebp,esp”指令,ebpebp被用来在堆栈中寻找调用者压入的参数,这时候ebp+08ebp+08就是参数1 1,ebp+0Cebp+0C就是参数2 2;p“sub esp,8sub esp,8”表示在堆栈中定义局部变量,局部变量1 1和2 2对应的地址分别是ebp-ebp-0404和ebp-08ebp-08。函数结束时,调用“add esp,8add esp,8”指令释放局部变量占用的堆栈。局部变量的范围从它的定义到它定义所在的代码块结束为止,也就是说,当函数调用结束后局部变量便会消失。p 最后调用“ret 8ret 8”
25、指令来平衡堆栈,retret指令后面加一个操作数表示在retret后,把堆栈指针espesp加上操作数,可以完成同样的功能。利用寄存器传递参数u(2 2)利用寄存器传递参数。u 绝大多数编译器提供商都遵循fastcallfastcall规范。fastcallfastcall的特点就是快,因为它是靠寄存器来传递参数的。u 不同编译器实现的_fastcall_fastcall稍有不同,如Microsoft Visual C+Microsoft Visual C+编译器采用fastcallfastcall规范传递参数时,最左边的两个不大于4 4字节(DWORDDWORD)的参数分别放在ecxecx寄
26、存器和edxedx寄存器中。u 当寄存器用完后,就要使用堆栈,其余参数仍然按从右到左的顺序压入堆栈,被调用的函数在返回前清理传送参数的堆栈。浮点值、远指针(指不在段内的指针,它表示的是段地址和偏移)和int64int64类型总是通过堆栈来传递的,其具体过程如下:30利用寄存器传递参数p 假设执行函数前堆栈指针espesp的值为K K;p 根据stdcallstdcall调用约定,先将参数par2par2压进栈,此时espesp为K-04hK-04h;p 再将par1par1压进栈,此时espesp为K-8hK-8h;p 参数进栈结束后,程序开始执行callcall指令,callcall指令把返
27、回地址压入堆栈,此时espesp为K-0ChK-0Ch;p 可以使用espesp来存取参数了。u 另外一款编译器Watcom CWatcom C总是通过寄存器来传递参数的,并严格地为每个参数分配一个寄存器,默认时,第一个参数用eaxeax,第二个参数用edxedx,第三个参数用ebxebx,第四个参数用ecxecx。如果寄存器用完,就会用堆栈来传递参数。Watcom CWatcom C可以由程序员指定任意一个寄存器传递参数,因此,其参数实际上可能通过任何寄存器进行传递。31函数返回值32u 函数被调用执行完后将向调用者返回一个或多个执行结果,称为函数返回值。函数返回值的方式是用returnre
28、turn操作符返回数值,还有通过全局变量返回数值等方式。u 最常用的是returnreturn操作符返回值。一般情况下,被调用函数的返回值放在eaxeax寄存器中返回,如果处理结果超过了eaxeax寄存器的容量,则将处理结果的高3232位放入edxedx寄存器中。u 按传引用调用允许函数修改原始变量的值。在调用某个函数时,当把变量的地址传给函数时,可以在函数中引用运算符修改调用函数中相应内存单元的值。例如,在调用maxmax时,要用两个地址(或两个指向intint型变量的指针)作为参数,函数会将较大的数放到参数a a所在的内存单元地址中返回。代码保护方法33u 主要内容p 序列号保护p 时间限
29、制p 静态分析的抵御序列号保护34u 当用户从网络上下载某个商业软件后,往往有使用时间或功能上的限制。输入一个有效的注册码后,用户才能有更多的使用时间或功能。软件公司常根据一个喜欢的计算机注册码程序(注册机,KeyGenKeyGen)算出一个序列号,用户输入这个序列号,软件就会取消各种限制。u 软件验证序列号就是验证一个函数,这样就可以将序列号表示为:序列号=F(x)=F(x)(1 1)u 其中,x x一般是已知的,可以表示用户名、注册码等。由于验证序列号合法性的代码是在机器上运行的,所以序列号会以明文的形式出现在内存中。可以通过调试工具来分析程序验证码的注册过程,找到序列号。另外,无论函数F
30、 F如何复杂,攻击者都可以把函数F F提取出来,从而编写一个通用的计算注册码程序。所以,上述序列号的验证方法极其脆弱,甚至可以通过修改比较指令的方法通过验证。序列号保护35u 为了避免上述危险,此处做一个F F的逆变换:F F-1-1(序列号)=x )=x (2 2)u 这样正确的序列号就不会直接出现在内存中,而且函数F F也没有直接出现在代码中,所以这种序列号的验证方法比上一种更加安全一些。u 破解序列号的验证方法除了可以采用修改比较指令的方法,还有以下几种方法可以考虑。p(1 1)F F-1-1的实现代码包含在软件中,通过找到F F-1-1解出F F函数的表达式,写出注册机。p(2 2)给
31、定一个用户名,用穷举法找到一个满足式(1 1)的序列号。p(3 3)给定一个序列号,采用式(1 1)变换得出一个用户名,从而得出一个正确的序列号/用户名对。u 由上可见,序列号的复杂性问题归根到底是一个数学问题,想要设计一个难以被破解的算法,软件保护的设计者要有一定的数学基础。时间限制36u 时间限制的程序有两类:p 每次运行时间限制;p 每次运行时间不限,但是有使用时间限制,如1 1周。u 1 1)每次运行时间限制u 这类程序每次都有运行时间限制,如运行10min10min或20min20min就停止,必须重启程序才能正常工作。在WindowsWindows操作系统中,计时器的实现有以下几种
32、选择。p SetTimeSetTime函数p timeSetEventtimeSetEvent函数p GetTickCountGetTickCount函数p timeGetTimetimeGetTime函数时间限制37u 2 2)使用时间限制p 首先在安装软件时取得当前系统日期,并将这个当前系统日期与软件现在运行的日期比较,当差值超出允许使用的天数(如3030天)时就停止软件运行。这种保护方式的机理简单,可以被绕过,例如,软件使用过期之后,可以调整当前系统日期,又可以使软件正常使用。p 如果要使保护措施比较全面,软件至少要保存两个时间,一个是上面所说的安装日期,这个时间可由安装程序在安装时记录
33、;另一个时间是软件最近一次的运行日期,用于防止用户将当前系统日期往前调而设置,软件每次退出时都要将该日期与当前系统日期比较,如果当前系统日期大于该日期,则用当前系统日期替换掉该日期,否则该日期保持不变。同时,每次启动时把该日期读出来与当前系统日期进行比较,如果该日期大于当前系统日期,则说明用户把系统的时间更改了,可以关闭程序。静态分析的抵御38u 1 1)花指令u 如果防御者巧妙地构造代码和数据,在指令流中加入很多垃圾数据,即“花指令”,干扰反汇编器,就会使得反汇编错误。u 对左图插入“花指令”的汇编代码进行反汇编得到右图代码。由于线性扫描式反汇编器是逐行反汇编的,所以代码中的垃圾数据E8hE
34、8h干扰了该反汇编器,使该反汇编器错误地确定了指令的起始位置,导致反汇编的一些跳转指令的跳转位置无效。u 不过随着反汇编技术的发展,这种简单的花指令已经很容易被反汇编器识别了。静态分析的抵御39u 2 2)SMCSMC技术u SMCSMC(Self Modifying CodeSelf Modifying Code)技术,就是一种将可执行文件中的代码和数据进行加密,防止别人使用反汇编工具对程序进行静态分析,只有程序运行时才对代码和数据进行解密,从而使计算机能正常运行程序和访问数据。u 以下的伪代码演示了一种SMCSMC技术的典型应用。静态分析的抵御40u 计算机病毒通常也会采用SMCSMC技术
35、动态修改内存中的可执行代码来达到对代码加密的目的,从而躲过杀毒软件的查杀或迷惑反病毒工作者对代码进行分析。现在,很多加密软件(“壳”程序)为了防止破解者跟踪自己的代码,也采用了动态代码修改技术对自身代码进行保护。u 在自己的软件中使用SMCSMC(代码自修改)技术可以极大地提高软件的安全性,保护私有数据和关键功能代码,对防止软件破解也可以起到很好的作用。u 像C/C+C/C+语言支持指针变量和内存直接访问,可以使用SMCSMC技术。p 可以根据可执行文件的结构,对整个代码段进行动态加/解密;p 或者利用C/C+C/C+语言中函数名称/函数地址的特性,实现对函数整体进行加/解密;p 或者采用在代
36、码中插入特征代码序列,通过查找匹配特征代码序列、定位代码的方式,实现对任意代码片段进行加/解密。静态分析的抵御41u 3 3)信息隐藏u 目前,大多数软件在设计时都采用了人机对话方式。所谓人机对话,是指软件在运行过程中在要由用户选择的地方显示相应的提示信息并等待用户进行按键选择,在执行一段程序之后,显示反映该段程序运行后状态的提示信息(程序是正常运行的,还是出现错误的),或者显示提示用户进行下一步工作的帮助信息。因此,解密者可以根据这些提示信息迅速找到核心代码。基于对安全性的考虑,要对这些敏感信息进行隐藏处理。u 假设有逻辑如下:静态分析的抵御42u 将编译后的程序用W32DasmW32Das
37、m进行反汇编,得到的代码如下。在对该段代码进行破解时,很容易通过在静态反汇编文本中对文本“You see meYou see me”的引用信息(快速定位条件)来判断位置,从而修改条件转移指令。静态分析的抵御43u 使用同样的逻辑,对文字内容进行隐藏,在程序中使用该文字的地方对文字内容进行还原。u 对一些关键信息进行隐藏处理,可以在一定程度上增加静态反编译的难度。例如,对“软件已经过期,请购买”等数据进行隐藏处理,可以有效防止解密者根据这些信息,利用静态返回便快速找到程序判断点而进行破解。信息隐藏的实现思路还有很多,例如,将要隐藏的字符加密存放,在需要时将其解密并显示出来。加壳技术44u 在自然
38、界中,植物用壳保护种子,动物用壳保护身体。在计算机中,壳是用于保护软件不被反编译的程序。“壳”这段代码会先于原始程序运行,并把压缩、加密后的代码还原成原始程序代码,然后再把执行权交还给原始程序代码。软件的壳分为加密壳、压缩壳、伪装壳、多层壳等类型,它们都是为了隐藏程序真正的OEPOEP(入口点),以防止程序被破解。u 1 1)压缩壳u 压缩壳的功能是减小软件的体积,而加密保护不是其重点。目前,常用的压缩壳有UPXUPX、ASPackASPack、PECompactPECompact等。u 2 2)加密壳u 加密壳的功能侧重于保护程序,其中的一些壳仅用于保护程序,另一些壳提供额外的功能,如提供注
39、册机制、使用限制等。ASProtectASProtect、EXECryptorEXECryptor是两种常见的加密壳。脱壳技术45u 1 1)寻找OEPOEP脱壳u OEPOEP(Original Entry PointOriginal Entry Point)代表程序的原始入口点。加壳的实质就是隐藏了OEPOEP(或者用了假的OEPOEP),只要找到程序中真正的OEPOEP,就可以立刻进行脱壳操作。PUSHADPUSHAD(压栈)代表程序的入口点;POPADPOPAD(出栈)代表程序的出口点,与PUSHADPUSHAD相对应。OEPOEP就会在POPADPOPAD附近。u 2 2)ESPES
40、P定律u ESPESP定律就是采用的“堆栈平衡”原理。左右两图分别是加了UPXUPX壳的程序在跳转到OEPOEP前后寄存器的值。脱壳技术46u 可以发现,除了EIPEIP,其他寄存器都一模一样。这是因为,UPXUPX开始时执行PUSHADPUSHAD指令,将所有寄存器入栈;退出时执行POPADPOPAD指令,将所有寄存器出栈。u 以上的方法并不仅适用于UPXUPX这样的压缩壳,对于加密壳同样适用。因为目前所有的PEPE壳,寄存器都是上面的值,到达OEPOEP后,绝大多数程序的第一条语句都是压栈。所以,灵活应用ESPESP定律,可以解决很多壳的保护问题。u 3 3)抓取内存映像u 抓取内存映像(
41、转存)是指将内存指定地址的映像文件读出,写入文件后保存下来。脱壳时,一般在壳到OEPOEP处执行dumpdump命令是正确的。如果主程序运行起来之后才执行dumpdump命令,则很多变量在执行dumpdump命令之前已经被初始化了,从而失去了参考价值。脱壳技术47u 4 4)重建输入表u 在加密壳中,破坏输入表是必要的功能。在脱壳中,对输入表进行处理是一个关键步骤。输入表结构中与实际运行相关的是IATIAT结构,这个结构作用是保存APIAPI的实际地址。当PEPE文件运行时,WindowsWindows装载器首先搜索OriginalFirstThunkOriginalFirstThunk函数。如果OriginalFirstThunkOriginalFirstThunk函数存在,加载程序迭代搜索数组中的每个指针,找到每个IMAGE_IMPORT_BY_NAMEIMAGE_IMPORT_BY_NAME结构所指的输入函数地址,然后加载器用函数的真正入口地址来替代由FirstThunkFirstThunk函数指向IMAGE_THUNK_DATAIMAGE_THUNK_DATA数组内的元素值。谢谢谢谢48