0

everybody. I have ported the famous packet capture software WinPcap from the NDIS 5 protocol to an NDIS 6 LWF. Everything is OK under Win7. However, the FilterAttach routine is never called under Win8. I found NdisFRegisterFilterDriver invoke in DriverEntry returns NDIS_STATUS_SUCCESS, this is so strange. Can anyone help me? thx!

Here's the code of DriverEntry

_Use_decl_annotations_
NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
{
    NDIS_FILTER_DRIVER_CHARACTERISTICS FChars;
    NTSTATUS Status = STATUS_SUCCESS;
//  NDIS_STRING FriendlyName = NDIS_STRING_CONST("WinPcap NDIS LightWeight Filter");
//  NDIS_STRING UniqueName   = NDIS_STRING_CONST("{5cbf81bd-5055-47cd-9055-a76b2b4e2637}"); //unique name, quid name
//  NDIS_STRING ServiceName = NDIS_STRING_CONST("npf6x"); //this to match the service name in the INF
    NDIS_STRING FriendlyName = RTL_CONSTANT_STRING(L"WinPcap NDIS LightWeight Filter");
    NDIS_STRING UniqueName   = RTL_CONSTANT_STRING(L"{5cbf81bd-5055-47cd-9055-a76b2b4e2637}"); //unique name, quid name
    NDIS_STRING ServiceName = RTL_CONSTANT_STRING(L"npf6x"); //this to match the service name in the INF
    WCHAR* bindT;
    PKEY_VALUE_PARTIAL_INFORMATION tcpBindingsP;
    UNICODE_STRING macName;
    ULONG OsMajorVersion, OsMinorVersion;

    TRACE_ENTER();

    UNREFERENCED_PARAMETER(RegistryPath);

    FilterDriverObject = DriverObject;

    //
    // Get OS version and store it in a global variable. 
    //
    // Note: both RtlGetVersion() and PsGetVersion() are documented to always return success.
    //
    //  OsVersion.dwOSVersionInfoSize = sizeof(OsVersion);
    //  RtlGetVersion(&OsVersion);
    //
    PsGetVersion(&OsMajorVersion, &OsMinorVersion, NULL, NULL);
    TRACE_MESSAGE2(PACKET_DEBUG_INIT, "OS Version: %d.%d\n", OsMajorVersion, OsMinorVersion);


    NdisInitUnicodeString(&g_NPF_Prefix, g_NPF_PrefixBuffer);

    //
    // Get number of CPUs and save it
    //
#ifdef NDIS620
    g_NCpu = NdisGroupMaxProcessorCount(ALL_PROCESSOR_GROUPS);
#else
    g_NCpu = NdisSystemProcessorCount();
#endif

    //
    // TODO: Most handlers are optional, however, this sample includes them
    // all for illustrative purposes.  If you do not need a particular 
    // handler, set it to NULL and NDIS will more efficiently pass the
    // operation through on your behalf.
    //

    //
    // Register as a service with NDIS
    //
    NdisZeroMemory(&FChars, sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS));
    FChars.Header.Type = NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS;
    FChars.Header.Size = sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS);
#if NDIS_SUPPORT_NDIS61
    FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_2;
#else
    FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_1;
