(我が家のPC運用)自作バッチでイベント収集


我が家のWindowsイベントログ収取

我が家にはWindows10デスクトップPCがあり、運用の1タスクとして定期的にイベントログを取集しています。

  • システム関連のイベント:更新プログラムの適用や時刻同期を取得。
  • セキュリティ関連のイベント:ログオンのタイミングやアカウントの書き換え等を取得。
  • WindowsDefender関連のイベント:スキャンの実施や更新の情報を取得。

イベントログ収集を自作バッチで実施しています。処理内容は

  • 上記3種のイベントログを取得しテキストファイル(イベントログファイル)に保存。
  • イベントログファイルを解析し、発生イベントIDと発生件数を集計、テキストファイル(イベント解析ファイル)に保存。
  • イベントログファイルを解析し、特筆すべきイベントをテキストファイル(イベント通知ファイル)に保存。
  • イベントログファイルを解析し、特筆すべきイベントのうち即時通知するものをアラーム。

バッチの特徴

  • 以下の3ファイルで構成します。ファイルは全てSJISで、バッチは同一場所、jarはバッチと同階層のjavaフォルダ下に配置します。
    • setenv.bat
    • getEventLog.bat
    • nnnmu24-local-eventlog.jar
  • コードは「こちら」に格納しているので、全体はそちらを参照願います。
  • 実行するのはgetEventLog.batです。このバッチ内でシステム、セキュリティ、WindowsDefenderの3イベントログを収集します。
  • nnnmu24-local-eventlog.jarでイベントログファイルを解析します。文字列の解析・加工が必要なためバッチでは厳しく、JAVAで実施します。
  • ファイルパスや定数をsetenv.batにまとめます。

出力例

イベント解析ファイル

----------------------------------------
[イベント件数]

イベント:(情報)-  5xxx    5件 ......(イベントIDの説明)
イベント:(情報)-  4xxx   30件 ......(イベントIDの説明)
イベント:(情報)-  5xxx    5件 ......(イベントIDの説明)
イベント:(情報)-  4xxx    5件 ......(イベントIDの説明)
イベント:(情報)-  4xxx    1件 ......(イベントIDの説明)
イベント:(情報)-  4xxx    1件 ......(イベントIDの説明)
イベント:(情報)-  5xxx  100件 ......(イベントIDの説明)
イベント:(情報)-  4xxx   30件 ......(イベントIDの説明)
イベント:(情報)-  4xxx    5件 ......(イベントIDの説明)
イベント:(情報)-  4xxx  100件 ......(イベントIDの説明)
イベント:(情報)-  4xxx   30件 ......(イベントIDの説明)
イベント:(情報)-  4xxx    5件 ......(イベントIDの説明)
イベント:(情報)-  4xxx    5件 ......(イベントIDの説明)

----------------------------------------
[イベント詳細]
...(イベントログのダンプより、各イベントを解析した内容)
...(イベントログのダンプより、各イベントを解析した内容)
...(イベントログのダンプより、各イベントを解析した内容)

イベント通知ファイル

[REPORT]2020-12-10T02:07:29.582,情報,(19 インストールの成功:...)
[REPORT]2020-12-10T23:49:40.691,情報,(19 インストールの成功:...)
[REPORT]2020-12-10T22:51:35.810,情報,(35 時刻同期を実行)
[HEAD]2020/12/10 23:50:10,異常なし

バッチのコード

getEventLog.bat

長いので一部抜粋です。

  • イベントログの収集はwevtutilで実施します。
  • @SystemTimeで収集範囲日時を指定します。GMT指定なので下記は前日の23:50から現時刻までのイベント収集となります。
  • %SYSTEM_EVENT_LOG_WORK_FILE%、%MYSHELL_NAME%は説明割愛。%SYSTEM_EVENT_LOG_WORK_FILE%等、以降のファイルパスは任意に指定。
  • システムイベントと同様にセキュリティイベントログ、WindowsDefenderイベントログを収集します。
  • セキュリティイベントは管理者権限でバッチを実行しないと取得できないので注意。
  • WindowsDefenderイベントは30日前から収集します。
rem 前日を取得し変数に格納
for /f "usebackq delims=" %%a in (`powershell "(get-date).AddDays(-1).ToString(\"yyyy-MM-dd\")"`) do set DATE_YESTERDAY=%%a
for /f "usebackq delims=" %%a in (`powershell "(get-date).AddDays(-30).ToString(\"yyyy-MM-dd\")"`) do set DATE_BEFMONTH=%%a

rem システムイベントログ
wevtutil qe System /f:Text "/q:*[System[TimeCreated[@SystemTime>='%DATE_YESTERDAY%T14:50:00']]]" > %SYSTEM_EVENT_LOG_WORK_FILE%
if not %errorlevel% == 0 (
    echo [ERROR]wevtutil(system)実行エラー
    echo [END]%MYSHELL_NAME%
    exit /b %EXIT_CODE_ERROR%
)

rem セキュリティイベントログ
wevtutil qe Security /f:Text "/q:*[System[TimeCreated[@SystemTime>='%DATE_YESTERDAY%T14:50:00']]]" > %SECUTITY_EVENT_LOG_WORK_FILE%
rem (エラー処理の記載省略)

