tcp/ip在物理层/数据链路层 实现简单抓包

socket的精妙之处在于协议族的横向转换和地址族的纵向转换.我们也可在更底层实现对流经host的数据流的监督和修改.尤其是监察数据,十分简单.

这里是混杂模式实现对ip数据流的监察与对tcp数据流的简单查看,需要root权限.这里忽略了tcp/ip的options选项.进一步感兴趣的同学可以查看wareshark的源码实现.

尊重原创,转载的同学,一定要注明来自DNA出处哦!

#pragma once
#ifdef __linux__
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <unistd.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <sys/ioctl.h>
#include <alloca.h>
#endif

#define false 0
#define true 1
#define ETH_ALEN 6

#define isHumanized 0
#pragma pack(1)
//链路层MAC头部部分
struct ethHeader
{
	unsigned char dst[6];//目的mac地址
	unsigned char src[6];//源mac地址
	unsigned short protocol;//上层(网络层)协议
};

struct ipHeader
{
	unsigned char headerLen : 4;//0x45 0100 0101
	unsigned char version :4;
	unsigned char toservice:8;
	unsigned short ipLen:16;
	unsigned short sequence:16;
	unsigned short segmentflag:16;
	unsigned char time2live:8;
	unsigned char protocol:8;
	unsigned short checksum:16;
	unsigned long sorceIp:32;
	unsigned long destIp:32;
	//unsigned char options[];
};

#ifdef BIGEND
//大端对齐的方式没试过,姑且按tcp表排序.
struct tcpHeader
{
	unsigned short sorcePort:16;
	unsigned short destPort:16;
	unsigned long sequence:32;
	unsigned long ack_seq:32;
	unsigned char headerLen:4;
	unsigned char residuum:6;
	unsigned char urgent:1;
	unsigned char acknowledge:1;
	unsigned char push:1; 
	unsigned char reset:1;
	unsigned char synchronize:1;
	unsigned char final:1;
	unsigned short window:16;
	unsigned short checksum:16;
	unsigned short urgentPoint:16;
	unsigned char options[];
};
#else
struct tcpHeader
{
	unsigned short sorcePort : 16;
	unsigned short destPort : 16;
	unsigned long sequence : 32;
	unsigned long ack_seq : 32;

	//网络:0100 ---- --00 0010 
	//主机:--00 0010 0100.

	unsigned char residuum1 : 4;	//----
	unsigned char headerLen : 4;	//0100
	unsigned char final : 1;		//0
	unsigned char synchronize : 1;  //1
	unsigned char reset : 1;		//0
	unsigned char push : 1;			//0
	unsigned char acknowledge : 1;  //0
	unsigned char urgent : 1;		//0
	unsigned char residuum2 : 2;	//--

	unsigned short window : 16;
	unsigned short checksum : 16;
	unsigned short urgentPoint : 16;
	unsigned char options[];
};
#endif

#pragma pack()

extern unsigned char print(unsigned char *buff,int len);
extern void printMyInfo(void);
struct ifreq ifstruct;
//网络字节序/大端对齐则true 小端对齐则false0x1234   低地址0x34-->高地址0x12

void testEnd()
{
	union test_end
	{
		unsigned int get1;
		unsigned char is1;
	};
	union test_end t;
	t.get1 = 1;
	if (t.is1)
	{
		printf("is1是低地址,在低位得到1,因此是小端对齐\n");
	}
	else
	{
		printf("is1是低地址,在低位没得1,因此是大端对齐\n");
	}
}

