Hyperscan是Intel的高性能正则表达式引擎,能支持几十万规则的同步匹配。自推出起即被广泛应用于字符串匹配。本文以QQ为例,介绍Hyperscan通过匹配完成识别的一个具体使用示范。
QQ使用TCP和UDP两种协议,使用不同的协议时净载荷的结构也略有不同。本文以使用UDP为例,对应抓包示例如下图所示。使用UDP协议时,数据净载荷部分以0x02开始、0x03结尾,本地端口通常从8000端口开始,多登录一个QQ终端使用端口则相应增加如8001、8002等。
Hyperscan的工作流程分为编译期和运行期。编译期将正则表达式作为输入,生成对应的数据库。该数据库可以被序列化后进行存储,供运行期提取使用;运行期则利用编译生成的数据库调用匹配引擎对输入进行模式匹配,可通过回调函数来自定义匹配发生后采取的行为。
Hyperscan分为块模式(block)和流模式(stream)两种模式。块模式对一段现成的完整数据进行匹配,匹配结束返回结果;流模式则是针对网络场景下跨报文匹配设计的特殊匹配模式。本示例中QQ的特征在一个完整的数据包中,采用块模式匹配。
首先,我们进入编译阶段。先定义编译需要的QQ相关的字段
qq_expr[]="^\x02.+\x03$";//QQ的对应正则表达式文件
qq_flag=HS_FLAG_DOTALL;
qq_id=1;//设置QQ对应的ID为1
然后调用编译函数完成编译
hs_compile_multi(qq_expr, qq_flag, qq_id, 1, HS_MODE_BLOCK, NULL, &qq_database, &hs_compile_err);
qq_database是编译生成的数据库。编译好后,再分配一段运行期需要的内存scratch:
hs_alloc_scratch(qq_database, qq_scratch);
然后,针对网络数据包,解析数据包头后,拿到净载荷payload及对应长度length。再利用database和scratch通过hs_scan函数运行完成匹配。
我们这里简单定义一个回调函数,命中后打印一条信息。
static int match_handler(unsigned int id, unsigned long long from,
unsigned long long to, unsigned int flags, void *ctx)
{
printf("Hey,It's QQ!");
return 0;
}
hs_scan(qq_database, payload, length, 0,qq_stratch, match_handler,NULL);
如此,在有QQ的UDP数据包时,就能匹配命中并进行相应打印。
使用结束时,调用hs_free_database(qq_database)以及hs_free_scratch(qq_scratch)完成对使用资源的释放。
以上就是一次简单的利用Hyperscan实现字符串匹配的完整过程,是一次针对QQ的简单DPI实现。