自写网卡抓包程序
来源:互联网 发布:床和床头柜尺寸 知乎 编辑:程序博客网 时间:2024/06/05 22:48
网上网卡抓包程序已经较多,我也就在这里写下近段时间我的感悟,一作自己整理思路,二为需要的人取用。本文所做的网卡抓包程序类似于简化版wireshark功能。废话不多说,我们开始。
我们的目标是获取所有经过本地网卡的报头信息,包括链路层的MAC头,网络层的IP头,传输层的tcp或udp头,以及应用层的http头,并将其完整打印出来。
整个思路其实很简单,无非就是获取经过网卡的数据帧,然后将其按照格式打印出来即可。
想要获取数据帧,就不得不借用原始套接字(raw socket),raw socket的原理按我理解相当于在数据帧进入网卡时直接通过调用raw socket将数据帧备份一遍,直接将备份的数据帧交由你写的抓包程序处理,而原本的数据帧仍然一步步通过协议栈解析。
一般情况下我们使用的raw socket有两类:1、socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送和接收ip数据包。2、socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧。这里我们选用第二种。此函数的第三个变量是socket的protocol,定义于<netinet/in.h>。常用的协议有ETH_P_IP , ETH_P_ARP,ETH_P_RARP或ETH_P_ALL,从字面上我们就可以很好理解。同时此函数返回值与一般的socket一样,就是个套接字。
- ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &nrecv_buff, sizeof(int));
- if (ret < 0)
- {
- perror("[Error]Set socket option");
- close(fd);
- exit(0);
- }
由于主机上的网卡可能不止一块,我们需要bind到某一块网卡上,这里就需要了解一个重要的结构类型struct ifreq。ifreq结构定义在/usr/include/net/if.h中,用来配置ip地址,激活接口,配置MTU等接口信息的。其中包含了一个接口名称和union(有可能是IP地址,广播地址,子网掩码,MAC号,MTU等)。ifreq包含在ifconf结构中。而ifconf结构通常是用来保存所有接口的信息的。利用ioctl与ifreq结构体我们可以获得某个网卡(特定接口名称ethx)的ip地址。
关于ioctl的解释可以参考my.oschina.net/dream0303/blog/179893。这里我们定义一个名为ifr的ifreq结构体,先strcpy(ifr.ifr_name, g_ifname)g_ifname可为(ethx自设),利用ioctl(fd, SIOCGIFINDEX, &ifr)即可得到此网卡的接口地址。(当有多块网卡时,每个网卡都会有一个索引值,值会随着正在被使用的网卡个数变化,这个索引值就是网卡的接口地址)。因此,通过索引值,我们就可以定位到本机上指定的一块网卡。需要注意的是,在我的测试机上有两块网卡,一块网卡仅服务于内网,因此我之前bind时接收到的全是arp数据包。
- strcpy(ifr.ifr_name, g_ifname);
- printf("Get networkcard port!\n");
- ret = ioctl(fd, SIOCGIFINDEX, &ifr);
- if (ret < 0)
- {
- perror("[Error]Get networkcard ip");
- close(fd);
- exit(0);
- }
然后通过网卡接口地址,将原始套接字绑定到网卡上,ret = bind(fd, (struct sockaddr *)&sock, sizeof(sock))这里的sock便不再是之前TCP连接时的 struct sockaddr_in 了,而是struct sockaddr_ll,具体定义可以参考hi.baidu.com/sjb811023/item/e0463c16ee4f5ba7feded5ff。
- sock.sll_family = AF_PACKET;
- sock.sll_ifindex = local_ifr.ifr_ifindex;
- sock.sll_protocol = htons(ETH_P_ALL);
- ret = bind(fd, (struct sockaddr *)&sock, sizeof(sock));
- if (ret < 0)
- {
- perror("[Error]Bind interface");
- close(fd);
- exit(0);
- }
- printf("Bind Success!\n");
这里的原理也很简单,先用recvfrom把完整的数据帧接收进来,再用各种结构头去读取就好了。
mac头:
- //获取链路层mac信息,以ether_header结构体抽出mac头
- pst_eth_head = (struct ethhdr*)nrecv_buff;
- //打印出以太帧报头的详细信息,并读出帧类型
- printf("Source MAC address : %02x:%02x:%02x:%02x:%02x:%02x\n", pst_eth_head->h_source[0], pst_eth_head->h_source[1], pst_eth_head->h_source[2], pst_eth_head->h_source[3], pst_eth_head->h_source[4], pst_eth_head->h_source[5]);
- printf("Destination MAC address : %02x:%02x:%02x:%02x:%02x:%02x\n", pst_eth_head->h_dest[0], pst_eth_head->h_dest[1], pst_eth_head->h_dest[2], pst_eth_head->h_dest[3], pst_eth_head->h_dest[4], pst_eth_head->h_dest[5]);
- us_eth_type = ntohs(pst_eth_head->h_proto);
- printf("%x\n", us_eth_type);
- //获取网络层ip信息
- pst_ip_head = (struct iphdr*)(nrecv_buff + sizeof(struct ethhdr));
- memset(&source, '\0', sizeof(source));
- source.sin_addr.s_addr = pst_ip_head->saddr;
- memset(&dest, '\0', sizeof(dest));
- dest.sin_addr.s_addr = pst_ip_head->daddr;
- //由于大小端的问题,所以buf中多字节类型都需要转化,而8位的单字节类型不用转化0,ntohs函数处理16位,ntohl函数处理32位
- printf("\tVersion : %d\n", (unsigned int)pst_ip_head->version);
- printf("\tHead Lenfth : %d\n", (unsigned int)pst_ip_head->ihl);
- printf("\tType of service : %d\n", (unsigned int)pst_ip_head->tos);
- printf("\tTotal length : %d\n", ntohs(pst_ip_head->tot_len));
- printf("\tIdentifier : %d\n", ntohs(pst_ip_head->id));
- printf("\tFragmented offset : %d\n", ntohs(pst_ip_head->frag_off));
- printf("\tTime to live : %d\n", (unsigned int)pst_ip_head->ttl);
- printf("\tProtocol : %d\n", (unsigned int)pst_ip_head->protocol);
- printf("\tHeader checksum : %d\n", ntohs(pst_ip_head->check));
- printf("\tSource IP : %s\n", inet_ntoa(source.sin_addr));
- printf("\tDestination IP : %s\n", inet_ntoa(dest.sin_addr));
- pst_tcp_head = (struct tcphdr*)(nrecv_buff + sizeof(struct ethhdr) + iphdr_len);
- //打印tcp信息
- printf("\tSource port : %u\n", ntohs(pst_tcp_head->source));
- printf("\tDestination port : %u\n", ntohs(pst_tcp_head->dest));
- printf("\tSequence number : %u\n", ntohl(pst_tcp_head->seq));
- printf("\tAcknowledgement number : %u\n", ntohl(pst_tcp_head->ack_seq));
- printf("\tHead length : %d\n", (unsigned int)pst_tcp_head->doff);
- printf("\tUrgent flags : %d\n", (unsigned int)pst_tcp_head->urg);
- printf("\tAcknowledgement Flag : %d\n", (unsigned int)pst_tcp_head->ack);
- printf("\tPush Flag : %d\n", (unsigned int)pst_tcp_head->psh);
- printf("\tReset Flag : %d\n", (unsigned int)pst_tcp_head->rst);
- printf("\tSynchronise Flag : %d\n", (unsigned int)pst_tcp_head->syn);
- printf("\tFinish Flag : %d\n", (unsigned int)pst_tcp_head->fin);
- printf("\tWindow size value : %d\n", ntohs(pst_tcp_head->window));
- printf("\tChecksum : %d\n", ntohs(pst_tcp_head->check));
- printf("\tUrgent pointer : %d\n", ntohs(pst_tcp_head->urg_ptr));
udp头:
- <span style="white-space:pre"> </span>printf("\tSource port : %d\n", ntohs(pst_udp_head->source));
- printf("\tDestination port : %d\n", ntohs(pst_udp_head->dest));
- printf("\tLength : %d\n", ntohs(pst_udp_head->len));
- printf("\tChecksum : %d\n", ntohs(pst_udp_head->check));
打印http头,http使用tcp的80端口
- void ethdump_print_req_http(){
- int len = strlen(data);
- char *buff;
- int off;
- memset(buff, '\0', SIZE);
- if (strncmp(data, "GET", 3) && strncmp(data, "POST", 4) == 0)
- {
- buff = get_line(buff, data, len);
- printf("%s", buff);
- off = strlen(buff);
- len = len - off;
- while(strcmp(buff, "\r\n")){
- memset(buff, '\0', SIZE);
- buff = get_line(buff, data + off, len);
- printf("%s", buff);
- off = strlen(buff) + off;
- len = len - off;
- }
- }
- }
最后感谢firefoxbug的耐心指导。
- 自写网卡抓包程序
- 网卡抓包程序
- 无线网卡抓包
- 使用 网卡混杂模式 编写网络抓包程序
- 抓包程序入门(摘抄自tcpdump文档)
- 虚拟网卡抓包方案
- 自己写个ping程序玩玩---附带抓包
- Android抓包程序
- libpcap 抓包程序
- windows抓包程序
- libpcap抓包程序
- libpcap抓包程序
- WinPcap教程(3):打开网卡抓包
- WinPcap教程(3):打开网卡抓包
- Wireshark用无线网卡抓包
- 设置网卡,抓带tag的包
- libpcap库多网卡抓包
- 局域网抓包c++写的抓包分析工具
- Memcached Wiki 一 关于
- c++11之初始化列表
- lua 精灵菜单
- 黑马程序员-----IO流
- 低洼
- 自写网卡抓包程序
- asis-ctf的writeup收集
- 安卓 动画效果
- UVA 11825 - Hackers' Crackdown(状态压缩DP)
- linux(菜鸟)---linux vim学习
- C++ template
- leetcode第一刷_Roman to Integer
- Leetcode 线性表 数 Add Two Numbers
- Memcached wiki 二 安装