def validate_process(val_loader, model, criterion, device, print_freq):
    losses = AverageMeter()
    top1 = AverageMeter()

    model.eval()  # switch to evaluate mode

    for i, (pcap, statistic, target) in enumerate(val_loader):

        pcap = pcap.to(device)
        statistic = statistic.to(device)
        target = target.to(device)

        with torch.no_grad():

            output = model(pcap, statistic)  # compute output
            loss = criterion(output, target)  # 计算验证集的 loss

            # measure accuracy and record loss
            prec1 = accuracy(output.data, target)
            losses.update(loss.item(), pcap.size(0))
            top1.update(prec1[0].item(), pcap.size(0))

            if (i + 1) % print_freq == 0:
                logger.info(
                    'Test: [{0}/{1}], Loss {loss.val:.4f} ({loss.avg:.4f}), Prec@1 {top1.val:.3f} ({top1.avg:.3f})'
                    .format(i, len(val_loader), loss=losses, top1=top1))

    logger.info(' * Prec@1 {top1.avg:.3f}'.format(top1=top1))

    return top1.avg
Пример #2
0
def validate_process(val_loader, model, device, print_freq):
    top1 = AverageMeter()

    model.eval()  # switch to evaluate mode

    for i, (pcap, statistic, target) in enumerate(val_loader):

        pcap = (pcap / 255).to(device)  # 也要归一化
        statistic = statistic.to(device)
        statistic = (statistic -
                     mean_val) / std_val  # 首先需要对 statistic 的数据进行归一化
        target = target.to(device)

        with torch.no_grad():

            output, _ = model(pcap, statistic)  # compute output

            # measure accuracy and record loss
            prec1 = accuracy(output.data, target)
            top1.update(prec1[0].item(), pcap.size(0))

            if (i + 1) % print_freq == 0:
                logger.info(
                    'Test: [{0}/{1}], Prec@1 {top1.val:.3f} ({top1.avg:.3f})'.
                    format(i, len(val_loader), top1=top1))

    logger.info(' * Prec@1 {top1.avg:.3f}'.format(top1=top1))

    return top1.avg
Пример #3
0
def train_process(train_loader, model, alpha, criterion_c, criterion_r,
                  optimizer, epoch, device, print_freq):
    """训练一个 epoch 的流程

    Args:
        train_loader (dataloader): [description]
        model ([type]): [description]
        criterion_c ([type]): 计算分类误差
        criterion_l ([type]): 计算重构误差
        optimizer ([type]): [description]
        epoch (int): 当前所在的 epoch
        device (torch.device): 是否使用 gpu
        print_freq ([type]): [description]
    """
    c_loss = AverageMeter()
    r_loss = AverageMeter()
    losses = AverageMeter()  # 在一个 train loader 中的 loss 变化
    top1 = AverageMeter()  # 记录在一个 train loader 中的 accuracy 变化

    model.train()  # 切换为训练模型

    for i, (pcap, statistic, target) in enumerate(train_loader):

        pcap = (pcap / 255).to(device)  # 也要归一化
        statistic = statistic.to(device)
        statistic = (statistic -
                     mean_val) / std_val  # 首先需要对 statistic 的数据进行归一化
        target = target.to(device)

        classific_result, fake_statistic = model(pcap, statistic)  # 分类结果和重构结果
        loss_c = criterion_c(classific_result, target)  # 计算 分类的 loss
        loss_r = criterion_r(statistic, fake_statistic)  # 计算 重构 loss
        loss = alpha * loss_c + loss_r  # 将两个误差组合在一起

        # 计算准确率, 记录 loss 和 accuracy
        prec1 = accuracy(classific_result.data, target)
        c_loss.update(loss_c.item(), pcap.size(0))
        r_loss.update(loss_r.item(), pcap.size(0))
        losses.update(loss.item(), pcap.size(0))
        top1.update(prec1[0].item(), pcap.size(0))

        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if (i + 1) % print_freq == 0:
            logger.info(
                'Epoch: [{0}][{1}/{2}], Loss {loss.val:.4f} ({loss.avg:.4f}), Loss_c {loss_c.val:.4f} ({loss_c.avg:.4f}), Loss_r {loss_r.val:.4f} ({loss_r.avg:.4f}), Prec@1 {top1.val:.3f} ({top1.avg:.3f})'
                .format(epoch,
                        i,
                        len(train_loader),
                        loss=losses,
                        loss_c=c_loss,
                        loss_r=r_loss,
                        top1=top1))
