diff --git a/scripts/evtx_dump_json.py b/scripts/evtx_dump_json.py index 788436a..d8e3e50 100644 --- a/scripts/evtx_dump_json.py +++ b/scripts/evtx_dump_json.py @@ -9,21 +9,16 @@ # - xmltodict >= 0.12.0 import os import json - +import sys import xmltodict +import argparse import Evtx.Evtx as evtx -def main(): - import argparse - - parser = argparse.ArgumentParser(description="Dump a binary EVTX file into XML.") - parser.add_argument("evtx", type=str, action="store", help="Path to the Windows EVTX event log file") - parser.add_argument("-o", "--output", type=str, action="store", help="Path of output JSON file") - args = parser.parse_args() +def main(evtx_file,output): - with evtx.Evtx(args.evtx) as log: + with evtx.Evtx(evtx_file) as log: # Instantiate the final json object final_json = [] @@ -34,32 +29,66 @@ def main(): # Convert the record to a dictionary for ease of parsing data_dict = xmltodict.parse(record.xml()) - # Loop through each key,value pair of the System section of the evtx logs and extract the EventRecordID - for event_system_key, event_system_value in data_dict["Event"]["System"].items(): - if event_system_key == "EventRecordID": - json_subline = {} - firstline = {event_system_key: event_system_value} + # Create first line of System Data based on the EventRecordID + json_subline = {} + json_subline.update({'EventRecordID':data_dict['Event']['System']['EventRecordID']}) - # Add information to the JSON object for this specific log - json_subline.update(firstline) # add the event ID to JSON subline + # Loop through each key,value pair of the System section of the evtx logs + for event_system_key, event_system_value in data_dict['Event']['System'].items(): - # Loop through each key, value pair of the EventData section of the evtx logs - for event_data_key, event_data_value in data_dict["Event"]["EventData"].items(): - for values in event_data_value: + if not (event_system_key=="EventRecordID") or not (event_system_key=="Execution"): + + # For nested dictionaries, loop through each and extract key information + if isinstance(event_system_value,dict): + for event_system_subkey,event_system_subvalue in event_system_value.items(): - # Loop through each subvalue within the EvenData section to extract necessary information - for event_data_subkey, event_data_subvalue in values.items(): - if event_data_subkey == "@Name": - data_name = event_data_subvalue - else: - data_value = event_data_subvalue + if event_system_key == "EventID" or event_system_key == "TimeCreated": + json_subline.update({event_system_key: event_system_subvalue}) + if event_system_key == "Security": + json_subline.update({event_system_subkey[1:]: event_system_subvalue}) - # Add information to the JSON object for this specific log - json_subline.update({data_name: data_value}) + else: + # Add information to the JSON object for this specific log + json_subline.update({event_system_key: event_system_value}) - # Print the JSON object for the specific log if not requested to output to file - if not args.output: - print(json_subline) + # Loop through each key, value pair of the EventData section of the evtx logs + if "EventData" in data_dict['Event'].keys() and data_dict['Event']['EventData'] != None: + for event_data_key, event_data_value in data_dict['Event']['EventData'].items(): + + # Check to see if the EventData Data contains a list + if isinstance(event_data_value,list) and event_data_key!="@Name": + for values in event_data_value: + + # Loop through each subvalue within the EvenData section to extract necessary information + for event_data_subkey,event_data_subvalue in values.items(): + if event_data_subkey == "@Name": + data_name = event_data_subvalue + else: + data_value = event_data_subvalue + + # Add information to the JSON object for this specific log + json_subline.update({data_name: data_value}) + + # Check to see if EventData contains a dictionary + if isinstance(event_data_value,dict) and event_data_key!="@Name": + for event_data_subkey,event_data_subvalue in event_data_value.items(): + if event_data_subkey == "@Name": + data_name = event_data_subvalue + else: + data_value = event_data_subvalue + + # Add information to the JSON object for this specific log + json_subline.update({data_name: data_value}) + + # Check to see if EventData contains a string + if isinstance(event_data_value,str) and event_data_key!="@Name": + beautify_event_data_value=event_data_value.replace("","").replace("\n"," ").replace("","") + json_subline.update({event_data_key: beautify_event_data_value}) + + # Loop through each key, value pair in UserData section, if present + if "UserData" in data_dict["Event"].keys(): + for user_data_key,user_data_value in data_dict['Event']['UserData'].items(): + json_subline.update({user_data_key: user_data_value}) # Add specific log JSON object to the final JSON object if not final_json: @@ -68,7 +97,7 @@ def main(): final_json.append(json_subline) # If output is desired - if args.output: + if output: # Output the JSON data if os.path.splitext(args.output)[1] == ".json": @@ -77,9 +106,16 @@ def main(): json_file = args.output + ".json" # Write to JSON file - with open(json_file, "w") as outfile: - json.dump(final_json, outfile) - + with open(json_file,"w") as outfile: + json.dump(final_json,outfile) + else: + print(json.dumps(final_json)) if __name__ == "__main__": - main() + + parser = argparse.ArgumentParser(description="Dump a binary EVTX file into JSON.") + parser.add_argument("evtx",type=str,action="store",help="Path to the Windows EVTX event log file") + parser.add_argument("-o","--output",type=str, action="store",help="Path of output JSON file") + args = parser.parse_args() + + main(args.evtx,args.output) diff --git a/tests/conftest.py b/tests/conftest.py index 37f4339..68ffcab 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,4 +13,4 @@ # see the following for a discussion: # https://www.revsys.com/tidbits/pytest-fixtures-are-magic/ # https://lobste.rs/s/j8xgym/pytest_fixtures_are_magic -from fixtures import * # noqa: F403 [unable to detect undefined names] +from fixtures import * # noqa: F403 [unable to detect undefined names] \ No newline at end of file diff --git a/tests/data/Microsoft-Windows-Sysmon-Operational.evtx b/tests/data/Microsoft-Windows-Sysmon-Operational.evtx new file mode 100644 index 0000000..866d301 Binary files /dev/null and b/tests/data/Microsoft-Windows-Sysmon-Operational.evtx differ diff --git a/tests/fixtures.py b/tests/fixtures.py index 131c0ac..7512f02 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -74,4 +74,4 @@ def data_path(): """ cd = os.path.dirname(__file__) datadir = os.path.join(cd, "data") - return datadir + return datadir \ No newline at end of file