一个简单的sniffer抓包程序

#include "unp.h"
#include <linux/if_ether.h>

/* Ethernet header are are always exactly 14 bytes */
#define SIZE_ETHERNET	14

/* Ethernet address are 6 bytes */
#define ETHER_ADDR_LEN	6

#define LINE_WIDTH		16

/*
+---------+---------+---------------------------------------------------+
| Version | Header  | Type of Service |           Total Length          |
|         |  Length |                 |                                 |
| (4bit)  | (4bit)  |   (8bit)        |             (16bit)             |
+---------+---------+-----------------+--------+------------------------+
|           Identification            |  Flag  |     Fragment Offset    |
|                                     |        |                        |
|               (16bit)               | (3bit) |        (13bit)         |
+-------------------+-----------------+--------+------------------------+
|   Time to Live    |    Protocol     |         Header Checksum         |
|                   |                 |                                 |
|      (8bit)       |     (8bit)      |            (16bit)              |
+-------------------+-----------------+---------------------------------+
|                              Source IP Address                        |
|                                                                       |
|                                  (32bit)                              |
------------------------------------------------------------------------+
|                           Destination IP Address                      |
|                                                                       |
|                                  (32bit)                              |
+-----------------------------------------------------------------------+
|                                 Options                               |
|                                                                       |
|                                  (32bit)                              |
+-----------------------------------------------------------------------+
|                                  Data                                 |
|                                                                       |
|                                  ...                                  |
+-----------------------------------------------------------------------+
*/


struct sniff_ip {
	u_char			ip_vhl;					/* version &0xf0 >> 4 | header len &0x0f*/
    unsigned char   ip_tos;					/* type of service*/
    unsigned short  ip_len;					/* total length */
    unsigned short  ip_id;					/* Identification */
    unsigned short  ip_off;					/* flag offset field */
	#define  IP_REF	0x800					/* reserved flagment flag */
	#define  IP_DF	0x400					/* dont fragment flag */
	#define  IP_MF	0x200					/* more fragment flag */
	#define  IP_OFFMASK	0x1fff				/* mask for fragment bits */
    unsigned char   ip_ttl;					/* time to live */				
    unsigned char   ip_protocol;			/* protocol */
    unsigned short  ip_cksum;				/* checksum */
    struct in_addr  ip_src;					/* source address */
    struct in_addr	ip_dst;					/* destination address */
};

#define IP_HL(ip)	((ip->ip_vhl) & 0x0f)
#define IP_V(ip)	(((ip->ip_vhl) & 0xf0) >> 4)

/*
Ehernet II帧结构:
帧头:6个字节的目的MAC地址和6字节的源MAC地址。2字节的类型字段,表示封装在数据中的数据类型。
数据:46-1500字节的数据字段。
帧尾 :4字节的帧效验序列。

+-------+--------------+----------------------------------------+------+
| D-MAC | S-MAC | TYPE |                    DATA                | CRC  |
| 6B    |  6B   | 2B   |                                        |  4B  |
+-------+--------------+----------------------------------------+------+ 

*/

struct sniff_ethernet {
	u_char	ether_dhost[ETHER_ADDR_LEN];	/* destination host address */
	u_char	ether_shost[ETHER_ADDR_LEN];	/* source host address */
	u_short ether_type;			/* IP? ARP? RARP? etc */
};

/* 
 TCP 格式
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               |                               |
|       Source Port             |        Destination Port       |
|                               |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                        Sequence Number                        |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                    Acknowledgment NUmber                      |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data |            |U|A|P|R|S|F|                               |
|offset|  Reserved  |R|C|S|S|Y|I|        Window                 |
|      |            |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               |                               |
|       Checksum                |          Urgent Pointer       |
|                               |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                               |               |
|                  Options                      |   Padding     |
|                                               |               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                             Data                              |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

typedef u_int tcp_seq;
struct sniff_tcp {
	u_short	th_sport;				/* source port */
	u_short th_dport;				/* destination port */
	tcp_seq	th_seq;					/* sequence number */
	tcp_seq th_ack;					/* acknowledgment number */
	u_char  th_offx2;				/* data offset, reserved , 因为保留位全为0,被拆成两部分*/
	#define TH_OFF(th) (((th->th_offx2) & 0xf0) >> 4) 
	u_char	th_flags;				/* flags */
	#define TH_FIN	0x01 
	#define TH_SYN	0x02 
	#define TH_RST	0x04 
	#define TH_PUSH	0x08 
	#define TH_ACK	0x10 
	#define TH_URG  0x20
	#define TH_ECE	0x40
	#define TH_CWR	0x80
	#define TH_FLAGS	(TH_FIN | TH_SYN | TH_RST | TH_ACK | TH_URG | TH_ECE | TH_CWR) 
	u_short	th_win;					/* window */
	u_short th_sum;					/* checksum */
	u_short th_urp;					/* urgent pointer */
};


/*
 * 打印一行(16个字节): offset hex ascii
 *
 * 00000   47 45 54 20 2f 20 48 54  54 50 2f 31 2e 31 0d 0a   GET / HTTP/1.1..
 */
void
print_hex_ascii_line(const u_char *payload,  ssize_t len, ssize_t offset) {
	ssize_t			i;
	int				gap;
	const u_char	*ch;

	/* offset */
	printf("%05d\t", offset);

	/* hex */
	ch = payload;
	for (i = 0; i < len; ++i) {
		printf("%02x ", *ch++);
		if (i == LINE_WIDTH / 2 - 1) 
			printf("%2c ", ' ');
	}
	if (len < LINE_WIDTH / 2) 
		printf("%2x ", ' ');	/* 不够8个字节时打印空格 */
	if (len < LINE_WIDTH) {
		gap = LINE_WIDTH - len;
		while(gap-- > 0) 
			printf("%2c ", ' ');
	}
	printf("\t");

	/* ascii */
	ch = payload;
	for (i = 0; i < len; ++i) {
		if (isprint(*ch))
			printf("%c", *ch);
		else
			printf("%c", '.');
		++ch;
	}
	printf("\n");
}

