ScriptRunner Blog
Handling Events with PowerShell and WMI
Inhaltsverzeichnis
In this blog post, we will have a look at WMI eventing. You’ll learn about the two types of WMI events (extrinsic and intrinsic) and how you can handle both with PowerShell.
This is part 2 of our series „Handling events with PowerShell“. Click here, for the first part of the series Handling Events with PowerShell and .NET
What is WMI Eventing
WMI stands for Windows management instrumentation. WMI is the Microsoft implementation of CIM (Common Information Model). CIM is a standard from DMTF (Distributed Management Task Force) which defines how to represent system information. In short CIM is a vendor neutral model used to represent system information. Microsoft’s implementation of CIM is known as WMI.
One can think of WMI as a database which stores information about the system. But how can we view this information and where is it stored? On a Windows system you can find WMI located at
C:\Windows\System32\wbem\Repository
as shown in fig. 1 below.
Wmic was the tool of choice to interact with WMI, but it has since been deprecated. Although it’s still available on Windows system and if you try to use it you will receive a message like the one shown in fig. 2.
Moving forward Microsoft recommends using PowerShell to interact with WMI. PowerShell has inbuilt cmdlets to interact with WMI. These were called “WMI cmdlets” however they have been deprecated in favor of CIM cmdlets since PowerShell version 3. In this article we will be using CIM cmdlets. In order to view available CIM cmdlets we will use our favorite cmdlet called Get-Command
Get-Command -noun *CIM*
Get-CimAssociatedInstance Get-CimClass Get-CimInstance Get-CimSession Invoke-CimMethod New-CimInstance New-CimSession New-CimSessionOption Register-CimIndicationEvent Remove-CimInstance Remove-CimSession Set-CimInstance
For the sake of completeness lets also take a look at the WMI cmdlets available in PowerShell, using the following cmdlet
Get-Command -noun *wmi*
resulting in the following list:
Get-WmiObject Invoke-WmiMethod Register-WmiEvent Remove-WmiObject Set-WmiInstance
It’s clear to see, that there are more CIM cmdlets than WMI cmdlets. CIM cmdlets are the future hence we won’t be talking about WMI cmdlets anymore.
As mentioned before, WMI is used to represent system information but how does it store this information? The answer is simple: WMI uses hierarchical structure starting from namespaces to store and manage system wide information. These namespaces have various classes underneath them where each class represents specific system information. So, for example the WIN32_DiskDrive class is used to represent information on disk drive. If the system has a disk drive, then this class will have an object with properties and methods representing the disk drive for that system.
We can use the Get-CIMInstance cmdlet to interact with WMI and view this object. The Get-CIMInstance requires class name as one of the parameters. As we can see in fig. 3, the Get-CIMInstance cmdlet returns the object for Win32_DiskDrive class. This object has properties such as DeviceID, Caption, Partitions, Size, Model etc.
Intrinsic WMI Events
Intrinsic events are tied closer to WMI itself. They are triggered in response to changes in WMI structure. For example, if a new process is created on the system it will result in a new instance being created for the WIN32_Process class, this will result in an event of type __Instancecreationevent. Another example would be a new class being created in WMI this will result in an event of type __ClassCreationEvent.
Just like everything in WMI is represented as objects, events are represented as objects too and each event type has an associated class as listed below. However, one thing to keep in mind is that these objects representing an event are short-lived hence we use pooling when we are creating our event filter else we can miss these objects being created.
Following are different types of Intrinsic events:
__NamespaceOperationEvent, __NamespaceModificationEvent, __NamespaceDeletionEvent, __NamespaceCreationEvent, __ClassOperationEvent __ClassDeletionEvent, __ClassModificationEvent, __ClassCreationEvent, __InstanceOperationEvent, __InstanceCreationEvent, __MethodInvocationEvent, __InstanceModificationEvent, __InstanceDeletionEvent, __TimerEvent
Extrinsic Events
Extrinsic events are generated based on underlying OS level changes. This is a major difference while intrinsic events are looking for changes within WMI structure, extrinsic events are looking for changes outside WMI at OS level. For example, Computer shutdown event is an OS level event hence its classified as Extrinsic event. Extrinsic events do not require pooling.
Following are different types of extrinsic events:
Win32_ComputerShutdownEvent, Win32_IP4RouteTableEvent, Win32_ProcessStartTrace, Win32_ModuleLoadTrace, Win32_ThreadStartTrace, Win32_VolumeChangeEvent, Msft_WmiProvider*, RegistryKeyChangeEvent, RegistryValueChangeEvent
In order to work with these events, we need to understand the concept of filters, but before we talk about filter, we take a quick segue to WQL.
WQL is WMI query language and is like SQL. It uses the same syntax as SQL to query WMI for information.
More information on WQL can be found at on Microsoft Documentation site.
Using WQL for event filters
We use WQL to create filters for events. A filter can be thought of as a search condition to look for particular event.
Let’s take an example: suppose we want to capture a new process creation event, particularly the calculator process. In order to do this, we need to create a WQL query for defining the filter for capturing this event. In this case we want to capture the creation of a new instance for WIN32_Process class where process name is „calc.exe“, once we capture this event, we want to take an action. For this specific example we want to print “WMI is awesome”.
Let’s start by defining our filter using WQL, in this case we are looking for instance creation event hence our query starts with
Select * from __InstanceCreationEvent
Next, since this is an Intrinsic Event we need to define the pooling interval hence our WQL query becomes
Select * from __InstanceCreationEvent within 10
Next, we need to define which class instance we are looking for? So, we add that information to our query
Select * from __InstanceCreationEvent within 10 where TargetInstance ISA “Win32_Process”
Next, we need to be specific in what type of instance we are looking for, in this case we are looking for a process instance with name property “calc.exe” hence our WQL query end up looking like below.
Select * from __InstanceCreationEvent within 10 where TargetInstance ISA “Win32_Process” and TargetInstance.Name like “%calc%”
Now that we have our filter defined, we need to subscribe for the event and define the action to take once the event is observed.
In order to do this, we would use the Register-CimIndicationEvent cmdlet. The Register-CimIndicationEvent cmdlet requires following parameters to subscribe for an event (as shown in Fig. 6)
-wqlQuery -SourceIdentifier -Action
The -SouceIdentifier parameter can be supplied with a user defined name.
If the cmdlet is successful you will receive a message like the one show in fig. 7. It indicates that we have successfully subscribed to the event.
As you can observe, once I launch the calc.exe process I get the message “WMI is Awesome” printed on the screen.
This works fine until you exit the PowerShell command prompt. Since this is a temporary event subscription it is dependent on the lifetime of the process under which it was created, in our case the PowerShell command prompt. Once we exit the parent process we lose our subscription.
WMI provides permanent WMI event subscription which makes our subscription permanent and persist system reboot.
This brings us to the next important concept we need to understand before we start building permanent event subscriptions. There are three major concepts to understand
- Event Filter
- Event Consumer
- Event Filter to Consumer binding
Event filters
We have seen event filters before in our last example, as previously mentioned Event filters are used to subscribe for particular events. You can think of them as search condition to look for particular events. But there is a difference: while in our last example we defined the filter and supplied it in the form of WQL query to the Register-CimIndicationEvent cmdlet, in case of permanent event subscription we will create our filter in WQL, however we will then initialize an object of class __EventFilter and supply our filter as one of the properties of this object.
Event consumer
Once our event filter matches the criteria we would like to take certain action like we did in our example for temporary event subscription. For permanent event subscription we have five action types provided by five different consumer classes to choose from.
- LogFileEventConsumer: Writes to a log file.
- ActiveScriptEventConsumer: Execute a Script.
- NTEventLogEventConsumer: Write to Windows event log.
- SMTPEventConsumer: Send an email.
- CommandLineEventConsumer: Command line execution.
Just like in the case of Event filters once we know what action we like to take we need to initialize an object of that class and supply it with values for certain properties.
Once you have the event filter and consumer defined the only work left is to join them together, filter to consumer binding do exactly that for us. We create an object of __FilterToConsumerBinding class and supply it with values for Filter and Consumer objects.
Example: setting up permanent WMI event subscription
Let’s take an example, suppose we want to generate Event log when a user plugs in a USB device. We can easily do this using WMI eventing.
It’s a three-step process:
- We first define the Filter for an Event representing “user plugging in USB stick”
- We then define a Consumer to generate Event log when this Filter matches the Event
- Finally, Filter-to-Consumer binding joins the Event Filter with the rightful Consumer
Fig. 8 shows a schematic representation of this process.
The New-CimInstance cmdlet is used to create an instance of CIM class and can be used for creating instances on remote computer if used with CimSession parameter.
In our script we use the New-CIMInstance cmdlet to create instances for EventFilter, EventConsumer and FilterToConsumer classes.
With that said, let’s bring all of this together and create our first WMI event subscription:
Step 1: First we define our WQL query for our filter:
$WQLQuery = 'SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA "Win32_USBControllerDevice"'
When a USB device is plugged into a system a new instance of WIN32_USBControllerDevice is created hence we are looking for an instance creation event. Now, we define our filter as shown below:
$WMIFilterInstance = New-CimInstance -ClassName __EventFilter -Namespace "rootsubscription" -Property @{Name="myFilter"; EventNameSpace="rootcimv2"; QueryLanguage="WQL"; Query=$WQLQuery }
As you can notice we are creating an object of class __EventFilter and store it in $WMIFilterInstance variable. We also provide values for some properties of this object, the most important being Name and Query.
Step 2: Next, we define our consumer, in our case we want to generate an event log hence we create an instance of NTEventLogEventConsumer class.
$WMIEventConsumer = New-CimInstance -ClassName NTEventLogEventConsumer -Namespace "rootsubscription" -Property @{Name="USBLogging"; EventId = [uint32] 1; EventType = [uint32] 4; #EventType can have following values; Error 1, FailureAudit 16, Information 4, SuccesAudit 8, Warning 2 SourceName="PowerShell-Script-Log"; Category= [uint16] 1000 } #Category is never really used but can have any value and basically meant to provide more information about the event
Some things to keep in mind are the EventID and EventType properties, also the sourceName is used to define the source of the event.
Step 3: Finally, we bind our filter with the consumer.
$WMIWventBinding = New-CimInstance -ClassName __FilterToConsumerBinding -Namespace "rootsubscription" -Property @{Filter = [Ref] $WMIFilterInstance; Consumer = [Ref] $WMIEventConsumer }
As you can notice in order to create the filter-to-consumer binding we need to supply the object we created earlier a value for the Filter and Consumer property. You can find the complete script I’m using here in my GitHub repository
Once executed, this script creates a permanent event subscription. The next time a user plugs in a USB device you will notice an entry in the Windows Event log being created, just as shown in fig. 9 and fig. 10 below.
Remove an event subscription
Just a final note before we end this blog post: if we want to remove permanent event subscription, we need to execute following three commands:
Get-CimInstance -ClassName __EventFilter -namespace rootsubscription -filter "name='myFilter'" | Remove-CimInstance Get-CimInstance -ClassName NTEventLogEventConsumer -Namespace rootsubscription -filter "name='FolderWriteLogging'" | Remove-CimInstance Get-CimInstance -ClassName __FilterToConsumerBinding -Namespace rootsubscription -Filter "Filter = ""__eventfilter.name='myFilter'""" | Remove-CimInstance
The Get-CimInstance cmdlet returns the object for the class specified by using the ClassName parameter which we pass it to Remove-CimInstance cmdlet for removal. More information on GetCimInstance can be found in the Microsoft Documentation.
Conclusion
As you can notice we can leverage WMI eventing for monitoring various changes across the system. WMI is powerful technology yet remains unknown among systems administrators. PowerShell has made it easy to interact and work with WMI by providing us with useful cmdlets. I hope this article will encourage the audience to learn more about WMI and leveraging PowerShell to use this technology for interesting projects.
Full control and complete traceability
You can leverage your monitoring capabilities even more by using ScriptRunners Report/Audit DB Connector. It lets you store all your reports on script execution in one central repository and provides you with complete traceability of all processes.
Zusammenhängende Posts
14 min read
Microsoft Teams – 3. Teil der Graph PowerShell Reihe
Okt 30, 2024 by Damian Scoles
MVP Damien Scoles berichtet über seine Erfahrungen mit Microsoft Graph. In seinem dritten Artikel geht er näher auf...
15 min read
Exchange Online – 2. Teil der Graph PowerShell Reihe
Okt 16, 2024 by Damian Scoles
Wie unterscheidet sich die Exchange Online-Administration mit dem Microsoft Graph PowerShell-Modul vom herkömmlichen...
11 min read
Graph PowerShell SDK – 1. Teil der Graph Artikelreihe
Okt 8, 2024 by Damian Scoles
Die ein oder andere Hürde steht Nutzern im Weg, wenn sie anfangen Graph zu nutzen. In drei Teilen liefert Damian Hilfe....
Über den Autor:
Sonny ist ein selbsternannter PowerShell-Prediger, der in der schönen Stadt Halifax an der Ostküste Kanadas lebt. Sonny arbeitet seit mehr als 10 Jahren im Bereich Cybersecurity und hat als primärer technischer Leiter und Fachexperte bei vielen Cyber Security Assessments für verschiedene private und öffentliche Organisationen fungiert. Sonny spricht regelmäßig auf verschiedenen Sicherheitskonferenzen wie BSides, AtlSecCon, ISACA, OWASP etc.