网络嗅探器
来源:互联网 发布:黑暗之魂1萝莉捏脸数据 编辑:程序博客网 时间:2024/05/22 06:51
一、网络嗅探器原理
1、首先回顾一下网卡的四种接收模式:
①广播模式:该模式下的网卡能够接收网络中的广播信息;②组播模式:设置在该模式下的网卡能够接收组播数据;③直接模式:在这种模式下,只有目的网卡才能接收该数据;④混杂模式:在这种模式下的网卡能够接收一切通过它的数据,而不管该数据是否是传给它的
2、原始套接字工作原理
首先介绍下套接字 ,套接字,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。常用的TCP/IP协议的3种套接字类型是:流套接字(SOCK_STREAM)、数据报套接字(SOCK_DGRAM)、原始套接字(SOCK_RAW)
3、嗅探器原理:
通常的套接字只响应广播模式和直接模式发出的数据帧,对于其他形式的数据帧网络接口会直接丢弃。用户模式下,对网卡混杂模式的设置是通过原始套接字实现的、创建原始套接字之后,将其绑定到一个明确的本地地址,然后向套接字发送SIO_RCVALL控制命令,让它接收所有的IP包,这样网卡就进入了混杂模式。
二、代码实现
首先创建原始套接字,将原始套接字绑定到一个明确的本地地址(不能为ADDR_ANY),然后通过ioctlsocket()函数(是控制套接口的模式),向套接字发送SIO_RCVALL控制命令,将网卡设置为混杂模式,这样就能接收到所有经过网卡的封包;然后已连接的数据报或流式套接口通过不断调用recv()函数来接收IP数据包,将接收到的数据存入缓冲数组;随后再通过自定义的IP数据包解析函数对接收到的数据进行解析,通过定义的IP地址结构体的指针对象从接收到的数据中取得数据包的IP头;然后从IP头中取出源IP地址和目的IP地址。
sniffer.cpp:
#include "../common/initsock.h"#include "../common/protoinfo.h" #include <stdio.h>#include <mstcpip.h> //用于处理TCP/IP协议的一个头文件,这里面包含了WinSockets一系列相关函数.#pragma comment(lib, "Advapi32.lib") //链接Advapi32.lib库文件CInitSock theSock; //创建CInitSock类的对象//自定义的函数,用于解析收到的TCP数据封包,解析出封包中的TCP头void DecodeTCPPacket(char *pData){ TCPHeader *pTCPHdr = (TCPHeader *)pData; /*ntohs()是一个函数名,作用是将一个16位数由网络字节顺序转换为主机字节顺序*/ printf(" Port: %d -> %d \n", ntohs(pTCPHdr->sourcePort), ntohs(pTCPHdr->destinationPort)); // 下面还可以根据目的端口号进一步解析应用层协议 switch(::ntohs(pTCPHdr->destinationPort)){ case 21: break; case 80: case 8080: break; }}/*自定义的函数,用于解析收到的IP数据封包,解析出封包中的IP头*/void DecodeIPPacket(char *pData){ IPHeader *pIPHdr = (IPHeader*)pData; in_addr source, dest; //in_addr是一个结构体,可以用来表示一个32位的IPv4地址 char szSourceIp[32], szDestIp[32]; // 定义两个数组用于存储源IP地址和目的IP地址 printf("\n\n*************************************************************************"); // 从IP头中取出源IP地址和目的IP地址 source.S_un.S_addr = pIPHdr->ipSource; // 从IP头中取出源IP地址 dest.S_un.S_addr = pIPHdr->ipDestination; // 从IP头中取出目的IP地址 strcpy(szSourceIp, ::inet_ntoa(source)); strcpy(szDestIp, ::inet_ntoa(dest)); printf(" %s ---> %s \n", szSourceIp, szDestIp); //打印出源IP地址和目的IP地址 // 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(){ // 创建原始套节字sRaw SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_IP); // 获取本地IP地址 char szHostName[56]; SOCKADDR_IN addr_in; struct hostent *pHost; //hostent是host entry的缩写,该结构记录主机的信息,包括主机名、别名、地址类型、地址长度和地址列表 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; /* ioctlsocket()是控制套接口的模式,向套接字发送SIO_RCVALL控制命令,将网卡设置为混杂模式,这样就能接收到所有经过网卡的封包 回顾一下网卡的四种接收模式: ①广播模式:该模式下的网卡能够接收网络中的广播信息; ②组播模式:设置在该模式下的网卡能够接收组播数据; ③直接模式:在这种模式下,只有目的网卡才能接收该数据; ④混杂模式:在这种模式下的网卡能够接收一切通过它的数据,而不管该数据是否是传给它的 */ if(ioctlsocket(sRaw, SIO_RCVALL, &dwValue) != 0) return ; // 开始接收封包 char buff[1024]; //用于接受数据的数组 int nRet; while(true){ /* recv函数用于已连接的数据报或流式套接口进行数据的接收 该函数的第一个参数指定接收端套接字描述符; 第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据; 第三个参数指明buf的长度; 第四个参数一般置0 */ nRet = recv(sRaw, buff, 1024, 0); //recv函数接收消息出错的话,会返回 0 if(nRet > 0){ DecodeIPPacket(buff); //调用该函数对IP封包进行解析 } } closesocket(sRaw); //关闭原始套接字}
protoinfo.h:
/*定义协议格式定义协议中使用的宏 */#ifndef __PROTOINFO_H__#define __PROTOINFO_H__#define ETHERTYPE_IP 0x0800#define ETHERTYPE_ARP 0x0806typedef 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 17typedef 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 0x80typedef 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__
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); //Winsock版本 /*为了在应用程序当中调用任何一个Winsock API函数,首先第一件事情就是必须通过WSAStartup函数完成对 Winsock服务的初始化,因此需要调用WSAStartup函数。使用Socket的程序在使用Socket之前必须调用WSAStartup函数。 该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本; 操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时, 操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。 以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。*/ if(::WSAStartup(sockVersion, &wsaData) != 0){ //(Windows异步套接字的启动命令)加载套接字库 exit(0); } } ~CInitSock() { //析构函数 ::WSACleanup(); //关闭加载的套接字库 }};
三、总结和拓展
几个重要的函数:
1、WSAStartup函数
为了在应用程序当中调用任何一个Winsock API函数,首先第一件事情就是必须通过WSAStartup函数完成对Winsock服务的初始化,因此需要调用WSAStartup函数。使用Socket的程序在使用Socket之前必须调用WSAStartup函数。
该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中以后应用程序就可以调用所请求的Socket库中的其它Socket函数了
2、recv函数
recv函数用于已连接的数据报或流式套接口进行数据的接收。
该函数的第一个参数指定接收端套接字描述符;
第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
第三个参数指明buf的长度;
第四个参数一般置0
3、拓展
在检查IP封包的端口时,我们可以根据端口号知道数据传输所使用的协议。如果是21端口,说明使用的就是FTP协议。然后通过比较字符串找出含有username和password的字段就能找出程序访问这些服务器所使用的用户名和密码。
- 网络嗅探器
- 网络嗅探器
- 网络嗅探器
- 网络嗅探器
- 网络嗅探器
- 网络嗅探器
- 网络嗅探器
- 网络嗅探器--网络大典
- sniff网络嗅探器原理
- 网络嗅探器 JunkieTheSniffer
- flashget网络嗅探器
- linux网络嗅探器
- 网络安全实验--网络嗅探器
- 简单网络嗅探器
- 网络!~
- 网络
- 网络
- 网络
- QuickSort快速排序源码
- 创建基于jsp的商品浏览器的四个步骤
- 机器学习--Logistic回归计算过程的推导
- parse_ini_file() 函数的使用
- POJ-2356 Find a multiple(鸽巢原理)题目数据太垃圾了!!
- 网络嗅探器
- angular.js中的事件
- java中abstract,public,protected,private以及不写作用域的时候在继承关系中的调用
- python中文乱码总结
- css让容器水平垂直居中的n种方式
- js中的arguments
- 238. Product of Array Except Self
- 基本类型序列化和反序列化
- exe电子书制作教程(超详细)【申明:来源于网络】