mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-11-02 22:54:01 +08:00
711 lines
22 KiB
Go
711 lines
22 KiB
Go
package tlsLayer
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/tls"
|
||
"flag"
|
||
"io"
|
||
"log"
|
||
"net"
|
||
"strings"
|
||
|
||
"github.com/hahahrfool/v2ray_simple/common"
|
||
)
|
||
|
||
var PDD bool //print tls detect detail
|
||
var OnlyTest bool
|
||
|
||
func init() {
|
||
//log.SetOutput(os.Stdout) //主要是日志太多,如果都能直接用管道放到文件中就好了,默认不是Stdout所以优点尴尬,操作麻烦点
|
||
|
||
flag.BoolVar(&PDD, "pdd", false, "print tls detect detail")
|
||
flag.BoolVar(&OnlyTest, "ot", false, "only detect tls, doesn't actually mark tls")
|
||
|
||
}
|
||
|
||
// 用于 探测 承载数据是否使用了tls, 它先与 底层tcp连接 进行 数据传输,然后查看传输到内容
|
||
// 可以参考 https://www.baeldung.com/linux/tcpdump-capture-ssl-handshake
|
||
type SniffConn struct {
|
||
net.Conn //这个 Conn本DetectConn 中不会用到,只是为了能让CopyConn支持 net.Conn
|
||
W *DetectWriter
|
||
R *DetectReader
|
||
|
||
RawConn *net.TCPConn // 这个是为了让外界能够直接拿到底层的连接
|
||
|
||
}
|
||
|
||
func (cc *SniffConn) Read(p []byte) (int, error) {
|
||
return cc.R.Read(p)
|
||
}
|
||
|
||
func (cc *SniffConn) Write(p []byte) (int, error) {
|
||
return cc.W.Write(p)
|
||
}
|
||
|
||
//这个暂时没用到,先留着
|
||
func (cc *SniffConn) ReadFrom(r io.Reader) (int64, error) {
|
||
if cc.RawConn != nil {
|
||
return cc.RawConn.ReadFrom(r)
|
||
}
|
||
return 0, io.EOF
|
||
}
|
||
|
||
//可选两个参数传入,优先使用rw ,为nil的话 再使用oldConn,作为 DetectConn 的 Read 和Write的 具体调用的主体
|
||
// is_secure 表示,是否使用更强的过滤手段(越强越浪费时间)
|
||
func NewSniffConn(oldConn net.Conn, rw io.ReadWriter, isclient bool, is_secure bool) *SniffConn {
|
||
|
||
var validOne io.ReadWriter = rw
|
||
if rw == nil {
|
||
validOne = oldConn
|
||
}
|
||
|
||
cc := &SniffConn{
|
||
Conn: oldConn,
|
||
W: &DetectWriter{
|
||
Writer: validOne,
|
||
},
|
||
R: &DetectReader{
|
||
Reader: validOne,
|
||
},
|
||
}
|
||
cc.W.isclient = isclient
|
||
cc.R.isclient = isclient
|
||
cc.W.is_secure = is_secure
|
||
cc.R.is_secure = is_secure
|
||
|
||
cc.W.peer = &cc.R.ComSniff
|
||
cc.R.peer = &cc.W.ComSniff
|
||
|
||
if netConn := oldConn.(*net.TCPConn); netConn != nil {
|
||
//log.Println("NewDetectConn: get netConn!") // 如果是客户端的socks5,网页浏览的话这里一定能转成 TCPConn, 不信取消注释试试
|
||
cc.RawConn = netConn
|
||
}
|
||
|
||
return cc
|
||
}
|
||
|
||
//是 proxy.UserContainer 的子集
|
||
type UserHaser interface {
|
||
HasUserByBytes(bs []byte) bool
|
||
UserBytesLen() int
|
||
}
|
||
|
||
type ComSniff struct {
|
||
IsTls bool
|
||
DefinitelyNotTLS bool
|
||
|
||
SpecialCommandBytes []byte //目前规定,使用uuid作为special command
|
||
|
||
UH UserHaser //为了在服务端能确认一串数据确实是有效的uuid,需要使用 UserHaser
|
||
|
||
SniffedHostName string
|
||
|
||
isclient bool
|
||
is_secure bool
|
||
|
||
packetCount int
|
||
|
||
handShakePass bool //握手测试通过
|
||
handshakeVer uint16
|
||
|
||
handshakeFailReason int
|
||
|
||
cantBeTLS13 bool //clienthello如果没有 supported_versions项,或者该项没有0304,则不可能协商出tls1.3。如果协商出了则是错误的;
|
||
|
||
peer *ComSniff //握手是需要判断clienthello+serverhello的,而它们一个是读一个是写,所以要能够让它们相互访问到之前判断好的数据
|
||
}
|
||
|
||
const (
|
||
CutType_big byte = iota + 1
|
||
CutType_small
|
||
CutType_fit
|
||
)
|
||
|
||
func (c *ComSniff) incr() {
|
||
c.packetCount++
|
||
}
|
||
|
||
func (c *ComSniff) GetFailReason() int {
|
||
return c.handshakeFailReason
|
||
}
|
||
|
||
// 总之,如果读写都用同样的判断代码的话,客户端和服务端应该就能同步进行 相同的TLS判断
|
||
func (cd *ComSniff) commonDetect(p []byte, isRead bool) {
|
||
|
||
/*
|
||
我们把tls的细节放在这个注释里,便于参考
|
||
|
||
首先是rfc文件
|
||
|
||
tls1.3标准 https://datatracker.ietf.org/doc/html/rfc8446
|
||
tls1.2标准 https://datatracker.ietf.org/doc/html/rfc5246
|
||
tls1.1标准 https://datatracker.ietf.org/doc/html/rfc4346
|
||
tls1.0标准: https://datatracker.ietf.org/doc/html/rfc2246
|
||
|
||
关于历代的演进,还可参考
|
||
https://program-think.medium.com/扫盲-https-和-ssl-tls-协议-4-历史版本的演变及-record-协议的细节-7400cefe7671
|
||
|
||
|
||
首先判断握手包,即第一个包
|
||
The Client Hello messages contain 01 in the sixth data byte of the TCP packet.
|
||
应该是这里定义的: https://datatracker.ietf.org/doc/html/rfc5246#section-7.4
|
||
|
||
//不过,首先要包在 Plaintext结构里
|
||
//https://datatracker.ietf.org/doc/html/rfc5246
|
||
|
||
struct {
|
||
ContentType type; //第一字节
|
||
ProtocolVersion version;//第2、3字节
|
||
uint16 length;// 第4、5字节
|
||
opaque fragment[TLSPlaintext.length];
|
||
} TLSPlaintext;
|
||
|
||
//ContentType 中,handshake始终是22,也就是说,tls连接的hello字节第一个肯定是22
|
||
// 从tls1.0 到 1.3, 这个情况都是一样的
|
||
|
||
tls1.0 ~ tls1.3: change_cipher_spec(20), alert(21), handshake(22),application_data(23)
|
||
|
||
enum {
|
||
hello_request(0), client_hello(1), server_hello(2),
|
||
certificate(11), server_key_exchange (12),
|
||
certificate_request(13), server_hello_done(14),
|
||
certificate_verify(15), client_key_exchange(16),
|
||
finished(20), (255)
|
||
} HandshakeType;
|
||
|
||
struct {
|
||
HandshakeType msg_type; // handshake type,第六字节
|
||
uint24 length; // bytes in message,第789字节
|
||
select (HandshakeType) {
|
||
case hello_request: HelloRequest;
|
||
case client_hello: ClientHello;
|
||
case server_hello: ServerHello;
|
||
case certificate: Certificate;
|
||
case server_key_exchange: ServerKeyExchange;
|
||
case certificate_request: CertificateRequest;
|
||
case server_hello_done: ServerHelloDone;
|
||
case certificate_verify: CertificateVerify;
|
||
case client_key_exchange: ClientKeyExchange;
|
||
case finished: Finished;
|
||
} body;
|
||
} Handshake;
|
||
|
||
然后见下面 TLSPlaintext 的定义,前五字节是 头部, 然后第六字节就是这个Handshake的第一字节,client就是 client_hello, 就是1
|
||
|
||
In the SSL handshake message, the 10th and 11th bytes of the data contain the TLS version.
|
||
|
||
数一下,ClientHello结构和 ServerHello结构 正好在第十字节的位置 开始
|
||
|
||
tls1.2:
|
||
|
||
struct {
|
||
ProtocolVersion client_version;
|
||
Random random;
|
||
SessionID session_id;
|
||
CipherSuite cipher_suites<2..2^16-2>;
|
||
CompressionMethod compression_methods<1..2^8-1>;
|
||
select (extensions_present) {
|
||
case false:
|
||
struct {};
|
||
case true:
|
||
Extension extensions<0..2^16-1>;
|
||
};
|
||
} ClientHello;
|
||
|
||
"The presence of extensions can be detected by
|
||
determining whether there are bytes following the compression_methods
|
||
at the end of the ClientHello. "
|
||
|
||
就是说,tls1.2中,如果读完 compression_methods后,还存在字节,则后面的字节都是 extensions
|
||
|
||
但是tls1.3的 clientHello的 ProtocolVersion是 legacy_version,依然是0303
|
||
https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2
|
||
// 而后面在 supported_versions extension 里会包含 0304
|
||
|
||
“TLS 1.3 ClientHellos are identified as having
|
||
a legacy_version of 0x0303 and a supported_versions extension
|
||
present with 0x0304 as the highest version indicated therein.”
|
||
|
||
ProtocolVersion:
|
||
TLSv1.0 – 0x0301
|
||
TLSv1.1 – 0x0302
|
||
TLSv1.2 – 0x0303
|
||
TLSv1.3 – 0x0304
|
||
|
||
tls1.3:
|
||
|
||
struct {
|
||
ProtocolVersion legacy_version = 0x0303;
|
||
Random random; //byte[32]
|
||
opaque legacy_session_id<0..32>; //前1字节表示长度
|
||
CipherSuite cipher_suites<2..2^16-2>; //前两字节表示长度,因为 2^16=64k,正好== 256*256, 两字节就可以完整表示
|
||
opaque legacy_compression_methods<1..2^8-1>; //前1字节表示长度
|
||
Extension extensions<8..2^16-1>; //前两字节表示长度
|
||
} ClientHello;
|
||
|
||
这么算的话,0303后面 至少需要跟 32 + 1 + 4 + 2 + 10 = 49 长度的数据
|
||
|
||
extensions: The actual "Extension"
|
||
format is defined in https://datatracker.ietf.org/doc/html/rfc8446#section-4.2
|
||
|
||
"In TLS 1.3, the use of certain extensions is mandatory, as functionality has moved into
|
||
extensions to preserve ClientHello compatibility with previous
|
||
versions of TLS. Servers MUST ignore unrecognized extensions."
|
||
|
||
"TLS 1.3 ClientHello messages always
|
||
contain extensions (minimally "supported_versions", otherwise, they
|
||
will be interpreted as TLS 1.2 ClientHello messages)"
|
||
|
||
这里的 extensions就不是用 present与否来判断了,因为tls1.3强制规定,必须存在extension
|
||
|
||
|
||
这个 <0..32> 到底是什么?发现在rfc的上方定义了:
|
||
|
||
https://datatracker.ietf.org/doc/html/rfc5246#section-4.3
|
||
|
||
Variable-length vectors are defined by specifying a subrange of legal
|
||
lengths, inclusively, using the notation <floor..ceiling>. When
|
||
these are encoded, the actual length precedes the vector's contents
|
||
in the byte stream. The length will be in the form of a number
|
||
consuming as many bytes as required to hold the vector's specified
|
||
maximum (ceiling) length. A variable-length vector with an actual
|
||
length field of zero is referred to as an empty vector.
|
||
|
||
就是说,首部第一字节要放一个 字节或多个字节来表示长度。一般小于256的长度显然就是占用一个字节。
|
||
|
||
数据部分:
|
||
|
||
tls1.2
|
||
https://datatracker.ietf.org/doc/html/rfc5246#section-6.2
|
||
|
||
struct {
|
||
uint8 major;
|
||
uint8 minor;
|
||
} ProtocolVersion;
|
||
|
||
enum {
|
||
change_cipher_spec(20), alert(21), handshake(22),
|
||
application_data(23), (255)
|
||
} ContentType;
|
||
|
||
struct {
|
||
ContentType type;
|
||
ProtocolVersion version;
|
||
uint16 length;
|
||
opaque fragment[TLSPlaintext.length];
|
||
} TLSPlaintext;
|
||
|
||
struct {
|
||
ContentType type; //23
|
||
ProtocolVersion version; //3,3
|
||
uint16 length; //第4,5字节
|
||
opaque fragment[TLSCompressed.length];
|
||
} TLSCompressed;
|
||
|
||
*/
|
||
|
||
n := len(p)
|
||
|
||
defer cd.incr()
|
||
|
||
p0 := p[0]
|
||
|
||
if cd.packetCount == 0 {
|
||
|
||
if n < 11 {
|
||
cd.DefinitelyNotTLS = true
|
||
cd.handshakeFailReason = 1
|
||
return
|
||
}
|
||
if p0 != 22 {
|
||
cd.DefinitelyNotTLS = true
|
||
cd.handshakeFailReason = 2
|
||
|
||
//只是不是握手信息, 有可能还是tls其它信息?可能是close alert等情况?这里应该再加一些处理,否则有时网页会打不开,刷新一下才能打开。
|
||
return
|
||
}
|
||
|
||
//version: p9, p10 , 3,1 3,2 3,3 3,4
|
||
if p[9] != 3 {
|
||
cd.DefinitelyNotTLS = true
|
||
cd.handshakeFailReason = 4
|
||
return
|
||
}
|
||
if p[10] == 0 || p[10] > 4 {
|
||
cd.DefinitelyNotTLS = true
|
||
cd.handshakeFailReason = 5
|
||
return
|
||
}
|
||
|
||
//第六字节是 Handshake.msg_type, 1是clienthello,2是serverhello
|
||
|
||
var helloValue byte = 1
|
||
|
||
//我们 DetectConn中,考察客户端浏览器传输来的流量 时 使用 Read, 考察远程真实服务端发来的流量 时 使用Write;
|
||
// 无论 代理程序的客户端还是 代理程序的服务端都是如此
|
||
|
||
if !isRead {
|
||
helloValue = 2
|
||
}
|
||
|
||
if p[5] != helloValue { //第六字节,
|
||
cd.DefinitelyNotTLS = true
|
||
cd.handshakeFailReason = 3
|
||
return
|
||
}
|
||
|
||
if cd.is_secure && cd.isclient {
|
||
|
||
//VersionTLS10,VersionTLS11,VersionTLS12,VersionTLS13
|
||
handshakeVer := uint16(p[10]) | uint16(p[9])<<8
|
||
|
||
//client hello
|
||
if isRead {
|
||
|
||
if handshakeVer == tls.VersionTLS12 { //0303
|
||
pAfter := p[11:]
|
||
if len(pAfter) < 49 {
|
||
cd.DefinitelyNotTLS = true
|
||
cd.handshakeFailReason = 6
|
||
return
|
||
}
|
||
|
||
cd.sniff_hello(pAfter, true) //代码很长!
|
||
if cd.DefinitelyNotTLS {
|
||
return
|
||
}
|
||
}
|
||
|
||
cd.handshakeVer = handshakeVer
|
||
|
||
} else { //server hello
|
||
if cd.peer.handshakeVer == tls.VersionTLS12 { //0303
|
||
if handshakeVer == tls.VersionTLS12 {
|
||
pAfter := p[11:]
|
||
if len(pAfter) < 32 {
|
||
cd.DefinitelyNotTLS = true
|
||
cd.handshakeFailReason = 6
|
||
return
|
||
}
|
||
|
||
cd.sniff_hello(pAfter, false) //代码很长!
|
||
if cd.DefinitelyNotTLS {
|
||
return
|
||
}
|
||
} else {
|
||
//可能吗?
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
cd.handShakePass = true
|
||
|
||
return
|
||
|
||
} else if !cd.handShakePass {
|
||
cd.DefinitelyNotTLS = true
|
||
return
|
||
}
|
||
|
||
if !OnlyTest {
|
||
if !isRead {
|
||
// 如果前面握手符合 tls特征,然后之后的命令却获得到了特殊命令的头部,则我们直接认为这是服务端发给我们的特殊指令
|
||
|
||
// 这个过程依然是 在 客户端的 读取出 wrc的数据,然后 调用 wlcdc.Write 时发生
|
||
// 就是说,同样是Write,服务端是在Write判断完后,发送特殊指令
|
||
// 而客户端是在 Write的判断过程中,检索特殊指令
|
||
// 说白了,就是在 服务端-> 客户端 这个方向发生的,
|
||
if cd.isclient && n >= len(cd.SpecialCommandBytes) {
|
||
|
||
if bytes.Equal(cd.SpecialCommandBytes, p[:len(cd.SpecialCommandBytes)]) {
|
||
|
||
if PDD {
|
||
log.Println("W 读到特殊命令!")
|
||
}
|
||
|
||
cd.IsTls = true
|
||
return
|
||
}
|
||
}
|
||
} else {
|
||
// Read也是同理,也是发送特殊指令,只不过Read的话,是客户端向 服务端 发送特殊指令
|
||
// 这里是不会被黑客攻击的,因为事件发生在第二个或者更往后的 数据包中,而vless的uuid检验则是从第一个就要开始检验。也不会遇到重放攻击,因为tls每次加密的秘文都是不一样的。
|
||
|
||
//这里就是服务端来读取 特殊指令
|
||
if !cd.isclient {
|
||
|
||
ubl := cd.UH.UserBytesLen()
|
||
|
||
if n >= ubl {
|
||
|
||
if cd.UH.HasUserByBytes(p[:ubl]) {
|
||
bs := common.GetBytes(ubl)
|
||
copy(bs, p[:ubl])
|
||
cd.SpecialCommandBytes = bs
|
||
|
||
if PDD {
|
||
log.Println("R 读到特殊命令! 剩余长度", n-ubl)
|
||
}
|
||
cd.IsTls = true
|
||
return
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
//下面的判断, 只会在 客户端的 Read,和 服务端的 Write中 发生
|
||
|
||
p1 := p[1]
|
||
p2 := p[2]
|
||
|
||
//客户端Read 时 少数情况 第一字节21,首部为 [21 3 3 0 26 0 0 0 0 0], 一般总长度为31
|
||
// 不过后来发现,这就是xtls里面隔离出的 alert的那种情况,
|
||
// 其它都是 能被捕捉到的。
|
||
|
||
// 23表示 数据层,第二字节3是固定的,然后第3字节 从1到4,表示tls版本,不过tls1.3 和 1.2这里都是 23 3 3
|
||
|
||
//见 https://datatracker.ietf.org/doc/html/rfc8446#section-5.1
|
||
// tls1.3 的 ContentType(23) 后面紧接着的是 legacy_record_version,而这个必须设成 0x0303
|
||
// 也就是说和tls1.2一样;而1.1的话是 0302, 1.0 是 0301,总之第三字节只会是1,2,3
|
||
//
|
||
// 不过因为我们先过滤的握手包,所以是能够区分 tls1.3/纯1.2 的;只是在这里验证一下
|
||
//
|
||
if p0 == 23 && p1 == 3 && p2 > 0 && p2 < 4 {
|
||
if PDD {
|
||
str := "W"
|
||
if isRead {
|
||
str = "R"
|
||
}
|
||
log.Println(str, "got TLS!", n)
|
||
}
|
||
|
||
if !OnlyTest {
|
||
cd.IsTls = true
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// 打印没过滤到的数据,一般而言,大多是非首包的 握手数据,以22开头
|
||
if PDD || OnlyTest {
|
||
min := 10
|
||
if n < 10 {
|
||
min = n
|
||
}
|
||
|
||
str := "Write,"
|
||
if isRead {
|
||
str = "Read,"
|
||
}
|
||
log.Println(" ======== ", str, n, p[:min])
|
||
}
|
||
|
||
}
|
||
|
||
func commonFilterStep(err error, cd *ComSniff, p []byte, isRead bool) {
|
||
if !OnlyTest && (cd.DefinitelyNotTLS || cd.IsTls) { //确定了是TLS 或者肯定不是 tls了的话,就直接return掉
|
||
return
|
||
}
|
||
if err != nil {
|
||
eStr := err.Error()
|
||
if strings.Contains(eStr, "use of closed") || strings.Contains(eStr, "reset by peer") || strings.Contains(eStr, "EOF") {
|
||
return
|
||
}
|
||
}
|
||
if !OnlyTest && cd.packetCount > 8 {
|
||
//都8个包了还没断定tls?直接推定不是!
|
||
cd.handshakeFailReason = -1
|
||
cd.DefinitelyNotTLS = true
|
||
return
|
||
}
|
||
|
||
if len(p) > 3 {
|
||
cd.commonDetect(p, isRead)
|
||
}
|
||
|
||
}
|
||
|
||
// DetectReader 对每个Read的数据进行分析,判断是否是tls流量
|
||
type DetectReader struct {
|
||
io.Reader
|
||
|
||
ComSniff
|
||
}
|
||
|
||
// 总之,我们在客户端的 Read 操作,就是 我们试图使用 Read 读取客户的请求,然后试图发往 外界
|
||
// 所以在socks5后面 使用的这个 Read,是读取客户端发送的请求,比如 clienthello之类
|
||
// 服务端的 Read 操作,也是读 clienthello,因为我们总是判断客户传来的数据
|
||
func (dr *DetectReader) Read(p []byte) (n int, err error) {
|
||
n, err = dr.Reader.Read(p)
|
||
|
||
commonFilterStep(err, &dr.ComSniff, p[:n], true)
|
||
|
||
if PDD {
|
||
if dr.DefinitelyNotTLS {
|
||
log.Println("R DefinitelyNotTLS, reasonNum: ", dr.handshakeFailReason)
|
||
}
|
||
}
|
||
|
||
/*
|
||
if dr.IsTls {
|
||
|
||
if dr.isclient {
|
||
//判断TLS成功后,会向服务端发送特殊指令
|
||
|
||
//特殊指令是直接在main.go里发送的, 即调用Read者 负责发送特殊指令
|
||
|
||
// 此时特殊指令的发送不受Reader控制,要在main函数中 Write 到 wrc中
|
||
return
|
||
} else {
|
||
// 服务端,能进入这里,就代表服务端接收到了特殊指令
|
||
|
||
//那么此时的p的前几字节都是特殊指令,是没用的,要跳过;后面是可能会接东西的,也可能不接,因为tcp是粘包的
|
||
//后面如果接了任何东西,那么接的东西都是需要 直连发送的
|
||
|
||
// 但是,不能把特殊指令去掉然后重新放到p中
|
||
// 因为Read是调用 底层的tls的,tls会把 特殊指令 以及跟着的直连数据一起解密;
|
||
// 我们需要从TeeConn的Recorder中读取真实的数据, 也是在main.go里操作
|
||
|
||
return
|
||
|
||
}
|
||
|
||
}*/
|
||
return
|
||
}
|
||
|
||
// DetectReader 对每个Read的数据进行分析,判断是否是tls流量
|
||
type DetectWriter struct {
|
||
io.Writer
|
||
ComSniff
|
||
}
|
||
|
||
// 直接写入,而不进行探测
|
||
func (dw *DetectWriter) SimpleWrite(p []byte) (n int, err error) {
|
||
n, err = dw.Writer.Write(p)
|
||
return
|
||
}
|
||
|
||
//用于通知 “我们要开始tls数据部分啦” 的 “特殊指令”,该指令会被tls加密发送,因此不用担心暴露
|
||
//var SpecialCommand = []byte{1, 2, 3, 4} //后来决定直接使用uuid作为特殊指令。也是加密传输的,所以安全性一样。和普通vless+tls一样,最怕的是中间人攻击。只要自己证书申请好就行。
|
||
|
||
//发现,数据基本就是 23 3 3, 22 3 3,22 3 1 , 20 3 3
|
||
// 一个首包不为23 3 3 的包往往会出现在 1184长度的包的后面,而且一般 1184长度的包 的开头是 22 3 3 0 122,且总是在Write里面发生
|
||
// 所以可以直接推测这个就是握手包; 实测 22 3 3 0 122 开头的,无一例外都是 1184长度,且后面接多个 开头任意的 Write
|
||
// 也有一些特殊情况,比如 22 3 1 首部的包,往往是 517 长度,后面也会紧跟着一些首部不为 22/23 的 Write
|
||
//
|
||
// 23 3 3 也是有可能 发生后面不为22/23的write,长度 不等
|
||
//
|
||
// 总之,我们在客户端的 Write 操作,就是 外界试图使用我们的 Write 写入数据
|
||
// 所以在socks5后面 使用的这个 Write, 应该是把 服务端的响应 写回 socks5,比如 serverhello 之类
|
||
// 服务端的 Write 操作,也是读 serverhello
|
||
//
|
||
// 根据之前讨论,23 3 3 就是 数据部分,TLSCiphertext
|
||
// https://halfrost.com/https_record_layer/
|
||
// 总之我们依然判断 23 3 3 好了,没那么多技巧,先判断是否存在握手包,握手完成后,遇到23 3 3 后,直接就
|
||
// 进入direct模式;
|
||
func (dw *DetectWriter) Write(p []byte) (n int, err error) {
|
||
//write和Read不一样,Write是,p现在就具有所有的已知信息,所以我们先过滤,然后再发送到远端
|
||
|
||
if dw.IsTls {
|
||
n, err = dw.Writer.Write(p)
|
||
return
|
||
}
|
||
|
||
commonFilterStep(nil, &dw.ComSniff, p, false)
|
||
|
||
// 经过判断之后,确认是 tls了,则我们缓存这个记录, 然后通过tls发送特殊指令
|
||
if dw.IsTls {
|
||
|
||
if dw.isclient {
|
||
// 客户端 DetectConn的Write被调用的话,那就是从 服务端的 tls连接中 提取出了新数据,准备通过socks5发往浏览器
|
||
//
|
||
//客户端判断出IsTLS,那就是收到了特殊指令,p的头部就是特殊指令;p后面可能还跟 数据
|
||
// 然而,一旦后面跟了数据,就完蛋了,这说明特殊指令 和 直连数据连在一起 整个被tls处理 了
|
||
|
||
//所以,关键点是,在客户端的tls的前面 也要进行过滤
|
||
// 就是因为 我们需要splice,所以原始数据和 tls解密过的数据 我们都要!
|
||
|
||
//客户端发现 浏览器 发送来的数据是 tls数据后, 要使用tls 发送特殊指令,然后,
|
||
// 服务端 在收到 特殊指令之前,已经收到了 tls前面的握手数据,已经处于 handshakePass的阶段
|
||
// 因此,服务端只要在 handshakePass 后面,把 原本的 net.TCPConn 做成一个 TeeReader, 然后给tls真正提供的
|
||
// 实际上是这个TeeReader,这样tls只要Read,则我们的TeeReader也会跟着 输出数据
|
||
// 然后就算tls读到了 直连数据 也没关系,因为我们的TeeReader也读到了!所以根本不必担心丢失数据的问题
|
||
|
||
// 总之就是,客户端的wrc的 tls 和 服务端的 wlc 的tls,我们都不直接提供tcp连接,而是包一层
|
||
|
||
// 如果特殊指令后面跟着 直连数据一起被tls 读到了的话
|
||
// 那么 原始的数据会包含至少两个 tls record,第一个tls record 解密出来 就是我们的特殊指令,而第二个以后的 record 则是我们要直连的数据
|
||
|
||
// 我们需要从TeeConn的Recorder中读取真实的数据
|
||
|
||
return
|
||
|
||
} else {
|
||
// 服务端Write被调用,那就是 获取到了远程服务器的数据,准备发向 客户端,此时我们发送特殊命令
|
||
|
||
// 查看golang源码,可以发现握手过程之后,tls的发送是没有缓存的,只要对 tls.Conn 调用一次Write,就会调用一次或多次net.Conn.Write;
|
||
// 所以我们的 特殊指令 是完全包含在一个完整的 tls record 被发出的。
|
||
|
||
// 此处也是不需要保存 要直连发送的数据p 的,在main.go里去直接操作就行
|
||
// 我们在这里发送特殊指令即可。
|
||
|
||
n, err = dw.Writer.Write(dw.SpecialCommandBytes)
|
||
|
||
if err == nil {
|
||
n = len(p)
|
||
|
||
}
|
||
return
|
||
|
||
}
|
||
|
||
}
|
||
n, err = dw.Writer.Write(p)
|
||
|
||
if PDD {
|
||
//log.Println("DetectWriter,W, 原本", len(p), "实际写入了", n, dw.Writer)
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
func GetTlsRecordNextIndex(p []byte) int {
|
||
if len(p) < 5 {
|
||
return -1
|
||
}
|
||
|
||
supposedLen := int(p[3])<<8 + int(p[4])
|
||
|
||
return 5 + supposedLen
|
||
|
||
}
|
||
|
||
func GetLastTlsRecordTailIndex(p []byte) (last_cursor int, count int) {
|
||
|
||
//log.Println("总长", len(p))
|
||
cursor := 0
|
||
for {
|
||
if cursor > len(p) {
|
||
return
|
||
}
|
||
index := GetTlsRecordNextIndex(p[cursor:])
|
||
//log.Println("此记录index", index, last_cursor, cursor)
|
||
count++
|
||
|
||
if last_cursor > len(p) {
|
||
return
|
||
} else if index < 0 {
|
||
return
|
||
}
|
||
last_cursor = cursor
|
||
cursor += index
|
||
|
||
}
|
||
|
||
}
|