How does ntoskrnl load the port drivers without creating Driver Objects?

Hello everyone, I’m studying about port drivers and trying to learn how they work. now As you all know, port drivers are like kernel libraries, and therefore they don’t have a driver object, so this means that they will not get loaded by standard methods of loading a driver because using normal APIs to create load the driver will cause the kernel to create a driver object for them.

so this got me wondering, How does ntoskrnl load the port drivers without creating Driver Objects?

Port (as in the port/mini-port model) are typically implemented as kernel-mode DLLs.

That should solve the mystery for you,

Peter

@“Peter_Viscarola_(OSR)” said:
Port (as in the port/mini-port model) are typically implemented as kernel-mode DLLs.

That should solve the mystery for you,

Peter

So what is the proper way of loading the kernel dlls, how does the ntoskrnl do it ? tried reading the https://www.osronline.com/article.cfm^id=171.htm but it didn’t explain the proper way of doing it, should i straight up call zwLoadDriver or…?

You’re making this much harder than it really is. It works exactly the same way it does in user mode. You understand how implicit linking works in user mode? You don’t DO anything. Your miniport code calls StorPortInitialize. Your makefile adds storport.lib as one of the libraries. That’s a DLL import library. The entry for StorPortInitialize tells the linker that the entry point is satisfied by the StorPortInitialize export from storport.sys, and it embeds that in the PE.

When your driver gets loaded by PnP, the kernel goes through the list of imported symbols. IoCallDriver gets satisfied by loading and linking to ntoskrnl.exe, and StorPortInitialize gets satisfied by loading and linking to storport.sys. All automatic.

@Tim_Roberts said:
You’re making this much harder than it really is. It works exactly the same way it does in user mode. You understand how implicit linking works in user mode? You don’t DO anything. Your miniport code calls StorPortInitialize. Your makefile adds storport.lib as one of the libraries. That’s a DLL import library. The entry for StorPortInitialize tells the linker that the entry point is satisfied by the StorPortInitialize export from storport.sys, and it embeds that in the PE.

When your driver gets loaded by PnP, the kernel goes through the list of imported symbols. IoCallDriver gets satisfied by loading and linking to ntoskrnl.exe, and StorPortInitialize gets satisfied by loading and linking to storport.sys. All automatic.

I know, but i want to learn how its working under the hood, how does the kernel load the “kernel dll”? because a normal driver load should cause a DRIVER_OBJECT to get created, but loading a kernel dll doesn’t, so i assume there should be a difference between loading a normal driver vs loading a kernel mode dll? so again, i know that when i write a driver and import a function from another module, the kernel does all magic of loading the other module if its not already loaded, but i want to learn how? does it call the zwLoadDriver? if so doesn’t that cause a driver_object to get created? there has to be a difference between loading a normal kernel driver vs a kernel dll.

@Tim_Roberts said:
You’re making this much harder than it really is. It works exactly the same way it does in user mode. You understand how implicit linking works in user mode? You don’t DO anything. Your miniport code calls StorPortInitialize. Your makefile adds storport.lib as one of the libraries. That’s a DLL import library. The entry for StorPortInitialize tells the linker that the entry point is satisfied by the StorPortInitialize export from storport.sys, and it embeds that in the PE.

When your driver gets loaded by PnP, the kernel goes through the list of imported symbols. IoCallDriver gets satisfied by loading and linking to ntoskrnl.exe, and StorPortInitialize gets satisfied by loading and linking to storport.sys. All automatic.

I know, but i want to learn how its working under the hood, there has to be a difference between how the kernel loads a kernel driver vs a kernel dll, because in the normal way of loading a driver like calling the zwLoadDriver will cause a DRIVER_OBJECT to get created, but kernel dlls don’t have a driver object, so there has to be a difference? what is the equivalent of LoadLibrary in kernel?

There is a DRIVER_OBJECT for storport.sys. It was just created long, long ago, when the system booted. When your driver loads, the system looks up the loaded driver list by name, finds your entry points, and stuffs the addresses.

There is a call that used to be (may still be but things have changed in this area over the years) that was an option of ZwSetSystemInformation. The problem is it is undocumented, and the information about ZwSetSystemInformation has a lot of errors. This is one of these just accept that it works unless you really have a very specific reason to do something more, and then you better explain what that is if you want any help.

There is no equivalent of LoadLibrary in the kernel. ZwLoadDriver will create a PDRIVER_OBJECT. The kernel does not export an API to load a driver as a DLL. When the kernel sees an import in your driver’s IAT and that import doesn’t resolve to the HAL or the kernel or an already loaded export driver, it will attempt to resolve the import by loading that driver as an export driver (e.g. a DLL) using a private function (not an export). This maps the export driver’s image into memory and then calls the DLL entrypoint (typically DllInitialize).

@Tim_Roberts said:
There is a DRIVER_OBJECT for storport.sys. It was just created long, long ago, when the system booted. When your driver loads, the system looks up the loaded driver list by name, finds your entry points, and stuffs the addresses.

But even when I’m debugging a kernel from the start of the boot, the storport/scsiport doesn’t seem to have a driver object when i use !drvobj? so why can’t windbg find its corresponding driver object and how can i find it then?

@Don_Burn said:
There is a call that used to be (may still be but things have changed in this area over the years) that was an option of ZwSetSystemInformation. The problem is it is undocumented, and the information about ZwSetSystemInformation has a lot of errors. This is one of these just accept that it works unless you really have a very specific reason to do something more, and then you better explain what that is if you want any help.

so are you saying by using this undocumented type of call to ZwSetSystemInformation i will be able to find the driver object of port drivers?

@Doron_Holan said:
There is no equivalent of LoadLibrary in the kernel. ZwLoadDriver will create a PDRIVER_OBJECT. The kernel does not export an API to load a driver as a DLL. When the kernel sees an import in your driver’s IAT and that import doesn’t resolve to the HAL or the kernel or an already loaded export driver, it will attempt to resolve the import by loading that driver as an export driver (e.g. a DLL) using a private function (not an export). This maps the export driver’s image into memory and then calls the DLL entrypoint (typically DllInitialize).

So at the end of the day, there is no difference between loading a normal kernel driver vs a kernel dll right? the kernel just loads it into memory, resolves its IAT/EAT, relocates the code, then calls its DriverEntry, just like any other kernel driver, correct?

Calls its DllInitialize (and DllUnload on unload).

AFAIK, ZwSetSystemInformation is used only to load win32k.sys and there is a check for that.

@Tim_Roberts said:
There is a DRIVER_OBJECT for storport.sys. It was just created long, long ago, when the system booted. When your driver loads, the system looks up the loaded driver list by name, finds your entry points, and stuffs the addresses.

Mr Roberts, i still couldn’t find the answer to this question : if there is a driver object for port drivers like storport, then why can’t windbg find them, even when I’m debugging from start of boot? how can i find them with windbg because using !drvobj will not find them.

If the port driver is only loaded as an export driver, there is no driver object.

1 Like