网络编程原始套接字
SOCKET_STREAM 流式套接字
SOCKET_DGRAM
SOCKET_RAW 原始套接字
IPPROTO_IP IP协议
IPPROTO_IGMP INTERNET 网关服务协议,在多播中用到
在AF_INET地址族下,有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW三种套接字类型。SOCK_STREAM也就是通常所说的TCP,而SOCK_DGRAM则是通常所说的UDP,而SOCK_RAW则是用于提供一些较低级的控制的;第3个参数依赖于第2个参数,用于指定套接字所用的特定协议,设为0表示使用默认的协议。
希望各位參與,進來的順便UP一下.
RAW SOCKET能够对较低层次的协议直接访问,网络监听技术很大程度上依赖于它。该文介绍了利用RAW SOCKET捕获网络底层数据包的步骤和方法,并开发了一个程序模型来进一步探讨了利用RAW SOCKET捕获数据包的方法。
【关键词】 RAW SOCKET;捕获;数据包
1 引言
随着信息技术的快速发展,网络已成为信息交换的主要手段,一些网络新业务在不断地兴起,如电子商务、移动支付等,这些都对网络安全提出了较高的要求。与此同时,黑客对网络的攻击从未停止,网络的安全问题变得日趋严峻。
很多网络攻击都是从监听开始的,网络监听最重要一步就是捕获局域网中的数据帧,因此,研究数据捕获技术对于保障网络安全有着重要的意义。
2 RAW SOCKET简介
同一台主机不同进程可以用进程号来唯一标识,但是在网络环境下进程号并不能唯一标识该进程。TCP/IP主要引入了网络地址、端口和连接等概念来解决网络间进程标识问题。套接字(Socket)是一个指向传输提供者的句柄,TCP/IP协议支持3种类型的套接字,分别是流式套接字、数据报式套接字和原始套接字。
流式套接字(SOCKET_STREAM)提供了面向连接、双向可靠的数据流传输服务。数据报式套接字(SOCKET_ DGRAM)提供了无连接服务,不提供无错保证。原始套接字(SOCKET_RAW)允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW。
3 RAW SOCKET编程
要使用原始套接字,必须经过创建原始套接字、设置套接字选项和创建并填充相应协议头这三个步骤,然后用send、WSASend函数将组装好的数据发送出去。接收的过程也很相似,只是需要用recv或WSARecv函数接收数据。下面介绍使用RAW SOCKET编程的几个步骤。
3.1 创建原始套接字
我们可以用socket或WSASocket函数来创建原始套接字,因为原始套接字能直接控制底层协议,因此只有属于“管理员”组的成员,才有权创建原始套接字。下面是用socket函数创建原始套接字的代码。
SOCKET sock;
Sock=socket (AF_INET, SOCK_RAW, IPPROTO_UDP);
上述创建原始套接字的代码使用的是UDP协议,如果要使用其它的协议,比如ICMP、IGMP、IP等协议,只需要把相应的参数改为IPPROTO_ICM、IPPROTO_ IGMP、IPPROTO_IP就可以了。另外,IPPROTO_UDP、IPPROTO_IP、IPPROTO_RAW这几个协议标志要求使用套接字选项IP_HDRINCL,而目前只有Windows 2000和Windows XP提供了对IP_HDRINCL的支持,这意味着在Windows 2000以下平台创建原始套接字时是不能使用IP、UDP、TCP协议的。
3.2 设置套接字选项
创建了原始套接字后,就要设置套接字选项,这要通过setsocketopt函数来实现,setsocketopt函数的声明如下:
int setsocketopt (
SOCKET s,
int level,
int optname,
const char FAR *optval,
int optlen
);
在该声明中,参数s是标识套接口的描述字,要注意的是选项对这个套接字必须是有效的。参数Level表明选项定义的层次,对TCP/IP协议族而言,支持SOL_SOCKET、IPPROTO_IP和IPPROTO_CP层次。参数Optname是需要设置的选项名,这些选项名是在Winsock头文件内定义的常数值。参数optval是一个指针,它指向存放选项值的缓冲区。参数optlen指示optval缓冲区的长度
3.3 创建并填充相应协议头
这一步就是创建IP和TCP协议头的数据结构,根据相关协议的定义进行编写即可,下面是一个TCP协议头的数据结构。
struct TCP
{
unsigned short tcp_sport;
unsigned short tcp_dport;
unsigned int tcp_seq;
unsigned int tcp_ack;
unsigned char tcp_lenres;
unsigned char tcp_flag;
unsigned short tcp_win;
unsigned short tcp_sum;
unsigned short tcp_urp;
};
4 一个利用RAW SOCKET捕获网络数据包的程序模型
下面介绍一个利用RAW SOCKET捕获网络数据包的程序模型。这个程序模型演示了如何使用RAW SOCKET捕获局域网中的数据包,它完成了网络底层数据的接收,能显示源地址、目标地址、源端口、目标端口和接收的字节数等信息。这个程序模型也说明了网络监听的基本原理,给捕获局域网中的数据包提供了一种方法,即先把网卡设置为混杂模式,然后利用RAW SOCKET接收IP层的数据。
程序在Visual C++.net 2003中调试并编译通过,运行环境为以太网, 程序代码可同时在Linux与windows环境下编译和运行,当然在编译时需要不同的头文件以及需要对代码作相应的改动。本程序模型在Windows下能直接运行,如果在Linux下运行,则需要先用手工把网卡设置为混杂模式,在root权限下用如下命令设置:ifconfig eth0 promisc。
在Unix/Linux下程序要包含以下这几个进行调用系统和网络函数的头文件:
#include〈stdio.h〉
#include〈sys/socket.h〉
#include〈netinet/in.h〉
#include〈arpa/inet.h〉
#include"headers.h"
为了方便基于Berkeley套接口的已有源程序的移植,Windows Sockets支持许多Berkeley头文件。这些Berkeley头文件被包含在WINSOCK2.H中,所以一个Windows Sockets应用程序只需包含WINSOCK2.H头文件就足够了,这也是目前推荐使用的一种方法。在Windows平台下程序改用以下这几个头文件:
#include "stdafx.h"
#include<stdio.h>
#include<Winsock2.h>
#include"headers.h"
headers.h是自己编写的头文件,它的作用是定义IP和TCP包的头结构。在程序中首先定义几个变量和结构,然后调用函数socket()建立socket连接,主要代码如下:
int _tmain(int argc, _TCHAR* argv[])
{
int sock,bytes_recieved,fromlen;
char buffer[65535];
struct sockaddr_in from;
struct ip *ip;
struct tcp *tcp;
sock=socket(AF_INET,SOCK_RAW,IPPROTO_TCP);
……
return 0;
}
程序的第二步用一个while(1)语句来建立一个死循环,用来不停地接收网络信息。首先用函数sizeof()取出一个socket结构的长度,这个参数是recvfrom()函数所必须的。从建立的socket连接中接收数据是通过函数recvfrom()是来实现的,因为recvfrom()函数需要一个sockaddr数据类型,所以用了一个强制类型转换,代码如下:fromlen=sizeof(from);
bytes_recieved=recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&from,&fromlen);
接下来用一条语句把接收到的数据转化为我们预先定义的结构,以便于查看,代码为:
ip=(struct ip *)buffer
还要用一条语句来指向TCP头,因为接收的数据中,IP头的大小是固定的4字节,所以用IP长度乘以4就能指向TCP头部分,代码为:
tcp=(struct tcp *)(buffer+(4*ip->ip_length))
最后就可以用打印语句把接收的字节数、数据的源地址、目标地址、源端口、目标端口、IP头长度和协议的类型输出来。