歡迎您光臨本站 註冊首頁

手把手教你玩轉ARP包

←手機掃碼閱讀     火星人 @ 2014-03-29 , reply:0

第一篇 手把手教你玩轉ARP包
目錄:
一. 關於ARP協議的基礎知識
1. ARP的工作原理
2. ARP包的格式
作者:
CSDN VC/MFC 網路編程 PiggyXP ^_^

一. 關於ARP協議的基礎知識
1.ARP的工作原理
本來我不想在此重複那些遍地都是的關於ARP的基本常識,但是為了保持文章的完整性以及照顧初學者,我就再??亂恍┪淖職桑?噬疃琳嚦梢災苯猶??私凇?br />
我們都知道乙太網設備比如網卡都有自己全球唯一的MAC地址,它們是以MAC地址來傳輸乙太網數據包的,但是它們卻識別不了我們IP包中的IP地址,所以我們在乙太網中進行IP通信的時候就需要一個協議來建立IP地址與MAC地址的對應關係,以使IP數據包能發到一個確定的地方去。這就是ARP(Address Resolution Protocol,地址解析協議)。

講到此處,我們可以在命令行窗口中,輸入
arp ?a
來看一下效果,類似於這樣的條目
210.118.45.100 00-0b-5f-e6-c5-d7 dynamic
就是我們電腦里存儲的關於IP地址與MAC地址的對應關係,dynamic表示是臨時存儲在ARP緩存中的條目,過一段時間就會超時被刪除(xp/2003系統是2分鐘)。

這樣一來,比如我們的電腦要和一台機器比如210.118.45.1通信的時候,它會首先去檢查arp緩存,查找是否有對應的arp條目,如果沒有,它就會給這個乙太網絡發ARP請求包廣播詢問210.118.45.1的對應MAC地址,當然,網路中每台電腦都會收到這個請求包,但是它們發現210.118.45.1並非自己,就不會做出相應,而210.118.45.1就會給我們的電腦回復一個ARP應答包,告訴我們它的MAC地址是xx-xx-xx-xx-xx-xx,於是我們電腦的ARP緩存就會相應刷新,多了這麼一條:
210.118.45.1 xx-xx-xx-xx-xx-xx dynamic

為什麼要有這麼一個ARP緩存呢,試想一下如果沒有緩存,我們每發一個IP包都要發個廣播查詢地址,豈不是又浪費帶寬又浪費資源?
而且我們的網路設備是無法識別ARP包的真偽的,如果我們按照ARP的格式來發送數據包,只要信息有效計算機就會根據包中的內容做相應的反應.

試想一下,如果我們按照ARP響應包的相應的內容來刷新自己的ARP緩存中的列表,嘿嘿,那我們豈不是可以根據這點在沒有安全防範的網路中玩些ARP包的小把戲了?在後面的文章里我就手把手來教你們如何填充發送ARP包,不過先別急,我們再繼續學點基礎知識^_^

2.ARP包的格式

既然我們要來做一個我們自己的ARP包,當然首先要學習一下ARP包的格式。

從網路底層看來,一個ARP包是分為兩個部分的,前面一個是物理幀頭,後面一個才是ARP幀。
首先,物理幀頭,它將存在於任何一個協議數據包的前面,我們稱之為DLC Header,因為這個幀頭是在數據鏈路層構造的,並且其主要內容為收發雙方的物理地址,以便硬體設備識別。
DLC Header
欄位 長度(Byte) 默認值 備註
接收方MAC 6 廣播時,為 ff-ff-ff-ff-ff-ff
發送方MAC 6
Ethertype 2 0x0806 0x0806是ARP幀的類型值
圖1 物理幀頭格式

圖1是需要我們填充的物理幀頭的格式,我們可以看到需要我們填充的僅僅是發送端和接收端的物理地址罷了,是不是很簡單呢?
接下來我們看一下ARP幀的格式.
ARP Frame
欄位 長度(Byte) 默認值 備註
硬體類型 2 0x1 乙太網類型值
上層協議類型 2 0x0800 上層協議為IP協議
MAC地址長度 1 0x6 乙太網MAC地址長度為 6
IP地址長度 1 0x4 IP地址長度為 4
操作碼 2 0x1表示ARP請求包,0x2表示應答包
發送方MAC 6
發送方IP 4
接收方MAC 6
接收方IP 4
填充數據 18 因為物理幀最小長度為64位元組,前面的42位元組再加上4個CRC校驗位元組,還差18個位元組
圖2 ARP幀格式

我們可以看到需要我們填充的同樣也只是MAC,IP,再加上一個1或2的操作碼而已。
3.ARP包的填充
1) 請求包的填充:
比如我們的電腦MAC地址為 aa-aa-aa-aa-aa-aa,IP為 192.168.0.1
我們想要查詢 192.168.0.99的MAC地址,應該怎麼來做呢?

首先填充DLC Header,通過前面的學習我們知道,想要知道某個計算機對應的MAC地址是要給全網發送廣播的,所以接收方MAC於是我們的DLC Header就填充完成了,如圖,加粗的是我們要手動輸入的值(當然我編的程序比較智能,會根據你選擇的ARP包類型幫你自動填入一些欄位,你一用便知^_^)。肯定是 ffffffffffff,發送方MAC當然是自己啦,

DLC Header
欄位 長度(Byte) 填充值
接收方MAC 6 ffffffffffff
發送方MAC 6 aaaaaaaaaaaa
Ethertype 2 0x0806
圖3 ARP請求包中 DLC Header內容

接下來是ARP幀,請求包的操作碼當然是 1,發送方的MAC以及IP當然填入我們自己的,然後要注意一下,這裡的接收方IP填入我們要查詢的那個IP地址,就是192.168.0.99了,而接收方MAC填入任意值就行,不起作用,於是,如圖,

ARP Frame
欄位 長度(Byte) 填充值
硬體類型 2 1
上層協議類型 2 0800
MAC地址長度 1 6
IP地址長度 1 4
操作碼 2 1
發送方MAC 6 aaaaaaaaaaaa
發送方IP 4 192.168.0.1
接收方MAC 6 任意值 xxxxxxxxxxxx
接收方IP 4 192.168.0.99
填充數據 18 0
圖4 ARP請求包中 ARP幀的內容

如果我們構造一個這樣的包發送出去,如果 192.168.0.99存在且是活動的,我們馬上就會收到一個192.168.0.99發來的一個響應包,我們可以查看一下我們的ARP緩存列表,是不是多了一項類似這樣的條目:
192.168.0.99 bb-bb-bb-bb-bb-bb
是不是很神奇呢?
我們再來看一下ARP響應包的構造

2) 響應包的填充
有了前面詳細的解說,你肯定就能自己說出響應包的填充方法來了吧,所以我就不細說了,列兩個表就好了

比如說給 192.168.0.99(MAC為 bb-bb-bb-bb-bb-bb)發一個ARP響應包,告訴它我們的MAC地址為 aa-aa-aa-aa-aa-aa,就是如此來填充各個欄位

DLC Header
欄位 長度(Byte) 填充值
接收方MAC 6 bbbbbbbbbbbb
發送方MAC 6 aaaaaaaaaaaa
Ethertype 2 0x0806
圖5 ARP響應包中 DLC Header內容

ARP Frame
欄位 長度(Byte) 填充值
硬體類型 2 1
上層協議類型 2 0800
MAC地址長度 1 6
IP地址長度 1 4
操作碼 2 2
發送方MAC 6 aaaaaaaaaaaa
發送方IP 4 192.168.0.1
接收方MAC 6 bbbbbbbbbbbb
接收方IP 4 192.168.0.99
填充數據 18 0
圖6 ARP響應包中 ARP幀的內容

這樣192.168.0.99的ARP緩存中就會多了一條關於我們192.168.0.1的地址映射。
好了,終於到了編程實現它的時候了^_^

