利用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 实验结果 ...
 
随机推荐
- 【转】Linux I2C设备驱动编写(一)
			
原文网址:http://www.cnblogs.com/biglucky/p/4059576.html 在Linux驱动中I2C系统中主要包含以下几个成员: I2C adapter 即I2C适配器 I ...
 - 2015第30周三Spring常用工具类
			
文件资源操作 文件资源的操作是应用程序中常见的功能,如当上传一个文件后将其保存在特定目录下,从指定地址加载一个配置文件等等.我们一般使用 JDK 的 I/O 处理类完成这些操作,但对于一般的应用程序来 ...
 - (转载)php反射类 ReflectionClass
			
(转载)http://hi.baidu.com/daihui98/item/a67dfb8213055dd75f0ec165 php反射类 ReflectionClass 什么是php反射类,可以 ...
 - rowspan和colspan
			
1.说明 rowspan:跨行colspan:跨列用于设计复杂的表格 2.例子<!DOCTYPE html> <html> <head> <meta char ...
 - LeetCode-Add Two Binary
			
Add BinaryApr 2 '12 3558 / 10570 Given two binary strings, return their sum (also a binary string). ...
 - LeetCode题目答案索引
			
LeetCode-Two Sum LeetCode-Median of Two Sorted Arrays LeetCode-Longest Substring Without Repeating C ...
 - 尚学堂 JAVA Day1 概念总结
			
1.什么是计算机语言?一些符号,这些符号按照计算机硬件结构可以理解的方式排列组合,方便人与计算机,计算机与计算机之间进行信息的交换. 2.什么是机器语言?就是简单的二进制0和1的组合.该语言是计算机硬 ...
 - java的 IO流之缓冲流(转载)
			
java缓冲流本身不具IO功能,只是在别的流上加上缓冲提高效率,像是为别的流装上一种包装.当对文件或其他目标频繁读写或操作效率低,效能差.这时使用缓冲流能够更高效的读写信息.因为缓冲流先将数据缓存起来 ...
 - 2013级C++第15周(春)项目——输入输出流及文件文件操作
			
课程首页在:http://blog.csdn.net/sxhelijian/article/details/11890759.内有完整教学方案及资源链接 本周程序阅读及程序调试中须要的文件,请到htt ...
 - http to https automatic--weblogic/jboss/tomcat--reference
			
weblogic reference from:http://middlewaremagic.com/weblogic/?p=2019 Many times we want to secure our ...