Linux 内核抓包功能实现基础(三) 抓包服务器的实现

上回博客我们讲到了内核抓包内核端的实现,通过上篇博客的例子我们就能够开始抓包了,整个抓包的拓扑图如下:

当开始抓包后,抓包机器将抓到的报文送到服务器上,假设服务器地址是192.168.199.123:8099,如果在抓包服务器上打开wireshark可以看到送过来的报文,如下图这样子:

可以看到抓包服务器上收到了报文,但是这样子的报文我们是无法直接拿来分析的,因为实际的报文是封装起来的,需要把数据提取出来封装成wireshark能够识别的格式才行,本篇博客就介绍一下抓包服务器的实现原理。

在看实际的代码之前,我们先来看一下抓包服务器开启后的样子,首先监听本地8099端口

其次将送到该端口的数据封装成pcap格式,使用wireshark打开后如下:

这是实际抓到的报文,能够清楚看到报文里面的各项数据。

下面介绍抓包服务器实现的原理。

抓包服务器功能大致如下:

1. 启动后监听报文送来的地址,在本例中是8099

2. 收到数据后将数据保存到文件中并以wireshark可识别的格式保存。

通常使用wireshark抓包保存的文件都已.pcap为后缀名。里面存储报文的实际数据。pcap文件格式如下:

每个pcap文件都有一个pcap首部,占24字节,之后就是pcap报文头和报文数据,报文头存储了抓包的时间戳等信息。

抓包服务器的任务就是新建一个pcap文件,写入pcap文件头后,之后每收到一个数据就写入一个pcap报文头和报文数据。这样等保存文件后就可以直接使用wireshark打开了。

winpcap是windows版本的开源抓包库,里面提供了包括pcap文件创建、保存等一系列接口,可以直接拿来使用,我这里的例子是windows64位的 golang版本的,Linux版本下有libpcap库可供使用。

代码如下:

// main.go
package main

import (
	"fmt"
	"net"

	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	//"github.com/google/gopacket/pcap"
	"github.com/google/gopacket/pcapgo"
)

type CaptureManager struct {
	F    *os.File       //抓包文件
	W    *pcapgo.Writer //用于写文件
	conn *net.UDPConn   //udp连接
}

func CaptureSaveFile() {
	for {
		//套接字接收缓存
		buffer := make([]byte, 65535)

		//接收数据
		num, _, err := CapMng.conn.ReadFromUDP(buffer)
		if nil != err {
			continue
		} else {
			captureInfo := gopacket.CaptureInfo{
				Timestamp:      time.Now().UTC(),
				CaptureLength:  num,
				Length:         num,
				InterfaceIndex: 0,
			}
			//写入pcap数据包头以及数据
			CapMng.W.WritePacket(captureInfo, buffer[:num])
			CapMng.F.Seek(0, os.SEEK_END)
		}
	}
}

var CapMng *CaptureManager

func main() {
	//全局管理控制块
	CapMng = &CaptureManager{}

	//创建目录
	os.MkdirAll("capture_packet", 0777)

	//创建文件名字
	t := time.Now()
	fil := "capture_packet" + "/" + "capture" + "_" + t.Format("20060102150405") + ".pcap"

	//创建文件
	CapMng.F, _ = os.Create(fil)

	//填充pcap文件头部24字节
	CapMng.W = pcapgo.NewWriter(CapMng.F)
	CapMng.W.WriteFileHeader(1024, layers.LinkTypeEthernet)

	//设置监听地址
	Addr, err := net.ResolveUDPAddr("udp", "0.0.0.0:"+strconv.Itoa(8099))

	if nil != err {
		fmt.Println("resulve udp addr fail")
		return
	} else {
		//创建udp连接
		CapMng.conn, err = net.ListenUDP("udp", Addr)
		if nil != err {
			fmt.Println("create conn fail")
			return
		} else {
			fmt.Println("Capture Start")
			go CaptureSaveFile()
		}
	}

	chSignal := make(chan os.Signal, 5)
	//signal.Notify(chSignal)
	//监听指定信号
	signal.Notify(chSignal, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT,
		syscall.SIGSEGV, syscall.SIGABRT)

	fmt.Println("capture server start")

	//阻塞直至有信号传入
	sig := <-chSignal
	switch sig {
	case syscall.SIGSEGV, syscall.SIGABRT:
	default:
	}
	fmt.Println("*************Server Recive signal %s, program exit", sig.String())
	CapMng.F.Close()
	CapMng.conn.Close()
	fmt.Println("CLOSE")
	time.After(1 * time.Second)
}

代码在github上,下载后可在安装了go环境下的windows上直接运行。

git@github.com:FuYuanDe/capture_demo.git

编译go程序可能有点麻烦,但既然都看到这了,估计这点麻烦也不会难道你

偷笑

 

到目前位置,内核抓包功能基本的框架已经搭起来了,但是和tcpdump相比,还有很多不足之处,比方说能否根据过滤条件抓取报文?其次,送到远端服务器上的时候如果mtu值很小怎么办?要不要分片?还有就是在POST_ROUTING上的hook函数抓到的报文是没有mac地址的!!!啥?没MAC地址! 没错,本机出去的报文在IP层还没有填写MAC地址,这时候抓包的话是无法得到目的mac地址的。有空的话再聊聊这些问题的解决方法。有问题的同志想要交流的话可以加群讨论一下奥

参考资料:

1.

Winpcap的安装使用方法和问题总结

https://blog.csdn.net/yu314092706/article/details/52937189

2. 

golang使用gopacket包进行数据包捕获,注入和分析

https://blog.csdn.net/ptmozhu/article/details/72652310?utm_source=itdadao&utm_medium=referral

3. pcap文件格式分析

https://www.cnblogs.com/2017Crown/p/7162303.html

またね!

大笑

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值