【验证码逆向专栏】某多多验证码逆向分析

news/2024/11/29 14:04:55/文章来源:https://www.cnblogs.com/ikdl/p/18576589

7AqXs9.png

声明

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

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

前言

某多多的验证码,类型有很多,滑块、点选、手势等等,其中点选的题目繁多,每刷一次都能给你整个新的出来。本文主要对验证码的相关加密算法(不同类型的都大差不差)进行逆向分析,识别模型的训练后续也会推出相关文章,上述内容都仅供学习交流:

7AqMXZ.png

逆向目标

  • 目标:某多多点选验证码逆向分析

  • 网站:感兴趣的小伙伴私聊

抓包分析

触发验证码,抓包,/api/phantom/vc_pre_ck_b 接口返回的 salt 值,会参与到加密参数的生成:

7AuBjG.png

/api/phantom/obtain_captcha 接口获取验证码图片以及题目内容,都经过了加密处理:

7AuLtb.png

  • pictures:验证码背景图片链接;
  • semantics:验证码需点击的题目内容。

该接口的请求参数中 anti_contentcaptcha_collect 都经过了加密,verify_auth_token 是触发验证码之后,返回的识别标志:

7AulyP.png

/api/phantom/user_verify 验证接口:

7Au0ee.png

  • 验证成功:{'code': 0, 'leftover': 9, 'result': True};
  • 验证失败:{'code': 3002, 'leftover': 9, 'result': False};
  • 验证时间过长:{'code': 1001, 'leftover': null, 'result': False}。

请求参数与 /api/phantom/obtain_captcha 接口基本相同,多了一个 verify_code,也就是点击的坐标:

7Au7Uw.png

anti_content 参数的解决思路,网上有很多详解文章,跟栈就行了,本文就不对此多加分析了:

7Aw00H.png

逆向分析

验证码图片

一般的验证码,其获取图片的接口,基本都是直接返回的下载链接,或者经过 Base64 编码后的值。本案例中,验证码的背景图片链接、标题都经过了加密处理,需要进行逆向分析。

这些图片内容,在后端进行加密,那必然会在前端解密出真实的链接,然后渲染到页面上,据此,基本就有两种方案。

第一种,跟栈分析,/api/phantom/obtain_captcha 接口调用的堆栈基本都在 _app.js 文件中,是异步的,关于异步跟栈的分析流程,之前的文章写过很多,就不再赘述了。跟栈进去,在下图 return 处下个条件断点 i.pictures,返回验证图片相关信息时即会断住:

7AuX9b.png

单步调试,分析,寻找解密点。向上跟栈到此处,可以看到,c 就是经过加密后的标题,经过 this.formatSemantics 方法处理后,还原出了明文标题:

7AuVnH.png

跟到 this.formatSemantics 中去,kc.Base64.decode(Oa.a.decode(e[0])) 方法解密出了明文值,最终的结果替换掉特殊字符 @ 即可:

7AucpZ.png

kc.Base64.decode 就是 base64 解码(https://www.kgtools.cn/secret/base64):

7AuvGU.png

跟进到 Oa.a.decode 中,将相关算法扣下来即可:

7Auazq.png

Python 复现:

# ======================
# -*-coding: Utf-8 -*-
# ======================
import re
import base64from loguru import loggerc = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 3, -1, 20, -1, 17, 8, -1, 30,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 22, 10, -1, -1, 15, 14, 6, -1, 5, -1, -1, 7, 18, -1, 25, 9, -1,28, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 21, -1, 31, 13, 16, -1, 26, -1, 27, -1, 0, 19, -1, 11, 4, -1,-1, 23, -1, 29, -1, -1, -1, -1, -1, -1]# 正则表达式, 用于匹配多字节的 UTF-8 字符
b = re.compile(r'[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}')# UTF-8 解码
def y(e):length = len(e)if length == 4:t = ((7 & ord(e[0])) << 18 | (63 & ord(e[1])) << 12 | (63 & ord(e[2])) << 6 | 63 & ord(e[3])) - 65536return chr(55296 + (t >> 10)) + chr(56320 + (t & 1023))elif length == 3:return chr((15 & ord(e[0])) << 12 | (63 & ord(e[1])) << 6 | 63 & ord(e[2]))else:return chr((31 & ord(e[0])) << 6 | 63 & ord(e[1]))def decode_captcha_img(e):t = len(e)if t % 8 != 0:return Nonen = []for r in range(0, t, 8):o = c[ord(e[r])]i = c[ord(e[r + 1])]a = c[ord(e[r + 2])]p = c[ord(e[r + 3])]h = c[ord(e[r + 4])]m = c[ord(e[r + 5])]v = c[ord(e[r + 6])]g = (31 & o) << 3 | (31 & i) >> 2b = (3 & i) << 6 | (31 & a) << 1 | (31 & p) >> 4y = (15 & p) << 4 | (31 & h) >> 1w = (1 & h) << 7 | (31 & m) << 2 | (31 & v) >> 3x = (7 & v) << 5 | 31 & c[ord(e[r + 7])]n.append(chr((31 & g) << 3 | b >> 5))n.append(chr((31 & b) << 3 | y >> 5))n.append(chr((31 & y) << 3 | w >> 5))n.append(chr((31 & w) << 3 | x >> 5))n.append(chr((31 & x) << 3 | g >> 5))_ = ''.join(n)_ = _.replace('#', '').replace('@?', '').replace('*&%', '').replace('<$|>', '')return _# 解码标题
def decode_captcha_title(pic_encode, decode_type=""):decoded_img = decode_captcha_img(pic_encode)if decoded_img is None:return Noneif decode_type == "title":decoded_title_str = base64.b64decode(decoded_img).decode('utf-8')# 使用正则表达式和 y 函数替换多字节字符decoded_title_str = b.sub(lambda match: y(match.group(0)), decoded_title_str)return decoded_title_strelse:return decoded_imgif __name__ == '__main__':# 标题测试样例title_pic = "4ntGHUqMtltGiVfsfSqLiVwLtwqFSsiK4lqHXHOMOFfLnUGsORG94HKF6XMSUHwLiG2pjVyt"decode_title_result = decode_captcha_title(title_pic, "title")logger.info(decode_title_result.replace("@", ""))# 背景图片测试样例bg_pic = "pictures[0]"  # 太长了, 自行替换测试decode_bg_result = decode_captcha_title(bg_pic)logger.info(decode_bg_result)

