WMI Internals Part 2

Reversing a WMI Provider

Jonathan Johnson
6 min readAug 15, 2022

In a previous post WMI Internals Part 1: Understanding the Basics I walked through some of the basic internal information behind WMI. The purpose of that post was to provide the appropriate background to understand this post, which will break down how it is common for WMI classes to invoke COM methods to perform the action requested.

Understanding how to extract these technologies and perform binary analysis to see them flow into each other enables us to better understand attacks that might rely on these technologies. This is equally important to defenders as it is to offensive professionals.

A Note: Originally when thinking about this post I was going to start at a WMI class and then break down various functions leading into the invocation of a COM method. However; when searching for good examples I came across a PowerShell function that ends up calling a WMI class, so we will actually start there.

Walkthrough

Step 1: Identify the PowerShell command. In our example we will focus on scheduled tasks and specifically Register-ScheduledTask:

The above command shows us that Register-ScheduledTask is a function, not a PS cmdlet. To understand this function more, let’s pull what we can out of Get-Command. After viewing the code for this function it is clear that after the call we will get an output type of Microsoft.Management.Infrastructure.CimInstance#MSFT_ScheduledTaskbut after digging a bit more we can see that by default this function uses a ParameterSetName “User”, which eventually leads the invocation of the RegisterByUser method which seems to be done through the PS_ScheduledTask class.

Previous to this I had never ran across a PowerShell command that did this so I talked to Matt Graeber and he let me know this was a cmdlet definition XML (CDXML). Not to get into this too much but a CDXML file defines a mapping between a PowerShell cmdlet and a WMI class/method. CDXML is an auto-generated script wrapper for a WMI class.

Step 2: Find the CDXML:

After some digging I found that the cdxml file for PS_ScheduledTask was found in: C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ScheduledTasks\PS_ScheduledTask_v1.0.cdxml. Within this file it shows that the WMI class name is: PS_ScheduledTask found in the Root/Microsoft/Windows/TaskScheduler WMI namespace.

Step 3: Find the WMI Provider for the PS_ScheduledTask class:

Get-CimClass -Namespace root/Microsoft/Windows/TaskScheduler -ClassName PS_ScheduledTask | Select-Object -ExpandProperty CimClassQualifiers

The following command will call the TaskScheduler namespace to filter through provider names. Once it matches ScheduledTaskProv it will print out a GUID value. This is the CLSID that will point us to the appropriate binary of the WMI Provider.

Step 4: Get the WMI provider binary:

Now I know that schedprov.dll is the WMI provider for the PS_ScheduledTask:RegisterByUser Method.

Note: This is a quick way to get this information and querying the registry could achieve the same goal.

Step 5: Analyze WMI Provider (schedprov.dll):

After opening up in IDA, if we open up the function window and search for RegisterByUser we see there is a function called: PS_ScheduledTask_Invoke_RegisterByUser.

As we can see, there is an immediate jump to another function called ScheduledTask_Invoke_RegisterByUser. After looking at this function block, we see a section that calls CoCreateInstance.

This function is in charge of creating a COM Class object. This function contains 5 parameters, but the 3 we care about are:

  • rclsid (Parameter 1) — “The CLSID associated with the data and code that will be used to create the object.”
  • rrid (Parameter 4) — “A reference to the identifier of the interface to be used to communicate with the object.”
  • ppv (Parameter 5) — “Address of pointer variable that receives the interface pointer requested in riid.”

If we look, IDA filled in the CLSID (CLSID_TaskScheduler — 0F87369F-A4E5–4CFC-BD3E-73E6154572DD) value with the proper symbols. Next, we see the GUID that represents which COM Interface this action comes from. Using OleViewDotNet we can see this value corresponds to ITaskService.

Lastly, if we want to find what COM methods we want to invoke we have to follow ppv. On the assumption that CoCreateInstance was successful another code block is jumped into which contains the code below. If we follow the code from earlier we see that certain offsets of ppv get called, which leads me to believe after applying the right structure we should be able to see which methods are being invoked. A decompiled and assembly example are shown below.

After knowing that the ITaskServices Interface was called, I started to look explicitly for calls related to creating a new task. After doing jumps into a couple of CreateNewTaskDefnition functions, I was able to find the NewTask method invoked. Shown below.

Decompiled Example

If we know that ppv holds the offset of the COM method invoked then we can apply that structure to ppv and see the methods called.

We can see after the structure is applied that ITaskService::NewTask is called.

Assembly Example

PPV and var_218 hold the same memory location. We see var_218 being dereferenced twice and then an offset of 50h is called. This offset relates to what COM method is being invoked within the ITaskService interface.

By making sure the proper type libraries are applied we can add the structure type ITaskServiceVtbl. We can then hover over 50h and apply the structure offset to see that this relates to the ITaskServiceVtbl.Connect method. Following the same methodology we can see that eventually ITaskServiceVtbl.NewTask was called later in the code.

After googling for ITaskService::NewTask, I was able to identify this Microsoft document that outlines this COM method:

Bringing it all together

After applying binary analysis to the WMI Provider binary (schedprov.dll) we were able to identify that the WMI class PS_ScheduledTask ends up invoking the COM method NewTask::ITaskService. The flow of functions we encountered today can be seen below:

The purpose of this post was to highlight how some technologies end up calling others to accomplish a task under the hood. It should be noted that not every WMI class will work just like this, even though WMI is built upon and structured like COM, the class might just end up calling a Win32 API to accomplish its task. Attacks rely on technologies the operating system exposes, understanding these concepts help us understand those technologies better and in turn those attacks.

If you are following along within the code or the MSFT documentation you might be wondering if there is another component after this. You would be correct if you think so. The next blog will go walkthrough how to take this a step further and go from COM methods being invoked calling RPC methods.

Hat Tip

Thank you to Matt Graeber and Alex Ionescu for reviewing this blog. As always, I appreciate your guys’ time and being willing to help people grow!

--

--

Jonathan Johnson

Principal Security Engineer @Prelude | Windows Internals