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!

Tuesday, March 15, 2011

Track Day Preparation

Terrorizing my sister at Thunderhill Raceway (2010)
So, you've decided that you try to attend a track day with your (cool) car but you don't know how to go about it.

First, you need to know who hosts track days. There are a bunch of organizations that do so, nothing to worry about, you'll get your shot. For a quick reference you can go here. This is calendar for 2011 which contains a lot of events hosted by a bunch of different people.



Personally, I like Hooked On Driving because while they tend to be more expensive than most, their events are very well organized. They have a pretty big staff and always provide coaching (coaches are either part of the staff or are C group drivers that volunteer to help out). They also provide a decent lunch and free water during the day. They also have group download and classroom sessions for the A & B groups.

Most organizations will split the day in different groups (3 or 4 groups are the most common).

Groups: Well, depending on the host you will have a Group A, B, C or 1, 2, 3 or HPDE1,2,3 etc... Sometimes the A group is the beginner group and the C is experienced, sometimes it's reversed.

But the host will let you know in advance as this is usually described on their web site. If it's your very first time on the track, choosing the beginner group is the best thing to do as it will provide you with invaluable coaching (when available) and experience. Also, those groups tend to run at a much slower pace, which help bringing your car back home in one piece.

Car: Well, not everyone owns a Ferrari. So, you ride what you bring.
I must say that there usually is a large ratio of BMW and Porshe, and since a couple years or so, Nissan GT-R, Audi R8. Occasionally though, you will spot some Ferraris, Lambos, Aston Martin and a few nut cases who bring some Lotus (me included). Actually, there is almost always one or two Elise/Exige  present.

I have also seen a bunch of Minis, Subaru WRX, Evo7, VW Bug, Mustangs, Challengers (seldom) so no matter what your car is, you can attend a track day. Of course, if you bring a Smart car you'll want to slit your wrists before lunch...

The things that are important, regardless of the car are at least those few things:

  • Tires. Make sure yours are in good shape and if you can afford sports tires, do it. I usually run Toyo R888.
  • Oil. You should get an oil change and verify that the drain plug is tight (losing oil on the track is uncool)
  • Tire gauge. Always good to have one even if there's tire service on track.
  • Shape. You car needs to be in good shape, without stuff hanging, flapping, loose, etc.
  • Brakes. It's kind of nice when you can actually brake instead of plowing into a wall.

Gear: You should not need anything really, besides a helmet. Then again, tracks like Thunderhill have skull protectors for rent. It's still better to have your own stuff though. There's a catch, most likely, your lid will have to be on par with the current helmet norms (SA Snell certified).

You can also bring driving gloves which are nice because they prevent potential blisters or having the steering wheel slip out of your sweaty little hands.

Before D-Day: Your track day is on Friday and it's Thursday evening. By now you should be ready to head to the motel for the night (assuming you go to a race track that's two or more hours from your home).

