【验证码逆向专栏】百某网数字九宫格验证码逆向分析

00

声明

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

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

目标

目标:百 X 网数字九宫格验证码逆向分析

网址:aHR0cHM6Ly9iZWlqaW5nLmJhaXhpbmcuY29tL296L3M5dmVyaWZ5X2h0bWw=

01

抓包分析

本例中的验证码不是很难,但网站埋了点儿坑,容易出现识别正确、参数也正确,但仍然请求不成功的情况。访问主页响应码为 307,接着请求了一个 bf.js 和两个 s.webp 的图片,然后又跳转到首页出现验证码。如果你没有以上步骤,请求主页直接就是 200 出现验证码,则需要清除 cookie 后再访问,因为第一次 307 到请求 bf.js 再到两次 s.webp 都是在设置 cookie。

第一次请求主页,response headers 会设置一个名为 _trackId__city 的 cookie,如下图所示:

02

然后带着这两个 cookie 请求了一个 bf.js,这个 js 用于后续两个 s.webp 请求参数的加密,这里注意,第一个坑,虽然可以直接调试 js 扣算法下来后面直接用就行了,但是这个 js 必须得请求一遍,不然后面请求主页的时候一直是 307。

03

然后请求第一个 s.webp,get 请求,有三个参数:cfsf,明显是加密得来的,同时请求的 cookie 也多了三个值:c0fc276cce08ba22dcc1fc276cce08ba22dcbxf,如下图所示:

04

05

然后请求第二个 s.webp,和第一个类似,get 请求也有三个参数:cfsf,cookie 和第一个一样,但第二次请求返回了一个名为 sbxf 的新 cookie,其值和 bxfc1fc276cce08ba22dc 其实是一样的,如下图所示:

06

然后带上 __trackId__cityc0fc276cce08ba22dcc1fc276cce08ba22dcbxfsbxf 这六个 cookie 再次访问主页,就是验证码页面了,返回的 html 里有个新的 js,很长一串,如下图所示:

07

08

然后观察这个 js,里面包含了验证码图片的 URL,以及需要点击的数字,如下图所示:

09

10

点击验证后,会给 verify_url 发一个 get 请求,请求参数主要有一个 data,即点击坐标(这个坐标也有讲究,有可能你的值是对的,但有时候也不成功,这个后文再细说),cookie 和前面的请求一样,如果验证成功,会返回 ret 为 0,且有一个 code 供后续请求使用,如下图所示:

11

12

获取 cookie

这里再注意一点,所有的请求,header 只需要 RefererUser-Agent 就行了,不要乱加,比如多了个 Host 也有可能导致后续请求不成功。

想要拿验证码,得先搞定 cookie,总体流程如下:

  1. 请求首页 s9verify_html 获取 __trackId__city,主要是 __trackId__city 要不要都行;
  2. 请求 bf.js,这一步不干啥,但必须得请求,不然 cookie 不能用;
  3. 第一次请求 s.webp,cookie 里多了 c0fc276cce08ba22dcc1fc276cce08ba22dcbxf,均为 js 生成;
  4. 第二次请求 s.webp,返回的 cookie 里多了 sbxf,其值和 bxf 一样,这一步可以理解为激活 cookie,使其有效。

前两步倒没有啥,第 3、4 步都有加密参数 sf,观察这两个 s.webp 都是 fetch 请求,所以我们直接一个 fetch 断点,断下后可以看到 cb 就是我们需要的两个参数:

13

14

观察 bf.js 是一个小小的类似 OB 混淆,可以 AST 解一下混淆,但这个逻辑不是很复杂,所以直接硬看也行,关键语句 cb = c3['s'](c7, c8),c7 是定值一个字符串 fc276cce08ba22dc,c8 也是定值表示颜色的字符串 rgba(255, 0, 0, 255)

15

主要是 c3['s']() 这个方法,跟进去,首先会取一下 c0fc276cce08ba22dcc1fc276cce08ba22dcbxf 三个值,如果有的话,直接返回,如果没有的话,会生成新的,生成方法主要是 c6 这个函数,如下图所示:

16

继续跟到 c6 方法里,首先对字符串 rgba(255, 0, 0, 255) 做了一个操作,生成了一张图片的 base64 字符串:

17

这里其实很明显是 canvas 绘图的一些操作,跟到 c7 看看确实是这样的:

18

这里对于我们扣算法来说,其实就不需要管了,因为同一台设备的同一个浏览器,按照相同的规则绘制的图片,base64 值是一样的,所以我们直接忽略 c7 这个方法,直接把生成的 base64 值拿来用就行了。

