获取 COM 服务器进程 ID 的 3 种方法

要创造新产品,拥有标准化和可靠的组件非常重要。例如,房屋建筑商使用标准尺寸的砖建造房屋。在编程方面,我们有开发标准。这些标准之一是由 Microsoft 创建的组件对象模型 (COM)

COM 有效地解决了代码重用问题,但其部分功能的实现尚不明确。例如,在开发数据泄漏保护系统时,您可能需要获取 COM 服务器进程 ID (PID) 以检查进程如何处理敏感数据。Microsoft 的文档没有提供明确的方法来执行此操作,因此我们决定分享我们的经验。在本文中,我们解释了 COM 服务器的工作原理,并展示了获取 COM 服务器 PID 的三种不同方法。

本指南对那些希望更多地了解 COM 服务器基础结构的人以及在实践中遇到 PID 问题的专家都很有用。

什么是COM?

代码可重用性是软件开发中的优先事项。通常,软件是使用特定的编程语言开发的,只有使用相同的语言开发其他组件才能有效地使用。COM 为开发人员提供了必须在各种环境中工作的组成模块。 

作为独立于平台、面向对象的技术标准,组件对象模型支持二进制组件的开发。随后,这些组件既可以在本地使用,也可以在分布式网络环境中使用。COM 的主要目的是提供一种方法,使用各种编程语言编写的对象和组件可以互操作,而无需更改可执行代码。根据 Microsoft 的文档,向客户端提供服务的对象称为 COM 服务器。服务表示为 COM 接口的实现,任何客户端都可以调用 COM 接口,该接口可以获取指向服务器对象上的接口之一的指针。 

与对象透明交互的能力是通过设计内置在 COM 中的。一个对象可以在同一进程、同一台机器或另一台机器上运行,但无论其类型如何,它始终只有一个编程模型。此功能称为位置透明度。

位置透明意味着对于 COM 用户来说,COM 服务器位于何处并不重要。对于用户来说,一切都是一样的。但是,如果 COM 服务器在另一个进程中并且用户需要其 PID,位置透明性的设计不允许用户获得它。让我们看看我们如何处理这个问题。 

为什么需要获取进程 ID?

获取进程 ID 是一项相当具体的任务。尽管如此,在某些情况下还是有必要的。例如,系统监控工具需要一个 PID,它应该提供有关在系统内运行的进程的信息,包括有关具有父子关系的进程的数据。COM 服务器不提供此信息,但了解哪个进程启动了服务器的创建会很有用。此外,没有简单或明显的方法可以挂钩此类进程,因为它们与原始进程之间没有父子关系。出路是使用我们将在下面介绍的方法来跟踪 COM 服务器的创建。 

此外,获取 PID 对于各种数据丢失防护 (DLP)系统尤其重要。在 DLP 系统中,有必要控制流程处理敏感文档的方式。通常,这是通过挂钩进程来完成的。有时,进程会生成子进程或COM进程,也需要对其进行控制以防止数据泄露。要挂钩 COM 进程,您需要知道它的 PID。这正是我们在开展其中一个项目时遇到的问题。 

在我们的一个项目中,我们使用了一个应用程序,该应用程序将WinAPI 挂钩用于ReadFileWriteFileCreateFile等函数来处理加密文件。文件以加密方式存储在磁盘上,当应用程序与文件交互时,它会动态解密或加密内容:

file_encryption_description_process

应用程序不知道该文件已加密并使用简单的 API 与其交互。有时,应用程序会启动也处理加密文件的子进程。这种情况可以通过挂钩CreateProcessCreateProcessAsUser函数并从返回值中检索子进程的 PID 来处理。PID 足以挂钩进程。这是它的工作原理:

hooking_using_PID

有时,应用程序会启动也处理加密文件的 COM 进程。没有简单或明显的方法可以挂钩此类进程,因为它们与原始进程之间没有父子关系。让我们看看如何从最基本的方面获取 COM 服务器进程 ID。

使用 COM 的基本原则

在获取 PID 之前,让我们快速回顾一下 COM 技术的一些基础知识,以更好地了解 COM 服务器的进程 ID 是什么以及 COM 服务器如何从内部工作。

如何获得COM接口

为了得到一个COM服务器的PID,我们首先要得到COM接口。获取COM接口主要有3个函数:

一般来说,获取COM接口的过程是这样的:

获取_com_interface

  1. 客户端调用CoCreateInstanceEx函数,导致 COM 将激活请求委托给其本地服务控制管理器 (SCM)。
  2. 本地 SCM 在 [HKCR\CLSID\CLSID\inprocServer32] 下的本地注册表中查找实现此 COM 类的进程内服务器。如果它找到进程内服务器,它会将进程内服务器的路径返回到 COM。COM 然后加载进程内服务器。
  3. 如果 SCM 找不到实现请求的 COM 类的进程内服务器,它会查看 SCM 缓存以查看请求的 COM 类的类工厂是否已被已运行的本地服务器注册。
  4. 如果 SCM 在缓存中找不到服务器,它会在 [HKCR\CLSID\CLSID\LocalServer32] 下查找本地服务器路径并生成本地服务器,该服务器注册服务器支持的类工厂。
  5. 如果 SCM 找不到本地服务器,它会在 [HKCR\AppID\AppID\RemoteServerName] 下查找RemoteServerName条目。然后本地 SCM 联系远程 SCM 并要求远程 SCM 处理激活请求。远程 SCM 将按照第 3 步到第 5 步,尝试生成一个支持所请求工厂的远程进程。

但是,无法知道创建的对象是在同一个进程中还是在另一个进程中,也没有 API 来获取托管 COM 对象的进程的 PID。为了更好地理解如何获取 COM 服务器进程 ID,让我们探讨 COM 如何与远程对象通信。  

COM 服务器如何与远程对象通信

客户端通过 COM 运行时提供的特殊代理访问远程 COM 对象,以实现位置透明。Microsoft 扩展并利用其现有的远程过程调用 (RPC)技术来允许远程对象进行通信。代理将所有参数和接口编组到远程存根,这些存根解组它们并调用远程对象的方法。您可以在 Microsoft 文档中阅读有关对象间通信的更多信息。

要获得 PID,我们需要了解代理如何编组 COM 接口以及编组后的表示具有哪些字段。COM 使用特殊的OBJREF结构来表示封送接口。这是此类接口的示例:

typedef struct tagOBJREF  {
    unsigned long signature;         
    unsigned long flags;             
    GUID        iid;               
    union      {
        struct          {
            STDOBJREF     std;     
            DUALSTRINGARRAY saResAddr;
        } u_standard;  
        struct          {
            STDOBJREF     std;     
            CLSID         clsid;   
            DUALSTRINGARRAY saResAddr;
        } u_handler;          
        struct          {
            CLSID         clsid;   
            unsigned long   cbExtension;
            unsigned long   size;    
            byte *pData;
        } u_custom;
        struct          {
            STDOBJREF     std;
            unsigned long   Signature1;
            DUALSTRINGARRAY saResAddr;
            unsigned long   nElms;
            unsigned long   Signature2;
            DATAELEMENT   ElmArray;
        } u_extended; 
    } u_objref;  
} OBJREF, *LPOBJREF; 

OBJREF 有四种不同的格式,它们由 u_objref 字段的定义指定。此外,STDOBJREF结构是编组接口表示的重要组成部分。下面是这种结构的一个例子:

typedef unsigned __int64 OXID;
typedef unsigned __int64 OID;
typedef GUID           IPID;
  
  
typedef struct tagSTDOBJREF  {
    unsigned long flags;
    unsigned long cPublicRefs;
    OXID oxid;
    OID  oid;
    IPID ipid;
} STDOBJREF; 

STDOBJREF 包含对编组和查找远程对象和接口很重要的字段:对象导出器标识符 (OXID)、对象标识符 (OID) 和接口指针标识符 (IPID)。我们将在下面描述的三种获取 PID 的方法中的两种中使用这些字段,让我们仔细看看它们:

  • 氧化。这是一个 64 位值,分配给为远程客户端导出接口或编组接口的单元。它在给定的机器中是唯一的。
  • 标识符。这是分配给存根管理器的 64 位值,可以将其描述为对象端的假客户端。它在特定公寓内是独一无二的。
  • IPID。这是一个 128 位的值,表示唯一的接口指针 ID,用于标识接口存根。