二. 發送ARP包的編程實現
1. 填充數據包
上面的那些關於ARP包各個欄位的表格,對應在程序里就是結構體,對應於上面的表格,於是我們需要三個下面這樣的結構體
// DLC Header
typedef struct tagDLCHeader
{
unsigned char DesMAC[6]; /* destination HW addrress */
unsigned char SrcMAC[6]; /* source HW addresss */
unsigned short Ethertype; /* ethernet type */
} DLCHEADER, *PDLCHEADER;
// ARP Frame
typedef struct tagARPFrame
{
unsigned short HW_Type; /* hardware address */
unsigned short Prot_Type; /* protocol address */
unsigned char HW_Addr_Len; /* length of hardware address */
unsigned char Prot_Addr_Len; /* length of protocol address */
unsigned short Opcode; /* ARP/RARP */

unsigned char Send_HW_Addr[6]; /* sender hardware address */
unsigned long Send_Prot_Addr; /* sender protocol address */
unsigned char Targ_HW_Addr[6]; /* target hardware address */
unsigned long Targ_Prot_Addr; /* target protocol address */
unsigned char padding[18];
} ARPFRAME, *PARPFRAME;
// ARP Packet = DLC header + ARP Frame
typedef struct tagARPPacket
{
DLCHEADER dlcHeader;
ARPFRAME arpFrame;
} ARPPACKET, *PARPPACKET;

這些結構體一定能看懂吧,在程序中就是對號入座就好了
1. 填充數據包

下面我舉個填充包頭的例子,我首先定義個了一個轉換字元的函數,如下

/****************************************************************************
* Name & Params::
* formatStrToMAC
* (
* const LPSTR lpHWAddrStr : 用戶輸入的MAC地址字元串
* unsigned char *HWAddr : 返回的MAC地址字元串(賦給數據包結構體)
* )
* Purpose:
* 將用戶輸入的MAC地址字元轉成數據包結構體需要的格式
****************************************************************************/
void formatStrToMAC(const LPSTR lpHWAddrStr, unsigned char *HWAddr)
{
unsigned int i, index = 0, value, temp;
unsigned char c;

_strlwr(lpHWAddrStr); // 轉換成小寫

for (i = 0; i < strlen(lpHWAddrStr); i++)
{
c = *(lpHWAddrStr + i);
if (( c>='0' && c<='9' ) || ( c>='a' && c<='f' ))
{
if (c>='0' && c<='9') temp = c - '0'; // 數字
if (c>='a' && c<='f') temp = c - 'a' + 0xa; // 字母
if ( (index % 2) == 1 )
{
value = value*0x10 + temp;
HWAddr[index/2] = value;
}
else value = temp;
index++;
}
if (index == 12) break;
}
}

// 開始填充各個欄位
ARPPACKET ARPPacket; // 定義ARPPACKET結構體變數

memset(&ARPPacket, 0, sizeof(ARPPACKET)); // 數據包初始化

formatStrToMAC(「DLC源MAC字元串」,ARPPacket.dlcHeader.SrcMAC); // DLC幀頭
formatStrToMAC(「DLC目的MAC字元串」,ARPPacket.dlcHeader.DesMAC);

formatStrToMAC(「ARP源MAC字元串」,ARPPacket.arpFrame.Send_HW_Addr); // 源MAC
ARPPacket.arpFrame.Send_Prot_Addr = inet_addr(srcIP); // 源IP
formatStrToMAC(「ARP目的MAC字元串」,ARPPacket.arpFrame.Targ_HW_Addr); // 目的MAC
ARPPacket.arpFrame.Targ_Prot_Addr = inet_addr(desIP); // 目的IP

ARPPacket.arpFrame.Opcode = htons((unsigned short)arpType); // arp包類型

// 自動填充的常量
ARPPacket.dlcHeader.Ethertype = htons((unsigned short)0x0806); // DLC Header的乙太網類型
ARPPacket.arpFrame.HW_Type = htons((unsigned short)1); // 硬體類型
ARPPacket.arpFrame.Prot_Type = htons((unsigned short)0x0800); // 上層協議類型
ARPPacket.arpFrame.HW_Addr_Len = (unsigned char)6; // MAC地址長度
ARPPacket.arpFrame.Prot_Addr_Len = (unsigned char)4; // IP地址長度

That』s all ! ^_^
填充完畢之後,我們需要做的就是把我們的ARPPACKET結構體發送出去

2.發送ARP數據包:

我們發送ARP包就要用到winpcap的api了,具體步驟及函數是這樣的,為了簡單易懂,我把錯誤處理的地方都去掉了,詳見代碼
/**********************************************************************
* Name & Params::
* SendARPPacket()
* Purpose:
* 發送ARP數據包
* Remarks:
* 用的是winpcap的api函數
***********************************************************************/
void SendARPPacket()
{
char *AdapterDeviceName =GetCurAdapterName(); // 首先獲得獲得網卡名字

lpAdapter = PacketOpenAdapter(AdapterDeviceName); // 根據網卡名字打開網卡

lpPacket = PacketAllocatePacket(); // 給PACKET結構指針分配內存

PacketInitPacket(lpPacket, &ARPPacket, sizeof(ARPPacket)); //初始化PACKET結構指針
// 其中的ARPPacket就是我們先前填充的ARP包

PacketSetNumWrites(lpAdapter, 1); // 每次只發送一個包

PacketSendPacket(lpAdapter, lpPacket, true) // Send !!!!! ^_^

PacketFreePacket(lpPacket); // 釋放資源
PacketCloseAdapter(lpAdapter);
}

呵呵,至此,關於ARP包最關鍵的部分就講完了,你現在就可以來隨心所欲的發送自己的ARP包了

既然作為一篇「科普文章」,接下來我再講一講與整個項目有關的附加步驟以及說明

三.附加步驟以及說明
1. 如何在VC中使用winpcap驅動
雖然winpcap開發包使用起來非常簡便,但是前期準備工作還是要費一番功夫的,缺一不可。^_^
首先就是要安裝它的驅動程序了,可以到它的主頁下載,更新很快的
http://winpcap.polito.it/install/default.htm
下載WinPcap auto-installer (driver +DLLs),直接安裝就好了,或者我提供的代碼包裡面也有。
希望以後用winpcap作開發的朋友,還需要下載 Developer's pack,解壓即可。

然後,需要設置我們工程的附加包含目錄為我們下載Developer's pack開發包的Inclulde目錄,連接器的附加依賴庫設置為Developer's pack的lib目錄。
當然,因為我們的工作比較簡單,就是借用winpcap發送數據包而已,所以只用從
winpcap開發包的include文件夾中,拷貝Packet32.h,到我們的工程來,並且包含它就可
以,但是要注意,Packet32.h本身還要包含一個Devioctl.h,也要一併拷貝進來,當然還有運
行庫Packet.lib,一共就是需要拷貝3個文件了,如果加入庫不用我多說了吧,在工程裡面設
置,或者是在需要它的地方加入 #pragma comment(lib, "Packet.lib")了。

整個項目其實可以分為四個部分,填充數據包、發送數據包、枚舉系統網卡列表和
相關信息以及枚舉系統ARP緩存列表,下面我再講一下如何獲得系統的網卡以及ARP列
表,這兩個部分都要用到IP Helper的api,所以要包含以及庫文件Iphlpapi.lib,
其實都是很簡單的,只用寥寥幾行就OK了
2. 枚舉系統網卡以及信息
最好是先定義關於網卡信息的一個結構體,這樣顯得結構比較清晰
// 網卡信息
typedef struct tagAdapterInfo
{
char szDeviceName[128]; // 名字
char szIPAddrStr[16]; // IP
char szHWAddrStr[18]; // MAC
DWORD dwIndex; // 編號
}INFO_ADAPTER, *PINFO_ADAPTER;

/*********************************************************************
* Name & Params::
* AddAdapInfoToList
* (
* CListCtrl& list : CARPPlayerDlg傳入的list句柄
* )
* Purpose:
* 獲得系統的網卡信息,並將其添加到list控制項中
* Remarks:
* 獲得網卡IP及MAC用到了IpHelper api GetAdaptersInfo
******************************************************************/
void AddAdapInfoToList(CListCtrl& list)
{
char tempChar;
ULONG uListSize=1;
PIP_ADAPTER_INFO pAdapter; // 定義PIP_ADAPTER_INFO結構存儲網卡信息
int nAdapterIndex = 0;

DWORD dwRet = GetAdaptersInfo((PIP_ADAPTER_INFO)&tempChar, &uListSize);//關鍵函數

if (dwRet == ERROR_BUFFER_OVERFLOW)
{
PIP_ADAPTER_INFO pAdapterListBuffer = (PIP_ADAPTER_INFO)new(char[uListSize]);
dwRet = GetAdaptersInfo(pAdapterListBuffer, &uListSize);
if (dwRet == ERROR_SUCCESS)
{
pAdapter = pAdapterListBuffer;
while (pAdapter) // 枚舉網卡然後將相關條目添加到List中
{
// 網卡名字
CString strTemp = pAdapter->AdapterName;
strTemp = "\\Device\\NPF_" + strTemp; // 加上前綴
list.InsertItem(nAdapterIndex,strTemp);
strcpy(AdapterList[nAdapterIndex].szDeviceName,strTemp);
// IP
strcpy(AdapterList[nAdapterIndex].szIPAddrStr,
pAdapter->IpAddressList.IpAddress.String );
list.SetItemText(nAdapterIndex,1,AdapterList[nAdapterIndex].szIPAddrStr);
// MAC
formatMACToStr( AdapterList[nAdapterIndex].szHWAddrStr, pAdapter->Address );
list.SetItemText(nAdapterIndex,2,AdapterLis[nAdapterIndex].szHWAddrStr);
// 網卡編號
AdapterList[nAdapterIndex].dwIndex = pAdapter->Index;

pAdapter = pAdapter->Next;
nAdapterIndex ++;
}
delete pAdapterListBuffer;
}
}
}

2)獲取ARP條目列表
// ARP條目信息
typedef struct tagARPInfo
{
char szIPAddrStr[16]; // IP
char szHWAddrStr[18]; // MAC
DWORD dwType; // 類型
}INFO_ARP, *PINFO_ARP;


/**********************************************************************
* Name & Params::
* AddARPInfoToList
* (
* CListCtrl& list : CARPPlayerDlg傳入的list句柄
* const short nAdapterIndex : 用戶選中的網卡編號
* )
* Purpose:
* 讀入系統的ARP緩存列表,.並添加到對話框中
* Remarks:
* 用到了IpHelper api GetIpNetTable
* 而且用到了WinSock的api,所以要包含
*****************************************************************/
void AddARPInfoToList(CListCtrl& list,const short nAdapterIndex)
{
char tempChar;
DWORD dwListSize = 1;
DWORD dwRet;
in_addr inaddr;
list.DeleteAllItems();

dwRet = GetIpNetTable((PMIB_IPNETTABLE)&tempChar, &dwListSize, TRUE); // 關鍵函數
if (dwRet == ERROR_INSUFFICIENT_BUFFER)
{
PMIB_IPNETTABLE pIpNetTable = (PMIB_IPNETTABLE)new(char[dwListSize]);
dwRet = GetIpNetTable(pIpNetTable, &dwListSize, TRUE);
if (dwRet == ERROR_SUCCESS)
{
for (int i=0; i<(int)pIpNetTable->dwNumEntries; i++)
{
// IP
inaddr.S_un.S_addr = pIpNetTable->table.dwAddr;
strcpy( ARPList.szIPAddrStr, inet_ntoa(inaddr) );
// MAC
formatMACToStr( ARPList.szHWAddrStr, pIpNetTable->table.bPhysAddr );
// Type
ARPList.dwType = pIpNetTable->table.dwType;

if (AdapterList[nAdapterIndex].dwIndex != pIpNetTable->table.dwIndex) continue;

list.InsertItem(i,ARPList.szIPAddrStr);
list.SetItemText(i,1,ARPList.szHWAddrStr);
switch(ARPList.dwType) { // 根據type的值來轉換成字元顯示
case 3:
list.SetItemText(i,2,"Dynamic");
break;
case 4:
list.SetItemText(i,2,"Static");
break;
case 1:
list.SetItemText(i,2,"Invalid");
default:
list.SetItemText(i,2,"Other");
}
}
}
delete pIpNetTable;
}
}
這樣一來,我們基本上大功告成了,其他還有一些東西在這裡就不講了,大家可以下載我的代碼看看就好了。
下面我們來用ARP包玩一些小把戲 ^_^。
四.ARP包的遊戲
既然我們可以自己來填充數據包,那麼來玩些ARP的「小遊戲」欺騙就是易如反掌了,當然,是在沒有安全防護的網路里,比如只有hub或者交換機把你們相連,而沒有路由分段……^_^
下面我就由淺入深的講一些介紹一些關於ARP的小伎倆。
1. 小伎倆
1) 你可以試著發一個請求包廣播,其中的ARP幀里關於你的信息填成這樣:
(為了節省篇幅,我只寫需要特別指出的填充欄位)
發送方MAC 6 隨便亂填一個錯誤的
發送方IP 4 填上你的IP
出現什麼結果?是不是彈出一個IP地址衝突的提示?呵呵,同樣的道理,如果發送方IP填成別人的,然後每隔1秒發一次………..-_-b
2) 比如你們都靠一個網關192.168.0.1 上網,如果你想讓192.168.0.77 上不了網,就可以偽裝成網關給192.168.0.77發一個錯誤的ARP響應包, like this
發送方MAC 6 隨便亂填一個錯誤的
發送方IP 4 網關IP 192.168.0.1
接收方就填192.168.0.77的相關信息,發送之後,它還能上網不?
這樣能折騰他好一陣子了,只要它的系統得不到正確的到網關的ARP映射表它就一直上不了網了
呵呵類似的伎倆還有很多,不過只停留在這點東西上也沒什麼意思,還是看看稍微高深一點的吧
2. ARP欺騙
因為在乙太網里,網路設備就是靠MAC信息來識別的計算機的,比如A電腦知道MAC地址為22-22-22-22-22-22的電腦是B,而如果我給A發送一個ARP響應包,告訴它我的MAC是22-22-22-22-22-22的話,A同樣會認為我的計算機是B了,那麼好,我們設想有這麼一個環境,
A的防火牆只對IP為192.168.0.2 MAC為 22-22-22-22-22-22的B有信任關係,而且A打開了21埠提供FTP服務,正常情況下因為防火牆的緣故我們的計算機是連不到A的,
於是我們想辦法讓B down掉,或者在它關機的時候,我們把我們的IP改成B的192.168.0.2,然後給A發送一個ARP回應包,告訴A更新一下ARP緩存列表,192.168.0.2的IP映射到我們的
MAC地址上來,於是,奇迹出現了,我們可以連到A的FTP上了,防火牆失效了^_^
不過這個辦法只能在同網段內生效,如果我們和A不在一個網段內,那就要複雜的多了,還要配合ICMP的重定向來控制報文的路由,這個我準備在以後闡述ICMP包的時候詳細講解,就不再此多說了。
3. 基於ARP欺騙的監聽原理
監聽的技術有很多了,不過我們常用的sniffer工具只能在基於hub的網路中起作用,碰到哪怕是交換機都無能為力了,這個時候我們的ARP欺騙技術就派上用場了。
還是假設有三台主機A,B,還有我們的主機,位於同一個交換式區域網中
A與B正在通信,如果我們想要刺探A??>B通信的內容,於是我們就可以給A發送一個偽造的ARP回應包,告訴A,B的IP對應的MAC條目為我們的MAC地址,於是,A也就會相應的刷新自己的ARP緩存,將發給B的數據,源源不斷的發送到我們的主機上來,這樣我就可以對接收到的數據包進行分析就好了,達到了監聽的目的。當然,因為動態ARP緩存是動態的,有超時時間的,所以我們必須每隔一段時間就給A發送一個ARP回應包
雖然我們這樣達到了目的,但是A到B的通信卻被停止了,為了不讓B發現,我們還要對每次接收到的數據包進行轉發,全部都轉發給B,這樣就天衣無縫了^_^
同樣的,如果我們還想監聽B à A的數據包,一樣給B發一個ARP回應包,告訴B,A的IP對應的MAC是我們的主機MAC,於是B到A的數據包也源源不斷的發到我們的主機上來了,當然我們也是一樣要對這些數據包進行轉發,如圖:
A <------> 我們的主機 <------> B
一切都無誤的話,A和B的通信內容就這樣不知不覺的被我們監聽到了^_^

感覺這個帖子不錯 轉載過來。。。。。。。。

[火星人 ] 手把手教你玩轉ARP包已經有693次圍觀

http://coctec.com/docs/linux/show-post-199576.html