programas - hola mundo c++
Cómo detectar la creación/finalización del proceso win32 en c++ (9)
Sé que para recibir notificaciones sobre la creación o terminación del proceso Win32 podemos implementar un controlador NT kernel-mode utilizando las API PsSetCreateProcessNotifyRoutine()
que ofrece la capacidad de registrar la función de devolución de llamada en todo el sistema que el sistema operativo llama cada vez que se inicia un nuevo proceso , sale o termina.
¿Es esto 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 de todo el sistema, eventos asincrónicos)?
Además de WMI, o si necesita evitar que se inicie el proceso o subproceso, o cuando necesite notificaciones sincrónicas, puede usar un enfoque de controlador kernel-mode. Nuestro producto CallbackProcess , por ejemplo, hace exactamente esto.
Anders tiene razón, WMI funciona muy bien para esto. Como necesitaba esto para un proyecto, puedo compartir el código para detectar la terminación del proceso (arbitraria) (dada 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.
Úselo con funciones globales o use boost :: bind para conectarse a un método arbitrario, ejemplo de este último:
class MyClass
{
public:
void myProcessTerminationCallback() { cout << "Wohoo!!" << endl; }
};
ProcessTerminationNotification::registerTerminationCallback(
boost::bind(&MyClass::myProcessTerminationCallback, <pointer to MyClass instance>),
1234); // Process ID = 1234
Como ya lo insinuó un comentario anterior, hay una desventaja con el uso de WMI para monitorear eventos de proceso ya que WMI no proporciona eventos de forma sincrónica, es decir, con un breve retraso.
El libro "Windows Internals Part 1" hace referencia a un mecanismo llamado "Event Tracing for Windows (ETW)", que es un mecanismo de bajo nivel para los eventos del sistema operativo.
La siguiente publicación de blog muestra cómo se puede usar ETW dentro de .Net para monitorear procesos: http://blogs.msdn.com/b/vancem/archive/2013/03/09/using-traceevent-to-mine-information-in-os-registered-etw-providers.aspx
El enganche de API debería ser la forma correcta de cumplir algo así. Puede enlazar createProcess (A / W / asUserA / W .... etc) y NtTerminateProcess
Las consultas de WMI pueden costar un gran rendimiento de la CPU si no se diseñan correctamente. Si se utiliza un evento intrínseco de la clase Win32_Process para rastrear el evento de creación de proceso, esto impacta heavily rendimiento. Un enfoque alternativo es aprovechar los registros de auditoría de seguridad. Puede habilitar el Seguimiento de procesos utilizando la Política de seguridad local o utilizando un GPO en el caso de varias máquinas. Una vez que se inicia el seguimiento del proceso, puede suscribirse a los registros de eventos de seguridad con una consulta XML personalizada para supervisar ciertos procesos de su interés. El ID de evento de creación de proceso es 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>
`
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.
Puede supervisar la creación de procesos conectando la función CreateProcessInternalW
. Al conectar esta función, incluso puede inyectar archivos DLL en el nuevo proceso.
Puede supervisar todos los procesos de creación de ventanas usando SetWindowsHookEx con un CBTProc , sin embargo, todo lo CBTProc requiere WMI, un controlador de Windows o un poco de '' Black Magic ''.
WMI es excelente y también funciona con nombres de procesos. Aunque si necesita rastrear la terminación del proceso, la manera más ligera y 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 llamará a WaitOrTimerCallback
una vez que WaitOrTimerCallback
el proceso.