js逆向-某赞滑块

声明

本文仅供学习参考,如有侵权可私信本人删除,请勿用于其他途径,违者后果自负!

如果觉得文章对你有所帮助,可以给博主点击关注和收藏哦!

前言

目标网站:aHR0cHM6Ly9hY2NvdW50LnlvdXphbi5jb20vbG9naW4=

接口:login

参数分析

image.png

输入账号密码点击登录,就会触发一个滑块验证。
如图所示:
image.png

这个滑块还是比较简单的,没有对轨迹的校验,这是校验了xy的距离缺口的距离。

正式开始分析,点击登录后会有发包的几个步骤。

image.png

第一个接口会下发两个参数,分别是tokenrandomStr
image.png
这两个参数会在后面使用到,所以比较重要。

第二个接口就是下发验证码图片的url
image.png

除了验证码图片和缺口图片之外还有一个cy的值,这个是滑块要校验的值。

第三个接口是验证滑块,会提交之前的token
image.png

同时返回校验后的结果
image.png

返回true或者是false

前两部分都是正常的请求,直到第三步才涉及到加密部分。加密值是userBehaviorData,这个值需要搞清楚是怎么生成的。
image.png

搜索结果又多处,分别打上断点,重新触发请求,拖动滑块。
image.png

发现已经可以断下了,j函数包裹着一个对象,然后就可以得到加密值。
对象中的值的具体含义列在下表;

::: hljs-center

字段解释
cxx轴滑行距离
cyx轴滑行距离
scale缩放比例
slidingEvents滑行距离数组

:::

其中y`在前文中已经通过接口得到了,缩放比例是个固定值0.5写死即可,通过多次测试发现对于轨迹没有校验写死即可。
那么现在只要识别滑块x轴到缺口的位置即可。

现在分析j函数,F11进入。
image.png

很明显是一个aes加密,iv和key都是函数B进行处理的。

此处下断点,然后刷新页面。
image.png

向上追栈,
image.png

这个时候发现之前从接口获得的randomStr派上了用场,先判断是否存在,如果存在就先使用引号进行分割,然后再反转,再合并再用@分割,以此便得到了keyiv

再继续回到D.aes.encrypt函数,F11进入。
image.png

知道了加密模式和Padding模式以及key和iv,可以使用python还原也可以使用js。

代码复现

流程和加密过程都已经了解了,现在要做的就是使用代码去模拟整个流程。滑块缺口位置的识别可以使用python的ocr库,ddddocr

image.png

测试了十次都是ok的,因为下载的图片是原有图片大小的2倍,所以记得缩放一下图片或者是将x、y缩小一倍。
这里没有做模拟登陆的过程,有兴趣的可以自己再完善一下。

最后贴一下代码:

import json
import math
import time

import execjs
from loguru import logger
import requests
from ddddocr import DdddOcr


def get_token():
    """
    获取滑块totken
    :return:
    """
    headers = {
        "authority": "passport.youzan.com",
        "accept": "*/*",
        "accept-language": "zh,zh-CN;q=0.9",
        "access-control-request-headers": "content-type",
        "access-control-request-method": "GET",
        "cache-control": "no-cache",
        "origin": "https://account.youzan.com",
        "pragma": "no-cache",
        "referer": "https://account.youzan.com/login",
        "sec-fetch-dest": "empty",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-site",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
    }
    url = "https://passport.youzan.com/api/captcha/get-behavior-captcha-token-v2.json"
    params = {
        "bizType": "15",
        "version": "1.0"
    }
    response = requests.get(url, headers=headers, params=params).json()
    token = response.get('data', {}).get('token', '')
    random_str = response.get('data', {}).get('randomStr', '')
    logger.info(f"获取token:{token}和random_str:{random_str}")
    return token, random_str


def get_captcha_img(token):
    """
    获取验证码信息
    :param token:
    :return:
    """
    headers = {
        "authority": "passport.youzan.com",
        "accept": "*/*",
        "accept-language": "zh,zh-CN;q=0.9",
        "cache-control": "no-cache",
        "content-type": "application/json",
        "origin": "https://account.youzan.com",
        "pragma": "no-cache",
        "referer": "https://account.youzan.com/login",
        "sec-ch-ua": "\"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\"",
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": "\"Windows\"",
        "sec-fetch-dest": "empty",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-site",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
    }
    url = "https://passport.youzan.com/api/captcha/get-behavior-captcha-data.json"
    params = {
        "token": token,
        "captchaType": "1"
    }
    res = requests.get(url, headers=headers, params=params)
    logger.info("获取验证码接口")
    return res.json()


def recognize_captcha(json_data):
    """
    识别验证码
    :param json_data:
    :return:
    """
    big_url = json_data.get('data').get('captchaObtainInfoResult').get('bigUrl')
    small_url = json_data.get('data').get('captchaObtainInfoResult').get('smallUrl')
    cy = json_data.get('data').get('captchaObtainInfoResult').get('cy')
    ocr = DdddOcr(show_ad=False)

    result = ocr.slide_match(requests.get(small_url).content, requests.get(big_url).content, simple_target=True)
    logger.info(f"验证码图片:{big_url},缺口图片:{small_url}")
    logger.info(f"识别验证码缺口距离:{result['target'][0]},y轴距离:{cy},{result['target'][1]}")
    return result['target'][0], cy


def encrypt_info(x, y, random_str):
    with open('demo.js', encoding='utf-8', mode='r') as f:
        js_code = f.read()
    result = execjs.compile(js_code).call('userBehaviorData', x, y, random_str)
    return result


def check_breach(cx, cy, token, random_str):
    headers = {
        "authority": "passport.youzan.com",
        "accept": "*/*",
        "accept-language": "zh,zh-CN;q=0.9",
        "cache-control": "no-cache",
        "content-type": "application/json",
        "origin": "https://account.youzan.com",
        "pragma": "no-cache",
        "referer": "https://account.youzan.com/login",
        "sec-ch-ua": "\"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\"",
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": "\"Windows\"",
        "sec-fetch-dest": "empty",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-site",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
    }

    url = "https://passport.youzan.com/api/captcha/check-behavior-captcha-data.json"
    data = {
        "token": token,
        "bizType": 15,
        "bizData": "",
        "captchaType": 1,
        "userBehaviorData": encrypt_info(math.ceil(cx / 2), math.ceil(cy / 2), random_str)
    }
    data = json.dumps(data, separators=(',', ':'))
    time.sleep(1)
    response = requests.post(url, headers=headers, data=data).json()
    logger.info(f"滑块识别结果:{response['data']['success']}")


if __name__ == '__main__':
    # for i in range(10):
    token, random_str = get_token()
    captcha_data = get_captcha_img(token)
    cx, cy = recognize_captcha(captcha_data)
    check_breach(cx, cy, token, random_str)

js部分:

const CryptoJS = require('crypto-js');


function aes_encrypt(data, randomStr) {
    var c = randomStr.split("").reverse().join("").split("@");
    var t = c[0],
        e = c[1],
        key = CryptoJS.enc.Utf8.parse(t),
        iv = CryptoJS.enc.Utf8.parse(e),
        src = CryptoJS.enc.Utf8.parse(JSON.stringify(data)),
        encrypted = CryptoJS.AES.encrypt(
            src, key, {
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Iso10126,
                iv: iv
            }).toString();
    return encrypted;
}

function userBehaviorData(x, y, randomStr) {
    var n = [
        {
            "mx": x,
            "my": y,
            "ts": Date.now()
        },
        {
            "mx": 1,
            "my": 0,
            "ts": 26
        },
        {
            "mx": 1,
            "my": 0,
            "ts": 8
        },
        {
            "mx": 2,
            "my": 0,
            "ts": 8
        },
        {
            "mx": 4,
            "my": 0,
            "ts": 8
        },
        {
            "mx": 6,
            "my": 0,
            "ts": 8
        },
        {
            "mx": 4,
            "my": -1,
            "ts": 8
        },
        {
            "mx": 7,
            "my": -1,
            "ts": 8
        },
        {
            "mx": 8,
            "my": -1,
            "ts": 9
        },
        {
            "mx": 8,
            "my": 0,
            "ts": 7
        },
        {
            "mx": 11,
            "my": 0,
            "ts": 9
        },
        {
            "mx": 10,
            "my": 0,
            "ts": 7
        },
        {
            "mx": 10,
            "my": 0,
            "ts": 9
        },
        {
            "mx": 9,
            "my": 0,
            "ts": 7
        },
        {
            "mx": 7,
            "my": 0,
            "ts": 9
        },
        {
            "mx": 5,
            "my": 0,
            "ts": 7
        },
        {
            "mx": 3,
            "my": 0,
            "ts": 10
        },
        {
            "mx": 1,
            "my": 0,
            "ts": 6
        },
        {
            "mx": 1,
            "my": 0,
            "ts": 11
        },
        {
            "mx": 1,
            "my": 0,
            "ts": 5
        }
    ];
    var result = aes_encrypt({
        cx: Math.ceil(x),
        cy: Math.ceil(y),
        scale: 0.5,
        slidingEvents: n
    }, randomStr);
    return result;

}


// console.log(userBehaviorData(40, 30, "r1PxHtuXO3vrlZ4P@4koBgr9UW9zDKuWZ"));

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/197527.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

科普 | 隧道代理IP,简化操作提升安全性

随着数字化时代的深入发展,企业对网络数据的依赖日益增强。在这样的背景下,隧道代理IP正在以其独特的优势改变传统的网络代理模式,为企业级数据采集领域带来革命性的变革。 隧道代理IP技术简介 隧道代理IP通过云端服务器实现自动化的HTTP代理…

检验科LIS系统源码,LIS系统,检验数据分析,生成检验报告

检验科LIS系统源码,全套LIS系统商业项目源码 LIS是HIS系统的一个重要的组成部分,其主要功能是将检验的实验仪器传出的检验数据经分析,生成检验报告,通过网络存储在数据库中,这样医生能够方便、及时的看到患者的检验结果…

96.STL-遍历算法 transform

目录 transform 语法: 功能描述: 函数原型: 代码示例: transform 是 C 标准模板库(STL)中的一个算法,用于对一个范围内的元素进行转换并将结果存储到另一个范围。以下是简要解释和一个示例…

leetcode 202.快乐数

代码: class Solution {//计算 n 每个位置上的数字的平方和public int quadraticSum(int n){int sum0;while (n>0){int in%10;sumi*i;n/10;}return sum;}public boolean isHappy(int n) {//慢指针int slown;//快指针int fastquadraticSum(n);while (slow!fast){…

JVM 参数介绍

在一些规模稍大的应用中,Java虚拟机(JVM)的内存设置尤为重要,想在项目中取得好的效率,GC(垃圾回收)的设置是第一步。 PermGen space:全称是Permanent Generation space.就是说是永久…

音视频学习(十九)——rtsp收流(tcp方式)

前言 本文主要介绍以tcp方式实现rtsp拉流。 流程图 流程说明: 客户端发起tcp请求,如向真实相机设备请求,端口一般默认554;tcp连接成功,客户端与服务端开始rtsp信令交互;客户端收到play命令响应后,开启线…

esp32 esp-idf V5.1.1版本看门狗配置

idf.py menuconfig打开配置窗口 选择Component config选项

LLM能力与应用全解析

一、简介 经过几年时间的发展,大语言模型(LLM)已经从新兴技术发展为主流技术。而以大模型为核心技术的产品将迎来全新迭代。大模型除了聊天机器人应用外,能否在其他领域产生应用价值?在回答这个问题前,需要…

【Docker项目实战】使用Docker部署Plik临时文件上传系统

【Docker实战项目】使用Docker部署Plik 临时文件上传系统 一、Plik介绍1.1 Plik简介1.2 Plik特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Plik镜像五、部署Plik临时…

手机爬虫用Fiddler详细教程

如果你正在进行手机爬虫的工作,那么一款强大而又实用的网络调试工具Fiddler将会是你的好帮手。今天,我将和大家分享一份详细的Fiddler教程,教你如何使用它来轻松捕获和分析手机App的网络请求。让我们一起来探索Fiddler的功能和操作&#xff0…

P25 C++ const关键字

前言 本期我们要讲的是 C 中的 const 关键字。 const 在改变生成代码方面做不了什么,它有点像类和结构体的可见性,这是一个机制,可以让我们的代码更加干净,并对开发人员写代码强制特定的规则。 const 就像你做出的承诺&#xf…

双馈风机频率二次跌落,永磁风机一次调频火电水电光伏储能直流一次调频,虚拟惯性下垂控制,虚拟同步机VSG控制,二次调频也可继续深入研究

双馈风机(永磁同步风机)虚拟惯性控制下垂控制参与系统一次调频的Matlab/Simulink模型,调频结束后转速回复,造成频率二次跌落SFD。 系统为三机九节点模型(可更换为四机两区域,十机39节点,IEEE39节点,IEEE11…

idea 2023使用技巧(一)

IntelliJ IDEA在业界被公认为最好的java开发工具之一。它能给你良好的开发体验。 idea版本号为2023.2.5。 1 基础操作 1.1索引 idea首次加载项目时,都会创建索引,创建索引的时间跟项目的文件多少成正比。idea的缓存和索引主要是用来加快文件查询&…

Gossip协议理解

概述 Gossip协议,又称epidemic协议,基于流行病传播方式的节点或进程之间信息交换的协议,在分布式系统中被广泛使用。 在1987年8月由施乐-帕洛阿尔托研究中心发表ACM上的论文《Epidemic Algorithms for Replicated Database Maintenance》中…

Python 和 Node.js 之间通信 JSON 数据

更多资料获取 📚 个人网站:ipengtao.com 在实际应用中,不同编程语言之间的通信是常见的需求。Python和Node.js是两个流行且功能强大的编程语言,它们之间使用JSON格式进行数据交换是一种高效和灵活的方式。本文将详细介绍如何在Py…

Java线程通信

线程通信 案例 package com.itheima.d4;public class ThreadTest {public static void main(String[] args) {Desk desk new Desk();//创建3个生产者线程new Thread(() -> {while (true) {desk.put();}}, "厨师1").start();new Thread(() -> {while (true) {…

Gee教程3.实现前缀树路由

需要完成的目标 使用 Trie 树实现动态路由(dynamic route)解析。支持两种模式:name和*filepath,(开头带有:或者*) 这里前缀树的实现修复了Go语言动手写Web框架 - Gee第三天 前缀树路由Router | 极客兔兔​​​​​​ 中路由冲突的bug。 Trie树简介 之前&#xff0…

MySQL--日志

日志 错误日志 错误日志是MySQL中最重要的日志之一,它记录了当mysqld启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息 当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。 该日志是默认开启的,默认…

SpringBoot使用动态Banner

SpringBoot使用动态Banner Spring Boot 2.0 提供了很多新特性&#xff0c;其中就有动态 Banner。 1、pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://w…

NX二次开发UF_MTX3_vec_multiply 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_MTX3_vec_multiply Defined in: uf_mtx.h void UF_MTX3_vec_multiply(const double vec [ 3 ] , const double mtx [ 9 ] , double vec_product [ 3 ] ) overview 概述 Returns…