在本教程中,让我们来看一看,原始套接字如何被用来绕过传统的TCP/IP协议栈,接收数据包,然后把那些数据包发送给特定的用户程序。
如果你不具备Linux内核相关的知识,但是对网络报文的内容感兴趣,那么原始套接字就是你所需要的。一个原始套接字是用来接收原始报文的。这意味着在以太层接收到的报文将会直接传递给原始套接字。准确地说,一个原始套接字绕过通常的TCP/IP处理过程,将报文发送给特定的用户程序。
原始套接字 vs 其他套接字
其他套接字,例如字节流套接字和数据报套接字,从传输层接收数据。这些数据不包含头部(header),只包含净荷(payload)。这意味着,没有关于源IP地址和MAC地址的信息。运行在同一台机器或不同机器上的程序在通信时,只能交换数据。
原始套接字的用途是完全不同的。原始套接字允许程序直接访问更低层的协议。这意味着原始套接字接收未提取(un-extracted)的报文。与字节流/数据报套接字不同,没有必要给一个原始套接字提供端口和IP地址。
网络报文与报文嗅探器
当一个应用程序向网络中发送数据时,数据会被许多的网络层次处理。在发送数据之前,它会被网络层次的各种头部包裹。包含所有的信息,例如原地址和目的地址,的被包裹的数据,被称为网络报文。根据以太网协议,有许多不同种类的网络报文,例如IP报文,Xerox PUP报文,Ethernet Loopback报文等。在Linux中,可以再if_ether.h中找到所有的协议。
当我们连接到因特网时,我们接收网络报文,我们的机器提取所有的网络层头部,将数据发送给特定的的程序。例如,当我们在浏览器中输入www.google.com时,我们收到来自Google的报文,我们的机器提取所有的头部,将数据传送给浏览器。
默认情况下,一台机器只会接受目的地之是它自己的报文,这种模式成为非混杂(Non-promiscuous)模式。但是,如果我们想要接收所有的报文,我们就必须切换到混杂(Promiscuous)模式。通过使用ioctl,我们可以切换到混杂模式。
如果我们对不同网络层次头部的内容或者结构感兴趣,我们可以借助报文嗅探器来访问这些信息。在Linux中有许多可用的报文嗅探器,例如Wireshark、tcpdump。
一个使用原始套接字的报文嗅探器
要开发一个报文嗅探器,首先要打开一个原始套接字。只有effective user ID是0或者具有CAP_NET_RAW能力的进程才被允许打开原始套接字。
打开一个原始套接字
要打开一个套接字,要知道三样东西
- 协议族
- 类型
- 协议
对于一个原始套接字,协议族是AF_PACKET,协议类型是SOCK_RAW,协议请参看if_ether.h头文件。要接受所有报文,可以将协议设置为宏ETH_P_ALL;要接受所有IP报文,可以将协议设置为宏ETH_P_IP。
int sock_r;
sock_r = socket(AP_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(sock_r < 0)
{
printf("error in socket\n");
return -1;
}
/* Note: run this program as root user
* Author:Subodh Saxena
*/
#include<stdio.h>
#include<malloc.h>
#include<string.h>
#include<signal.h>
#include<stdbool.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<linux/if_packet.h>
#include<netinet/in.h>
#include<netinet/if_ether.h> // for ethernet header
#include<netinet/ip.h> // for ip header
#include<netinet/udp.h> // for udp header
#include<netinet/tcp.h>
#include<arpa/inet.h> // to avoid warning at inet_ntoa
FILE* log_txt;
int total,tcp,udp,icmp,igmp,other,iphdrlen;
struct sockaddr saddr;
struct sockaddr_in source,dest;
void ethernet_header(unsigned char* buffer,int buflen)
{
struct ethhdr *eth = (struct ethhdr *)(buffer);
fprintf(log_txt,"\nEthernet Header\n");
fprintf(log_txt,"\t|-Source Address : %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n",eth->h_source[0],eth->h_source[1],eth->h_source[2],eth->h_source[3],eth->h_source[4],eth->h_source[5]);
fprintf(log_txt,"\t|-Destination Address : %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n",eth->h_dest[0],eth->h_dest[1],eth->h_dest[2],eth->h_dest[3],eth->h_dest[4],eth->h_dest[5]);
fprintf(log_txt,"\t|-Protocol : %d\n",eth->h_proto);
}
void ip_header(unsigned char* buffer,int buflen)
{
struct iphdr *ip = (struct iphdr*)(buffer + sizeof(struct ethhdr));
iphdrlen =ip->ihl*4;
memset(&source, 0, sizeof(source));
source.sin_addr.s_addr = ip->saddr;
memset(&dest, 0, sizeof(dest));
dest.sin_addr.s_addr = ip->daddr;
fprintf(log_txt , "\nIP Header\n");
fprintf(log_txt , "\t|-Version : %d\n",(unsigned int)ip->version);
fprintf(log_txt , "\t|-Internet Header Length : %d DWORDS or %d Bytes\n",(unsigned int)ip->ihl,((unsigned int)(ip->ihl))*4);
fprintf(log_txt , "\t|-Type Of Service : %d\n",(unsigned int)ip->tos);
fprintf(log_txt , "\t|-Total Length : %d Bytes\n",ntohs(ip->tot_len));
fprintf(log_txt , "\t|-Identification : %d\n",ntohs(ip->id));
fprintf(log_txt , "\t|-Time To Live : %d\n",(unsigned int)ip->ttl);
fprintf(log_txt , "\t|-Protocol : %d\n",(unsigned int)ip->protocol);
fprintf(log_txt , "\t|-Header Checksum : %d\n",ntohs(ip->check));
fprintf(log_txt , "\t|-Source IP : %s\n", inet_ntoa(source.sin_addr));
fprintf(log_txt , "\t|-Destination IP : %s\n",inet_ntoa(dest.sin_addr));
}
void payload(unsigned char* buffer,int buflen)
{
int i=0;
unsigned char * data = (buffer + iphdrlen + sizeof(struct ethhdr) + sizeof(struct udphdr));
fprintf(log_txt,"\nData\n");
int remaining_data = buflen - (iphdrlen + sizeof(struct ethhdr) + sizeof(struct udphdr));
for(i=0;i<remaining_data;i++)
{
if(i!=0 && i%16==0)
fprintf(log_txt,"\n");
fprintf(log_txt," %.2X ",data[i]);
}
fprintf(log_txt,"\n");
}
void tcp_header(unsigned char* buffer,int buflen)
{
fprintf(log_txt,"\n*************************TCP Packet******************************");
ethernet_header(buffer,buflen);
ip_header(buffer,buflen);
struct tcphdr *tcp = (struct tcphdr*)(buffer + iphdrlen + sizeof(struct ethhdr));
fprintf(log_txt , "\nTCP Header\n");
fprintf(log_txt , "\t|-Source Port : %u\n",ntohs(tcp->source));
fprintf(log_txt , "\t|-Destination Port : %u\n",ntohs(tcp->dest));
fprintf(log_txt , "\t|-Sequence Number : %u\n",ntohl(tcp->seq));
fprintf(log_txt , "\t|-Acknowledge Number : %u\n",ntohl(tcp->ack_seq));
fprintf(log_txt , "\t|-Header Length : %d DWORDS or %d BYTES\n" ,(unsigned int)tcp->doff,(unsigned int)tcp->doff*4);
fprintf(log_txt , "\t|----------Flags-----------\n");
fprintf(log_txt , "\t\t|-Urgent Flag : %d\n",(unsigned int)tcp->urg);
fprintf(log_txt , "\t\t|-Acknowledgement Flag : %d\n",(unsigned int)tcp->ack);
fprintf(log_txt , "\t\t|-Push Flag : %d\n",(unsigned int)tcp->psh);
fprintf(log_txt , "\t\t|-Reset Flag : %d\n",(unsigned int)tcp->rst);
fprintf(log_txt , "\t\t|-Synchronise Flag : %d\n",(unsigned int)tcp->syn);
fprintf(log_txt , "\t\t|-Finish Flag : %d\n",(unsigned int)tcp->fin);
fprintf(log_txt , "\t|-Window size : %d\n",ntohs(tcp->window));
fprintf(log_txt , "\t|-Checksum : %d\n",ntohs(tcp->check));
fprintf(log_txt , "\t|-Urgent Pointer : %d\n",tcp->urg_ptr);
payload(buffer,buflen);
fprintf(log_txt,"*****************************************************************\n\n\n");
}
void udp_header(unsigned char* buffer, int buflen)
{
fprintf(log_txt,"\n*************************UDP Packet******************************");
ethernet_header(buffer,buflen);
ip_header(buffer,buflen);
fprintf(log_txt,"\nUDP Header\n");
struct udphdr *udp = (struct udphdr*)(buffer + iphdrlen + sizeof(struct ethhdr));
fprintf(log_txt , "\t|-Source Port : %d\n" , ntohs(udp->source));
fprintf(log_txt , "\t|-Destination Port : %d\n" , ntohs(udp->dest));
fprintf(log_txt , "\t|-UDP Length : %d\n" , ntohs(udp->len));
fprintf(log_txt , "\t|-UDP Checksum : %d\n" , ntohs(udp->check));
payload(buffer,buflen);
fprintf(log_txt,"*****************************************************************\n\n\n");
}
void data_process(unsigned char* buffer,int buflen)
{
struct iphdr *ip = (struct iphdr*)(buffer + sizeof (struct ethhdr));
++total;
/* we will se UDP Protocol only*/
switch (ip->protocol) //see /etc/protocols file
{
case 6:
++tcp;
tcp_header(buffer,buflen);
break;
case 17:
++udp;
udp_header(buffer,buflen);
break;
default:
++other;
}
printf("TCP: %d UDP: %d Other: %d Toatl: %d \r",tcp,udp,other,total);
}
int main()
{
int sock_r,saddr_len,buflen;
unsigned char* buffer = (unsigned char *)malloc(65536);
memset(buffer,0,65536);
log_txt=fopen("log.txt","w");
if(!log_txt)
{
printf("unable to open log.txt\n");
return -1;
}
printf("starting .... \n");
sock_r=socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(sock_r<0)
{
printf("error in socket\n");
return -1;
}
while(1)
{
saddr_len=sizeof saddr;
buflen=recvfrom(sock_r,buffer,65536,0,&saddr,(socklen_t *)&saddr_len);
if(buflen<0)
{
printf("error in reading recvfrom function\n");
return -1;
}
fflush(log_txt);
data_process(buffer,buflen);
}
close(sock_r);// use signals to close socket
printf("DONE!!!!\n");
}
/* Note: run this program as root user
* Author:Subodh Saxena
*/
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/ioctl.h>
#include<net/if.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<netinet/if_ether.h>
#include<netinet/udp.h>
#include<linux/if_packet.h>
#include<arpa/inet.h>
struct ifreq ifreq_c,ifreq_i,ifreq_ip; /// for each ioctl keep diffrent ifreq structure otherwise error may come in sending(sendto )
int sock_raw;
unsigned char *sendbuff;
#define DESTMAC0 0xd0
#define DESTMAC1 0x67
#define DESTMAC2 0xe5
#define DESTMAC3 0x12
#define DESTMAC4 0x6f
#define DESTMAC5 0x8f
#define destination_ip 10.240.253.10
int total_len=0,send_len;
void get_eth_index()
{
memset(&ifreq_i,0,sizeof(ifreq_i));
strncpy(ifreq_i.ifr_name,"wlan0",IFNAMSIZ-1);
if((ioctl(sock_raw,SIOCGIFINDEX,&ifreq_i))<0)
printf("error in index ioctl reading");
printf("index=%d\n",ifreq_i.ifr_ifindex);
}
void get_mac()
{
memset(&ifreq_c,0,sizeof(ifreq_c));
strncpy(ifreq_c.ifr_name,"wlan0",IFNAMSIZ-1);
if((ioctl(sock_raw,SIOCGIFHWADDR,&ifreq_c))<0)
printf("error in SIOCGIFHWADDR ioctl reading");
printf("Mac= %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n",(unsigned char)(ifreq_c.ifr_hwaddr.sa_data[0]),(unsigned char)(ifreq_c.ifr_hwaddr.sa_data[1]),(unsigned char)(ifreq_c.ifr_hwaddr.sa_data[2]),(unsigned char)(ifreq_c.ifr_hwaddr.sa_data[3]),(unsigned char)(ifreq_c.ifr_hwaddr.sa_data[4]),(unsigned char)(ifreq_c.ifr_hwaddr.sa_data[5]));
printf("ethernet packaging start ... \n");
struct ethhdr *eth = (struct ethhdr *)(sendbuff);
eth->h_source[0] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[0]);
eth->h_source[1] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[1]);
eth->h_source[2] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[2]);
eth->h_source[3] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[3]);
eth->h_source[4] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[4]);
eth->h_source[5] = (unsigned char)(ifreq_c.ifr_hwaddr.sa_data[5]);
eth->h_dest[0] = DESTMAC0;
eth->h_dest[1] = DESTMAC1;
eth->h_dest[2] = DESTMAC2;
eth->h_dest[3] = DESTMAC3;
eth->h_dest[4] = DESTMAC4;
eth->h_dest[5] = DESTMAC5;
eth->h_proto = htons(ETH_P_IP); //0x800
printf("ethernet packaging done.\n");
total_len+=sizeof(struct ethhdr);
}
void get_data()
{
sendbuff[total_len++] = 0xAA;
sendbuff[total_len++] = 0xBB;
sendbuff[total_len++] = 0xCC;
sendbuff[total_len++] = 0xDD;
sendbuff[total_len++] = 0xEE;
}
void get_udp()
{
struct udphdr *uh = (struct udphdr *)(sendbuff + sizeof(struct iphdr) + sizeof(struct ethhdr));
uh->source = htons(23451);
uh->dest = htons(23452);
uh->check = 0;
total_len+= sizeof(struct udphdr);
get_data();
uh->len = htons((total_len - sizeof(struct iphdr) - sizeof(struct ethhdr)));
}
unsigned short checksum(unsigned short* buff, int _16bitword)
{
unsigned long sum;
for(sum=0;_16bitword>0;_16bitword--)
sum+=htons(*(buff)++);
do
{
sum = ((sum >> 16) + (sum & 0xFFFF));
}
while(sum & 0xFFFF0000);
return (~sum);
}
void get_ip()
{
memset(&ifreq_ip,0,sizeof(ifreq_ip));
strncpy(ifreq_ip.ifr_name,"wlan0",IFNAMSIZ-1);
if(ioctl(sock_raw,SIOCGIFADDR,&ifreq_ip)<0)
{
printf("error in SIOCGIFADDR \n");
}
printf("%s\n",inet_ntoa((((struct sockaddr_in*)&(ifreq_ip.ifr_addr))->sin_addr)));
/****** OR
int i;
for(i=0;i<14;i++)
printf("%d\n",(unsigned char)ifreq_ip.ifr_addr.sa_data[i]); ******/
struct iphdr *iph = (struct iphdr*)(sendbuff + sizeof(struct ethhdr));
iph->ihl = 5;
iph->version = 4;
iph->tos = 16;
iph->id = htons(10201);
iph->ttl = 64;
iph->protocol = 17;
iph->saddr = inet_addr(inet_ntoa((((struct sockaddr_in *)&(ifreq_ip.ifr_addr))->sin_addr)));
iph->daddr = inet_addr("destination_ip"); // put destination IP address
total_len += sizeof(struct iphdr);
get_udp();
iph->tot_len = htons(total_len - sizeof(struct ethhdr));
iph->check = htons(checksum((unsigned short*)(sendbuff + sizeof(struct ethhdr)), (sizeof(struct iphdr)/2)));
}
int main()
{
sock_raw=socket(AF_PACKET,SOCK_RAW,IPPROTO_RAW);
if(sock_raw == -1)
printf("error in socket");
sendbuff=(unsigned char*)malloc(64); // increase in case of large data.Here data is --> AA BB CC DD EE
memset(sendbuff,0,64);
get_eth_index(); // interface number
get_mac();
get_ip();
struct sockaddr_ll sadr_ll;
sadr_ll.sll_ifindex = ifreq_i.ifr_ifindex;
sadr_ll.sll_halen = ETH_ALEN;
sadr_ll.sll_addr[0] = DESTMAC0;
sadr_ll.sll_addr[1] = DESTMAC1;
sadr_ll.sll_addr[2] = DESTMAC2;
sadr_ll.sll_addr[3] = DESTMAC3;
sadr_ll.sll_addr[4] = DESTMAC4;
sadr_ll.sll_addr[5] = DESTMAC5;
printf("sending...\n");
while(1)
{
send_len = sendto(sock_raw,sendbuff,64,0,(const struct sockaddr*)&sadr_ll,sizeof(struct sockaddr_ll));
if(send_len<0)
{
printf("error in sending....sendlen=%d....errno=%d\n",send_len,errno);
return -1;
}
}
}
https://opensourceforu.com/2015/03/a-guide-to-using-raw-sockets/