什么是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的調試擴展,以及您可能發現這些調用被卡住的一些常見狀態。