qq2005beta1/2/3 c<-->s 协议
sunwang<sunwangme@hotmail.com>
release 1
2005-09-19
说明
======
所有的short long都是网络字节顺序
参考
======
libeva-0.3.2-1
miranda-qq-0.1.0.9
0. 怎么发包
======
0.0 Packet::setPasswordKey ::setSessionKey
0.1 XXXPacket::OutPacket
0.2 XXXPacket重载putBody
0.3 new XXXPacktet(cmd,needAck)
0.4 XXXPacket->fill(buffer,&len) //这里完成所有的header body的填充解密工作
0.5 Net->send(buffer,len)
1. 怎么收包
======
1.0 Net->recv(buffer,len) //如果是tcp,根据buffer的头两个字节进行完整性分析务必取到一个完整的包
1.1 new InPacket(buffer,len)
1.2 new XXXPacket
1.3 XXXPacket->setInPacket(InPacket)
1.4 XXXPacket->parse() //这里调用parseBody
1.5 XXXPacket重载parseBody
2. 命令格式
======
tcpPacketLen:2 //UDP没有此字段;tcp时候,指整个包的长度,用于包的完整性分析;udp不需要此字段,每次都会收到一个完整的包
packetStartTag:1 = 0x02
version:2
command:2
sequence:2 //由包的发起方填充,可以是任意值,应答方必须保证sequence不变
id:4
body[] //QQ_CMD_REQUEST_LOGIN_TOKEN,QQ_CMD_LOGIN不加密或者用特殊方式加密外,其他包的body都用TEA+sessionKey加密
packetEndTag:1 = 0x03
fields comment:
version:
const short QQ_CLIENT_062E = 0x062E; /** client GB QQ2000c build 630 */
const short QQ_CLIENT_072E = 0x072E; /** client En QQ2000c build 305 */
const short QQ_CLIENT_0801 = 0x0801; /** client En QQ2000c build 630 */
const short QQ_CLIENT_0A1D = 0x0A1D; /** client Gb QQ2003c build 0808 */
const short QQ_CLIENT_0B07 = 0x0B07; /** client Gb QQ2003c build 0925 */
const short QQ_CLIENT_0B2F = 0x0B2f; /** client GB QQ2003iii build 0117 */
const short QQ_CLIENT_0B35 = 0x0B35; /** GB QQ2003iii build 0304 (offical release) */
const short QQ_CLIENT_0B37 = 0x0B37; /** QQ20003 */
const short QQ_CLIENT_0C0B = 0x0C0B; /** QQ2004 */
const short QQ_CLIENT_0C0D = 0x0C0D; /** QQ2004 preview*/
const short QQ_CLIENT_0C21 = 0x0C21; /** QQ2004 */
const short QQ_CLIENT_0C49 = 0x0C49; /** QQ2004II */
const short QQ_CLIENT_0D05 = 0x0D05; /** QQ2005 beta1 */
const short QQ_CLIENT_0D51 = 0x0D51; /** QQ2005 beta2 */
3. 命令集合
======
31 commands now,divided to 9 sets
session cmd set:6
======
const short QQ_CMD_REQUEST_LOGIN_TOKEN = 0x0062;
const short QQ_CMD_LOGIN = 0x0022;
const short QQ_CMD_KEEP_ALIVE = 0x0002;
const short QQ_CMD_LOGOUT = 0x0001;
const short QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS = 0x0081;
const short QQ_CMD_CHANGE_STATUS = 0x000D;
account cmd set:8
======
const short QQ_CMD_MODIFY_INFO = 0x0004;
const short QQ_CMD_SEARCH_USER = 0x0005;
const short QQ_CMD_GET_USER_INFO = 0x0006;
const short QQ_CMD_GET_LEVEL = 0x005C;
const short QQ_CMD_REQUEST_KEY = 0x001D; //c <-->c 之间的一些key,如file transfer
const short QQ_CMD_FRIEND_REMARK_OP = 0x003E;
const short QQ_CMD_REQUEST_EXTRA_INFORMATION = 0x0065; //QQ_CMD_GET_USER_INFO不能得到完整信息,扩展信息用这个取
const short QQ_CMD_SIGNATURE_OP = 0x0067; //个人的签名,一般只取在线好友的签名
friend cmd set:6
======
const short QQ_CMD_GET_FRIEND_LIST = 0x0026;
const short QQ_CMD_GET_FRIEND_ONLINE = 0x0027;
const short QQ_CMD_ADD_FRIEND = 0x0009;
const short QQ_CMD_DELETE_FRIEND = 0x000A;
const short QQ_CMD_ADD_FRIEND_AUTH = 0x000B;
const short QQ_CMD_DELETE_ME = 0x001C; //如果我将对方拖到黑名单,协议规定要发这个命令给服务器以要求对方从联系人中删除我
group cmd set:3
======
const short QQ_CMD_GROUP_NAME_OP = 0x003C;
const short QQ_CMD_UPLOAD_GROUP_FRIEND = 0x003D;
const short QQ_CMD_DOWNLOAD_GROUP_FRIEND = 0x0058;
chat cmd set:2
======
const short QQ_CMD_SEND_IM = 0x0016;
const short QQ_CMD_RECV_IM = 0x0017;
qun cmd set:1
======
const short QQ_CMD_QUN_CMD = 0x0030;
phone cmd set:2
======
const short QQ_CMD_CELL_PHONE_1 = 0x0021;
const short QQ_CMD_CELL_PHONE_2 = 0x0029;
system cmd set:2
======
const short QQ_CMD_ACK_SYS_MSG = 0x0012;
const short QQ_CMD_RECV_MSG_SYS = 0x0080;
debug cmd set:1
======
const short QQ_CMD_TEST = 0x0031;
4. 命令解析
======
下面只讨论body,各个字段都是加密前或者解密后的内容.
后面跟了[x]的命令,暂时不解析
4.1 QQ_CMD_REQUEST_LOGIN_TOKEN
======
c -> s : 需要ack,body不加密
null:1 = 0x00
c <- s : body不加密
replyCode:1
tokenLength:1 //现在得到的都是8字节的token
token:tokenLength
fields comment:
replyCode:
const unsigned char QQ_REQUEST_LOGIN_TOKEN_REPLY_OK = 0x00;
4.2 QQ_CMD_LOGIN
======
c -> s : 需要ack,特殊加密方式
iniKey:16 = {
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01
}
encryptedStream:length //是一个416字节的流用iniKey使用TEA算法加密后得到的长度为length字节的流
///416字节的流说明如下//
nullEncryptedStream:16 //用passwordKey(=password'md5'md5)作为128位密码使用TEA算法加密空串得到128位流
unknown:36 = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x4c, 0x28, 0xeb, 0xaa, 0xe9,
0x7f, 0x2a, 0x7c, 0xf9, 0xa9, 0x0f, 0x84, 0xdd,
0xbb, 0xb2, 0x0f, 0x01
}
loginMode:1
unknown:16 = {
0x43, 0xc3, 0x80, 0x19, 0x46, 0x05, 0x3e, 0x4b,
0x91, 0x60, 0x5b, 0xf8, 0x17, 0xd7, 0xf3, 0x5b
}
tokenLength:1
token:tokenLength
unknown:100 = {
0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
}
zeroRest:416-$ = {0} //剩下的用零填充
///
c <- s : //不是用passwordKey就是用iniKey使用TEA加密
replyCode:1
IF replyCode=QQ_LOGIN_REPLY_OK
sessionKey:16
qqNumber:4
userIP:4
userPort:2
serverIP:4
serverPort:2
loginTime:4
unknown:2
fileShareToken:24 //file share token used in Qun disk share file access
unknown:16
clientKey:32 //client key for QQ Home
unknown:12
lastLoginIP:4
lastLoginTime:4
unknown:8
IF replyCode=QQ_LOGIN_REPLY_REDIRECT
qqNumber:4
redirectedServerIP:4
redirectedServerPort:2
IF replyCode=QQ_LOGIN_REPLY_PWD_ERROR
messageError:bodyLength-1 //the messege is encoded by "GB18030"
IF replyCode=QQ_LOGIN_REPLY_MISC_ERROR
unknown: bodyLength-1
fileds comment:
replyCode:
const unsigned char QQ_LOGIN_REPLY_OK = 0x00;
const unsigned char QQ_LOGIN_REPLY_REDIRECT = 0x01;
const unsigned char QQ_LOGIN_REPLY_PWD_ERROR = 0x05;
const unsigned char QQ_LOGIN_REPLY_MISC_ERROR = 0x06;
loginMode:
const unsigned char QQ_LOGIN_MODE_NORMAL = 0x0A;
const unsigned char QQ_LOGIN_MODE_INVISIBLE = 0x28;
4.3 QQ_CMD_LOGOUT
======
c -> s : 不需要ack
passwordKey:16 //password'md5'md5
4.4 QQ_CMD_KEEP_ALIVE
======
每分钟c向s发送一次,可以保证NAT的bind的持久性,并助于无状态服务器跟踪用户的状态
c -> s : 需要ack
id:4
c <- s :
是5个用0x1f结尾的不定长的string
xxxx/0x1f //unknown
xxxx/0x1f //unknown
xxxx/0x1f //onlineUsersNumber
xxxx/0x1f //userIP
xxxx/0x1f //userPort
4.5 QQ_CMD_GET_FRIEND_LIST
======
c -> s : 需要ack
position:2
sorted:1
unknown:1 = 0x00
unknown:1 = 0x01
c <- s :
position:2
friendItem[]:bodyLength-2
fields comment:
position:
const short QQ_FRIEND_LIST_POSITION_START = 0x0000;
const short QQ_FRIEND_LIST_POSITION_END = 0xFFFF;
由于是无状态服务器,每次设定列表的position,从0开始,如果返回了10个好友,请第二次从10开始,直到返回0xffff
sorted:
const unsigned char QQ_FRIEND_LIST_UNSORTED = 0;
const unsigned char QQ_FRIEND_LIST_SORTED = 1;
friendItem:
qqNumber:4
face:2
age:1
gender:1
const unsigned char QQ_FRIEND_GENDER_GG = 0x0;
const unsigned char QQ_FRIEND_GENDER_MM = 0x1;
nicknameLength:1
nickname:nicknameLength
unknown:2
extFlag:1
bit1:qqshow
commonFlag:1
bit1:memeber
bit4:TCP mode
bit5:mobile QQ
bit6:mobile binding
bit7:webcam
4.6 QQ_CMD_GET_FRIEND_ONLINE
======
c -> s : 需要ack
unknown:1 = 0x02
position:1
unknown:3 = 0x00
c <- s :
position:1
friendOnlineItem[]:bodyLength-1
fields comment:
position:
const char QQ_FRIEND_ONLINE_LIST_POSITION_START = 0x00;
const char QQ_FRIEND_ONLINE_LIST_POSITION_END = 0xFF;
由于是无状态服务器,每次设定列表的position,从0开始,如果返回了10个好友,请第二次从10开始,直到返回0xff
friendOnlineItem:
friendStatusItem:
qqNumber:4
unknown:1
ip:4
port:2
unknown:1
status:1
const unsigned char QQ_FRIEND_STATUS_ONLINE = 10
const unsigned char QQ_FRIEND_STATUS_OFFLINE = 20
const unsigned char QQ_FRIEND_STATUS_LEAVE = 30
const unsigned char QQ_FRIEND_STATUS_INVISIBLE = 40
unknown:2
unknownKey:16
unknown:2
extFlag:1
commonFlag:1
unknown:2
ending:1
4.7 QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS
======
c <- s : 需要ack
friendStatusItem
c -> s :
null
4.8 QQ_CMD_CHANGE_STATUS
======
c -> s : 需要ack
status:1
miscStatus:4
const int QQ_MISC_STATUS_HAVING_VIDEO = 0x00000001
c <- s :
replyCode:1
const unsigned char QQ_CHANGE_STATUS_REPLY_OK = 0x30
4.9 QQ_CMD_SEND_IM
======
c -> s : 需要ack
sender:4
receiver:4
version:2
sender:4
receiver:4
fileSessionKey:16
contentType:2
const short QQ_IM_NORMAL_TEXT = 0x000B
sequence:2
sendingTime:4
face:2
content:
unknown:3 = {0x00,0x00,0x00}
font:1 = 0x01 //Has Font Attribute?
unknown:4 = {0x00,0x00,0x00,0x00}
replyType:1
const unsigned char QQ_IM_NORMAL_REPLY = 0x01
const unsigned char QQ_IM_AUTO_REPLY = 0x02
messageString //string without 0x00 at the end
spaceFlag:1 = 0x20
terminateFlag:1 = 0x00
fontFlag:1
const unsigned char fontFlag_bold = 0x20
const unsigned char fontFlag_italic =0x40
const unsigned char fontFlag_underline = 0x80
const unsigned char fontFlag_sizeMask = 0x1f
red:1
green:1
blue:1
terminateFlag:1 = 0x00
encoding:2
const short QQ_IM_ENCODING_GB = 0x8602
fontNameString //just set font name to "Song Ti" in Chinese
EnterFlag:1 = 0x0d
c <- s :
replyCode:
const unsigned char QQ_SEND_IM_REPLY_OK = 0x00
4.10 QQ_CMD_RECV_IM
======
c <- s : 需要ack
version:2
sender:4
receiver:4
fileSessionKey:16
contentType:2
sequence:2
sendingTime:4
face:2
content:
unknown:3 = {0x00,0x00,0x00}
font:1 = 0x01 //Has Font Attribute?
unknown:4 = {0x00,0x00,0x00,0x00}
replyType:1
const unsigned char QQ_IM_NORMAL_REPLY = 0x01
const unsigned char QQ_IM_AUTO_REPLY = 0x02
messageString //string
spaceFlag:1 = 0x20
terminateFlag:1 = 0x00
fontFlag:1
red:1
green:1
blue:1
terminateFlag:1 = 0x00
encoding:2
fontNameString //just set font name to "Song Ti" in Chinese
EnterFlag:1 = 0x0d
c -> s :
null
4.11 QQ_CMD_GET_USER_INFO
======
c -> s : 需要ack
id:4
c <- s :
是37个用0x13结尾的不定长的string
xxxx/0x1e //Info_qqID
xxxx/0x1e //Info_nick
xxxx/0x1e //...
xxxx/0x1e //Info_qqShow
xxxx/0x1e //Info_unknown6
fields commnet:
enum Info_Index{
Info_qqID, Info_nick, Info_country, Info_province, Info_zipcode,
Info_address, Info_telephone, Info_age, Info_gender, Info_name,
Info_email, Info_pagerSn, Info_pagerNum, Info_pagerSp, Info_pagerBaseNum,
Info_pagerType, Info_occupation,Info_homepage, Info_authType, Info_unknown1,
Info_unknown2, Info_face, Info_mobile, Info_mobileType,Info_intro,
Info_city, Info_unknown3, Info_unknown4, Info_unknown5, Info_openHp,
Info_openContact,Info_college, Info_horoscope,Info_zodiac, Info_blood,
Info_qqShow, Info_unknown6
};
4.12 QQ_CMD_MODIFY_INFO
======
c -> s : 需要ack
是39个用0x13结尾的不定长的string,QQ_CMD_GET_USER_INFO的基础上加了密码修改的字段
xxxx/0x1e //currentPassword,不改密码就为null
xxxx/0x1e //newPassword,不改密码就为null
xxxx/0x1e //Info_qqID
xxxx/0x1e //Info_nick
xxxx/0x1e //...
xxxx/0x1e //Info_qqShow
xxxx/0x1e //Info_unknown6
c <- s :
message:bodyLength //如果开头就是id,说明修改成功
4.13 QQ_CMD_SEARCH_USER
======
c -> s : 需要ack
searchType:1
const unsigned char QQ_SEARCH_ALL = 0x31;
const unsigned char QQ_SEARCH_CUSTOM = 0x30;
divider:1 = 0x1f
IF searchType=QQ_SEARCH_ALL
pageString: //string,从0开始的页数index
IF searchType=QQ_SEARCH_CUSTOM
qqNumberString: //可以为null,必须填充0x2D
divider:1 = 0x1f
qqNumberString: //可以为null,必须填充0x2D;如果不是全匹配,必须用0x25结尾
divider:1 = 0x1f
qqNumberString: //可以为null,必须填充0x2D;如果不是全匹配,必须用0x25结尾
divider:1 = 0x1f
pageString: //string,从0开始的页数index
endFlag:1 = 0x00
c <- s :
OnlineUserItem[]:bodyLength //如果开头两个字节是0x2d 0x31说明结束了
fields comment:
OnlineUserItem:
qqNumberString:
divider:1 = 0x1e or 0x1f
nicknameString:
divider:1 = 0x1e or 0x1f
priviceString:
divider:1 = 0x1e or 0x1f
faceString:
divider:1 = 0x1e or 0x1f
divider:1 = 0x1f
4.14 QQ_CMD_GET_LEVEL
======
c -> s : 需要ack
unknown:1 = 0x00
qqNumber[]:bodyLength-1 //one array contains qq id
c <- s :
replyCode:1
LevelUserItem[]:bodyLength-1
fields comment:
LevelUserItem:
qqNumber:4
onelineTime:4
level:2
timeRemainder:2
4.15 QQ_CMD_REQUEST_KEY
======
c -> s : 需要ack
keyType:1
c <- s :
keyType:1
const unsigned char QQ_REQUEST_UNKNOWN_KEY = 0x03
const unsigned char QQ_REQUEST_FILE_AGENT_KEY = 0x04
replyCode:1
const unsigned char QQ_REQUEST_KEY_REPLY_OK = 0x00
IF replyCode=QQ_REQUEST_KEY_REPLY_OK & IF keyType=QQ_REQUEST_FILE_AGENT_KEY
fileAgentKey:16
unknown:12
tokenLength:1
fileAgenttoken:tokenLength
4.16 QQ_CMD_FRIEND_REMARK_OP [x]
======
好友备注设置
c -> s : 需要ack
c <- s :
4.17 QQ_CMD_REQUEST_EXTRA_INFORMATION
======
c -> s : 需要ack
requestType:1 = 0x01
offset:2 //和前面取用户列表一样的用法,从零开始
c <- s :
replyCode:1 = 0x01
offset:2
unknown:1 = 0x03
UserExtraItem[]:bodyLength-4
fields comment:
UserExtraItem:
qqNumber:4
infoFlag:2 //information flags( signature, QQ tang, QQ album, QQ firend making )
const unsigned short QQ_EXTAR_INFO_PAL = 0x0004; // QQ friends center flag
const unsigned short QQ_EXTAR_INFO_USER_HEAD = 0x0010; // QQ customized face flag, QQ calls them user head, because all pictures stored in UserHead directory
const unsigned short QQ_EXTAR_INFO_ALBUM = 0x0100; // QQ ablum flag
const unsigned short QQ_EXTAR_INFO_TANG = 0x0800; // QQ tang flag, is QQ tang a sort of game? not sure what it is.
const unsigned short QQ_EXTAR_INFO_SIGNATURE = 0x4000; // QQ personal signature flag
delimiter:1 = 0x00
4.18 QQ_CMD_SIGNATURE_OP
======
qq个性签名
c -> s : 需要ack
subCmd:1
const unsigned char QQ_SIGNATURE_MODIFY = 0x01
const unsigned char QQ_SIGNATURE_DELETE = 0x02
const unsigned char QQ_SIGNATURE_REQUEST = 0x03
IF subCmd=QQ_SIGNATURE_MODIFY
unknown:1 = 0x00
length:1
signature:length
IF subCmd=QQ_SIGNATURE_DELETE
null
IF subCmd=QQ_SIGNATURE_REQUEST
number:2
signRequestItem<number>: bodyLength-3
c <- s :
subCmd:1
replyCode:1
const unsigned char QQ_SIGNATURE_REQUEST_REPLY_OK = 0x00
IF subCmd=QQ_SIGNATURE_MODIFY
null
IF subCmd=QQ_SIGNATURE_DELETE
null
IF subCmd=QQ_SIGNATURE_REQUEST
signItem[]: bodyLength-2
fields comment:
signRequestItem:
qqNumber:4
unknown:4 = time or 0x00 00 00 01 or 0x00 00 00 02
signItem:
qqNumber:4
lastModifyTime:4
signLength:1
sign:signLength
4.19 QQ_CMD_ADD_FRIEND
======
c -> s : 需要ack
qqNumberString:bodyLength
c <- s :
qqNumberString: //string,my qq id
delimiter:1 = 0x1f
replyCode:1
const unsigned char QQ_AUTH_NO_AUTH = 0x30; //Add Successful
const unsigned char QQ_AUTH_NEED_AUTH = 0x31; //Adding not allowed
const unsigned char QQ_AUTH_NO_ADD = 0x32; //Need authorization,
4.20 QQ_CMD_ADD_FRIEND_AUTH
======
c -> s : 需要ack
qqNumberString:bodyLength
delimiter:1 = 0x1f
subCmd:1
const unsigned char QQ_MY_AUTH_APPROVE = 0x30; //Accept
const unsigned char QQ_MY_AUTH_REJECT = 0x31; //Reject
const unsigned char QQ_MY_AUTH_REQUEST = 0x32; //Request
delimiter:1 = 0x1f
message:bodyLength-$
c <- s :
replyCode: //服务器决定帮助投递,并回了一个ack而已,真正结果在 s-->c QQ_CMD_RECV_MSG_SYS 的包里面
const unsigned char QQ_ADD_FRIEND_AUTH_REPLY_OK = 0x30
4.21 QQ_CMD_DELETE_FRIEND
======
c -> s : 需要ack
qqNumberString:bodyLength
c <- s :
replyCode:1
const unsigned char QQ_DELETE_FRIEND_REPLY_OK = 0x00
4.22 QQ_CMD_DELETE_ME
======
c -> s : 需要ack
qqNumber:4
c <- s :
replyCode:1 //服务器决定帮助投递,并回了一个ack而已
const unsigned char QQ_DELETE_ME_REPLY_OK = 0x00
4.23 QQ_CMD_GROUP_NAME_OP
======
c -> s : 需要ack
subCmd:1
const unsigned char QQ_UPLOAD_GROUP_NAME = 0x2 //upload group name
const unsigned char QQ_DOWNLOAD_GROUP_NAME = 0x1//download group name
IF subCmd=QQ_UPLOAD_GROUP_NAME
groupNameItem[]:bodyLength-1
IF subCmd=QQ_DOWNLOAD_GROUP_NAME
unknown:1 = 0x02
unknown:4 = 0x00 00 00 00
c <- s :
subCmd:1
replyCode:1
const unsigned char = QQ_CMD_GROUP_NAME_OP_REPLY_OK = 0x00 //i can NOT sure
IF subCmd=QQ_DOWNLOAD_GROUP_NAME
unknown:4
groupNameItem[]:bodyLength-6
fields comment:
groupNameItem:
groupIndex:1 //fixed group,ie,the first one,should not upload
groupName:16 //fixed length array,faint ,so ugly
4.24 QQ_CMD_UPLOAD_GROUP_FRIEND
======
c -> s : 需要ack
groupFriendItem[]:bodyLength
c <- s :
replyCode:1
const unsigned char = QQ_CMD_UPLOAD_GROUP_FRIEND_REPLY_OK = 0x00 //i can NOT sure
fields comment:
groupFriendItem:
qqNumber:4
groupIndex:1 //(groupIndex>> 2) & 0x3f is right,at QQ_CMD_DOWNLOAD_GROUP_FRIEND
4.25 QQ_CMD_DOWNLOAD_GROUP_FRIEND
======
c -> s : 需要ack
unknown:1 = 0x01
unknown:1 = 0x02
unknown:2 = 0x00 00
startID:4 //start from 0x00000000
c <- s :
unknown:6
nextID:4 //next query from this ID
groupFriendItem[]:bodyLength-10
4.26 QQ_CMD_QUN_CMD [x]
======
c -> s : 需要ack
c <- s :
fields comment:
const unsigned char QQ_QUN_CMD_CREATE_QUN = 0x01;
const unsigned char QQ_QUN_CMD_MODIFY_MEMBER = 0x02;
const unsigned char QQ_QUN_CMD_MODIFY_QUN_INFO = 0x03;
const unsigned char QQ_QUN_CMD_GET_QUN_INFO = 0x04;
const unsigned char QQ_QUN_CMD_ACTIVATE_QUN = 0x05;
const unsigned char QQ_QUN_CMD_SEARCH_QUN = 0x06;
const unsigned char QQ_QUN_CMD_JOIN_QUN = 0x07;
const unsigned char QQ_QUN_CMD_JOIN_QUN_AUTH = 0x08;
const unsigned char QQ_QUN_CMD_EXIT_QUN = 0x09;
const unsigned char QQ_QUN_CMD_SEND_IM = 0x0A;
const unsigned char QQ_QUN_CMD_GET_ONLINE_MEMBER = 0x0B;
const unsigned char QQ_QUN_CMD_GET_MEMBER_INFO = 0x0C;
const unsigned char QQ_QUN_CMD_MODIFY_CARD = 0x0E;
const unsigned char QQ_QUN_CMD_REQUEST_CARD = 0x10;
const unsigned char QQ_QUN_CMD_SEND_IM_EX = 0x1A;
const unsigned char QQ_QUN_CMD_ADMIN = 0x1B;
const unsigned char QQ_QUN_CMD_TRANSFER = 0x1C;
const unsigned char QQ_QUN_CMD_CREATE_TEMP_QUN = 0x30;
const unsigned char QQ_QUN_CMD_MODIFY_TEMP_QUN_MEMBER = 0x31;
const unsigned char QQ_QUN_CMD_EXIT_TEMP_QUN = 0x32;
const unsigned char QQ_QUN_CMD_GET_TEMP_QUN_INFO = 0x33;
const unsigned char QQ_QUN_CMD_SEND_TEMP_QUN_IM = 0x35;
const unsigned char QQ_QUN_CMD_GET_TEMP_QUN_MEMBERS = 0x37;
4.27 QQ_CMD_CELL_PHONE_1 [x]
======
c -> s : 需要ack
c <- s :
4.28 QQ_CMD_CELL_PHONE_2 [x]
======
c -> s : 需要ack
c <- s :
4.29 QQ_CMD_ACK_SYS_MSG [x]
======
c -> s : 需要ack
c <- s :
4.30 QQ_CMD_RECV_MSG_SYS
======
c <- s : 需要ack
typeString/0x1f:
const short QQ_MSG_SYS_BEING_ADDED = 1 //being added by other party
const short QQ_MSG_SYS_ADD_FRIEND_REQUEST = 2 //Somebody request me
const short QQ_MSG_SYS_ADD_FRIEND_APPROVED = 3 //Accept my request
const short QQ_MSG_SYS_ADD_FRIEND_REJECTED = 4 //I was rejected
const short QQ_MSG_SYS_UPDATE_HINT = 9 //faint,server update message
fromQQNumberString/0x1f:
toQQNumberString/0x1f:
message:bodyLength-$
c -> s :
null
4.31 QQ_CMD_TEST [x]
======
c -> s : 需要ack
c <- s :