Elastic.EventLogs.Sysmon

Ship the the Sysmon event log in ECS schema.

The Elastic Common Schema (ECS) is an open source specification, developed with support from the Elastic user community. ECS defines a common set of fields to be used when storing event data in Elasticsearch, such as logs and metrics.

NOTE: ECS is poorly documented. There is no clear documentation of where each field in the ECS record comes from other than the actual source code of the winlogbeats client. This artifact implements the winlogbeat transformation as described in https://github.com/elastic/beats/blob/master/x-pack/winlogbeat/module/sysmon/ingest/sysmon.yml

There may be slight variations between the data produced by this artifact and the official winlogbeat client. If you find such variation, please file an issue on Velociraptor’s GitHub issue board.


name: Elastic.EventLogs.Sysmon
description: |
  Ship the the Sysmon event log in ECS schema.

  The Elastic Common Schema (ECS) is an open source specification,
  developed with support from the Elastic user community. ECS defines
  a common set of fields to be used when storing event data in
  Elasticsearch, such as logs and metrics.

  NOTE: ECS is poorly documented. There is no clear documentation of
  where each field in the ECS record comes from other than the actual
  source code of the winlogbeats client. This artifact implements the
  winlogbeat transformation as described in
  https://github.com/elastic/beats/blob/master/x-pack/winlogbeat/module/sysmon/ingest/sysmon.yml

  There may be slight variations between the data produced by this
  artifact and the official winlogbeat client. If you find such
  variation, please file an issue on Velociraptor's GitHub issue
  board.

reference:
  - https://www.elastic.co/guide/en/ecs/current/ecs-reference.html

parameters:
  - name: LogFileGlob
    default: C:/Windows/System32/WinEvt/Logs/Microsoft-Windows-Sysmon%4Operational.evtx

