[微信協議分析] 文本消息


聲明:微信客戶端協議是二進制協議而且加密,難以分析協議具體編碼格式,我不做逆向工程。只是簡單抓包分析業務的實現流程,在這里記錄下來用於參考學習,並不是破解協議。

 

參考:

微信協議簡單調研筆記

微信破解研究總結

Sync協議

  道聽途說,加上上面參考中都是提到微信使用Sync協議。去年項目中因此也嘗試參考 Microsoft Exchange ActiveSync 協議來優化消息協議,實現過程中才發現Sync並不是表面上那么簡單。

  Sync 有啥問題呢?

  1. SyncKey 生成維護成本

    SyncKey 在ActiveSync中為字符串,客戶端不需要解析,但服務端實現要用數字自增,需要強一致性,且不能回退。

  2. 消息的訂閱模式 采用類似Zookeeper的One time triggler 還是每條消息都推送一條通知能

    One time trigger能夠避免並發通知時,獲取消息時重復問題,但增加了交互成本,和客戶端實現復雜性。

  3. 自己發的消息,SyncKey怎么獲取

    尤其要支持多端同步發消息,保證消息同步;也只好消息發完在給自己同步一遍(自己設備發的可以不帶消息體)

  4. 消息推送延時加重

    Sync 消息體獲取方式:Notify - Ack - get - Mssage, 也就是至少第四個應用包才能返回消息,在移動網絡下成本很高。文中提到消息通過單獨https請求,那么延時更為嚴重了(嗯,實測新版本並非如此)。

 

手機客戶端不再Sync協議

抓包分析版本:Android 微信6.0, 抓包分析可參考:android 移動網絡實時抓包

在wifi、gprs網絡狀況下都相同,客戶端會依次嘗試使用80、8080、443 端口連接服務器;消息發送、接收都使用長連接進行.

 

協議格式:

4byte Packet Len(包含4字節本身)

2byte Head Len(包含2字節本身) + 2byte Version(1) + 4byte Operation + 4byte SeqId + ….

(Packet Len - Head Len)  Body

 

協議交互方式:

- 客戶端請求(一應一答,通過seqid匹配):

      seqid = 1 開始,依次遞增,服務器回復相同的seqid 作為應答

- 服務器推送通知(單向):

     seqid = 0,Operation = 7a,  客戶端不需要應答

 

主要業務:

-心跳包:

     發起客戶端請求,Operation = 0c,長度為16字節,算是最小的包

-發消息:

     發起客戶端請求,Operation = ed

  單點在線時發完消息后,應答攜帶SyncKey,不再同步,多點在線時,通過通知同步SyncKey,將隨后文章分析。

-收消息:

     1. 服務器推送消息, Operation = 7a,  Body 中攜帶消息內容

    抓包分析時,通過改變消息體大小,可能到接收方第一個包length 也會隨之變化,可確認消息是push的。

     2. 發起客戶端單向請求,即消息的應答Ack

-加密:

     未分析,一般來說像whatsapp那樣,使用 hash(密碼/OTP + 長連接第一個請求獲取RandomCode) 做RC4 的密鑰

 總結:

現在版本的微信消息推送,並非Sync方式,而是推送+ack方式;

從他們協議設計來看,應該最開始用的是Notify + Sync Req + Sync Rsp 方式,因為協議上是不支持服務器發起請求的;

現在改成 Sync 消息+ 單向 Ack Req 的push方式,雖然協議上怪異, 相比Sync 消息接收會更加及時。從以前看到的文章都是說用的Sync協議,應該是是后期版本做了修改,push方式更為高效、而且通過順序的SyncKey也能夠修復丟失的消息。

下面一個通知包示例:

4 byte: 265 PacketLen

2 byte: 16   Head Length

2 byte: 1     Protocol Version

4 byte: 7a   Operation

4 byte: 0   SeqId //這是一個通知性消息

4 byte: unknow header

 

……. Body

 

Web客戶端

  web微信客戶端使用比較標准的Sync協議,Sync協議也比較適合web長輪詢模型。

  移動客戶端模式下,協議是二進制的而且有加密,很難分析;微信側重手機端,web端主體協議應該保持與移動端一致,可通過web端推測整體協議實現,json也比較好分析。

  接收一條消息后SyncKey變化: 

  synckey:1_624161340|2_624162225|3_624162051|11_624161867|201_1420112604|1000_1420104656
  synckey:1_624161340|2_624162226|3_624162051|11_624161867|201_1420112631|1000_1420104656

  可以看出:

  - Synckey 有多個,應該是應對不同業務,其中2為為所有個人消息、討論組消息,其他可能是聯系人、朋友圈、訂閱號等,201 為當前時間戳。

  - SyncKey 的值為數字自增,不是從0開始,應該有個固定的初始值。  

  - 發消息時,發完需要自己給再自己同步回一下SyncKey。

- 下面是一條消息增量同步結構,一堆要同步字段+是否修改FlagMask,同步協議變得很簡潔。

{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
}
,
"AddMsgCount": 1,
"AddMsgList": [{
"MsgId": 1625734358,
"FromUserName": "@sssss",
"ToUserName": "@ssssssss2",
"MsgType": 1,
"Content": "捶地笑……",
"Status": 3,
"ImgStatus": 1,
"CreateTime": 1420109883,
"VoiceLength": 0,
"PlayLength": 0,
"FileName": "",
"FileSize": "",
"MediaId": "",
"Url": "",
"AppMsgType": 0,
"StatusNotifyCode": 0,
"StatusNotifyUserName": "",
"RecommendInfo": {
"UserName": "",
"NickName": "",
"QQNum": 0,
"Province": "",
"City": "",
"Content": "",
"Signature": "",
"Alias": "",
"Scene": 0,
"VerifyFlag": 0,
"AttrStatus": 0,
"Sex": 0,
"Ticket": "",
"OpCode": 0
}
,
"ForwardFlag": 0,
"AppInfo": {
"AppID": "",
"Type": 0
}
,
"HasProductId": 0,
"Ticket": ""
}
],
"ModContactCount": 0,
"ModContactList": [],
"DelContactCount": 0,
"DelContactList": [],
"ModChatRoomMemberCount": 0,
"ModChatRoomMemberList": [],
"Profile": {
"BitFlag": 0,
"UserName": {
"Buff": ""
}
,
"NickName": {
"Buff": ""
}
,
"BindUin": 0,
"BindEmail": {
"Buff": ""
}
,
"BindMobile": {
"Buff": ""
}
,
"Status": 0,
"Sex": 0,
"PersonalCard": 0,
"Alias": "",
"HeadImgUpdateFlag": 0,
"HeadImgUrl": "",
"Signature": ""
}
,
"ContinueFlag": 0,
"SyncKey": {
"Count": 6,
"List": [{
"Key": 1,
"Val": 624161340
}
,{
"Key": 2,
"Val": 624162166
}
,{
"Key": 3,
"Val": 624162051
}
,{
"Key": 11,
"Val": 624161867
}
,{
"Key": 201,
"Val": 1420109883
}
,{
"Key": 1000,
"Val": 1420104656
}
]
}
,
"SKey": ""
}

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM