场景需求
在一个系统中,内部网络开放了Socks5代理(代理软件是V2ray),采用了账号密码的方式进行授权;在这个过程中需要对使用代理的客户端进行日志监控,主要包括来源IP、登录账号以及活动时间。
谷歌了一圈,发现Golang已经有一个很强大的Gopacket包,能对设备网卡进行直接抓包,解包等操作,而正好解决上面我的需求;但该包的功能并不仅限于此,比如拦截修改貌似也能做到,只是这里暂不需要所以没有深入。
Ps:其实要实现这个需求,也可以选择硬刚v2ray的源代码,加上需要实现的功能,然后重新编译运行也行;但是初学Golang,看了V2ray的源码 还有很多疑惑的地方,所以本文这种“笨方法”只能说是一种权宜之策吧。
抓包分析
在socks5的认证过程中,客户端会将账号密码明文形式发送给服务端,而我们要做的就是抓下这个数据包;在服务端配置的所有账号密码中,账号密码必然会带有meg
这个关键词,开放的端口是6688
,传输的数据长度必然小于128个字节。
所以过滤器规则便是:抓取所有TCP中,目的端口是6688,数据长度小于128字节,抓取之后取出TCP的承载数据并且含有关键词meg
,精准命中。
Ps:在写本文回忆的时候,突然发现此方法依然存在一些问题,比如如果有人恶意伪造数据包呢?直接拉低整个监控日志的有效性,看来选择直接修改V2ray才能避免此类情况。
源码Demo
package main
import (
"bytes"
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"log"
"time"
)
func main() {
port := "6688"
device := "eth0"
log.Println("开始监控数据:" + device)
handle, err := pcap.OpenLive(device, 1024, false, 30*time.Second)
if err != nil {
log.Fatal(err)
return
}
defer handle.Close()
// 过滤规则,指定TCP协议,指定端口
var filter = fmt.Sprintf("tcp and port %s and len <= 128", port)
err = handle.SetBPFFilter(filter)
if err != nil {
log.Fatal(err)
}
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {
// 过滤需要抓包的数据关键词,ASCII
meg := []byte{109, 101, 103}
data := packet.Data()
if !bytes.Contains(data, qimeng) {
continue
}
// 解析TCP数据
ipLayer := packet.Layer(layers.LayerTypeIPv4)
tcpLayer := packet.Layer(layers.LayerTypeTCP)
if tcpLayer == nil {
continue
}
// IP层
ip, _ := ipLayer.(*layers.IPv4)
// TCP层
tcp, _ := tcpLayer.(*layers.TCP)
// TCP负载数据
tcpData := tcpLayer.LayerPayload()
// 来源IP
srcIp := ip.SrcIP.String()
// 来源端口
srcPort := tcp.SrcPort.String()
// 解析来自 SOCKS5 客户端的账号密码数据
userLen := tcpData[1]
username := string(tcpData[2 : 2+userLen])
passLen := userLen + 2 + 1
password := string(tcpData[passLen:])
log.Printf("账号:%s(%s) 上线成功,来源地址:%s:%s", username, password, srcIp, srcPort)
}
}