Loading a Windows Driver Class other than NetService to act as an NDIS Filter

769 views Asked by At

Is it possible to take a Windows driver such as a Ports class driver, then have it also set itself up as an NDIS filter (NetService class) driver by calling NdisFRegisterFilterDriver() in it's DriverEntry()? This would be essentially having the driver work double duty as a Ports and NetService class driver, but within a single code base and binary.

I'm attempting to do this and I'm seeing the call to register the NDIS driver fail, specifically with the following trace message:

[0][mp]<==ndisCreateFilterDriverRegistry, FilterServiceName 807EFA18 Status c0000001
[0][mp]==>NdisFRegisterFilterDriver: DriverObject 84C6C428      
[0][mp]==>ndisCreateFilterDriverRegistry, FilterServiceName 807EFA18
[0][mp]<==ndisCreateFilterDriverRegistry, FilterServiceName 807EFA18 Status c0000001

I've looked around and it seems that the NDIS driver is heavily dependent on the values placed in the registry from the INF and the INF itself. I've tried to spoof the registry keys by adding the NetCfgInstanceId by hand and calling that value out in my code before trying to register the NDIS filter, but have hit a point where it just seems like the wrong way to go about it.

What is the recommended way to go about this? At this point I'd imagine that this would require a Ports class driver and NetService class driver separately, with some kind of composite driver to tie them together to be able to communicate, or have a way for one or the other to communicate through interprocess communication.

1

There are 1 answers

1
Jeffrey Tippet On BEST ANSWER

A stern warning

Do not attempt to "install" a filter by manually writing registry keys. As you've noticed, it's not easy, and even if you seem to get it working, it will all collapse when the OS tries to install the next LWF. Furthermore, I added some additional hardening features designed exactly to prevent people from doing this to Windows 10; you'll have to do some significant damage to the OS before you can hijack network bindings in Windows 10.

How to structure your driver package

Anyway, what you're describing is indeed possible. The way to do it is to provide the following in your driver package:

  1. A PNP-style INF. This INF has:
    1. The PORTS class
    2. An AddService directive, that installs your driver service
    3. A CopyFiles directive to bring in any files you need
    4. Any other bits you need for the PNP device
  2. A NetCfg-style INF. This INF has:
    1. The NETSERVICE class
    2. The usual LWF stuff: Characteristics=0x40000, FilterMediaTypes=xxx, FilterType=xxx, etc.
    3. A reference to the service you installed in the other INF (HKR,Ndi,Service,,xxx)
    4. Do not include an AddService or CopyFiles; that's already taken care of by the first INF
  3. One .sys file. This driver does:
    1. In DriverEntry, call NdisFRegisterFilterDriver, and pass the name of your service "xxx"
    2. In DriverEntry, call WdfDriverCreate or fill out the DRIVER_OBJET dispatch table as you normally would for any other PNP driver
    3. Implement FilterAttach and etc normally; implement your WDF EvtXxx or WDM IRP handlers normally
    4. Don't forget to call NdisFDeregisterFilterDriver in EvtDriverUnload or DriverUnload, and also in the failure path for DriverEntry

How to install this fine mess

The good news is that, with these 2 INFs, you can meet your requirement of having 1 .sys file do two things. The bad news is that you've now got 2 INFs. Worse, one of the INFs is a NetCfg-style INF, so you can't just Include+Need it. The only way to install a NetCfg-style INF is to call INetCfgClassSetup::Install (or NetCfg.exe, its command-line wrapper). Windows Update only knows how to install PNP-style INFs, and PNP only knows how to Include other PNP-style INFs.

So the simplest solution is to ship an installer exe/msi that invokes the INetCfg API. If you can do that, it's simply a matter of a couple calls to SetupCopyOemInf and the INetCfg boilerplate that you can find in the bindview sample.

But, if you have to support a hardware-first installation, you need to bring out the big guns. You'll need to write a Co-Installer and include it with your driver package. The Co-Installer's job is to call the INetCfg APIs when your driver package is installed, and deregister when the package is uninstalled.

Co-Installers are generally discouraged, and are not supported for Universal drivers. So you should avoid a Co-Installer unless you've got no choice. Unfortunately I cannot think of any other way to register an NDIS LWF when a PNP device driver is installed through Windows Update. (This doesn't mean there isn't a crafty way to do it; I don't know everything.)

Note that you'd need a Co-Installer anyway even if you were shipping 2 .sys files. The need to call INetCfg doesn't change just because you merged the driver binaries.

Limitations

You'll have a full-fledged NDIS LWF driver, as well as a full-fledged PNP device driver. The only (minor) thing that doesn't work is that you cannot call NdisRegisterDeviceEx in this driver. The reason is that when you call NdisRegisterDeviceEx from a LWF, NDIS will attempt to co-opt your driver's dispatch table. But in this PNP+LWF dual driver, the dispatch table is owned by WDF or by you. This limitation is no problem, since you can call WdfDeviceCreate, and this routine is easier to use and has more features than the NDIS one anyway.

With the above configuration, the driver service is owned by PNP. That means the lifetime of your .sys file is owned by PNP. You cannot manually "net start" a PNP driver service; the only way to get your .sys file loaded is to actually enumerate your hardware. That means you can't have your NDIS LWF running when the hardware is not present. Typically this is what you'd want anyway. If it's not, you can try messing with the ServiceName directive, but there's some weird caveats with that, and I don't fully understand it myself.