现在,让我们直接进入获取PID的方法。为简洁起见,我们在教程中省略了错误处理步骤。

 

获取 COM 服务器 PID

获取 COM 服务器的 PID 有三种基本方法:

get_pid

让我们仔细看看它们中的每一个。

从 IPID 获取 COM 服务器 PID

注意:为简洁起见,省略了错误处理。

第一种方法是从 IPID 获取 PID。为了说明这个方法,我们将编写一个小应用程序,它指示 COM 启动一个单独的进程来承载一个 COM 对象:

#include <atlbase.h>
#include <windows.h>
  
  
int main()
{
    ⁄⁄ Initializes the COM library in the current thread and identifies the concurrency model as a single-thread apartment.
    CoInitialize(NULL);
  
    CComPtr<IUnknown> excelInterface;
  
    excelInterface.CoCreateInstance(
 
        OLESTR("Excel.Application"), ⁄⁄ Launch Microsoft Excel app
        NULL,                      ⁄⁄ No aggregation
        CLSCTX_LOCAL_SERVER        ⁄⁄ Create an object in another process
    );
      
    ...
}
 

Excel 应用程序已启动。使用进程资源管理器Sysinternals的工具,我们可以看到,它是由svchost.exe的,它是由COM运行时加载:

Excel_application_launched_by_the_COM_runtime

但是只有 IUnknown 接口,我们不能说一个对象是本地的还是远程的。让我们编码调试一下以查看我们需要的字段:

#include <atlbase.h>
#include <windows.h>
  
  
int main()
{
    ...
  
    CComPtr<Istream> marshalStream;
    CreateStreamOnHGlobal(NULL, TRUE, &marshalStream);
  
    CoMarshalInterface(
        marshalStream,   ⁄⁄ Where to write the marshaled interface
        IID_IUnknown,  ⁄⁄ ID of the marshaled interface
        excelInterface,  ⁄⁄ The interface to be marshaled
        MSHCTX_INPROC,   ⁄⁄ Unmarshaling will be done in the same process
        NULL,          ⁄⁄ Reserved and must be NULL
        MSHLFLAGS_NORMAL ⁄⁄ The data packet produced by the marshaling process will be unmarshaled in the destination process
    );
  
    HGLOBAL memoryHandleFromStream = NULL;
    GetHGlobalFromStream(marshalStream, &memoryHandleFromStream);
  
    LPOBJREF objef = reinterpret_cast <LPOBJREF> (GlobalLock(memoryHandleFromStream));
  
  
    ...
} 

然后我们使用调试器查看IPID结构的内容:

IPID结构的内容

 

Data2 的前两个字节是远程进程的 PID。

从 OBJREF 获取 PID 的代码如下所示:

#include <atlbase.h>
#include <windows.h>
  
int main()
{
    ...
  
    ⁄⁄ Valid OBJREF has this field = 0x574f454d
    ⁄⁄ https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dcom/fe6c5e46-adf8-4e34-a8de-3f756c875f31
    if (objef && objef->signature == OBJREF_SIGNATURE)
    {
        IPID ipid;
  
        if (objef->flags == OBJREF_STANDARD)
        {
            ipid = objef->u_objref.u_standard.std.ipid;
        }
        else if (objef->flags == OBJREF_HANDLER)
        {
            ipid = objef->u_objref.u_handler.std.ipid;
        }
        else if (objef->flags == OBJREF_EXTENDED)
        {
            ipid = objef->u_objref.u_extended.std.ipid;
        }
  
        DWORD pid = 0;
  
        if (GetCOMServerPID(ipid, &pid))
        {
            std::cout << "PID: " << pid << std::endl;
        }
    }
  
  
    ...
} 

要提取 PID,我们使用GetCOMServerPID函数:

BOOL GetCOMServerPID(__in IPID ipid, __out DWORD* pid)
{
    static const int COM_SERVER_PID_OFFSET = 4;
  
    *pid = *reinterpret_cast<LPWORD>(
        (reinterpret_cast<LPBYTE>(&ipid) + COM_SERVER_PID_OFFSET)
    );
  
    ⁄⁄ IPID contains only 16-bit for PID, and if the PID > 0xffff, then it's clapped to 0xffff
    return *pid != 0xffff;
} 

