马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
详解 github.com/google/gopacket/pcap 包
github.com/google/gopacket/pcap 是 Go 语言中一个强大的网络数据包捕获库,它是 gopacket 项目标一部分,提供了对 libpcap(Linux/Unix)和 WinPcap(Windows)的 Go 语言绑定,用于及时网络数据包捕获和分析。
核心功能
- 及时网络数据包捕获
- 过滤网络流量(BPF过滤器)
- 读取息争析pcap文件
- 统计网络接口信息
基本使用
1. 安装
- go get github.com/google/gopacket
- go get github.com/google/gopacket/pcap
复制代码 2. 获取网络接口列表
- devices, err := pcap.FindAllDevs()
- if err != nil {
- log.Fatal(err)
- }
- for _, device := range devices {
- fmt.Printf("Device: %s\n", device.Name)
- fmt.Printf("Description: %s\n", device.Description)
- fmt.Printf("Flags: %d\n", device.Flags)
- for _, address := range device.Addresses {
- fmt.Printf("\tIP: %s\n", address.IP)
- fmt.Printf("\tNetmask: %s\n", address.Netmask)
- }
- }
复制代码 3. 打开网络接口进行捕获
- handle, err := pcap.OpenLive(
- "eth0", // 接口名
- 65536, // 最大包长度
- true, // 是否启用混杂模式
- pcap.BlockForever, // 超时时间
- )
- if err != nil {
- log.Fatal(err)
- }
- defer handle.Close()
复制代码 4. 设置BPF过滤器
- err = handle.SetBPFFilter("tcp and port 80")
- if err != nil {
- log.Fatal(err)
- }
复制代码 5. 读取数据包
- packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
- for packet := range packetSource.Packets() {
- // 处理每个数据包
- fmt.Println(packet)
- }
复制代码 高级功能
1. 剖析数据包
- // 解析以太网层
- ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
- if ethernetLayer != nil {
- ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)
- fmt.Println("Source MAC: ", ethernetPacket.SrcMAC)
- fmt.Println("Destination MAC: ", ethernetPacket.DstMAC)
- }
- // 解析IP层
- ipLayer := packet.Layer(layers.LayerTypeIPv4)
- if ipLayer != nil {
- ip, _ := ipLayer.(*layers.IPv4)
- fmt.Println("Source IP: ", ip.SrcIP)
- fmt.Println("Destination IP: ", ip.DstIP)
- }
- // 解析TCP层
- tcpLayer := packet.Layer(layers.LayerTypeTCP)
- if tcpLayer != nil {
- tcp, _ := tcpLayer.(*layers.TCP)
- fmt.Println("Source Port: ", tcp.SrcPort)
- fmt.Println("Destination Port: ", tcp.DstPort)
- }
复制代码 2. 写入pcap文件
- handle, err := pcap.OpenDead(layers.LinkTypeEthernet, 65536)
- if err != nil {
- log.Fatal(err)
- }
- defer handle.Close()
- writer, err := pcap.NewWriter(handle, "output.pcap")
- if err != nil {
- log.Fatal(err)
- }
- defer writer.Close()
- // 创建并写入数据包
- // ... (创建数据包代码)
- err = writer.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
- if err != nil {
- log.Fatal(err)
- }
复制代码 3. 读取pcap文件
- handle, err := pcap.OpenOffline("input.pcap")
- if err != nil {
- log.Fatal(err)
- }
- defer handle.Close()
- packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
- for packet := range packetSource.Packets() {
- fmt.Println(packet)
- }
复制代码 性能优化
- 重用缓冲区:淘汰内存分配
- var buf [4096]byte
- for {
- data, ci, err := handle.ReadPacketData()
- if err != nil {
- continue
- }
- copy(buf[:], data)
- // 处理数据
- }
复制代码 - 零拷贝处理:直接操作原始数据
- packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
复制代码 - 并发处理:使用多个goroutine处理数据包
- packets := make(chan gopacket.Packet, 1000)
- go func() {
- for packet := range packetSource.Packets() {
- packets <- packet
- }
- close(packets)
- }()
- for i := 0; i < runtime.NumCPU(); i++ {
- go func() {
- for packet := range packets {
- // 处理数据包
- }
- }()
- }
复制代码 常见题目
- 权限题目:需要root或管理员权限才气捕获网络数据包
- 接口不可用:确保接口名称精确且处于运动状态
- 过滤器语法错误:BPF过滤器需要精确语法
- 内存走漏:确保及时关闭handle和释放资源
实际应用示例
简单的HTTP哀求捕获
- handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever)if err != nil { log.Fatal(err)}defer handle.Close()err = handle.SetBPFFilter("tcp and port 80")
- if err != nil {
- log.Fatal(err)
- }
- packetSource := gopacket.NewPacketSource(handle, handle.LinkType())for packet := range packetSource.Packets() { appLayer := packet.ApplicationLayer() if appLayer != nil { payload := appLayer.Payload() if bytes.Contains(payload, []byte("HTTP")) { fmt.Printf("%s\n", payload) } }}
复制代码 pcap 包是 Go 中网络监控和安全工具开发的基础,结合 gopacket 的其他组件可以构建强大的网络分析工具。
使用 github.com/google/gopacket/pcap 剖析 DNS 哀求和响应
github.com/google/gopacket/pcap 可以捕获息争析 DNS 哀求和响应,但需要结合 gopacket/layers 包中的 DNS 层剖析功能。
1. 基本 DNS 剖析设置
首先需要导入必要的包:
- import (
- "github.com/google/gopacket"
- "github.com/google/gopacket/layers" // 包含DNS层定义
- "github.com/google/gopacket/pcap"
- )
复制代码 2. 捕获 DNS 流量的配置
方法一:捕获全部DNS流量(端口53)
- handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever)
- if err != nil {
- panic(err)
- }
- defer handle.Close()
- // 设置BPF过滤器捕获DNS流量(UDP和TCP端口53)
- err = handle.SetBPFFilter("udp port 53 or tcp port 53")
- if err != nil {
- panic(err)
- }
复制代码 方法二:区分哀求和响应
- // 捕获发往DNS服务器的请求
- err = handle.SetBPFFilter("dst port 53")
- // 或者捕获来自DNS服务器的响应
- err = handle.SetBPFFilter("src port 53")
复制代码 3. 剖析DNS数据包
- packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
- for packet := range packetSource.Packets() {
- // 检查是否包含DNS层
- dnsLayer := packet.Layer(layers.LayerTypeDNS)
- if dnsLayer != nil {
- dns, _ := dnsLayer.(*layers.DNS)
- analyzeDNS(dns) // 自定义解析函数
- }
- }
复制代码 4. 详细DNS剖析函数
- func analyzeDNS(dns *layers.DNS) {
- // 判断是请求还是响应
- if dns.QR {
- fmt.Println("[DNS Response]")
- } else {
- fmt.Println("[DNS Query]")
- }
-
- // 打印基本信息
- fmt.Printf("ID: %d, OpCode: %s, RecursionDesired: %t\n",
- dns.ID, dns.OpCode, dns.RD)
-
- // 解析问题部分(查询的问题)
- for _, question := range dns.Questions {
- fmt.Printf("Query: %s (Type: %s, Class: %s)\n",
- string(question.Name),
- question.Type,
- question.Class)
- }
-
- // 解析回答部分(响应的资源记录)
- for _, answer := range dns.Answers {
- fmt.Printf("Answer: %s -> %s (Type: %s, TTL: %d)\n",
- string(answer.Name),
- getAnswerValue(answer), // 自定义函数获取值
- answer.Type,
- answer.TTL)
- }
-
- // 解析权威名称服务器部分
- for _, ns := range dns.Authorities {
- fmt.Printf("Authority: %s -> %s\n",
- string(ns.Name),
- getAnswerValue(ns))
- }
-
- // 解析附加记录部分
- for _, extra := range dns.Additionals {
- fmt.Printf("Additional: %s -> %s\n",
- string(extra.Name),
- getAnswerValue(extra))
- }
- }
- // 辅助函数:根据类型获取DNS记录的值
- func getAnswerValue(answer layers.DNSResourceRecord) string {
- switch answer.Type {
- case layers.DNSTypeA:
- return answer.IP.String()
- case layers.DNSTypeAAAA:
- return answer.IP.String()
- case layers.DNSTypeCNAME:
- return string(answer.CNAME)
- case layers.DNSTypeMX:
- return fmt.Sprintf("%s (pref %d)", string(answer.MX), answer.Preference)
- case layers.DNSTypeNS:
- return string(answer.NS)
- case layers.DNSTypeTXT:
- return string(answer.TXT)
- case layers.DNSTypeSOA:
- return fmt.Sprintf("MName: %s, RName: %s", string(answer.SOA.MName), string(answer.SOA.RName))
- default:
- return fmt.Sprintf("[Unhandled Type %d]", answer.Type)
- }
- }
复制代码 5. 处理TCP DNS流量
DNS通常使用UDP,但大响应大概使用TCP:
- // 在分析函数中添加TCP处理
- tcpLayer := packet.Layer(layers.LayerTypeTCP)
- if tcpLayer != nil {
- tcp, _ := tcpLayer.(*layers.TCP)
- if tcp.SYN {
- // TCP握手开始
- } else if len(tcp.Payload) > 0 {
- // 尝试解析TCP负载中的DNS
- dns := &layers.DNS{}
- err := dns.DecodeFromBytes(tcp.Payload, gopacket.NilDecodeFeedback)
- if err == nil {
- analyzeDNS(dns)
- }
- }
- }
复制代码 6. 完整示例:DNS监控工具
- package main
- import (
- "fmt"
- "log"
- "time"
-
- "github.com/google/gopacket"
- "github.com/google/gopacket/layers"
- "github.com/google/gopacket/pcap"
- )
- func main() {
- // 1. 获取网络设备
- devices, err := pcap.FindAllDevs()
- if err != nil {
- log.Fatal(err)
- }
-
- // 打印可用设备
- fmt.Println("Available devices:")
- for _, dev := range devices {
- fmt.Printf("- %s", dev.Name)
- if dev.Description != "" {
- fmt.Printf(" (%s)", dev.Description)
- }
- fmt.Println()
- }
-
- // 2. 打开设备
- var deviceName = "eth0" // 根据实际情况修改
- var snapshotLen int32 = 1024
- var promiscuous = true
- var timeout = 30 * time.Second
-
- handle, err := pcap.OpenLive(deviceName, snapshotLen, promiscuous, timeout)
- if err != nil {
- log.Fatal(err)
- }
- defer handle.Close()
-
- // 3. 设置过滤器
- var filter = "udp port 53 or tcp port 53"
- err = handle.SetBPFFilter(filter)
- if err != nil {
- log.Fatal(err)
- }
-
- // 4. 处理数据包
- packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
- for packet := range packetSource.Packets() {
- processPacket(packet)
- }
- }
- func processPacket(packet gopacket.Packet) {
- // 检查DNS层
- dnsLayer := packet.Layer(layers.LayerTypeDNS)
- if dnsLayer != nil {
- dns, _ := dnsLayer.(*layers.DNS)
- printDNSInfo(dns)
- }
-
- // 检查TCP DNS
- tcpLayer := packet.Layer(layers.LayerTypeTCP)
- if tcpLayer != nil {
- tcp, _ := tcpLayer.(*layers.TCP)
- if len(tcp.Payload) > 0 && (tcp.SrcPort == 53 || tcp.DstPort == 53) {
- dns := &layers.DNS{}
- err := dns.DecodeFromBytes(tcp.Payload, gopacket.NilDecodeFeedback)
- if err == nil {
- printDNSInfo(dns)
- }
- }
- }
- }
- func printDNSInfo(dns *layers.DNS) {
- // 打印基本信息
- direction := "Query"
- if dns.QR {
- direction = "Response"
- }
-
- fmt.Printf("\n=== %s ===\n", direction)
- fmt.Printf("Transaction ID: 0x%x\n", dns.ID)
- fmt.Printf("Flags: %s (QR: %t, OpCode: %s, AA: %t, TC: %t, RD: %t, RA: %t, Z: %d, RCode: %s)\n",
- dns.Flags, dns.QR, dns.OpCode, dns.AA, dns.TC, dns.RD, dns.RA, dns.Z, dns.ResponseCode)
-
- // 打印问题部分
- if len(dns.Questions) > 0 {
- fmt.Println("\nQuestions:")
- for _, q := range dns.Questions {
- fmt.Printf("- %s (Type: %s, Class: %s)\n",
- string(q.Name), q.Type, q.Class)
- }
- }
-
- // 打印回答部分
- if len(dns.Answers) > 0 {
- fmt.Println("\nAnswers:")
- for _, a := range dns.Answers {
- fmt.Printf("- %s: %s (Type: %s, TTL: %d)\n",
- string(a.Name), getDNSRecordValue(a), a.Type, a.TTL)
- }
- }
-
- // 打印权威部分
- if len(dns.Authorities) > 0 {
- fmt.Println("\nAuthorities:")
- for _, a := range dns.Authorities {
- fmt.Printf("- %s: %s (Type: %s, TTL: %d)\n",
- string(a.Name), getDNSRecordValue(a), a.Type, a.TTL)
- }
- }
-
- // 打印附加部分
- if len(dns.Additionals) > 0 {
- fmt.Println("\nAdditionals:")
- for _, a := range dns.Additionals {
- fmt.Printf("- %s: %s (Type: %s, TTL: %d)\n",
- string(a.Name), getDNSRecordValue(a), a.Type, a.TTL)
- }
- }
- }
- func getDNSRecordValue(r layers.DNSResourceRecord) string {
- switch r.Type {
- case layers.DNSTypeA:
- return r.IP.String()
- case layers.DNSTypeAAAA:
- return r.IP.String()
- case layers.DNSTypeCNAME:
- return string(r.CNAME)
- case layers.DNSTypeMX:
- return fmt.Sprintf("%s (pref %d)", string(r.MX), r.Preference)
- case layers.DNSTypeNS:
- return string(r.NS)
- case layers.DNSTypePTR:
- return string(r.PTR)
- case layers.DNSTypeSOA:
- return fmt.Sprintf("MName: %s, RName: %s", string(r.SOA.MName), string(r.SOA.RName))
- case layers.DNSTypeTXT:
- return string(r.TXT)
- case layers.DNSTypeSRV:
- return fmt.Sprintf("Target: %s, Port: %d, Priority: %d, Weight: %d",
- string(r.SRV.Name), r.SRV.Port, r.SRV.Priority, r.SRV.Weight)
- default:
- return fmt.Sprintf("[Type %d Data]", r.Type)
- }
- }
复制代码 注意事项
- 权限要求:需要root或管理员权限才气捕获网络数据包
- 性能考虑:高流量环境下大概需要优化处理逻辑
- DNS over HTTPS/TLS:这种方法无法剖析DoH/DoT加密的DNS流量
- EDNS扩展:假如需要剖析EDNS扩展信息,需要额外处理OPT记录
通过这种组合使用 pcap 和 layers 包,你可以构建功能强大的DNS监控和分析工具,可以或许详细剖析DNS协议的各种细节。
从 layers.DNS 中获取域名和对应 IP 的方法
要从 layers.DNS 数据包中提取域名和对应的 IP 地点,你需要检查 DNS 响应中的回答部分 (Answers)。以下是详细方法和示例代码:
基本方法
- 检查 DNS 响应:确保是 DNS 响应 (QR == true)
- 遍历回答记录:检查 Answers 切片
- 过滤 A 和 AAAA 记录:获取 IPv4 和 IPv6 地点
- 剖析记录数据:将二进制数据转换为可读格式
示例代码
- package main
- import (
- "fmt"
- "github.com/google/gopacket"
- "github.com/google/gopacket/layers"
- "github.com/google/gopacket/pcap"
- "net"
- )
- func main() {
- // 打开网络接口
- handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever)
- if err != nil {
- panic(err)
- }
- defer handle.Close()
-
- // 设置过滤器只捕获 DNS 响应
- err = handle.SetBPFFilter("udp and port 53")
- if err != nil {
- panic(err)
- }
-
- // 开始捕获数据包
- packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
- for packet := range packetSource.Packets() {
- // 检查是否包含 DNS 层
- dnsLayer := packet.Layer(layers.LayerTypeDNS)
- if dnsLayer == nil {
- continue
- }
-
- dns, _ := dnsLayer.(*layers.DNS)
-
- // 只处理响应包
- if !dns.QR {
- continue
- }
-
- // 遍历所有回答记录
- for _, answer := range dns.Answers {
- var ip string
-
- // 根据记录类型处理
- switch answer.Type {
- case layers.DNSTypeA:
- // IPv4 地址
- if len(answer.Data) == net.IPv4len {
- ip = net.IP(answer.Data).String()
- }
- case layers.DNSTypeAAAA:
- // IPv6 地址
- if len(answer.Data) == net.IPv6len {
- ip = net.IP(answer.Data).String()
- }
- default:
- continue // 跳过非IP记录
- }
-
- if ip != "" {
- fmt.Printf("域名: %s, IP: %s, TTL: %d\n",
- string(answer.Name), ip, answer.TTL)
- }
- }
- }
- }
复制代码 更完整的处理函数
下面是一个更完整的函数,可以处理各种情况:
- func extractDomainsAndIPs(dns *layers.DNS) map[string][]string {
- result := make(map[string][]string)
-
- // 首先收集所有查询的域名
- var domains []string
- for _, q := range dns.Questions {
- domains = append(domains, string(q.Name))
- }
-
- // 处理回答记录
- for _, answer := range dns.Answers {
- domain := string(answer.Name)
- var ip string
-
- switch answer.Type {
- case layers.DNSTypeA:
- if len(answer.Data) >= net.IPv4len {
- ip = net.IP(answer.Data).String()
- }
- case layers.DNSTypeAAAA:
- if len(answer.Data) >= net.IPv6len {
- ip = net.IP(answer.Data).String()
- }
- case layers.DNSTypeCNAME:
- // 处理CNAME记录
- cname := string(answer.Data)
- result[domain] = append(result[domain], "CNAME: "+cname)
- continue
- default:
- continue
- }
-
- if ip != "" {
- result[domain] = append(result[domain], ip)
- }
- }
-
- return result
- }
复制代码 处理特别情况
- CNAME 记录:假如遇到 CNAME 记录,你大概需要继续查找对应的 A/AAAA 记录
- 多IP情况:一个域名大概对应多个IP地点
- 压缩域名:layers.DNS 已经自动处理了DNS名称压缩
性能优化发起
- 预分配切片:假如你知道大致数量,可以预分配切片大小
- 重用缓冲区:在处理大量数据包时重用缓冲区
- 并行处理:使用 goroutine 池处理数据包
完整示例(带CNAME处理)
- func processDNSResponse(dns *layers.DNS) {
- if !dns.QR {
- return // 不是响应包
- }
-
- // 创建域名到IP的映射
- domainToIPs := make(map[string][]string)
- cnameMap := make(map[string]string)
-
- // 首先处理CNAME记录
- for _, answer := range dns.Answers {
- if answer.Type == layers.DNSTypeCNAME {
- domain := string(answer.Name)
- cname := string(answer.Data)
- cnameMap[domain] = cname
- }
- }
-
- // 然后处理A和AAAA记录
- for _, answer := range dns.Answers {
- var ip string
- domain := string(answer.Name)
-
- switch answer.Type {
- case layers.DNSTypeA:
- if len(answer.Data) >= net.IPv4len {
- ip = net.IP(answer.Data).String()
- }
- case layers.DNSTypeAAAA:
- if len(answer.Data) >= net.IPv6len {
- ip = net.IP(answer.Data).String()
- }
- default:
- continue
- }
-
- if ip != "" {
- // 检查是否有CNAME链
- finalDomain := domain
- for {
- if cname, exists := cnameMap[finalDomain]; exists {
- finalDomain = cname
- } else {
- break
- }
- }
- domainToIPs[finalDomain] = append(domainToIPs[finalDomain], ip)
- }
- }
-
- // 打印结果
- for domain, ips := range domainToIPs {
- fmt.Printf("域名: %s\n", domain)
- for _, ip := range ips {
- fmt.Printf(" IP: %s\n", ip)
- }
- }
- }
复制代码 通过这些方法,你可以有用地从 layers.DNS 数据包中提取域名和对应的IP地点,并处理各种DNS记录类型和特别情况。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |