网络程序抓包工具

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




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值