zoukankan      html  css  js  c++  java
  • 使用gopacket 解析一个简单的sql server 协议

    这篇应该说是属于基于gopacket 分析sql server 数据包的一个简单测试(没什么技术含量,大部分关于sql server解析的还在测试)

    预备知识

    sql server使用的是tds协议,这个协议在微软的官方能看到相关的技术文档,我们可以参考技术文档分析以及学习协议,通过
    wireshark也是一个很不错的学习tds 的方式

    环境准备

    • go 项目
     
    go mod init appdemo
    • 添加gopacket包
    go get github.com/google/gopacket
    go get gopkg.in/alecthomas/kingpin.v2
    • 主要代码
    package main
    import (
        "fmt"
        "unicode/utf16"
        "encoding/binary"
        "encoding/hex"
        "github.com/google/gopacket"
        "github.com/google/gopacket/layers"
        "github.com/google/gopacket/pcap"
        "gopkg.in/alecthomas/kingpin.v2"
    )
    var (
        device = kingpin.Flag("device", "device  for capture").Default("en0").String()
    )
    func main() {
        kingpin.Parse()
        sqlserverOffline()
    }
    func sqlserverOffline() {
        port := uint16(1433)
        filter := getFilter(port)
        // 使用离线模式
        handle, err := pcap.OpenOffline("packets/appdemochai.pcapng")
        if err != nil {
            fmt.Printf("pcap open live failed: %v", err)
            return
        }
        // 设置过滤模式
        if err := handle.SetBPFFilter(filter); err != nil {
            fmt.Printf("set bpf filter failed: %v", err)
            return
        }
        defer handle.Close()
        packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
        packetSource.NoCopy = true
        for packet := range packetSource.Packets() {
            printPacketInfo(packet)
        }
    }
    // ucs 数据类型转换,tds 7.x 之后基于ucs支持unicode
    func ucs22str2(s []byte) (string, error) {
        if len(s)%2 != 0 {
            return "", fmt.Errorf("Illegal UCS2 string length: %d", len(s))
        }
        buf := make([]uint16, len(s)/2)
        for i := 0; i < len(s); i += 2 {
           // 注意此处使用小端模式
            buf[i/2] = binary.LittleEndian.Uint16(s[i:])
        }
        return string(utf16.Decode(buf)), nil
    }
    // 打印每层关于数据处理的信息,此处写死了一个sql 解析的处理,主要测试下中文的
    func printPacketInfo(packet gopacket.Packet) {
        // Let's see if the packet is an ethernet packet
        ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
        if ethernetLayer != nil {
            fmt.Println("=====================================")
            fmt.Println("Ethernet layer detected.")
            ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)
            fmt.Println("Source MAC: ", ethernetPacket.SrcMAC)
            fmt.Println("Destination MAC: ", ethernetPacket.DstMAC)
            fmt.Println("Ethernet type: ", ethernetPacket.EthernetType)
            fmt.Printf("%s
    ", hex.Dump(ethernetPacket.Payload))
            fmt.Println("=====================================")
        }
        ipLayer := packet.Layer(layers.LayerTypeIPv4)
        if ipLayer != nil {
            fmt.Println("=====================================")
            fmt.Println("IPv4 layer detected.")
            ip, _ := ipLayer.(*layers.IPv4)
            fmt.Printf("From %s to %s
    ", ip.SrcIP, ip.DstIP)
            fmt.Println("Protocol: ", ip.Protocol)
            fmt.Printf("%s
    ", hex.Dump(ip.Payload))
            fmt.Println("=====================================")
        }
        tcpLayer := packet.Layer(layers.LayerTypeTCP)
        if tcpLayer != nil {
            fmt.Println("=====================================")
            fmt.Println("TCP layer detected.")
            tcp, _ := tcpLayer.(*layers.TCP)
            fmt.Printf("From port %d to %d
    ", tcp.SrcPort, tcp.DstPort)
            fmt.Println("Sequence number: ", tcp.Seq)
            fmt.Printf("%s
    ", hex.Dump(tcp.Payload))
            fmt.Println("=====================================")
        }
        applicationLayer := packet.ApplicationLayer()
        if applicationLayer != nil {
            fmt.Println("=====================================")
            fmt.Println("Application layer/Payload found.")
            fmt.Printf("%s
    ", hex.Dump(applicationLayer.Payload()))
            // just for test one pacget with 152 length for fetch dbquery
            if len(applicationLayer.Payload()) == 152 {
                packettype := applicationLayer.Payload()[:1][0]
                // tds rpc pacakgetype
                if packettype == 3 {
                    fmt.Println("============do rpc  query====================")
                    // using slice for demo fetch datas
                    sqltext := applicationLayer.Payload()[57:125]
                    sql, _ := ucs22str2(sqltext)
                    fmt.Printf("%s
    ", sql)
                }
            }
        }
        if err := packet.ErrorLayer(); err != nil {
            fmt.Println("Error decoding some part of the packet:", err)
        }
    }
    func getFilter(port uint16) string {
        filter := fmt.Sprintf("tcp and ((src port %v) or (dst port %v))", port, port)
        return filter
    }
    • 代码说明
      gopacket 支持离线以及实时的数据处理,以上使用了离线模式,gopacket处理数据的套路:
     
    pcap.OpenOffline or pcap.OpenLive 处理离线以及实时处理
    handle.SetBPFFilter(filter) 配置过滤,处理我们关注的数据
    gopacket.NewPacketSource 处理我们获取到的数据,使用了通道进行数据传输,我们可以获取通道的数据
    ethernetLayer := packet.Layer(layers.LayerTypeEthernet) 处理各层数据的解码,我们可以结合tcp/ip 的分层模型,进行每层数据处理
    applicationLayer := packet.ApplicationLayer() 应用层数据处理,因为我们的sql server 是处于应用层的协议,我们可以在此处理数据
    • 简单关于tds说明
      tds 协议的pacaket包含两部分,packet header (8个字节)以及packet data(通过header 的packet length 计算获取),packet header 主要包含了关于sql 的操作(packet 类型),数据大小,数据状态。。。
      packet data 是我们的核心部分,对于数据的处理我们需要结合实际的tds 规范处理,上边的处理就是一个简单的对于rpc 请求的处理,rpc 的类型
      为3,同时对于字符串的处理,需要使用小端模式,而且tds 7.x 使用ucs 支持unicde 所以包装了一个ucs 类型转换的处理
    • 运行效果

    截取部分关于sql 中文处理的

    说明

    以上处理的很简单,对于实际集合gopacket 我们可以包装一个自定义的层(专门解析sql server 数据包),这样代码以及扩展性上就比较好了

    参考资料

    https://github.com/google/gopacket
    https://colobu.com/2019/06/01/packet-capture-injection-and-analysis-gopacket/
    链接: https://pan.baidu.com/s/1Yll128f0vQLqMKW7PL6QNQ 密码: if9n

  • 相关阅读:
    一些比较水的题目
    oracle not in,not exists,minus 数据量大的时候的性能问题
    简单的oracle分页语句
    oracle 查询结果集运算
    Spring注解详解
    HTTP报头Accept 和 Content-Type的区别
    vue 实现分转元的 过滤器
    oracle or语句的坑
    CSS样式 让你的输入的小写自动变成大写。
    js 十分精确身份证验证
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/13745027.html
Copyright © 2011-2022 走看看