Пример #4
0
def statisticFeature2JSON(folder_path):
    """将 folder_path 中所有的 session 计算统计特征, 并保存为 json 文件; 
    对于一些较大的 pcap 文件, 为了速度, 我们都只处理前 500000 个 packets

    Args:
        folder_path (str): 所在的路径
    """
    pcap_statisticFeature = {}
    featuresCalc = FeaturesCalc(min_window_size=1)  # 初始化计算统计特征的类
    for (root, _, files) in os.walk(folder_path):
        logger.info('正在提取 {} 下 pcap 文件的统计特征'.format(root))
        for Ufile in tqdm.tqdm(files):
            pcapPath = os.path.join(root, Ufile)  # 需要转换的pcap文件的完整路径
            packets = rdpcap(pcapPath)  # 读入 pcap 文件
            if len(packets) < 500000:  # 太大的 pcap 文件
                features = featuresCalc.compute_features(
                    packets_list=packets)  # 计算特征
            else:
                logger.info('正在处理 {} 文件'.format(pcapPath))
                features = featuresCalc.compute_features(
                    packets_list=packets[:500000])
            pcap_statisticFeature[Ufile] = features
    # 将统计特征写入 json 文件
    with open("statistic_features.json", "w") as f:
        json.dump(pcap_statisticFeature, f)
    logger.info('统计特征计算完成, 保存为 json 文件!')
    logger.info('==========\n')
Пример #5
0
def data_loader(pcap_file,
                statistic_file,
                label_file,
                trimed_file_len,
                batch_size=256,
                workers=1,
                pin_memory=True):
    """读取处理好的 npy 文件, 并返回 pytorch 训练使用的 dataloader 数据

    Args:
        pcap_file (str): pcap 文件转换得到的 npy 文件的路径
        statistic_file (str): 统计特征对应的 npy 文件路径
        label_file (str): 上面的 pcap 文件对应的 label 文件的 npy 文件的路径
        trimed_file_len (int): pcap 被裁剪成的长度
        batch_size (int, optional): 默认一个 batch 有多少数据. Defaults to 256.
        workers (int, optional): 处理数据的进程的数量. Defaults to 1.
        pin_memory (bool, optional): 锁页内存, 如果内存较多, 可以设置为 True, 可以加快 GPU 的使用. Defaults to True.

    Returns:
        DataLoader: pytorch 训练所需要的数据
    """
    # 载入 npy 数据
    pcap_data = np.load(pcap_file)  # 获得 pcap 文件
    statistic_data = np.load(statistic_file)  # 获得 统计特征
    label_data = np.load(label_file)  # 获得 label 数据

    # 将 npy 数据转换为 tensor 数据
    pcap_data = torch.from_numpy(pcap_data.reshape(-1, 1,
                                                   trimed_file_len)).float()
    statistic_data = torch.from_numpy(statistic_data).float()
    label_data = torch.from_numpy(label_data).long()
    logger.info('pcap 文件大小, {}; statistic 文件大小, {}; label 文件大小: {}'.format(
        pcap_data.shape, statistic_data.shape, label_data.shape))

    # 将 tensor 数据转换为 Dataset->Dataloader
    res_dataset = torch.utils.data.TensorDataset(pcap_data, statistic_data,
                                                 label_data)  # 合并数据
    res_dataloader = torch.utils.data.DataLoader(
        dataset=res_dataset,
        batch_size=batch_size,
        shuffle=True,
        pin_memory=pin_memory,
        num_workers=1  # set multi-work num read data
    )

    return res_dataloader
