【验证码逆向专栏】最新某度旋转验证码 v2 逆向分析
声明
本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K哥爬虫】联系作者立即删除!
前言
七月底有爬友反馈,某度旋转验证码 v2 的加密算法更新了。对各个站点测试后发现,站长后台的算法及请求参数更新了,因为近期也发过某度系列验证码的文章,为了避免失效部分对粉丝的误导,现再次对相关算法进行逆向分析。
逆向目标
- 目标:某度旋转验证码 v2,fs 参数逆向分析
- 网站:aHR0cHM6Ly96aXl1YW4uYmFpZHUuY29tL2xpbmtzdWJtaXQvdXJs
- 加密算法:AES、MD5、SHA1、SHA256、SHA512、SHA3
抓包分析
init
接口有四个请求参数,ver
为固定值 1,_
为 13 位时间戳,refer
为该网站的 url,ak
是固定值,相当于盾 ID,不同网站不一样,很多厂商的验证码也都会有这么一个参数:
init
接口响应返回的参数中 as
、tk
在后续请求中会用到:
填写链接地址,点击提交,即会弹出旋转验证码,此时抓包到 style
接口, 请求参数中,tk
参数是 init
接口响应返回的, _
是时间戳,其他的都是定值:
style
接口响应返回的参数中,path
即旋转验证码的底图,backstr
是生成 fs
参数值的关键部分:
转动验证码后,会抓包到 log
接口,请求参数中,ak
为盾 ID,tk
、as
均是之前的接口响应返回的,fs
参数需要逆向分析,由轨迹、旋转角度、as
、backstr
参数等加密生成。最新的 v2 较之前多了一个浏览器指纹参数 fuid,不同浏览器的值不一致,相同浏览器的值短时间内不会改变:
# "Edge": "FOCoIC3q5fKa8fgJnwzbE67EJ49BGJeplOzf+4l4EOvDuu2RXBRv6R3A1AZMa49I27C0gDDLrJyxcIIeAeEhD8JYsoLTpBiaCXhLqvzbzmvy3SeAW17tKgNq/Xx+RgOdb8TWCFe62MVrDTY6lMf2GrfqL8c87KLF2qFER3obJGnCfjdYr2J6wEsox+bQtrTEGEimjy3MrXEpSuItnI4KDyOhCNLIvGcJ9TrqWJqhR97vnz96e18U/ntNdoDIMLzy/6P9rOWdIYWmTQAeeG69438PcpN++VzDmPtrURexo5YYWpVFkRs9k5n0AC3djzGRuXr1+yVZXtGGofFzxBmdr9HtaANtMMPysO2XXACLNUNkLLWJN9fLc3OAWce48tpeQQ2ufd7knx4Oo6OM0PpOVDwQcezbGX85VEmymh7f7M5kIyVl+w5yn2AY4BmLrEWEsyw9SzzW8eHQ5zYIUjiw9hXi7OMMoCL+ptOvZYbHZs0R5qLHmmDCW1M8MMX5yyJF0BV1dQvKslKnAJwZu4XCbsXKn3UXosU1U30/poiN2VeXkBPeo8+Xj/4BIoC2I7WZ6zkFa/Uwd5SvC91kvff2a/Z4OwyTQNM7ES9HmRhChdWg0SJ2xEs1aiXAit16RiTlf82esJH+X/j52G7R3ErwQeJT3QoDv64R2702+8NbGIjf1ZOfxhUCpmJqV4jeHSaHRmnKgJZsK91XhhrdJKXdsbt3phIOpxGLupULr2K+v1DNdId8/HuE0776+tTpUl7shVCeM/XWrdkhru42pifhiujnDhIblsLt8grnj5/GRqcD6ZPAXqJW3lLc0/ub9jXgvXK/EczRgKl+7/tTBkPTCrUVtajA0luHLQOrVsXuN1v0/PR3i09SuFzZJkJBKE3M6rYvPttK9NQiBxhxYWDhX82uQu2XK8+8oU3gxCIaJwsQmX/It0kaZ45PZHFqtD40uOX0sXuThvUin4N4RSI2G9d7jPkj5hbBFquQKM4S+tDJ34jmplOTrqqKT7PPVfrdgd4OkK13pEy86BsJ8M0gKXgtivUgM8Bjl1m/pkg0SuDyntWLdrmMxcZYvgySvSSwQ2Qtm8EkKHIMyR/XgfHnpX5vadGpRMro2qaE8u+x8w1gJHIRKib2u6Q1JtQiZE1Rde/vRx8xKfg6uYR37n0BvfgJE5+KbeuwCyAvJRGUA2fpt0VClIfV0m2PRG7bvH00OODKY6cFi7NgWAK6Jc1G4Ugkfp7W8I0ZYwNpTTxVoxIIBF37aBhyiPWPAOeYXBqA"
# Chrome: "FOCoIC3q5fKa8fgJnwzbE67EJ49BGJeplOzf+4l4EOvDuu2RXBRv6R3A1AZMa49IZbsw3/U3NYEqD0LjhKzgMn8fIES5OyXlgwN5I+F8wHowpWWfXkQJw8/9AsO5Q2VOvnc2JlHGIlGS8Vq2z4OA80lVLon08EG3PPxkVZGm39fDi2exK9NDrZB+tNLX6ISxE5PzBgXpCOJ6oP9F1B0OBWaCMD/m01n8FhdDNCvP8EO5cetU79+pgL+ECRdtN6V4VElGJE0mxV4+4Zq4Jf/Xe/q8CkoTNf7Ti1glGYmN32UM9dg0uX+VzET/mmTRe4Dt+MuVHSzsI/bKCjPbpaOqfM8UsxDJUG9hyrGZ8QHa1kC04aTxkkTxI275dv3+ijS1zkWOdjFiy1eD/0R8HcRWYp2smk9EmXBkIAHL4H0gC9lQtdjey37/kyl4JA9Fp4zjuVO0arsD8MrGy1divU++B1KdawGqXpnbOcHZ3CctNGrpgmswaScc6DNWb34jFj0X3tdRE0uuHuqiYa5BClFS2V0TCorKi4CobgR419xWaX8IKLJiaNNLOShWdZdlQO2DXXVxcinzKHqUvWTYx45jsiUVlY78AHQGol6CJLQQ8Q797MShlazvdSwPXgJP5z0uMJp9L+3x/Y2GGhW5sit55sFuMXafALTYf69FCUw5+nVIRs150a4+KK+tA0Eu7Itiu3dM2pflKYWwPE6SDZznyejQ08vd+HpXRB/zhfSUcIYlT5gFEiMIA6SXZCo/XT7vC8D3gHdN+yr46XdVol/WkjFQof0JQH/Vhjj5C1xcAyNxq/VVBT01vdKk6zo6c08e84FEVMLd0m3XWtjFOYu7wRI7lldw2pSxyGnWvA4aiYWcWvvKNJtqB8wHqc5RPr9KRzhbxJnTM5K1vTx4xT/1ZUR3pU7nQKZo/4kP9XycIr/Jg3XMRSnqCBUJlagKAFPt2HF0LdsSk4WWcldb97Ar584nVGbSjPXEUVH0VgbUEm+dADzPoLP+NPMYOyhwgfADiqWaXyKT4UNESYXsPBkdGk6mLCaNSEQsDN1G2677Se3qjzDcyXBnEmHEFptRbmyJzKJ73veHPqfFYtsHO9jH0XnhYk8zKdRuqQ7dnuNIDwxm3UCPo22uFI0ZcgPvQm01s+8jYiMEFJDVra9jWyWTdMpMuhT3p2yYLf70CvUwIkw="
过一段时间,fuid
的值是会变化的:
fuid
在 fingerprint.js
文件中生成,由 user-agent、canvas、plugins 等浏览器属性构成,可以逐个跟栈分析,F
方法是对 URI 进行编码,即 encodeURIComponent()
,U
方法为 AES 加密,加密模式为 ECB,填充方式为 PKCS7,key 为固定值:
- ECB:Electronic Code Book(电子码本模式),是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文;
- PKCS7:在填充时首先获取需要填充的字节长度 = 块长度 - (数据长度 % 块长度), 在填充字节序列中所有字节填充为需要填充的字节长度值。
相关环境:
{
"userAgent": "Mozilla%2F5.0%20(Windows%20NT%2010.0%3B%20WOW64)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F86.0.4240.198%20Safari%2F537.36",
"canvas": "2a5a8f1d4173bc025dab2d50816ba134",
"language": "zh-CN",
"colorDepth": "24",
"deviceMemory": "8",
"hardwareConcurrency": "8",
"screenResolution": "1920%2C1080",
"availableScreenResolution": "1032%2C1920",
"timezoneOffset": "-480",
"timezone": "",
"sessionStorage": "true",
"localStorage": "true",
"indexedDb": "true",
"addBehavior": "false",
"openDatabase": "true",
"cpuClass": "",
"platform": "Win32",
"plugins": "null",
"webgl": "1af17d7a73799a2516da34f3b37bb363",
"webglVendorAndRenderer": "Google%20Inc.~ANGLE%20(NVIDIA%20NVS%205400M%20Direct3D11%20vs_5_0%20ps_5_0)",
"adBlock": "false",
"hasLiedLanguages": "false",
"hasLiedResolution": "false",
"hasLiedOs": "false",
"hasLiedBrowser": "false",
"touchSupport": "0%2Cfalse%2Cfalse",
"fonts": "42",
"audio": "null"
}
验证码未转正,响应返回的 op
值为 3:
验证码转正,则响应返回的 op
值为 1:
urlsubmit
接口的请求参数 ds
、tk
是 log
接口响应返回的,url
为提交的链接地址:
以下是目前遇到过的链接提交之后的几种结果:
- 提交成功 --->
- 验证码信息有误 --->
- 未输入提交链接 --->
- 未添加 cookies ---> not allowed
逆向分析
log
接口有一个加密参数 fs
,通过跟栈会发现,和之前一样,fs
参数的加密位置在 mkd_v2.js
这个文件里面,跟进去之后,直接 ctrl + f
搜索 fs =
,有两个结果,这里就是生成 fs
参数的关键位置:
先分析第一个 n.fs
:
n.fs = (0, f.Li)(JSON.stringify(this.rzData), this.secondHandle)
JSON.stringify(this.rzData)
是将 this.rzData
转换成字符串形式,在第 12871 行打下断点,this.rzData
由 backstr
及鼠标轨迹等参数构成,backstr
参数是 style
接口响应返回的,ac_c
是旋转比例,现在的底图是 AI 生成的,和之前的风景图不一样了,识别模型最好重新训练,轨迹和旧版不一样,需要模拟构造不能直接固定,且校验相对严格,轨迹中有个参数很重要:
向上跟栈,即可定位到计算 ac_c
的位置:
Number((this.distance / (e - 52)).toFixed(2))
this.distance
是滑动的距离,e
为定值 290,实际上就是滑动条能够滑动的最大长度:
this.secondHandle
主要包含 as
参数,该参数是 init
接口响应返回的:
JSON.stringify(this.rzData)
、this.secondHandle
作为参数传递到 f.Li
函数中,进行加密处理之后生成了第一个 fs 值:
鼠标选中 f.Li
后跟进去:
encrypt(key, word, !0)
即关键加密函数:
先来分析下传入的三个参数,!0
为 true,word
参数即 JSON.stringify(this.rzData)
,key
参数定义在第 13312 行,旧版 v2 的 fs
参数是 AES 加密,key 为 "as + appsapi2"
,新版的 key 经过 getNewKey(params.as)
方法做了进一步的处理,params.as
是 init
接口响应返回的,跟进到 getNewKey
函数中去:
t
即 as
,e
为 t + "appsapi2"
,r
取了 e
的最后一位字符,下面是一段 switch...case 语句,根据 r
的值选择相应的加密算法,截取加密后的字符串的前 16 位,即是 key 值,目前主要碰到过三种加密算法,MD5、SHA3-256 以及 SHA3-512,以下通过 JavaScript 对其进行复现:
const CryptoJS = require('crypto-js');
function getNewKey(as){
/**
* encryptedStr(SHA3-256) ---> f25f1614appsapi2
* encryptedValue ---> 49d3a9685870cc30f63330b8136c7adfdb8859c6b538308992a1c9a456db2e59
*
* encryptedValue(MD5) ---> 5e4ebc8cappsapi2
* encryptedValue ---> c30b8b5289e46489598de382a658cc7f
*/
var encryptedStr = as + "appsapi2";
var r = as.substr(as.length - 1, 1);
switch (true) {
case ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'a', 'b', 'c', 'd', 'e', 'f', 'g'].includes(r):
encryptedValue = CryptoJS.MD5(encryptedStr).toString();
break;
case ['H', 'I', 'J', 'K', 'L', 'M', 'N', 'h', 'i', 'j', 'k', 'l', 'm', 'n'].includes(r):
encryptedValue = CryptoJS.SHA1(encryptedStr).toString(CryptoJS.enc.Hex);
break;
case ['O', 'P', 'Q', 'R', 'S', 'T', 'o', 'p', 'q', 'r', 's', 't'].includes(r):
encryptedValue = CryptoJS.SHA256(encryptedStr).toString(CryptoJS.enc.Hex);
break;
case ['U', 'V', 'W', 'X', 'Y', 'Z', 'u', 'v', 'w', 'x', 'y', 'z'].includes(r):
encryptedValue = CryptoJS.SHA512(encryptedStr).toString(CryptoJS.enc.Hex);
break;
case ['0', '1', '2', '3', '4'].includes(r):
encryptedValue = CryptoJS.SHA3(encryptedStr, { outputLength: 256 }).toString(CryptoJS.enc.Hex);
break;
case ['5', '6', '7', '8', '9'].includes(r):
encryptedValue = CryptoJS.SHA3(encryptedStr, { outputLength: 512 }).toString(CryptoJS.enc.Hex);
break;
default:
encryptedValue = e;
}
var key = encryptedValue.slice(0, 16);
return key;
}
var as = "7d7f8765";
var key = getNewKey(as);
console.log('key:', key);
SHA3-512 结果校验一致:
至此 key
参数分析完了,回到第 13315 行,跟进到加密函数 encrypt
中,和旧版 v2 一样,是 AES 加密,不同点在于:1. 旧版的 key = as + "appsapi2",新版 key 的值为 as + "appsapi2" 加密后取前 16 位字符;2. 旧版的填充方式 padding 为 Pkcs7,新版的为 ZeroPadding:
- PKCS7:在填充时首先获取需要填充的字节长度 = 块长度 - (数据长度 % 块长度), 在填充字节序列中所有字节填充为需要填充的字节长度值;
- ZeroPadding:在填充时首先获取需要填充的字节长度 = 块长度 - (数据长度 % 块长度), 在填充字节序列中所有字节填充为 0 。
接下来分析第二个 n.fs
,也就是最终的 fs
参数的值,同样是经过 f.Li
函数加密生成,不过传入的两个参数发生了改变,common_en
的值即第一个 n.fs
的值,backstr
以及 as
是前面接口响应返回的值,key
即上文所述的 AES 算法加密的密钥值:
n.fs = (0, f.Li)(JSON.stringify(
{
common_en: n.fs,
backstr: this.cfg.backstr
}), {
key: this.newKey,
as: this.cfg.as,
method: "aes-ecb"
})
在第 12873 行打下断点,此时第一个 n.fs
参数的值生成了:
在第 12882 行打下断点,此时生成了第二个 n.fs
参数的值,即最终的 fs
参数的值,对比验证一下:
结果验证
【验证码逆向专栏】最新某度旋转验证码 v2 逆向分析的更多相关文章
- python 验证码识别示例(二) 复杂验证码识别
在这篇博文中手把手教你如何去分割验证,然后进行识别. 一:下载验证码 验证码分析,图片上有折线,验证码有数字,有英文字母大小写,分类的时候需要更多的样本,验证码的字母是彩色的,图片上有雪花等噪点,因 ...
- css3 向上淡入 小图标翻转 360度旋转
代码 <!DOCTYPE HTML> <html> <style type="text/css"> div { border: 1px soli ...
- Python 爬虫入门(四)—— 验证码上篇(主要讲述验证码验证流程,不含破解验证码)
本篇主要讲述验证码的验证流程,包括如何验证码的实现.如何获取验证码.识别验证码(这篇是人来识别,机器识别放在下篇).发送验证码.同样以一个例子来说明.目标网址 http://icp.alexa.cn/ ...
- python 验证码识别示例(五) 简单验证码识别
今天介绍一个简单验证的识别. 主要是标准的格式,没有扭曲和变现.就用 pytesseract 去识别一下. 验证码地址:http://wscx.gjxfj.gov.cn/zfp/webroot/xfs ...
- 爬虫(十二):图形验证码的识别、滑动验证码的识别(B站滑动验证码)
1. 验证码识别 随着爬虫的发展,越来越多的网站开始采用各种各样的措施来反爬虫,其中一个措施便是使用验证码.随着技术的发展,验证码也越来越花里胡哨的了.最开始就是几个数字随机组成的图像验证码,后来加入 ...
- 逆向中静态分析工具——IDA初学者笔记之字符串分析
逆向中静态分析工具——IDA初学者笔记之字符串分析 程序中往往包含很多字符串资源,这些资源存在于PE文件的rdata段,使用IDA反编译后,可以查找到这些字符串, 逆向破解程序通常需要一个突破点,而这 ...
- Anroid逆向学习从编写so到静动态调试分析arm的一次总结
Anroid逆向学习从编写so到静动态调试分析arm的一次总结 一.前言 最近跟着教我兄弟学逆向这篇教程学习Android逆向,在第七课后作业反复折腾了好几天,正好在折腾的时候对前面的学习总结一波,动 ...
- python语言验证码识别,以后不用老输入验证码了。
1.Python 3.6 安装包 1.要加环境变量 2.pip安装PIL库 3.pip安装pytesseract模块 2.tesseract-ocr-setup-4.00.00dev.exe -- ...
- ASP.NET Core 使用 Google 验证码(reCAPTCHA v3)代替传统验证码
写在前面 友情提示: Google reCAPTCHA(v3下同) 的使用不需要"梯子",但申请账号的时候需要! Google reCAPTCHA 的使用不需要"梯子&q ...
- Dede织梦验证码不显示,织梦后台登陆验证码不显示解决方法
关于"织梦验证码不显示"的解决方法 "织梦验证码无法显示出来"的问题分析? 1.之前显示正常,但是换了服务器后就不能够正常显示:(这种通常是网站程序经过迁移后所 ...
随机推荐
- Git 工具 - 子模块: submodule与subtree的使用
git日常使用中,基本都是一个项目一个Git仓库的形式,那么当我们的代码中碰到了业务级别的需要复用的代码,我们一般怎么做呢? 比如:某个工作中的项目需要包含并使用另一个项目. 也许是第三方库,或者你独 ...
- 火山引擎DataTester:无需研发人力,即刻开启企业A/B实验
近日,火山引擎A/B测试平台-- DataTester 对产品内A/B实验的"可视化编辑器"进行了新的升级,对交互.预览.Xpath的层次结构视图等能力均做了优化. 据介绍,火 ...
- 火山引擎 DataTester 应用故事:一个 A/B 测试,将产品 DAU 提升了数十万
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 疫情让线下的需求大量转移到线上,催生出了远程办公.网络授课.线上健身等新的生态现象.如何更好地为用户服务,提升 ...
- python指定大小文件生成
使用特定大小的随机数生成,使用随机数生成器生成特定大小的字节,并将其写入文件中 import os def generate_file(file_path, file_size_bytes): wit ...
- 在低代码开发平台 ILLA Cloud 中使用 Hugging Face 上的模型
ILLA Cloud 是一个面向开发者的开源低代码开发平台,平台专注于帮助开发者快速建立企业内部应用,为开发者节约数据调用与页面设计的时间.平台具有面向开发者.数据整合.协同开发.灵活部署等功能与特点 ...
- vue学习笔记 十一、计算属性介绍
系列导航 vue学习笔记 一.环境搭建 vue学习笔记 二.环境搭建+项目创建 vue学习笔记 三.文件和目录结构 vue学习笔记 四.定义组件(组件基本结构) vue学习笔记 五.创建子组件实例 v ...
- 深度学习(六)——神经网络的基本骨架:nn.Module的使用
一.torch.nn简介 官网地址: torch.nn - PyTorch 2.0 documentation 1. torch.nn中的函数简介 Containers:神经网络的骨架 Convolu ...
- 项目管理之问,ChatGPT作答
项目管理 帮我列一份<项目管理>的大纲 当然可以!以下是一个典型的<项目管理>大纲: I. 项目管理概述 A. 项目管理定义和目标 B. 项目管理的重要性和价值 C. 项目管理 ...
- md文件的基本常用编写语法
md简介 .md即markdown文件的基本常用编写语法,是一种快速标记.快速排版语言,现在很多前段项目中的说明文件readme等都是用.md文件编写的,而且很多企业也在在鼓励使用这种编辑方式.下面就 ...
- Laravel - 配置 数据库