主要讲解最重要的数组加密流程
先定位到这里,前面就不赘述了,不重要的部分先略过。
f_func_26(19,0,null,[d,f_func_25(1,false,1,4,18),RY9(W_func_7(67),void0,void0,d,this.H.Z),AQt(),vV1(),oV1(),JQo(),u])// 这个流程其实就是 Promise.all([d, f_func_25(1, false, 1, 4, 18), RY9(W_func_7(67), void 0, void 0, d, this.H.Z), AQt(), vV1(), oV1(), JQo(), u])[d,f_func_25(1,false,1,4,18),RY9(W_func_7(67),void0,void0,d,this.H.Z),AQt(),vV1(),oV1(),JQo(),u]// 所以这个的结果,就等于 then(function(b, B, C, h, x, Y, r, V, K, F, P, z, S) 里面的第一个入参。它是一个 8 位数组,和上面的结果一一对应。P=R_func_17(13,b);// 这个就是一个迭代器,可以这样理解:下面就是按照顺序提取出每一项的结果r=P.next().value;// dK=P.next().value;// f_func_25(1, false, 1, 4, 18)B=P.next().value;// RY9(W_func_7(67), void 0, void 0, d, this.H.Z)z=P.next().value;// AQt()h=P.next().value;// vV1()S=P.next().value;// oV1()Y=P.next().value;// JQo()C=P.next().value;// u// 我们需要的是 r.xp 数据,也就是第一个数据 r。它对应的就是 d 这个函数- 可以看到,
d这个函数就在上面
d=lHd(W_func_7(68),a_func_37(18)).then(function(b,B){returnf_func_37(24,function(C,h){if(C.H==1)returna_func_14(12,2,Q.GE.send(dz,newgBj()),C);B=C.D;b.im(B.xp);returnC.return(B);});});- 那么我们就要进入到
c_func_47里面的83分支返回的函数
接下来就是重点了,
首先,我们需要知道 recaptcha v3 的流程是怎么运行的。
如果仔细看,就会发现同样的js代码会加载两次,也就是请求两次https://www.gstatic.com/recaptcha/releases/[key]/recaptcha__zh_cn.js。
一个是主线程的(通过 HTML 加载),一个是子线程的(通过 JS 加载)。
为什么要这样呢?
我之前的文章里说过,它里面基本都是通过postMessage通信,把数据当作参数传递,然后返回结果。
假设一下:
main 主线程
anchor 子线程
那么流程就是这样的:
anchor 发送待加密的参数
main 接收参数,加密完成后返回结果
现在我们来复现一下。
会发现有几个参数传入了。
// 意思如下:dz='a'// 它向 iframe/channel 发送了一个 "a" 消息newgBj()='...'// 附带的参数现在我们直接去搜索这个dz,发现刚好有三处(三张图片放到一起了)。
看到上面这个图片了吧,我在另一个地方下了一个断点。
直接点击右上角的“继续执行脚本”(F8)。
看到规律了吧,这就是整个项目的核心:几乎所有的数据都是这种模式,发送通知、接收参数、加密后返回。
回到加密流程这个主题。
当前需要进入到H_func_36里面的10分支。
F=function(E){E.im(K);// 这是一个回调,用于把加密数据回填到数组里面returnE.CN();// 把结果传给下一次调用,用作下一个函数的入参,应该是这样的}O_// 这里面有很多函数,是对应加密用的。现在我这里是 35 个by// 这是对应上面每个函数在数组里面的位置returnc_func_47(80,x[E.sc],V[E.sc]).call(u,v,h,E.sc);// 这里是真正调用的位置,E.sc 从 0 开始,一直递增调用// 后面有一个 call,说明第一次返回的是一个函数,然后接着又调用了一次进入到c_func_47里面的80分支。
第一次会返回return f_func_37(24, function(n, E, v, A) {...这个函数,后面又通过call调用。
// 这里就直接快速定位// 之前是这样调用的c_func_47(80,x[E.sc],V[E.sc]).call(u,v,h,E.sc);// 第二个参数就是函数// 那么我们就直接去 c_func_47 里面找第二个参数在哪里被用到了// M 就是第二个入参// 然后直接找 M 是在哪里被调用的// 调用函数最常见的方式就是 call 或者 apply// 这里面只有这里调用了 MK=I_func_3(1,5,3,E[0],"",function(){returnM.apply(F,V);},U);// 后面这个就是调用并生成结果,K.D(N) 就是结果a_func_14(36,E[1],K.D(N),n);这里面非常复杂,我直接简化了,其实大致就是这样的:
直接进入到t_func_31里面的64分支。
插入日志后,就可以输出非常多的数据。
最后得到了很多数据,但是还有一些在附近,需要继续找一下。这里目前只有 35 层加密的数据(现在不知道还有多少)。
中途这些数据就比较好找了。
到这里,这个大数组的加密流程就结束了。
然后再提一些其他的内容。
这里有一个地方收集了一些浏览器属性,最后做了哈希加密。
U=W_func_9(4,"Silk","MSIE","CriOS",true);最终加密的数据全部集中到了这里。
进入到f_func_9里面的35分支。
然后进入到M7里面。
varM7=function(e){returnH_func_40.call(this,1,e);};跳转进入到M.B这里。
最终的数据
这个项目里面的 JS 代码,我全部通过之前博客里的 Babel 方式还原了,并且支持跑通到加密的位置。但是你会发现,上面的数组里有检查 JS 地址的逻辑,所以过网站就不要想了(也没有必要过)。
主要是用来跑流程、学习知识点。