int main(int argc,char *argv[])
{
	//int socket(int domain,int type, int protocol)
	//domain-----------:协议族 AF_INET AF_INET6 AF_LOCAL等
	//type-------------:套接口类型 SOCK_STREAM(TCP) SOCK_DGRAM(UDP) SOCK_RAW(原始socket)
	//wareshark        :socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
	//ETH_P_ALL        :该协议监听所有底层包,主机和网络字节序手动转换.
	//vpn              :socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP))
	//ETH_P_IP         :该协议监听所有ip包
	//ping             :socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
	//IPPROTO_ICMP     :该协议监听所有ICMP包
	//IPPROTO_IP(0)    :该协议接收ip包
	//IPPROTO_RAW(255) :该协议发送ip包,而不能接收
	//UDP数据报        :socket(AF_INET, SOCK_DGRAM, 0)
	//TCP数据流        :socket(AF_INET, SOCK_STREAM,0)
	//AF address families  PF protocol families
	//socket()的fd只是存在于协议族的空间中.
	//bind()分配一个指定的协议地址(网络字节序的ip与端口).
	//客户端的ip和端口可以不用bind指定,而是在connect()时由系统生成.
	//socket()的套接口默认未连接的主动套接口(需要connect的client),listen后成为被动套接口(内核将接受client对它的连接请求).
	//未完成连接队列:处于SYN_RCVD   状态的套接口的集合
	//已完成连接队列:处于ESTABLISHED状态的套接口的集合
	//accetp:TCP服务器调用.从已完成连接队列返回下一个连接.队列为空则阻塞.第三个参数是输入输出参数len.
	//send:每个tcp套接口都有自己的发送缓冲区,可以用SO_SNDBUF调节大小.内核将data拷贝到发送缓冲区:data若大于发送缓冲区,返回-1;data小于发送缓冲区,但大于剩余空间,且非阻塞,则拷贝一部分,若阻塞,则一直等待将所有数据拷贝至缓冲区才返回,因此阻塞返回值==len;若缓冲区满,且非阻塞,则等待.网络断开或错误,则返回-1.(TCP可靠:对方确认后才能删除发送缓冲区数据).
	//sendto:UDP以数据报方式进行数据传输.因无连接,故需知名地址.因不可靠,故UDP没有自己的发送缓冲区,data被拷贝到内核缓冲区,链路层发送数据后删除拷贝.若无足够内核缓冲区,则返回ENOBUFS错误.
	if (argc >= 2) {
		testEnd(); return 0;
	}
	int fd = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
	char buff[4096];
	memset(buff,0x0,4096);
	int i = 0;
	int n = 0;
	struct sockaddr_ll sll;
	memset(&ifstruct,0x0,sizeof(ifstruct));
	memset(&sll,0x0,sizeof(sll));
	strcpy(ifstruct.ifr_name,"eth0");//接口(网卡)名称
	ioctl(fd,SIOCGIFINDEX,&ifstruct);//获取接口索引 SIOC:serial input/output controller
	perror("ioctl获取SIOC索引");
	sll.sll_family = AF_PACKET;//必须AF_PACKET族
	sll.sll_ifindex = ifstruct.ifr_ifindex;//接口所在索引
	sll.sll_protocol = htons(ETH_P_ALL);//物理层协议 任何协议
	sll.sll_hatype = ARPHRD_ETHER;//硬件类型 以太
	sll.sll_pkttype = PACKET_OTHERHOST;//包类型 混杂模式下发往其他host的包
	sll.sll_halen = ETH_ALEN;//MAC地址长度 一般6
	sll.sll_addr[6] = 0;//sll_addr[8] 物理层协议地址
	sll.sll_addr[7] = 0;
	ioctl(fd,SIOCGIFHWADDR,&ifstruct);//获取接口地址
	perror("ioctl获取SIOC地址");
	ioctl(fd,SIOCGIFFLAGS,&ifstruct);//获取接口flags
	perror("ioctl获取SIOC标志");
	ifstruct.ifr_flags |= IFF_PROMISC;
	ioctl(fd,SIOCSIFFLAGS,&ifstruct);//设置为混杂模式
	perror("ioctl设置混杂模式");
	bind(fd,(struct sockaddr *)&sll,sizeof(struct sockaddr_ll));//绑定
	perror("bind尝试将获取以及设置生效");
	printMyInfo();

	while(true)
	{
		memset(buff,0x0,4096);
		n = read(fd,buff,1024);
		if(n < 0) perror("读取物理层数据");
		print((unsigned char *)buff,n);
	}
	printf("\nend!\n");
}

