经过上一篇博客的总结,我知道到了原始套接字接收到的字符串的开始字段是IP数据报的首部,所以我想除了之前利用win_pcap可以捕获数据包以外,理论上来说原始套接字也可以实现对IP数据报的捕获。思路也很简单,捕获到字符串以后转换成指向IP首部结构体的指针,再打印相关信息就可以了。
当然由于网卡会默认丢掉不属于本机的数据包,所以需要将套接字设置为接收所有数据。win_pcap是直接对网卡进行设置。ioctlsocket( )函数用于控制套接字上的I/O行为,同时获取与那个套接字上挂起的网络I/O操作的有关信息。
int ioctlsocket(
SOCKET s,//套接字
long cmd,//对s的命令
u_long* argp//命令参数指针
)//如果成功的话返回0,如果失败的话返回错误代码
函数的示例(让socket接收所有数据包):
u_long flag=1; ioctlsocket(s,SIO_RCVALL,&flag);
于是有了以下一段代码:
#include "stdafx.h" #include <winsock2.h> #include <ws2tcpip.h> #include "mstcpip.h" //自定义 #include <stdio.h> #pragma comment(lib, "ws2_32.lib") struct IPHEADER { unsigned char h_verlen; //4位首部长度,4位IP版本号 unsigned char tos; //8位服务类型TOS unsigned short total_len; //16位总长度(字节) unsigned short ident; //16位标识 unsigned short frag_and_flags; //3位标志位 unsigned char ttl; //8位生存时间 TTL unsigned char proto; //8位协议 (TCP, UDP 或其他) unsigned short checksum; //16位IP首部校验和 in_addr sourceIP; //32位源IP地址 in_addr destIP; //32位目的IP地址 }; #define BUFSIZE 256 #define RECVBUF 1520 int analyzeIP(IPHEADER *pip, int buflen) { if (buflen < 20) { printf("此包不完整!\n"); } printf("版本号:%d\n", pip->h_verlen >> 4); printf("首部长度(字节):%d\n", (pip->h_verlen & 0x0f)*4); printf("总长度:%d\n", pip->total_len); printf("协议类型:%d\n", pip->proto); printf("源地址:%s\n", inet_ntoa(pip->sourceIP)); printf("目的地址:%s\n", inet_ntoa(pip->destIP)); return 0; } int main() { SOCKET s; WSADATA wsa; int retval; retval = WSAStartup(MAKEWORD(2, 2), &wsa); if (retval == SOCKET_ERROR) { printf("startup failed\n"); WSACleanup(); return 0; } s = socket(AF_INET, SOCK_RAW, IPPROTO_IP); if (s == INVALID_SOCKET) { printf("socket error\n"); WSACleanup(); return 0; } sockaddr_in addr; char hostname[BUFSIZE] = { 0 }; int namelen; namelen = gethostname(hostname, BUFSIZE);//取得本机名,存放在hostname里面 hostent *ph = gethostbyname(hostname); if (ph == NULL) { printf("gethostbyname failed\n"); WSACleanup(); closesocket(s); return 0; } addr.sin_family = AF_INET; addr.sin_port = htons(0); memcpy(&(addr.sin_addr), ph->h_addr, ph->h_length); retval = bind(s, (sockaddr*)&addr, sizeof(sockaddr_in)); if (retval == SOCKET_ERROR) { printf("bind error\n"); closesocket(s); WSACleanup(); return 0; } unsigned long flag = 1; retval = ioctlsocket(s, SIO_RCVALL, &flag);//将网卡设置为混杂 if (retval != 0) { printf("ioctlsocket error\n"); closesocket(s); WSACleanup(); return 0; } char recvbuf[RECVBUF]; size_t size; IPHEADER *p_ipheader; int cnt = 0; while (1) { size = recv(s, recvbuf, RECVBUF, 0); if (size == 0 || size == SOCKET_ERROR) { printf("recv error\n"); closesocket(s); WSACleanup(); continue; } cnt++; printf("第 %d 个包\n", cnt); p_ipheader = (IPHEADER*)recvbuf; analyzeIP(p_ipheader, size); memset(recvbuf, 0, sizeof(recvbuf)); } WSACleanup(); closesocket(s); return 0; }
但当我把这个程序和win_pcap的程序一起运行的时候,发现这个程序捕获的包没有win_pcap的程序捕获得多,而且这个程序捕获的包部分信息也很奇怪,比如源地址,目的地址等,于是我猜想这可能是windows处于安全原因对原始套接字的使用也做出了限制。