PythonでWindows形式のイベントログであるevtxデータをCSVに変換してみました - Verup編

## 概要

NetAppファイルサーバのイベントログであるevtxデータを サクッと確認するために CSV形式 へ変換保存するプログラムです。[この記事](https://qiita.com/turupon/items/1956600a14571b9f0b6e) に 「Read/Write Offset」と「Read/Write Count」を参照できるようにバージョンアップしました。

### 実行環境

macOS Ventura 13.0 
python 3.8.12

### 事前準備

事前に以下のモジュールをインポートしておきます。

```
$ pip install python-evtx
```

### 実行プログラム

```python:extviewer3.py
import argparse
import time
from Evtx.Evtx import Evtx
import pandas as pd


# CSVファイルへの変換
def evtx_to_csv(evtx_file, mode):
    # イベントスキーマーの定義
    SCHEMA = 'http://schemas.netapp.com/events/event'

    #　カラムの定義
    EventID = []
    EventName = []
    TimeCreated = []
    Computer = []
    SubjectIP = []
    DomainName = []
    UserName = []
    ObjectType = []
    ObjectName = []
    RWOffset = []
    RWCount = []
    
    # イベントファイルのオープン
    with Evtx(evtx_file) as log:

        # イベントレコードの取得
        for record in log.records():
            elm = record.lxml()

            # # 個々のrecord構造の確認（最初に確認しましょう）
            # print(record.xml())
            
            # Logon/off のイベントは無視する
            eventid_str = elm.xpath("//event:EventID", namespaces={"event":SCHEMA})[0].text
            if eventid_str == '4624' or eventid_str == '4625' or eventid_str == '4634':
                continue

            # イベントデータの取得
            EventID.append(elm.xpath("//event:EventID", namespaces={"event":SCHEMA})[0].text)
            EventName.append(elm.xpath("//event:EventName", namespaces={"event":SCHEMA})[0].text)
            TimeCreated.append(elm.xpath("//event:TimeCreated", namespaces={"event":SCHEMA})[0].get("SystemTime"))
            Computer.append(elm.xpath("//event:Computer", namespaces={"event":SCHEMA})[0].text)
            SubjectIP.append(elm.xpath("//event:Data", namespaces={"event":SCHEMA})[0].text)
            DomainName.append(elm.xpath("//event:Data", namespaces={"event":SCHEMA})[5].text)
            UserName.append(elm.xpath("//event:Data", namespaces={"event":SCHEMA})[6].text)
            ObjectType.append(elm.xpath("//event:Data", namespaces={"event":SCHEMA})[8].text)
            ObjectName.append(elm.xpath("//event:Data", namespaces={"event":SCHEMA})[10].text)

            eventname_str = elm.xpath("//event:EventName", namespaces={"event":SCHEMA})[0].text

            if eventname_str == 'Read Object' or eventname_str == 'Write Object' :
                RWOffset.append(elm.xpath("//event:Data", namespaces={"event":SCHEMA})[11].text)
                RWCount.append(elm.xpath("//event:Data", namespaces={"event":SCHEMA})[12].text)
            else :
                RWOffset.append('')
                RWCount.append('')


        # データフレームを作成
        df = pd.DataFrame({
            'EventID' : EventID,
            'EventName' : EventName,
            'TimeCreated' : TimeCreated,
            'Computer' : Computer,
            'SubjectIP' : SubjectIP,
            'DomainName' : DomainName,
            'UserName' : UserName,
            'ObjectType' : ObjectType,
            'ObjectName' : ObjectName,
            'RWOffset' : RWOffset,
            'RWCount' : RWCount
        })

        if mode == 'csv' :
            # CSVファイルへの書き込み
            output_file = evtx_file + '.csv'
            df.to_csv(output_file, header=True, index=True)
        else :
            # 画面への表示
            print(df.to_markdown())


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='extvをjson形式にするプログラム')
    parser.add_argument('-i', '--input', type=str, default=INPUT_FILE, help='対象となる extvファイル')
    parser.add_argument('-m', '--mode', type=str, default='view', help='View:画面表示,  csv:CSVファイル保存')
    args = parser.parse_args()

    # 経過時間の計測開始
    start = time.time()
    evtx_to_csv(args.input, args.mode)
    generate_time = time.time() - start

    print("\n処理時間:{0}".format(generate_time) + " [sec]\n")
```

### プログラムの実行

.evtxデータを画面に表示してみます。

```
$ python extviewer3.py -i 20230511_13.evtx
|    |   EventID | EventName             | TimeCreated                | Computer           | SubjectIP   | DomainName   | UserName      | ObjectType                          | ObjectName                                                            | RWOffset   | RWCount   |
    ： 
    ：
| 10 |      4663 | Read Object           | 2023-05-09 13:07:44.851498 | FAS2750A/kg_domain | 10.2.1.6    | NETAPP       | administrator | File                                | (zidoma);/00_Develop/HeaderEncryption/testdir/rs-audit.db.encryptedrn | 0          | 4096      |
| 11 |      4663 | Write Object          | 2023-05-09 13:07:44.935497 | FAS2750A/kg_domain | 10.2.1.6    | NETAPP       | administrator | File                                | (zidoma);/00_Develop/HeaderEncryption/testdir/rs-audit.db.encryptedrn | 0          | 4096      |
    ： 
    ：
処理時間:0.18537306785583496 [sec]
```

.evtxデータをCSVに変換保存すしてみます。

```
$ python extviewer3.py -i 20230511_13.evtx -m csv

処理時間:0.13499021530151367 [sec]
```

作成されたCSVファイルを確認します。

```
$ vim 20230511_13.evtx.csv
-------------------------------
,EventID,EventName,TimeCreated,Computer,SubjectIP,DomainName,UserName,ObjectType,ObjectName,RWOffset,RWCount
    ： 
    ：
10,4663,Read Object,2023-05-09 13:07:44.851498,FAS2750A/kg_domain,10.2.1.6,NETAPP,administrator,File,(zidoma);/00_Develop/HeaderEncryption/testdir/rs-audit.db.encryptedrn,0,4096
11,4663,Write Object,2023-05-09 13:07:44.935497,FAS2750A/kg_domain,10.2.1.6,NETAPP,administrator,File,(zidoma);/00_Develop/HeaderEncryption/testdir/rs-audit.db.encryptedrn,0,4096
    ： 
    ：
```

### まとめ

NetAppファイルサーバのイベントログの必要なデータとして「Read/Write Offset」と「Read/Write Count」を取得できるようにしました。これで、ファイルのどの場所から何バイト Read/Write したのかを把握できるようになりました。