char *inet_htoa(unsigned long ipaddr)//c0 a8 00 45
{
	if(ipaddr == 0) return NULL;

	static char buff[15] = {0};
	char *p = buff;
	unsigned char flag = false;
	unsigned char num = (ipaddr >> 24) & 0xff;//c0
	*p = num / 100;//1.92 = 1
	if(*p != 0){
		flag = true;
		*p++ += 0x30; // '1'
	}
	*p = (num / 10) % 10;//19.2 => 19 => 9
	if(*p != 0 || flag == true) {
		*p++ += 0x30;// '9'
	}
	flag = false;
	*p++ = num % 10 + 0x30;// '2'
	*p++ = '.';
	
	num = (ipaddr >> 16) & 0xff;//a8
	*p = num / 100;
	if(*p != 0){
		flag = true;
		*p++ += 0x30;
	}
	*p = (num / 10) % 10;
	if(*p != 0 || flag == true){
		*p++ += 0x30;
	}
	flag = false;
	*p++ = num % 10 + 0x30;
	*p++ = '.';

	num = (ipaddr >> 8) & 0xff;//a8
	*p = num / 100;
	if(*p != 0){
		flag = true;
		*p++ += 0x30;
	}
	*p = (num / 10) % 10;
	if(*p != 0 || flag == true){
		*p++ += 0x30;
	}
	flag = false;
	*p++ = num % 10 + 0x30;
	*p++ = '.';

	num = ipaddr & 0xff;//a8
	*p = num / 100;
	if(*p != 0){
		flag = true;
		*p++ += 0x30;
	}
	*p = (num / 10) % 10;
	if(*p != 0 || flag == true){
		*p++ += 0x30;
	}
	*p++ = num % 10 + 0x30;
	*p = 0;
	
	return buff;
}

char *judge_proto(unsigned char c)
{
	static char array[5] = "0x";
	switch(c){
		case 0x01:
		return "0x01:ICMP";
		case 0x02:
		return "0x02:IGMP";
		case 0x06:
		return "0x06:TCP";
		case 0x11:
		return "0x11:UDP";
		case 0x84:
		return "0x84:SCTP";
		default:
		array[2] = ((c >> 4) & 0x0f) + 0x30;
		array[3] = (c & 0x0f) + 0x30;
		array[4] = '\0';
		return array;
	}
}

