2010-08-04 4 views
8

Проблема с разбором журналов Snort с использованием модуля pyparsing.Синхронизация журналов Snort с PyParsing

Проблема состоит в том, чтобы отделить журнал Snort (который содержит многострочные записи, разделенные пустой строкой) и получать пираринг для синтаксического анализа каждой записи как целого фрагмента, а не читать строки за строкой и ожидать, что грамматика будет работать с в каждой строке (очевидно, это не так).

Я попытался преобразовать каждый фрагмент в временную строку, удалив строки новой строки внутри каждого фрагмента, но он отказывается обрабатывать правильно. Возможно, я полностью ошибаюсь, но я так не думаю (аналогичная форма отлично работает для журналов типа syslog, но это однострочные записи и поэтому поддаются основному файловому итератору/обработке строк)

Вот пример лога и код, который я до сих пор:

[**] [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, вы делаете это бесправие, и т.д. ., сильно оценен.

ответ

13
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']] 
+0

Ты бог. Это решение выходит за рамки моего опыта, но скоро будет реализовано, как только я заставлю себя понять все рабочие части. Спасибо! –

+0

+1 - Хороший ответ ~ unutbu, избили меня до удара! (Ваш код группы выглядит довольно сумасшедшим, мне придется разобраться в нем, когда я получу несколько минут.) – PaulMcG

+0

+ много просто для прекрасного и элегантного использования 'groupby'. – katrielalex

0

Ну, я не знаю 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, вы можете сделать это, прежде чем yield ИНГ его.

+0

Спасибо, это намного более чистое, чем оригинал, однако все еще задыхается, ожидая [**] как вторую строку, а не первую строку следующего фрагмента. –

+0

Я все еще не уверен, что понимаю. Вы имеете в виду, что 'pyparsing' не понимает куски? Я считаю, что он рассматривает новые строки как пробелы и игнорирует их. – katrielalex

4

У вас есть некоторые регулярные выражения отучиться делать, но, надеюсь, это не будет слишком болезненным. Самый большой преступник в вашем мышлении является использование этой конструкции:

some_stuff + Regex(".*") + 
       Suppress(string_representing_where_you_want_the_regex_to_stop) 

Каждый subparser в Pyparsing парсер довольно много автономной, и работает последовательно через входящий текст. Таким образом, термин Regex не имеет возможности смотреть в будущее на следующее выражение, чтобы увидеть, где должно останавливаться повторение '*'. Другими словами, выражение Regex(".*") будет читать только до конца строки, так как здесь ".*" останавливается без указания многострочного.

В pyparsing эта концепция реализована с использованием SkipTo. Вот как пишется ваша строка заголовка:

header = Suppress("[**] [") + Combine(integer + ":" + integer + ":" + integer) + 
      Suppress("]") + Regex(".*") + Suppress("[**]") 

Ваш «*» проблема становится решена путем изменения его:

header = Suppress("[**] [") + Combine(integer + ":" + integer + ":" + integer) + 
      Suppress("]") + SkipTo("[**]") + Suppress("[**]") 

То же самое для ЦБС.

Одна последняя ошибка, ваше определение даты коротка один «:» + целое:

date = integer + "/" + integer + "-" + integer + ":" + integer + "." + 
      Suppress(integer) 

должно быть:

date = integer + "/" + integer + "-" + integer + ":" + integer + ":" + 
      integer + "." + Suppress(integer) 

Я думаю, что эти изменения будут достаточно, чтобы начать разбор вашего данные журнала.

Вот некоторые другие предложения типа:

У вас есть много повторных Suppress("]") выражений. Я начал определение все мои suppressable знаки препинания в очень компактной и легкой для поддержания заявления, как это:

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.

+0

Да. Я признаю, что я определенно потянулся за молотом с регулярным выражением на этом (вы должны это увидеть, это довольно громоздко), но прошлой ночью нырнул в пипаринг, и это то, к чему я пришел. Определенно некоторые изменения парадигмы, но с огромным количеством данных и moreso, дисперсия данных и полей, pyparsing был единственным другим вариантом. Благодарим вас за понимание! –

+0

И от автора Pyparsing nontheless! Еще раз спасибо! –

+0

Комментарий с вопросом: больше не нужно использовать метод setResultsName() для полей именования? Это похоже на подразумеваемый ярлык выше, но я не могу найти это в документации. Благодаря! –

Смежные вопросы