Windows IT Pro
Windows IT Library
  - Advertise        
Windows IT Pro Logo

  Home  |   Books  |   Chapters  |   Topics  |   Authors  |   Book Reviews  |   Whitepapers  |   About Us  |   Contact Us  |   ITTV  |   IT Jobs

search for  on    power search   help
 






Local Procedure Call
View the book table of contents
Author: Prasad Dabak
Milind Borate
Sandeep Phadke
Published: October 1999
Copyright: 1999
Publisher: M&T Books
 


NtImpersonateClientOfPort
int _stdcall

NtImpersonateClientOfPort(

HANDLE PortHandle,

PLPCMESSAGE pLpcMessage);
A subsystem may need to perform some processing in the security context of the calling thread. The NtImpersonateClientOfPort() function enables the server thread to assume the security context of the client thread. The function uses the pLpcMessage parameter to identify the process ID and thread ID of the client thread.


LPC SAMPLE PROGRAMS

In this section, we present two sample programs. The first program demonstrates the short message communication using LPC, and the second program demonstrates the communication using shared memory.

On the CD: The sample program can be found in the PORT.C file on the accompanying CD-ROM. The data prototypes and structure definitions for port-related functions can be found in UNDOCNT.H, which is also on the CD-ROM.

Short Message LPC Sample
The PORT.C file contains the program that acts as both the client and the server for demonstrating short message communication. When the program is invoked without any parameters, it acts as the server. If invoked with some parameter, it acts as a client (the parameter is a dummy parameter and gets ignored). You should start the program in server mode first. The server-mode program first creates a port and then loops into a “receive request–process request–reply request” sequence. It uses the NtReplyWaitReceivePort() function to accept requests. The connection requests are treated differently than other requests. In case of a connection request, the server thread has to accept the connection and complete the connection sequence. For requests, other than the connection request, the server prints the message, inverts all the bytes in the message, and sends this inverted message back as the reply.

Once the server is ready to accept connections, you can run another instance of the program–this time in client mode. The client-mode program connects to the port created by the server-mode instance. It first demonstrates the use of the NtRequestPort() function to send a datagram. Then, the client sends a request and waits for a reply in a loop. You can start multiple client sessions; the server portion of the program can handle multiple client requests.

We list and explain the PORT.C file in this section.

Listing 8-1: PORT.C
/***************************************************/

/* Demonstrates the short message LPC provided by the

 * port object

 */


#include <windows.h> #include <stdio.h>
#include "undocnt.h" #include "print.h"
#define PORTNAME L"\\Windows\\MyPort"
Apart from regular header inclusions, the initial portion of the PORT.C file has the definition of the name of the message port used by the sample program. It is a complete path name starting from the root of the object directory. Note that the wide character set is used instead of the normal ASCII character set because we are directly invoking the system services and the system services understand only the Unicode character set.
/* A real server function would do some meaningful

 * processing here. As we are writing just a sample

 * server, we have a dummy server function that just

 * inverts all the bytes in the message

 */

void ProcessMessageData(PLPCMESSAGE pLpcMessage)

{

DWORD *ptr;

DWORD i;


ptr = (DWORD *)(pLpcMessage->MessageData); for(i=0; i<pLpcMessage->ActualMessageLength/sizeof(DWORD); i++) { ptr[i] = ~ptr[i]; } return; }
This is a dummy processing function on the server side. This function is passed the LPC request message, received by the server. The function should return the reply message in the same memory space. As the comment says, the function simply inverts all the bytes in the message. Because we only want to demonstrate the working of the LPC, we do not provide any intricate server functionality. You can modify this function to implement the functionality provided by your server.
BOOL

ProcessConnectionRequest(

PLPCMESSAGE LpcMessage,

PHANDLE pAcceptPortHandle)

{

HANDLE AcceptPortHandle;

int rc;


*pAcceptPortHandle=NULL;
printf("Got the connection request\n"); PrintMessage(LpcMessage); ProcessMessageData(LpcMessage);
rc = NtAcceptConnectPort( &AcceptPortHandle, 0, LpcMessage, 1, 0, NULL); if (rc != 0) { printf("NtAcceptConnectPort failed, rc=%x\n", rc); return FALSE; }
printf("AcceptPortHandle=%x\n", AcceptPortHandle); rc = NtCompleteConnectPort(AcceptPortHandle); if (rc != 0) { CloseHandle(AcceptPortHandle); printf("NtCompleteConnectPort failed, rc=%x\n", rc); return FALSE; } *pAcceptPortHandle = AcceptPortHandle; return TRUE; }
The server part of the program calls this function when it receives a connection request from the client. This function receives the message containing the connection request and returns the port handle specific to the client. The function first prints the message then calls the ProcessMessageData() function. As described earlier, the message data in a connection request consists of nothing but the ConnectInfo passed to the NtConnectPort() function by the client.

