# wxWebR **Repository Path**: yaohaonan/wxWebR ## Basic Information - **Project Name**: wxWebR - **Description**: 解析微信网页登录 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2019-03-27 - **Last Updated**: 2021-03-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # wxWebR ## 前言 工作之余突然对微信的网页版的协议通信感兴趣可以搞一波了解下。毕竟网页版容易抓,靠一个浏览器的开发者工具的Network就开始抓包解析了。 这是我2019年03月写的文档。因为微信的协议可能随时存在变动,以下内容可以参考一波。本人爬虫的方法很简单,就是尽一切方法模仿被爬虫者的行为。 该文档目前只写到获取最新消息。先记录一波。有空更新 ## 流程步骤 (这里只说到成功登录到微信并获取最新消息,因为只是模仿到这里,其他的全部都已经清清楚楚只是看你调不调用而已了。该拿到的参数都有了) 1. 去获取一个uuid,可以根据这个uuid获取一张二维码登录的图片 2. 微信客户端扫描该二维码,在客户端确认登录。 3. 浏览器不停的调用一个接口,如果返回登录成功,则调用登录接口 4. 循环遍历一个检查是否有新消息的接口。 5. 如果新消息的接口返回有新消息的状态码去获取消息接口。(完) ## WebWechat API (这里也只说到成功登录到微信并获取最新消息。其他可以自行去抓包或者参考其他文档比如[码云参考->python版(老版本)](https://gitee.com/xiaoxunzhao/wxBot/ "python版") ### 第一步获取UUID(参考方法 getUUID) | API | 获取 UUID | | --- | --------- | | url | https://login.wx.qq.com/jslogin | | method | POST | | data | URL Encode(text/javascript) | | params | **appid**: `应用ID` 参考:wx782c26e4c19acffb
**redirect_uri** `转发地址` 参考:https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage
**fun**: `应用类型` 参考:new
**lang**: `语言` 参考:zh\_CN
**_**: `时间戳` 参考:当前时间毫秒级13位数的时间戳 返回数据(String): ``` window.QRLogin.code = 200; window.QRLogin.uuid = "xxx" ``` > 注:目前看来参数除了**_** `当前时间毫秒级13位数的时间戳` 其他都是固定的值。在微信给的js里面[index_ad43596.js](https://res.wx.qq.com/a/wx_fed/webwx/res/static/js/index_ad43596.js "js文件")里搜索API_jsLogin可以得到这些参数。而且大部分不理解参数都可以从这个js文件得到答案。 ![输入图片说明](https://images.gitee.com/uploads/images/2019/0328/211815_af92319d_1436018.png "001.png") 显示二维码就不浪费时间直接就是拿第一步拿到的uuid拼接就是二维码地址:**https://login.weixin.qq.com/qrcode/{uuid}** ### 第二步等待扫码登录即微信确认登录 | API | 获取二维码扫描登录状态 | | --- | --------- | | url | https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login | | method | GET(text/javascript) | | params | **tip** :这个东西根据浏览器走的话就是第一次为1,后面都是为0,网上说是扫码状态
**loginicon** 参考:true
**uuid** : 获取到的uuid
**_** : 当前时间毫秒级13位数的时间戳 返回数据(String): ``` window.code=xxx; xxx: 【未扫码的话】 -> window.code=408; 【手机扫码但是未登录】 -> window.code = 201; 【手机取消登录】 -> window.code=400; 【手机授权登录】 -> window.code=200; 当返回200时: wechatLoginStatus:window.code=200; window.redirect_uri= "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=XXX&uuid=XXX&lang=XXX&scan=XXX"; 当返回201时可以获取扫码用户头像的base64数据哦(好像没啥用) ``` > 注: **像这种成功的话得到这种参数自己一定存起来,下一步肯定要用即`ticket`,`lang`,`uuid`,`scan`** ### 第三步扫描成功去获取你的登录凭据 | API | 获取登录凭据| | --- | --------- | | url | https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage | | method | GET(text/plain;charset=utf-8)| | params | **ticket** : `ticket`
**uuid** : `uuid`
**lang** : `lang`
**scan** : `scan`
**fun** : 参考:new
**version** 参考:v2| 返回数据(XML): ``` 0 OK xxx xxx xxx xxx 1 ``` **核心数据点:** `skey`, `wxsid`, `wxuin`, `pass_ticket` > 注: 切记,第三步会返回cookie,存下来,这里这个接口会得到cookie的。 `cookie`哪里用到我会说明的。 ### 第四步微信初始化 | API | 微信初始化| | --- | --------- | | url | https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=&lang=`lang`&pass_ticket=`pass_ticket` | | method | POST | | data | JSON(application/json; charset=UTF-8) | | header | Content-Type: application/json; charset=UTF-8 | | params | {
     BaseRequest: {
         Uin: `wxuin`,
         Sid: `wxsid`,
         Skey: `skey`,
         DeviceID: xxx,
     }
} | > 注:`r`这个参数是通过js的一个 **~new Date** 这是一个类似二进制反转的一个,可自行百度或者打开控制台输出一下就知道了。
`DeviceID`这个参数是一个简单js拼接出来的参数 **"e"+(Math.random().toFixed(15)).substring(2, 17)** 返回数据(JSON): ``` { "BaseResponse": { "Ret": 0, "ErrMsg": "" }, "Count": 11, "ContactList": [...], "SyncKey": { "Count": 4, "List": [ { "Key": 1, "Val": 635705559 }, ... ] }, "User": { "Uin": xxx, "UserName": xxx, "NickName": xxx, "HeadImgUrl": xxx, "RemarkName": "", "PYInitial": "", "PYQuanPin": "", "RemarkPYInitial": "", "RemarkPYQuanPin": "", "HideInputBarFlag": 0, "StarFriend": 0, "Sex": 1, "Signature": "Apt-get install B", "AppAccountFlag": 0, "VerifyFlag": 0, "ContactFlag": 0, "WebWxPluginSwitch": 0, "HeadImgFlag": 1, "SnsFlag": 17 }, "ChatSet": xxx, "SKey": xxx, "ClientVersion": 369297683, "SystemTime": 1453124908, "GrayScale": 1, "InviteStartCount": 40, "MPSubscribeMsgCount": 2, "MPSubscribeMsgList": [...], "ClickReportInterval": 600000 } ``` 第四步中获取 `SyncKey`, `User` 后面的消息监听用。 ### 第五步消息检查是否有新消息(这里要带上cookie) | API | synccheck | | --- | --------- | | url | https://webpush.wx2.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=&skey=`skey`&sid=`wxsid`&uin=`wxuin`&deviceid=xxx&synckey=`Synckey`&_=xxx | | method | POST | | data | JSON(text/javascript) | | header | ContentType: application/json; charset=UTF-8 | | params | | 返回数据(String): ``` window.synccheck={retcode:"xxx",selector:"xxx"} retcode: 0 正常 1100 失败/登出微信 1101 在其他地方登录了微信 1102 无凭据或者凭据已失效 selector: 0 正常 2 新的消息 7 进入/离开聊天界面 ``` > 注: deviceid 和上面的`DeviceID`这个参数是一样的:一个简单js拼接出来的参数 **"e"+(Math.random().toFixed(15)).substring(2, 17)** 我们叫他`时间戳取反`吧 _是一个当前时间毫秒级13位数的时间戳 ### 第六步获取最新消息(要带上Cookie) | API | webwxsync | | --- | --------- | | url | https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=`wxsid`&skey=`Skey`| | method | POST | | data | JSON(text/plain) | | header | ContentType: application/json; charset=UTF-8 | | params | {
     BaseRequest: { Uin: `User下的Uin`, Sid: `wxsid`, Skey: `skey`, DeviceID: `DeviceID`},
     SyncKey: `syncKey`,
     rr: `时间戳取反`
} | 返回数据(JSON): ``` { 'BaseResponse': {'ErrMsg': '', 'Ret': 0}, 'SyncKey': { 'Count': 不确定个数, 'List': [ {'Val': 636214192, 'Key': 1}, ... ] }, 'ContinueFlag': 0, 'AddMsgCount': 1, 'AddMsgList': [ { 'FromUserName': '', 'PlayLength': 0, 'RecommendInfo': {...}, 'Content': "", 'StatusNotifyUserName': '', 'StatusNotifyCode': 5, 'Status': 3, 'VoiceLength': 0, 'ToUserName': '', 'ForwardFlag': 0, 'AppMsgType': 0, 'AppInfo': {'Type': 0, 'AppID': ''}, 'Url': '', 'ImgStatus': 1, 'MsgType': 51, 'ImgHeight': 0, 'MediaId': '', 'FileName': '', 'FileSize': '', ... }, ... ], 'ModChatRoomMemberCount': 0, 'ModContactList': [], 'DelContactList': [], 'ModChatRoomMemberList': [], 'DelContactCount': 0, ... } ``` >注 这一块比较要特别注意,得到消息后发现返回的信息也是有`SyncKey` 这个要更新一波直接拿下来替换自己的旧的,不然第五步检查消息是会出现问题就是刷的特别快,而且消息是不正确的。因为真正成功访问的到是微信的请求不会立即返回,一个请求会跑的比较久至少几十秒,因为防止疯狂遍历,不用担心因为一旦有数据返回微信服务器会立刻返回数据给你进入下一阶段的循环。这一步也是比较坑的就是`SyncKey`要记得更新就行了。还有一个很骚的点就是`_`这个参数虽然是毫秒级的13位时间戳,但是在微信第一次获取后他不会再去new了,就是一直+1+1; ## 致谢 > 本项目受到以下项目的启发: > - [Zhao / wxBot](https://gitee.com/xiaoxunzhao/wxBot/)