计算机网络课程设计之嗅探器实现

资料来源网络,侵权请联系删除

原理

  简单说明一下什么是网络嗅探器,网络嗅探器是一个抓取所有经过网卡数据的软件,在一般使用电脑时,网卡只接受到发送至本机的数据,那是因为这是网卡是非混杂模式的,挡一个目的地址非本机地址的数据包经过网卡时,网卡在数据链路层(mac地址)检测到是非本机数据,则直接丢弃,当设置为混杂模式时,所有经过网卡的数据包均可被读取出来。

步骤

在这里插入图片描述

一些疑惑

解析头还比较简单,关键在于得到数据后(应用层数据,二进制),数据部分依照什么协议、编码(例如ASCLL码直接查表),这部分信息如何得到,加以思索后还是觉得无法单纯从IP数据包中得到。

贴代码时间

datastruct.h :IP结构体、TCP结构体、UDP结构体和ICMP结构体

#ifndef _DATASTRUCT_H_
#define _DATASTRUCT_H_

typedef struct mac_header
{
	char destaddr[6];
	char souraddr[6];
	unsigned short frametype;
}mac_header, *pmac_header;

#pragma pack(push, 1)
typedef struct ip_header
{
	unsigned char ver4_hlen4;		// 版本 和 首部长度
	unsigned char diffserv;		// 区分服务
	unsigned short tlen;		// 总长度
	unsigned short flag;		// 标识
	unsigned short flag4_fraoff12; // 标志 和 片偏移
	unsigned char ttl;			// 生存时间
	unsigned char proto;					// 协议
	unsigned short crc;			// 首部检验和
	unsigned long souraddr;		// 源地址
	unsigned long destaddr;		// 目的地址
}ip_header, *pip_header;

typedef struct udp_header
{
	unsigned short sourport;
	unsigned short destport;
	unsigned short tlen;
	unsigned short crc;
}udp_header, *pudp_header;

typedef struct tcp_header
{
	unsigned short sourport;
	unsigned short destport;
	unsigned long snum;
	unsigned long acknum;
	unsigned short off4_reser6_flag6;
	unsigned short window;
	unsigned short crc;
	unsigned short emerp;
}tcp_header, *ptcp_header;

typedef struct iph_pack
{
	unsigned short hlen;
	unsigned short tlen; //IP总长
	unsigned long souraddr;
	unsigned long destaddr;
}iph_pack;

#pragma pack(pop)

#endif

sniffer.cpp


// NetSniffer.cpp : 定义控制台应用程序的入口点。
//
/*
简单说明一下什么是网络嗅探器,网络嗅探器是一个抓取所有经过网卡数据的软件,在一般使用电脑时,网卡
只接受到发送至本机的数据,那是因为这是网卡是非混杂模式的,挡一个目的地址非本机地址的数据包经过网
卡时,网卡在数据链路层(mac地址)检测到是非本机数据,则直接丢弃,当设置为混杂模式时,所有经过网卡
的数据包均可被读取出来。
*/

#include "pch.h"
#include <WinSock2.h>
#include <ws2tcpip.h>
#include <mstcpip.h>

#include "datastruct.h"
#include "procdata.h"

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

#define BUF 256
#define RECVBUF 1500

int main(int argc, char* argv[])
{
	// Declare some variables
	WSADATA wsaData;
	int iResult = 0;
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != NO_ERROR) {
		wprintf(L"Error at WSAStartup()\n");
		return 1;
	}


	SOCKET sock = INVALID_SOCKET;
	// 理论上在socket 第三个参数设定为制定协议类型(如0x0800或0x0806或多个等等),则可以获取
	// 数据链路层的数据,即包括了以太网帧的首部在数据部分,但window 封闭了这一层,所以获取
	// 数据只能去到ip层。若想获取以太网帧可以使用winpcap 开发库,linux下是libpcap。(注意:
	// Linux下可用socket套接字获取数据链路层)
	if ((sock = socket(AF_INET, SOCK_RAW, /*htons(0x0800)*/IPPROTO_IP)) == INVALID_SOCKET)
	{
		printf("socket error:%d\n", GetLastError());
		WSACleanup();
		return 1;
	}
	// hostent 结构体包含了所有能抓取到的网卡对应的地址
	struct sockaddr_in addr;
	char hostname[BUF] = { 0 };
	
	int namelen = gethostname(hostname, BUF);
	struct hostent *phost = gethostbyname(hostname);
	if (phost != NULL)
	{
		addr.sin_family = AF_INET;
		addr.sin_port = htons(0);
		char *paddr;
		int i = 0;
		for (paddr = *phost->h_addr_list; paddr != NULL; ++i, paddr = *(phost->h_addr_list + i))
		{
			memcpy(&(addr.sin_addr), paddr, phost->h_length);
			printf("[%d] %s\n", i, inet_ntoa(addr.sin_addr));
		}
		printf("Please chose a device for Sniffer:");
		int index;
	reenter:
		scanf("%d", &index);
		if (index < 0 || index >= i)
			goto reenter;
		paddr = *(phost->h_addr_list + index);
		memcpy(&(addr.sin_addr), paddr, phost->h_length);
	}
	else
	{
		closesocket(sock);
		WSACleanup();
		return 1;
	}

	if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == SOCKET_ERROR)
	{
		closesocket(sock);
		WSACleanup();
		return 1;
	}

	// 设置网卡为混杂模式
	unsigned long flag = 1;
	if (ioctlsocket(sock, SIO_RCVALL, &flag) != 0)
	{
		closesocket(sock);
		WSACleanup();
		return 1;
	}

	char recvbuf[RECVBUF];
	size_t size = 0;
	pip_header pip;

	int count = 0;
	int cnt2 = 0;
	while (1)
	{	// 接受到IP数据包

		if (cnt2 == 30) {
			int a;
			printf("输入任意数字继续:");
			scanf("%d", &a);
			cnt2 = 0;
		}
		cnt2++;

		size = recv(sock, recvbuf, RECVBUF, 0);
		if (size == 0 || size == SOCKET_ERROR)
		{
			if (size == SOCKET_ERROR)
			{
				closesocket(sock);
				WSACleanup();
				return 1;
			}
			continue;
		}
		count++;
		//
		printf("[%0.4d]\t", count);
		pip = (pip_header)recvbuf; //强转为IP类型
		switch (pip->proto)
		{
		case IPPROTO_TCP:
			procTcpPack(recvbuf);
			break;
		case IPPROTO_UDP:
			procUdpPack(recvbuf);
			break;
		case IPPROTO_ICMP:
			procIcmpPack(recvbuf);
			break;
		case IPPROTO_IGMP:
			printf("Catch a IGMP, len : %d.\n", ntohs(pip->tlen));
			break;
		case IPPROTO_EGP:
			printf("Catch a EGP, len : %d.\n", ntohs(pip->tlen));
			break;
		case IPPROTO_IGP:
			printf("Catch a IGP, len : %d.\n", ntohs(pip->tlen));
			break;
		case IPPROTO_ESP:
			printf("Catch a ESP, len : %d.\n", ntohs(pip->tlen));
			break;
		case MIB_IPPROTO_OSPF:
			printf("Catch a OSPF, len : %d.\n", ntohs(pip->tlen));
			break;

		default:
			printf("Unknow proto.\n");
			break;
		};
		memset(recvbuf, 0, sizeof(recvbuf));
	}
	return 0;
}

procdata.h

#ifndef _PROCDATA_H
#define _PROCDATA_H

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include "datastruct.h"
#include <memory.h>
#include <stdio.h>
#include <WinSock2.h>
#include <ws2tcpip.h>
#include <mstcpip.h>

struct iph_pack procIpHeader(char *ipdata);

// udp,tcp,icmp 和 http,ftp... 均是被封装在ip数据包中
void procUdpPack(char *ipdata);
void procTcpPack(char *ipdata);
void procIcmpPack(char *ipdata);
void procIgmpPack(char *ipdata);


#endif

procdata.cpp 用于解析IP数据部分

#include "pch.h"
#include "procdata.h"
// 处理ip首部
struct iph_pack procIpHeader(char *ipdata)
{
	struct iph_pack len;
	memset(&len, 0, sizeof(len));
	if (!ipdata)
		return len;
	struct ip_header *iph = (struct ip_header *)ipdata;
	len.hlen = (iph->ver4_hlen4 & 0xf) * 4;
	//printf("header len : %d ", len.hlen);
	len.tlen = iph->tlen;
	len.souraddr = iph->souraddr;
	len.destaddr = iph->destaddr;

	return len;
}

// 处理IP数据,得到udp报文
void procUdpPack(char *ipdata)
{
	// 得到IP首部
	struct iph_pack hPack = procIpHeader(ipdata);
	// 随便找一个变量作判断
	if (hPack.hlen == 0)
		return;

	// 扣掉ip首部,剩下的就是udp首部
	struct udp_header *uh = (struct udp_header *)(ipdata + hPack.hlen);

	unsigned char* datatcp = nullptr;

	datatcp = (unsigned char *)ipdata + sizeof(iph_pack) + sizeof(udp_header);
	printf("\n数据\n");
	for (int i = 0; i < 20; i++)
	{
		printf("%x", datatcp[i]);
	}
	printf("\n");

	struct in_addr addr1, addr2;
	printf("total_len(%-4d)\t", ntohs(hPack.tlen));
	char buf[16];
	memset(buf, 0, 16);
	switch (ntohs(uh->destport))
	{
		// DNS
	case 53:
		strncpy(buf, "DNS", 3);
		break;

		// TFTP
	case 69:
		strncpy(buf, "TFTP", 4);
		break;

		// SNMP
	case 161:
		strncpy(buf, "SNMP", 4);
		break;

	case 520:
		strncpy(buf, "RIP", 3);
		break;

		// 非常用的熟知端口号
	default:
		strncpy(buf, "UDP", 3);
		break;
	}
	printf("%-6s\t", buf);
	addr1.s_addr = hPack.souraddr;
	addr2.s_addr = hPack.destaddr;
	printf("%-15s:%5d  ->  ",
		inet_ntoa(addr1), ntohs(uh->sourport)
	);
	printf("%-15s:%5d\n", inet_ntoa(addr2), ntohs(uh->destport));

	 
}

// 处理IP数据,得到TCP报文
void procTcpPack(char *ipdata)
{
	struct iph_pack hPack = procIpHeader(ipdata);
	// 随便找一个变量作判断
	if (hPack.hlen == 0)
		return;

	struct tcp_header *th = (struct tcp_header *)(ipdata + hPack.hlen);
	unsigned char* datatcp = nullptr;

	datatcp = (unsigned char *)ipdata + sizeof(iph_pack) + sizeof(tcp_header);

	struct in_addr addr1, addr2;
	printf("total_len(%-4d)\t", ntohs(hPack.tlen));
	char buf[16];
	memset(buf, 0, 16);
	switch (ntohs(th->destport))
	{
		// FTP
	case 21:
		strncpy(buf, "FTP", 3);
		break;

		// TELNET
	case 23:
		strncpy(buf, "TELNET", 6);
		break;

		// SMTP
	case 161:
		strncpy(buf, "SMTP", 4);
		break;

		// HTTP
	case 80:
		strncpy(buf, "HTTP", 4);
		break;

		// 非常用的熟知端口号
	default:
		strncpy(buf, "TCP", 3);
		break;
	}
	printf("%-6s\t", buf);
	addr1.s_addr = hPack.souraddr;
	addr2.s_addr = hPack.destaddr;
	printf("%-15s:%5d  ->  ",
		inet_ntoa(addr1), ntohs(th->sourport)
	);
	
	printf("%-15s:%5d\n", inet_ntoa(addr2), ntohs(th->destport));
}

void procIcmpPack(char *ipdata)
{
	struct iph_pack hPack = procIpHeader(ipdata);
	struct in_addr addr1, addr2;
	char buf[16] = "ICMP";

	printf("total_len(%-4d)\t%-6s\t%-22s ->  ",
		ntohs(hPack.tlen),
		buf,
		inet_ntoa((addr1.s_addr = hPack.souraddr, addr1))
	);
	printf("%-22s\n", inet_ntoa((addr2.s_addr = hPack.destaddr, addr2)));

}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值