Пример #6
0
def save_pcap2npy(pcap_dict,
                  file_name,
                  statistic_feature_json,
                  label2index={}):
    """将 pcap 文件分为训练集和测试集, 并进行保存. 
    => pcap 数据从 二进制 转换为 十进制, 调用 getIntfrom_pcap 函数
    => 从 json 文件读取每一个 pcap 的统计特征
    => 将数据保存在 data 中, data = [[pcap, statistic, label], [], ..]
    => 将 data 数据保存为 npy 文件

    Args:
        pcap_dict (dict): 见函数 get_train_test 的输出
        file_name (str): 最后保存的文件的名称
    """
    with open(statistic_feature_json, "r") as json_read:
        statistic_feature_dict = json.load(json_read)  # 获取每个 pcap 的统计特征

    data = []
    index = 0
    for label, pcap_file_list in pcap_dict.items():
        logger.info('模式, {}, 正在将 {} 的 pcap 保存为 npy'.format(file_name, label))
        if label not in label2index:
            label2index[label] = index
            index = index + 1
        for pcap_file in pcap_file_list:
            pcap_file_name = os.path.normpath(pcap_file).split('\\')[-1]
            pcap_content = getIntfrom_pcap(
                pcap_file)  # 返回 pcap 的 十进制 内容, 这里 pcap_file 是 pcap 文件的路径名
            statistic_feature = statistic_feature_dict[
                pcap_file_name]  # 得到统计特征
            data.append([pcap_content, statistic_feature,
                         label2index[label]])  # 将 pcap 和 label 添加到 data 中去

    np.random.shuffle(data)  # 对数据进行打乱

    pcap_data = np.array([i[0] for i in data])  # raw pcap 减裁
    statistic_data = np.array([i[1] for i in data])  # statistic data
    y = np.array([i[2] for i in data])  # label

    logger.info('数据的大小, {}; 统计特征的大小, {}; 标签的大小, {};'.format(
        pcap_data.shape, statistic_data.shape, y.shape))  # 打印数据大小

    np.save('{}-pcap.npy'.format(file_name), pcap_data)
    np.save('{}-statistic.npy'.format(file_name), statistic_data)
    np.save('{}-labels.npy'.format(file_name), y)

    logger.info('将 {} 的数据保存为 npy !'.format(file_name))
    logger.info('==========\n')

    return label2index
Пример #7
0
def train_process(train_loader, model, criterion, optimizer, epoch, device, print_freq):
    """训练一个 epoch 的流程

    Args:
        train_loader (dataloader): [description]
        model ([type]): [description]
        criterion ([type]): [description]
        optimizer ([type]): [description]
        epoch (int): 当前所在的 epoch
        device (torch.device): 是否使用 gpu
        print_freq ([type]): [description]
    """
    losses = AverageMeter()  # 在一个 train loader 中的 loss 变化
    top1 = AverageMeter()  # 记录在一个 train loader 中的 accuracy 变化

    model.train()  # 切换为训练模型

    for i, (pcap, statistic, target) in enumerate(train_loader):

        pcap = pcap.to(device)
        statistic = statistic.to(device)
        target = target.to(device)

        output = model(pcap, statistic)  # 得到模型预测结果
        loss = criterion(output, target)  # 计算 loss

        # 计算准确率, 记录 loss 和 accuracy
        prec1 = accuracy(output.data, target)
        losses.update(loss.item(), pcap.size(0))
        top1.update(prec1[0].item(), pcap.size(0))

        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        if (i+1) % print_freq == 0:
            logger.info('Epoch: [{0}][{1}/{2}], Loss {loss.val:.4f} ({loss.avg:.4f}), Prec@1 {top1.val:.3f} ({top1.avg:.3f})'.format(
                epoch, i, len(train_loader), loss=losses, top1=top1))
Пример #8
0
def preprocess_pipeline():
    """对流量进行预处理, 处理流程为:
    0. 将所有流量文件转移到新的文件夹, 这时候没有细分, 就是所有文件进行转移
    1. 接着将 pcapng 文件转换为 pcap 文件
    2. 接着将不同的流量新建文件夹, 分成不同的类别, pcap transfer
    3. 接着将 pcap 文件按照五元组分为不同的 session, 使用 SplitCap 来完成 (这一步可以选择, 提取 all 或是 L7)
    计算
    4. 对 session 进行处理, 匿名化处理, ip, mac, port
    5. 将所有的 pcap 转换为一样的大小, 转换前统计一下原始 session 的大小
    6. 对于每一类的文件, 划分训练集和测试集, 获得每一类的所有的 pcap 的路径
    7. 将所有的文件, 最终保存为 npy 的格式
    """
    cfg = setup_config()  # 获取 config 文件
    logger.info(cfg)

    transfer_pcap(cfg.pcap_path.raw_pcap_path,
                  cfg.pcap_path.new_pcap_path)  # 转移文件
    pcapng_to_pcap(cfg.pcap_path.new_pcap_path)  # 将 pcapng 转换为 pcap
    pcap_transfer(
        cfg.pcap_path.new_pcap_path,
        cfg.pcap_path.new_pcap_path)  # 将文件放在指定文件夹中, 这里新的文件夹查看 yaml 配置文件
    pcap_to_session(cfg.pcap_path.new_pcap_path,
                    cfg.tool_path.splitcap_path)  # 将 pcap 转换为 session
    statisticFeature2JSON(
        cfg.pcap_path.new_pcap_path)  # 计算 pcap 的统计特征 (特征可以只计算一次, 后面就不需要再运行了)
    anonymize(cfg.pcap_path.new_pcap_path)  # 对指定文件夹内的所有 pcap 进行匿名化处理
    pcap_trim(
        cfg.pcap_path.new_pcap_path,
        cfg.train.TRIMED_FILE_LEN)  # 将所有的 pcap 转换为一样的大小, 同时统计原始 session 的长度
    train_dict, test_dict = get_train_test(
        cfg.pcap_path.new_pcap_path,
        cfg.train.train_size)  # 返回 train 和 test 的 dict
    label2index = save_pcap2npy(
        train_dict, 'train',
        cfg.pcap_path.statistic_feature)  # 保存 train 的 npy 文件
    save_pcap2npy(test_dict, 'test', cfg.pcap_path.statistic_feature,
                  label2index)  # 保存 test 的 npy 文件
    logger.info('index 与 label 的关系, {}'.format(label2index))
Пример #9
0
def get_tensor_data(pcap_file, statistic_file, label_file, trimed_file_len):
    """读取处理好的 npy 文件, 并返回 pytorch 训练使用的 dataloader 数据

    Args:
        pcap_file (str): pcap 文件转换得到的 npy 文件的路径
        statistic_file (str): 统计特征转换得到的 npy 文件的路径
        label_file (str): 上面的 pcap 文件对应的 label 文件的 npy 文件的路径
        trimed_file_len (int): pcap 被裁剪成的长度
    """
    # 载入 npy 数据
    pcap_data = np.load(pcap_file)  # 获得 pcap 文件
    statistic_data = np.load(statistic_file)
    label_data = np.load(label_file)  # 获得 label 数据

    # 将 npy 数据转换为 tensor 数据
    pcap_data = torch.from_numpy(pcap_data.reshape(-1, 1,
                                                   trimed_file_len)).float()
    statistic_data = torch.from_numpy(statistic_data).float()
    label_data = torch.from_numpy(label_data).long()
    logger.info(
        '导入 Tensor 数据, pcap 文件大小, {}; statistic 大小, {}; label 文件大小: {}'.format(
            pcap_data.shape, statistic_data.shape, label_data.shape))

    return pcap_data, statistic_data, label_data
Пример #10
0
def pcap_transfer(before_folder_path, new_pcap_path):
    """将原始的 pcap 文件从旧的文件夹, 转移到新的文件夹, 并进行好分类

    Args:
        before_folder_path (str): 旧的文件夹的名称
        new_pcap_path (str): 新的文件夹的名称
    """
    for pcap_type in PCAP_LABEL_DICT:
        logger.info('开始移动 {} 类型的 pcap 文件'.format(pcap_type))
        folder_path = os.path.join(new_pcap_path, pcap_type)  # 新的文件夹
        os.makedirs(folder_path, exist_ok=True)  # 新建新的文件夹
        for pcap_name in PCAP_LABEL_DICT[pcap_type]:
            pcap_name_file = '{}.pcap'.format(pcap_name)
            src_path = os.path.join(before_folder_path,
                                    pcap_name_file)  # pcap 文件的原始地址
            dts_path = os.path.join(folder_path, pcap_name_file)
            os.rename(src_path, dts_path)  # 移动文件
            logger.info('文件移动, {} --> {}'.format(src_path, dts_path))

    logger.info('文件移动完毕.')
    logger.info('============\n')
