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

转自http://eslxf.blog.51cto.com/918801/197738

 

1.3        DriverEntry函数的具体实现

NPF的DriverEntry注册所有驱动程序的I/O回调函数、创建设备、在NDIS内把NPF定注册为一个协议驱动程序。NPF的DriverEntry函数的主要代码如下:
packetNtx/driver/packet.c  91~282
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
    )
{
    …
    /*根据操作系统的版本,定义跳过回环数据包的正确标识*/
//获得操作系统的版本
    PsGetVersion(&OsMajorVersion, &OsMinorVersion, NULL, NULL);
    //根据不同的操作系统,定义跳过回环数据包的正确标识 
    if((OsMajorVersion == 5) && (OsMinorVersion == 0))
    {    // Windows 2000 需要NDIS_FLAGS_DONT_LOOPBACK与
// NDIS_FLAGS_SKIP_LOOPBACK两个标识
        g_SendPacketFlags = NDIS_FLAGS_DONT_LOOPBACK |
                                NDIS_FLAGS_SKIP_LOOPBACK_W2K;
    }
    else
    {   // Windows XP、Windows 2003与后续的操作系统
//只需要NDIS_FLAGS_DONT_LOOPBACK标识
        g_SendPacketFlags =  NDIS_FLAGS_DONT_LOOPBACK;
    }  
 
/*初始化设备名称的前缀*/
    NdisInitUnicodeString(&g_NPF_Prefix, g_NPF_PrefixBuffer);
   
    /*获得CPU的个数,并保存该值*/  
    g_NCpu = NdisSystemProcessorCount();
   
    /*零初化ProtocolChar结构体*/
    RtlZeroMemory(&ProtocolChar,sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
 
    /*向NDIS注册协议驱动程序*/
//用协议数据(版本、名称等)与回调函数地址设置ProtocolChar
#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) {
        //注册失败,函数返回
        return Status;
    }  
 
    /*设置IRP派遣函数和卸载例程*/
    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;// 卸载例程
 
/*获取系统中可用的网络适配器信息*/
    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);//给一个适配器创建一个设备对象
    }
 
    return STATUS_SUCCESS;
 
    /*处理函数错误*/
RegistryError:   
    NdisDeregisterProtocol(
        &Status,
        g_NdisProtocolHandle
        );
 
    Status=STATUS_UNSUCCESSFUL;
    return(Status);
}

1.3.1      getAdaptersList函数

函数getAdaptersList返回系统中可用的MAC链表,函数的原型如下:
PWCHAR getAdaptersList(VOID);
函数返回一个包含网络适配器链表的字符串。
该适配器链表从注册表的SYSTEM//CurrentControlSet//Control//Class
//{4D36E972-E325-11CE-BFC1-08002BE10318}注册项获取。函数首先遍历该注册表项,获取 子项的信息,再打开子项的“Linkage”子项,在“Linkage”子项下查找“Export”键名的键值,如在图5-2中,键值为“/Device/NdisWanIp”。并把键值存储到内存中,形成一个列表。函数最后返回该列表。
NPF试图从这个链表创建它的绑定。 通过这种方式,它可以动态的加载或卸载,而不用通过控制面板操作。
clip_image002
图5-2 注册表项0006/Linkage
函数的主要代码如下:
PWCHAR getAdaptersList(void)
{
    PKEY_VALUE_PARTIAL_INFORMATION result = NULL;
    OBJECT_ATTRIBUTES objAttrs;
    NTSTATUS status;
    HANDLE keyHandle;
    UINT BufPos=0;
    UINT BufLen=4096;
 
    /*分配DeviceNames所需的内存空间*/
    PWCHAR DeviceNames = (PWCHAR) ExAllocatePoolWithTag(PagedPool, BufLen, '0PWA');
   
    if (DeviceNames == NULL)
{//分配失败,函数返回
            return NULL;
    }
 
  /*
*设置一个OBJECT_ATTRIBUTES类型的参数objAttrs,为了后续调用
*其中NDIS_STRING AdapterListKey =
*  NDIS_STRING_CONST("//Registry//Machine//System//CurrentControlSet
*      //Control//Class//{4D36E972-E325-11CE-BFC1-08002BE10318}");
*/
    InitializeObjectAttributes(&objAttrs, &AdapterListKey,
        OBJ_CASE_INSENSITIVE, NULL, NULL);
 
/*打开注册表表项,返回objAttrs中所描述的注册表表项的句柄*/
    status = ZwOpenKey(&keyHandle, KEY_READ, &objAttrs);
    if (!NT_SUCCESS(status)) {
        //打开失败
    }
    else { //打开成功      
        ULONG resultLength;
        KEY_VALUE_PARTIAL_INFORMATION valueInfo;
        CHAR AdapInfo[1024];
        UINT i=0;
        /*遍历设备链表,获取一个已打开注册表项子项的信息*/                      while((status=ZwEnumerateKey(keyHandle,i,KeyBasicInformation,
AdapInfo,sizeof(AdapInfo),&resultLength))==STATUS_SUCCESS)
{
            WCHAR ExportKeyName [512];
//所打开的注册表项
PWCHAR ExportKeyPrefix =
L"//Registry//Machine//System//CurrentControlSet//
Control//Class//{4D36E972-E325-11CE-BFC1-08002BE10318}//" ;
            UINT ExportKeyPrefixSize =
sizeof (L"//Registry//Machine//System//CurrentControlSet//
Control//Class//{4D36E972-E325-11CE-BFC1-08002BE10318}" );
            //需要打开的子项"Linkage"
            PWCHAR LinkageKeyPrefix = L"//Linkage";
            UINT LinkageKeyPrefixSize = sizeof(L"//Linkage");
            //所查找的键名为"Export"
            NDIS_STRING FinalExportKey = NDIS_STRING_CONST("Export");
 
            PKEY_BASIC_INFORMATION tInfo=
                            (PKEY_BASIC_INFORMATION)AdapInfo;
            UNICODE_STRING AdapterKeyName;
            HANDLE ExportKeyHandle;
            //合成要打开的注册表子项,如图5-2中的为:
//”//Registry//Machine//System//CurrentControlSet//Control
Class//{4D36E972-E325-11CE-BFC1-08002BE10318}
0006//Linkage”
            RtlCopyMemory(ExportKeyName,ExportKeyPrefix,
                    ExportKeyPrefixSize);
            RtlCopyMemory((PCHAR)ExportKeyName+ExportKeyPrefixSize,
tInfo->Name,tInfo->NameLength+2);               RtlCopyMemory(
(PCHAR)ExportKeyName+ExportKeyPrefixSize+tInfo->NameLength,
                    LinkageKeyPrefix,LinkageKeyPrefixSize);
        /*给一个Unicode字符串初始化赋值*/
            RtlInitUnicodeString(&AdapterKeyName, ExportKeyName);
/*设置一个OBJECT_ATTRIBUTES类型的参数objAttrs,为了后续调用*/
            InitializeObjectAttributes(&objAttrs, &AdapterKeyName,
                    OBJ_CASE_INSENSITIVE, NULL, NULL);     
          /*打开注册表表项,返回objAttrs中所描述的注册表表项的句柄*/
status=ZwOpenKey(&ExportKeyHandle,KEY_READ,&objAttrs);
if  (!NT_SUCCESS(status))
{//打开失败,跳出本次循环
                continue;
            }
            /*查找“Export”键名的键值信息*/
            status = ZwQueryValueKey(ExportKeyHandle, &FinalExportKey,
                    KeyValuePartialInformation, &valueInfo,
                    sizeof(valueInfo), &resultLength);
               
if  (!NT_SUCCESS(status) && (status != STATUS_BUFFER_OVERFLOW)) {
                //查询失败
            }
            else { //查询成功
/*计算所需的内存大小*/
                ULONG valueInfoLength = valueInfo.DataLength +
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]);
/*分配内存,用于查询*/
                PKEY_VALUE_PARTIAL_INFORMATION valueInfoP =
                 (PKEY_VALUE_PARTIAL_INFORMATION) ExAllocatePoolWithTag
(PagedPool, valueInfoLength, '1PWA');
                if (valueInfoP != NULL) {
                    status = ZwQueryValueKey(ExportKeyHandle,
                            &FinalExportKey,
                            KeyValuePartialInformation,
                            valueInfoP,
                            valueInfoLength, &resultLength);
                    if (!NT_SUCCESS(status)) {
                        //查询失败
                    }
                    else{//查询成功
                        if( BufPos + valueInfoP->DataLength > BufLen ) {
                        //如果DeviceNames的内存空间不够,翻倍增长内存空间             
                           PWCHAR DeviceNames2 =
(PWCHAR)ExAllocatePoolWithTag
(PagedPool, BufLen<< 1, '0PWA');
                            if( DeviceNames2 ) {//分配成功,数据转存
                                RtlCopyMemory((PCHAR)DeviceNames2,
(PCHAR)DeviceNames, BufLen);
                                BufLen <<= 1;//内存长度翻倍
                                ExFreePool(DeviceNames);
                                DeviceNames = DeviceNames2;
                            }
                        }
                        if( BufPos + valueInfoP->DataLength < BufLen ) {                            //复制“Export”键名的键值到DeviceNames中
                            RtlCopyMemory((PCHAR)DeviceNames+BufPos,                                   valueInfoP->Data,valueInfoP->DataLength);
                            BufPos+=valueInfoP->DataLength-2;
                        }
                    }
                    ExFreePool(valueInfoP);
                }
                else {
//分配用于查询的内存失败
                }
            }//一次查找“Export”键名的键值信息结束
           
//设置结束符
            DeviceNames[BufPos/2]=0;
            DeviceNames[BufPos/2+1]=0;
            //关闭注册表子项
            ZwClose (ExportKeyHandle);
            i++;
        }//结束while语句
 
/*关闭注册表项*/
        ZwClose (keyHandle);           
    }
/*设置函数返回内容*/
    if(BufPos==0){
        ExFreePool(DeviceNames);
        return NULL;
    }  
    return DeviceNames;
}

1.3.2        getTcpBindings函数

函数getTcpBindings返回绑定到TCP/IP的MAC。函数原型如下:
PKEY_VALUE_PARTIAL_INFORMATION getTcpBindings(VOID);
如果getAdaptersList函数失败,NPF通过该函数试图获取TCP/IP的绑定。函数通过对注册表项HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Linkage下Bind键值获得TCP-IP绑定的适配器。函数返回指向注册表”Bind”键名的键值的指针,该注册表键值包含绑定了TCP/IP的适配器。
clip_image004
图5-3 注册表项Tcpip/Linkage
clip_image006
图5-4 Bind键名的键值

  • 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、付费专栏及课程。

余额充值