利用libpcap抓取QQ号码信息
最近想在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的另一个号码,原因未知... 当时忘了记录,最近实验了几次又一直没出现,没有图片了。
利用libpcap抓取QQ号码信息的更多相关文章
- 利用libpcap抓取数据包
转载自:http://blog.csdn.net/tennysonsky/article/details/44811899 概述 libpcap是一个网络数据包捕获函数库,tcpdump就是以libp ...
- [Python爬虫] 之二十八:Selenium +phantomjs 利用 pyquery抓取网站排名信息
一.介绍 本例子用Selenium +phantomjs爬取中文网站总排名(http://top.chinaz.com/all/index.html,http://top.chinaz.com/han ...
- .net实现扫描二维码登录webqq群抓取qq群信息
一.流程 1. //获得二维码的qrsig,cookie标志 2. //登录二维码获得二维码的状态,及最新的url 3. //登录此网址,获得Cookies 4.//cookies,筛选出skey信息 ...
- [Python爬虫] 之三十一:Selenium +phantomjs 利用 pyquery抓取消费主张信息
一.介绍 本例子用Selenium +phantomjs爬取央视栏目(http://search.cctv.com/search.php?qtext=消费主张&type=video)的信息(标 ...
- 利用wireshark抓取远程linux上的数据包
原文发表在我的博客主页,转载请注明出处. 前言 因为出差,前后准备总结了一周多,所以博客有所搁置.出差真是累人的活计,不过确实可以学习到很多东西,跟着老板学习做人,学习交流的技巧.入正题~ wires ...
- 通过Scrapy抓取QQ空间
毕业设计题目就是用Scrapy抓取QQ空间的数据,最近毕业设计弄完了,来总结以下: 首先是模拟登录的问题: 由于Tencent对模拟登录比较讨厌,各个防备,而本人能力有限,所以做的最简单的,手动登录后 ...
- Python爬虫实战---抓取图书馆借阅信息
Python爬虫实战---抓取图书馆借阅信息 原创作品,引用请表明出处:Python爬虫实战---抓取图书馆借阅信息 前段时间在图书馆借了很多书,借得多了就容易忘记每本书的应还日期,老是担心自己会违约 ...
- Python爬虫实战八之利用Selenium抓取淘宝匿名旺旺
更新 其实本文的初衷是为了获取淘宝的非匿名旺旺,在淘宝详情页的最下方有相关评论,含有非匿名旺旺号,快一年了淘宝都没有修复这个. 可就在今天,淘宝把所有的账号设置成了匿名显示,SO,获取非匿名旺旺号已经 ...
- 对比使用Charles和Fiddler两个工具及利用Charles抓取https数据(App)
对比使用Charles和Fiddler两个工具及利用Charles抓取https数据(App) 实验目的:对比使用Charles和Fiddler两个工具 实验对象:车易通App,易销通App 实验结果 ...
随机推荐
- BZOJ2749: [HAOI2012]外星人
2749: [HAOI2012]外星人 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 377 Solved: 199[Submit][Status] ...
- 关于Cookie和Session【转载】
当你第一次访问一个网站的时候,网站服务器会在响应头内加上Set-Cookie:PHPSESSID=nj1tvkclp3jh83olcn3191sjq3(php服务器),或Set-Cookie JSES ...
- 一些提高开发效率的 Category
最近工作陆续生产了一些方便开发的工具类,尽管最终没被收入使用,但不妨碍个人使用,故特此开一篇博文,也记录一些自己踩的坑. UIGestureRecognizer+Block 简单来说,你可以这样使用 ...
- Highcharts下载与使用_数据报表图
Highcharts简介 Highcharts:功能强大.开源.美观.图表丰富.兼容绝大多数浏览器的纯js图表库 Highcharts是一款纯javascript编写的图表库,能够很简单便捷的在Web ...
- @余凯_西二旗民工 【SVM之菜鸟实现】—5步SVM
#翻译#了下 余凯老师的 心法 以前的一篇博文:二分类SVM方法Matlab实现 前几日实现了下,虽然说是Linear-SVM,但是只要可以有映射函数也可以做kernel-svm function [ ...
- PHP开发Android应用程序(转)
第一部分是指在Android系统的手机上直接写PHP脚本代码并立即运行:第二部分则继续讲解如何把写好的PHP脚本代码打包成akp安装文件. 首先,在手机上安装两个apk包. 一个是SL4A(Scrip ...
- 我的Android进阶之旅------>Android 设置默认语言、默认时区
1. 设置默认时区 PRODUCT_PROPERTY_OVERRIDES += \ persist.sys.timezone=Asia/Shanghai\ 注:搜索“persist.sys.timez ...
- C#递归搜索指定目录下的文件或目录
诚然可以使用现成的Directory类下的GetFiles.GetDirectories.GetFileSystemEntries这几个方法实现同样的功能,但请相信我不是蛋疼,原因是这几个方法在遇上[ ...
- 从零基础入门JavaScript(2)
在上次的学习当中,我已经对JavaScript有了一定基础上的理解,比如:JavaScript的发展史,JavaScript中变量的命名,各种运算符,以及数据的类型与它们之间的转化.还有就是一些最基本 ...
- Java请求参数类QueryParameter
import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.StringUtils; /** * 请求 ...