Js逆向案例-最新过极验滑块(2022-06-08)

请求分析

极验滑块

在这里插入图片描述

请求流程分析

1. 第一次请求 https://www.geetest.com/demo/slide-float.html分析:
在这里插入图片描述

  • register-slide
 (1)  获取到  challenge 和 gt 值,后续还原加密值需要。
 (2)  每个应用网站都会对应一个唯一的id值即gt;
  • get.php :
 (1)  主要需要获取到响应 结果重的: c、s
 (2)  发起请求的url中需要一次w值,本次案例此处w值不研究,直接置空【此处的 W值 不是很重要】

2. 点击 按钮生成滑块图片的过程分析:
在这里插入图片描述

  • ajax.php?
 (1)  携带 challenge & w 进行校验,
  • get.php?
 (1) 携带有效challenge请求获得 完整背景图片+ 缺口图链接,图片是个乱序图片,需要还原;
 (2) 如果有返回challenge则取代前面的第一个challenge,同时请求图片的响应会返回c和s参数,在后面研究w参数加密会用

在这里插入图片描述
3. 滑动滑块的请求分析:

  • ajax.php?gt
(1) 携带challenge,w值,w值的加密会涉及到轨迹信息
(2) 【w 值后续重点还原】;
(3) 响应结果种类:

	距离识别错误返回{"success": 0, "message": "fail"}
	距离正确但轨迹异常则返回{"success": 0, "message": "forbidden"}
	校验通过则返回{success: 1, message: "success", validate: "f98af504ec3208dc19911b0de0b083c7", score: "3"}

参数还原

底图还原

在这里插入图片描述

  • 1 由于验证码图片是通过Canvas画布画出来的, 可以通过Canvas监听确定乱序代码的位置。
  • 2 通过调试,获取到几个重要值:drawImage 、getImageData、putImageData,基本确定乱序算法在此处;
  • 3 分析算法,原图是260×160的图片,它将一张图片 首先进行上下对等分割,后将分割后的图再次26等分,最后铜通过一个数组结合算法,依次分配每张小图在画布上的位置。
    在这里插入图片描述
    在这里插入图片描述
  • python还原代码实现
    def image_reduction(self):
        """
        ****** 乱序图片还原 ******
        Image.new(mode, size, color=0)
        mode:模式,通常用"RGB"这种模式,如果需要采用其他格式,可以参考博文:PIL的mode参数
        size:生成的图像大小
        color:生成图像的颜色,默认为0,即黑色。
        """
        img_path_list = ["bg.png", "fullbg.png"]
        for index, img in enumerate(img_path_list):
            image = Image.open(img)
            standard_img = Image.new("RGBA", (260, 160))  # 创建一个空画布
            position = [39, 38, 48, 49, 41, 40, 46, 47, 35, 34, 50, 51, 33, 32, 28, 29, 27, 26, 36, 37, 31, 30, 44, 45, 43, 42, 12, 13, 23, 22, 14, 15, 21, 20, 8, 9, 25, 24, 6, 7, 3, 2, 0, 1, 11, 10, 4, 5, 19, 18, 16, 17]
            s = 80
            for c in range(52):
                a = position[c] % 26 * 12 + 1
                b = s if position[c] > 25 else 0
                im = image.crop(box=(a, b, a + 10, b + 80))  # box参数分别代表:左上角x,y 坐标;右下角x,y坐标
                standard_img.paste(im, box=(c % 26 * 10, 80 if c > 25 else 0))  # 将im 贴到standard_img 指定坐标位置
                """
                standard_img.paste(im, box=None, mask=None)  // 图像粘贴在原图像上,box参数:左上角 x y
                """
            standard_img.save(f"img{index}.png")

w 参数

  • 在前面的请求分析中 我们确定要想验证成功,需要在请求中携带w 值;通过观察发现js文件做了一个unicode混淆字符串加密,所以直接通过搜索w的unicode码 【\\u0077":
    在这里插入图片描述
    确定w = h+ u, 接下来需要依次分别还原h、u。

先捏个软柿子- u 还原

  • 分析:
  1. 进入生成u值的方法中,稍调试会明显发现了 rsa加密。
    在这里插入图片描述
    2. 现在需要拿到 要加密的数据,即 this$_CBEEc(742)的值,经过调试发现最后生成的方法是由4个t( )函数相加的随机值,这个方法我们自行实现就好
    在这里插入图片描述
  2. 代码实现
// 获取随机数:对应 t() + t() + t() + t()
function get_t(){
		var t="";
		for (i=0; i<4; i++){
			t+=(65536 * (1 + Math["random"]()) | 0)["toString"](16)["substring"](1)
		}
		return t
	}

h 还原

l = V[$_CAGEe(342)](gt[$_CAGEe(209)](o), r[$_CAGEe(742)]()),

h = m[$_CAGEe(733)](l)

  • h的实现依赖l的值,所以先还原 l
L 还原
  • 参数分析:
    第一个参数 :需要 另一个参数O值,通过Json.stringify() 转为json字符串;
    第二个参数:发现依然是 生成随机字符串,可通过调用自己实现的方法 function get_t() 获取。
    在这里插入图片描述

*o 值生成分析

参数解释
aa对轨迹和响应的c和s参数进行了加密
ep涉及v和tm,v是js文件的版本,tm和window[“performance”][“timing”]相关,可以根据Performance.timing 时间产生的先后顺序以及时间间隔,用当前的时间戳减去相应的值来模拟
imgload图片加载时间,随机生成
passtime轨迹滑动总时长
rp对gt、challenge、passtime进行了md5加密
userresponse对滑块距离和challenge进行了加密
  • 经过多次滑动测试发现 需要自行生成的参数值:
userresponse
passtime
aa
rp
  • aa 参数分析
    • 1.找到aa 生成的位置,发送是由上一个栈传递过来的,找到调用的函数
      在这里插入图片描述
    • . 2 分析 生成l 的函数
      在这里插入图片描述

其中大数组中最后一个数组:
passtime = 大数组[-1][0][2]
滑动距离 = 大数组[-1][0][0]

    • 3.探索轨迹加密函数==
      过程分析:
      将未加密的轨迹大数字循环后一个 下x,y,t 减去前一个数组的xyt >>> 组成一个新的大数组 >>>> 循环新的大数字,分别取出每个数组的x,y,t ;取出的值经过算法 分别push 进三个新数组中 r i o >>> 最后数组join + 特殊字符加密
      在这里插入图片描述
    • 代码实现: 可以部分扣 也可以直接导出
  • userrespons 参数分析

H函数加密结果返回而得,传入滑块距离和challenge,导出来可以直接用
  • rp 参数分析
U 函数加密结果返回而得,比较简单

在这里插入图片描述

生成轨迹大数组、滑动距离、滑动时间代码 (测试)

# -*- coding:utf-8 -*-
# author:jackwu 
# time:2022/6/1
# description: 背景图还原 & 获取滑动距离 & 轨迹数组 & 滑动时间
import os
import sys
import requests
import time
import random
PROJECT_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PROJECT_PATH)
from urllib.request import urlretrieve
from PIL import Image


class GetTrackInfo(object):
    def __init__(self):
        self.challenge = ""
        self.gt = ""

    def get_gt_challenge(self):
        """获取gt,challenge"""
        url = f"https://www.geetest.com/demo/gt/register-slide?t{self.get_timestamp()}"
        resp = requests.get(url).json()
        self.challenge = resp["challenge"]
        self.gt = resp["gt"]

    def get_image_data(self):
        """获取完整背景图&缺口图片,测试直接写死"""
        img_list = [
            "https://static.geetest.com/pictures/gt/09b7341fb/bg/82baf5d7d.jpg",   # 含缺口图 bg
            "https://static.geetest.com/pictures/gt/09b7341fb/09b7341fb.jpg"       # 完整背景图 fullbg
        ]
        for index, url in enumerate(img_list):
            img_name = "bg" if index == 0 else "fullbg"
            urlretrieve(url, f'{img_name}.png')

    def image_reduction(self):
        """
        ****** 乱序图片还原 ******
        Image.new(mode, size, color=0)
        mode:模式,通常用"RGB"这种模式,如果需要采用其他格式,可以参考博文:PIL的mode参数
        size:生成的图像大小
        color:生成图像的颜色,默认为0,即黑色。
        """
        img_path_list = ["bg.png", "fullbg.png"]
        for index, img in enumerate(img_path_list):
            image = Image.open(img)
            standard_img = Image.new("RGBA", (260, 160))  # 创建一个空画布
            position = [39, 38, 48, 49, 41, 40, 46, 47, 35, 34, 50, 51, 33, 32, 28, 29, 27, 26, 36, 37, 31, 30, 44, 45, 43, 42, 12, 13, 23, 22, 14, 15, 21, 20, 8, 9, 25, 24, 6, 7, 3, 2, 0, 1, 11, 10, 4, 5, 19, 18, 16, 17]
            s = 80
            for c in range(52):
                a = position[c] % 26 * 12 + 1
                b = s if position[c] > 25 else 0
                im = image.crop(box=(a, b, a + 10, b + 80))  # box参数分别代表:左上角x,y 坐标;右下角x,y坐标
                standard_img.paste(im, box=(c % 26 * 10, 80 if c > 25 else 0))  # 将im 贴到standard_img 指定坐标位置
                """
                standard_img.paste(im, box=None, mask=None)  // 图像粘贴在原图像上,box参数:左上角 x y
                """
            standard_img.save(f"img{index}.png")

    def get_track_info(self, threshold=60):
        """
        1. 获取滑动距离,返回滑动时间 & 轨迹大数组;
        2. 比较两张图片的像素点RGB的绝对值是否小于阈值60,如果在阈值内则相同,反之不同
        """
        pic_path = "img0.png"
        cut_pic_path = "img1.png"
        pic_img = Image.open(pic_path)
        cut_img = Image.open(cut_pic_path)
        width, height = pic_img.size
        for x in range(40, width - 40):  # 从左往右
            for y in range(5, height - 10):  # 从上往下
                pixel1 = pic_img.load()[x, y]
                pixel2 = cut_img.load()[x, y]
                if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(pixel1[2] - pixel2[2]) < threshold:
                    continue
                else:
                    return x

    def get_slide_track(self, distance):
        """
        根据滑动距离生成滑动轨迹
        :param distance: 需要滑动的距离
        :return: 滑动轨迹<type 'list'>: [[x,y,t], ...]
            x: 已滑动的横向距离
            y: 已滑动的纵向距离, 除起点外, 均为0
            t: 滑动过程消耗的时间, 单位: 毫秒
        """

        if not isinstance(distance, int) or distance < 0:
            raise ValueError(f"distance类型必须是大于等于0的整数: distance: {distance}, type: {type(distance)}")
        # 初始化轨迹列表
        slide_track = [
            [random.randint(-50, -10), random.randint(-50, -10), 0],
            [0, 0, 0],
        ]
        # 共记录count次滑块位置信息
        count = 30 + int(distance / 2)
        # 初始化滑动时间
        t = random.randint(50, 100)
        # 记录上一次滑动的距离
        _x = 0
        _y = 0
        for i in range(count):
            # 已滑动的横向距离
            x = round(self.__ease_out_expo(i / count) * distance)
            # 滑动过程消耗的时间
            t += random.randint(10, 20)
            if x == _x:
                continue
            slide_track.append([x, _y, t])
            _x = x
        slide_track.append(slide_track[-1])
        return slide_track, slide_track[-1][2]   # 大数组,滑动时间

    def __ease_out_expo(self, sep):
        if sep == 1:
            return 1
        else:
            return 1 - pow(2, -10 * sep)

    @staticmethod
    def get_timestamp():
        """获取毫秒级的时间戳"""
        t = time.time()
        return str(round(t * 1000))

    def main(self):
        self.get_gt_challenge()
        self.get_image_data()
        self.image_reduction()
        distance = self.get_track_info()
        track_array, passtime = self.get_slide_track(distance)
        print(distance,)
        print(track_array)
        print(passtime)


if __name__ == '__main__':
    s = GetTrackInfo()
    s.main()

JS 导出(可脱离V8环境)

  • 有需请私。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值