微信App通信协议案例学习参考指南

本文介绍了一个基于微信Android App通信协议的项目MicroChat,探讨了微信如何通信,包括使用的端口、域名、协议分析等,并提供了MicroChat的基础功能扩展思路,适合学习者了解微信通信协议。
摘要由CSDN通过智能技术生成

在1月初无意看到某微信爱好者学习交流群里发现讨论一个名为 MicroChat (基于Mars)利用微信AndroidAPP客户端通讯协议代码!!, 震惊之余,已对作者膜拜。心情激动之下下载了下来,参考了一些文章对原始版本进行了部分修正和应用测试。 后测试增加了一些功能实现以及对扩展模拟任意设备方式登入验证,和特定功能处理的思路。本人能力有限,技术很菜很水,但是秉着对技术向往以及分享我的坑给更多的学习者科普了解,故整理编辑了一篇文章带领大家先一睹为快。并且对MicroChat基础功能做了一些扩展思路,如果有错误的地方,欢迎批评指正 !

Wechat Protocol

准备工作

开发环境:

开发工具: Visual Studio 2015 及以上版本(marsWin32SDK 需要vc140以上)
抓包和分析工具: Wireshark / Fiddler / Charles、TCPDump
编译配套: Boost 、 ATL

附加目录

如果本机缺少引用目录请手动附加

其他提醒

编译顺序为: Mars相关依赖 / SQLite3 -> MicrochatSDK(基于Mars Win32 Example) -> MicroChat(用户层)

微信是如何通信的呢?

端口

经过使用扫描器对微信常用域名的扫描发现远程端口有: 80 443 8080 5222 5223 5228 等

域名

dns.weixin.qq.com
support.weixin.qq.com    80/8080
short.weixin.qq.com        443/8080 (sz)
long.weixin.qq.com          80/443 (sz)
wx.qlogo.cn                    80
timg.cn  等

基本执行过程概况

  1. 程序启动后,优先尝试DNS解析特定域名(上述域名,会返回所有节点);
dns查询
dns.weixin.qq.com
返回一组IP地址long.weixin.qq.com
返回一组IP地址,本次通信中,微信使用了最后一个IP作为TCP长连接的连接地址。
http://dns.weixin.qq.com/cgi-bin/micromsg-bin/newgetdns?uin=0&clientversion=620888113&scene=0&net=1
用于请求服务器获得最优IP路径。服务器通过结算返回一个xml定义了域名:IP对应列表。
仔细阅读,可看到微信已经开始了国际化的步伐:香港、加拿大、韩国等。
具体文本,请参考:https://gist.github.com/yongboy/9341884
获取到long.weixin.qq.com最优IP,然后建立到101.227.131.105的TCP长连接
  1. 如果DNS查询不可用,程序转为使用hardcode的ip链接服务;
  2. 如果dns可用,返回的ip为根据ISP智能解析的结果,程序使用返回的ip链接服务;
  3. 程序在注册、验证、解封、小程序等内置内容请求、阶段会使用https链接,加密协议为腾讯的mmtls;
  4. 客户端使用tcp 80/8080连接远端服务器。80/8080两个端口同时或任何单独一个,均可提供服务;
  5. 80端口为短链接,8080为长链接, 程序会优先使用8080端口;
请求确认连接后获取数据。
提交请求中包含 账号 密码 登录方式(可以模拟任何设备~) 设备信息 网络信息 网络设备信息 地理位置 等~

POST http://short.weixin.qq.com/cgi-bin/micromsg-bin/getprofile HTTP/1.1  (application/octet-stream)
返回一个名为“micromsgresp.dat”的附件,个人信息

POST http://short.weixin.qq.com/cgi-bin/micromsg-bin/whatsnews HTTP/1.1  (application/octet-stream)
仍然返回一个名为“micromsgresp.dat”的附件
大概是微信新版本介绍和验证之类的、资讯、订阅更新等...

POST http://short.weixin.qq.com/cgi-bin/micromsg-bin/downloadpackage HTTP/1.1  (application/octet-stream)
输出为micromsgresp.dat文件
然后还会再有一个 micromsgresp.dat创建,大概可能是未读消息和联系人列表吧
(测试大概总共会有1~3次)

