Python爬虫进阶:普通验证码识别训练入门之手势验证码(二)

声明

本文逆向于2025年8月24日,转载公众号将往的爬虫逆向日记仅做技术学习交流。本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

点击跳转:Python爬虫进阶:手把手带你AST还原某验三代(一)

一、前言

我们续接上一篇文章,

某招投标服务平台AST详细解析

上一篇我们讲了简单的AST。后面因为有些朋友找我说加了个手势验证,让我看看。因此我们这篇就解决下这个问题。我们先从获取图片集开始。

二、定位参数

我们先获取图片

可以看到获取图片的参数有8个参数,其中三个疑似加密参数。

20250918230811470-image

加密参数

我们通过搜索可以看到vi是固定参数,通过app.e1709a3b.js链接获取

k也是固定参数,通过/config获取。那么现在就剩一个参数en。

我们通过调用栈,可以进入加密的js。

20250918230837364-image

ob混淆

可以看出来是一个ob,我们不用处理太多。去掉字符串和计算即可。继续用上一篇的解混淆代码即可

traverse(ast,{
NumericLiteral({ node }){
if(node.extra&&/^0[obx]/i.test(node.extra.raw)){
            node.extra=undefined;
}
},
StringLiteral({ node }){
if(node.extra&&/\\[ux]/gi.test(node.extra.raw)){
            node.extra=undefined;
}
},
});
traverse(ast,{
NumericLiteral:function(path){
if(path.node.extra&& path.node.value){

const numstr = path.node.extra;
const numstring = path.node.value;


path.replaceWith(types.numericLiteral(numstring));

}else{


}
},

}, opts ={});
functioncontainsSequenceExpression(path){
let containsSequence =false;


    path.traverse({
SequenceExpression(_path){
            containsSequence =true;
            _path.stop();

}
});
return containsSequence;
}

traverse(ast,{
"BinaryExpression|UnaryExpression|MemberExpression|CallExpression"(path){
if(containsSequenceExpression(path)){
return;
}
if(path.isUnaryExpression({operator:"-"})||
            path.isUnaryExpression({operator:"void"})){
return;
}
const{confident, value}= path.evaluate();

if(!confident ||typeof value =="function")
return;

if(typeof value =='number'&&(!Number.isFinite(value))){
return;
}

console.log(path.toString(),"--->", value);

        path.replaceWith(types.valueToNode(value));
},
}, opts ={});

接下来我们本地替换,然后就可以愉快的打断点了。

20250918230946684-image

en断点

可以看到en的主要参数是_0x1ad3b0

_0x1ad3b0 = _0x462038 + _0x5a360b + _0x4ea789 + _0x9aff09 + _0x2c0c93 + _0xa7e8b1 + _0x1dbf44 + _0xdc56eb + _0x3147d8 + _0x15de99 + _0x2c673b['globalMd5']["slice"](0,5);
_0x4fc671 =_0x7b8fe7(_0x4d2c6e(3,15), _0x1ad3b0);

_0x110e21 ={
"en": _0x4fc671
}

我们先看_0x462038参数,可以看到是获取了canvas指纹

20250918231026884-image

canvas指纹

然后对获取到的canvas指纹

extractCRC32FromBase64=function(_0x46598c){
      _0x46598c = _0x46598c["replace"]('data:image/png;base64,','');
var _0x3581b0 =atob(_0x46598c);
var _0x2e2e35 = _0x3581b0["slice"](-16,-12);
returnthis["string2Hex"](_0x2e2e35["toString"]());
};

进行了替换和截取。

_0x5a360b参数也是获取了浏览器的指纹,然后进行了拼接,在计算加密。

20250918231111918-image

浏览器指纹

每次浏览器的md5不一样是因为duration值变化的原因

duration值

计算是

var _0x5aca84 =Date["now"]();
var _0x4c232c =Date["now"]()- _0x5aca84;
"duration": _0x4c232c

_0x4ea789参数是取了iv的后五位然后传入canvs那个函数计算后得到的值

_0x4ea789=_0x462038(iv['substr'](-5,5));

_0x9aff09参数是先取iv[‘substr’](-5, 5)的值,然后添加到环境值的里面

"param":{
'value': _0x31afda,
"duration":0
}

在通过 _0xa3e7ff['hashComponents']计算得出,然后取[0,8]得出值。

_0x2c0c93参数是拼接了三个参数后经过MD5哈希后得到的

_0x31beb9 ="adszzSECRETB";
_0x2c0c93 = _0x5e9fab["hex_md5"](_0x4ea789 + _0x9aff09 + _0x31beb9)["slice"](0,5);

20250918231315132-image

hex_md5

20250918231337826-image

网页

可以看到是标准算法,直接引库即可

const crypto =require('crypto');

_0xa7e8b1_0x1dbf44是固定值

_0xa7e8b1="0123456789qwe"
_0x1dbf44="0123456789qwe"

_0x15de99是取useragenthost,和固定值相加,然后在md5计算得出

_0x1641cc = _0x7b361c["uaDelExtra"](navigator["userAgent"])+location['host']+ _0x2c673b["secretC"]['substr'](0,10);
_0x15de99 = _0x5e9fab['hex_md5'](_0x1641cc)["slice"](0,5);

