局域网内QQ号码的嗅探

来源:互联网 发布:公司软件开发源代码 编辑:程序博客网 时间:2024/05/12 06:29
 最近刚看完《Windows网络与通信程序设计》,颇有些感受,其中的内容都是我很喜欢的系统网络底层知识。C++是C的扩充,引入了面向对象的概念,今天中午看了下《数据结构 c++ 语言描述》,认识到“面向对象”对于数据有很好的管理与操作,而数据像是信息在计算机的表现形式(反正是很基础的东东)。虽然只是初阅此书,但回想起《Windows网络与通信程序设计》的一些内容,意识到在底层里头大都是跟内存打交道,又再想想前些日子做外挂的经历,C++、指、内存简直是底层知识的半壁江山。

        本想研究研究代理的设计,理清一下思路,但似乎已没那么多的时间(-_-!,要学的东东太多了)。下午的时候就用自已刚学的知识做点好玩的。以前下载的QQ嗅探器若非注册太都是被限制了些功能(可以破解软件),要不然的话可能又会有木马、病毒之类的恶意程序,还是自已编写的好(早就想写了,不过以前不懂原理),对于Hub的局域网环境下,只需把网卡设置为混杂模式就可以收到所有经过本机网卡的数据,那要怎么设置呢?(曾经看书浏览网页的时候,大都千篇一律地这么讲)其实所谓的设置成“混杂模式”,依我看来也就是在程序设计的时候:在调用bind() 绑定套接字后再调用 ioctlsocket()函数设置为接收所有的数据,即ioctlsocket(raw_sock,SIO_RCVALL, &dwValue) 其中 #defineSIO_RCVALL _WSAIOW(IOC_VENDOR,1)dwValueWSADATA类型 ,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 加入它即可,-_-! 找了很久才发现)。程序效果:

原创粉丝点击