您的位置:首页 > 理论基础 > 计算机网络

linux 高级网络编程进阶之rawsocket

2016-11-04 17:51 507 查看
在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 2048

int main(int argc, char **argv)
{
int rawsock;
char buffer[BUFFER_MAX];
char * ethhead;
char * iphead;
char * phead;

// create rawsocket
if ((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 mac
fprintf(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 address
printf("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报文头, 将数据包发出去,用这
4000
种方式可以伪造ip, 写Dos 攻击程序等。 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息