Window NDIS 协议驱动开发

1. 注册 Ndis 协议驱动

NdisRegisterProtocol

VOID NdisRegisterProtocol(
  _Out_ PNDIS_STATUS                   Status,
  _Out_ PNDIS_HANDLE                   NdisProtocolHandle,
  _In_  PNDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics,
  _In_  UINT                           CharacteristicsLength
);

在 DriverEntry 中,调用 NdisRegisterProcotol 通知内核注册协议驱动。
除此之外,还需要修改注册表

HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\000n\Linkage
UpperBind:REG_MULTI_SZ:xxx\nProcotolName\n

1. n 的值取决于
HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\000n\Ndi\Interfaces
LowerRange:REG_SZ == ethernet
如果有多张网卡则需要都修改才能都绑定协议驱动,否则只有修改的才能绑定上

2. ProcotolName 的值取决于 NdisRegisterProtocol 函数的 ProtocolCharacteristics 参数中 Name 这一属性

1.1 注册方法

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, 
                     IN PUNICODE_STRING RegistryPath) {
    NDIS_PROTOCOL_CHARACTERISTICS PChars;
    NDIS_STATUS                   Status;
    NDIS_STRING                   Name;
    PNDIS_HANDLE                  ndisHandle;
    UNREFERENCED_PARAMETER(DriverObject);
    UNREFERENCED_PARAMETER(RegistryPath);

    NdisZeroMemory(&gGlobal, sizeof(Global));
    InitializeListHead(&gGlobal.AdapterList);
    KeInitializeSpinLock(&gGlobal.AdapterListLock);

    NdisInitUnicodeString(&Name, NDIS_PROTOL_NAME);
    NdisZeroMemory(&PChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
    PChars.MajorNdisVersion = 5;
    PChars.MinorNdisVersion = 0;
    PChars.Name = Name;
    PChars.OpenAdapterCompleteHandler = ndisCallback_OpenAdapterCompleteHandler;
    PChars.CloseAdapterCompleteHandler = ndisCallback_CloseAdapterCompleteHandler;
    PChars.SendCompleteHandler = ndisCallback_SendCompleteHandler;
    PChars.TransferDataCompleteHandler = ndisCallback_TransferDataCompleteHandler;
    PChars.ResetCompleteHandler = ndisCallback_ResetCompleteHandler;
    PChars.RequestCompleteHandler = ndisCallback_RequestCompleteHandler;
    PChars.ReceiveHandler = ndisCallback_ReceiveHandler;
    PChars.ReceiveCompleteHandler = ndisCallback_ReceiveCompleteHandler;
    PChars.StatusHandler = ndisCallback_StatusHandler;
    PChars.StatusCompleteHandler = ndisCallback_StatusCompleteHandler;
    PChars.BindAdapterHandler = ndisCallback_BindAdapterHandler;
    PChars.UnbindAdapterHandler = ndisCallback_UnbindAdapterHandler;
    PChars.PnPEventHandler = ndisCallback_PnPEventHandler;

    NdisRegisterProtocol(&Status, &gGlobal.ProcotolHandle, &PChars, sizeof(PChars));

    return NDIS_STATUS_SUCCESS == Status ? STATUS_SUCCESS : NDIS_STATUS_FAILURE;
}

至此,对于协议驱动的注册已经全部完成,回调函数部分在后面的章节进行说明
后续的所有回调函数的说明都基于 DDK 提供的例子和公司大师兄的项目以及自己编写的 Demo 和理解构成 = =

1.1.1 OpenAdapterCompleteHandler 回调函数

VOID OpenAdapterCompleteHandler(
  _In_ NDIS_HANDLE ProtocolBindingContext,
  _In_ NDIS_STATUS Status,
  _In_ NDIS_STATUS OpenErrorStatus
)

函数说明:

如名字一般的意思,当打开适配器完成时的回调函数。
在参考源码中,发现在 BindAdapterHandler 中,尝试打开网卡设备的时候,有可能会出现 PENDING 的状态,这时候根据业务逻辑,可能会需要在 BindAdapterHandler 中等待 PENDING 的结束,这时候就需要利用 Event 和这个回调函数进行同步。

参数:

  1. ProcotolBindingContext
    在 BindAdapterHandler 回调函数中,打开网卡时手动设置的绑定变量。
  2. Status
    打开适配器的结果。
  3. OpenErrorStatus
    如果 Status != NDIS_STATUS_SUCCESS 时,对错误状态的附加说明。
PS : 需要注意的是,后续绝大部分的回调函数的第一个参数都是 ProtocolBindingContext, 这个变量是在 BindAdapterHandler 函数中,打开网卡的时候绑定的自定义变量。

Demo :

VOID ndisCallback_OpenAdapterCompleteHandler(
    __in NDIS_HANDLE ProtocolBindingContext,
    __in NDIS_STATUS Status,
    __in NDIS_STATUS OpenErrorStatus
) {
    PAdapterData pAdapt = (PAdapterData)ProtocolBindingContext;
    UNREFERENCED_PARAMETER(OpenErrorStatus);
    pAdapt->BindStatus = Status;
    NdisSetEvent(&pAdapt->BindEvent);
}

1.1.2 CloseAdapterCompleteHandler 回调函数

VOID ProtocolCloseAdapterComplete(
  _In_ NDIS_HANDLE ProtocolBindingContext,
  _In_ NDIS_STATUS Status
)

函数说明:

当关闭适配器完成时的回调函数。
在参考源码中,发现在 UnbindAdapterHandler 中,在关闭网卡设备的时候,有可能会出现 PENDING 的状态,这时候根据业务逻辑,可能会需要在 UnbindAdapterHandler 中等待 PENDING 的结束,这时候就需要利用 Event 和这个回调函数进行同步。

参数:

  1. ProcotolBindingContext
    在 BindAdapterHandler 回调函数中,打开网卡时手动设置的绑定变量。
  2. Status
    关闭适配器的结果。

——————————————— 待续 ————————————————

1.1.3 SendCompleteHandler 回调函数

VOID ProtocolSendComplete(
  _In_ NDIS_HANDLE  ProtocolBindingContext,
  _In_ PNDIS_PACKET Packet,
  _In_ NDIS_STATUS  Status
)

函数说明:

当协议驱动调用 NdisSendPacketsNdisSend 发送数据时候,如果函数返回值为 PENDING 则当数据发送结束的时候会调用这个回调函数。

参数:

  1. ProcotolBindingContext
    在 BindAdapterHandler 回调函数中,打开网卡时手动设置的绑定变量。
  2. Packet
    发送时候提供的数据包
  3. Status
    发送结果

1.1.4 TransferDataCompleteHandler 回调函数

VOID ProtocolTransferDataComplete(
  _In_ NDIS_HANDLE  ProtocolBindingContext,
  _In_ PNDIS_PACKET Packet,
  _In_ NDIS_STATUS  Status,
  _In_ UINT         BytesTransferred
)

函数说明:

这个蛋疼的函数是在调用 NdisTransferData 的时候,如果返回的结果是 PENDING 则会在接受数据完成的时候调用这个回调函数。

参数:

  1. ProtocolBindingContext
    …和之前的都一样
  2. Packet
    调用 NdisTransferData 的时候提供的 Packet
  3. Status
    传输的结果
  4. BytesTransferred
    传输的大小

1.1.5 ResetCompleteHandler 回调函数

VOID ProtocolResetComplete(
  _In_ NDIS_HANDLE ProtocolBindingContext,
  _In_ NDIS_STATUS Status
)

函数说明:

这个函数也是蛋疼无比,因为我还没用过,根据 MSDN 说明来看,这个回调函数是在调用 NdisReset 的时候,如果返回 PENDING 的话,则在完成的时候回调这个函数。
由于没试过,所以不知道具体的效果,不过根据 MSDN 说明,Reset 的过程会等待所有的 IO 完成的时候才会完成。(想想也是…别人特么数据都没发完你就 reset 了,你让别人情何以堪 ,,ԾㅂԾ,,)

1.1.6 RequestCompleteHandler 回调函数

VOID ProtocolRequestComplete(
  _In_ NDIS_HANDLE   ProtocolBindingContext,
  _In_ PNDIS_REQUEST NdisRequest,
  _In_ NDIS_STATUS   Status
)

函数说明:

这个函数主要是 NdisRequest 的时候,如果返回 PENDING 则在操作完成时,回调这个函数。

参数说明:

  1. ProtocolBindingContext
  2. NdisRequest
    在调用 NdisRequest 函数的时候会要求提供一个指针,这个值就是那时候提供的指针
  3. Status
    操作结果

1.1.7 ReceiveHandler 回调函数

NDIS_STATUS ProtocolReceive(
  _In_ NDIS_HANDLE ProtocolBindingContext,
  _In_ NDIS_HANDLE MacReceiveContext,
  _In_ PVOID       HeaderBuffer,
  _In_ UINT        HeaderBufferSize,
  _In_ PVOID       LookAheadBuffer,
  _In_ UINT        LookaheadBufferSize,
  _In_ UINT        PacketSize
)

有点复杂的函数,留坑。。。

1.1.8 ReceiveCompleteHandler 回调函数

VOID ProtocolReceiveComplete(
  _In_ NDIS_HANDLE ProtocolBindingContext
)

函数说明:

暂时不懂有啥很有用的地方

1.1.9 StatusHandler 回调函数

VOID ProtocolStatus(
  _In_ NDIS_HANDLE ProtocolBindingContext,
  _In_ NDIS_STATUS GeneralStatus,
  _In_ PVOID       StatusBuffer,
  _In_ UINT        StatusBufferSize
)

函数说明:

暂时不懂有啥很有用的地方

1.1.10 StatusCompleteHandler 回调函数

VOID ProtocolStatusComplete(
  _In_ NDIS_HANDLE ProtocolBindingContext
)

函数说明:

暂时不懂有啥很有用的地方

1.1.11 BindAdapterHandler 回调函数

VOID ProtocolBindAdapter(
  _Out_ PNDIS_STATUS Status,
  _In_  NDIS_HANDLE  BindContext,
  _In_  PNDIS_STRING DeviceName,
  _In_  PVOID        SystemSpecific1,
  _In_  PVOID        SystemSpecific2
)

函数说明:

主要是用于打开网卡的

Demo:

    ULONG       PacketFilter = NDIS_PACKET_TYPE_DIRECTED;
    NDIS_MEDIUM MediumArray[1] = { NdisMedium802_3 };
    NDIS_STATUS OpenErrorCode;
    UINT        MediumIndex;
    PAdapterData pAdapt = NULL;
    ULONG       ReqProcessBytes;
    do
    {
        //创建关联数据
        if (
            (*Status = NdisAllocateMemoryWithTag(&pAdapt, sizeof(AdapterData), NDIS_ALLOC_TAG))
            != NDIS_STATUS_SUCCESS) break;
        NdisZeroMemory(pAdapt, sizeof(AdapterData));
        // 复制 DeviceName
        if (
            (*Status = NdisAllocateMemoryWithTag(&pAdapt->DeviceName.Buffer, DeviceName->Length, NDIS_ALLOC_TAG))
            != NDIS_STATUS_SUCCESS) break;
        NdisMoveMemory(pAdapt->DeviceName.Buffer, DeviceName->Buffer, DeviceName->Length);
        NdisInitUnicodeString(&pAdapt->DeviceName, pAdapt->DeviceName.Buffer);

        // 创建 PacketPool
        NdisAllocatePacketPoolEx(Status,
            &pAdapt->PoolHandle,
            ETH_MIN_PACKET_POOL_SIZE,
            ETH_MAX_PACKET_POOL_SIZE - ETH_MIN_PACKET_POOL_SIZE,
            ETH_RESERVED_SIZE);
        if (*Status != NDIS_STATUS_SUCCESS) break;
        // 创建 BufferPool
        NdisAllocateBufferPool(Status, &pAdapt->BuffHandle, ETH_MAX_PACKET_POOL_SIZE);
        if (*Status != NDIS_STATUS_SUCCESS) break;
        // 创建绑定事件,防止打开出现 Pending 状态
        NdisInitializeEvent(&pAdapt->BindEvent);
        // 打开网卡
        NdisOpenAdapter(Status,
            &OpenErrorCode,
            &pAdapt->BindHandle,
            &MediumIndex, MediumArray, 1,
            gGlobal.ProcotolHandle,
            (NDIS_HANDLE)pAdapt,
            DeviceName, 0, NULL);

        if (NDIS_STATUS_PENDING == *Status) {
            NdisWaitEvent(&pAdapt->BindEvent, 0);
            *Status = pAdapt->BindStatus;
        }
        if (NDIS_STATUS_SUCCESS != *Status) {
            break;
        }
        // 配置过滤模式
        *Status = ndis_DoRequest(pAdapt, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &PacketFilter, sizeof(PacketFilter), &ReqProcessBytes);
        if (NDIS_STATUS_SUCCESS != *Status)break;
        // 读取所谓的友善的名字= =
        NdisQueryAdapterInstanceName(
            &pAdapt->DeviceDescr,
            pAdapt->BindHandle
        );
        // 将网卡数据插入全局信息
        ExInterlockedInsertTailList(&gGlobal.AdapterList,
            (PLIST_ENTRY)pAdapt, &gGlobal.AdapterListLock);
        // 打印一下
        KdPrint(("Binded Device Name : %wZ Descr Name : %wZ\n", &pAdapt->DeviceName, &pAdapt->DeviceDescr));
    } while (0);
    if (*Status != NDIS_STATUS_SUCCESS)
    {
        if (pAdapt)
        {
            if (pAdapt->BuffHandle)
                NdisFreeBufferPool(pAdapt->BuffHandle);
            if (pAdapt->PoolHandle)
                NdisFreePacketPool(pAdapt->PoolHandle);
            if (pAdapt->DeviceName.Buffer)
                NdisFreeMemoryWithTag(pAdapt->DeviceName.Buffer, NDIS_ALLOC_TAG);
            NdisFreeMemoryWithTag(pAdapt, NDIS_ALLOC_TAG);
        }

    }

1.1.12 UnbindAdapterHandler 回调函数

VOID ProtocolUnbindAdapter(
  _Out_ PNDIS_STATUS Status,
  _In_  NDIS_HANDLE  ProtocolBindingContext,
  _In_  NDIS_HANDLE  UnbindContext
)

1.1.13 PnPEventHandler 回调函数

NDIS_STATUS ProtocolPnPEvent(
  _In_ NDIS_HANDLE    ProtocolBindingContext,
  _In_ PNET_PNP_EVENT NetPnPEvent
)

发表评论

电子邮件地址不会被公开。 必填项已用*标注