如果PID小于65535,我们会在执行这个函数后得到它。如果它更大,我们需要使用另一种方法。

从 OXID 解析器获取 COM 服务器 PID

注意:为简洁起见,省略了错误处理。

每台 COM 机器都运行一个称为Object Resolver的特殊管理器服务,也称为 OXID Resolver。它运行在每台支持 COM 的机器上,并执行两个重要功能:

  • 存储连接远程对象所需的远程过程调用 (RPC) 字符串绑定,并向本地客户端提供 RPC 字符串绑定。
  • 将 ping 消息发送到本地机器有客户端的远程对象,并接收本地机器上运行的对象的 ping 消息。

基本上,OXID Resolver 存储有关 COM 服务器的信息(地址、端口等)。我们可以通过调用ResolveOxid方法从 OXID 解析器中检索此信息:

⁄* [idempotent] *⁄ error_status_t ResolveOxid(
    ⁄* [in] *⁄ handle_t hRpc,
    ⁄* [in] *⁄ OXID *pOxid,
    ⁄* [in] *⁄ unsigned short cRequestedProtseqs,
    ⁄* [size_is][ref][in] *⁄ unsigned short arRequestedProtseqs[  ],
    ⁄* [ref][out] *⁄ DUALSTRINGARRAY **ppdsaOxidBindings,
    ⁄* [ref][out] *⁄ IPID *pipidRemUnknown,
    ⁄* [ref][out] *⁄ DWORD *pAuthnHint); 

首先,我们应该检索 RPC 绑定以连接到 OXID 解析器。我们可以通过传输控制协议(TCP)端口 135 与 OXID Resolver 建立连接:

port_authority_database

这是一个例子:

⁄⁄ OXID Resolver server listens to TCP port 135
⁄⁄ https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/service-overview-and-network-port-requirements
  
RPC_WSTR OXIDResolverStringBinding = 0;
  
RpcStringBindingComposeW(
    NULL,
    RPC_WSTR(L"ncacn_ip_tcp"),
    RPC_WSTR(L"127.0.0.1"),
    RPC_WSTR(L"135"),
    NULL,
    &OXIDResolverStringBinding
);
  
RPC_BINDING_HANDLE OXIDResolverBinding = 0;
  
RpcBindingFromStringBindingW(
    OXIDResolverStringBinding,
    &OXIDResolverBinding
);
  
⁄⁄ Make OXID Resolver authenticate without a password
  
RpcBindingSetOption(OXIDResolverBinding, RPC_C_OPT_BINDING_NONCAUSAL, 1);
  
RPC_SECURITY_QOS securityQualityOfServiceSettings;
securityQualityOfServiceSettings.Version         = 1;
securityQualityOfServiceSettings.Capabilities    = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
securityQualityOfServiceSettings.IdentityTracking  = RPC_C_QOS_IDENTITY_STATIC;
securityQualityOfServiceSettings.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
  
RpcBindingSetAuthInfoExW(
    OXIDResolverBinding,
    RPC_WSTR(L"NT Authority\\NetworkService"),
    RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
    RPC_C_AUTHN_WINNT,
    NULL,
    RPC_C_AUTHZ_NONE,
    &securityQualityOfServiceSettings
);

现在,我们可以使用 OXID Resolver 来获取服务器的字符串绑定。字符串绑定类似于逻辑地址。 

字符串绑定结构

让我们调用 ResolveOxid 方法来获取服务器的字符串绑定:

unsigned short requestedProtocols[] = { TCP_PROTOCOL_ID };
  
LPDUALSTRINGARRAY COMServerStringBindings = NULL;
IPID            remoteUnknownIPID     = GUID_NULL;
DWORD           authHint              = 0;
  
ResolveOxid(
    OXIDResolverBinding,
    &oxid,
    _countof(requestedProtocols),
    requestedProtocols,
    &COMServerStringBindings,
    &remoteUnknownIPID,
    &authHint
);

