【问题标题】:How to detect win32 process creation/termination in c++如何在 C++ 中检测 win32 进程的创建/终止
【发布时间】:2011-04-03 03:10:53
【问题描述】:

我知道要接收有关 Win32 进程创建或终止的通知,我们可能会使用 API PsSetCreateProcessNotifyRoutine() 实现 NT 内核模式驱动程序,该驱动程序提供注册系统范围回调函数的能力,每次当新进程启动、退出或终止。

这是否可能在不创建 NT 内核模式驱动程序的情况下,仅使用使用 c++ 的 Win32 API 函数?当然不使用无限循环查询活动进程列表的基本解决方案。

是否有任何库或 win32 API 提供相同的功能(系统范围的回调、异步事件)?

【问题讨论】:

    标签: c++ winapi process


    【解决方案1】:

    如果设计不当,WMI 查询会消耗大量 CPU 性能。如果使用 Win32_Process 类的内部事件来跟踪进程创建事件,这会影响性能heavily。另一种方法是利用安全审计日志。您可以使用本地安全策略或在多台计算机的情况下使用 GPO 启用进程跟踪。流程跟踪开始后,您可以使用自定义 XML 查询订阅安全事件日志,以监控您感兴趣的某些流程。进程创建事件 ID 为 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>
    

    `

    【讨论】:

      【解决方案2】:

      除了 WMI,或者如果您需要阻止进程或线程启动,或者当您需要同步通知时,您可以使用内核模式驱动程序的方法。例如,我们的CallbackProcess 产品就是这样做的。

      【讨论】:

        【解决方案3】:

        WMI 很棒,它也适用于进程名称。虽然如果您需要跟踪进程终止,更轻量级和更简单的方法如下:

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

        一旦进程终止,此代码将调用WaitOrTimerCallback

        【讨论】:

        • 有没有办法用您的解决方案检测该进程的退出代码?
        • 除了跟踪进程创建之外,还有其他方法吗?
        • @brunoqc 我在这个问题中发现了 WMI 方式。
        • 您确定PROCESS_ALL_ACCESS 是严格要求的吗? SYNCHRONIZE 还不够吗?
        • 别忘了致电UnregisterWait function
        【解决方案4】:

        正如之前的评论已经暗示的那样,使用 WMI 监视进程事​​件有一个缺点,因为 WMI 不能同步提供事件,即。有短暂的延迟。

        “Windows Internals Part 1”一书提到了一种称为“Windows 事件跟踪 (ETW)”的机制,它是操作系统事件的低级机制。

        以下博客文章展示了如何在 .Net 中使用 ETW 来监控进程: http://blogs.msdn.com/b/vancem/archive/2013/03/09/using-traceevent-to-mine-information-in-os-registered-etw-providers.aspx

        【讨论】:

        • 不幸的是,ETW 也没有提供同步/实时事件。 ETW 事件也受到缓冲。
        • 我不想说明这一点,但 Windows 没有“实时”,它随时调度多个线程,您的线程总是等待有关系统创建进程的事件根据“延迟”的严格定义,消耗此类延迟的事件 - 这不像 Windows 会暂停进程创建并等待您的线程在继续之前从回调中返回。所以从这个意义上说,无论你是从缓冲区消费事件并且有延迟,还是 Windows 说,将消息发布到你的线程队列,都是一样的——异步。事件(消息)队列。
        【解决方案5】:

        您可以通过挂钩CreateProcessInternalW 函数来监控进程创建。通过 hook 这个函数,你甚至可以将 DLLs 注入到新进程中。

        【讨论】:

          【解决方案6】:

          Anders 是正确的,WMI 可以很好地解决这个问题。由于我在一个项目中需要这个,我可以分享用于检测(任意)进程终止的代码(给定它的 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");
              }
          }
          

          请注意,这里省略了初始化 COM 和 COM 进程安全性(CoInitializeEx 和 CoInitializeSecurity)的代码,因为它应该在应用程序初始化中完成。

          将它与全局函数一起使用或使用 boost::bind 连接到任意方法,后者的示例:

          class MyClass
          {
          public:
              void myProcessTerminationCallback() { cout << "Wohoo!!" << endl; }
          };
          
          
          ProcessTerminationNotification::registerTerminationCallback(
              boost::bind(&MyClass::myProcessTerminationCallback, <pointer to MyClass instance>),
              1234); // Process ID = 1234
          

          【讨论】:

          • 有人知道 WMI 如何在内部实现事件吗?
          • 根据“Windows Internals Part 1”一书,有一种称为“Windows 事件跟踪 (ETW)”的机制,它能够跟踪进程的创建和终止。最后,WMI 解决方案存在一个重大缺陷,因为它不能实时(同步)提供事件。通过查看 WMI WQL 的 WITHIN 子句可以看出。
          • 很少(如果有的话)需要实时信息。在 99.999% 的情况下,足以知道进程已经死亡。
          • 使用 C++0x,您应该能够用 STL 替换对 boost 的依赖。或者自己滚动。这留作练习 ;)
          • 我认为这个问题是WITHIN 子句导致查询不报告在一秒钟内启动和终止的进程,即不会报告非常短暂的进程。跨度>
          【解决方案7】:

          您可以使用SetWindowsHookExCBTProc 监控所有窗口创建进程,但除此之外,还需要 WMI、Windows 驱动程序或一点点“Black Magic

          【讨论】:

          • 这仅跟踪带有窗口的进程,但对于很多场景来说这是一个很好的答案。这也将程序集注入到所述进程中。
          • CBT 挂钩无法跟踪进程创建/终止。这不是问题的答案。
          • 没有。 CBT 挂钩可以监视窗口的创建和销毁。窗口创建/销毁和进程创建/终止之间没有关系。问题是关于后者。
          • 一个进程可以创建一个窗口,销毁它,休眠一段时间,比如一个月,然后再创建一个新窗口。当需要通知应用程序进程终止时,您如何建议 CBT 挂钩处理这种情况?这不是对所提出问题的回答。真的就是这么简单。
          • 您已成为会员超过 7 年。你应该知道规则。到场外资源的链接应被视为可选信息。如果您的答案本身不包含足够的信息,您不妨完全忽略该链接。到场外资源的链接确实变得无法访问,这意味着您的答案不再是。
          【解决方案8】:

          API-hooking 应该是完成类似内容的正确方法。您可以挂钩 createProcess(A/W/asUserA/W.... etc) 和 NtTerminateProcess

          【讨论】:

          • 这无法实现系统范围的监控,除非您在内核中执行此操作并且使用 PatchGuard 这实际上是不可能的。
          【解决方案9】:

          我唯一能想到的是 WMI,不确定它是否提供进程创建回调,但它可能值得研究。

          【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-11-10
          • 2020-10-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-14
          相关资源
          最近更新 更多