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

函数的主要代码如下:

PKEY_VALUE_PARTIAL_INFORMATION getTcpBindings(void)

{

  PKEY_VALUE_PARTIAL_INFORMATION result = NULL;

  OBJECT_ATTRIBUTES objAttrs;

  NTSTATUS status;

  HANDLE keyHandle;

 

/*

*设置一个OBJECT_ATTRIBUTES类型的参数objAttrs,为了后续调用

*其中NDIS_STRING tcpLinkageKeyName =

* NDIS_STRING_CONST(\\Registry\\Machine\\System

*      L"\\CurrentControlSet\\Services\\Tcpip\\Linkage");

*/

  InitializeObjectAttributes(&objAttrs, &tcpLinkageKeyName,

      OBJ_CASE_INSENSITIVE, NULL, NULL);

  

/*打开注册表表项,返回objAttrs中所描述的注册表表项的句柄*/

  status = ZwOpenKey(&keyHandle, KEY_READ, &objAttrs);

  if (!NT_SUCCESS(status))

  {//打开失败

  }

  else

  {//成功打开

ULONG resultLength;

        KEY_VALUE_PARTIAL_INFORMATION valueInfo;

/*

*对注册表表项进行查询,其中bindValueName的定义为

*NDIS_STRING bindValueName = NDIS_STRING_CONST("Bind");

*/

        status = ZwQueryValueKey(keyHandle, &bindValueName,

                 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, '2PWA');

 

          if (valueInfoP != NULL)

          {  /*对注册表表项进行查询,获取键名为”Bind”的键值信息*/

              status = ZwQueryValueKey(keyHandle, &bindValueName,

                  KeyValuePartialInformation,

                  valueInfoP,

                  valueInfoLength, &resultLength);

 

              if (!NT_SUCCESS(status))

              {//查询失败

                  ExFreePool(valueInfoP);

              }

              else

              {

                  if (valueInfoLength != resultLength)

                  {//失败,查询结果的长度前后不一致

                      ExFreePool(valueInfoP);

                  }

                  else

                  {

                      if (valueInfoP->Type != REG_MULTI_SZ)

                      {//失败,键名”Bind”的键值类型不是REG_MULTI_SZ

                              ExFreePool(valueInfoP);

                      }

                      else

                      { /*所有的操作正确*/

                          result = valueInfoP;

                      }

                  }

              }

          }

      }

/*关闭注册表*/

      ZwClose(keyHandle);

  }

/*返回查询的数据*/

return result;

}

 

1.3.3       NPF_CreateDevice函数

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

函数原型如下:

BOOLEAN

NPF_CreateDevice(IN OUT PDRIVER_OBJECT adriverObjectP,

                     IN PUNICODE_STRING amacNameP

)

参数adriverObjectP是用来与设备相关联的驱动对象,例如NPF的一个实例。参数 amacNameP是所创建设备将要指向的网络接口名称。

如果函数成功,返回非0值。

NPF对每一个可用的网络适配器只创建一个设备。该新设备指向该NPF驱动程序,但包含关于原始设备的信息。通过这种方式,当用户打开该新设备时,NPF将能够确定使用正确的适配器。

函数的主要代码如下:

BOOLEAN NPF_CreateDevice(IN OUT PDRIVER_OBJECT adriverObjectP,

                     IN PUNICODE_STRING amacNameP)

{

    NTSTATUS status;

    PDEVICE_OBJECT devObjP;

    UNICODE_STRING deviceName;

    UNICODE_STRING deviceSymLink;

   

/*检查amacNameP->Buffer是否包含合法的 “\\Device\\”子字符串*/

    if (RtlCompareMemory(amacNameP->Buffer, devicePrefix.Buffer,

        devicePrefix.Length) < devicePrefix.Length)

    {

        return FALSE;

    }

/*分配设置设备对象的名称的内存空间*/

    deviceName.Length = 0;

    deviceName.MaximumLength = (USHORT)(amacNameP->Length +

        g_NPF_Prefix.Length + sizeof(UNICODE_NULL));

    deviceName.Buffer = ExAllocatePoolWithTag(PagedPool,

 deviceName.MaximumLength, '3PWA');

    if (deviceName.Buffer == NULL)

        return FALSE;

 

/*分配用户可见的设备名称的内存空间*/

    deviceSymLink.Length = 0;

    deviceSymLink.MaximumLength=

(USHORT)(amacNameP->Length-devicePrefix.Length

        + symbolicLinkPrefix.Length

        + g_NPF_Prefix.Length

        + sizeof(UNICODE_NULL));

    deviceSymLink.Buffer = ExAllocatePoolWithTag(NonPagedPool,

deviceSymLink.MaximumLength, '3PWA');

    if (deviceSymLink.Buffer  == NULL)

    {

        ExFreePool(deviceName.Buffer);

        return FALSE;

    }

 

/*生成设置设备对象的名称*/

    RtlAppendUnicodeStringToString(&deviceName, &devicePrefix);

    RtlAppendUnicodeStringToString(&deviceName, &g_NPF_Prefix);

    RtlAppendUnicodeToString(&deviceName, amacNameP->Buffer +

        devicePrefix.Length / sizeof(WCHAR));

 

/*生成用户可见的设备名称*/

    RtlAppendUnicodeStringToString(&deviceSymLink, &symbolicLinkPrefix);

    RtlAppendUnicodeStringToString(&deviceSymLink, &g_NPF_Prefix);

    RtlAppendUnicodeToString(&deviceSymLink, amacNameP->Buffer +

        devicePrefix.Length / sizeof(WCHAR));

       

/*为驱动程序的使用者分配内存,并初始化一个设备对象*/

    status = IoCreateDevice(adriverObjectP,

        sizeof(DEVICE_EXTENSION),

        &deviceName,

        FILE_DEVICE_TRANSPORT,

        FILE_DEVICE_SECURE_OPEN,   

        FALSE,

        &devObjP);

 

    if (NT_SUCCESS(status))

    {

PDEVICE_EXTENSION devExtP =

(PDEVICE_EXTENSION)devObjP->DeviceExtension;

        devObjP->Flags |= DO_DIRECT_IO;

//设置适配器的名称

        RtlInitUnicodeString(&devExtP->AdapterName,amacNameP->Buffer);

       

       /*在一个设备对象名称与一个用户可见的名称之间建立一个符号连接*/

        if (IoCreateSymbolicLink(&deviceSymLink,&deviceName) !=

STATUS_SUCCESS)

        {//创建连接失败,函数返回

            ExFreePool(deviceName.Buffer);

            ExFreePool(deviceSymLink.Buffer);

            devExtP->ExportString = NULL;

            return FALSE;

        }

        /*设置应用程序可见的设备名称*/

        devExtP->ExportString = deviceSymLink.Buffer;

        ExFreePool(deviceName.Buffer);

        return TRUE;

    }

    else

    { //IoCreateDevice调用失败,函数返回

        ExFreePool(deviceName.Buffer);

        ExFreePool(deviceSymLink.Buffer);      

        return FALSE;

    }

}

