你只管努力,剩下的交给天意。
文章只提供学习,如有侵权请立即联系我。
本文章更新部分有误以更新,抱歉!!!
前言
某验官网:官网
总体来说某验的验证码配合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
方法。
随便的debugger
一下看看这个值是什么。
给这里打上断点看看
然后一步步执行看看
第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
住了。
会发现s
的值里面就是我们需要找的
ok这里查看调用栈看看这个变量是在哪里是哪生成的。
这里发现还在上面,我们继续找。
到这里发现值是从这里来的。
继续我们发现原来就是r
这个值,不过这是空的那应该就是eval
这里做了什么不为人知的事情了,我们在这里debugger
看看他到底做了什么。
ok,一二三四再来一次。
我们打好断点后,再次点击,发现直接debugger
住了,好我们进去看看。
我们发现这里生成了gxjn
这个变量值。
我们在进入第二个eval
里看看,他做了什么。
没错这个值就在第二个eval
里,看看我们看到的这个值是不是我们想要的w
。
果然没错,就在这里。
好,到了这里,我们就需要再去还原还原第二个eval代码看看,里面到底做了什么,还原完成代码如下图(可以手动删除无用的for
循环和switch
and case
看起来会更清晰)。
好了,删除完成的代入如下,基本上就很清晰了,我们先去看看gxjn
这个值是怎么来的。
直接找到第一个eval那里,去还原看看。
原来就是_
对象属性生成的呀。
看看这几个值都是什么吧。
进去执行看看,这是使用鼠标的轨迹加密了,这个轨迹不能写死,不然只能成功一次。
e值加密的方法如下
function encode_fIeV(e) {
var i = "()*,-./0123456789:?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
function SwsR(e) {
var t = "";
var r = e["length"] / 6;
for (var n = 0; n < r; n = n + 1) {
t += i["charAt"](parseInt(e["slice"](n * 6, (n + 1) * 6), 2));
}
return t;
}
function t(e) {
var t = [];
var r = [];
var n = [];
var i = [];
for (var o = 0, a = e["length"]; o < a; o = o + 1) {
var s = e[o];
var _ = s["length"];
t["push"](s[0]);
r["push"](_ === 2 ? s[1] : s[2]);
if (_ === 3) {
n["push"](s[1][0]);
i["push"](s[1][1]);
}
}
var c = g(t);
var l = YlIj(r, false);
var u = YlIj(n, true);
var f = YlIj(i, true);
var p = c + l + u + f;
var d = p["length"];
if (d % 6 != 0) {
p += Tr_F(0, 6 - d % 6);
}
return SwsR(p);
}
var p = {
"move": 0,
"down": 1,
"up": 2,
"scroll": 3,
"focus": 4,
"blur": 5,
"unload": 6,
"unknown": 7
};
var d = 8;
function Tr_F(e, t) {
var r = e["toString"](2);
var n = r["length"];
var i = "";
for (var o = n + 1; o <= t; o = o + 1) {
i += "0";
}
r = i + r;
return r;
}
function g(e) {
var t = [];
var r = e["length"];
var n = 0;
while (n < r) {
var i = e[n];
var o = 0;
while (true) {
if (o >= 1 << 4) {
break;
}
var a = n + o + 1;
if (a >= r) {
break;
}
var s = e[a];
if (s !== i) {
break;
}
o += 1;
}
n = n + 1 + o;
var _ = p[i];
if (o != 0) {
t["push"](_ | d);
t["push"](o - 1);
} else {
t["push"](_);
}
}
var c = Tr_F(r | 32768, 16);
var l = "";
for (var u = 0, f = t["length"]; u < f; u = u + 1) {
l += Tr_F(t[u], 4);
}
return c + l;
}
function UyHc(e, t) {
var r = [];
for (var n = 0, i = e["length"]; n < i; n = n + 1) {
r["push"](t(e[n]));
}
return r;
}
function VySC(e, t) {
var r = [];
UyHc(e, function (e) {
if (t(e)) {
r["push"](e);
}
});
return r;
}
function WaSD(e) {
var t = (1 << 15) - 1;
e = UyHc(e, function (e) {
if (e > t) {
return t;
} else if (e < -t) {
return -t;
}
return e;
});
var r = e["length"];
var n = 0;
var i = [];
while (n < r) {
var o = 1;
var a = e[n];
var s = Math["abs"](a);
while (true) {
if (n + o >= r) {
break;
}
if (e[n + o] !== a) {
break;
}
if (s >= 127 || o >= 127) {
break;
}
o += 1;
}
if (o > 1) {
i["push"]((a < 0 ? 49152 : 32768) | o << 7 | s);
} else {
i["push"](a);
}
n += o;
}
return i;
}
function XyMI(e, t) {
if (e === 0) {
return 0;
}
return Math["log"](e) / Math["log"](t);
}
function YlIj(e, t) {
e = WaSD(e);
var r = [],
n = [],
i;
UyHc(e, function (e) {
var t = Math["ceil"](XyMI(Math["abs"](e) + 1, 16));
if (t === 0) {
t = 1;
}
r["push"](Tr_F(t - 1, 2));
n["push"](Tr_F(Math["abs"](e), t * 4));
});
var o = r["join"]("");
var a = n["join"]("");
if (!t) {
i = "";
} else {
i = UyHc(VySC(e, function (e) {
return e != 0 && e >> 15 != 1;
}), function (e) {
return e < 0 ? "1" : "0";
})["join"]("");
}
var s = Tr_F(e["length"] | 32768, 16);
return s + o + a + i;
}
return t(e);
}
function RandomNum(minNum, maxNum, decimalNum) {
var max = 0,
min = 0;
minNum <= maxNum ? (min = minNum, max = maxNum) : (min = maxNum, max = minNum);
switch (arguments.length) {
case 1:
return Math.floor(Math.random() * (max + 1));
break;
case 2:
return Math.floor(Math.random() * (max - min + 1) + min);
break;
case 3:
return (Math.random() * (max - min) + min).toFixed(decimalNum);
break;
default:
return Math.random();
break;
}
}
var sliding_len = RandomNum(60, 100),
start_x = RandomNum(600, 1000),
start_y = RandomNum(300, 350),
init_x = RandomNum(-70, -40),
trajectory = new Array();
trajectory.push(['move', [start_x, start_y], 0]);
for (var s = 0; sliding_len > s; s++) {
init_x += RandomNum(5, 7);
if (Math.abs(init_x) >= 10) {
trajectory.push(['move', [init_x, RandomNum(-7, 7)], RandomNum(10, 25)])
} else {
trajectory.push(['move', [0 + RandomNum(-1, 2), RandomNum(-3, 5)], RandomNum(10, 20)])
init_x = 0
}
if (s === sliding_len - 1) {
trajectory.push(["down", [0, 0], RandomNum(2, 4)]);
trajectory.push(["up", [0, 0], RandomNum(60, 100)]);
}
}
e = encode_fIeV(trajectory);
这四个值就只检测了e
的值其他的写死就好了,像这样↓↓↓
还有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
随机数生成的。
对于其他的函数什么的你就缺什么补什么就行了,直接找到那个函数,在找到它相关的基本上没啥问题。
后面会更新有滑动的和点选的。
如有错误的地方请及时联系我-