searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

WinDivert介绍及snat实例

2024-07-05 09:55:59
403
0

# 简介

WinDivert是一种用户模式的数据包捕获和转移包,可用于windows vista、windows 2008、windows 7、windows 8 和 windows 10。WinDivert允许用户模式应用程序捕获、修改、丢弃从windows网络堆栈发送的网络数据包,而无需编写内核模式代码。

主要有以下功能:
1. 捕获网络数据包
2. 筛选/丢弃网络数据包
3. 嗅探网络数据包
4. 注入网络数据包
5. 修改网络数据包  
    主要特点:可抓包,修改包
**注意:需要管理员权限!!!**


原理图如上,WinDivert.sys 驱动安装在Windows network stack 下,其具体步骤为:

- 一个新网络包进入到network stack 之后会被WinDivert.sys 截获;
- 如果截获的包被 PROGRAM-defined 过滤器匹配成功,该包将会被重定向,PROGRAM 就会调用 WinDivertRecv() 方法来读取包;如果匹配失败,截获的包将不会被重定向;
- PROGRAM 将对包进行 drops,modify, 或者 re-inject 操作。

# 基本api
## WinDivertOpen 打开
打开一个windivert对象,返回一个对象指针。打开的过程中,需要指定过滤规则,过滤层,过滤器的优先级,以及windivert对象的工作模式。

```c++
WINDIVERTEXPORT HANDLE WinDivertOpen(
    __in        const char *filter,
    __in        WINDIVERT_LAYER layer,
    __in        INT16 priority,
    __in        UINT64 flags);
```
filter:过滤条件,类似于tcpdump的抓包条件
layer:过滤层,一般network和forward,network类似于input和output的链(本机收和本机发),forward是收发本机转发包
```c++
typedef enum
{
    WINDIVERT_LAYER_NETWORK = 0,        /* Network layer. */
    WINDIVERT_LAYER_NETWORK_FORWARD = 1,/* Network layer (forwarded packets) */
    WINDIVERT_LAYER_FLOW = 2,           /* Flow layer. */
    WINDIVERT_LAYER_SOCKET = 3,         /* Socket layer. */
    WINDIVERT_LAYER_REFLECT = 4,        /* Reflect layer. */
} WINDIVERT_LAYER, *PWINDIVERT_LAYER;
```
priority:优先级,值越大,优先级越高
flags:windivert对象到底是用于监听、丢包、还是修改包等模式,可选WINDIVERT_FLAG_SNIFF表示仅仅收包不修改原包,原包还是正常发送
```c++
#define WINDIVERT_FLAG_SNIFF            0x0001
#define WINDIVERT_FLAG_DROP             0x0002
#define WINDIVERT_FLAG_RECV_ONLY        0x0004
#define WINDIVERT_FLAG_READ_ONLY        WINDIVERT_FLAG_RECV_ONLY
#define WINDIVERT_FLAG_SEND_ONLY        0x0008
#define WINDIVERT_FLAG_WRITE_ONLY       WINDIVERT_FLAG_SEND_ONLY
#define WINDIVERT_FLAG_NO_INSTALL       0x0010
#define WINDIVERT_FLAG_FRAGMENTS        0x0020
```
打开这个windivert对象 后就可以进行收包了

## WinDivertRecv 接收原包

```c++
WINDIVERTEXPORT BOOL WinDivertRecv(
    __in        HANDLE handle,
    __out_opt   VOID *pPacket,
    __in        UINT packetLen,
    __out_opt   UINT *pRecvLen,
    __out_opt   WINDIVERT_ADDRESS *pAddr);
```
接收特定WinDivert对象的捕获的包的函数, 这里符合过滤条件的包都会依次被此接口收上来处理

## WinDivertHelperParsePacket 解析原包信息

