发送接收ip数据包 【接收除了以太网帧头部后面的ip层数据】
socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)
发送接收以太网数据帧数据包 【接收包括以太网帧头部的所有 以太网帧层的所有数据】
socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))
参数说明:
1)AF_INET和PF_PACKET的区别
使用AF_INET可以接收协议类型为(tcp udp icmp等)发往本机的ip数据包,而使用PF_PACKET可以监听网卡上的所有数据帧。
注:创建原始套接字需要启动管理员权限否则,socket会创建失败
ip与端口的假象【混杂模式下端口号,IP可能都是假象】【原始套接字bind IP的目的是bind网卡】
然后需要为原始套接字 bind ip和端口号;这里端口号可以使用ADDR_ANY随机指定一个;因为对于原始套接字来说 端口号只是一个假象,原始套接字可以接收所有其绑定ip对应的网卡适配器上面流经的数据;
所以ip也是一种假象,重要的是为原始套接字绑定一个网卡适配器也就是所谓的网口;如果一台主机上有多个网口需要抓包,则需要创建多个套接字bind所有这些的网口;然后在这些套接字上监听抓取数据
网卡对数据帧进行硬过滤(根据网卡的模式不同采取不同的操作,如果设置了混杂模式,则不做任何过滤直接交给下一层,否则非本机mac或者广播mac的会被直接丢弃)
DWORD dwBufferLen[10] = { 0 };
DWORD dwBufferInLen = 1;
DWORD dwBytesReturned = 0;
// 设置该SOCKET为接收所有流经绑定的IP的网卡的所有数据,包括接收和发送的数据包
iRet = WSAIoctl(sock, SIO_RCVALL, &dwBufferInLen, sizeof(dwBufferInLen),//设置混杂模式
&dwBufferLen, sizeof(dwBufferLen), &dwBytesReturned, NULL, NULL);
if (iRet != 0) {
printf("WSAIoctl failed with error %d\n", WSAGetLastError());
}
最后 可以调用iRet = recvfrom(sock, strBuffer, sizeof(strBuffer), 0, (sockaddr*)&addrFrom, &fromlen); 函数进行数据包抓取了
以socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP) 进行ip包的数据抓取举例
对于ip包层来说,他不管包是TCP发的还是UDP发的,这都是上层协议;包都是ip包
原始套接字上即可以抓取由TCP发送过来的数据也可以抓取由UDP发送来的数据,对于ip包来说他是不管这个包是什么包的;因为那是他的上层协议去做的事情;那么在抓包的过程中接受的包的大小由什么决定了呢
ip包层抓取的数据包大小就是对端用TCP或者UDPsend发送的长度 + 40或者28【即加上IP报头和TCP或者UDP报头的长度】;【另外要说一点是虽然以太网帧协议的数据域大小上线是1500字节;如果发生端send发送的数据超过这个上线,在发送端就会对这个包进行分片;但是这一切对ip包层抓取的包来说是无感的;即在IP包层已经重组了以太网帧的1500数据域,即ip包层可以突破1500的抓包】
IP包层抓包对于这二种协议的接收都不应该低于数据域+40/28【以发送端send为标准】。否则IP包层接收recvform会返回错误;
ip包的接收缓冲区大小不能低于其接收的数据包大小;而其数据包大小=40/28 +数据域大小;这个数据域大小就是发送端用tcp或者udp进行数据发送时候的send的大小【注意是以发送端为标准】
所以对于ip包来说其处理接收缓冲区的方式有点类似于UDP包层的处理;但是仅仅是类似,ip包层不用管上层的协议对应的究竟是什么包;接收的大小也可以突破以太网帧的数据域大小;这就是协议栈分离效果的体现
从这点我们也可以来理解上层UDP/tcp的接收行为;【在udp层udp接受缓冲区必须不小于发生端包长度,TCP层可以流式接收】
对于udp来说很好理解,udp发送端发送的数据包大小就是其udp接收端接收的数据包大小;也是抓包时 ip数据包的大小的数据域组成;=>抓包接收的大小是ip包=28+udp包
但是对于tcp来说在传输层面上,tcp是流式协议,接收方的tcp 可以一个一个字节接收;可以一次性接收都可以;但是这跟ip包的接收recvform得到的大小无关;=>其ip抓包的大小是=40+tcp发送端发送的大小
所以用普通流式套接字你是可以一个一个的接收;但是原始套接字抓包,必须要提供的接收缓冲区不小于 20 + 20/8 +数据域大小 【数据域大小可能是0】
示例代码:
#include
#include
#include "protocol.h"
#pragma comment(lib,"WS2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS
using namespace std;
WsaInit Init;
// 使用原始套接字抓取网卡数据包
int capturenetpackage()
{
// 此处必须以管理员运行VS,否则返回10013
SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP); //处理 ip
if (sock == SOCKET_ERROR) {
printf("socket failed with error %d\n", WSAGetLastError());
exit(-1);
}
char strHostName[255];
int iRet = gethostname(strHostName, sizeof(strHostName));
if (iRet != 0) {
printf("gethostname failed with error %d\n", WSAGetLastError());
exit(-1);
}
// 根据主机名取得主机地址
hostent* pHostent = gethostbyname(strHostName);
// getaddrinfo()
sockaddr_in addrSelf;
addrSelf.sin_family = AF_INET;
addrSelf.sin_port = htons(ADDR_ANY);//端口是假象
memcpy(&addrSelf.sin_addr.S_un.S_addr, pHostent->h_addr_list[0], pHostent->h_length);
char szip[32]{};
for (int i = 0; pHostent->h_addr_list[i] != 0; ++i)
{
addrSelf.sin_addr.s_addr = *(u_long*)pHostent->h_addr_list[i];
inet_ntop(AF_INET, &addrSelf.sin_addr, szip, 32);
//printf("\tIPv4 Address %d: %s\n", i, szip);
}
printf("self local ip addr is %s\n", szip);
iRet = bind(sock, (PSOCKADDR)&addrSelf, sizeof(addrSelf));//bind网口 //ip也是假象 关键在于网口
if (iRet != 0) {
printf("bind failed with error %d\n", WSAGetLastError());
exit(-1);
}
DWORD dwBufferLen[10] = { 0 };
DWORD dwBufferInLen = 1;
DWORD dwBytesReturned = 0;
// 设置该SOCKET为接收所有流经绑定的IP的网卡的所有数据,包括接收和发送的数据包
iRet = WSAIoctl(sock, SIO_RCVALL, &dwBufferInLen, sizeof(dwBufferInLen),
&dwBufferLen, sizeof(dwBufferLen), &dwBytesReturned, NULL, NULL);
if (iRet != 0) {
printf("WSAIoctl failed with error %d\n", WSAGetLastError());
}
//14+20 +20 14+20+8
sockaddr_in addrFrom;
int fromlen = sizeof(addrFrom);
time_t temp;
char strFromIP[16]{};
char strCurTime[32]{};
char strBuffer[35]{};
char strData[4096] { };
IPHeader ipData;
TCPHeader tcpData;
UDPHeader udpData;
NetData netData;
while (true)
{
memset(strBuffer, 0, sizeof(strBuffer));
iRet = recvfrom(sock, strBuffer, sizeof(strBuffer), 0, (sockaddr*)&addrFrom, &fromlen);
if (iRet <= 0) {
printf("recv failed with error %d\n", WSAGetLastError());
continue;
}
char szTemp[32]{};
inet_ntop(AF_INET, &addrFrom.sin_addr, szTemp, 32);
strcpy(strFromIP, szTemp);
// if(strcmp(strFromIP, "192.168.0.104") == 0
// || strcmp(strFromIP, "192.168.0.1") == 0
// || strcmp(strFromIP, "192.168.0.103") == 0){
// continue;
// }
/*if (strcmp(strFromIP, "192.168.37.1") != 0) { //设置一种逻辑过滤
continue;
}*/
// 处理IP包头数据
memcpy(&ipData, strBuffer, sizeof(ipData));
BYTE Protocol = 0;
sockaddr_in addrSrc, addrDst;
char strSrcIP[16] = { 0 }, strDstIP[16] = { 0 };
addrSrc.sin_addr.S_un.S_addr = ipData.SrcAddr;
addrDst.sin_addr.S_un.S_addr = ipData.DstAddr;
inet_ntop(AF_INET, &addrSrc.sin_addr, szTemp, 32);
strcpy(strSrcIP, inet_ntoa(addrSrc.sin_addr));
inet_ntop(AF_INET, &addrDst.sin_addr, szTemp, 32);
strcpy(strDstIP, inet_ntoa(addrDst.sin_addr));
/*if (strcmp(strSrcIP, "192.168.0.104") != 0 || strcmp(strDstIP, "192.168.0.104") != 0) { //设置一种逻辑过滤
continue;
}*/
int iSrcPort = 0;
int iDstPort = 0;
// UDP协议
if (ipData.Protocol == IPPROTO_UDP)
{
/*if (iRet != 1052) {
continue;
}*/
memcpy(&udpData, strBuffer + sizeof(ipData), sizeof(udpData));
iSrcPort = ntohs(udpData.SrcPort);
iDstPort = ntohs(udpData.DstPort);
memcpy(&netData, strBuffer + sizeof(ipData) + sizeof(udpData), sizeof(netData));
}
// TCP协议
else if (ipData.Protocol == IPPROTO_TCP)
{
/*if (iRet != 1064) {
continue;
}*/
memcpy(&tcpData, strBuffer + sizeof(ipData), sizeof(tcpData));
iSrcPort = ntohs(tcpData.SrcPort);
iDstPort = ntohs(tcpData.DstPort);
memcpy(&netData, strBuffer + sizeof(ipData) + sizeof(tcpData), sizeof(netData));
}
else {}
time(&temp);
strftime(strCurTime, sizeof(strCurTime), "%Y-%m-%d %H:%M:%S", localtime(&temp));
if (ipData.Protocol == IPPROTO_TCP)
printf("TCP Catch!\n");
else if (ipData.Protocol == IPPROTO_UDP)
printf("UDP Catch!\n");
printf("pack catch Time:%s PackLength:%d\n", strCurTime, iRet);
printf("strSrcIP:%s iSrcPort:%d\n", strSrcIP, iSrcPort);
printf("strDstIP:%s iDstPort:%d\n", strDstIP, iDstPort);
}
return 0;
}
int main()
{
capturenetpackage();
return 0;
参考链接:https://blog.csdn.net/Toobad321/article/details/77869543
https://blog.csdn.net/caoshangpa/article/details/51530685