linux 高级网络编程进阶之rawsocket

来源:互联网 发布:sencha architect mac 编辑:程序博客网 时间:2024/04/27 14:57

在linux套接字编程中,常见的套接字类型有SOCK_STREAM, SOCK_DGRAM  . 

int socket(int domain, int type, int protocal), 其中 type 字段的 选项可以是:

    SOCK_STREAM

    SOCK_DGRAM

    SOCK_SEQPACKET

    SOCK_RAW

          Provides raw network protocal access.

    SOCK_RDM

    SOCK_PACKET

原始套接字rawsocket 因其能获得最底层的IP包,因而有其特殊的作用。其用处,例如:

1. 怎样发送一个自定义的IP包 ?

2. 怎样发送一个ICMP协议包 ?

3. 怎样分析所有经过网络的包 ,而不管这包是否是发给自己的?

4. 怎样伪装本地IP地址 ?

以上所有这些,原始套接字(SOCK_RAW), 都可以帮你实现! !

(tips: 原始套接字广泛应用于高级网络编程,也是一种广泛的黑客手段,著名的网络sniffer, 拒绝服务攻击Dos IP欺骗 等都可以通过原始套接字实现)

原始套接字的使用,有一点需要注意:只有管理员权限 (root) 才可使用

在创建套接字时的第三个选项 protocal , 可以指定获取某一类型的 rawsocket

    socket(PF_PACKET, SOCK_RAW, int protocal )

protocal 选项:

    - 不能为0

    - 传参数时需要使用 htons() 转换

ETH_P_IP : IPv4 数据包

ETH_P_ARP : ARP 数据包

ETH_P_ALL : 任何协议的数据包

下面将介绍一种,最简单最有效的 rawsocket 应用:抓取ip包

在此之前,有两点需要了解:一是 以太网帧结构,二是ip 包报文头结构

Frame 以太网帧结构


 从帧首界定符SFD之后, 从 DA 开始 就是 帧结构的帧头了,这里介绍一下前导码

前导码:

  10101010  10101010 10101010   10101010   10101010   10101010   10101010  10101011 

前导码的作用是通知接收节点做好接收准备, 接收节点收到 10101011 后就知道帧 开始了

IP报文头



了解以上两个头部之后,就可以使用rawsocket了, 这里给出一个头部分析的例子

analysis_rawsocket.c 

#include <stdio.h>#include <stdlib.h>#include <sys/socket.h>#include <unistd.h>#include <sys/types.h>#include <linux/if_ether.h>#include <linux/in.h>#define BUFFER_MAX 2048int main(int argc, char **argv){int rawsock;char buffer[BUFFER_MAX];char * ethhead;char * iphead;char * phead;// create rawsocketif ((rawsock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0){perror("rawsock create error");exit(1);}long framecount = 0;while ( 1 ) {int readnum = recvfrom(rawsock, buffer, 2048, 0 , NULL, NULL);if (readnum < 42) {printf("error: head is incomplete! \n");exit(1);}ethhead = (char *)buffer;phead = ethhead;int ethernetmask = 0XFF;framecount++;printf("--------------Analysis Packet[%d]-------------\n", framecount);// printf src mac and dst macfprintf(stderr, "MAC:");int i = 6;for (; i <= 11; i++)fprintf(stderr, "%.2x:", phead[i]ðernetmask);fprintf(stderr, "--------->");for (i = 0; i <= 5; i++)fprintf(stderr, "%.2x", phead[i]ðernetmask);printf("\n");iphead = ethhead + 14;phead = iphead + 12;// print ip addressprintf("IP:");for (i = 0; i<= 3; i++) {printf("%d", phead[i]ðernetmask);if (i !=3) {printf(".");}}printf("---------->");for (i = 4; i <= 7; i++) {printf("%d", phead[i] & ethernetmask);if (i != 7) {printf(".");}}printf("\n");int prototype = (iphead + 9)[0];phead = iphead + 20;// print Protocal mesg printf("Protocal:");switch(prototype) {case IPPROTO_ICMP:printf("ICMP\n");break;case IPPROTO_IGMP:printf("IGMP\n");break;case IPPROTO_TCP:printf("TCP | source port: %u |", (phead[0]<<8) & 0xFF00 | phead[1] & 0xFF);printf("destport: %u\n", (phead[2]<<8) & 0xFF00 | phead[3] & 0xFF);int i;for (i = 0; i < readnum -54; i++)putchar(phead[19 + i]);break;case IPPROTO_UDP:printf("UDP | source port: %u |", (phead[0]<<8) & 0xFF00 | phead[1]&0xFF);printf("destport: %u\n", (phead[2]<<8) & 0XFF00 | phead[3] & 0xFF);break;case IPPROTO_RAW:printf("RAW:\n");break;default:printf("Unknow\n");}printf("-------------------------------END-------------------------------\n");}return 0;}

以上rawsocket 可以抓取通过网卡的所有数据包, 分析了收到的数据报类型, 并着重就 tcp 报文, 抓取了报文内容并打印出来。


总结与分析:

    使用字段分析的编程方式,原理简单, 只要对帧头和ip头的字段熟悉, 可以用非常小的代码量就写出抓包工具, 并且方便自定义修改; 

    以上是将截取到的 数据报 分析出来, 下一次博客内容将 自定义IP 包, 自己填充ip报文头, 将数据包发出去,用这种方式可以伪造ip, 写Dos 攻击程序等。 



0 0
原创粉丝点击