Search Windows EVTX files with precision

  • Share on Pinterest

Microsoft introduced a proprietary binary format called EVTX back in Vista and Server 2008 packed with new enhancements and features like log channels, new event properties, etc. Check out SANS’s EVTX and Windows Event Logging white paper for a detailed tour.

Today one can use various tools for analyzing EVTX files like EvtxECmd and Timeline Explorer by Eric Zimmerman. The former can dump EVTX into CSV, XML, and JSON formats for analysis by other tools. Alternatively, you can use python-evtx and evtx, where I will be using the latter which is faster than any other implementation.

First, get the EVTX dump from your Windows. You can use Event Viewer or directly locate and copy the EVTX files of interest. My choice for this demonstration is the Security channel.

After getting the EVTX file, run the omerbenamram’s evtx tool to dump the EVTX into JSON.

./evtx_dump -o json Security.evtx > JsonLog.txt

If you inspect the outputted JSON file, you can notice that it is not a pure JSON file.

Record number identifier separates the Individual JSON logs. We can easily remove that using a sed one-liner.

sed -E 's/Record [[:digit:]]+//g' JsonLog.txt > ProcJsonLog.txt

The processed JSON file is ready for the analysis phase. For easy processing of the JSON file, I use jq which is a lightweight and flexible command-line JSON processor. We can exploit jq’s rich features to process the JSON file writing very minimal code.

As an example, the following one-liner will print the top 10 Event IDs from the JSON file.

jq -c '.Event.System.EventID' ProcJsonLog.txt | sort | uniq -c | sort -nr | head -10

If you inspect the jq one-liner and the log sample below, you can note we can walk the JSON using jq’s dot ‘.’ operator. If you need to print the ParentProcessName field, you need to write .Event.EventData.ParentProcessName.

  "Event": {
    "#attributes": {
      "xmlns": ""
    "EventData": {
      "CommandLine": "C:\\Windows\\system32\\dmclient.exe utcwnf",
      "MandatoryLabel": "S-1-16-16384",
      "NewProcessId": "0x3258",
      "NewProcessName": "C:\\Windows\\System32\\dmclient.exe",
      "ParentProcessName": "C:\\Windows\\System32\\svchost.exe",
      "ProcessId": "0x754",
      "SubjectDomainName": "KNOWLEDGEBASE",
      "SubjectLogonId": "0x3e7",
      "SubjectUserName": "EXODUS$",
      "SubjectUserSid": "S-1-5-18",
      "TargetDomainName": "-",
      "TargetLogonId": "0x0",
      "TargetUserName": "-",
      "TargetUserSid": "S-1-0-0",
      "TokenElevationType": "%%1936"
    "System": {
      "Channel": "Security",
      "Computer": "Exodus.knowledgebase.local",
      "Correlation": null,
      "EventID": 4688,
      "EventRecordID": 18137509,
      "Execution": {
        "#attributes": {
          "ProcessID": 4,
          "ThreadID": 6296
      "Keywords": "0x8020000000000000",
      "Level": 0,
      "Opcode": 0,
      "Provider": {
        "#attributes": {
          "Guid": "54849625-5478-4994-A5BA-3E3B0328C30D",
          "Name": "Microsoft-Windows-Security-Auditing"
      "Security": null,
      "Task": 13312,
      "TimeCreated": {
        "#attributes": {
          "SystemTime": "2021-03-24T09:27:01.017533Z"
      "Version": 2

Now, let’s come to the main part of this blog. One of the most important Windows events is 4688 (Process Creation). By default, the command line is not included in the process creation events. So, I highly recommend administrators to enable it to get the most visibility for hunting malicious activity.

The following one-liner will search for process creation events that have powershell.exe in their command lines.

jq -c 'select(.Event.System.EventID == 4688) 
| select(.Event.EventData.CommandLine
| contains("powershell.exe"))' 

Again, if you want to search for any command prompt executions by user Erza,

jq -c 'select(.Event.System.EventID == 4688 
and .Event.EventData.SubjectUserName == "Erza" ) 
| select(.Event.EventData.NewProcessName 
| contains("cmd.exe"))

I don’t recommend anyone to remember these long one-liners where a single character error will mess up the whole thing. So, I went out to write a script to do this job for me. My aim is to write a SIEM-like query and the script will do the necessary conversions for me.

Let me introduce you to exfinder. Using it, I can now easily search for process creation events that have powershell.exe in their command lines as

./ 'EventID=4688 Command=powershell.exe'

Want to check whether HAFNIUM operators used Procdump to dump LSASS memory after successfully exploiting your Exchange servers,

./ 'EventID=4688 Command= -ma lsass'

By default, jq will print the whole JSON log that triggered the match. Most of the time you do not want this. So, I went one step further and added the ‘project‘ operator like in Azure Sentinel to print out only the provided fields.

./ 'EventID=4688 Command=powershell.exe | project User, Host, Command'
["cyril","Exodus.knowledgebase.local","\"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe\" "] 
["cyril","Exodus.knowledgebase.local","\"C:\Windows\system32\sc.exe\" create ExampleService \"binPath=powershell.exe -nop -noni whoami\""]


In conclusion, I showed you one of the possible ways to quickly search for artifacts in EVTX files with precision and may help to decrease your IR time. The exfinder script is just an example of what one can quickly achieve writing minimal code and should not be mistake it as a fully fledged out tool. You can readily fork the project and add support for other important events if you like.