PRISMA原理以及python包使用介绍

来源:互联网 发布:外国域名注册商 编辑:程序博客网 时间:2024/06/13 13:47

刚开始一直在寻找PRISMA的相关文档,感觉很少,结果后面才发现在下载包中的vignettes以及inst文件下面,有对它的详细介绍以及示例展示,还是自己不够细心,导致浪费了很长一段时间。QAQ


Introduction

  • PRISMA可以高效地处理sally输出文件,很快地处理n-gram过程;
  • Testing-based feature dimension reduction(降维);
  • 最优矩阵分解

    整体流程图:
    这里写图片描述


Loading the Data

首先需要 获得“.sally”文件,就是通过sally对原始网络流量进行处理;
在目录/inst/extdata中有提供测试数据集asap,其中原始数据asap.raw如图所示:
这里写图片描述
asap.cfg是Sally的配置文件,通过如下命令得到PRISMA输入。

sally -c asap.cfg asap.raw asap.sallypython sallyProcessing.py asap.sally asap.fsally

这里笔者想说一下很重要的一点,就是sally是干嘛的(之前很久没有弄懂,终于看了源码以及帮助文档之后弄懂了)。

下面以granularity为tokens,输出格式为“text”为例:

  • 首先sally会根据配置文件中设定的“ngram_delim”来对原始的“.raw”数据进行划分,当然这里还会有一个自动对URI进行解码的过程。划分了之后,就得到了很多的tokens,把所有的报文序列中分割得到的tokens都作为一个特征,也作为初始矩阵的一维,所以才会有图中的“16777216”这么大的维数,很显然这是一个稀疏矩阵,选择用这种格式输出其实也可以看做是用最简单的方法来存储结果,因为真的用矩阵存会相当大,所以就只记录下来“value”为“1”的项;

图片名称

  • 然后,对于这些token一个一个的在报文序列中进行匹配,如果哪里有匹配的就会做一个记录,也就是下面的这种形式
417718110:NT:1,528173057:admin.php:1,542593949:5.1:1,709591711:Windows:1,1070264077:NeBkxkA0rw:1,1070607007:HTTP:1,1220141225:action:1,1369927740:de:1,1710609481:U:1,1720296684:GET:1,1920966484:9.20:1,2451033089:www.foobar.com:1,2452995035:cgi:1,2497091255:1.1:1,2543529916:Accept:1,2761047053:*:1,2844811140:Opera:1,3060078669:Host:1,3250181107:par:1,4032950582:rename:1,4261263756:User-Agent:1 line0

每个匹配的token特征由三部分组成:

  • dimension :标识这个特征的index (specifies the index of the dimension),因为划分后得到了很多很多的token,每一个token都是一个特征(后面的reduction就是为了获得具有代表性的token),为了标识这些特征所以每一个特征都有一个index 。
  • feature: a textual representation of the feature
  • value: the value at the dimension.

这里笔者将详细讲解sallyProcessing.py函数:

#!/usr/bin/pythonimport sysfrom optparse import OptionParserusage = "usage: %prog in.sally out.fsally"parser = OptionParser(usage)(options, args) = parser.parse_args()if len(args) != 2:    parser.print_help()    sys.exit()sallyIn = file(sys.argv[1])sallyOut = file(sys.argv[2], "w")# skip first linesallyIn.readline()allNgrams = {}count = 0for l in sallyIn:    count += 1    if count % 1000 == 0:         print(count)    info = l.split(" ")    if info[0] == "":        curNgrams = []    else:        curNgrams = [ngramInfo.split(":")[1] for ngramInfo in info[0].split(",")]        allNgrams.update(allNgrams.fromkeys(curNgrams))    sallyOut.write("%s\n" % " ".join(curNgrams))sallyOut.write("%s\n" % " ".join(allNgrams.keys()))sallyOut.close()sallyIn.close()

相信熟悉python的很快就能看明白,笔者是菜鸟所以自己抠了一个短的输入文件进行测试,便于理解。
输入:

这里写图片描述

增加了print之后的显示:

这里写图片描述

输出:

这里写图片描述

实际上这里的sallyProcessing.py主要作用就是把.sally文件中格式变换一下,将“.sally”文件中“148165591:6.0:1,417718110:NT:1,709591711:Windows:1”这种格式的内容中的中间值(这里是6.0和NT以及Windows)读出来存在.fsally中,其实也就是得到分好的gram的集合,也作为PRISMA的输入。

这一部分主要是为了比较高效地完成token划分过程,然后将划分token的结果转换成PRISMA比较好处理的输入。


The PRISMA Data Set

对于上面得到的输入,可以通过如下方法加载加以展示。

进入R环境,加载数据

Rlibrary("PRISMA")

loadPrismaData("asap")

读取asap数据集:

data(asap)asap

