答应arhat组长的任务拖后了这么长时间,表示歉意.
今天是我的生日,赶紧把刚出炉的东东放出来.顺便祝自己生日快乐,多发文章,多得精华. 隐蔽调试绕过软件壳保护技术
摘要
软件加壳技术的发展越来越对众多的逆向工程人员和软件安全分析人员构成挑战.各种保护方式,如:打包工具,混淆器,虚拟机和调试探测器一类的技术应用的很是普遍.现在,我们用一种新的方法来对付它们.在这里我将向大家展示隐蔽调试的平台,它的名字是Saffron(番红花).Saffron是基于动态探测技术之上的一种新近开发的一款页错误辅助调试器.结合这两种技术我们可以很有效的从大多数受壳保护的软件系统中把壳移除出去.
1.引言
对逆向工程师和技术员来说,现在的软件程序分析起来是比较困难的.其中有的方法被用来预防标准的反汇编技术.这些方法在反盗版和保护知识产权领域都是首先被使用的,但是近来发现,这些技术也被用在恶意的软件中,用来阻止和防御我们的分析.这些技术手段经常被人们称为混淆.压缩或壳.
软件保护有很多方法,首先执行的是简单的调试器检测.在WINDOWS系统下通过检测当前的进程执行块(PEB)是否存在调试位来判断.在程序仍然保持调试功能的时候,我们有方法来切换这个位.不幸的是反调试技术校正了象这种扫描 INT 3的方法, 寻找一个调试指令调用的出现可以有效的阻止并检测到调试器的访问.为了确定一个非标准的系统设备.硬件调试检测和内存调试方法也同样适用.
我们常常会构建一个虚拟机环境来检测设备应用状态.这样做有利于在一个独立的环境下比原始硬件更精确的控制并隔离运行中的代码.这里也有个别的软件壳技术可以用来检测虚拟机的存在.所有当前的虚拟机都可以列出用来改变程序运行的可识别的特征.
移位译码构架让分析二进制混淆格式变的隐伏和困难许多.部分地译码一个正在运行的程序,执行那个代码,然后在执行一个新的部分前重复编码.上面这些给我们进行译码,反汇编和调试制造了最大的困难.
软件壳因为malware的应用变得紧张起来,合法的软件通过在未来的某些时候分析并修改自身这样的技术来保护自己.Windows Server 2003和Windows Vista集成一个子系统用来保护他们内部的处理过程.另外的一些软件用这个子系统来实现减少自身分配空间和阻止进行逆向工程.这些都给安全分析人员出了一个最大的难题,了解并评估应用程序风险以及检测并防御反malware威胁.
这篇论文将会给大家展示一般性的去包技术和清除二进制执行时混淆技术.首先给大家展示动态译码技术,用来找到并进而洞察后期译码(the post-decode),非移位译码构架(non shifting-decode-frame)软件壳技术.第二个技术就是修改操作系统的页错误处理程序来达到监视代码执行的目的.必须确保这个页面错误处理程序在整个进程得到完整的调用,下面这些与页面标识技术类似,OllyBonE[6],PaX[7],Shadow Walker[8].通过两种技术我们还可以运用单步调试技术进行有效的反普通压缩和反通过malware(盐铁矿)软件压缩.
这篇论文依下列各项组织.
1.深入探讨检测调试器的方法和技术并给出一个通用的绕过调试器的方法.
2.展示虚拟机检测方法.
3.给大家带来隐藏调试构架方面的需求.
4.论述并解答什么是动态译码.
5.详细说明,OS辅助方法,页错误居中处理.
6.着重展示malware(盐铁矿)分析技术及应用.
7.最后主要发布检测机制,以及将来进一步防止被发现并保持对调试系统的访问.
1.1相关工作
以前动态探测被用来监视程序进程.对SPIKE框架[9]建议采用自动跟踪程序执行的方法,以提取我们需要的相关信息.Cifuentes,Waddington,和Van Emmerik三人建议运行时调试malware(盐铁矿)以便于迅速的了解程序的执行步骤.[10]Ma,Dunagon,et.al三人使用PIN系统去跟踪恶意代码注入到一个脆弱的服务中.[11]TTanal
yze工具[12]也用来说明重要的分析状态.
内存管理器进程引发若干破坏机会.这个系统的破坏者包括rootkit,Shadow Walker(隐形者)用这种方法来绕过内存参考,rootkit的发现并预防.Shadow Walker隐形者隐藏操作系统给rootkit分配的内存页或者隐藏非隐形的执行体.怎么做到呢?.它通过标记自身全部的非分页内存和标记dirty-bit.因为这个原因,每个内存访问产生的页面错误处理调用都被映射到rootkit内存页里面.利用其破坏内存页面调度系统,当它接收到允许他们通过的页面调度请求时.rootkit分配非rootkit访问内存中的无用信息.[8]
在没有硬件支持的Linux操作系统下,PaX系统用页面错误机制来拒绝执行.Pax用处理程序标志来标记全部的内存页.就是因为这个的原因,硬件页面错误处理程序可以通过调用页错误中断处理程序来调节发生的错误.错误产生后,PaX简单的确认一下企图执行的动作是否是上一个执行操作的结果.如果结果是一个要执行的操作,那么一个页错误就产生了,当然这个程序肯定会被终止执行[7].
OllyBonE使用一个相似的techinique像PaX和Shadow Walker一样执行break-on-execute.利用这个接口我们可以和OllyDbg一起使中断在内存区域.这帮我们逆向工程人员确认原始的入口点在哪一个可执行的区域,然后在这个点上发起一个调试.OllyBonE最后还是使用页表中的管理程序位.
2.软件壳技术
对于逆向工程人员来说知道怎么样去中止软件壳的方法是非常有用的.逆向工程人员有一个普通的置位工具用来从二进制数据中找到对他们有用的信息.软件开发者保护他的代码的目的是阻止逆向工程人员分析出他的工作流程.只有对这两个相互作用的方面有深刻的认识,我们的分析技术才会有显著的提高.这一段将要论述打包,虚拟机检测,调试器检测,最后我们会分析下移位译码问题.
2.1打包/加密
打包是一种一个可执行文件迷惑另一个可执行文件或者缩小自身大小的方法.打包机是一类有特色的工具,它通过被打包者附加一个小型的译码器,或在询问中分离二进制混淆代码.一但译码或者解包进程完成,控制权从译码器移交到程序的目标代码处.这个时候就正式成为一个正常的可执行文件了. 打包机对malwar
e(盐铁矿)分析员来说有点困难.首先,当前的方法一般可以得到需求,分析员通过调试器手动单步执行来找到真正的可执行代码,或者通过深入的分析汇编占位解码程序并写出一个解码器.其次,在第2.3节我们会描述很多技术来阻挠解包进程.
2.2虚拟机检测
对malware(盐铁矿)程序设计者来说,检测是否存在一个虚拟机是一项很重要的工作,对通过malware(盐铁矿)保护的代码来说也是很有用的.一个恶意的程序不会想运行在一个虚拟机上.malware(盐铁矿)设计者典型的目标应该是,每一天都在运行任务(比如说:邮件,联机银行等)的单机系统,如果一个虚拟机被检测出来,那么它很可能在分析我们的malware(盐铁矿)系统.我们不应该忘记在X86体系结构下有固定的瑕点,在硬件层级上不支持虚拟化.某些指令被知道是有问题的.[13]有一些普通的方法被用来检测它们.
通篇论文贯串介绍高级虚拟机检测方法,在ring-0,或者内核执行空间,和用户特权级别空间一个单一的指令下必须产生同样的结果.在X86体系结构中是由SLDT,SIDT,和SGDT指令组成.malware(盐铁矿)设计者仅仅简单的执行这些指令,然后给出结果.在虚拟机下执行这些指令和在实际的硬件环境下执行这些指令产生的结果是不一样的.[2]有一种方法能有效的防止在虚拟机环境下检测并禁止加速模式.(在VMWare环境下)这种性能经常会被用来逃避检测.非常不幸的是,在非加速模式这些方法仍然被用来处理识别一个虚拟机的存在.
2.3检测调试器
调试正在执行中的程序是众多强有力技术中的一种,对每个逆向分析人员迅速的理解程序流程有非常明显的作用.用这种方法,我们可以明白一个执行体在真实运行时是什么样的状态,以及被用来监视系统调用.不幸的是发现进程被调试并被检测出来是非常trival的.这一部分将会详细论述进程调试.
2.3.1 Windows调试API
Windows操作系统提供一组健壮的API用来开发自定义的应用程序调试器.在机器指令级别允许操作系统单步跟踪一个正在运行的程序并用一个回调的方法来实现.OllyDbg,WinDbg和Visual Studio debuggers都是用的这些API.这些API允许正在运行的程序接收基于指令级级别的事件.检测并发现这种类型的调试器是在简单不过的了,只要查看这个正在运行的程序的进程执行块PEB就可以了.这个PEB是一个数据结构,其中包涵这个正在运行的程序在操作系统内部的相关信息.在这个数据结构内有一个字段对我们是非常有用的,就是BeingDebugged(正在被调试)字段.如果这个标志位被置位了,标志着一个调试器附加到这个进程上了.幸运的是对逆向分析人员来说这个位可以被反复切换但不会失去调试能力.
2.3.2 检测INT3指令
用INT3指令同样可以实现一个调试器的功能,其往往做为一个断点异常.它个指令在操作系统内部做为一个CPU陷阱出现.然后这个陷阱通过操作系统传到正在运行的程序中. 这为开发者设置断点提供了另外一种方法.甚至,程序设计师们几乎不用直接设置INT3指令到他们的程序中去,如果这个指令被观测到,那么有一个进程是监视进程.Malware(盐铁矿)程序设计者实现了多种方法来检测这个INT3指令的出现,当发现它的时候改变程序的执行.一个简单的CRC检查或者MD5SUM都可以检测并确认我们的代码执行是否被INT3改变.
2.3.3 未经处理的结构异常的处理
结构异常处理(SEH)展开对逆向分析人员产生了另外一个有趣的问题.SEH是正在运行的应用程序捕捉异常的一种方法.当一个特殊的程序产生一个运行时错误的时候SEH被展开.正常情况下一个SEH达至执行条件会被传送到程序开发者定义好了的处理代码部分,或者象对待一个系统未处理的异常并执行停机操作.Malware(盐铁矿)软件设计者利用这个做为一种方法设计一种脱壳器.Malware(盐铁矿)软件设计者插入被脱程序中一个SEH和他们自己定义的处理程序.这个处理程序被做为有脱壳指令的具有代表性的装置. SEH结构包涵一个指向前一个SEH的结构和一个指向当前异常处理程序的结构.当触发一个SEH异常的时候,Malware(盐铁矿)程序栈解开直到一个合适的处理程序被找到后.由于调试接口的特性,调试器会将自己的SEH处理程序插入到这个异常栈中.当这个调试程序运行的时候,将会引发一个异常.由于这个原因,调试器栈会捕捉和处理SEH插入,调试器做这个动作的时候可能会和防止Malware(盐铁矿)解包自身的处理过程发生碰撞.调试器是没有办法来识别是程序自己的错误产生了一个异常还是调试程序产生的异常,这些都会阻挠我们的脱壳. 象OllyDbg那样的调试器,允许逆向分析人员在调试器里调试每一个异常处理.或者手动调试它插入到被调试应用软件的栈中.如果有很多SEH被使用,那么这将是非常累人的和乏味的过程.
2.3.4 跳到中指令
有代表性的调试器将会尝试解释一个正在运行中执行体的机器代码,并输出让人易于阅读和理解的代码.其会给没有固定大小的Intel指令置位,这就创造了无数机会用来迷惑运行时执行体.有代表性的技巧是这样的,用一个长指令和一个0X90的值做为一个参数.这个最后的参数,被编译成它自己是空的,或者没有任何操作指令.这将会引起CPU去运行下一个指令并继续执行.
2.3.5 移位译码设计
移位译码设计方法用在被解压的可执行体部分,先执行,然后重新编码.这种方法用来防止执行后进行静态分析.也用来预防调试器单步运行可执行体到原始入口的位置.然后可以转储整个可执行体.它还可以被用在有重要影响的程序,分析和产生一系列问题来阻碍调试器的快速分析.正因为如此对逆向分析人员来说唯一可用的选项就是利用解码机制手动解码可执行体,或者用动态调试的方法提取出来对他们有用的相关信息.本章的其余章节将会把焦点放在对程序执行的动态监控上.
3.调试要求
软件壳保护技术都存在一个单一的普遍的失效点,那就是处理器必须执行真正的机器代码.只要重现一次,分析人员就能够观测到并明白代码的函数性和功能.当它们象RISE[16]方法那样的时候得添加更多的困难,使分析员观测到和明白,困惑代码执行在常规的大部分加壳技术中应用,并屈服于这样的基本形势.
隐蔽调试是一种如果没有调试器指示现在状态也能单步并分析一个正在运行的程序的方法.需要一个隐蔽调试系统如下:首先,它会处理一个wide-variety执行体,这篇论文限制它的上下文的作用域,以利于便携式可执行PE文件在Windows操作系统下运行.malware精选大多数PE文件的威胁并在Windows平台上呈现出来.给出我们一种不同的PE文件保护方法,如果没有困难的话,一个系统需要处理这些中的每一个.
效率做为一个主要的关注点.给malware充足的捐款是非常重要的,可以在不需要过多开销的情况下迅速得到相关的信息.分析人员的时间是非常宝贵的,因此速度和自动化是非常必要的.
4.动态探测
动态探测(DI)被用来跟踪一个调试单元的正确执行.在计算机体系结构分析以及程序分析方面有很多应用.DI工具象Valgrind和PIN都提供对一个程序执行特证的了解.对malware分析来说PIN非常的有用.
Inter PIN是一个高度可配置的跟踪程序执行时的软件工具.PIN接口有机器代码分析刚开始时注册过的一系列的回调程序.每个单步的指令都可能改变一个程序的执行方向.读和写内存地址同样会被监视.我们所列举的工具都轻松实现了监视一个运行中的程序这样的功能,包括改变程序执行方向也是它们必备的能力.
4.1 压缩可执行程序
Saffron用PIN的动态运行中跟踪来监视程序执行流程.返还回来的结果是非常令人吃惊的.首先用这种方法可以找到一个被压缩的可执行程序的原始入口点.其次对用多种方法来保护和压缩一个可执行程序.通过跟踪内存读和写,我们可以观察到在执行期间内存的哪一部分被程序修改.而且我们可以通过这些信息来监视这块被写的内存区域的执行状态.这是需要我们高度关注的潜在原始入口点.
4.2 结果
象ASPack,UPX,FSG,PeCompact和TeLock这样的压缩工具我们都可以考虑用来进行初始化设置测试.其中任何一个都能压缩或者混淆一段恶意的代码.除了TeLock外都特别指明利用了PIN模式并能识别压缩执行体的原始入口点.得出的结果我们可以用手工逆向这个执行体来核对.TeLock这个工具当被用来监视程序运行的时候,会用到PIN探测库,这是非常不稳定的.TeLock在这个执行体放弃原始入口点控制权前用了一个内存CRC校验.
动态探测还会产生其它一些问题,首先速度会非常慢,特别是在指令级别进行监视.压缩工具用SEH来解包的时候,也会呈现出和用标准调试器(象PIN)那样,并产生同样的问题.因此着眼于从内核模式控制执行,移出用户空间是非常必要的.
5.页面错误处理程序调试
页面错误处理程序是现代操作系统非常重要的一部分.它最初是用来协助CPU的内存管理单元来进行页面调度,写磁盘,虚拟内存空间目录管理的.它也执行象内核模式对用户模式内存区域访问实施许可这样的任务.它在操作系统内有专用的空间,大体上,它控制一个系统上运行的全部应用程序的内存访问,能被用来控制一个可执行程序的执行过程.成批的这类工作被Joe Stewart写的OllyBonE工具采用并改进.本节我们将论述这种新方法是怎么实现的,还有被称为Saffron的是哪一个?.
5.1 X86内存管理
X86体系结构结合了程序分段和页面调度机制来实现虚拟内存.[15]虚拟内存仅仅分配一个保护地址空间,让进程运行在计算机系统上.它允许一个非邻近的物理内存地址空间被做为一个邻近的空间(虚拟内存)使用.对于这个内存地址应用程序要进行引用,必需经过一个转换成主存储器上的物理地址空间的过程.对这个转换的缓存来说,(TLB)首先负责执行这次检查.TLB应用的目的就是提高速度,在硬件的协助下更快的查找到进程在内存上的一段
5.2 TLB过程
在内核中.TLB是一个用来转换内存块的联合数组.当一个虚拟地址在程序中被首先引用后,TLB将会注意它是不是在自己的缓存中存在.如果可以不费周折的找到一块物理地址,这种被归类为"(命中)hit".如果找不到的话被归类为"miss".在这种miss情况下,对内存管理架构来说通常有两种方法.第一种通过TLB找到段表检查是否有一个内存页存在.如果在内存中找到了一个,返回并存入到TLB中以备将来检查.如果没有找到这个页.或者PTE中页表入口任何一个访问标志都被设为页错误.内存错误处理程序会被调用.直到操作系统对内存地址执行一个软件检查.如果OS找不到这个内存页的索引,那么一个异常就会出现,OS必须马上处理它[15].
下表:典型的TLB/OS交互
每个进程自己设置被使用的段.在一个进程上下文切换中,TLB转储并清除自己的缓存,同时启动新进程内存的转换.实际上特有的内存被一个叫页表入口(PTE)的数据结构控制.这个PTE包涵不同的详细的内存段权限控制标志.
当TLB开始把一个进程的虚拟地址转换成一个物理地址,首先会检查自己本地的缓存.如果当前缓存不能够装下这个虚拟地址,它会开始手动遍历页表来在硬件中找到这个虚拟地址.一但这个页表被找到,它会检查一个字段序列确认权限是正确的.这步完成后,返回内存地址给指令引用.上图说明了这个过程.
5.3 Saffron的页错误实现
在一个执行体上实现调试,这个程序必须允许执行步进,暂停或转向执行操作.此外它们应该拥有足够的权限来知道内存错误具体发生在哪个位置.Shadow Walker用它们自己拥有的非分页内存.PaX在低级别访问在Linux下能控制内存管理机制的OS原语.OllyBonE依赖于逆向分析人员标记自己发现的发生内存错误的地方.非常不幸的是,在执行中我们是预知不道这类情况的.Saffron由两个特别的原素组成.首先它用一个混合内存检查和标记机制来识别内存区域.其次当页错误错误发生时一个修改版的页错误陷阱处理程序被调用.
内存映像算法是一个brute-force,在整个进程的虚拟内存空间.所有的内存地址都被基于页边界进行枚举.这些地址被管理程序置二进制位并进行标记. 对优化这个标记过程来说页标志边界是非常有用的.
在Shadow Walker 和 OllyBonE 中执行页错误检测的代码是非常相似的.这类方法的改进是从一个错误操作移到一个记录操作.另一个改进是在被记录后允许地址继续执行一次用来代替触发调试中断.
5.4 压缩执行的结果
在4.1节中可以实现同样的方法.不能修改进程地址空间,就没有办法来检测可执行体的上下文环境.进而监视运行中的执行体,并允许产生一个可执行的粗粒度的视图.我们可预期允许一个被压缩的执行体在这里被检测出来原始入口点.
这种方法也有一些问题,自从页面对齐在一个4K的边界上后,在Saffron中当一块内存读或写的时候将会被多次忽视.甚至用这种方法仍然能非常有效的跟踪一个执行体,并找出原始入口点在哪里被定位或者得到一个有用的,剥离混淆代码的进程内存空间信息Dump.
6. 结束语和将来的工作
动态探测和破坏页错误处理程序对跟踪不明的或者进行混淆过的执行体是非常有用的.动态译码给于执行体很多控制权,当跟踪正在运行的执行体时,它应该会成为首先尝试的方法.如果没一种特别历害并且精巧的方法能破坏它,页错误执行机制通常被用来跟踪内存使用轨迹.
我们有很多工作要做,比如发现如何让页错误跟踪机制更坚固的方法.因为要在跟踪运行中的执行体上使用页错误机制,Intel架构把数据TLB和指令TLB拆分开来.其中一个使用这种方法的应用领域是移位架构译码解包方法.
7. 感谢
我们要对帮助我们清除页错误处理算法中无用信息的Lorie Liebrock说声谢谢.特别要谢谢Bill Weiss,他帮助我们调试Windows内核并给于很多独创性的建议.也谢谢#vax 和 Jerry DeLapp 的帮助和支持.
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: