[置顶] 用抓包工具抓取和分析YY音频

YY的音频数据传输是P2P协议,音频的编码为AAC,下面抓去的音频编码的信息和频谱信息。



音频编码为AAC,采样为44K,码率24kb/s。音频编码在24kb/s码率能达到15K的音质。值得大家学习啊。

 

1.准备工具

procexp.exe      分析YY的进程信息

Procmon.exe      分析YY的网络数据包

wireshark.exe    分析网络包的内容

 

2.分析YY的进程信息

  使用procexp分析YY的大致信息,比如进程号,网络连接等


 

3.分析YY的网络传输信息

使用procmon分析YY的网络数据,根据上面的得到的进程ID设置过滤,只接受YY的UDP数据包


过滤后得到数据包如下:



从上面的数据可以看到端口为8456的UDP接受数据最多,可以看出这个端口接受的就是P2P音频数据。

 

4.使用wireshark抓取P2P音频数据包

设置wireshark的过滤器,只抓去端口为8456的UDP数据包


抓去的数据如下:



查看UDP数据流,这里面存的就是YY的音频数据。从下面的数据看不出来具体的音频编码。

不急,我们多看几个数据包就会发现,他们都有固定的数据头,紧接着都刚好是0xff|0xf1(这个刚好是aac ADTS的同步头)。所以我们可以按照这个思路分析下去。

 

5. 使用代码分析pcap抓去的数据包

详细分析参考代码:

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <assert.h>

///
typedef unsigned int  bpf_u_int32;
typedef unsigned char u_char;
typedef unsigned short  u_short;
typedef unsigned int u_int;
typedef int bpf_int32;

/*
Pcap文件分析如下:
+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
|Pcap Header	|Packet Header	|Packet Body	|Packet Header	|Packet Body	|		
+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+

*/
/*
Pcap文件头
+-------+-------+-------+-------+
|		Magic 4B				|
+-------+-------+-------+-------+
|	Major 2B	|  Minor 2B		|
+-------+-------+-------+-------+
|		ThisZone 4B				|
+-------+-------+-------+-------+
|		SigFigs 4B				|
+-------+-------+-------+-------+
|		SnapLen 4B				|
+-------+-------+-------+-------+
|		LinkType 4B				|
+-------+-------+-------+-------+

Pcap文件头24B各字段说明:
Magic:4B:0x1A 2B 3C 4D:用来标示文件的开始
Major:2B,0x02 00:当前文件主要的版本号     
Minor:2B,0x04 00当前文件次要的版本号
ThisZone:4B当地的标准时间;全零
SigFigs:4B时间戳的精度;全零
SnapLen:4B最大的存储长度    
LinkType:4B链路类型
常用类型:
0            BSD loopback devices, except for later OpenBSD
1            Ethernet, and Linux loopback devices
6            802.5 Token Ring
7            ARCnet
8            SLIP
9            PPP
*/
typedef struct pcap_file_header {
	bpf_u_int32 magic;
	u_short version_major;
	u_short version_minor;
	bpf_int32 thiszone;    
	bpf_u_int32 sigfigs;   
	bpf_u_int32 snaplen;   
	bpf_u_int32 linktype;  
}pcap_file_header;

/*
Packet 包头和Packet数据组成
+-------+-------+
|Packet Header	|
+-------+-------+
|Packet Body	|
+-------+-------+

Paket 包头
+-------+-------+-------+-------+
|		Timestamp 4B			|
+-------+-------+-------+-------+
|		Timestamp 4B			|
+-------+-------+-------+-------+
|		Caplen 4B				|
+-------+-------+-------+-------+
|		Len	4B					|
+-------+-------+-------+-------+

字段说明:
Timestamp:时间戳高位,精确到seconds     
Timestamp:时间戳低位,精确到microseconds
Caplen:当前数据区的长度,即抓取到的数据帧长度,由此可以得到下一个数据帧的位置。
Len:离线数据长度:网络中实际数据帧的长度,一般不大于caplen,多数情况下和Caplen数值相等。
Packet 数据:即 Packet(通常就是链路层的数据帧)具体内容,长度就是Caplen,这个长度的后面,就是当前PCAP文件中存放的下一个Packet数据包,也就 是说:PCAP文件里面并没有规定捕获的Packet数据包之间有什么间隔字符串,下一组数据在文件中的起始位置。我们需要靠第一个Packet包确定。
*/
typedef struct  timestamp{
	bpf_u_int32 timestamp_s;
	bpf_u_int32 timestamp_ms;
}timestamp;

typedef struct pcap_header{
	timestamp ts;
	bpf_u_int32 capture_len;
	bpf_u_int32 len;

}pcap_header;

//help funtion
void  printPcapHeader(pcap_file_header * ph)	
{
	printf("=====================\n"
	   "magic:0x%0x\n"
	   "version_major:%u\n"
	   "version_minor:%u\n"
	   "thiszone:%d\n"
	   "sigfigs:%u\n"
	   "snaplen:%u\n"
	   "linktype:%u\n"
	   "=====================\n",
	   ph->magic,
	   ph->version_major,
	   ph->version_minor,
	   ph->thiszone,
	   ph->sigfigs,
	   ph->snaplen,
	   ph->linktype);
}

void printPacketHeader(pcap_header * ph)
{
	printf("=====================\n"
		"ts.timestamp_s:%u\n"
		"ts.timestamp_ms:%u\n"
		"capture_len:%u\n"
		"len:%d\n"
		"=====================\n",
		ph->ts.timestamp_s,
		ph->ts.timestamp_ms,
		ph->capture_len,
		ph->len);
}
//
/*
	UDP包分析
*/
/* 4字节的IP地址 */
typedef struct ip_address{
	u_char byte1;
	u_char byte2;
	u_char byte3;
	u_char byte4;
}ip_address;

/* IPv4头 */
typedef struct ip_header{
	u_char  ver_ihl;        // 版本 (4 bits) + 首部长度 (4 bits)  /// 首部长度
	u_char  tos;            // 服务类型(Type of service) 
	u_short tlen;           // 总长(Total length) 
	u_short identification; // 标识(Identification)
	u_short flags_fo;       // 标志位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits)
	u_char  ttl;            // 存活时间(Time to live)
	u_char  proto;          // 协议(Protocol)
	u_short crc;            // 首部校验和(Header checksum)
	ip_address  saddr;      // 源地址(Source address)
	ip_address  daddr;      // 目的地址(Destination address)
	u_int   op_pad;         // 选项与填充(Option + Padding)
}ip_header;

/* UDP头 */
typedef struct udp_header{
	u_short sport;          // 源端口(Source port)
	u_short dport;          // 目的端口(Destination port)
	u_short len;            // UDP数据包长度(Datagram length)   /// UDP总长度
	u_short crc;            // 校验和(Checksum)
}udp_header;

u_char pkt_data[65536];

int main()
{
	FILE * fp = fopen("yy.p2p.packet_long.pcap", "rb");
	if (!fp)
	{
		fprintf(stderr, "open file error\n");
		return -1;
	}
	FILE * aacfp = fopen("yy.p2p.packet_long.pcap.aac", "wb");
	if (!fp)
	{
		fprintf(stderr, "open file error\n");
		return -1;
	}

	//1. Read pcap file header
	pcap_file_header pfh;
	fread(&pfh, 1, sizeof(pfh), fp);
	printPcapHeader(&pfh);
	

	//2. Read pcap packet
	while (!feof(fp))
	{
		pcap_header ph;
		if (fread(&ph, 1, sizeof(ph), fp) != sizeof(ph))
			break;
		printPacketHeader(&ph);
		
		if (fread(pkt_data, 1, ph.capture_len, fp) != ph.capture_len)
			break;

		//3. 分析消息包内容
		//fseek(fp, ph.capture_len, 1);
		// 获取IP数据包头的位置
		ip_header *ih = (ip_header *)(pkt_data + 14); // 14以太网头部长度
			
		// 只处理UDP包
		if (ih->proto != 0x11)
			continue;

		// 获取UDP首部的位置
		u_int ip_len = (ih->ver_ihl & 0x0f) * 4;
		udp_header * uh = (udp_header *)((u_char *)ih + ip_len);
	
		// 
		u_short sport = ntohs(uh->sport);  
		u_short dport = ntohs(uh->dport);
		u_short udplen = ntohs(uh->len);

		if(sport != 8455) // yy源端口为8455
			continue; 
		/* 打印IP地址和UDP端口 */
		printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
			ih->saddr.byte1,
			ih->saddr.byte2,
			ih->saddr.byte3,
			ih->saddr.byte4,
			sport,
			ih->daddr.byte1,
			ih->daddr.byte2,
			ih->daddr.byte3,
			ih->daddr.byte4,
			dport);

		//
		u_char * udp_data = (u_char *)uh + 8;
		//u_char * aac_data = udp_data + 34;
		//u_short  aac_len = udplen - 8 - 34;

		u_char * aac_data = pkt_data + 14 + ip_len + 8 + 34;
		int  aac_len = ph.capture_len - (14 + ip_len + 8 + 34);
		//assert(aac_len > 0); 
		if (aac_len <= 0) continue;   /// 只有 yy头,没有aac数据
		assert(aac_len < ph.capture_len);

		printf("aac len = %d pkt_len = %d\n", aac_len, ph.capture_len);
		assert(aac_data[0] == 0xff && aac_data[1] == 0xf1);
			
		fwrite(aac_data, 1, aac_len, aacfp);

	}

	fclose(fp);
	fclose(aacfp);
	return 0;
}


posted on 2012-11-27 20:24 小小程序员001 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/musicfans/archive/2012/11/27/2819285.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值