一、引言
大多数操作系统至少被划分为内核层和应用层两个层次。内核为应用程序提供最基本的系统服务,比如I/O管理、虚拟内存、任务调度等等,这些功能以“系统调用”方式提供给用户。应用程序可以在自己代码中使用系统调用来实现对系统的访问或对内核功能的调用。系统调用是应用层使用内核层功能的唯一接口。
一般说来,入侵的目标是为了获取敏感信息,比如访问、修改某些重要文件,甚至控制主机执行新的远程攻击,这些操作大部分都需要内核层提供的功能,因此不可避免地要使用多个系统调用。通过监视系统调用的执行,可以在很大程度上发现入侵行为,从而对程序进行保护。
二、相关研究
New Mexico大学的Forrest等人从免疫学的角度考虑计算机系统的保护机制。通过大量试验发现:对一个特定的程序来说,其系统调用序列是相当稳定的,这些系统调用序列可以刻画程序的执行。他们使用短序列匹配方法来计算实际系统调用序列与正常序列模式的相似程度。试验表明这种方法是有效的,系统检测出许多类型的攻击。
Wenke Lee使用机器学习方法来检测系统调用。他分别采用两种方式进行试验:第一种方式是利用规则学习算法RIPPER来学习正常系统调用短序列和异常短序列的特点。这种方法实际上属于误用检测,并不能发现新的攻击模式。第二种方式是利用RIPPER产生的规则来进行预测,最后根据预测错误率来判断是否产生入侵。在这种方式中,将一个短序列(即N-Gram)看作是一个待分类的特征向量,前N-1个调用作为该向量的特征,第N个调用作为该向量的类别,然后利用RIPPER建立一系列规则,这些规则表明“给定前N-1系统调用,那么最后一个系统调用应该是多少”。由于这种方式不需要攻击短序列,因此可以用于异常检测。
随后又有许多研究人员使用多种建模方式来分析系统调用短序列,包括SVM、隐马尔可夫模型、神经网络方法、有限状态自动机]等等。
New Mexico大学的Somayaji和Forrest又进一步考察了检测到入侵后如何采取措施来阻断入侵行为。他们将检测模块植入到Linux内核中用来检查每一个系统调用。当发现可疑行为时,根据概率值分别采用延迟或者阻断措施。这种方法的主要缺点是降低了系统的性能。
综上所述,目前这些研究主要集中在采用哪一种模型来更好地表示程序的运行行为。它们的处理对象是系统调用名字,而并不考虑系统调用的其它信息(比如参数和返回值),同时也不区分同一调用在不同位置的调用情况。
显然,不同位置的系统调用应该是不一样的,即使它们具有同一个调用名。为了描述方便,本文使用open@L1表示在L1这个位置上调用open。换句话说,open@L1已经包含了位置信息。那么,open@L1应该与open@L2是有区别的,因为它们调用时的位置不一样。但是如果仅仅采用系统调用名,就没有办法区分它们之间的不同。因此需要有一种方法来识别不同位置的系统调用,这样做有以下几个好处:
■ 可以更加细粒度地区分每一个系统调用,这使得检测模型更加准确;
■ 能够有效地抵抗模仿攻击(Mimicry Attack)。因为攻击者不仅仅需要伪造系统调用名字,还需要伪造调用时的地址,这大大增加了难度。
■ 识别出不同位置的系统调用,有利于以后的参数分析。
本文剩余部分将提出使用系统堆栈中的返回地址链来表示系统调用的发生位置,并得到一个新的安全审计事件L-Call,然后建立基于Markov链的检测模型,最后从信息论和实际检测效果进行分析。
三、一种新的安全审计事件L-Call
当一个应用程序在运行时,它在内存中的映象可以分为3个部分:代码段、数据段和堆栈段。其中堆栈是一种抽象的数据结构,其最大特点是“先进后出”,对堆栈的操作主要有PUSH和POP两种,即将一个元素入栈和出栈。在进行函数调用的时候,堆栈被用来保存参数和返回地址。图1显示了一个堆栈的组成图,其中每一个函数调用对应于一个栈帧(Stack Frame)。当调用一个函数时,首先将参数入栈,然后将该函数的返回地址入栈,接着将上一个栈帧的帧地址入栈,最后是该函数使用的内部变量。
图1 用户堆栈组成示意图
1. 函数的嵌套调用和返回地址链
系统调用由内核执行并完成一定功能,通常封装在库中。下列程序片断在不同地方使用(见图2)。
(责任编辑:adminadmin2008)