【验证码逆向专栏】某验二代滑块验证码逆向分析

news2024/11/19 0:38:36

声明

本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!

本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K哥爬虫】联系作者立即删除!


文章目录

    • 声明
    • 逆向目标
    • 抓包情况
    • 逆向分析
      • 获取 H7z 值
      • 获取 r7z 值
        • 获取 userresponse 值
        • 获取 passtime 值
        • 获取 imgload 值
        • 获取 aa 值
        • 获取 ep 值
        • 获取 rp 值
      • 获取 w 值
    • 结果验证


逆向目标

  • 目标:某验二代滑块验证码逆向分析
  • 主页:aHR0cDovL3d3dy5qc2dzai5nb3YuY246NTg4ODgvbWluaS9uZXR3ZWIvU01MaWJyYXJ5LmpzcA==
  • 说明:大多数逻辑其实和三四代都一样,相同的就简写了,有疑惑的地方可以看以前的文章
  • 【验证码逆向专栏】某验三代滑块验证码逆向分析
  • 【验证码逆向专栏】某验四代滑块验证码逆向分析

抓包情况

主页点击搜索就会跳出二代的验证码,netWebServlet.json 的请求,会返回 challengegt

01

有个 get.php 的请求,返回了一个新的 challenge,这个请求之后的操作,都要用这个新的 challenge,不然是验证不成功的,其他的还有验证码背景图片、乱序图片地址、cs 等值,之前写过三代的文章,都是类似的,这里就不一一分析了。

02

然后是 ajax.php 验证是否通过,通过之后返回一个 validate,请求里同样是需要我们逆向的 w 参数:

03

04

然后同样还是 netWebServlet.json 接口,带上 get.php 请求返回的 challenge 以及 ajax.php 返回的 validate,请求拿到一个 name 的字段。

05

06

后续的搜索数据,带上这个 name 就行了:

07

逆向分析

搞过三、四代的都知道我们可以直接搜索 w 的 Unicode 值 \u0077 即可定位,但是二代则不是 Unicode,而是16进制的编码,搜索 \x77 即可定位,当然按照正常流程,跟栈也能很容易找到加密的位置。

08

获取 H7z 值

从上图中可以知道 w 的值为 r7z + H7z,先看 H7z

09

跟进这个方法,来到一大串控制流,这里还是推荐用 AST 还原一下,后续可能有一些循环啥的,硬跟的话容易出错,当然直接全部扣一把梭也是可以的,H7z 的核心其实就是 RSA 加密随机字符串,三代四代都有,这里就不细讲了。

10

获取 r7z 值

然后就是 r7z,主要由以下两句代码生成:

11

q7z = n0B[M9r.R8z(699)](h7B[M9r.C8z(105)](Y7z), V7z[M9r.R8z(818)]())
r7z = p7B[M9r.R8z(260)](q7z)

可以看到其中有个变量 Y7z 参与了计算,先来看看他是怎么来的,直接搜索即可定位,可以发现同样是16进制的编码,由五个值组成:userresponsepasstimeimgloadaaep

12

获取 userresponse 值

挨个分析,首先是 userresponse,将滑动距离和 challenge 的值传入一个方法,得到一个 9 位字符串:

13

上图中 g7z 就是滑动距离,搜索可以看到定义的地方,尺子量一下对比一下,和滑动的距离是一致的:

14

15

然后再来看看那个方法,跟进去之后也是一大串 switch-case 控制流:

16

还原一下代码如下:

function getUserResponse(L0z, o0z) {
    for (var j0z = o0z.slice(32), c0z = [], X0z = 0; X0z < j0z.length; X0z++){
        var K0z = j0z.charCodeAt(X0z);
        c0z[X0z] = K0z > 57 ? K0z - 87 : K0z - 48;
    }
    j0z = 36 * c0z[0] + c0z[1];
    var k0z = Math.round(L0z) + j0z;
    o0z = o0z.slice(0, 32);
    var n0z, f0z = [[], [], [], [], []], Q0z = {}, N0z = 0;
    X0z = 0;
    for (var i0z = o0z.length; i0z > X0z; X0z++){
        n0z = o0z.charAt(X0z), Q0z[n0z] || (Q0z[n0z] = 1, f0z[N0z].push(n0z), N0z++, N0z = 5 == N0z ? 0 : N0z);
    }
    var y0z, v0z = k0z, B0z = 4, x0z = "", I0z = [1, 2, 5, 10, 50];
    while ( v0z > 0) {
        v0z - I0z[B0z] >= 0 ? (y0z = parseInt(Math.random() * f0z[B0z].length, 10),
        x0z += f0z[B0z][y0z], v0z -= I0z[B0z]) : (f0z.splice(B0z, 1),
        I0z.splice(B0z, 1), B0z -= 1);
    }
    return x0z;
}

获取 passtime 值

passtime 不用考虑是怎么通过函数获取的,含义就是滑动完成所花费的时间,直接取轨迹的最后一个值即可,这个也和三四代是一样的,获取语句为:var passtime = track[track.length - 1][2],如下图所示,轨迹的最后一个值时间为 871,passtime 的值同样也为 871。

17

18

获取 imgload 值

imgload 也没啥特别的,从字面意思猜测应该是图片加载耗时,实测直接写死即可,或者整个随机值就行。

获取 aa 值

aa 的值就是 F7z,如下图所示:

19

搜索 F7z,定位到下图所示的地方,向一个方法中传入了一个时间戳:

20

跟进去同样是 switch-case 控制流,需要注意的是下图中 c7B[M9r.R8z(781)](M9r.R8z(764), K1z) 的值其实就是轨迹。

21

这段控制流还原一下就变成这样了:

function getF7z(track){
    var o5r = 6;
    for (var N1z, X1z = s6z(track), f1z = [], B1z = [], o1z = [], t1z = 0, j1z = X1z.length; t1z < j1z; t1z++){
        if (o5r * (o5r + 1) % 2 + 8) {
            N1z = u6z(X1z[t1z]),
            N1z ? B1z.push(N1z) : (f1z.push(O6z(X1z[t1z][0])),
            B1z.push(O6z(X1z[t1z][1]))),
            o1z.push(O6z(X1z[t1z][2]));
            o5r = o5r >= 17705 ? o5r / 3 : o5r * 3;
        }
    }
    return f1z.join("") + "!!" + B1z.join("") + "!!" + o1z.join("");
}

function s6z(F6z){
    for (var Y6z, g6z, a6z, E6z = [], D6z = 0, P6z = [], J6z = 0, l6z = F6z.length - 1; J6z < l6z; J6z++) {
        Y6z = Math.round(F6z[J6z + 1][0] - F6z[J6z][0]),
        g6z = Math.round(F6z[J6z + 1][1] - F6z[J6z][1]),
        a6z = Math.round(F6z[J6z + 1][2] - F6z[J6z][2]),
        P6z.push([Y6z, g6z, a6z]),
        0 == Y6z && 0 == g6z && 0 == a6z || (0 == Y6z && 0 == g6z ? D6z += a6z : (E6z.push([Y6z, g6z, a6z + D6z]), D6z = 0));
    }
    return 0 !== D6z && E6z.push([Y6z, g6z, D6z]), E6z;
}

function O6z(r6z){
    var d6z = "()*,-./0123456789:?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqr"
      , m6z = d6z.length
      , Z6z = ""
      , H6z = Math.abs(r6z)
      , W6z = parseInt(H6z / m6z);
    W6z >= m6z && (W6z = m6z - 1), W6z && (Z6z = d6z.charAt(W6z)), H6z %= m6z;
    var q6z = "";
    return r6z < 0 && (q6z += "!"), Z6z && (q6z += "$"), q6z + Z6z + d6z.charAt(H6z);
}

function u6z(R6z){
    for (var z6z = [[1, 0], [2, 0], [1, -1], [1, 1], [0, 1], [0, -1], [3, 0], [2, -1], [2, 1]], h6z = 0, C6z = z6z.length; h6z < C6z; h6z++){
        if (R6z[0] == z6z[h6z][0] && R6z[1] == z6z[h6z][1]){
            return "stuvwxyz~"[h6z]
        }
    }
    return 0;
}

以上只是 F7z 第一次生成的地方,后面还有二次处理,如下图所示:

22

同样跟进去,三个传入的参数分别是第一次生成的 F7zget.php 请求返回的 cs 参数。

23

同样是一段控制流,还原后如下:

function getF7z2(Q1z, v1z, T1z){
    var i1z, x1z = 0, c1z = Q1z, y1z = v1z[0], k1z = v1z[2], L1z = v1z[4];
    while (1){
        if (i1z = T1z.substr(x1z, 2)){
            x1z += 2;
            var n1z = parseInt(i1z, 16)
              , M1z = String.fromCharCode(n1z)
              , I1z = (y1z * n1z * n1z + k1z * n1z + L1z) % Q1z.length;
            c1z = c1z.substr(0, I1z) + M1z + c1z.substr(I1z);
        }else {
            return c1z
        }
    }
    return Q1z
}

至此 aa 参数分析完毕!

获取 ep 值

ep 的值就是一个版本号,此处是 {'v': '6.0.9'},写死即可。

24

获取 rp 值

自此 Y7z 的第一步生成就分析完毕了,注意接下来还有一步,向 Y7z 里新增了一个 rp 参数:

25

这个值的组成看起来很长,实际上是将 gt、challenge 前 32 位以及 passtime 相加经过 MD5 加密后得到的。

