网络嗅探器
时间:2022-04-22
本文章向大家介绍网络嗅探器,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
网络嗅探器:把网卡设置成混杂模式,并可实现对网络上传输的数据包的捕获与分析。
原理:
通常的套接字程序只能响应与自己MAC地址相匹配的 或者是 广播形式发出的数据帧,对于其他形式的数据帧网络接口采取的动作是直接丢弃
为了使网卡接收所有经过他的封包,要将其设置成混杂模式,通过原始套接字来实现。
设置混杂模式:
创建原始套接字,
绑定到一个明确的本地地址,
向套接字发送SIO_RCVALL控制命令,
接收所有的IP包
代码实现步骤:
1 创建原始套接字
2 绑定到明确地址
3 这是SIO_RCVALL控制代码
4 进入循环,调用recv函数接收经过本地网卡的IP封包。
主程序代码如下:
void main()
{
//创建原始套接字
SOCKET sRaw = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
//获取本地IP地址
char szHostName[56];
SOCKADDR_IN addr_in;
struct hostent *pHost;
gethostname(szHostName,56);
if((pHost=gethostbyname((char*)szHostName))==NULL)
return;
//套接字绑定
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(0);
memcpy(&addr_in.sin_addr.S_un.S_addr,pHost->h_addr_list[0],pHost->h_length);
printf("Binding to interface:%sn",::inet_ntoa(addr_in.sin_addr));
if(bind(sRaw,(sockaddr*)&addr_in,sizeof(addr_in))==SOCKET_ERROR)
return;
//设置SIO_RECVALL控制代码
DWORD dwValue = 1;
if(ioctlsocket(sRaw,SIO_RCVALL,&dwValue)!=0)
return;
//开始接收封包
char buff[1024];
int nRet;
while(true)
{
nRet = recv(sRaw,buff,1024,0);
if(nRet>0)
{
DecodeIPPacket(buff);
}
}
closesocket(sRaw);
}
程序接收到IP封包后,调用自定义的DecodeIPPacket进行解包。取出封包中的协议头,向用户打印出协议信息。
解析IP头代码:
void DecodeIPPacket(char *pData)
{
IPHeader *pIPHdr = (IPHeader*)pData;
in_addr source,dest;
char szSourceIp[32],szDestIp[32];
printf("nn------------------------------------------------n");
//从IP头中取出源IP和目的IP
source.S_un.S_addr = pIPHdr->ipSource;
dest.S_un.S_addr = pIPhdr->ipDestination;
strcpy(szSourceIp,::inet_ntoa(source));
strcpy(szDestIp,::inet_ntoa(dest));
printf(" %s->%sn",szSourceIp,szDestIp);
//IP长度
int nHeaderLen = (pIPHdr->iphVerLen & 0xf)*sizeof(ULONG);
switch(pIPHdr->ipProtocol)
{
case IPPROTO_TCP:
DecodeTCPPacket(pData+nHeaderLen);
break;
case IPPROTO_UDP:
break;
case IPPROTO_ICMP:
break;
}
}
解析TCP头代码如下:取出端口号,输出
void DecodeTCPPacket(char *pData)
{
TCPHeader &pTCPHdr = (TCPHeader*)pData;
printf("Port:%d->%dn",ntohs(pTCPHdr->sourcePort),ntohs(pTCOHdr->destinationPort));
switch(::ntohs(pTCPHdr->destinationPort))
{
case 21:
break;
case 80:
break;
case 8080:
break;
}
}
VS下完整代码:
initsock.h:
#include <winsock2.h>
#pragma comment(lib, "WS2_32") // 链接到WS2_32.lib
class CInitSock
{
public:
CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
{
// 初始化WS2_32.dll
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if(::WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup();
}
};
protoinfo.h:
//////////////////////////////////////////////////
// protoinfo.h文件
/*
定义协议格式
定义协议中使用的宏
*/
#ifndef __PROTOINFO_H__
#define __PROTOINFO_H__
#define ETHERTYPE_IP 0x0800
#define ETHERTYPE_ARP 0x0806
typedef struct _ETHeader // 14字节的以太头
{
UCHAR dhost[6]; // 目的MAC地址destination mac address
UCHAR shost[6]; // 源MAC地址source mac address
USHORT type; // 下层协议类型,如IP(ETHERTYPE_IP)、ARP(ETHERTYPE_ARP)等
} ETHeader, *PETHeader;
#define ARPHRD_ETHER 1
// ARP协议opcodes
#define ARPOP_REQUEST 1 // ARP 请求
#define ARPOP_REPLY 2 // ARP 响应
typedef struct _ARPHeader // 28字节的ARP头
{
USHORT hrd; // 硬件地址空间,以太网中为ARPHRD_ETHER
USHORT eth_type; // 以太网类型,ETHERTYPE_IP ??
UCHAR maclen; // MAC地址的长度,为6
UCHAR iplen; // IP地址的长度,为4
USHORT opcode; // 操作代码,ARPOP_REQUEST为请求,ARPOP_REPLY为响应
UCHAR smac[6]; // 源MAC地址
UCHAR saddr[4]; // 源IP地址
UCHAR dmac[6]; // 目的MAC地址
UCHAR daddr[4]; // 目的IP地址
} ARPHeader, *PARPHeader;
// 协议
#define PROTO_ICMP 1
#define PROTO_IGMP 2
#define PROTO_TCP 6
#define PROTO_UDP 17
typedef struct _IPHeader // 20字节的IP头
{
UCHAR iphVerLen; // 版本号和头长度(各占4位)
UCHAR ipTOS; // 服务类型
USHORT ipLength; // 封包总长度,即整个IP报的长度
USHORT ipID; // 封包标识,惟一标识发送的每一个数据报
USHORT ipFlags; // 标志
UCHAR ipTTL; // 生存时间,就是TTL
UCHAR ipProtocol; // 协议,可能是TCP、UDP、ICMP等
USHORT ipChecksum; // 校验和
ULONG ipSource; // 源IP地址
ULONG ipDestination; // 目标IP地址
} IPHeader, *PIPHeader;
// 定义TCP标志
#define TCP_FIN 0x01
#define TCP_SYN 0x02
#define TCP_RST 0x04
#define TCP_PSH 0x08
#define TCP_ACK 0x10
#define TCP_URG 0x20
#define TCP_ACE 0x40
#define TCP_CWR 0x80
typedef struct _TCPHeader // 20字节的TCP头
{
USHORT sourcePort; // 16位源端口号
USHORT destinationPort; // 16位目的端口号
ULONG sequenceNumber; // 32位序列号
ULONG acknowledgeNumber; // 32位确认号
UCHAR dataoffset; // 高4位表示数据偏移
UCHAR flags; // 6位标志位
//FIN - 0x01
//SYN - 0x02
//RST - 0x04
//PUSH- 0x08
//ACK- 0x10
//URG- 0x20
//ACE- 0x40
//CWR- 0x80
USHORT windows; // 16位窗口大小
USHORT checksum; // 16位校验和
USHORT urgentPointer; // 16位紧急数据偏移量
} TCPHeader, *PTCPHeader;
typedef struct _UDPHeader
{
USHORT sourcePort; // 源端口号
USHORT destinationPort;// 目的端口号
USHORT len; // 封包长度
USHORT checksum; // 校验和
} UDPHeader, *PUDPHeader;
#endif // __PROTOINFO_H__
#include "../common/initsock.h"
#include "../common/protoinfo.h"
#include <stdio.h>
#include <mstcpip.h>
#pragma comment(lib, "Advapi32.lib")
CInitSock theSock;
void DecodeTCPPacket(char *pData)
{
TCPHeader *pTCPHdr = (TCPHeader *)pData;
printf(" Port: %d -> %d n", ntohs(pTCPHdr->sourcePort), ntohs(pTCPHdr->destinationPort));
// 下面还可以根据目的端口号进一步解析应用层协议
switch(::ntohs(pTCPHdr->destinationPort))
{
case 21:
break;
case 80:
case 8080:
break;
}
}
void DecodeIPPacket(char *pData)
{
IPHeader *pIPHdr = (IPHeader*)pData;
in_addr source, dest;
char szSourceIp[32], szDestIp[32];
printf("nn-------------------------------n");
// 从IP头中取出源IP地址和目的IP地址
source.S_un.S_addr = pIPHdr->ipSource;
dest.S_un.S_addr = pIPHdr->ipDestination;
strcpy(szSourceIp, ::inet_ntoa(source));
strcpy(szDestIp, ::inet_ntoa(dest));
printf(" %s -> %s n", szSourceIp, szDestIp);
// IP头长度
int nHeaderLen = (pIPHdr->iphVerLen & 0xf) * sizeof(ULONG);
switch(pIPHdr->ipProtocol)
{
case IPPROTO_TCP: // TCP协议
DecodeTCPPacket(pData + nHeaderLen);
break;
case IPPROTO_UDP:
break;
case IPPROTO_ICMP:
break;
}
}
void main()
{
// 创建原始套节字
SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
// 获取本地IP地址
char szHostName[56];
SOCKADDR_IN addr_in;
struct hostent *pHost;
gethostname(szHostName, 56);
if((pHost = gethostbyname((char*)szHostName)) == NULL)
return ;
// 在调用ioctl之前,套节字必须绑定
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(0);
memcpy(&addr_in.sin_addr.S_un.S_addr, pHost->h_addr_list[0], pHost->h_length);
printf(" Binding to interface : %s n", ::inet_ntoa(addr_in.sin_addr));
if(bind(sRaw, (PSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR)
return;
// 设置SIO_RCVALL控制代码,以便接收所有的IP包
DWORD dwValue = 1;
if(ioctlsocket(sRaw, SIO_RCVALL, &dwValue) != 0)
return ;
// 开始接收封包
char buff[1024];
int nRet;
while(TRUE)
{
nRet = recv(sRaw, buff, 1024, 0);
if(nRet > 0)
{
DecodeIPPacket(buff);
}
}
closesocket(sRaw);
}
运行结果
- sublime学习笔记
- Java程序员必须掌握的常用Linux命令。
- SAMP论文学习
- IEEE Trans 2009 Stagewise Weak Gradient Pursuits论文学习
- async和enterproxy控制并发数量
- 从零开始写项目终极【维护网站、修复Bug】
- Redis 数据结构与内存管理策略(下)
- Redis 数据结构与内存管理策略(上)
- Servlet第三篇【request和response简介、response的常见应用】
- Java 10的10个新特性,将彻底改变你写代码的方式!
- JDK9新特性实战:简化流关闭新姿势。
- Druid数据库连接池就是这么简单
- 使用 github 做代码管理,知道这些就够了
- 二叉树就这么简单
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法