Simple KMDF driver should work on Windows 10, version 1507 but does not

Hi folks!

I’ve recently upgraded a project to VS 2022 and Windows 11 SDK/WDK and tested the resulting build of a KMDF-based USB driver on Windows 10, version 1507 out of curiosity, because I remembered an open issue I addressed a while back.

Unfortunately it doesn’t want to load; if I give it the sc start ... command (it’s a filter so supposed to work) I get a cryptic error 127 (only reference I found online) which apparently hints at the required WDF version not being present?

Now I remember having the same issue with older WDKs, so I doubt it has anything to do with the upgrade however in the past I could “fix” this by setting the Target OS version to Windows 8, which is no longer possible. I also tried to clamp down the KMDF versions in the build properties, after all the driver doesn’t use any feature not available in 1.11 or 1.15, which is outlined in MS docs to be compatible with Windows 10 Threshold 1.

Any ideas on how I can further find the underlying issue? Maybe some preprocessor magic trickery my project settings are missing? I do not have to support version 1507 but I am curious to why it struggles to load despite having all the right build settings, according to official docs.

Thanks & cheers

If you can find a copy of depends.exe, that will quickly show you what is the undefined api in the target os image. Otherwise I’ve checked this out: https://github.com/lucasg/Dependencies and it appears to be equivalent.
You then have to fix up your driver to to test for or not use the offending apis.

@Mark_Roddy said:
If you can find a copy of depends.exe, that will quickly show you what is the undefined api in the target os image. Otherwise I’ve checked this out: https://github.com/lucasg/Dependencies and it appears to be equivalent.

I already did that and it is not particularly helpful since WDF is loaded dynamically.

@Mark_Roddy said:
You then have to fix up your driver to to test for or not use the offending apis.

If I knew what to fix I wouldn’t have made my post here :wink:

Currently setting up comparing builds in disassembly with 8.1 vs 10 copies of the same driver.

Also it is not something in my code; the KMDF driver template which ships with VS/WDK doesn’t load either.

I made a couple screenshots and experimented in parallel, will provide in a few.

Check the ExAllocatePool2() usage, which is available only on Win 10 2004+.

@tnodir said:
Check the ExAllocatePool2() usage, which is available only on Win 10 2004+.

Not used, no memory allocation API whatsoever, even the bare bones sample project the VS template generates has the same issue.

Project properties

Alright, so here’s more context. It is easy to reproduce, I simply let VS generate a brand new KMDF sample driver project:

Nothing special, nothing changed:

Test machine

Now we try to load the resulting package on Windows 10, 1507:

Binary differences

Some details I’ve gathered so far about what happens when the Target OS is Windows 8.1 v. 10:

Exports

Managed to find a difference! The WppRecorder.sys export imp_WppRecorderReplay doesn’t exist on 1507 vs, my reference machine running 21H2. Details of gathered findings ahead.

Imports

Sample Driver, Windows 8.1 build

WdfVersionBind, WDFLDR.SYS, False, None
WdfVersionBindClass, WDFLDR.SYS, False, None
WdfVersionUnbind, WDFLDR.SYS, False, None
WdfVersionUnbindClass, WDFLDR.SYS, False, None
WppAutoLogStart, WppRecorder.sys, False, None
WppAutoLogStop, WppRecorder.sys, False, None
WppAutoLogTrace, WppRecorder.sys, False, None

Sample Driver, Windows 10 build

WdfLdrQueryInterface, WDFLDR.SYS, False, None
WdfVersionBind, WDFLDR.SYS, False, None
WdfVersionBindClass, WDFLDR.SYS, False, None
WdfVersionUnbind, WDFLDR.SYS, False, None
WdfVersionUnbindClass, WDFLDR.SYS, False, None
WppAutoLogStart, WppRecorder.sys, False, None
WppAutoLogStop, WppRecorder.sys, False, None
WppAutoLogTrace, WppRecorder.sys, False, None
imp_WppRecorderReplay, WppRecorder.sys, False, None

Exports

WdfLdr.sys

Windows 10, version 1507

WdfVersionUnbindClass, 0x000076a0, None
WdfLdrDiagnosticsValueByNameAsULONG, 0x00001e20, None
WdfLdrQueryInterface, 0x000075b0, None
WdfRegisterClassLibrary, 0x0000d180, None
WdfRegisterLibrary, 0x0000d030, None
WdfVersionBind, 0x0000d2d0, None
WdfVersionBindClass, 0x00001670, None
WdfVersionUnbind, 0x0000d000, None

Windows 10, version 21H2

WdfLdrDiagnosticsValueByNameAsULONG, 0x000020f0, None
WdfLdrQueryInterface, 0x00006400, None
WdfRegisterClassLibrary, 0x0000d2f0, None
WdfRegisterLibrary, 0x0000d180, None
WdfVersionBind, 0x0000d460, None
WdfVersionBindClass, 0x000017f0, None
WdfVersionUnbind, 0x0000dc20, None
WdfVersionUnbindClass, 0x00006510, None

