明日花2017年9月作品:你回到我这个年龄我才4岁,但是我到你这个年龄你都67了,问我和你个多大..用C语言编程怎么编???

来源:百度文库 编辑:高考问答 时间:2024/04/28 00:47:59

一 前言

SNIFF真是一个古老的话题,关于在网络上采用SNIFF来获取敏感信息已经不是什么
新鲜事,也不乏很多成功的案例,那么,SNIFF究竟是什么呢? SNIFF就是嗅探器,就是
窃听器,SNIFF静悄悄的工作在网络的底层,把你的秘密全部记录下来。看过威尔史密斯
演的《全民公敌》吗?SNIFF就象里面精巧的窃听器一样,让你防不胜防。

SNIFF可以是软件,也可以是硬件,既然是软件那就要分平台,有WINDOWS下的、UNXI
下的等,硬件的SNIFF称为网络分析仪,反正不管硬件软件,目标只有一个,就是获取在网
络上传输的各种信息。本文仅仅介绍软件的SNIFF。

当你舒适的坐在家里,惬意的享受网络给你带来的便利,收取你的EMAIL,购买你喜欢
的物品的时候,你是否会想到你的朋友给你的信件,你的信用卡帐号变成了一个又一个的
信息包在网络上不停的传送着,你是否曾经这些信息包会通过网络流入别人的机器呢?你
的担忧不是没有道理的,因为SNIFF可以让你的担忧变成实实在在的危险。就好象一个人躲
在你身后偷看一样。。。。。。

二 网络基础知识

“网络基础知识”,是不是听起来有点跑题了?虽然听起来这和我们要谈的SNIFF没什么
关系,可是还是要说一说的,万丈高楼平地起,如果连地基都没打好,怎么盖楼?!如果你
对网络还不是十分清楚的话,最好能静下心来好好看看,要知道,这是基础的基础,在这里
我只是简单的说一下,免得到时候有人迷糊,详细的最好能够自己去找书看看。

(1)TCP/IP体系结构

开放系统互连(OSI)模型将网络划分为七层模型,分别用以在各层上实现不同的功能,
这七层分别为:应用层、表示层、会话层、传输层、网络层、数据链路层及物理层。而TCP/IP
体系也同样遵循这七层标准,只不过在某些OSI功能上进行了压缩,将表示层及会话层合并入
应用层中,所以实际上我们打交道的TCP/IP仅仅有5层而已,网络上的分层结构决定了在各层
上的协议分布及功能实现,从而决定了各层上网络设备的使用。实际上很多成功的系统都是基
于OSI模型的,如:如帧中继、ATM、ISDN等。

TCP/IP的网络体系结构(部分)

-----------------------------------
| SMTP | DNS | HTTP | FTP | TELNET| 应用层
-----------------------------------
| TCP | UDP | 传输层
-----------------------------------
| IP | ICMP | ARP RARP | 网络层
------------------------
| IEEE 802 以太网 SLIP/PPP PDN etc| 数据链路层
-----------------------------------
| 网卡 电缆 双绞线 etc | 物理层
-----------------------------------

从上面的图中我们可以看出,第一层物理层和第二层数据链路层是TCP/IP的基础,而
TCP/IP本身并不十分关心低层,因为处在数据链路层的网络设备驱动程序将上层的协议和
实际的物理接口隔离开来。网络设备驱动程序位于介质访问子层(MAC)。

(2)网络上的设备

中继器:中继器的主要功能是终结一个网段的信号并在另一个网段再生该信号,一句话,
就是简单的放大而已,工作在物理层上。

网 桥:网桥使用MAC物理地址实现中继功能,可以用来分隔网段或连接部分异种网络,工
作在数据链路层。

路由器:路由器使用网络层地址(IP,X.121,E.164等),主要负责数据包的路由寻径,也能
处理物理层和数据链路层上的工作。

网 关:主要工作在网络第四层以上,主要实现收敛功能及协议转换,不过很多时候网关都
被用来描述任何网络互连设备。

(3)TCP/IP与以太网

以太网和TCP/IP可以说是相互相成的,可以说两者的关系几乎是密不可分,以太网在
一二层提供物理上的连线,而TCP/IP工作在上层,使用32位的IP地址,以太网则使用48位
的MAC地址,两者间使用ARP和RARP协议进行相互转换。从我们上面TCP/IP的模型图中可以
清楚的看到两者的关系。