然后又将 base64 值进行了一个 c3["hash"]() 的操作,根据最终的值,或者跟到方法里去看,很容易发现这个其实就是个 MD5 的操作:

19

20

接着往下看,八个字符串为一组,将 md5 值分为四组,然后四组之间用 0 或者 1 连接,拼接成新的 35 位字符串,拼接的是 0 还是 1,取决于中间的三目语句,判断是否为 true,支持情况下都是 true,所以扣算法的话根本就没必要再跟进去看是怎么判断的,直接用 1 拼接就完事儿了。然后将固定的字符串 fc276cce08ba22dc 和这 35 位字符串拼接起来再一次 MD5,就得到了参数 s 的值,而参数 f 的值则是这个 35 位字符串。

21

第一个 s.webp fetch 操作就完成了,接着是第二个 s.webp,就在第一个 fetch 附近,如下图的 ce 就是第二次的 s、f 参数的值:

22

这里生成的方法大致是一样的,首先 cd 是一个新的图片的 base64 值,这个值是第一次 s.webp 请求成功返回的,先把这个新的 base64 MD5 加密一下,生成一个新的字符串,相当于替换了第一次请求固定的字符串 fc276cce08ba22dc,后续的流程和第一次都一样了:

23

这两次生成 s 和 f 的值的流程可以精简成以下 js 实现:

MD5 = require("md5")var baseImg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAADcCAYAAAAbWs+BAAAAAXNSR0IArs4c6QAAIABJREFUeF7tnQm..."function getParams(c8) {var cb = MD5(baseImg), cc = cb.substring(0, 8), cd = cb.substring(8, 16), ce = cb.substring(16, 24), cf = cb.substring(24, 32), cg = cc + 1 + cd + 1 + ce + 1 + cf;return {"s": MD5(c8 + cg),"f": cg};
}function getFirstParams() {return getParams("fc276cce08ba22dc")
}function getSecondParams(img) {return getParams(MD5(img))
}console.log(getFirstParams())
console.log(getSecondParams("data:image/png;base64,dqyixSOIJuJN0IRG288itylhqNFFXVqL"))

然后这个 cookie 值,你可以去 Hook 一下看看,但实际上观察一下就可以发现 c0fc276cce08ba22dc 就是第一次 s.webp 请求的 s 参数,c1fc276cce08ba22dcbxf 就是第一次 s.webp 请求的 f 参数,所以直接拿来用就行了。

获取验证码

带上前面生成的正确的 cookie,再次请求主页,响应码为 200,然后在返回的 html 里可以看到有个超长的 js 地址,这个 js 直接把 .js 替换成 .jpg 就是验证码地址,替换成 .valid 就是验证结果的地址,这个 js 返回的内容里面就包含了要点击的数字。

24

25

26

获取点击坐标

最终提交的坐标是长这样的:

27

由于这个图片是九宫格的样式,一般的识别都是一排,所以这里可以将九宫格裁剪后重新排列一下(当然自己会搞深度学习的话可以单独给这种九宫格训练一下,就不用重新裁剪排列了),重新排列前后对比如下:

28

这一步的利用 Python 的 PIL 库很容易实现:

from PIL import Image# 打开九宫格验证码
captcha = Image.open("captcha.jpg")# 将图片等分成三份,每份长宽为 150px 和 50px
part1 = captcha.crop((0, 0, 150, 50))
part2 = captcha.crop((0, 50, 150, 100))
part3 = captcha.crop((0, 100, 150, 150))
part1.save("part1.jpg")
part2.save("part2.jpg")
part3.save("part3.jpg")# 创建新的图片,长宽为 450px 和 50px
new_captcha = Image.new("RGB", (450, 50))# 将三份图片按顺序拼接到新的图片上
new_captcha.paste(part1, (0, 0))
new_captcha.paste(part2, (150, 0))
new_captcha.paste(part3, (300, 0))# 保存新的图片
new_captcha.save("captcha_new.jpg")

这样处理后,怎样得到对应的坐标呢?以上图为例,假设我们需要点击 question = [1, 8, 3, 6],我们识别 captcha_new.jpg 结果为 recognition_result = "172958643",生成最后的坐标流程如下:

