4

I need to the the Handle or the PID for all the process that are just being created while I run my program.

So far I have used this code that tell me each time that a process is created. The problem is that I just get the information about a process created but I don't have any information about the process itself. https://msdn.microsoft.com/en-us/library/aa390425(VS.85).aspx

This is the fuction where I get the event but I don't know from where I can get the info for the new process:

HRESULT EventSink::Indicate(long lObjectCount,
    IWbemClassObject **apObjArray)
{
    HRESULT hres = S_OK;

    for (int i = 0; i < lObjectCount; i++)
    {
        printf("Event occurred\n");
    }

    return WBEM_S_NO_ERROR;
}

Thank you

user1618465
  • 1,813
  • 2
  • 32
  • 58

3 Answers3

7

Since you are using this WQL sentence SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process', In order to get additional info about the created processes you must access the TargetInstance property which in this case will return a Win32_Process instance.

Try this sample

#include "stdafx.h"
#include <conio.h>

#ifndef EVENTSINK_H
#define EVENTSINK_H

#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>

# pragma comment(lib, "wbemuuid.lib")

class EventSink : public IWbemObjectSink
{
    LONG m_lRef;
    bool bDone;

public:
    EventSink() { m_lRef = 0; }
    ~EventSink() { bDone = true; }

    virtual ULONG STDMETHODCALLTYPE AddRef();
    virtual ULONG STDMETHODCALLTYPE Release();
    virtual HRESULT
        STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv);

    virtual HRESULT STDMETHODCALLTYPE Indicate(
        LONG lObjectCount,
        IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray
        );

    virtual HRESULT STDMETHODCALLTYPE SetStatus(
        /* [in] */ LONG lFlags,
        /* [in] */ HRESULT hResult,
        /* [in] */ BSTR strParam,
        /* [in] */ IWbemClassObject __RPC_FAR *pObjParam
        );
};

#endif    // end of EventSink.h

ULONG EventSink::AddRef()
{
    return InterlockedIncrement(&m_lRef);
}

ULONG EventSink::Release()
{
    LONG lRef = InterlockedDecrement(&m_lRef);
    if (lRef == 0)
        delete this;
    return lRef;
}

HRESULT EventSink::QueryInterface(REFIID riid, void** ppv)
{
    if (riid == IID_IUnknown || riid == IID_IWbemObjectSink)
    {
        *ppv = (IWbemObjectSink *) this;
        AddRef();
        return WBEM_S_NO_ERROR;
    }
    else return E_NOINTERFACE;
}


HRESULT EventSink::Indicate(long lObjectCount,
    IWbemClassObject **apObjArray)
{
    HRESULT hr = S_OK;
    _variant_t vtProp;

    for (int i = 0; i < lObjectCount; i++)
    {

        hr = apObjArray[i]->Get(_bstr_t(L"TargetInstance"), 0, &vtProp, 0, 0);
        if (!FAILED(hr))
        {
            IUnknown* str = vtProp;
            hr = str->QueryInterface(IID_IWbemClassObject, reinterpret_cast< void** >(&apObjArray[i]));
            if (SUCCEEDED(hr))
            {
                _variant_t cn;
                hr = apObjArray[i]->Get(L"Caption", 0, &cn, NULL, NULL);
                if (SUCCEEDED(hr))
                {
                    if ((cn.vt == VT_NULL) || (cn.vt == VT_EMPTY))
                        wcout << "Caption : " << ((cn.vt == VT_NULL) ? "NULL" : "EMPTY") << endl;
                    else
                        if ((cn.vt & VT_ARRAY))
                            wcout << "Caption : " << "Array types not supported (yet)" << endl;
                        else
                            wcout << "Caption : " << cn.bstrVal << endl;
                }
                VariantClear(&cn);

                hr = apObjArray[i]->Get(L"CommandLine", 0, &cn, NULL, NULL);
                if (SUCCEEDED(hr))
                {
                    if ((cn.vt == VT_NULL) || (cn.vt == VT_EMPTY))
                        wcout << "CommandLine : " << ((cn.vt == VT_NULL) ? "NULL" : "EMPTY") << endl;
                    else
                        if ((cn.vt & VT_ARRAY))
                            wcout << "CommandLine : " << "Array types not supported (yet)" << endl;
                        else
                            wcout << "CommandLine : " << cn.bstrVal << endl;
                }
                VariantClear(&cn);

                hr = apObjArray[i]->Get(L"Handle", 0, &cn, NULL, NULL);
                if (SUCCEEDED(hr))
                {
                    if ((cn.vt == VT_NULL) || (cn.vt == VT_EMPTY))
                        wcout << "Handle : " << ((cn.vt == VT_NULL) ? "NULL" : "EMPTY") << endl;
                    else
                        if ((cn.vt & VT_ARRAY))
                            wcout << "Handle : " << "Array types not supported (yet)" << endl;
                        else
                            wcout << "Handle : " << cn.bstrVal << endl;
                }
                VariantClear(&cn);


            }
        }
        VariantClear(&vtProp);

    }

    return WBEM_S_NO_ERROR;
}

