关于原始套接字

</pre>数据报式套接字(SOCK_DGRAM),仅限于UDP;<p></p><p>流式套接字(SOCK_STREAM),仅限于TCP;</p><p>TCP与UDP各自有独立的port互不影响,没一个进程可以使用多个port;</p><p></p><p>原始套接字(SOCK_RAW),它实现于系统核心.可以接收本机网卡上所有的数据帧(数据包),对于监听网络流量和分析网络数据很有作用.开发人员可发送自己组装的数据包到网络上.原始套接字可以收发内核没有处理的数据包  因此,要访问其他协议发送的数据需要使用原始套接字(SOCK_RAW);</p><p></p><p>int socket(PF_PACKET, SOCK_RAW, protocol)功能:创建链路层的原始套接字</p><p><span style="font-size:12px;">protocol:指定可以接收或发送的数据包类型</span></p><p>ETH_P_IP:IPV4数据包ETH_P_ARP:ARP数据包ETH_P_ALL:任何协议类型的数据包;</p><p>例如:</p><p>成功返回套接字;</p><p>sock_raw_fd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL));</p><p>头文件:</p><p>#include <sys/socket.h>#include <netinet/ether.h></p><p>使用原始套接字进行编程开发时,首先要对不同协议的数据包进行学习,需要手动对IP、TCP、UDP、ICMP等包头进行组装或者拆解。</p><p>组装/拆解udp数据包流程:port来标记给哪个进程<img src="https://img-blog.csdn.net/20150822130901589?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /></p><p></p><p></p><pre name="code" class="cpp">*
 * =====================================================================================
 *
 *       Filename:  
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Dr. moshui (no_water), [email protected]
 *   Organization:  
 *
 * =====================================================================================
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ether.h>

int main(int argc,char *argv[])
{
	int i = 0;
	unsigned char buf[1024] = "";
	unsigned char type[10] = {1, 6, 17};//ICMP->1、TCP->6、UDP->17
	char name[10][128] = {"ICMP", "TCP", "UDP"};
	int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	while(1)
	{
		unsigned char src_mac[18] = "";
		unsigned char dst_mac[18] = "";
		unsigned char dst_ip[16] = "";
		unsigned char src_ip[16] = "";
		//获取链路层的数据帧
		recvfrom(sock_raw_fd, buf, sizeof(buf),0,NULL,NULL);
		//从buf里提取目的mac、源mac
		sprintf(dst_mac,"%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
		sprintf(src_mac,"%02x:%02x:%02x:%02x:%02x:%02x", buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]);
		//判断是否为IP数据包
		if(buf[12]==0x08 && buf[13]==0x00)
		{	
			printf("______________IP数据报_______________\n");
			printf("MAC:%s >> %s\n",src_mac,dst_mac);
			//获取源IP、目的IP
			sprintf(src_ip,"%d:%d:%d:%d", buf[26], buf[27], buf[28], buf[29]);
			sprintf(dst_ip,"%d:%d:%d:%d", buf[30], buf[31], buf[32], buf[33]);
			printf("IP:%s >> %s\n",src_ip,dst_ip);
			for(i=0;i<10;i++)
			{
				if(buf[23] == type[i])
				{
					printf("协议类别:%s\n",name[i]);
				}
			}
		}//判断是否为ARP数据包
		else if(buf[12]==0x08 && buf[13]==0x06)
		{
			printf("______________ARP数据报_______________\n");
			printf("MAC:%s >> %s\n",src_mac,dst_mac);
			//获取源IP、目的IP
			sprintf(src_ip,"%d:%d:%d:%d", buf[28], buf[29], buf[30], buf[31]);
			sprintf(dst_ip,"%d:%d:%d:%d", buf[38], buf[39], buf[40], buf[41]);
			printf("IP:%s >> %s\n",src_ip,dst_ip);
		}//判断是否为RARP数据包
		else if(buf[12]==0x80 && buf[13]==0x35)
		{
			printf("______________RARP数据报_______________\n");
			printf("MAC:%s>>%s\n",src_mac,dst_mac);
			//获取源IP、目的IP(RARP报文格式与ARP报文格式一样)
			sprintf(src_ip,"%d:%d:%d:%d", buf[28], buf[29], buf[30], buf[31]);
			sprintf(dst_ip,"%d:%d:%d:%d", buf[38], buf[39], buf[40], buf[41]);
			printf("IP:%s>>%s\n",src_ip,dst_ip);
		}
	}
	return 0;
}


混杂模式

设置混杂模式:ifconfig eth0 promisc

取消混杂模式:ifconfig eth0 -promisc


用sendto发送原始套接字数据:

sendto(sock_raw_fd, msg, msg_len, 0,(struct sockaddr*)&sll, sizeof(sll));

sock_raw_fd:原始套接字
msg:发送的消息(封装好的协议数据)
sll:本机网络接口,指发送的数据应该从本机的哪个网卡出去,而不是以前的目的地址


本机网络接口
struct sockaddr_ll sll;
#include <netpacket/packet.h>


