windows WDF驱动开发总结(7)--网络驱动开发(NDIS)

来源:互联网 发布:希特勒我的奋斗 知乎 编辑:程序博客网 时间:2024/05/31 06:22

NDIS 网络设备接口规范

(1)    NDIS_PROTOCOL_CHARACTERISTICS

函数功能:This structure is used to specify the version numbers and various callback functions for a protocol.

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;
  RECEIVE_PACKET_HANDLER ReceivePacketHandler;
  BIND_HANDLER BindAdapterHandler;
  UNBIND_HANDLER UnbindAdapterHandler;
  TRANSLATE_HANDLER TranslateHandler;
  UNLOAD_PROTOCOL_HANDLER UnloadHandler;
} NDIS_PROTOCOL_CHARACTERISTICS, *PNDIS_PROTOCOL_CHARACTERISTICS

 

(2)    IoCreateSymbolicLink

函数功能:sets up a symbolic link between a device object name and a user-visible name for the device.

NTSTATUS IoCreateSymbolicLink(
  __in  PUNICODE_STRING SymbolicLinkName,
  __in  PUNICODE_STRING DeviceName
);

参数:

SymbolicLinkName [in]

Pointer to a buffered Unicode string that is the user-visible name.

DeviceName [in]

Pointer to a buffered Unicode string that is the name of the driver-created device object.

 

 

驱动程序的设备对象(DeviceObject)为了给应用程序提供一个接口通常使用IoCreateSymbolicLink生成一个应用程序可见的符号链接,应用程序通过该符号连接与设备对象进行通讯。 WDM中则不是使用IoCreateSymbolicLink生成符号链接,取而代之的是IoRegisterDeviceInterface函数。

 

 

(3) NdisRegisterProtocol

函数功能:registers an NDIS driver's Protocol_* entry points and name with the NDIS library when the driver initializes.

VOID NdisRegisterProtocol(

  PNDIS_STATUS Status,             //out

  PNDIS_HANDLE NdisProtocolHandle, //out

  PNDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics,

  UINT CharacteristicsLength

);

Parameters

Status

[out] Pointer to a caller-supplied variable that gives the status of the registration attempt.

NdisProtocolHandle

[out] Pointer to a caller-supplied variable in which this function returns a handle representing the registered driver.

ProtocolCharacteristics

[in] Pointer to an NDIS_PROTOCOL_CHARACTERISTICS structure set up by the caller.

CharacteristicsLength

[in] Specifies the size, in bytes, of the structure at ProtocolCharacteristics. If the build directive NDIS40 is specified in the sources ahead of #include ndis.h, this value is supplied automatically.

Return Values

The following table shows the return values for this function.

Value

Description

NDIS_STATUS_SUCCESS

The NDIS library registered the caller as a protocol driver.

NDIS_STATUS_BAD_CHARACTERISTICS

The CharacteristicsLength is too small for the MajorNdisVersion specified in the buffer at ProtocolCharacteristics.

NDIS_STATUS_BAD_VERSION

The MajorNdisVersion specified in the buffer at ProtocolCharacteristics is invalid.

NDIS_STATUS_RESOURCES

A shortage of resources, possibly memory, prevented the NDIS library from registering the caller.

 

如果调用成功会返回一个协议NDIS50_PROTOCOL_BLOCK的数据指针(微软称为NdisProtocolHandle),实际上它是一个注册协议驱动单链表首指针。通过该指针即可遍历所有系统安装的协议,实现一些邪恶的用途。正常情况下,如果你使用了标准的官方做法 (使用标准的inf安装文件,并添加协议驱动到网卡),系统会在你注册协议驱动后,调用

BindAdapterHandler,也就是你的PacketBindAdapter函数,但是很多不喜欢驱动签名,且不喜欢 inf 安装并邪恶的人,发明了直接在 DriverEntry 里面使用NdisOpenAdpater直接打开网卡的方法,windows 200后不再支持这种方法。

预编译指令

#pragma alloc_text这个宏仅用来指定某个函数的可执行代码在编译出来后在sys文件中的位置。INIT节的特点是在初始化完毕之后被释放,即不再占用内存空间了,PAGE节的特点是位于可以进行分页交换的内存空间,这些空间在内存紧张时可以被交换到硬盘上以节省内存。PAGELK:说明加载后位于不可分页交换的内存空间中。

    函数DriverEntry只需要在初始化时执行一次。注意,放在PAGE内的函数不可以在Dispatch级调用,因为这种函数的调用可能诱发缺页中断,但是缺页中断不能再Dispatch级完成。为此,一般都用一个宏PAGED_CODE()进行测试,如果发现当前中断级为Dispatch级,则程序直接报异常,让程序员及早发现。

 

(3)    IoAttachDevie

函数功能:attaches the caller's device object to a named target device object, so that I/O requests bound for the target device are routed first to the caller.

NTSTATUS IoAttachDevice(

  __in   PDEVICE_OBJECT SourceDevice,

  __in   PUNICODE_STRING TargetDevice,

  __out  PDEVICE_OBJECT *AttachedDevice

);

Parameters

SourceDevice [in]

Pointer to the caller-created device object.

调用者生成用来过滤的虚拟设备;

TargetDevice [in]

Pointer to a buffer containing the name of the device object to which the specified SourceDevice is to be attached.

要绑定的目标设备,为 一个字符串。

AttachedDevice [out]

Pointer to caller-allocated storage for a pointer. On return, contains a pointer to the target device object if the attachment succeeds.

       一个用于返回的指针的指针,绑定成功后,被绑定的设备指针被返回到这个地址。如果一个设备被其它设备绑定,IoAttachDevice总是会绑定设备栈上最顶层的设备。

Return Value

can return one of the following NTSTATUS values:

STATUS_SUCCESS

STATUS_INVALID_PARAMETER

STATUS_OBJECT_TYPE_MISMATCH

STATUS_OBJECT_NAME_INVALID

STATUS_INSUFFICIENT_RESOURCES

 

5IoAttachDeviceToDeviceStack IoAttachDeviceToDeviceStackSafe函数

PDEVICE_OBJECT IoAttachDeviceToDeviceStack(

  __in  PDEVICE_OBJECT SourceDevice,        //过滤设备

  __in  PDEVICE_OBJECT TargetDevice //要被绑定的设备栈中的设备

);

 

NTSTATUS IoAttachDeviceToDeviceStackSafe(

  __in   PDEVICE_OBJECT SourceDevice,

  __in   PDEVICE_OBJECT TargetDevice,

  __out  PDEVICE_OBJECT *AttachedToDeviceObject // 返回最终被绑定的设备

);

 

IoAttachDeviceToDeviceStack IoAttachDeviceToDeviceStackSafe类似,前者主要在windows 2000等较低的版本中使用,后者在windows xp以上的版本使用,它和IoAttachDevice的区别是,可以绑定没有名字的设备。

 

6)从名字获取设备对象IoGetDeviceObjectPointer

函数功能:

NTSTATUS 
  IoGetDeviceObjectPointer(
    IN PUNICODE_STRING  ObjectName,    //
设备的名字

    IN ACCESS_MASK  DesiredAccess, //
存储权限
    OUT PFILE_OBJECT  *FileObject, //
获得这个设备对象的同时会得到的一个文件对象(File Object)
    OUT PDEVICE_OBJECT  *DeviceObject //
设备对象

    );

如果打开成功了,记得一定要把文件对象解除引用,调用的函数如下:

ObDereferenceObject(fileobj);

 

(7)一个写请求(串口一次发送的数据)保存的地方

 1irp->MDLAddress, 一个是irp->UserBuffer,一个是irp->AssociatedIrp.SystemBuffer.不同的IO类别,IRP的缓冲区不同。SystemBuffer是一般用于比较简单且不追求效率的情况,将应用层中的内存空间中的缓冲数据拷贝到内核空间。UserBuffer是最追求效率的方法,应用层的地址直接放在userBuffer,在内核空间中访问,在当前进程和发送请求进行一致的情况下,这种方法是正确的,但是一旦内核进程已经切换,就不对了。当然最简单的方法是把应用层的地址空间映射到内核空间,这需要在页表中增加一个映射,通过构造MDL可以实现这个功能,IRP中的MDLAddress是一个MDL的指针,从这个MDL中可以读出一个内核空间的虚拟地址。这就弥补了UserBuffer的不足,同时比SystemBuffer的安全拷贝方法要轻量,因为这个内存实际上还在老地方,没有拷贝。

    采用下面方法读取数据。调用的函数是MmGetSystemAddressForMdlSafe

如何动态卸载:

 

(8)注册协议NdisRegisterProtocol

函数功能:registers an NDIS driver's Protocol_* entry points and name with the NDIS library when the driver initializes.

VOID NdisRegisterProtocol(
  PNDIS_STATUS Status,
  PNDIS_HANDLE NdisProtocolHandle,
  PNDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics,
  UINT CharacteristicsLength
);

Parameters

Status

[out] Pointer to a caller-supplied variable that gives the status of the registration attempt.

NdisProtocolHandle

[out] Pointer to a caller-supplied variable in which this function returns a handle representing the registered driver.

ProtocolCharacteristics

[in] Pointer to an NDIS_PROTOCOL_CHARACTERISTICS structure set up by the caller.

CharacteristicsLength

