awkの逆参照に問題があります。

awkの逆参照に問題があります。

最近はセキュリティロギングに興味があり、bash-shellをよりよく扱いたいと思います。 awkは9つの逆参照だけを保存することを発見しました。ただし、10個の逆参照を使用する必要があります。

試験を終えた

awk '{print gensub(/^([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}).+?\sID\s(\[[0-9]{4}\]).+?\sTargetUserName\s=\s(.+?)\sTargetDomainName\s=\s(.+?)\sTargetLogonId\s=\s(.+?)\sLogonType\s=\s([0-9]{1,2})\s(.+?\sWorkstationName\s=\s(.+?)\sLogonGuid\s=\s.+?TransmittedServices\s=\s.+?\sLmPackageName\s=\s.+?KeyLength\s=\s.+?\sProcessId\s=\s.+?\sProcessName\s=\s.+?\sIpAddress\s=\s(.+?)\sIpPort\s\=\s([0-9]{1,}))?.+?$/,"\\5,\\4,\\3,\\2\\6,\\1,\\8,\\9,","g") }'

ターゲット文字列(実際には何千もの文字列)

2017-03-21T02:00:00 kornawesome Security/Microsoft-Windows-Security-Auditing ID [4624] :EventData/Data -> SubjectUserSid = S-1-5-18 SubjectUserName = PRETENDERS$ SubjectDomainName = WORKGROUP SubjectLogonId = 0x00000000000004j7 TargetUserSid = X-12-54-181 TargetUserName = SYSTEMS TargetDomainName = NT AUTHORITY TargetLogonId = 0x00000000000003e7 LogonType = 8 LogonProcessName = Lxxoi   AuthenticationPackageName = Negotiate WorkstationName = - LogonGuid = {00344000-0000-0000-0000-0000000003440} TransmittedServices = - LmPackageName = Stainless KeyLength = 0 ProcessId = 0x0000000000000244 ProcessName = C:/Windows/System32/services.exe IpAddress = 10.0.0.0 IpPort = 10.5.3.2 ImpersonationLevel = %%1122

awkを使用して実行できる他の方法がある場合は、デフォルトのbash配列と連想配列を使用したいと思います。私(初心者)のために...親切な説明もお願いします。

答え1

セキュリティログの問題の1つは、一部のテキストがユーザー制御下にある可能性があるため、正規表現を使用してコンテンツを分割することが問題になることです。ただし、複数の式を使用して問題を分割することができ、これは9つの逆参照制限を中心に機能します。たとえば、すべてのログエントリがタイムスタンプで始まる場合は、そのエントリを削除できます。

awk '{t=$1 ;$1=""; 
print gensub(/^.+?\sID\s(\[[0-9]{4}\]).+?\sTargetUserName\s=\s(.+?)\sTargetDomainName\s=\s(.+?)\sTargetLogonId\s=\s(.+?)\sLogonType\s=\s([0-9]{1,2})\s(.+?\sWorkstationName\s=\s(.+?)\sLogonGuid\s=\s.+?TransmittedServices\s=\s.+?\sLmPackageName\s=\s.+?KeyLength\s=\s.+?\sProcessId\s=\s.+?\sProcessName\s=\s.+?\sIpAddress\s=\s(.+?)\sIpPort\s\=\s([0-9]{1,}))?.+?$/,"\\4,\\3,\\2,\\1\\5" t ",\\7,\\8,","g") }'

オプションであるため、WorkstationName\s=\s(.+?)\sLogonGuidパターンの一部として使用できます。

awk {t=$1; $1="" ; printf("%s", gensub(/^.+?WorkstationName\s=\s(.+?)\sLogonGuid.*$/,"\\1,")); printf("%s,", t)}

フィールドを取り出すと、この操作を繰り返すことができます。

@casはコメントで、データを前のコンテンツEventData/Data ->と次のコンテンツの2つの部分として見ることができ、次のコンテンツを分割できることを指摘しました=(スペースはスペースと同じです)。もう一度ステップをキー/値のペアとして処理し、オプションの/\s\S+\s=\s/4番目のパラメータを分割してsplitキーを取得します。いくつかの重要な仮定があります。つまり、ユーザーが行に等号を入れることができず、各データに単語キーがあるという仮定があります。キーと値のインデックスは1だけ異なり、行の最初の部分はで終わりますv[1]

/usr/bin/awk '{
    n=split($0,v,/\s\S+\s=\s/,k)
    printf("There are %d fields\n",n)
    for(i=0;i<n;i++) { printf("%d key \"%s\" value \"%s\"\n",i,k[i],v[i+1]) }
}'