载波监听/冲突检测(CSMA/CD)技术被普遍的使用在以太网中,所谓载波监听是指在以
太网中的每个站点都具有同等的权利,在传输自己的数据时,首先监听信道是否空闲,如
果空闲,就传输自己的数据,如果信道被占用,就等待信道空闲。而冲突检测则是为了防
止发生两个站点同时监测到网络没有被使用时而产生冲突。以太网采用广播机制,所有与
网络连接的工作站都可以看到网络上传递的数据。

为了加深你的理解,我们来看看下面的图,一个典型的在以太网中客户与服务器使用
TCP/IP协议的通信。

用户进程 FTP客户 <-------------------------> FTP服务器 应用层
| |
内核中的协议栈 TCP <-------------------------> TCP 传输层
| |
内核中的协议栈 IP <-------------------------> IP 网络层
| |
以太网驱动程序 <-------------------------> 以太网驱动程序 数据链路层

——————-------------------------------
以太网

??唆唆了这么多,有人烦了吧?相信我,这是基础的基础,可以说是说得是很简单拉,
如果需要,拿出个几十万字来说上面的内容,我想也不嫌多,好了,让我们进入下一节,
sniff的原理。

三 SNIFF的原理

要知道在以太网中,所有的通讯都是广播的,也就是说通常在同一个网段的所有网络接
口都可以访问在物理媒体上传输的所有数据,而每一个网络接口都有一个唯一的硬件地址,
这个硬件地址也就是网卡的MAC地址,大多数系统使用48比特的地址,这个地址用来表示网
络中的每一个设备,一般来说每一块网卡上的MFC地址都是不同的,每个网卡厂家得到一段
地址,然后用这段地址分配给其生产的每个网卡一个地址。在硬件地址和IP地址间使用ARP
和RARP协议进行相互转换。

在正常的情况下,一个网络接口应该只响应这样的两种数据帧:

1.与自己硬件地址相匹配的数据帧。 2.发向所有机器的广播数据帧。

在一个实际的系统中,数据的收发是由网卡来完成的,网卡接收到传输来的数据,网卡
内的单片程序接收数据帧的目的MAC地址,根据计算机上的网卡驱动程序设置的接收模式判
断该不该接收,认为该接收就接收后产生中断信号通知CPU,认为不该接收就丢掉不管,所
以不该接收的数据网卡就截断了,计算机根本就不知道。CPU得到中断信号产生中断,操作
系统就根据网卡的驱动程序设置的网卡中断程序地址调用驱动程序接收数据,驱动程序接收
数据后放入信号堆栈让操作系统处理。而对于网卡来说一般有四种接收模式:

广播方式:该模式下的网卡能够接收网络中的广播信息。 组播方式:设置在该模式下的网卡能够接收组播数据。 直接方式:在这种模式下,只有目的网卡才能接收该数据。 混杂模式:在这种模式下的网卡能够接收一切通过它的数据,而不管该数据是否是传给它的。

好了,现在我们总结一下,首先,我们知道了在以太网中是基于广播方式传送数据的,也
就是说,所有的物理信号都要经过我的机器,再次,网卡可以置于一种模式叫混杂模式
(promiscuous),在这种模式下工作的网卡能够接收到一切通过它的数据,而不管实际上数
据的目的地址是不是他。这实际上就是我们SNIFF工作的基本原理:让网卡接收一切他所能接
收的数据。

(图一)

我们来看一个简单的例子,如图一所示,机器A、B、C与集线器HUB相连接,集线器HUB通
过路由器Router访问外部网络。这是一个很简单也很常见的情况,比如说在公司大楼里,我
所在的网络部办公室里的几台机器通过集线器连接,而网络部、开发部、市场部也是同样如
此,几个部门的集线器通过路由器连接。还是回到我们的图一上来,值得注意的一点是机器
A、B、C使用一个普通的HUB连接的,不是用SWITCH,也不是用ROUTER,使用SWITCH和ROUTER
的情况要比这复杂得多。

