Windows.Events.TrackProcessesETW

This artifact uses ETW to track process execution using the Velociraptor Process Tracker.

The Process Tracker keeps track of exited processes, and resolves process call chains from it in memory cache.

This event artifact enables the global process tracker and makes it possible to run many other artifacts that depend on the process tracker.

This tracker DOES NOT require Sysmon and is incompatible with Windows.Events.TrackProcesses and Windows.Events.TrackProcessesBasic (only one should be running).


name: Windows.Events.TrackProcessesETW
description: |
  This artifact uses ETW to track process execution using the
  Velociraptor Process Tracker.

  The Process Tracker keeps track of exited processes, and resolves
  process call chains from it in memory cache.

  This event artifact enables the global process tracker and makes it
  possible to run many other artifacts that depend on the process
  tracker.

  This tracker DOES NOT require Sysmon and is **incompatible** with
  `Windows.Events.TrackProcesses` and
  `Windows.Events.TrackProcessesBasic` (only one should be running).

type: CLIENT_EVENT

parameters:
  - name: MaxSize
    type: int64
    description: Maximum size of the in memory process cache (default 10k)
  - name: PollPeriod
    type: int64
    description: How often to run pslist to track processes (in Seconds)
    default: 60
  - name: AlsoForwardUpdates
    type: bool
    description: |
      If set we also send process tracker state updates to
      the server.

export: |
    LET EventSource = SELECT *
      FROM watch_etw(kernel_tracer_type=["process", "image_load"],
                     guid="kernel")

    LET LRU <= lru(size=1000)

    -- only used to see what is goinng on.
    LET BuildDebugEvent(System, EventData) = dict(
      id=str(str=System.ProcessID),
      parent_id="0",
      update_type="debug",
      start_time=NULL,
      end_time=NULL,
      System=System,
      EventData=EventData,
      data=dict(foo=NULL)
    )

    LET BuildTerminateEvent(System, EventData) = dict(
      id=str(str=EventData.ProcessId),
      parent_id=str(str=EventData.ParentId),
      update_type="exit",
      start_time=NULL,
      end_time=System.TimeStamp,
      System=System,
      EventData=EventData,
      data=get(item=LRU, field=str(str=EventData.ProcessId)).data
    )

    LET BuildStartEvent(System, EventData) = dict(
      id=str(str=EventData.ProcessId),
      parent_id=str(str=EventData.ParentId),
      update_type="start",
      start_time=System.TimeStamp,
      end_time=NULL,
      System=System,
      EventData=EventData,
      data=dict(
        ProcessId=EventData.ProcessId,
        Created=System.TimeStamp,
        ParentId=EventData.ParentId,
        Username=EventData.UserSID,
        Name=EventData.ImageFileName,
        CommandLine=EventData.CommandLine,
        Exe=EventData.CommandLine
    ))

    -- Insert the event into the local LRU cache and return it.
    LET Cache(Pid, Event) = set(item=LRU, field=str(str=Pid), value=Event) && Event

    -- Enrich the event with the new key value and return it.
    LET Enrich(Pid, Key, Value) = set(item=get(item=LRU,
         field=str(str=Pid)).data, field=Key, value=Value) &&
         get(item=LRU, field=str(str=Pid))

    -- Analyze the event and emit the relevant row if needed.
    LET EmitEvent(System, EventData) = if(

      -- Enrich process data with full executable path from
      -- LoadImage. This event usually comes after the CreateProcess
      -- so we have to re-emit the same event with the updated data.
      condition=System.KernelEventType = "LoadImage" && EventData.FileName =~ ".exe$",
      then=Enrich(Pid=EventData.ProcessId, Key="Exe", Value=EventData.FileName),
      else=if(

        -- Cache the process record so we can re-emit it with extra details later.
        condition=System.KernelEventType = "CreateProcess",
        then=Cache(Pid=EventData.ProcessId, Event=
             BuildStartEvent(System=System, EventData=EventData)),
        else=if(
          condition=System.KernelEventType = "TerminateProcess",
          then=BuildTerminateEvent(System=System, EventData=EventData))))

    LET UpdateQuery = SELECT * FROM foreach(row=EventSource,
    query={
      SELECT id, parent_id, update_type, start_time, end_time, data
      FROM foreach(row=EmitEvent(System=System, EventData=EventData))
    })

    LET SyncQuery = SELECT Pid AS id,
      Ppid AS parent_id,
      CreateTime AS start_time,
      dict(
        Name=Name,
        Username=Username,
        Exe=Exe,
        CommandLine=CommandLine) AS data
      FROM pslist()

precondition: |
  SELECT OS From info() where OS = 'windows'

sources:
- query: |
    LET Tracker <= process_tracker(
      max_size=MaxSize, sync_query=SyncQuery, update_query=UpdateQuery, sync_period=60000)

    SELECT * FROM process_tracker_updates()
      WHERE update_type = "stats" OR AlsoForwardUpdates