[in] Specifies the size, in bytes, of the structure at ProtocolCharacteristics. If the build directive NDIS40 is specified in the sources ahead of #include ndis.h, this value is supplied automatically.

 

 

(9)协议与网卡的绑定

    设备对象之间的绑定(attach)与协议和网卡之间的绑定(bind)不同。

    如果说一个协议驱动绑定了一个网卡,那么意味着:

(1)            网卡收到的数据包会提交给这个协议。

(2)            协议可以使用这个网卡发送数据包。

协议和网卡的绑定并非总是一对一得关系,一般的协议都是采用绑定所有网卡的方法。如果计算机上插入了2块网卡,如果默认安装了TCP/IP的协议驱动,那么这2块网卡所接收到的数据包都是可以被TCP/IP协议接收到的。即TCP/IP这个协议驱动同时绑定在了2个网卡上。

    同时,一般一个网卡也可以绑定多个协议,假设同时安装了TCP/IPIPX两种协议驱动,那么当一个网卡接收到一个数据包时,实际上它会向2个协议提交这个数据包。当然,一般来说,一个数据包只会被一个协议处理,协议会直接无视它接收到的实际上并非本协议处理的数据包。

    绑定过程(包括驱动的加载和卸载)是这个协议驱动中最复杂的部分。一般的开发者都是直接拷贝WDKndisprot范例中的实现。

NdisProtBindAdapter

 

 

(10) IoCreateDeviceSecure

函数功能:creates a named device object and applies the specified security settings.

函数原型:

 

NTSTATUS IoCreateDeviceSecure(

  __in      PDRIVER_OBJECT DriverObject,

  __in      ULONG DeviceExtensionSize,

  __in_opt  PUNICODE_STRING DeviceName,

  __in      DEVICE_TYPE DeviceType,

  __in      ULONG DeviceCharacteristics,

  __in      BOOLEAN Exclusive,

  __in      PCUNICODE_STRING DefaultSDDLString,

  __in_opt  LPCGUID DeviceClassGuid,

  __out     PDEVICE_OBJECT *DeviceObject

);

 

参数:

DriverObject [in]

Pointer to the driver object for the caller. Each driver receives a pointer to its driver object in a parameter to its DriverEntry routine. WDM function and filter drivers also receive a driver object pointer in their AddDevice routines.

DeviceExtensionSize [in]

Specifies the driver-determined number of bytes to be allocated for the device extension of the device object. The internal structure of the device extension is driver-defined.

DeviceName [in, optional]

Optionally points to a buffer that contains a null-terminated Unicode string that names the device object. The string must be a full path name. If a name is not supplied, the FILE_AUTOGENERATED_DEVICE_NAME flag must be present in the DeviceCharacteristics parameter. (To create an unnamed device object, use the IoCreateDevice routine.)

DeviceType [in]

Specifies one of the system-defined FILE_DEVICE_XXX constants that indicate the type of device (such as FILE_DEVICE_DISK, FILE_DEVICE_KEYBOARD, and so on), or a vendor-defined value for a new type of device. For more information, see Specifying Device Types. (Because a bus driver might not have information about a device's type, a device type value for a PDO can be specified in an INF AddReg directive.)

DeviceCharacteristics [in]

Specifies one or more system-defined constants, ORed together, that provide additional information about the driver's device. For a list of possible device characteristics, see DEVICE_OBJECT. For more information about how to specify device characteristics, see Specifying Device Characteristics. Most drivers specify FILE_DEVICE_SECURE_OPEN for this parameter.

Exclusive [in]

Specifies if the device object represents an exclusive device. Most drivers set this value to FALSE. For more information, see Specifying Exclusive Access to Device Objects.

DefaultSDDLString [in]

Specifies a string representation for the default security settings of the device object. The security that is applied to the device object is derived from this string unless the system administrator places an override in the section of the registry identified by the DeviceClassGuid parameter.

The security setting is specified in a subset of Security Descriptor Definition Language (SDDL). A set of predefined constants (SDDL_DEVOBJ_XXX) are also provided. For more information, see Securing Device Objects.

DeviceClassGuid [in, optional]

Pointer to a GUID that identifies a section of the registry containing possible overrides for the DefaultSDDLString, DeviceType, DeviceCharacteristics, and Exclusive parameters.

Note   You should always specify a custom class GUID. You should not specify an existing class GUID. If you specify an existing class GUID, other drivers that attempt to specify that existing class GUID might fail to install or might install with incorrect security settings.

DeviceObject [out]

Pointer to a variable that receives a pointer to the newly created DEVICE_OBJECT structure. The DEVICE_OBJECT structure is allocated from nonpaged pool.

 

评论:该函数在windows 2000以后的系统上使用,如果是 windows 9.x,则采用IoCreateDevice

原创粉丝点击