我们假设一下机器A上的管理员为了维护机器C,使用了一个FTP命令向机器C进行远程登陆,
那么在这个用HUB连接的网络里数据走向过程是这样的。首先机器A上的管理员输入的登陆机
器C的FTP口令经过应用层FTP协议、传输层TCP协议、网络层IP协议、数据链路层上的以太网
驱动程序一层一层的包裹,最后送到了物理层,我们的网线上。接下来数据帧送到了HUB上,
现在由HUB向每一个接点广播由机器A发出的数据帧,机器B接收到由HUB广播发出的数据帧,
并检查在数据帧中的地址是否和自己的地址相匹配,发现不是发向自己的后把这数据帧丢弃,
不予理睬。而机器C也接收到了数据帧,并在比较之后发现是发现自己的,接下来他就对这数
据帧进行分析处理。

在上面这个简单的例子中,机器B上的管理员如果很好奇,他很想知道究竟登陆机器C上FTP
口令是什么?那么他要做的很简单,仅仅需要把自己机器上的网卡置于混杂模式,并对接收到
的数据帧进行分析,从而找到包含在数据帧中的口令信息。

四 做一个自己的sniff

在上一节里,我们已经知道了SNIFF的基本原理是怎么一回事,这一节我们来亲自动手做一个
自己的sniff,毕竟,用程序代码来说话比什么都要来得真实,也容易加深理解。

回头想一想我们上面说的原理,我们要做的事情有几件:

1. 把网卡置于混杂模式。 2. 捕获数据包。 3. 分析数据包。

注:下面的源代码取至Chad Renfro的<< Basic Packet-Sniffer Construction from the Ground Up>>
一文中
/************************Tcp_sniff_2.c********************/
1.#include
2.#include
3.#include
4.#include
5.#include
6.#include
7.#include
8.#include
9.#include "headers.h"

#define INTERFACE "eth0"

/*Prototype area*/

10.int Open_Raw_Socket(void);
11.int Set_Promisc(char *interface, int sock);
12.int main() {
13.int sock, bytes_recieved, fromlen;
14.char buffer[65535];
15.struct sockaddr_in from;
16.struct ip *ip;
17.struct tcp *tcp;
18.sock = Open_Raw_Socket();
19. Set_Promisc(INTERFACE, sock);

20. while(1)
22. {
23. fromlen = sizeof from;
24. bytes_recieved = recvfrom(sock, buffer, sizeof buffer, 0, (struct sockaddr *)&from, &fromlen);
25. printf("\nBytes received ::: %5d\n",bytes_recieved);
26. printf("Source address ::: %s\n",inet_ntoa(from.sin_addr));
27. ip = (struct ip *)buffer;
/*See if this is a TCP packet*/
28. if(ip->ip_protocol == 6) {
29. printf("IP header length ::: %d\n",ip->ip_length);
30. printf("Protocol ::: %d\n",ip->ip_protocol);
31. tcp = (struct tcp *)(buffer + (4*ip->ip_length));
32. printf("Source port ::: %d\n",ntohs(tcp->tcp_source_port));
33. printf("Dest port ::: %d\n",ntohs(tcp->tcp_dest_port));
34. }

35. }
36.}
37.int Open_Raw_Socket() {
38. int sock;
39. if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
/*Then the socket was not created properly and must die*/
40. perror("The raw socket was not created");
41. exit(0);
42. };
43. return(sock);
44. }

45.int Set_Promisc(char *interface, int sock ) {
46. struct ifreq ifr;
47. strncpy(ifr.ifr_name, interface,strnlen(interface)+1);
48. if((ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)) {
/*Could not retrieve flags for the interface*/
49. perror("Could not retrive flags for the interface");
50. exit(0);
51. }
52. printf("The interface is ::: %s\n", interface);
53. perror("Retrieved flags from interface successfully");
54. ifr.ifr_flags |= IFF_PROMISC;
55. if (ioctl (sock, SIOCSIFFLAGS, &ifr) == -1 ) {
/*Could not set the flags on the interface */
56. perror("Could not set the PROMISC flag:");
57. exit(0);
58. }
59. printf("Setting interface ::: %s ::: to promisc", interface);
60. return(0);
61. }

/***********************EOF**********************************/

