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我们即需要填写小端口的特征回调函数,也需要填写协议驱动的特征回调函数,这两个分别通过NdisIMRegisterLayeredMiniport和NdisRegisterProtocol
这两个函数来注册小端口驱动和协议驱动。注册完毕之后然后调用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从注册表取得设备名,即传入到PtBindAdapter的DEVICENAME参数
@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函数想上层取得通知接受完成
之后。。。。。。