我在 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

安全日志的一个问题是某些文本可能处于用户控制之下,因此使用正则表达式来分解内容是有问题的。然而,您可以使用多个表达式来分解事物,这可以解决 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 ->和后面的内容,并且后面的内容可以拆分=(空格等于空格)。我会更进一步,将其视为键/值对,然后拆分/\s\S+\s=\s/并使用可选的第四个参数来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"

从这里您可以更进一步,创建一个名为 data 的关联数组

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)将每个 key=value 对拆分为最多字段:直到第一的 =符号,以及之后的一切。这意味着该值是否包含符号并不重要=。像这样的事情在 Perl 中比在 awk 中更容易完成。使用正则表达式和捕获组也更容易,如循环开头的前两行所示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;

相关内容