HRESULT EventSink::SetStatus(
    /* [in] */ LONG lFlags,
    /* [in] */ HRESULT hResult,
    /* [in] */ BSTR strParam,
    /* [in] */ IWbemClassObject __RPC_FAR *pObjParam
    )
{
    if (lFlags == WBEM_STATUS_COMPLETE)
    {
        printf("Call complete. hResult = 0x%X\n", hResult);
    }
    else if (lFlags == WBEM_STATUS_PROGRESS)
    {
        printf("Call in progress.\n");
    }

    return WBEM_S_NO_ERROR;
}    // end of EventSink.cpp


int main(int iArgCnt, char ** argv)
{
    HRESULT hres;

    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------

    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres))
    {
        cout << "Failed to initialize COM library. Error code = 0x"
            << hex << hres << endl;
        return 1;                  // Program has failed.
    }

    // Step 2: --------------------------------------------------
    // Set general COM security levels --------------------------
    // Note: If you are using Windows 2000, you need to specify -
    // the default authentication credentials for a user by using
    // a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
    // parameter of CoInitializeSecurity ------------------------

    hres = CoInitializeSecurity(
        NULL,
        -1,                          // COM negotiates service
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
        RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities 
        NULL                         // Reserved
        );


    if (FAILED(hres))
    {
        cout << "Failed to initialize security. Error code = 0x"
            << hex << hres << endl;
        CoUninitialize();
        return 1;                      // Program has failed.
    }

    // Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------

    IWbemLocator *pLoc = NULL;

    hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc);

    if (FAILED(hres))
    {
        cout << "Failed to create IWbemLocator object. "
            << "Err code = 0x"
            << hex << hres << endl;
        CoUninitialize();
        return 1;                 // Program has failed.
    }

    // Step 4: ---------------------------------------------------
    // Connect to WMI through the IWbemLocator::ConnectServer method

    IWbemServices *pSvc = NULL;

    // Connect to the local root\cimv2 namespace
    // and obtain pointer pSvc to make IWbemServices calls.
    hres = pLoc->ConnectServer(
        _bstr_t(L"root\\CIMV2"),
        NULL,
        NULL,
        0,
        NULL,
        0,
        0,
        &pSvc
        );

    if (FAILED(hres))
    {
        cout << "Could not connect. Error code = 0x"
            << hex << hres << endl;
        pLoc->Release();
        CoUninitialize();
        return 1;                // Program has failed.
    }

    cout << "Connected to root\\CIMV2 WMI namespace" << endl;


    // Step 5: --------------------------------------------------
    // Set security levels on the proxy -------------------------

    hres = CoSetProxyBlanket(
        pSvc,                        // Indicates the proxy to set
        RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx 
        RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx 
        NULL,                        // Server principal name 
        RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
        RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
        NULL,                        // client identity
        EOAC_NONE                    // proxy capabilities 
        );

    if (FAILED(hres))
    {
        cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return 1;               // Program has failed.
    }

    // Step 6: -------------------------------------------------
    // Receive event notifications -----------------------------

    // Use an unsecured apartment for security
    IUnsecuredApartment* pUnsecApp = NULL;

    hres = CoCreateInstance(CLSID_UnsecuredApartment, NULL, CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment, (void**)&pUnsecApp);

    EventSink* pSink = new EventSink;
    pSink->AddRef();

    IUnknown* pStubUnk = NULL;
    pUnsecApp->CreateObjectStub(pSink, &pStubUnk);

    IWbemObjectSink* pStubSink = NULL;
    pStubUnk->QueryInterface(IID_IWbemObjectSink, (void **)&pStubSink);

    _bstr_t WQL = L"Select * From __InstanceCreationEvent Within 1 "
        L"Where TargetInstance ISA 'Win32_Process' "
        ;

    // The ExecNotificationQueryAsync method will call
    // The EventQuery::Indicate method when an event occurs
    hres = pSvc->ExecNotificationQueryAsync(
        _bstr_t("WQL"), WQL, WBEM_FLAG_SEND_STATUS, NULL, pStubSink);

    // Check for errors.
    if (FAILED(hres))
    {
        printf("ExecNotificationQueryAsync failed with = 0x%X\n", hres);
        pSvc->Release();
        pLoc->Release();
        pUnsecApp->Release();
        pStubUnk->Release();
        pSink->Release();
        pStubSink->Release();
        CoUninitialize();
        return 1;
    }

    // Wait for the event
    cout << "Press any key to terminate" << endl;
    while (!_kbhit()) {}

    hres = pSvc->CancelAsyncCall(pStubSink);

    // Cleanup
    // ========

    pSvc->Release();
    pLoc->Release();
    pUnsecApp->Release();
    pStubUnk->Release();
    pSink->Release();
    pStubSink->Release();
    CoUninitialize();

    return 0;   // Program successfully completed.  
}
KulaGGin
  • 943
  • 2
  • 12
  • 27
