利用原始套接字的抓包原理

利用原始套接字的抓包原理:

抓包层

发送接收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所有这些的网口;然后在这些套接字上监听抓取数据

然后设置混杂模式 SIO_RCVALL

网卡对数据帧进行硬过滤(根据网卡的模式不同采取不同的操作,如果设置了混杂模式,则不做任何过滤直接交给下一层,否则非本机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 <iostream>
#include <time.h>
#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

MICROSOFT FOUNDATION CLASS LIBRARY : WS232LIB AppWizard has created this WS232LIB application for you. This application not only demonstrates the basics of using the Microsoft Foundation classes but is also a starting point for writing your application. This file contains a summary of what you will find in each of the files that make up your WS232LIB application. WS232LIB.dsp This file (the project file) contains information at the project level and is used to build a single project or subproject. Other users can share the project (.dsp) file, but they should export the makefiles locally. WS232LIB.h This is the main header file for the application. It includes other project specific headers (including Resource.h) and declares the CWS232LIBApp application class. WS232LIB.cpp This is the main application source file that contains the application class CWS232LIBApp. WS232LIB.rc This is a listing of all of the Microsoft Windows resources that the program uses. It includes the icons, bitmaps, and cursors that are stored in the RES subdirectory. This file can be directly edited in Microsoft Visual C++. WS232LIB.clw This file contains information used by ClassWizard to edit existing classes or add new classes. ClassWizard also uses this file to store information needed to create and edit message maps and dialog data maps and to create prototype member functions. resWS232LIB.ico This is an icon file, which is used as the application s icon. This icon is included by the main resource file WS232LIB.rc. resWS232LIB.rc2 This file contains resources that are not edited by Microsoft Visual C++. You should place all resources not editable by the resource editor in this file. AppWizard creates one dialog class: WS232LIBDlg.h, WS232LIBDlg.cpp - the dialog These files contain your CWS232LIBDlg class. This class defines the behavior of your application s main dialog. The dialog s template is in WS232LIB.rc, which can be edited in Microsoft Visual C++. Other standard files: StdAfx.h, StdAfx.cpp These files are used to build a precompiled header (PCH) file named WS232LIB.pch and a precompiled types file named StdAfx.obj. Resource.h This is the standard header file, which defines new resource IDs. Microsoft Visual C++ reads and updates this file. Other notes: AppWizard uses "TODO:" to indicate parts of the source code you should add to or customize. If your application uses MFC in a shared DLL, and your application is in a language other than the operating system s current language, you will need to copy the corresponding localized resources MFC42XXX.DLL from the Microsoft Visual C++ CD-ROM onto the system or system32 directory, and rename it to be MFCLOC.DLL. ("XXX" stands for the language abbreviation. For example, MFC42DEU.DLL contains resources translated to German.) If you don t do this, some of the UI elements of your application will remain in the language of the operating system.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值