ARP (Address Resolution Protocol,地址解析協議),是一種用於將 `IP` 地址轉換為物理地址(`MAC地址`)的協議。它在 `TCP/IP` 協議棧中處於鏈路層,為了在區域網中能夠正確傳輸數據包而設計,由協議數據單元和對應的操作命令組成。`ARP` 既可以由操作系統處... ...
ARP (Address Resolution Protocol,地址解析協議),是一種用於將 IP
地址轉換為物理地址(MAC地址
)的協議。它在 TCP/IP
協議棧中處於鏈路層,為了在區域網中能夠正確傳輸數據包而設計,由協議數據單元和對應的操作命令組成。ARP
既可以由操作系統處理,也可以由網卡處理。
該協議的作用是通過一個區域網上的互聯網協議(IP)地址來查詢對應的物理硬體地址,如數據包發送到路由器時,ARP 協議將使用嵌入在數據包中的目的 IP 地址查找對應的物理地址,路由器根據獲取的 MAC 地址轉發數據包到下一個網路。
協議工作過程如下:
- 主機A通過查找其ARP緩存表,比對目標的IP地址是否存在於ARP緩存表中。
- 如果目標機器的IP地址不存在於本地ARP緩存表中,則主機A需要進行ARP請求過程,它廣播一個ARP請求。
- 當其他主機收到這個請求時,它會比對主機A設置的這個目標IP地址和自己的IP地址是否一致。
- 如果一致的話,說明被查詢的這個IP地址正是自己的IP地址,此時這個主機就會直接向主機A發送ARP響應數據包。
- 主機A在獲得了目標主機的MAC地址信息之後,會把這個MAC地址信息存儲到自己的ARP緩存表中,以便以後再次使用。
ARP主機探測原理是通過發送 ARP 查詢報文,來獲取目標主機的 MAC 地址,進而獲取目標主機的 IP 地址。
主機探測的具體實現步驟如下:
- 構造一個
ARP
查詢報文,報文中的目標IP
地址為需要探測的主機IP
地址,源IP
地址為探測主機的IP地址,源MAC
地址為探測主機網卡的MAC
地址。 - 發送
ARP
查詢報文。如果目標主機線上,且相應功能正常,它將返回一個ARP
響應報文,其中包含目標主機的MAC
地址。 - 接收到
ARP
響應報文之後,分析報文,從中提取出目標主機的MAC
地址和IP
地址等信息。
在Windows
系統下,我們可以調用SendARP()
函數實現ARP
探測,該函數用於發送ARP
請求到指定的 IP
地址,以獲取其 MAC
地址。該函數參數傳入目標 IP
地址時能夠返回對應 MAC
地址。
SendARP 函數原型如下:
DWORD SendARP(
IN IPAddr DestIP, // 目標 IP 地址
IN IPAddr SrcIP, // 源 IP 地址(可以為 0)
OUT PULONG pMacAddr, // 接收目標 MAC 地址
IN OUT PULONG PhyAddrLen // 接收目標 MAC 地址的緩衝區大小,單位為位元組
);
該函數的第一個參數為目標IP
地址,第二個參數為本地主機IP
地址(可以填 0),第三個參數為接收返回的目標 MAC
地址的指針,第四個參數為指向緩衝區大小的指針。
當調用 SendARP()
函數時,如果目標 IP
地址是在同一物理網路中,則返回目標 IP
地址對應的 MAC
地址,並且函數返回值為 NO_ERROR
。如果目標 IP
地址無效,或者無法獲得對應的 MAC
地址,則函數返回值為錯誤代碼,應該根據錯誤代碼來進行處理。
如下代碼實現了掃描區域網中指定ARP
主機地址的功能。代碼主要使用了SendARP()
函數來查詢目標主機的MAC
地址,並將結果輸出。具體實現步驟如下:
#include <stdio.h>
#include <winsock2.h>
#include <IPHlpApi.h>
#pragma comment (lib,"ws2_32.lib")
#pragma comment (lib,"iphlpapi.lib")
// 掃描區域網中指定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;
}
根據埠探測中所使用的方法,實現多線程也很容易,如下代碼實現了使用多線程方式掃描區域網記憶體活的主機。代碼中使用 SendARP()
函數來探測目標主機是否存活,並使用多線程方式來加快掃描速度,同時使用臨界區來控制多線程條件下的輸出效果。
具體實現過程如下:
-
定義
checkActive()
函數,該函數使用SendARP()
函數來判斷目標主機是否存活。如果目標主機存活,則在屏幕上輸出其IP
和MAC
地址。 -
定義
threadProc()
函數來作為多線程的回調函數。該函數接收一臺主機的IP
地址,並調用checkActive()
函數來探測該主機是否線上。 -
在
main()
函數中,定義開始和結束的IP
地址,並使用for
迴圈遍歷這個IP
地址段。在迴圈中,使用CreateThread()
函數來創建多個線程,每個線程負責探測其中一臺主機是否線上。 -
在
checkActive()
函數中,多線程會涉及到在界面上的輸出,為了控制多線程在輸出上的次序,使用了EnterCriticalSection()
和LeaveCriticalSection()
函數來表示臨界區,只有進入臨界區的線程能夠列印輸出,其他線程需要等待進入臨界區。
#include <stdio.h>
#include <winsock2.h>
#include <iphlpapi.h>
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"iphlpapi.lib")
// 臨界區,控制多線程列印順序
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)
{
// 線程進入臨界區,其他線程不能再進入,控制多線程在界面上的列印順序
EnterCriticalSection(&g_critical);
printf("[+] 發現存活主機: %-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]);
}
}
// 線程離開臨界區,其他線程能夠繼續進入
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");
// 定義結束IP
ip_end.S_un.S_addr = inet_addr("192.168.9.254");
// 迴圈探測主機
//初始臨界區
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
網段記憶體活的主機,並輸出該主機的MAC信息,輸出效果圖如下所示;
本文作者: 王瑞
本文鏈接: https://www.lyshark.com/post/57dc46.html
版權聲明: 本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
文章出處:https://www.cnblogs.com/LyShark/p/17778431.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!