Наличие проблемы с парсингом журналов Фырканья с помощью pyparsing модуля.
Проблема с разделением журнала Фырканья (который имеет многострочные записи, разделенный пустой строкой) и получение pyparsing для парсинга каждой записи в целом разделяют на блоки, вместо того, чтобы читать в линию за линией и ожидание, что грамматика работает с каждой строкой (очевидно, это не делает.)
Я попытался преобразовать каждый блок во временную строку, сняв новые строки в каждом блоке, но это отказывается обрабатывать правильно. Я могу быть полностью на ложном пути, но я не думаю так (подобная форма работает отлично на журналы типа системного журнала, но это - короткие записи и тем самым предоставьте себя своему основному итератору файла / обработка строки),
Вот образец журнала и кода, который я имею до сих пор:
[**] [1:486:4] ICMP Destination Unreachable Communication with Destination Host is Administratively Prohibited [**]
[Classification: Misc activity] [Priority: 3]
08/03-07:30:02.233350 172.143.241.86 -> 63.44.2.33
ICMP TTL:61 TOS:0xC0 ID:49461 IpLen:20 DgmLen:88
Type:3 Code:10 DESTINATION UNREACHABLE: ADMINISTRATIVELY PROHIBITED HOST FILTERED
** ORIGINAL DATAGRAM DUMP:
63.44.2.33:41235 -> 172.143.241.86:4949
TCP TTL:61 TOS:0x0 ID:36212 IpLen:20 DgmLen:60 DF
Seq: 0xF74E606
(32 more bytes of original packet)
** END OF DUMP
[**] ...more like this [**]
И обновленный код:
def snort_parse(logfile):
header = Suppress("[**] [") + Combine(integer + ":" + integer + ":" + integer) + Suppress("]") + Regex(".*") + Suppress("[**]")
cls = Optional(Suppress("[Classification:") + Regex(".*") + Suppress("]"))
pri = Suppress("[Priority:") + integer + Suppress("]")
date = integer + "/" + integer + "-" + integer + ":" + integer + "." + Suppress(integer)
src_ip = ip_addr + Suppress("->")
dest_ip = ip_addr
extra = Regex(".*")
bnf = header + cls + pri + date + src_ip + dest_ip + extra
def logreader(logfile):
chunk = []
with open(logfile) as snort_logfile:
for line in snort_logfile:
if line !='\n':
line = line[:-1]
chunk.append(line)
continue
else:
print chunk
yield " ".join(chunk)
chunk = []
string_to_parse = "".join(logreader(logfile).next())
fields = bnf.parseString(string_to_parse)
print fields
Любая справка, указатели, RTFMs, Вы Делаете Его Заблуждения, и т.д., значительно ценивший.
import pyparsing as pyp
import itertools
integer = pyp.Word(pyp.nums)
ip_addr = pyp.Combine(integer+'.'+integer+'.'+integer+'.'+integer)
def snort_parse(logfile):
header = (pyp.Suppress("[**] [")
+ pyp.Combine(integer + ":" + integer + ":" + integer)
+ pyp.Suppress(pyp.SkipTo("[**]", include = True)))
cls = (
pyp.Suppress(pyp.Optional(pyp.Literal("[Classification:")))
+ pyp.Regex("[^]]*") + pyp.Suppress(']'))
pri = pyp.Suppress("[Priority:") + integer + pyp.Suppress("]")
date = pyp.Combine(
integer+"/"+integer+'-'+integer+':'+integer+':'+integer+'.'+integer)
src_ip = ip_addr + pyp.Suppress("->")
dest_ip = ip_addr
bnf = header+cls+pri+date+src_ip+dest_ip
with open(logfile) as snort_logfile:
for has_content, grp in itertools.groupby(
snort_logfile, key = lambda x: bool(x.strip())):
if has_content:
tmpStr = ''.join(grp)
fields = bnf.searchString(tmpStr)
print(fields)
snort_parse('snort_file')
дает
[['1:486:4', 'Misc activity', '3', '08/03-07:30:02.233350', '172.143.241.86', '63.44.2.33']]
Ну, я не знаю Snort или pyparsing
, так что заранее извиняюсь, если я скажу что-то глупое. Мне неясно, заключается ли проблема в том, что pyparsing
не может обрабатывать записи, или вы не можете отправить их на pyparsing
в правильном формате. Если последнее, почему бы не сделать что-то подобное?
def logreader( path_to_file ):
chunk = [ ]
with open( path_to_file ) as theFile:
for line in theFile:
if line:
chunk.append( line )
continue
else:
yield "".join( *chunk )
chunk = [ ]
Конечно, если вам нужно изменить каждый фрагмент перед его отправкой на pyparsing
, вы можете сделать это до того, как даст
его .
Вам нужно отучиться от регулярных выражений, но, надеюсь, это не будет слишком болезненно. Самым большим виновником вашего мышления является использование этой конструкции:
some_stuff + Regex(".*") +
Suppress(string_representing_where_you_want_the_regex_to_stop)
Каждый подпарсер в синтаксическом анализаторе pyparsing в значительной степени автономен и последовательно работает с входящим текстом. Таким образом, термин Regex не имеет возможности заглянуть вперед к следующему выражению, чтобы увидеть, где должно остановиться повторение '*'
. Другими словами, выражение Regex (". *")
будет просто читать до конца строки, поскольку именно там ". *"
останавливается без указания multiline.
В pyparsing эта концепция реализована с помощью SkipTo. Вот как написана ваша строка заголовка:
header = Suppress("[**] [") + Combine(integer + ":" + integer + ":" + integer) +
Suppress("]") + Regex(".*") + Suppress("[**]")
Ваша проблема с ". *" Решается путем изменения ее на:
header = Suppress("[**] [") + Combine(integer + ":" + integer + ":" + integer) +
Suppress("]") + SkipTo("[**]") + Suppress("[**]")
То же самое для cls.
И последняя ошибка, ваше определение даты короче на единицу ':' + целое число:
date = integer + "/" + integer + "-" + integer + ":" + integer + "." +
Suppress(integer)
должно быть:
date = integer + "/" + integer + "-" + integer + ":" + integer + ":" +
integer + "." + Suppress(integer)
Я думаю, этих изменений будет достаточно, чтобы начать анализ данных журнала.
Вот еще несколько предложений по стилю:
У вас много повторяющихся Подавить ("]")
выражений. Я начал определять все мои подавляемые знаки препинания в очень компактном и простом в обслуживании операторе вроде этого:
LBRACK,RBRACK,LBRACE,RBRACE = map(Suppress,"[]{}")
(разверните, чтобы добавить любые другие символы пунктуации, которые вам нравятся). Теперь я могу использовать эти символы по их символическим именам, и полученный код мне немного легче читать.
Заголовок начинается с header = Suppress ("[**] [") + ...
. Мне никогда не нравилось видеть пробелы, встроенные в литералы таким образом, так как он обходит часть устойчивости синтаксического анализа, которую дает вам pyparsing с его автоматическим пропуском пробелов.Если по какой-то причине пробел между «[**]» и «[» был изменен на использование 2 или 3 пробелов или табуляции, то ваш подавленный литерал не сработает. Объедините это с предыдущим предложением, и заголовок будет начинаться с
header = Suppress("[**]") + LBRACK + ...
Я знаю, что это сгенерированный текст, поэтому изменение этого формата маловероятно, но он лучше подходит для сильных сторон pyparsing.
После того, как вы проанализируете свои поля, начните присваивать имена результатов различным элементам в вашем анализаторе. Это упростит лот , чтобы получить данные позже. Например, измените cls на:
cls = Optional(Suppress("[Classification:") +
SkipTo(RBRACK)("classification") + RBRACK)
. Это позволит вам получить доступ к данным классификации, используя fields.classification
.