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.
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:
PORTS
classAddService
directive, that installs your driver serviceCopyFiles
directive to bring in any files you needNETSERVICE
classCharacteristics=0x40000
,FilterMediaTypes=xxx
,FilterType=xxx
, etc.HKR,Ndi,Service,,xxx
)AddService
orCopyFiles
; that's already taken care of by the first INFDriverEntry
, callNdisFRegisterFilterDriver
, and pass the name of your service "xxx"DriverEntry
, callWdfDriverCreate
or fill out theDRIVER_OBJET
dispatch table as you normally would for any other PNP driverFilterAttach
and etc normally; implement your WDF EvtXxx or WDM IRP handlers normallyNdisFDeregisterFilterDriver
inEvtDriverUnload
orDriverUnload
, and also in the failure path forDriverEntry
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 callINetCfgClassSetup::Install
(orNetCfg.exe
, its command-line wrapper). Windows Update only knows how to install PNP-style INFs, and PNP only knows how toInclude
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 theINetCfg
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 callNdisRegisterDeviceEx
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 callWdfDeviceCreate
, 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.