某当网登录滑块【协议+OCR】

一、观察接口特征

  1. 获取密钥接口。第一个接口getRankey:
  • 请求载荷分析:
  • 响应值分析:

    2.获取滑块图片接口。 跳过isShowSlide接口直接看getSlidingVerifyCode接口,因为返回值无重要信息:
  • 请求载荷里面含requestId,大概率就是第一次分析的接口中返回回来的
  • 响应值:很明显能看见返回了很多重要信息,可见这里是验证码的核心接口,因为含有背景和滑块图的地址。
  1. 验证滑块接口。滑动滑块,分析滑块验证接口checkSlidingVerifyCode:
  • 请求载荷:
  • 响应载荷对比(失败和成功):

  1. 登录验证接口。accountLogin:

二、定位每个接口的sign值生成位置并分析其加密逻辑

  1. 直接全局搜索sign
  2. 直接点进去断点观察
  3. 接着对r进行了一次编码,但得到的新r没有变化,可以不用管
  4. 对r进行了标准的MD5加密
  5. 将md5加密后的字符串传入Y函数,断点跟进Y函数内部可以看见是一个AES对称加密算法,上帝视角后面的sign值都走的这个逻辑,因为后面都是要挨个断点调试的,所以就能看出来。所以我们直接将这个AES加密过程拿到本地js文件中保存下来,方便后续纯nodejs调用。

三、定位checkSlidingVerifyCode接口中的point_json

  1. 还是直接全局搜索point_json
  2. 定位到point_json生成位置,就是将滑块到缺口的横纵坐标对象和getSlidingVerifyCode接口返回的encryptKey值当参数传给Y函数进行加密。
  3. 注意x的值,浏览渲染比例是350,所以它除的是350,而我们拿到的图片比例要除以408。

四、理清逆向思路

我们发现:

  1. 关键有4个接口:
  • getRankey接口获取密钥 rankey值 和 requestId值
  • getSlidingVerifyCode接口获取滑块、背景图片,encryptKey、token和y值
  • checkSlidingVerifyCode接口获取 checkCode 值
  • accountLogin接口做最后的登录处理
  1. 每个接口请求载荷中:
  • 均需要时间戳模拟,直接上time库
  • permanent_id均从cookie中直接取值
  • 后3个接口中的requestId值均从第一个接口的返回值中拿
  • 每个接口的sign值都是将该接口的请求参数拼接后经过md5加密,再和rankey值一起(除getRankey接口传空字符串)传入Y函数,也就是AES对称加密函数加密得到的值
  • checkSlidingVerifyCode接口中的point_json值就是将明文密码和'qrcode_p@ssw0rdK'传入Y函数的返回值

五、代码展示

python 复制代码
import requests, time, hashlib, execjs, json
from ddddocr import DdddOcr

