QQ2006 正式版协议分析
本篇文章源自《黑客防线》2007年10月刊
转载请注明版权
作者:BinSys
===================================
本文的分析环境为Windows XP SP2 +QQ2006正式版+Snifer Portable 4.7.5,所有涉及代码部分均为VB描述,因为啥?别的不会了!嘻嘻。
QQ协议概述
QQ基本通信协议是一套基于TCP/IP协议的自己开发的二进制应用层网络协议,基本通信协议支持TCP以及UDP两种方式,而且两种方式基本数据结构大同小异,只是TCP多了一个数据包(以下简称包)长度描述的头部信息。QQ基本协议采用应答机制,也就是说发送的每一个包服务器都会回应对应的包的执行结果,服务器发送的每一个包你也要回应(登出包除外,它没回应。),这也许是因为UDP协议的不可靠性吧。
QQ协议的加密算法
QQ基本协议目前为止用了两种公开的加密算法:十六轮的TEA(Tiny Encryption Algorithm)和MD5。QQ密码密钥是先把QQ密码的字符串用MD5加密得到一个16字节的Byte数组,再把此数组作为明文用MD5加密,之后得到的是一个16字节的Byte数组备用,即QQ密码密钥。(在这里我可吃了好多苦,网上的都说是二次MD5,我就把密码的一次MD5 结果作为字符串再用MD5加密,怎么都和QQ2006的加密结果不一样,后来看了IceQQ的加密类才发现的。)TEA是一种小型的可逆加密算法,但是TX使用了独特的填充反馈机制。如果整个明文不是8的倍数,还要在头部用16字节的随机数填充,然后在尾部补0,直到是8的倍数。QQ数据明文被分成N个8字节的明文单元,用TEA以16字节的密钥依次加密每个单元产生8字节的密文,密文再参与下一单元的加密。由于头部填充了随机字节,所以即使是同一明文的密文,也会因随机数的不同以及反馈机制而不同。
QQ协议分析
QQ的大体登录过程用VB伪代码可以表示如下(0X0091等为命令编号,将在下文解释)。注意,本文各命令测试用的QQ号不同,所以包内QQ号字段会变化,但不影响协议分析。
Step0 QQ客户端→(0X0091)→服务器
Step1 QQ客户端←(0X0091)←服务器
If 0X0091返回=可以请求登录令牌 Then
Goto Step3
Else
更新返回的服务器地址
Goto Step0
End If
Step3 QQ客户端→(0X00BA)→服务器
Step4 QQ客户端←(0X00BA)←服务器
Step5 QQ客户端→(0X0022)→服务器
Step4 QQ客户端←(0X0022)←服务器
Select Case 0X0022返回
Case 登录成功
Goto Step5
Case 重定向
更新返回服务器地址
Goto Step3
Case 密码错误
重新输入密码
Goto Step3
End Select
Step5 QQ客户端→(0X0002)→服务器
然后就是一遍一遍的发送心跳包(告诉服务器自己在线),想登出时发送登出包。
QQ0F5F协议发送包头部为:“02 0F 5F 00 BA 17 4A 25 DF 2F BF”,共10字节,分别表述如下。
第0字节:02;基本包标志;1字节,固定。
第1~2字节:0F 5F;协议版本;2字节,根据协议版本变化。
第3~4字节:00 BA;命令;2字节,根据不同命令变化,请求包与对应的回复包这里相同。
第5~6字节:17 4A;包序列号;2字节,请求包与对应的回复包这里相同。
第7~10字节:25 DF 2F BF;QQ号的十六进制。我的测试号码为635383743;4字节。
QQ0F5F协议接收包头部与发送包相比少了第7~10字节,第1~2字节有时为“0X01 0X00”,其他不变,共7字节。发送包与对应的回复包命令和序列号相同。所有包尾为0X03,1字节。服务器与客户端各自有自己的包序列号,各自先初始一个,然后每次发一个包用当前序列号,再将序列号加1以备下次使用。当为0XFFFF时清零(0X0000)。例如客户向服务器发送命令0X0091,包序列号为0X0410,那么服务器返回命令为0X0091、包序列号为0X0410的包。下一次客户又发了一个命令为0X00BA、包序列号为0X0410+1,即0X0041的包,服务器返回命令为0X00BA、包序列号为0X0041的包的处理结果。下面是我的测试包,具有唯一性。
(1)0X0091命令(客户端发起)
这是TX新加入的,我理解的意思是询问服务器本QQ号是否可以在这里登录,因为TX的服务器有很多,不只是有域名的那几个,可能分管不同版本、不同地区、不同号段。具体格式如下。
发送:
02 0F 5F 00 91 01 11 25 DF 2F BF 75 60 28 8C 5D 30 74 A7 52 50 12 CE 54 C2 00 47 05 E6 E2 C6 23 3A B3 F7 51 9D A7 EC CA 28 0B B5 C4 B1 12 88 A4 01 DC 01 E0 D7 25 6F 5F F3 E0 E6 03
共计60字节,其中第0~10字节为头部,11字节;第11~26字节为初始密钥(随机产生),16字节;第27~58字节为加密的数据,32字节;第59字节为包尾,1字节。密文(第27~58字节)用初始密钥解密后为“00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00”,共16个 0X00,这个固定。
服务器返回1:
02 0F 5F 00 91 01 11 C8 38 BD F8 EA 72 0D 1C 1C F0 E7 72 39 F6 84 F2 03
共24字节,其中第0~6字节为头部,7字节;第7~22字节为加密的数据,16字节;第23字节为包尾,1字节;密文(第7~22字节)用初始密钥解密后为“00 00”,表示可以发送请求登录令牌的包。
服务器返回2:
02 0F 5F 00 91 01 11 E3 0E 5A 51 8F F9 96 B4 6E DE A2 9F 9E EC AB 0D 93 4E 24 92 32 18 B4 A2 97 42 06 EF FE D4 F5 58 03
共40字节,其中第0~6字节为头部,7字节;第7~38字节为加密的数据,32字节;第39字节为包尾,1字节。密文(第7~38字节)用初始密钥解密后为“00 01 01 00 00 00 01 00 00 00 00 DB 85 3E 08”,共15字节。密文中的第0~10字节固定,我们可以凭借第0和第1字节是否全是0来判断是否需要重定向新的服务器;第11~14字节为服务器分配的新服务器地址,本例为219.133.62.8(按位转换成十进制),这是告诉客户端重新去另一个服务器试试(再向新的服务器发送0X0091命令)。
(2)0X00BA命令(客户端发起)
这个是请求登录令牌命令,格式如下。
发送:
02 0F 5F 00 BA 01 12 25 DF 2F BF 75 60 28 8C 5D 30 74 A7 52 50 12 CE 54 C2 00 47 3D E8 6D C2 D9 0F AA FB C8 F7 4E 4E 59 84 7F 56 58 7A A6 EB 0D 75 1D 05 03
共52字节,其中第0~10字节为头部,11字节;第11~26字节为本包密钥,16字节;第27~50字节为加密的数据,24字节;第51字节为包尾,1字节。密文用初始密钥解密后为“01 00 05 00 00 00 00”,固定,共7字节。
服务器返回:
02 0F 5F 00 BA 01 12 2B 64 8C 2A 0F 89 7D 4C 38 19 84 3D 34 63 7E F1 15 5B 2E 8F 70 F0 74 16 1D 41 7B 7D A1 29 0E CD D5 3E B8 37 2D 8C 1B 82 56 94 D3 BC 07 FD 12 4A 03
共56字节,其中第0~6字节为头部,7字节;第7~54字节为加密的数据,48字节;第55字节为包尾,1字节。密文用本包密钥解密后为“01 00 05 00 00 20 66 86 D4 BC 1E 0A C1 56 D0 00 FE E6 50 3B C5 60 FF 5B 79 20 7E F9 E2 42 02 7C 78 98 33 BA 55 C7”,共38字节。密文的第0~4字节固定为“01 00 05 00 00”,5字节;第5字节“20”为登录令牌长度(十六进制),1字节;第6~37字节为登录令牌,32字节。
(3)0X0022命令(客户端发起)
这个是请求登录的命令,里面有一些你的密码等数据。
发送:
02 0F 5F 00 22 F2 22 25 DF 2F BF 72 5D 24 89 5A 2D 71 ……省略,详见杂志相关……AF A2 90 A8 1A 19 1E E4 F7 03
共492字节,其中第0~10字节为头部,11字节;第11~26字节为本包密钥,16字节;第27~490字节为加密的数据,24字节;第491字节为包尾,1字节。密文用本包密钥解密后如下:
7E B4 7B D3 95 89 78 5B 60 21 2……省略,详见杂志相关……00 00 00 00 00 00 00 00 00 00 00 00
共448字节,其中第0~15字节表示用TEA以密码二次MD5的结果为密钥加密一个只有1字节00的数组,服务器只看能否解密,并不关心内容。第16~50字节固定,含义未知。第51字节每天都变化,规则未知,但用06总是可以登录成功。第52字节为登录模式,正常是0X0A,隐身是0X28。第53~77字节固定,全00,含义未知。第78~93字节固定,含义未知。第94字节固定,为登录令牌长度(十六进制)。第95~126字节为登录令牌。
服务器返回登录成功:
02 0F 5F 00 22 F2 22 1A 94 D7 61 7C A……省略,详见杂志相关……69 AA DA 89 94 BC 1D 03
共208字节,其中第0~6字节为头部;第7~206字节为加密的数据;第207字节为包尾。密文用QQ密码二次MD5作密钥解密后如下:
00 4B 67 32 58 67 50 6A 69 65……省略,详见杂志相关……00 00 00 00 00 00 00 00 00 00 00 00 08 02 04 08 08 08 08 08 04 02
共184字节,第0字节为0x00表示登录成功,我们依靠这个字节判断登录成功与否。第1~16字节为会话的SessionKey,以后所有的包的解密密钥都是它。第17~20字节为登录的QQ 号。第21~24字节为服务器测试到的你的IP。第25~26字节为服务器测试到的你的端口。第27~30字节为服务器测试到自己的你的IP。第31~32字节为服务器测试到自己的你的端口。第33~36字节为本次登录时间,从1970-1-1开始的毫秒数,计算时应乘以1000。第37~38字节未知。第39~62字节为认证令牌,作用未知。第63~66字节为未知服务器IP1。第67~68字节为未知服务器端口1。第69~72字节为未知服务器IP2。第73~74字节为未知服务器端口2。第75~82字节未知。第83~114字节为client key,用于登录QQ家园之类的地方。第115~126字节未知。第127~130字节为上次登录IP。第131~134字节为上次登录时间,计算时应乘以1000。第135~183字节未知。
服务器返回重定向(其实只要用了0x0019命令得到了正确的服务器地址,则一般不会出现这个回复):
02 0F 5F 00 22 F2 22 30 11 CA F4 53 30 1A A6 4A 53 21 93 DB 6A E5 F2 50 77 59 2A FA 70 38 21 0E 50 51 7C 90 89 53 63 03
共40字节,第0~6字节为头部;第7~38字节为加密的数据