ARP (Address Resolution Protocol,地址解析協(xié)議),是一種用于將 IP
地址轉(zhuǎn)換為物理地址(MAC地址
)的協(xié)議。它在 TCP/IP
協(xié)議棧中處于鏈路層,為了在局域網(wǎng)中能夠正確傳輸數(shù)據(jù)包而設(shè)計,由協(xié)議數(shù)據(jù)單元和對應(yīng)的操作命令組成。ARP
既可以由操作系統(tǒng)處理,也可以由網(wǎng)卡處理。
該協(xié)議的作用是通過一個局域網(wǎng)上的互聯(lián)網(wǎng)協(xié)議(IP)地址來查詢對應(yīng)的物理硬件地址,如數(shù)據(jù)包發(fā)送到路由器時,ARP 協(xié)議將使用嵌入在數(shù)據(jù)包中的目的 IP 地址查找對應(yīng)的物理地址,路由器根據(jù)獲取的 MAC 地址轉(zhuǎn)發(fā)數(shù)據(jù)包到下一個網(wǎng)絡(luò)。
協(xié)議工作過程如下:
- 主機A通過查找其ARP緩存表,比對目標(biāo)的IP地址是否存在于ARP緩存表中。
- 如果目標(biāo)機器的IP地址不存在于本地ARP緩存表中,則主機A需要進行ARP請求過程,它廣播一個ARP請求。
- 當(dāng)其他主機收到這個請求時,它會比對主機A設(shè)置的這個目標(biāo)IP地址和自己的IP地址是否一致。
- 如果一致的話,說明被查詢的這個IP地址正是自己的IP地址,此時這個主機就會直接向主機A發(fā)送ARP響應(yīng)數(shù)據(jù)包。
- 主機A在獲得了目標(biāo)主機的MAC地址信息之后,會把這個MAC地址信息存儲到自己的ARP緩存表中,以便以后再次使用。
ARP主機探測原理是通過發(fā)送 ARP 查詢報文,來獲取目標(biāo)主機的 MAC 地址,進而獲取目標(biāo)主機的 IP 地址。
主機探測的具體實現(xiàn)步驟如下:
- 構(gòu)造一個
ARP
查詢報文,報文中的目標(biāo)IP
地址為需要探測的主機IP
地址,源IP
地址為探測主機的IP地址,源MAC
地址為探測主機網(wǎng)卡的MAC
地址。 - 發(fā)送
ARP
查詢報文。如果目標(biāo)主機在線,且相應(yīng)功能正常,它將返回一個ARP
響應(yīng)報文,其中包含目標(biāo)主機的MAC
地址。 - 接收到
ARP
響應(yīng)報文之后,分析報文,從中提取出目標(biāo)主機的MAC
地址和IP
地址等信息。
在Windows
系統(tǒng)下,我們可以調(diào)用SendARP()
函數(shù)實現(xiàn)ARP
探測,該函數(shù)用于發(fā)送ARP
請求到指定的 IP
地址,以獲取其 MAC
地址。該函數(shù)參數(shù)傳入目標(biāo) IP
地址時能夠返回對應(yīng) MAC
地址。
SendARP 函數(shù)原型如下:
DWORD SendARP(
IN IPAddr DestIP, // 目標(biāo) IP 地址
IN IPAddr SrcIP, // 源 IP 地址(可以為 0)
OUT PULONG pMacAddr, // 接收目標(biāo) MAC 地址
IN OUT PULONG PhyAddrLen // 接收目標(biāo) MAC 地址的緩沖區(qū)大小,單位為字節(jié)
);
該函數(shù)的第一個參數(shù)為目標(biāo)IP
地址,第二個參數(shù)為本地主機IP
地址(可以填 0),第三個參數(shù)為接收返回的目標(biāo) MAC
地址的指針,第四個參數(shù)為指向緩沖區(qū)大小的指針。
當(dāng)調(diào)用 SendARP()
函數(shù)時,如果目標(biāo) IP
地址是在同一物理網(wǎng)絡(luò)中,則返回目標(biāo) IP
地址對應(yīng)的 MAC
地址,并且函數(shù)返回值為 NO_ERROR
。如果目標(biāo) IP
地址無效,或者無法獲得對應(yīng)的 MAC
地址,則函數(shù)返回值為錯誤代碼,應(yīng)該根據(jù)錯誤代碼來進行處理。
如下代碼實現(xiàn)了掃描局域網(wǎng)中指定ARP
主機地址的功能。代碼主要使用了SendARP()
函數(shù)來查詢目標(biāo)主機的MAC
地址,并將結(jié)果輸出。具體實現(xiàn)步驟如下:
#include <stdio.h>
#include <winsock2.h>
#include <IPHlpApi.h>
#pragma comment (lib,"ws2_32.lib")
#pragma comment (lib,"iphlpapi.lib")
// 掃描局域網(wǎng)中指定ARP主機地址
void ArpScan(char *LocalIP,char *TargetIP)
{
ULONG localIP = inet_addr(LocalIP);
ULONG targetIP = inet_addr(TargetIP);
ULONG macBuf[2] = { 0 };
ULONG macLen = 6;
DWORD retValue = SendARP(targetIP, localIP, macBuf, &macLen);
unsigned char *mac = (unsigned char*)macBuf;
printf("IP: %-12s --> MAC: ", TargetIP);
for (int x = 0; x < macLen; x++)
{
printf("%.2X", mac[x]);
if (x != macLen - 1)
printf("-");
}
printf("\n");
}
int main(int argc,char * argv[])
{
for (int x = 1; x < 100; x++)
{
char target[32] = { 0 };
sprintf(target, "192.168.1.%d", x);
ArpScan("192.168.1.2", target);
}
system("pause");
return 0;
}
根據(jù)端口探測中所使用的方法,實現(xiàn)多線程也很容易,如下代碼實現(xiàn)了使用多線程方式掃描局域網(wǎng)內(nèi)存活的主機。代碼中使用 SendARP()
函數(shù)來探測目標(biāo)主機是否存活,并使用多線程方式來加快掃描速度,同時使用臨界區(qū)來控制多線程條件下的輸出效果。
具體實現(xiàn)過程如下:
-
定義
checkActive()
函數(shù),該函數(shù)使用SendARP()
函數(shù)來判斷目標(biāo)主機是否存活。如果目標(biāo)主機存活,則在屏幕上輸出其IP
和MAC
地址。 -
定義
threadProc()
函數(shù)來作為多線程的回調(diào)函數(shù)。該函數(shù)接收一臺主機的IP
地址,并調(diào)用checkActive()
函數(shù)來探測該主機是否在線。 -
在
main()
函數(shù)中,定義開始和結(jié)束的IP
地址,并使用for
循環(huán)遍歷這個IP
地址段。在循環(huán)中,使用CreateThread()
函數(shù)來創(chuàng)建多個線程,每個線程負(fù)責(zé)探測其中一臺主機是否在線。 -
在
checkActive()
函數(shù)中,多線程會涉及到在界面上的輸出,為了控制多線程在輸出上的次序,使用了EnterCriticalSection()
和LeaveCriticalSection()
函數(shù)來表示臨界區(qū),只有進入臨界區(qū)的線程能夠打印輸出,其他線程需要等待進入臨界區(qū)。
#include <stdio.h>
#include <winsock2.h>
#include <iphlpapi.h>
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"iphlpapi.lib")
// 臨界區(qū),控制多線程打印順序
CRITICAL_SECTION g_critical;
bool checkActive(in_addr ip)
{
ULONG dstMac[2] = { 0 };
memset(dstMac, 0xff, sizeof(dstMac));
ULONG size = 6;
HRESULT re = SendARP(ip.S_un.S_addr, 0, dstMac, &size);
if (re == NO_ERROR && size == 6)
{
// 線程進入臨界區(qū),其他線程不能再進入,控制多線程在界面上的打印順序
EnterCriticalSection(&g_critical);
printf("[+] 發(fā)現(xiàn)存活主機: %-15s ---> MAC :", inet_ntoa(ip));
BYTE *bPhysAddr = (BYTE *)& dstMac;
for (int i = 0; i < (int)size; i++)
{
// 如果是mac地址的最后一段,就輸出換行
if (i == (size - 1))
{
printf("%.2X\n", (int)bPhysAddr[i]);
}
else
{
// 否則沒有到最后一段,依舊輸出,但不換行
printf("%.2X-", (int)bPhysAddr[i]);
}
}
// 線程離開臨界區(qū),其他線程能夠繼續(xù)進入
LeaveCriticalSection(&g_critical);
return true;
}
else
{
return false;
}
}
// 啟動多線程
DWORD WINAPI threadProc(LPVOID lpThreadParameter)
{
in_addr ip;
ip.S_un.S_addr = (ULONG)lpThreadParameter;
checkActive(ip);
return 0;
}
int main(int argc, char *argv[])
{
in_addr ip_start, ip_end;
// 定義開始IP
ip_start.S_un.S_addr = inet_addr("192.168.9.1");
// 定義結(jié)束IP
ip_end.S_un.S_addr = inet_addr("192.168.9.254");
// 循環(huán)探測主機
//初始臨界區(qū)
InitializeCriticalSection(&g_critical);
for (in_addr ip = ip_start; ip.S_un.S_addr < ip_end.S_un.S_addr; ip.S_un.S_un_b.s_b4++)
{
printf("探測: %s \r", inet_ntoa(ip));
CreateThread(NULL, 0, threadProc, (LPVOID)ip.S_un.S_addr, 0, 0);
}
system("pause");
return 0;
}
編譯并運行上述代碼片段,則會探測192.168.9.1
到192.168.9.254
網(wǎng)段內(nèi)存活的主機,并輸出該主機的MAC信息,輸出效果圖如下所示;文章來源:http://www.zghlxwxcb.cn/news/detail-710930.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-710930.html
到了這里,關(guān)于16.2 ARP 主機探測技術(shù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!