2011-04-30 79 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