声明
本文逆向于2025年8月24日,转载公众号将往的爬虫逆向日记仅做技术学习交流。本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
点击跳转:Python爬虫进阶:手把手带你AST还原某验三代(一)
一、前言
我们续接上一篇文章,
上一篇我们讲了简单的AST。后面因为有些朋友找我说加了个手势验证,让我看看。因此我们这篇就解决下这个问题。我们先从获取图片集开始。
二、定位参数
我们先获取图片
可以看到获取图片的参数有8个参数,其中三个疑似加密参数。
加密参数
我们通过搜索可以看到vi
是固定参数,通过app.e1709a3b.js
链接获取
k
也是固定参数,通过/config
获取。那么现在就剩一个参数en。
我们通过调用栈,可以进入加密的js。
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 ={});
接下来我们本地替换,然后就可以愉快的打断点了。
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指纹
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
参数也是获取了浏览器的指纹,然后进行了拼接,在计算加密。
浏览器指纹
每次浏览器的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);
hex_md5
网页
可以看到是标准算法,直接引库即可
const crypto =require('crypto');
_0xa7e8b1
和_0x1dbf44
是固定值
_0xa7e8b1="0123456789qwe"
_0x1dbf44="0123456789qwe"
_0x15de99
是取useragent
,host
,和固定值相加,然后在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哈希而成,至此。发包的所有参数都完成了。
响应数据
可以看到成功获取数据了
接下来我们就需要还原图片
三、图片还原
我们可以看到下载下来的图片是乱序的,因此我们需要还原一下图片
我们通过调用栈可以找到此处
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;
}
至此图片还原的参数就完了,通过我们的代码生成的还原顺序
图片还原
可以看到图片正常还原了。
接下来我们补到之前的获取接口里面。这样我们就可以获取识别的图片集了。
图片集
可以看到流程正常。下篇就开始图片识别训练了。
点击跳转:
暂无评论内容