可以看到由10000个样本,然后有10034个特征(这些特征就是上面得到的token,虽然最初划分的得到的tokens很多,但是由于会有重复的,通过sallyProcessing.py处理之后,将tokens都提取出来了,会得到很多相同的token,于是就会合并掉,最终这里只有10034个tokens了也就是10034个特征),通过处理变成了一个12*24的矩阵。
所谓的12*24是因为PRISMA会将一些tokens整合到一个集合中,用这一个集合来作为一个特征,这个在另一篇文章中有提到对其的配置方法,具体是通过计算token之间的关联系数(correlation coefficient)将关联系数相近的合并为一组,如图第二行就是将“admin.php par action”作为一个特征,这里有12个(或者组更合适)特征;

asap$data

asap$group


Dimensionnality Reduction

上面得到了12*24的矩阵是经过了降维(这里可以参见ASAP的论文),具体实现将在下面详细讲解。
首先在提取报文结构的时候,由于数据集庞大,分析时候必须关注一些具有代表性的特征,所以不能把初步划分的10034个tokens都作为最终分析的特征,需要进行降维,去除掉多余的特征(如时间戳、cookie等等,这些都是随机出现,而且某一种取值基本都只会出现一次)。

论文中提到,会使用stastical test-driven dimension reduction,排除掉特征集中的constant以及volatile特征,详细参见PRISMA的论文。

这里的test去除了值出现频繁很高以及很低的token,在实际报文序列中,这些可能对应着变值字段和一个定值字段,在找出具有代表性的特征的时候,丢掉了这些,但是在后面的template推断过程中又将其考虑进去了,这样才能得到更准确的格式信息。

扩展:这一步完成以后,报文数据就被转换到了向量空间,并且经过了降维,筛选出了具有代表性的token特征(详细的可以参考ASAP论文中的讲解),使用什么方法来提取报文格式以及状态机,个人觉得就可以“仁者见仁智者见智”了。


Clustering for Event Inference

出现在同一个特定通信event中的报文,通常都会有相似的结构特征(similar structural features),所以可以利用这种结构来提取event信息。首先定义一种metric来衡量两个报文之间的相似性,譬如欧氏距离(其中的Φw(x)表示 w 在报文 x 中出现的次数)

在PRISMA的论文中提到了两种聚类方法:

  • Part-based Clustring

论文提到这种方法适用于Assembled of parts类型协议效果较好。先进行NMF,然后再按照NMF得到的坐标计算报文之间的相似度来聚类。

这里写图片描述

非负矩阵分解实质就是通过将处理得到的矩阵A:features×N(data point),分解成两个矩阵B:features×e,C:e×N,(e远小于features的个数),得到的矩阵B可以解释成一个新的基准向量,矩阵C表示在这个新的向量空间中每一个点的坐标【也就是说B(B1,B2,...,Be)定义了新的基准矢量,C(C1,C2,...,CN)则表示每一个点对应的每个基准矢量的权值)】,C里面的Ci就是每一个数据点的坐标,这些坐标就用来计算报文之间的相似度,进行聚类(这里可以参考上面的流程图来理解)。

而具体怎么实现非负矩阵分解就不讲了(因为我也讲不清楚:-D囧:-D)。

  • Position-based Clustering

论文中提到这种方法适用于Monolithic communication,where tokens are weighted according to their absolute position in the message,因为某些协议体现出position-dependent 特征,提出了一种weighted distance measure:


Inference of the State Machine

不论采用哪种聚类方法,都会将报文分成若干类,而每一类就代表了一个Event,正是需要依赖这些Event来进行状态机推断。论文中是通过Markov模型来进行推断,笔者会在后面文章中详细讲述。


Learning Templates and ruls

大部分协议逆向工作都是先完成协议格式的提取,再利用得到的格式信息进行状态机推断,而PRISMA则是先得到状态机,再利用状态机信息来得到格式信息Template,以及会话之间转换的rules(为了完成后面的Protocol Simulation)。

Templates:

首先需要说明,Markov模型的一个状态对应状态机中的一个状态,而每一个状态里面可能会有多个会话,一个会话中也会有多种格式的报文也就是会有多个Template。Learning Templates 的具体步骤如下:

  • 根据之前的划分方法将原始报文序列tokenize;
  • 根据Markov模型将原始报文划分成不同会话的报文;
  • 对于Markov模型中的每一个状态

    • 将相同token数目的报文放到一个group中
    • 如果一个group中的报文在某一个相同的位置,有一个相同的token,那么就认为是最终Template中的一个定值字段,否则就认为是一个变值字段

最终会得到Markov模型中每一个状态的Template,这个Template代表了这个状态下通用的报文格式。需要注意的是,每一个状态可能会对应很多个template,因为这里是按照token数量进行的分组,每一组都会得到一个Template。
实际上这里做了很大的简化,会对得到的结果的准确率造成影响。

Rules:
所谓的Rules其实就是结合Markov模型,会话信息,原始报文,提取出Template中每一个字段的取值,这个就不用赘述了。

0 0