Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to monitor for process startup.
Microsoft Scripting Guy, Ed Wilson, is here. This morning, I spent a decent amount of time answering an email from a former colleague. I will actually share his email and my reply this coming weekend. In the meantime, I was playing around Register-CimIndicationEvent. This is not really a new Windows PowerShell cmdlet—I mean, it has been around for a while (since Windows PowerShell 3.0), but not much has been written about it. There is little useful documentation for the cmdlet either.
In today’s post, I will talk about the following Windows PowerShell cmdlets:
- Register-CimIndicationEvent
- Get-Event
- Get-EventSubscriber
- Unregister-Event
- Select-Object
- Remove-Event
To monitor for any process startup, I can use the WMI class Win32_ProcessStartTrace. When the event fires, the class provides information about the process ID, the parent process ID, the process name, the session ID, and even the SID for the user context that generates the event. This information becomes available after I retrieve the generated event. For more information, see MSDN: Win32_ProcessStartTrace class.
Register for the event
Note This procedure requires admin rights. To start an elevated Windows PowerShell console, right-click the Windows PowerShell console icon and select Run as Administrator from the action menu.
The first thing I need to do is to register to receive events. To do this, I use the Register-CimIndicationEvent cmdlet. When I do, I want to specify the WMI class to monitor and a name that I can use to track the generated event subscription. Here is the command I use:
Register-CimIndicationEvent -ClassName Win32_ProcessStartTrace -SourceIdentifier "ProcessStarted"
When I run the command, nothing returns. But I can use the Get-EventSubscriber cmdlet to verify that something actually happened (such as creating a new event subscription). Here is the command and its associated output:
PS C:\> Get-EventSubscriber
SubscriptionId : 7
SourceObject : Microsoft.Management.Infrastructure.CimCmdlets.CimIndicationWatcher
EventName : CimIndicationArrived
SourceIdentifier : ProcessStarted
Action :
HandlerDelegate :
SupportEvent : False
ForwardEvent : False
Hey, is anything happening?
I have not deliberately started any processes yet (like Calculator or Notepad), but when I use the Get-Event cmdlet, I see that the operating system generated a bunch of processes anyway. So there are already new processes starting up. The command with no parameters is shown here:
Get-Event
Here is what the output looks like at this point:
There is one property that I can basically understand and that is the TimeGenerated property. Everything else is wrapped up in other objects. One thing I can do is take advantage of the ability of Windows PowerShell to do what I call “the automatic foreach.” That is, I can select a specific property from a collection of objects, and Windows PowerShell will display that object. Here, I choose the SourceEventArgs property:
PS C:\> (Get-Event).sourceeventargs
NewEvent MachineId Bookmark Context
——– ——— ——– ——-
Win32_ProcessStartT…
Win32_ProcessStartT…
Win32_ProcessStartT…
Win32_ProcessStartT…
Win32_ProcessStartT…
<output truncated>
Notice that each NewEvent says that it is an instance of Win32_ProcessStartTrace. Remember the MSDN document I mentioned earlier? This is where that comes into play. I can now cherry pick which of the properties of Win32_ProcessStartTrace I want to see. First, I look at the NewEvent property that contains instances of Win32_ProcessStartTrace. Here is the result:
PS C:\> (Get-Event).sourceeventargs.newevent
SECURITY_DESCRIPTOR :
TIME_CREATED : 130552887343479772
ParentProcessID : 4512
ProcessID : 9304
ProcessName : SearchProtocolHost.exe
SessionID : 1
Sid : {1, 1, 0, 0…}
PSComputerName :
SECURITY_DESCRIPTOR :
TIME_CREATED : 130552887343479773
ParentProcessID : 4512
ProcessID : 3540
ProcessName : SearchFilterHost.exe
SessionID : 0
Sid : {1, 1, 0, 0…}
PSComputerName :
<output truncated>
Yep. That is what I thought would be there. So now let me get a list for only the ProcessName property to see what is actually starting up:
Cool. But when are these events happening? If I look at the Time_Created property, all I get is a bunch of numbers. When I look at MSDN, it simply says it is a string that represents time created—not exactly the most helpful Help in the world. Here is the output:
PS C:\> (Get-Event).sourceeventargs.newevent.Time_Created
130552887343479772
130552887343479773
130552887430300826
130552889482027640
130552889482027641
<output truncated>
So, I decide to try one of my favorite tricks to translate this. I use the [System.Management.ManagementDateTimeConverter] class. But no matter what I try, I get error messages. Here are some efforts that didn’t work:
[System.Management.ManagementDateTimeConverter]::ToDmtfdatetime(130552788310990332)
[System.Management.ManagementDateTimeConverter]::Totimespan(130552788310990332)
[System.Management.ManagementDateTimeConverter]::Todatetime(130552788310990332)
So basically, I give up?
No, I don’t give up—not with Windows PowerShell. There is always a way to do what I need to do when using Windows PowerShell. I decide to pull out another one of my favorite tricks: the custom property with Select-Object. Remember that earlier when I was looking at the output from Get-Event, I saw the TimeGenerated property.
This property comes from Get-Event and is logged when the event generates. It does not come from WMI, so it is a proper DateTime object. I need to pick the property from this value and then get the process name from the object held in the NewEvent property. Here is the command I decided to use (this is a one-line command that I broke into two lines for readability online):
Get-Event |
select timegenerated, @{L='e'; E = {$_.sourceeventargs.newevent.processname}}
The output from this command is shown here:
Cleanup work
Now I need to do a little bit of cleanup. First, I remove all of the events. To do this, I use Get-Event and pipe the output to Remove-Event. I next confirm that this worked by using Get-Event:
get-event | Remove-Event
get-event
Second, I need to unregister the event subscribers. To do this, I use Get-EventSubscriber and pipe the output to Unregister-Event. Once again, I call the prior command to confirm. Here are the two lines of script:
Get-EventSubscriber | Unregister-Event
Get-EventSubscriber
That is all there is to using Register-CimIndicationEvent to monitor for process startup. I will continue talking about processes tomorrow when I will talk about more groovy things.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
0 comments