2010-08-24 27 views
27

Sé que para recibir notificaciones sobre la creación o terminación del proceso Win32 podemos implementar un controlador NT kernel-mode usando las API PsSetCreateProcessNotifyRoutine() que ofrece la capacidad de registrar la función de devolución de llamada del sistema que es llamado por SO cada vez que un nuevo proceso se inicia, finaliza o finaliza.Cómo detectar la creación/finalización del proceso win32 en C++

¿Esto es posible sin crear un controlador NT kernel-mode, solo usando las funciones de Win32 API usando C++? No usar la solución básica de un ciclo infinito consultando la lista de procesos activos, por supuesto.

¿Hay alguna biblioteca o API win32 que proporcione la misma funcionalidad (devolución de llamada a nivel del sistema, eventos asincrónicos)?

Respuesta

10

Lo único que se me ocurre es WMI, no estoy seguro si proporciona una devolución de llamada de creación de proceso, pero podría valer la pena investigarlo.

+8

Sí WMI puede proporcionar lo que estoy buscando (creación de proceso/devolución de llamada de terminación). Si alguien es interesante sobre cómo echar un vistazo a http://msdn.microsoft.com/en-us/library/aa390425%28VS.85%29.aspx Gracias – Nuno

-1

La API de enganche debería ser la forma correcta de cumplir algo así. Usted puede conectar CreateProcess (A/W/asUserA/W .... etc) y NtTerminateProcess

+0

Eso no puede lograr la supervisión de todo el sistema a menos que lo haga en kernel y con PatchGuard eso no es realmente posible. –

6

puede supervisar toda la ventana creación de procesos utilizando SetWindowsHookEx con un CBTProc, sin embargo, nada más que eso requiere o WMI, un controlador de Windows o un poco de 'Black Magic'

+1

Esto solo rastrea los procesos con Windows, pero esta es una buena respuesta para muchos escenarios. Esto también inyecta asambleas en dicho proceso. –

+0

Los ganchos CBT no pueden rastrear la creación/finalización del proceso. Esta no es una respuesta a la pregunta. – IInspectable

+0

@IInspectable: los ganchos CBT pueden rastrear procesos de ventanas; sin embargo, el resto de mi respuesta responde la pregunta; Los ganchos CBT son simples en términos de procesos basados ​​en ventanas. – Necrolis

5

Anders es correcto, WMI funciona muy bien para esto. Ya que necesitaba esto para un proyecto que puede compartir el código para la detección (arbitraria) la terminación del proceso (dado su 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"); 
    } 
} 

Tenga en cuenta que el código para inicializar la seguridad del proceso COM y COM (CoInitializeEx y CoInitializeSecurity) se omite aquí, ya que debe hacerse en la inicialización de la aplicación.

lo uso con funciones globales o el uso boost :: bind para conectarse a un método arbitrario, ejemplo de esto último:

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


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

¿Alguien tiene una idea de cómo WMI implementa los eventos internamente? –

+0

De acuerdo con el libro "Windows Internals Parte 1" hay un mecanismo llamado "Seguimiento de eventos para Windows (ETW)" que es capaz de rastrear la creación y terminación del proceso. Al final, hay una desventaja significativa con la solución WMI ya que no proporciona los eventos en tiempo real (sincrónicamente). Como se puede ver mirando la cláusula WITHIN de WMI WQL. –

+0

Existen muy pocas circunstancias, si las hay, en las que se necesita información en tiempo real. Es en el 99.999% de los casos suficiente para SABER que el proceso ha muerto. – Robert

1

Puede supervisar la creación de procesos enganchando CreateProcessInternalW función. Al conectar esta función, incluso puede inyectar archivos DLL en el nuevo proceso.

4

Como ya lo insinuó un comentario anterior, hay un inconveniente al usar WMI para monitorear eventos de proceso ya que WMI no proporciona eventos de forma síncrona, por ejemplo. con un breve retraso.

El libro "Windows Internals Part 1" se refiere a un mecanismo llamado "Event Tracing for Windows (ETW)", que es un mecanismo de bajo nivel para los eventos del sistema operativo.

La siguiente entrada del blog muestra cómo ETW se puede utilizar dentro .Net para monitorear los procesos: http://blogs.msdn.com/b/vancem/archive/2013/03/09/using-traceevent-to-mine-information-in-os-registered-etw-providers.aspx

+2

Desafortunadamente, resulta que ETW tampoco proporciona eventos síncronos/en tiempo real. Los eventos ETW también están sujetos a almacenamiento en búfer. –

19

WMI es grande y funciona con los nombres de proceso también. Aunque si es necesario realizar un seguimiento de la terminación del proceso de la manera más ligera y más fácil es la siguiente:

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); 

Este código se llama WaitOrTimerCallback una vez que el proceso ha terminado.

+0

¿Hay alguna manera de detectar el código de salida de ese proceso con su solución? –

+0

¿Hay alguna manera como esta, pero para rastrear la creación de procesos? – brunoqc

+1

@brunoqc Encontré solo WMI en esta pregunta. –

0

Además de WMI, o si necesita evitar que el proceso o subproceso se inicie, o cuando necesite notificaciones sincrónicas, puede utilizar un enfoque de controlador kernel-mode. Nuestro producto CallbackProcess, por ejemplo, hace exactamente esto.

Cuestiones relacionadas