SNIFF(原始套接字系列六)
大家知道,以太网采用广播机制,所有与网络连接的工作站都可以看到网络上传递的数据。通过查看包含在帧中的目标地址,确定是否进行接收或放弃。如果证明数据确实是发给自己的,工作站将会接收数据并传递给高层协议进行处理。但是,如果让网卡置于混杂模式(Promiscuous mode),则网卡不会鉴别帧的MAC地址,而是一律接收。
上图给出了以太网的帧格式,网卡是通过图中的MAC地址进行ID标识的。传说中的网络嗅探(sniffer)就是指让网卡进入混杂模式从而接收正在局域网总线上发送的所有报文。为什么能够嗅探到局域网上的所有报文,是因为基于IEEE 802.3的以太网在MAC层采用广播方式发送帧。因此,从理论上来讲,我们可以编写黑客程序监听局域网上的所有信息。QQ、MSN监听软件就是基于这一机理的,它可以监听局域网上所有用户的QQ、MSN聊天记录。
为了实现sniffer,我们应首先让网卡进入混杂模式并建立和设置原始套接字为亲自处理报头:
//初始化SOCKET WSADATA wsaData; iErrorCode = WSAStartup(MAKEWORD(2, 1), &wsaData); CheckSockError(iErrorCode, "WSAStartup"); SockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_IP); CheckSockError(SockRaw, "socket"); //获取本机IP地址 char name[MAX_HOSTNAME_LAN]; iErrorCode = gethostname(name, MAX_HOSTNAME_LAN); CheckSockError(iErrorCode, "gethostname"); struct hostent *pHostent; pHostent = (struct hostent*)malloc(sizeof(struct hostent)); pHostent = gethostbyname(name); SOCKADDR_IN sa; sa.sin_family = AF_INET; sa.sin_port = htons(6000); memcpy(&sa.sin_addr.S_un.S_addr, pHostent->h_addr_list[0], pHostent->h_length); iErrorCode = bind(SockRaw, (PSOCKADDR) &sa, sizeof(sa)); CheckSockError(iErrorCode, "bind"); //设置SOCK_RAW为SIO_RCVALL,以便接收所有的IP包 DWORD dwBufferLen[10]; DWORD dwBufferInLen = 1; DWORD dwBytesReturned = 0; iErrorCode = WSAIoctl(SockRaw, SIO_RCVALL, &dwBufferInLen, sizeof(dwBufferInLen) , &dwBufferLen, sizeof(dwBufferLen), &dwBytesReturned, NULL, NULL); CheckSockError(iErrorCode, "Ioctl"); |
下面就可以接收并处理IP报文:
//侦听IP报文 while (1) { memset(RecvBuf, 0, sizeof(RecvBuf)); iErrorCode = recv(SockRaw, RecvBuf, sizeof(RecvBuf), 0); CheckSockError(iErrorCode, "recv"); iErrorCode = DecodeIpPack(RecvBuf, iErrorCode); CheckSockError(iErrorCode, "Decode"); } |
Sniffer程序接收到报文后,即可调用相应的程序来分析具体的报文。
对于sniffer我们不得不说的是,仅仅将网卡置于混杂模式并不能保证我们能嗅探到交换式局域网上的所有帧,因为交换式局域网已经不再是广播式/总线传输了,为了能嗅探到交换式局域网上的帧,我们需要采用另一项技术ARP欺骗。