• [微信协议分析] 文本消息


    声明:微信客户端协议是二进制协议而且加密,难以分析协议具体编码格式,我不做逆向工程。只是简单抓包分析业务的实现流程,在这里记录下来用于参考学习,并不是破解协议。

    参考:

    微信协议简单调研笔记

    微信破解研究总结

    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": ""
    }

      

  • 相关阅读:
    企业级Nginx负载均衡与keepalived高可用实战(一)Nginx篇
    Elasticsearch由浅入深(十一)内核原理
    Elasticsearch由浅入深(十一)索引管理
    Elasticsearch由浅入深(十)搜索引擎:相关度评分 TF&IDF算法、doc value正排索引、解密query、fetch phrase原理、Bouncing Results问题、基于scoll技术滚动搜索大量数据
    Elasticsearch由浅入深(九)搜索引擎:query DSL、filter与query、query搜索实战
    Elasticsearch由浅入深(八)搜索引擎:mapping、精确匹配与全文搜索、分词器、mapping总结
    Elasticsearch由浅入深(七)搜索引擎:_search含义、_multi-index搜索模式、分页搜索以及深分页性能问题、query string search语法以及_all metadata原理
    Elasticsearch由浅入深(六)批量操作:mget批量查询、bulk批量增删改、路由原理、增删改内部原理、document查询内部原理、bulk api的奇特json格式
    Elasticsearch由浅入深(五)_version乐观锁、external version乐观锁、partial update、groovy脚本实现partial update
    Elasticsearch由浅入深(四)ES并发冲突、悲观锁与乐观锁、_version乐观锁并发
  • 原文地址:https://www.cnblogs.com/lulu/p/4199544.html
Copyright © 2020-2023  润新知