转载:http://www.cnblogs.com/hfww/archive/2012/05/11/2495245.html
常见的网络攻击方式是在命令中带有特定的攻击代码。Snort的基本原理 是把经过处理的数据包和定义好的规则相匹配,来判断是否有入侵发生。在Snort 检测引擎中,进行的处理主要是:在经过处理的数据包(“文本”)中搜索是否存 在入侵特征(“模式”),此过程即为模式匹配。本论文随后的叙述中,将一直 采用“文本”和“模式”这两个名词来描述匹配过程中涉及到的不同文本。模式 匹配算法是Snort的核心运算模块,匹配算法的性能直接决定Snort的整体性能。
Snort 2.9.1.1中的模式匹配有两种:单一模式匹配和多模式快速匹配。
2.1 单模式匹配
单模式匹配即常用的单一字符串查找,Snort中其实现在mstring.c里,其接口 源码在mstring.h里,其中一个典型的接口如下:
[language={c}] int mSearch(const char *, int, const char *, int, int *, int *);
这里的实现主要是为了解决匹配字符串在标准C库不提供的问题,其实现的算法 是基于Boyer-Moore算法(简称BM算法)。下面本文分析一下BM算法。
2.1.1 Boyer-Moore算法原理
BM算法由R.S.Boyer和J.S.Moore在1977年设计实现的一个经典单关键 词匹配算法, 被认为是常规应用中效率最高的字符串严格匹配(Exact String Matching)算法,其时间复杂度可以达到亚线性,而 且对于没有规律的模式匹配串,其最差情况下也只需要3n次比较。
BM算法从右到左扫描模式串中的字符;当遇到不匹配的字符时,它通过优先计算 的Bad-character跳转表以及Good-suffix跳转表寻找最大的跳转长度。其算法表 示如下:
计算bad-character跳转表 |
计算good-suffix跳转表 |
n ← |text| |
n ← |pattern| |
j ← 0 |
while j ≤ n−m: |
从右到左匹配pattern与text的子串text[j:j + m] |
若匹配成功: |
报告一次成功的匹配 |
令j ← j + good−suffix−table[0] |
否则: |
根据匹配失败的位置i得到good−suffix跳转长度; |
根据匹配失败的位置i和导致匹配失败的字符c得到bad−character |
跳转的长度 |
比较上面两个长度,选择较大的一个,令其为k |
令j ← j + k |
2.1.2 Bad-character跳转
当匹配过程中遇到了不匹配字符时,可以向后移动窗口使文本串中不匹配的字符 a与模式串中字符a最后出现的位置对齐。
如在以下情况,text与pattern不匹配:
若pattern中存在a,与text对齐,得到:
若a在text中不存在,则移动窗口到a出现位置之后:
这加快了模式串不匹配时向后移动的速度,而不是常规方法那样一个一个向后移 动。
2.1.3 Good-suffix跳转
假设通过自右向左的匹配,已经得到了pattern[i:m] = text[j + i:j + m] = u, 且pattern[i−1] ≠ text[j + i−1],那么可能分两种情况:
情况一,pattern中,在i之前还存在子串u,并且子串u之前的 字符不等于pattern[i−1]:
这种情况下我们对齐i之前的子串u。
情况二,pattern中,i之前不存在子串u,但pattern的一个 前缀v与u的一个后缀相匹配:
这种情况下,我们可以移动窗口使v对齐u匹配的后缀。
2.1.4 Snort中实现Boyer-Moore
Snort中实现如下,其中buf和blen是字符串和其长度,ptrn和plen是模式串和其 长度,skip和shift是预先计算的Bad-character表和Good-suffix表:
[language={c}] int mSearch(const char *buf, int blen, const char *ptrn, int plen, int *skip, int *shift){ int b_idx = plen; if(plen == 0) return 1; while(b_idx <= blen) { int p_idx = plen, skip_stride, shift_stride; while(buf[--b_idx] == ptrn[--p_idx]) { if(b_idx < 0) return 0; if(p_idx == 0) { UpdateDoePtr(((const uint8_t *)&(buf[b_idx]) + plen), 0); return 1; } } skip_stride = skip[(unsigned char) buf[b_idx]]; shift_stride = shift[p_idx]; b_idx += (skip_stride > shift_stride) ? skip_stride : shift_stride; } return 0; }
2.2 多模式匹配
随着互联网的发展,攻击手段和方法变得多样,IDS要分析的数据包也越来越 多,攻击特征库变得无比庞大,单一匹配时需要的计算量将是攻击特征字节数、 数据包字节数、每秒的数据包数和数据库的攻击特征数的乘积。
当网络数据流的产生速度超过了系统检测引擎的处理能力时,必然导致数据 包不能及时分析就被丢弃。这些被丢弃的数据包很可能包含具有攻击特征的恶意 数据,这样就会遗漏该攻击事件,产生漏报。传统的单一模式匹配方法的运算速 度正影响着Snort的整体性能[3]。
因此Snort从2.0开始使用多模式快速匹配规则引擎来加快Snort的字符串匹配速 度。在2.9.1.1版源码的sfutil/mpse.h头文件里可以看到Snort定义了以下几种 模式匹配方法:
[language={c}] /* * Pattern Matching Methods */ //#define MPSE_MWM 1 #define MPSE_AC 2 //#define MPSE_KTBM 3 #define MPSE_LOWMEM 4 //#define MPSE_AUTO 5 #define MPSE_ACF 6 #define MPSE_ACS 7 #define MPSE_ACB 8 #define MPSE_ACSB 9 #define MPSE_AC_BNFA 10 #define MPSE_AC_BNFA_Q 11 #define MPSE_ACF_Q 12 #define MPSE_LOWMEM_Q 13 #ifdef INTEL_SOFT_CPM #define MPSE_INTEL_CPM 14 #endif /* INTEL_SOFT_CPM */
以上几种模式匹配算法的可以归为以下几类:
-
Wu-Manber算法。
-
Aho-Corasick算法。
-
SFKSearch算法。
-
IntelPmSearch。
2.2.1 Wu-Manber算法
Wu-Manber算法将过滤思想和Boyer-Moore算法思想结合起来。Boyer-Moore算法 中的不良字符转移机制记录了字符集中所有字符在模式串中出现的最右位置距离 模式串串尾的距离。在算法匹配过程中,可以根据这个位置信息安全地移动而不 用担心忽略任何可能出现的匹配。但是随着模式串个数的增加,各个字符出现在 模式串尾端的概率也相应增加了,相应地与串尾的距离缩小,因而所能跳过的距 离也同样变小,所以这种转移机制的效果在多模式串的情况下被极大地削弱了。
Wu-Manber算法利用块字符扩展了不良字符的转移效果来解决这个问题, 同时 用散列表来筛选匹配阶段应进行匹配的模式串,减少算法匹配时间。
在每一次对匹配情况的考察中,我们不再一个字符一个字符地进行考察,取而代 之地,我们一次考察一“块”, 即考察B个字符。根据这B个字符的匹配情况来决 定模式串的移动距离。当模式串个数较少时, 发生散列冲突的可能性较小, 通常取B=2,否则取B=3。
Wu-Manber 算法首先要对模式串的集合进行预处理, 预处理阶段将建立三个表 格:SHIFT表,HASH表和PREFIX表。SHIFT表中存储字符集中所有块字符在文本中 出现时的转移距离。HASH表用来存储匹配窗口内尾块字符散列值相同的模式串。 PREFIX表用来存储匹配窗口内首块字符散列值相同的模式串。在对模式串进行匹 配的时候就是利用这三个表完成文本的扫描和寻找匹配的过程。[6]
值得注意的是,在旧版的Snort中,Wu-Manber算法实现的函数接口是 mwmSearch,但在最新版2.9.1.1中改算法已不再使用。
2.2.2 Aho-Corasick算法
Aho-Corasick(简称AC)算法是KMP算法向多模式串情形的扩展,是最经典的多模 式匹配算法。该算法于1975年提出,最早是用于图书馆书目查询,利用多个模式 串构建一个有限状态模式匹配自动机,把所有的模式串合并在一个集合中,这样 就可以在对文本进行一次扫描的情况下查找多个模式串。此算法有两个特征:a) 扫描待匹配文本时完全不需要回溯.b)算法时间复杂度为O(n),与模式串的个数 和每个模式串的长度无关。AC算法中无论模式串是否在目标串中,目标串中的每 个字符都必须输入到状态机中,所以无论是最好情况还是最坏情况,AC算法模式 串匹配的时间复杂度都是O(n),包括预处理时间在内,AC算法总时间复杂度是 O(M+n),其中M为所有模式串的长度总和。
对多模式串匹配算法而言,AC算法要比单模式匹配算法KMP、BM等效率要高,但 AC算法对文本模式串进行搜索时没有跳跃,而是按顺序输人字符,无法跳过不必 要的比较。因此在模式串数量较少的实际搜索过程中,AC算法性能不是很好。
在Snort2.9.1.1版本中共有三个基于AC算法的模式匹配方法实现。第一个是 AC_BNFA,其是基于NFA状态机的AC算法。其接口定义在sfutil/bnfa_search.h 里,实现在sfutil/bnfa_search.c中。 关键接口如下:
[language={c}] unsigned bnfaSearch( bnfa_struct_t * pstruct, unsigned char * t, int tlen, int (*match)(void * id, void *tree, int index, void *data, void *neg_list), void * sdata, unsigned sindex, int* current_state );
第二个实现AC_ACSM和第三个实现AC_ACSM2参考了这篇论文[1]。 AC_ACSM的实现使用了较多内存,而AC_ACSM2在减少内存使用和一些操作性能上 进行了优化。其接口如下:
[language={c}] int acsmSearch ( ACSM_STRUCT * acsm,unsigned char * T, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void * data, int* current_state ); int acsmSearch2 ( ACSM_STRUCT2 * acsm,unsigned char * T, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void * data, int* current_state );
2.2.3 SFKSearch算法
SFKSearch是一种占较少内存的Snort算法,利用Trie树(一种树形结构,用 于保存大量的字符串)来储存模式集,用兄弟和孩子连接来表示整个模式集。匹 配时采用和前面相同的坏字符法,根据匹配结果沿着两个方向来进行匹配。直到 最后确定是否有匹配发生。
KTrieSearch是基于SFKSearch算法的实现。其接口定义在 sfutil/sfksearch.h中,其实现定义在sfutil/sfksearch.c中,典型接口如下:
[language={c}] int KTrieSearch( ACSM_STRUCT * acsm,unsigned char * T, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void * data, int* current_state );
2.2.4 IntelPmSearch
这是一个比较新的技术,简单来说就是使用第三方硬件加速卡来处理模式匹配。 这种方法使用Intel的QuickAssist技术来加速Snort中模式匹配的速度。【324029】 它允许在Intel的IA构架里面无缝的集成、使用第三方协处理器,并用软件库来 提供相应的功能[2]。其实现在sfutil/intel-soft-pcm.c和 sfutil/intel-soft-pcm.h中。