TDI网络过滤驱动应用(一)

TDI网络过滤驱动应用

在前面的文章中,我们分析了TDI网络过滤驱动的基本开发框架以及TDI网络过滤驱动框架下面防火墙实例tdifw的实现,可以参考:

  1. TDI网络过滤驱动开发指南
  2. TDI网络过滤驱动之tdifw实现原理分析

对于TDI网络过滤驱动下面的防火墙应用,是TDI驱动最简单的一种应用场景,我们只需要对TDI_CONNECTtdi_event_connecttdi_send_datagramtdi_event_receive_datagram消息/回调进行防火墙规则处理即可。

下面我们分析一下TDI网络过滤驱动的其他应用,包括:

  1. 进程流量限定。
  2. DNS重定向代理。
  3. TCP重定向代理。

这些例子相比TDIFW防火墙将会复杂很多,例如最简单的流量限定。我们需要对发送和接收数据进行限流操作的话,需要对发送的IRP以及接收的数据进行缓存和挂起操作,当低于流量限定的时候再激活操作。整个过程涉及到数据接收的TDI请求重注入,相比防火墙的拦截来说要复杂很多。

1. 技术概览

我们先来看一下整个TDI网络驱动应用的框架,如下:
在这里插入图片描述

在上述框架中:

  1. 应用程序发起的网络请求或者底层来的网络数据包都先放入IoQueue队列中,然后将整个数据置于PENDING状态。
  2. 将数据包通过RequestQueue队列来进行其他处置,例如可以如下:
    1. 将数据包通过IRP请求传递给用户层,让用户层计算流量或者进行UDP转发(TCP连接转发)等。
    2. 可以在RequestQueue延迟完成数据包的传递,达到流量限定的目的。
    3. 在用户的进行数据包的DUMP(类似TCPDUMP工具进行网络抓包)。

2. 数据包的抓取

通过上面我们得知,TDI驱动有一个重要的操作,就是需要对网络数据包进行抓取,将数据包抓取到用户层,提供给用户层做各种分析。对于TCP数据我们有如下消息可以采集:

  1. IRP_MJ_CREATE:创建地址对象和连接对象。
  2. TDI_SEND:采集TCP/UDP数据被发送的事件,我们可以从IRP中提取发送的数据,用来采集本机发出去的数据。
  3. TDI_RECEIVE:采集本机主动接收数据的事件。
  4. TDI_CONNECT:TCP的连接请求,我们可以替换连接请求的数据,达到TCP代理的功能。
  5. TDI_EVENT_CONNECT:被动接收连接的事件,改事件只能用作通知功能。
  6. TDI_EVENT_RECEIVE/TDI_EVENT_RECEIVE_EXPEDITED:接收数据包的事件,抓取该事件我们可以采集本机接收的数据包信息。

对于UDP数据我们有如下事件可以采集:

  1. IRP_MJ_CREATE:创建地址对象。
  2. TDI_SEND/TDI_SEND_DATAGRAM:采集UDP数据被发送的事件,我们可以从IRP中提取发送的数据,用来采集本机发出去的数据。
  3. TDI_RECEIVE_DATAGRAM:采集本机主动接收数据的事件。
  4. TDI_EVENT_RECEIVE_DATAGRAM:接收UDP数据包的事件,抓取该事件我们可以采集本机接收的数据包信息。

对于上述所有的事件,可以从用户层的处理来分为两类:

  1. 通知类。通知类事件是通知用户层有什么事件发生,例如当UDP接收到IRP_MJ_CREATE消息的时候,可以得到UDP_CREATED被创建的消息。
  2. 决策类。决策类是需要用户层对该消息进行响应的事件,例如TCP_CONNECTED响应,需要我们对建立的连接请求进行决策(例如TCP的连接代理)。

通知类主要是通知用户层当前TDI的各种状态和情况;而决策类的事件是整个事件采集的核心,我们可以对数据进行加密或者解密,对请求进行代理转发,或者对数据进行审计。

3. 应用实例

下面我们来看一下如下TDI实例的应用,包括:

  1. 进程流量限定。
  2. DNS重定向代理。
  3. TCP重定向代理。

3.1 TrafficShaper(限流)

流量限定主要实现的原理是对send, recv, sendto, recvfrom等数据长度进行统计,在单位时间内数据量的大小如果超过限流大小就暂停数据的发送和接收,实现大致如下:

virtual void tcpReceive(ENDPOINT_ID id, const char * buf, int len)
{
    bytesIn += len;
    if (bytesIn > ioLimit)
    {
        suspendLimit(...);
    }
}