_0xdc56eb是取了localStorage["getItem"]("vaptchanu")['split'](",")[0]的值

_0x3147d8是取了localStorage["getItem"]("vaptchaut")的值

当这两个取不出来时候,默认值是0123456789qwertyuiopasdf

_0x2c673b['globalMd5']参数是verify.2.2.4.js生成的

_0x42e6c5['hex_md5'](_0x1ba583['splicingObj']({
'cdn_servers':this['config']['cdn_servers'],
'css_version':this['config']['css_version'],
'guideVersion':this['config']["\u0067\u0075\u0069\u0064\u0065\u0056\u0065\u0072\u0073\u0069\u006f\u006e"],
'help':this['config']['help'],
'is_vip':this['config']['is_vip'],
'js_path':this['config']['js_path'],
'knock':this['config']['knock'],
'net_way':this['config']['net_way'],
'node':this["\u0063\u006f\u006e\u0066\u0069\u0067"]['node'],
'sdk_ver':this['config']['sdk_ver'],
'server':this['config']['server']
}))

通过读取参数进行MD5哈希而成,至此。发包的所有参数都完成了。

20250918231524902-image

响应数据

可以看到成功获取数据了

接下来我们就需要还原图片

三、图片还原

我们可以看到下载下来的图片是乱序的,因此我们需要还原一下图片

我们通过调用栈可以找到此处

this["imageOnload"](_0x30d5d2, _0x4d909c[_0x21f32d]);

其中_0x30d5d2是图片链接,_0x4d909c[_0x21f32d]是固定数组

接下来走了

0x20cf7f["splitImage"](this['imgDom'], _0x2ad573, _0x14f1da, _0x30ace9);

通过此方法进行运算。_0x3c6107是图片链接, _0x32ccb0是图片的拼接顺序, _0x265086是图片尺寸。因此我们只需要关心 _0x32ccb0即可。

经过断点我们观察到_0x44f25b就是我们需要的拼接顺序

_0x3716fd = _0x55756b + _0x189920 +parseInt(_0x4036be['secretC'])+ _0x1ee50f;
_0x44f25b = _0x7b361c['Decrypt'](_0x351ba8['img_order'], _0x3716fd);

_0x1ee50f函数是取生成_0x4036be['globalPow'];,而globalPow是通过如下代码生成的。

 _0x428b9b =newWorker("./work2.js?v=0");
            _0x428b9b["onmessage"]=function(_0x1d89d8){
              _0x428b9b["terminate"]();
              _0x2c673b['globalPow']= _0x1d89d8["data"];
};
            _0x428b9b["postMessage"]({
'start':'start',
'str': _0x32cfbf
});

通过调用work2.js?v=0代码,传入get的"r"参数,生成的数字。

parseInt(_0x4036be['secretC'])是固定值

_0x55756b参数是_0x7b361c["hex2int"](_0x4c8bd1['GenerateFP'](_0x351ba8["ha"]));先取get的ha值,然后通过两次计算得到的,其中的_0x4c8bd1['GenerateFP']就是我们之前的canvas的指纹计算。

_0x189920参数是往指纹里面添加了如下

"param":{
"value": hb,
"duration":0
}

然后计算得出。但是当hb为0时_0x189920 为0

_0x7b361c['Decrypt']是通过如下代码生成的。

function_0x5c1bb4(_0x411c29, _0x114289){
var _0x1a8d54 ='';
    _0x1a8d54 =(parseInt(_0x411c29)- _0x114289)["toString"]();
if(_0x1a8d54["length"]<10){
      _0x1a8d54 ="0"+ _0x1a8d54;
}
return _0x1a8d54;
}

至此图片还原的参数就完了,通过我们的代码生成的还原顺序

20250918231644869-7a8dc40e0bf42f2e59ff7ef24bc9ceaa

图片还原

可以看到图片正常还原了。

接下来我们补到之前的获取接口里面。这样我们就可以获取识别的图片集了。

20250918231736155-image

图片集

可以看到流程正常。下篇就开始图片识别训练了。

点击跳转:

Python爬虫进阶:手把手带你AST还原某验三代(一)

🎀 🌸

📜 重要提示:
如有解压密码:看下载页、看下载页、看下载页。
源码工具资源类具有可复制性: 建议具有一定思考和动手能力的用户购买。
请谨慎考虑: 小白用户和缺乏思考动手能力者不建议赞助。
虚拟商品购买须知: 虚拟类商品,一经打赏赞助,不支持退款。请谅解,谢谢合作!
邻兔跃官网:lt.lintuyue.com(如有解压密码看下载页说明)。

文章版权声明 1、本网站名称:邻兔跃lT
2、本站永久网址:https://lt.lintuyue.com/
3、本站内容主要来源于互联网优质资源整合、网友积极投稿以及部分原创内容,仅供内部学习研究软件设计思想和原理使用,学习研究后请自觉删除,请勿传播,因未及时删除所造成的任何后果责任自负,如有侵权,请联系站长进行删除处理。
4、本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
5、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
6、本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容