MENU

使用 Go 对TCP数据进行抓包分析

July 21, 2021 • Read: 3244 • 技术杂谈

场景需求

在一个系统中,内部网络开放了Socks5代理(代理软件是V2ray),采用了账号密码的方式进行授权;在这个过程中需要对使用代理的客户端进行日志监控,主要包括来源IP、登录账号以及活动时间。

谷歌了一圈,发现Golang已经有一个很强大的Gopacket包,能对设备网卡进行直接抓包,解包等操作,而正好解决上面我的需求;但该包的功能并不仅限于此,比如拦截修改貌似也能做到,只是这里暂不需要所以没有深入。

Ps:其实要实现这个需求,也可以选择硬刚v2ray的源代码,加上需要实现的功能,然后重新编译运行也行;但是初学Golang,看了V2ray的源码 还有很多疑惑的地方,所以本文这种“笨方法”只能说是一种权宜之策吧。

抓包分析

在socks5的认证过程中,客户端会将账号密码明文形式发送给服务端,而我们要做的就是抓下这个数据包;在服务端配置的所有账号密码中,账号密码必然会带有meg这个关键词,开放的端口是6688,传输的数据长度必然小于128个字节。

所以过滤器规则便是:抓取所有TCP中,目的端口是6688,数据长度小于128字节,抓取之后取出TCP的承载数据并且含有关键词meg,精准命中。

socks 认证.png

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)
    }
}

参考文章

  1. https://colobu.com/2019/06/01/packet-capture-injection-and-analysis-gopacket/
  2. https://juejin.cn/post/6844903923518537741