The ProcessConnectionRequest() function starts the real work by calling the NtAcceptConnectPort() function. The only parameter of significance to this function is the message containing the connection request. The function returns a handle to the port in the AcceptPortHandle parameter. This function returns a nonzero value if it fails. If the function succeeds, the ProcessConnectionRequest() function calls the NtCompleteConnectPort() function, which accepts the port handle returned by the NtAcceptConnectPort() function as the parameter. The NtCompleteConnectPort() function also returns a zero on success and a value other than zero on failure.

In this function, we accept all the connection requests. You may want to modify this function to selectively accept connection requests. For example, you might permit the connection only for certain users or only if the client provides certain connection information. If your server can accept only a single client at a time, you need to reject all further connection requests. As described earlier, you can reject connection requests by passing the acceptIt parameter as zero.
BOOL

ProcessLpcRequest(

HANDLE PortHandle,

PLPCMESSAGE LpcMessage)

{

int rc;

printf("Got the LPC request\n");

PrintMessage(LpcMessage);

ProcessMessageData(LpcMessage);


rc = NtReplyPort(PortHandle, LpcMessage); if (rc != 0) { printf("NtReplyPort failed, rc=%x\n", rc); return FALSE; } return TRUE; }
In this program, we chose to use two function calls to reply to a message and receive the next message, instead of using a single call to the NtReplyWaitReceive() function. The ProcessLpcRequest() function, a small utility function, prints the received message, processes it (inverts the bytes by calling the ProcessMessageData() function), and sends back the processed data as the reply using the NtReplyPort message.
int server(OBJECT_ATTRIBUTES *ObjectAttr)

{

BOOL RetVal;

HANDLE PortHandle;

int rc;

LPCMESSAGE LpcMessage;


/* Create the named port */ rc = NtCreatePort(&PortHandle, ObjectAttr, 0x0, 0x0, 0x00000);
if (rc != 0) { printf("Error creating port, rc=%x\n", rc); return -1; } printf("Port created, PortHandle=%d\n", PortHandle); memset(&LpcMessage, 0, sizeof(LpcMessage));
while (1) { HANDLE AcceptPortHandle;
/* Wait for the message on the port*/ rc = NtReplyWaitReceivePort(PortHandle, NULL, NULL, &LpcMessage); if (rc != 0) { printf("NtReplyWaitReceivePort failed"); CloseHandle(PortHandle); return -1; }
RetVal = TRUE; switch (LpcMessage.MessageType) { case LPC_CONNECTION_REQUEST: RetVal = ProcessConnectionRequest( &LpcMessage, &AcceptPortHandle); break;
case LPC_REQUEST: RetVal = ProcessLpcRequest( PortHandle, &LpcMessage); break;
default: PrintMessage(&LpcMessage); break; }
if (RetVal == FALSE) { break; } } return 0; }
As described earlier, the same LPC demonstration program acts as the server and the client. The main() function calls the server() function when the program is invoked without any parameters. The server() function is passed a pointer to the OBJECT_ATTRIBUTES structure that contains the object name of the communication port. The function creates a port with this name, upon which it gets back a handle to the port. As described earlier, the MaxConnectInfoLength and MaxDataLength parameters to the NtCreatePort() function are ignored so we simply pass them as zero. The NtCreatePort() function returns a zero on success and a nonzero value on failure.

After successful creation of the port, the server() function goes into a receive-process-reply loop. The function uses the NtReplyWaitReceivePort() function to receive requests from clients. Since we use this function only to receive requests, the pLpcMessageOut parameter passes as NULL. The NtReplyWaitReceivePort() function returns zero on success, and the pLpcMessageIn contains the client request. This request can take the form of a LPC_CONNECTION_REQUEST, a LPC_DATAGRAM, a LPC_REQUEST, and so on. The server processes each type of requests differently. It processes the LPC_CONNECTION_REQUEST by performing the connection protocol. It accomplishes this by calling the ProcessConnectionRequest() function. With a LPC_REQUEST message, the server needs to do the requested processing and reply to the request. Since we are not implementing any significant functionality in the server, we just print the message, invert the message bytes, and return a reply. We do this in the ProcessLpcRequest() function. For LPC_DATAGRAM messages, a reply is not expected. These messages and all other messages, including LPC_PORT_CLOSED and LPC_CLIENT_DIED, are handled in the default case of the switch statement. A real server may need to perform different processing for these messages. For example, a real server might free up per-client resources on receiving a LPC_PORT_CLOSED message.