WppRecorder.sys

Windows 10, version 1507

WppAutoLogStart, 0x00001660, None
WppAutoLogStop, 0x000018f0, None
WppAutoLogTrace, 0x00001960, None
imp_WppRecorderConfigure, 0x00001af0, None
imp_WppRecorderDumpLiveDriverData, 0x00001e30, None
imp_WppRecorderGetCounterHandle, 0x00001d90, None
imp_WppRecorderGetTriageInfo, 0x00001e00, None
imp_WppRecorderIsDefaultLogAvailable, 0x00001bd0, None
imp_WppRecorderLinkCounters, 0x00001da0, None
imp_WppRecorderLogCreate, 0x00001c10, None
imp_WppRecorderLogDelete, 0x00001d60, None
imp_WppRecorderLogDumpLiveData, 0x00001e70, None
imp_WppRecorderLogGetDefault, 0x00001bb0, None
imp_WppRecorderLogSetIdentifier, 0x00001d10, None

Windows 10, version 21H2

WppAutoLogStart, 0x00001d60, None
WppAutoLogStop, 0x00001a10, None
WppAutoLogTrace, 0x00001130, None
imp_WppRecorderConfigure, 0x00003040, None
imp_WppRecorderDumpLiveDriverData, 0x00003150, None
imp_WppRecorderGetCounterHandle, 0x00003180, None
imp_WppRecorderGetTriageInfo, 0x000031a0, None
imp_WppRecorderIsDefaultLogAvailable, 0x000031e0, None
imp_WppRecorderLinkCounters, 0x00003220, None
imp_WppRecorderLogCreate, 0x000021c0, None
imp_WppRecorderLogDelete, 0x00001b30, None
imp_WppRecorderLogDumpLiveData, 0x00003280, None
imp_WppRecorderLogGetDefault, 0x00002290, None
imp_WppRecorderLogSetIdentifier, 0x00001d20, None
imp_WppRecorderReplay, 0x000040d0, None

Yep, that was it. Disabled Inflight Trace Recorder, rebuilt, works!

1 Like

FYI the WppRecorder.sys export imp_WppRecorderReplay was first introduced in Windows 10 version 1607.

You can probably use mmgetsystemroutineaddress to determine at runtime if ifr is available.

@Mark_Roddy said:
You can probably use mmgetsystemroutineaddress to determine at runtime if ifr is available.

This doesn’t work due to the following reasons:

  • MmGetSystemRoutineAddress can not resolve functions not directly exported by NtosKrnl.exe or HAL
  • The WDK-delivered property sheets link against wpprecorder.lib which drags in a direct dependency to WppRecorder.sys, therefore failing driver load before even one line of DriverEntry gets executed

So what I ended up doing is a little trickery; I made my own wpprecorder.lib with imp_WppRecorderReplay patched out and then added the following to the project file, throwing out linking against the WDK edition of wpprecorder.lib:

  <!-- this undoes the automatic linking of the WDK version of "wpprecorder.lib" so we can slip in our own instead -->
  <Target Name="RemoveWppRecorder" AfterTargets="SetLinkLibraryPaths">
    <ItemGroup>
      <Link>
        <AdditionalDependencies>$(ProjectDir)libs\$(PlatformName)\WppRecorder.lib;$(DmfRootPath)\$(Configuration)\$(PlatformName)\lib\DmfK\DmfK.lib;$(DmfRootPath)\$(Configuration)\$(PlatformName)\individual_libs\DmfKModules.Template\DmfKModules.Template.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfLdr.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfDriverEntry.lib</AdditionalDependencies>
        <AdditionalDependencies Condition="'$(Platform)'=='ARM64'">
		%(AdditionalDependencies);arm64rt.lib
		</AdditionalDependencies>
      </Link>
    </ItemGroup>
  </Target>

I bet there is a more elegant way to remove a specific library from the list but after hours of trial and error with the quirky MSBuild syntax I decided to settle on the easy solution :grin:

I then defined my own function body to satisfy the linker and resolve that one function during runtime instead:

//
// This satisfies the linker when looking for the import we patched out of "wpprecorder.lib"
//
VOID
imp_WppRecorderReplay(
    _In_ PVOID       WppCb,
    _In_ TRACEHANDLE WppTraceHandle,
    _In_ ULONG       EnableFlags,
    _In_ UCHAR       EnableLevel
)
{
    //
    // Export not available, nothing to do
    // 
    if (!G_WppRecorderReplay)
    {
        return;
    }

    //
    // Forward call to export driver
    // 
    G_WppRecorderReplay(WppCb, WppTraceHandle, EnableFlags, EnableLevel);
}

You can find the code I crafted to do the resolving published here.

The resulting binary no longer has the offending dependency: