关于SARD漏洞数据集的预处理

前言 关于SARD

软件保障参考数据集(SARD)包含118种CWE漏洞类型,是专门为评估静态分析工具的能力而开发的。数据集中的每一个样本都以其函数名称为标签,后缀字符串为 “good “或 “bad”,因此我们可以很容易地利用这些后缀信息来生成每个样本的标签。
为了从SARD中构建一个java漏洞数据集,需要对SARD进行一定的预处理。
这里SARD使用Juiletjava。

一、删除文件中的antbuild、web.xml等无用文件和文件夹:

/**
	 * SARD数据集预处理:去除web.xml、antbuild等无用文件和文件夹
	 * @param filePath
	 */
	public static void getFile(String filePath) {
		File file = new File(filePath);
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for (File file1 : files) {
                if(file1.isDirectory()){
                	if("antbuild".equals(file1.getName())){
                		deleteFileOrDirectory(file1.getAbsolutePath());
                	}
                    getFile(file1.getAbsolutePath());
                }else{
                	if("ServletMain.java".equals(file1.getName())){
                		deleteFileOrDirectory(file1.getAbsolutePath());
                	}
                }
            }
        }else{
        	if("ServletMain.java".equals(file.getName())){
        		deleteFileOrDirectory(file.getAbsolutePath());
        	}
        }
	}
	/**
     * 删除文件或文件夹
     *
     * @param fileName 文件名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteFileOrDirectory(String fileName) {
        File file = new File(fileName);  // fileName是路径或者file.getPath()获取的文件路径
        if (file.exists()) {
            if (file.isFile()) {
                return deleteFile(fileName);  // 是文件,调用删除文件的方法
            } else {
                return deleteDirectory(fileName);  // 是文件夹,调用删除文件夹的方法
            }
        } else {
            System.out.println("文件或文件夹删除失败:" + fileName);
            return false;
        }
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        if (file.isFile() && file.exists()) {
            file.delete();
            System.out.println("删除文件成功:" + fileName);
            return true;
        } else {
            System.out.println("删除文件失败:" + fileName);
            return false;
        }
    }

    /**
     * 删除文件夹
     * 删除文件夹需要把包含的文件及文件夹先删除,才能成功
     *
     * @param directory 文件夹名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteDirectory(String directory) {
        // directory不以文件分隔符(/或\)结尾时,自动添加文件分隔符,不同系统下File.separator方法会自动添加相应的分隔符
        if (!directory.endsWith(File.separator)) {
            directory = directory + File.separator;
        }
        File directoryFile = new File(directory);
        // 判断directory对应的文件是否存在,或者是否是一个文件夹
        if (!directoryFile.exists() || !directoryFile.isDirectory()) {
            System.out.println("文件夹删除失败,文件夹不存在" + directory);
            return false;
        }
        boolean flag = true;
        // 删除文件夹下的所有文件和文件夹
        File[] files = directoryFile.listFiles();
        for (int i = 0; i < files.length; i++) {  // 循环删除所有的子文件及子文件夹
            // 删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            } else {  // 删除子文件夹
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
        }

        if (!flag) {
            System.out.println("删除失败");
            return false;
        }
        // 最后删除当前文件夹
        if (directoryFile.delete()) {
            System.out.println("删除成功:" + directory);
            return true;
        } else {
            System.out.println("删除失败:" + directory);
            return false;
        }
    }

二、二分类问题数据集的构建

111

三、多分类问题数据集的构建

package Main;

import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

public class JavaParser {
	/**
	 * SARD数据集预处理:去除web.xml、antibuild等无用文件和文件夹
	 * @param filePath
	 */
	public static void getFile(String filePath) {
		File file = new File(filePath);
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for (File file1 : files) {
                if(file1.isDirectory()){
                	if("antbuild".equals(file1.getName())){
                		deleteFileOrDirectory(file1.getAbsolutePath());
                	}
                    getFile(file1.getAbsolutePath());
                }else{
                	if("ServletMain.java".equals(file1.getName())){
                		deleteFileOrDirectory(file1.getAbsolutePath());
                	}
                }
            }
        }else{
        	if("ServletMain.java".equals(file.getName())){
        		deleteFileOrDirectory(file.getAbsolutePath());
        	}
        }
	}
   
	public static void main(String[] args) throws IOException {
		String filePath = "C:\\Users\\rooooot\\Desktop\\SARD-Java\\generality test\\CWE614_Sensitive_Cookie_Without_Secure";
		String targetPath = "C:\\Users\\rooooot\\Desktop\\SARD-Java\\multi-class classification";
		HashMap<String, String> cweMap = new HashMap<String, String>();
    	cweMap.put("CWE643", "xpathi");
    	cweMap.put("CWE614", "securecookie");
    	cweMap.put("CWE327", "crypto");
    	cweMap.put("CWE328", "hash");
    	cweMap.put("CWE90", "ldapi");
    	cweMap.put("CWE89", "sqli");
    	cweMap.put("CWE78", "cmdi");
		GenerateClassFile(filePath,targetPath,cweMap);
  }
    /**
     * 根据CWE编号生成类级Java文件以及xml标签
     * @param filePath
     * @throws IOException 
     */
    public static void GenerateClassFile(String Path,String targetPath,HashMap<String, String> cweMap) throws IOException {
    	File directory = new File(Path);
    	int i = 0;
    	for(File javaFile:directory.listFiles()) {
    		//无漏洞函数缓存表
        	ArrayList<String> goodList = new ArrayList<String>();
        	//有漏洞函数缓存表
        	ArrayList<String> badList = new ArrayList<String>();
        	JavaProjectBuilder builder =  new JavaProjectBuilder();
            //源码目录
            builder.addSourceTree(javaFile);
            //读取CWE编号
            String[] filenames = javaFile.getName().split("_");
            String cwe = filenames[0];
            //读取文件编号
            String fileno = filenames[filenames.length-1].split("\\.")[0];
            //不包含跨文件代码,直接生成
            if(!check(fileno)) {
            	//读取漏洞函数及无漏洞函数
                Collection<JavaClass> classes = builder.getClasses();
                Iterator<JavaClass> it = classes.iterator();
                //逐个类进行读取
                while(it.hasNext()) {
                	JavaClass cls = builder.getClassByName(it.next().getCanonicalName());
                    List<JavaMethod> methods = cls.getMethods();
                    for(JavaMethod method : methods){
                        String methodName = method.getName(); //方法名
                        if(ignoreCaseIndexOf(methodName,"bad")!=-1) {
                        	badList.add(method.getSourceCode());
                        }else if(ignoreCaseIndexOf(methodName,"good")!=-1){
                        	goodList.add(method.getSourceCode());
    					}
                    }
                }
                //生成无漏洞类文件及其xml说明文件
                //重建java类文件
                File f = new File(targetPath+"\\"+javaFile.getName());//新建一个文件对象,如果不存在则创建一个该文件
                FileWriter fw;
                StringBuffer codeFile = new StringBuffer(readFile(javaFile.getAbsolutePath()));
                try {
                	fw = new FileWriter(f);
                	for(String text:badList) {
                		int start = ignoreCaseIndexOf(codeFile.toString(), text);
                		if(start!=-1) {
                			codeFile.replace(start, start+text.length(),"");
                		}else {
    						System.out.println("文件解析异常:"+javaFile.getAbsolutePath());
    					}
                    }
                	fw.write(codeFile.toString());//将字符串写入到指定的路径下的文件中
                	fw.close();
                } catch (IOException e) { 
                	e.printStackTrace(); 
                	}
                String[] args = {javaFile.getName().split("\\\\.")[0],cweMap.get(cwe),Integer.toString(i),"false",cwe.replaceAll("CWE", "")};
                i++;
                createXML(targetPath, args);
                //生成有漏洞类文件及其xml说明文件
                //重建java类文件
                File fv = new File(targetPath+"\\"+"v_"+javaFile.getName());//新建一个文件对象,如果不存在则创建一个该文件
                FileWriter fwv;
                StringBuffer codeFilev = new StringBuffer(readFile(javaFile.getAbsolutePath()));
                try {
                	fwv = new FileWriter(fv);
                	for(String text:goodList) {
                		int start = ignoreCaseIndexOf(codeFilev.toString(), text);
                		if(start!=-1) {
                			codeFilev.replace(start, start+text.length(),"");
                		}else {
    						System.out.println("文件解析异常:"+javaFile.getAbsolutePath());
    					}
                    }
                	fwv.write(codeFilev.toString());//将字符串写入到指定的路径下的文件中
                	fwv.close();
                } catch (IOException e) { 
                	e.printStackTrace(); 
                	}
                String[] argsv = {"v_"+javaFile.getName().split("\\\\.")[0],cweMap.get(cwe),Integer.toString(i),"true",cwe.replaceAll("CWE", "")};
                i++;
                createXML(targetPath, argsv);
            }else {   //包含跨文件代码,暂时忽略
    			
    		}
    	}
    	System.out.println("总文件数:"+i);
    }
    /**
     * 根据CWE编号生成函数片段级Java文件及xml标签
     * @param filePath
     */
    public static void GenerateSliceFile(String filePath) {
    	
    }
    /**
     * 删除文件或文件夹
     * @param fileName 文件名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteFileOrDirectory(String fileName) {
        File file = new File(fileName);  // fileName是路径或者file.getPath()获取的文件路径
        if (file.exists()) {
            if (file.isFile()) {
                return deleteFile(fileName);  // 是文件,调用删除文件的方法
            } else {
                return deleteDirectory(fileName);  // 是文件夹,调用删除文件夹的方法
            }
        } else {
            System.out.println("文件或文件夹删除失败:" + fileName);
            return false;
        }
    }
    /**
     * 删除文件
     *
     * @param fileName 文件名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        if (file.isFile() && file.exists()) {
            file.delete();
            System.out.println("删除文件成功:" + fileName);
            return true;
        } else {
            System.out.println("删除文件失败:" + fileName);
            return false;
        }
    }

    /**
     * 删除文件夹
     * 删除文件夹需要把包含的文件及文件夹先删除,才能成功
     *
     * @param directory 文件夹名
     * @return 删除成功返回true,失败返回false
     */
    public static boolean deleteDirectory(String directory) {
        // directory不以文件分隔符(/或\)结尾时,自动添加文件分隔符,不同系统下File.separator方法会自动添加相应的分隔符
        if (!directory.endsWith(File.separator)) {
            directory = directory + File.separator;
        }
        File directoryFile = new File(directory);
        // 判断directory对应的文件是否存在,或者是否是一个文件夹
        if (!directoryFile.exists() || !directoryFile.isDirectory()) {
            System.out.println("文件夹删除失败,文件夹不存在" + directory);
            return false;
        }
        boolean flag = true;
        // 删除文件夹下的所有文件和文件夹
        File[] files = directoryFile.listFiles();
        for (int i = 0; i < files.length; i++) {  // 循环删除所有的子文件及子文件夹
            // 删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            } else {  // 删除子文件夹
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
        }

        if (!flag) {
            System.out.println("删除失败");
            return false;
        }
        // 最后删除当前文件夹
        if (directoryFile.delete()) {
            System.out.println("删除成功:" + directory);
            return true;
        } else {
            System.out.println("删除失败:" + directory);
            return false;
        }
    }
    /**
     * 根据传入的参数创建XML标签文件
     * @param path:目标文件路径
     * @param args {文件名,category的值,test-number的值,vulnerability的值,cwe编号}
     */
    public static void createXML(String path,String[] args) {
    	// 1.声明文件名称
        String fileName = args[0];
        // 2.创建dom对象
        Document document = DocumentHelper.createDocument();
        // 3.添加节点,根据需求添加,这里我只是设置了一个head节点,下面有name和age两个子节点
        Element test_metadata = document.addElement("test-metadata");
        Element wbe_version = test_metadata.addElement("wbe-version");
        wbe_version.setText("1.1");
        Element category = test_metadata.addElement("category");
        category.addText(args[1]);
        Element test_number = test_metadata.addElement("test-number");
        test_number.addText(args[2]);
        Element vulnerability = test_metadata.addElement("vulnerability");
        vulnerability.addText(args[3]);
        Element cwe = test_metadata.addElement("cwe");
        cwe.addText(args[4]);
        // 4、格式化模板
        //OutputFormat format = OutputFormat.createCompactFormat();
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("UTF-8");
        // 5、生成xml文件
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            XMLWriter writer = new XMLWriter(out, format);
            writer.write(document);
            writer.close();
        } catch (IOException e) {
            System.out.println("生成xml文件失败。文件名【" + fileName + "】");
        }
        // 6、生成的XML利用文件输出流输出到文件
        try (FileOutputStream fos = new FileOutputStream(path+"\\"+fileName + ".xml")) {
            fos.write(out.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 判断字符串中是否包含字母
     * @param str
     * @return
     */
    public static boolean check(String str) {
    	String regex=".*[a-zA-Z]+.*";
    	Matcher m=Pattern.compile(regex).matcher(str);
    	return m.matches();
    }
    /** 
     * 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始,不区分大小。 
     *  
     * @param subject 被查找字符串。 
     * @param search 要查找的子字符串。 
     * @return 指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。 
     */  
    public static int ignoreCaseIndexOf(String subject, String search) {  
        return ignoreCaseIndexOf(subject, search,-1);  
    }  
      
    /** 
     * 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始,不区分大小。 
     *  
     * @param subject 被查找字符串。 
     * @param search 要查找的子字符串。 
     * @param fromIndex 开始查找的索引位置。其值没有限制,如果它为负,则与它为 0 的效果同样:将查找整个字符串。 
     *          如果它大于此字符串的长度,则与它等于此字符串长度的效果相同:返回 -1。 
     * @return 指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。 
     */  
    public static int ignoreCaseIndexOf(String subject, String search, int fromIndex) {  
        //当被查找字符串或查找子字符串为空时,抛出空指针异常。  
        if (subject == null || search == null) {  
            throw new NullPointerException("输入的参数为空");  
        }  
        fromIndex = fromIndex < 0 ? 0 : fromIndex;  
        if (search.equals("")) {  
            return fromIndex >= subject.length() ? subject.length() : fromIndex;  
        }  
        int index1 = fromIndex;  
        int index2 = 0;   
        char c1;  
        char c2;  
        loop1: while (true) {  
            if (index1 < subject.length()) {  
                c1 = subject.charAt(index1);  
                c2 = search.charAt(index2);  
            } else {  
                break loop1;  
            }  
            while (true) {  
                if (isEqual(c1, c2)) {  
                    if (index1 < subject.length() - 1  
                            && index2 < search.length() - 1) {  
                        c1 = subject.charAt(++index1);  
                        c2 = search.charAt(++index2);  
                    } else if (index2 == search.length() - 1) {  
                        return fromIndex;  
                    } else {  
                        break loop1;  
                    }  
                } else {  
                    index2 = 0;  
                    break;  
                }  
            }  
            //重新查找子字符串的位置  
            index1 = ++fromIndex;  
        }  
        return -1;  
    }  
     
    /** 
     * 判断两个字符是否相等。 
     * @param c1 字符1 
     * @param c2 字符2 
     * @return 若是英文字母,不区分大小写,相等true,不等返回false; 
     *          若不是则区分,相等返回true,不等返回false。 
     */  
    private static boolean isEqual(char c1,char c2){  
            //  字母小写                   字母大写  
        if(((97<=c1 && c1<=122) || (65<=c1 && c1<=90))  
                && ((97<=c2 && c2<=122) || (65<=c2 && c2<=90))  
                && ((c1-c2==32) || (c2-c1==32))){  
            return true;  
        }  
        else if(c1==c2){  
            return true;  
        }  
        return false;  
    }  
    /**
     * 将java文件读取成字符串返回
     * @param file
     * @return
     * @throws IOException
     */
    public static String readFile(String file) throws IOException {
    	BufferedReader reader = new BufferedReader(new FileReader (file));
    	String line = null;
    	StringBuilder stringBuilder = new StringBuilder();
    	String ls = System.getProperty("line.separator");
    	try {
    	while((line = reader.readLine()) != null) {
    	stringBuilder.append(line);
    	stringBuilder.append(ls);
    	}
    	return stringBuilder.toString();
    	} finally {
    	reader.close();
    	}
    	}
}

2024.06.14更新:
我把我处理好的数据集放在下面,需要的自行下载:

链接:https://pan.baidu.com/s/17cUtYQebqfQwELP43DwUhw?pwd=7rc8 
提取码:7rc8
<think>好的,我现在需要解决用户的问题:如何用Python编写基于神经网络的安全漏洞检测器来检查C语言程序中的问题。用户提到希望基于神经网络,并且参考了引用[4]中的方法,特别是中间语言控制流图的特征提取方案。 首先,我需要理解用户的需求。用户想要一个Python程序,使用神经网络来检测C语言代码中的安全漏洞。根据引用[4],现有的方法多是将源代码转为文本序列,使用RNN处理,但这样会忽略控制流特征。而用户提到的文章贡献是设计了一种基于中间语言控制流图(CFG)的方案,这样可以保留更多结构信息。 接下来,我需要确定实现步骤。根据引用[4],关键步骤可能包括:1. 将C代码转换为中间语言(如LLVM IR)并提取控制流图;2. 将CFG转换为适合神经网络输入的图结构数据;3. 构建图神经网络(GNN)模型;4. 训练和评估模型。 首先,数据预处理部分。如何将C代码转为LLVM IR?可能需要使用Clang编译器,或者工具如Joern。然后提取CFG。例如,使用Clang的API或者工具生成控制流图,可能需要编写脚本处理这些步骤。 然后,图表示。每个CFG需要转换为节点和边的结构,节点代表基本块,边代表控制流。每个节点的特征可能包括操作码、操作数等,需要向量化。这部分可能需要使用一些图处理库,如PyTorch Geometric或DGL。 接下来,模型构建。根据引用[4],使用图神经网络,如GCN、GAT等。需要设计一个GNN模型,能够处理图结构数据,并输出是否存在漏洞的分类结果。这里可能需要参考现有的GNN模型架构,并调整以适应代码漏洞检测的任务。 训练数据方面,用户需要标记好的数据集,比如SARD漏洞数据集或者真实项目中的漏洞数据。数据预处理后,划分训练集、验证集和测试集。 代码实现部分,Python中可以使用PyTorch或TensorFlow,结合图神经网络库。例如,使用PyTorch Geometric来定义GNN层,构建模型,然后训练和评估。 可能遇到的挑战包括:如何有效提取CFG特征,处理不同大小的图结构,以及数据不平衡问题(漏洞样本可能较少)。需要数据增强或采用特定的损失函数。 另外,用户提供的引用[3]提到C#中调用Python的库,但这里用户明确要求用Python,所以可以直接使用Python的深度学习库。引用[4]中的方法强调控制流图,所以必须确保正确提取这些结构。 现在需要将上述思路整理成步骤,并给出示例代码框架。同时,确保步骤符合用户的需求,并引用相关文献中的方法,特别是中间语言控制流图的方案。</think>### 基于神经网络的C语言代码漏洞检测实现步骤 #### 1. **代码特征提取(控制流图生成)** 将C语言代码转换为中间语言(LLVM IR)并提取控制流图: ```python # 使用clang生成LLVM IR def generate_llvm_ir(c_file): import subprocess cmd = f"clang -S -emit-llvm {c_file} -o {c_file}.ll" subprocess.run(cmd, shell=True, check=True) # 使用networkx构建控制流图 def build_cfg(llvm_file): from llvmlite import ir, analysis import networkx as nx cfg = nx.DiGraph() # 解析LLVM IR并构建CFG(伪代码) # ... return cfg ``` #### 2. **图结构数据转换** 将控制流图转换为神经网络可处理的格式: ```python def graph_to_tensor(cfg): import torch from torch_geometric.data import Data # 节点特征(示例:基本块操作码的one-hot编码) node_features = [...] # 边连接关系(source,target) edge_index = [[...],[...]] return Data(x=torch.tensor(node_features), edge_index=torch.tensor(edge_index)) ``` #### 3. **图神经网络模型构建** 基于PyTorch Geometric实现图卷积网络: ```python import torch from torch_geometric.nn import GCNConv class VulnDetector(torch.nn.Module): def __init__(self, num_features): super().__init__() self.conv1 = GCNConv(num_features, 16) self.conv2 = GCNConv(16, 8) self.classifier = torch.nn.Linear(8, 2) # 二分类 def forward(self, data): x, edge_index = data.x, data.edge_index x = self.conv1(x, edge_index).relu() x = self.conv2(x, edge_index) return self.classifier(x) ``` #### 4. **训练与验证** ```python # 数据集加载(假设已预处理为PyG格式) dataset = load_dataset() model = VulnDetector(num_features=128) optimizer = torch.optim.Adam(model.parameters(), lr=0.01) criterion = torch.nn.CrossEntropyLoss() for epoch in range(100): for data in dataset: out = model(data) loss = criterion(out, data.y) # 假设data.y包含标签 loss.backward() optimizer.step() ``` ### 关键技术细节 1. **特征工程** - 节点特征:基本块的操作码类型、操作数值类型、上下文关系等 - 边特征:控制流类型(条件跳转/直接跳转) 2. **模型优化** - 使用图注意力机制(GAT)增强关键路径识别能力 - 添加全局池化层处理可变图大小: ```python from torch_geometric.nn import global_mean_pool x = global_mean_pool(x, data.batch) # 聚合图特征 ``` 3. **数据增强** - 通过代码变异生成等价漏洞样本 - 使用对抗训练增强鲁棒性 ### 典型应用场景 1. 开发阶段集成到CI/CD流程中 2. 开源项目代码审计 3. 嵌入式系统固件安全分析 [^1]: 本实现参考了中间语言控制流图的代码特征提取方案,通过保留代码结构特征提升检测精度[^4]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值