//#define BINARYDATA
unsigned char print(unsigned char *buff,int n)
{
	int i = 0;
	if(n < 0) {printf("recv err.\n");return -1;}
	if(n == 0){printf("二进制数据流为0\n");return -1;}
//#define BINARYDATA
#ifdef BINARYDATA
#if isHumanized
	printf("this is binary!\n");
	if(n > 0) {
		printf("\n------------二进制数据流开始----------\n");
		while(n-- >= 0)
		{
			printf("%02x ",buff[i++]);
		}
		printf("\n------------二进制数据流结束----------\n");
	}
	printf("解析二进制数据流......\n");
	printf("数据流接收端MAC:[%02x:%02x:%02x:%02x:%02x:%02x]\n",
	buff[0],buff[1],buff[2],buff[3],buff[4],buff[5]);
	printf("数据流发送端MAC:[%02x:%02x:%02x:%02x:%02x:%02x]\n",
	buff[6],buff[7],buff[8],buff[9],buff[10],buff[11]);
	
	if(ntohs(*(unsigned short *)&buff[12]) != 0x0800) {
		goto END;
	}
	else {
		printf("解析到ipv4包.正在进一步解析......\n");
	}
#else
	if (n > 0) {
		printf("\n------------二进制数据流开始----------\n");
		while (n-- >= 0)
		{
			printf("%02x ", buff[i++]);
		}
		printf("\n------------二进制数据流结束----------\n");
	}
	printf("解析二进制数据流......\n");
	struct ethHeader * ethheader = (struct ethHeader *)buff;
	printf("数据流接收端MAC:[%02x:%02x:%02x:%02x:%02x:%02x]\n",
		ethheader->dst[0], 
		ethheader->dst[1], 
		ethheader->dst[2], 
		ethheader->dst[3], 
		ethheader->dst[4], 
		ethheader->dst[5]);
	printf("数据流发送端MAC:[%02x:%02x:%02x:%02x:%02x:%02x]\n",
		ethheader->src[6], 
		ethheader->src[7], 
		ethheader->src[8], 
		ethheader->src[9], 
		ethheader->src[10], 
		ethheader->src[11]);

	if (ntohs(*(unsigned short *)&buff[12]) != 0x0800) {
		goto END;
		}
	else {
		printf("解析到ipv4包.正在进一步解析......\n");
	}
#endif //BINARYDATA
#endif
	if((buff[14] & 0xf0) != 0x40) return -1;
#if isHumanized
	printf("ip首部长度:%u\n",buff[14] & 0x0f);
	printf("ip服务类型:%u\n",buff[15]);
	printf("ip总长度  :%u\n",ntohs(*(unsigned short *)&buff[16]));
	printf("ip标识序列:%u\n",ntohs(*(unsigned short *)&buff[18]));
	printf("ip分片偏移:%u\n",ntohs(*(unsigned short *)&buff[20]));
	printf("ip生存时间:%u\n",buff[22]);
	printf("ip上层协议:%s\n",judge_proto(buff[23]));
	printf("ip校验和  :%u\n",ntohs(*(unsigned short *)&buff[24]));
	printf("ip源地址  :%s\n",inet_htoa(ntohl(*(unsigned long *)&buff[26])));
	printf("ip目的地址:%s\n",inet_htoa(ntohl(*(unsigned long *)&buff[30])));
#else
	struct ipHeader *ipheader = (struct ipHeader *)&buff[14];
	printf("ip首部长度:%u\n", ipheader->headerLen);
	printf("ip服务类型:%u\n", ipheader->toservice);
	printf("ip总长度  :%u\n", ntohs(ipheader->ipLen));
	printf("ip标识序列:%u\n", ntohs(ipheader->sequence));
	printf("ip分片偏移:%u\n", ntohs(ipheader->segmentflag));
	printf("ip生存时间:%u\n", ipheader->time2live);
	printf("ip上层协议:%s\n", judge_proto(ipheader->protocol));
	printf("ip校验和  :%u\n", ntohs(ipheader->checksum));
	printf("ip源地址  :%s\n", inet_htoa(ntohl(ipheader->sorceIp)));
	printf("ip目的地址:%s\n", inet_htoa(ntohl(ipheader->destIp)));
#endif
	if(buff[23] != 0x06) goto END;
#if isHumanized
	printf("解析到tcp包,正在进一步解析\n");
	printf("tcp源端口     :%u\n",ntohs(*(unsigned short *)&buff[34]));
	printf("tcp目的端口   :%u\n",ntohs(*(unsigned short *)&buff[36]));
	printf("tcp标识序列   :%u\n",ntohs(*(unsigned long *)&buff[38]));
	printf("tcp确认序列   :%u\n",ntohs(*(unsigned long *)&buff[42]));
	printf("tcp首部长度   :%u\n",(buff[46] >> 4) & 0x0f);
	printf("tcp紧急标识urg:%u\n",(buff[47] >> 5) & 0x01);
	printf("tcp回应标识ack:%u\n",(buff[47] >> 4) & 0x01);
	printf("tcp请勿缓存psh:%u\n",(buff[47] >> 3) & 0x01);
	printf("tcp重置连接rst:%u\n",(buff[47] >> 2) & 0x01);
	printf("tcp建立连接syn:%u\n",(buff[47] >> 1) & 0x01);
	printf("tcp释放连接fin:%u\n",(buff[47] & 0x01));
	printf("tcp滑动窗口win:%u\n",ntohs(*(unsigned short *)&buff[48]));
	printf("tcp校验和     :%u\n",ntohs(*(unsigned short *)&buff[50]));
	printf("tcp紧急指针   :%u\n",ntohs(*(unsigned short *)&buff[52]));

	int len = ntohs(*(unsigned short *)&buff[16]) - 4 * (buff[14] & 0x0f) - 4 * ((buff[46] >> 4) & 0x0f);
	printf("tcp字段的数据长度(单位:bytes):%d\n",len);
	if(len == 0) {goto END;}
	printf("开始解析tcp数据报,将以ASCII形式解析......\n");
	for(i = 54; i < len + 54; ++i)
	{
		printf("%c",buff[i]);
	}
#else
	struct tcpHeader *tcpheader = (struct tcpHeader *)&buff[14 + 4 * (ipheader->headerLen)];
	//struct tcpHeader *tcpheader = (struct tcpHeader *)malloc(sizeof(struct tcpHeader));
	//memcpy(tcpheader, &buff[14 + 4 * (ipheader->version_ihl)],sizeof(struct tcpHeader));

	printf("解析到tcp包,正在进一步解析\n");
	printf("tcp源端口     :%u\n", ntohs(tcpheader->sorcePort));
	printf("tcp目的端口   :%u\n", ntohs(tcpheader->destPort));
	printf("tcp标识序列   :%u\n", ntohs(tcpheader->sequence));
	printf("tcp期望序列   :%u\n", ntohs(tcpheader->ack_seq));
	printf("tcp首部长度   :%u\n", (tcpheader->headerLen));
	printf("tcp紧急标识urg:%u\n", tcpheader->urgent);
	printf("tcp回应标识ack:%u\n", tcpheader->acknowledge);
	printf("tcp请勿缓存psh:%u\n", tcpheader->push);
	printf("tcp重置连接rst:%u\n", tcpheader->reset);
	printf("tcp建立连接syn:%u\n", tcpheader->synchronize);
	printf("tcp释放连接fin:%u\n", tcpheader->final); 
	printf("tcp滑动窗口win:%u\n", ntohs(tcpheader->window));
	printf("tcp校验和     :%u\n", ntohs(tcpheader->checksum));
	printf("tcp紧急指针   :%u\n", ntohs(tcpheader->urgentPoint));

	unsigned int len = ntohs(ipheader->ipLen) - 4 * (ipheader->headerLen) - 4 * (tcpheader->headerLen);
	if (len == 0) { goto END; }
	printf("开始解析tcp数据报,将以ASCII形式解析......\n");
	char *data = (char *)alloca(len + 1);
	strncpy(data, tcpheader->options, len);
	data[len] = 0;
	int tmp;
	for (i = 0; i < len; i+=tmp)
	{
		tmp = printf("%s\n", &data[i]);
	}
	sleep(1);

#endif

	printf("\n");

END:
	printf("解析结束.\n\n");
	return 0;
}

void printMyInfo()
{
	printf("本机网卡名称:\"%s\",本机MAC地址:[%02x:%02x:%02x:%02x:%02x:%02x]\n",
	ifstruct.ifr_name,
	(unsigned char)(ifstruct.ifr_hwaddr.sa_data[0]),
	(unsigned char)(ifstruct.ifr_hwaddr.sa_data[1]),
	(unsigned char)(ifstruct.ifr_hwaddr.sa_data[2]),
	(unsigned char)(ifstruct.ifr_hwaddr.sa_data[3]),
	(unsigned char)(ifstruct.ifr_hwaddr.sa_data[4]),
	(unsigned char)(ifstruct.ifr_hwaddr.sa_data[5]));
}

猜你喜欢

转载自blog.csdn.net/qq_39937902/article/details/82379434