Search This Blog

Wednesday, March 30, 2011

Mini-Filter messaging

Recently I have been working on some OS monitoring and, as a mean to communicate with my user mode application, I ended up resorting to the Mini-filter Messaging.

What is it?
Well, when you write a mini-filter (like a file mini filter) Windows provides you with a set of API that can be used to send messages to an user mode application. The API are as follows:

On the driver side:
And on the user mode side:

With all of those, you should be able to make your own communication channel.

First, on the driver side, you will need to create the communication port with FltCreateCommunication port. You can use a default security descriptor to pass as a parameter because it's a little easier and it does not have to be super secure either.

To create the descriptor, just use something like this:

PSECURITY_DESCRIPTOR sd;
NTSTATUS status = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);

Then, pick a name for your port like (L\\MyCommunicationPort) and create the communication port.
There are some parameters passed to the API that are callback functions. You must use the pfnConnect and the pfnDisconnect callback (meaning that setting them to NULL would be useless) but you may leave pfnMessageNotify empty.

Once the user mode application will connect to the port, the pfnConnect function will be called and you will be able to start sending out messages.

The 'Connect' callback will provide you with a [inClientPort] that you must save (in a global variable for example) as you will need it to send messages.

Now, to send a message, that's easy, just use FltSendMessage().
By looking at the documentation, you will notice that it requires a PFLT_FILTER parameter that is your handle that came from the FltRegisterFilter() call that you made in your DriverEntry() function.

The next argument is the clientPort that you have saved earlier from the pfnConnect callback (you did, right?)

You need to provide a buffer to send, as well as its length. Personally, I keep the buffer size below 1KB because I don't know what the maximum size is and 1K is most of what I would need anyway.

You may provide a reply buffer as well, in case you want the user mode application to reply to your messages. I use it to make sure that the messages were delivered properly.

The timeout is a little tricky to figure out because the length is a multiple of 100 nanoseconds and it can be absolute or relative. So, if you want to set a 1 second timeout, you would need to enter a number such as this: -10000000. To make it easier I wrote a couple of macros:

#define K_NANOSECOND  10000000
#define K_MILLISECOND  10000

#define K_RELATIVE_WAIT_S(a)  -((LONGLONG) a * K_SECOND)
#define K_RELATIVE_WAIT_MS(a)  -((LONGLONG) a * K_MILLISECOND)

The code to use would look like: timeout.QuadPart = K_RELATIVE_WAIT_S(1) to wait for 1 second.

There is a very good example of the whole messaging system that comes with the DDK, which is under <drive:>\WINDDK\<release version>\src\filesys\minifilter\scanner\

What I should point out are a few little quirks that are not that trivial to figure out.
First, after you connect to the port, you will need to 'prime' the messaging by doing a few FilterGetMessage() calls. You will create a buffer per call and those buffers will be re-used during the life time of you application.

The buffer will be filled with data when GetQueuedCompletionStatus() returns in your thread(s). it will be followed by a FilterReplyMessage() and a FilterGetMessage().

One thing about the FilterReplyMessage() call though, you will pass your structure which must contain a FILTER_REPLY_HEADER structure. Before sending the reply, you must fill up the fields contained in there like this:

reply.ReplyHeader.Status = 0;
reply.ReplyHeader.MessageId = mystruct.fltHeader.MessageId;

the mystuct.fltHeader is the FILTER_MESSAGE_HEADER that must be included in the structure used to receive message like this:

typedef struct myStruct {
  FILTER_MESSAGE_HEADER fltHeader;
  MY_DATA  data;
} MY_STRUCT, *LPMY_STRUCT;

I would advise you to reply to the kernel messages in order to verify that the delivery went right. Otherwise, as I experienced it, some messages may get lost and some may get duplicated.

Happy messaging!

No comments:

Post a Comment