简单数据抓包工具的实现

483 阅读2分钟

·一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

今天的目的是实现一个简单的数据抓包工具,为此我们将借助原始套接字的强大功能。 出于简化问题的考虑,这里不考虑因为特殊目的增长协议头部数据的分组。仅仅考虑每一层头部都是准确的长度的数据包。 对于不属于这类的数据包,直接放弃处理。

首先声明一个原始套接字,并且我们要在数据链路层的级别使用它,以获得尽可能多的数据信息。

sock = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP))

然后开始不断从中读取数据

size = recvfrom(sock, buffer, 1024, 0, 0, 0);

在我们限定的这种条件下 size 的值必须得是大于 42的 。

为什么呢? 因为他有14字节的 mac 帧。 20字节的 IP 头部 8 字节的头,可能是TCP可能是UDP,也有可能是ICMP等等。 所以我们先去掉不符合这些条件的包。

if(size < 42){
    continue;
}

然后我们可以开始解析数据了,首先是两个mac地址,各自占了6个字节。

        auto p = buf;
        char dst_mac[6];
        char src_mac[6];
        memcpy(dst_mac, p, 6);
        memcpy(src_mac, p + 6, 6);

之后是我们的IP帧,我们要拿到其中的源IP和目标IP。首先要从数据包的最开始跳到IP包的开始,然后在IP帧的后半部分才是两个IP地址。另外我们要从IP头中得到后续数据使用的协议。

p += 14;
char src_ip[4];
char dst_ip[4];
memcpy(src_ip, p + 12, 4);
memcpy(dst_ip, p + 16, 4);
char proto = p[9];

然后是我们的其他数据解析。这里需要首先对协议进行识别。协议定义都有对应的常量,因此我们简单写个switch语句或者ifelse语句就行了。 不同情况不同处理。

        p += 20;

        switch (proto)
        {
        case IPPROTO_TCP:
            //可以获取端口,syn, ack, fin, rst之类的信息
            break;
        
        case IPPROTO_UDP:
            //可以获取端口,
            break;

        default:
            break;
        }

注意p偏移到了这部分数据。

对于具体需要的数据可以用类似的方式获取。

先前得到的信息我们也可以按自己的需要处理。注意对于IP地址可以直接使用系统自带的格式转换函数得到字符串形式。

这里还有一个在某些时候有效的优化方法,由于这里数据不会被修改,如果在本次循环之间数据就能被处理完成,那么我们可以不同定义这么多中间变量,直接让指针指在某个位置,只要处理时注意固定长度即可。