背景图片链接还原比标题少一步解码:

7AuzLG.png

第二种,直接搜索 decode,尝试定位解密算法的位置。跟栈进去后发现,_app.js 文件内容未经过混淆,ctrl + f 搜索一下,把感觉像的地方都打上断点,刷新验证码,断住后,也能找到相关算法的位置:

7AuCzt.png

将还原的结果去掉前面的 data:image/png;base64,(标识信息),复制到 K哥工具站(https://www.kgtools.cn/convert/base64img)验证一下,无误:

7AujHe.png

captcha_collect 参数

总共有两个接口需要 captcha_collect 参数,分别是获取图片的接口 /api/phantom/obtain_captcha 和验证接口 /api/phantom/user_verify,本文将逐一分析。

两个接口,和前文一样,各用一种方案分析。/obtain_captcha 接口的 captcha_collect 参数,跟栈调试,和前文一样,下个条件断点 i.includes('0as'),在 anti_content 参数生成之后,图片结果响应返回之前,单步往下调试:

7AuI6w.png

跟到下图处会发现,此时的 captcha_collect 参数是由 Oa.a.getPrepareToken() 参数生成的:

7AufQ6.png

h(m(JSON.stringify(e)), k, C) 方法生成了 captcha_collect 参数的值,e 包含了一些环境相关的信息,k、C 明显也经过了加密处理:

7AuivO.png

先跟到 m 方法中去看看,这里是将各环境参数组成的字符串,使用 Gzip 压缩算法处理后得到的结果:

7AusIQ.png

在 JavaScript 中,一般使用两种方式实现 Gzip 压缩,分别是 pako 库和 zlib 模块:

  • pako:JavaScript 库,支持浏览器端和 Node.js 环境。pako 被设计为轻量级且跨平台,因此可以在浏览器中直接使用,无需依赖额外的本地模块或工具。适用于:浏览器端应用、前端开发、客户端 JavaScript 压缩;
  • zlib:是 Node.js 自带的原生模块,专门用于在 Node.js 环境中处理压缩和解压操作。zlib 基于 C++ 库,性能上通常更好,适用于处理大规模的数据压缩任务。适用于:服务器端应用、Node.js 后端开发、处理大文件压缩。

直接使用 pako 库即可:

npm install pako
const pako = require('pako');
// Gzip 压缩明文
const compressedText = pako.gzip(text);
// 将压缩后的字节数组转换为字符串
String.fromCharCode.apply(null, new Uint8Array(compressedText));

接下来,分析 h 函数,其就在 m 函数上面,跟过去下断点,key 和 iv 对应前文提到的 k 和 C:

7Au6Lf.png

AES 加密算法(https://www.kgtools.cn/secret/aes):

7AuFxc.png

k、C 定义在 getPrepareToken 函数上的 init 函数中,写的很明显了 aes_keyaes_iv

7AuZ43.png

是哪加密的呢?往上跟栈,发现 Za(c) 方法生成的 aes_keyaes_iv,c 就是前文提到的 /api/phantom/vc_pre_ck_b 接口响应返回的 salt 值:

7Aue9j.png

跟进去,将代码扣下来即可,也可以用 Python 复现加密算法:

7AuoK5.png

第二个,captcha_collect 参数,直接在 _app.js 文件中局部搜索定位,逐个下断分析。定位到下图处,c 就是点击的坐标,Oa.a.getImageClickToken() 函数生成的就是验证接口的 captcha_collect 参数的值:

7Aw9Pm.png

跟进去,这里的 J(X["concat"]([B, U, $, V, H, q, G])) 就是将数组 X 和 [B, U, $, V, H, q, G] 拼接之后加密,得到 captcha_collect 的值,包含了环境、轨迹等参数,校验不严:

7Awk64.png

J 函数跟进去和第一个 captcha_collect 参数的加密方式一致,k、C 相同:

7AwTNh.png

至此,整个验证码的加密分析流程就结束了。

相关算法源码,会分享到知识星球当中,需要的小伙伴自取,仅供学习交流。

结果验证

7AwpIY.png

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

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

相关文章

使用静态html绘制流程图

方案一使用svg<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Flowchart Example</title&g…

Vulnhub-Earth靶机笔记

Earth 靶机笔记 概述 这是一台 Vulnhub 的靶机,主要是 Earth 靶机地址:https://vulnhub.com/entry/the-planets-earth,755/#download 一、nmap 扫描 1、端口扫描 -sT 以 TCP 全连接扫描,--min-rate 10000 以最低 10000 速率进行扫描,-p-进行全端口扫描,-o ports 结果输出到…

hhdb数据库介绍(10-2)

集群管理 计算节点集群 集群管理主要为用户提供对计算节点集群的部署、添加、启停监控、删除等管理操作。 集群管理记录 集群管理页面显示已部署或已添加的计算节点集群信息。可以通过左上角搜索框模糊搜索计算节点集群名称进行快速查找。同时也可以通过右侧展开展开/隐藏更多按…

如何查看CUDA版本

在安装pytorch或TensorFlow等包时,需要和cuda版本匹配,此时需要查看cuda版本: 在终端输入命令nvidia-sim

hhdb数据库介绍(10-17)

配置 服务器 服务器菜单可配置集群中所有服务器的SSH信息,方便管理平台对服务器进行各种状态监控。此外也支持添加集群外的服务器到管理平台中进行监控。 自动获取服务器IP 服务器页面会自动显示集群内所有的服务器IP以及服务器中关联的服务程序。 单节点集群模式 管理平台自动…

hhdb数据库介绍(10-16)

配置 存储节点参数 存储节点参数通过可视化方式将部分无需重启的参数展示在管理平台上,方便运维人员进行管理。目前支持存储节点实例和计算节点配置库实例的参数管理。参数列表 参数列表展示存储节点信息、版本信息、参数名称、参数当前值、参数默认值、参数有效值范围、参数生…

无线AC AP监控运维方案,保障无线网络稳定运行

智和信通无线网络运维方案通过统一管理跨区域、跨厂商、跨型号的AC/AP设备,对关键性能指标和运行态势进行监控管理。提供常见无线设备品牌支持和资源监测点及指标,实现对不同时期、不同品牌、不同型号无线AC控制器、瘦AP、胖AP的管控。 当前,无线网络已经成为企业信息…

hhdb数据库介绍(10-11)

配置 逻辑库 功能说明: 逻辑库是客户端程序连接计算节点服务器后,可以访问的数据库,描述数据库表的集合,类似于直接连接存储节点实例后,看到的一个数据库。 功能入口: 在关系集群数据库可视化管理平台页面中选择配置->逻辑库。 在逻辑库页面,输入逻辑库名称,点击“搜…

hhdb数据库介绍(10-7)

OEM管理 为方便更换管理平台的产品Logo以及产品名称信息。引入“OEM管理功能”对外提供可视化更新Logo以及产品名称信息的入口。 功能入口: 具有管理权限的用户登录平台,通过 OEM管理的超链接进入功能页面,超链接路径需要在原访问页面url的基础上添加?page=oem,示例:http…

task1安装

task01 安装 1.安装并激活anaconda>通过输入conda init实现。 2.更换镜像源。(国内网站速度过慢) 1)condia更换镜像源 通过校园网联合镜像站复制镜像源,进行换源2)pypi更换镜像源 pip config set global.index-url https://mirrors.cernet.edu.cn/pypi/web/simple 3.课…

Free5GC源码研究(11) - SMF研究(下)

前文已经研究过SMF的概念和Nsmf_PDUSession的创建和释放,本文继续研究其更新,以及SMF的其他服务。SMF实现 Nsmf_PDUSession SMContext的更新是SMF中的一个核心功能,负责处理PDU会话的各种状态更新和转换。它需要处理多种不同类型的更新请求,并确保会话状态的正确转换。理解…

定义和基本导数规则(斜率截距,指数,对数)

率截距形式简介 方程的斜率和 y 截距 y=mx+b m是斜率 b是截距 来自图的斜率-截距方程 证明m是斜率,x=1减去x=0的那一块 例子: 例子:m的斜率 来自斜率和点的斜率-截距方程 例子:先得出斜率,再代入求b的值 点-斜率形式简介 例题: