2024年6月3日发(作者:)

基于ICMP协议和广播的洪水Ping编程

下面,我们分析一个程序——基于ICMP协议和广播的洪水Ping编程。

我们介绍这样3个问题:

 Ping命令以及基于Ping命令的Ping to Death攻击的基本原理

 洪水Ping攻击的基本原理

 洪水Ping攻击程序的分析

先来看第一个问题。

1、 Ping命令及Ping to Death攻击

1) Ping命令格式:

Ping命令,我们大家都使用过了。例如:

ping 192.168.0.101 –l 1024

这里,Ping的目标地址是:192.168.0.101,ICMP数据包的大小=1024字节

利用这个命令,可以测试从源主机到目标主机网络、TCP/IP 协议栈的工作是否正常。

手工执行的时候,我们需要在Dos命令行输入:ping 192.168.0.101(目标地址)。执行Ping

命令,本质上就是源主机向目标主机发送一个 ICMP的数据包,只不过,这个ICMP数据

报的类型=8(回显请求)。目标主机接收到类型=8的ICMP数据报后,向源主机发送一个类

型=0的回显应答数据包。这是Ping命令执行的基本过程。

2) Ping to Death攻击的原理

从这个过程,我们可以看出:源主机发送一个ICMP请求后,目标主机必须回应。在早

期的操作系统中,例如:Windows95,由于操作系统默认ICMP数据包不会很大,所以,它

们给ICMP数据包预留的缓存不够大,也没有做内存溢出检查。所以,早期的操作系统不能

很好的处理过大的ICMP数据包。当源主机发送一个异常大的ICMP数据包时,目标主机可

能出现内存溢出,死机。这就是Ping to Death攻击的基本原理。

2、洪水Ping攻击的基本原理

随着操作系统的升级,目前Windows系统已经没有了这种BUG,大的Ping包基本没有

了攻击效果。但是,对于一个网站,或者服务器,如果我们在同一时间,向它们发送巨量的

Ping数据包,服务器也必须给予回应,效果上,也会导致服务器变慢,甚至瘫痪。这就是

洪水Ping攻击。

理论上,当大量用户(成千上万)同一时间发送Ping命令到同一台主机,会产生明显

的攻击效果。但是,这不符合黑客攻击的基本原则:用最少的攻击资源换取被攻击者最大的

消耗。

那么,究竟怎样做,才能以最小的代价换取对方最大的牺牲呢?

我们来看,正常情况下,当主机A发送了一个ICMP的报文(类型=8,回显请求)给

主机B,主机B会发送一个ICMP的应答报文(类型=0,回显应答)。由于ICMP是封装在

IP数据报中发送,基于无连接的应用,这就给攻击者了一个机会。(由于发送方可以不建立

连接直接向接收方发送数据,接收方不认证对方身份就必须处理,这就营造了一个可乘之机。

如果是面向连接的,接收方在接收数据前,需要与发送方先建立连接,这样,没有接收方的

允许,连接就无法建立,后续的攻击数据就无法发送)。

好,这是正常情况下ICMP的请求和应答。现在,我们假定:主机A伪装成主机C向

主机B发送了一个ICMP回显请求数据包,结果会怎样呢?主机B会误以为,主机C向自

己发送了一个ICMP的回显请求,它会主动向主机C发送一个ICMP的回显应答。

这里,攻击者发送了一个攻击数据包给主机B,造成了对主机B、C的同时攻击,攻击

1

效果放大了一倍。而且,我们隐藏了攻击主机A的地址,在攻击过程中没有留下我们的痕

迹。

再进一步,如果我们伪装成被攻击的主机,向一个广播地址发送一个ICMP回显请求数

据包,结果又会怎样呢?由于这是个广播数据包,网络内收到广播数据报的主机都会处理,

当大量的主机发现主机C向它们发送了一个ICMP回显请求,它们会同时发送ICMP回显

应答。如果一个局域网内有N台主机,我们在这个局域网内发送一个这样的广播数据包,

被攻击主机就会收到N个回显应答攻击包,相当于一次攻击放大了N倍。

这样,就达到了“以最小的代价换取对方最大的牺牲”的目标。

那么,具体程序是如何做的呢?

3、洪水Ping攻击程序分析

通常情况下,当我们的顶层应用程序需要发送数据时,主机的TCP/IP协议栈会自动将

我们的数据封装在IP数据报中发送出去。这时,我们的IP数据报中源地址就填写了我们自

己的地址。但是,为了伪造主机C,我们需要自己构造IP数据包。自己填写源、目标IP地

址。

为了完成这种底层操作,我们需要使用一个winsock API的函数——setsockopt。

1)setsockopt函数解析

Setsockopt函数用于任意类型(数据报套接口、流式套接口、原始套接口)、任意状态

套接口的选项设置,它的函数原型是:

int setsockopt ( SOCKET s, int level, int optname, const char FAR * optval, int optlen );

这里,s——需要改变选项设置的套接字;

level——指定控制套接字的层次,可以取三个值:1)SOL_SOCKET:通用套接字选项;

2)IPPROTO_IP:IP选项;3)IPPROTO_TCP:TCP选项。

optname——套接字选项的名称。对不同的选项,可以取不同的值,例如:

选项名称 说明 数据类型

========================================================================

SOL_SOCKET

------------------------------------------------------------------------

SO_BROADCAST 允许发送广播数据 BOOL

SO_DEBUG 允许调试 BOOL

SO_DONTROUTE 不查找路由 BOOL

SO_ERROR 获得套接字错误 int

SO_KEEPALIVE 保持连接 BOOL

SO_LINGER 延迟关闭连接 struct linger

SO_OOBINLINE 带外数据放入正常数据流 BOOL

SO_RCVBUF 接收缓冲区大小 int

SO_SNDBUF 发送缓冲区大小 int

SO_RCVLOWAT 接收缓冲区下限 int

SO_SNDLOWAT 发送缓冲区下限 int

SO_RCVTIMEO 接收超时 struct timeval

SO_SNDTIMEO 发送超时 struct timeval

SO_REUSERADDR 允许重用本地地址和端口 BOOL

SO_TYPE 获得套接字类型 int

SO_BSDCOMPAT 与BSD系统兼容 int

2

==========================================================================

IPPROTO_IP

--------------------------------------------------------------------------

IP_HDRINCL 在数据包中包含IP首部 BOOL

IP_OPTINOS IP首部选项 int

IP_TOS 服务类型

IP_TTL 生存时间 int

==========================================================================

IPPRO_TCP

--------------------------------------------------------------------------

TCP_MAXSEG TCP最大数据段的大小 int

TCP_NODELAY 不使用Nagle算法 int

=========================================================================

optval——这是一个指针,我们看选项。选项的值有2类:1)布尔类型,其方式为激活

(设置为TRUE)或禁止(设置为FALSE),比如:选项SO_BROADCAST设置为TRUE,

表示套接字允许发送广播数据;2)整型或结构。

对于布尔类型的选项,如果选项设置为TRUE,optval指向非零整型数,反之,如果选

项设置为FALSE,optval指向一个等于零的整型数。这时,optlen =sizeof(int)。

对于其他选项,optval指向那个存储整型或结构的内存地址,optlen是整型、结构的长

度。

调用setsockopt函数后,如果没有错误发生,返回0,否则,返回一个错误代码。好了,

有关套接字的选项设置,大家可以下来自己看,这里只要知道:构造广播的IP 数据包需要

改变套接口默认的设置就行了。

2)Sleep函数

Sleep函数挂起当前正在执行的线程,等待一段时间后,在向下执行。它的用法

Sleep(800);

表示当前线程等待800(单位是千分之一秒)

3)程序分析

好了下面,我们来看一下程序的实现。这是一个控制台命令程序。它由这么几个部分组

成:

1、 文件头

2、 常量及结构定义

(定义了2个常量,2个结构)

3

注意:IP数据报、ICMP数据报包头的定义与TCP/IP协议是一致的。

3、 定义计算校验和的子函数

4、 最后是主程序

这个程序可以接受3个参数(第一个参数是你打算伪装的主机的IP地址,第二个参数是目

标地址,一般是一个广播地址,第三个参数可选,指定数据包的大小)。在DOS下,输入:

4

FakeSourceIp DestinationIp [PacketSize]

例如:

192.168.15.23 192.168.15.255 6400

表明,我们想伪装的主机IP地址= 192.168.15.23,我们希望向局域网192.168.15.0发送广播

包(广播地址的主机ID全为1,因此,该局域网广播地址=192.168.15.255)

好了,前面,我们讲过:如果我们在命令行输入上述命令,argc=4,argv[1]= FakeSourceIp,

argv[2]= DestinationIp,argv[3]= PacketSize。

另外,程序的开头,还定义了一些变量,我们这里不多讲了。后面用到了再讲。

主程序的第二部分对输入参数进行检查,当参数输入不对时,打印帮助信息。

如果参数正确,将命令行输入的参数保存在相应的变量中。

接下来,调用注册函数,这是winsock编程的第一步。如果注册成功,创建一个原始套接口。

5

套接口创建成功后,调用setsockopt函数2次,改变套接口的选项设置。第一次,告诉套接

口,发送用户数据时,包含IP首部,也就是说,我们要自己构造IP 报的头部。第二次,改

变超时重发的时间(

如果你设了 SO_SNDTIMEO,则在阻塞一定时间后,如果socket buffer还是没有

空闲,TCP/IP会取消阻塞,让你的程序可以继续运行

)。

再接下来,填写好目标地址结构,构造IP数据报的头部,构造ICMP数据包,然后将它们

放入缓冲区SendBuf。注意,在构造IP数据报的头部时,我们在源地址字段填入了伪地址。

在构造ICMP数据报时,它的类型为8,表明这是一个回显请求。

6

最后,计算数据包的校验和,填写数据包总长度,使用一个无限循环,重复发送这个数据包。

好了,程序的主要流程就这样。下来,我给大家一份电子文档,大家下来后,一边看资料,

一边调试。

需要强调的是:

1)这个程序只能运行在Windows2000下,WinXP不允许伪造IP地址发送数据包

2)微软的系统不会回应广播地址的包。因此,如果你想更好的做个测试,最好在Unix

系统下,使用Ping命令测试。你可以在Unix机器上Ping一下你局域网的广播地址,这时,

可以看到很多很多主机发给你的应答数据包。但在Windows下就不行了,Windows的Ping

程序不会对多个应答数据包进行解包。

3)尽管如此,我们仍然可以在Windows2000下测试这个程序,用Sniffer可以观察到伪

造的IP数据包发送情况。这个程序的目的,就是希望大家了解如何使用原始套接口,如何

自己构造和发送一个底层的IP数据包。

7