Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Raw PDO to send IOCTL to upper filter driver (kbfiltr/moufiltr) to enable/disable device

Tags:

I am quite new to driver development and trying to write a simple filter driver that will enable or disable a keyboard or mouse device. If I can make it work, I want to use it to disable the touchpad on my laptop when a mouse is plugged in. I realize there is probably software out there that does this already, but I am really interested in device drivers and want to learn how to do this myself.

I am using the kbfiltr and moufiltr examples that ship with the WDK, installed as upper filter drivers. The kbfiltr example creates a pdo which can be enumerated and connected to by a usermode program. This allows me to send IOCTLs to the PDO that are handled by KbFilter_EvtIoDeviceControlForRawPdo. However, when I try and do anything at all related to the filter driver, like call into KbFilter_EvtIoInternalDeviceControl so I can do something like

VOID
KbFilter_EvtIoInternalDeviceControl(
    IN WDFQUEUE      Queue,
    IN WDFREQUEST    Request,
    IN size_t        OutputBufferLength,
    IN size_t        InputBufferLength,
    IN ULONG         IoControlCode
    )
    ...
    hDevice = WdfIoQueueGetDevice(Queue);
    devExt = FilterGetData(hDevice);

    switch (IoControlCode) {      
    ...
      case IOCTL_INTERNAL_KEYBOARD_DISCONNECT:
       //
       // Clear the connection parameters in the device extension.
       //
       devExt->UpperConnectData.ClassService = NULL;
       break;
    ...
    }

I get a BSOD. It is not the above code, in the vanilla example the set to null is commented out, just calling into Kbfilter causes the BSOD. I have tried to set the device extension directly in the PDO but this also causes a BSOD, presumably because it is the PDO devExt, not kbfiltr's?

(related: what is a good way of getting the stack trace from a BSOD? I am using Virtual PC as my test environment and an unchecked build of XPSP3)

I can't send an IOCTL_INTERNAL_KEYBOARD_DISCONNECT directly to the driver stack (I understand that input devices accept only one connection at a time?) hence the need for the raw PDO. I really only need to send two IOCTLs (to enable and disable) and I figured I would just use the keyboard disconnect and connect since these were already defined.

If I am wrong about any of these assumptions, please let me know, I know I really am a noob at this but I haven't found a lot of documentation about this kind of communication via a PDO.

like image 353
Dale Avatar asked Sep 16 '09 03:09

Dale


2 Answers

Ok, I have finally solved this and my driver is working.

Implementation of a KMDF filter driver:

Thanks to Sergius who suggested the COM-port approach because this helped me set up WinDbg. This awesome blog post explains how to get it set up quickly, basically you let VPC set up a com port as a named pipe, enable kernel debug mode on the virtualized OS, and connect to it while it is booting. Then you can get all the DbgPrint messages when the driver is loading and do a lot more, but just the trace messages during the startup process were a huge help for me.

I think my main problem was trying to reuse an internal IOCTL in KbFiltr. This was just a bad design idea on my part because I didn't understand the difference between internal IOCTL and other IOCTLs - Internal IOCTLS such as IOCTL_INTERNAL_KEYBOARD_DISCONNECT have restricted access conditions and should only be sent by other drivers or the kernel. Also this KB article "How to send IOCTL to filter driver" is an example using the same control device structure, but it is WDM.

Anyway, after fighting with the KbFiltr example all weekend, I finally gave up and started over using the WDF Toaster/filtr example. This is a more barebones KMDF filter driver and I had to fill in a lot of blanks using KbFiltr and MouFiltr. The Toaster filter driver operation is similar to KbFiltr but it creates a control device instead of a PDO. It also sets a dos device name for the control device so you can communicate with it from usermode without having to Pinvoke to do that step. The control device allows you to control all devices which have your filter driver loaded just by iterating over the collection. A waitlock is used to synchronize access to the collection.

I also was able to just modify the INF file (to use Mouse class instead of Toaster class) and apply it straight out of the box on my test machine with no modification to the driver code! It is so much easier to start with something that is working. This page gives a comprehensive list of things you should change to adapt the samples.

like image 74
Dale Avatar answered Oct 25 '22 02:10

Dale


First of all: You can do what you want to do (disable the touchpad on my laptop when a mouse is plugged in) in the user-mode. It will be much simpler and safer. Look at Using Device Installation Functions and WM_DEVICECHANGE

To debug problems in your code: Get a memory dump from BSOD or setup a kernel debugger connection (using a COM-port on your virtual PC redirected to a pipe). See Debugging Tools for Windows

Have fun!

like image 29
Sergey Podobry Avatar answered Oct 25 '22 02:10

Sergey Podobry