export: |
  -- ECS clears many fields from EventData but we preserve them all,
  -- although to ensure that Elastic does not reject the fields we
  -- convert them all to strings.
  LET NormalizeEventData(EventData) = to_dict(item={
    SELECT _key, str(str=_value) AS _value FROM items(item=EventData)
  })

  LET OpcodesLookup <= dict(
    `0`= "Info",
    `1`= "Start",
    `2`= "Stop",
    `3`= "DCStart",
    `4`= "DCStop",
    `5`= "Extension",
    `6`= "Reply",
    `7`= "Resume",
    `8`= "Suspend",
    `9`= "Send")

  LET LevelLookup <= dict(
    `0`= "Information",
    `1`= "Critical",
    `2`= "Error",
    `3`= "Warning",
    `4`= "Information",
    `5`= "Verbose")

  LET CategoryLookup <= dict(
     `1`=["process",],
     `2`=["file",],
     `3`=["network",],
     `4`=["process",],
     `5`=["process",],
     `6`=["driver",],
     `7`=["process",],
     `8`=["process",],
     `9`=["process",],
     `10`=["process",],
     `11`=["file",],
     `12`=["configuration","registry"],
     `13`=["configuration","registry"],
     `14`=["configuration","registry"],
     `15`=["file",],
     `16`=["configuration",],
     `17`=["file",],
     `18`=["file",],
     `19`=["process",],
     `20`=["process",],
     `21`=["network",],
     `22`=["network",],
     `23`=["file",],
     `24`=["",],
     `25`=["process",],
     `26`=["file",],
     `27`=["file",],
     `28`=["file",],
     `255`=["process",])

  LET TypeLookup <= dict(
     `1`=["start",],
     `2`=["change",],
     `3`=["start", "connection", "protocol"],
     `4`=["change",],
     `5`=["end",],
     `6`=["start",],
     `7`=["change",],
     `8`=["change",],
     `9`=["access",],
     `10`=["access",],
     `11`=["creation",],
     `12`=["change",],
     `13`=["change",],
     `14`=["change",],
     `15`=["access",],
     `16`=["change",],
     `17`=["creation",],
     `18`=["access",],
     `19`=["creation",],
     `20`=["creation",],
     `21`=["access",],
     `22`=["connection", "protocol", "info"],
     `23`=["deletion",],
     `24`=["change",],
     `25`=["change",],
     `26`=["deletion",],
     `27`=["creation", "denied"],
     `28`=["deletion", "denied"],
     `255`=["error",])

  LET DNSLookup <= dict(
        `1`= "A",
        `2`= "NS",
        `3`= "MD",
        `4`= "MF",
        `5`= "CNAME",
        `6`= "SOA",
        `7`= "MB",
        `8`= "MG",
        `9`= "MR",
        `10`= "NULL",
        `11`= "WKS",
        `12`= "PTR",
        `13`= "HINFO",
        `14`= "MINFO",
        `15`= "MX",
        `16`= "TXT",
        `17`= "RP",
        `18`= "AFSDB",
        `19`= "X25",
        `20`= "ISDN",
        `21`= "RT",
        `22`= "NSAP",
        `23`= "NSAPPTR",
        `24`= "SIG",
        `25`= "KEY",
        `26`= "PX",
        `27`= "GPOS",
        `28`= "AAAA",
        `29`= "LOC",
        `30`= "NXT",
        `31`= "EID",
        `32`= "NIMLOC",
        `33`= "SRV",
        `34`= "ATMA",
        `35`= "NAPTR",
        `36`= "KX",
        `37`= "CERT",
        `38`= "A6",
        `39`= "DNAME",
        `40`= "SINK",
        `41`= "OPT",
        `43`= "DS",
        `46`= "RRSIG",
        `47`= "NSEC",
        `48`= "DNSKEY",
        `49`= "DHCID",
        `100`= "UINFO",
        `101`= "UID",
        `102`= "GID",
        `103`= "UNSPEC",
        `248`= "ADDRS",
        `249`= "TKEY",
        `250`= "TSIG",
        `251`= "IXFR",
        `252`= "AXFR",
        `253`= "MAILB",
        `254`= "MAILA",
        `255`= "ANY",
        `65281`= "WINS",
        `65282`= "WINSR"
  )

  LET DnsStatusLookup <= dict(
    `5`= "ERROR_ACCESS_DENIED",
    `0`= "SUCCESS",
    `8`= "ERROR_NOT_ENOUGH_MEMORY",
    `13`= "ERROR_INVALID_DATA",
    `14`= "ERROR_OUTOFMEMORY",
    `123`= "ERROR_INVALID_NAME",
    `1214`= "ERROR_INVALID_NETNAME",
    `1223`= "ERROR_CANCELLED",
    `1460`= "ERROR_TIMEOUT",
    `4312`= "ERROR_OBJECT_NOT_FOUND",
    `9001`= "DNS_ERROR_RCODE_FORMAT_ERROR",
    `9002`= "DNS_ERROR_RCODE_SERVER_FAILURE",
    `9003`= "DNS_ERROR_RCODE_NAME_ERROR",
    `9004`= "DNS_ERROR_RCODE_NOT_IMPLEMENTED",
    `9005`= "DNS_ERROR_RCODE_REFUSED",
    `9006`= "DNS_ERROR_RCODE_YXDOMAIN",
    `9007`= "DNS_ERROR_RCODE_YXRRSET",
    `9008`= "DNS_ERROR_RCODE_NXRRSET",
    `9009`= "DNS_ERROR_RCODE_NOTAUTH",
    `9010`= "DNS_ERROR_RCODE_NOTZONE",
    `9016`= "DNS_ERROR_RCODE_BADSIG",
    `9017`= "DNS_ERROR_RCODE_BADKEY",
    `9018`= "DNS_ERROR_RCODE_BADTIME",
    `9101`= "DNS_ERROR_KEYMASTER_REQUIRED",
    `9102`= "DNS_ERROR_NOT_ALLOWED_ON_SIGNED_ZONE",
    `9103`= "DNS_ERROR_NSEC3_INCOMPATIBLE_WITH_RSA_SHA1",
    `9104`= "DNS_ERROR_NOT_ENOUGH_SIGNING_KEY_DESCRIPTORS",
    `9105`= "DNS_ERROR_UNSUPPORTED_ALGORITHM",
    `9106`= "DNS_ERROR_INVALID_KEY_SIZE",
    `9107`= "DNS_ERROR_SIGNING_KEY_NOT_ACCESSIBLE",
    `9108`= "DNS_ERROR_KSP_DOES_NOT_SUPPORT_PROTECTION",
    `9109`= "DNS_ERROR_UNEXPECTED_DATA_PROTECTION_ERROR",
    `9110`= "DNS_ERROR_UNEXPECTED_CNG_ERROR",
    `9111`= "DNS_ERROR_UNKNOWN_SIGNING_PARAMETER_VERSION",
    `9112`= "DNS_ERROR_KSP_NOT_ACCESSIBLE",
    `9113`= "DNS_ERROR_TOO_MANY_SKDS",
    `9114`= "DNS_ERROR_INVALID_ROLLOVER_PERIOD",
    `9115`= "DNS_ERROR_INVALID_INITIAL_ROLLOVER_OFFSET",
    `9116`= "DNS_ERROR_ROLLOVER_IN_PROGRESS",
    `9117`= "DNS_ERROR_STANDBY_KEY_NOT_PRESENT",
    `9118`= "DNS_ERROR_NOT_ALLOWED_ON_ZSK",
    `9119`= "DNS_ERROR_NOT_ALLOWED_ON_ACTIVE_SKD",
    `9120`= "DNS_ERROR_ROLLOVER_ALREADY_QUEUED",
    `9121`= "DNS_ERROR_NOT_ALLOWED_ON_UNSIGNED_ZONE",
    `9122`= "DNS_ERROR_BAD_KEYMASTER",
    `9123`= "DNS_ERROR_INVALID_SIGNATURE_VALIDITY_PERIOD",
    `9124`= "DNS_ERROR_INVALID_NSEC3_ITERATION_COUNT",
    `9125`= "DNS_ERROR_DNSSEC_IS_DISABLED",
    `9126`= "DNS_ERROR_INVALID_XML",
    `9127`= "DNS_ERROR_NO_VALID_TRUST_ANCHORS",
    `9128`= "DNS_ERROR_ROLLOVER_NOT_POKEABLE",
    `9129`= "DNS_ERROR_NSEC3_NAME_COLLISION",
    `9130`= "DNS_ERROR_NSEC_INCOMPATIBLE_WITH_NSEC3_RSA_SHA1",
    `9501`= "DNS_INFO_NO_RECORDS",
    `9502`= "DNS_ERROR_BAD_PACKET",
    `9503`= "DNS_ERROR_NO_PACKET",
    `9504`= "DNS_ERROR_RCODE",
    `9505`= "DNS_ERROR_UNSECURE_PACKET",
    `9506`= "DNS_REQUEST_PENDING",
    `9551`= "DNS_ERROR_INVALID_TYPE",
    `9552`= "DNS_ERROR_INVALID_IP_ADDRESS",
    `9553`= "DNS_ERROR_INVALID_PROPERTY",
    `9554`= "DNS_ERROR_TRY_AGAIN_LATER",
    `9555`= "DNS_ERROR_NOT_UNIQUE",
    `9556`= "DNS_ERROR_NON_RFC_NAME",
    `9557`= "DNS_STATUS_FQDN",
    `9558`= "DNS_STATUS_DOTTED_NAME",
    `9559`= "DNS_STATUS_SINGLE_PART_NAME",
    `9560`= "DNS_ERROR_INVALID_NAME_CHAR",
    `9561`= "DNS_ERROR_NUMERIC_NAME",
    `9562`= "DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER",
    `9563`= "DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION",
    `9564`= "DNS_ERROR_CANNOT_FIND_ROOT_HINTS",
    `9565`= "DNS_ERROR_INCONSISTENT_ROOT_HINTS",
    `9566`= "DNS_ERROR_DWORD_VALUE_TOO_SMALL",
    `9567`= "DNS_ERROR_DWORD_VALUE_TOO_LARGE",
    `9568`= "DNS_ERROR_BACKGROUND_LOADING",
    `9569`= "DNS_ERROR_NOT_ALLOWED_ON_RODC",
    `9570`= "DNS_ERROR_NOT_ALLOWED_UNDER_DNAME",
    `9571`= "DNS_ERROR_DELEGATION_REQUIRED",
    `9572`= "DNS_ERROR_INVALID_POLICY_TABLE",
    `9573`= "DNS_ERROR_ADDRESS_REQUIRED",
    `9601`= "DNS_ERROR_ZONE_DOES_NOT_EXIST",
    `9602`= "DNS_ERROR_NO_ZONE_INFO",
    `9603`= "DNS_ERROR_INVALID_ZONE_OPERATION",
    `9604`= "DNS_ERROR_ZONE_CONFIGURATION_ERROR",
    `9605`= "DNS_ERROR_ZONE_HAS_NO_SOA_RECORD",
    `9606`= "DNS_ERROR_ZONE_HAS_NO_NS_RECORDS",
    `9607`= "DNS_ERROR_ZONE_LOCKED",
    `9608`= "DNS_ERROR_ZONE_CREATION_FAILED",
    `9609`= "DNS_ERROR_ZONE_ALREADY_EXISTS",
    `9610`= "DNS_ERROR_AUTOZONE_ALREADY_EXISTS",
    `9611`= "DNS_ERROR_INVALID_ZONE_TYPE",
    `9612`= "DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP",
    `9613`= "DNS_ERROR_ZONE_NOT_SECONDARY",
    `9614`= "DNS_ERROR_NEED_SECONDARY_ADDRESSES",
    `9615`= "DNS_ERROR_WINS_INIT_FAILED",
    `9616`= "DNS_ERROR_NEED_WINS_SERVERS",
    `9617`= "DNS_ERROR_NBSTAT_INIT_FAILED",
    `9618`= "DNS_ERROR_SOA_DELETE_INVALID",
    `9619`= "DNS_ERROR_FORWARDER_ALREADY_EXISTS",
    `9620`= "DNS_ERROR_ZONE_REQUIRES_MASTER_IP",
    `9621`= "DNS_ERROR_ZONE_IS_SHUTDOWN",
    `9622`= "DNS_ERROR_ZONE_LOCKED_FOR_SIGNING",
    `9651`= "DNS_ERROR_PRIMARY_REQUIRES_DATAFILE",
    `9652`= "DNS_ERROR_INVALID_DATAFILE_NAME",
    `9653`= "DNS_ERROR_DATAFILE_OPEN_FAILURE",
    `9654`= "DNS_ERROR_FILE_WRITEBACK_FAILED",
    `9655`= "DNS_ERROR_DATAFILE_PARSING",
    `9701`= "DNS_ERROR_RECORD_DOES_NOT_EXIST",
    `9702`= "DNS_ERROR_RECORD_FORMAT",
    `9703`= "DNS_ERROR_NODE_CREATION_FAILED",
    `9704`= "DNS_ERROR_UNKNOWN_RECORD_TYPE",
    `9705`= "DNS_ERROR_RECORD_TIMED_OUT",
    `9706`= "DNS_ERROR_NAME_NOT_IN_ZONE",
    `9707`= "DNS_ERROR_CNAME_LOOP",
    `9708`= "DNS_ERROR_NODE_IS_CNAME",
    `9709`= "DNS_ERROR_CNAME_COLLISION",
    `9710`= "DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT",
    `9711`= "DNS_ERROR_RECORD_ALREADY_EXISTS",
    `9712`= "DNS_ERROR_SECONDARY_DATA",
    `9713`= "DNS_ERROR_NO_CREATE_CACHE_DATA",
    `9714`= "DNS_ERROR_NAME_DOES_NOT_EXIST",
    `9715`= "DNS_WARNING_PTR_CREATE_FAILED",
    `9716`= "DNS_WARNING_DOMAIN_UNDELETED",
    `9717`= "DNS_ERROR_DS_UNAVAILABLE",
    `9718`= "DNS_ERROR_DS_ZONE_ALREADY_EXISTS",
    `9719`= "DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE",
    `9720`= "DNS_ERROR_NODE_IS_DNAME",
    `9721`= "DNS_ERROR_DNAME_COLLISION",
    `9722`= "DNS_ERROR_ALIAS_LOOP",
    `9751`= "DNS_INFO_AXFR_COMPLETE",
    `9752`= "DNS_ERROR_AXFR",
    `9753`= "DNS_INFO_ADDED_LOCAL_WINS",
    `9801`= "DNS_STATUS_CONTINUE_NEEDED",
    `9851`= "DNS_ERROR_NO_TCPIP",
    `9852`= "DNS_ERROR_NO_DNS_SERVERS",
    `9901`= "DNS_ERROR_DP_DOES_NOT_EXIST",
    `9902`= "DNS_ERROR_DP_ALREADY_EXISTS",
    `9903`= "DNS_ERROR_DP_NOT_ENLISTED",
    `9904`= "DNS_ERROR_DP_ALREADY_ENLISTED",
    `9905`= "DNS_ERROR_DP_NOT_AVAILABLE",
    `9906`= "DNS_ERROR_DP_FSMO_ERROR",
    `9911`= "DNS_ERROR_RRL_NOT_ENABLED",
    `9912`= "DNS_ERROR_RRL_INVALID_WINDOW_SIZE",
    `9913`= "DNS_ERROR_RRL_INVALID_IPV4_PREFIX",
    `9914`= "DNS_ERROR_RRL_INVALID_IPV6_PREFIX",
    `9915`= "DNS_ERROR_RRL_INVALID_TC_RATE",
    `9916`= "DNS_ERROR_RRL_INVALID_LEAK_RATE",
    `9917`= "DNS_ERROR_RRL_LEAK_RATE_LESSTHAN_TC_RATE",
    `9921`= "DNS_ERROR_VIRTUALIZATION_INSTANCE_ALREADY_EXISTS",
    `9922`= "DNS_ERROR_VIRTUALIZATION_INSTANCE_DOES_NOT_EXIST",
    `9923`= "DNS_ERROR_VIRTUALIZATION_TREE_LOCKED",
    `9924`= "DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME",
    `9925`= "DNS_ERROR_DEFAULT_VIRTUALIZATION_INSTANCE",
    `9951`= "DNS_ERROR_ZONESCOPE_ALREADY_EXISTS",
    `9952`= "DNS_ERROR_ZONESCOPE_DOES_NOT_EXIST",
    `9953`= "DNS_ERROR_DEFAULT_ZONESCOPE",
    `9954`= "DNS_ERROR_INVALID_ZONESCOPE_NAME",
    `9955`= "DNS_ERROR_NOT_ALLOWED_WITH_ZONESCOPES",
    `9956`= "DNS_ERROR_LOAD_ZONESCOPE_FAILED",
    `9957`= "DNS_ERROR_ZONESCOPE_FILE_WRITEBACK_FAILED",
    `9958`= "DNS_ERROR_INVALID_SCOPE_NAME",
    `9959`= "DNS_ERROR_SCOPE_DOES_NOT_EXIST",
    `9960`= "DNS_ERROR_DEFAULT_SCOPE",
    `9961`= "DNS_ERROR_INVALID_SCOPE_OPERATION",
    `9962`= "DNS_ERROR_SCOPE_LOCKED",
    `9963`= "DNS_ERROR_SCOPE_ALREADY_EXISTS",
    `9971`= "DNS_ERROR_POLICY_ALREADY_EXISTS",
    `9972`= "DNS_ERROR_POLICY_DOES_NOT_EXIST",
    `9973`= "DNS_ERROR_POLICY_INVALID_CRITERIA",
    `9974`= "DNS_ERROR_POLICY_INVALID_SETTINGS",
    `9975`= "DNS_ERROR_CLIENT_SUBNET_IS_ACCESSED",
    `9976`= "DNS_ERROR_CLIENT_SUBNET_DOES_NOT_EXIST",
    `9977`= "DNS_ERROR_CLIENT_SUBNET_ALREADY_EXISTS",
    `9978`= "DNS_ERROR_SUBNET_DOES_NOT_EXIST",
    `9979`= "DNS_ERROR_SUBNET_ALREADY_EXISTS",
    `9980`= "DNS_ERROR_POLICY_LOCKED",
    `9981`= "DNS_ERROR_POLICY_INVALID_WEIGHT",
    `9982`= "DNS_ERROR_POLICY_INVALID_NAME",
    `9983`= "DNS_ERROR_POLICY_MISSING_CRITERIA",
    `9984`= "DNS_ERROR_INVALID_CLIENT_SUBNET_NAME",
    `9985`= "DNS_ERROR_POLICY_PROCESSING_ORDER_INVALID",
    `9986`= "DNS_ERROR_POLICY_SCOPE_MISSING",
    `9987`= "DNS_ERROR_POLICY_SCOPE_NOT_ALLOWED",
    `9988`= "DNS_ERROR_SERVERSCOPE_IS_REFERENCED",
    `9989`= "DNS_ERROR_ZONESCOPE_IS_REFERENCED",
    `9990`= "DNS_ERROR_POLICY_INVALID_CRITERIA_CLIENT_SUBNET",
    `9991`= "DNS_ERROR_POLICY_INVALID_CRITERIA_TRANSPORT_PROTOCOL",
    `9992`= "DNS_ERROR_POLICY_INVALID_CRITERIA_NETWORK_PROTOCOL",
    `9993`= "DNS_ERROR_POLICY_INVALID_CRITERIA_INTERFACE",
    `9994`= "DNS_ERROR_POLICY_INVALID_CRITERIA_FQDN",
    `9995`= "DNS_ERROR_POLICY_INVALID_CRITERIA_QUERY_TYPE",
    `9996`= "DNS_ERROR_POLICY_INVALID_CRITERIA_TIME_OF_DAY",
    `10054`= "WSAECONNRESET",
    `10055`= "WSAENOBUFS",
    `10060`= "WSAETIMEDOUT"
  )

  LET ParseDNSAnswers(X) = SELECT if(condition=_value =~ "^type",
  then=dict(
     data=parse_string_with_regex(
        string=regex_replace(source=_value, replace="", re="::ffff:"),
        regex="(?P<Data>[^\\s]+)$").Data,
     type=get(item=DNSLookup,
       field=parse_string_with_regex(
         string=_value, regex="type:\\s+([0-9]+)").g1)),
  else=dict(
     data=regex_replace(source=_value, replace="", re="::ffff:"),
     type=if(condition=regex_replace(source=_value, replace="", re="::ffff:") =~ ":",
             then="AAAA", else="A")
  )) AS Field
  FROM foreach(row=split(string=X, sep=";"))
  WHERE _value

  LET ParseHashes(Hashes) = to_dict(item={
     SELECT split(string=_value, sep="=")[0] AS _key,
            split(string=_value, sep="=")[1] AS _value
     FROM foreach(row=split(string=Hashes, sep=","))
  })

  LET _EventToECSBase(System, EventData) = dict(
     ecs=dict(version="1.12.0"),
     log=dict(level=System.Level),
     event=dict(
        module="sysmon",
        kind="event",
        code=System.EventID.Value,
        category=get(item=CategoryLookup, field=str(str=System.EventID.Value)),
        type=get(item=TypeLookup, field=str(str=System.EventID.Value)),
        created=timestamp(epoch=System.TimeCreated.SystemTime)
     ),
     error=dict(
       code=if(condition=System.EventID.Value = 255, then=EventData.ID, else=0)
     ),
     rule=dict(
       name=EventData.RuleName
     ),
     message=if(condition=System.EventID.Value = 255, then=EventData.Type, else=""),
     winlog=dict(
        api="wineventlog",
        channel=System.Channel,
        computer_name=System.Computer,
        event_data=NormalizeEventData(EventData=EventData),
        event_id=System.EventID.Value ,
        opcode=get(item=OpcodesLookup, field=str(str=System.Opcode)),
        process=dict(
           pid=System.Execution.ProcessID,
           thread=dict(
             id=System.Execution.ThreadID
           )
        ),
        provider_guid=System.Provider.Guid,
        provider_name=System.Provider.Name,
        record_id=str(str=System.EventRecordID),
        user=dict(
          identifier=System.Security.UserID
        )
     )
  )

  LET _EventToECSProcess(System, EventData) = dict(
     process=dict(
       hash=ParseHashes(Hashes=EventData.Hashes),
       entity_id=EventData.ProcessGuid || EventData.SourceProcessGuid || EventData.SourceProcessGUID,
       pid=EventData.ProcessId || EventData.SourceProcessId,
       executable=EventData.Image || EventData.SourceImage || EventData.Destination,
       command_line=EventData.CommandLine,
       working_directory=EventData.CurrentDirectory,
       parent=dict(
          pid=EventData.ParentProcessId,
          entity_id= EventData.ParentProcessGuid,
          executable=EventData.ParentImage,
          command_line=EventData.ParentCommandLine,
          args=commandline_split(command=EventData.ParentCommandLine),
          args_count=len(list=commandline_split(command=EventData.ParentCommandLine)),
          name=pathspec(parse=EventData.ParentImage, path_type="windows").Basename
       ),
       thread=dict(
          id= EventData.SourceThreadId || 0
       ),
       pe=if(condition=System.EventID.Value != 7, then=dict(
         original_file_name=EventData.OriginalFileName || "",
         company=EventData.Company || "",
         description=EventData.Description || "",
         file_version=EventData.FileVersion || "",
         product= EventData.Product || ""
       )),
       args=commandline_split(command=EventData.CommandLine),
       args_count=len(list=commandline_split(command=EventData.CommandLine)),
       name=pathspec(parse=EventData.Image, path_type="windows").Basename
    )
  )

  LET _EventToECSNetwork(System, EventData) = dict(
    network=dict(
      transport=EventData.Protocol,
      protocol=if(condition=System.EventID.Value = 22, then="dns", else=EventData.DestinationPortName || EventData.SourcePortName),
      direction=if(condition= EventData.Initiated, then="egress", else="ingress"),
      type=if(condition= EventData.SourceIsIpv6, then="ipv6", else="ipv4")
    ),
    source=dict(
      ip=EventData.SourceIp,
      domain=EventData.SourceHostname,
      port=EventData.SourcePort
    ),
    destination=dict(
      ip=EventData.DestinationIp,
      domain=EventData.DestinationHostname,
      port=EventData.DestinationPort
    ),
    dns=dict(
      answers=ParseDNSAnswers(X=EventData.QueryResults).Field,
      question=dict(
         name=EventData.QueryName
      ),
      status=get(item=DnsStatusLookup, field=str(str=EventData.QueryStatus))
    )
  )

  LET _ParseRegData(X) = if(condition=X =~ "^DWORD",
     then=dict(
        strings=[str(str=int(int= parse_string_with_regex(string=X, regex="\\((.+?)\\)").g1)),],
        type="DWORD"),
     else=if(condition=X =~ "^Binary Data",
             then=dict(
               strings=["Binary Data",],
               type="REG_BINARY"),
             else=if(condition=X =~ "^QWORD",
                     then=dict(
                         strings=[str(str=int(int= regex_replace(re="-0x", replace="",
                                  source=parse_string_with_regex(string=X, regex="\\((.+?)\\)").g1))),],
                         type="QWORD"),
                     else=dict(strings=X, type=parse_string_with_regex(string=X, regex="(^[^\\S]+)").g1)
            )
     ))

  LET _EventToECSRegistry(System, EventData) = dict(
     process=dict(
       entity_id=EventData.ProcessGuid || EventData.SourceProcessGuid || EventData.SourceProcessGUID,
       pid=EventData.ProcessId || EventData.SourceProcessId,
       executable=EventData.Image || EventData.SourceImage || EventData.Destination,
       name=pathspec(parse=EventData.Image, path_type="windows").Basename
     ),
     registry=dict(
        hive=pathspec(parse=EventData.TargetObject, path_type="registry")[0],
        key=pathspec(parse=EventData.TargetObject, path_type="registry")[1:],
        path=EventData.TargetObject,
        value=pathspec(parse=EventData.TargetObject, path_type="registry").Basename,
        data= _ParseRegData(X=EventData.Details)
     )
  )

  LET _EventToECSFile(System, EventData) = dict(
    file=dict(
      path=EventData.TargetFilename || EventData.Device || EventData.ImageLoaded,
      directory=pathspec(parse=EventData.TargetFilename || EventData.Device || EventData.ImageLoaded, path_type="windows").Dirname,
      name=EventData.PipeName || pathspec(parse=EventData.TargetFilename || EventData.Device || EventData.ImageLoaded, path_type="windows").Basename,
      code_signature=dict(
        subject_name= EventData.Signature || "",
        status = EventData.SignatureStatus || "",
        signed=if(condition=EventData.Signed, then=TRUE, else=FALSE),
        valid=EventData.SignatureStatus = "Valid"
      ),
      process=dict(
        entity_id=EventData.ProcessGuid || EventData.SourceProcessGuid || EventData.SourceProcessGUID,
        pid=EventData.ProcessId || EventData.SourceProcessId,
        executable=EventData.Image || EventData.SourceImage || EventData.Destination,
        name=pathspec(parse=EventData.Image, path_type="windows").Basename,
        hash=ParseHashes(Hashes=EventData.Hash)
      ),
      pe=dict(
        original_file_name=EventData.OriginalFileName || "",
        company=EventData.Company || "",
        description=EventData.Description || "",
        file_version=EventData.FileVersion || "",
        product=EventData.Product || ""
      ),
      sysmon=dict(
        file=dict(
          archived=if(condition=EventData.Archived =~ "true", then=TRUE, else=FALSE),
          is_executable=if(condition=EventData.is_executable, then=TRUE, else=FALSE)
        )
      )
    )
  )

  LET SysmonEventToECS(System, EventData) = _EventToECSBase(System=System, EventData=EventData) + if(
        condition=get(item=CategoryLookup, field=str(str=System.EventID.Value)) =~ "process",
        then=_EventToECSProcess(System=System, EventData=EventData),
      else=if(
        condition=get(item=CategoryLookup, field=str(str=System.EventID.Value)) =~ "network",
        then=_EventToECSNetwork(System=System, EventData=EventData),
      else=if(
        condition=get(item=CategoryLookup, field=str(str=System.EventID.Value)) =~ "registry",
        then=_EventToECSRegistry(System=System, EventData=EventData),
      else=if(
        condition=get(item=CategoryLookup, field=str(str=System.EventID.Value)) =~ "file",
        then=_EventToECSFile(System=System, EventData=EventData),
      else=dict()))))

sources:
  - query: |
      SELECT * FROM foreach(row={
        SELECT * FROM foreach(row={
           SELECT OSPath FROM glob(globs=LogFileGlob)
        }, query={
           SELECT SysmonEventToECS(System=System, EventData=EventData) AS ECS
           FROM parse_evtx(filename=OSPath)
        })
      }, column="ECS")

    notebook:
      - type: vql_suggestion
        name: "Upload to Elastic"
        template: |
          /*
             * Modify the Elastic parameters to upload this dataset.
             * You might need to add authentication to Elastic.
          */
          LET ElasicAddress = "http://localhost:9200"

          // Uncomment this when you are ready to upload the data
          LET X = SELECT *
          FROM elastic_upload(
            addresses=ElasicAddress,
            index="winlogbeat-velo",
            action="create",
            query={
               SELECT timestamp(epoch=now()) AS `@timestamp`,
                      ClientId,
                      client_info(client_id=ClientId).Hostname AS Hostname,
                      *
               FROM source(artifact="Elastic.EventLogs.Sysmon")
               LIMIT 10
            })