Optimiere die Ereignisüberwachung mit PowerShell WMI! Erfahre, wie Systemaufgaben automatisiert und die Ereignisverwaltung mühelos rationalisiert werden können. Es gibt zwei Arten von WMI-Ereignissen, dies deckt beide ab, die extrinsische WMI-Ereignisbehandlung und die intrinsische.
Es gibt zwei Artikel zur Behandlung von Ereignissen mit PowerShell. Nutze .NET in PowerShell für ereignisgesteuertes Scripting ist der andere Artikel.
Was ist WMI-Eventing
WMI steht für Windows Management Instrumentation. WMI ist die Microsoft-Implementierung von CIM (Common Information Model). CIM ist ein Standard der DMTF (Distributed Management Task Force), der definiert, wie Systeminformationen dargestellt werden können. Kurz gesagt ist CIM ein herstellerneutrales Modell, das zur Darstellung von Systeminformationen verwendet wird. Microsofts Implementierung von CIM ist als WMI bekannt.
Man kann sich WMI als eine Datenbank vorstellen, die Informationen über das System speichert. Aber wie können wir diese Informationen einsehen und wo werden sie gespeichert? Auf einem Windows-System finden wir WMI wie unten dargestellt.
C:\Windows\System32\wbem\Repository
Wmic war das Tool der Wahl, um mit WMI zu interagieren, aber es ist inzwischen veraltet. Es ist zwar noch auf Windows-Systemen verfügbar, aber wenn man versucht, es zu verwenden, erscheint eine Meldung wie die hier gezeigte:
Veralteter WMIC-Befehl
Mit Blick auf die Zukunft empfiehlt Microsoft die Verwendung von PowerShell zur Interaktion mit WMI. PowerShell verfügt über eingebaute Cmdlets zur Interaktion mit WMI. Diese wurden WMI cmdlet genannt, sind jedoch seit PowerShell Version 3 zugunsten von CIM cmdlets veraltet. In diesem Artikel werden wir CIM-Cmdlets verwenden. Um die verfügbaren CIM-Cmdlets anzuzeigen, werden wir unser Lieblings-Cmdlet namensGet-Command verwenden.
tabindex="0">
Get-Command -noun *CIM*
Damit erhalten wir die folgende Liste von CIM-Cmdlets:
Get-CimAssociatedInstance
Get-CimClass
Get-CimInstance
Get-CimSession
Invoke-CimMethod
New-CimInstance
New-CimSession
New-CimSessionOption
Register-CimIndicationEvent
Remove-CimInstance
Remove-CimSession
Set-CimInstance
Der Vollständigkeit halber wollen wir auch einen Blick auf die in der PowerShell verfügbaren WMI-Cmdlets werfen und zwar mit folgendem Cmdlet:
Get-Command -noun *wmi*
Resulting in the following list:
Get-WmiObject
Invoke-WmiMethod
Register-WmiEvent
Remove-WmiObject
Set-WmiInstance
Es ist klar zu erkennen, dass es mehr CIM-Cmdlets als WMI-Cmdlets gibt. CIM-Cmdlets sind die Zukunft, daher werden wir nicht mehr von WMI-Cmdlets sprechen.
Wie bereits erwähnt, wird WMI verwendet, um Systeminformationen darzustellen, aber wie speichert es diese Informationen? Die Antwort ist einfach: WMI verwendet eine hierarchische Struktur, die von Namespaces ausgeht, um systemweite Informationen zu speichern und zu verwalten. Diesen Namespaces sind verschiedene Klassen untergeordnet, wobei jede Klasse spezifische Systeminformationen darstellt. So wird z. B. die Klasse WIN32_DiskDrive verwendet, um Informationen über Festplattenlaufwerke darzustellen. Wenn das System über ein Laufwerk verfügt, dann hat diese Klasse ein Objekt mit Eigenschaften und Methoden, die das Laufwerk für dieses System darstellen.
Wir können das Cmdlet Get-CIMInstance verwenden, um mit WMI zu interagieren und dieses Objekt anzuzeigen. Das Cmdlet Get-CIMInstance erfordert den Klassennamen als einen der Parameter. Wie unten zu sehen ist, gibt das Cmdlet Get-CIMInstance das Objekt für die Klasse Win32_DiskDrive zurück. Dieses Objekt hat Eigenschaften wie DeviceID, Caption, Partitions, Size, Model etc.
Win32_DiskDrive-Objekte
Nehmen wir ein anderes Beispiel: Angenommen – und das ist auf einem modernen Laptop höchst unwahrscheinlich – ich möchte Informationen über mein CD-ROM 😊. WMI hat die Klasse Win32_CDROMDrive, die zur Darstellung von CD-ROM-Informationen verwendet wird. Verwenden wir das Get-CIMInstance-Cmdlet, um das Objekt zu erhalten.
Keine Objekte für Win32_CDROMDrive
Da ich einen modernen Laptop benutze, habe ich kein CD-ROM-Laufwerk, daher hat unser Cmdlet kein Objekt zurückgegeben (wie unten gezeigt).
Zwei interessante Beobachtungen hier: Auch wenn das CD-ROM-Objekt nicht existiert, können wir das Objekt abfragen, ohne eine Fehlermeldung zu erhalten, und zweitens, auch wenn das CD-ROM nicht auf dem System vorhanden ist, hat WMI die Klasse Win32_CDROMDrive verfügbar.
In ähnlicher Weise können wir WMI nach Informationen über alle laufenden Prozesse auf einem System abfragen, wie unten gezeigt:
Objekte für die Klasse Win32_Process
Wenn du weitere Informationen wünscht oder erkunden möchtest, welche anderen Klassen in WMI
verfügbar sind, ist die Microsoft-Dokumentation zu WMI ein guter Ausgangspunkt.
Neben der Darstellung von Systeminformationen bietet WMI auch eine leistungsstarke Funktion namens Eventing. Im Wesentlichen bietet WMI die Möglichkeit, systemweite Ereignisse zu überwachen und den Benutzer zu benachrichtigen. Es gibt zwei Arten von Ereignissen in WMI: intrinsische und extrinsische Ereignisse.
Intrinsische WMI-Ereignisse
Intrinsische Ereignisse sind enger mit WMI selbst verbunden. Sie werden als Reaktion auf Änderungen in der WMI-Struktur ausgelöst. Wenn zum Beispiel ein neuer Prozess auf dem System erstellt wird, führt dies dazu, dass eine neue Instanz für die Klasse WIN32_Process erstellt wird, was zu einem Ereignis vom Typ __Instancecreationevent führt. Ein anderes Beispiel wäre eine neue Klasse, die in WMI erstellt wird, dies führt zu einem Ereignis vom Typ __ClassCreationEvent.
Genauso wie alles in WMI als Objekte dargestellt wird, werden auch Ereignisse als Objekte dargestellt, und jeder Ereignistyp hat eine zugehörige Klasse, wie unten aufgeführt. Es ist jedoch zu beachten, dass diese Objekte, die ein Ereignis repräsentieren, kurzlebig sind. Daher verwenden wir Pooling, wenn wir unseren Ereignisfilter erstellen, da wir sonst die Erstellung dieser Objekte verpassen könnten.
Nachfolgend sind verschiedene Arten von Intrinsic Events aufgeführt:
__NamespaceOperationEvent,
__NamespaceModificationEvent,
__NamespaceDeletionEvent,
__NamespaceCreationEvent,
__ClassOperationEvent,
__ClassDeletionEvent,
__ClassModificationEvent,
__ClassCreationEvent,
__InstanceOperationEvent,
__InstanceCreationEvent,
__MethodInvocationEvent,
__InstanceModificationEvent,
__InstanceDeletionEvent,
__TimerEvent
Extrinsische WMI-Ereignisse
Extrinsische Ereignisse werden auf der Grundlage von Änderungen auf Betriebssystemebene erzeugt. Dies ist ein wesentlicher Unterschied: Während intrinsische Ereignisse nach Änderungen innerhalb der WMI-Struktur suchen, suchen extrinsische Ereignisse nach Änderungen außerhalb von WMI auf Betriebssystemebene. So ist z. B. das Ereignis "Computer shutdown" ein Ereignis auf Betriebssystemebene und wird daher als extrinsisches Ereignis eingestuft. Extrinsische Ereignisse erfordern kein Pooling.
Nachfolgend sind verschiedene Arten von extrinsischen Ereignissen aufgeführt:
Win32_ComputerShutdownEvent,
Win32_IP4RouteTableEvent,
Win32_ProcessStartTrace,
Win32_ModuleLoadTrace,
Win32_ThreadStartTrace,
Win32_VolumeChangeEvent,
Msft_WmiProvider*,
RegistryKeyChangeEvent,
RegistryValueChangeEvent
Um mit diesen Ereignissen arbeiten zu können, müssen wir das Konzept der Filter verstehen, aber bevor wir über Filter sprechen, machen wir einen kurzen Abstecher zu WQL.
WQL ist die WMI-Abfragesprache und ist wie SQL. Sie verwendet die gleiche Syntax wie SQL, um WMI nach Informationen abzufragen. Weitere Informationen zu WQL finden sich auf der Microsoft Documentation Seite.
Verwendung von WQL für Ereignisfilter
Wir verwenden WQL, um Filter für Ereignisse zu erstellen. Einen Filter kann man sich als Suchbedingung vorstellen, um nach bestimmten Ereignissen zu suchen.
Nehmen wir ein Beispiel: Nehmen wir an, wir wollen ein Ereignis zur Erstellung eines neuen Prozesses erfassen, insbesondere den Prozess des Taschenrechners. Dazu müssen wir eine WQL-Abfrage erstellen, um den Filter für die Erfassung dieses Ereignisses zu definieren. In diesem Fall wollen wir die Erstellung einer neuen Instanz der Klasse WIN32_Process erfassen, wobei der Prozessname calc.exe ist, und sobald wir dieses Ereignis erfasst haben, wollen wir eine Aktion durchführen. In diesem speziellen Beispiel wollen wir "WMI is awesome" ausgeben.
Beginnen wir mit der Definition unseres Filters mittels WQL, in diesem Fall suchen wir nach dem Ereignis der Instanzerstellung, daher beginnt unsere Abfrage mit:
Select * from __InstanceCreationEvent
Da es sich um ein Eigenes Ereignis handelt, müssen wir als Nächstes das Pooling-Intervall definieren, sodass unsere WQL-Abfrage folgendermaßen aussieht:
Select * from __InstanceCreationEvent within 10
Als nächstes müssen wir definieren, nach welcher Klasseninstanz wir suchen. Also fügen wir diese Information zu unserer Abfrage hinzu:
Select * from __InstanceCreationEvent within 10 where TargetInstance ISA "Win32_Process"
Als Nächstes müssen wir genau angeben, nach welcher Art von Instanz wir suchen, in diesem Fall suchen wir nach einer Prozessinstanz mit der Namenseigenschaft "calc.exe", daher sieht unsere WQL-Abfrage am Ende wie folgt aus.
Select * from __InstanceCreationEvent within 10 where TargetInstance ISA "Win32_Process" and TargetInstance.Name like "%calc%"
Nachdem wir nun unseren Filter definiert haben, müssen wir das Ereignis abonnieren und die Aktion festlegen, die ausgeführt werden soll, sobald das Ereignis beobachtet wird.
Zu diesem Zweck wird das Cmdlet Register-CimIndicationEvent verwendet. Das Cmdlet Register-CimIndicationEvent benötigt folgende Parameter, um ein Ereignis zu abonnieren (wie in Abb. 6 dargestellt)
-wqlQuery
-SourceIdentifier
-Action
Der Parameter -SouceIdentifier kann mit einem benutzerdefinierten Namen versehen werden.
WMI temporäres Ereignisabonnement
If the cmdlet is successful you will receive a message like the one show below. It indicates that we have successfully subscribed to the event.
Successful event subscription
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
Lets 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.
Its 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
The graphic below shows a schematic representation of this process.
Process for permanent WMI event subscription
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, lets 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 2SourceName="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 Im 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 below.
Windows Event Log
Event log showing Event ID and source
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-CimInstanceGet-CimInstance -ClassName NTEventLogEventConsumer -Namespace rootsubscription -filter "name='FolderWriteLogging'" | Remove-CimInstanceGet-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.