#endif

    FChars.MajorNdisVersion = NDIS_FILTER_MAJOR_VERSION;
    FChars.MinorNdisVersion = NDIS_FILTER_MINOR_VERSION;
    FChars.MajorDriverVersion = 1;
    FChars.MinorDriverVersion = 0;
    FChars.Flags = 0;

    FChars.FriendlyName = FriendlyName;
    FChars.UniqueName = UniqueName;
    FChars.ServiceName = ServiceName;

    FChars.SetOptionsHandler = NPF_RegisterOptions;
    FChars.AttachHandler = NPF_Attach;
    FChars.DetachHandler = NPF_Detach;
    FChars.RestartHandler = NPF_Restart;
    FChars.PauseHandler = NPF_Pause;
    FChars.SetFilterModuleOptionsHandler = NPF_SetModuleOptions;
    FChars.OidRequestHandler = NPF_OidRequest;
    FChars.OidRequestCompleteHandler = NPF_OidRequestComplete;
    FChars.CancelOidRequestHandler = NPF_CancelOidRequest;

    FChars.SendNetBufferListsHandler = NPF_SendEx;
    FChars.ReturnNetBufferListsHandler = NPF_ReturnEx;
    FChars.SendNetBufferListsCompleteHandler = NPF_SendCompleteEx;
    FChars.ReceiveNetBufferListsHandler = NPF_TapEx;
    FChars.DevicePnPEventNotifyHandler = NPF_DevicePnPEventNotify;
    FChars.NetPnPEventHandler = NPF_NetPnPEvent;
    FChars.StatusHandler = NPF_Status;
    FChars.CancelSendNetBufferListsHandler = NPF_CancelSendNetBufferLists;

    DriverObject->DriverUnload = NPF_Unload;

    //
    // Initialize spin locks
    //
    //NdisAllocateSpinLock(&FilterListLock);

    //InitializeListHead(&FilterModuleList);


    // 
    // Standard device driver entry points stuff.
    //
    DriverObject->MajorFunction[IRP_MJ_CREATE] = NPF_OpenAdapter;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = NPF_CloseAdapter;
    DriverObject->MajorFunction[IRP_MJ_CLEANUP] = NPF_Cleanup; 
    DriverObject->MajorFunction[IRP_MJ_READ] = NPF_Read;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = NPF_Write;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = NPF_IoControl;

    bindP = getAdaptersList();

    if (bindP == NULL)
    {
        TRACE_MESSAGE(PACKET_DEBUG_INIT, "Adapters not found in the registry, try to copy the bindings of TCP-IP.");

        tcpBindingsP = getTcpBindings();

        if (tcpBindingsP == NULL)
        {
            TRACE_MESSAGE(PACKET_DEBUG_INIT, "TCP-IP not found, quitting.");
            goto RegistryError;
        }

        bindP = (WCHAR *)tcpBindingsP;
        bindT = (WCHAR *)(tcpBindingsP->Data);
    }
    else
    {
        bindT = bindP;
    }

    for (; *bindT != UNICODE_NULL; bindT += (macName.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR))
    {
        RtlInitUnicodeString(&macName, bindT);
        NPF_CreateDevice(DriverObject, &macName);
    }


    Status = NdisFRegisterFilterDriver(DriverObject,
        (NDIS_HANDLE) FilterDriverObject,
        &FChars,
        &FilterDriverHandle);
    if (Status != NDIS_STATUS_SUCCESS)
    {
        TRACE_MESSAGE(PACKET_DEBUG_INIT, "Failed to register filter with NDIS.");
        TRACE_EXIT();
        return Status;
    }


    TRACE_EXIT();
    return STATUS_SUCCESS;

    RegistryError : NdisFDeregisterFilterDriver(FilterDriverHandle);

    Status = STATUS_UNSUCCESSFUL;
    TRACE_EXIT();
    return(Status);
}
hsluoyz
  • 2,739
  • 5
  • 35
  • 59

2 Answers2

1

Why your FilterAttach routine isn't called — I don't know. I can't think of any significant differences between Windows 7 and Windows 8. (On the other hand, Windows 8.1 Preview does have some substantial binding changes.)

Check if the filter is bound in usermode. Use Get-NetAdapterBinding from powershell to ensure that there is a binding from the NIC to your filter, and that the binding is enabled.

Verify the miniports are started normally. Use !ndiskd.miniport to see if the miniports are otherwise bound normally. Check if your filter is listed on the miniport's list of filter bindings.

A couple unrelated notes:

  • I don't think the RegistryError label should call NdisFDeregisterFilterDriver, since the filter wouldn't have been registered with NDIS yet.
  • Code like getAdaptersList and getTcpBindings sounds scary, but I suppose that's probably pre-existing code from the old driver. Note that we don't support rummaging around in the registry, and would rather you use INetCfg in usermode to discover adapters. For a LWF, we prefer that your filter is always bound to all capable adapters. If perf is a concern, the LWF can optionally insert/remove itself dynamically into the datapath as needed, using NdisFRestartFilter and NdisSetOptionalHandlers.