函数NPF_CreateDevice主要使用IoCreateDevice与IoCreateSymbolicLink系统接口函数实现。

IoCreateDevice 函数为驱动程序的使用者分配内存,并初始化一个设备对象。函数原型如下:

NTSTATUS

  IoCreateDevice(

    IN PDRIVER_OBJECT  DriverObject,

    IN ULONG  DeviceExtensionSize,

    IN PUNICODE_STRING  DeviceName  OPTIONAL,

    IN DEVICE_TYPE  DeviceType,

    IN ULONG  DeviceCharacteristics,

    IN BOOLEAN  Exclusive,

    OUT PDEVICE_OBJECT  *DeviceObject

    );

函数各参数作用如下:

参数DriverObject 为输入参数,DriverObject为指向驱动对象的指针。每个驱动程序有唯一的驱动对象与之对应,但每个驱动对象会有若干个设备对象。

参数DeviceExtensionSize为输入参数,指定设备扩展的大小,I/O管理器会根据这个大小,在内存中创建设备对象的设备扩展。驱动程序使用该设备扩展来维护DeviceObject所描述设备I/O操作的上下文。设备扩展的内部结构由驱动程序自定义,NPF中_DEVICE_EXTENSION的定义如下

/*包含与每个适配器关联的结构体,在该适配器上NPF被绑定*/

typedef struct _DEVICE_EXTENSION {

//适配器的名称

NDIS_STRING    AdapterName;

//设备导出的名称,比如,应用程序通过WinPcap用来打开该适配器的名称

PWSTR          ExportString;   

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

参数DeviceName为可选的输入参数,设置设备对象的名称。指向一个以0结尾的Unicode字符串,同时字符串必须为完整的路径。利用该字符串对设备对象命名。

参数DeviceType 为输入参数,描述一个系统定义的FILE_DEVICE_XXX常量,表示设备的种类(诸如FILE_DEVICE_DISK、FILE_DEVICE_KEYBOARD等)或一个厂商定义的新类型设备。NPF设置为FILE_DEVICE_TRANSPORT值。

参数DeviceCharacteristics 为输入参数,描述一个或多个系统定义的常量,各常量可位或操作。该参数提供了有关驱动设备的额外信息。其中NPF所有的值为FILE_DEVICE_SECURE_OPEN。如果设备没有任何相关特性,设为0。

参数Exclusive为输入参数,指示设备对象是否表现为一个互斥设备。也就是说,只有一个句柄在同一时刻能够发送I/O请求,同一进程的多线程能够通过一个单独的句柄发送请求。

参数DeviceObject为输出参数,如果函数调用成功,指向新创建的设备对象。一个设备对象代表驱动程序所支持的一个物理的、虚拟的或逻辑的设备。

NPF中IoCreateDevice函数返回STATUS_SUCCESS则认为函数调用成功,否则为失败。IoCreateDevice函数创建一个设备对象并返回一个指向该对象的指针。当不再需要该对象时,调用者负责调用IoDeleteDevice函数释放该对象。 NPF在卸载驱动函数NPF_Unload中释放该对象。

如果指定了设备名,只能被内核模式下的其它驱动程序所识别。但是在用户模式下的应用程序无法识别这个设备。可以通过符号连接找到该设备,使得用户模式下的应用程序能识别该设备。符号连接可以理解为设备对象起了个“别名”。设备对象的名称只能被内核模式的驱动识别,而别名已可以被用户模式下的应用程序识别。创建符号连接采用IoCreateSymbolicLink函数。

函数IoCreateSymbolicLink在一个设备对象名称与一个用户可见的名称之间建立一个符号连接。函数原型如下:

NTSTATUS

  IoCreateSymbolicLink(

    IN PUNICODE_STRING  SymbolicLinkName,

    IN PUNICODE_STRING  DeviceName

    );

参数SymbolicLinkName为输入参数,指向一个Unicode字符串,该字符串为用户可见的名称。参数DeviceName为输入参数,指向一个Unicode字符串,该字符串为驱动程序所创建设备对象的名称。

如果符号连接创建成功,IoCreateSymbolicLink函数返回 STATUS_SUCCESS。当不再需要该符号连接时,NPF在卸载驱动函数NPF_Unload中调用IoDeleteSymbolicLink函数删除该符号连接。

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