RRUZ
  • 134,889
  • 20
  • 356
  • 483
  • 1
    WMI was a mistake. 700 lines of code to subscribe to process creation events. Really, Microsoft? This should literally be 1 line of code: call function, provide your listener function. The end. – KulaGGin Jul 23 '23 at 11:36
  • I have a question about your implementation of Intake method. What's the point of you getting all the properties like TargetInstance, Caption and CommandLine before the Handle, when the only one that's really needed is Handle? Can't you just do `apObjArray[i]->Get(L"Handle", 0, &cn, NULL, NULL)` without first calling Get with TargetInstance? – KulaGGin Jul 23 '23 at 11:43
  • I tried using this code and I get error: **"a value of type "const wchar_t *" cannot be assigned to an entity of type "BSTR"** at line 285: `WQL = L"Select * From __InstanceCreationEvent Within 1 "`. My project is just a default Visual Studio project with /SUBSYSTEM:WINDOWS and Unicode Character Set. – KulaGGin Jul 23 '23 at 12:24
2

The example above seems to have a memory leak inside EventSink::Indicate.

IUnknown* str = vtProp;

This assignment uses operator IUnknown* of the _variant_t class, which implicitly calls an AddRef() on the object. So str->Release() needs to be called after usage, since

VariantClear(&vtProp);

is not enough to decrement the ref count of the object.

jo be
  • 21
  • 2
  • You could've edit his answer and someone would later accept it. Also, looks like all these raw pointers are candidates for ComPtrs or other smart pointers, so you don't have to release them manually: exception safe, less code, cleaner code and all that. – KulaGGin Jul 23 '23 at 11:46
0

Took the answer posted by KRUZ:

And made an actual event dispatcher out of it:

ProcessCreatedEventDispatcher.h:

#pragma once
#include <functional>
#include <vector>

#include <Wbemidl.h>
#include <wrl.h>

using namespace Microsoft::WRL;

class ProcessCreatedEventDispatcher : public IWbemObjectSink {

public:
    ProcessCreatedEventDispatcher();
    ~ProcessCreatedEventDispatcher();

    ULONG STDMETHODCALLTYPE AddRef() override;
    ULONG STDMETHODCALLTYPE Release() override;
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) override;
    HRESULT STDMETHODCALLTYPE Indicate(LONG lObjectCount, IWbemClassObject __RPC_FAR* __RPC_FAR* apObjArray) override;
    HRESULT STDMETHODCALLTYPE SetStatus(LONG lFlags, HRESULT hResult, BSTR strParam, IWbemClassObject __RPC_FAR* pObjParam) override;

    using NewProcessCreatedListener = void(HANDLE ProcessHandle);
    std::vector<std::function<NewProcessCreatedListener>> NewProcessCreatedListeners{};
