TDI 过滤驱动
TDI FILTER 过滤驱动的功能一般用来进行整个系统中的所有网络流量的分析,记录和管理,可以实现非常强大的管理功能,这里就将讨论它的设计架构,和具体实现的方法。
进行系统级网络数据包的过滤,很明显,第一步需要在系统内核中截取到网络数据包,那么在WINDOWS平台下,应该如何实现这样的功能?
在WINDOWS内核中,数据的通信载体是IRP包,如果希望截取到IRP数据包,当然必须生成核模块以驱动的方式加载至内核之中。如果只是需要用来进行IRP数据包的截取,进而进行数据的分析,及下一步工作的控制。比较合适的方式就是使用TDI FILTER驱动的方式。
它在内核中的结构如图所示:
TDI FILTER ( 你的DRIVER )
TDI DRIVER ( AFD.SYS )
附加至TDI设备的方法:
在DriverEntry时,生成两个设备,将其附加至(Attach)至Tdi驱动的Udp和Tcp设备,实现IRP包过滤功能,具体代码如下:
#define UDP_DEVICE_NAME L"//Device//Udp"
#define TCP_DEVICE_NAME L"//Device//Tcp"
#define TDI_FILTER_DEVICE_NAME L"//Device//TdiFilter"
typedef struct __TDI_FILTER_DEVICE_EXTENSION
{
PDEVICE_OBJECT pTdiDeviceObject; //过滤设备至少需要记录下真正Tdi网络设备的指针,来调用真正的TDI设备功能。
} TDI_FILTER_DEVICE_EXTENSION, *PTDI_FILTER_DEVICE_EXTENSION;
DriverEntry( PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath )
{
UNICODE_STRING TdiDeviceName;
......
RtlInitUnicodeString( ( PUNICODE_STRING )&TdiDeviceName,
UDP_DEVICE_NAME );
ntStatus = IoCreateDevice(
DriverObject,
sizeof( TDI_FILTER_DEVICE_EXTENSION ), //指定设备扩展长度
NULL,
FILE_DEVICE_NETWORK, //网络类型设备
0,
0,
&DeviceObject
); //生成一个无名、网络类型设备,附加至TDI TCP/UDP设备,实现过滤功能。
if( NT_SUCCESS( ntStatus ) )
{
DeviceObject->Flags |= DO_DIRECT_IO; //生成新的页表将同样的用户内存空间映射至系统虚拟内存空间来进行通信
ntStatus = IoAttachDevice(
DeviceObject,
TdiDeviceName,
( PDEVICE_OBJECT* )DeviceObject->DeviceExtension //附加至的设备的指针将会输出至此参数中,这样就将真正的TDI设备的指针记录在过滤设备的扩展中
);
}
}
TDI驱动的组织结构分为两个部分:
1.庞大的INTERNAL IO CONTROL子功能,包括以下功能:
TDI_ASSOCIATE_ADDRESS 可以通过它截取出自己和对端的套接字信息,一般就是IP地址+端口号,可以在此IRP功能响应中进行套接字信息的记录。
TDI_DISASSOCIATE_ADDRESS 它的IRP包是在closesocket函数时发生的,所以如果我们在TDI_ASSOCIATE_ADDRESS中记录了信息,需要在此IRP的功能响应中取消之前的记录。
TDI_CONNECT
TDI_LISTEN
TDI_ACCEPT
TDI_DISCONNECT
TDI_SEND 它的IRP包是在调用send函数时发生的,必然,对它的响应将会实现对基于TCP协议的网络上传流量的截取。
TDI_RECEIVE 它的IRP包是在调用recv函数时发生的,必然,对它的响应将会实现对基于TCP协议的网络下载流量的截取。
TDI_SEND_DATAGRAM 它的IRP包是在调用sendto函数时发生的,必然,对它的响应将会实现对基于UDP协议的网络上传流量的截取。
TDI_RECEIVE_DATAGRAM 它的IRP包是在调用recvfrom函数时发生的,必然,对它的响应将会实现对基于UDP协议的网络下载流量的截取。
TDI_SET_EVENT_HANDLER 它的IRP包是在TDI驱动中注册一些回调用函数,当接收到数据包时,将会首先执行它们,它的具体功能将会在下一步讲述。
TDI_QUERY_INFORMATION
TDI_SET_INFORMATION
TDI_ACTION
TDI_DIRECT_SEND
TDI_DIRECT_SEND_DATAGRAM
在TDI_SET_EVENT_HANDLER子功能中,可以注册以下回调涵数:
TDI_EVENT_CONNECT
TDI_EVENT_DISCONNECT
TDI_EVENT_ERROR
TDI_EVENT_RECEIVE 对应于recv函数有返回数据时,将会调用此回调函数。
TDI_EVENT_RECEIVE_DATAGRAM 对应于recvfrom函数接收到数据时,将会调用此回调函数。
TDI_EVENT_RECEIVE_EXPEDITED 对应于函数接收到带外数据时,将会调用此回调函数。( 带外数也就是OOB数据, 在全部IRP数据包中会优先进行发送或接收,TCP协议功能 )
TDI_EVENT_SEND_POSSIBLE
以下将讲述数据具体传输回调功能的过滤方法
4.实现事件回调函数挂钩的方法:
响应IRP_MJ_INTERNAL_DEVICE_CONTROL中的TDI_SET_EVENT_HANDLER子功能,记录下原始的注册事件回调函数和参数,但真正注册的是自己的回调函数,来截取所有的事件回调函数调用,实现过滤功能。
具体代码如下:
typedef struct __TDI_EVENT_CONTEXT_WRAP
{
DWORD dwEventContextMark; //对自己生成的结构实例加一个四字节的标志,可以不使用。
DWORD dwEventType; //记录事件回调函数的类型
PVOID pOrgEventHandler; //记录原始的事件回调函数
PVOID pOrgEventContext; //记录原始的事件回调函数参数
PFILE_OBJECT pAssocAddr; //记录事件回调函数所绑定的本机套接字
PDEVICE_OBJEXT pDeviceObjext; //记录注册事件IRP所发送至的TDI设备
} TDI_EVENT_HANDLER_WRAP, *PTDI_EVENT_HANDLER_WRAP;
typedef struct __TDI_EVENT_HANDLER_LINK
{
LIST_ENTRY List; //将事件回调钩子记录以链表形式进行管理
PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap;
} TDI_EVENT_HANDLER_LIST, *PTDI_EVENT_HANDLER_LIST;
LIST_ENTRY g_TdiEventHandlerInfoList;
NTSTATUS DeviceInternalIoControl( PDEVICE_OBJECT DeviceObject, PIRP Irp )
{
PKIRQL OldIrql;
PLIST_ENTRY pListEntry;
PIO_STACK_LOCATION IrpSp;
PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap;
PTDI_EVENT_HANDLER_LIST pTdiEventHandlerList;
PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap_;
PTDI_EVENT_HANDLER_LIST pTdiEventHandlerList_;
PTDI_FILTER_DEVICE_EXTENSION pTdiDeviceExtension;
pTdiDeviceExtension = ( PTDI_FILTER_DEVICE_EXTENSION )DeviceObject->DeviceExtension;
switch( IrpSp->MinorFunction )
{
case TDI_SET_EVENT_HANDLER:
pTdiSetEvent = ( PTDI_REQUEST_KERNEL_SET_EVENT )&pIrpSp->Parameters;
if( TDI_EVENT_RECEIVE == pTdiSetEvent->EventType )//||
TDI_EVENT_RECEIVE_EXPEDITED == pTdiSetEvent->EventType ||
TDI_EVENT_CHAINED_RECEIVE == pTdiSetEvent->EventType ||
TDI_EVENT_CHAINED_RECEIVE_EXPEDITED == pTdiSetEvent->EventType ||
TDI_EVENT_RECEIVE_DATAGRAM == pTdiSetEvent->EventType )
{
pTdiEventHandlerList = NULL;
pTdiEventHandlerWrap = NULL;
pProcessNetWorkTrafficInfo = NULL;
if( NULL == pTdiSetEvent->EventHandler )
{
//注意!如果注册的事件回调函数是NULL的话,它表示的取消之前曾经注册过的事件回调函数, 这里当然不能挂钩,可以加入释放钩子资源的操作。
goto CALL_PDO_DRIVER;
}
KeAcquireSpinLock( &g_SpLockTdiEventHandlerInfo, &OldIrql ); //对事件回调函数钩子列表写操作加锁保护
pListEntry = g_TdiEventHandlerInfoList.Flink;
for( ; ; )
{
if( pListEntry == &g_TdiEventHandlerInfoList )
{
pTdiEventHandlerWrap_ = NULL;
break;
}
pTdiEventHandlerList_ = ( PTDI_EVENT_HANDLER_LIST )pListEntry;
pTdiEventHandlerWrap_ = pTdiEventHandlerList_->pTdiEventHandlerWrap;
if( pTdiEventHandlerWrap_->pAssocAddr == pFileObject &&
pTdiEventHandlerWrap_->dwEventType == dwEventType ) //如果此本机套接字对象的相应事件回调函数已经存在,则直接对其进行修改就可以了,而不是不断的新建事件件回调钩子
{
pTdiEventHandlerWrap_->pOrgEventHandler = pEventHandler;
pTdiEventHandlerWrap_->pOrgEventContext = pEventContext;
break;
}
}
if( NULL == pTdiEventHandlerWrap_ ) //没有找到,加入新的事件回调函数钩子
{
pTdiEventHandlerWrap = ( PTDI_EVENT_HANDLER_WRAP )ExAllocatePoolWithTag( NonPagedPool, sizeof( TDI_EVENT_HANDLER_WRAP ), 0 );
if( NULL == pTdiEventHandlerWrap )
{
goto RELEASE_RESOURCE;
}
pTdiEventHandlerList = ( PTDI_EVENT_HANDLER_LIST )ExAllocatePoolWithTag( NonPagedPool, sizeof( TDI_EVENT_HANDLER_LIST ), 0 );
if( NULL == pTdiEventHandlerList )
{
goto RELEASE_RESOURCE;
}
pTdiEventHandlerWrap->dwEventContextMark = TDI_EVENT_CONTEXT_MARK;
pTdiEventHandlerWrap->dwEventType = dwEventType;
pTdiEventHandlerWrap->pOrgEventHandler = pEventHandler;
pTdiEventHandlerWrap->pOrgEventContext = pEventContext;
pTdiEventHandlerWrap->pAssocAddr = pFileObject;
pTdiEventHandlerWrap->pDeviceObject = pTdiDeviceExtension->pTdiDeviceObject;
pTdiEventHandlerList->pTdiEventHandlerWrap = pTdiEventHandlerWrap;
InsertTailList( &g_TdiEventHandlerInfoList, pTdiEventHandlerList );
}
else
{
pTdiEventHandlerWrap = pTdiEventHandlerWrap_;
pTdiEventHandlerList = pTdiEventHandlerList_;
}
KeReleaseSpinLock( &g_SpLockTdiEventHandlerInfo, OldIrql ); //释放事件回调钩子列表锁
if( TDI_EVENT_RECEIVE == pTdiSetEvent->EventType ||
TDI_EVENT_RECEIVE_EXPEDITED == pTdiSetEvent->EventType )
{
pTdiSetEvent->EventHandler = TdiFilterRecvEventHandler; //加入自己的事件过滤回调函数
}
else if( TDI_EVENT_CHAINED_RECEIVE == pTdiSetEvent->EventType ||
TDI_EVENT_CHAINED_RECEIVE_EXPEDITED == pTdiSetEvent->EventType )
{
pTdiSetEvent->EventHandler = TdiFilterChainedRecvHandler;
}
else
{
pTdiSetEvent->EventHandler = TdiFilterRecvDatagramEventHandler;
}
pTdiSetEvent->EventContext = pTdiEventHandlerWrap;
IoSkipCurrentIrpStackLocation( pIrp );
ntStatus = IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );
if( !NT_SUCCESS( ntStatus ) )
{
if( NULL == pTdiEventHandlerWrap_ )
{
//如果是新加入的事件回调函数钩子,可以在出错时将其释放, 也可以保留至套接字关闭时,再进行释放
KeAcquireSpinLock( &g_SpLockTdiEventHandlerInfo, &OldIrql ); //对事件回调函数钩子列表写操作加锁保护
RemoveEntryList( ( PLIST_ENTRY )pTdiEventHandlerList );
ExFreePoolWithTag( pTdiEventHandlerWrap );
ExFreePoolWithTag( pTdiEventHandlerList );
KeReleaseSpinLock( &g_SpLockTdiEventHandlerInfo, OldIrql ); //释放事件回调钩子列表锁
}
}
return ntStatus;
}
break;
default:
goto CALL_PDO_DRIVER;
break;
}
RELEASE_RESOURCE:
if( NULL != pTdiEventHandlerWrap )
{
ExFreePoolWithTag( pTdiEventHandlerWrap, NonPagedPool );
}
if( NULL != pTdiEventHandlerList )
{
ExFreePoolWithTag( pTdiEventHandlerList, NonPagedPool );
}
CALL_PDO_DRIVER:
IoSkipCurrentIrpStackLocation( pIrp );
return IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );
}
以上对事件回调函数加入了钩子,下一步,必须考虑对其释放的问题,否则,当原始回调函数对应的套接字释放后,你的系统将会崩溃,以下为具体代码:
在套接字关闭后,要在IRP_MJ_CLEANUP功能函数中将相关的事件回调钩子释放掉:
NTSTATUS TdiFilterCleanUp(PDEVICE_OBJECT DeviceObject, PIRP pIrp )
{
NTSTATUS ntStatus;
KIRQL OldIrql;
PTDI_EVENT_HANDLER_LIST pTdiEventHandlerList;
PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap;
PFILE_OBJECT pFileObject;
TDI_FILTER_DEVICE_EXTENSION *pDeviceExtension;
PIO_STACK_LOCATION pIrpSp;
pDeviceExtension = ( TDI_FILTER_DEVICE_EXTENSION* )DeviceObject->DeviceExtension;
pIrpSp = IoGetCurrentIrpStackLocation( pIrp );
pFileObject = pIrpSp->FileObject;
...
//如果是主控制设备,要将调用IoCompleteIrp完成Irp, 如果是过滤设备,调用PDO设备驱动
IoSkipCurrentIrpStackLocation( pIrp );
ntStatus = IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );
if( !NT_SUCCESS( ntStatus ) )
{
DebugPrintEx( CLEANUP_INFO,"netmon TdiFilterCleanUp IoCallDriver return ERROR/n" );
return ntStatus;
}
//下一步,释放套接字对应的事件回调钩子
KeAcquireSpinLock( &g_SpLockTdiEventHandlerInfo, &OldIrql );
FIND_LIST_AGAIN:
pListEntry = g_TdiEventHandlerInfoList.Flink;
for( ; ; )
{
if( pListEntry == &g_TdiEventHandlerInfoList )
{
break;
}
pTdiEventHandlerList = ( PTDI_EVENT_HANDLER_LIST )pListEntry;
pTdiEventHandlerWrap = pTdiEventHandlerList->pTdiEventHandlerWrap;
if( pTdiEventHandlerWrap->pAssocAddr == pFileObject )
{
RemoveEntryList( pListEntry );
ExFreePoolWithTag( pTdiEventHandlerWrap, 0 );
ExFreePoolWithTag( pTdiEventHandlerList, 0 );
goto FIND_LIST_AGAIN;
}
pListEntry = pListEntry->Flink;
}
KeReleaseSpinLock( &g_SpLockTdiEventHandlerInfo, OldIrql );
return ntStatus;
}
那么,可以在事件回调过滤钩子函数对数据进行处理了
NTSTATUS TdiFilterRecvEventHandler( IN PVOID TdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN ULONG ReceiveFlags,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *BytesTaken,
IN PVOID Tsdu,
OUT PIRP *IoRequestPacket
)
{
NTSTATUS ntStatus;
PIO_STACK_LOCATION pIrpSp;
PTDI_EVENT_HANDLER_WRAP pEventHandlerWrap;
PTDI_COMPLETION_WRAP pCompletionWrap;
LARGE_INTEGER RecvedDataSize;
pEventHandlerWrap = ( PTDI_EVENT_HANDLER_WRAP )TdiEventContext;
if( FALSE == g_bFiltering ) //是否进行过滤
{
goto CALL_ORIGINAL_EVENT_HANDLER;
}
if( FALSE != bStopRecv )
{
ntStatus = STATUS_DATA_NOT_ACCEPTED;
goto RELEASE_PROCESS_IO_INFO_RETURN;
}
ntStatus = ( ( ClientEventReceive )pEventHandlerWrap->pOrgEventHandler )(
pEventHandlerWrap->pOrgEventContext,
ConnectionContext,
ReceiveFlags,
BytesIndicated,
BytesAvailable,
BytesTaken,
Tsdu,
IoRequestPacket
);
if( NULL != BytesTaken &&
0 != *BytesTaken )
{
//这里对数据进行处理, 比如可以进行通信数据量的统计
}
if( STATUS_MORE_PROCESSING_REQUIRED != ntStatus )
{
goto RELEASE_PROCESS_IO_INFO_RETURN;
}
if( NULL == *IoRequestPacket )
{
goto RELEASE_PROCESS_IO_INFO_RETURN;
}
//IoRequestPacket表示当前接收IRP中的数据如果并不完整, 并且认为接下来的数据是有价值,需要接收的话,那么需要自己新建一个IRP包,将其指针传入此参数中,并返回STATUS_MORE_PROCESSING_REQUIRED,通知IO管理不终止此IRP,TDI驱动将继续接收接下来的数据。
//所以如果此IRP包存在,可以截取它的信息,具体方法下一步讲述。
return ntStatus;
CALL_ORIGINAL_EVENT_HANDLER:
return ( ( ClientEventReceive )pEventHandlerWrap->pOrgEventHandler )( //直接调用原始的IRP钩子函数,不进行处理
pEventHandlerWrap->pOrgEventContext,
ConnectionContext,
ReceiveFlags,
BytesIndicated,
BytesAvailable,
BytesTaken,
Tsdu,
IoRequestPacket
);
}
上面讲述了使用事件回调函数钩子的方式进行通信数据的截取方法,下面讲述直接IRP包数据传输方式,也就是以下4个子功能的截取方法:
TDI_SEND
TDI_RECEIVE
TDI_SEND_DATAGRAM
TDI_RECEIVE_DATAGRAM
在IRP_MJ_INTERNAL_DEVICE_CONTROL函数中响应以上子功能时,确认参数DeviceObject为TDI过滤设备,对所有截取到的IRP加入自己的完成函数,在此IRP被完成时( 调用IoCompleteRequest )此完成函数被调用,取得IRP处理的返回结果,进行处理。具体数据的处理对应于TDI_SEND子功能可以在IoCallDriver之前得到,因为它是应用程序传给你的,而TDI_RECEIVE子功能,应该在TDI事件回调函数或Completion回调函数中取得。
相关代码如下:
typedef struct __TDI_COMPLETION_WRAP
{
...//可以加入用来记录/处理数据的成员, 比如通信标志, 流量统计等
PIO_COMPLETION_ROUTINE pCompletionRoutine;
LPVOID pContext;
} TDI_COMPLETION_WRAP, *PTDI_COMPLETION_WRAP;
加入自定义的CompletionRoutine的方法:
if( TDI_SEND == MinorFunction ||
TDI_SEND_DATAGRAM == MinorFunction ||
TDI_RECEIVE == MinorFunction ||
TDI_RECEIVE_DATAGRAM == MinorFunction )
{
if( TDI_RECEIVE == MinorFunction &&
TDI_RECEIVE_PEEK == ( ULONG )pIrpSp->Parameters.Others.Argument2 )
{
//TDI_RECEIVE_PEEK不会真正接收数据,可以不需要对其进行过滤。
goto SKIP_CURRENT_STACK_LOCATION;
}
pCompletionWrap = ( PTDI_COMPLETION_WRAP )ExAllocateFromNPagedLookasideList( &g_CompletionWrapList ); //可以使用链表或HASH等数据结构来管理所有的CompletionRoutine包装信息,这里使用了NPAGED_LOOKASIDE_LIST,它的优势在于系统中所有的NPAGED_LOOKASIDE_LIST资源的最大占用量将会被内存管理器动态管理
if( NULL == pCompletionWrap )
{
goto SKIP_CURRENT_STACK_LOCATION;
}
//这里可以设置CompletionRoutine的具体工作参数,比如具体操作的类型,原始的Completion函数等,在用户层传送至的IRP中是不会设置CompletionRoutine函数的,但其它驱动传送至的IRP中可能会进行设置,如在Receive事件回调函数中的IoRequestPacket参数
IoCopyCurrentIrpStackLocationToNext( pIrp ); //设置下一个设备栈工作参数
IoSetCompletionRoutine( pIrp,
TdiFilterCompletion,
pCompletionWrap,
TRUE,
TRUE,
TRUE
);//这里就为这个IRP加入自己的CompletionRoutine函数
goto CALL_PDO_DRIVER;
SKIP_CURRENT_STACK_LOCATION:
IoSkipCurrentIrpStackLocation( pIrp );
CALL_PDO_DRIVER:
return IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );
}
具体的Completion函数的工作:
NTSTATUS TdiFilterCompletion( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, LPVOID pContext )
{
NTSTATUS ntStatus;
PTDI_COMPLETION_WRAP pCompletionWrap;
LARGE_INTEGER TransferredDataSize;
PIRP pMasterIrp;
PIO_STACK_LOCATION pIrpSp;
ntStatus = pIrp->IoStatus.Status;
pCompletionWrap = ( PTDI_COMPLETION_WRAP )pContext;
if( NT_SUCCESS( ntStatus ) )
{
//可以在这里对成功传输的数据进行处理
}
//这里可以调用原始的Completion函数
RETURN_SUCCESS:
return ntStatus;
}
需要注意的是,如果为IRP包加入了CompletionRoutine之后,那么在驱动卸载( Unload )之前,必须保证所有IRP已经执行过此Completion函数, 如果在驱动被从内存中卸载后才执行, 将会使系统崩溃。
处理方法为:
1.不实现DriverUnload函数,使驱动只有在系统关闭,底层设备被卸载时,才能完成真正的卸载。这是的一般FILTER驱动的工作方式,
2.使用线程同步的方法保证Completion函数的执行,Windows XP或之后的系统也提供了一个API, SetCompletionRoutineEx来保证驱动在Completion函数完成前不被卸载。
至此,讲述TDI过滤驱动组织框架,可以为它添加一些更加完善的功能。
进行系统级网络数据包的过滤,很明显,第一步需要在系统内核中截取到网络数据包,那么在WINDOWS平台下,应该如何实现这样的功能?
在WINDOWS内核中,数据的通信载体是IRP包,如果希望截取到IRP数据包,当然必须生成核模块以驱动的方式加载至内核之中。如果只是需要用来进行IRP数据包的截取,进而进行数据的分析,及下一步工作的控制。比较合适的方式就是使用TDI FILTER驱动的方式。
它在内核中的结构如图所示:
TDI FILTER ( 你的DRIVER )
TDI DRIVER ( AFD.SYS )
附加至TDI设备的方法:
在DriverEntry时,生成两个设备,将其附加至(Attach)至Tdi驱动的Udp和Tcp设备,实现IRP包过滤功能,具体代码如下:
#define UDP_DEVICE_NAME L"//Device//Udp"
#define TCP_DEVICE_NAME L"//Device//Tcp"
#define TDI_FILTER_DEVICE_NAME L"//Device//TdiFilter"
typedef struct __TDI_FILTER_DEVICE_EXTENSION
{
PDEVICE_OBJECT pTdiDeviceObject; //过滤设备至少需要记录下真正Tdi网络设备的指针,来调用真正的TDI设备功能。
} TDI_FILTER_DEVICE_EXTENSION, *PTDI_FILTER_DEVICE_EXTENSION;
DriverEntry( PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath )
{
UNICODE_STRING TdiDeviceName;
......
RtlInitUnicodeString( ( PUNICODE_STRING )&TdiDeviceName,
UDP_DEVICE_NAME );
ntStatus = IoCreateDevice(
DriverObject,
sizeof( TDI_FILTER_DEVICE_EXTENSION ), //指定设备扩展长度
NULL,
FILE_DEVICE_NETWORK, //网络类型设备
0,
0,
&DeviceObject
); //生成一个无名、网络类型设备,附加至TDI TCP/UDP设备,实现过滤功能。
if( NT_SUCCESS( ntStatus ) )
{
DeviceObject->Flags |= DO_DIRECT_IO; //生成新的页表将同样的用户内存空间映射至系统虚拟内存空间来进行通信
ntStatus = IoAttachDevice(
DeviceObject,
TdiDeviceName,
( PDEVICE_OBJECT* )DeviceObject->DeviceExtension //附加至的设备的指针将会输出至此参数中,这样就将真正的TDI设备的指针记录在过滤设备的扩展中
);
}
}
TDI驱动的组织结构分为两个部分:
1.庞大的INTERNAL IO CONTROL子功能,包括以下功能:
TDI_ASSOCIATE_ADDRESS 可以通过它截取出自己和对端的套接字信息,一般就是IP地址+端口号,可以在此IRP功能响应中进行套接字信息的记录。
TDI_DISASSOCIATE_ADDRESS 它的IRP包是在closesocket函数时发生的,所以如果我们在TDI_ASSOCIATE_ADDRESS中记录了信息,需要在此IRP的功能响应中取消之前的记录。
TDI_CONNECT
TDI_LISTEN
TDI_ACCEPT
TDI_DISCONNECT
TDI_SEND 它的IRP包是在调用send函数时发生的,必然,对它的响应将会实现对基于TCP协议的网络上传流量的截取。
TDI_RECEIVE 它的IRP包是在调用recv函数时发生的,必然,对它的响应将会实现对基于TCP协议的网络下载流量的截取。
TDI_SEND_DATAGRAM 它的IRP包是在调用sendto函数时发生的,必然,对它的响应将会实现对基于UDP协议的网络上传流量的截取。
TDI_RECEIVE_DATAGRAM 它的IRP包是在调用recvfrom函数时发生的,必然,对它的响应将会实现对基于UDP协议的网络下载流量的截取。
TDI_SET_EVENT_HANDLER 它的IRP包是在TDI驱动中注册一些回调用函数,当接收到数据包时,将会首先执行它们,它的具体功能将会在下一步讲述。
TDI_QUERY_INFORMATION
TDI_SET_INFORMATION
TDI_ACTION
TDI_DIRECT_SEND
TDI_DIRECT_SEND_DATAGRAM
在TDI_SET_EVENT_HANDLER子功能中,可以注册以下回调涵数:
TDI_EVENT_CONNECT
TDI_EVENT_DISCONNECT
TDI_EVENT_ERROR
TDI_EVENT_RECEIVE 对应于recv函数有返回数据时,将会调用此回调函数。
TDI_EVENT_RECEIVE_DATAGRAM 对应于recvfrom函数接收到数据时,将会调用此回调函数。
TDI_EVENT_RECEIVE_EXPEDITED 对应于函数接收到带外数据时,将会调用此回调函数。( 带外数也就是OOB数据, 在全部IRP数据包中会优先进行发送或接收,TCP协议功能 )
TDI_EVENT_SEND_POSSIBLE
以下将讲述数据具体传输回调功能的过滤方法
4.实现事件回调函数挂钩的方法:
响应IRP_MJ_INTERNAL_DEVICE_CONTROL中的TDI_SET_EVENT_HANDLER子功能,记录下原始的注册事件回调函数和参数,但真正注册的是自己的回调函数,来截取所有的事件回调函数调用,实现过滤功能。
具体代码如下:
typedef struct __TDI_EVENT_CONTEXT_WRAP
{
DWORD dwEventContextMark; //对自己生成的结构实例加一个四字节的标志,可以不使用。
DWORD dwEventType; //记录事件回调函数的类型
PVOID pOrgEventHandler; //记录原始的事件回调函数
PVOID pOrgEventContext; //记录原始的事件回调函数参数
PFILE_OBJECT pAssocAddr; //记录事件回调函数所绑定的本机套接字
PDEVICE_OBJEXT pDeviceObjext; //记录注册事件IRP所发送至的TDI设备
} TDI_EVENT_HANDLER_WRAP, *PTDI_EVENT_HANDLER_WRAP;
typedef struct __TDI_EVENT_HANDLER_LINK
{
LIST_ENTRY List; //将事件回调钩子记录以链表形式进行管理
PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap;
} TDI_EVENT_HANDLER_LIST, *PTDI_EVENT_HANDLER_LIST;
LIST_ENTRY g_TdiEventHandlerInfoList;
NTSTATUS DeviceInternalIoControl( PDEVICE_OBJECT DeviceObject, PIRP Irp )
{
PKIRQL OldIrql;
PLIST_ENTRY pListEntry;
PIO_STACK_LOCATION IrpSp;
PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap;
PTDI_EVENT_HANDLER_LIST pTdiEventHandlerList;
PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap_;
PTDI_EVENT_HANDLER_LIST pTdiEventHandlerList_;
PTDI_FILTER_DEVICE_EXTENSION pTdiDeviceExtension;
pTdiDeviceExtension = ( PTDI_FILTER_DEVICE_EXTENSION )DeviceObject->DeviceExtension;
switch( IrpSp->MinorFunction )
{
case TDI_SET_EVENT_HANDLER:
pTdiSetEvent = ( PTDI_REQUEST_KERNEL_SET_EVENT )&pIrpSp->Parameters;
if( TDI_EVENT_RECEIVE == pTdiSetEvent->EventType )//||
TDI_EVENT_RECEIVE_EXPEDITED == pTdiSetEvent->EventType ||
TDI_EVENT_CHAINED_RECEIVE == pTdiSetEvent->EventType ||
TDI_EVENT_CHAINED_RECEIVE_EXPEDITED == pTdiSetEvent->EventType ||
TDI_EVENT_RECEIVE_DATAGRAM == pTdiSetEvent->EventType )
{
pTdiEventHandlerList = NULL;
pTdiEventHandlerWrap = NULL;
pProcessNetWorkTrafficInfo = NULL;
if( NULL == pTdiSetEvent->EventHandler )
{
//注意!如果注册的事件回调函数是NULL的话,它表示的取消之前曾经注册过的事件回调函数, 这里当然不能挂钩,可以加入释放钩子资源的操作。
goto CALL_PDO_DRIVER;
}
KeAcquireSpinLock( &g_SpLockTdiEventHandlerInfo, &OldIrql ); //对事件回调函数钩子列表写操作加锁保护
pListEntry = g_TdiEventHandlerInfoList.Flink;
for( ; ; )
{
if( pListEntry == &g_TdiEventHandlerInfoList )
{
pTdiEventHandlerWrap_ = NULL;
break;
}
pTdiEventHandlerList_ = ( PTDI_EVENT_HANDLER_LIST )pListEntry;
pTdiEventHandlerWrap_ = pTdiEventHandlerList_->pTdiEventHandlerWrap;
if( pTdiEventHandlerWrap_->pAssocAddr == pFileObject &&
pTdiEventHandlerWrap_->dwEventType == dwEventType ) //如果此本机套接字对象的相应事件回调函数已经存在,则直接对其进行修改就可以了,而不是不断的新建事件件回调钩子
{
pTdiEventHandlerWrap_->pOrgEventHandler = pEventHandler;
pTdiEventHandlerWrap_->pOrgEventContext = pEventContext;
break;
}
}
if( NULL == pTdiEventHandlerWrap_ ) //没有找到,加入新的事件回调函数钩子
{
pTdiEventHandlerWrap = ( PTDI_EVENT_HANDLER_WRAP )ExAllocatePoolWithTag( NonPagedPool, sizeof( TDI_EVENT_HANDLER_WRAP ), 0 );
if( NULL == pTdiEventHandlerWrap )
{
goto RELEASE_RESOURCE;
}
pTdiEventHandlerList = ( PTDI_EVENT_HANDLER_LIST )ExAllocatePoolWithTag( NonPagedPool, sizeof( TDI_EVENT_HANDLER_LIST ), 0 );
if( NULL == pTdiEventHandlerList )
{
goto RELEASE_RESOURCE;
}
pTdiEventHandlerWrap->dwEventContextMark = TDI_EVENT_CONTEXT_MARK;
pTdiEventHandlerWrap->dwEventType = dwEventType;
pTdiEventHandlerWrap->pOrgEventHandler = pEventHandler;
pTdiEventHandlerWrap->pOrgEventContext = pEventContext;
pTdiEventHandlerWrap->pAssocAddr = pFileObject;
pTdiEventHandlerWrap->pDeviceObject = pTdiDeviceExtension->pTdiDeviceObject;
pTdiEventHandlerList->pTdiEventHandlerWrap = pTdiEventHandlerWrap;
InsertTailList( &g_TdiEventHandlerInfoList, pTdiEventHandlerList );
}
else
{
pTdiEventHandlerWrap = pTdiEventHandlerWrap_;
pTdiEventHandlerList = pTdiEventHandlerList_;
}
KeReleaseSpinLock( &g_SpLockTdiEventHandlerInfo, OldIrql ); //释放事件回调钩子列表锁
if( TDI_EVENT_RECEIVE == pTdiSetEvent->EventType ||
TDI_EVENT_RECEIVE_EXPEDITED == pTdiSetEvent->EventType )
{
pTdiSetEvent->EventHandler = TdiFilterRecvEventHandler; //加入自己的事件过滤回调函数
}
else if( TDI_EVENT_CHAINED_RECEIVE == pTdiSetEvent->EventType ||
TDI_EVENT_CHAINED_RECEIVE_EXPEDITED == pTdiSetEvent->EventType )
{
pTdiSetEvent->EventHandler = TdiFilterChainedRecvHandler;
}
else
{
pTdiSetEvent->EventHandler = TdiFilterRecvDatagramEventHandler;
}
pTdiSetEvent->EventContext = pTdiEventHandlerWrap;
IoSkipCurrentIrpStackLocation( pIrp );
ntStatus = IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );
if( !NT_SUCCESS( ntStatus ) )
{
if( NULL == pTdiEventHandlerWrap_ )
{
//如果是新加入的事件回调函数钩子,可以在出错时将其释放, 也可以保留至套接字关闭时,再进行释放
KeAcquireSpinLock( &g_SpLockTdiEventHandlerInfo, &OldIrql ); //对事件回调函数钩子列表写操作加锁保护
RemoveEntryList( ( PLIST_ENTRY )pTdiEventHandlerList );
ExFreePoolWithTag( pTdiEventHandlerWrap );
ExFreePoolWithTag( pTdiEventHandlerList );
KeReleaseSpinLock( &g_SpLockTdiEventHandlerInfo, OldIrql ); //释放事件回调钩子列表锁
}
}
return ntStatus;
}
break;
default:
goto CALL_PDO_DRIVER;
break;
}
RELEASE_RESOURCE:
if( NULL != pTdiEventHandlerWrap )
{
ExFreePoolWithTag( pTdiEventHandlerWrap, NonPagedPool );
}
if( NULL != pTdiEventHandlerList )
{
ExFreePoolWithTag( pTdiEventHandlerList, NonPagedPool );
}
CALL_PDO_DRIVER:
IoSkipCurrentIrpStackLocation( pIrp );
return IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );
}
以上对事件回调函数加入了钩子,下一步,必须考虑对其释放的问题,否则,当原始回调函数对应的套接字释放后,你的系统将会崩溃,以下为具体代码:
在套接字关闭后,要在IRP_MJ_CLEANUP功能函数中将相关的事件回调钩子释放掉:
NTSTATUS TdiFilterCleanUp(PDEVICE_OBJECT DeviceObject, PIRP pIrp )
{
NTSTATUS ntStatus;
KIRQL OldIrql;
PTDI_EVENT_HANDLER_LIST pTdiEventHandlerList;
PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap;
PFILE_OBJECT pFileObject;
TDI_FILTER_DEVICE_EXTENSION *pDeviceExtension;
PIO_STACK_LOCATION pIrpSp;
pDeviceExtension = ( TDI_FILTER_DEVICE_EXTENSION* )DeviceObject->DeviceExtension;
pIrpSp = IoGetCurrentIrpStackLocation( pIrp );
pFileObject = pIrpSp->FileObject;
...
//如果是主控制设备,要将调用IoCompleteIrp完成Irp, 如果是过滤设备,调用PDO设备驱动
IoSkipCurrentIrpStackLocation( pIrp );
ntStatus = IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );
if( !NT_SUCCESS( ntStatus ) )
{
DebugPrintEx( CLEANUP_INFO,"netmon TdiFilterCleanUp IoCallDriver return ERROR/n" );
return ntStatus;
}
//下一步,释放套接字对应的事件回调钩子
KeAcquireSpinLock( &g_SpLockTdiEventHandlerInfo, &OldIrql );
FIND_LIST_AGAIN:
pListEntry = g_TdiEventHandlerInfoList.Flink;
for( ; ; )
{
if( pListEntry == &g_TdiEventHandlerInfoList )
{
break;
}
pTdiEventHandlerList = ( PTDI_EVENT_HANDLER_LIST )pListEntry;
pTdiEventHandlerWrap = pTdiEventHandlerList->pTdiEventHandlerWrap;
if( pTdiEventHandlerWrap->pAssocAddr == pFileObject )
{
RemoveEntryList( pListEntry );
ExFreePoolWithTag( pTdiEventHandlerWrap, 0 );
ExFreePoolWithTag( pTdiEventHandlerList, 0 );
goto FIND_LIST_AGAIN;
}
pListEntry = pListEntry->Flink;
}
KeReleaseSpinLock( &g_SpLockTdiEventHandlerInfo, OldIrql );
return ntStatus;
}
那么,可以在事件回调过滤钩子函数对数据进行处理了
NTSTATUS TdiFilterRecvEventHandler( IN PVOID TdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN ULONG ReceiveFlags,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *BytesTaken,
IN PVOID Tsdu,
OUT PIRP *IoRequestPacket
)
{
NTSTATUS ntStatus;
PIO_STACK_LOCATION pIrpSp;
PTDI_EVENT_HANDLER_WRAP pEventHandlerWrap;
PTDI_COMPLETION_WRAP pCompletionWrap;
LARGE_INTEGER RecvedDataSize;
pEventHandlerWrap = ( PTDI_EVENT_HANDLER_WRAP )TdiEventContext;
if( FALSE == g_bFiltering ) //是否进行过滤
{
goto CALL_ORIGINAL_EVENT_HANDLER;
}
if( FALSE != bStopRecv )
{
ntStatus = STATUS_DATA_NOT_ACCEPTED;
goto RELEASE_PROCESS_IO_INFO_RETURN;
}
ntStatus = ( ( ClientEventReceive )pEventHandlerWrap->pOrgEventHandler )(
pEventHandlerWrap->pOrgEventContext,
ConnectionContext,
ReceiveFlags,
BytesIndicated,
BytesAvailable,
BytesTaken,
Tsdu,
IoRequestPacket
);
if( NULL != BytesTaken &&
0 != *BytesTaken )
{
//这里对数据进行处理, 比如可以进行通信数据量的统计
}
if( STATUS_MORE_PROCESSING_REQUIRED != ntStatus )
{
goto RELEASE_PROCESS_IO_INFO_RETURN;
}
if( NULL == *IoRequestPacket )
{
goto RELEASE_PROCESS_IO_INFO_RETURN;
}
//IoRequestPacket表示当前接收IRP中的数据如果并不完整, 并且认为接下来的数据是有价值,需要接收的话,那么需要自己新建一个IRP包,将其指针传入此参数中,并返回STATUS_MORE_PROCESSING_REQUIRED,通知IO管理不终止此IRP,TDI驱动将继续接收接下来的数据。
//所以如果此IRP包存在,可以截取它的信息,具体方法下一步讲述。
return ntStatus;
CALL_ORIGINAL_EVENT_HANDLER:
return ( ( ClientEventReceive )pEventHandlerWrap->pOrgEventHandler )( //直接调用原始的IRP钩子函数,不进行处理
pEventHandlerWrap->pOrgEventContext,
ConnectionContext,
ReceiveFlags,
BytesIndicated,
BytesAvailable,
BytesTaken,
Tsdu,
IoRequestPacket
);
}
上面讲述了使用事件回调函数钩子的方式进行通信数据的截取方法,下面讲述直接IRP包数据传输方式,也就是以下4个子功能的截取方法:
TDI_SEND
TDI_RECEIVE
TDI_SEND_DATAGRAM
TDI_RECEIVE_DATAGRAM
在IRP_MJ_INTERNAL_DEVICE_CONTROL函数中响应以上子功能时,确认参数DeviceObject为TDI过滤设备,对所有截取到的IRP加入自己的完成函数,在此IRP被完成时( 调用IoCompleteRequest )此完成函数被调用,取得IRP处理的返回结果,进行处理。具体数据的处理对应于TDI_SEND子功能可以在IoCallDriver之前得到,因为它是应用程序传给你的,而TDI_RECEIVE子功能,应该在TDI事件回调函数或Completion回调函数中取得。
相关代码如下:
typedef struct __TDI_COMPLETION_WRAP
{
...//可以加入用来记录/处理数据的成员, 比如通信标志, 流量统计等
PIO_COMPLETION_ROUTINE pCompletionRoutine;
LPVOID pContext;
} TDI_COMPLETION_WRAP, *PTDI_COMPLETION_WRAP;
加入自定义的CompletionRoutine的方法:
if( TDI_SEND == MinorFunction ||
TDI_SEND_DATAGRAM == MinorFunction ||
TDI_RECEIVE == MinorFunction ||
TDI_RECEIVE_DATAGRAM == MinorFunction )
{
if( TDI_RECEIVE == MinorFunction &&
TDI_RECEIVE_PEEK == ( ULONG )pIrpSp->Parameters.Others.Argument2 )
{
//TDI_RECEIVE_PEEK不会真正接收数据,可以不需要对其进行过滤。
goto SKIP_CURRENT_STACK_LOCATION;
}
pCompletionWrap = ( PTDI_COMPLETION_WRAP )ExAllocateFromNPagedLookasideList( &g_CompletionWrapList ); //可以使用链表或HASH等数据结构来管理所有的CompletionRoutine包装信息,这里使用了NPAGED_LOOKASIDE_LIST,它的优势在于系统中所有的NPAGED_LOOKASIDE_LIST资源的最大占用量将会被内存管理器动态管理
if( NULL == pCompletionWrap )
{
goto SKIP_CURRENT_STACK_LOCATION;
}
//这里可以设置CompletionRoutine的具体工作参数,比如具体操作的类型,原始的Completion函数等,在用户层传送至的IRP中是不会设置CompletionRoutine函数的,但其它驱动传送至的IRP中可能会进行设置,如在Receive事件回调函数中的IoRequestPacket参数
IoCopyCurrentIrpStackLocationToNext( pIrp ); //设置下一个设备栈工作参数
IoSetCompletionRoutine( pIrp,
TdiFilterCompletion,
pCompletionWrap,
TRUE,
TRUE,
TRUE
);//这里就为这个IRP加入自己的CompletionRoutine函数
goto CALL_PDO_DRIVER;
SKIP_CURRENT_STACK_LOCATION:
IoSkipCurrentIrpStackLocation( pIrp );
CALL_PDO_DRIVER:
return IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );
}
具体的Completion函数的工作:
NTSTATUS TdiFilterCompletion( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, LPVOID pContext )
{
NTSTATUS ntStatus;
PTDI_COMPLETION_WRAP pCompletionWrap;
LARGE_INTEGER TransferredDataSize;
PIRP pMasterIrp;
PIO_STACK_LOCATION pIrpSp;
ntStatus = pIrp->IoStatus.Status;
pCompletionWrap = ( PTDI_COMPLETION_WRAP )pContext;
if( NT_SUCCESS( ntStatus ) )
{
//可以在这里对成功传输的数据进行处理
}
//这里可以调用原始的Completion函数
RETURN_SUCCESS:
return ntStatus;
}
需要注意的是,如果为IRP包加入了CompletionRoutine之后,那么在驱动卸载( Unload )之前,必须保证所有IRP已经执行过此Completion函数, 如果在驱动被从内存中卸载后才执行, 将会使系统崩溃。
处理方法为:
1.不实现DriverUnload函数,使驱动只有在系统关闭,底层设备被卸载时,才能完成真正的卸载。这是的一般FILTER驱动的工作方式,
2.使用线程同步的方法保证Completion函数的执行,Windows XP或之后的系统也提供了一个API, SetCompletionRoutineEx来保证驱动在Completion函数完成前不被卸载。
至此,讲述TDI过滤驱动组织框架,可以为它添加一些更加完善的功能。
相关推荐
总之,TDI过滤驱动开发是网络驱动开发领域的重要一环,它要求开发者具备扎实的系统编程基础,熟悉内核模式下的编程规则,并且对网络协议和系统性能优化有一定了解。通过深入研究TDI,开发者可以构建高效、可靠的网络...
目 录 (0) TDI概要 (1) 准备工作 (2) TDI设备与驱动入手 (3) 绑定设备 (4) 简单的处理请求 (5) 基础过滤框架 (6) 主要过滤的请求类型 (7) CREATE的过滤 (8) 准备解析ip地址与端口 (9...
一个简单的tdi过滤驱动例子,服务器、客户端
两篇非常不错的TDI开发教程,想做这方面开发的朋友值得一看 !
2. **TDI过滤驱动的结构**:描述如何构建一个TDI过滤驱动,包括驱动的结构、接口函数和事件处理。 3. **网络I/O拦截**:讲解如何利用TDI驱动拦截网络数据传输,包括TCP/IP和其他网络协议。 4. **文件操作与网络...
开源个人防火墙源码 学习驱动和防火墙的不错资料
描述中的“WDK下驱动和应用层序通信,Windows_TDI过滤驱动开发”进一步指出了这个示例是基于Windows Driver Kit (WDK)的,WDK是开发、调试和部署驱动程序的工具集。TDI过滤驱动则是TDI架构的一部分,它允许开发者插入...
- TDI过滤驱动是一种特殊的驱动程序,它位于TDI提供者(通常是协议驱动)和应用程序之间,可以监控、修改或拒绝通过TDI接口进行的数据流。 2. **TDI过滤驱动开发** - 开发者需要了解如何使用Windows驱动程序开发...
某开源防火墙源代码 TDI网络过滤驱动开发
TDI驱动编程实例,完整的PCAUSA样本,包括各种内核模式TDI代码,TDI过滤器_重定向和NDIS filters.完成PCAUSA样本,包括variou TDI的内核模式代码。 TDI过滤器_重定向和NDIS过滤器。.rar
在本文中,我们将深入探讨TDI(Transport Driver Interface)网络过滤驱动及其在网络流量管理中的应用。 TDI是Microsoft Windows操作系统中的一种低级网络接口,允许应用程序和驱动程序与网络传输层进行交互。它为...
3. **TDI过滤驱动(TDI Filter Drivers)**: 这类驱动位于TDI客户驱动和TDI传输驱动之间,主要用于拦截两者之间的通信。在Windows平台上,大多数防火墙都是通过实现TDI过滤驱动来实现其功能的。 #### 三、TDI驱动的...
`Windows_TDI过滤驱动开发.pdf`可能介绍了如何编写TDI过滤驱动,这涉及网络层数据包的处理,提供网络协议的透明过滤和修改。 5. **Windows设备驱动程序WDF(Windows Driver Frameworks)开发源码**: `Windows设备...
Windows_TDI(Transport Driver Interface)过滤驱动开发.pdf,TDI是Windows早期的网络接口,虽然现在已被WDI(Windows Driver Interface)取代,但该文档可能讲解了如何编写TDI过滤驱动,用于网络数据包的捕获和...
主要知识重点包括:Windows串口与键盘过滤驱动、 Windows虚拟存储设备与存储设备过滤驱动、Windows文件系统过滤驱动、文件系统透明加密/解密驱动、Windows各类网络驱动(包括TDI 过滤驱动及3类NDIS驱动),以及最新...
主要知识重点包括:Windows串口与键盘过滤驱动、Windows虚拟存储设备与存储设备过滤驱动、Windows文件系统过滤驱动、文件系统透明加密/解密驱动、Windows各类网络驱动(包括TDI过滤驱动及3类NDIS驱动),以及最新的...
主要知识重点包括:windows串口与键盘过滤驱动、windows虚拟存储设备与存储设备过滤驱动、windows文件系统过滤驱动、文件系统透明加密/解密驱动、windows各类网络驱动(包括tdi过滤驱动及3类ndis驱动),以及最新的...
主要知识重点包括:windows串口与键盘过滤驱动、windows虚拟存储设备与存储设备过滤驱动、windows文件系统过滤驱动、文件系统透明加密/解密驱动、windows各类网络驱动(包括tdi过滤驱动及3类ndis驱动),以及最新的...
主要知识重点包括:windows串口与键盘过滤驱动、windows虚拟存储设备与存储设备过滤驱动、windows文件系统过滤驱动、文件系统透明加密/解密驱动、windows各类网络驱动(包括tdi过滤驱动及3类ndis驱动),以及最新的...
windows文件系统过滤驱动、文件系统透明加密/解密驱动、windows各类网络驱动(包括tdi过滤驱动及3类ndis驱动) ,以及最新的wdf驱动开发模型。有助于读者熟悉windows内核驱动的体系结构,并精通信息安全类的内核...