Jeffrey Tippet
  • 3,146
  • 1
  • 14
  • 15
  • 1) I executed "Get-NetAdapterBinding" command and I saw my driver, it is enabled, just as the Properties window of the adpater. 2)the NdisFDeregisterFilterDriver call after the RegistryError label is a bug, I fixed it. 3) getAdaptersList and getTcpBindings are from the old code, I will see if we have time to improve it. 4) For the binding, I have binded all capable adapters in the current FilterAttach code, no matter whether used or not. The NPF_OpenAdapter function called by the ring3 is not a "real" open adapter function now. I just assign the stored handle to it. – hsluoyz Aug 21 '13 at 08:28
  • I cannot check the !ndiskd.miniport command result for now, because another problem occurred. My driver cannot even be installed on another Windows 8 machine. Here's the new post: http://stackoverflow.com/questions/18352982/why-ndisfregisterfilterdriver-failed-with-ndis-status-failure-under-windows-8 – hsluoyz Aug 21 '13 at 08:56
  • In the context of NdisFDeregisterFilterDriver call, the NPF_RegisterOptions(FilterSetOptions) function is called, it nearly did nothing but returned NDIS_STATUS_SUCCESS. Then NdisFDeregisterFilterDriver returned NDIS_STATUS_SUCCESS. Everything seems to be normal except that FilterAttach routine is never called. – hsluoyz Aug 26 '13 at 14:58
  • Hi, I found where is wrong, it seems to be the inf file problem. When I set StartType in the [NPF_Service_Inst] section from 3 (SERVICE_DEMAND_START) to 1 (SERVICE_SYSTEM_START), the FilterAttach is just called normally!! Then I unload the driver, change StartType from 1 to 3 again, install it and FilterAttach is called too. So it seems that you cann't run the driver with StartType 3 for the first time, which is so strange. In fact, I want to use the StartType 3, as the user can choose whether to run the driver immediatelly after WinPcap is installed. – hsluoyz Aug 27 '13 at 09:48
  • I found the official example "ndislwf" also has this problem: if I changed the StartType of netlwf.inf to 3, and run the command "net start ndislwf" manually, then its FilterAttach will not be called. I want to know why StartType = 3 will affect the NDIS filter like this? Does it mean that an NDIS filter can not use StartType = 3? – hsluoyz Aug 27 '13 at 09:59
1

Thanks for the additional diagnostic information that you gave in the comments. Your description of the problem has allowed me to isolate this as a bug in Windows.

The problem occurs when first installing a filter. NDIS may, in some cases, ignore the notification that the bindings on a miniport have changed, if the filter driver hasn't started in kernelmode yet.

There are several workarounds - as you've noted, one workaround is to change the StartType of the filter. Another workaround would be to use INetCfg to disable and re-enable the filter bindings to each miniport after installation. You can also disable and re-enable the miniports, or reboot the computer, although those are rather disruptive workarounds.

✓ This bug does not affect Windows 7.
✗ This bug affects Windows 8 and Windows Server 2012.
✓ This bug does not affect Windows 8.1 and Windows Server 2012 R2. (I unknowingly fixed this bug while cleaning up some code in NDIS.)

If you cannot wait for the free Windows 8.1 update to roll out to all Windows 8 machines, you can contact Microsoft WDK support. Please reference WindowsSE:452306 so that they can find my notes on the issue, or have them contact me internally.

//
// DisableEnableBindings
//
// Purpose:  This code can be used to quickly disable/enable all bindings to a particular
// NDIS protocol or filter.
//
// Usage:  Run this and provide the name of a NetCfg component.  For example, "ms_pacer".

#include <Windows.h>
#include <netcfgx.h>

#include <atlbase.h>
#include <atlcom.h>

#include <stdio.h>
#include <vector>

#define MY_APP_NAME L"DisableEnableBindings test app"

bool RestartAllBindings(INetCfg *netcfg, PCWSTR name)
{
    HRESULT hr;
    CComPtr<INetCfgComponent> comp;
    CComPtr<INetCfgComponentBindings> bindings;

    hr = netcfg->FindComponent(name, &comp);
    if (FAILED(hr))
    {
        wprintf(L"INetCfg::FindComponent 0x%08x\n", hr);
        return false;
    }

    hr = comp.QueryInterface(&bindings);
    if (FAILED(hr))
    {
        wprintf(L"QueryInterface(INetCfgComponentBindings) 0x%08x\n", hr);
        return false;
    }

    CComPtr<IEnumNetCfgBindingPath> enumerator;
    hr = bindings->EnumBindingPaths(EBP_BELOW, &enumerator);
    if (FAILED(hr))
    {
        wprintf(L"INetCfgComponentBindings::EnumBindingPaths 0x%08x\n", hr);
        return false;
    }

    // Loop over all bindings that involve this component
    while (true)
    {
        CComPtr<INetCfgBindingPath> path;
        hr = enumerator->Next(1, &path, nullptr);
        if (hr == S_FALSE)
        {
            // Reached end of list; quit.
            break;
        }
        if (FAILED(hr))
        {
            wprintf(L"IEnumNetCfgBindingPath::Next 0x%08x\n", hr);
            return false;
        }

        PWSTR token = nullptr;
        hr = path->GetPathToken(&token);
        if (FAILED(hr))
        {
            wprintf(L"INetCfgBindingPath::GetPathToken 0x%08x\n", hr);
            continue;
        }

        wprintf(L"Found binding %s\n", token);
        CoTaskMemFree(token);

        hr = path->IsEnabled();
        if (FAILED(hr))
        {
            wprintf(L"INetCfgBindingPath::IsEnabled 0x%08x\n", hr);
            continue;
        }

        if (S_FALSE == hr)
        {
            wprintf(L"\tPath is already disabled.  Skipping over it.\n");
            continue;
        }

        // Diable

        hr = path->Enable(FALSE);
        if (FAILED(hr))
        {
            wprintf(L"INetCfgBindingPath::Enable(FALSE) 0x%8x\n", hr);
            continue;
        }

        hr = netcfg->Apply();
        if (FAILED(hr))
        {
            wprintf(L"INetCfg::Apply 0x%08x\n", hr);
            return false;
        }

        wprintf(L"\tPath disabled\n");

        // Enable

        hr = path->Enable(TRUE);
        if (FAILED(hr))
        {
            wprintf(L"INetCfgBindingPath::Enable(TRUE) 0x%8x\n", hr);
            return false;
        }

        hr = netcfg->Apply();
        if (FAILED(hr))
        {
            wprintf(L"INetCfg::Apply 0x%08x\n", hr);
            return false;
        }

        wprintf(L"\tPath enabled\n");
    }



    return true;
}

