recaptcha v3 无感

张开发
2026/4/20 14:16:37 15 分钟阅读

分享文章

recaptcha v3 无感
最近正在学习recaptcha v3无感流程。感觉这个验证码整体流程非常精妙全部流程都是通过MessageChannel和postMessage通讯所有数据例如传参、加解密数据。以下需要讲解的是通过babel解混淆以及 JS 代码的关键定位语句。先讲解babel解混淆它的js代码混淆非常厉害先是把函数都放到数组里面data1[function(){},function(){},function(){}]data2[function(){},function(){},function(){}]// .... 非常多然后每个函数里面是由多个分支任务实现通过第一个形参e我这里是这个来实现走向哪个分支任务跑。走向的话还是通过了非常多的计算、||也称混淆用来比较计算形参e加了非常多的嵌套// case1(e2^25)ee-31efunc1(0,1)// case2function(e,M,k,q,U,Q,u){((Q[15,call,3],e2)3)1(U.RM,c_func_15(4,M,function(){U.RbY.call(k,q);}));if((e|16)(e))y[call](this,M,0,cK.D$);returnu;}然后非常多的括号、逗号、三元运算符以及使用、||代替if判断语句一些简单的数组实现加密字符串以及使用非常多的call。流程截图先看下图片整体流程是如何的。源代码解混淆代码我抽了一些重要的部分展示的。发现有些有点重复了主要嵌套太多了但是不要紧就没有没有处理整体流程没有问题就好了太多递归处理了我怕出现异常或者不可控的解密结果我是分了很多步才跑完的中间太多了需要考虑的了。如何还原到recaptcha_zh_cn_decode.sequence.control.ifconst.dispatch.js最终文件路径recaptcha/recaptcha_zh_cn_decode.sequence.control.ifconst.dispatch.js。下面是仓库里当前实现是怎么一步步得到它的按脚本顺序前一步的输出是后一步的输入。各阶段写盘前都会用上一级的formatter_worker/FormatterWorker.js做格式化。1.babel_main.js读recaptcha_zh_cn.js用babel/parser解析成 AST。按约定模式找到要解密的调用例如f[18](...)用decode.js里的de配合e_code.txt里的数据算出明文字符串并替换。顺带做数组字面量、逗号表达式与赋值等相关整理给后面 AST 变换铺路。当前脚本把结果写到recaptcha_zh_cn_decode1.js。下一步restore_sequence.js默认读的是recaptcha_zh_cn_decode.js所以要自己复制或改名或在跑restore_sequence.js时把第一个命令行参数指向上一步的真实路径。2.restore_sequence.js可多轮输入默认recaptcha_zh_cn_decode.js可用 argv 覆盖。输出默认recaptcha_zh_cn_decode.sequence.js。作用把能安全处理的逗号表达式SequenceExpression拆成顺序语句让执行顺序摊开。多轮同一文件反复扫直到一轮里几乎不再产生改动默认最多 6 轮第三个命令行参数可改轮数。3.restore_control_flow.js可多轮输入默认recaptcha_zh_cn_decode.sequence.js。输出默认recaptcha_zh_cn_decode.sequence.control.js。作用把cond stmt这类当分支用的表达式尽量改成if (cond) { ... }等形式控制流更好读。同样是多轮 默认可调轮数道理和上一步一样。4.restore_if_e_constants.js输入默认recaptcha_zh_cn_decode.sequence.control.js。输出默认recaptcha_zh_cn_decode.sequence.control.ifconst.js。作用只处理名字符合X_func_数字、且第一个形参是e的函数。对这些函数里if的条件若能在「只涉及e与可静态推断内容」的前提下枚举出会进入分支的e就改写成[...].includes(e)对「真分支是空块、实际逻辑在另一侧」这类混淆按取反后的语义来归并避免误判。单轮脚本输入输出路径可用 argv 指定。5.restore_dispatch_by_calls.js输入默认recaptcha_zh_cn_decode.sequence.control.ifconst.js。输出默认recaptcha_zh_cn_decode.sequence.control.ifconst.dispatch.js即最终文件。作用在函数体静态推断仍不完整时到调用点收集「第一个实参是哪些常量」用来补全或修正对e的分发说明。遇到bind/apply等看不清第一个参数的情况会退化为占位或保留原条件注释推不全或列表过长时会用约定好的占位写法例如~、截断避免生成巨大无意义的数字列表。链路与命令备忘步骤脚本默认产出1babel_main.jsrecaptcha_zh_cn_decode1.js2restore_sequence.jsrecaptcha_zh_cn_decode.sequence.js3restore_control_flow.jsrecaptcha_zh_cn_decode.sequence.control.js4restore_if_e_constants.jsrecaptcha_zh_cn_decode.sequence.control.ifconst.js5restore_dispatch_by_calls.jsrecaptcha_zh_cn_decode.sequence.control.ifconst.dispatch.js在recaptcha目录下若第一步生成的是recaptcha_zh_cn_decode1.js可连续执行nodebabel_main.jsnoderestore_sequence.js ./recaptcha_zh_cn_decode1.js ./recaptcha_zh_cn_decode.sequence.jsnoderestore_control_flow.js ./recaptcha_zh_cn_decode.sequence.js ./recaptcha_zh_cn_decode.sequence.control.jsnoderestore_if_e_constants.js ./recaptcha_zh_cn_decode.sequence.control.js ./recaptcha_zh_cn_decode.sequence.control.ifconst.jsnoderestore_dispatch_by_calls.js ./recaptcha_zh_cn_decode.sequence.control.ifconst.js ./recaptcha_zh_cn_decode.sequence.control.ifconst.dispatch.jsrestore_sequence.js与restore_control_flow.js若要多轮在命令末尾加第三个参数数字即可。中途有很多这样子的也解密data[function(){return0},function(){return1},function(){return2}]// ↓data_func_0function(){return0}data_func_1function(){return1}data_func_2function(){return2}// 然后调用者 也要改动哦// 源代码data[0]()data[1]()data[2]()// 解密data_func_0()data_func_1()data_func_2()// 为什么要这样呢// 因为这样子我们静态分析流程时候按住CTRL 点击这个函数名就可以跳转所有关联的代码中vscode// 源代码是不能的解完后的代码有几个需要注意的因为我发现有很多函数调用都不是明摆着的所以看不到引用的好像都是基于匿名bind的所以会导致找不到进入分支的地方。因为我都是通过引用调用才得出那些任务是什么数字的走向。可能说的不太详细简单来说就是没有找到有关的走向。我之前是直接把所有有可能的数字都会填上的但是这个太多了我就直接放弃了现在是通过引用的目录得到分支里面的数字。三张图片解答希望您能看得懂还有一点细节if((e^35)4){}else{dG_func_32(15,f_func_17(23,28),M);}// ** 之前的取值都是以真取出来的 **// 像这样子的为什么会出现 {} 空语句都是我先通过 或者 || 这个解出来的得出if语句的// 那么如果是真的话就是空语句那对于我们而言是没有用的不管这个// 所以我们要取反那就是取假的不符合的这样子这条分支任务代码才有意义// 中间有很多这样子的语句细节非常重要的最后说明一点中间还有好多的细节我都没有处理好太复杂了或者语句有冲突可能需要一些小工具单独做的。简单说说调试这个代码的重点部分如果觉得本篇文章不错的话记得点点赞哦bro下次再见啦拜~~~

更多文章