import randomquestion = [1, 8, 3, 6]           # 要点击的数字
recognition_result = "172958643"  # captcha_new.jpg 识别的结果mapping_table = {"0": f"{str(random.randint(15, 35))},{str(random.randint(15, 35))}|","1": f"{str(random.randint(65, 85))},{str(random.randint(15, 35))}|","2": f"{str(random.randint(115, 135))},{str(random.randint(15, 35))}|","3": f"{str(random.randint(15, 35))},{str(random.randint(65, 85))}|","4": f"{str(random.randint(65, 85))},{str(random.randint(65, 85))}|","5": f"{str(random.randint(115, 135))},{str(random.randint(65, 85))}|","6": f"{str(random.randint(15, 35))},{str(random.randint(115, 135))}|","7": f"{str(random.randint(65, 85))},{str(random.randint(115, 135))}|","8": f"{str(random.randint(115, 135))},{str(random.randint(115, 135))}|",
}answer = ""
for q in question:for r in recognition_result:if q == int(r):answer += mapping_table[str(recognition_result.index(r))]print(answer)

每一个数字的图片大小是 50x50,如果我要点击上图中的数字 1,那么我的 x、y 坐标范围就应该为 [0~50, 0~50],如果我要点击上图中的数字 8,那么我的 x、y 坐标范围就应该为 [100~150, 50~100]

但是进过多次测试,点击区域要靠正中心一点,成功率才高,所以坐标范围前后各增加、减少了 15。对应数字 1 的坐标范围就应该是 [15~35, 15~35],数字 8 的坐标范围就应该是 [115~135, 65~85]

这里为了简便,直接定义了一个映射表 mapping_table,如果我点击数字 8,那么 captcha_new.jpg 识别结果 172958643 中,8 的位置是 5,对应 mapping_table["5"],也就是 random.randint(115, 135), str(random.randint(65, 85)

结果验证

29

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

修改ubuntu终端目录背景颜色

Ubuntu终端上有部分目录是黄绿色底色,看着很不舒服。如下图所示: 这是由于修改用户权限导致的问题。 通过下面指令可以看到 echo $LS_COLORS | grep "ow" ​ 可以看到ow的默认参数是34:42ow:OTHER_WRITABLE,即其他用户可写权限 …

分类预测 | Matlab实现PSO-GRU粒子群算法优化门控循环单元的数据多输入分类预测

分类预测 | Matlab实现PSO-GRU粒子群算法优化门控循环单元的数据多输入分类预测 目录 分类预测 | Matlab实现PSO-GRU粒子群算法优化门控循环单元的数据多输入分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现PSO-GRU粒子群算法优化门控循环单元的数据…

『亚马逊云科技产品测评』活动征文|阿里云服务器亚马逊服务器综合评测

授权声明:本篇文章授权活动官方亚马逊云科技文章转发、改写权,包括不限于在 Developer Centre, 知乎,自媒体平台,第三方开发者媒体等亚马逊云科技官方渠道 文章目录 引言一、亚马逊&阿里云发展历史介绍1.1 亚马逊发展历史1.2…

腾讯云CVM服务器5年可选2核4G和4核8G配置

腾讯云服务器网整理五年云服务器优惠活动 txyfwq.com/go/txy 配置可选2核4G和4核8G,公网带宽可选1M、3M或5M,系统盘为50G高性能云硬盘,标准型S5实例CPU采用主频2.5GHz的Intel Xeon Cascade Lake或者Intel Xeon Cooper Lake处理器,…

基于MS16F3211芯片的触摸控制灯的状态变化和亮度控制(11.15)

1.任务所需实现基本功能 关机状态时白灯亮蓝灯灭,此时长按按键无反应,白灯亮度降低的状态,蓝灯保持灭的状态。点按按键一次,白灯熄灭,蓝灯亮此时W引脚控制的灯亮。继续点按按键。蓝灯亮,此时W引脚控制的灯…

iOS 设置图标和upload包时显示错误

右键-show in finder-AppIcon.appiconset-然后替换图片 然后遇到个问题 就是图片不能有alpha [Xcode]应用图标:ERROR ITMS-90717: “Invalid App Store Icon. The App Store Icon in the asset catalog in x… 具体操作:只需确保【AppIcon】图片集中不…

【评论送书】十本架构师成长和软件架构技术相关的好书(可以任选)

正文开始前给大家推荐个网站,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 参与规则 本次送书1~5本参与方式:关注博主、点赞、收藏、评论(从评论区…

基础课4——客服中心管理者面临的挑战

客服管理者在当今的数字化时代也面临着许多挑战。以下是一些主要的挑战: 同行业竞争加剧:客服行业面临着来自同行业的竞争压力。为了获得竞争优势,企业需要不断提高自身的产品和服务质量,同时还需要不断降低成本、提高效率。然而…

Linux下SPI环回测试

文章目录 前言一、回环测试代码1.1 头文件 spidev.h2.2 c代码 spidev_test.c 二、 编译验证2.1 交叉编译2.2 测试 前言 linux下做spi回环测试 一、回环测试代码 1.1 头文件 spidev.h /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /** include/linux/spi…

蓝桥杯 插入排序

插入排序的思想 插入排序是一种简单直观的排序算法,其基本思想是将待排序的元素逐个插入到已排序序列 的合适位置中,使得已排序序列逐渐扩大,从而逐步构建有序序列,最终得到完全有序的序 列。 它类似于我们打扑克牌时的排序方式&…

竞赛 题目:基于卷积神经网络的手写字符识别 - 深度学习

文章目录 0 前言1 简介2 LeNet-5 模型的介绍2.1 结构解析2.2 C1层2.3 S2层S2层和C3层连接 2.4 F6与C5层 3 写数字识别算法模型的构建3.1 输入层设计3.2 激活函数的选取3.3 卷积层设计3.4 降采样层3.5 输出层设计 4 网络模型的总体结构5 部分实现代码6 在线手写识别7 最后 0 前言…

探索未来,开启无限可能:打造智慧应用,亚马逊云科技大语言模型助您一臂之力

文章目录 什么是大模型?大模型训练方法亚马逊云科技推出生成式AI新工具 —— aws toolkit使用教程 总结 什么是大模型? 近期,生成式大模型是人工智能领域的研究热点。这些生成式大模型,诸如文心一言、文心一格、ChatGPT、Stable …

Linux 系统目录结构

Linux 系统目录结构 登录系统后,在当前命令窗口下输入命令: ls / 你会看到如下图所示: 以下是对这些目录的解释: /bin: bin 是 Binaries (二进制文件) 的缩写, 这个目录存放着最经常使用的命令。 /boot: 这里存放…

【Java 进阶篇】JQuery 案例:优雅的隔行换色

在前端的设计中,页面的美观性是至关重要的。而其中一个简单而实用的设计技巧就是隔行换色。通过巧妙地使用 JQuery,我们可以轻松地实现这一效果,为网页增添一份优雅。本篇博客将详细解析 JQuery 隔行换色的实现原理和应用场景,让我…

python数据处理作业11:建一个5*3的随机数组和一个3*2的数组,其元素为1,2,3,4,5,6,求两矩阵的积

每日小语 打碎的杯子,烫伤的手,对菩萨是堪忍,因为他在里面得悟甚深之法,心生欢喜。 可是对一般人来说,一生何止打破千百个杯子?何止烫伤过千百次手?他只是痛苦地忍受,只记得下次要…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(三)

员工分页查询和账号启用禁用功能 1. 员工分页查询1.1 需求分析和设计1.1.1 产品原型1.1.2 接口设计 1.2 代码开发1.2.1 设计DTO类1.2.2 封装PageResult1.2.3 Controller层1.2.4 Service层接口1.2.5 Service层实现类1.2.6 Mapper层 1.3 功能测试1.4 代码完善 2. 启用禁用员工账号…

数据结构—内部排序(下)

文章目录 8.内部排序(下)(6).归并排序#1.先做合并#2.再来排序#3.代码实现#4.稳定性与时间复杂度分析 (7).快速排序#1.算法思想#2.代码实现#3.稳定性与时间复杂度分析 (8).基数排序#1.算法思想#2.稳定性和时间复杂度分析 小结 8.内部排序(下) (6).归并排序 那我们的时间复杂度还…

C++ [多态]

本文已收录至《C语言和高级数据结构》专栏! 作者:ARMCSKGT 多态 前言正文多态的概念多态的定义构成多态的条件关于final和override关于重载,重写和重定义 抽象类概念补充 多态的原理虚表指针和虚表关于虚函数的调用动态绑定和静态绑定 单继承与多继承中的…

it is missing from your system. Install or enable PHP‘s sodium extension.

Composer with –ignore-platform-reqext-sodium it is missing from your system. Install or enable PHP’s sodium extension. 出现如上的报错是的 开启php.ini中的 sodium的扩展

Docker Rootfs

一、rootfs 介绍 rootfs 是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。在 Linux 操作系统中,这两部分是分开存放的,操作系统只有在开机启动时才会加载指定版本的内核镜像。 实际上,同一台机器上的所有容器&am…