通过ioctl来获取网络接口地址
int ioctl(int fd, int request,void *)
#include <sys/ioctl.h>




获取到当前网段中所有机器的MAC地址:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>				//struct ifreq
#include <sys/ioctl.h>			//ioctl、SIOCGIFADDR
#include <sys/socket.h>
#include <netinet/ether.h>		//ETH_P_ALL
#include <netpacket/packet.h>	//struct sockaddr_ll
#include <pthread.h>
#include <netinet/in.h>
void *send_arp_ask(void *arg);
int main(int argc,char *argv[])
{
	//1.创建通信用的原始套接字
	int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	
	//2.创建发送线程
	pthread_t tid;
	pthread_create(&tid, NULL, (void *)send_arp_ask, (void *)sock_raw_fd);
	
	while(1)
	{
		//3.接收对方的ARP应答
		unsigned char recv_msg[1024] = "";
		recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0, NULL, NULL);
		if(recv_msg[21] == 2)			//ARP应答
		{
			char resp_mac[18] = "";		//arp响应的MAC
			char resp_ip[16] = "";		//arp响应的IP
			
			sprintf(resp_mac, "%02x:%02x:%02x:%02x:%02x:%02x", \
			recv_msg[22],recv_msg[23],recv_msg[24],recv_msg[25],recv_msg[26],recv_msg[27]);
			sprintf(resp_ip, "%d.%d.%d.%d", recv_msg[28], recv_msg[29], recv_msg[30], recv_msg[31]);
			printf("IP:%s - MAC:%s\n",resp_ip, resp_mac);
		}
	}
	
	return 0;
}

void *send_arp_ask(void *arg)
{
	int i = 0;
	int sock_raw_fd = (int)arg;
	//1.根据各种协议首部格式构建发送数据报
	unsigned char send_msg[1024] = {
		//--------------组MAC--------14------
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //dst_mac: FF:FF:FF:FF:FF:FF
		0x00, 0x0c, 0x29, 0x75, 0xa6, 0x51, //src_mac: 00:0c:29:75:a6:51
		0x08, 0x06,							//类型:0x0806 ARP协议
		
		//--------------组ARP--------28-----
		0x00, 0x01, 0x08, 0x00,				//硬件类型1(以太网地址),协议类型0x0800(IP)	
		0x06, 0x04, 0x00, 0x01,				//硬件、协议地址分别是6、4,op:(1:arp请求,2:arp应答)
		0x00, 0x0c, 0x29, 0x75, 0xa6, 0x51,	//发送端的MAC地址
		172,  20,   226,  12,  				//发送端的IP地址
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	//目的MAC地址(由于要获取对方的MAC,所以目的MAC置零)
		172,  20,   226,  11				//目的IP地址
	};
	
	//2.数据初始化
	struct sockaddr_ll sll;					//原始套接字地址结构
	struct ifreq ethreq;					//网络接口地址
	strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);	//指定网卡名称
	
	//3.将网络接口赋值给原始套接字地址结构
	ioctl(sock_raw_fd, SIOCGIFINDEX, (char *)ðreq);
	bzero(&sll, sizeof(sll));
	sll.sll_ifindex = ethreq.ifr_ifindex;
	
	//4.本地机的IP
	if(!(ioctl(sock_raw_fd, SIOCGIFADDR, (char *)ðreq)))	
	{
		int num = ntohl(((struct sockaddr_in*) (ðreq.ifr_addr))->sin_addr.s_addr);
		for(i=0; i<4; i++)
		{
			send_msg[31-i] = num>>8*i & 0xff;	//将发送端的IP地址组包
		}
	}
	
	//5.获取本地机(eth0)的MAC
	if (!(ioctl(sock_raw_fd, SIOCGIFHWADDR, (char *) ðreq)))
    {
		for(i=0; i<6; i++)
		{
			//将src_mac、发送端的MAC地址组包
			send_msg[22+i] = send_msg[6+i] = (unsigned char) ethreq.ifr_hwaddr.sa_data[i];			
		}
    }
	
	while(1)
	{
		int i = 0;
		int num[4] = {0};
		unsigned char input_buf[1024] = "";
		
		//6.获取所要扫描的网段(172.20.226.0)
		printf("input_dst_Network:172.20.226.0\n");
		fgets(input_buf, sizeof(input_buf), stdin);
		sscanf(input_buf, "%d.%d.%d.", &num[0], &num[1], &num[2]//目的IP地址 
		);
		
		//7.将键盘输入的信息组包
		for(i=0;i<4;i++)
			send_msg[38+i] = num[i];//将目的IP地址组包
		
		//8.给1~254的IP发送ARP请求
		for(i=1; i<255; i++)
		{
			send_msg[41] = i;
			int len = sendto(sock_raw_fd, send_msg, 42, 0 , (struct sockaddr *)&sll, sizeof(sll));
			if(len == -1)
			{
				perror("sendto");
			}
		}
		sleep(1);
	}
	




发布了20 篇原创文章 · 获赞 10 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章