```c++
WINDIVERTEXPORT BOOL WinDivertHelperParsePacket(
    __in        const VOID *pPacket,
    __in        UINT packetLen,
    __out_opt   PWINDIVERT_IPHDR *ppIpHdr,
    __out_opt   PWINDIVERT_IPV6HDR *ppIpv6Hdr,
    __out_opt   UINT8 *pProtocol,
    __out_opt   PWINDIVERT_ICMPHDR *ppIcmpHdr,
    __out_opt   PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr,
    __out_opt   PWINDIVERT_TCPHDR *ppTcpHdr,
    __out_opt   PWINDIVERT_UDPHDR *ppUdpHdr,
    __out_opt   PVOID *ppData,
    __out_opt   UINT *pDataLen,
    __out_opt   PVOID *ppNext,
    __out_opt   UINT *pNextLen);
```
解析的形成的数据包如下格式
```
typedef struct
{
    UINT32 HeaderLength:17;
    UINT32 FragOff:13;
    UINT32 Fragment:1;
    UINT32 MF:1;
    UINT32 PayloadLength:16;
    UINT32 Protocol:8;
    UINT32 Truncated:1;
    UINT32 Extended:1;
    UINT32 Reserved1:6;
    PWINDIVERT_IPHDR IPHeader;
    PWINDIVERT_IPV6HDR IPv6Header;
    PWINDIVERT_ICMPHDR ICMPHeader;
    PWINDIVERT_ICMPV6HDR ICMPv6Header;
    PWINDIVERT_TCPHDR TCPHeader;
    PWINDIVERT_UDPHDR UDPHeader;
    UINT8 *Payload;
} WINDIVERT_PACKET, *PWINDIVERT_PACKET;
```
解析完后就可以进行修改了,比如修改5元祖

## WinDivertHelperCalcChecksums 
修改后的包重新进行校验和计算

```c++
WINDIVERTEXPORT BOOL WinDivertHelperCalcChecksums(
    __inout     VOID *pPacket, 
    __in        UINT packetLen,
    __out_opt   WINDIVERT_ADDRESS *pAddr,
    __in        UINT64 flags);
```

## WinDivertSend 发送原包

```c++
WINDIVERTEXPORT BOOL WinDivertSend(
    __in        HANDLE handle,
    __in        const VOID *pPacket,
    __in        UINT packetLen,
    __out_opt   UINT *pSendLen,
    __in        const WINDIVERT_ADDRESS *pAddr);
```
校验和重新计算后的包就会重新发送

windivert源码也有一些实例,都是c写的,这里不再过多介绍

# snat实现
PyDivert基于python 的WinDivert实现,我们以python这种简单语言快速验证,实例类似于iptables中的snat功能。

我们实现的场景如下:  A和B设备通过wan或者隧道(wifi也行)连接,A设备的lan口连接内网服务器和打印机等,由于打印机无法配置路由(与设备A的lan同网段),因此设备B想通打印机需要设备A做snat. 这样回包才能达到A后再打到B


这里我们需要把ipv4的主要协议包icmp、tcp、udp代理,需要snat的网络接口设备A的lan口,假设ifIdx为12, 在设备A上首先需要抓取ifIdx口forward出去的数据包,其次在修改了src_ip后,由于打印机回包是dst_ip会是设备A的lan接口ip,要捕获这个包,还需要抓取network上进来的包,在进来的数据包上做还原,这样dst_ip被还原后才能从A回到B
过滤条件为
```python
outgoing_filter = f"ip and (tcp or udp or icmp) and ifIdx == 12"   layer = forward
incoming_filter = f"ip and (tcp or udp or icmp) and ifIdx == 12"   layer = network
NEW_SRC_IP = '192.168.1.1'   #ifIdx=12的接口ip
```


其次,由于需要还原包,我们要根据连接信息进行匹配还原,类似于linux连接跟踪conntrack, 由于线程共用connections,还需要锁机制保护
```python
connections = [key:['proto', 'orig_src_ip', 'orig_src_port', 'orig_dst_ip', 'orig_dst_port', 'nat_src_ip']....]
connection_lock = threading.Lock()
```
这里连接跟踪为一个dict,key为连接跟踪信息,后续回包需要根据此key找到对应的value, 还原出ip,注意有与icmp和tcp/udp的协议字段不一致,需要做区分, tcp及udp用五元组做key,  icmp用ip及icmp_id做key

这里我们起2个线程分别抓forward和network的包
```python
thread_forward = threading.Thread(target=handle_forward_layer)
thread_network = threading.Thread(target=handle_network_layer)
```

