添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
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消息数据结构
消息类型 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 200862日
结构尺寸(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 是内核使用的实际报文格式。
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
LpcReplyMessageId
包含在客户端线程等待响应时发送到服务器的消息的ID。
LpcWaitingOnPort
LPC客户端线程用于存储它正在等待答复的LPC客户端通信端口。
LpcReplySemaphore
在服务器处理LPC消息时,用于阻止LPC客户端线程。
LpcExitThreadCalled
Lpc API用来确定线程当前是否正在退出,如果退出,则从API调用返回STATUS_THREAD_IS_TERMINATING。
LpcReplyChain
用于将线程排队到等待来自服务器通信/连接端口的答复的线程列表。 该列表的列表头位于LPCP_PORT_OBJECT-> LpcReplyChainHead中。
重要的LPC连接端口 当我寻找wersvc.dll的漏洞函数接口时,我第一时间想到的是RPC接口(本机RPC调用),之后使用RPC view查看此服务提供的接口,结果出乎预料的是此服务没有提供任何RPC接口函数。
接着我又想到了COM,于是我又使用OleViewDotNet发现还是此服务也未提供任何COM接口,如下图所示,没有COM接口的进程以及ID不会显示在图中。
后来尝试使用Process Monitor查看此服务进程的属性,发现此服务提供了一个ALPC端口:\WindowsErrorReportingServicePort
https://www.hexacorn.com/blog/2019/09/19/silentprocessexit-quick-look-under-the-hood有提到这个端口。
!alpc /lpp ffffcb04f56130c0  (eprocess)
if ( *((__int16 *)a2 + 2) < 0 && *(_DWORD *)a2 == 0x11000E8 ) return 0i64; v8 = *((_DWORD *)a2 + 10); if ( v8 != 0x10000000 && v8 != 268435462 && v8 != 0x20000000 ) { if ( v8 == 2 ) { v9 = *((_DWORD *)a2 + 2); if ( v9 != GetCurrentProcessId() ) return 2147942405i64; } else if ( v8 != 0x50000000 && v8 != 0x60000000 && v8 != 0x80000000 && v8 != 0x90000000 && v8 != 0x30000000 && v8 != 0x40000000 && v8 != 0x10000007 && v8 != 0xB0000000 && (unsigned int)(v8 + 1073741822) > 2 && v8 != 0xD0000002 && v8 != 0xE0000002 && v8 != 0xF0000002 && v8 != 0xF0010002 && v8 != 0xF0020002 && v8 != 0xF0030002 && v8 != 0xF0040002 && v8 != 0xF0050002 && v8 != 0xF0060002 ) { return 0xD000001Ci64; } } return 0i64; 
当CWerService::CheckIfValidPortMessage返回0时,后面会创建一个线程池,设置回调函数为CWerService::StaticDispatchPortRequestWorkItem,并将消息缓冲区传递给回调函数。
CWerService::StaticDispatchPortRequestWorkItem在确认缓冲区后会直接调用CWerService::DispatchPortRequestWorkItem,继续将消息缓冲区传递。
接着进入CWerService::DispatchPortRequestWorkItem,可以很清楚的看到报告中所在的漏洞函数,由此可知,调用CWerService::SvcCollectMemoryInfo需要将消息缓冲区的自定义数据头4个字节设置为0xF0030002,且消息长度不能超过1400. 如果未设置NtAlpcConnectPort的第四个参数ALPC_PORT_ATTRIBUTES中的MaxMessageLength字段,则NtAlpcSendWaitReceivePort发送的消息的长度不能超过0x200,并且将返回0xC000002F 如果发送长度超过服务器指定的最大长度(NtAlpcSendWaitReceivePort是底部的第三个参数),则将获得0xC0000707 错误 CreateFile将不受磁盘存储空间的影响。 WriteFile会。