上面这段程序中有很详细的注解,不过我想还是有必要说一说,首先
第10行--int Open_Raw_Socket(void); 是我们的自定义函数,具体内容如下:

37.int Open_Raw_Socket() {
38. int sock;
39. if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
/*Then the socket was not created properly and must die*/
40. perror("The raw socket was not created");
41. exit(0);
42. };
43. return(sock);
44. }

第39行 if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {

这里我们调用了socket函数,使创建了了一个原始套接口,使之收到TCP/IP信息包。

接下来第11行-int Set_Promisc(char *interface, int sock),这也是我们的自定义函数,
目的是把网卡置于混杂模式,具体内容如下:
45.int Set_Promisc(char *interface, int sock ) {
46. struct ifreq ifr;
47. strncpy(ifr.ifr_name, interface,strnlen(interface)+1);
48. if((ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)) {
/*Could not retrieve flags for the interface*/
49. perror("Could not retrive flags for the interface");
50. exit(0);
51. }
52. printf("The interface is ::: %s\n", interface);
53. perror("Retrieved flags from interface successfully");
54. ifr.ifr_flags |= IFF_PROMISC;
55. if (ioctl (sock, SIOCSIFFLAGS, &ifr) == -1 ) {
/*Could not set the flags on the interface */
56. perror("Could not set the PROMISC flag:");
57. exit(0);
58. }
59. printf("Setting interface ::: %s ::: to promisc", interface);
60. return(0);
61. }

首先 struct ifreq ifr; 定一了一个ifrreg的结构ifr,接下来
strncpy(ifr.ifr_name, interface,strnlen(interface)+1);,就是把我们网络设备的名字填
充到ifr结构中,在这里 #define INTERFACE "eth0" ,让我们再往下看,
ioctl(sock, SIOCGIFFLAGS, &ifr),SIOCGIFFLAGS请求表示需要获取接口标志,现在到了
第54行,在我们成功的获取接口标志后把他设置成混杂模式,
ifr.ifr_flags |= IFF_PROMISC;ioctl (sock, SIOCSIFFLAGS, &ifr)。OK,现在我们所说的
第一步已经完成--------把网卡置于混杂模式。

现在进入第二步,捕获数据包。从第20行开始,我们进入了一个死循环,while(1),在
第24行,recvfrom(sock, buffer, sizeof buffer, 0, (struct sockaddr *)&from, &fromlen),
这个函数要做的就是接收数据,冰把接收到的数据放入buffer中。就是这么简单,已经完成了我
们要捕获数据包的任务。

到了第三步,分析数据包。27行,ip = (struct ip *)buffer,使我们在头文件中的IP结
构对应于所接收到的数据,接下来判断在网络层中是否使用的是TCP协议,
if(ip->ip_protocol == 6) ,如果答案是,tcp信息包从整个IP/TCP包 buffer + (4*ip->ip_length)
地址处开始,所以31行 tcp = (struct tcp *)(buffer + (4*ip->ip_length)),然后对应
结构把你所需要的信息输出。
/*************************headers.h**************************/
/*structure of an ip header*/
struct ip {
unsigned int ip_length:4; /*little-endian*/
unsigned int ip_version:4;
unsigned char ip_tos;
unsigned short ip_total_length;
unsigned short ip_id;
unsigned short ip_flags;
unsigned char ip_ttl;
unsigned char ip_protocol;
unsigned short ip_cksum;
unsigned int ip_source; unsigned int ip_dest;
};

/* Structure of a TCP header */
struct tcp {
unsigned short tcp_source_port;
unsigned short tcp_dest_port;
unsigned int tcp_seqno;
unsigned int tcp_ackno;
unsigned int tcp_res1:4, /*little-endian*/
tcp_hlen:4,
tcp_fin:1,
tcp_syn:1,
tcp_rst:1,
tcp_psh:1,
tcp_ack:1,
tcp_urg:1,
tcp_res2:2;
unsigned short tcp_winsize;
unsigned short tcp_cksum;
unsigned short tcp_urgent;
};
/*********************EOF***********************************/

从上面的分析我们可以清楚的认识到,认识一个SNIFF需要对TCP/IP协议有着详细的了解,
否则你根本无法找到你需要的信息。有了上面的基础,你可以自己来做一个你需要的SNIFF了。

五 常用的SNIFF

很少有原因会让你自己亲自动手来做一个自己的SNIFF,除非你是想了解他的原理,或者是
其他一些特别的原因,比如你要在某个特殊的环境拦截一些特殊的数据包。下面我们就来看
看一些在网络上经常使用的SNIFF。

(1)windows环境下

windows环境下当然是大名鼎鼎的netxray以及sniffer pro了,实际上很多人都是用他在
windows环境下抓包来分析,不过我想很少有人笨到去在别人的机器上安装一个图形界面的SNIFF,
除非他和管理员很熟悉........ netxray的使用就不多说了,反正windows下的东西就是
click,click,click,非常的方便用户。

(2)UNUX环境下

UNUX环境下的sniff可以说是百花齐放,一抓就是一大把,如sniffit,snoop,tcpdump,dsniff
等都是比较常见的,他们都有一个好处就是发布源代码,可以让你研究,当然也都是免费的:)

