明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

使用Sniffer截獲流經(jīng)本機網(wǎng)卡的IP數(shù)據(jù)包

[摘要]Win2K下的Sniffer源代碼。[代碼性質(zhì)] VC完整應(yīng)用程序代碼[代碼作者] zw[文件大小] 130K [更新日期] 2002-11-26 19:47:00 [下載次數(shù)] 6015 http://www.vckbase.com/code/downcode.asp?id=1692IP包監(jiān)聽...
Win2K下的Sniffer源代碼。

[代碼性質(zhì)] VC完整應(yīng)用程序代碼
[代碼作者] zw
[文件大小] 130K
[更新日期] 2002-11-26 19:47:00
[下載次數(shù)] 6015
http://www.vckbase.com/code/downcode.asp?id=1692

IP包監(jiān)聽程序(For 9x)源代碼 詳細信息 < 局域網(wǎng) >  

IP包監(jiān)聽程序源代碼(包含VXD源代碼)

[代碼性質(zhì)] VC完整應(yīng)用程序代碼
[代碼作者] HiHint
[文件大小] 158K
[更新日期] 2002-3-30 8:48:00
[下載次數(shù)] 9047  
http://www.vckbase.com/code/downcode.asp?id=1508



從事網(wǎng)絡(luò)安全的技術(shù)人員和相當一部分準黑客(指那些使用現(xiàn)成的黑客軟件進行攻擊而不是根據(jù)需要去自己編寫代碼的人)都一定不會對網(wǎng)絡(luò)嗅探器(sniffer)感到陌生,網(wǎng)絡(luò)嗅探器無論是在網(wǎng)絡(luò)安全還是在黑客攻擊方面均扮演了很重要的角色。通過使用網(wǎng)絡(luò)嗅探器可以把網(wǎng)卡設(shè)置于混雜模式,并可實現(xiàn)對網(wǎng)絡(luò)上傳輸?shù)臄?shù)據(jù)包的捕獲與分析。此分析結(jié)果可供網(wǎng)絡(luò)安全分析之用,但如為黑客所利用也可以為其發(fā)動進一步的攻擊提供有價值的信息。可見,嗅探器實際是一把雙刃劍。 雖然網(wǎng)絡(luò)嗅探器技術(shù)被黑客利用后會對網(wǎng)絡(luò)安全構(gòu)成一定的威脅,但嗅探器本身的危害并不是很大,主要是用來為其他黑客軟件提供網(wǎng)絡(luò)情報,真正的攻擊主要是由其他黑軟來完成的。而在網(wǎng)絡(luò)安全方面,網(wǎng)絡(luò)嗅探手段可以有效地探測在網(wǎng)絡(luò)上傳輸?shù)臄?shù)據(jù)包信息,通過對這些信息的分析利用是有助于網(wǎng)絡(luò)安全維護的。權(quán)衡利弊,有必要對網(wǎng)絡(luò)嗅探器的實現(xiàn)原理進行介紹。

  嗅探器設(shè)計原理

  嗅探器作為一種網(wǎng)絡(luò)通訊程序,也是通過對網(wǎng)卡的編程來實現(xiàn)網(wǎng)絡(luò)通訊的,對網(wǎng)卡的編程也是使用通常的套接字(socket)方式來進行。但是,通常的套接字程序只能響應(yīng)與自己硬件地址相匹配的或是以廣播形式發(fā)出的數(shù)據(jù)幀,對于其他形式的數(shù)據(jù)幀比如已到達網(wǎng)絡(luò)接口但卻不是發(fā)給此地址的數(shù)據(jù)幀,網(wǎng)絡(luò)接口在驗證投遞地址并非自身地址之后將不引起響應(yīng),也就是說應(yīng)用程序無法收取到達的數(shù)據(jù)包。而網(wǎng)絡(luò)嗅探器的目的恰恰在于從網(wǎng)卡接收所有經(jīng)過它的數(shù)據(jù)包,這些數(shù)據(jù)包即可以是發(fā)給它的也可以是發(fā)往別處的。顯然,要達到此目的就不能再讓網(wǎng)卡按通常的正常模式工作,而必須將其設(shè)置為混雜模式。

  具體到編程實現(xiàn)上,這種對網(wǎng)卡混雜模式的設(shè)置是通過原始套接字(raw socket)來實現(xiàn)的,這也有別于通常經(jīng)常使用的數(shù)據(jù)流套接字和數(shù)據(jù)報套接字。在創(chuàng)建了原始套接字后,需要通過setsockopt()函數(shù)來設(shè)置IP頭操作選項,然后再通過bind()函數(shù)將原始套接字綁定到本地網(wǎng)卡。為了讓原始套接字能接受所有的數(shù)據(jù),還需要通過ioctlsocket()來進行設(shè)置,而且還可以指定是否親自處理IP頭。至此,實際就可以開始對網(wǎng)絡(luò)數(shù)據(jù)包進行嗅探了,對數(shù)據(jù)包的獲取仍象流式套接字或數(shù)據(jù)報套接字那樣通過recv()函數(shù)來完成。但是與其他兩種套接字不同的是,原始套接字此時捕獲到的數(shù)據(jù)包并不僅僅是單純的數(shù)據(jù)信息,而是包含有 IP頭、 TCP頭等信息頭的最原始的數(shù)據(jù)信息,這些信息保留了它在網(wǎng)絡(luò)傳輸時的原貌。通過對這些在低層傳輸?shù)脑夹畔⒌姆治隹梢缘玫接嘘P(guān)網(wǎng)絡(luò)的一些信息。由于這些數(shù)據(jù)經(jīng)過了網(wǎng)絡(luò)層和傳輸層的打包,因此需要根據(jù)其附加的幀頭對數(shù)據(jù)包進行分析。下面先給出結(jié)構(gòu).數(shù)據(jù)包的總體結(jié)構(gòu):

數(shù)據(jù)包
IP頭 TCP頭(或其他信息頭) 數(shù)據(jù)

  數(shù)據(jù)在從應(yīng)用層到達傳輸層時,將添加TCP數(shù)據(jù)段頭,或是UDP數(shù)據(jù)段頭。其中UDP數(shù)據(jù)段頭比較簡單,由一個8字節(jié)的頭和數(shù)據(jù)部分組成,具體格式如下:


16位 16位
源端口 目的端口
UDP長度 UDP校驗和

  而TCP數(shù)據(jù)頭則比較復雜,以20個固定字節(jié)開始,在固定頭后面還可以有一些長度不固定的可選項,下面給出TCP數(shù)據(jù)段頭的格式組成:


16位  16位
源端口 目的端口
順序號
確認號
TCP頭長 (保留)7位 URG ACK  PSH RST SYN FIN  窗口大小
校驗和  緊急指針
可選項(0或更多的32位字)
數(shù)據(jù)(可選項)

  對于此TCP數(shù)據(jù)段頭的分析在編程實現(xiàn)中可通過數(shù)據(jù)結(jié)構(gòu)_TCP來定義:


typedef struct _TCP{ WORD SrcPort; // 源端口
WORD DstPort; // 目的端口
DWORD SeqNum; // 順序號
DWORD AckNum; // 確認號
BYTE DataOff; // TCP頭長
BYTE Flags; // 標志(URG、ACK等)
WORD Window; // 窗口大小
WORD Chksum; // 校驗和
WORD UrgPtr; // 緊急指針
} TCP;
typedef TCP *LPTCP;
typedef TCP UNALIGNED * ULPTCP;

  在網(wǎng)絡(luò)層,還要給TCP數(shù)據(jù)包添加一個IP數(shù)據(jù)段頭以組成IP數(shù)據(jù)報。IP數(shù)據(jù)頭以大端點機次序傳送,從左到右,版本字段的高位字節(jié)先傳輸(SPARC是大端點機;Pentium是小端點機)。如果是小端點機,就要在發(fā)送和接收時先行轉(zhuǎn)換然后才能進行傳輸。IP數(shù)據(jù)段頭格式如下:


