深度剖析WinPcap之(六)——驱动程序的初始化与清除(1)

本文转自http://eslxf.blog.51cto.com/918801/197413

 

驱动程序的初始化主要由函数DriverEntry完成,卸载主要由函数DriverUnload完成。下面主要分析驱动程序的初始化与清除过程,以及相关的基础知识。
clip_image002
图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的进程就是系统进程。系统进程在系统启动的时候,就已经被创建了。
 
clip_image002
图5-2 System进程
驱动加载的时候,系统进程启动新的线程,调用执行体组件中的对象管理器,创建一个驱动对象。这个驱动对象是一个DRIVER_OBJECT的结构体。另外,系统进程调用执行体组件中的配置管理程序,查询此驱动程序在注册表中对应的项。
系统线程调用驱动程序的DriverEntry例程时,同时传进两个参数,分别是
pDrivelobject和pRegistryPath。其中,第一个是指向刚才被创建驱动对象的指针,第二个是指向设备服务键的键名称符串的指针。
为了和NDIS库建立通信,驱动程序的DriverEntry必须调用NdisRegisterProtocol作为协议驱动程序注册,稍候将作详细描述。
DriverEntry返回值是NTSTATUS的数据,NTSTATUS被定义为32位的无符号长整型。在驱动程序开发中,人们习惯用NTSTATUS返回状态。DriverEntry的返回值如果表示成功,则意味着加载驱动成功,否则意味着加载驱动失败,调用对象管理程序销毁驱动对象。如果该程序成功,它必须返回STATUS_SUCCESS,否则,它必须返回一个在ntstatus.h定义的错误状态码。下面列出STATUS_SUCCESS与STATUS_UNSUCCESSFUL的定义。
 st1/:*{behavior:url(#ieooui) }
 
#define  STATUS_SUCCESS             ((NTSTATUS)0x 00000000L )
#define  STATUS_UNSUCCESSFUL        ((NTSTATUS)0xC 0000001L )

1.2.1       协议驱动程序注册

驱动程序在 DriverEntry 环境中通过调用 NdisRegisterProtocolNDIS 注册 ProtocolXxx 函数。 NdisRegisterProtocol 定义如下:
VOID NdisRegisterProtocol(
OUT PNDIS_STATUS Status,
OUT PNDIS_HANDLE NdisProtocolHandler,
IN NDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics,
IN UINT CharacteristicsLength
);
参数Status是指向调用者提供的一个变量的指针,该函数可以返回下列的值:
NDIS_STATUS_SUCCESS
NDIS库把调用者注册为一个协议驱动程序
NDIS_STATUS_BAD_CHARACTERISTICS
对参数ProtocolCharacteristics 中MajorNdisVersion所描述的版本而言CharacteristicsLength太短
NDIS_STATUS_BAD_VERSION
参数ProtocolCharacteristics 中MajorNdisVersion描述的版本不可用
NDIS_STATUS_RESOURCES
资源不够,可能是内存,阻止NDIS库注册调用者
参数NdisProtocolHandle指向调用者提供的一个变量,函数将通过它返回一个描述已注册驱动的一个句柄。参数ProtocolCharacteristics指向一个NDIS_PROTOCOL_CHARACTERISTICS结构体,由调用者设置。参数CharacteristicsLength描述ProtocolCharacteristics所指结构体的大小。
该调用的返回句柄 NdisProtocolHandler 对协议驱动程序是透明的,协议驱动程序必须保存该句柄,并在将来对 NDIS 的调用中作为输入参数传递,例如,打开低层适配 器(调用 NdisOpenAdapter函数时 )。
在调用 NdisRegisterProtocol 之前, DriverEntry 必须完成以下操作:
<!--[if !supportLists]--> Ø         <!--[endif]--> 对 NDIS_PROTOCOL_CHARACTERISTICS 结构体进行零初始化,例如调用 NdisZeroMemory 函数。这将确保可选入口点的尚未使用的成员设置为 NULL 。如果该结构没被置零,那么在调用 NdisRegisterProtocol 之前,任何不使用的成员必须置为 NULL;
<!--[if !supportLists]--> Ø         <!--[endif]--> 在 NDIS_PROTOCOL_CHARACTERISTICS 结构中指定协议兼容的 NDIS 版本 ;
<!--[if !supportLists]--> Ø         <!--[endif]--> 在 NDIS_PROTOCOL_CHARACTERISTICS 结构中设置驱动程序导出的必需的和可选的 ProtocolXxx 函数的地址。
WinPcapNPF 中的具体实现为:首先分配一个 NDIS_PROTOCOL_CHARACTERISTICS 的结构体,然后执行零初始化,接着用协议数据(版本、名称等)与驱动程序所需回调函数的地址设置该结构体。最后调用 NdisRegisterProtocol 函数把 NPF注册为一个NDIS协议驱动程序 。下面为 NPF 中对应的代码:
//声明一个NDIS_PROTOCOL_CHARACTERISTICS结构实例ProtocolChar;
NDIS_PROTOCOL_CHARACTERISTICS  ProtocolChar;
 
//协议名称PacketDriver
NDIS_STRING ProtoName = NDIS_STRING_CONST("PacketDriver");
 
//对ProtocolChar的内存空间清零
RtlZeroMemory(&ProtocolChar,sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
 
//用协议数据(版本、名称等)与回调函数地址设置ProtocolChar
#ifdef #ifdef NDIS50
    ProtocolChar.MajorNdisVersion            = 5;
#else
    ProtocolChar.MajorNdisVersion            = 3;
#endif
    ProtocolChar.MinorNdisVersion            = 0;
    ProtocolChar.Reserved                    = 0;
    ProtocolChar.OpenAdapterCompleteHandler  = NPF_OpenAdapterComplete;
    ProtocolChar.CloseAdapterCompleteHandler = NPF_CloseAdapterComplete;
    ProtocolChar.SendCompleteHandler         = NPF_SendComplete;
    ProtocolChar.TransferDataCompleteHandler = NPF_TransferDataComplete;
    ProtocolChar.ResetCompleteHandler        = NPF_ResetComplete;
    ProtocolChar.RequestCompleteHandler      = NPF_RequestComplete;
    ProtocolChar.ReceiveHandler              = NPF_tap;
    ProtocolChar.ReceiveCompleteHandler      = NPF_ReceiveComplete;
    ProtocolChar.StatusHandler               = NPF_Status;
    ProtocolChar.StatusCompleteHandler       = NPF_StatusComplete;
#ifdef NDIS50
    ProtocolChar.BindAdapterHandler          = NPF_BindAdapter;
    ProtocolChar.UnbindAdapterHandler        = NPF_UnbindAdapter;
    ProtocolChar.PnPEventHandler             = NPF_PowerChange;
    ProtocolChar.ReceivePacketHandler        = NULL;
#endif
    ProtocolChar.Name                        = ProtoName;
 
//把NPF注册为一个NDIS协议驱动程序
    NdisRegisterProtocol(
        &Status,
        &g_NdisProtocolHandle,
        &ProtocolChar,
        sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
if (Status != NDIS_STATUS_SUCCESS) {
//注册协议驱动程序到NDIF失败,程序结束
        return Status;
    }

1.2.2       设置卸载例程和IRP的派遣函数

DriverEntry 函数中,一般设置卸载例程和 IRP 的派遣函数。设置卸载例程和设置派遣函数都是对驱动对象的设置。设备对象中的 MajorFunction 是一个函数指针数组, IRP_MJ_CREATEIRP_MJ_ CLOSEIRP_MJ_WR'lTE 等代表数组的第几个元素。下列为 NPFDriverEntry 函数中的设置:
DriverObject->MajorFunction[IRP_MJ_CREATE] = NPF_Open;
DriverObject->MajorFunction[IRP_MJ_CLOSE]  = NPF_Close;
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;
DriverObject->DriverUnload = NPF_Unload; // 卸载例程
 

1.2.3       获取系统中可用网络适配器的信息

函数getAdaptersList获取系统中可用的网络适配器信息,返回一个包含系统中可用网络适配器列表的字符串。如果getAdaptersList失败了,NPF试图通过调用getTcpBindings函数获得绑定了TCP/IP的网络适配器信息。函数getTcpBindings返回指向包含绑定了TCP/IP适配器的注册表键值的指针。
下面为 DriverEntry 函数中相关的代码。
bindP = getAdaptersList();
if (bindP == NULL)
{
               //在注册表中没找到适配器,试图复制TCP-IP绑定的适配器
              tcpBindingsP = getTcpBindings();
                    
              if (tcpBindingsP == NULL)
              {
                      //没有找到绑定了TCP/IP的适配器,程序执行错误处理
                     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); //给网络适配器创建一个设备
}
 

1.2.4     对可用的网络适配器创建一个设备

函数NPF_CreateDevice对一个给定的网络适配器创建一个设备。NPF驱动程序也调用 NPF_CreateDevice 函数,通过IoCreateDevice系统接口把Open/close,read/write与IOCTL请求的句柄地址传递给操作系统。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
介绍 本手册提供了WinPcap编程接口的描述及其源代码。它与详尽的WinPcap核心资料一起,为编程人员提供了详细的函数与结构的描述,同时也提供了若干教程和程序范例。 您可以点击页面顶部的导航链接,或者使用页面左边的树形控件,来跳转到您感兴趣的内容。 本文档使用 the Doxygen documentation system创建,您可以登录 http://www.doxygen.org 阅览相关内容。 什么是WinPcap WinPcap是一个基于Win32平台的,用于捕获网络数据包并进行分析的开源库. 大多数网络应用程序通过被广泛使用的操作系统元件来访问网络,比如sockets。 这是一种简单的实现方式,因为操作系统已经妥善处理了底层具体实现细节(比如协议处理,封装数据包等等),并且提供了一个与读写文件类似的,令人熟悉的接口。 然而,有些时候,这种“简单的方式”并不能满足任务的需求,因为有些应用程序需要直接访问网络中的数据包。也就是说,那些应用程序需要访问原始数据包,即没有被操作系统利用网络协议处理过的数据包。 WinPcap产生的目的,就是为Win32应用程序提供这种访问方式; WinPcap提供了以下功能 捕获原始数据包,无论它是发往某台机器的,还是在其他设备(共享媒介)上进行交换的 在数据包发送给某应用程序前,根据用户指定的规则过滤数据包 将原始数据包通过网络发送出去 收集并统计网络流量信息 以上这些功能需要借助安装在Win32内核中的网络设备驱动程序才能实现,再加上几个动态链接库DLL。 所有这些功能都能通过一个强大的编程接口来表现出来,易于开发,并能在不同的操作系统上使用。这本手册的主要目标是在一些程序范例的帮助下,叙述这些编程接口的使用。 如果您现在就想开始摸索这些功能,您可以直接进入 WinPcap用户手册. 哪些程序在使用WinPcap WinPcap可以被用来制作许多类型的网络工具,比如具有分析,解决纷争,安全和监视功能的工具。特别地,一些基于WinPcap的典型应用有: 网络与协议分析器 (network and protocol analyzers) 网络监视器 (network monitors) 网络流量记录器 (traffic loggers) 网络流量发生器 (traffic generators) 用户级网桥及路由 (user-level bridges and routers) 网络入侵检测系统 (network intrusion detection systems (NIDS)) 网络扫描器 (network scanners) 安全工具 (security tools) 什么是WinPcap做不到的 WinPcap能 独立地 通过主机协议发送和接受数据,如同TCP-IP。这就意味着WinPcap不能阻止、过滤或操纵同一机器上的其他应用程序的通讯:它仅仅能简单地"监视"在网络上传输的数据包。所以,它不能提供类似网络流量控制、服务质量调度和个人防火墙之类的支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值