处理forward和network的接口如下
```python
def handle_forward_layer():
    with pydivert.WinDivert(outgoing_filter,1,0,0) as w_forward:
        while True:
            packet = w_forward.recv()
            if packet.direction == pydivert.Direction.OUTBOUND:
                ip_header = packet.ipv4
                tcp_header = packet.tcp
                udp_header = packet.udp
                icmp_header = packet.icmpv4
                with connection_lock:
                    # 处理外网到内网的包,修改源 IP 为 LAN IP
                    if tcp_header is not None:
                        # 构建正向和反向键
                        #print("origin forward %s packet, TCP %s:%s --> %s:%s" % (a_1,ip_header.src_addr,tcp_header.src_port,ip_header.dst_addr,tcp_header.dst_port))
                        rev_key = ('TCP', ip_header.dst_addr, tcp_header.dst_port, NEW_SRC_IP, tcp_header.src_port)
                        connections[rev_key] = ConnectionInfo('TCP', ip_header.src_addr, tcp_header.src_port, ip_header.dst_addr, tcp_header.dst_port, NEW_SRC_IP, current_time)
                        ip_header.src_addr = NEW_SRC_IP
                        #print("modify forward %s packet, TCP %s:%s --> %s:%s" % (a_1,ip_header.src_addr,tcp_header.src_port,ip_header.dst_addr,tcp_header.dst_port))

                    elif udp_header is not None:
                        #print("origin forward %s packet, UDP %s:%s --> %s:%s" % (a_1,ip_header.src_addr,udp_header.src_port,ip_header.dst_addr,udp_header.dst_port))
                        rev_key = ('UDP', ip_header.dst_addr, udp_header.dst_port, NEW_SRC_IP, udp_header.src_port)
                        connections[rev_key] = ConnectionInfo('UDP', ip_header.src_addr, udp_header.src_port, ip_header.dst_addr, udp_header.dst_port, NEW_SRC_IP, current_time)
                        ip_header.src_addr = NEW_SRC_IP
                        #print("modify forward %s packet, TCP %s:%s --> %s:%s" % (a_1,ip_header.src_addr,udp_header.src_port,ip_header.dst_addr,udp_header.dst_port))

                    elif icmp_header is not None:
                        icmp_id = struct.unpack('!H', icmp_header.payload[0:2])[0]
                        #print("origin forward %s packet, ICMP %s --> %s %s" % (a_1,ip_header.src_addr,ip_header.dst_addr,icmp_id))
                        rev_key = ('ICMP', ip_header.dst_addr, icmp_id, NEW_SRC_IP)
                        connections[rev_key] = ConnectionInfo('ICMP', ip_header.src_addr, 0, ip_header.dst_addr, 0, NEW_SRC_IP, current_time)
                        ip_header.src_addr = NEW_SRC_IP
                        #print("modify forward %s packet, ICMP %s --> %s %s" % (a_1,ip_header.src_addr,ip_header.dst_addr,icmp_id))

  
            packet.recalculate_checksums()
            w_forward.send(packet)
```

