main(int argc, char **argv)
{
OBJECT_ATTRIBUTES ObjectAttr;
UNICODE_STRING uString;
int rc;
/* Initializes the object attribute structure */
memset(&ObjectAttr, 0, sizeof(ObjectAttr));
ObjectAttr.Length = sizeof(ObjectAttr);
RtlInitUnicodeString(&uString, PORTNAME);
ObjectAttr.ObjectName = &uString;
if (argc == 1) {
/* If no parameters are specified for the
* program, act as the server
*/
rc = server(&ObjectAttr);
} else {
/* If any command line parameter is specified
* it acts as the client
*/
rc = client(&uString);
}
return rc;
}
The main() function simply represents the control function that calls either the server part or the client part depending on whether the user specifies a parameter. Before passing on the control to one of these functions, the main() function initializes a UNICODE_STRING and an OBJECT_ATTRIBUTES structure with the port name. These pass as parameters to the server() and client() functions.
Apart from the PORT.C file, the sample program contains a PRINT.C file and a PRINT.H file. The PRINT.C file contains utility routines to print the LPCMESSAGE structure, and the PRINT.H file contains the prototypes for these functions.
Shared Section LPC Sample
The following program demonstrates the LPC using shared memory. The program resembles the one demonstrating short message LPC, except that it uses a shared memory to pass parameters and get results to/from the server. This sample program uses the same PORT.H file, used by the short message LPC sample. The SSLPC.C file in this sample program replaces the PORT.C file from the earlier sample program. The SSLPC.C file contains the server code as well as the client code.
Similar to the short message LPC sample, the same program works as the server as well as the client depending on whether a parameter is passed while invoking the program. You should start the program in the server mode first and when the server is ready, start the same program in client mode from another command window. The client creates a shared section for passing parameters and receiving results. The client then establishes communication with the server and asks for a string sent to the server as the parameter. The client copies the string to the shared section and sends a message to the server. Upon receiving the message, the server reverses the string in the shared section and sends a reply. The client prints the reversed string after receiving the reply. The server permits you to start multiple client sessions simultaneously.
Listing 8-2: SSLPC.C
#include <windows.h>
#include <stdio.h>
#include undocnt .h"
#include "..\port\print.h"
#define SHARED_SECTION_SIZE 0x10000
typedef struct SharedLpcMessage {
DWORD ServerBaseAddress;
DWORD MessageOffset;
} SHAREDLPCMESSAGE, *PSHAREDLPCMESSAGE;
This initial portion of the file contains, apart from the required include directives, a couple of important definitions. The client creates the section and therefore determines the size of the shared section. The server is intimated about the size of the section at the time of connection. The operating system sets the SharedSectionSize field, in the LPC message, to the size of the shared section when it passes a LPC_CONNECTION_REQUEST message to the server. The server might choose to reject the connection request if it disagrees with the section size chosen by the client. For example, the section size might prove too small for the replies from the server.
The section size definition is followed by the definition for the message that the client sends over the port when the client wants to invoke some service from the server. As described earlier, the actual parameters pass via the shared section; the message simply indicates to the server that the client wants to invoke some service. In this sample program, we choose to pass the port message containing the server-side base address of the shared section and the offset of the copied parameters within the shared section. The server, in this sample program, does not keep track of the shared section information for the connected clients. (Remember that the server is informed of the details of the shared section when it accepts the connection request via NtAcceptConnectPort().) The server depends solely on the shared-section information passed by the client with every LPC request. In a nondevelopment environment, with unreliable clients, the server should either maintain the track of the shared-section information itself or verify the information sent by the client.
/* Extract the message string from the shared section
* and reverse it
*/
void ProcessMessageData(PLPCMESSAGE pLpcMessage)
{
PSHAREDLPCMESSAGE SharedLpcMessage;
char *ServerView;
SharedLpcMessage =
(PSHAREDLPCMESSAGE)(pLpcMessage->MessageData);
ServerView =
((char *)SharedLpcMessage->ServerBaseAddress)+
SharedLpcMessage->MessageOffset;
strrev(ServerView);
}
The ProcessMessageData() function resembles that in the short message communication sample, except that it operates on the shared section instead of the data passed in the LPC message. As described earlier, the client sends LPC requests, containing the server-side base address of the shared section and the offset of the copied parameters within the shared section. The ProcessMessageData() function retrieves this information from the LPC message and calculates the memory address, where the client copied the parameter string. The function reverses this string, and the client sees the reversed string when the client receives the reply from the server.
BOOL
ProcessConnectionRequest(
PLPCMESSAGE LpcMessage,
PHANDLE pAcceptPortHandle)
{
LPCSECTIONMAPINFO mapInfo;
HANDLE AcceptPortHandle;
PrintMessage(LpcMessage);
/* If you get the connection request, accept and
* complete the request
*/
memset(&mapInfo, 0, sizeof(mapInfo));
mapInfo.Length=0x0C;
rc = NtAcceptConnectPort(
&AcceptPortHandle,
0,
LpcMessage,
1,
0,
&mapInfo);
if (rc != 0) {
printf("NtAcceptConnectPort failed rc=%x\n", rc);
return FALSE;
}
printf("AcceptPortHandle=%x\n", AcceptPortHandle);
printf("mapInfo.SectionSize=%x\n",
mapInfo.SectionSize);
printf("mapInfo.ServerBaseAddress=%x",
mapInfo.ServerBaseAddress);
rc = NtCompleteConnectPort(AcceptPortHandle);
if (rc != 0) {
printf("NtCompleteConnectPort failed, rc=%x\n",
rc);
return FALSE;
}
*pAcceptPortHandle = AcceptPortHandle;
return TRUE;
}
The ProcessConnectionRequest() here also resembles the one in the shared section LPC sample. The only difference between the two functions is in the value they pass for the mapInfo parameter to NtAcceptConnectPort(). If the server passes a non-NULL value for the mapInfo parameter and the client has not sent the shared section information with the connection request, the call fails. Therefore, the ProcessConnectionRequest() function, in the shared section LPC sample, passes NULL as the mapInfo parameter. Here, the ProcessConnectionRequest() function passes a pointer to the LPCSECTIONMAPINFO structure, where it receives the information about the shared section for use in parameter passing. The sample program does not use this information. A real server might keep track of the shared-section information per client; for example, it can maintain a hash table indexed by the client thread ID. The server can later retrieve the shared-section information from the hash table whenever it receives a LPC request. In this sample program, the client sends the shared section information, with every LPC request, as a part of the message sent over the port.
int server(OBJECT_ATTRIBUTES *ObjectAttr)
{
HANDLE PortHandle;
int rc;
LPCMESSAGE LpcMessage;
HANDLE AcceptPortHandle;
BOOL FirstTime=TRUE;
/* Create the named port */
rc = NtCreatePort(&PortHandle, ObjectAttr, 0x0,
0x0, 0x00000);
if (rc != 0) {
printf("NtCreatePort failed, rc=%x\n", rc);
return -1;
}
printf("Port created, PortHandle=%d\n", PortHandle);
memset(&LpcMessage, 0, sizeof(LpcMessage));
while (1) {
if ((FirstTime) ||
(LpcMessage.MessageType != LPC_REQUEST)) {
/* If this is the first message or if the
* previous message was not a LPC request, then
* do not send any reply but just wait on the
* message.
*/
rc = NtReplyWaitReceivePort(
PortHandle,
NULL,
NULL,
&LpcMessage);
FirstTime=FALSE;
} else {
/* Send a reply to the previous message and wait
* for the new message.
*/
printf("Sending reply and Waiting for"
" the request....\n");
rc = NtReplyWaitReceivePort(
PortHandle,
0,
&LpcMessage,
&LpcMessage);
if (rc != 0) {
printf("NtReplyWaitReceivePort"
" failed, rc=%x\n", rc);
return -1;
}
}
if (LpcMessage.MessageType ==
LPC_CONNECTION_REQUEST) {
printf("Got the connection request\n");
ProcessConnectionRequest(&LpcMessage,
pAcceptPortHandle)
} else if (LpcMessage.MessageType == LPC_REQUEST) {
/* Process the message received and send the reply
* in the next iteration of the while loop.
*/
printf("Got the request\n");
PrintMessage(&LpcMessage);
ProcessMessageData(&LpcMessage);
}
}
return 0;
}
The server() function implements the server-side functionality of the sample program. It starts by creating a port object. After successful creation of the port, the function goes in a receive request – process request – send reply loop. The server continues in the loop until you terminate it by pressing Ctrl+C or with the help of the Task Manager.
The server() function receives a new request and replies to the previous request using a single call to the NtReplyWaitReceive() function. A reply needs to be sent only if the previous request is of type LPC_REQUEST. Hence, the function calls the NtReplyWaitReceive() function with a NULL pLpcMessageOut parameter when it receives the first request or the previous request is not of type LPC_REQUEST. Otherwise, the message received from the client sends as the pLpcMessageOut parameter. In both cases, upon return from the NtReplyWaitReceive() function, the LpcMessage structure contains the next request sent by the client.
The server handles only the LPC_REQUEST and LPC_CONNECTION_REQUEST type messages; other messages are ignored. For LPC_CONNECTION_REQUEST messages, the server establishes a communication channel with the client by calling the ProcessConnectionRequest() function. For LPC_REQUEST messages, the server prints the message and calls the ProcessMessageData() function that reverses the string that passes as a parameter in the shared section. The reply to the LPC_REQUEST message is sent by a call to the NtReplyWaitReceivePort() function in the next iteration.
int client(UNICODE_STRING *uString)
{
static int Param3;
HANDLE hFileMapping;
LPCSECTIONINFO sectionInfo;
LPCSECTIONMAPINFO mapInfo;
DWORD ServerBaseAddress;
DWORD ClientBaseAddress;
char *ClientView;
HANDLE PortHandle;
int rc;
LPCMESSAGE LpcMessage;
hFileMapping = CreateFileMapping(
(HANDLE)0xFFFFFFFF,
NULL,
PAGE_READWRITE,
0,
SHARED_SECTION_SIZE,
NULL);
if (hFileMapping == NULL) {
printf("Unable to create file mapping\n");
return -1;
}
memset(§ionInfo, 0, sizeof(sectionInfo));
memset(&mapInfo, 0, sizeof(mapInfo));
sectionInfo.Length = 0x18;
sectionInfo.SectionHandle = hFileMapping;
sectionInfo.SectionSize = SHARED_SECTION_SIZE;
mapInfo.Length = 0x0C;
printf("ClientProcessId=%x, ClientThreadId=%x\n",
GetCurrentProcessId(),
GetCurrentThreadId());
rc = NtConnectPort(
&PortHandle,
uString,
&Param3,
§ionInfo,
&mapInfo,
NULL,
NULL,
NULL);
if (rc != 0) {
printf("Connect failed, rc=%x %d\n", rc);
return -1;
}
printf("PortHandle=%x\n", PortHandle);
printf("Client Base address=%x\n",
sectionInfo.ClientBaseAddress);
printf("Server Base address=%x\n",
sectionInfo.ServerBaseAddress);
ServerBaseAddress =
sectionInfo.ServerBaseAddress;
ClientBaseAddress =
sectionInfo.ClientBaseAddress;
while (1) {
static char MessageString[SHARED_SECTION_SIZE];
int MessageOffset = 0;
PSHAREDLPCMESSAGE SharedLpcMessage;
printf("Enter the message string, enter "
"quit to exit : ");
gets(MessageString);
if (stricmp(MessageString, "quit") == 0) {
CloseHandle(PortHandle);
return 0;
}
fflush(stdin);
printf("Enter the offset in shared memory "
"where the message is to be kept : ");
scanf("%d", &MessageOffset);
if ((MessageOffset+strlen(MessageString)) >=
SHARED_SECTION_SIZE) {
printf("Message cannot fit in shared "
"memory window\n");
return -1;
}
/* Fill in the message */
memset(&LpcMessage, 0, sizeof(LpcMessage));
LpcMessage.ActualMessageLength=0x08;
LpcMessage.TotalMessageLength=0x20;
SharedLpcMessage =
(PSHAREDLPCMESSAGE)(LpcMessage.MessageData);
printf("Server base address=%x\n",
ServerBaseAddress);
SharedLpcMessage->ServerBaseAddress =
ServerBaseAddress;
SharedLpcMessage->MessageOffset =
MessageOffset;
ClientView = ((char *)ClientBaseAddress) +
MessageOffset;
strcpy(ClientView, MessageString);
/* Send the message and wait for the reply */
printf("Sending request and waiting for "
"reply....\n");
rc=NtRequestWaitReplyPort(
PortHandle,
&LpcMessage,
&LpcMessage);
if (rc != 0) {
printf("NtRequestWaitReplyport failed, rc=%x\n",
rc);
return -1;
}
/* Print the reply received */
printf("Got the reply\n");
PrintMessage(&LpcMessage);
//
printf("Reply = %s\n", ClientView);
}
return 0;
}