Posted tagged ‘windows kernel’

File Minifilter Part4: Sending huge data from kernel to user mode efficiently using variable-sized structures

February 23, 2009

Today I will discuss how to send huge chunks of data (usually received in write operations) from kernel mode file system filter to its user-mode counter part. The default implementation of microsft in its sample of file scanner minifilter can send few KB of data per call (fltsendmessage) . The reason is that the structure defined to send different parameters including data to be written can contain only handful of KB due to the small size of stack in the kernel mode.

There are many different structures defined internally in the windows code which can send more data at runtime than these are defined for at compile time. These structures are defined with a fixed header at begining and followed by a variable-sized array.  Then these structures are allocated with sizeof(structure) + number of bytes need(which are accesible by this last array later on). The old new thing is the one cool blog explaining about this variable-sized structure approach.

For kernel mode

typedef struct _SCANNER_INFO {
//other members above here ....
INT iLengthContents;
CHAR  szContents[1];

This is how you will allocate this structure

iNewSize = FIELD_OFFSET(SCANNER_INFO, szContents[iLengthRequired); //this macro FIELD_OFFSET is used to get the offset which in this case will be the total size of the structure

SCANNER_INFO* pScanInfo = ExAllocatePoolWithTag( NonPagedPool,iNewSize,'info' );

Then use this structure to send it to user-mode as follows

FltSendMessage(fitlerHandle, port, pScanInfo , iNewSize,pReplyBuf, &iReplySize,&lTimeout);

In the user mode the default sample contains the following the structure to receive data from kernel mode in overlapped way.

typedef struct _SCANNER_MESSAGE {
//  Required structure header.
//  Private scanner-specific fields begin here.

Allocate and use this message struct as follows.

pMsg = (PSCANNER_MESSAGE)malloc(iSize);

//the actual buffer needed by getfilter callback, excluding overlapped size
memset( & pMsg ->Ovlp, 0, sizeof( OVERLAPPED ) );
hr = FilterGetMessage(iport, & pMsg ->MessageHeader,  iSize,  &msg->Ovlp );

This way we can overcome the limitation of sending more data then a kernel-mode stack can hold thus improving performance of the filter (used in encryption, virus scans etc) by manifolds.


File Minifilter part 3: How to know if current I/O request is from network in a file minifilter

February 16, 2009

This article is the part of series I started out to share my own difficulties faced while writing a file mini filter driver. Today I shall discuss how to find out if the current I/O request is comming from outside the local machine i.e. network.

One pretty easy way is to find the source of the current session. In a simple network there can be three sources.

  1. NTLMSSP (NT lan manager)
  2. SYSTEM (system user services and processes)
  3. USER32 ( all the prcesses running in the user space

The key here is to find this source if it is NTLM ( default lan manager on windows machine, some  network might use kerberos but its pretty easy to figure out once we get the source of the current I/O request’s session) then the current I/O request is comming from network e.g trying to access some shared folder. 

In case of SID we needed TOKEN_USER whereas here we are looking for  TOKEN_SOURCE  information associated with the current request.

Get token information as follows

PFLT_CALLBACK_DATA Data //comming from callback parameter

TOKEN_SOURCE **ppSource;
PACCESS_TOKEN pToken =  SeQuerySubjectContextToken(&(Data->Iopb->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext));
status = SeQueryInformationToken(pToken,TokenSource,ppSource);

TOKEN_SOURCE’s SourceName member contains the name of the source we are looking for, if it is NTLMSSP then the current request is comming from the network.

File Minifilter part 2: How to get SID and User name for I/O requests in File minifilter

February 16, 2009

Getting user name associated with the current I/O request can be obtained as shown below.

First of all we need a TOKEN_USER object from the PFLT_CALLBACK_DATA object.
There are two methods to achieve this.

A) From current thread(if impersonation is used) or process token

TOKEN_USER* TokenUser;
use ZwOpenThreadTokenEx() to get token handle.
HANDLE token;
if ((status=ZwOpenThreadTokenEx(NtCurrentThread(), GENERIC_READ, TRUE,OBJ_KERNEL_HANDLE , &token)) != STATUS_SUCCESS)
status=ZwOpenProcessTokenEx(NtCurrentProcess(), GENERIC_READ, OBJ_KERNEL_HANDLE,&token);

Get TokenUser from this token handle

ZwQueryInformationToken(token, TokenUser, NULL, 0, &len); //to get required length
if (!(*ppUser=ExAllocatePoolWithTag(NonPagedPool, len, 'ofni'))) //tagged memory allocation
if ((status=ZwQueryInformationToken(token, TokenUser, *ppUser, len, &len)) != STATUS_SUCCESS)
ExFreePoolWithTag(*ppUser, 'nacS');

B) From subject context present in the callback object

PFLT_CALLBACK_DATA Data; //passed from file filter module as parameter to callbacks
TOKEN_USER **ppUser; // token user to be used to extract SID
PACCESS_TOKEN pToken = SeQuerySubjectContextToken(&(Data->Iopb->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext));
*ppUser = NULL;
status = SeQueryInformationToken(pToken,TokenUser,ppUser);

Once TOKEN_USER structure is filled; following code can be used to generate a valid SID

TOKEN _USER* pUser; //it is the TOKEN_USER filled from one of the above mentioned methods

unsigned long dwStrSidSize=0;
SID* sid = (SID*)pUser->User.Sid;
dwStrSidSize = sprintf(strSID,"S-%u-",sid->Revision);
IdAuth = (sid->IdentifierAuthority.Value[5]) + (sid->IdentifierAuthority.Value[4] << 8 ) + (sid->IdentifierAuthority.Value[3] << 16) + (sid->IdentifierAuthority.Value[2] << 24);
dwStrSidSize += sprintf(strSID+strlen(strSID),"%u",IdAuth);
if (sid->SubAuthorityCount)
for (index = 0; index < sid->SubAuthorityCount;index++)
dwStrSidSize += sprintf(strSID+dwStrSidSize,"-%u",sid->SubAuthority[index]);

At the end free the allocated TOKEN USER as follows

ExFreePoolWithTag(pUser, 'ofni');

This way SID string can be achieved for current I/O request. This string can be cached (using SCANNER_STREAM_HANDLE_CONTEXT; will write about it in some other post) to be used in all the callbacks related to current I/O request.

This SID string can be passed to user mode code where user name, domain name, domain groups and other associated information can be retrieved.
For example:
use ConvertStringSidToSid to get SID structure.
LookupAccountSid can be used to retrieve account name of the user from this SID structure.

File MiniFilter Part1: Process name and ID in File Filter driver callbacks

February 16, 2009

Getting process name and process id is pretty easy in the file filter scanner callbacks.

Scanner callbacks contains a parameter PFLT_CALLBACK_DATA which has a pointer to thread which initiated the current I/O request.

Using this thread we can get process id in file filter as follows

PFLT_CALLBACK_DATA Data; //passed by file filter module
PEPROCESS objCurProcess = IoThreadToProcess( Data->Thread );
int iCurProcID    = PsGetProcessId(objCurProcess);

To get Process name

CHAR*  pStrProcessName = PsGetProcessImageFileName(objCurProcess);


So if you want to monitor I/O requests from some particular process you can do it on the basis of process name or process id. The following code will bypass all the subsequent File Filter Scanner callbacks from all the processes except explorer

if(_stricmp(pStrProcessName,"explorer.exe")==0 )
     return FLT_PREOP_SUCCESS_NO_CALLBACK;  // no more callbacks i.e. for postCreate, Pre/post Writes, Reads, setinfo and others.