```python
 def handle_network_layer():
        with pydivert.WinDivert(incoming_filter,0,0,0) as w_network:
            while True:
                packet = w_network.recv()
                # 从此接口进来的包需要做还原
                if packet.direction == pydivert.Direction.INBOUND:
                    ip_header = packet.ipv4
                    tcp_header = packet.tcp
                    udp_header = packet.udp
                    icmp_header = packet.icmpv4
                    with connection_lock:
                        if tcp_header is not None:
                            #print("origin network %s packet, TCP %s:%s --> %s:%s" % (a_2,ip_header.src_addr,tcp_header.src_port,ip_header.dst_addr,tcp_header.dst_port))
                            key = ('TCP', ip_header.src_addr, tcp_header.src_port, ip_header.dst_addr, tcp_header.dst_port)
                            if key in connections:
                                nat_info = connections[key]
                                ip_header.dst_addr = nat_info.orig_src_ip
                                updated_conn_info = nat_info._replace(last_access_time=current_time)
                                connections[key] = updated_conn_info

                            #print("modify network %s packet, TCP %s:%s --> %s:%s" % (a_2,ip_header.src_addr,tcp_header.src_port,ip_header.dst_addr,tcp_header.dst_port))

                        elif udp_header is not None:
                            #print("origin network %s packet, UDP %s:%s --> %s:%s" % (a_2,ip_header.src_addr,udp_header.src_port,ip_header.dst_addr,udp_header.dst_port))
                            key = ('UDP', ip_header.src_addr, udp_header.src_port, ip_header.dst_addr, udp_header.dst_port)
                            if key in connections:
                                nat_info = connections[key]
                                ip_header.dst_addr = nat_info.orig_src_ip
                                updated_conn_info = nat_info._replace(last_access_time=current_time)
                                connections[key] = updated_conn_info
                            #print("modify network %s packet, UDP %s:%s --> %s:%s" % (a_2,ip_header.src_addr,udp_header.src_port,ip_header.dst_addr,udp_header.dst_port))

                        elif icmp_header is not None:
                            icmp_id = struct.unpack('!H', icmp_header.payload[0:2])[0]
                            #print("origin network %s packet, ICMP %s --> %s %s" % (a_2,ip_header.src_addr,ip_header.dst_addr,icmp_id))
                            key = ('ICMP', ip_header.src_addr, icmp_id, ip_header.dst_addr)
                            if key in connections:
                                nat_info = connections[key]
                                ip_header.dst_addr = nat_info.orig_src_ip
                                updated_conn_info = nat_info._replace(last_access_time=current_time)
                                connections[key] = updated_conn_info
                            #print("modify network %s packet, ICMP %s --> %s %s" % (a_2,ip_header.src_addr,ip_header.dst_addr,icmp_id))

                packet.recalculate_checksums()
                w_network.send(packet)
```

按照这个逻辑后,就可以实现snat功能

当然连接跟踪还需要考虑性能和超时时间等,当前demo仅做参考

0条评论
0 / 1000
h****n
2文章数
0粉丝数
h****n
2 文章 | 0 粉丝
h****n
2文章数
0粉丝数
h****n
2 文章 | 0 粉丝
原创

WinDivert介绍及snat实例

2024-07-05 09:55:59
403
0

# 简介

WinDivert是一种用户模式的数据包捕获和转移包,可用于windows vista、windows 2008、windows 7、windows 8 和 windows 10。WinDivert允许用户模式应用程序捕获、修改、丢弃从windows网络堆栈发送的网络数据包,而无需编写内核模式代码。

主要有以下功能:
1. 捕获网络数据包
2. 筛选/丢弃网络数据包
3. 嗅探网络数据包
4. 注入网络数据包
5. 修改网络数据包  
    主要特点:可抓包,修改包
**注意:需要管理员权限!!!**


原理图如上,WinDivert.sys 驱动安装在Windows network stack 下,其具体步骤为:

- 一个新网络包进入到network stack 之后会被WinDivert.sys 截获;
- 如果截获的包被 PROGRAM-defined 过滤器匹配成功,该包将会被重定向,PROGRAM 就会调用 WinDivertRecv() 方法来读取包;如果匹配失败,截获的包将不会被重定向;
- PROGRAM 将对包进行 drops,modify, 或者 re-inject 操作。

# 基本api
## WinDivertOpen 打开
打开一个windivert对象,返回一个对象指针。打开的过程中,需要指定过滤规则,过滤层,过滤器的优先级,以及windivert对象的工作模式。

