本文将通过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地址信息
地址族(Adddress Family) | 含义 |
AF_INET | IPv4网络协议中使用的地址族 |
AF_INET6 | IPv6网络协议中使用的地址族 |
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();
}