目录
1、环境
2、生成切片对应的hash
3、获取要删除的切片位置信息
4、对切片进行token化
1、环境
从数据预处理开始,操作系统:windows 10 ,软件:pycharm
注:对官方提供的文件,做了一些改动,并做了必要的标注。
2、生成切片对应的hash
对应的是create_hash.py :
## coding: utf-8
'''
This file is used to get the hash of slices
'''
import os
import pickle
def get_hashs(slicepath, hashpath):
"""
# Arguments
slicepath: String type, the src of slice files
hashpath: String type, the src to save hash
# Return
None
"""
for filename in os.listdir(slicepath): # 对四种类型的SeVC分别进行处理
if(filename.endswith(".txt") is False): # 判断切片文件是否以.txt结尾,是,则继续执行;不是,则跳过本次循环
continue
print("\n", filename) # 输出当前处理的txt文件
datalist = [] # 用于保存每一个切片序列特有的hash值
filepath = os.path.join(slicepath, filename) # 获取当前处理的txt文件的完整路径
f1 = open(filepath, 'r') # 以读取模式打开当前文件,并且将一个表示文件及其内容的对象存储到变量f1中
slicelists = f1.read().split("------------------------------") # 读取当前文件,并且以“----”为标准对文件内容进行切割
f1.close() # 关闭f1对象
if slicelists[0] == '': # 删除每一类SeVC包含的冗余的信息
del slicelists[0]
if slicelists[-1] == '' or slicelists[-1] == '\n' or slicelists[-1] == '\r\n':
del slicelists[-1]
for slicelist in slicelists: # 对每一个切片序列分别进行处理
sentences = slicelist.split('\n') # 对切片序列按行进行切割
if sentences[0] == '\r' or sentences[0] == '': # 删除每一个切片序列包含的冗余信息
del sentences[0]
if sentences == []:
continue
if sentences[-1] == '':
del sentences[-1]
if sentences[-1] == '\r':
del sentences[-1]
sentences = sentences[1:] # 获取切片序列的部分信息
new_sens = [] # 用于保存函数切片的代码部分,不包括切片代码后面的行数
for sentence in sentences: # 对剩余的信息逐一处理,删除每一行代码后面的代码行
if (is_number(sentence.split(' ')[-1])) is False:
continue
else:
sentence = ' '.join(sentence.split(' ')[:-1]) # 以空格为切割条件对一行切片序列进行切割,然后用空格将切分后的元素组合起来(不包括代码行)
new_sens.append(sentence) # 将得到的不包含代码行号的代码作为一个整体放入一个新的列表中
slicelist = " ".join(new_sens) # 得到不包含代码行的完整切片序列
# print(slicelist)
data = hash(slicelist) # 对切片序列做hash运算,得到每一个切片序列对应的一串特殊的hash值
datalist.append(data) # 依次将hash值保存起来
f = open(os.path.join(hashpath,(filename[:-4]+".pkl")), 'wb') # 保存计算的hash值
pickle.dump(datalist, f)
f.close()
def is_number(s): # 用于判断s是否为一个数字
try:
float(s)
return True
except ValueError:
pass
try:
import unicodedata
unicodedata.numeric(s)
return True
except (TypeError, ValueError):
pass
return False
if __name__ == '__main__':
# windows系统下,文件路径之间要使用“//”
# linux系统下,文件路径之间要使用“/”
SLICEPATH = './/file//test_data//4//' # 程序切片路径(未打标签)
HASHPATH = './/file//hash_slices//' # 切片hash存储路径
sentenceDict = get_hashs(SLICEPATH, HASHPATH) # 获取每一个切片序列对应的hash值
print('\nsuccess!')
在路径上新建不存在的文件夹.
运行结果:
(1)在"./file/hash_slices/"目录下,生成四种SyVCs对应的pkl文件;
(2)以api_slices.pkl文件为例,里面保存的是每个切片对应的hash值。
3、获取要删除的切片位置信息
对应的是delete_list.py :
## coding: utf-8
import pickle
import os
def dedouble(Hashpath,Deletepath):
for filename in os.listdir(Hashpath): # 获取保存切片hash值的文件名,并分别处理
hashpath = os.path.join(Hashpath, filename) # 获取保存切片hash值的文件完整路径
f = open(hashpath, 'rb') # 打开保存切片hash值的文件,并将一个表示文件及其内容的对象存储到变量f中
hashlist = pickle.load(f) # 将文件中的切片hash值保存到hashlist中
f.close() # 关闭f对象
datalist = [] # 用于保存无重复的切片hash值
delete_list = [] # 用于保存重复的切片索引,也就是保存重复切片的位置
hash_index = -1 # 用于记录重复切片的索引
for data in hashlist: # 删除重复的切片hash,并记录其位置
hash_index += 1
if data not in datalist:
datalist.append(data)
else:
delete_list.append(hash_index) # index of slices to delete
with open(os.path.join(Deletepath, filename), 'wb') as f: # 保存要删除的切片的位置信息
pickle.dump(delete_list, f)
f.close()
if __name__ == '__main__':
hashpath = './/file//hash_slices//' # 切片hash值得保存地址
deletepath = './/file/delete_list//' # 重复切片的位置保存地址
dedouble(hashpath, deletepath) # 删除切片文件中重复的切片
在路径上新建不存在的文件夹。
运行结果:
(1)在"./file/delete_list/"目录下,生成了四种SyVCs对应的pkl文件;
(2)以api_slices.pkl文件为例,打开该文件,可以看到里面保存了重复的切片文件的位置信息。
以索引值为11的切片为例,可以看到它与索引值为0的切片重了。
4、对切片进行token化
对应process_dataflow_func.py :
## coding: utf-8
'''
This python file is used to precess the vulnerability slices, including read the pkl file and split codes into corpus.
Run main function and you can get a corpus pkl file which map the same name slice file.
'''
import os
import pickle
from mapping import *
'''
get_sentences function
-----------------------------
This function is used to split the slice file and split codes into words
# Arguments
_path: String type, the src of slice files
labelpath: String type, the src of label files
deletepath: delete list, delete repeat slices
corpuspath: String type, the src to save corpus
maptype: bool type, choose do map or not
# Return
[slices[], labels[], focus[]]
'''
def get_sentences(_path,labelpath,deletepath,corpuspath,maptype=True):
FLAGMODE = False # FLAGMODE 用于确定切片来自NVD还是来自SARD
if "SARD" in _path:
FLAGMODE = True
for filename in os.listdir(_path): # 获取保存四种程序切片的文件名,以SeVC类型为单位进行处理
if(filename.endswith(".txt") is False): # 如果保存切片文件的文件名不是txt文件,结束此次循环
continue
print(filename)
filepath = os.path.join(_path, filename) # 获取切片文件的完整路径
f1 = open(filepath, 'r') # 以读取模式打开切片文件,并将一个表示文件及其内容的对象存储到变量f1中
slicelists = f1.read().split("------------------------------") # 读取切片文件,并且以"————"作为切割准则,对文件进行切割
f1.close() # 关闭f1对象
if slicelists[0] == '': # 删除切片文件,前后的冗余信息
del slicelists[0]
if slicelists[-1] == '' or slicelists[-1] == '\n' or slicelists[-1] == '\r\n':
del slicelists[-1]
filepath = os.path.join(labelpath, filename[:-4] + "_label.pkl") # 获取打完标签后的文件的路径,标识这个文件是否为有漏洞的文件
f1 = open(filepath, 'rb') # 以读取模式打开标签后的切片文件,并将一个表示文件及其内容的对象存储到变量f1中
labellists = pickle.load(f1) # 读取标签后的切片文件,并将其保存在 labellists 中
f1.close() # 关闭f1对象
filepath = os.path.join(deletepath, filename[:-4] + ".pkl") # 获取保存重复切片的位置信息的文件路径,为了删除重复的切片
f = open(filepath, 'rb') # # 以读取模式打开保存重复切片的位置信息的文件,并将一个表示文件及其内容的对象存储到变量f中
list_delete = pickle.load(f) # 保存重复切片的位置信息的文件,并将其保存在list_delete中
f.close() # 关闭f对象
lastprogram_id = 0
program_id = 0
index = -1
slicefile_corpus = [] # 保存一般化后的切片生成的token序列,以切片为单位
slicefile_labels = [] # 保存已处理的切片所在文件的标签
slicefile_focus = [] # 保存SyVC在token序列中的位置
slicefile_filenames = [] # 保存已处理的切片的头部信息
slicefile_func = []
focuspointer = None
for slicelist in slicelists: # 对每一个切片分别进行处理
slice_corpus = [] # 用于保存经过清洗后的切片
focus_index = -1 # 保存SyVC在token中的位置
flag_focus = 0 # 用于控制代码含有SyVC的语句执行流程,有三个状态:0,1,2
index = index + 1 # 用于标识现在所处理的切片在整个切片文件中的索引
sentences = slicelist.split('\n') # 以行为单位,对切片进行分割
if sentences[0] == '\r' or sentences[0] == '': # 删除每一个切片前后无关的信息
del sentences[0]
if sentences == []:
continue
if sentences[-1] == '':
del sentences[-1]
if sentences[-1] == '\r':
del sentences[-1]
focuspointer = sentences[0].split(" ")[-2:] # 获取切片准则,即SyVCs,以及获取切片准则所在的代码行数
sliceid = index # 将索引赋值给sliceid,将其与要删除的切片索引相对比
if sliceid in list_delete: # 如果当前处理的切片是要删除的切片,则直接跳到下一轮循环
continue
file_name = sentences[0] # 获取每个切片的头部信息
program_id = sentences[0].split(" ")[1].split("/")[-1] # 获取切片的文件名,例:CVE_2005_2617_PATCHED_syscall32_setup_pages.c
# if FLAGMODE: # 满足条件,就是对来自SARD的切片进行处理;否则,对NVD的切片进行处理
# program_id = sentences[0].split(" ")[1].split("/")[-4] + sentences[0].split(" ")[1].split("/")[-3] + \
# sentences[0].split(" ")[1].split("/")[-2]
# else:
# program_id = sentences[0].split(" ")[1].split("/")[-1] # 获取切片的文件名,例:CVE_2005_2617_PATCHED_syscall32_setup_pages.c
# if lastprogram_id == 0:
# lastprogram_id = program_id
#
# # 这里有问题,相邻两个切片同属于一个文件,则导致后一个无法保存
# if not (lastprogram_id == program_id): # lastprogram_id !=program_id 时,进入if条件语句,
# folder_path = os.path.join(corpuspath, str(lastprogram_id)) # 语料库保存路径(先产生切片的token序列,再保存)
# savefilename = folder_path + '/' + filename[:-4] + '.pkl' # 以切片所属的文件为单位,每个文件下将SeVC分别保存,归类保存
# if lastprogram_id not in os.listdir(corpuspath): # 当前切片所属文件未生成文件夹,就新建;否则,不新建
# os.mkdir(folder_path) # 以当前切片文件名创建文件夹
# if savefilename not in os.listdir(folder_path): # 同一文件夹下,保存四种类型SeVC的文件
# f1 = open(savefilename, 'wb') # 以写入模式打开标签后的切片文件,并将一个表示文件及其内容的对象存储到变量f1中
# pickle.dump([slicefile_corpus, slicefile_labels, slicefile_focus, slicefile_func, slicefile_filenames], f1) # 保存切片相关信息到相应文件中
# else:
# f1 = open(savefilename, 'rb') # 以读取模式打开标签后的切片文件,并将一个表示文件及其内容的对象存储到变量f1中
# data = pickle.load(f1) # 读取文件内容,并将其保存在 data 中
# f1.close() # 关闭f1对象
# f1 = open(savefilename, 'wb') # 以写入模式打开标签后的切片文件,并将一个表示文件及其内容的对象存储到变量f1中
# pickle.dump([slicefile_corpus + data[0], slicefile_labels + data[1], slicefile_focus + data[2],
# slicefile_func + data[3], slicefile_filenames + data[4]], f1) # 往文件中保存相关信息
# f1.close() # 关闭f1对象
# slicefile_corpus = [] # 保存一般化后的切片生成的token序列,以切片为单位;重置
# slicefile_focus = [] # 保存SyVC在token序列中的位置;重置
# slicefile_labels = [] # 保存已处理的切片所在文件的标签;重置
# slicefile_filenames = [] # 保存已处理的切片的头部信息;重置
# slicefile_func = []
# lastprogram_id = program_id # 先将切片一般化,再切分为token序列,最后保存;保存完后,重置所有保存信息的变量,进入下一轮循环
sentences = sentences[1:] # 获取除第一行以外的切片信息(第一行不是由切片技术产生的信息)
for sentence in sentences: # 对每个切片的每一行进行单独处理
if sentence.split(" ")[-1] == focuspointer[1] and flag_focus == 0: # 确定当前的代码行数是否与SyVC所在的代码行匹配
flag_focus = 1 # flag_focus作为匹配SyVC所在代码行的标志
sentence = ' '.join(sentence.split(" ")[:-1]) # 获取切片每一行的代码,不包括表示代码行的编号
start = str.find(sentence, r'printf("') # 在sentence中寻找指定的字符串,找到就返回第一次出现的位置;否则返回-1
if start != -1: # 找到指定的字符串,进入条件语句
start = str.find(sentence, r'");') # 同上
sentence = sentence[:start + 2] # 获取整个printf语句(去掉’;’号)
fm = str.find(sentence, '/*') # 在每行代码中寻找'/*',找到返回第一次出现的位置;否则,返回-1
if fm != -1: # '/*'开始,在C语言中为注释,这一步是为了删除切片中的注释性语句
sentence = sentence[:fm] # 删除'/*'之后的注释性语句
else:
fm = str.find(sentence, '//') # 在代码中寻找‘//’的位置
if fm != -1: # 删除//之后的注释性语句
sentence = sentence[:fm]
sentence = sentence.strip() # 移除字符串头尾指定的字符(默认为空格或换行符)或字符序列
list_tokens = create_tokens(sentence) # 对代码行进行切分,切分成token序列
if flag_focus == 1: # 当前代码行中存在SyVC,进行的操作
# if "expr" in filename: # 注意,这句似乎有问题,就是所有的filename里都不包含‘expr’,这段代码都没用
# focus_index = focus_index + int(len(list_tokens) / 2)
# flag_focus = 2
# slicefile_focus.append(focus_index)
# else:
if focuspointer[0] in list_tokens: # 判断SyVC是否在当前代码行的token序列中 这里是处理api、integeroverflow两类切片
focus_index = focus_index + list_tokens.index(focuspointer[0]) # 记录SyVC在token中的位置
flag_focus = 2 #
slicefile_focus.append(focus_index) # 保存SyVC在token序列中的位置与slicefile_focus
else:
# if 'U' in focuspointer[0]: # 这里改过!!! 这里是处理arraysuse、pointersuse两类切片
focus = focuspointer[0].split('\'')[1] # 改的目的是为了获取arraysuse、pointersuse两类切片中SyVC在token中的位置
# 未改前:if focuspointer[0] in list_tokens:
if focus in list_tokens:
# 未改前:focus_index = focus_index + list_tokens.index(focuspointer[0].replace('*', ''))
focus_index = focus_index + list_tokens.index(focus) # +1的目的是focu的索引从0开始
flag_focus = 2
slicefile_focus.append(focus_index)
# else:
# flag_focus = 0
if flag_focus == 0: # 当前代码中不存在SyVC,进行的操作
focus_index = focus_index + len(list_tokens) # 保存token的个数
if maptype:
slice_corpus.append(list_tokens) # 保存经过数据清洗后的切片
else:
slice_corpus = slice_corpus + list_tokens
if flag_focus == 0:
continue
# slicefile_labels与slicefile_filenames是一一对应的
slicefile_labels.append(labellists[file_name]) # 保存已处理的切片所在文件的标签
slicefile_filenames.append(file_name) # 保存已处理的切片的头部信息
if maptype:
slice_corpus, slice_func = mapping(slice_corpus) # 以切片为单位,将自定义的函数名、自定义的变量名进行一般化处理,并将其保存在变量slice_corpus中
slice_func = list(set(slice_func))
if slice_func == []:
slice_func = ['main']
sample_corpus = [] # 用于保存一般化后的切片
for sentence in slice_corpus: # 对经过一般化后的切片逐行进行处理
list_tokens = create_tokens(sentence) # 将经过一般化后的代码行切分为token序列
sample_corpus = sample_corpus + list_tokens # 将token序列依次放入sample_corpus中
slicefile_corpus.append(sample_corpus) # 保存一般化后的切片生成的token序列,以切片为单位
slicefile_func.append(slice_func) #
else:
slicefile_corpus.append(slice_corpus)
folder_path = os.path.join(corpuspath, str(program_id)) # 用于保存最后一个切片所对应的文件的相关信息
savefilename = filename[:-4] + '.pkl'
savefilepath = folder_path + '/' + filename[:-4] + '.pkl'
if program_id not in os.listdir(corpuspath):
os.mkdir(folder_path)
if savefilename not in os.listdir(folder_path):
f1 = open(savefilepath, 'wb')
pickle.dump([slicefile_corpus, slicefile_labels, slicefile_focus, slicefile_func, slicefile_filenames], f1)
else:
f1 = open(savefilepath, 'rb')
data = pickle.load(f1)
f1.close()
f1 = open(savefilepath, 'wb')
pickle.dump([slicefile_corpus+data[0], slicefile_labels+data[1], slicefile_focus+data[2], slicefile_func+data[3], slicefile_filenames+data[4]], f1)
f1.close()
slicefile_corpus = [] # 保存一般化后的切片生成的token序列,以切片为单位;重置
slicefile_focus = [] # 保存SyVC在token序列中的位置;重置
slicefile_labels = [] # 保存已处理的切片所在文件的标签;重置
slicefile_filenames = [] # 保存已处理的切片的头部信息;重置
slicefile_func = []
if __name__ == '__main__':
SLICEPATH = './/file//test_data//4//' # 由extract_df.py文件产生的切片文件路径
LABELPATH = './/file//label_data//' # 由make_label_nvd.py文件产生的标签文件路径
DELETEPATH = './/file//delete_list//' # 由delete_list.py产生的保存重复切片的位置信息的文件路径
CORPUSPATH = './/file//corpus//' # 语料库保存路径
MAPTYPE = True
sentenceDict = get_sentences(SLICEPATH, LABELPATH, DELETEPATH, CORPUSPATH, MAPTYPE)
print('success!')
主要目的是将重复切片删除,对剩下的切片token化。
运行结果:
打开上图中的api_slices.pkl文件:
以函数名为文件夹名(CVE....),将每一个切片保存到相应的CVE文件夹下,然后对自定义的函数名进行符号化,再对整个切片token化。