bool ConnectToNetCfg(PCWSTR name)
{
    HRESULT hr;
    CComPtr<INetCfg> netcfg;
    CComPtr<INetCfgLock> lock;

    // Before we can get started, we need to do some initialization work.

    hr = netcfg.CoCreateInstance(CLSID_CNetCfg);
    if (FAILED(hr))
    {
        wprintf(L"CoCreateInstance(CLSID_CNetCfg 0x%08x\n", hr);
        return false;
    }

    hr = netcfg.QueryInterface(&lock);
    if (FAILED(hr))
    {
        wprintf(L"QueryInterface(INetCfgLock) 0x%08x\n", hr);
        return false;
    }

    // Note that this call can block.
    hr = lock->AcquireWriteLock(INFINITE, MY_APP_NAME, nullptr);
    if (FAILED(hr))
    {
        wprintf(L"INetCfgLock::AcquireWriteLock 0x%08x\n", hr);
        return false;
    }

    hr = netcfg->Initialize(nullptr);
    if (FAILED(hr))
    {
        wprintf(L"INetCfg::Initialize 0x%08x\n", hr);
        return false;
    }

    bool ok = RestartAllBindings(netcfg.p, name);

    hr = netcfg->Uninitialize();
    if (FAILED(hr))
    {
        wprintf(L"INetCfg::Uninitialize 0x%08x\n", hr);
    }

    hr = lock->ReleaseWriteLock();
    if (FAILED(hr))
    {
        wprintf(L"INetCfgLock::ReleaseWriteLock 0x%08x\n", hr);
    }

    return ok;
}

int wmain(int argc, wchar_t **argv)
{
    if (argc != 2)
    {
        wprintf(L"Usage: DisableEnableBindings <componentId>\n");
        return 2;
    }

    PCWSTR name = argv[1];

    CComPtr<INetCfg> netcfg;
    CComPtr<INetCfgLock> lock;

    HRESULT hr;

    hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr))
    {
        wprintf(L"CoInitializeEx 0x%08x\n", hr);
        return 1;
    }

    bool ok = ConnectToNetCfg(name);

    CoUninitialize();

    wprintf(ok ? L"Succeeded.\n" : L"Failed.\n");

    return ok ? 0 : 1;
}
Jeffrey Tippet
  • 3,146
  • 1
  • 14
  • 15
  • Hi, I don't want to use the StartType=1 way because you know there's an option in the original WinPcap installer for choosing whether to start the driver immediately, and I don't want to change it for now. What do you mean by "use INetCfg to disable and re-enable the filter bindings"? Is this a costly way? I don't want my capture software to impact other modules' functions in the system. Also I didn't find a proper place to ask for the Microsoft WDK support or report bugs, some webpages just charge for a product code or need to be paid. Do you have any recommendations for this? Thx – hsluoyz Aug 28 '13 at 13:01
  • Well, the INetCfg approach isn't costly in terms of user impact. It might take a few seconds of time to run, but that's probably not a huge cost for an install program. Let me update the answer above with some example INetCfg code that does this. – Jeffrey Tippet Aug 28 '13 at 22:29