Пример #11
0
def transfer_pcap(before_path, after_path):
    """将 before_path 中的所以文件转移到 after_path 中去

    Args:
        before_path (str): 转移之前的文件路径
        after_path (str): 转移之后的文件路径
    """
    ignore_list = [
        'youtubeHTML5_1.pcap', 'torFacebook.pcap', 'torGoogle.pcap',
        'torTwitter.pcap'
    ]  # 不需要转移的 pcap 文件
    os.makedirs(after_path, exist_ok=True)  # 新建目标目录
    for file in os.listdir(before_path):
        src_path = os.path.join(before_path, file)
        dst_path = os.path.join(after_path, file)
        if file in ignore_list:
            logger.info('忽略文件 {}'.format(src_path))
        else:
            logger.info('开始转移文件, {} --> {}'.format(src_path, dst_path))
            shutil.copy(src_path, dst_path)
    logger.info('文件全部转移完成')
    logger.info('=============\n')
Пример #12
0
def get_file_path(folder_path):
    """获得 folder_path 下 pcap 文件的路径, 以 dict 的形式返回. 
    返回的包含每个大类(Chat, Email), 下每个小类(AIMchat1, aim_chat_3a), 中 pcap 的文件路径.
    返回数据类型如下所示:
    {
        'Chat': {
            'AIMchat1': ['D:\\Traffic-Classification\\data\\preprocess_data\\Chat\\AIMchat1\\AIMchat1.pcap.TCP_131-202-240-87_13393_178-237-24-202_443.pcap', ...]
            'aim_chat_3a': [...],
            ...
        },
        'Email': {
            'email1a': [],
            ...
        },
        ...
    }

    Args:
        folder_path (str): 包含 pcap 文件的根目录名称
    """
    pcap_dict = {}
    for (root, _, files) in os.walk(folder_path):
        if len(files) > 0:
            logger.info('正在记录 {} 下的 pcap 文件'.format(root))
            folder_name_list = os.path.normpath(root).split(
                os.sep
            )  # 将 'D:\Traffic-Classification\data\preprocess_data' 返回为列表 ['D:', 'Traffic-Classification', 'data', 'preprocess_data']
            top_category, second_category = folder_name_list[
                -2], folder_name_list[-1]
            if top_category not in pcap_dict:
                pcap_dict[top_category] = {}
            if second_category not in pcap_dict[top_category]:
                pcap_dict[top_category][second_category] = []
            for Ufile in files:
                pcapPath = os.path.join(root, Ufile)  # 需要转换的pcap文件的完整路径
                pcap_dict[top_category][second_category].append(pcapPath)
    logger.info('将所有的 pcap 文件整理为 dict !')
    logger.info('==========\n')
    return pcap_dict
Пример #13
0
def pcap_to_session(pcap_folder, splitcap_path):
    """将 pcap 文件转换为 session 文件, 将 pcap_folder 下所有的 pcap 文件进行转换

    Args:
        pcap_folder (str): 放置 pcap 文件的路径
        splitcap_path (str): splitcap.exe 工具所在的路径
    """
    splitcap_path = os.path.normpath(splitcap_path)  # 处理成 windows 下的路径格式
    for (root, _, files) in os.walk(pcap_folder):
        # root 是根目录
        # dirs 是在 root 目录下的文件夹, 返回的是一个 list;
        # files 是在 root 目录下的文件, 返回的是一个 list, 所以 os.path.join(root, files) 返回的就是 files 的路径
        for Ufile in files:
            pcap_file_path = os.path.join(root, Ufile)  # pcap 文件的完整路径
            pcap_name = Ufile.split('.')[0]  # pcap 文件的名字
            pcap_suffix = Ufile.split('.')[1]  # 文件的后缀名
            try:
                assert pcap_suffix == 'pcap'
            except:
                logger.warning('查看 pcap 文件的后缀')
                assert pcap_suffix == 'pcap'
            os.makedirs(os.path.join(root, pcap_name), exist_ok=True)  # 新建文件夹
            prog = subprocess.Popen(
                [
                    splitcap_path, "-p", "100000", "-b", "100000", "-r",
                    pcap_file_path, "-o",
                    os.path.join(root, pcap_name)
                ],  # 只提取应用层可以加上, "-y", "L7"
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                shell=True)
            _, _ = prog.communicate()
            # logger.info(err.decode('GB2312'))
            os.remove(pcap_file_path)  # 删除原始的 pcap 文件
            logger.info('处理完成文件 {}'.format(Ufile))
    logger.info('完成 pcap 转换为 session.')
    logger.info('============\n')
