How I Leveraged WMI to Enumerate a Process Modules and Their Base Addresses


In this blog post, using WMI we’ll leverage Windows Management Instrumentation (WMI) to extract the loaded modules of a specific process and understand how to get each module base address, show the advantages and the ability to perform ShellCode injection in .text section directly using a technique shared before by Netero1010 Security Lab


There are many reasons to use WMI but lets sum up some of these:

Stealth: WMI operations are often less likely to raise suspicion compared to direct API calls. Direct API calls to functions like OpenProcess and EnumProcessModules may be monitored by endpoint security solutions as indicators of malicious activity.

East/West : WMI allows for remote queries, enabling to gather information about processes and it’s modules on remote computers without having to establish direct connections or drop custom binaries. This capability is valuable for reconnaissance activities across networked environments, where direct interaction with target systems may raise alarms.

Getting started

Several days ago, i came cross the following article that shows the possibility to use WMI for enumerating a process modules.
The script would communicate with win32_process and map the results with CIM_ProcessExecutable to get the list of loaded Modules (DLL).

# Copied from : 

function Get-Executables()
    PARAM (
        [string] $ProcessName = $(throw "Please specify a process name.")

        # Retrieve instance of Win32_Process based on the process name passed into the function
        $Procs = Get-WmiObject -Namespace root\cimv2 -Query "select * from Win32_Process where Name = '$ProcessName'"

        # If there are no processes returned from the query, then simply exit the function
        if (-not $Procs)
            Write-Host "No processes were found named $ProcessName"
        # If one process is found, get the value of __PATH, which we will use for our next query
        elseif (@($Procs).Count -eq 1)
            Write-Verbose "One process was found named $ProcessName"
            $ProcPath = @($Procs)[0].__PATH
            Write-Verbose "Proc path is $ProcPath"
        # If there is more than one process, use the process at index 0, for the time being
        elseif ($Procs.Count -gt 1)
            Write-Host "More than one process was found named $ProcessName"
            $ProcPath = @($Procs)[0].__PATH
            Write-Host "Using process with path: $ProcPath"

        # Get the CIM_ProcessExecutable instances for the process we retrieved
        $ProcQuery = "select * from CIM_ProcessExecutable where Dependent = '$ProcPath'".Replace("\","\\")

        Write-Verbose $ProcQuery
        $ProcExes = Get-WmiObject -Namespace root\cimv2 -Query $ProcQuery

        # If there are instances of CIM_ProcessExecutable for the specified process, go ahead and grab the important properties
        if ($ProcExes)
            foreach ($ProcExe in $ProcExes)
                # Use the [wmi] type accelerator to retrieve an instance of CIM_DataFile from the WMI __PATH in the Antecentdent property
                $ExeFile = [wmi]"$($ProcExe.Antecedent)"
                # If the WMI instance we just retrieve "IS A" (think WMI operator) CIM_DataFile, then write properties to console
                if ($ExeFile.__CLASS -eq 'CIM_DataFile')
                    Select-Object -InputObject $ExeFile -Property FileName,Extension,Manufacturer,Version -OutVariable $Executables

    # Do a little clean-up work. Not exactly necessary, but useful for debugging in PowerGUI
        Write-Verbose "End: Cleaning up variables used for function"
        Remove-Item -ErrorAction SilentlyContinue -Path variable:ExeFile,variable:ProcessName,variable:ProcExe,

# Call the function we just defined, with its single parameter
. Get-Executables gmailgrowl.exeCode language: PowerShell (powershell)

while the regular approach for doing that is by using  OpenProcess,  GetModuleBaseName and EnumProcessModules. As the following code below.

#include <stdio.h>
#include <windows.h>
#include <psapi.h>

int main() {
    DWORD processId = 1234; // Replace 1234 with the actual process ID
    // Open the target process
  if (hProcess == NULL) {
        printf("Failed to open process. Error code: %d\n", GetLastError());
        return 1;
    // Get the loaded modules of the target process
    HMODULE hModules[1024];
    DWORD cbNeeded;
    if (EnumProcessModules(hProcess, hModules, sizeof(hModules), &cbNeeded)) {
        for (int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
            TCHAR szModuleName[MAX_PATH];
            if (GetModuleFileNameEx(hProcess, hModules[i], szModuleName, sizeof(szModuleName) / sizeof(TCHAR))) {
                printf("Module name: %s\n", szModuleName);
                MODULEINFO moduleInfo;
                if (GetModuleInformation(hProcess, hModules[i], &moduleInfo, sizeof(moduleInfo))) {
                    printf("Base address: 0x%p\n", moduleInfo.lpBaseOfDll);
                    printf("Size of module: %lu bytes\n", moduleInfo.SizeOfImage);
                } else {
                    printf("Failed to get module information. Error code: %d\n", GetLastError());
    } else {
        printf("Failed to enumerate modules. Error code: %d\n", GetLastError());
    // Close the handle to the process
    return 0;

Code language: C++ (cpp)

Using windows API approach is effective but it might get detected by the end-point solution due to malicious activity if it is coded in the basic way.

Using WMI instead.

The published code base will do the following steps in order, and you can use customise it with your injection technique, and it can be configured to communicate with remote systems as well.

  • Retrieve the specific process name from Win32_Process class
    SELECT * FROM Win32_Process WHERE Name =
  • Extract the Process ID and pass it to CIM_ProcessExecutable Class
  • Apply search filer to CIM_ProcessExecutable class and query the instance for a process with Handle number.
    SELECT * FROM CIM_ProcessExecutable WHERE Dependent = \"\\\\.\\root\\cimv2:Win32_Process.Handle=
  • Extract the Module (Name, Path ) and Base Address
  • Perform ShellCode injection technique

You can refer to following repo, feel free to contribute and always credit

Thanks to:

– Netero1010 Security Lab – Chris.
– Trevor Sullivan

Please follow and like us: