某当网登录滑块【协议+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)

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

相关推荐
赶紧写完去睡觉6 小时前
Anaconda 创建虚拟环境与使用
python·pycharm·conda
迷途呀6 小时前
Python:函数中的参数类型
开发语言·笔记·python·langchain·nlp
你驴我7 小时前
WhatsApp 多账号下消息已读回执的实时聚合与推送实践
后端·python
AC赳赳老秦9 小时前
OpenClaw 采集任务日志审计:全程记录采集行为,满足合规溯源与企业审计要求
java·大数据·python·数据挖掘·数据分析·php·openclaw
huangdong_9 小时前
电商图片下载工具横向对比深度评测:固乔、FATKUN、图快、当图、淘蛙、存图宝七款工具全面解析
python
满怀冰雪9 小时前
04-Paddle Tensor 基础:形状、数据类型、广播与索引
python·深度学习·神经网络·paddle
cui_ruicheng9 小时前
Python从入门到实战(十五):文件操作与目录管理
开发语言·python
xcLeigh10 小时前
Doubao-Seed-Evolving大模型接入教程|搭建全品类提示词+AI工具导航网页
前端·人工智能·python·ai·html·ai开发·豆包
不如语冰10 小时前
AI大模型入门-模块导入import
数据结构·人工智能·pytorch·python