@yinnner
2018-12-20T13:55:59.000000Z
字数 9174
阅读 1062
论文地址:Towards security defect prediction with AI
代码仓库地址:sa-bAbI
核心文件:
sa_babi/.├── generate.py # 主要生成代码的文件├── sa_tag.py # 每行代码性质标签└── templates.py # 生成不同模块(控制流、函数体等)的模版
如果要添加漏洞种类,则则需要往sa_tag.py中添加新的行标签,往templates.py中添加新的模块模版。
generate.py重要函数逻辑如下:
main()函数逻辑generators(含有各种例子生成器的数组) linear_only表示代码顺序执行,不包含控制流,生成器只有一种。有以下几种生成器,对应相应的函数:
buffe write 格式:"$buf_var[$idx_var] = '$char';"
fv含义:free variable
| 函数名/生成器 | 作用 |
|---|---|
| gen_cond_example | idx_var经过while循环模块得到 |
| gen_for_example | $idx_var经过for循环模块得到 |
| gen_fv_cond_example | 有一个自由变量,经过if条件判断模块 |
| gen_fv_while_example | 有一个自由变量,经过while循环模块 |
| gen_fv_for_example | 有一个自由变量,for循环模块 |
| gen_tautonly_linear_example | 不经过控制流直接进行不安全的buffer write |
以上生成器逻辑几乎相同,只是需要的变量和判断safe的条件不一样。逻辑如下:
_get_anon_vars()生成得到。dummy_vars,只用来传递数值,实际不起什么作用。_get_char()生成得到。substitutions字典将其封装好。templates.xxx_MAIN_LINES。safe用来指示。templates.xxx_DEC_INIT_PAIRS。_assemble_general_example组装模块生成代码。gen_tautonly_linear_example函数由于不经过控制流,直接进行buffer write,因此所有变量都是dummy_vars,substitutions、main_lines、dec_init_pairs均为空。
如果要添加漏洞种类,我觉得可以按照以上函数的模式写gen_xxx_{Vulnerability Name}_example7个对应函数,具体是否要写那么多个要看漏洞的性质,main函数可能只需要少量改动。
_assemble_general_example()组装模块函数逻辑:
main_lines加一句templates.BUFWRITE_LINES。_get_lines得到所有代码行。(这个函数及相应调用函数需要修改。)_get_tags得到所有代码行标签。_get_instance_str得到最终c语言代码文本。_get_lines()获得所有代码行,包含声明初始化、主体内容、dummy变量
(只用来传递数值,实际不起什么作用)的使用。函数逻辑:
_get_setup_lines获得声明初始化代码行。include_cond_bufwrite == True),则为最后一个代码行(即为buffer write代码)打上Tag.BUFWRITE_COND_SAFE或 Tag.BUFWRITE_COND_UNSAFE(通过safe判断)。_insert_dummies为现有代码行添加dummy变量相关的代码行。_insert_dummies()插入所有dummy变量的声明和赋值语句。函数逻辑如下:
num_dummies次,调用_insert_referential_dummy依次插入每一个dummy变量的相关代码行。_insert_referential_dummy()插入指定dummy变量的声明、初始化和buffer write语句。这里面出现的buffer write不经过控制流,因此标签为BUFWRITE_TAUT_SAFE 或 BUFWRITE_TAUT_UNSAFE。
函数逻辑如下:
dum_len和buffer write需要用到的下标dum_idx。dum_buf_var;指示buffer write位置的下标——dum_int_var。lines中。dum_idx、 dum_len的大小关系判断buffer write是否安全。body_tags中。_test()测试每个生成器生成代码。
这个函数大部分不需要修改,只需要改从745-757行测试标签种类的代码。
| 函数名 | 作用 |
|---|---|
| _get_char() | 获得一个字符 |
| _get_anon_vars() | 获得MAX_NUM_VARS个变量并打乱 |
| _get_tags() | 获得完整的标签,即加上函数主体外的其他标签(#include... int main() {return 0;}) |
| _get_instance_str() | 替换模版中的文本并获得最终c语言代码文本。 |
| _get_setup_lines() | 获得打乱顺序的声明初始化变量代码行 |
| _get_args() | 设置命令行参数格式并获得命令行参数(可能会修改) |
| _get_full_template() | 似乎这个文件里面没有使用到 |
*.c.tok文件*.c.tok文件是按json格式组织的,在tokenizer工具中生成的,是在执行完生成源文件数据后才进行的步骤。
首先明确深度学习神经网络里面的一些概念:
以上三者的关系是:一般情况下,一趟会训练一次整个的训练集;在一趟中分steps_per_epoch步进行,一步处理batch_size个数据。即 训练集数据总数。
[ 1., 0., 0.]表示苹果,[ 0., 1., 0.]表示雪梨,[ 0., 0., 1.]表示香蕉。这样便于代码使用。用np.argmax()就可获得最大值的下标,由于数组只有0、1,取最大值就取到了1所在的索引,其实就获得了标签索引。逻辑如下:
utils.load_data()从npy文件中获得代码实例矩阵instances_mat、标签矩阵labels_mat和 训练集与验证集字典索引partition。datagen.DataGenerator()得到数据生成器data_generator。num_classes。run_experiments开始训练。逻辑如下:
datagen.DataGenerator()得到验证集数据生成器val_data_generator。datagen.generate_balanced()从验证集数据生成器中得到平均了每个类分布(每个类的分布是相同的)后的生成器,再调用next返回该生成器的下一个项目,即获得验证集代码实例矩阵val_instances_mat、验证集标签矩阵val_labels_mat。np.argmax()可获得验证集标签索引数组y_true。np.zeros()初始化混淆矩阵(误差矩阵)cnf_matrices。juliet_memnet.get_model()获得未训练的网络model。predicssklearn.metrics.confusion_matrix()获得预测结果和标签y_true的混淆矩阵并将其存入数组中。==注意:只要import juliet_memnet 就会执行这段代码!!==
utils.generate_sa_data()生成sa_babi数据(就是这一步会调用utils.save_data()生成npy文件)utils.load_data()从npy文件加载获得代码实例矩阵instances_mat、标签矩阵labels_mat和 训练集与验证集分割后的字典索引partition。datagen.DataGenerator()得到数据生成器data_generator。预处理数据:
from sa_babi.sa_tag import Tag获得所有标签。生成sa_babi数据矩阵并且保存到工作目录中。
输入参数:
working_dir :保存数据的工作目录,默认是sa-train-1000coarse_labels:一般不管他。布尔类型,如果是true,表明只有安全和不安全两种情况,默认为false。逻辑如下:
get_examples()从tokens/*.c.tok获得实例instance、标签labels、路径paths。set()获得不重复的标签集合unique_elements。tautonly_elements。is_tautonly。coarse_labels或者is_tautonly,重新映射labels里的标签,即不区分TAUT和COND,只保留safe和unsafe。get_vocab_mapping()获得词汇映射为整数的字典。get_data_dimensions()获得数据的维数:实例数目、最大行数、最大行长。get_example_matrices()获得实例矩阵、标签矩阵。get_partition()获得训练集与验证集分割后的字典索引partition。print_data_stats()打印数据。save_data()将实例矩阵、标签矩阵、整数映射字典、分割数据集的字典、路径存入npy文件和pkl文件中。从tokens/*.c.tok获得实例instance、标签labels、路径paths。
返回值含义如下:
instance[i][j][k]是第i个文件第j行第k个token字符串。
instances: list of list of list of strinstances[i][j][k] represents- the i-th CWE121 C example- the j-th line- the k-th token in that line
labels[i][j]是第i个文件第j行对应标签的整数值。
labels: list of list of intlabels[i][j] represents- the i-th CWE121 C example- the j-th linein SA: the value is the integer Tag value
paths这些*.c.tok*文件的路径数组。该函数逻辑如下:
*.c.tok*文件的路径数组path_list。ann_tok_to_lines()得到代码行数组lines。lines,获得包含代码行号和tokens的实例代码数组instances: labels: *.c.tok*与对应的 *.c文件名称相同,因此通过*.c.tok*文件名加上路径拼接找到对应的 *.c文件路径。*.c文件路径为参数,调用get_sa_tags()获得sa_babi数据集标签数组tags。*.c.tok*文件中不包含include行,因此移除相应的标签。tags中标签对应整数构成最后所需的标签。将instances、labels转换为以0填充的np数组。
返回值含义如下:
instances_mat:代码实例矩阵,3维np数组,各维度最大值:[num_examples, max_numlines, max_linelen]labels_mat :标签矩阵:2维np数组,各维度最大值: [num_examples, max_numlines]。 labels_mat[i][j] 是第i个文件第j行的标签函数逻辑如下:
get_data_dimensions()获得数据的维数:实例数目、最大行数、最大行长。get_vocab_mapping()获得词汇映射为整数的字典。instances_mat、labels_mat。instances中的token,来给instances_mat中token对应位置赋映射字典中对应的整数值;同时在两层循环的地方,将labels对应的值直接复制给labels_mat对应的值,注意此时的labels已经合并了body和other标签。获得训练集与验证集分割后的字典索引partition。
逻辑如下:
partition字典结果如下:
partition = {'train': idx_list[:num_train],'validation': idx_list[num_train:]}
将*.c.tok文件转换为代码行数组lines。
*.c.tok文件格式如下:
{"filename": "/mnt/data/src/0a8b6849a9.c","tokens": [{"kind": "Keyword","line": 2,"sem": "FunctionDecl","text": "int"},{"kind": "Identifier","line": 2,"sem": "FunctionDecl","sym": {"id": "c:@F@main","kind": "FunctionDecl","type": "int ()"},"text": "main"},....]}
函数逻辑如下:
tok_data。line_data,该字典长这样——{行号:token字符串数组}。注意这里行号从1开始,但是为了统一从0开始索引,规定line_data[0]=[]。tok_data,将token加入字典中对应行号的数组里。lines。获得从词汇元素(代码里的token)到整数的字典映射。
逻辑如下:
vocab,有重复。sorted(list(set(vocab)))获得无重复并且排序后的词汇。将实例矩阵、标签矩阵、整数映射字典、分割数据集的字典、路径存入npy文件和pkl文件中。
将实例矩阵、标签矩阵、整数映射字典、分割数据集的字典、路径从npy文件和pkl文件中加载出来。
print_data_stats():打印数据。get_data_dimensions():获得数据的维数:实例数目、最大行数、最大行长。get_sa_tags()获得sa_babi数据集标签数组tags。| 函数名 |
|---|
| generate_data() |
| generate_choi_data() |
| get_juliet_label() |
| get_choi_examples() |
| simp_tok_to_lines() |
| get_vuln_lines() |
| get_label_counts() |
| get_tok_line() |
python validate.py 可能出现的问题和解决方法
找不到matplotlib模块——因为在enviroment.yml里被原作者注释了…,所以在sa_babi环境下
pip intall matplotlib。在virtualenv环境下使用matplotlib绘图时遇到了这样的问题:
>>> import matplotlib.pyplot as pltTraceback (most recent call last):File "<stdin>", line 1, in <module>...in <module>from matplotlib.backends import _macosxRuntimeError: Python is not installed as a framework. The Mac OS X backend will not be able to function correctly if Python is not installed as a framework. See the Python documentation for more information on installing Python as a framework on Mac OS X. Please either reinstall Python as a framework, or try one of the other backends. If you are Working with Matplotlib in a virtual enviroment see 'Working with Matplotlib in Virtual environments' in the Matplotlib FAQ解决方案来自https://www.cnblogs.com/harelion/p/5637767.html:
似乎是因为虚拟环境与默认环境的安装配置不同造成的。
搜索错误信息之后,在STO上找到了解决方案:
1、pip安装matplotlib之后,会在根目录下产生一个.matplotlib的目录:
cd ~/.matplotlib
total 280-rw-r--r-- 1 me staff 78K 10 4 2015 fontList.cache-rw-r--r-- 1 me staff 59K 1 17 15:56 fontList.py3k.cachedrwxr-xr-x 2 me staff 68B 10 4 2015 tex.cache2、在这个目录下创建一个名为
matplotlibrc的文件,内容是:
backend: TkAgg然后保存退出,重启Python交互界面或重新运行脚本,import正常执行。
STO答案地址:http://stackoverflow.com/questions/21784641/installation-issue-with-matplotlib-python
- 似乎跑到后面还会报错,还没解决....