본문 바로가기
.Net

.NET - Better Windows Event Log Conversion to JSON

by 올엠 2024. 2. 27.
반응형

윈도우 이벤트를 프로그램으로 가져온 경험이 있다면 데이터로 처리하기 어렵다는 점을 많이 느낄것이다. 이글에서는 왜 윈도우 이벤트를 쉽게 가져오기가 힘든지와, 어떻게 하면 최선의 방법으로 이벤트 로그를 JSON으로 가공할 수 있는지에 대해 얘기해보도록 하겠다.

먼저 윈도우 이벤트는 다중언어를 지원하기 위해 XML로 구성되어 있다는 것을 이해할 필요가 있다.

즉 이벤트들은 다중언어(일본어, 한국어, 중국어)별로 동일한 이벤트에 대한 표시 언어를 XML로 만들어 두고, 이에 대한 값, Value만을 저장하는 방식이다.

이벤트 템플릿은은 아래 파워셀 명령을 통해 쉽게 확인 할 수 있다.

Widows Event ID Offer Multiple Lang, So they make Lang template like application.

EventLog base format → XML

Event Template!?

Widows Event ID Offer Multiple Lang, So they make Lang template like application.

EventLog base format → XML

EventData에서 XML 데이터 이름 요소 목록을 가져오는 방법

출처: <https://docs.microsoft.com/ko-kr/windows/security/threat-protection/auditing/how-to-list-xml-elements-in-eventdata>

$secEvents = get-winevent -listprovider "microsoft-windows-security-auditing"
PS C:\WINDOWS\system32> $SecEvents.events[100]
PS C:\WINDOWS\system32> $SecEvents.events[100].Template

윈도우에서 이벤트 로그를 가져오는 방법은 대표적으로 2가지를 제공한다.

1. System.Diagnostics.Eventlog 

https://docs.microsoft.com/ko-kr/dotnet/api/system.diagnostics.eventlog?view=dotnet-plat-ext-3.1

첫번째로 제공하는 방식은 Eventviewer 와 동일한 수준으로 보여주는 Eventlog 클래스이다.

본 클래스의 장점은 시각화 레벨에서 동일하게 보여주기 때문에 만약 사용자에게 제공하는 화면이 중요하다면 사용하기 용의하다.

코드를 통해 값을 확인해 보면, 띄어쓰기, 구분자까지 모두 저장되는 것을 알 수 있다.

"특수 권한을 새 로그온에 할당했습니다.\r\n\r\n주체:\r\n\t보안 ID:\t\tS-1-5-18\r\n\t계정 이름:\t\tSYSTEM\r\n\t계정 도메인:\t\tNT AUTHORITY\r\n\t로그온 ID:\t\t0x3e7\r\n\r\n권한:\t\tSeAssignPrimaryTokenPrivilege\r\n\t\t\tSeTcbPrivilege\r\n\t\t\tSeSecurityPrivilege\r\n\t\t\tSeTakeOwnershipPrivilege\r\n\t\t\tSeLoadDriverPrivilege\r\n\t\t\tSeBackupPrivilege\r\n\t\t\tSeRestorePrivilege\r\n\t\t\tSeDebugPrivilege\r\n\t\t\tSeAuditPrivilege\r\n\t\t\tSeSystemEnvironmentPrivilege\r\n\t\t\tSeImpersonatePrivilege"

만약 위 데이터에서 필요한 값을 빼오기 위해서 Regex를 이용하여 구문을 추출해야 하는데, 이벤트 양이 많다면 CPU overhead 가 있을 가능성 높아진다.

2. System.Diagnostics.Eventing.Reader.EventLogReader

https://docs.microsoft.com/ko-kr/dotnet/api/system.diagnostics.eventing.reader.eventlogreader?view=dotnet-plat-ext-3.1

두번째는 실제 값만을 가져오는 EventLogReader가 있다.

실제 저자를 비롯하여 많은 개발자들이 사용하고 있을 것을 추측된다.

EventLogReader 클래스는 수집하는 이벤트의 XML 구조에서 값만을 가져오게 된다.

(Property는 EventData 구조체의 Value를 가지고 있음)

그리고 필요한 이벤트에 대해 검색 쿼리를 만들어 사용할 수 있는 유연함도 함께 제공된다.

이벤트에 대한 세부 검색 쿼리 제공 관련 글 → https://stackoverflow.com/questions/2462426/how-to-query-for-an-event-log-details-with-a-given-event-id

EventLogQuery를 통해 특정 이벤트를 수집 가능


다만 불편한 부분은 XML의 템플릿 값에 있는 이름값은 제공하지 않기 때문에 자주 사용하지 않은 이벤트라면, 값만을 통해서 어떤 의미인지 확인이 어렵고, 사용자들에게 제공할 때, 값만 제공해야 하는 불편함이 있어, 이름 수동으로 추가 작업해줘야 하는 경우가 많다.

그렇다면, 보다 효율적으로 이름 값을 확인 할 수 있는 방법이 있을까?

We want to get Key Name

우선 저자가 찾은 방법은 다행이 XML 키와 값의 순서가 동일하여, XML 타입에는 키 이름 값이 존재하기 때문에 키을 찾아서 이를 값과 매핑해주는 방식으로 해결하였다. 

https://stackoverflow.com/questions/31488175/how-to-find-out-eventproperty-name

XDocument xmlrecord = XDocument.Parse(record.ToXml());

XNamespace ns = "http://schemas.microsoft.com/win/2004/08/events/event"; 

int indexcount = 0;   

foreach (var node in xmlrecord.Descendants(ns + "Data"))
{ 
    string name = (string)node.Attribute("Name"); 
}

 

위 프로그램을 실행하면, 아래와 같이 각 이벤트 내용에서 키와 값을 매핑하여 보여줄 수 있다.

키와 값을 매칭하여 표시

반응형