尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

JavaScript逆向分析:从混淆代码到签名算法破解

JavaScript逆向分析:从混淆代码到签名算法破解 1. 项目背景与核心挑战去年在SWPUCTF新生赛中遇到的js_sign题目是一个典型的JavaScript逆向分析挑战。这类题目通常会考察选手对前端代码混淆、加密算法逆向以及浏览器调试工具的熟练运用能力。在实际比赛中这道题的正确解决率不足30%成为了区分新手与进阶选手的重要分水岭。这道题的核心难点在于它采用了多层嵌套的JavaScript混淆技术同时结合了自定义的签名验证机制。参赛者需要先理解代码的执行逻辑然后逆向推导出正确的签名生成算法最终才能获取到flag。整个过程涉及AST语法树分析、Hook技术应用以及加密算法逆向等多个技术维度。2. 题目环境搭建与初步分析2.1 基础环境准备首先需要搭建一个本地调试环境。推荐使用Chrome DevTools配合Node.js环境# 安装调试工具 npm install -g js-beautify npm install -g crypto-js题目通常会提供一个HTML页面和若干JS文件。使用开发者工具F12的Sources面板可以看到完整的客户端代码结构。第一步应该是对代码进行格式化处理// 使用js-beautify处理压缩代码 js-beautify -r challenge.min.js2.2 代码结构特征识别经过格式化的代码通常会显示以下特征结构(function(_0x12ab3d, _0x21d5f6) { var _0x5623a5 function(_0x4f8c62) { // 混淆代码逻辑 }; _0x5623a5.prototype._0x3f4a9c function() { // 核心验证逻辑 }; new _0x5623a5(); })(window, document);这种立即执行函数(IIFE)是JavaScript混淆的典型手法。其中变量名都被替换为十六进制形式函数逻辑被分散到多个原型方法中。我们需要重点关注包含sign、check、verify等关键词的函数片段。3. 代码反混淆实战技巧3.1 自动化反混淆工具链推荐使用以下工具组合进行初步反混淆AST解混淆使用Babel插件进行语法树还原npm install babel/core babel/parser动态Hook通过Proxy对象监控关键函数调用const handler { apply: function(target, thisArg, argumentsList) { console.log(调用 ${target.name} 参数:, argumentsList); return target.apply(thisArg, argumentsList); } }; window.crypto.subtle new Proxy(window.crypto.subtle, handler);字符串还原处理常见的字符串加密手法// 处理形如\x68\x65\x6c\x6c\x6f的十六进制编码 function decodeHexStr(str) { return str.replace(/\\x([0-9A-Fa-f]{2})/g, (_, p1) String.fromCharCode(parseInt(p1, 16))); }3.2 手动逆向关键算法在自动化解混淆后通常会暴露出核心的签名验证逻辑。典型的签名验证流程包含以下环节输入预处理对用户输入进行标准化处理function normalize(input) { return input.trim().toLowerCase().replace(/\s/g, ); }哈希计算使用特定算法生成消息摘要async function generateHash(message) { const encoder new TextEncoder(); const data encoder.encode(message); const hash await crypto.subtle.digest(SHA-256, data); return Array.from(new Uint8Array(hash)) .map(b b.toString(16).padStart(2, 0)).join(); }签名生成将哈希值与特定密钥结合运算function generateSign(hash, key) { let result ; for (let i 0; i hash.length; i) { const charCode hash.charCodeAt(i) ^ key.charCodeAt(i % key.length); result String.fromCharCode(charCode); } return btoa(result); }4. 调试技巧与问题排查4.1 断点设置策略在Chrome DevTools中设置智能断点事件监听断点在Event Listener Breakpoints中勾选所有鼠标/键盘事件DOM突变断点右键点击关键DOM元素选择Break on → Attribute modificationsXHR断点在Sources → XHR Breakpoints中添加API端点URL4.2 常见错误处理时间戳验证失败// 常见的时间戳校验逻辑 const timestamp Date.now(); if (Math.abs(timestamp - serverTime) 30000) { throw new Error(Timestamp expired); }解决方案使用本地代理修改请求时间戳或Hook Date对象const realDate Date; Date class extends realDate { constructor() { super(); return new realDate(1625097600000); // 固定时间戳 } };环境检测绕过// 检测开发者工具 if (window.outerWidth - window.innerWidth 200 || window.outerHeight - window.innerHeight 200) { alert(DevTools detected!); window.close(); }解决方案使用无头浏览器或修改窗口属性Object.defineProperty(window, outerWidth, {value: 1024}); Object.defineProperty(window, innerWidth, {value: 1024});5. 完整解题流程示例以实际题目为例演示完整解题步骤初始页面分析!-- 页面包含一个输入框和验证按钮 -- input typetext idflag-input button onclickverifyFlag()Submit/button定位核心验证函数function verifyFlag() { const input document.getElementById(flag-input).value; const sign generateSign(input); if (sign aGVsbG8gd29ybGQh) { alert(Correct! Flag: input); } else { alert(Wrong sign!); } }逆向签名算法function generateSign(input) { // 实际题目会更复杂这里做简化示例 const key secret; let result ; for (let i 0; i input.length; i) { result String.fromCharCode( input.charCodeAt(i) ^ key.charCodeAt(i % key.length) ); } return btoa(result); }构造正确输入// 已知正确签名为aGVsbG8gd29ybGQhbase64解码为hello world! function reverseSign(targetSign) { const decoded atob(targetSign); // hello world! const key secret; let flag ; for (let i 0; i decoded.length; i) { flag String.fromCharCode( decoded.charCodeAt(i) ^ key.charCodeAt(i % key.length) ); } return flag; } console.log(reverseSign(aGVsbG8gd29ybGQh)); // 输出正确flag6. 高级技巧与防护措施6.1 对抗反调试技术现代JavaScript保护方案会采用多种反调试手段无限debugger// 解决方案右键行号选择Never pause here setInterval(function() { debugger; }, 1000);性能检测// 解决方案Hook performance.now() const origPerf performance.now; performance.now function() { return 0; };函数重定义检测// 解决方案使用Proxy保持原始引用 const origFn targetFunction; targetFunction new Proxy(origFn, { apply: function(target, thisArg, args) { return target.apply(thisArg, args); } });6.2 WebAssembly逆向当核心逻辑被编译为WASM时使用wasm2c工具转换wasm2c challenge.wasm -o challenge.c关键函数HookWebAssembly.instantiate new Proxy(WebAssembly.instantiate, { apply: function(target, thisArg, args) { const instance target.apply(thisArg, args); // 修改导出函数 instance.exports.verify function() { return true; }; return instance; } });7. 实战经验与心得在多次CTF比赛中总结出以下经验代码特征速查表十六进制变量名 → 简单混淆数组位移操作 → 字符串解密大量三元运算符 → 控制流平坦化WebAssembly调用 → 核心算法保护调试效率技巧使用console.table展示结构化数据通过monitorEvents(document)捕获全局事件使用copy()函数复制调试数据到剪贴板团队协作建议使用Git管理逆向过程中的各个版本通过注释标记关键函数的作用共享HackMD笔记记录发现过程性能优化对大型数组操作使用TypedArray使用Worker处理CPU密集型任务通过Chrome Performance面板分析热点函数在实际比赛中建议先快速浏览整个代码结构标记出所有与输入验证相关的函数然后从输出点如alert/dialog反向追踪验证逻辑。遇到复杂混淆时可以尝试选择性替换变量名来提升可读性但要注意保持原有作用域关系。
返回列表