private:
    LONG m_lRef{};
    ComPtr<IWbemServices> pSvc{};
    ComPtr<IWbemLocator> pLoc{};
    ComPtr<IUnsecuredApartment> pUnsecApp{};
    ComPtr<IUnknown> pStubUnk{};
    ComPtr<IWbemObjectSink> pStubSink{};
};

ProcessCreatedEventDispatcher.cpp:

#include "ProcessCreatedEventDispatcher.h"

# pragma comment(lib, "wbemuuid.lib")

#include <iostream>
#include <functional>
#include <string>
#include <vector>

#define _WIN32_DCOM
#include <Windows.h>
#include <comdef.h>
#include <Wbemidl.h>
#include <wrl.h>

using namespace std;
using namespace Microsoft::WRL;

ProcessCreatedEventDispatcher::ProcessCreatedEventDispatcher() {
    HRESULT hres;
    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------

    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if(FAILED(hres)) {
        cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl;
        return; // Program has failed.
    }

    // Step 2: --------------------------------------------------
    // Set general COM security levels --------------------------
    // Note: If you are using Windows 2000, you need to specify -
    // the default authentication credentials for a user by using
    // a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
    // parameter of CoInitializeSecurity ------------------------

    hres = CoInitializeSecurity(NULL,
        -1, // COM negotiates service
        NULL, // Authentication services
        NULL, // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication 
        RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
        NULL, // Authentication info
        EOAC_NONE, // Additional capabilities 
        NULL // Reserved
    );


    if(FAILED(hres)) {
        cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl;
        CoUninitialize();
        return; // Program has failed.
    }

    // Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------
    hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)pLoc.GetAddressOf());

    if(FAILED(hres)) {
        cout << "Failed to create IWbemLocator object. " << "Err code = 0x" << hex << hres << endl;
        CoUninitialize();
        return; // Program has failed.
    }

    // Step 4: ---------------------------------------------------
    // Connect to WMI through the IWbemLocator::ConnectServer method

    // Connect to the local root\cimv2 namespace
    // and obtain pointer pSvc to make IWbemServices calls.
    hres = pLoc->ConnectServer(_bstr_t(L"root\\CIMV2"),
        NULL,
        NULL,
        0,
        NULL,
        0,
        0,
        &pSvc
    );

    if(FAILED(hres)) {
        cout << "Could not connect. Error code = 0x" << hex << hres << endl;
        pLoc->Release();
        CoUninitialize();
        return; // Program has failed.
    }

    cout << "Connected to root\\CIMV2 WMI namespace" << endl;


    // Step 5: --------------------------------------------------
    // Set security levels on the proxy -------------------------

    hres = CoSetProxyBlanket(pSvc.Get(), // Indicates the proxy to set
        RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx 
        RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx 
        NULL, // Server principal name 
        RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx 
        RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
        NULL, // client identity
        EOAC_NONE // proxy capabilities 
    );

    if(FAILED(hres)) {
        cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return; // Program has failed.
    }

    // Step 6: -------------------------------------------------
    // Receive event notifications -----------------------------

    // Use an unsecured apartment for security
    hres = CoCreateInstance(CLSID_UnsecuredApartment, NULL, CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment, (void**)&pUnsecApp);

    this->ProcessCreatedEventDispatcher::AddRef();

    pUnsecApp->CreateObjectStub(this, &pStubUnk);

    pStubUnk->QueryInterface(IID_IWbemObjectSink, &pStubSink);

    _bstr_t WQL = L"Select * From __InstanceCreationEvent Within 1 "
        L"Where TargetInstance ISA 'Win32_Process' ";

    // The ExecNotificationQueryAsync method will call
    // The EventQuery::Indicate method when an event occurs
    hres = pSvc->ExecNotificationQueryAsync(_bstr_t("WQL"), WQL, WBEM_FLAG_SEND_STATUS, NULL, pStubSink.Get());

    // Check for errors.
    if(FAILED(hres)) {
        printf("ExecNotificationQueryAsync failed with = 0x%X\n", hres);
        pSvc->Release();
        pLoc->Release();
        pUnsecApp->Release();
        pStubUnk->Release();
        this->ProcessCreatedEventDispatcher::Release();
        pStubSink->Release();
        CoUninitialize();
        return;
    }
}