Y7z["rp"] = md5(gt + challenge.slice(0, 32) + passtime)

26

上图中 I0B 就是 MD5 方法,跟进去其实是可以看到很多 MD5 特征的,如下图所示:

27

自此 Y7z 的值就搞定了,然后接着前面的看,也就是 q7z 的值,同样和三四代一样的,encrypt 是 AES 加密,Y7z 经过 JSON.stringify() 处理为字符串作为待加密对象,后面是 16 为随机字符串作为 AES 的 Key,注意这里的随机字符串应该和获取 H7z 值时的随机字符串一致,不然是验证不成功的。

28

然后下一步就是获取 r7z 的值,将上一步得到的 q7z 经过一个方法进行处理,跟进方法,又是和三四代一样的,熟悉的 res + end,如下图所示:

29

直接扣代码,或者直接使用三代的代码即可:

function $_GJF(e) {
    var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()";
    return e < 0 || e >= t["length"] ? "." : t["charAt"](e);
}

function $_HBO(e, t) {
    return e >> t & 1;
}

function $_HCX(e, o) {
    var i = this;
    o || (o = i);
    for (var t = function(e, t) {
        for (var n = 0, r = 24 - 1; 0 <= r; r -= 1)
            1 === $_HBO(t, r) && (n = (n << 1) + $_HBO(e, r));
        return n;
    }, n = "", r = "", s = e.length, a = 0; a < s; a += 3) {
        var c;
        if (a + 2 < s)
            c = (e[a] << 16) + (e[a + 1] << 8) + e[a + 2],
            n += $_GJF(t(c, 7274496)) + $_GJF(t(c, 9483264)) + $_GJF(t(c, 19220)) + $_GJF(t(c, 235));
        else {
            var _ = s % 3;
            2 == _ ? (c = (e[a] << 16) + (e[a + 1] << 8),
            n += $_GJF(t(c, 7274496)) + $_GJF(t(c, 9483264)) + $_GJF(t(c, 19220)),
            r = ".") : 1 == _ && (c = e[a] << 16,
            n += $_GJF(t(c, 7274496)) + $_GJF(t(c, 9483264)),
            r = "." + ".");
        }
    }
    return {
        "res": n,
        "end": r
    };
}

获取 w 值

自此 w 的就已经出来了,r7z + H7z 即为 w 的值。

30

结果验证

测试过掉验证码抓取数据成功:

31

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

svn安装与配置并集成

svn安装 客户端安装与下载 下载地址&#xff1a;https://tortoisesvn.net/downloads.zh.html 根据自身设备情况下载 1、更换安装路径&#xff0c;点击next&#xff08;如果安装路径选择不了那么先点击next在点击back就可以选择安装路径了&#xff09; 2、如果需要集成idea在下…

详解“陌生的“位段

目录 一、什么是位段&#xff1f; 二、位段的内存分配 三、位段的应用 一、什么是位段&#xff1f; C 语言允许一个结构体中以位为单位来指定其成员所占内存长度&#xff0c;这种以位为单位的成员称为"位段"或"位域"&#xff08;bit field&#xff09;…

Golang——异常和error

Go 提供了两种处理错误 方式&#xff0c; 一 种是借助 panic和 recover 的抛出捕获机制&#xff0c; 另一种使用error 错误类型 一、异常 1、go没有try/catch&#xff0c;而是使用panic/recover。 panic包出异常&#xff0c;后续代码不再执行 recover再defer中捕获异常&#x…

c++--stack,queue,priority_queue

前言 对于栈和队列我们是不陌生的&#xff0c;在数据结构阶段已经学习过&#xff0c;记得当时我们还是用c语言将它一步一步造出来&#xff0c;因为压栈与出栈正好满足数组的尾插与头删&#xff0c;数组的代价是及小的。对于队列是头出队列&#xff0c;尾插。所以就栈的实现就用…

React 学习笔记总结(七)

针对React拓展相关的学习。 文章目录一. React 项目打包二. React扩展 之 setState两种写法三. React扩展 之 lazyLoad(懒加载)四、React的 stateHook五、React 的 Effect Hook六、React 的 RefHook七、React 的 Fragment八、React 的 Context九、React 的 PureComponent1. Com…

centos配置网卡自动获取ipv4和ipv6地址

环境&#xff1a; os&#xff1a;centos7 1.背景 正常网卡通过dhcp是不会自动获取ipv6地址的&#xff0c;一般手动执行dhclient -6 <网卡名>可以获取。 现在需求是&#xff1a;实现os启动后自动获取到ipv6地址 2.方法 在网卡对应的配置文件/etc/sysconfig/network-sc…

一、初识Node.js

