呵呵,很老得技术了, 不过自己这两天写了一个,大伙见笑了。
Sniff的实现主要分为三步:
1、将网卡设置为混杂模式
2、捕获IP数据包
3、分析IP数据包
以下是实现
///
MySniff.h
///
#include <winsock2.h>
#include <iostream>
#include <string.h>
#include <ws2tcpip.h>
#include <mstcpip.h>
using namespace std;
class MySniff
{
public:
MySniff(void);
~MySniff(void);
public:
int InitSniff(); //监听初始化函数
int ListenFunction(); //监听函数
void ReleaseSocket(); // 释放Socket函数
void IpAnalyse(char *buf); //IP数据包分析函数
private:
SOCKET m_socket; //用于监听的socket
WSADATA m_wsaData; //用于初始化网络编程的变量
HOSTENT* m_hostent; //用于存放本机的IP地址列表
struct sockaddr_in m_sockAddr; //用于监听的IP地址
};
/**************************************************************
*数据类型:ProtoStruct
*主要功能:定义了协议与协议名称得结构体,便于根据协议获得协议
* 协议名称
**************************************************************/
typedef struct _ProtoStruct
{
int proto; //协议标识
string protoText; //协议名称
}ProtoStruct;
/**************************************************************
*数据类型:IpHeader
*主要功能:定义了对应于IP数据报报头的数据结构,当对接收的IP数
* 据进行强制类型转换后,可以取出与之对应的数据
**************************************************************/
typedef struct _IpHeader
{
unsigned char verAndHeader_len; //版本及首部长度
unsigned char serverType; //数据报类型
unsigned short totalLen; //数据包大小
unsigned short ident; //数据报标识
unsigned short flags; //标志及片偏移
unsigned char ttl; //生存时间
unsigned char proto; //协议
unsigned short checkSum; //首部校验和
unsigned int sourceIp; //源地址
unsigned int destIp; //目的地址
}IpHeader;
/**************************************************************
*数据类型:TcpHeader
*主要功能:定义了对应于TIP数据报报头的数据结构,当对接收的TIP数
* 据进行强制类型转换后,可以取出与之对应的数据
**************************************************************/
typedef struct _TcpHeader
{
unsigned short sourPort; //源端口
unsigned short desPort; //目的端口
unsigned int seqNo; //序号
unsigned int askNo; //确认号
unsigned char offset; //数据偏移,数据起始处距离报文段起始处有多远
unsigned char controtBit; //控制比特位
unsigned short wndSize; //窗口大小,用于控制对方发送的数据量
unsigned short chkSum; //检验和
unsigned short urgPtr; //紧急指针
}TcpHeader;
/**************************************************************
*数据类型:UdpHeader
*主要功能:定义了对应于UDP数据报报头的数据结构,当对接收的UDP数
* 据进行强制类型转换后,可以取出与之对应的数据
**************************************************************/
typedef struct _UdpHeader
{
unsigned short sourProt; //源端口
unsigned short destPort; //目的端口
unsigned short len; //UDP数据报长度
unsigned chkSum; //检验和
}UdpHeader;
/**************************************************************
*数据类型:TcmpHeader
*主要功能:定义了对应于TCMP数据报报头的数据结构,当对接收的
* TCMP数据进行强制类型转换后,可以取出与之对应的数据
**************************************************************/
typedef struct _IcmpHeader
{
unsigned char type; //报文类型
unsigned char code; //代码
unsigned short chkSum; //检验和
}IcmpHeader;
//定义UDP头部长度
#define UDP_HEAD_LEN 8
//定义ICMP头部长度
#define ICMP_HEAD_LEN 4
/*********************************End of File***************************************/
//
MySniff.cpp
//
#include "MySinff.h"
ProtoStruct myProtos[12] =
{
{ IPPROTO_IP , "IP" },
{ IPPROTO_ICMP , "ICMP" },
{ IPPROTO_IGMP , "IGMP" },
{ IPPROTO_GGP , "GGP" },
{ IPPROTO_TCP , "TCP" },
{ IPPROTO_PUP , "PUP" },
{ IPPROTO_UDP , "UDP" },
{ IPPROTO_IDP , "IDP" },
{ IPPROTO_ND , "NP" },
{ IPPROTO_RAW , "RAW" },
{ IPPROTO_MAX , "MAX" },
{ NULL , "" }
};
string GetProto(int proto);
MySniff::MySniff(void)
{
}
MySniff::~MySniff(void)
{
}
/********************************************************************************
* 函数介绍:本函数用于完成监听初始化功能,主要包括注册DLL,初始化socket
* 输入参数:无
* 输出参数:无
* 返回值 :初始化成功标志,或初始化失败的错误类型
*********************************************************************************/
int MySniff::InitSniff()
{
if(WSAStartup(MAKEWORD(2,2),&m_wsaData) == SOCKET_ERROR)
{
ReleaseSocket();
return -1;
}
if((m_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IP)) == SOCKET_ERROR)
{
ReleaseSocket();
return -2;
}
int rcvTime = 5000;
if(setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&rcvTime, sizeof(rcvTime)) == SOCKET_ERROR)
{
return -3;
}
char flag[256];
setsockopt(m_socket, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag));
char localName[50];
gethostname(localName, sizeof(localName));
struct hostent *myHost;
myHost = gethostbyname(localName);
m_sockAddr.sin_family = AF_INET;
m_sockAddr.sin_addr = *(in_addr *)myHost->h_addr_list[0];
m_sockAddr.sin_port = htons(8080);
if(bind(m_socket, (struct sockaddr*)&m_sockAddr, sizeof(m_sockAddr)) == SOCKET_ERROR)
{
return -4;
}
unsigned long dwValue = 1;
ioctlsocket(m_socket, SIO_RCVALL, &dwValue);
return 0;
}
/*****************************************************
*函数功能: 关闭用于监听的socket,释放socket资源
*输入参数: 无
*输出参数: 无
*返回值: 无
*****************************************************/
void MySniff::ReleaseSocket()
{
shutdown(m_socket, SD_BOTH);
closesocket(m_socket);
WSACleanup();
}
/*****************************************************
*函数功能: 监听函数,完成监听功能,即截获IP数据报并完成
数据报类型分析,以及数据分析。
*输入参数: 无
*输出参数: 无
*返回值: 是否成功,若失败返回失败原因
*****************************************************/
int MySniff::ListenFunction()
{
char buf[65535];
IpHeader *ipHeader; //存放IP数据包包头的结构变量
in_addr ipAddr; //地址类型的变量
unsigned short headerLen, totalLen; //数据包得报头长度和数据包总长度
unsigned short sourPort, destPort;
string strSourIp, strDestIp; //源IP地址,目的IP地址
string strSourPort, strDestPort; //源端口地址,目的端口地址
char *data; //指向协议数据包数据的指针
string strProto; //协议名称
int ret = 0;
while(true)
{
// memset(buf, 0, 1024);
totalLen = 0;
ret = recv(m_socket, buf, sizeof(buf), 0);
if(ret == SOCKET_ERROR)
{
continue;
}
char *workBuf = buf;
ipHeader = (IpHeader *)workBuf; //强制转换IP数据包头
ipAddr.S_un.S_addr = ipHeader->sourceIp; //获取源IP地址
strSourIp = inet_ntoa(ipAddr); //将获取的源IP地址转化为字符串形式
ipAddr.S_un.S_addr = ipHeader->destIp; //获取目的IP地址
strDestIp = inet_ntoa(ipAddr); //将获取的目的IP地址转化为字符串形式
unsigned char proto = ipHeader->proto;
strProto = GetProto(proto);
headerLen = ipHeader->verAndHeader_len&0xf;
headerLen = headerLen * 4;
totalLen = ntohs(ipHeader->totalLen);
totalLen = totalLen - headerLen;
switch(proto)
{
case IPPROTO_ICMP:
{
IcmpHeader *icmpHeader; //存放ICMP协议数据包包头的结构变量
icmpHeader = (IcmpHeader *)(workBuf + headerLen);
strSourPort = "-";
strDestPort = "-";
char *nowData = ((char*)icmpHeader) + ICMP_HEAD_LEN; //将data指向ICMP数据包的数据部分
totalLen -= ICMP_HEAD_LEN; //得到ICMP数据包的数据部分的总长度
data = new char[totalLen+1];
// memcpy(data, nowData, totalLen);
break;
}
case IPPROTO_TCP:
{
TcpHeader *tcpHeader; //存放TCP协议数据包包头的结构变量
tcpHeader = (TcpHeader *)(workBuf + headerLen);
sourPort = ntohs(tcpHeader->sourPort);
destPort = ntohs(tcpHeader->desPort);
headerLen = ((tcpHeader->offset)>>4)&0xf;
headerLen = headerLen * 4;
char *nowData = ((char*)tcpHeader) + headerLen;
totalLen -= headerLen; //得到TCP数据包的数据部分的总长度
data = new char[totalLen+1];
// memcpy(data, nowData, totalLen);
break;
}
case IPPROTO_UDP:
{
UdpHeader *udpHeader; //存放UDP协议数据包包头的结构变量
udpHeader = (UdpHeader *)(workBuf + headerLen);
sourPort = ntohs(udpHeader->sourProt);
destPort = ntohs(udpHeader->destPort);
char *nowData = ((char*)udpHeader) + UDP_HEAD_LEN;
totalLen -= UDP_HEAD_LEN; //得到UDP数据包的数据部分的总长度
data = new char[totalLen+1];
// memcpy(data, nowData, totalLen);
break;
}
}//end switch;
cout << strProto.c_str() << "/t" << strSourIp.c_str() << "/t" << strDestIp.c_str()
<< "/t" << sourPort << "/t" << destPort << "/t" << ret << "/t" <<totalLen << endl;
// cout << data << endl;
}
return 0;
}
/*****************************************************
*函数功能: 根据指定协议类型,获取协议名称
*输入参数:
int proto 协议类型
*输出参数: 无
*返回值: 协议对应的协议名称
*****************************************************/
string GetProto(int proto)
{
bool bFound = false ;
for( int i = 0 ; i < 11 ; i++ )
{
if( myProtos[i].proto == proto )
{
bFound = true ;
break ;
}
}
if( bFound )
return myProtos[i].protoText ;
return myProtos[11].protoText ;
}
/*********************************End of File***************************************/
///
Sniff.cpp
#include "MySinff.h"
#include <windows.h>
#include <process.h>
int main()
{
MySniff mySniff;
if(mySniff.InitSniff() != 0)
{
cout << "init sniff error!" << endl;
}
else
{
mySniff.ListenFunction();
}
char ch;
cin >> ch;
return 0;
}