Here's my own checklist as I leave to work in the morning:

  • Good tires
  • Tire gauge
  • Couple tools (just in case)
  • Wheel lock (in case your wheels bolts/nuts have a funky shape or require a lock)
  • Back pack with necessary stuff (tooth brush/paste, clean clothes, make-up if you're into that sort of things)
  • Helmet
  • Gloves
  • ODBII scanner (cos' my Lotus loves to throw some CEL at me every so often, but you won't need that)
  • Ice chest
  • Gatorade (or whatever you want to drink)
  • Cookies (I love cookies!)
  • Car key (in case you're towing your fancy sports car)
  • Phone
  • Wallet

I usually don't bring much more than that because I don't have another vehicle to tow my car to the track. Towing your car is something I would strongly suggest for different reasons:

  • You drive a car that can get uncomfortable after a couple of hours
  • A truck or SUV is easier to drive at the end of the day and you'll feel more relax and not in track mode anymore
  • You could wreck your car and thus, get stuck 3.5 hours from home, which would suck donkey balls.
  • You could puncture a tire on the way there which could ruin your day if you have a car that (like the Elise) doesn't have a spare tire.
  • Your new tires are still brand spanking new when you get to the track
  • You want to put racing slicks or semi-slicks on your car
D-Day: Finally, it's about 7:30AM and you and the love of your life (no, not your spouse...) made it to the race track and you are enjoying the paddocks for the first time in your life. It's a pretty special moment to tell the truth (for you and your baby).

You start starring at some of the other cars in there, Lambos, Porshe, R8, proper race cars, and my Lotus just because it does look really cool ^_^. A lot of people wander in the paddocks and snap pictures of cool cars, I do it from time to time when I spot some fancy cars (Aston Martins for instance).

Usually, there is a drivers meeting at 8:00AM or so that is mandatory. They talk about being safe and also the fact that track days are NOT race days. You don't win anything besides driving your car back home in one piece. They also explain the meaning of the flags that you will see on track (White, Yellow, Black, Meatball, Checkered, Red, etc) and they will also tell you where the corner worker stations are.

The track usually goes hot at 8:30 or 9:00 depending if it's Winter or Summer and will close at about 5PM.
Usually the faster groups go first (pretty sure that they do that to dry the track if it's a little wet :)

People are usually very friendly, drivers, staff, track people, etc. Most likely because we all share the same passion and sometimes drive similar cars. Lotus guys talk to Lotus guys so on so forth. It's nice to mingle because sometimes you get to talk to seasoned drivers that will share tips with you.

Track is hot!: It's finally your turn to go on track.

If you go with HOD, they will assign a coach to you. They are all qualified and very nice. The coach will drive the first two laps at some 40mph with you as a passenger. They do this to show you the track layout and they will point out the turning points, braking zones, etc.

You should use that first session to figure out the way the track goes and try to remember the more difficult turns (for instance, Turn 5 and 9 at Thunderhill as they are blind turns). You can also feel how the car responds to your driving.

As you come out of the track, you should immediately go check with the tire guys (assuming they are there) for your air pressure and temperature of the rubber. They will be able to set you up correctly.

Well, I'm not going to go into details about what happens during the day because this post really is about the preparation for the track day. Hope that you'll give it a try and enjoy it.

Monday, March 7, 2011

Registry Monitoring sans Context

So, you've been asked to monitor the Windows registry with your driver and as you are a good programmer you've decided to do it by using the proper API and not by hooking the SSDT like every other coder does.

Very cool, problem is, you have no clue on how to go about it.
Fear not programmer friend! Here are 4 steps to help you out.

Step 1: CmRegisterCallbackEx

This is an API you can used in order to have Windows notify you whenever something happens with the registry. You can register pre-operation or post-operation callbacks, depending on your need, although monitoring will use post-operation callbacks.

Step 2: Creating a dispatch function

NTSTATUS RegistryMonitoringCallbackDispatch(
  __in      PVOID CallbackContext,
  __in_opt  PVOID Argument1,
  __in_opt  PVOID Argument2
)
{
  USHORT Class = (USHORT)Argument1;
  if (NULL == g_pCallbackFcns[Class])
  {
    return STATUS_SUCCESS;
  }
  return (*(g_pCallbackFcns[Class]))(CallbackContext, Argument1, Argument2);
}


As we can see above, this is a very simple function. All it does is check Argument1, which gives the type of notification and use it as an index for calling the appropriate callback.

The callbacks are defined like this:


NTSTATUS RegNtDeleteKeyCallback(__in PVOID CallbackContext, __in_opt PVOID Argument1, __in_opt PVOID Argument2);


They basically all follow the same prototype (which is the same as the dispatch function) and we can therefore, store them in a simple table which I have defined as such:


PEX_CALLBACK_FUNCTION g_pCallbackFcns[MaxRegNtNotifyClass] = {0};


The initialization of the monitoring module contains code such as this:


// Set the callbacks
//
g_pCallbackFcns[RegNtDeleteKey] = RegNtDeleteKeyCallback;


Now that this is done, we can go to the next step.

Step 3: Answering the call

Now that we have registered our driver with the system, whenever a registry change happens, our code will be called upon.

In that code, we don't have to do much. For example, we can merely output the name of a value that's being deleted.

If we read the documentation from Microsoft (and we always do, of course), we notice that each structure contained as Argument2 contains a pointer to a user defined context. Usually in that context you will want to store stuff like the name of the key that's being opened and its handle. This way, when a value gets added, modified, deleted etc, you will be able to know its location (under which key it's at).

But, because we're lazy and that's the point of this blog, we don't have to go through the whole context handling stuff as there's an easier way to remember what the key name is.

Step 4: Obtaining the name of a registry object.

Well, here the function that you'll need:


NTSTATUS GetObjectName(PUNICODE_STRING ObjectName, USHORT ccbLength, PVOID Object)
{
  NTSTATUS status = STATUS_SUCCESS;
  ULONG len = sizeof(OBJECT_NAME_INFORMATION) + sizeof(WCHAR) * ccbLength;
  PUCHAR Buffer = memalloc(len);
  POBJECT_NAME_INFORMATION ObjectNameInfo = (POBJECT_NAME_INFORMATION) Buffer;
  if (Buffer == NULL)
      return STATUS_NO_MEMORY;
  ObjectName->MaximumLength = ccbLength * sizeof(WCHAR);
  __try {
      // Make sure that the object is not null
      //
      if (NULL != Object)
      {
          RtlZeroMemory(Buffer, len);
          status = ObQueryNameString(Object, ObjectNameInfo, len, &len);
          if (NT_SUCCESS(status))
          {
              RtlCopyUnicodeString(ObjectName, &ObjectNameInfo->Name);
          }
      }
  }
  __except (EXCEPTION_EXECUTE_HANDLER)
  {
      // Object may have been invalid (This can happen when

      // dealing with registry notification)
      //
      status = GetExceptionCode();
  }

  memfree(Buffer);
  return status;
}


Not that hard now, was it?

Note: the 'memalloc' and 'memfree' are placeholders for proper allocation functions.

Then, for example, let's say that we are monitoring a value that's being deleted, here's a possible callback code for it:


NTSTATUS RegNtDeleteValueKeyCallback(__in PVOID CallbackContext, __in_opt PVOID Argument1, __in_opt PVOID Argument2)
{
    NTSTATUS status = 0;
    PREG_DELETE_VALUE_KEY_INFORMATION infoDeleteKey = (PREG_DELETE_VALUE_KEY_INFORMATION) Argument2;
    UNICODE_STRING ObjectName = {0};
    WCHAR* Buffer = (WCHAR*) memalloc(sizeof(WCHAR) * BUFFER_SIZE);
    HANDLE Pid = PsGetCurrentProcessId();
    RtlUnicodeStringInit(&ObjectName, Buffer);
    GetObjectName(&ObjectName, BUFFER_SIZE, infoDeleteKey->Object);
    SendRegistryDeleteValueNotification(ObjectName.Buffer, infoDeleteKey->ValueName->Buffer, Pid);
    DBGPRINT("Deleting value: %p [%ws\\%ws]\r\n", infoDeleteKey->Object, ObjectName.Buffer, infoDeleteKey->ValueName->Buffer);

    memfree(Buffer);
    return STATUS_SUCCESS;
}


In the code above, we get the name of the value from the structure we get in Argument2 but the name of the key is unknown. Fortunately, we have that nifty little function written above that will help us out.

So there you have it, registry monitoring made painless (kinda).