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

资讯详情

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

JS逆向实战:破解海关数据平台AES+RSA混合加密与反调试机制

JS逆向实战:破解海关数据平台AES+RSA混合加密与反调试机制 1. 项目缘起与目标从一次数据需求说起最近在做一个跨境贸易相关的数据分析项目需要获取某海关公示平台的公开数据比如企业备案信息、商品归类裁定结果等。这些数据对市场趋势分析很有价值。然而当我像往常一样打开浏览器开发者工具准备分析一下网络请求时却发现事情没那么简单。页面加载正常但关键的查询接口返回的数据要么是乱码要么直接返回一个加密的字符串常规的Fetch/XHR抓包分析完全失效。这立刻引起了我的警觉——这个平台大概率在前端做了比较强的反爬和加密措施。这其实是一个典型的JS逆向场景。目标不是破解什么高深的系统而是理解前端JavaScript如何对请求参数进行加工以及如何对返回的密文进行解密最终还原出我们能够直接使用的明文数据。整个过程就像侦探破案需要耐心地跟踪代码执行流程理清其中的加密、混淆逻辑。今天我就把这次针对某海关公示平台的分析过程完整地记录下来重点会放在反调试对抗、代码混淆解析以及核心加解密逻辑定位这几个关键环节。无论你是数据分析师、爬虫工程师还是对Web安全感兴趣的前端开发者相信这套分析思路都能给你带来启发。2. 初探战场识别基础防御与反调试机制打开目标网站进入数据查询页面随意输入一个条件点击查询。打开Chrome DevTools的Network面板果然发现了一个关键的POST请求其Response是一段看似毫无规律的字符串类似U2FsdGVkX1...这样的Base64编码样式或者是一长串十六进制字符。这明确告诉我们数据在传输过程中被加密了。我的第一反应是查看发起这个请求的JavaScript代码。于是我切换到Sources面板准备搜索相关的URL关键词或可能的函数名。但就在这时页面突然自动刷新了并且开发者工具的控制台Console里出现了一个错误随后整个DevTools变得异常卡顿甚至浏览器标签页直接崩溃。这就是遇到了前端反调试。2.1 常见的反调试手段与应对这个平台使用的反调试技术并不罕见但组合起来效果显著。我遇到的几种包括无限Debugger循环这是最经典的手段。代码中会插入一个debugger;语句或者通过Function构造函数动态生成包含debugger的代码。当DevTools打开时执行流会不断在此处中断导致无法正常操作。检测开发者工具通过判断window.outerHeight与window.innerHeight的差值因为打开DevTools会改变窗口尺寸或者检测console.log等方法的特殊行为如对其重定向进行判断来感知DevTools是否开启。性能监控干扰有些代码会故意消耗大量性能当检测到执行时间异常因为单步调试会极大降低执行速度时触发页面跳转或死循环。我的应对策略是“以静制动”和“关键点绕过”禁用无限Debugger在Sources面板找到对应的JS文件右键行号选择“Never pause here”或者更彻底地在Event Listener Breakpoints中取消所有脚本断点的监听。对于动态生成的debugger可以尝试在控制台重写Function构造函数或eval但这可能破坏页面功能。更稳健的方法使用“条件断点”或“Overrides”功能。我个人的习惯是在疑似包含debugger语句的代码行设置一个条件断点条件为false永不触发。但更好的方法是使用Chrome的Local Overrides本地覆盖。我首先在Network面板找到加载的主JS文件右键选择Save for overrides。然后在Sources面板的Overrides标签下找到这个文件的本地副本直接搜索所有debugger;语句并将其注释掉// debugger;。刷新页面Chrome会加载你修改后的本地文件从而绕过这个防御。使用“无头”模式或插件对于复杂的检测可以启动Chrome的无头模式--headless或者使用一些屏蔽反调试的浏览器插件需谨慎评估安全性。但在本次分析中通过Local Overrides注释掉debugger已经足够。处理完反调试后页面终于可以稳定运行我也能顺畅地进行代码搜索和断点调试了。3. 深入核心解构混淆后的JavaScript代码解决了反调试下一步就是直面经过混淆的JavaScript代码。直接打开主JS文件看到的景象通常是变量名都被替换成了a,b,c,_0xabc123这样的短标识符函数调用被层层包裹字符串和关键常量被编码如十六进制\x68\x65\x6c\x6c\x6f或Unicode\u0068\u0065\u006c\u006c\u006f表示hello控制流被平坦化即原本清晰的if-else或switch逻辑被拆散成一个巨大的switch语句或数组分发器使得执行流程难以阅读。3.1 混淆代码的还原思路面对混淆我们的目标不是完全还原出可读性极高的原始代码那需要专门的解混淆工具而是理解其核心逻辑特别是找到参数加密和响应解密的入口函数。我的分析路径如下搜索关键线索在混淆的JS文件中搜索与加密可能相关的关键词。由于字符串可能被编码我直接搜索一些通用词汇的编码形式比如encrypt、decrypt、AES、DES、CryptoJS一个常用的前端加密库、JSON.stringify参数序列化等。有时也会搜索接口URL的一部分或者像param、data这样的参数名。定位网络请求发起处在Network面板中找到那个加密的POST请求右键选择Copy-Copy as cURL。然后回到Sources面板使用CtrlShiftF进行全局搜索搜索刚才复制的请求URL中的一部分独特路径。这能快速定位到发起这个fetch或XMLHttpRequest的代码位置。使用XHR/Fetch断点这是一个非常高效的方法。在Sources面板的XHR/fetch Breakpoints区域点击号添加一个包含目标URL关键字的断点例如*api/query*。当页面发起对应请求时执行流会自动暂停在发起请求的那一行JavaScript代码上。从这里开始单步调试F10步入下一个函数调用F11步进到函数内部是追溯加密逻辑的最佳起点。动态调试与观察在断点处暂停后重点观察调用栈Call Stack、作用域Scope中的变量。查看传递给请求的参数是什么样子。通常你会看到一个参数对象其中某个字段比如data、encryptedData、sign的值已经是加密后的字符串。我们的任务就是向上回溯找到生成这个加密值的函数。在本案例中通过XHR/fetch Breakpoints我成功在发起请求前中断。观察调用栈发现了一个名为_0x12a3b的函数混淆后的名字它接收一个明文对象返回一个加密字符串。这个函数就是我们的核心目标。4. 逆向实战剖析加密函数与算法识别进入_0x12a3b函数内部代码混淆严重但通过单步执行和观察变量变化可以梳理出大致流程参数序列化将传入的JavaScript对象查询条件使用JSON.stringify转化为字符串。随机密钥生成代码中动态创建了一个16字节的随机数作为本次加密的密钥key。同时还生成了一个初始化向量IV。加密算法识别跟踪代码发现它调用了CryptoJS.AES.encrypt方法。这明确了使用的是AES加密算法。进一步观察模式通常是CBC模式填充方式为PKCS7。密钥传递的奥秘AES加密本身需要密钥。如果密钥是前端随机生成的那服务端如何解密这里就是关键。我注意到生成的随机密钥key并没有直接发送给服务器。而是用另一个固定的公钥硬编码在JS中对这个随机key进行了RSA加密。最终POST请求的包体结构大致如下{ encryptedData: 这里是AES加密后的业务参数密文, encryptedKey: 这里是RSA加密后的AES密钥密文, iv: 这里是AES加密使用的IV通常是明文或Base64 }这种“RSA加密AES密钥AES加密业务数据”的方式即非对称加密与对称加密的结合在Web安全中非常常见兼顾了安全性和性能。4.1 在Node.js环境中复现加密分析清楚逻辑后下一步就是用Node.js复现这个加密过程从而能够自主构造合法的请求。我们需要以下几步提取关键参数从混淆的JS代码中找到那个固定的RSA公钥通常是一个很长的Base64字符串或PEM格式的字符串。同时确认AES的模式如CBC、填充如PKS7、以及IV的生成规则可能是固定的也可能是随机的。安装依赖库在Node.js项目中使用crypto-js进行AES加密使用node-rsa或crypto模块进行RSA加密。npm install crypto-js node-rsa编写复现代码const CryptoJS require(crypto-js); const NodeRSA require(node-rsa); // 1. 准备业务参数 const queryParams { page: 1, size: 20, // ... 其他查询条件 }; const plainText JSON.stringify(queryParams); // 2. 生成随机的AES密钥和IV (16字节) const aesKey CryptoJS.lib.WordArray.random(16); // 128位密钥 const iv CryptoJS.lib.WordArray.random(16); // 3. AES加密业务数据 const encryptedData CryptoJS.AES.encrypt(plainText, aesKey, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }).ciphertext.toString(CryptoJS.enc.Base64); // 输出Base64格式密文 // 4. RSA加密AES密钥 // 从JS中提取的公钥可能是PEM格式 const publicKeyStr -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...你的公钥内容 -----END PUBLIC KEY-----; const key new NodeRSA(publicKeyStr, pkcs8-public); key.setOptions({ encryptionScheme: pkcs1 }); // 注意加密方案可能是pkcs1或oaep const encryptedKey key.encrypt(aesKey.toString(CryptoJS.enc.Base64), base64); // 5. 构造最终请求参数 const postData { encryptedData: encryptedData, encryptedKey: encryptedKey, iv: iv.toString(CryptoJS.enc.Base64) // IV通常也Base64编码后发送 }; console.log(最终请求体:, JSON.stringify(postData, null, 2));注意以上代码是通用逻辑示例。最关键的是encryptionSchemeRSA填充方案和AES的mode、padding必须与目标网站完全一致。这些细节需要你在调试时仔细查看CryptoJS.AES.encrypt和RSA加密函数调用时的参数。5. 解密响应数据完成数据获取闭环发送我们构造的加密请求后会收到服务端的响应。响应体同样是一个包含加密数据的结构例如{ success: true, encryptedContent: U2FsdGVkX19..., otherInfo: ... }解密过程是加密的逆过程但逻辑通常在前端另一个函数中。我们需要用类似的方法找到解密函数。通常可以在接收到响应后的回调函数或者全局搜索decrypt、CryptoJS.AES.decrypt来定位。5.1 逆向解密逻辑通过调试发现解密流程如下从响应中取出encryptedContent字段Base64编码的密文。使用本次会话中相同的AES密钥注意这个密钥是前端随机生成并用RSA加密后传给服务端的服务端用私钥解密后会用这个同样的密钥加密返回数据和IV有时是新的有时复用请求的进行AES解密。解密后得到明文字符串再用JSON.parse解析成对象。在Node.js中复现解密// 假设我们从响应中拿到了 encryptedContent, 并且我们有之前生成的 aesKey 和 iv const encryptedContentFromServer U2FsdGVkX19...; // 服务端返回的密文 // 解密。注意CryptoJS.AES.decrypt 期望的密文格式可能是CipherParams对象或Base64字符串。 // 如果是Base64字符串CryptoJS能自动识别。 const decryptedBytes CryptoJS.AES.decrypt(encryptedContentFromServer, aesKey, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); // 将解密后的WordArray转为UTF-8字符串 const decryptedText decryptedBytes.toString(CryptoJS.enc.Utf8); console.log(解密后的响应:, JSON.parse(decryptedText));至此我们就完成了从“观察加密请求”到“自主构造加密请求”再到“解密响应数据”的完整闭环。整个爬虫或数据获取程序就可以基于这套加解密逻辑来实现了。6. 经验总结与进阶思考回顾这次对某海关公示平台的JS逆向过程有几点心得值得分享耐心比技巧更重要逆向工程很少是一帆风顺的。你会遇到各种反调试、复杂的混淆、动态加载的代码。耐心地设置断点、观察变量、梳理调用栈是解决问题的根本。不要指望一眼就能看懂混淆后的代码。善用开发者工具的高级功能Local Overrides、XHR/fetch Breakpoints、Event Listener Breakpoints、条件断点这些都是对抗反调试和定位代码的神器。特别是Local Overrides它能让你在本地修改JS文件并持久化生效对于干掉debugger和做一些简单的代码打桩比如console.log输出关键变量非常有用。理解常见的加密模式Web前端加密尤其是对传输数据的加密AES RSA的组合非常普遍。理解其“用RSA保护对称密钥用对称密钥加解密数据”的范式能让你快速抓住重点。此外也要留意MD5、SHA256等用于生成签名的哈希算法它们可能用于参数防篡改签名验证。注意算法的具体参数同样是AES有ECB、CBC、CFB等多种模式填充方式也有PKCS7、ZeroPadding等。RSA加密也有PKCS1、OAEP等不同填充方案。这些细节必须通过调试完全确定否则复现的加密结果会与服务端不匹配。一个技巧是在调试时将前端生成的中间变量如原始的AES密钥、IV、加密后的结果通过console.log输出与你Node.js复现的每一步结果进行比对快速定位不一致的环节。法律与道德边界最后必须强调JS逆向技术应当用于学习、安全研究以及获取公开的、法律允许范围内的数据。本次分析的海关公示平台数据本身就是公开信息我们的目的是理解其公开接口的调用方式。切勿将此技术用于破解他人系统、窃取非公开数据或进行任何非法活动。在商业项目中如果需要对这类接口进行长期稳定的数据获取最稳妥的方式是尝试联系平台方询问是否有官方的数据接口或合作方式。技术是工具用之有道方能行稳致远。
返回列表