virtual void tcpSend(ENDPOINT_ID id, const char * buf, int len)
{
    bytesOut += len;
    if (bytesOut > ioLimit)
    {
        suspendLimit(...);
    }
}

virtual void udpReceive(ENDPOINT_ID id, const char * buf, int len)
{
    bytesIn += len;
    if (bytesIn > ioLimit)
    {
        suspendLimit(...);
    }
}

virtual void udpSend(ENDPOINT_ID id, const char * buf, int len)
{
    bytesOut += len;
    if (bytesOut > ioLimit)
    {
        suspendLimit(...);
    }
}

除了在数据接收或者发送的回调函数中统计流量信息之后,我们需要额外创建一个线程,该线程统计单位事件内流量的状态:

  1. 如果单位事件内流量超出限流,那么暂停数据的收发。
  2. 如果单位事件内流量低于限流,那么恢复数据的收发。

那么suspendLimit怎么暂停数据的收发呢?方法就是针对TDI_SEND这种发送请求,将其IRP挂起,不再进行发送处理;等到流量恢复的时候再次进行发送。

3.2 DnsRedirector(DNS重定向)

我们知道,如果使用gethostbyname函数我们可以获取主机名对应的IP地址信息,该函数如下:

hostent * gethostbyname(
  const char *name
);

例如我们浏览器访问网站的时候,就会对网站地址进行域名解析(也就是通过gethostbyname获取域名对应的IP地址)。

其实域名解析是利用DNS协议,向域名服务器发送DNS请求来查询主机名对应的主机地址的;该协议格式是固定的,并且有固定的端口,这个端口就是53,并且使用UDP协议进行发送。

因此如果我们可以对53端口的UDP数据包进行拦截加转发,那么我们就可以使用DNS查询的重定向了。

因此我们只需要响应回调函数udpSend,对53端口的数据进行转发处理,如下:

virtual void udpSend(ENDPOINT_ID id, const unsigned char * remoteAddress, const char * buf, int len)
{
    //...
}

我们对udpSend回调中的数据,可以使用socketsendto以及recvfrom发送DNS请求,并获取DNS请求的返回数据。

在这里有一个主要的操作就是recvfrom数据注入;我们在代理线程中将原始DNS数据包代理之后,然后通过recvfrom接收DNS请求的返回数据后,需要将数据重新注入给TDI驱动,让TDI驱动返回给原始进程,从而达到DNS代理的过程。

recvfrom数据注入就是将数据发送给TDI驱动之后,TDI驱动通过TDI_RECEIVE_DATAGRAM或者TDI_EVENT_RECEIVE_DATAGRAM返回数据。

3.3 TcpRedirector(TCP重定向)

其实DnsRedirector就是UDP重定向的一个例子;而TcpRedirector是TCP重定向的一个例子。TCP重定向其实应用非常广泛,例如很多VPN代理的TCP服务(TCP流量)就是通过TCP重定向来实现的。

不过本人看过一些大厂的VPN实现TCP代理用的还是LSP(Layered service provider)来做的,本文来讨论如何通过TDI驱动来实现TCP代理。

TCP代理主要是对connect进行处理,将连接的地址换成代理进程监控的地址,如下:

virtual void tcpConnectRequest(ENDPOINT_ID id, PTCP_CONN_INFO pConnInfo)
{
    //...
    memcpy(pConnInfo->remoteAddress, redirectToAddress, sizeof(pConnInfo->remoteAddress));
    //...
}

从这里我们可以看到,TCP的代理实现起来其实比较简单的,该功能最复杂的一个点是代理服务器的实现,一般要实现比较高效我们应当使用完成端口来对套接字进行监控。这里我们并不对TCP代理服务器做详细介绍。

4. 总结与参考

这里我们从三个实例(限流,UDP重定向,TCP重定向)大致了解TDI网络过滤驱动的基本应用场景;对于驱动防火墙的TDI网络过滤驱动复杂应用,我们需要将网络数据包进行截获以及重注入处理。

