【项目实战】我是如何从分析到手写一个微信机器人的 - 登录篇

相信各位都有在使用微信,并且也知晓web版微信的存在。今天我们就来讲一下,如何通过模拟web版微信接口来制作一个全自动的微信机器人。本篇是该实战项目的第一篇,主讲如何实现一个登陆过程。整个的分析过程实际上是对https://res.wx.qq.com/a/wx_fed/webwx/res/static/js/index_4f3487a.js文件的解读。

0x00逻辑图集

在对一个站点进行逻辑复现前,制作一张逻辑脑图会对整个过程有更好的理解。帮助你理清思路。

图集标注

为了让图像更为整洁,我们将带括号的内容,例如【verb】作为一个变量来表示。

并且为了让逻辑路线更为清晰,我们声明如下几类线以区分下一步的操作。

登录逻辑

接下来则是一张通过mindnode画成的网页微信登录过程。

其中的session指代该次会话,可以自动处理整个过程中出现的set-cookie事件。

0x01过程分解

而后我们将web微信的登录过程划分成以下几个步骤。

  • 1.打开web微信首页
  • 2.扫描二维码
  • 3.模拟扫描及确认过程
  • 4.完成登录
  • 5.登录后的信息获取

0x02过程详解

1.打开web微信首页

首先我们需要获取到二维码以用于扫描。通过观察web请求过程我们发现,https://login.weixin.qq.com/qrcode/【xxx】为其实际地址。而【xxx】的内容取决于对/jslogin的请求结果。

通过搜索关键字 jslogin 我们发现了些有趣的内容,在这一段位置包含了后续所有请求所需的路径。包括当前关联的 API_jsLogin

所以我们得知该路径为如下地址,尾部_的时间戳可忽略。

https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN

2.获取二维码图片

现在我们模拟获取并打印 QRcode的过程。

import re
from imgcat import imgcat
import requests_html

session = requests_html.HTMLSession()
API_jsLogin = 'https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN'
QR_code = 'https://login.weixin.qq.com/qrcode/{}'

def get_qrcode_uid():
    resp = session.get(API_jsLogin)
    uid = re.split(r'"|";', resp.text)[1]
    print(f'uid is {uid}')
    return uid

def get_qrcode_img(uid):
    resp = session.get(QR_code.format(uid))
    return imgcat(resp.content)
    
uid = get_qrcode_uid()
get_qrcode_img(uid)

这边我们使用 imgcat来直接在命令行中显示图片,注意,此模块只适用于mac。我们将如上代码写入名为testimg.py中并运行得到如下结果。可见二维码已经被成功打印。

3.模拟扫描及确认过程

现在我们是不是扫码就登录了呢。可惜并不是,我们需要开启一个超时时间为25秒的循环请求,总循环时间不超过5分钟且直到成功为止。或许有同学会问,为什么呢?我们不妨回过头来观察下之前的过程动画。在获取到二维码之前,有一个状态一直为pedding的请求。

而在我们通过手机微信完成扫描的瞬间,该请求完成了。并且该请求返回的内容包含一张base64编码的图像即你的头像。

接下来我们要做的是,在客户端进行确认登录的操作。通过观察请求过程我们发现,监听二维码扫描及登录确认几乎为同一个请求,于是我们编写如下代码进行模拟。

# 延用上文session及所有import
mport execjs
import time
import base64

API_login = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/login'
API_check_login = 'https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login'

def get_timestamp(reverse=False):
    if reverse:
        return execjs.eval("~new Date")
    return int(time.time() * 1e3)
        
def login_wait(confirm = True):
    return session.get(
        API_check_login if confirm else API_login,
        params={
            "loginicon": "true",
            "uuid": uid,
            "tip": 0 if confirm else 1,
            "r": get_timestamp(True),
            "_": get_timestamp()
        },timeout=25)
        
nums = 10
while nums > 0:
    try:
        print("等待客户端扫描,剩余次数", nums)
        res = login_wait()
        if "userAvatar" in res.text:
            print("即将打印头像")
            imgcat(base64.b64decode(re.findall("base64,(.*?)';", res.text)[0]))
            break
        nums -= 1
    except Exception as e:
        pass
    
print("等待客户端确认")
redirect_uri = re.findall('redirect_uri="(.*?)"',login_wait(True).text)[0]
print('即将跳转',redirect_uri)

运行如上代码后。我们通过手机扫码机确认并成功获取了自己的头像及确认后需跳转的url。

4.完成登录

在获取redirect_uri后,我们直接将其进行访问以获取必须票据,其中结果为一个待处理的xml格式字符串。我们将编写如下代码进行模拟及处理。

def get_auth_data(resp):
    return {
        key: resp.html.find(key)[0].text
        for key in ["skey", "wxsid", "wxuin", "pass_ticket", "isgrayscale"]
    }


def get_ticket():
    resp = session.get(
        redirect_uri, params={"fun": "new", "lang": "zh_CN", "_": get_timestamp()}
    )
    print("Get Ticket:", requests_html.requests.utils.dict_from_cookiejar(resp.cookies))
    auth_data = get_auth_data(resp)
    session.cookies.update(
        requests_html.requests.utils.cookiejar_from_dict(
            {"last_wxuin": auth_data["wxuin"]}
        )
    )
    if list(filter(lambda item: item[1], auth_data.items())):
        return auth_data


auth_data = get_ticket()

现在,我们也成功得取得了票据信息。此处的信息将帮助我们得以成功获取个人实况信息。

5.登录后的信息获取

接下来我们看到下一个请求的响应体中,已经包含了我的昵称S045pd。也就是说此刻我们已成功登录,并且该请求获取了当前个人的信息。

这里挑选了几个较为直观的字段:

  • ChatSet 当前消息对象ID集合若干个(你在和谁聊天top10)
  • ContactList 当前消息对象集合若干个(你在和谁聊天top10)

    • MemberList 如果为群组,则为群组人员列表
  • MPSubscribeMsgList 公众号列表
  • SKey 一个重要的参数
  • SyncKey 一个消息交互的重要参数
  • SystemTime 系统时间
  • User 用户信息

    • HeadImgUrl 头像
    • NickName 昵称
    • Sex 性别
    • Signature 个性签名
    • UserName 当前ID

当然这里获取的仅仅是基础信息,好友列表并不是全的。下期将编写关于 《Webot微信机器人项目实战【好友导出】》。

在公众号后台回复:微信机器人即可获得此次文章所用代码。

完整代码欢迎关注开源项目: Webot


更多精彩尽在公众号:进击的Hunter

你可能感兴趣的:(python)