一、初识Node.js 1.什么是Node.js Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 Node.js 的官网地址&#xff1a; https://nodejs.org/zh-cn/ 2.Node.js中JavaScript运行环境 注意&#xff1a; 浏览器是 JavaScript 的前端运行环境。Node.js 是 JavaScript 的…

The VMware Authorization Service is not running 解决办法

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java案例分…

ARM惹众怒,美国芯片行业也开始抛弃它,跟随中国芯片支持新架构

ARM曾以为它已经垄断了移动芯片市场&#xff0c;因此可以肆意&#xff0c;甚至还传出计划提高芯片架构授权费的消息&#xff0c;去年还计划限制高通等美国芯片企业自研核心架构&#xff0c;此前它已得罪中国芯片&#xff0c;可以说ARM真正惹了众怒&#xff0c;如此情况下这些芯…

开源文档协作套件CryptPad

本文完成于 2022年 9 月&#xff0c;11月的时候补充了些内容&#xff0c;修改了部分图片&#xff0c;原本是准备发的&#xff0c;不知什么原因又给拖延了&#xff1b; 当时的版是 v5.1.0&#xff0c;而现在最新的版本是 v5.2.1 &#xff1b; 什么是 CryptPad &#xff1f; Cryp…

WEB页面执行shell命令

上面是我的微信和QQ群&#xff0c;欢迎新朋友的加入。1.安装apache服务apt-get install apache22.创建shell脚本cd /var/www/cgi-bin/ vim shell#!/bin/sh alias urldecodesed "s g;s%\\\\xg" | xargs -0 printf "%b" echo "Content-Type: text/html;…

springboot通过controller,dao,service,serviceImpl的基类快速实现各个实体类的增删改查分页等操作

1.pom.xml文件: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/PO…

【1】K8s上部署KubeSphere

目录 1、环境初始化 【1】改名字和主机名解析 【2】时间同步 【3】禁用iptables和firewalld服务&#xff08;三台都要设置&#xff09; 【4】禁用selinux&#xff08;三台都要设置&#xff09; 【5】禁用swap分区 【6】修改linux的内核参数 2、安装docker 【1】安装doc…

化学试剂Silane-PEG-Biotin,硅烷聚乙二醇生物素材料说明

英文名称&#xff1a;Biotin-PEG-Silane 中文名称&#xff1a;生物素-聚乙二醇-硅烷 分子量&#xff1a;1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k。。。 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 用 途&#xff…

RS485 TO ETH (B)无法联网怎么解决?

1 设置下静态IP&#xff0c;例如&#xff1a; RS485 TO ETH (B) 设置为 192.168.1.200 端口号1111&#xff1b; 电脑设置为192.168.1.199 端口号1111&#xff1b; 2 用互联网社区分享的TCP服务器测试下&#xff1a;120.79.100.197 端口号 10002 3 请关闭电脑的所有防火墙&am…

Java 中 List 排序的 3 种方法

在某些特殊的场景下&#xff0c;我们需要在 Java 程序中对 List 集合进行排序操作。比如从第三方接口中获取所有用户的列表&#xff0c;但列表默认是以用户编号从小到大进行排序的&#xff0c;而我们的系统需要按照用户的年龄从大到小进行排序&#xff0c;这个时候&#xff0c;…

1.1.1信号的概念、描述及分类

目录 1.信号的概念 2.描述 3.分类 3.1确定信号和随机信号 ​3.2连续信号和离散信号​​​​​​​ 两者之间存在这样一种关系 3.3周期信号和非周期信号 3.4能量信号和功率信号 一些例题 1.信号的概念 信号是表示消息的 物理量 &#xff0c;如电信号可以通过 幅度 、 …

23.指针数组的概念,定义方法

1.二维数组 二维数组有行有列&#xff0c;二维数组可以看成由多个一维数组构成的&#xff0c;是多个一维数组的集合&#xff0c;可以认为二维数组的每一个元素都是一个一维数组。 #include<stdio.h> int main() {int a[3][5];printf("a%p\n",a);printf("…

Zabbix与乐维监控对比分析(八)——其他功能篇

前面我们详细介绍了Zabbix与乐维监控的架构与性能、Agent管理、自动发现、权限管理、对象管理、告警管理、可视化、图形图表及网络功能方面的对比分析&#xff0c;接下来我们将对二者其他功能进行对比分析。 本篇是Zabbix与乐维监控对比分析专题系列文章的第八篇&#xff0c;也…

Visual Studio Code快捷键的使用-RK3568开发

在 Visual Studio Code 软件中使用快捷键可以增加自身的效率&#xff0c;一些常用的快捷键如下所 示&#xff1a; F1 打开可以输入命令 F2 重命名变量&#xff0c;方便重构 F5 运行和调试代码 F12 去到定义的地方 shiftF12 查找所有引用 ctrlg 会让你输入数字&#xff0c;快速定…