web-dev-qa-db-fra.com

Comment détecter la création/fin de processus win32 en c ++

Je sais que pour recevoir des notifications sur la création ou la fin d'un processus Win32, nous pourrions implémenter un pilote en mode noyau NT à l'aide des APIs PsSetCreateProcessNotifyRoutine(), qui offre la possibilité d'enregistrer une fonction de rappel à l'échelle du système appelée par le système d'exploitation chaque fois qu'un nouveau processus démarre, se termine. ou est terminé.

Est-ce possible sans créer un pilote en mode noyau NT, en utilisant uniquement les fonctions de l'API Win32 en utilisant c ++? Ne pas utiliser la solution de base d'un cycle infini interrogeant la liste des processus actifs, bien sûr.

Existe-t-il une bibliothèque ou une API Win32 offrant les mêmes fonctionnalités (rappel système, événements asynchrones)?

30
Nuno

La seule chose à laquelle je puisse penser est WMI, je ne sais pas si elle fournit un rappel de création de processus, mais cela vaut peut-être la peine.

10
Anders

WMI est génial et fonctionne aussi avec les noms de processus. Bien que si vous avez besoin de suivre les terminaisons de processus, le moyen le plus léger et le plus simple est le suivant:

VOID CALLBACK WaitOrTimerCallback(
    _In_  PVOID lpParameter,
    _In_  BOOLEAN TimerOrWaitFired
    )
{
    MessageBox(0, L"The process has exited.", L"INFO", MB_OK);
    return;
}

DWORD dwProcessID = 1234;
HANDLE hProcHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID);

HANDLE hNewHandle;
RegisterWaitForSingleObject(&hNewHandle, hProcHandle , WaitOrTimerCallback, NULL, INFINITE, WT_EXECUTEONLYONCE);

Ce code appellera WaitOrTimerCallback une fois le processus terminé.

22
Anton K

Vous pouvez surveiller tous les processus de création de fenêtres à l’aide de SetWindowsHookEx avec un CBTProc . Toutefois, rien de plus n’exige WMI, un pilote Windows ou un peu de ' Black Magic '.

6
Necrolis

Anders a raison, WMI fonctionne bien pour cela. Etant donné que j'en avais besoin pour un projet, je peux partager le code de détection (arbitraire) de la fin du processus (en fonction de son ID):

ProcessTerminationNotification.h:

#ifndef __ProcessTerminationNotification_h__
#define __ProcessTerminationNotification_h__

#include <boost/function.hpp>

namespace ProcessTerminationNotification
{
    typedef boost::function< void(void) > TNotificationFunction;

    void registerTerminationCallback(TNotificationFunction callback, unsigned processId);
}
#endif // __ProcessTerminationNotification_h__

ProcessTerminationNotification.cpp:

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

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

#include "ProcessTerminationNotification.h"

class EventSink : public IWbemObjectSink
{
    friend void ProcessTerminationNotification::registerTerminationCallback(TNotificationFunction callback, unsigned processId);

    CComPtr<IWbemServices> pSvc;
    CComPtr<IWbemObjectSink> pStubSink;

    LONG m_lRef;
    ProcessTerminationNotification::TNotificationFunction m_callback;

public:
    EventSink(ProcessTerminationNotification::TNotificationFunction callback)
        : m_lRef(0) 
        , m_callback(callback)
    {}
    ~EventSink()
    {}

    virtual ULONG STDMETHODCALLTYPE AddRef()
    {
        return InterlockedIncrement(&m_lRef);
    }
    virtual ULONG STDMETHODCALLTYPE Release()
    {
        LONG lRef = InterlockedDecrement(&m_lRef);
        if (lRef == 0)
            delete this;
        return lRef;
    }
    virtual HRESULT STDMETHODCALLTYPE 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;
    }

    virtual HRESULT STDMETHODCALLTYPE Indicate( 
        LONG lObjectCount,
        IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray
        )
    {
        m_callback();
        /* Unregister event sink since process is terminated */
        pSvc->CancelAsyncCall(pStubSink);
        return WBEM_S_NO_ERROR;
    }

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

};