ProcessCreatedEventDispatcher::~ProcessCreatedEventDispatcher() {
    auto Result = pSvc->CancelAsyncCall(pStubSink.Get());
    pSvc->Release();
    pLoc->Release();
    pUnsecApp->Release();
    pStubUnk->Release();
    this->ProcessCreatedEventDispatcher::Release();
    pStubSink->Release();
    CoUninitialize();
}

ULONG ProcessCreatedEventDispatcher::AddRef() {
    return InterlockedIncrement(&m_lRef);
}

ULONG ProcessCreatedEventDispatcher::Release() {
    LONG lRef = InterlockedDecrement(&m_lRef);
    if(lRef == 0)
        delete this;
    return lRef;
}

HRESULT ProcessCreatedEventDispatcher::QueryInterface(REFIID riid, void** ppv) {
    if(riid == IID_IUnknown || riid == IID_IWbemObjectSink) {
        *ppv = (IWbemObjectSink*)this;
        AddRef();
        return WBEM_S_NO_ERROR;
    }
    else return E_NOINTERFACE;
}


HRESULT ProcessCreatedEventDispatcher::Indicate(long lObjectCount, IWbemClassObject** apObjArray) {
    HRESULT hr = S_OK;
    _variant_t vtProp;

    std::string ProcessHandleString{};

    for(int i = 0; i < lObjectCount; i++) {
        hr = apObjArray[i]->Get(_bstr_t(L"TargetInstance"), 0, &vtProp, 0, 0);
        if(!FAILED(hr)) {
            ComPtr<IUnknown> str = static_cast<IUnknown*>(vtProp);
            hr = str->QueryInterface(IID_IWbemClassObject, reinterpret_cast<void**>(&apObjArray[i]));
            if(SUCCEEDED(hr)) {
                _variant_t cn;

                hr = apObjArray[i]->Get(L"Handle", 0, &cn, NULL, NULL);
                if(SUCCEEDED(hr)) {
                    if((cn.vt == VT_NULL) || (cn.vt == VT_EMPTY))
                        std::cout << "Handle : " << ((cn.vt == VT_NULL) ? "NULL" : "EMPTY") << endl;
                    else if((cn.vt & VT_ARRAY))
                        std::cout << "Handle : " << "Array types not supported (yet)" << endl;
                    else {
                        std::wstring WideProcessHandle = std::wstring(cn.bstrVal);
                        ProcessHandleString = std::string(WideProcessHandle.begin(), WideProcessHandle.end());

                        for(auto& NewProcessCreatedListener : NewProcessCreatedListeners) {
                            NewProcessCreatedListener(reinterpret_cast<void*>(std::stoul(ProcessHandleString, nullptr)));
                        }
                    }
                }
                VariantClear(&cn);
            }
        }
        VariantClear(&vtProp);
    }

    return WBEM_S_NO_ERROR;
}

HRESULT ProcessCreatedEventDispatcher::SetStatus(
    /* [in] */ LONG lFlags,
    /* [in] */ HRESULT hResult,
    /* [in] */ BSTR strParam,
    /* [in] */ IWbemClassObject __RPC_FAR* pObjParam
) {
    if(lFlags == WBEM_STATUS_COMPLETE) {
        printf("Call complete. hResult = 0x%X\n", hResult);
    }
    else if(lFlags == WBEM_STATUS_PROGRESS) {
        printf("Call in progress.\n");
    }

    return WBEM_S_NO_ERROR;
} // end of EventSink.cpp

Usage of this class is very easy:

#include <conio.h>
#include <iostream>

#include "ProcessCreatedEventDispatcher.h"

int main(int iArgCnt, char** argv) {
    ProcessCreatedEventDispatcher ProcessCreatedEventDispatcher{};
    ProcessCreatedEventDispatcher.NewProcessCreatedListeners.emplace_back([](auto ProcessHandle) {
        std::cout << "New Process Handle: " << std::hex << "0x" << ProcessHandle << std::endl;
        std::flush(std::cout);
    });

    // Wait for key press to exit the program
    std::cout << "Press any key to terminate" << std::endl;
    while(!_kbhit()) {}

    return 0; // Program successfully completed.  
}

Is it any good? I'd say yes: modular, RAII and basic observer design pattern.

KulaGGin
  • 943
  • 2
  • 12
  • 27