QQ协议体系概述
1. QQ协议体系概述
2. 请求登录令牌
3. 登录
4. 改变状态
5. 得到好友列表
6. 得到在线好友
7. Keep Alive
8. 得到用户资料
9. 登出
10. 查用户
QQ的协议非常庞大,这些做一些概述,要注意,不要认为下面的说法一定是对的,只能说目前看起来好像是这样:
加密解密
QQ的加密解密用的是TEA算法(puzzlebird的说法),不详细解释了。QQ的包一般都是加密的(包头包尾除外),但是有个别包是不加密的,以后如果不做特别说明,则默认这个包是需要加密的。此外,用什么密钥加密也有不同,不过基本上都是用会话密钥加密,以后如果不做特殊说明,表示是用会话密钥加密。这里要注意一下,有时候你收到的包可能不是用会话密钥加密的,比如离线的消息。你人都不在了,哪里来的会话密钥?所以服务器在你下次登录的时候,会把你还没收到过的消息用密码密钥加密再传给你。这是一种特殊的情况,要分清楚。
UDP和TCP
QQ支持UDP和TCP登录(如果使用HTTP代理,则相当于TCP登录),UDP登录没有什么好说的,TCP登录时,不管什么包的开头两字节都是包长度,这个长度包括了这两个字节。
包头包尾
QQ协议有多种包头,每种包头都分别代表了一类用途的包,但是不是所有的包都有包尾,以下是一些
存在的包头包尾格式参考
包头包尾包头之后的固定格式说明
0x00 无发送方QQ版本,或者是服务器版本,2字节
随机密钥,1字节,如果这个字节是0x23,那么密钥就是0x23232323,这个密钥用来加密发送者和接受者的QQ号。加密算法: QQ号取反再与密钥异或
发送者QQ号的加密形式,4字节
接受者QQ号的加密形式,4字节
0x00系列的包,用在文件传输过程中,传递控制信息。也会出现在点对点通信中。
0x02 0x03 源标志, 2字节,表示了这个包从何处来,主要用来标识客户端版本,如果其标识的是服务器,这个字段的具体用处还不清楚
包命令, 2字节
包序号, 2字节, 原则是保证短期内这个序号不要重复就可以,一般我们处理的时候都是递增,到最大再
归0
0x02系列包主要完成一些基本任务,基本上处理了这个系列的包,QQ的功能就差不多了。
0x03 无格式同第一行0x03系列的包,用在文件传输过程中,传递数据信息
0x04 0x03 客户端版本号,2字节
整个的包长,2字节
序号,2字节
我的QQ号,4字节
未知的8字节
0x04系列的包,用在文件传输过程中,如果使用服务器中转模式传送文件,则用到这些包0x06 未知未知还没怎么研究过这种包是干什么的
请求登录令牌
登录QQ,要发的第一个包就是Request Login Token Packet。这个包会向服务器请求一个24字节大小的令牌(也不一定是24,只能说目前是24字节),然后在接下来的登录中,没有这个令牌,你是登录不了的。这个令牌是在服务器端生成的,具体的生成算法我们当然还无从得知,但是它肯定是参考了你的IP,你的端口,还有你的其他什么信息生成这个令牌的。因为你把在A机器上得到的令牌用到B机器上,你就会登录不了,如果你把A 机器上的IP给改了,你照样也登录不了。
请求包格式
头部
未知的1字节,0x00
尾部
Note: 此包不加密
回复包格式
头部
回复码,1字节,0x00表示成功
登录令牌长度,1字节
登录令牌
尾部
Note: 此包不加密
成功时
操作成功时,核心层会触发QQ_GET_LOGIN_TOKEN_SUCCESS事件,这个事件携带的
包是RequestLoginTokenReplyPacket,可用的字段如下:
replyCode: byte,回复码
登录
QQ登录目前有多种模式,比如普通QQ号,登录,绑定手机号登录,还有什么普通模式,TM模式。目前我们只支持普通模式和QQ号登录。
请求包格式
头部
初始密钥,16字节
用户的密码密钥加密一个空串得到的16字节
36字节的固定内容,未知含义
登录状态,隐身登录还是什么,1字节
16字节固定内容,未知含义
登录令牌长度,1字节
登录令牌
登录模式,1字节,目前只支持普通模式
未知1字节,0x40
后面段的个数,1字节,1个段9字节(猜测)
段,每次基本都是固定内容,未知含义
长度不足则全部填0直到符合登录包长度,UDP模式登录请求包长度为416字节
尾部
Note: 此包使用初始密钥加密,注意头部之后就是初始密钥,初始密钥是不加密的。
说明
初始密钥在2004以前用的是一个固定值: 16个0x01字节。2004之后,采用随机密钥
密码密钥是通过对用户的密码进行2次MD5生成的
密码密钥加密一个空串是干什么呢?主要是服务器用来验证密码的,如果服务器能用密码密钥解开这16个字节,那么它就认为密码是正确的。在这里,我们不一定非要加密一个空串,其实任意字符串都可以,但是你要保证密文只有16个字节。
登录请求包的固定内容,含义是未知的,而且,也不能说内容是固定的,即使我们把这些字段全部替换成0,依然能够登录
回复包格式
回复包可能有多种情况,包体的第一个字节是回复码,可能的取值如下:
QQ_LOGIN_REPLY_OK: 登录成功
QQ_LOGIN_REPLY_REDIRECT: 重定向
QQ_LOGIN_REPLY_PASSWORD_ERROR: 密码错误
QQ_LOGIN_REPLY_OK:
头部
回复码,1字节
会话密钥,16字节
用户的QQ号,4字节
用户的IP,4字节
用户的端口,2字节
服务器的IP,4字节
服务器的端口,2字节
本次登录时间,4字节
未知的26字节
未知服务器1的IP,服务器的作用未知,4字节
未知服务器1的端口,2字节
未知服务器2的IP,4字节
未知服务器2的端口,2字节
2个未知字节
2个未知字节
好qq号
Client Key, 32字节
12个未知字节
上次登录时的IP,4字节
上次登录时的时间,4字节
8个未知字节
尾部
QQ_LOGIN_REPLY_REDIRECT:
头部
回复码,1字节
用户QQ号,4字节
重定向的服务器IP,4字节
重定向的服务器端口,2字节
尾部
QQ_LOGIN_REPLY_PASSWORD_ERROR:
头部
回复码,1字节
错误消息字符串
尾部
Note: 登录回复包使用密码密钥加密,或者使用初始密钥加密,在处理时,应该先尝试使用密码密钥解密,如果失败,则再用初始密钥解密。为什么要这样呢,因为你可能密码输入错误,这样的话服务器用密码密钥加密的包你就解密不了了,所以会用初始密钥加密。
说明
时间都是4个字节,其表示从1970-1-1开始的毫秒数再除以1000
Client Key是用在访问一些网络服务时,比如QQ秀,通过Client Key,TX可以直接定位到你的QQ秀页面,还有什么QQ家园啦,有可能聊天室也要用到这个。
那些未知的服务器,可能是用来发广告用的,猜想~
成功时
登录成功或者重定向时,核心层会触发QQ_LOGIN_SUCCESS事件,这个事件携带的包是LoginReplyPacket,用户应该检查replyCode的值,然后进行相应的操作:
当replyCode为QQ_LOGIN_REPLY_OK时,有以下字段可用:
sessionKey: byte[],会话密钥
ip: byte[],用户IP
port: int, 用户端口
serverIp: byte[], 服务器IP
serverPort: int, 服务器端口
loginTime: int, 本次登录时间
lastLoginTime: int, 上次登录时间
clientKey: byte[], Client Key
当replyCode为QQ_LOGIN_REPLY_REDIRECT时,有以下字段可用:
redirectIp: byte[], 重定向到的服务器IP
redirectPort: int, 重定向到的服务器端口
重定向到零地址时
在登录高峰期,登录重定向时有可能得到一个0地址,这时核心层会触发
QQ_LOGIN_REDIRECT_NULL事件,这个事件携带的包是LoginReplyPacket,不过这个包没有什么可用信息。
密码错误时
如果密码错误,核心层会触发QQ_LOGIN_PASSWORD_ERROR事件,这个事件携带的包是LoginReplyPacket,可用的字段如下:
replyMessage: String, 错误信息字符串
未知错误时
如果回复码不是以上三种,则核心层会触发QQ_LOGIN_UNKNOWN_ERROR事件,这个事件携带的包是LoginReplyPacket,但是没有可用字段。
改变状态
登录之后的第一件事就是切换自己的状态,不然你发不出消息,这个一定要注意了,不是登录成功之后就能发消息,而是改变状态之后才能发消息。LumaQQ核心层自动处理登录后的状态改变,所以基本上你可以不管状态改变的事件,就看你的需要了。
请求包格式
头部
想要切换到的状态,1字节,定义如下
QQ_FRIEND_STATUS_ONLINE: 在线
QQ_FRIEND_STATUS_OFFLINE: 离线
QQ_FRIEND_STATUS_AWAY: 离开
QQ_FRIEND_STATUS_HIDDEN: 隐身
是否显示虚拟摄像头,4字节,最低位置1表示显示虚拟摄像头,其他位似乎无用