2011-04-30 80 views
1

这是一些Perl代码,它将两个文件作为输入。这些文件包含TCP数据包。它使用第一个文件中的数据包训练正常数据包,然后在第二个文件中打印异常数据包。如何打印文件中所有输入的计算得分?

while (<>) { 
    if (($time, $to, $port, $from, $duration, $flags, $length, $text) = /(.{19}) (.{15}):(\d+) (.{15}):\d+ \+(\d+) (\S+) (\d+) (.*)/) { 
     $text =~ s/\^M//g; 
     $text =~ s/\^ /\n/g; 
     if (($port == 25 || $port == 80) && $text =~ /\n\n/) {$text = "$`\n";} 
     $text =~ s/^\^@//; 
     if ($time =~ /(\d\d)\/(\d\d)\/\d\d\d\d (\d\d):(\d\d):(\d\d)/) { 
      $now = ((($1 * 31 + $2) * 24 + $3) * 60 + $4) * 60 + $5; 
     } 
     foreach ($text =~ /.*\n/g) { 
      if (($k, $v) = /(\S*)(.*)/) { 
       $k = substr($k, 0, 30); 
       $v = substr($v, 0, 100); 
       $score = 0; 
       $comment = ""; 
       &alarm($port,  $k); 
       &alarm($to,   $flags); 
       &alarm("To",  "$to:$port"); 
       &alarm($to,   $from); 
       &alarm("$to:$port", $from); 
       if ($score > 30000) { 
        $score = log($score)/(10 * log(10)); 
        printf(" # 0 $time $to %8.6f \#%s\n", $score, substr($comment, 0, 300)); 
       } 
      } 
     } 
    } 
} 

sub alarm { 
    local ($key, $val, $sc) = @_; 
    if ($now < 10300000) { 
     ++$n{$key}; 
     if (++$v{$key . $val} == 1) { 
      ++$r{$key}; 
      $t{$key} = $now; 
     } 
    } elsif ($n{$key} > 0 && !$v{$key . $val}) { 
     $score += ($now - $t{$key}) * $n{$key}/$r{$key}; 
     $comment .= " $key=$val"; 
     $t{$key} = $now; 
    } 
} 

exit; 

我是新来的Perl和一小部分,我的项目,它需要的是一个异常分数将被打印在第二个文件中的所有数据包。任何人都可以告诉如何修改代码?

+0

你寻求帮助与算法,或者只是询问如何打印值出来吗? – 2011-04-30 04:33:18

+0

我只需要打印出每个数据包的分数值,而不仅仅是用于某些数据包...... – 2011-04-30 04:47:46

回答

2

从我在这里可以看到,它看起来好像代码(因为它是现在)在某个截断时间之前查找数据包,并存储它是否在%n%v散列中看到某些条件。

为什么不给你的alarm功能称为$training一个额外的标志。如果为true,则只计算数据包值,否则,计算此异常的分数(如果是1),并返回该值。如果没有异常,或者如果你在训练模式下的时候,刚刚返回零:

sub alarm { 
     my ($key, $val, $training) = @_; 
     my $score = 0; 
     if ($training) { 
      ...do your accounting... 
     } else { 
      ...do your comparisons & set score accordingly... 
     } 
     return $score; 
    } 

扔掉你的大while成一个子程序,并有子程序取一个文件名,无论是在训练模式或不。

 sub examine { 
     my ($file, $training) = @_; 
     if (open my $fh, '<', $file) { 
      while (<$fh>) { 
       ...this is your big while loop... 
       ...pass $training along to your alarm() calls... 
      } 
     } else { 
      die "Failed to open $file: $!\n'; 
     } 
    } 

你的主要程序现在是:

 use constant TRAINING => 1; 

    examine('file1', TRAINING); 
    examine('file2', !TRAINING); 

更多笔记:

  • 使用my()代替local,虽然它不会显着影响这个程序,这是一个好习惯进入。
  • 不要使用众所周知的函数名称alarm,如果它确实没有做任何类型的事情,而是将其命名为check_packet_values,或者对您和您的团队有意义。
  • 停止使用幻数

    use constant { 
        CUTOFF_TIME => 10300000, 
        ANOMALY_SCORE => 30000 
    }; 
    
  • 使用一个真正的日期/时间解析器,这样你的价值有一定的意义。 str2time from Date::Parse会给你你的时间在纪元秒(1970年1月1日以来的秒)。

  • 使用意味着某事的变量名称。 %n%v在此代码中很难理解,但是%n_seen%value_seen(以及%first_seen_time代替%t)。请记住,如果使用较短的变量名称,则代码运行速度不会更快。
  • 在可行时停止使用全局变量。计数器可以是全局的,但是您的注释只能在正在初始化和打印注释的例程中构建。因此,而不是做你在做什么,怎么样:

    $to_score = check_packet_value($to, $flags) 
        and push @comments, "$to=$flags"; 
    ... 
    $score = $to_score + $from_score + ... 
    if (!$training && $score > ANOMALY_THRESHOLD) { 
        print "blah blah blah @comments\n"; 
    } 
    
  • 而且,永远,永远使用$` - 它会导致你的整个脚本巨大的性能处罚(即使它永远不会调用此功能)。相反的:

    if ($text =~ /\n\n/) { $text = $` } 
    

使用

if ($text =~ /(.*)\n\n/) { 
     $text = $1; 
    } 

(编辑:系统将会发出警告约$`)

0

我可能误解了你的问题和评论,所以请原谅我,如果这是你问的不是...

你的printf函数目前驻留在此if ($score > 30000)检查里面,所以你只能得到输出如果$score是> 30000

if ($score>30000) { 
    $score=log($score)/(10*log(10)); 
    printf(" # 0 $time $to %8.6f \#%s\n", $score, substr($comment, 0, 300)); 
} 

如果你要打印输出不管$score的,你只需要检查是否移动的printf线外此。

+0

给出了两个文件作为该程序的输入,并且只需要打印出该数据包中的数据包的异常分数第二个文件... – 2011-04-30 05:00:30

+0

好的,对不起...代码有点难以理解,所以我不确定我能否帮助你。 – 2011-04-30 05:08:04