利用PCAP实现网络数据截获修改(二)

上一边我们实现了利用PCAP,将报文从一个网卡复制到另一个网卡。实现了一种截断正常网络传输而不影响原有传输的一种方案。
考虑到网络数据是带有校验的,如果我们修改了tcp的payload部分,那么则需要重新进行校验,所以本篇主要研究了对TCP和UDP部分数据的校验计算。
首先简单介绍一下网上的关于校验和的计算,
TCP和UDP数据基本一样,公式就是伪头部+tcp(或udp)的全部数据,计算和放入一个unsigned int的数字中,然后如果高16位有数据,则将其移入低16位加入,循环操作直到高16位没有数据为止,然后取反作为校验和。

伪头部的结构,共12个字节。

typedef struct _check_subhdr         // udp计算checksum时的 亚头部: 4 byte源ip地址 + 4 byte目的ip地址 + 0x00 + 1 byte协议 + UDP 长度(2byte)
{
    unsigned int  	src_ip; 
    unsigned int  	dst_ip; 
    char     		mbz;           // must be zero
    char     		protocol; 
    unsigned short  len;           // 这里的长度是指  udp packet中  udp头部和数据部分的总长度
} check_subhdr;

校验和的计算函数如下

u_short core_checksum(u_short *buffer,int size)  
{  
    unsigned int cksum=0;  
    while(size>1)  
    {  
    	unsigned short tmp=*buffer++;
        cksum+=tmp;  
        size-=sizeof(u_short); 
		
		//printf("0x%04x+",tmp);
    }  
    if(size)  
    {  
        cksum+=*(u_char *)buffer;  
    }  
	//printf("=cksum:%08x\n",cksum);
    //将32位数转换成16   
    while (cksum>>16) 
	{
		cksum=(cksum>>16)+(cksum & 0xffff); 
	}
	
	//printf("cksum2:%08x\n",cksum);
    return (u_short) (~cksum);  
}

计算TCP报文的TCP部分校验的函数如下

u_short get_tcp_checksum(struct iphdr *ipptr,struct tcphdr *tcpptr,int pkt_len) 
{
	int i=0;
	char cleancnum[2]={0,0};
	//u_short tcp_part_len = pkt_len-sizeof(struct ether_header)-sizeof(struct iphdr);    
	u_short tcp_part_len = ntohs(ipptr->tot_len)-(ipptr->ihl*4);

    check_subhdr tcp_subhdr; 
    tcp_subhdr.protocol = ipptr->protocol; 
    tcp_subhdr.dst_ip = ipptr->daddr; 
    tcp_subhdr.src_ip = ipptr->saddr; 
    tcp_subhdr.mbz = 0x00; 
    tcp_subhdr.len = htons(tcp_part_len); 

    int subhdr_len = sizeof(check_subhdr); 
    int buf_size = tcp_part_len + subhdr_len;         // 亚包头 + udp包头 + 数据部分的总长度
    if (pkt_len < buf_size)
        return 0; 
    u_char* buffer = (u_char*)malloc(buf_size); 
    memset(buffer, 0x00, buf_size); 

    memcpy(buffer, (char*)&tcp_subhdr, subhdr_len); 
    //memcpy(buffer + subhdr_len, (char*)udpptr, udp_part_len); 

	//先拷贝16个字节有效数据,
	memcpy(buffer + subhdr_len, (char*)tcpptr, 16); 
	//拷贝两个00作为校验码
	memcpy(buffer + subhdr_len + 16, (char*)cleancnum, 2); 
	//拷贝剩余部分数据
	memcpy(buffer + subhdr_len + 18, ((char*)tcpptr)+18, tcp_part_len-18); 

	u_short checksum=0;
	checksum = core_checksum((u_short *)buffer,buf_size);
	
    free(buffer);
    return checksum;  
}

这里遇到了一个思考两天才解决的问题
//u_short tcp_part_len = pkt_len-sizeof(struct ether_header)-sizeof(struct iphdr);
u_short tcp_part_len = ntohs(ipptr->tot_len)-(ipptr->ihl*4);
从网上找到的这个tcp部分的长度计算,正常情况下,我们就这么算,除去eth头和IP头,剩下的部分就是所有tcp的字段,猛一看没什么问题,实际用起来,就会发现,偶尔就会有一些报文计算出来的校验和和报文里的不一致,但是源报文的pcap在wireshark中,并不会出现校验错误。这个问题一直困扰了我一天,直到我发现有些报文的IP头中的数据总长度,存在异常,
在这里插入图片描述

如图,IP部分总长度表示40,即IP和后面的TCP加起来一共40个字节,但是显然后面还有6个字节,按照开始的计算方式,TCP长度是26个,并不是20,所以算来算去,总是出错。
最终调整为
u_short tcp_part_len = ntohs(ipptr->tot_len)-(ipptr->ihl*4);
问题得以解决,所以不能看你收到了多少字节,要看IP头部告诉你,应该是多少字节。

这样,本篇到此,实现了数据的TCP和UDP部分校验和的计算,为后面修改报文数据,打下了基础。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用 libpcap实现利用组播地址抓取数据的示例代码: ```c #include <pcap.h> #include <stdio.h> void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *data); int main(int argc, char *argv[]) { char *device = "eth0"; int snaplen = 65535; int promisc = 1; int to_ms = 1000; char errbuf[PCAP_ERRBUF_SIZE]; char *filter_exp = "udp and dst host 224.0.0.1"; // 过滤规则 // 打开网络接口 pcap_t *handle = pcap_open_live(device, snaplen, promisc, to_ms, errbuf); if (handle == NULL) { fprintf(stderr, "pcap_open_live() failed: %s\n", errbuf); return 1; } // 编译过滤规则 struct bpf_program filter; if (pcap_compile(handle, &filter, filter_exp, 0, PCAP_NETMASK_UNKNOWN) == -1) { fprintf(stderr, "pcap_compile() failed: %s\n", pcap_geterr(handle)); pcap_close(handle); return 1; } // 设置过滤规则 if (pcap_setfilter(handle, &filter) == -1) { fprintf(stderr, "pcap_setfilter() failed: %s\n", pcap_geterr(handle)); pcap_close(handle); return 1; } // 进行数据包捕获和处理 pcap_loop(handle, -1, packet_handler, NULL); // 关闭网络接口 pcap_close(handle); return 0; } // 数据包处理函数 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *data) { // 处理数据包 // ... } ``` 在以上示例中,我们使用 `pcap_open_live()` 函数打开了一个名为 `eth0` 的网络接口,并设置了捕获数据包的最大长度为 65535,开启了混杂模式,设置了超时时间为 1000 毫秒。如果函数执行失败,则使用 `pcap_errbuf` 缓冲区存储错误信息。我们还设置了一个过滤规则 `udp and dst host 224.0.0.1`,该规则表示只捕获目标地址为 `224.0.0.1` 的 UDP 数据包。 在接下来的代码中,我们使用 `pcap_compile()` 函数编译过滤规则,再使用 `pcap_setfilter()` 函数设置过滤规则。最后,我们使用 `pcap_loop()` 函数进行数据包捕获和处理,每当捕获到一个数据包时,就会调用 `packet_handler()` 函数进行处理。在 `packet_handler()` 函数中,我们可以对捕获到的数据包进行处理。最后,我们使用 `pcap_close()` 函数关闭网络接口。 需要注意的是,上述代码中的过滤规则是一个示例,您需要根据实际情况来设置过滤规则。如果您需要捕获其他组播地址的数据包,可以将 `224.0.0.1` 替换为其他组播地址。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖哥王老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值