NDIS生成的Filter例程已经非常完善,但根据需要还是要对它进行改造,以适应实际的需求,在这一类的改造中,主要涉及的三个方面: 处理OID、发送数据包、接收数据包。
需求和定义
一般来说,Filter 驱动要么需要对某些数据包进行处理、要么是需要对某些网络适配器的行为进行修改,所以需求最终是处理OID请求、发送和接收数据包。
为了便于演示,将会提供下面3个最基本的功能:
- 发起OID请求;
- 发送ICMP数据包;
- 接收的数据包;
案例将会创建4个IOCTL来演示:
#define _NDIS_CONTROL_CODE(request,method) \CTL_CODE(FILE_DEVICE_PHYSICAL_NETCARD, request, method, FILE_ANY_ACCESS)// 获取当前存在的网卡个数
// 输出缓冲区是一个ULONG的值,这个值指出当前系统上有 N 个网络适配器
#define IOCTL_FILTER_GET_NUMBER _NDIS_CONTROL_CODE(14, METHOD_BUFFERED)// 获取所有网卡的地址
// 输出缓冲区是一个 N*6 大小的缓冲区,根据加载顺序记录 Mac 地址
#define IOCTL_FILTER_GET_ALL_ADDRESS _NDIS_CONTROL_CODE(15, METHOD_BUFFERED)// 发送一个UDP广播包
// IOCTL_FILTER_SEND_DATA 输入/输出缓冲区
typedef struct _tagPACKET_INFO
{UCHAR SrcMac[_MAC_LENGTH];// Mac地址UCHAR DesMac[_MAC_LENGTH];// Mac地址ULONG SrcIPv4; // 源IP地址,源地址ULONG DesIPv4; // 目的IP地址,源地址// USHORT SrcPort; // 源端口 注意ICMP数据包没有使用这两个值// USHORT DesPort; // 目的端口 注意ICMP数据包没有使用这两个值USHORT Length; // 数据长度CHAR Data[_DATA_SIZE]; // 数据内容
}PACKET_INFO, * PPACKET_INFO;
#define IOCTL_FILTER_SEND_DATA _NDIS_CONTROL_CODE(16, METHOD_BUFFERED)// 接收最近的数据包
// 输入/输出缓冲区
#define IOCTL_FILTER_RECEIVE_DATA _NDIS_CONTROL_CODE(17, METHOD_BUFFERED)
这里的宏定义比较普通,只是NDIS自行封装了_NDIS_CONTROL_CODE(request,method) 。
设备对象和网络适配器
Filter层抽象了网络架构,无论是什么类型的网络适配器,它们对于Filter都是一样的接口,这也是为什么Filter驱动被用于网络数据包过滤的原因之一:
无论 以太网、Wifi、1394、蓝牙这些网络适配器都会在同一时间挂载Filter驱动,Filter驱动将作为所有的网络适配器过滤驱动来实现和设计的。
这里有一个概念上的区别,当Filter驱动被加载到内核中时,它作为一个模块,可能会实例化为多个Filter对象,每个Filter对象依附在一个网络适配器之上,这带来了两面性:
Filter驱动的通讯是针对Filter而不是每个网络适配器;
每个网络适配器都会有自己的上下文;
这两个点带来的直观影响是NDISFilterDeviceIoControl函数和具体的网络适配器不直接相关。
发送OID
NDIS 定义对象标识符 (OID) 值来标识适配器参数,其中包括操作参数,例如设备特征、可配置的设置和统计信息。这里使用获取网卡Mac地址来演示;
Mac地址涉及的OID包括:
1. OID_802_3_CURRENT_ADDRESS:NIC 当前使用的地址。网络管理软件无法使用 NDIS 接口库设置当前工作站地址。 它必须将此地址设置为配置参数。2. OID_802_3_PERMANENT_ADDRESS:硬件中编码的 NIC 的地址。
代码看起来如下:
NDIS_STATUS FilterGetMacAddress(_In_ NDIS_HANDLE FilterModuleContext)
{PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext;UCHAR Mac[_MAC_LENGTH];ULONG BytesProcessed = 0;NDIS_STATUS Status = 0;NdisZeroMemory(&Mac[0], _MAC_LENGTH);Status = filterDoInternalRequest(pFilter, NdisRequestQueryInformation, OID_802_3_PERMANENT_ADDRESS,(PVOID)&Mac[0], _MAC_LENGTH, 0, 0, &BytesProcessed);if (Status == NDIS_STATUS_SUCCESS){RtlCopyMemory(&pFilter->Mac[0], &Mac[0], _MAC_LENGTH);}return Status;
}NDIS_STATUS
filterDoInternalRequest(_In_ PMS_FILTER FilterModuleContext,_In_ NDIS_REQUEST_TYPE RequestType,_In_ NDIS_OID Oid,_Inout_updates_bytes_to_(InformationBufferLength, *pBytesProcessed)PVOID InformationBuffer,_In_ ULONG InformationBufferLength,_In_opt_ ULONG OutputBufferLength,_In_ ULONG MethodId,_Out_ PULONG pBytesProcessed)
{FILTER_REQUEST FilterRequest;PNDIS_OID_REQUEST NdisRequest = &FilterRequest.Request;NDIS_STATUS Status;ASSERT(pBytesProcessed != NULL);*pBytesProcessed = 0;NdisZeroMemory(NdisRequest, sizeof(NDIS_OID_REQUEST));NdisInitializeEvent(&FilterRequest.ReqEvent);NdisRequest->Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST;NdisRequest->Header.Revision = NDIS_OID_REQUEST_REVISION_1;NdisRequest->Header.Size = sizeof(NDIS_OID_REQUEST);NdisRequest->RequestType = RequestType;switch (RequestType){case NdisRequestQueryInformation:NdisRequest->DATA.QUERY_INFORMATION.Oid = Oid;NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer =InformationBuffer;NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength =InformationBufferLength;break;case NdisRequestSetInformation:NdisRequest->DATA.SET_INFORMATION.Oid = Oid;NdisRequest->DATA.SET_INFORMATION.InformationBuffer =InformationBuffer;NdisRequest->DATA.SET_INFORMATION.InformationBufferLength =InformationBufferLength;break;case NdisRequestMethod:NdisRequest->DATA.METHOD_INFORMATION.Oid = Oid;NdisRequest->DATA.METHOD_INFORMATION.MethodId = MethodId;NdisRequest->DATA.METHOD_INFORMATION.InformationBuffer =InformationBuffer;NdisRequest->DATA.METHOD_INFORMATION.InputBufferLength =InformationBufferLength;NdisRequest->DATA.METHOD_INFORMATION.OutputBufferLength = OutputBufferLength;break;default:ASSERTMSG("Invalid request type in filterDoInternalRequest",FALSE);break;}NdisRequest->RequestId = (PVOID)FILTER_REQUEST_ID;Status = NdisFOidRequest(FilterModuleContext->FilterHandle,NdisRequest);if (Status == NDIS_STATUS_PENDING){NdisWaitEvent(&FilterRequest.ReqEvent, 0);Status = FilterRequest.Status;}if (Status == NDIS_STATUS_SUCCESS){if (RequestType == NdisRequestSetInformation){*pBytesProcessed = NdisRequest->DATA.SET_INFORMATION.BytesRead;}if (RequestType == NdisRequestQueryInformation){*pBytesProcessed = NdisRequest->DATA.QUERY_INFORMATION.BytesWritten;}if (RequestType == NdisRequestMethod){*pBytesProcessed = NdisRequest->DATA.METHOD_INFORMATION.BytesWritten;}//// The driver below should set the correct value to BytesWritten// or BytesRead. But now, we just truncate the value to InformationBufferLength//if (RequestType == NdisRequestMethod){if (*pBytesProcessed > OutputBufferLength){*pBytesProcessed = OutputBufferLength;}}else{if (*pBytesProcessed > InformationBufferLength){*pBytesProcessed = InformationBufferLength;}}}return Status;
}