class Spider():
    def __init__(self):
        self.cookies = "自己复制cookie"
        self.headers = "自己的header"
        self.ocr = DdddOcr()

        with open("./main.js", "r", encoding="utf-8") as f:
            self.ctx = execjs.compile(f.read())

    def get_rankey_requestId(self):
        url = "https://login.dangdang.com/api/customer/loginapi/getRankey"
        ts = int(time.time()*1000)
        data = {
            "t": f'{ts}',
            "ct": "pc",
            "permanent_id": self.cookies["__permanent_id"],
            "requestId": "",
            "sign": "dxuL7I267hpFvpCnlF0IvrEpJz3GO2NkHMGo2A5LdukezDpDPhqo+7lqMpklKBlS"
        }
        # 检测了ct开头
        params_data = f"ct=pc&permanent_id={data['permanent_id']}&t={ts}"
        sign = self.ctx.call("aes", hashlib.md5(params_data.encode()).hexdigest(), '')
        data["sign"] = sign
        res = requests.post(url, headers = self.headers, cookies = self.cookies, data = data)
        print(res.json())
        return res.json()["rankey"], res.json()["requestId"]

    def get_VerifyImage(self, rankey, requestId):
        url = "https://login.dangdang.com/api/customer/loginapi/getSlidingVerifyCode"
        ts = int(time.time()*1000)
        data = {
            "t": f'{ts}',
            "ct": "pc",
            "permanent_id": self.cookies["__permanent_id"],
            "requestId": requestId,
            "situation": "login",
        }
        params_data = f"ct=pc&permanent_id={data['permanent_id']}&requestId={requestId}&situation=login&t={ts}"
        sign = self.ctx.call("aes", hashlib.md5(params_data.encode()).hexdigest(), rankey)
        data["sign"] = sign
        response = requests.post(url, headers=self.headers, cookies=self.cookies, data=data).json()
        slide_url = response['data']['slideImg']
        bg_url = response['data']['bgImg']
        encryptKey = response['data']['encryptKey']
        y = response['data']['y']
        token = response['data']['token']
        print(response)
        return slide_url, bg_url, encryptKey, y, token

    def get_move_x(self, slide_url, bg_url):
        slide_bytes = requests.get(slide_url).content
        bg_bytes = requests.get(bg_url).content
        res = self.ocr.slide_match(slide_bytes, bg_bytes, simple_target=True)
        if res:
            gap_x = res['target'][0]
            print(f'缺口x坐标: {gap_x}')
            return gap_x
        else:
            print("识别失败")

    def checkSlidingVerifyCode(self, x, y, encryptKey, requestId, token, rankey):
        url = "https://login.dangdang.com/api/customer/loginapi/checkSlidingVerifyCode"
        ts = int(time.time()*1000)
        data = {
            "t": f'{ts}',
            "ct": "pc",
            "permanent_id": self.cookies["__permanent_id"],
            "requestId": requestId,
            "situation": "login",
            "verifyToken": token,
            "slide_cost_time": "108",
            "need_new_verifydata": "0",
            "sign": "uPjqm2GFKsc2NxfEE5oZreAFgvmlZB9RTsNNgUXJAZ6TUiQS9ClQ8w02BLTquYMj"
        }
        t = {"x":x / 408,"y":y}
        point_json = self.ctx.call("aes", json.dumps(t, separators = (',', ':')), encryptKey)
        data['point_json'] = point_json

        params_data = f"ct=pc&need_new_verifydata=0&permanent_id={data['permanent_id']}&point_json={data['point_json']}&requestId={requestId}&situation=login&slide_cost_time=108&t={ts}&verifyToken={token}"
        sign = self.ctx.call("aes", hashlib.md5(params_data.encode()).hexdigest(), rankey)
        data['sign'] = sign
        response = requests.post(url, headers=self.headers, cookies=self.cookies, data=data)
        print(response.json())
        return response.json()["data"]['checkCode']

    def login(self, requestId, token, checkCode, rankey):
        url = "https://login.dangdang.com/api/customer/loginapi/accountLogin"
        ts = int(time.time()*1000)

        username = '123456789'
        psw = '123456'
        password = self.ctx.call("aes", psw, "qrcode_p@ssw0rdK")

        data = {
            "t": f"{ts}",
            "ct": "pc",
            "permanent_id": self.cookies["__permanent_id"],
            "requestId": requestId,
            "username": username,
            "password": password,
            "autokey": "off",
            "token": token,
            "check_code": checkCode,
            "check_code_type": "1",
        }
        params_data = f"autokey=off&check_code={checkCode}&check_code_type=1&ct=pc&password={data['password']}&permanent_id={data['permanent_id']}&requestId={requestId}&t={ts}&token={token}&username={username}"
        sign = self.ctx.call('aes', hashlib.md5(params_data.encode()).hexdigest(), rankey)
        data['sign'] = sign
        response = requests.post(url, headers=self.headers, cookies=self.cookies, data=data)
        print(response.json())

if __name__ == '__main__':
    s = Spider()
    # 第一个接口拿密钥
    rankey, requestId = s.get_rankey_requestId()
    # 第二个接口拿图片地址,encryptKey,y和token
    slide_url, bg_url, encryptKey, y, token = s.get_VerifyImage(rankey, requestId)
    # ocr识别 x 距离
    x = s.get_move_x(slide_url, bg_url)
    # 第三个接口获取checkCode值
    checkCode = s.checkSlidingVerifyCode(x, y, encryptKey, requestId, token, rankey)
    # 第4个接口直接登录
    s.login(requestId, token, checkCode, rankey)

结语:本文仅供学习使用,如有侵权,立即删除。

相关推荐
Sirius.z8 小时前
第R6周:LSTM实现糖尿病探索与预测
python
名字还没想好☜8 小时前
Python f-string 进阶:数字格式化、对齐填充、调试 = 号与嵌套表达式
开发语言·数据库·python·字符串格式化·f-string
·薯条大王8 小时前
经济实惠玩云服务器|一台云服务器多人共用,子账号配置教程
java·linux·运维·服务器·汇编·c++·python
Dxy12393102169 小时前
Python 如何使用 MySQL 的事务
python·mysql
今儿敲了吗13 小时前
Python ——第三方包
笔记·python
麒麟水手13 小时前
国产银河麒麟系统开发实录:跑通Streamlit,我踩过的6个坑
python
麒麟水手14 小时前
麒麟系统部署检查清单:上线前30分钟,逐项自查这4类问题
python
用户03321266636715 小时前
使用 Python 在 PDF 中添加或删除数字签名
python
代码方舟16 小时前
零信任架构实战:基于天远柠檬查出险-登记证API构建自动化车辆履约评估网关
人工智能·python·架构·自动化
估值探索者16 小时前
【Python实时盯盘与预警 #08】成交额突然放大2倍?Python窗口比较抓异动
java·开发语言·python