什么是LPC
LPC(Local-Process-communicationandnotlocalprocedure-Calls)是一种在NT内核中实现的基于消息的高速通信机制。LPC可用于两个用户模式进程之间、用户模式进程与内核模式驱动程序之间或两个内核模式驱动程序之间的通信。一个例子是通过LPC通信的两个用户模式进程。像CSRSS.exe与SMSS.exe通信,在创建登录会话或任何与LSASS.exe。出于安全原因,通过lsa认证端口。用户模式进程与内核模式驱动程序通信的另一个例子是 KSecDD.sys司与LSASS.exe通信用于在读/写加密文件期间对EFS密钥进行加密和解密。
LPC使用两种不同的机制在客户端和服务器进程之间传递数据。它使用LPC消息缓冲区(对于小于304字节的数据),或者使用映射到客户机和服务器地址空间的共享内存部分(对于大于304字节的数据)。
除了用作在同一系统上运行的进程之间选择远程过程调用的协议外,LPC还用于整个系统,例如用于Win32应用程序与CSRSS.exe文件,安全参考监视器与LSASS的通信,WinLogon与LSASS的通信等。
LPC强制客户机进程和服务器进程之间的同步通信模型。Vista反对使用一种称为异步本地进程间通信(ALPC)的新机制来使用LPC。与LPC相比,ALPC有一个固有的优势,即从客户端到服务器的所有调用都是异步的,即客户端不需要阻塞/等待服务器响应消息。在Vista中,对lpcapis的旧应用程序调用会自动重定向到更新的alpcapi。
LPC APIs
LPC api是本机api,即它们在用户模式下通过NTDLL.dll在内核模式下NTOSKRNL.exe文件. lpcapis没有在Win32级别公开,因此Win32应用程序不能直接使用LPC工具。然而,在使用RPC时,Win32应用程序可以通过协议序列“ncalrpc”将LPC指定为其底层传输,从而间接地使用LPC。所有lpcapis都以单词“Port”结尾,这意味着LPC通信端点。
API |
Description |
NtCreatePort |
Used by server to create a connection port |
NtConnectPort |
Used by client to connect to a connection port |
NtListenPort |
Used by server to listen for connection requests on the connection port. |
NtAcceptConnectPort |
Used by server to accept connection requests on the connection port |
NtCompleteConnectPort |
Used by server to complete the acceptance of a connection request |
NtRequestPort |
Used to send a datagram message that does not have a reply |
NtRequestWaitReplyPort |
Used to send a message and wait for a reply |
NtReplyPort |
Used to send a reply to a particular message |
NtReplyWaitReplyPort |
Used to send a reply to a particular message and wait for a reply to a previous message |
NtReplyWaitReceivePort |
Used by server to send a reply to the client and wait to receive a message from the client |
NtImpersonateClientOfPort |
Used by server thread to temporarily borrow the security context of a client thread |
下图说明了LPC服务器进程侦听来自潜在客户端的连接请求所采取的步骤,以及客户端连接到侦听服务器所采取的步骤。
LPC客户机-服务器连接建立顺序
注意:许多服务器进程使用NtReplyWaitReceivePort()API而不是NtListenPort()。NtListenPort()除去连接请求之外的所有LPC消息。因此NtListenPort()只能用于第一个连接。对于以后的连接请求,使用NtReplyWaitReceivePort()。
下图说明了LPC客户机向已建立连接的LPC服务器发送请求所采取的步骤,以及服务器响应消息所采取的步骤。
客户端-服务器数据传输序列
LPC Data Structures
LPC Port Data Structure
LPC Port 被称为端口。LPC实现使用相同的端口结构来表示各种类型的端口。LPC使用的端口是服务器连接端口,这些端口是由服务器进程创建的用于接受来自客户端的传入连接的命名端口。客户机通信端口由客户机进程创建以连接到服务器进程和服务器进程创建的服务器通信端口。
LPC端口类型及其关系
LPCP_PORT_OBJECT是LPC用来表示LPC端口的内部数据结构。LPCP_PORT_对象是从带有标记“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
Field |
Description |
ConnectedPort |
Points to the Server Communication Port |
ConnectionPort |
Points to the Server Connection Port |
MsgQueue.Semaphore |
Used to signal the server thread about the presence of a message in MsgQueue.ReceiveHead |
MsgQueue.ReceiveHead |
Head of a doubly linked list containing all the messages that are waiting to be dequeued by the server. |
MsgQueue.NonPagedPortQueue |
Points to the LPCP_NONPAGED_PORT_QUEUE structure for the client communication port for tracking lost replies. |
LpcReplyChainHead |
Head of a doubly linked list containing all the threads that are waiting for replies to messages sent to this port. |
LPC Message Data Structure
LPC消息是将信息从LPC客户机传送到LPC服务器的数据结构,可以是各种类型的,如连接、请求、关闭等。
LPCP_MESSAGE是LPC用来表示消息的内部数据结构。LPCP_MESSAGE结构是从带有标记“LpcM”的系统范围的lookaside列表中分配的。
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
Field |
Description |
Request.MessageId |
Generated from the value of a global epoch LpcpNextMessageId. Used to uniquely identify a message. |
SenderPort |
Points to the LPCP_PORT_OBJECT of the client communication port |
Entry |
Is the list entry that is used to queue the message to the Server Communication/Connection Port’s MsgQueue.ReceiveHead |
Request |
Is a copy of the message buffer that was provided as the Request parameter to the call to NtRequestWaitReplyPort() or a copy the message buffer that was provided as the Reply parameter to NtReplyWaitRecivePortEx(). |
LPC related fields in Thread Data Structure
kd> dt nt!_ETHREAD -y Lpc +0x1c8 LpcReplyChain : _LIST_ENTRY +0x1f4 LpcReplySemaphore : _KSEMAPHORE +0x208 LpcReplyMessage : Ptr32 Void +0x208 LpcWaitingOnPort : Ptr32 Void +0x228 LpcReceivedMessageId : Uint4B +0x23c LpcReplyMessageId : Uint4B +0x250 LpcReceivedMsgIdValid : Pos 0, 1 Bit +0x250 LpcExitThreadCalled : Pos 1, 1 Bit
下表描述了用于LPC客户端和服务器进程之间通信的ETHREAD数据结构的字段。
Field |
Description |
LpcReplyMessageId |
Contains the ID of the message sent to the server while the client thread is waiting on the response. |
LpcWaitingOnPort |
Used by the LPC Client Thread to stores the LPC client communication port on which it is waiting for a reply. |
LpcReplySemaphore |
Used to block the LPC Client Thread while the server is processing the LPC message. |
LpcExitThreadCalled |
Used by the Lpc APIs to determine if the thread is currently in the process of exiting and if so returns STATUS_THREAD_IS_TERMINATING from the API call. |
LpcReplyChain |
Used to queue the thread to the list of threads waiting for replies from the server communication/connection port. The list head of this list is in LPCP_PORT_OBJECT->LpcReplyChainHead. |
Important global LPC connection ports
Field |
Description |
LsaAuthenticationPort |
Used for the following tasks:
|
SmssWinStationApiPort |
This port is used by the CSRSS to manage Window Stations i.e. multiple sessions. |
ErrorLogPort |
This port is used by IO manager and other components to send error log entries to the EventLog service. |
Important LPC port fields in the Process
(nt!_EPROCESS)
Field |
Description |
SecurityPort |
LSA uses this port for EFS (encrypted file system) and authentication. |
ExceptionPort |
When a thread does not catch an exception an LPC_EXCEPTIONmessage is sent to this port. A subsystem might register an LPC port in this field to receive second chance exception information of process running in the subsystem. Default action of CSRSS is to terminate the process. |
DebugPort |
The kernel dispatches user mode exceptions to the process debugger on this port. Debug outputs using OutputDebugString( ) are passed to this port as DBG_PRINTEXCEPTION_C. |
Important LPC port fields in the Thread
(nt!_ETHREAD)
Field |
Description |
TerminationPort |
This is not a single port rather a chain of ports registered with the Process Manager. The process manager notifies all ports when the thread terminates. |
LpcWaitingOnPort |
This field has the address of the client communication port when the client thread waits for a reply to a LPC message from a server thread. |
请继续关注……在下一篇文章中,我们将卷起袖子,深入研究LPC的调试扩展,以及您可能发现这些调用被卡住的一些常见状态。