vislee / leevis.com

Blog
87 stars 13 forks source link

客户端流量劫持 #206

Open vislee opened 3 months ago

vislee commented 3 months ago

概述

一些场景需要选择性的将指定部分的流量劫持到服务端。 例如,ssl vpn就是把客户机访问公司内网的流量劫持后通过加密隧道转到公司内网。

image

原理

加密隧道可以用:socks5 over tls 实现。

如何进行流量劫持呢?有2种:

  1. 使用操作系统代理。
  2. 使用tun等虚拟网卡。

使用操作系统代理

操作系统网络设置一般都支持代理,只需要把本地的socks5客户端监听端口配置到网络设置SOCKS代理就可以实现流量劫持。 一般只有浏览器才会使用这个代理,每个app都有自己配置socks代理的地方,不通用。 image

使用tun虚拟网卡

TUN模拟了网络层设备,用户态可以直接读该tun设备,读到的数据是IP报文。如果要拿到payload就需要解析tcp/ip协议,有两种方案得到payload:

  1. 使用操作系统的协议栈解析。
  2. 用户态解析tcp/ip协议栈。
使用操作系统的协议栈解析

启动代理程序监听本地的IP和端口,等待接收tun设备转过来的数据。 通过路由或者fake ip,把应用程序的流量先重定向到tun设备,用户态程序读取到tun设备的IP报文做NAT转换再写回去(注意目标IP和端口是上述代理监听的IP和端口)。通过操作系统协议栈再收到应用程序拿到tcp的payload,然后可以通过socks5转到服务端。

NAT:

image

tcpdump抓包

image

程序日志:

image

用户态解析tcp/ip协议栈

TCP首部校验和计算三部分:TCP首部+TCP数据+TCP伪首部。

TCP伪首部: 共有12字节,包含IP首部的一些字段,有如下信息:32位源IP地址、32位目的IP地址、8位保留字节(置0)、8位传输层协议号(TCP是6,UDP是17)、16位TCP报文长度(TCP首部+数据)。

首先,把伪首部、TCP报头、TCP数据分为16位的字,如果总长度为奇数个字节,则在最后增添一个位都为0的字节。 把TCP报头中的校验和字段置为0。 其次,用反码相加法(对每16bit进行二进制反码求和)累加所有的16位字(进位也要累加,进位则将高位叠加到低位)。 最后,将上述结果作为TCP的校验和,存在检验和字段中。


func sumCompat(b []byte) (sum uint32) {
    n := len(b)
    if n&1 != 0 {
        n--
        sum += uint32(b[n]) << 8
    }

    for i := 0; i < n; i += 2 {
        sum += (uint32(b[i]) << 8) | uint32(b[i+1])
    }
    return
}

总结

https://zu1k.com/posts/coding/tun-mode/