对于TDI网络过滤驱动更加全面的使用,我们可以参考NetFilter SDK 2,该SDK链接为https://netfiltersdk.com/,可惜的是该SDK是一个收费的SDK,不过它提供了相关的Sample代码,包括TrafficShaper,TcpRedirector,DnsRedirector和SocksProxyServer还是非常具有参考性的。

  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于TDI 的 TCP数据传输 1.上位机 上位机包括tcp和tcp.cpp 1.1 对外函数说明 HANDLE TdiTcpOpen(); TdiTcpOpen用于打开设备,成功返回有效的句柄,失败返回INVALID_HANDLE_VALUE. BOOL TdiTcpClose(HANDLE hDevice); TdiTcpClose用于关闭设备,成功返回TRUE,失败返回FALSE; hDevice为TdiTcpOpen返回的句柄 BOOL TdiTcpConnect(HANDLE hDevice,const PCHAR pIpAddres,USHORT uPort); TdiTcpConnect用于与服务器建链,pIpAddres为服务器IP地址,uPort为服务器端口地址。 hDevice为TdiTcpOpen返回的句柄 pIpAddres为IP地址,如”10.0.0.20” uPort为端口地址 BOOL TdiTcpSend(HANDLE hDevice,PVOID pBuff,ULONG nLen,PULONG pRtn); TdiTcpSend用于给服务器发送数据. hDevice为TdiTcpOpen返回的句柄 pBuff接向发送数据的指针 nLen发送数据长度 pRtn发送成功长度 BOOL TdiTcpRcv(HANDLE hDevice,PVOID pBuff,ULONG nLen,PULONG pRtn); TdiTcpRcv用于从服务器接收数据 hDevice为TdiTcpOpen返回的句柄 pBuff接收数据缓冲区 nLen接收数据缓冲区长度 pRtn实际接收数据长度指针 BOOL TdiTcpSetRcvTimeOut(HANDLE hDevice,ULONG ulSecond); TdiTcpSetRcvTimeOut用于设置接收数据超时时间,默认为3秒。 hDevice为TdiTcpOpen返回的句柄 ulSecond为超时时间 2.下位机 下位机包括D1603.h D1603.cpp和Tdifun.cpp TdiFun.h 2.1 关键数据结构 驱动应用连接服务器结构体 typedef struct _CONNECT_STRUCT { ULONG ip; //服务器IP地址 USHORT port; //服务器端口 }CONNECT_STRUCT,*PCONNECT_STRUCT; //设备展结构体 typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT pDeviceObject; //设备指针 UNICODE_STRING wstrDeviceName ; //设备名 UNICODE_STRING wstrSymbolicLinkName;//设备链接名 }DEVICE_EXTENSION,*PDEVICE_EXTENSION; 读数据链接 typedef struct _RCV_IPR_LIST { PIRP pIrp; //指向读IPR LIST_ENTRY ListEntry; //链表 }RCV_IPR_LIST,*PRCV_IPR_LIST; 当前链接上下文 typedef struct _SOCKET_CONTEXT { HANDLE TransportAddressHandle; //传输地址句柄 FILE_OBJECT* pTrasnportAddressFile;//传输地址指针 HANDLE ConnectionHandle;//连接地址句柄 FILE_OBJECT* pConnectionFile;//连接地址指针 LIST_ENTRY RcvHead; //接收IRP链表头 KEVENT event; //接收数据同步事件 ULONG uTimeOut; // 接收数据超时 }SOCKET_CONTEXT,*PSOCKET_CONTEXT; 2.2 外函数说明 驱动装载主入口函数 NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath); 驱动卸载函数 VOID D1603Unload(PDRIVER_OBJECT DriverObject); 默认IRP回调函数 NTSTATUS D1603Dispatch(PDEVICE_OBJECT DeviceObject,PIRP Irp); TdiTcpOpen对应的IPR函数 NTSTATUS D1603Create(PDEVICE_OBJECT
TDI和WFP是Windows操作系统中的两种不同的驱动技术。TDI是Transport Driver Interface的缩写,它在Windows 2000到Windows Vista期间被支持。TDI是一套接口的集合,用于连接用户态的socket和NDIS协议驱动,实现socket的创建、发送和接收数据。\[1\] WFP是Windows Filtering Platform的缩写,它是取代TDI的新技术。WFP是一种网络过滤平台,用于在网络数据包传输过程中进行过滤和处理。WFP提供了一种灵活的方式来管理和控制网络流量,可以实现防火墙、入侵检测和网络安全等功能。与TDI相比,WFP提供了更高级的网络过滤和处理功能,并且支持更多的Windows操作系统版本。\[1\] 总结来说,TDI是一种用于连接用户态的socket和NDIS协议驱动的接口集合,而WFP是一种网络过滤平台,用于在网络数据包传输过程中进行过滤和处理。WFP相比于TDI提供了更高级的网络过滤和处理功能,并且支持更多的Windows操作系统版本。 #### 引用[.reference_title] - *1* *2* *3* [Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows ...](https://blog.csdn.net/zhangge3663/article/details/100918732)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值