我们请求了 TCP 地址,但服务器可能不支持任何网络。Microsoft Excel 似乎不使用任何 TCP 连接。在这种情况下,请求应该会导致错误,但不会。要弄清楚为什么没有错误,让我们在 Sysinternals 工具中查看 Process Explorer,看看在执行上述代码后 Microsoft Excel 使用了哪些 TCP/IP 连接:

粘贴图像 0 2

如您所见,Microsoft Excel 有两个开放端口。 

OXID_entry

Object Resolver 使 Microsoft Excel 打开一个 TCP 端口并将该绑定返回给我们。现在可以通过GetTcpTable2函数或 PowerShell 命令轻松获取使用该端口的进程:

Get-NetTCPConnection | where Localport -eq 24043 | select Localport,OwningProcess
  
Localport OwningProcess
--------- -------------
    24043       12144
    24043       12144 

此方法是获取 COM 服务器 PID 的最正确方法。通过从 OXID 解析器获取 PID,我们使用文档化的 API 并在连接远程对象期间遵循与 COM 运行时相同的步骤。

 

从 ALPC 端口获取 COM 服务器 PID 

注意:为简洁起见,省略了错误处理。

前面两种方法都是使用OBJREF接口来查找PID的。另一方面,这种方法采用了一种截然不同的方法,因为它使用高级本地过程调用(ALPC)。COM 使用此过程在同一台机器上的对象之间进行通信。

选择_protocol_sequence

 

通过API Monitor跟踪RpcBindingFromStringBinding函数,我们可以看到客户端从中获取 RPC 绑定的CoMarshalInterface函数内部的字符串绑定:

string_binding_in_comarshall 接口

让我们使用 Sysinternals 工具中的 Process Explorer 查看 COM 服务器的打开句柄:

Opened_handles_of_a_COM_server

我们可以看到 COM 服务器使用与客户端传递给RpcBindingFromStringBinding函数相同的 ALPC 端口。为了找到 COM 服务器的 PID,我们应该:

注意:确保RpcBindingFromStringBinding函数由 COM 运行时调用,而不是由普通的 RPC 调用调用。该RpcBindingFromStringBinding功能作为COM通信实现的元素,如果我们检查的API监控这一功能,我们感兴趣的是COM,我们必须确保它是由COM运行时调用。

此方法最具挑战性的部分是找到一个使用句柄值的进程,因为没有记录的方法可以做到这一点。我们必须探索 ntdll.dll 函数和结构以获取有关句柄和所有正在运行的进程的信息。首先,我们需要获取一些宏和定义,因为大多数 ntdll.dll 常量和结构没有在公共头文件中公开:

#define NT_SUCCESS(x) ((x) >= 0)
  
  
#define STATUS_INFO_LENGTH_MISMATCH   0xc0000004
  
  
#define SystemHandleInformation       16
#define SystemExtendedHandleInformation 0x40
#define ObjectBasicInformation        0
#define ObjectNameInformation         1
#define ObjectTypeInformation         2
  
typedef NTSTATUS(NTAPI *NtQuerySystemInformationType)(
    ULONG SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
);
  
typedef NTSTATUS(NTAPI *NtDuplicateObjectType)(
    HANDLE SourceProcessHandle,
    HANDLE SourceHandle,
    HANDLE TargetProcessHandle,
    PHANDLE TargetHandle,
    ACCESS_MASK DesiredAccess,
    ULONG Attributes,
    ULONG Options
);
  
typedef NTSTATUS(NTAPI *NtQueryObjectType)(
    HANDLE ObjectHandle,
    ULONG ObjectInformationClass,
    PVOID ObjectInformation,
    ULONG ObjectInformationLength,
    PULONG ReturnLength
);
  
typedef NTSTATUS(NTAPI *NtCloseType)(
    HANDLE ObjectHandle
);
  
typedef struct _SYSTEM_HANDLE
{
    ULONG ProcessId;
    BYTE ObjectTypeNumber;
    BYTE Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
  
typedef struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
{
    PVOID Object;
    ULONG_PTR  ProcessId;
    ULONG_PTR  Handle;
    ULONG GrantedAccess;
    USHORT CreatorBackTraceIndex;
    USHORT ObjectTypeIndex;
    ULONG HandleAttributes;
    ULONG Reserved;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX;
  
typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG HandleCount;
    SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
  
typedef struct _SYSTEM_HANDLE_INFORMATION_EX
{
    ULONG HandleCount;
    ULONG reserved;
#ifdef _M_X64
    PVOID reserved2;
#endif
    SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
} _SYSTEM_HANDLE_INFORMATION_EX, *P_SYSTEM_HANDLE_INFORMATION_EX;
  
typedef enum _POOL_TYPE
{
    NonPagedPool,
    PagedPool,
    NonPagedPoolMustSucceed,
    DontUseThisType,
    NonPagedPoolCacheAligned,
    PagedPoolCacheAligned,
    NonPagedPoolCacheAlignedMustS
} POOL_TYPE, *PPOOL_TYPE;
  
typedef struct _OBJECT_TYPE_INFORMATION
{
  UNICODE_STRING Name;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccess;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    USHORT MaintainTypeList;
    POOL_TYPE PoolType;
    ULONG PagedPoolUsage;
    ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; 

要获得 PID,我们需要调用一个函数将所有打开的 ALPC 端口和相应的 PID 打印到标准输出:

#include "ntdefines.h"
  
NtQuerySystemInformationType NtQuerySystemInformation = reinterpret_cast<NtQuerySystemInformationType>(
        GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQuerySystemInformation"));
  
NtDuplicateObjectType NtDuplicateObject = reinterpret_cast<NtDuplicateObjectType>(
        GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtDuplicateObject"));
  
NtQueryObjectType NtQueryObject = reinterpret_cast<NtQueryObjectType>(
        GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryObject"));
  
  
void GetALPCPorts()
{    
    const ULONG     INITIAL_SYSTEM_INFORMATION_LENGTH = 0x10000;
    std::vector<BYTE> infoStorage                     = std::vector<BYTE>(INITIAL_SYSTEM_INFORMATION_LENGTH);
  
    ⁄⁄ NtQuerySystemInformation won't give us the correct buffer size, so we double the buffer size until the operation is successful 
    while (NtQuerySystemInformation(SystemHandleInformation, infoStorage.data(), infoStorage.size(), NULL) == STATUS_INFO_LENGTH_MISMATCH)
    {
        infoStorage.resize(infoStorage.size() * 2);
    }
      
    PSYSTEM_HANDLE_INFORMATION handleInfo = reinterpret_cast <psystem_handle_information>(infoStorage.data());
      
    for(ULONG i = 0; i < handleInfo->HandleCount; ++i)
    {
        ⁄⁄ Skip if the type isn't an ALPC port. Note: This value might be different on other systems. This was tested on 64-bit Windows 10.
        if (handleInfo->Handles[i].ObjectTypeNumber != 0x2e)
        {
            continue;
        }
        
        HANDLE process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, handleInfo->Handles[i].ProcessId);
  
        ⁄⁄ Duplicate the handle so we can query it
        HANDLE dublicatedHandle;
  
        NtDuplicateObject(process, reinterpret_cast<HANDLE>(handleInfo->Handles[i].Handle),
            GetCurrentProcess(), &dublicatedHandle, 0, 0, 0);
  
        CloseHandle(process);
          
        ⁄⁄ Let's suppose it's enough space for storing the name
        std::vector<BYTE> objectName(8192);
  
        ⁄⁄ Try to query the name
        NtQueryObject(dublicatedHandle, ObjectNameInformation,
            objectName.data(), objectName.size(), NULL);
  
        CloseHandle(dublicatedHandle);
  
        POBJECT_NAME_INFORMATION objectNameInformation = reinterpret_cast<pobject_name_information>(objectName.data());
        
        if (objectNameInformation->Name.Buffer != NULL)
        {
            std::wcout << "ALPC Port: " << std::wstring(objectNameInformation->Name.Buffer, objectNameInformation->Name.Length ⁄ sizeof(wchar_t))
                << " PID: " << handleInfo->Handles[i].ProcessId << std::endl;
        }
    }
}
  
  
int main()
{
    GetALPCPorts();
    return 0;
} 

正如我们所见,这是所需的过程。现在我们只需要通过已知的 ALPC 端口名称过滤它:

COM_process_ID_found_through_the_ALPC_port

这种方法最耗时,因为它涉及检查系统中的每个进程和每个句柄的名称。 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值