Short Message LPC Mechanism AnalysisInter-process communication (IPC) is the communication technology used to run programs and processes between multi-task operating systems or networked computers. There are two types of process communication. Local Procedure Call (LPC) and Remote Procedure Call (RPC ). For unknown reasons, Microsoft did not publish LPC interfaces. Microsoft provides a set of RPC for communication between client servers. Windows NT optimized RPC and switched it to LPC, except that the client server is all on the same machine.
This article only discusses the Short-Message LPC communication process. The following flowchart can be referenced:
To save space, we will not list the disassembly code of each function one by one. Let's look at the process directly:
The server calls the ntcreateport () function to create a port. It returns a port handle, and the server uses this handle to call the ntlistenport () function to wait for and accept requests. Any client can send a connection request through this port and obtain a port handle for communication.
Ntcreateport function prototype:
Ntstatus
Ntcreateport (
Out phandle porthandle,
In pobject_attributes objectattributes,
In ulong maxconnectioninfolength,
In ulong maxmessagelength,
In ulong maxpoolusage
)
On the top of the page, ntcreateportwill call the lpcpcreateportlistener and drop ntoskrnl.exe to Ida. You will see:
_ Stdcall ntcreateport (x, x) proc near
Page: 004c04e6 004 push 0
Page: 004c04e8 008 push [EBP + reserved]
Page: 004c04eb 00c push [EBP + maxmessagesize]
Page: 004c04ee 010 push [EBP + maxdatasize]
Page: 004c04f1 014 push [EBP + objectattributes]
Page: 004c04f4 018 push [EBP + porthandle]
Page: 004c04f7 01c call lpcpcreateport (x, x)
_ Stdcall ntcreateport (x, x) endp
The following is a prototype of the lpcpcreateport function:
Ntstatus
Lpcpcreateport (
Out phandle porthandle,
In pobject_attributes objectattributes,
In ulong maxconnectioninfolength,
In ulong maxmessagelength,
In ulong maxpoolusage,
In Boolean waitable
)
The ntlistenport function is as follows:
Ntstatus
Ntlistenport (
In handle porthandle,
Out pport_message connectionrequest
)
It is worth noting that ntreplywaitreceiveport (porthandle, null, null, connectionrequest) is actually called in the implementation of the ntlistenport function. Therefore, ntreplywaitreceiveport can be called directly as a program.
Ntstatus
Ntreplywaitpoliceport (
In handle porthandle,
Out pvoid * portcontext optional,
In pport_message replymessage optional,
Out pport_message receivemessage
)
Before formal programming, it is necessary to introduce the structure of the lpcmessage, which is defined as follows:
Typedef struct lpcmessage {
Word actualmessagelength;
Word totalmessagelength;
DWORD messagetype;
DWORD clientprocessid;
DWORD clientthreadid;
DWORD messageid;
DWORD sharedsectionsize;
Byte messagedata [max_message_data];
} Lpcmessage, * plpc_message;
The official structure provided by M $ is:
Typedef struct _ tlpc_portmsg {
Port_message h;
Ulong data [tlpc_max_msg_data_length];
} Tlpc_portmsg, * ptlpc_portmsg;
The port_message structure defines the first few items in the lpcmessage.
In our programming, replace pport_message with plpc_message.
It is worth noting that messagetype, M $ is defined as follows:
Char * lpcmsgtypes [] = {
"** Invalid **",
"Maid ",
"Maid ",
"Maid ",
"Maid ",
"Maid ",
"Maid ",
"Maid ",
"Maid ",
"Maid ",
"Maid ",
Null
};
The server-related code is as follows:
# Define portname l "\ myport"
Handle porthandle, acceptporthandle;
Ap-southeast-1;
Lsa_object_attributes objectattr;
Unicode_string ustring;
Int RC;
Rtlinitunicodestring (& ustring, portname );
Memset (& objectattr, 0, sizeof (objectattr ));
Objectattr. Length = sizeof (objectattr );
Objectattr. objectname = & ustring;
Rc = ntcreateport (& porthandle, & objectattr, 0x100, 0x0, 0x00000 );
If (RC! = 0 ){//...}
Memset (& lpcmessage, 0, sizeof (lpcmessage ));
While (1 ){
Rc = ntreplywaitpoliceport (porthandle,
Null, null, & lpcmessage );
If (RC! = 0 ){//...}
If (maid request = maid) // 0x000a
{
Rc = ntacceptconnectport (
& Acceptporthandle,
Null,
& Amp; lpcmessage,
True,
Null,
Null );
If (RC! = 0 ){//...}
Rc = ntcompleteconnectport (acceptporthandle );
If (RC! = 0 ){
Closehandle (acceptporthandle );
//...
}
}
}
From the code above, we can see that when the client program ntconnectport () sends a connection request to the server that is waiting, the server uses the ntacceptconnectport () function to accept the request. When the client program receives the message of lpc_connection_request, return a new port handle for the connection requested by the customer, and use the ntcompleteconnectport () function to complete the connection protocol.
Of course, you can also process other messages, such as lpc_request, and call ntreplyport (). However, for the message "lpc_port_closed", the resources occupied by each customer are released. These are described below.
The two functions are prototype:
Ntstatus
Ntacceptconnectport (
Out phandle porthandle,
In pvoid portcontext optional,
In pport_message connectionrequest,
In Boolean acceptconnection,
In out pport_view serverview optional,
Out premote_port_view clientview optional
)
Ntstatus
Ntcompleteconnectport (
In handle porthandle
)
Because the client is relatively simple, we only look at the prototype of ntconnectport:
Ntsysapi
Ntstatus
Ntapi
Ntconnectport (
Out phandle porthandle,
In punicode_string portname,
In psecurity_quality_of_service securityqos, //-> winnt. h
In out pport_view clientview optional,
In out premote_port_view serverview optional,
Out Pulong maxmessagelength optional,
In out pvoid connectioninformation optional,
In out Pulong connectioninformationlength optional
)
The simple implementation code can be written as follows:
Rc = zwconnectport (& porthandle, & ustring,
(Psecurity_quality_of_service) & securityqos, // static int
0, 0, 0, connectdatabuffer, (unsigned long *) & size );
/*
Rc = ntrequestport (porthandle, & lpcmessage );
Rc = ntrequestwaitreplyport (porthandle,
& Amp; lpcmessage,
& Amp; lpcmessage );
* /// Something else...
After the handshake is complete, you can start communication through this port. The customer uses the ntrequestport () function to send requests to the subsystem. If you want a response to the request, you can use the ntrequestwaitreplyport () function to send the request and wait for the response. The server uses the ntreplywaitreceive () function to receive the requested message and uses the ntreplyport () function to send the reply message. Of course, you can also use the combined function ntreplywaitpoliceport () to complete both steps. Here, the ntreplywaitpoliceport function is used to receive post-processing messages, for example:
Rc = ntreplywaitpoliceport (porthandle,
Null,
Null,
& Amp; lpcmessage );
If (RC! = 0 ){//...}
Switch (lpcmessage. messagetype ){
Case lpc_connection_request:
Rc = ntacceptconnectport (//...)
//...
Break;
Case lpc_request:
Rc = ntreplyport (//...)
//...
Break;
Default:
Break;
The related function declaration is as follows:
Ntstatus
Ntrequestport (
In handle porthandle,
In pport_message requestmessage
)
Ntstatus
Ntrequestwaitreplyport (
In handle porthandle,
In pport_message requestmessage,
Out pport_message replymessage
)
Ntstatus
Ntreplywaitpoliceport (
In handle porthandle,
Out pvoid * portcontext optional,
In pport_message replymessage optional,
Out pport_message receivemessage
)
For Shared-section LPC communication, there are several differences:
1. You can use the createfilemapping function to create a shared section and specify the size of the Section. The server obtains the size of this section during connection.
2. When calling functions such as ntconnectport and ntacceptconnectport, you need to fill in the corresponding pport_view clientview and premote_port_view serverview. For the ntrequestwaitreplyport function of the client, you need to fill in the lpcmessage structure when sending.
References:
1. <unreceivented Windows NT> -- Prasad Dabak/Dong Yan
2. <A Study on Windows 2000 kernel exploit> -- ey4s
3.ntoskrnl.exe, NTDLL. dll