Tcp过滤器的实现
1 引言
前段时间做了一个基于IPFILTERDRIVER的ip过滤驱动,使用了一段时间却出现了不少问题:
1、首先,IPFILTERDRIVER只能挂接一个驱动。也就是说,如果其他的驱动先挂接了IPFILTERDRIVER,那么我们的安装将失败;
2、其次,有的2000pro系统下居然缺少IPFILTERDRIVER驱动组件;
3、最后,某些系统会出现997错误,即“重叠IO操作正在进行”。
基于上述问题,重新实现了一个Tcp设备的过滤驱动程序TcpFilter,使用它来拦截ip访问操作。
2 过滤驱动程序
在2000以上的系统中,都实现了一个Tcp设备"//Device//Tcp",大部分上层的Intel网络通信都是通过这个设备完成的,如果我们实现一个Tcp设备的上层过滤程序,便能拦截到用户的网络访问。Tcp设备过滤程序的实现和其他驱动的过滤程序没什么两样,其代码如下:
RtlInitUnicodeString( &usFilterName, FILTER_NAME );
IoCreateDevice( pDrvObj,
sizeof(DEVICE_EXTENSION),
&usFilterName, // filter driver name
FILE_DEVICE_UNKNOWN,
0, TRUE,
&g_pFilterDevObj );
RtlInitUnicodeString(&DosDeviceName, DOS_DEVICE_NAME);
IoCreateSymbolicLink( &DosDeviceName, &usFilterName );
RtlInitUnicodeString( &usTargetName, TARGET_NAME );
IoGetDeviceObjectPointer( &usTargetName,
FILE_ALL_ACCESS,
&pTargetFileObj,
&pTargetDevObj );
// Initialize the Device Extension
pDevExt = (PDEVICE_EXTENSION) g_pFilterDevObj->DeviceExtension;
pDevExt->pDeviceObject = g_pFilterDevObj; // back pointer
// Pile this new filter on top of the existing target
pDevExt->pTargetDevice = // downward pointer
IoAttachDeviceToDeviceStack( g_pFilterDevObj, pdo);
for (i=0; i<=IRP_MJ_MAXIMUM_FUNCTION; i++)
if (i!=IRP_MJ_POWER)
pDrvObj->MajorFunction[i] = Dispatch;
3 Dispatch函数
Dispatch函数的完整代码如下:
NTSTATUS Dispatch(
IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp )
{
PDEVICE_EXTENSION pFilterExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( pIrp );
PIO_STACK_LOCATION pNextIrpStack;
UCHAR MajorFunction = pIrpStack->MajorFunction;
UCHAR MinorFunction = pIrpStack->MinorFunction;
PVOID p = &pIrpStack->Parameters;
if(MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL &&
MinorFunction == TDI_CONNECT) // IOCTL_TDI_CONNECT
{
TDI_REQUEST_KERNEL_CONNECT* trk = (TDI_REQUEST_KERNEL_CONNECT*)p;
TRANSPORT_ADDRESS* ta = (TRANSPORT_ADDRESS*)
trk->RequestConnectionInformation->RemoteAddress;
if(ta->Address[0].AddressType == TDI_ADDRESS_TYPE_IP)
{// only handle ip address
TDI_ADDRESS_IP* ipaddr = (TDI_ADDRESS_IP*)&ta->Address[0].Address;
USHORT port = ((ipaddr->sin_port&0xff) << 8) |
((ipaddr->sin_port&0xff00) >> 8);
DbgPrint(" ==> %u.%u.%u.%u(%u)",
ipaddr->in_addr&0xff,
(ipaddr->in_addr&0xff00)>>8,
(ipaddr->in_addr&0xff0000)>>16,
(ipaddr->in_addr&0xff000000)>>24,
port);
if(CheckIPAddress(ipaddr->in_addr))
{
pIrp->IoStatus.Status = STATUS_REMOTE_NOT_LISTENING;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_REMOTE_NOT_LISTENING;
}
}
}
else if(MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
NTSTATUS status = STATUS_SUCCESS;
// 处理用户自定义的IoControlCode
switch(pIrpStack->Parameters.DeviceIoControl.IoControlCode)
{
case ADD_FILTER:
DbgPrint("add filter %d", pIrpStack->Parameters.DeviceIoControl.InputBufferLength);
AddFilterIPList((PULONG)pIrp->AssociatedIrp.SystemBuffer,
pIrpStack->Parameters.DeviceIoControl.InputBufferLength/4);
break;
default:
goto passthru;
break;
}
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
}
passthru:
// Copy args to the next level
pNextIrpStack = IoGetNextIrpStackLocation( pIrp );
/* IoCopyCurrentIrpStackLocationToNext( pIrp );
// *pNextIrpStack = *pIrpStack;
// DbgPrint("Dispatch: Majorfun: %d, MinorFun: %d.", pIrpStack->MajorFunction,
// pIrpStack->MinorFunction);
// Set up a completion routine to handle the bubbling
// of the "pending" mark of an IRP
IoSetCompletionRoutine(
pIrp,
GenericCompletion,
NULL,
TRUE, TRUE, TRUE );
*/
// 如果使用上面的代码会出现NO_MORE_IRP_STACK_LOCATIONS蓝屏
IoSkipCurrentIrpStackLocation( pIrp );
// Pass the IRP to the target.
return IoCallDriver(
pFilterExt->pTargetDevice,
pIrp );
}
3.1 函数流程
Dispatch函数捕获上层的所有命令,首先拦截上层的Tcp连接操作,如果上层连接的地址没通过测试(CheckIPAddress),则中断此次连接return STATUS_REMOTE_NOT_LISTENING。其次,捕获应用层发给驱动的命令字,最后,把所有其他的命令传递给下层驱动处理。
3.2 TDI客户端
Dispatch函数接收到的命令由TDI客户端发送,如果想详细了解TDI的通信过程可以参考DDK或使用上述函数跟踪。TDI的网络命令其MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL,当TDI企图建立远程连接时,MinorFunction == TDI_CONNECT。此时远程地址的解析方式可参考上述完整代码。
3.3 IoSkipCurrentIrpStackLocation
刚开始的时候,我使用的是IoCopyCurrentIrpStackLocationToNext。但启动驱动不久便会出现蓝屏,bug码为:NO_MORE_IRP_STACK_LOCATIONS,费了很长时间也没解决此问题,最后索性改为IoSkipCurrentIrpStackLocation才一切正常。至今仍不知问题出在哪里,望高人指点迷津。
4 UnLoad问题
本来实现了一个unload函数,可每次卸载驱动的时候都蓝屏,错误码DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS,搞不定他,所以硬着头皮每次重起系统来调试程序。我写的unload函数的代码如下,希望有高人指点。
VOID
DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING DosDeviceName;
PDEVICE_OBJECT pFilterObj = DriverObject->DeviceObject;
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pFilterObj->DeviceExtension;
// Remove symbolic link
RtlInitUnicodeString(&DosDeviceName, DOS_DEVICE_NAME);
IoDeleteSymbolicLink(&DosDeviceName);
// detach
ObDereferenceObject(pTargetFileObj);
IoDetachDevice(pDevExt->pTargetDevice);
// Remove the device
IoDeleteDevice(pFilterObj);
DbgPrint("tcp filter unload ok");
}