rem WindowsDefenderイベントログ
wevtutil qe "Microsoft-Windows-Windows Defender/Operational" /f:Text "/q:*[System[TimeCreated[@SystemTime>='%DATE_BEFMONTH%T14:50:00']]]" > %DEFENDER_EVENT_LOG_WORK_FILE%
rem (エラー処理の記載省略)
  • 収集したイベントログファイルの解析をnnnmu24-local-eventlog.jarで実施します。
  • jarは4つの引数でモード、イベントログファイルパス、イベント解析ファイルパス、イベント通知ファイルパスを指定します。 モードはシステム、セキュリティ、WindowsDefenderの指定で、[1,2,3]のいずれかです。
rem システムイベントログ
java -jar %JARMJ% 1 %SYSTEM_EVENT_LOG_WORK_FILE% %SYSTEM_EVENT_LOG_PARSE_FILE% %REPORT_PATH%
if not %errorlevel% == 100 if not %errorlevel% == 109 (
    echo [ERROR]java(system)実行エラー
    echo [END]%MYSHELL_NAME%
    exit /b %EXIT_CODE_ERROR%
)

rem セキュリティイベントログ
java -jar %JARMJ% 2 %SECUTITY_EVENT_LOG_WORK_FILE% %SECUTITY_EVENT_LOG_PARSE_FILE% %REPORT_PATH%
rem (エラー処理の記載省略)

rem WindowsDefenderイベントログ
java -jar %JARMJ% 3 %DEFENDER_EVENT_LOG_WORK_FILE% %DEFENDER_EVENT_LOG_PARSE_FILE% %REPORT_PATH%
rem (エラー処理の記載省略)

nnnmu24-local-eventlog.jar

長いので一部抜粋です。

  • イベントログファイルの解析は、複雑な処理はしていません。行先頭文字列を判定しイベントのラベル「Log Name:」「Event ID:」等で分類し、オブジェクトに格納しています。
  • その後固有イベントの解析を実行します。以下のコードは工夫すれば短くできると思いますが、ベタに書いてます。
EventLogDto eventLogDto = new EventLogDto();
for (String line: lines) {
    if (line.startsWith("Event[")) {
        eventLogDto = new EventLogDto();
        eventLogDtoList.add(eventLogDto);
        eventLogDto.setEvent(line);
    } else if (line.startsWith("  Log Name: ")) {
        eventLogDto.setLogName(line.substring("  Log Name: ".length()));
    } else if (line.startsWith("  Source: ")) {
        eventLogDto.setSource(line.substring("  Source: ".length()));
    } else if (line.startsWith("  Date: ")) {
        eventLogDto.setDate(line.substring("  Date: ".length()));
    } else if (line.startsWith("  Event ID: ")) {
        String eventId = line.substring("  Event ID: ".length());
        eventLogDto.setEventId(eventId);
        eventCountMap.computeIfAbsent(eventId, k -> 0);
        eventCountMap.computeIfPresent(eventId, (k, v) -> v + 1);
    } else if (line.startsWith("  Task: ")) {
        eventLogDto.setTask(line.substring("  Task: ".length()));
    } else if (line.startsWith("  Level: ")) {
        String level = line.substring("  Level: ".length());
        level = level.substring(0, 2); // level is max 2word
        eventLogDto.setLevel(level);
        eventLevelMap.put(eventLogDto.getEventId(), level);
    } else if (line.startsWith("  Opcode: ")) {
        eventLogDto.setOpcode(line.substring("  Opcode: ".length()));
    } else if (line.startsWith("  Keyword: ")) {
        eventLogDto.setKeyword(line.substring("  Keyword: ".length()));
    } else if (line.startsWith("  User: ")) {
        eventLogDto.setUser(line.substring("  User: ".length()));
    } else if (line.startsWith("  User Name: ")) {
        eventLogDto.setUserName(line.substring("  User Name: ".length()));
    } else if (line.startsWith("  Computer:" )) {
        eventLogDto.setComputer(line.substring("  Computer: ".length()));
    } else if (line.startsWith("  Description: ")) {
    } else if (line.equals("")) {
    } else {
        eventLogDto.addDetail(line);
    }
}
  • 一部のイベントは個別に解析します。
  • 時刻の同期やウィルススキャンの実行は最新の日時のみイベント通知ファイルで通知。
  • 4624のログインイベントはPersonalParser_4624というクラスでイベントの詳細メッセージを解析しログイン契機をイベント通知ファイルで通知。
private List<String> parseElement(EventLogDto element) {

    PersonalParserIf personalParser = null;
    if (mode == Const.MODE_SYSTEM) {
        switch (element.getEventId()) {
        case "35":
            // 最新のみ通知するイベントID
            latestEventLogDtoMap.put(element.getEventId(), element);
            break;
        default:
            break;
        }
    } else if (mode == Const.MODE_DEFENDER) {
        switch (element.getEventId()) {
        case "1001":
        case "2000":
        case "2002":
            // 最新のみ通知するイベントID
            latestEventLogDtoMap.put(element.getEventId(), element);
            break;
        default:
            break;
        }
    } else if (mode == Const.MODE_SECURITY) {
        switch (element.getEventId()) {
        case "4624":
            // 個別ルールによる判定
            personalParser = new PersonalParser_4624();
            break;
        default:
            break;
        }
    }

    if (personalParser == null) {
        return null;
    }
    return personalParser.parse(element);
}