```c++
WINDIVERTEXPORT HANDLE WinDivertOpen(
    __in        const char *filter,
    __in        WINDIVERT_LAYER layer,
    __in        INT16 priority,
    __in        UINT64 flags);
```
filter:过滤条件,类似于tcpdump的抓包条件
layer:过滤层,一般network和forward,network类似于input和output的链(本机收和本机发),forward是收发本机转发包
```c++
typedef enum
{
    WINDIVERT_LAYER_NETWORK = 0,        /* Network layer. */
    WINDIVERT_LAYER_NETWORK_FORWARD = 1,/* Network layer (forwarded packets) */
    WINDIVERT_LAYER_FLOW = 2,           /* Flow layer. */
    WINDIVERT_LAYER_SOCKET = 3,         /* Socket layer. */
    WINDIVERT_LAYER_REFLECT = 4,        /* Reflect layer. */
} WINDIVERT_LAYER, *PWINDIVERT_LAYER;
```
priority:优先级,值越大,优先级越高
flags:windivert对象到底是用于监听、丢包、还是修改包等模式,可选WINDIVERT_FLAG_SNIFF表示仅仅收包不修改原包,原包还是正常发送
```c++
#define WINDIVERT_FLAG_SNIFF            0x0001
#define WINDIVERT_FLAG_DROP             0x0002
#define WINDIVERT_FLAG_RECV_ONLY        0x0004
#define WINDIVERT_FLAG_READ_ONLY        WINDIVERT_FLAG_RECV_ONLY
#define WINDIVERT_FLAG_SEND_ONLY        0x0008
#define WINDIVERT_FLAG_WRITE_ONLY       WINDIVERT_FLAG_SEND_ONLY
#define WINDIVERT_FLAG_NO_INSTALL       0x0010
#define WINDIVERT_FLAG_FRAGMENTS        0x0020
```
打开这个windivert对象 后就可以进行收包了

## WinDivertRecv 接收原包

```c++
WINDIVERTEXPORT BOOL WinDivertRecv(
    __in        HANDLE handle,
    __out_opt   VOID *pPacket,
    __in        UINT packetLen,
    __out_opt   UINT *pRecvLen,
    __out_opt   WINDIVERT_ADDRESS *pAddr);
```
接收特定WinDivert对象的捕获的包的函数, 这里符合过滤条件的包都会依次被此接口收上来处理

## WinDivertHelperParsePacket 解析原包信息

```c++
WINDIVERTEXPORT BOOL WinDivertHelperParsePacket(
    __in        const VOID *pPacket,
    __in        UINT packetLen,
    __out_opt   PWINDIVERT_IPHDR *ppIpHdr,
    __out_opt   PWINDIVERT_IPV6HDR *ppIpv6Hdr,
    __out_opt   UINT8 *pProtocol,
    __out_opt   PWINDIVERT_ICMPHDR *ppIcmpHdr,
    __out_opt   PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr,
    __out_opt   PWINDIVERT_TCPHDR *ppTcpHdr,
    __out_opt   PWINDIVERT_UDPHDR *ppUdpHdr,
    __out_opt   PVOID *ppData,
    __out_opt   UINT *pDataLen,
    __out_opt   PVOID *ppNext,
    __out_opt   UINT *pNextLen);
```
解析的形成的数据包如下格式
```
typedef struct
{
    UINT32 HeaderLength:17;
    UINT32 FragOff:13;
    UINT32 Fragment:1;
    UINT32 MF:1;
    UINT32 PayloadLength:16;
    UINT32 Protocol:8;
    UINT32 Truncated:1;
    UINT32 Extended:1;
    UINT32 Reserved1:6;
    PWINDIVERT_IPHDR IPHeader;
    PWINDIVERT_IPV6HDR IPv6Header;
    PWINDIVERT_ICMPHDR ICMPHeader;
    PWINDIVERT_ICMPV6HDR ICMPv6Header;
    PWINDIVERT_TCPHDR TCPHeader;
    PWINDIVERT_UDPHDR UDPHeader;
    UINT8 *Payload;
} WINDIVERT_PACKET, *PWINDIVERT_PACKET;
```
解析完后就可以进行修改了,比如修改5元祖

## WinDivertHelperCalcChecksums 
修改后的包重新进行校验和计算

```c++
WINDIVERTEXPORT BOOL WinDivertHelperCalcChecksums(
    __inout     VOID *pPacket, 
    __in        UINT packetLen,
    __out_opt   WINDIVERT_ADDRESS *pAddr,
    __in        UINT64 flags);
```

## WinDivertSend 发送原包

```c++
WINDIVERTEXPORT BOOL WinDivertSend(
    __in        HANDLE handle,
    __in        const VOID *pPacket,
    __in        UINT packetLen,
    __out_opt   UINT *pSendLen,
    __in        const WINDIVERT_ADDRESS *pAddr);
```
校验和重新计算后的包就会重新发送

