【网络编程】捕获网卡IP数据报

本文将通过winsock从应用层捕捉网络层的IP数据报。

唉,原来的时候一直希望能在应用层实现网络游戏加速,发现可以捕捉网卡IP数据报后觉得可能有希望写出来。后面想了想得出结论:可以捕获没卵用,因为没法拦截(包已经发出去了,才反馈给你的,充其量自己就是个监听者),然后又搜了搜那我争取拦截呗,又发现了应用层下似乎只能通过lsp劫持实现,然后游戏呢绝壁检测你的,最终得出结论,还得上驱动【呃虚拟网卡可以吗,我不晓得,去研究一下吧】。

1.使用winsock

	//初始化套接字
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
	{
		cout << endl << "WSASTartup初始化失败" << endl;
		return;
	}

需要俩头文件

#include <winsock.h>

 #pragma comment(lib,"ws2_32.lib")

相当于初始化加载winsock.dll,然后winsock返回给你一个WSAData告诉你本机中winsock的信息

2.创建套接字

SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP); //三个参分别为通信发生的区字段,套接字的类型,与IP协议
	if (sock == INVALID_SOCKET)
	{
		cout << endl << "创建Socket失败!" << endl;
		closesocket(sock);
		WSACleanup();
	}

socket(a,b,c)

a为AF_INET 【通信发生的区字段,具体啥意思,还有啥我不知道捏,自行百度吧。】

b为SOCK_STREAM,SOCK_DGRAM,SOCK_RAW三个之一。【套接字的类型】

SOCK_STREAM(tcp)SOCK_DGRAM(UDP)SOCK_RAW(IP)

c为参数

IPPROTO_IP = 0,   /* Dummy protocol for TCP   */
IPPROTO_ICMP = 1,   /* Internet Control Message Protocol */
IPPROTO_IGMP = 2,   /* Internet Group Management Protocol */
IPPROTO_IPIP = 4,   /* IPIP tunnels (older KA9Q tunnels use 94) */
IPPROTO_TCP = 6,   /* Transmission Control Protocol */
IPPROTO_EGP = 8,   /* Exterior Gateway Protocol   */
IPPROTO_PUP = 12,   /* PUP protocol     */
IPPROTO_UDP = 17,   /* User Datagram Protocol   */
IPPROTO_IDP = 22,   /* XNS IDP protocol    */
IPPROTO_DCCP = 33,   /* Datagram Congestion Control Protocol */
IPPROTO_RSVP = 46,   /* RSVP protocol    */
IPPROTO_GRE = 47,   /* Cisco GRE tunnels (rfc 1701,1702) */

IPPROTO_IPV6 = 41,   /* IPv6-in-IPv4 tunnelling   */

IPPROTO_ESP = 50,            /* Encapsulation Security Payload protocol */
IPPROTO_AH = 51,             /* Authentication Header protocol       */
IPPROTO_BEETPH = 94,        /* IP option pseudo header for BEET */
IPPROTO_PIM    = 103,   /* Protocol Independent Multicast */

IPPROTO_COMP   = 108,                /* Compression Header protocol */
IPPROTO_SCTP   = 132,   /* Stream Control Transport Protocol */
IPPROTO_UDPLITE = 136, /* UDP-Lite (RFC 3828)    */

IPPROTO_RAW = 255,   /* Raw IP packets    */
IPPROTO_MAX

对于socket(AF_INET, SOCK_RAW, IPPROTO_IP),其原型为

int socket (int domain, int type, int protocol);

1 参数protocol用来指明所要接收的协议包,如果是象IPPROTO_TCP(6)这种非0、非255的协议,当操作系统内核碰到ip头中protocol域和创建socket所使用参数protocol相同的IP包,就会交给这个raw socket来处理,因此,一般来说,要想接收什么样的数据包,就应该在参数protocol里来指定相应的协议。当内核向此raw socket交付数据包的时候,是包括整个IP头的,并且已经是重组好的IP包。

2 如果protocol是IPPROTO_RAW(255),这时候,这个socket只能用来发送IP包,而不能接收任何的数据。发送的数据需要自己填充IP包头,并且自己计算校验和。

3 对于protocol为0(IPPROTO_IP)的raw socket。用于接收任何的IP数据包。其中的校验和和协议分析由程序自己完成

【上面那段我抄的,差不多意思能明白点,但是这个socket函数的b和c参数不是冗余了吗,感觉是有点捏。想了想也有可能是下面这样滴】

b参数指明这个socket能盛放啥类型。c参数指明内核你要给我捕捉啥协议的。【小白理解。可能有错】

3.hostent结构体的说明

4.地址信息的表示

sockaddr_in结构体

struct sockaddr_in

{

    short sin_family;        //协议族Address family

    unsigned short sin_port;    //16位TCP/UDP端口号

    struct in_addr sin_addr;    //32位IP地址

    unsigned char sin_zero[8];  //没有实际意义,只是为了跟SOCKADDR结构在内存中对齐

};

结构体sockaddr_in的成员分析

成员sin_family:

每种协议适用的地址族均不同。比如,IPv4使用4字节地址族,IPv6使用16字节地址族,可以参考表1-2保存的sin_family地址信息

表1-2   地址族
地址族(Adddress Family)含义
AF_INETIPv4网络协议中使用的地址族
AF_INET6IPv6网络协议中使用的地址族
AF_LOCAL本地通信中采用的Unix协议的地址族

AF_LOCAL只是为了说明具有多种地址族而添加的

成员sin_port:

该成员保存16位端口号,且以网络字节序保存(后续还会说明何为网络字节序)

成员sin_addr:

该成员保存32位IP地址信息,且也以网络字节序保存。为理解好该成员,应同时观察结构体in_addr。但结构体in_addr声明为uint32_t,因此只需当做32位整数即可

成员sin_zero:

无特殊含义,只是为了结构体sockaddr_in的大小与sockaddr结构体保持一致而插入的成员。必须填充为0,否则无法得到想要的结果,后续还会介绍sockaddr

【以下个人小白理解,可能有错误。】

【操作系统内部维护了一个类似socket列表的东西,socket呢就是负责你进程使用操作系统的通信资源的,一开始socket被申请出来了,但是占用的本机IP和端口都没有声明。这时候你可以调用bind来指定,指定之后,你的程序就用声明的这一块资源来做事,声明的本机IP和端口就被占用了。操作系统就不会把这个ip和端口分配给别的进程亦或者说别的socket来使用】

5.WSAIoctl

控制一个socket的模式,自行百度吧

6.所有代码,没说的东西都是没啥技术含量的。翻翻书就会的

代码在vs2019编译通过,vs2019有安全审查【可能导致编译不通过】,取消的话

【VS socket】不兼容老版本函数的问题_aaaabbbwwww的博客-CSDN博客

#include <iostream> 
#include <winsock2.h> 
#include <ws2tcpip.h> 
#include <fstream>
#include <windows.h> 
#pragma comment(lib,"ws2_32.lib") //指定连接到网络应用和internet

#define IO_RCVALL _WSAIOW(IOC_VENDOR,1)

typedef struct IP_HEAD
{
	union //定义联合
	{
		unsigned char Version;
		unsigned char HeadLen;
	};
	unsigned char ServiceType;
	unsigned short TotalLen;
	unsigned short Identifier;
	union
	{
		unsigned short Flags;
		unsigned short FragOffset;
	};
	unsigned char TimeToLive;
	unsigned char Protocol;
	unsigned short HeadChecksum;
	unsigned int SourceAddr;
	unsigned int DestinAddr;
	unsigned char Options;
}ip_head; //定义IP头部的数据结构

void main(int argc, char* argv[])
{

	using namespace std;
	ofstream outfile("C://logfile.txt", ios::out);
	/*
	if(argc!=2)
	{
	cout<<endl<<"请以下格式输入命令行:PackParse packet_sum"<<endl;
	return;
	}
	*/

	//初始化套接字
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
	{
		cout << endl << "WSASTartup初始化失败" << endl;
		return;
	}

	//创建套接字
	SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP); //三个参分别为通信发生的区字段,套接字的类型,与IP协议
	if (sock == INVALID_SOCKET)
	{
		cout << endl << "创建Socket失败!" << endl;
		closesocket(sock);
		WSACleanup();
	}

	/*
	BOOL flag=TRUE;
	if(setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *) &flag,sizeof(flag))==SOCKET_ERROR)
	{
	cout<<endl<<"setsockopt操作失败:"<<WSAGetLastError()<<endl;
	closesocket(sock);
	WSACleanup();

	}
	*/

	char hostName[128]; //获取主机名 
	if (gethostname(hostName, 128) == SOCKET_ERROR)
	{
		cout << endl << "gethostname操作失败:" << WSAGetLastError() << endl;
		closesocket(sock);
		WSACleanup();

	}

	hostent* pHostIP; //获取本地IP
	if ((pHostIP = gethostbyname(hostName)) == NULL)
	{
		cout << endl << "gethostbyname操作失败:" << WSAGetLastError() << endl;
		closesocket(sock);
		WSACleanup();
	}

	sockaddr_in host_addr; //绑定到本机套接字
	host_addr.sin_family = AF_INET;
	host_addr.sin_port = htons(9003);
	host_addr.sin_addr = *(in_addr*)pHostIP->h_addr_list[0];
	if (bind(sock, (PSOCKADDR)&host_addr, sizeof(host_addr)) == SOCKET_ERROR)
	{
		cout << endl << "bind操作失败:" << WSAGetLastError() << endl;
		closesocket(sock);
		WSACleanup();
	}

	DWORD dwBufferLen[10]; //修改本机套接字类型,以接受所有数据
	DWORD dwBufferInLen = 1;
	DWORD dwBytesReturned = 0;
	if (WSAIoctl(sock, IO_RCVALL, &dwBufferInLen, sizeof(dwBufferInLen), &dwBufferLen, sizeof(dwBufferLen), &dwBytesReturned, NULL, NULL) == SOCKET_ERROR)
	{
		cout << endl << "WSAIoctl操作失败:" << WSAGetLastError() << endl;
		closesocket(sock);
		WSACleanup();
	}


	cout << endl << "开始解析IP包:" << endl;
	char buffer[65535]; //设置缓冲区
	//int packsum=atoi("3"); //字符串转换为整形
	while(1)
	{
		if (recv(sock, buffer, 65535, 0) > 0) //四个参数分别是套接字描述符,缓冲区的地址,缓冲区大小,附加标志
		{
			dec(cout);
			ip_head ip = *(ip_head*)buffer;
			cout << "-----------------------" << endl;
			cout << "版本:" << (ip.Version >> 4) << endl; //要右移4位获取头部长度字段,第一个char的高四位bit存放version。当前是IPv4。第一个char的低四位bit存放报头长度,它指出了按32   bit   长标定的报头长度,报头的实际长度是   h_len   <<   2   。如果报头长度不是32   bit   的整数倍,则由填充域添0补充。
			cout << "头部长度:" << ((ip.HeadLen & 0x0f) * 4) << endl; //获取头部长度字段按32bit为一单位,故要乘以4
			cout << "服务类型:Priority" << (ip.ServiceType >> 5) << ", Service" << ((ip.ServiceType >> 1) & 0x0f) << endl;
			cout << "总长度:" << ntohs(ip.TotalLen) << endl;//获取总长度字段 
			cout << "标识符:" << ntohs(ip.Identifier) << endl;//获取标识字段 
			cout << "标志位:" << ((ip.Flags >> 15) & 0x01) << ",DF= " << ((ip.Flags >> 14) & 0x01) << ",Mf=" << ((ip.Flags >> 13) & 0x01) << endl; //获得标志字段
			cout << "片偏移:" << (ip.FragOffset & 0x1fff) << endl; //获取分段偏移字段
			cout << "生存周期:" << (int)ip.TimeToLive << endl; //获取生存时间字段
			cout << "协议:Protocol" << (int)ip.Protocol << endl; //获取协议字段
			cout << "头部校验和:" << hex << ntohs(ip.HeadChecksum) << hex << endl; //获取头校验和字段
			cout << "原地址:" << inet_ntoa(*(in_addr*)&ip.SourceAddr) << endl; //获取源IP地址字段
			cout << "目的IP地址:" << inet_ntoa(*(in_addr*)&ip.DestinAddr) << endl; //获取目的IP地址字段

			char str[100];
			int is = 128;
			//cout << "版本:" << itoa(is, str, 2) << endl; //要右移4位获取头部长度字段,第一个char的高四位bit存放version。当前是IPv4。第一个char的低四位bit存放报头长度,它指出了按32   bit   长标定的报头长度,报头的实际长度是   h_len   <<   2   。如果报头长度不是32   bit   的整数倍,则由填充域添0补充。
			/*cout<<"头部长度:"<<ip.HeadLen<<endl; //获取头部长度字段按32bit为一单位,故要乘以4
			cout<<"服务类型:"<<ip.ServiceType<<", Service"<<ip.ServiceType<<endl;
			cout<<"总长度:"<<ip.TotalLen<<endl;//获取总长度字段
			cout<<"标识符:"<<ip.Identifier<<endl;//获取标识字段
			cout<<"标志位:"<<ip.Flags<<endl; //获得标志字段
			cout<<"片偏移:"<<ip.FragOffset<<endl; //获取分段偏移字段
			cout<<"生存周期:"<<(int)ip.TimeToLive<<endl; //获取生存时间字段
			cout<<"协议:Protocol"<<(int)ip.Protocol<<endl; //获取协议字段
			cout<<"头部校验和:"<<ip.HeadChecksum<<endl; //获取头校验和字段
			cout<<"原地址:"<<inet_ntoa(*(in_addr *)&ip.SourceAddr)<<endl; //获取源IP地址字段
			cout<<"目的IP地址:"<<inet_ntoa(*(in_addr *)&ip.DestinAddr)<<endl; //获取目的IP地址字段
			*/
			outfile << "-----------------------" << endl;
			dec(outfile);
			outfile << "版本:" << (ip.Version >> 4) << endl;
			outfile << "头部长度:" << ((ip.HeadLen & 0x0f) * 4) << endl;
			outfile << "服务类型:Priority" << (ip.ServiceType >> 5) << ", Service" << ((ip.ServiceType >> 1) & 0x0f) << endl;
			outfile << "总长度:" << ntohs(ip.TotalLen) << endl;
			outfile << "标识符:" << ntohs(ip.Identifier) << endl;
			outfile << "标志位:" << ((ip.Flags >> 15) & 0x01) << ",DF= " << ((ip.Flags >> 14) & 0x01) << ",Mf=" << ((ip.Flags >> 13) & 0x01) << endl;
			outfile << "片偏移:" << (ip.FragOffset & 0x1fff) << endl;
			outfile << "生存周期:" << (int)ip.TimeToLive << endl;
			outfile << "协议:Protocol" << (int)ip.Protocol << endl;
			outfile << "头部校验和:" << hex << ntohs(ip.HeadChecksum) << endl;
			outfile << "原地址:" << inet_ntoa(*(in_addr*)&ip.SourceAddr) << endl;
			outfile << "目的IP地址:" << inet_ntoa(*(in_addr*)&ip.DestinAddr) << endl;
		}
	}

	closesocket(sock);
	WSACleanup();
}

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值