前言:本文章为2018年7月21日看雪安全开发者峰会上,我方阿里安全猎户座实验室研究人员的分享《自动逆向机器人》,根据组织方速记内容进行的整理,与现场ppt对照。格式可能比较杂乱冗长,请多谅解。如有问题,请邮件联系讲师:弗为,fuwei.wfw@alibaba-inc.com
阿里安全猎户座试验室主要关注二进制程序分析与漏洞挖掘、软件供应链安全。第一点,程序分析和漏洞挖掘。讲讲我们有哪些新的内容和新的思考,供大家共同探讨。第二点,软件供应链安全,打一个广告,在我现在讲话的时候,阿里正在举办一个软件供应链比赛,叫“功守道”,现在正在进行第二赛季的第一场分赛,后续希望大家关注并采取到其中。
今天是看雪安全开发者峰会,我们讲的东西又是逆向,先致敬一下这本书,7年前我的第一份工作是到绿盟实习,当时给我安排的认为是丢给我很多远程控制软件,让我分析远程控制软件的行为是什么、逻辑是什么样、私有的协议是怎么实现的等等,这个工作对当时本科生的我来讲是小白,完全没有概念,完全自学,这本书给了我很多启发和新知识,让我觉得这个领域太神奇了,做这个东西非常酷。但时间长了以后,很多同事和同学都了解逆向是怎么回事了,觉得非常枯燥,甚至于能够得到提升的空间非常有限。这里提到一开始入门之后就由此走上一个逆向痛并快乐的过程。
这简单讨论了我们作为一般的安全开发或者安全分析人员做逆向时的几个痛点,第一,这个事做得时间长了,就会沦为一种机械重复的劳动,就像我分析病毒木马一样,时间长了以后在我看来都是一般操作,再去分析的话没有什么新鲜点。第二,一般操作就算了,有时遇到很多比较烦心的事,比如有些软件有反调试机制。第三,比如代码中有混淆,它会很花费我们的时间,让我们觉得很烦心,这也对我们的成就感大打折扣。第四,我们做逆向时,一开始提到加密和解密,如果涉及到Linux,涉及到内核的调试、内核的逆向分析,就需要全新的知识领域、全新的工具,才能够去入门和把它分析起来。这是我们很希望乐享其成的事情,但很遗憾是没有的。之前都做X86分析,这时涉足移动端、设计APP,也许就需要更新自己的知识,但最底层的思想和方法论是没有区别的。这时候如果有放之四海而皆准的方法论,对我们来说是最合理的。第五,不稳定重现,分析漏洞触发动态分析时好弄,但如果它是不稳定重现,它符合一定条件,特别是条件没有清楚的时候,这时我们只能用静态分析去看它的特征。另一方面,今天是看雪开发者峰会,开发者有这样的问题,比如做云服务或者服务端开发,底下做测试时是没有问题的,到线上时出了一个core,分析一下是什么原因,是不是安全问题,这时候我都不知道它怎么回事,无从分析触发原因,这时如果有一个东西告诉我这个东西是在什么场景下出现的,甚至直接把复现的环境给你,你分析起来也会很方便。第六,数据流跟踪 ,比如我们去分析一个程序的bug是不是漏洞,看它是不是恶意人员传入的数据所引起的,如果不是的话就没必要分析了,如果是的话就是一个潜在漏洞。这时是不是一个外部数据传到这里,就需要反复的去调试,比如看一下,开始把输入数据设定为特殊数值,或者逐渐往回调试,这个方法也可以,但纯粹的人工操作很烦心。逆向人员一般会有这么六个头疼的问题。
为了解决这几个问题,我们有几个朴素的想法,很多人工不需要去涉及的工作,能够自动化由机器完成的,交由机器去完成就可以了。这样的机器工具需要怎样的能力?这里有个不恰当的比喻,首先,它能够把程序的流程进行录像,能够把任何可能的变量、任何可能的情况数据都捕捉到,把整个流程记录下来。接下来,可以有播放机,在这里不只是去看记录下来的日志,而是能够反复、稳定、重现我们之前执行过程记录,这样在平台上就可以用我们顺手的工具,比如调试或者熟知的方法去进行分析,来看一下针对稳定重现的过程是怎样的状况,然后分析一下它的触发应用或者现场是怎样的。
另外,在这个重现过程中,我们希望它有一个显微镜的工具,这个情况下我们分析不到程序最终问题在哪,很多时候是我们看得不够底层、不够细。如果我拿到是一个执行过程记录,它是函数执行过程,比如某个参数我们知道,但函数执行过程中是怎样的控制流、是什么样的行为、是否发生预期之外的函数异常分支,我们需要知道。这时在最底层时就希望,比如在X86执行是指令级,从最底层指令级相当于我们处于CPU的角度,任何所需要的数据原则上都能够拿到,所以有这样的东西是最好的,也是一个最基础的要求。
针对这三个逆向工作一般可能会希望拥有的基础能力,我们去研发了这么一套平台,在这里把它叫“分析平台”,或者像演讲的标题一样,叫“自动逆向机器人”。接下来循序渐进讲讲这个平台是怎么实现的,不推销产品和它内部的技术,更多讲一下我们的思考和思路,希望对在座各位有所借鉴。
第一,需要解决一个问题,就是怎样去做刚才提到的准确录制和重放过程。个人是个科幻迷,这是前不久刚结束的《西部世界》的图,这是这季集中所体现出来的情节,主角是一个被测试的机器人,它就是这个程序,最终希望得到的不是机器人有什么变量、意外的行为,而是希望机器人还原出原始真人的行为,这时我们的程序和原始人的行为保持完全的一致性,任何出现不一致的情况最终都可能导致人和程序间的偏差越来越大,导致完全的崩痪。
这里有一个朴素的概念,大家对一个程序进行录制,或者跑两个完全一样的进程,保证后者和前者是一模一样的,从全系统层面都是一样的,应该怎么去做?保证两者所有外部因素都完全一致就可以了。刚才提到的外部因素都包括什么?比如初始状态CPU执行是怎样的,完整内存是怎样的,外设是怎样的,访问的磁盘文件是哪些,这些都保证一致是不是就可以了?显然还不是。包括执行过程中操作系统有一个中断,或者系统做了一个调动,这些情况都可能存在变数,我们的做法是把这个变量全都给确定下来,我们最终的做法是基于全系统的虚拟化,对所有我们能够考虑到的因素都落到平台上去进行记录,但是我们不去记录程序执行过程当中是怎样的,我们只需要考虑变量。执行过程当中它访问了磁盘文件或者有端口的io,或者有网络行为,或者有中断,我们都在重放时恰当的时机给重放的进程,就能够保证重放的进程和开始的进程是完全一样的,在此基础上保证后者和前者完全相同,在此基础上进行测试。
我们再谈谈确定性和变化,有了这样一个所谓确定性的东西,就可以去反复的重放,有了反复的重放,我们就可以保证后者和前者并行化分析,放在多种服务器上分析,或者分段进行分析,这是在确定性的基础上。另外,如果它对程序过程不造成变动,比如这样一个变量不允许程序正常控制流的变化,程序有没有它都不影响程序控制流分支,这时我们就可以去修改数据,比如我们去修改一下硬盘输入口的变量,有了这个输入函数就会对输出产生变化。
第二,在全系统和最细粒度底层进行分析的视角。这块所谓的“最细粒度”就是刚才提到在X86或者任何形式架构下都是指令级。这又引用了《黑客帝国》里的一个场景,当我们看的是一行行代码的时候,实际上如果我们有足够强的抽象能力,最终是能够看出这些代码所反映的是什么样的场景,比如这些代码最终反馈的是当中的几个人的行动和思想。这个不是原创,在学术界有广泛研究,叫“虚拟机自省”,这个工作既然是基于全系统的虚拟化,它相当于把自己摆在了CPU处理器的角度,我看到的也就是一条条指令,这个时候我要想知道这些指令所反馈的是什么样的。比如它在怎样的进程和线程里,把进程和线程信息反映出来,或者网络、内存管理等等,这些都相当于“自省”,即在虚拟机内部意识到全局的信息。我们从指令级把这些还原出来,是学术领域的一般操作,感兴趣的同学可以去看一下。
既然已经站在了CPU角度,我们就是上帝视角、上帝模式,这样可以做到什么?刚才提到有些烦人的东西阻碍我们调试的进程,比如它会反调试,它会检测我当前是不是处于虚拟机,如果是虚拟机的话直接去进行终止,这些是基于虚拟机和真机的不同点。我们既然是已经处在CPU的角度,针对这些东西我们也都可以对应去进行反制,比如对应在我们的平台里在虚拟机层面进行绕过,就可以把它们的机制对应的反制过去。
另外,我们讲到一个“关联搜索”的概念,正常进行程序调试是线性的,A指令执行完了会自动到了B指令,如果想回溯的话,比如现在想看一下某条指令执行时访问的内存地址,它读了这个内存,我想看它之前在哪里写了这个内存地址,正常的思路是重新调试,看看之前有哪些操作,对应这个还需要在外部去考虑程序执行过程中有哪些变量。在我们现在既然是确定性重放,整个程序执行的过程我们都是拿到的,我们甚至只需要去在最终拿到的执行记录里去搜索当前是某个地址,搜索对这个内存的所有操作就可以了,这也是作为逆向人员期望的最朴素需要拥有的能力之一。
接下来讲一些不那么朴素的东西。之前提到一个痛点是想知道程序的数据是从哪来的、最终往哪去,分析或者执行过程中对数据进行了怎样的操作,这个东西在2002年时已经给出对应学术界的解决方案,这个就要做污点分析。我看在座很多是学生,大家以后可能接触到这样的概念。另外和“污点分析”相关的一个概念叫“符号执行”,程序执行过程中某个访问的内存数据定义为一个符号,后续的指令是对这个符号的运算,把这个东西记录跟踪下来,知道最终到了哪里,原始符号到了哪里。这两个概念很朴素,很多学生对这个概念有所了解,有他学生的老师以后也会说,“现在已经2018年了,不要再考虑在这上面发论文了”,为什么?因为这两个概念确实有用,但它们的实用性遭到很多质疑。比如符号执行和污点跟踪需要在指令级上做重的分析,这时它的速度是非常慢的,速度慢了以后就必然引起很多其他问题,比如程序跟原来执行状态是状态不一样的。
我们给出的解法是把这两种东西扬长避短揉在一块,污点分析的长处是跟踪出来数据是怎样的,比如它能够告诉你某个时刻程序访问了一个内存,这个内存是不是被原始输入数据污染到这里了,但它不能告诉你数据是怎样到这里的。它反映的是数据的一种形式,为了让它满足另外一个形式,它要求原始数据是什么样的,这个没有。而符号执行的痛点,业界基本把符号执行和另外一个概念绑定在一起,就是“约束求解”,我既然知道某一个数据怎样到了另外一处,我要想让它在下游某一处的数据满足什么样形式去触发一个漏洞,它要求回过头来原始输入数据形式应该怎样,这就是中间的路径去尝试一下求解。但是这样的求解被证明有运算爆炸的问题,即便到现在的运算能力了,但依然没有办法解决这个问题。
我们把这两个揉在一块,只需要做符号的标定以及符号执行过程中所完成的污点分析,最终能够知道程序输入的时候它的数据是怎样的变量,到结束时或者我们感兴趣时它是这样的变量,经过怎样的运算变换的。我们这里提到还原出来程序或者函数在数学层面的本质,我们没学编程时函数是数学的概念,我们这里把它还原成数学的概念,函数就是输入数据和输出数据的变换和映射关系,我们把分析的问题还原成为数学的问题,这是最理想的情况。
我们刚才提到扬长避短,只需要做这样的跟踪,不再尝试做约束求解这样繁重的运算不可达的解法,只需要后续它满足什么样的形式,有这样的数学概念,我们就能够知道函数输出输出和最终输出数据之间满足怎样的关系,输出数据应该有怎样的可行域在这里,我们有这样的信息就能够做非常多有意思的事情。
最后讲讲性能。这个图是幽灵和漏洞的图,纯粹的安全人员拿到这个,这里有一个安全漏洞,了解一下它的前世今生是怎样的,回过头来知道它是由于分支预测引起来的,就会想为什么Intel他们做这么一个出力不讨好的东西。但计算机架构设计的人,经过很长时间对处理器进行改造和架构优化,比如引入流水线机制,最终实现串行的执行过程,尽可能把它并行起来,让它去消除执行过程当中的停等问题。这从处理器设计的角度,由串行到并行。我们这把这样的架构引入到我们提到的分析平台、分析框架里,因为这个程序分析也类似于程序执行,它也是顺序执行的一个东西。我们要想尝试让它的分析提速的话,为什么不能引入这样一些被证明是有效的方式方法和思路的沉淀。
我们就把这样的一个运算架构引入到分析平台里,我们引入了流水线机制,引入了并行,甚至引入了归并,比如程序执行我们是知道的,如果是一个循环可以改变的话,我们可以一次性的跑过去。由这样一些改进机制,回应刚才提到的问题,学术界和工业界都已经废弃掉了污点分析、符号执行,怎么再把它拿回到工程实践中,让它在2018年还能够使用起来,发挥它们作为理论最应该发挥的最大化价值。引用各种各样的改进措施,在当前阶段我们能够保证录制阶段的性能损失相比于程序正常执行在30倍以下,这对于纯的CPU、纯的指令执行的环境下所评估到的结果。如果它有IO,有磁盘访问,有网络的话,这个实际上会更快。
讲了这么多,最终我们来体会一下这是怎样一个东西,分成四层:1、模拟层。去做全系统的模拟、虚拟化,在虚拟化的基础上去反制。2、引擎层,拿到细粒度分析的能力,并且有分析插件,尝试重放过程中的并行化。3、平台层。我们需要有污点分析能力、符号执行能力,以及有自然语言的转译等等。这是工程实践的,我们不多讲,今天我们不是展示一个产品,更多是分享理念。4、应用层。接下来分享一个有意思的阶段,我们结合基础能力,能够迸发出怎样的应用场景、能力及结果?
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2019-1-11 19:11
被kanxue编辑
,原因: