利用libpcap抓取QQ号码信息
2024-08-26 14:56:10
最近想在QQ登录时把QQ号码信息记录下来,百度了很多都没有找到具体方式,最近用Wireshark分析报文+libpcap库嗅探实现了这个小功能。
通讯背景:
QQ客户端在通讯时使用UDP协议,其中数据消息报文为UDP协议,控制报文为OICQ协议(UDP协议的一种封装),控制报文命令常见如下(括号内为改命令在OICQ报文中对应二进制编码的十进制表示):
"log out(1)",
"Heart Message(2)",
"Set status(13)",
"Receive message(23)",
"Request KEY(29)", //登录时
"Get friend online(39)",
"Group name operation(60)",
"MEMO Operation(62)",
"Download group friend(88)",
"Get level(92)",
"Request login(98)", //离线时
"Request extra information(101)",
"Signature operation(103)",
"Get status of friend(129)",
"Get friend's status of group(181)",
QQ客户端使用的端口为4000,服务器的端口为8000,当存在多个QQ客户端时,端口号从4000依次向上累加。
报文分析:
在Windows下,由Wireshark抓包分析,QQ在登录与运行时,会向服务器发送UDP以及OICQ报文,这里假定一台机器上少于100个QQ号码登录,定义过滤器如下:
从oicq过滤中发现可以百分百命中含有QQ号码的报文,确定位置在以太网数据包的第49~52字节,以4字节的无符号整形数表示。但libpcap的过滤器仅支持到udp的过滤,于是按下面的filter来过滤测试:
发现,在udp数据包同样的位置也存放有明文的qq号码信息,于是确认了抓取条件(udp.srcport<4100 是为了避免某些不符合规则报文信息的干扰)。
调试代码:
运行环境为Linux,需要安装libpcap,并且在链接时 -lpcap。
头文件:
#ifndef __SNIFFER_H__
#define __SNIFFER_H__ #include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h> /* 以太网帧头部 */
#define ETHER_ADDR_LEN 6 struct sniff_ethernet{
u_char ether_dhost[ETHER_ADDR_LEN]; /* 目的主机的地址 */
u_char ether_shost[ETHER_ADDR_LEN]; /* 源主机的地址 */
u_short ether_type;
}; /* IP数据包的头部 */
struct sniff_ip{
#if BYTE_ORDER == LITTLE_ENDIAN
u_int ip_hl:, /* 头部长度 */
ip_v:; /* 版本号 */
#if BYTE_ORDER == BIG_ENDIAN
u_int ip_v:, /* 版本号 */
ip_hl:; /* 头部长度 */
#endif
#endif /* not _IP_VHL */
u_char ip_tos; /* 服务的类型 */
u_short ip_len; /* 总长度 */
u_short ip_id; /* 包标志号 */
u_short ip_off; /* 碎片偏移 */
#define IP_RF 0x8000 /* 保留的碎片标志 */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* 多碎片标志*/
#define IP_OFFMASK 0x1fff /* 分段位 */
u_char ip_ttl; /* 数据包的生存时间 */
u_char ip_p; /* 所使用的协议 */
u_short ip_sum; /* 校验和 */
struct in_addr ip_src,ip_dst; /* 源地址、目的地址*/
}; /* TCP 数据包的头部 */
typedef u_int tcp_seq; struct sniff_tcp{
u_short th_sport; /* 源端口 */
u_short th_dport; /* 目的端口 */
tcp_seq th_seq; /* 包序号 */
tcp_seq th_ack; /* 确认序号 */
#if BYTE_ORDER == LITTLE_ENDIAN
u_int th_x2:, /* 还没有用到 */
th_off:; /* 数据偏移 */
#endif
#if BYTE_ORDER == BIG_ENDIAN
u_int th_off:, /* 数据偏移*/
th_x2:; /* 还没有用到 */
#endif
u_char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FINTH_SYNTH_RSTTH_ACKTH_URGTH_ECETH_CWR)
u_short th_win; /* TCP滑动窗口 */
u_short th_sum; /* 头部校验和 */
u_short th_urp; /* 紧急服务位 */
}; #endif /* __SNIFFER_H__ */
源码:
#include "sniffer.h" void getPacket(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
static int id = ;
const struct sniff_ethernet *ethernet; /* 以太网帧头部*/
const struct sniff_ip *ip; /* IP包头部 */
const struct sniff_tcp *tcp; /* TCP包头部 */
const char *payload; /* 数据包的有效载荷*/ int size_ethernet = sizeof(struct sniff_ethernet);
int size_ip = sizeof(struct sniff_ip);
int size_tcp = sizeof(struct sniff_tcp); ethernet = (struct sniff_ethernet*)(packet);
ip = (struct sniff_ip*)(packet + size_ethernet);
tcp = (struct sniff_tcp*)(packet + size_ethernet + size_ip);
payload = (u_char *)(packet + size_ethernet + size_ip + size_tcp); int sport = ntohs(tcp->th_sport);
int dport = ntohs(tcp->th_dport); //for QQ
if (dport != || sport > )
{
return ;
}
printf("packet: %d\n", ++id);
printf("%s:%d -> ", inet_ntoa(ip->ip_src), sport);
printf("%s:%d \n", inet_ntoa(ip->ip_dst), dport);
printf("QQ:%d\n", packet[]****** +
packet[]**** +
packet[]** +
packet[]); /*for test
int i;
for(i=0; i<pkthdr->len; ++i)
{
printf(" %02x", packet[i]);
if ((i + 1) % 16 == 0 )
{
printf("\n");
}
if ((i + 1) % 8 == 0 )
{
printf(" ");
}
}*/ printf("\n");
} int main(int argc, char **argv)
{
pcap_t *devic = NULL;
char *devStr = NULL;
char errBuf[PCAP_ERRBUF_SIZE] = "";
char *filter_rule = "dst port 8000";
struct bpf_program filter; devStr = pcap_lookupdev(errBuf);
if (!devStr)
{
printf("Error: %s\n", errBuf);
return -;
}
printf("Success: %s\n", devStr); devic = pcap_open_live(devStr, , , , errBuf);
if (!devic)
{
printf("Error: %s\n", errBuf);
return -;
} pcap_compile(devic, &filter, filter_rule, , );
pcap_setfilter(devic, &filter); pcap_loop(devic, -, getPacket, NULL); pcap_close(devic); return ;
}
测试结果:
备注:
在测试时发现,极少的情况OICQ协议里,含有"MEMO Operation(62)"的数据包中,会概率性出现非该测试QQ的另一个号码,原因未知... 当时忘了记录,最近实验了几次又一直没出现,没有图片了。
最新文章
- angularjs——插值字符串
- C# 创建Windows Service
- iOS---SQLite数据库框架之FMDB -Swift
- LeetCode题解-----First Missing Positive
- zepto源码--整体框架--学习笔记
- 给用户添加sudo权限
- 关于padding
- 同一个页面多个CALayer重绘的办法
- Apriori算法Python实现
- Java 测试并行编程(三)
- github的拉取、提交,创建分支与合并
- 读取FTP上的某个文本文档内容到本地
- 将golang中变量重置为零的reflect方法
- hello C#
- sql的一个查询,情景:a表中存在的数据,且在b表中不存在 (not in,not exists
- maven工程插件配置
- python寻找list中最大值、最小值并返回其所在位置
- SQL一些问题
- timer Compliant Controller project (3)--bom and sch
- Django基础之Model操作