16位 16位
版本  IHL  服務(wù)類型 總長
標識  標志 分段偏移
生命期 協(xié)議  頭校驗和
源地址
目的地址
選項(0或更多)

  同樣,在實際編程中也需要通過一個數(shù)據(jù)結(jié)構(gòu)來表示此IP數(shù)據(jù)段頭,下面給出此數(shù)據(jù)結(jié)構(gòu)的定義:


typedef struct _IP{
union{ BYTE Version; // 版本
BYTE HdrLen; // IHL
};
BYTE ServiceType; // 服務(wù)類型
WORD TotalLen; // 總長
WORD ID; // 標識
union{ WORD Flags; // 標志
WORD FragOff; // 分段偏移
};
BYTE TimeToLive; // 生命期
BYTE Protocol; // 協(xié)議
WORD HdrChksum; // 頭校驗和
DWORD SrcAddr; // 源地址
DWORD DstAddr; // 目的地址
BYTE Options; // 選項
} IP;
typedef IP * LPIP;
typedef IP UNALIGNED * ULPIP;


  在明確了以上幾個數(shù)據(jù)段頭的組成結(jié)構(gòu)后,就可以對捕獲到的數(shù)據(jù)包進行分析了。

嗅探器的具體實現(xiàn)

  根據(jù)前面的設(shè)計思路,不難寫出網(wǎng)絡(luò)嗅探器的實現(xiàn)代碼,下面就給出一個簡單的示例,該示例可以捕獲到所有經(jīng)過本地網(wǎng)卡的數(shù)據(jù)包,并可從中分析出協(xié)議、IP源地址、IP目標地址、TCP源端口號、TCP目標端口號以及數(shù)據(jù)包長度等信息。由于前面已經(jīng)將程序的設(shè)計流程講述的比較清楚了,因此這里就不在贅述了,下面就結(jié)合注釋對程序的具體是實現(xiàn)進行講解,同時為程序流程的清晰起見,去掉了錯誤檢查等保護性代碼。主要代碼實現(xiàn)清單為:

// 檢查 Winsock 版本號,WSAData為WSADATA結(jié)構(gòu)對象
WSAStartup(MAKEWORD(2, 2), &WSAData);
// 創(chuàng)建原始套接字
sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW));
// 設(shè)置IP頭操作選項,其中flag 設(shè)置為ture,親自對IP頭進行處理
setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag));
// 獲取本機名
gethostname((char*)LocalName, sizeof(LocalName)-1);
// 獲取本地 IP 地址
pHost = gethostbyname((char*)LocalName));
// 填充SOCKADDR_IN結(jié)構(gòu)
addr_in.sin_addr = *(in_addr *)pHost->h_addr_list[0]; //IP
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(57274);
// 把原始套接字sock 綁定到本地網(wǎng)卡地址上
bind(sock, (PSOCKADDR)&addr_in, sizeof(addr_in));
// dwValue為輸入輸出參數(shù),為1時執(zhí)行,0時取消
DWORD dwValue = 1;
// 設(shè)置 SOCK_RAW 為SIO_RCVALL,以便接收所有的IP包。其中SIO_RCVALL
// 的定義為: #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)
ioctlsocket(sock, SIO_RCVALL, &dwValue);

  前面的工作基本上都是對原始套接字進行設(shè)置,在將原始套接字設(shè)置完畢,使其能按預期目的工作時,就可以通過recv()函數(shù)從網(wǎng)卡接收數(shù)據(jù)了,接收到的原始數(shù)據(jù)包存放在緩存RecvBuf[]中,緩沖區(qū)長度BUFFER_SIZE定義為65535。然后就可以根據(jù)前面對IP數(shù)據(jù)段頭、TCP數(shù)據(jù)段頭的結(jié)構(gòu)描述而對捕獲的數(shù)據(jù)包進行分析:

while (true)
{
// 接收原始數(shù)據(jù)包信息
int ret = recv(sock, RecvBuf, BUFFER_SIZE, 0);
if (ret > 0)
{
// 對數(shù)據(jù)包進行分析,并輸出分析結(jié)果
ip = *(IP*)RecvBuf;
tcp = *(TCP*)(RecvBuf + ip.HdrLen);
TRACE("協(xié)議: %s\r\n",GetProtocolTxt(ip.Protocol));
TRACE("IP源地址: %s\r\n",inet_ntoa(*(in_addr*)&ip.SrcAddr));
TRACE("IP目標地址: %s\r\n",inet_ntoa(*(in_addr*)&ip.DstAddr));
TRACE("TCP源端口號: %d\r\n",tcp.SrcPort);
TRACE("TCP目標端口號:%d\r\n",tcp.DstPort);
TRACE("數(shù)據(jù)包長度: %d\r\n\r\n\r\n",ntohs(ip.TotalLen));
}
}

  其中,在進行協(xié)議分析時,使用了GetProtocolTxt()函數(shù),該函數(shù)負責將IP包中的協(xié)議(數(shù)字標識的)轉(zhuǎn)化為文字輸出,該函數(shù)實現(xiàn)如下:

#define PROTOCOL_STRING_ICMP_TXT "ICMP"
#define PROTOCOL_STRING_TCP_TXT "TCP"
#define PROTOCOL_STRING_UDP_TXT "UDP"
#define PROTOCOL_STRING_SPX_TXT "SPX"
#define PROTOCOL_STRING_NCP_TXT "NCP"
#define PROTOCOL_STRING_UNKNOW_TXT "UNKNOW"
……
CString CSnifferDlg::GetProtocolTxt(int Protocol)
{
switch (Protocol){
case IPPROTO_ICMP : //1 /* control message protocol */
return PROTOCOL_STRING_ICMP_TXT;
case IPPROTO_TCP : //6 /* tcp */
return PROTOCOL_STRING_TCP_TXT;
case IPPROTO_UDP : //17 /* user datagram protocol */
return PROTOCOL_STRING_UDP_TXT;
default:
return PROTOCOL_STRING_UNKNOW_TXT;
}

  最后,為了使程序能成功編譯,需要包含頭文件winsock2.h和ws2tcpip.h。在本示例中將分析結(jié)果用TRACE()宏進行輸出,在調(diào)試狀態(tài)下運行,得到的一個分析結(jié)果如下:

協(xié)議: UDP
IP源地址: 172.168.1.5
IP目標地址: 172.168.1.255
TCP源端口號: 16707
TCP目標端口號:19522
數(shù)據(jù)包長度: 78
……
協(xié)議: TCP
IP源地址: 172.168.1.17
IP目標地址: 172.168.1.1
TCP源端口號: 19714
TCP目標端口號:10
數(shù)據(jù)包長度: 200
……

  從分析結(jié)果可以看出,此程序完全具備了嗅探器的數(shù)據(jù)捕獲以及對數(shù)據(jù)包的分析等基本功能。

  小結(jié)

  本文介紹的以原始套接字方式對網(wǎng)絡(luò)數(shù)據(jù)進行捕獲的方法實現(xiàn)起來比較簡單,尤其是不需要編寫VxD虛擬設(shè)備驅(qū)動程序就可以實現(xiàn)抓包,使得其編寫過程變的非常簡便,但由于捕獲到的數(shù)據(jù)包頭不包含有幀信息,因此不能接收到與 IP 同屬網(wǎng)絡(luò)層的其它數(shù)據(jù)包, 如 ARP數(shù)據(jù)包、RARP數(shù)據(jù)包等。在前面給出的示例程序中考慮到安全因素,沒有對數(shù)據(jù)包做進一步的分析,而是僅僅給出了對一般信息的分析方法。通過本文的介紹,可對原始套接字的使用方法以及TCP/IP協(xié)議結(jié)構(gòu)原理等知識有一個基本的認識。本文所述代碼在Windows 2000下由Microsoft Visual C++ 6.0編譯調(diào)試通過。