深度剖析WinPcap之(六)——驱动程序的初始化与清除
驱动程序的初始化主要由函数DriverEntry完成,卸载主要由函数DriverUnload完成。下面主要分析驱动程序的初始化与清除过程,以及相关的基础知识。
图5-1 函数调用关系图
1.1 结构体_NDIS_PROTOCOL_CHARACTERISTICS
结构体_NDIS_PROTOCOL_CHARACTERISTICS的定义如下: typedef struct _NDIS_PROTOCOL_CHARACTERISTICS { UCHAR MajorNdisVersion; UCHAR MinorNdisVersion; UINT Reserved;
OPEN_ADAPTER_COMPLETE_HANDLER OpenAdapterCompleteHandler; CLOSE_ADAPTER_COMPLETE_HANDLER CloseAdapterCompleteHandler; SEND_COMPLETE_HANDLER SendCompleteHandler;
TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler; RESET_COMPLETE_HANDLER ResetCompleteHandler; REQUEST_COMPLETE_HANDLER RequestCompleteHandler; RECEIVE_HANDLER ReceiveHandler;
RECEIVE_COMPLETE_HANDLER ReceiveCompleteHandler; STATUS_HANDLER StatusHandler;
STATUS_COMPLETE_HANDLER StatusCompleteHandler; NDIS_STRING Name;
//
//使用下列任何成员,需要把MajorNdisVersion设置为0x04或0x05 //
RECEIVE_PACKET_HANDLER ReceivePacketHandler; BIND_HANDLER BindAdapterHandler; UNBIND_HANDLER UnbindAdapterHandler; PNP_EVENT_HANDLER PnPEventHandler; UNLOAD_PROTOCOL_HANDLER UnloadHandler; //
//使用下列任何成员,需要把MajorNdisVersion设置为0x05 //面向连接的协议驱动程序使用,不分析。 //
CO_SEND_COMPLETE_HANDLER CoSendCompleteHandler; CO_STATUS_HANDLER CoStatusHandler;
CO_RECEIVE_PACKET_HANDLER CoReceivePacketHandler; CO_AF_REGISTER_NOTIFY_HANDLER CoAfRegisterNotifyHandler;
} NDIS_PROTOCOL_CHARACTERISTICS, *PNDIS_PROTOCOL_CHARACTERISTICS;
下面分别说明结构体_NDIS_PROTOCOL_CHARACTERISTICS各成员的作用,其中,只在面向连接的协议驱动程序中使用的成员不作分析说明。 MajorNdisVersion
描述驱动程序所使用NDIS库的主要版本。当前值为0x05(WinPcap当前使用的为版本5,NDIS目前已到版本6),虽然NDIS库支持早期采用NDIS V4.0开发的驱动程序,但不再对V3.0版本的协议驱动程序进行支持。 MinorNdisVersion
描述NDIS的次版本,当前为0x00。 Reserved
该成员保留给系统使用。 OpenAdapterCompleteHandler
这是一个必须提供的函数。如果协议驱动程序对NdisOpenAdapter的调用返回NDIS_STATUS_PENDING,则接着调用ProtocolOpenAdapterComplete来完成绑定操作。 CloseAdapterCompleteHandler
这是一个必须提供的函数。如果协议驱动程序对NdisCloseAdapter的调用返回
NDIS_STATUS_PENDING,则接着调用ProtocolCloseAdapterComplete来完成解除绑定操作。
SendCompleteHandler
这是一个必须提供的函数。如果协议驱动程序对NdisSendPackets或 NdisSend的调用返回NDIS_STATUS_PENDING,则接着调用SendCompleteHandler来完成发送处理。 TransferDataCompleteHandler
如果协议驱动程序可以把它自己绑定到一个低层非面向连接的NIC驱动程序(以
NdisMIndicateReceivePacket指定并不是接收所有的数据包)上,这就是一个必须提供的函数。当协议驱动程序发起传输数据请求,对NdisTransferData的调用返回
NDIS_STATUS_PENDING时,ProtocolTransferDataComplete函数被调用。专门面向连接的协议驱动程序不需要ProtocolTransferDataComplete 函数。 ResetCompleteHandler
这是一个必须提供的函数。当协议驱动程序调用NdisReset函数执行复位操作,返回值为NDIS_STATUS_PENDING,则接着调用ProtocolResetComplete完成复位操作。 RequestCompleteHandler
这是一个必须提供的函数。当协议驱动程序调用NdisRequest函数查询和设置操作,返回值为NDIS_STATUS_PENDING,则调用ProtocolRequestComplete完成操作。 ReceiveHandler
对绑定到一个非面向连接的NIC驱动程序上的NDIS协议驱动程序,这是一个必须提供的函数。ProtocolReceive决定协议驱动程序的使用者对一个被接收的网络数据包是否感兴趣,如果感兴趣,就复制所需要的数据,可能地,调用NdisTransferData重新获取剩余的所需数据包。
ReceiveCompleteHandler
这是一个必须提供的函数。驱动程序完成前面所述的一个或多个从一个NIC驱动程序接收所需数据的操作后,ProtocolReceiveComplete完成诸如通知协议驱动程序使用者之类的后处理。
StatusHandler
这是一个必须提供的函数。ProtocolStatus函数用来处理低层NIC驱动程序所指示状态的改变。
StatusCompleteHandler
这是一个必须提供的函数。ProtocolStatusComplete完成一个状态改变的操作,该操作由底层驱动程序调用NdisMIndicateStatus发起。 Name
一个NDIS_STRING类型,包含调用者初始化的一个字符串,用来命名该驱动程序,采用系统默认的字符集。
对Windows 2000与后面的驱动,该字符串为Unicode字符。也就是说,对Windows 2000与以后的版本,NDIS定义NDIS_STRING类型为一个UNICODE_STRING类型。当协议被安装时,该字符串必须与注册表所指定的(在Services条目之下)匹配。
NdisRegisterProtocol把所提供的字符串转换为大写字符,因此一个协议驱动程序的编写者不能假设通过改变一个已经注册协议名称的大小写来为驱动创建一个唯一的名称。 ReceivePacketHandler
这是一个可选函数。如果协议驱动程序绑定到支持多数据包(multipacket )的NIC驱动程序(通过调用NdisMIndicateReceivePacket指定),那么ProtocolReceivePacket函数应该被提供。
BindAdapterHandler
这是一个必须提供的函数。NDIS调用该函数请求协议驱动程序绑定到低层网卡或虚拟网卡上,网卡名作为该处理程序的参数传递。 UnbindAdapterHandler
这是一个必须提供的函数。NDIS调用ProtocolUnbindAdapter释放对低层网卡或虚拟网卡的绑定,网卡名作为参数传递。当绑定成功解除时,ProtocolUnbindAdapter函数调用NdisCloseAdapter,并释放资源。 PnPEventHandler
这是一个必须提供的函数。NDIS调用ProtocolPnPEvent来指示即插即用事件或电源管理事件。
UnloadHandler
这是一个可选函数。NDIS调用ProtocolUnload函数来响应用户卸载中间层驱动程序的请求。对于每一个绑定的适配器,在调用NDIS的 ProtocolUnbindAdapter之后,调用ProtocolUnload函数卸载驱动程序。ProtocolUnload执行驱动程序决定的清除操作。 1.2 NPF的DriverEntry函数的主要流程
和编写普通应用程序一样,驱动程序也有个入口函数,也就是首先被执行的函数。该函数通常被命名为DriverEntry,可以指定另外的名称,但最好遵循这个名称。该函数的原型如下: NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath );
DriverEntry主要是对驱动程序进行初始化工作,它由系统进程所调用。在Windows中有个特殊的进程叫做系统进程。打开进程管理器,可见里面有个名为System的进程就是系统进程。系统进程在系统启动的时候,就已经被创建了。
图5-2 System进程
驱动加载的时候,系统进程启动新的线程,调用执行体组件中的对象管理器,创建一个驱动对象。这个驱动对象是一个DRIVER_OBJECT的结构体。另外,系统进程调用执行体组件中的配置管理程序,查询此驱动程序在注册表中对应的项。
系统线程调用驱动程序的DriverEntry例程时,同时传进两个参数,分别是
pDrivelobject和pRegistryPath。其中,第一个是指向刚才被创建驱动对象的指针,第二个是指向设备服务键的键名称符串的指针。
为了和NDIS库建立通信,驱动程序的DriverEntry必须调用NdisRegisterProtocol作为协议驱动程序注册,稍候将作详细描述。