網絡嗅探器

網絡嗅探器:把網卡設置成混雜模式,並可實現對網絡上傳輸的數據包的捕獲與分析。windows

原理:api

  一般的套接字程序只能響應與本身MAC地址相匹配的 或者是 廣播形式發出的數據幀,對於其餘形式的數據幀網絡接口採起的動做是直接丟棄網絡

  爲了使網卡接收全部通過他的封包,要將其設置成混雜模式,經過原始套接字來實現。socket

設置混雜模式:tcp

  建立原始套接字,函數

  綁定到一個明確的本地地址,spa

  向套接字發送SIO_RCVALL控制命令,code

  接收全部的IP包blog

代碼實現步驟:接口

  1 建立原始套接字

  2 綁定到明確地址

  3 這是SIO_RCVALL控制代碼

  4 進入循環,調用recv函數接收通過本地網卡的IP封包。

主程序代碼以下

void main() { //建立原始套接字
    SOCKET sRaw = socket(AF_INET,SOCK_RAW,IPPROTO_IP); //獲取本地IP地址
    char szHostName[56]; SOCKADDR_IN addr_in; struct hostent *pHost; gethostname(szHostName,56); if((pHost=gethostbyname((char*)szHostName))==NULL) return; //套接字綁定
    addr_in.sin_family = AF_INET; addr_in.sin_port = htons(0); memcpy(&addr_in.sin_addr.S_un.S_addr,pHost->h_addr_list[0],pHost->h_length); printf("Binding to interface:%s\n",::inet_ntoa(addr_in.sin_addr)); if(bind(sRaw,(sockaddr*)&addr_in,sizeof(addr_in))==SOCKET_ERROR) return; //設置SIO_RECVALL控制代碼
    DWORD dwValue = 1; if(ioctlsocket(sRaw,SIO_RCVALL,&dwValue)!=0) return; //開始接收封包
    char buff[1024]; int nRet; while(true) { nRet = recv(sRaw,buff,1024,0); if(nRet>0) { DecodeIPPacket(buff); } } closesocket(sRaw); }

程序接收到IP封包後,調用自定義的DecodeIPPacket進行解包。取出封包中的協議頭,向用戶打印出協議信息。

解析IP頭代碼

void DecodeIPPacket(char *pData) { IPHeader *pIPHdr = (IPHeader*)pData; in_addr source,dest; char szSourceIp[32],szDestIp[32]; printf("\n\n------------------------------------------------\n"); //從IP頭中取出源IP和目的IP
    source.S_un.S_addr = pIPHdr->ipSource; dest.S_un.S_addr = pIPhdr->ipDestination; strcpy(szSourceIp,::inet_ntoa(source)); strcpy(szDestIp,::inet_ntoa(dest)); printf(" %s->%s\n",szSourceIp,szDestIp); //IP長度
    int nHeaderLen = (pIPHdr->iphVerLen & 0xf)*sizeof(ULONG); switch(pIPHdr->ipProtocol) { case IPPROTO_TCP: DecodeTCPPacket(pData+nHeaderLen); break; case IPPROTO_UDP: break; case IPPROTO_ICMP: break; } }

解析TCP頭代碼以下:取出端口號,輸出

void DecodeTCPPacket(char *pData) { TCPHeader &pTCPHdr = (TCPHeader*)pData; printf("Port:%d->%d\n",ntohs(pTCPHdr->sourcePort),ntohs(pTCOHdr->destinationPort)); switch(::ntohs(pTCPHdr->destinationPort)) { case 21: break; case 80: break; case 8080: break; } }

VS下完整代碼:

initsock.h:

#include <winsock2.h>
#pragma comment(lib, "WS2_32")    // 連接到WS2_32.lib

class CInitSock { public: CInitSock(BYTE minorVer = 2, BYTE majorVer = 2) { // 初始化WS2_32.dll
 WSADATA wsaData; WORD sockVersion = MAKEWORD(minorVer, majorVer); if(::WSAStartup(sockVersion, &wsaData) != 0) { exit(0); } } ~CInitSock() { ::WSACleanup(); } };

protoinfo.h:

//////////////////////////////////////////////////
// protoinfo.h文件

/* 定義協議格式 定義協議中使用的宏 */ #ifndef __PROTOINFO_H__ #define __PROTOINFO_H__


#define ETHERTYPE_IP    0x0800
#define ETHERTYPE_ARP   0x0806 typedef struct _ETHeader         // 14字節的以太頭
{ UCHAR dhost[6];            // 目的MAC地址destination mac address
    UCHAR    shost[6];            // 源MAC地址source mac address
    USHORT    type;                // 下層協議類型,如IP(ETHERTYPE_IP)、ARP(ETHERTYPE_ARP)等
} ETHeader, *PETHeader; #define ARPHRD_ETHER     1

// ARP協議opcodes
#define    ARPOP_REQUEST    1        // ARP 請求 
#define    ARPOP_REPLY        2        // ARP 響應 typedef struct _ARPHeader        // 28字節的ARP頭
{ USHORT hrd; // 硬件地址空間,以太網中爲ARPHRD_ETHER
    USHORT    eth_type;            // 以太網類型,ETHERTYPE_IP ??
    UCHAR    maclen;                // MAC地址的長度,爲6
    UCHAR    iplen;                // IP地址的長度,爲4
    USHORT    opcode;                // 操做代碼,ARPOP_REQUEST爲請求,ARPOP_REPLY爲響應
    UCHAR    smac[6];            // 源MAC地址
    UCHAR    saddr[4];            // 源IP地址
    UCHAR    dmac[6];            // 目的MAC地址
    UCHAR    daddr[4];            // 目的IP地址
} ARPHeader, *PARPHeader; // 協議
#define PROTO_ICMP    1
#define PROTO_IGMP    2
#define PROTO_TCP     6
#define PROTO_UDP     17 typedef struct _IPHeader        // 20字節的IP頭
{ UCHAR iphVerLen; // 版本號和頭長度(各佔4位)
    UCHAR     ipTOS;          // 服務類型 
    USHORT    ipLength;       // 封包總長度,即整個IP報的長度
    USHORT    ipID;              // 封包標識,唯一標識發送的每個數據報
    USHORT    ipFlags;          // 標誌
    UCHAR     ipTTL;          // 生存時間,就是TTL
    UCHAR     ipProtocol;     // 協議,多是TCP、UDP、ICMP等
    USHORT    ipChecksum;     // 校驗和
    ULONG     ipSource;       // 源IP地址
    ULONG     ipDestination;  // 目標IP地址
} IPHeader, *PIPHeader; // 定義TCP標誌
#define   TCP_FIN   0x01
#define   TCP_SYN   0x02
#define   TCP_RST   0x04
#define   TCP_PSH   0x08
#define   TCP_ACK   0x10
#define   TCP_URG   0x20
#define   TCP_ACE   0x40
#define   TCP_CWR   0x80 typedef struct _TCPHeader        // 20字節的TCP頭
{ USHORT sourcePort; // 16位源端口號
    USHORT    destinationPort;    // 16位目的端口號
    ULONG    sequenceNumber;        // 32位序列號
    ULONG    acknowledgeNumber;    // 32位確認號
    UCHAR    dataoffset;            // 高4位表示數據偏移
    UCHAR    flags;                // 6位標誌位 //FIN - 0x01 //SYN - 0x02 //RST - 0x04 //PUSH- 0x08 //ACK- 0x10 //URG- 0x20 //ACE- 0x40 //CWR- 0x80
 USHORT windows; // 16位窗口大小
    USHORT    checksum;            // 16位校驗和
    USHORT    urgentPointer;        // 16位緊急數據偏移量 
} TCPHeader, *PTCPHeader; typedef struct _UDPHeader { USHORT sourcePort; // 源端口號 
    USHORT            destinationPort;// 目的端口號 
    USHORT            len;            // 封包長度
    USHORT            checksum;        // 校驗和
} UDPHeader, *PUDPHeader; #endif // __PROTOINFO_H__

 

#include "../common/initsock.h" #include "../common/protoinfo.h" #include <stdio.h> #include <mstcpip.h>

#pragma comment(lib, "Advapi32.lib") CInitSock theSock; void DecodeTCPPacket(char *pData) { TCPHeader *pTCPHdr = (TCPHeader *)pData; printf(" Port: %d -> %d \n", ntohs(pTCPHdr->sourcePort), ntohs(pTCPHdr->destinationPort)); // 下面還能夠根據目的端口號進一步解析應用層協議
    switch(::ntohs(pTCPHdr->destinationPort)) { case 21: break; case 80: case 8080: break; } } void DecodeIPPacket(char *pData) { IPHeader *pIPHdr = (IPHeader*)pData; in_addr source, dest; char szSourceIp[32], szDestIp[32]; printf("\n\n-------------------------------\n"); // 從IP頭中取出源IP地址和目的IP地址
    source.S_un.S_addr = pIPHdr->ipSource; dest.S_un.S_addr = pIPHdr->ipDestination; strcpy(szSourceIp, ::inet_ntoa(source)); strcpy(szDestIp, ::inet_ntoa(dest)); printf(" %s -> %s \n", szSourceIp, szDestIp); // IP頭長度
    int nHeaderLen = (pIPHdr->iphVerLen & 0xf) * sizeof(ULONG); switch(pIPHdr->ipProtocol) { case IPPROTO_TCP: // TCP協議
        DecodeTCPPacket(pData + nHeaderLen); break; case IPPROTO_UDP: break; case IPPROTO_ICMP: break; } } void main() { // 建立原始套節字
    SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_IP); // 獲取本地IP地址
    char szHostName[56]; SOCKADDR_IN addr_in; struct  hostent *pHost; gethostname(szHostName, 56); if((pHost = gethostbyname((char*)szHostName)) == NULL) return ; // 在調用ioctl以前,套節字必須綁定
    addr_in.sin_family  = AF_INET; addr_in.sin_port = htons(0); memcpy(&addr_in.sin_addr.S_un.S_addr, pHost->h_addr_list[0], pHost->h_length); printf(" Binding to interface : %s \n", ::inet_ntoa(addr_in.sin_addr)); if(bind(sRaw, (PSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR) return; // 設置SIO_RCVALL控制代碼,以便接收全部的IP包 
    DWORD dwValue = 1; if(ioctlsocket(sRaw, SIO_RCVALL, &dwValue) != 0) return ; // 開始接收封包
    char buff[1024]; int nRet; while(TRUE) { nRet = recv(sRaw, buff, 1024, 0); if(nRet > 0) { DecodeIPPacket(buff); } } closesocket(sRaw); }

運行結果