本想研究研究代理的设计,理清一下思路,但似乎已没那么多的时间(-_-!,要学的东东太多了)。下午的时候就用自已刚学的知识做点好玩的。以前下载的QQ嗅探器若非注册太都是被限制了些功能(可以破解软件),要不然的话可能又会有木马、病毒之类的恶意程序,还是自已编写的好(早就想写了,不过以前不懂原理),对于Hub的局域网环境下,只需把网卡设置为混杂模式就可以收到所有经过本机网卡的数据,那要怎么设置呢?(曾经看书浏览网页的时候,大都千篇一律地这么讲)其实所谓的设置成“混杂模式”,依我看来也就是在程序设计的时候:在调用bind() 绑定套接字后再调用 ioctlsocket()函数设置为接收所有的数据,即ioctlsocket(raw_sock,SIO_RCVALL, &dwValue) 其中 #defineSIO_RCVALL _WSAIOW(IOC_VENDOR,1) ,dwValue 为WSADATA类型 ,raw_sock 为已绑定的原始套接字。这样的话,就可以调用recv 来接收所有的经过本地网卡的数据。接收到数据报文之后,再进行过滤数据报,然后再解包。
至于解包,就是最关键的一步了,因为数据报里头就有我们想要的数据了,这也是困扰了我比较长时间的问题,刚看完书才模糊地知道大概的嗅探流程,解包的话就要知道所截到的QQ数据报的协议格式,晕啊,又不是普通的HTTP、TCP、Telnet等数据报文,咋知道它的协议格式是什么呀...嗯,就百度吧,上面搜了下,也没有什么具体的说明,都只是一个思路( 难道又要自已抓包分析```汗 ),后来在中国X黑客小组中发现了《嗅探局域网的qq》一文,就参考了其中解包分析及程序设计的方法,呵呵果然成功了~~~
即然是向大虾们学习到的知识,也拿自已的代码出来共享共享吧,只是初步地实现了嗅探还有众多的不足,望高人指点:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib")
#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)
#define MAX_HOSTNAME_LAN 255
#define MAX_ADDR_LEN 16
//-------------协议头-------------
struct IPHeader {
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地址
};
struct UDPHeader {
USHORT sourcePort; // 源端口号
USHORT destinationPort;// 目的端口号
USHORT len; // 封包长度
USHORT checksum; // 校验和
};
//-------------协议头-------------
void main()
{
SOCKET raw_sock; //用来创建原始套接字
WSADATA wsdata; //Windows Sockets实现细节在WSAData结构中的描述
unsigned char *pUdpData; //无符号字符的指针
int QQ,UdpDataLen; //QQ号、UDP的数据长度
SOCKADDR_IN sa,saSource, saDest; //本地地址 ; 数据报的源地址、目的地址
struct hostent FAR * pHostent; //一个结构体指针,用于得到本主机的一些信息
char FAR name[MAX_HOSTNAME_LAN]; //名字指针???
int nRet; //接收到的数据长度
//用于保存源地址、接收的缓冲区(IP数据报的最大长度为65535)
char szSourceIP[MAX_ADDR_LEN],szDestIP[MAX_ADDR_LEN],RecvBuf[65535] = {0};
struct IPHeader *pIpheader; //IP报文头指针
struct UDPHeader *pUdpheader; //UDP报文头指针
//定义winsock的版本
WSAStartup(MAKEWORD(2,1),&wsdata);
//创建原始套接字
if ((raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP))==SOCKET_ERROR)
exit(1);
gethostname(name, MAX_HOSTNAME_LAN);
pHostent = gethostbyname(name);
// 在调用ioctl之前,套节字必须绑定
sa.sin_family = AF_INET;
sa.sin_port = htons(6000);
memcpy(&sa.sin_addr.S_un.S_addr, pHostent->h_addr_list[0], pHostent->h_length);
if (bind(raw_sock, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR)
exit(1);
// 设置SIO_RCVALL控制代码,以便接收所有的IP包
DWORD dwValue = 1;
if(ioctlsocket(raw_sock, SIO_RCVALL, &dwValue) != 0)
exit(0) ;
//使 IP报头指针 指向接收的缓冲区(这里已进行结构体的强制类型转化)
pIpheader = (struct IPHeader *)RecvBuf;
//使 UDP报头指针 指向接收的缓冲区(由于 UDP报文 的前面封装有 IP报文 故要加上其大小)
pUdpheader = (struct UDPHeader *) (RecvBuf+ sizeof(struct IPHeader ));
//输出信息的排列格式
printf("%-15s %-15s %-20s\n", "本地地址", "远程地址", "QQ号码");
//------------------------------------------------嗅探-------------------------------------
while (1)
{
//memset:将内存中的每个字节的内容全部设置为 0的ASCII值 ,通常为新申请的内存做初始化工作
memset(RecvBuf, 0, sizeof(RecvBuf));
//接收网卡中所有到达的数据
nRet=recv(raw_sock, RecvBuf, sizeof(RecvBuf), 0);
if(nRet>0)
{
UdpDataLen =(ntohs(pIpheader->ipLength)-(sizeof(struct IPHeader)+sizeof(struct UDPHeader)));
if((pIpheader->ipProtocol)==IPPROTO_UDP&&UdpDataLen!=0)
{
//关于指针这种用法
pUdpData=(unsigned char *) RecvBuf+sizeof(struct IPHeader)+sizeof(struct UDPHeader);
UdpDataLen=ntohs(pIpheader->ipLength)-(sizeof(struct IPHeader)+sizeof(struct UDPHeader));
//这里只能通过嗅探 8000端口 进而进行解包
//(下面是对QQ的一个协议的分析,进而解包)
if(ntohs(pUdpheader->destinationPort)==8000)
{
if(UdpDataLen%4==0 && UdpDataLen>0)
if(pUdpData[0]==0x02 && pUdpData[3]==0x00 && pUdpData[UdpDataLen-1]==0x03)
{
QQ = (pUdpData[7]&0xff);
QQ = (QQ<<8) + (pUdpData[8]&0xff);
QQ = (QQ<<8) + (pUdpData[9]&0xff);
QQ = (QQ<<8) + (pUdpData[10]&0xff);
saSource.sin_addr.s_addr = pIpheader->ipSource;
saDest.sin_addr.s_addr = pIpheader->ipDestination;
//strncpy()函数:
//将字符串 inet_ntoa(saSource.sin_addr) 中
//最多 MAX_ADDR_LEN 个字符复制到字符数组 szSourceIP 中
strncpy(szSourceIP, inet_ntoa(saSource.sin_addr), MAX_ADDR_LEN);
strncpy(szDestIP, inet_ntoa(saDest.sin_addr), MAX_ADDR_LEN);
//输出信息
printf("%-15s %-15s %-20d\n",szSourceIP,szDestIP,QQ);
}
}
}
}
}
//-----------------------------------------------------------------------------------------
//后续工作,关闭套接字、释放资源
closesocket(raw_sock);
::WSACleanup();
}
我的编译环境:WinXPSP3 + SDK2003SP1 (注意:若有出错:reference to a zero-sized array is illegal ,那是因为wspiapi.h 中 #define _WSPIAPI_COUNTOF 语句造成的,只需向wspiapi.h 加入它即可,-_-! 找了很久才发现)。程序效果: