你只管努力,剩下的交给天意。
文章只提供学习,如有侵权请立即联系我。
前言
某验官网:官网
总体来说某验的验证码配合js总共分为三套
总体来说就分为这三种,当前了点选还分了文字,语序,图标,九空格,空间其略有变化的九宫格
其他的一模一样可以使用同一套js解决。
这篇文章只写无感!!!
ok那就开始吧!!!
进入我上面提供的官网进来点击只能组合验证如下图:
咱们先点击一下试试看看效果(这里有可能出现的不是无感的而是滑动
多试几次)
这里可以看到validate
这个值那么没错他就是我们现在需要获取到的值。
ok有目标咱们再看看请求的时候都需要了什么参数
看来参数挺多的哈gt,challenge,lang,client_type,w,callback
,这都啥啊?
不过我们一眼可以看出来lang,pt,client_type,callback
这几个值都是写死的,你可以多尝试几次看看是否一样?
我知道你尝试了而且确定了是一样,那么我们就不用管它了,来看看gt,challenge,w
。
我们先来搜索一下吧,看看是不是在其他的请求的返回值
里。
果不其然就在这里,这就是gt
和challenge
的值了(gt的值是会变得
应该是跟ip绑定的,不要以为他不变就是死的)。
既然找到这个了,那么就剩下一个w
了。
搜索之后无果,那么就需要去看他的来源。
掉头发的又来了。
嗯嗯嗯,js大概就是这样(心里存了一万句来着祖安的问候),那咋整啊???
就是得还原呗,怎么还原呢?生整啊?不如先看看代码的结构吧!
整体是一个自执行函数,函数里面有两个函数一个是有四个方法的函数
一个是自执行
可以看到自执行函数
里面有很多的这种调用了AENQy函数的DsH
方法。
给这里打上断点看看
然后一步步执行看看
第0个是不是就是object,那么就是AENQ
对象生成了一个很大的数组然后根据不同的下表获取到不同的值,也就达到了混淆的效果了。
那我们怎么整呢?用字符串替换还原?nonono 太难受这写到什么时候了。
推荐使用node
的ast
,关于ast还原可以去看看相关的文章(我这里就不介绍了)。
这里就是我们刚才看到的那个list需要单独引入进来(const {$_DAIr} = require('./ast_字符还原模型.js');
)如下图
里面写了注释可以很清楚地看到流程 注:(删除套娃节点,以及无用的变量可能会出现问题,因为有的代码可能在eval里执行,表面看起来这个变量没用,但是在eval中使用到的,导致这个变量在全局看起来没有使用,而删除的错误)
如果有这种情况建议直接使用源码的js不用使用还原过后js
// 这是文件流
const fs = require('fs');
// 解析js代码为json格式
const {parse} = require("@babel/parser");
// 根据是json格式的节点来处理json格式的js代码
const traverse = require("@babel/traverse").default;
// 节点的处理类型以及判断等等
const t = require("@babel/types");
// 根据处理好的json格式代码在还原成js代码
const generator = require("@babel/generator").default;
// 读取你的需要操作的js代码
const jscode = fs.readFileSync("./jseval.js", {encoding: "utf-8"});
// 引入你需要还原的js字符串list
const {$_DAIr} = require('./ast_字符还原模型.js');
const ast = parse(jscode);
// 字符串还原
const visitor = {
CallExpression(path) {
//加密字符还原如_DAED(39)还原成真实的字符串
const {callee, arguments} = path.node;
if (!t.isIdentifier(callee) || arguments.length !== 1) return;
if (!t.isNumericLiteral(arguments[0])) return;
path.replaceWith(t.valueToNode($_DAIr(arguments[0].value).toString()))
},
StringLiteral(path) {
// 还原"\u0024\u005f\u0049\u0042\u0052" unicode对节点extra删除还原 注:中文需要在generator代码还原时添加opts = {jsescOption: {"minimal": true}}
delete path.node.extra
}
};
// 删除无用的js代码
const remove = {
VariableDeclarator(path) {
//对于没有引用变量删除
const {id} = path.node;
const binding = path.scope.getBinding(id.name);
if (!binding || binding.constantViolations.length > 0) {
return;
}
if (binding.referencePaths.length === 0) {
path.remove();
}
},
BlockStatement(path) {
//删除没必要的套娃节点
if (path.node.body.length < 3) {
return
}
const path_var = path.node.body[0];
const path_function = path.node.body[1];
if (path_var == null || !t.isVariableDeclaration(path_var) || !path_var.hasOwnProperty('declarations')) {
return
}
if (path_var.declarations === undefined && path_var.declarations.length > 2) {
return
}
if (!t.isMemberExpression(path_var.declarations[0].init)) {
return
}
if (path_function == null && !t.isExpressionStatement(path_function)) {
return
}
delete path.node.body[0];
delete path.node.body[1];
}
};
traverse(ast, visitor);
traverse(ast, remove);
// 在还原回js
let {code} = generator(ast, opts = {jsescOption: {"minimal": true}});
//写入新的js
fs.writeFile('sense.js', code, (err) => {
});
还原过后的代码就成这个样子喽、结构看起来很清晰,我们在搜索就可以搜索到我们需要用到的js代码中了。
然后我们找到跟我们刚才获取gt,challenge,lang,client_type,w,callback
样子很像的那个位置在游览器上断点,如下图
在这里我们就可以看到w
值了,你以为这样就完了吗?
张:汤师爷你给我解释解释什么tm的是惊喜!
汤:惊喜嘛,就是惊喜。
张:汤师爷解释解释什么tm的叫tm的惊喜
黄:惊喜就是这个并不是我们要找validate
的参数。
汤:惊喜就是还有一个w
值需要找。
你会发现我们获取的这请求的值并不是我们想要的,而是get.php请求的返回值。
我们接着看获取到validate
那个请求的页面。
看到这里点进去给每一个打debugger
,测试看看它请求这个页面的时候到底是走了哪一步。
第一个是这个函数打上debugger
测试,是否走了这里。
然而并没有、
再看第二个
在点击提交,会发现debugger
住了。
这里发现还在上面,我们继续找。
到这里发现值是从这里来的。
继续我们发现原来就是r
这个值,不过这是空的那应该就是eval
这里做了什么不为人知的事情了,我们在这里debugger
看看他到底做了什么。
ok,一二三四再来一次。
我们打好断点后,再次点击,发现直接debugger
住了,好我们进去看看。
我们发现这里生成了gxjn
这个变量值。
我们在进入第二个eval
里看看,他做了什么。
没错这个值就在第二个eval
里,看看我们看到的这个值是不是我们想要的w
。
果然没错,就在这里。
好,到了这里,我们就需要再去还原还原第二个eval代码看看,里面到底做了什么,还原完成代码如下图(可以手动删除无用的for
循环和switch
and case
看起来会更清晰)。
好了,删除完成的代入如下,基本上就很清晰了,我们先去看看gxjn
这个值是怎么来的。
直接找到第一个eval那里,去还原看看。
原来就是_
对象属性生成的呀。
进去执行看看,这是使用鼠标的轨迹加密了,不过这个轨迹可以写死就行了。
这四个值直接写死某验并没有检测。
还有i
的值就是我们获取到的那个get.php
这个请求里面的参数,按照get.php
返回值进行修改就可以了(这个aeskey
不是get.php
里面的但是是重点,后面我会说到)继续找吧,其他的值应该没什么太难的了直接对应写上就行,我们继续看第二个eval
。
可以根据eval2还原的代码看到,我们需要_
对象,并排需要他的很多属性,
你可以去找找这些值都可以写死,当前如果是get.php
返回值的话直接传进去就行。
var a = [["lang", i["lang"] || "zh-cn"], ["type", "fullpage"], ["tt", tvBD(e, i["c"], i["s"]) || -1], ["light", n || -1], ["s", AWYz(OYFZ["fIeV"](t))], ["h", AWYz(OYFZ["fIeV"](r))], ["hh", AWYz(r)], ["hi", AWYz(_["LJXM"])], ["vip_order", _["vip_order"] || -1], ["ct", _["ct"] || -1], ["ep", _["hFWy"]() || -1], ["passtime", o || -1], ["rp", AWYz(i["gt"] + i["challenge"] + o)]];
小彩蛋:
(说一下这个LJXM
这个值的由来,他根据lToF里面的hqsa
也就是当前页面所有html
元素的个数生成的,里面包括了很多如下图,不过LJXM
这个值可以直接写死就行没必要非要生成。)
重点!!!重点!!!重点!!!
还记得我们用获取第一次获取get.php
这个请求吗?get.php
请求的时候生成了一个aeskey
,这个aeskey
在你提交ajax.php
这个请求的时候你还需要这个aeskey
如果两个aeskey
不一样就无法解析,所以整体来说就是get.php
请求只是给某验了一个aseskey
这个值,而ajax.php
提交的时候需要用get.php
提交的aeskey
来解析,这就是aeskey
随机数生成的。
对于其他的函数什么的你就缺什么补什么就行了,直接找到那个函数,在找到它相关的基本上没啥问题。
后面会更新有滑动的和点选的。
如有错误的地方请及时联系我-