1. sniffit

sniffit可以运行在Solaris、SGI和Linux等平台上,由Lawrence Berkeley Laboratory 实验
室开发的一个免费的网络监听软件。最近Sniffit 0.3.7也推出了NT版本,并也支持WINDOWS2000.

使用方法:
-v 显示版本信息
-a 以ASCII形式将监听的结果输出。
-A 在进行记录时,所有不可打印的字符都用代替
-b 等同于同时使用参数-t & -s。
-d 将监听所得内容以十六进制方式显示在当前终端
-p 记录连接到的包,0为所有端口。缺省为0。
-P protocol 选择要检查的协议,缺省为TCP。可能的选择有IP、TCP、ICMP、UDP和他们的组合。
-s 指定sniffer 检查从 发送的数据包。 -t 指定sniffer 检查发送到的数据包。
-i 进入交互模式
-l 设定数据包大小,default是300字节
注:参数可以用@来表示一个IP范围,比如 -t 192.168.@ -t和-s 只适用于TCP/UDP数据包,对
于ICMP和IP也进行解释。但如果只选择了-p参数,则只用于TCP和UDP包。

举例说明:

#sniffit -a -p 21 -t xxx.xxx.xxx.xxx

监听流向机器xxx.xxx.xxx.xxx的21端口(FTP)的信息,并以ASCII显示

#sniffit -d -p 23 -b xxx.xxx.xxx.xxx

监听所有流出或流入机器xxx.xxx.xxx.xxx的23端口(telnet)的信息,并以16进制显示

你可以在这里找到sniffit http://reptile.rug.ac.be/~coder/sniffit/sniffit.html

2. snoop

snoop默认情况安装在Solaris下,是一个用于显示网络交通的程序,不过SNIFF是把双刃剑,
既然管理员能用他来监视自己的网络,当然一个心怀恶意的入侵者也可以用他来SNIFF自己感兴
趣的内容。值得一提的是, SNOOP被发现存在一个缓冲区溢出漏洞,当以导致入侵者以运行
snoop(通常为root)的身份远程进入系统。这是题外话,暂且就此打住。

使用方法:
[ -a ] # Listen to packets on audio
[ -d device ] # settable to le?, ie?, bf?, tr?
[ -s snaplen ] # Truncate packets
[ -c count ] # Quit after count packets
[ -P ] # Turn OFF promiscuous mode
[ -D ] # Report dropped packets
[ -S ] # Report packet size
[ -i file ] # Read previously captured packets
[ -o file ] # Capture packets in file
[ -n file ] # Load addr-to-name table from file
[ -N ] # Create addr-to-name table
[ -t r|a|d ] # Time: Relative, Absolute or Delta
[ -v ] # Verbose packet display
[ -V ] # Show all summary lines
[ -p first[,last] ] # Select packet(s) to display
[ -x offset[,length] ] # Hex dump from offset for length
[ -C ] # Print packet filter code

例如:

#snoop -o saved A B

监听机器A与B的谈话,并把内容存储于文件saved中

3. tcpdump

tcpdmp也算是一个很有名气的网络监听软件,FREEBSD还把他附带在了系统上,是一个被
很多UNIX高手认为是一个专业的网络管理工具。

