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试图从这个链表创建它的绑定。通过这种方式,它可以动态的加载或卸载,而不用通过控制面板操作。

深度剖析WinPcap之(六)——驱动程序的初始化与清除(2)
图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的适配器。

深度剖析WinPcap之(六)——驱动程序的初始化与清除(2)
图5-3 注册表项Tcpip\Linkage
深度剖析WinPcap之(六)——驱动程序的初始化与清除(2)
图5-4 Bind键名的键值


 

本文出自 “千江月” 博客,请务必保留此出处http://eslxf.blog.51cto.com/918801/197738