The server side of the program continuously loops, receiving-processing-replying the client requests. We did not program an exit for the server part. This is generally the case with servers, and that’s the reason why they are called daemons in Unix terminology. Generally, servers start up with the system boot and continue processing client requests until the system shuts down. With our server, you can kill it by pressing Ctrl+C in the command window or by using the Task Manager.
int client(UNICODE_STRING *uString)

{

static int Param3;

HANDLE PortHandle;

DWORD ConnectDataBuffer[] = {0, 1, 2, 3, 4, 5};

int Size = sizeof(ConnectDataBuffer);

DWORD i;

DWORD Value=0xFFFFFFFF;

int rc;

LPCMESSAGE LpcMessage;

DWORD *ptr;


printf("ClientProcessId=%x, ClientThreadId=%x\n", GetCurrentProcessId(), GetCurrentThreadId());
rc = NtConnectPort(&PortHandle, uString, &Param3, 0, 0, 0, ConnectDataBuffer, &Size); if (rc != 0) { printf("Connect failed, rc=%x\n", rc); return -1; }
printf("Connect success, PortHandle=%d\n", PortHandle); for (i = 0; i < Size/sizeof(DWORD); i++) { printf("%x ", ConnectDataBuffer[i]); }
printf("\n\n"); rc = NtRegisterThreadTerminatePort(PortHandle); if (rc != 0) { printf("Unable to register thread" " termination port\n"); CloseHandle(PortHandle); return -1; }
/* Demonstrates how to send a datagram using * NtRequestPort */ memset(&LpcMessage, 0, sizeof(LpcMessage)); LpcMessage.ActualMessageLength=0x08; LpcMessage.TotalMessageLength=0x20; ptr=(DWORD *)LpcMessage.MessageData; ptr[0]=0xBABABABA; ptr[1]=0xCACACACA;
rc=NtRequestPort(PortHandle, &LpcMessage);
while (1) { /* Fill in the message */ memset(&LpcMessage, 0, sizeof(LpcMessage)); LpcMessage.ActualMessageLength=0x08; LpcMessage.TotalMessageLength=0x20; ptr = (DWORD *)LpcMessage.MessageData; ptr[0] = Value; ptr[1] = Value-1;
printf("Stop sending message (Y/N)? "); fflush(stdin); if (toupper(getchar()) == ’Y’) { CloseHandle(PortHandle); break; }
/* Send the message and wait for the reply */ printf("Sending request/waiting for reply"); 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); Value -= 2; } return 0; }
The client() function implements the client-side portion of the LPC sample. The function prints the process ID and the thread ID; you can match it with the process ID and thread ID printed from the messages received by the server.

The client() function starts its job by connecting to the port created by the server process. It passes six double words as the connectInfo. You can verify that the server receives these words as the message data with the LPC_CONNECTION_REQUEST. Upon return from the NtConnectPort() function, the client gets a handle to the port. Also, the connectInfo buffer fills with the data message passed to the NtAcceptConnectPort() function by the server.

Further, the client calls the NtRegisterThreadTerminatePort() function, with the newly acquired port handle as the parameter, so that the operating system sends a LPC_CLIENT_DIED message over the port when the client terminates. The client calls this function only if the server needs to know about the client death. We call this function here to demonstrate the mechanism.

The client also demonstrates the datagram communication via the LPC. As described earlier, the NtRequestPort() function passes LPC_DATAGRAM type requests. Note that the client fills in only the message length fields and the actual message data; the operating system fills in the remaining fields in the LPCMESSAGE structure before the message passes to the server. The client() function sends two double words as the message data, which the server prints upon reception of the message.

After demonstrating the datagram communication, the client goes in a “send request – wait for reply” loop. Every time, before sending the request, it asks the user whether to continue or quit. If the user wants to continue with the demonstration, the client sends a sample request over the port using the NtRequestWaitReply() function. The message data consists of two double words inverted by the server and sent back as the reply. The NtRequestWaitReply() function returns to the client after it gets the reply message from the server. In this program, we used the same buffer to pass the request message and to receive the reply message. You can use different buffers for this purpose.



Page: 1, 2, 3, 4, 5, 6

next page



Windows IT Pro Home Register FAQ for Windows WinInfo News
Europe Edition About Us Contact Us/Customer Service Media Kit Affiliates / Licensing  
SQL Server Magazine Office & SharePoint Pro Windows Dev Pro IT Job Hound ITTV
IT Library Technology Resource Directory Connected Home Windows Excavator Windows SuperSite 
 
 Windows IT Pro is a Division of Penton Media Inc.
 Copyright © 2008 Penton Media, Inc., All rights reserved. Terms and Use | Privacy Statement | Reprints and Licensing