JS 混淆与反调试绕过实战
JS 混淆与反调试绕过实战前言在上一篇《JS 逆向入门读懂前端加密参数突破接口签名校验》中我们学习了定位 sign 签名函数、提取加密算法。但在真实逆向场景很多网站不会把明文 JS 直接给到你。他们会使用 JS 混淆压缩、反调试代码只要按下 F12 打开开发者工具页面立刻触发无限 debugger代码全是乱码变量名全是_0xabc123普通断点调试根本无法开展。JS 混淆用来降低代码可读性反调试用来阻止调试器动态跟踪代码执行。两者结合是前端最常用的防护手段。本文结合实战讲解各类混淆类型、反调试原理以及对应的绕过方案适合做接口安全测试、JS 逆向学习。免责声明本文内容仅用于授权环境安全测试、网络安全学习。严禁对未授权网站、H5、小程序进行逆向、反调试、批量接口请求违规操作触犯法律法规请遵守网络安全相关规定。0x01 JS 混淆的常见类型与特征JS 混淆本质不改变代码原有业务逻辑对变量、字符串、执行流程进行变形增加阅读难度。常见分为 5 类很多网站会叠加多种混淆一起使用。1.1 变量 / 标识符混淆最简单将有业务含义的变量、函数名替换为无意义十六进制名称。原始代码function getSign(params){ return md5(params); }混淆后function _0x34af(_0x98bc){ return _0x56de(_0x98bc); }特点只是改名代码执行流程不变。反混淆浏览器 Source 面板点击{}格式化代码手动重命名变量适合新手练习。1.2 字符串数组混淆高频把所有字符串统一存入数组代码中通过数组下标读取字符串字符串本身还会做 Base64、Unicode、十六进制编码全局搜索关键词sign、md5直接失效。示例const _0x44[md5,timestamp,sign]; console[_0x44[0]];处理思路在 Console 把数组打印出来或者断点在数组初始化完成之后直接打印数组内容还原原始字符串。1.3 控制流平坦化最头疼的混淆把原本线性顺序的代码拆成while switch循环跳转打乱正常执行顺序插入大量无用 case 分支。阅读代码时完全看不出原本逻辑是 JS 混淆器如 obfuscator的标志性功能。特点代码看起来循环嵌套很多大量 case阅读极其困难但代码运行逻辑不变。处理思路不硬啃全部代码优先 XHR 断点定位加密入口动态调试看实际走哪个 case 分支不需要静态通读全部代码。1.4 垃圾代码注入插入大量永远不会执行的假分支、无效函数干扰阅读。if(12){ // 永远不会执行的垃圾代码 }else{ // 真正业务逻辑 }调试的时候单步调试会自动跳过无效分支动态调试可以无视垃圾代码。1.5 动态执行混淆 eval /new Function核心逻辑字符串放在eval()、new Function()里面页面运行的时候才动态解析并执行代码。静态查看源码看不到真实业务逻辑代码是运行时生成。eval(var a1; debugger;); new Function(debugger)();这也是无限 debugger 常用写法静态检索找不到 debugger 关键字。0x02 常见 JS 反调试手段原理反调试就是 JS 代码检测当前是否开启了 Chrome DevTools 调试器如果检测到就触发 debugger、关闭页面、抛出异常打断逆向人员的调试。2.1 无限 debugger最常见两种实现方式直接写死debugger;定时器循环动态生成 debugger典型代码setInterval(function(){ new Function(debugger)(); },500)打开 F12 之后每隔几百毫秒触发一次断点F8 放行之后立刻再次断住无法正常调试。2.2 窗口尺寸检测检测开发者工具是否打开原理DevTools 打开浏览器窗口可视区域高度 / 宽度会发生变化。JS 监听 resize 事件判断窗口尺寸差值一旦检测到 DevTools 打开触发反调试逻辑。2.3 时间差检测高精度计时检测代码连续执行一段循环记录前后时间差。如果开启调试器断点会造成代码执行时间变长时间差异常判定当前正在被调试。使用performance.now()获取高精度时间。2.4 控制台 Console 检测检测 console 对象、console.log 是否被调用部分站点检测 console.toString 特征。有些站点会检测控制台面板是否打开一旦打开直接关闭页面。2.5 键盘快捷键监听监听F12、CtrlShiftI、CtrlU快捷键拦截按键禁止打开开发者工具、禁止查看源码。局限只能拦截快捷键用户通过右键检查打开开发者工具该方法失效。0x03 实战反调试绕过方案从简单到进阶3.1 方式一右键添加【一律不在此处暂停】静态 debugger 首选找到 debugger 代码行行号右键选择一律不在此处暂停。Chrome 会直接忽略这一行断点。适用写死debugger;的静态代码。缺点对 eval/new Function 动态生成的 debugger 无效。3.2 方式二条件断点设置 false右键 debugger 行添加条件断点填入false。无论代码执行到这一行条件永远为 false不会触发 debugger 暂停。3.3 方式三Console 直接重写 debugger 函数万能简单脚本打开 Console在页面触发 debugger之前执行这段代码把 debugger 置空。debugger function(){};注意如果已经断住先 F8 放行快速粘贴脚本再刷新页面。3.4 方式四Hook Function绕过 new Function (“debugger”)很多无限 debugger 是通过new Function动态创建重写 Function 构造函数拦截包含 debugger 字符串的调用const originalFunc Function; Function function(){ const str arguments[0]; if(str str.includes(debugger)){ return function(){}; } return originalFunc.apply(this,arguments); }3.5 方式五Hook setInterval干掉定时器无限 debugger针对 setInterval 循环触发 debugger 的场景Hook 定时器拦截定时执行 debugger 的任务。const originalSetInterval setInterval; setInterval function(fn,time){ if(fn.toString().indexOf(debugger) ! -1){ return null; } return originalSetInterval(fn,time); }3.6 方式六Overrides 本地文件替换静态 JS 文件终极方案Chrome Source - Overrides选择本地文件夹启用覆盖。找到目标混淆 JS 文件右键 Save for overrides本地打开文件删除所有 debugger、反调试检测代码CtrlS 保存刷新页面浏览器加载本地修改后的 JS 文件绕过全部静态反调试。适合JS 文件一次性加载不是动态 eval 生成代码的场景。缺点eval 动态执行的代码Overrides 无法修改内部字符串。3.7 方式七Tampermonkey 篡改猴脚本预注入 Hook如果页面一加载就触发反调试来不及手动在 Console 粘贴代码可以使用篡改猴脚本页面加载前自动注入 Hook 代码。简易 Tampermonkey 模板// UserScript // name Bypass AntiDebug // match https://xxx.com/* // grant none // /UserScript (function(){ debugger function(){}; const _Func Function; Function function(){ if(arguments[0] arguments[0].includes(debugger)){ return (){} } return _Func.apply(this,arguments) } })()0x04 混淆代码反混淆实战技巧绕过反调试之后我们就可以安心调试混淆代码下面是反混淆实操方法。4.1 格式化代码Source 面板打开 JS点击{}大括号按钮一键美化压缩的单行代码。混淆代码美化之后虽然变量名是乱码但代码分行能看清结构。4.2 字符串数组还原字符串数组混淆找到数组定义的位置断点在数组初始化完成之后Console 直接打印数组变量查看全部明文。console.log(_0x44)可以复制数组内容做字典替换批量替换代码中的_0x44[0]为真实字符串。4.3 控制流平坦化不用硬读静态代码控制流平坦化的代码静态阅读非常痛苦。逆向的核心不是静态读代码而是动态调试。使用 XHR 断点断住接口请求顺着调用栈跟着 F11 单步进入加密函数观察变量实时值只关注加密逻辑的执行分支忽略不会走到的 case。4.4 提取关键加密函数剥离浏览器环境找到加密逻辑之后复制函数剥离 window、document 等浏览器环境依赖放到 NodeJS 中运行验证加密结果。如果存在环境缺失如缺少 CryptoJS把对应的 JS 库一起导出。0x05 高级对抗反 Hook 检测现在很多高强度混淆站点增加反 Hook 检测用来检测开发者是否重写了原生函数。常见检测手段判断Function.toString()内容是否被篡改校验原生 API 的哈希值检测 window 对象属性是否被 Proxy 代理。遇到反 Hook 检测简单的置空 Hook 脚本直接失效。解决思路使用 Proxy 做无痕代理保留原生 toString 特征使用 CDP 协议远程调试不在页面 JS 层面 Hook更换浏览器Firefox 部分反调试检测失效。0x06 实战完整逆向流程推荐打开 Burp 抓包确定目标接口确认存在 sign 等加密参数打开 Chrome 开发者工具页面触发无限 debugger优先使用 Hook 脚本在页面加载前注入绕过反调试开启 XHR 断点触发接口请求断住代码格式化混淆 JS通过调用栈回溯定位加密入口函数动态调试还原字符串数组理清加密逻辑提取加密算法本地 NodeJS 复现验证输出结果复现成功回到 Burp Repeater自动生成签名开展接口漏洞测试。0x07 高频踩坑总结❌坑 1找到 debugger 直接 F8 放行反复无限断点没有提前 Hook 或者禁用 debugger放行之后定时器再次触发 debugger需要提前执行 Hook 脚本。❌坑 2静态通读控制流平坦化混淆代码控制流平坦化静态阅读效率极低逆向优先动态调试跟着执行流走。❌坑 3混淆绕过成功但提取函数在 Node 报错代码依赖浏览器全局对象window、navigator、crypto需要补全浏览器环境或者只提取纯算法部分。❌坑 4Hook 脚本写太晚页面 JS 已经执行完毕页面一加载就执行反调试必须 Tampermonkey 预注入脚本不能等页面加载完再粘贴。❌坑 5混淆 加密无法逆向JS 混淆只是增加阅读成本JS 代码最终必须交给浏览器解释执行只要浏览器能跑就一定能动态调试还原不存在绝对无法逆向的前端 JS。0x08 企业前端防护思路甲方视角JS 混淆、反调试只能提高逆向成本不能彻底阻止逆向不能作为接口安全的唯一防护敏感业务、资金交易逻辑不要全部放在前端 JS核心校验放在后端混淆推荐使用成熟的 JS-Obfuscator叠加控制流平坦化、字符串加密增加逆向成本增加反 Hook 检测、环境校验检测到被调试时返回虚假参数高敏感接口增加 IP 风控、行为风控、验证码不要单纯依靠前端签名 混淆。总结JS 混淆用来静态阅读防护反调试用来动态调试防护。两者组合是前端保护 JS 逻辑的常用手段。逆向对抗的核心思路绕过反调试动态调试优先不硬啃静态混淆代码。普通站点使用简单 Hook 脚本、Overrides 文件替换就可以绕过高强度站点会加入反 Hook、时间差检测需要更复杂的无痕 Hook 或者 CDP 远程调试。再次强调学习这类技术的目的是授权环境的接口安全测试。前端混淆与反调试只能阻挡普通爬虫无法阻挡具备逆向能力的测试人员后端业务校验才是安全的最后防线。一句话记住混淆是增加阅读难度反调试是打断动态跟踪浏览器能执行的 JS就一定可以被逆向分析。最后关于网络安全技术储备学好网络安全不论是就业还是做副业赚钱都不错但要学会网络安全还是要有一个学习规划。最后大家分享一份全套的网络安全学习资料给那些想学习网络安全的小伙伴们一点帮助对于0基础小白入门如果你是零基础小白想快速入门网络安全是可以考虑的。一方面是学习时间相对较短学习内容更全面更集中。二方面是可以找到适合自己的学习方案包括网安成长学习路线图、SRC黑客文档、护网行动、黑客必读书单、面试题、学习视频等教程。带你从零基础系统性的学好网络安全需要的可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】1.成长路线图学习规划要学习一门新的技术作为新手一定要先学习成长路线图方向不对努力白费。对于从来没有接触过网络安全的同学我们帮你准备了详细的学习成长路线图学习规划。可以说是最科学最系统的学习路线大家跟着这个大的方向学习准没问题。2.网安入门到进阶视频教程很多朋友都不喜欢晦涩的文字我也为大家准备了视频教程其中一共有21个章节每个章节都是当前板块的精华浓缩。全套教程文末领取哈3.SRC黑客文档大家最喜欢也是最关心的SRC技术文籍黑客技术也有收录SRC技术文籍黑客资料由于是敏感资源这里不能直接展示哦全套教程文末领取哈4.护网行动资料其中关于HW护网行动也准备了对应的资料这些内容可相当于比赛的金手指5.黑客必读书单随着互联网技术的飞速发展网络安全已经成为了当今科技领域的一大热点。这些SQL注入、CCNA、Web渗透、Linux服务器等以其强大的语言理解和防御能力正在守护着我们网络世界。 那以下这些PDF籍就是非常不错的学习资源。6.网络安全岗面试题合集当你自学到这里你就要开始思考找工作的事情了而工作绕不开的就是真题和面试题。这份完整版的网络安全学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】