抓包程序主要需要理解的是ip结构,和tcp结构和udp结构等等
sinffer.cpp文件
// sinffer.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "pub.h" int main(int argc, char* argv[]) { if (argc < 3) { printf("usage: %s IPAddress port [byte]\n", argv[0]); return 0; } printf("sniffer\\nauthor: xhf by 2016-7-15\\nversion is 1.0.0\n\n"); SOCKET sock; int iPort = atoi(argv[2]); int iRes = SocketCreate(sock, argv[1], (unsigned short)iPort); if (iRes != 0) { LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, iRes, // 这个地方放的是lasterror的返回值 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL ); printf("Error:%d %s\n", iRes, (LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return -1; } while (true) { if (argc ==4) { iRes = SnifferReceive(sock, true); } else { iRes = SnifferReceive(sock); } if (iRes != 0) { LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, iRes, // 这个地方放的是lasterror的返回值 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL ); } } return 0; }
pub.h
#pragma once #include <stdio.h> #include <stdlib.h> #include <string.h> #include <wtypes.h> // 定义协议的名称结构 typedef struct _PROTN2T { int proto; char *pprototext; } PROTN2T; // 协议数 #define PROTO_NUM 11 // IP头结构 typedef struct _IPHEADER { unsigned char header_len:4;// 头长度,4个位长 unsigned char version:4;// 版本号,4个位长 unsigned char tos;// 服务类型(主要定义包的优先级) unsigned short total_len;// 包整个长度的字节数 unsigned short ident;// 标识,由于IP包发送时候在网络传送时可能还要分割为更小的包,标识唯一确定每个发送的数据包 unsigned short flags;// 综合标志位(前3位:标志,后13位:分块偏移,用来重组分割的IP数据包) unsigned char ttl;// 生存时间,代表网络上的存活寿命 unsigned char proto;// 协议 unsigned short checksum;// 头校验和,该位确保目的主机接收数据和发送数据的相同 unsigned int sourceIP;// 源IP unsigned int destIP;// 目的IP } IPHEADER; // UDP头长度 #define UDP_HEAD_LEN 8 #define PSEUDO_HEAD_LEN 12 // ICMP头长度 #define ICMP_HEAD_LEN 8 struct TCPPacketHead { WORD SourPort;// 16位源端口 WORD DesPort;// 16目的端口 DWORD SeqMo;// 32位序列号,指出了TCP段数据区其实数据位置 DWORD AckNo;// 32位确认号,指出连接期望从数据流中接收的下一字节数据,例如:如果收到最后一个字节序号为630,那么TCP将发一个为631的确认号 BYTE HLen;// 头长度 BYTE FLag;// 标识位,紧急(URG),确认(ACK),推送(PSH),重置(RST),同步(SYN),完成(FIN) WORD WndSize;// 16位窗口大小 WORD ChkSum;// 16位TCP校验和 WORD UrgPtr;// 16位紧急指针 }; // ICMP包头部结构 struct ICMPPacketHead { BYTE Type;// 类型 BYTE Code;// 编码 WORD ChkSum;// 16位TCP校验和 }; // UDP包头结构 struct UDPPacketHead { WORD SourcePort;// 源端口 WORD DestPort;// 目的端口 WORD Len;// 消息包长度 WORD ChkSum;// 16位TCP校验和 }; int SnifferReceive(SOCKET &sock, bool ShowByte = false); int SocketCreate(SOCKET &sock, const char *IPAddr, unsigned short Port);
pub.cpp
#pragma once #include "stdafx.h" #include <winsock2.h> #include <MSTcpIP.h> #include "pub.h" #pragma comment(lib,"ws2_32.lib") char *Get_proto_name(unsigned char proto)// 通过struct _PROTN2T结构的proto成员,得到协议名 { switch (proto) { case IPPROTO_IP: return "IP"; case IPPROTO_ICMP: return "ICMP"; case IPPROTO_IGMP: return "IGMP"; case IPPROTO_GGP: return "GGP"; case IPPROTO_TCP: return "TCP"; case IPPROTO_PUP: return "PUP"; case IPPROTO_UDP: return "UDP"; case IPPROTO_IDP: return "IDP"; case IPPROTO_ND: return "ND"; case IPPROTO_RAW: return "RAW"; case IPPROTO_MAX: return "MAX"; default: return "UNKNOW"; } } void PrintByte(const char *Buf, size_t BufSize)// 将二进制数转化为16进制字符串,打印到屏幕上 { for (size_t i = 0; i < BufSize; i++) { printf("%.2x", (unsigned char)Buf[i]); /* if ((i % 8) == 7) { printf(" "); } if ((i % 16) == 15) { printf("\n") }*/ } } int SnifferReceive(SOCKET &sock, bool ShowByte) { IPHEADER *ipHeader = NULL; TCPPacketHead *tcpHeader = NULL; ICMPPacketHead *icmpHeader = NULL; UDPPacketHead *udpHeader = NULL; BYTE *pData = NULL; // 存放数据的buf char *pLastBuf = NULL;// 最后一个buf WORD wSrcPort, wDestPort;// 源端口和目的端口 char sSrcIPAddr[32], sDestIPAddr[32], sProtoName[32]; memset(sSrcIPAddr, 0, sizeof(sSrcIPAddr)); memset(sDestIPAddr, 0, sizeof(sDestIPAddr)); memset(sProtoName, 0, sizeof(sProtoName)); in_addr inaddr; char sBuf[8192];// Socket默认的Buffer位8K char *pBuf = sBuf; memset(sBuf, 0, sizeof(sBuf)); int iRes = recv(sock, sBuf, sizeof(sBuf), 0); if (iRes == SOCKET_ERROR) { return WSAGetLastError(); } // 得到IP包头 ipHeader = (IPHEADER *)pBuf; // 得到IP包头总长度 WORD iLen = ntohs(ipHeader->total_len); while (true) { if (iLen <= iRes) { // 得到IP包的源地址 inaddr.S_un.S_addr = ipHeader->sourceIP; strcpy(sSrcIPAddr, inet_ntoa(inaddr)); // 得到IP包的目的地址 inaddr.S_un.S_addr = ipHeader->destIP; strcpy(sDestIPAddr, inet_ntoa(inaddr)); // 得到包的协议名称 strcpy(sProtoName, Get_proto_name(ipHeader->proto)); // 得到IP包头的长度(因为header_len为4比特的数据,所以需要这样计算) int iHdrLen = ipHeader->header_len & 0xf; iHdrLen *= 4; // 总长度减包头长度得到的数据的长度 int iTotalLen = ntohs(ipHeader->total_len); iTotalLen -= iHdrLen; switch (ipHeader->proto) { case IPPROTO_ICMP: { icmpHeader = (ICMPPacketHead *)(sBuf + iHdrLen); pData = ((BYTE*)icmpHeader) + ICMP_HEAD_LEN; iTotalLen -= ICMP_HEAD_LEN; } case IPPROTO_TCP: { tcpHeader = (TCPPacketHead *)(sBuf + iHdrLen); // 得到源端口 wSrcPort = ntohs(tcpHeader->SourPort); // 得到目标端口 wDestPort = ntohs(tcpHeader->DesPort); iHdrLen = tcpHeader->HLen >> 4; iHdrLen *= 4; pData = ((BYTE *)tcpHeader) + iHdrLen; iTotalLen -= iHdrLen; break; } case IPPROTO_UDP: { udpHeader = (UDPPacketHead *)(&sBuf[iHdrLen]); // 得到源端口 wSrcPort = ntohs(udpHeader->SourcePort); // 得到目标端口 wDestPort = ntohs(udpHeader->DestPort); pData = ((BYTE *)udpHeader) + UDP_HEAD_LEN; iTotalLen -= UDP_HEAD_LEN; break; } } static unsigned int iSequence = 0; iSequence++; /* Internet 组管理协议(IGMP)是因特网协议家族中的一个组播协议,用于IP主机向人一个相邻的 路由器报告他们的组成员情况 */ if (strcmp(sProtoName, "IGMP") != 0)// 如果是IGMP协议,就补打印协议内容 { // 过滤掉广播消息 if (strncmp(sDestIPAddr, "192.168.0.255", strlen("192.168.0.255")) != 0) { printf("------------------begin %.10u------------------\n", iSequence); printf("ProtoName:%s\nSrcAddr:%s\nDestAddr:%s\nSrcPort:%d\nDestPort:%d\n", sProtoName, sSrcIPAddr, sDestIPAddr, wSrcPort, wDestPort); if (ShowByte) { printf("Byte:\n"); PrintByte((char*)pData, iTotalLen); } printf("\nASCII:\n%s\n", (char *)pData); } } //printf("------------------end %d.10u------------------\n\n", iSequence); if (iLen < iRes) { iRes -= iLen; pBuf += iLen; ipHeader = (IPHEADER *)pBuf; } else { break;// 如果ipHeader->total_len == iRes则退出 } } else// 已经读到的buffer的最后部分,即包的长度 { int iLast = iLen - iRes; if (pLastBuf) delete []pLastBuf; pLastBuf = new char[iLen]; int iReaden = iRes; memcpy(pLastBuf, pBuf, iReaden); iRes = recv(sock, pLastBuf + iReaden, iLast, 0); if (iRes == SOCKET_ERROR) return WSAGetLastError(); pBuf = pLastBuf; ipHeader = (IPHEADER *)pBuf; if (iRes == iLast) iRes = iLen; else { // 读剩余所有的数据 iReaden += iRes; iLast -= iRes; while (true) { iRes = recv(sock, pLastBuf + iReaden, iLast, 0); if (iRes == SOCKET_ERROR) return WSAGetLastError(); iReaden += iRes; iLast -= iRes; if (iLast <= 0) break; } } } } return 0; } // 指定的IP地址和端口上,建立一个原始的socket。 int SocketCreate(SOCKET &sock, const char *IPAddr, unsigned short Port) { // 初始化win socket环境 unsigned short wVersion; WSADATA wsaData; wVersion = MAKEWORD(1, 1); int iRes = WSAStartup(wVersion, &wsaData); if (iRes != 0) return iRes; // 创建原始的socket sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP); if (sock == INVALID_SOCKET) return WSAGetLastError(); // 设置超时选项 int iRecTime = 50000; // 50秒,设置接收超时 if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&iRecTime, sizeof(iRecTime)) == SOCKET_ERROR) return WSAGetLastError(); // 将socket bind到一个具体的断口和地址 sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(Port); addr.sin_addr.s_addr = inet_addr(IPAddr); if (bind(sock, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) { return WSAGetLastError(); } // 设置socket模式,当调用WSAIoctl函数的时候为了能让socket能接收网络的所有IP包, // 传给WSAIoctl函数的socket句柄必须设置成AF_INET, SOCK_RAW,和IPPROTO_IP协议,而且 // 这个socket必须显示的bind到本地的一个端口和地址 DWORD dwBufferInLen = 1; DWORD dwBufferLen[10]; DWORD dwBytesReturned = 0; // 调用WSAIoctl, 设置socket可以接收所有的IP包 if (WSAIoctl(sock, SIO_RCVALL, &dwBufferInLen, sizeof(dwBufferInLen), &dwBufferLen, sizeof(dwBufferLen), &dwBytesReturned, NULL, NULL) == SOCKET_ERROR) { return WSAGetLastError(); } return 0; }
时间: 2024-10-11 05:09:09