第1章 逆向与漏洞分析概述
本章主要介绍逆向与漏洞分析的相关概念,澄清一些词汇含义及表述边界,同时对漏洞的危害性评估、漏洞的影响及发展历程做进一步介绍。学习本章后,相信读者会对逆向和漏洞有进一步的认识,为后续学习打下基础。
1.1 逆向概述
逆向是比较宽泛的概念,本书阐述的逆向特指软件逆向。与逆向相关的研究领域较多,涉及的研究点也比较多,如计算机领域的逆向可以粗略地分为硬件逆向、固件逆向、软件逆向,按照对象分类,仅软件逆向一项就可以分为操作系统逆向、浏览器逆向、协议逆向、可执行程序逆向、 APP逆向等。
*早期的程序是由穿孔纸带组成的,这个时期的程序功能极其简单,只需要将纸带执行一遍即可理解原理,无须软件逆向。后来指令被普及使用,程序员使用底层汇编语言编写程序,汇编语言是机器可以直接理解并执行的代码,所以也无须逆向。再后来,编译型的高级语言出现,程序员使用 C、C++等语言编写软件。相比于汇编语言时代的程序,这个时期的软件规模大、代码复杂度高,软件的形态也出现了源代码和二进制两种形态,并且通常用户拿到的都是二进制程序,无法直接理解程序的编码逻辑,从而产生了软件逆向分析的需求。
1.1.1 基本概念
在计算机软件领域,理解一个对象*直观的做法是理解该对象的输入和输出,软件逆向的理解也是如此。本质上来讲,软件逆向的输入是被逆向的程序,输出是分析的结果。被逆向的程序不一定是完整的可执行程序,可以是程序片段,也可以是常用的库文件,如 DLL等。分析结果由分析目标决定,通常由源代码、图表、文档、密钥等关键数据组成,如图1-1所示。
图1-1软件逆向的输入和输出
在软件领域,软件逆向属于门槛较高的一类。一方面,由于软件由高级语言编写,编译为二进制程序后,很多高级语言的语义信息等都会丢失,再加上编译优化、加密等机制,程序原本的信息消失殆尽,很难从二进制准确地逆向分析出程序的真实结构和处理逻辑。另一方面,由于软件开发成本高,为了防止软件被逆向分析,软件开发团队会采用多种保护方法,包括防止调试、防止分析、防止篡改等。
1.1.2 基本流程
通常软件逆向可分解为,如图1-2所示的流程。对于待分析的目标二进制代码,通过解码/反汇编得到中间语言,在中间语言的基础上进行数据流分析、控制流分析,*后结合一些分析和优化方法,得到逆向结果,如类 C的源代码、图表等数据。
图1-2 软件逆向分析的基本流程图
(1)解码
/反汇编是指将目标二进制代码转化为汇编语言的过程。需要提醒读者注意反汇编与反编译的差异,如图1-3所示。编译和反编译不一定针对汇编语言进行,通常是针对一种设计好的中间语言(中间表示)进行的。在反编译过程中,首先会生成一种类汇编或者汇编代码,中间代码也有很多级别,类汇编或者汇编代码只是一种低级中间语言。反汇编是针对汇编语言进行的,编译和反编译不一定针对汇编语言进行。
(2)中间语言翻译是将汇编程序表示为一种中间表示形式,如代码模型中的三地址码、静态单赋值等,都属于中间表示。
(3)数据流分析、控制流分析都属于逆向分析中的特定环节,其中数据流分析用于获取数据之间的传播关系,可以用来提取数据结构;控制流分析是分析程序的控制传递关系,如函数调用,可以用来提取程序的执行逻辑。
1.1.3 作用
软件逆向是用逆向的思维理解软件的功能和设计,逆向分析的思想在计算机甚至现代技术出现以前就已经存在,逆向分析不只应用在计算机领域,在科学研究和工业制造中的使用同样广泛。
软件逆向分析主要有以下几个方面的作用。
1.软件破解
一些软件对使用权限进行了限制,如试用30天,试用版中特定功能无法启用等。一些黑客使用软件逆向的方法对这些限制进行破解,达到获得使用权限的目的。常用的软件爆破、算法理解、注册机编写等都属于软件破解的一部分。
2.恶意软件分析
恶意软件包括木马、病毒等类型,通常以二进制的形式传播。常见的杀毒软件使用恶意代码特征识别的方式进行查杀。提取恶意代码的特征依赖于逆向分析过程,如恶意代码的字符串特征、Android重打包代码识别等,都可以通过软件逆向的方法识别。
3.软件相似性分析
软件工程思想的普及使代码复用的频率、范围显著增大。与此同时带来了软件相似性方面的研究,因为代码复用的同时,被复用的代码中存在的漏洞、后门等也都随之传播,需要通过逆向分析的方法明确代码复用的程度,以及哪些代码被复用了。软件相似性分析可用于版权保护、代码成分分析等。
4.漏洞分析
软件漏洞分析通常包括漏洞挖掘、分析、利用、防护四个环节,其中每个环节均需要软件逆向分析的结果来辅助进行。挖掘过程需要软件逆向分析的结果提高漏洞挖掘的针对性,漏洞分析环节需要软件逆向分析过程来识别漏洞成因,漏洞利用环节需要软件逆向分析来辅助定位漏洞利用点,漏洞防护环节需要使用软件逆向分析明确漏洞的修复位置。
1.2 漏洞的概念
《辞海》中对“漏洞”一词有两个解释:①会漏出东西的缝隙、小孔;②比喻破绽、不周密的地方。第一个解释主要描述具体的物体存在漏洞的现象,第二个解释更加抽象一些,可以描述一个概念、一个想法或一个策略等。“漏洞”前面加上“软件”二字,构成“软件漏洞”,字面意思可以理解为软件破绽,即软件在编程实现上存在的不周密现象。而这些破绽、不周密存在以下几种可能性:①被厂商自己提前发现,未向公众公开,私下修复了;②被厂商自己发现,但是软件已经推向市场,只能向公众公开,并发布修复版本,供用户替换;③被厂商自己发现,未向公众公开,也未修复;④被别人发现,向公众公开,迫使厂商修复;⑤被别人发现,未向公众公开,也未修复;⑥还未被发现。第①、②两种情况属于正常的软件厂商行为。对于第④种情况,大多数厂商是支持的,如微软的漏洞奖励计划,有些漏洞的奖金高达10万美元。对于第③和第⑤两种情况,漏洞的善、恶两面性就出现了,需要根据被发现的漏洞的危害性以及使用者的目的来考虑,如攻击者利用 Windows“永恒之蓝”漏洞制造的 WannaCry勒索病毒,需要用户支付高额赎金才能解锁被加密的文件,这是漏洞“恶”的一面。当然,漏洞也有它“善”的一面,如漏洞的发现促使厂商研究各种安全机制,促进了技术的进步,正如高尔基所说:“人生的意义就在于人的自我完善。”这或许是对漏洞*积极的阐述了。
下面介绍一下漏洞的基本概念。
1.2.1 漏洞的基本定义
漏洞(Vulnerability)又叫脆弱性,通俗来讲,漏洞是指计算机系统中存在的缺陷,或者是系统使用过程中产生的问题。关于漏洞的准确定义问题,学术界、产业界、国际组织及机构在不同历史阶段、从不同角度都给出过不同的定义,但至今尚未形成广泛共识。曾经有过基于访问控制的定义、基于状态迁移的定义、基于安全策略违背的定义,以及基于脆弱点或者可被利用弱点的定义等。
其中一个易于理解、较为广泛接受的说法为:漏洞是指软件系统或者信息产品在设计、实现、配置、运行等过程中,操作实体有意或无意产生的缺陷、瑕疵或错误,它们以不同形式存在于信息系统的各个层次和环节中,且随着信息系统的变化而改变。漏洞一旦被恶意主体利用,就会对信息系统的安全造成损害,从而影响构建于信息系统之上的正常服务的运行,危害信息系统及信息的安全属性。
如果从计算的角度去理解漏洞,也许可以更本质化、更一般化地理解漏洞的含义。计算被认为是基于给定的基本规则进行演化的过程。计算其实是在探索事物之间的等价关系,或者说同一性,而计算机则是一种利用电子学原理,根据一系列指令对数据进行处理的工具。从这种意义上来看,可以将漏洞理解为计算实现的某种“瑕疵”,这种“瑕疵”既可以表现为逻辑意义上的暗功能,又可以以具体实现中的特定错误形式呈现。
1.漏洞的载体
目前几乎所有与软件、固件、硬件相关的系统、设备均存在漏洞。对软件而言,又可以分为有源代码的源代码漏洞和无源代码的二进制漏洞;对固件而言,目前使用固件的手机、打印机、车载系统等均存在漏洞;对硬件而言,理论上硬件由门电路组成,可以通过逻辑门穷举的方式遍历所有输入,在出厂前做到故障排查,但是一方面这种排查需要穷举的空间过大,另一方面许多硬件的漏洞是通过侧信道等方式表现的,如2018年引起广泛关注的 Intel CPU“幽灵”漏洞,攻击者利用侧信道泄露对内存进行探测,进而枚举内存中的数据。
2.漏洞的几种描述
在漏洞领域有几个词汇用于描述漏洞,但其实是不够准确的。
(1)异常(Crash),异常不是漏洞,而是一种现象,可能由漏洞导致,也可能由别的原因导致,如临时断电,由于服务器备用电源启动不及时,系统出现异常。
(2)缺陷(Bug),我们在使用 IDE环境(如 Visual Studio)编写程序的时候,点击编译后,如果程序有错误,通常会提示有多少“ Error”和“Warning”,这些是缺陷的一种。缺陷比漏洞要更加宽泛,确切地说,能够被利用的缺陷才能称为漏洞。
(3)脆弱性(Vulnerability),这个词是与漏洞在表述上*为接近的一个词,通常二者是可以混用的。
(4)挖掘/发掘/检测,在描述漏洞挖掘过程时,有时出于词汇敏感的原因,在一些场景下,通常使用发掘或者检测这些描述,但是表达的意思都是漏洞挖掘这个过程。
1.2.2 漏洞的特征
从时间维度上看,漏洞的生命周期包含产生、发现、修复三个阶段,不同阶段的漏洞具有不同的特征。
1)漏洞产生的不可避免统计表明,程序员平均每写1000行代码,就会有1个缺陷,一个大型的应用程序,代码行数动辄数十万行,甚至过亿行,存在漏洞是不可避免的。
从一方面来说,不恰当的操作会导致漏洞。例如,程序员在编写代码时的疏忽、运维人员设置安全配置时的不当操作,以及用户设置的口令过于简单等。因此,从理论上讲,所有的信息系统或设备都会存在设计、实现或者配置上的漏洞。
从另一方面来说,认知的有限性也会导致漏洞。人类对事物的认知需要一个过程,在特定阶段我们的认知能力是有限的,当下认定为正确的、合理的,在未来某个阶段会发现是错误的、不合理的,例如,“千禧年危机”就是人的有限认知而导致的漏洞。20世纪60年代,当时的计算机存储器的成本很高,如果用四位数字表示年份,要占用更多的存储空间,抬高成本。因此,计算机系统的编程人员采用两位十进制数来表示,例如,1980年用80表示,1999年用99表示,1980年出生的人,在1999年就是19岁(99.80),没有问题。但是到了20世纪90年代末,大家突然意识到,2000年用00表示,1980年出生的人,在2000年时是.80岁(00.80)。这会导致某些程序在计算时得不到正确的结果,无疑会引发各种系统功能的紊乱。
2)漏洞发现的不可预知关于漏洞的不可预知问题,可以概括为4W问题,即人们不知道什么时候(When)、会在什么地方(Where)、由谁(Who)、发现什么样(What)的漏洞。首先,漏洞总是存在于具体的环境或者条件中,对组成信息系统的软、硬件设备而言,不同的软、硬件设备中都可能存在不同的安全漏洞。
其次,已有的检测方法无法检测到未知类型的漏洞。漏洞的类型从*初的简单口令问题,发展到缓冲区溢出、结构化查询语言注入、跨站脚本和竞态漏洞等,已有的检测方法均是在已知漏洞的先验知识下进行检测,无法检测到未知类型的漏洞。
*后,目前人类无法预测新的漏洞类型,也做不到对特定类型漏洞的穷举。这也是漏洞发现存在不可预知性的重要因素。
3)漏洞修复的不可终止
一方面,软件厂商在漏洞修复时普遍采用打补丁的方式,如微软的“永恒之蓝”,即在原软件中添加或者覆盖部分代码
展开