Пример #14
0
def CENTIME_train_pipeline(alpha):
    cfg = setup_config()  # 获取 config 文件
    logger.info(cfg)

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    logger.info('是否使用 GPU 进行训练, {}'.format(device))

    model_path = os.path.join(cfg.train.model_dir,
                              cfg.train.model_name)  # 模型的路径
    model = resnet_AE(model_path, pretrained=False,
                      num_classes=12).to(device)  # 初始化模型

    criterion_c = nn.CrossEntropyLoss()  # 分类用的损失函数
    criterion_r = nn.L1Loss()  # 重构误差的损失函数
    optimizer = optim.Adam(model.parameters(), lr=cfg.train.lr)  # 定义优化器
    logger.info('成功初始化模型.')

    train_loader = data_loader(
        pcap_file=cfg.train.train_pcap,
        label_file=cfg.train.train_label,
        statistic_file=cfg.train.train_statistic,
        trimed_file_len=cfg.train.TRIMED_FILE_LEN)  # 获得 train dataloader

    test_loader = data_loader(
        pcap_file=cfg.train.test_pcap,
        label_file=cfg.train.test_label,
        statistic_file=cfg.train.test_statistic,
        trimed_file_len=cfg.train.TRIMED_FILE_LEN)  # 获得 train dataloader

    logger.info('成功加载数据集.')

    best_prec1 = 0
    for epoch in range(cfg.train.epochs):
        adjust_learning_rate(optimizer, epoch, cfg.train.lr)  # 动态调整学习率

        train_process(train_loader, model, alpha, criterion_c, criterion_r,
                      optimizer, epoch, device, 80)  # train for one epoch
        prec1 = validate_process(test_loader, model, device,
                                 20)  # evaluate on validation set

        # remember the best prec@1 and save checkpoint
        is_best = prec1 > best_prec1
        best_prec1 = max(prec1, best_prec1)

        # 保存最优的模型
        save_checkpoint(
            {
                'epoch': epoch + 1,
                'state_dict': model.state_dict(),
                'best_prec1': best_prec1,
                'optimizer': optimizer.state_dict()
            }, is_best, model_path)

    # 下面进入测试模式, 计算每个类别详细的准确率
    logger.info('进入测试模式.')
    model = resnet_AE(model_path, pretrained=True,
                      num_classes=12).to(device)  # 加载最好的模型
    index2label = {j: i
                   for i, j in cfg.test.label2index.items()
                   }  # index->label 对应关系
    label_list = [index2label.get(i) for i in range(12)]  # 12 个 label 的标签
    pcap_data, statistic_data, label_data = get_tensor_data(
        pcap_file=cfg.train.test_pcap,
        statistic_file=cfg.train.test_statistic,
        label_file=cfg.train.test_label,
        trimed_file_len=cfg.train.TRIMED_FILE_LEN)  # 将 numpy 转换为 tensor

    pcap_data = (pcap_data / 255).to(device)  # 流量数据
    statistic_data = (statistic_data.to(device) -
                      mean_val) / std_val  # 对数据做一下归一化
    y_pred, _ = model(pcap_data, statistic_data)  # 放入模型进行预测
    _, pred = y_pred.topk(1, 1, largest=True, sorted=True)

    Y_data_label = [index2label.get(i.tolist()) for i in label_data]  # 转换为具体名称
    pred_label = [
        index2label.get(i.tolist()) for i in pred.view(-1).cpu().detach()
    ]

    logger.info('Alpha:{}'.format(alpha))
    display_model_performance_metrics(true_labels=Y_data_label,
                                      predicted_labels=pred_label,
                                      classes=label_list)

    logger.info('Finished! (* ̄︶ ̄)')