GET http://support.weixin.qq.com/cgi-bin/mmsupport-bin/reportdevice?channel=34&deviceid=A952001f7a840c2a&clientversion=620888113&platform=0&lang=zh_CN&installtype=0 HTTP/1.1
客户端自身信息和设备信息校验反馈
POST http://short.weixin.qq.com/cgi-bin/micromsg-bin/reportstrategy HTTP/1.1  (application/octet-stream)
返回分块数据
GET http://wx.qlogo.cn/mmhead/Q3auHgzwzM7NR4TYFcoNjbxZpfO9aiaE7RU5lXGUw13SMicL6iacWIf2A/96
图片等一些静态资源都会被分配到wx.qlogo.cn域名下,会根据加载或手动访问情况异步多线程下载缓存在本地目录。
  1. 当连续2次心跳发送失败时,客户端会弹出提示“当前网络状况不好,是否提交反馈数据”,确认后客户端试图通过web提交反馈数据;
心跳频率约为5分钟

登陆之后,会建立一个long.weixin.qq.com的HTTP长连接,端口号为8080

具体查看长连接初始数据通信,没有发现任何包含"HTTP"字样的数据,以为是微信自定义的TCP/HTTP通信格式。据分析,用于可能用于获取数据、心跳交换消息等用途吧。

初始消息传输
个人资料、离线未阅读消息部分等通过 POST HTTP短连接单独获取。如上..

二进制简单分析
抽取微信某次HTTP协议方式通信数据,16进制表示,每两个靠近的数字为一个byte字节。

协议文本分析

微信协议报文可能如下:

一个消息包 = 消息头 + 消息体 消息头固定16字节长度,消息包长度定义在消息头前4个字节中。

单纯摘取第0000行为例,共16个字节的头部:

00 00 00 10 00 10 00 01 00 00 00 06 00 00 00 0f

16进制表示,每两个紧挨着数字代表一个byte字节。

微信消息包格式:

  1. 前4字节表示数据包长度,可变 值为16时,意味着一个仅仅包含头部的完整的数据包(可能表示着预先定义好的业务意义),后面可能还有会别的消息包
  2. 2个字节表示头部长度,固定值,0x10 = 16
  3. 2个字节表示谢意版本,固定值,0x01 = 1
  4. 4个字节操作码说明数字,可变
  5. 序列号,可变
  6. 头部后面紧跟着消息体,非明文,加密形式
  7. 一个消息包,最小16 byte字节
如果对报文不是很了解可以学习参考一下其他底层通信协议 比如.. ModbusTCP

通过数据多次采样分析:

0000 - 0040为单独的数据包 0050行为下一个数据包的头部,前四个字节值为0xca = 202,表示包含了从0050-0110共202个字节数据 一次数据发送,可能包含若干子数据包 换行符\n,16进制表示为0x0a,在00f0行,包含了两个换行符号 一个数据体换行符号用于更细粒度的业务数据分割 是否蒙对,需要问问做微信协议的同学 所有被标记为HTTP协议通信所发送数据都包含换行符号 动手试试猜想,模拟微信TCP长连接 开始很不解为什么会出现如此怪异的HTTP双通道长连接请求,难道基于TCP通信,然后做了一些手脚?很常规的TCP长连接,传输数据时(不是所有数据传输),被wireshark误认为HTTP长连接。这个需要做一个实验证实一下自己想法,设想如下:

写一个Ping-Pong客户端、服务器端程序,然后使用Wireshark看一下结果,是否符合判断。

测试服务端代码

https://gist.githubusercontent.com/yongboy/9341037/raw/pong_server.c

/**
 * nieyong@youku.com
 * how to compile it:
 * gcc pong_server.c -o pong_server /usr/local/lib/libev.a -lm
 */
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <err.h>
#include <unistd.h>

#include "../include/ev.h"

static int server_port = 8080;

struct ev_loop *loop;
typedef struct {
      
    int fd;
    ev_io ev_read;
} client_t;

ev_io ev_accept;

static void free_res(struct ev_loop *loop, ev_io *ws);

int setnonblock(int fd) {
      
    int flags = fcntl(fd, F_GETFL);
    if (flags < 0)
        return flags;

    flags |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags) < 0)
        return -1;

    return 0;
}

static void read_cb(struct ev_loop *loop, ev_io *w, int revents) {
      
    client_t *client = w->data;
    int r = 0;
    char rbuff[1024];
    if (revents & EV_READ) {
      
        r = read(client->fd, &rbuff, 1024);
    }

    if (EV_ERROR 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值