LPC介绍
https://www.jiwo.org/ken/detail.php?id=2858
尝试使用Process Monitor查看此服务进程的属性,发现此服务提供了一个ALPC端口:\WindowsErrorReportingServicePort
https://www.hexacorn.com/blog/2019/09/19/silentprocessexit-quick-look-under-the-hood有提到这个端口。
!alpc /lpp ffffcb04f56130c0 (eprocess)
一目了然,我们能看到此服务正启动了ALPC服务并且还有些进程连接了这个端口。
LPC介绍
LPC(本地过程调用)是用于通信的Microsoft Windows内核组件进程之间(IPC)。这个没有文档记录的接口是在Windows后台使用的,大多数系统组件使用LPC接口与较低级别的安全通信,服务列表仅通过此通道可用。
例如:[出自:jiwo.org]
CSRSS使用LPC端口管理线程和进程,RPC接口使用LPC传输进行本地通信,OLE通信基于LPC生成的端口。
该通信组件对于Windows体系结构非常重要。 可以看成具有权限级别支持的本地套接字。 作为Windows内核的内部组件,它是
微软没有文档记录的。
LPC 可用于两个用户模式过程、用户模式过程和内核模式驱动程序之间或两个内核模式驱动程序之间的通信。
LPC端口是Windows对象,进程可以创建命名的LPC端口,其他端口可以连接到这些端口,进程可以通过引用其名称进行连接。
可从使用Process Explorer查看LPC端口,几乎每个Windows进程都有一个LPC端口, LPC端口可以受ACL保护,共享段可用于LPC连接。
LPC 在客户端和服务器流程之间强制执行同步通信模型。Vista 使用一种称为异步本地过程间通信 (ALPC) 的新机制来弃用 LPC。ALPC 比 LPC 具有固有的优势,因为从客户端到服务器的所有呼叫都是异步的,即客户端不需要阻止/等待服务器响应消息。在 Vista 中,对 LPC ABI 的旧应用调用会自动重定向到较新的 ALPC ABI。
LPC API是本机API,即它们由NTDLL.dll以用户模式导出,由NTOSKRNL.exe以内核模式导出。 LPC API未公开,因此Win32应用程序不能直接使用LPC工具。 但是,在使用RPC时,Win32应用程序可以通过协议序列“ ncalrpc”指定LPC作为其基础传输,从而间接使用LPC。 所有LPC API均以单词“ Port”结尾,这表示LPC通信端点。
NtCreatePort 服务器用来创建连接端口 NtConnectPort 客户端用于连接到连接端口 NtListenPort 服务器用来在连接端口上侦听连接请求。 NtAcceptConnectPort 服务器用于在连接端口上接受连接请求 NtCompleteConnectPort 服务器用来完成对连接请求的接受 NtRequestPort 用于发送没有回复的数据报消息 NtRequestWaitReplyPort 用于发送消息并等待回复 NtReplyPort 用于发送对特定消息的回复 NtReplyWaitReplyPort 用于发送对特定消息的答复并等待对上一条消息的答复 NtReplyWaitReceivePort 服务器使用它向客户端发送回复,并等待从客户端接收消息 NtImpersonateClientOfPort 服务器线程用于临时借用客户端线程的安全上下文
下图说明了LPC服务器进程侦听来自潜在客户端的入站连接请求所采取的步骤,以及客户端连接到侦听服务器所采取的步骤。
注意:许多服务器进程使用NtReplyWaitReceivePort()API而不是NtListenPort()。 NtListenPort()丢弃除连接请求之外的所有LPC消息。 因此,NtListenPort()只能用于第一个连接。 对于以后的连接请求,使用NtReplyWaitReceivePort()。
下图说明了LPC客户端向已建立连接的LPC服务器发送请求所采取的步骤,以及服务器响应消息所采取的步骤。
LPC端点称为端口。 LPC实现使用相同的端口结构来表示各种类型的端口。 LPC使用的端口是服务器连接端口,它们是由服务器进程创建的端口,用于接受来自客户端的传入连接。 由客户端进程创建以连接到服务器进程的客户端通信端口,以及由服务器进程创建的服务器通信端口。
LPCP_PORT_OBJECT是LPC用来表示LPC端口的内部数据结构。 LPCP_PORT_OBJECTs被分配到带有标签“PORT”的页面缓冲池之外。
kd> dt nt!_LPCP_PORT_OBJECT +0x000 ConnectionPort : Ptr32 _LPCP_PORT_OBJECT +0x004 ConnectedPort : Ptr32 _LPCP_PORT_OBJECT +0x008 MsgQueue : _LPCP_PORT_QUEUE +0x018 Creator : _CLIENT_ID +0x020 ClientSectionBase : Ptr32 Void +0x024 ServerSectionBase : Ptr32 Void +0x028 PortContext : Ptr32 Void +0x02c ClientThread : Ptr32 _ETHREAD +0x030 SecurityQos : _SECURITY_QUALITY_OF_SERVICE +0x03c StaticSecurity : _SECURITY_CLIENT_CONTEXT +0x078 LpcReplyChainHead : _LIST_ENTRY +0x080 LpcDataInfoChainHead : _LIST_ENTRY +0x088 ServerProcess : Ptr32 _EPROCESS +0x088 MappingProcess : Ptr32 _EPROCESS +0x08c MaxMessageLength : Uint2B +0x08e MaxConnectionInfoLength : Uint2B +0x090 Flags : Uint4B +0x094 WaitEvent : _KEVENT
ConnectedPort
指向服务器通讯端口
ConnectionPort
指向服务器连接端口
MsgQueue.Semaphore
用于向服务器线程发送有关MsgQueue.ReceiveHead中消息存在的信号。
MsgQueue.ReceiveHead
双向链表的头,包含所有等待服务器出队的消息。
MsgQueue.NonPagedPortQueue
指向用于客户端通信端口的LPCP_NONPAGED_PORT_QUEUE结构,以跟踪丢失的答复。
LpcReplyChainHead
双向链表的头,包含所有正在等待答复发送到此端口的消息的线程。
Flags 标志位: #define SERVER CONNECTION_PORT0x00000001 #define UNCONNECTED COMMUNICATION PORT 0x00000002 #define SERVER COMMUNICATION _PORT Bx00000003 #define CLIENT_COMMUNICATION_PORT 0x00000004 #define PORT_WAITABLE0x20000000 #define PORT NAME DELETEDBx40000000 #define PORT DYNAMIC SECURITY0x80000000
LPC 使用两种不同的机制在客户端和服务器进程之间传递数据。它使用 LPC 消息缓冲区(对于小于 304 字节的数据大小),或者使用映射到客户端和服务器地址空间的共享内存部分(用于数据大小超过 304 字节),32位上Msg最大328(包括消息头24),64位上Msg最大648(包括消息头40) )
客户端和服务器之间的所有交互都使用消息结构。 连接期间程序将消息发送到服务器。 它认为此消息与其他消息一样,只是该消息类型字段指定它是客户端连接请求。
此设计实现允许服务器在单个线程上处理所有请求。即使它减少了关键LPC端口上的应答时间,每个线程仍然可以使用经典的一个连接。
如果不查看消息的结构和类型,就无法理解LPC。
LPC消息是将信息从LPC客户端传送到LPC服务器的数据结构,并且可以是各种类型,例如连接,请求,关闭等。
消息类型 typedef enum _LPC_MSG_TYPE { LPC_NEW_MSG, LPC_REQUEST, LPC_REPLY, LPC_DATAGRAM, LPC_LOST_REPLY, LPC_PORT_CLOSED, LPC_CLIENT_DIED, LPC_EXCEPTION, LPC_DEBUG_EVENT, LPC_ERROR_EVENT, LPC_CONN_REQ, } LPC_MSG_TYPE; 消息结构 struct _PORT_MESSAGE { union { struct { SHORT DataLength; //0x0 SHORT TotalLength; //0x2 } s1; //0x0 ULONG Length; //0x0 } u1; //0x0 union { struct { SHORT Type; //0x4 SHORT DataInfoOffset; //0x6 } s2; //0x4 ULONG ZeroInit; //0x4 } u2; //0x4 union { struct _CLIENT_ID ClientId; //0x8 double DoNotUseThisField; //0x8 }; ULONG MessageId; //0x18 union { ULONGLONG ClientViewSize; //0x20 ULONG CallbackId; //0x20 }; }; 在此结构中,仅使用前三个字段。 ClientId字段指示已处理呼叫者
并穿线; 现在,该信息由内核函数填充。 这个领域负责
报告了欺骗错误。 当用户发送正常消息时,自定义数据将存储在
在DataSize字段中指定的LPC_MESSAGE结构和数据大小。 自定义数据取决于服务器
请求格式为套接字上的数据。 TotalSize字段是DataSize和LPC_MESSAGE的总和
Page 4 of 26 2008年6月2日
结构尺寸(0x18)。 MsgType字段通常是LPC_NEW_MSG,LPC_REQUEST或LPC_REPLY。
根据功能,错误的消息类型将得到纠正或返回错误。
kd> dt nt!_LPCP_MESSAGE +0x000 Entry : _LIST_ENTRY +0x000 FreeEntry : _SINGLE_LIST_ENTRY +0x004 Reserved0 : Uint4B +0x008 SenderPort : Ptr32 Void +0x00c RepliedToThread : Ptr32 _ETHREAD +0x010 PortContext : Ptr32 Void +0x018 Request : _PORT_MESSAGE
Request.MessageId
从全局的LpcpNextMessageId的值生成。 用于唯一地标识一条消息。
SenderPort
指向客户端通信端口的LPCP_PORT_OBJECT
Entry
是用于将消息排队到服务器通信/连接端口的MsgQueue.ReceiveHead的列表条目。
Request
是作为对NtRequestWaitReplyPort()的调用的Request参数提供的消息缓冲区的副本,或者是作为对NtReplyWaitRecivePortEx()的Reply参数提供
的消息缓冲区的副本。
其中 PORT_MESSAGE 是对用户端可见的报文,LPCP_MESSAGE 是内核使用的实际报文格式。
LPCP_MESSAGE结构是从系统lookaside 列表中分配的,标签为“ LpcM”。