windivert源码也有一些实例,都是c写的,这里不再过多介绍

# snat实现
PyDivert基于python 的WinDivert实现,我们以python这种简单语言快速验证,实例类似于iptables中的snat功能。

我们实现的场景如下:  A和B设备通过wan或者隧道(wifi也行)连接,A设备的lan口连接内网服务器和打印机等,由于打印机无法配置路由(与设备A的lan同网段),因此设备B想通打印机需要设备A做snat. 这样回包才能达到A后再打到B


这里我们需要把ipv4的主要协议包icmp、tcp、udp代理,需要snat的网络接口设备A的lan口,假设ifIdx为12, 在设备A上首先需要抓取ifIdx口forward出去的数据包,其次在修改了src_ip后,由于打印机回包是dst_ip会是设备A的lan接口ip,要捕获这个包,还需要抓取network上进来的包,在进来的数据包上做还原,这样dst_ip被还原后才能从A回到B
过滤条件为
```python
outgoing_filter = f"ip and (tcp or udp or icmp) and ifIdx == 12"   layer = forward
incoming_filter = f"ip and (tcp or udp or icmp) and ifIdx == 12"   layer = network
NEW_SRC_IP = '192.168.1.1'   #ifIdx=12的接口ip
```


其次,由于需要还原包,我们要根据连接信息进行匹配还原,类似于linux连接跟踪conntrack, 由于线程共用connections,还需要锁机制保护
```python
connections = [key:['proto', 'orig_src_ip', 'orig_src_port', 'orig_dst_ip', 'orig_dst_port', 'nat_src_ip']....]
connection_lock = threading.Lock()
```
这里连接跟踪为一个dict,key为连接跟踪信息,后续回包需要根据此key找到对应的value, 还原出ip,注意有与icmp和tcp/udp的协议字段不一致,需要做区分, tcp及udp用五元组做key,  icmp用ip及icmp_id做key

这里我们起2个线程分别抓forward和network的包
```python
thread_forward = threading.Thread(target=handle_forward_layer)
thread_network = threading.Thread(target=handle_network_layer)
```

处理forward和network的接口如下
```python
def handle_forward_layer():
    with pydivert.WinDivert(outgoing_filter,1,0,0) as w_forward:
        while True:
            packet = w_forward.recv()
            if packet.direction == pydivert.Direction.OUTBOUND:
                ip_header = packet.ipv4
                tcp_header = packet.tcp
                udp_header = packet.udp
                icmp_header = packet.icmpv4
                with connection_lock:
                    # 处理外网到内网的包,修改源 IP 为 LAN IP
                    if tcp_header is not None:
                        # 构建正向和反向键
                        #print("origin forward %s packet, TCP %s:%s --> %s:%s" % (a_1,ip_header.src_addr,tcp_header.src_port,ip_header.dst_addr,tcp_header.dst_port))
                        rev_key = ('TCP', ip_header.dst_addr, tcp_header.dst_port, NEW_SRC_IP, tcp_header.src_port)
                        connections[rev_key] = ConnectionInfo('TCP', ip_header.src_addr, tcp_header.src_port, ip_header.dst_addr, tcp_header.dst_port, NEW_SRC_IP, current_time)
                        ip_header.src_addr = NEW_SRC_IP
                        #print("modify forward %s packet, TCP %s:%s --> %s:%s" % (a_1,ip_header.src_addr,tcp_header.src_port,ip_header.dst_addr,tcp_header.dst_port))

                    elif udp_header is not None:
                        #print("origin forward %s packet, UDP %s:%s --> %s:%s" % (a_1,ip_header.src_addr,udp_header.src_port,ip_header.dst_addr,udp_header.dst_port))
                        rev_key = ('UDP', ip_header.dst_addr, udp_header.dst_port, NEW_SRC_IP, udp_header.src_port)
                        connections[rev_key] = ConnectionInfo('UDP', ip_header.src_addr, udp_header.src_port, ip_header.dst_addr, udp_header.dst_port, NEW_SRC_IP, current_time)
                        ip_header.src_addr = NEW_SRC_IP
                        #print("modify forward %s packet, TCP %s:%s --> %s:%s" % (a_1,ip_header.src_addr,udp_header.src_port,ip_header.dst_addr,udp_header.dst_port))

                    elif icmp_header is not None:
                        icmp_id = struct.unpack('!H', icmp_header.payload[0:2])[0]
                        #print("origin forward %s packet, ICMP %s --> %s %s" % (a_1,ip_header.src_addr,ip_header.dst_addr,icmp_id))
                        rev_key = ('ICMP', ip_header.dst_addr, icmp_id, NEW_SRC_IP)
                        connections[rev_key] = ConnectionInfo('ICMP', ip_header.src_addr, 0, ip_header.dst_addr, 0, NEW_SRC_IP, current_time)
                        ip_header.src_addr = NEW_SRC_IP
                        #print("modify forward %s packet, ICMP %s --> %s %s" % (a_1,ip_header.src_addr,ip_header.dst_addr,icmp_id))

  
            packet.recalculate_checksums()
            w_forward.send(packet)
```

