弁護士が裁判で労働時間を立証する際に使う…かどうかはともかくとして,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)
あたりで取得することになりますが,全レコードを走査することが多いでしょうから,以下はそういう感じにします。
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]
- System
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のサイトからダウンロードできます。