原始套接字学习笔记(1)

一般来说,我们会用到如下三种套接字:

TCP:SOCK_STREAM套接字

UDP:SOCK_DGRAM套接字

原始套接字:SOCK_RAW套接字

对于TCP和UDP两种套接字,相对来说只要配置好IP地址和端口号就可以了,比较简单,这里我们主要介绍原始套接字的使用。

1.原始套接字简介

  原始套接字的强大之处在于,不同与UDP和TCP套接字只能访问传输层和传输层以上的数据包,原始套接字可以访问传输层以下的数据包,实现上至应用层下至链路层的数据操作,尤其适合用来进行抓包等工作。

2.原始套接字的建立

  常用的原始套接字的建立方式有如下两种:

int sockfd=socket(PF_PACKET,SOCK_PACKET,htons(ETH_P_ALL));//这个socket可以访问处理链路层及以上所有的数据包
int sockfd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP));//这个socket可以访问处理链路层及以上所有的IP数据包

3.原始套接字权限

  原始套接字需要root权限即管理员权限才能够创建,所以需要sudo和su进入root模式,而且在使用原始套接字进行抓包的过程中需要设置网卡为混杂模式。

  下面给出两个例子,供大家参考:

  第一个,利用原始套接字进行发包,使用wireshark抓包查看;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>

#include <net/if.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include <netpacket/packet.h>
#include <netdb.h>

#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <resolv.h>
#include <signal.h>
#include <getopt.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

//首先定义网卡端口
#define PHYSICALPORT "eth0"
#define PHYSICALPORT_LEN 30

//创建实际网卡端口数组
char physical_port[30];

//定义缓存大小
#define BUFSIZE 1024*5
char sendbuf[BUFSIZE]={0};

int main()
{
    memcpy(physical_port,PHYSICALPORT,PHYSICALPORT_LEN);
     int sock_send;
    sock_send=socket(PF_PACKET,SOCK_PACKET,htons(ETH_P_ALL));
    if(sock_send<0)
    {
        perror("scoket created");
    }

    //设置发包地址
    struct sockaddr send_addr;
    memset(&send_addr,0,sizeof(send_addr));
    strcpy(send_addr.sa_data,physical_port);

    //创建发送程序
    while(fgets(sendbuf,sizeof(sendbuf),stdin)!=0)
    {
        int len=sendto(sock_send,&sendbuf,strlen(sendbuf),0,&send_addr,sizeof(send_addr));
        memset(sendbuf,0,sizeof(sendbuf));
    }
    return 0;
}

  使用wireshark抓包可以得到如下结果:

   第二个,使用原始套接字建立一个接收程序,抓取本机的数据进行分析:

   代码如下:

   

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>

#include <net/if.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include <netpacket/packet.h>
#include <netdb.h>

#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <resolv.h>
#include <signal.h>
#include <getopt.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

//首先定义网卡端口
#define PHYSICALPORT "eth0"
#define PHYSICALPORT_LEN 30

//创建实际网卡端口数组
char physical_port[30];

//定义缓存大小
#define BUFSIZE 1024*5
char recvbuf[BUFSIZE]={0};

void ethernet_setpormisc(int fd,int i_flags);

int main()
{
    memcpy(physical_port,PHYSICALPORT,PHYSICALPORT_LEN);
    //首先创建一个原始套接字
    int sock_recv;
    sock_recv=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
    if(sock_recv<0)
    {
        perror("physicl socket created");
    }

    //设置网卡为混杂模式;
    ethernet_setpormisc(sock_recv,1);

    //setsockopt
    int recvbuf_size=BUFSIZE;
    setsockopt(sock_recv,SOL_SOCKET,SO_RCVBUF,&recvbuf,sizeof(char));

    //获取物理网卡接口索,用以传输数据
    struct ifreq ifr_recv;
    strcpy(ifr_recv.ifr_name,physical_port);
    if(ioctl(sock_recv,SIOCGIFINDEX,&ifr_recv)<0)
    {
        perror("[3]get interface index");
    }
    //绑定物理网卡
    struct sockaddr_ll local_addr;
    local_addr.sll_family=PF_PACKET;
    local_addr.sll_ifindex=ifr_recv.ifr_ifindex;
    local_addr.sll_protocol=htons(ETH_P_ALL);
    if((bind(sock_recv,(struct sockaddr *)&local_addr,sizeof(local_addr)))<0)
    {
        perror("[4]bind physical address");
    }   

    //开始接收数据包
    while(1)
    {
        recvfrom(sock_recv,recvbuf,BUFSIZE,0,NULL,NULL);
        printf("%s",recvbuf);
        memset(recvbuf,0,sizeof(recvbuf));
    }
    close(sock_recv);
}