void ProcessTerminationNotification::registerTerminationCallback( TNotificationFunction callback, unsigned processId )
{
    CComPtr<IWbemLocator> pLoc;

    HRESULT 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;
        throw std::exception("ProcessTerminationNotificaiton initialization failed");
    }

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

    CComPtr<EventSink> pSink(new EventSink(callback));

    // 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, 
        &pSink->pSvc
        );

    if (FAILED(hres))
    {
        cout << "Could not connect. Error code = 0x" 
            << hex << hres << endl;
        throw std::exception("ProcessTerminationNotificaiton initialization failed");
    }

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

    hres = CoSetProxyBlanket(
        pSink->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;
        throw std::exception("ProcessTerminationNotificaiton initialization failed");
    }

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

    // Use an unsecured apartment for security
    CComPtr<IUnsecuredApartment> pUnsecApp;

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

    CComPtr<IUnknown> pStubUnk; 
    pUnsecApp->CreateObjectStub(pSink, &pStubUnk);

    pStubUnk->QueryInterface(IID_IWbemObjectSink,
        (void **) &pSink->pStubSink);

    // The ExecNotificationQueryAsync method will call
    // The EventQuery::Indicate method when an event occurs
    char buffer[512];
    sprintf_s(buffer, "SELECT * " 
        "FROM __InstanceDeletionEvent WITHIN 1 "
        "WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.ProcessId=%u", processId);

    hres = pSink->pSvc->ExecNotificationQueryAsync(
        _bstr_t("WQL"), 
        _bstr_t(buffer), 
        WBEM_FLAG_SEND_STATUS, 
        NULL, 
        pSink->pStubSink);

    // Check for errors.
    if (FAILED(hres))
    {
        cout << "ExecNotificationQueryAsync failed "
            "with = 0x" << hex << hres << endl;
        throw std::exception("ProcessTerminationNotificaiton initialization failed");
    }
}

Notez que le code pour initialiser la sécurité des processus COM et COM (CoInitializeEx et CoInitializeSecurity) est omis ici car il doit être effectué lors de l'initialisation de l'application.

Utilisez-le avec des fonctions globales ou utilisez boost :: bind pour vous connecter à une méthode quelconque, exemple de cette dernière:

class MyClass
{
public:
    void myProcessTerminationCallback() { cout << "Wohoo!!" << endl; }
};


ProcessTerminationNotification::registerTerminationCallback(
    boost::bind(&MyClass::myProcessTerminationCallback, <pointer to MyClass instance>),
    1234); // Process ID = 1234
5
Robert

Comme déjà suggéré par un commentaire précédent, l'utilisation de WMI pour surveiller les événements de processus présente un inconvénient, car WMI ne fournit pas les événements de manière synchrone, .i.e. avec un court délai.

Le livre "Windows Internals Part 1" fait référence à un mécanisme appelé "Suivi des événements pour Windows (ETW)", qui est un mécanisme de bas niveau pour les événements du système d'exploitation.

Le billet de blog suivant montre comment utiliser ETW dans .Net pour surveiller les processus: http://blogs.msdn.com/b/vancem/archive/2013/03/09/using-traceevent- to-mine-information-in-os-registered-etw-providers.aspx

4
Christian K.

Vous pouvez surveiller la création de processus en associant la fonction CreateProcessInternalW. En reliant cette fonction, vous pouvez même injecter des DLL dans le nouveau processus.

1
zwclose7

Outre WMI, ou si vous souhaitez empêcher le démarrage du processus ou du thread, ou lorsque vous avez besoin de notifications synchrones, vous pouvez utiliser une approche de pilote en mode noyau. Notre CallbackProcess product, par exemple, fait exactement cela. 

Les requêtes WMI peuvent coûter cher en performances CPU si elles ne sont pas conçues correctement. Si un événement intrinsèque de la classe Win32_Process est utilisé pour suivre un événement de création de processus, cela a un impact sur les performances lourdement . Une autre approche consiste à exploiter les journaux d'audit de sécurité. Vous pouvez activer le suivi des processus à l'aide de la stratégie de sécurité locale ou à l'aide d'un GPO dans le cas de plusieurs ordinateurs. Une fois le suivi du processus lancé, vous pouvez vous abonner aux journaux des événements de sécurité avec une requête XML personnalisée pour surveiller certains processus qui vous intéressent. L'ID d'événement de création de processus est 4688. `

<QueryList>
 <Query Id="0" Path="Security">
   <Select Path="Security">
       *[EventData[Data[@Name='NewProcessName'] ='C:\Windows\Explorer.exe']]
       and
       *[System[(EventID=4688)]]
   </Select>
 </Query>
</QueryList>

`

0
Red John