使用方法:
tcpdump采用命令行方式,它的命令格式为:
tcpdump [ -adeflnNOpqStvx ][ -c 数量 ][ -F 文件名 ][ -i 网络接口 ][ -r 文件名]
[ -s snaplen ][ -T 类型 ][ -w 文件名 ][表达式 ]
1. tcpdump的选项介绍
-a 将网络地址和广播地址转变成名字;
-d 将匹配信息包的代码以人们能够理解的汇编格式给出;
-dd 将匹配信息包的代码以c语言程序段的格式给出;
-ddd 将匹配信息包的代码以十进制的形式给出;
-e 在输出行打印出数据链路层的头部信息;
-f 将外部的Internet地址以数字的形式打印出来;
-l 使标准输出变为缓冲行形式;
-n 不把网络地址转换成名字;
-t 在输出的每一行不打印时间戳;
-v 输出一个稍微详细的信息,例如在ip包中可以包括ttl和服务类型的信息;
-vv 输出详细的报文信息;
-c 在收到指定的包的数目后,tcpdump就会停止;
-F 从指定的文件中读取表达式,忽略其它的表达式;
-i 指定监听的网络接口;
-r 从指定的文件中读取包(这些包一般通过-w选项产生);
-w 直接将包写入文件中,并不分析和打印出来;
-T 将监听到的包直接解释为指定的类型的报文,常见的类型有rpc和snmp

2. tcpdump的表达式介绍

表达式是一个正则表达式,tcpdump利用它作为过滤报文的条件,如果一个报文满足表达式
的条件,则这个报文将会被捕获。如果没有给出任何条件,则网络上所有的信息包将会被截获。

在表达式中一般如下几种类型的关键字,一种是关于类型的关键字,主要包括host,net,
port, 例如 host 210.27.48.2,指明 210.27.48.2是一台主机,net 202.0.0.0 指明 202.0.0.0
是一个网络地址,port 23 指明端口号是23。如果没有指定类型,缺省的类型是host.

第二种是确定传输方向的关键字,主要包括src , dst ,dst or src, dst and src ,这些关
键字指明了 传输的方向。举例说明,src 210.27.48.2 ,指明ip包中源地址是210.27.48.2 ,
dst net 202.0.0.0 指明目的网络地址是202.0.0.0 。如果没有指明方向关键字,则缺省是
src or dst关键字。

第三种是协议的关键字,主要包括fddi,ip ,arp,rarp,tcp,udp等类型。Fddi指明是在
FDDI(分布式光纤数据接口网络)上的特定的网络协议,实际上它是"ether"的别名,fddi和ether
具有类似的源地址和目的地址,所以可以将fddi协议包当作ether的包进行处理和分析。其他的
几个关键字就是指明了监听的包的协议内容。如果没有指定任何协议,则tcpdump将会监听所有
协议的信息包。

除了这三种类型的关键字之外,其他重要的关键字如下:
gateway, broadcast,less,greater,还有三种 逻辑运算,取非运算是 'not ' '! ', 与运算
是'and','&&';或运算 是'or' ,'||'。

举例使用:

#tcpdump host AAA.BBB.CCC.DDD

将监听IP地址为AAA.BBB.CCC.DDD的机器的通话

#tcpdump tcp port 23 host AAA.BBB.CCC.DDD

将监听IP地址为AAA.BBB.CCC.DDD的机器的23端口的通话

4. dsniff

之所以要谈谈dsniff,是因为他不仅仅是一个sniff,在他的整个套件包中,包含了很多
其它有用的工具,如arpspoof,dnsspoof,macof,tcpkill等等,SNIFF的手段更加的多样和
复杂化。dsniff是由DugSong开发的你可以在他的主页上找到这个工具。 目前dsniff支持
OpenBSD (i386), Redhat Linux (i386), 和Solaris (sparc). 并且在FreeBSD, Debian Linux,
Slackware Li

设大的是X,小的是Y,根据原题可得2Y-X=4和2X-Y=67这两个方程,解得X=46,Y=25。如果用C做的话,设两个变量,一个X,一个Y,这两个变量各从1循环到100,每循环一次分别代入两个方程看是否满足,当循环到46和25时就会出结果,详细代码我看就不用了吧?你不会连这都不会?

哇,上面这位老兄真牛,是不是最近跑业务,在给百度推销硬盘啊??