//创建设置网卡混杂模式函数
void ethernet_setpormisc(int fd,int i_flags)
{
    //首先获取网卡接口标志位
    struct ifreq ifr_s;
    memcpy(ifr_s.ifr_name,physical_port,sizeof(physical_port));
    if(ioctl(fd,SIOCGIFFLAGS,&ifr_s)<0)
    {
        perror("[1]get interface flags");
    }
    if(i_flags==0)
    {
        //取消混杂模式
        ifr_s.ifr_flags &= ~IFF_PROMISC;
    }
    else
    {
        //设置为混杂模式
        ifr_s.ifr_flags |= IFF_PROMISC;
    }   

    //将接口设置为相应的模式
    if(ioctl(fd,SIOCSIFFLAGS,&ifr_s)<0)
    {
        perror("[2]set interface flags");
    }
}

  抓取到的数据包如下所示: 

 

  因为以%s字符串形式打印出来,所以有很多乱码,这里需要再写包解析函数进行解析!

时间: 2024-12-20 04:47:24

原始套接字学习笔记(1)的相关文章

【DAY16】第十六天Socket套接字学习笔记

序列化的补充知识点 ---------------- 将对象用于网络间传输或者本地化存储. 也叫串行化 过程将java对象转换成byte[] java.io.Serializable //可串行化接口,标识性.JVM. 串行化版本UID : // 深度复制是指将整个对象图进行复制. transient : //临时关键字. ObjectInputStream / ObjectOuputStream ByteArrayInputStream / ByteArrayOutputStream read

Linux网络编程:原始套接字的魔力【上】

基于原始套接字编程 在开发面向连接的TCP和面向无连接的UDP程序时,我们所关心的核心问题在于数据收发层面,数据的传输特性由TCP或UDP来保证: 也就是说,对于TCP或UDP的程序开发,焦点在Data字段,我们没法直接对TCP或UDP头部字段进行赤裸裸的修改,当然还有IP头.换句话说,我们对它们头部操作的空间非常受限,只能使用它们已经开放给我们的诸如源.目的IP,源.目的端口等等. 今天我们讨论一下原始套接字的程序开发,用它作为入门协议栈的进阶跳板太合适不过了.OK闲话不多说,进入正题. 原始

Linux 原始套接字--myping的实现

一.套接字的类型 A.流套接字(SOCK_STREAM) 用于提供面向连接.可靠的数据传输服务,其使用传输层的TCP协议 B.数据报套接字(SOCK_DGRAM) 用于提供一个无连接.不可靠的服务,其使用传输层上的UDP协议 C.原始套接字(SOCK_RAM) 原始套接字是相对表中套接字(即前面两种套接字)而言的.它与标准套接字的区别是原始套接字可以读写内核没有处理的IP数据包,流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据. 所以要访问其他协议的数据必须使用原始套接字.

Linux基础(11)原始套接字

一边接收函数返回一边判断返回值时一定要把接收的优先级加()提高再去判断 例 if((sockfd = socket()) < 0) 问题: 如何实现SYN扫描器扫描端口 , 比如AB两个设备要进行连接 , A通过端口发一个SYN包给B,B在收到后返回一个ACK包确认连接 , 但是在不确定B端口号时 该如何进行连接 , 答: A给B的每一个端都发一个SYN包, 如果哪个有返回说明端口是开放的, TCP和UDP都无法发实现这样的连接方式 , 所以要使用原始套接字 #include <netinet

linux netlink套接字学习资料

理论: http://blog.csdn.net/unbutun/article/details/3394061 进一步深入: http://edsionte.com/techblog/archives/4134 http://edsionte.com/techblog/archives/4140 http://edsionte.com/techblog/archives/4134 实践: http://bbs.chinaunix.net/thread-3766684-1-1.html 附录代码

python使用原始套接字 解析原始ip头数据

使用底层套接字解码底层流量,是这次做的重点工作. 首先来捕获第一个包 # coding:utf-8import socket # 监听的主机IP host = "192.168.1.100" socket_protocol = socket.IPPROTO_ICMP sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol) sniffer.bind((host, 0)) sniffer.setso

linux原始套接字(3)-构造IP_TCP发送与接收

一.概述                                                    tcp报文封装在ip报文中,创建tcp的原始套接字如下: 1 sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP); 此时只能构造tcp报文,如果想进一步构造ip首部,那么就要开启sockfd的IP_HDRINCL选项: 1 int on = 1; 2 setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on

linux原始套接字(2)-icmp请求与接收

一.概述                                                    上一篇arp请求使用的是链路层的原始套接字.icmp封装在ip数据报里面,所以icmp请求可以直接使用网络层的原始套接字,即socket()第一个参数是PF_INET.如下: 1 sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); icmp报文不同的类型有不同的格式,我们以icmp回显请求和会显应答报文格式(即ping程序使用的报文类型)

linux原始套接字(1)-arp请求与接收

一.概述                                                   以太网的arp数据包结构: arp结构op操作参数:1为请求,2为应答. 常用的数据结构如下: 1.物理地址结构位于netpacket/packet.h 1 struct sockaddr_ll 2 { 3 unsigned short int sll_family; 4 unsigned short int sll_protocol; 5 int sll_ifindex; 6 unsi