```python
 def handle_network_layer():
        with pydivert.WinDivert(incoming_filter,0,0,0) as w_network:
            while True:
                packet = w_network.recv()
                # 从此接口进来的包需要做还原
                if packet.direction == pydivert.Direction.INBOUND:
                    ip_header = packet.ipv4
                    tcp_header = packet.tcp
                    udp_header = packet.udp
                    icmp_header = packet.icmpv4
                    with connection_lock:
                        if tcp_header is not None:
                            #print("origin network %s packet, TCP %s:%s --> %s:%s" % (a_2,ip_header.src_addr,tcp_header.src_port,ip_header.dst_addr,tcp_header.dst_port))
                            key = ('TCP', ip_header.src_addr, tcp_header.src_port, ip_header.dst_addr, tcp_header.dst_port)
                            if key in connections:
                                nat_info = connections[key]
                                ip_header.dst_addr = nat_info.orig_src_ip
                                updated_conn_info = nat_info._replace(last_access_time=current_time)
                                connections[key] = updated_conn_info

                            #print("modify network %s packet, TCP %s:%s --> %s:%s" % (a_2,ip_header.src_addr,tcp_header.src_port,ip_header.dst_addr,tcp_header.dst_port))

                        elif udp_header is not None:
                            #print("origin network %s packet, UDP %s:%s --> %s:%s" % (a_2,ip_header.src_addr,udp_header.src_port,ip_header.dst_addr,udp_header.dst_port))
                            key = ('UDP', ip_header.src_addr, udp_header.src_port, ip_header.dst_addr, udp_header.dst_port)
                            if key in connections:
                                nat_info = connections[key]
                                ip_header.dst_addr = nat_info.orig_src_ip
                                updated_conn_info = nat_info._replace(last_access_time=current_time)
                                connections[key] = updated_conn_info
                            #print("modify network %s packet, UDP %s:%s --> %s:%s" % (a_2,ip_header.src_addr,udp_header.src_port,ip_header.dst_addr,udp_header.dst_port))

                        elif icmp_header is not None:
                            icmp_id = struct.unpack('!H', icmp_header.payload[0:2])[0]
                            #print("origin network %s packet, ICMP %s --> %s %s" % (a_2,ip_header.src_addr,ip_header.dst_addr,icmp_id))
                            key = ('ICMP', ip_header.src_addr, icmp_id, ip_header.dst_addr)
                            if key in connections:
                                nat_info = connections[key]
                                ip_header.dst_addr = nat_info.orig_src_ip
                                updated_conn_info = nat_info._replace(last_access_time=current_time)
                                connections[key] = updated_conn_info
                            #print("modify network %s packet, ICMP %s --> %s %s" % (a_2,ip_header.src_addr,ip_header.dst_addr,icmp_id))

                packet.recalculate_checksums()
                w_network.send(packet)
```

按照这个逻辑后,就可以实现snat功能

当然连接跟踪还需要考虑性能和超时时间等,当前demo仅做参考

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0