サンプルデータに付属

There are 22 fields
0 key "" value "2017-03-21T02:00:00 kornawesome Security/Microsoft-Windows-Security-Auditing ID [4624] :EventData/Data ->"
1 key " SubjectUserSid = " value "S-1-5-18"
2 key " SubjectUserName = " value "PRETENDERS$"
3 key " SubjectDomainName = " value "WORKGROUP"
4 key " SubjectLogonId = " value "0x00000000000004j7"
5 key " TargetUserSid = " value "X-12-54-181"
6 key " TargetUserName = " value "SYSTEMS"
7 key " TargetDomainName = " value "NT AUTHORITY"
8 key " TargetLogonId = " value "0x00000000000003e7"
9 key " LogonType = " value "8"
10 key " LogonProcessName = " value "Lxxoi  "
11 key " AuthenticationPackageName = " value "Negotiate"
12 key " WorkstationName = " value "-"
13 key " LogonGuid = " value "{00344000-0000-0000-0000-0000000003440}"
14 key " TransmittedServices = " value "-"
15 key " LmPackageName = " value "Stainless"
16 key " KeyLength = " value "0"
17 key " ProcessId = " value "0x0000000000000244"
18 key " ProcessName = " value "C:/Windows/System32/services.exe"
19 key " IpAddress = " value "10.0.0.0"
20 key " IpPort = " value "10.5.3.2"
21 key " ImpersonationLevel = " value "%%1122"

ここでは、さらに一歩進んで、データという連想配列を作成できます。

for(i=1;i<n;i++) {gsub(/[ =]/,"",k[i]);data[k[i]]=v[i+1]}

data["IpPort"]これにより、フィールド20か21かを気にせずにこのような内容を印刷できます。

答え2

icarusはすでにこの質問に答えているので、awk次のように日付とIDを変数として抽出し、イベントデータをハッシュ(連想配列)に抽出する方法は次のとおりですperl

#!/usr/bin/perl -l

use strict;

while(<>) {
  if (m/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}).+?\sID\s(\[\d{4}\]).*?Data -> (.*)$/) {
    my ($date,$id,$eventdata) = ($1,$2,$3);

    print $date;
    print $id;

    # decorate the key names with a tab (i.e. add a tab before each) 
    $eventdata =~ s/([^[:blank:]]+) *= */\t$1=/g;
    # remove tab from beginning of $eventdata
    $eventdata =~ s/^\t//;       #/

    # split $eventdata on tabs, and split again into key=value pairs
    # and store in %data hash.    
    my %data = map { my($k,$v) = split("=",$_,2); $k => $v } split(/ *\t/,$eventdata);

    foreach my $key (sort keys %data) { printf "%s=%s\n", $key, $data{$key} };
  };
};

(この#/説明はU&Lの壊れたPerl構文の強調を修正するためのものです。)

,2ジョブは、各キーと値のペアを最大に分割して終了しますsplit("=",$_,2)二つフィールド: まで最初 =記号とそれ以降のすべて。つまり、値に符号が含まれているかどうかは重要ではありません=。これは、awkよりもPerlで行う方が簡単です。ループの先頭の最初の2行に示すように、正規表現とキャプチャグループを使用する方が簡単ですwhile(<>)

たとえば、別の名前で保存してkei.pl実行可能にし、chmod +x kei.pl次のように実行します。

$ ./kei.pl input 
2017-03-21T02:00:00
[4624]
AuthenticationPackageName=Negotiate
ImpersonationLevel=%%1122
IpAddress=10.0.0.0
IpPort=10.5.3.2
KeyLength=0
LmPackageName=Stainless
LogonGuid={00344000-0000-0000-0000-0000000003440}
LogonProcessName=Lxxoi
LogonType=8
ProcessId=0x0000000000000244
ProcessName=C:/Windows/System32/services.exe
SubjectDomainName=WORKGROUP
SubjectLogonId=0x00000000000004j7
SubjectUserName=PRETENDERS$
SubjectUserSid=S-1-5-18
TargetDomainName=NT AUTHORITY
TargetLogonId=0x00000000000003e7
TargetUserName=SYSTEMS
TargetUserSid=X-12-54-181
TransmittedServices=-
WorkstationName=-

ただし、日付とIDもハッシュに含めるには、行の%data = map ...後に次を追加してprint $date;print $id;行を削除します。

$data{'DATE'} = $date;
$data{'ID'} = $id;

関連情報