LoginSignup
11
5

More than 3 years have passed since last update.

.evtxファイルをPythonで解析する

Last updated at Posted at 2019-01-04

弁護士が裁判で労働時間を立証する際に使う…かどうかはともかくとして,Windowsのシステムログを記録したEVTX (Windows XML Event Log)ファイルを,WindowsのEvent Viewerアプリケーション以外で処理したい場面があろうかと思います。
Python-Evtx というPythonのライブラリがあるので,試してみましょう。

pip install python-evtx

なお,Python-Evtxのドキュメントはほとんど存在しない状況ですが,プロジェクトページのscriptディレクトリにサンプルスクリプトがあります。一番簡単なのはevtx_dump.pyです。閑話休題。

Python-Evtx導入

evtx.Evtx(filepath)すると,イベントログを扱うオブジェクトが得られます。
「withと一緒に使え」との指示があるので,どうやら処理は基本的に,with evtx.Evtx(filepath) as log:のwithブロック内に書くことになるっぽいです。
任意のレコードを取得したい場合は,log.get_record(number)あたりで取得することになりますが,全レコードを走査することが多いでしょうから,以下はそういう感じにします。

test.py
import Evtx.Evtx as evtx

def main():
    # python test.py hoge.evtx で,hoge.evtxに処理をする。evtx_dump.pyのコピペ。
    import argparse    
    parser = argparse.ArgumentParser(
        description="Dump a binary EVTX file into XML.")
    parser.add_argument("evtx", type=str,
                        help="Path to the Windows EVTX event log file")
    args = parser.parse_args()

    # ここから本題
    with evtx.Evtx(args.evtx) as log:
        for record in log.records():
            # 個々のrecordに対する処理をする。

if __name__ == "__main__":
    main()

個々のレコードをXMLとして処理する。

EVTXはバイナリ形式らしいのですが,個々のレコードは.lxml()関数で,XMLを扱うlxmlライブラリのElementオブジェクトとして取り出すことができます。(lxmlがインストールされていない方は,pip install lxmlして下さい。)

with evtx.Evtx(args.evtx) as log:
    #テスト段階では↓の方が良いかも。
#    elm = log.get_record(1).lxml()
#    print(elm)
    for record in log.records():
        elm = record.lxml()
        print(elm.tag)
# -> "<Element {http://schemas.microsoft.com/win/2004/08/events/event}Event at ID的な文字列>"がバーっと。

print(record.xml())してみると分かるのですが,個々のrecordは次のような構造を持っているようです。それぞれの内容については,Windowsのシステムログに詳しい人に聞いてみてください。(私は素人ですので…)

  • Event
    • System
      • Provider
      • EventID #たぶんWindowsにおけるイベントID
      • Version
      • Level
      • Task
      • Opcode
      • Keywords
      • TimeCreated #たぶんログが出力された日時
      • EventRecordID
      • Correlation
      • Execution
      • Channel
      • Computer
      • Security
    • EventData
      • Data[@Name=SubjectUserSid]
      • Data[@Name=SubjectUserName]
      • Data[@Name=SubjectDomainName]
      • Data[@Name=SubjectLogonId]
      • Data[@Name=NewProcessId] #EventID=4688の場合?
      • Data[@Name=NewProcessName] #EventID=4688の場合?
      • Data[@Name=TokenElevationType]
      • Data[@Name=ProcessId]
      • Data[@Name=ProcessName] #EventID=4688以外の場合?
      • Data[@Name=CommandLine]

record.lxml()で取得したレコードは,上記の階層構造のXMLエレメントなので,lxmlのxpath関数でいい感じに取り出すことができます。
ただし,このElementツリーにはネームスペースが指定されているため,xpath関数にはnamespacesを指定する必要があります
たとえばTimeCreatedを取得してみるなら,こんな感じ。

# 上記のwith:とかfor:の中。
elm = log.get_record(200).lxml() #<-たとえば200番目のrecord
print(
    elm.xpath(
        "//event:TimeCreated",
        namespaces={"event":"http://schemas.microsoft.com/win/2004/08/events/event"}
    )[0].get("SystemTime") #TimeCreatedはSystemTime属性に入ってるっぽい。
)
# -> 2019-01-04 12:34:56.789012 みたいな感じ。

あとはご自由に

私の場合,とりあえずExcelで処理してしまう方がいろいろとできて便利なので,print文(適宜,出力先を指定)で,タブ区切りテキスト(.tsv)に書き出して,Excelで読み込んであーだこーだ悩みます。
私の場合,システムログを読む知識がないのでEventIDとTimeCreatedだけを書き出していますが,圧倒的大多数はEventID==4688 (A new process has been created)なので,

# forブロックの中。namespaceのschema定数は,別箇所で定義しとく。
if elm.xpath("//event:EventID", namespaces={"event":schema})[0].text == "4688":
    continue

って感じですっ飛ばしちゃいます。
そんでもって,.tsvに書き出したファイルをExcelで開いて,TimeCreatedの日付時刻の表示形式をそれっぽくして,EventIDに対して説明シートを参照(VLOOKUP)する列を付け加えれば,なんとなくそれっぽいものの出来上がり。
ちなみにEventIDの説明のリスト(Excel)はMicrosoftのサイトからダウンロードできます。

参考文献

ElementTreeやlxmlで名前空間を含むXMLの要素を取得する (orangain flavor)

11
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
5