中间层驱动学习笔记《寒江》

 

NDIS驱动对上次协议驱动来说像是一个小端口驱动,但是对小端口驱动来说又像是一个协议驱动,所以在中间层驱动中是不停的从小端口驱动跳到协议驱动中去,有点复杂

 

 

一个协议驱动,是可以接受本机接受到的所有包的,但是不能拦截。这个是和中间层驱动的一个很大的差别。

 

首先介绍的是NDIS包描述符的一些结构

/**The NDIS_PACKET structure defines the packet descriptors with chained buffer descriptors for which pointers are passed to many NdisXxx, MiniportXxx, and ProtocolXxx functions.

/

typedef struct _NDIS_PACKET {
    NDIS_PACKET_PRIVATE  Private;
    union {
        struct {
             UCHAR       MiniportReserved[2*sizeof(PVOID)];
             UCHAR       WrapperReserved[2*sizeof(PVOID)];
        };
        struct {
             UCHAR       MiniportReservedEx[3*sizeof(PVOID)];
             UCHAR       WrapperReservedEx[sizeof(PVOID)];
        };
        struct {
             UCHAR       MacReserved[4*sizeof(PVOID)];
        };
    };
    ULONG_PTR            Reserved[2];
    UCHAR                ProtocolReserved[1];
} NDIS_PACKET, *PNDIS_PACKET, **PPNDIS_PACKET;

 

仅有第一个域需要研究的。NDIS_PACKET_PRIVATE  Private;

Msdm上描述为:This is reserved for use exclusively by NDIS. Drivers must call the appropriate NdisXxx functions or NDIS-supplied macros to affect the contents of this area.

 NDIS_PACKET结构其实就是网络数据包的描述符,并且这个网络数据包并不是连续的,而是通过链表的形式连接起来的

 

这个NdisPacketOobOffset的结构如下

typedef struct _NDIS_PACKET_OOB_DATA {
    union {
        ULONGLONG  TimeToSend;
        ULONGLONG  TimeSent;
    };
    ULONGLONG  TimeReceived;
    UINT  HeaderSize;
    UINT  SizeMediaSpecificInfo;
    PVOID  MediaSpecificInformation;
    NDIS_STATUS  Status;
}  NDIS_PACKET_OOB_DATA, *PNDIS_PACKET_OOB_DATA;//
这个被翻译成带外数据包,就是在正常的数据网络包另外需要传大的信息

 

紧接着NDIS_PACKET_OOB_DATA结构的是typedef struct _NDIS_PACKET_EXTENSION
{
  PVOID  NdisPacketInfo[MaxPerPacketInfo];
} NDIS_PACKET_EXTENSION, *PNDIS_PACKET_EXTENSION;

下面从DriverEntry开始

------------------------------------------------------------------------------------------------------------------------------------------------------

首先是NdisMInitializeWrapper初始化包装句柄,并且这个函数传出NDIS_HANDLE句柄,这个函数是告知NDIS一个新的小端口驱动诞生了,呵呵

DriverEntry我们即需要填写小端口的特征回调函数,也需要填写协议驱动的特征回调函数,这两个分别通过NdisIMRegisterLayeredMiniportNdisRegisterProtocol

这两个函数来注册小端口驱动和协议驱动。注册完毕之后然后调用NdisIMAssociateMiniport函数将我们的小端口驱动和协议驱动连接起来.

接下来是这样的:

//

    systemAddDevice = DriverObject->DriverExtension->AddDevice;

    DriverObject->DriverExtension->AddDevice = myAddDevice;

 

    // Hook分发函数

    //这个几个是全局变量

    systemCreate = DriverObject->MajorFunction[IRP_MJ_CREATE];

    DriverObject->MajorFunction[IRP_MJ_CREATE] = myCreate;

 

    systemWrite = DriverObject->MajorFunction[IRP_MJ_WRITE];

    DriverObject->MajorFunction[IRP_MJ_WRITE] = myWrite;

 

    systemRead = DriverObject->MajorFunction[IRP_MJ_READ];

    DriverObject->MajorFunction[IRP_MJ_READ] = myRead;

 

    systemDeviceControl = DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL];

    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = myDeviceControl;

//你肯定会觉得很纳闷,为啥在中间层驱动中会是这样的注册普通的控制设备的分发历程呢?

因为在中间层驱动中,这些分发历程是给NDIS本身用的,如果我们直接填写我们的控制设备分发历程的话会导致NDIS驱动的失败。

看看下面的就明白了:

 

// 这是我们自己处理IRP_MJ_CREATE的方法

NTSTATUS

myCreate(

        IN PDEVICE_OBJECT DeviceObject,

        IN PIRP Irp

        )

{

    // 如果创建我们的设备,直接返回成功

    if(DeviceObject == gDeviceObject)

       return STATUS_SUCCESS;

 

    // 不要忘了系统过程

    return systemCreate(DeviceObject, Irp);

}

//判断是不是我们的控制设备,是的话就先调用我们的控制设备请求,然后再继续调用NDIS本身的分发历程,这是个完美的办法,呵呵

------------------------------------------------------------------------------------------------------------------------------------------------------

如何将我们的设备通过某种形式绑定到其他驱动中,来实现截获数据呢?

NDIS驱动并不像WDM驱动那样通过设备栈来绑定上下驱动的关系,由PNP管理器来管理注册.

只要我们填好必要的回调函数后,PNP会帮我门来绑定NIC设备

 

-------------------------------------------------------------------------------------------------------------------------------------------------

 

下面来看看整个中间层驱动的全部调用过程,理清下,是寒江上面的最后一章的例子里面的

 

@1从注册表取得设备名,即传入到PtBindAdapterDEVICENAME参数

@2调用驱动注册的PtBindAdapter函数,实现对小端口的绑定。这个函数的调用有可能异步完成,那时PtOpenAdapterComplete函数将会被调用.

 

@3继续调用NdisIMIinitializeDeviceInstanceEx函数,这个函数将会调用中间层的MpInitialize函数来初始化虚拟NIC

 

@4从上面的那个函数直接开始调用小端口驱动的初始化了。至此小端口驱动和协议驱动将会被连接起来,下面开始发送和接收数据包了

 

@5

发送数据包:

这个是在函数MpSend中实现的.

中间层驱动发送数据包,最终都必须调用NdisSend/NdisSendPackets/NdisCoSendPackets这个系列的函数。

 

 

 

 

 

具体流程:

当网络有数据包要发送时,协议驱动收到这个网络包,它做一些处理后,接着传入自己保存的那个绑定句柄,然后调用NdisSend往下发送包

NdisSend在内部通过协议驱动的绑定句柄,找到它所绑定的中间层驱动,然后找到中间层驱动的MpSend/mpSendPackets函数调用往下发送

直到包被小端口取得 MpSend/MpSendPackets收到,它通过IO或中断,将数据包发送到最终的物理设备上

 

在这个驱动例子中,首先发送时用MpSend函数发送,然后发送完成后调用协议驱动的PtSendComplete函数

 

 

接收数据包:

//接受数据包部分;;;;

首先是协议驱动收到网络包接收通知后,会在适当的时候调用NdisTransferData函数要求底层

驱动将完整的数据包数据发送给它。中间层驱动在这个函数中继续讲这个请求下发到底层,当底层立刻返回数据包是,我们的

MPTransferData能立刻截获到从而实现过滤。

要是这个函数异步完成的话,那我们就在PtTransferDataComplete中截获到完整的数据包。

 

理清下这个驱动流程,便于整体上把握,时间太紧,来不及抄下这个驱动来加深下理解了,等要用到的时候在来熟悉下。呵呵

 

 

 

 

 

 

 

 

 

/************************************************************************/
/* 首先向底层驱动请求得到完整的包描述符,如果获取描述符成功,则构造一个心的包描述符
调用NdisMIndicateReceiverPacket通知上层驱动,如果获取描述符失败,则调用NdismxXIndaicateReceive函数通知sh
上层取得
                                                                */
/************************************************************************/


/************************************************************************/
/* NdisMXxxIndicateReceive函数的调用,将导致上层取得 PtReceive函数的调用
下层驱动来调用NdisMXxxIndicateReceive函数通知上层驱动对数据包进行接收,会在将来收到完整的数据
包的后,选择一个比较空闲的时间调用NdisMXxxIndicateReceiveComplete函数,通知上层取得完成的数据包已经
接收完成。这将导致中间层取得PtReceiveComplete函数被调用。我们在这个函数中必须继续调用NdisMXxxIndicate
ReceiveComplete函数想上层取得通知接受完成 

之后。。。。。。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值