• go中网络流量分析gopacket库的使用


    一、前言

            近来,笔者想学习下用go构造tcp数据包,这里涉及到gopacket库的使用,故这里记录下关于gopacket库的一些学习记录

    二、介绍

            gopacket 是 Go 语言的网络数据包处理库,它提供了方便的 API 来读取、分析、修改和生成网络数据包。你可以使用这个库来修改数据包内容,以实现特定的网络测试或安全目的。

    修改数据包的过程大致如下:

    1. 使用 gopacket 读取数据包并解码为特定协议的数据结构(如 IP、TCP 等)。

    2. 修改相应的字段,如源 IP 地址、目的 IP 地址、源端口、目的端口等。

    3. 将数据包编码为原始字节流并写回网络。

    通过 gopacket 库可以很方便地实现数据包的修改。

    三、使用

    加载:go get -u github.com/google/gopacket

    1、枚举所有网络设备

    1. func GetAllDevs() {
    2. //获取所有设备信息
    3. devices, err := pcap.FindAllDevs()
    4. if err != nil {
    5. log.Fatal(err)
    6. }
    7. //打印设备信息
    8. fmt.Println("找到的Devices 信息:")
    9. for _, device := range devices {
    10. fmt.Println("\n名字:", device.Name)
    11. fmt.Println("描述信息:", device.Description)
    12. fmt.Println("Devices 地址信息:", device.Addresses) //网口地址信息列表包括IP 、 掩码、 广播地址 、P2P
    13. for _, address := range device.Addresses {
    14. fmt.Println("- IP 地址为:", address.IP)
    15. fmt.Println("- 掩码为:", address.Netmask)
    16. fmt.Println("- 广播地址为:", address.Broadaddr)
    17. fmt.Println("- P2P地址为:", address.P2P)
    18. fmt.Println()
    19. }
    20. }
    21. }
    22. //获取所有有IP的网络设备名称
    23. func GetAllDevsHaveAddress() (string, error) {
    24. pcapDevices, err := pcap.FindAllDevs()
    25. if err != nil {
    26. return "", errors.New(fmt.Sprintf("list pcapDevices failed: %s", err.Error()))
    27. }
    28. var buf strings.Builder
    29. for _, dev := range pcapDevices {
    30. fmt.Sprint("Dev:", dev.Name, "\tDes:", dev.Description)
    31. buf.WriteString(dev.Name)
    32. if len(dev.Addresses) > 0 {
    33. for _, ips := range dev.Addresses {
    34. fmt.Sprint("\tAddr:", ips.IP.String())
    35. //buf.WriteString(ips.IP.String())
    36. }
    37. }
    38. buf.WriteString("\n")
    39. }
    40. return buf.String(), nil
    41. }
    42. //通过IP获取设备名称
    43. func GetDevByIp(ip net.IP) (devName string, err error) {
    44. devices, err := pcap.FindAllDevs()
    45. if err != nil {
    46. return
    47. }
    48. for _, d := range devices {
    49. for _, address := range d.Addresses {
    50. _ip := address.IP.To4()
    51. if _ip != nil && _ip.IsGlobalUnicast() && _ip.Equal(ip) {
    52. return d.Name, nil
    53. }
    54. }
    55. }
    56. return "", errors.New("can not find dev")
    57. }

    2、获取活动网卡IP、网卡MAC、网关、网关mac、活动网卡设备名称

             这里主要演示,根据目标IP获取活动网卡的IP、网卡mac、网关、网关mac,然后通过IP获取设备名称

    1. package example
    2. import (
    3. "errors"
    4. "fmt"
    5. "net"
    6. "github.com/google/gopacket/pcap"
    7. "github.com/google/gopacket/routing"
    8. "github.com/jackpal/gateway"
    9. "github.com/libp2p/go-netroute"
    10. )
    11. // 查找与指定IP地址相匹配的本地IP地址和MAC地址,其实就是检测是否同网段
    12. func GetIfaceMac(ifaceAddr net.IP) (src net.IP, mac net.HardwareAddr) {
    13. interfaces, _ := net.Interfaces() //获取本地计算机上的网络接口信息
    14. for _, iface := range interfaces { //遍历所有网络接口
    15. if addrs, err := iface.Addrs(); err == nil { //获取接口的IP地址列表
    16. for _, addr := range addrs { //遍历该接口的IP地址
    17. if addr.(*net.IPNet).Contains(ifaceAddr) { //检查每个IP地址是否包含了给定的ifaceAddr(即传参),这里用的contains方法,检查给定IP和接口IP是否同一子网
    18. return addr.(*net.IPNet).IP, iface.HardwareAddr //匹配就返回接口IP和mac地址
    19. }
    20. }
    21. }
    22. }
    23. return nil, nil
    24. }
    25. // 通过IP获取网络设备名称
    26. func GetDevByIp(ip net.IP) (devName string, err error) {
    27. devices, err := pcap.FindAllDevs() //获取所有网络设备
    28. if err != nil {
    29. return
    30. }
    31. for _, d := range devices {
    32. for _, address := range d.Addresses {
    33. _ip := address.IP.To4() //检测IP是否位ipv4地址
    34. if _ip != nil && _ip.IsGlobalUnicast() && _ip.Equal(ip) { //满足地址为ipv4,地址等于传入IP、地址是全局单播地址 三个条件则符合
    35. return d.Name, nil
    36. }
    37. }
    38. }
    39. return "", errors.New("未能找到对应设备")
    40. }
    41. // 通过扫描的目标IP获取发包的网卡信息,返回源IP、源mac、网关IP、设备名称
    42. func GetIpFromRouter(dstIp net.IP) (srcIp net.IP, srcMac net.HardwareAddr, gw net.IP, devName string, err error) {
    43. //先验证扫描IP是否同网段
    44. srcIp, srcMac = GetIfaceMac(dstIp)
    45. if srcIp == nil {
    46. //如果不是同网段,则查询路由
    47. var r routing.Router//创建一个 routing.Router 类型的变量,用于查询路由信息
    48. r, err = netroute.New() //初始化routing.Router
    49. if err == nil {
    50. var iface *net.Interface //创建变量iface用于保存与路由相关的网络接口信息
    51. iface, gw, srcIp, err = r.Route(dstIp) //通过路由查询路由信息,包括目标IP地址 dstIp 对应的路由信息。iface 保存了与该路由信息关联的网络接口,gw 保存了网关IP地址,srcIp 保存了与该路由信息关联的本地IP地址
    52. if err == nil {
    53. if iface != nil {
    54. srcMac = iface.HardwareAddr //如果找到了与目标IP地址匹配的路由信息,即 iface 不为 nil,则设置 srcMac 为该网络接口的MAC地址。否则,继续下一步
    55. } else {
    56. _, srcMac = GetIfaceMac(srcIp)
    57. }
    58. }
    59. }
    60. //如果在之前的步骤中出现错误或者 srcMac 为 nil,它尝试取得第一个默认网关的信息。
    61. if err != nil || srcMac == nil {
    62. //取第一个默认路由
    63. gw, err = gateway.DiscoverGateway()//获取第一个默认网关的IP地址
    64. if err == nil {
    65. srcIp, srcMac = GetIfaceMac(gw)
    66. }
    67. }
    68. }
    69. gw = gw.To4()
    70. srcIp = srcIp.To4()
    71. devName, err = GetDevByIp(srcIp)
    72. if srcIp == nil || err != nil || srcMac == nil {
    73. if err == nil {
    74. err = fmt.Errorf("err")
    75. }
    76. return nil, nil, nil, "", fmt.Errorf("no router,%s", err)
    77. }
    78. return
    79. }

    ps:笔者之前的思路想岔了,执迷于先获取到活动网卡的IP然后通过IP再去获取网卡设备名称,后来反省过来,针对扫描来说是通过目标IP来获取源IP,源mac,网卡信息 

    3、抓取网卡数据包

    1. var (
    2. device string = "\\Device\\NPF_{111223-123344}" //网卡名称,也可使用FindAllDevs函数获得网卡名称,比如\NPF_{6A5F41。。。}
    3. snapshot_len int32 = 1024 //每个数据包读取的最大长度
    4. promiscuous bool = false //是否将网口设置为混杂模式,即是否接收目的不为本机的包
    5. err error
    6. timeout time.Duration = 30 * time.Second //设置抓包返回的超时时间,如果设置成30s,即每30s刷新下数据包,设置为负数,就立即刷新数据包
    7. handle *pcap.Handle //是一个*Handle类型的返回值,可能作为gopacket其他函数调用时作为函数参数来传递 (一定记得释放掉)
    8. )
    9. func GetPacp() {
    10. handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout) //打开网络设备并进行实时捕获数据包
    11. if err != nil {
    12. log.Fatal(err)
    13. }
    14. defer handle.Close() //最后一定要关闭handle
    15. packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) //第一个参数为OpenLive的返回值,指向Handle类型的指针变量handle。第二个参数为handle.LinkType()此参数默认是以太网链路,一般我们抓包,也是从2层以太网链路上抓取。
    16. //读取数据包,packetSource.Packets()是个channel类型,此处是从channel类型的数据通道中持续的读取网络数据包
    17. for packet := range packetSource.Packets() {
    18. fmt.Println(packet)
    19. }
    20. }

    4、解码数据包各layer内容

    1. import (
    2. "fmt"
    3. "log"
    4. "github.com/google/gopacket"
    5. "github.com/google/gopacket/layers"
    6. "github.com/google/gopacket/pcap"
    7. )
    8. func DecodeLayers() {
    9. //打开device
    10. handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
    11. if err != nil {
    12. log.Fatal(err)
    13. }
    14. defer handle.Close()
    15. packetSource2 := gopacket.NewPacketSource(handle, handle.LinkType())
    16. for packet := range packetSource2.Packets() {
    17. printPacketInfo(packet)
    18. }
    19. }
    20. func printPacketInfo(packet gopacket.Packet) {
    21. // 判断数据包是否为以太网数据包,可解析出源mac地址、目的mac地址、以太网类型(如ip类型)等
    22. ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
    23. if ethernetLayer != nil {
    24. fmt.Println("检测到以太网数据包")
    25. ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)
    26. fmt.Println("源 Mac 地址为:", ethernetPacket.SrcMAC)
    27. fmt.Println("目的 Mac地址为:", ethernetPacket.DstMAC)
    28. //以太网类型通常是 IPv4,但也可以是 ARP 或其他
    29. fmt.Println("以太网类型为:", ethernetPacket.EthernetType)
    30. fmt.Println(ethernetPacket.Payload)
    31. fmt.Println()
    32. }
    33. // 判断数据包是否为IP数据包,可解析出源ip、目的ip、协议号等
    34. ipLayer := packet.Layer(layers.LayerTypeIPv4) //这里抓取ipv4的数据包
    35. if ipLayer != nil {
    36. fmt.Println("检测到IP层数据包")
    37. ip, _ := ipLayer.(*layers.IPv4)
    38. // IP layer variables:
    39. // Version (Either 4 or 6)
    40. // IHL (IP Header Length in 32-bit words)
    41. // TOS, Length, Id, Flags, FragOffset, TTL, Protocol (TCP?),
    42. //Checksum,SrcIP, DstIP
    43. fmt.Printf("源ip为 %d 目的ip为 %d\n", ip.SrcIP, ip.DstIP)
    44. fmt.Println("协议版本为:", ip.Version)
    45. fmt.Println("首部长度为:", ip.IHL)
    46. fmt.Println("区分服务为:", ip.TOS)
    47. fmt.Println("总长度为:", ip.Length)
    48. fmt.Println("标识id为:", ip.Id)
    49. fmt.Println("标志为:", ip.Flags)
    50. fmt.Println("片偏移", ip.FragOffset)
    51. fmt.Println("TTL", ip.TTL)
    52. fmt.Println("协议Protocol为:", ip.Protocol)
    53. fmt.Println("校验和为:", ip.Checksum)
    54. fmt.Println("基础层", ip.BaseLayer)
    55. fmt.Println("内容contents:", ip.Contents)
    56. fmt.Println("可选字段为:", ip.Options)
    57. fmt.Println("填充为:", ip.Padding)
    58. fmt.Println()
    59. }
    60. // 判断数据包是否为TCP数据包,可解析源端口、目的端口、seq序列号、tcp标志位等
    61. tcpLayer := packet.Layer(layers.LayerTypeTCP)
    62. if tcpLayer != nil {
    63. fmt.Println("检测到tcp数据包")
    64. tcp, _ := tcpLayer.(*layers.TCP)
    65. // SrcPort, DstPort, Seq, Ack, DataOffset, Window, Checksum, Urgent
    66. // Bool flags: FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS
    67. fmt.Printf("源端口为 %d 目的端口为 %d\n", tcp.SrcPort, tcp.DstPort)
    68. fmt.Println("序列号为:", tcp.Seq)
    69. fmt.Println("确认号为:", tcp.Ack)
    70. fmt.Println("数据偏移量:", tcp.DataOffset)
    71. fmt.Println("标志位:", tcp.CWR, tcp.ECE, tcp.URG, tcp.ACK, tcp.PSH, tcp.RST, tcp.SYN, tcp.FIN)
    72. fmt.Println("窗口大小:", tcp.Window)
    73. fmt.Println("校验值:", tcp.Checksum)
    74. fmt.Println("紧急指针:", tcp.Urgent)
    75. fmt.Println("tcp选项:", tcp.Options)
    76. fmt.Println("填充:", tcp.Padding)
    77. fmt.Println()
    78. }
    79. //检测数据包中所有的层数
    80. fmt.Println("所有 数据包 layer有:")
    81. for _, layer := range packet.Layers() {
    82. fmt.Println("--", layer.LayerType())
    83. }
    84. //检测判断layer是否存在错误
    85. if err := packet.ErrorLayer(); err != nil {
    86. fmt.Println("解码数据包的某些部分出错:", err)
    87. }
    88. }

  • 相关阅读:
    【C++】-C++11中的知识点(上)--右值引用,列表初始化,声明
    MySQL 数据类型以及最优选取规则
    rsyslog-日志管理 logrotate-日志轮转
    原生小程序一键获取手机号
    Nginx监控与告警:确保服务稳定运行
    遇到Bug漏测,不能总想着甩锅吧
    UniApp状态管理:从深入理解到灵活运用
    Turbo译码部分的迭代停止准则
    springboot采用协同过滤算法的家政服务平台的设计与实现毕业设计源码260839
    微信小程序03-文字一左一右显示,行内块元素居中
  • 原文地址:https://blog.csdn.net/youuzi/article/details/134059230