This post shows a small Powershell script that I created to capture login events using WMI event subscription in Windows. Note that this method only applies to current Powershell session and is not persistent. I also show more persistent method using Managed Object Format and WMI repository.

Using Powershell and Register-WmiEvent cmdlet

Here’s a small script that I created to capture login events using WMI event subscription and Powershell. What the script does in practice:

Script content

  1. Specify query that returns win32_LogOnSession events
  2. Use Register-WmiEvent to subscribe to these events
  3. When LogOnSession event occurs:
    1. Find related username based on the LogonId information that is returned in the event
    2. Find SID of the username (tested only with non-ad machine/users)
# Get win32_LogOnSession events
$query = "Select TargetInstance From __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'win32_LogOnSession'"
# Subscripe to events based on the defined query
Register-WmiEvent -SourceIdentifier LogonCapture -Query $query -Action {
            # Get logon type from the WMI event
            $thisLogonType = $event.SourceEventArgs.NewEvent.TargetInstance.LogonType
            # Get logonID from the WMI event
            $thisLogonId = $event.SourceEventArgs.NewEvent.TargetInstance.LogonId
            # Get logged on users
            $LoggedOnUsers = Get-WmiObject -Class win32_loggedonuser | Select-Object Antecedent,Dependent
            # Loop over logged on users
            foreach ($userobj in $LoggedOnUsers) {
                # Parse logonID from the current item
                $LogonID = $userobj.Dependent | Select-String -Pattern 'win32_LogonSession\.LogonId="(.*)"' | % { $_.matches.groups[1] }
                # Check if the logonID matches with the ID of the WMI event
                if ($LogonID.Value -eq $thisLogonId ) {
                    # If logon IDs matched then extract associated username 
                    $username = $userobj.Antecedent | Select-String -Pattern ',Name="(.*)"' | % { $_.matches.groups[1] }
                    # Get SID of the username
                    $userObjForSid = New-Object System.Security.Principal.NTAccount($username.Value)
                    $strSID = $userObjForSid.Translate([System.Security.Principal.SecurityIdentifier])
                    # Write event details to console
                    Write-Host "New LogOn event for user $username (SID: $strSID) of type $thisLogonType"
                }
            }
               
}

Example run

Below is an example where I have saved the script as “monitor_login.ps1” and launched it. The shown output is coming from the Register-WmiEvent command.

After the event subscription was registered I logged in few times via RDP. The New LogOn event for user... texts are result from those logins.

You can find the below line from the script which constructs the output line.

Write-Host "New LogOn event for user $username (SID: $strSID) of type $thisLogonType"

Using Managed Object Format and WMI repository

Here is an example of how to add persistent WMI event subscription by using Managed Object Format (MOF) and Mofcomp compiler.

Add subscriptions to WMI repository with MOF

Below is the MOF file I’m using. The basic idea is pretty simple. It defines a filter that captures win32_LogOnSession events and a consumer that writes output of Get-WmiObject -Class win32_loggedonuser to file C:\users_<MM-dd-yyyy-HH-mm>.json.

// Set the namespace as root\subscription.
// The CommandLineEventConsumer is already compiled
// in the root\subscription namespace. 
#pragma namespace ("\\\\.\\Root\\subscription")

// Create an instance of the command line consumer
// and give it the alias $CMDLINECONSUMER

instance of CommandLineEventConsumer as $CMDLINECONSUMER
{
 Name = "CmdLineConsumer_Example";
 CommandLineTemplate = "powershell -Command \"Get-WmiObject -Class win32_loggedonuser | ConvertTo-Json | Out-File C:\\users_$(Get-Date -Format 'MM-dd-yyyy-HH-mm').json\"";
 RunInteractively = False;
};    

// Create an instance of the event filter
// and give it the alias $CMDLINEFILTER
// The filter queries for instance creation event
// for instances of the MyCmdLineConsumer class

instance of __EventFilter as $CMDLINEFILTER
{
    Name = "CmdLineFilter";
    EventNameSpace = "root\\cimv2";
    Query = "Select TargetInstance From __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'win32_LogOnSession'";
    QueryLanguage = "WQL";
};

// Create an instance of the binding
// between filter and consumer instances.

instance of __FilterToConsumerBinding
{
     Consumer = $CMDLINECONSUMER;
     Filter = $CMDLINEFILTER;
};

Next, I save the file as test.mof and compile it, so it’s added to the WMI repository.

Explore WMI repository

One option to view the WMI repository is to use WMI explorer. Here’s how the created filter and consumer can be observed in the app: