抓包程序主要需要理解的是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;
}
#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;
}