您的位置:首页 > 编程语言 > Python开发

PRISMA原理以及python包使用介绍

2016-10-17 20:35 489 查看
刚开始一直在寻找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.sally
python 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/python
import sys
from optparse import OptionParser

usage = "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 line
sallyIn.readline()
allNgrams = {}
count = 0
for 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环境,加载数据

R
library("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




c87c

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中每一个字段的取值,这个就不用赘述了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  PRISMA