/*
 * 打印有效载荷(避免打印二进制)
 */
void
print_payload(const u_char *payload, ssize_t len) {
	ssize_t			len_rem = len;
	ssize_t			line_len;
	ssize_t			offset	= 0;
	const u_char	*ptr = payload;

	if (len_rem == 0) {
		return;
	}

	/* 只有一行 */
	if (len <= LINE_WIDTH) {
		print_hex_ascii_line(ptr, len, offset);
		return;
	}

	/* data跨越多行 */
	for ( ; ; ) {
		printf("len_rem: %d offset : %d\n", len_rem, offset);
		if (len_rem <= LINE_WIDTH) {
			print_hex_ascii_line(ptr, len_rem, offset);
			break;
		}
		print_hex_ascii_line(ptr, LINE_WIDTH, offset);
		len_rem -= LINE_WIDTH;
		ptr += LINE_WIDTH;
		offset += LINE_WIDTH;
	}
}

void
got_packet(const u_char *packet) {
	static int count			= 1;		/* packet counter */

	/* 声明指向分组的指针 */
	struct sniff_ethernet		*ethernet;
	struct sniff_tcp			*tcp;
	struct sniff_ip				*ip;
	const u_char				*p, *payload;

	ssize_t size_ip;
	ssize_t size_tcp;
	ssize_t size_payload;			/* 有效载荷 */

	printf("\nPacket number %d:\n", count++);

	printf("\t源主机\t\t\t目标主机\n");
	ethernet = (struct sniff_ethernet *)packet;
	p = ethernet->ether_shost;
	printf("\t%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
				p[6] & 0xff, p[7] & 0xff,p[8] & 0xff, p[9] & 0xff,p[10] & 0xff, p[11] & 0xff);
	p = ethernet->ether_dhost;
	printf("\t%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
			p[0] & 0xff, p[1] & 0xff,p[2] & 0xff, p[3] & 0xff, p[4] & 0xff, p[5] & 0xff);

	ip = (struct sniff_ip *)(packet + SIZE_ETHERNET);
	size_ip = IP_HL(ip) * 4;
	if (size_ip < 20) {
		printf(" Invalid IP header length: %zu\n", size_ip);
		return;
	}

	printf("\tFrom:%s", inet_ntoa(ip->ip_src));
	printf("\tTo:%s\n", inet_ntoa(ip->ip_dst));

	switch(ip->ip_protocol) {
		case IPPROTO_TCP:
			printf("\tProtocl: TCP\n");
			break;
		case IPPROTO_UDP:
			printf("\tProtocl: UDP\n");
			return;
		case IPPROTO_ICMP:
			printf("\tProtocl: ICMP\n");
			return;
		case IPPROTO_IP:
			printf("\tProtocl: IP\n");
			return;
		case IPPROTO_RAW:
			printf("\tProtocl: RAW\n");
			return;
		default:
			printf("\tProtocl: Unknown\n");
			return;
	}

	/*
	 * 只处理tcp
	 */

	/* 定义和计算tcp head偏移 */
	tcp = (struct sniff_tcp *)(packet + SIZE_ETHERNET + size_ip);
	size_tcp = TH_OFF(tcp) * 4;
	if (size_tcp < 20) {
		printf("\tInvalid TCP header length:%zu bytes\n", size_tcp);
		return;
	}
	
	printf("\t源端口: %d\t\t目标端口:%d\n", tcp->th_sport, tcp->th_dport);

	/* 定义/计算有效载荷段偏移 */
	payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp); 

	/* 计算tcp 载荷大小 */
	size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);

	/*
	 * 打印有效载荷的长度
	 */
	if (size_payload > 0) {
		printf("\tPayload (%zu bytes): \n", size_payload);
		print_payload(payload, size_payload);
	}
}

/*
* PF_PACKET协议簇可以让一个应用程序把数据包变成似乎从网络层接收的样子
* 但是没有办法抓到那些不是发向自己主机的包。
* 正如我们前面看到的,网卡丢弃所有不含有主机MAC地址的数据包
* 这是因为网卡处于非混杂模式,即每个网卡只处理源地址是它自己的帧!
* 只有三个例外:
* 			如果一个帧的目的MAC地址是一个受限的广播地址(255.255.255.255)那么它将被所有的网卡接收:
* 			如果一个帧的目的地址是组播地址,那么它将被那些打开组播接收功能的网卡所接收;
* 			网卡如被设置成混杂模式,那么它将接收所有流经它的数据包
*/

int
main() {
    int                 sockfd;
    ssize_t             n;
    u_char              buf[MAXLINE];

    if ((sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0) {
        perror("create sockt");
        exit(EXIT_FAILURE);
    }

	//抓包
	for ( ; ; ) {
		n = recvfrom(sockfd, buf, MAXLINE, 0, 0, 0);


		/*******************************************
		 ** 14   6(dest)+6(source)+2(type or length)
		 ** +
		 ** 20   ip header 
		 ** +
		 ** 8    icmp,tcp or udp header
		 ** = 42
		 *********************************************/

		if (n < 42) {
			fprintf(stdout, "Incomplete header, packet corrupt.\n"); 
			continue;
		}

		got_packet(buf);
	}

	return 0;
}


说明:  此程序中的unp.h 是参考UNP这本书,但是只是简单的包含了socket必须的头文件以及几个出错打印函数。

参考:  Unix Network Programming -v3

                  http://blog.csdn.net/linux_embedded/article/details/8836847     

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值