5、SySeVR复现——Data preprocess(上)

目录

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)   
                            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化。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值