资料来源网络,侵权请联系删除
原理
简单说明一下什么是网络嗅探器,网络嗅探器是一个抓取所有经过网卡数据的软件,在一般使用电脑时,网卡只接受到发送至本机的数据,那是因为这是网卡是非混杂模式的,挡一个目的地址非本机地址的数据包经过网卡时,网卡在数据链路层(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)));
}