室内装修哪家好,正规的百度快排seo,做营销策划的上哪个网站好,网站建设 尚瑞科技猿人学第三届-第二题-滑块缺口之涟漪
1. 网络包分析
1.1 目标API
https://match2025.yuanrenxue.cn/match2025/topic/2_captcha_jpg1.2 关键载荷参数
参数名: mmc作用: 这是我们需要还原的加密参数2. JS代码解混淆
2.1 初始分析
通过启动器进入JS代码后#xff0c;发现代码经过…猿人学第三届-第二题-滑块缺口之涟漪1. 网络包分析1.1 目标APIhttps://match2025.yuanrenxue.cn/match2025/topic/2_captcha_jpg1.2 关键载荷参数参数名:mmc作用: 这是我们需要还原的加密参数2. JS代码解混淆2.1 初始分析通过启动器进入JS代码后发现代码经过了严重混淆变量名使用了大量隐藏字符Unicode特殊字符代码结构被打乱如果不解混淆代码几乎无法阅读2.2 解混淆工具使用在线工具进行解混淆https://js-deobfuscator.ve重要设置✅ 勾选变量优化✅ 点击清理代码按钮移除无用代码2.3 解混淆后的问题修复第一个报错解混淆后替换原JS代码浏览器报错2:1015 Uncaught ReferenceError: yrx_ﱞﱞﱞﱞﱞﱞﱞﱞﱞ is not defined at 2:1015:13 at 2:1166:11 at 2:1181:11 匿名 2:1015 匿名 2:1166 匿名 2:1181报错位置代码{try{gyrx_ﱞﱞﱞﱞﱞﱞﱞﱞﱞ(f,newTextEncoder().encode(Navigator.prototype.userAgent.call(navigator))_|_);}catch(a){gyrx_ﱞﱞﱞﱞﱞﱞﱞﱞﱞ(f,newTextEncoder().encode(navigator.userAgentDate.now(r)));}}定位原始代码搜索关键字TextEncoder找到解混淆前的代码try{yrx_ﱞﱞﱞﱞﱞﱞﱞyrx_ﱞﱞﱞﱞﱞﱞﱞﱞﱞ(yrx_ﱞﱞﱞﱞﱞﱞ,(newTextEncoder)[encode](Navigator[prototype][userAgent][call](navigator))_|_)}catch(yrx_ﱞ){yrx_ﱞﱞﱞﱞﱞﱞﱞyrx_ﱞﱞﱞﱞﱞﱞﱞﱞﱞ(yrx_ﱞﱞﱞﱞﱞﱞ,(newTextEncoder)[encode](navigator[userAgent]Date[now](r)))}yrx_ﱞﱞﱞﱞ-61327*-122-10*527解决方案通过对比解混淆前后的代码发现函数定义在下方varyrx_ﱞﱞﱞﱞﱞﱞﱞﱞﱞfunction(yrx_ﱞﱞﱞﱞ,yrx_ﱞﱞﱞﱞﱞ){varyrx_ﱞﱞﱞﱞﱞﱞyrx_ﱞﱞ(this,(function(){returnyrx_ﱞﱞﱞﱞﱞﱞ[toString]()[search]((((.)))$)[toString]()[constructor](yrx_ﱞﱞﱞﱞﱞﱞ)[search]((((.)))$)}));yrx_ﱞﱞﱞﱞﱞﱞ();varyrx_ﱞﱞﱞﱞﱞﱞﱞyrx_ﱞﱞﱞ(this,(function(){varyrx_ﱞfunction(){varyrx_ﱞ;try{yrx_ﱞFunction(return (function() ({}.constructor(return this)( )));)()}catch(yrx_ﱞﱞ){yrx_ﱞwindow}returnyrx_ﱞ}// ... 更多代码}));}我们需要手动重命名这个函数。将其重命名为Getutf8arry// 修改后的代码try{gGetutf8arry(f,newTextEncoder().encode(Navigator.prototype.userAgent.call(navigator))_|_);}catch(a){gGetutf8arry(f,newTextEncoder().encode(navigator.userAgentDate.now(r)));}d-8;同时修改函数定义varGetutf8arryfunctione(d,f){varia||27;for(;;){if(i16){if(i8){if(i4){if(i2){if(i1){j[l]255-l;i14;}else{// ... 更多代码}}}}}}}第二个报错再次测试后发现还有一个报错位置}elseif(c11){edocument.getElementById(captchaCanvas);c-3;}else{kyrx_ﱞﱞﱞﱞﱞﱞﱞﱞﱞ(f,j);// 这里还有一个未重命名的调用c-7;}修改为}elseif(c11){edocument.getElementById(captchaCanvas);c-3;}else{kGetutf8arry(f,j);// 修改函数名c-7;}修改完成后刷新页面图片正常显示说明解混淆已完成。3. 加密逻辑分析3.1 核心加密函数分析从我们重命名的Getutf8arry函数入手分析其返回值try{gGetutf8arry(f,newTextEncoder().encode(Navigator.prototype.userAgent.call(navigator))_|_);}catch(a){gGetutf8arry(f,newTextEncoder().encode(navigator.userAgentDate.now(r)));}关键发现try块是混淆陷阱new TextEncoder().encode(...) _|_会报错因为 Uint8Array 不能与字符串相加真正的加密在catch块实际执行的是navigator.userAgent Date.now(r)返回值g是一个长度为124的Uint8Array数组这是典型的RC4 加密算法的特征。3.2 加密参数还原参数1加密数据data// 真正的加密数据生成newTextEncoder().encode(navigator.userAgentDate.now(r))还原为函数/** * 生成加密所需的输入数据UA 时间戳 * returns {Uint8Array} 输入数据的字节数组 */functiongenerateInputData(){// 获取浏览器的 UserAgentconstuserAgentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36// 获取当前时间戳consttimestampDate.now().toString();// 将UA和时间戳合并constcombinedStringuserAgenttimestamp;// 转换为字节数组returnnewTextEncoder().encode(combinedString);}参数2加密密钥key通过调试将密钥f转换为字符串console.log(newTextDecoder().decode(f))// 输出: b31ac05816bb570d发现是一个16位的随机字符串。向上查找代码找到密钥生成逻辑if(d12){if(d10){if(d9){eArray.apply(null,Array(16)).map(function(){varba||0;for(;;){return0123456789abcdef0123456789abcdef0123456789abcdef6789abcdef789abcdef6789abcdef.charAt(Math.floor(Math.random()*62));}}).join();// ... 更多代码}}}验证0123456789abcdef0123456789abcdef0123456789abcdef6789abcdef789abcdef6789abcdef.charAt(Math.floor(Math.random()*62));// d0123456789abcdef0123456789abcdef0123456789abcdef6789abcdef789abcdef6789abcdef.charAt(Math.floor(Math.random()*62));// e0123456789abcdef0123456789abcdef0123456789abcdef6789abcdef789abcdef6789abcdef.charAt(Math.floor(Math.random()*62));// f每次运行生成一个字符Array(16)生成16个字符与密钥长度一致b31ac05816bb570d.length// 16还原为函数/** * 生成16位随机字符串作为加密密钥 * returns {string} 16位随机字符串 */functiongenerateRandomKey(){constcharset0123456789abcdef0123456789abcdef0123456789abcdef6789abcdef789abcdef6789abcdef;constkeyArray.apply(null,Array(16)).map(function(){returncharset.charAt(Math.floor(Math.random()*62));}).join();returnkey;}将密钥转换为 Uint8Array/** * 将密钥字符串转换为Uint8Array * param {string} key - 密钥字符串 * returns {Uint8Array} 字节数组 */functionkeyToByteArray(key){returnnewTextEncoder().encode(key);}3.3 RC4加密算法还原将Getutf8arry函数复制到本地/** * RC4变种加密算法 * param {Uint8Array} key - 加密密钥16字节 * param {Uint8Array} data - 待加密数据 * returns {Uint8Array} 加密后的数据 */functionrc4Encrypt(key,data){vari27;// 固定为27通过单步调试确定//此处省略自行复制}重要修改将i a || 27改为固定值i 27通过单步调试确定3.4 Token格式化找到将加密数组转换为最终token的位置。全局搜索mmcelseif(d3){h.data{mmc:Array.from(g).map(function(b){varca||0;for(;;){returnb.toString(16).padStart(2,0);}}).join()e};}单步调试后验证h.data// {mmc: 6a014756a723eec83e1ff6bf8e0ad3e420df3c896f60768a70...543f677c3633be4ab9ab1c9dc1c72e7e3d3f3db51a0982988}还原为函数/** * 将加密结果转换为token格式 * param {Uint8Array} encryptedData - 加密后的数据 * param {string} key - 原始密钥 * returns {object} 包含mmc字段的数据对象 */functionformatToken(encryptedData,key){constresult{data:{mmc:Array.from(encryptedData).map(function(byte){returnbyte.toString(16).padStart(2,0);}).join()key}};returnresult;}格式说明将每个字节转换为2位16进制字符串所有字节拼接后追加原始密钥最终形成完整的mmc参数4. 完整代码实现constUSER_AGENTMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36/** * 生成16位随机字符串作为加密密钥 * returns {string} 16位随机字符串 */functiongenerateRandomKey(){constcharset0123456789abcdef0123456789abcdef0123456789abcdef6789abcdef789abcdef6789abcdef;constkeyArray.apply(null,Array(16)).map(function(){returncharset.charAt(Math.floor(Math.random()*62));}).join();returnkey;}/** * 将密钥字符串转换为Uint8Array * param {string} key - 密钥字符串 * returns {Uint8Array} 字节数组 */functionkeyToByteArray(key){returnnewTextEncoder().encode(key);}/** * 生成加密所需的输入数据UA 时间戳 * returns {Uint8Array} 输入数据的字节数组 */functiongenerateInputData(){// 获取浏览器的 UserAgentconstuserAgenttypeofnavigator!undefined?navigator.userAgent:USER_AGENT;// 获取当前时间戳consttimestampDate.now().toString();// 将UA和时间戳合并constcombinedStringuserAgenttimestamp;// 转换为字节数组returnnewTextEncoder().encode(combinedString);}/** * RC4变种加密算法 * param {Uint8Array} key - 加密密钥16字节 * param {Uint8Array} data - 待加密数据 * returns {Uint8Array} 加密后的数据 */functionrc4Encrypt(key,data){// 此处省略具体实现请从原始JS中复制完整的rc4Encrypt函数// 记得将 i a || 27 改为 i 27}/** * 将加密结果转换为token格式 * param {Uint8Array} encryptedData - 加密后的数据 * param {string} key - 原始密钥 * returns {object} 包含mmc字段的数据对象 */functionformatToken(encryptedData,key){constresult{data:{mmc:Array.from(encryptedData).map(function(byte){returnbyte.toString(16).padStart(2,0);}).join()key}};returnresult;}/** * 生成完整的加密token * returns {string} 加密后的token字符串 */functiongenerateToken(){// 1. 生成随机密钥constkeygenerateRandomKey();console.log(生成密钥:,key);// 2. 将密钥转换为字节数组constkeyByteskeyToByteArray(key);console.log(密钥字节数组:,keyBytes);// 3. 生成输入数据UA 时间戳constinputDatagenerateInputData();console.log(输入数据:,inputData);// 4. 执行RC4加密constencryptedDatarc4Encrypt(keyBytes,inputData);console.log(加密结果:,encryptedData);// 5. 格式化为最终tokenconsttokenDataformatToken(encryptedData,key);returntokenData.data.mmc;}// 使用示例consttokengenerateToken();console.log(最终Token:,token);5. 总结5.1 加密流程生成随机密钥16位随机字符串从特定字符集中选择准备加密数据UserAgent 当前时间戳RC4加密使用密钥对数据进行RC4变种加密格式化输出将加密结果转为16进制字符串并追加原始密钥5.2 关键技术点混淆陷阱try块中的代码会报错真正的逻辑在catch块RC4算法使用了RC4的变种实现状态机控制通过变量i控制执行流程固定为27Token格式加密数据的16进制表示 原始密钥5.3 注意事项时间戳必须与服务器时间同步UserAgent必须与浏览器一致密钥每次请求都需要重新生成最终的mmc参数包含加密数据和密钥两部分