#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