当前位置: 首页 > news >正文

SpiderDemo题解系列——第3篇:调试拦截与非对称加密挑战 — JS逆向调试实战(第23题)

目录

  • 一、分析和扣代码
  • 二、请求测试
  • 三、视频讲解

一、分析和扣代码

调试拦截原理参考文章:https://blog.csdn.net/xw1680/article/details/153742917
绕过参考文章:https://blog.csdn.net/xw1680/article/details/138547184

按照常规流程按下 F12 打开浏览器调试工具时,触发 debugger 断点,如下图所示:

在这里插入图片描述

简单观察一下这些堆栈是如何生成的 debugger,了解过其原理后你就会发现——它通常是通过 Function 构造函数动态创建的。接下来我们可以向上追溯调用栈,看看具体的生成位置:

在这里插入图片描述在这里插入图片描述

发现这是通过 Function 构造函数直接传入字符串 "debugger" 生成的,这种方式触发的断点可以直接通过选择 "Never pause here 来跳过。回到断点位置后,按以下步骤操作即可:

此时又出现了如下所示的 debugger 断点:
在这里插入图片描述

学习过原理后我们就能判断,这种类型的 debugger 已经无法通过 Never pause here 绕过。虽然形式上依然是通过 Function 构造函数生成的,但这次传入的字符串是动态拼接的,将 debugger 与时间戳等变量结合使用。接下来,我们回到堆栈查看具体的调用位置:

在这里插入图片描述
我们先继续往下执行,观察接下来是否还会出现其他断点,然后再统一处理这些 debugger 断点:

在这里插入图片描述
这个断点学习过原理后也应该很熟悉——它是通过 eval 动态生成的,传入的字符串同样是动态拼接的,将 debugger 与时间戳结合使用。接下来,我们回溯堆栈查看具体调用位置:
在这里插入图片描述
它还有一层嵌套,我们继续往上查看:

在这里插入图片描述

可以发现,它正位于我们之前分析的 Function 堆栈的下方,属于同一条控制流。接下来我们就可以去仔细查看这一整块代码的来源以及调用方式,结果发现:
在这里插入图片描述
至此,我们已经追溯到了源头——原来是一个 setInterval 定时器,每隔 500 毫秒执行一次 debug 函数。解决方案可以有三种:

  1. 直接修改源代码:将 setInterval(debug, 0x1f4); 注释掉
  2. Hook setInterval 函数:拦截定时器逻辑
  3. Hook Function 和 eval 函数:拦截动态生成的 debugger 语句

笔者尝试了方案 ①,会导致网页跳转到其他页面,推测有额外检测机制;相比之下,方案 ② 和 ③ 更可行。为了简化操作,这里选择 Hook setInterval,对应的 hook 脚本如下:

_setInterval = setInterval
setInterval = function (a,b) {if(a.toString().indexOf('debugger') == -1){return function(){}}else{_setInterval(a,b)}
}Function.prototype.toString = function () {return `function ${this.name}() { [native code] }`
}

使用 hook 脚本时,需要特别注意 hook 的时机,否则可能失效或引发错误。操作步骤如下:首先在页面中打上 script 执行断点:

按下 Ctrl + F5 强制刷新网页:

在控制台输入并执行 hook 脚本后,切换到 Sources 面板,先取消之前勾选的 script 脚本断点,然后点击下方按钮继续操作:
在这里插入图片描述
可以发现,此时我们已经正常进入网页,并且无限 debugger 已经被成功屏蔽,页面可以正常调试了。接下来切换到 Network 面板,点击页面上的页码,观察对应的数据包,会很容易发现请求参数和请求头中存在加密处理,如下所示:
在这里插入图片描述
在这里插入图片描述
同样地,我们可以通过搜索关键字快速定位到加密逻辑所在的位置:

return l.interceptors.request.use(function(e) {var n, t, r = e.url.match(/\/page\/(\d+)\//);if (r) {var c = parseInt(r[1]), a = new URLSearchParams(e.url.split("?")[1] || "").get("challenge_type") || "fsymmetry_challenge", o = Date.now(), s = "".concat(c, "_").concat(a, "_").concat(o);e.headers["X-Auth-Key"] = u(s),e.headers["X-Signature"] = (n = s,(t = new JSEncrypt).setPrivateKey("-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC1vKwZUIv7pgpJUXXPpDlD4+VEon3a0ANOrNmqAESrcGfkmYzD\nCo2JeuYezhBGjBNjwVmSct/Y3BBOCRGT2bvtCJGdS12RMvHbFcdbwS/Adh48+rhL\niMNYXLm+7pI3e2k6TlScxKa7EeeZpVtew/Cv5z6ol0llNPp6BdqAlOa8DwIDAQAB\nAoGAS0GaWI9AsFAFEXBgoz/jkMf14DKTgEFEJVexeNLMnNuawhCNuBSOIMCaO2Zk\nWfpWaygdUeYs6M3UGKRruXhf92g/BRmJK5FzR0kWW4qw6WwlYob3TPc3c9MFOjmp\nVtWQ0VSeEPrnBNoQRccKl0dGBnToHGuV+KEuKx8oWZc/JM0CQQDH/cvlx0BKz2zN\n6PM8FidAvc+Wgon8YW81KJgC7iJIrK9FOpctOE3L1pdF7guOQNVGRqN4HCIgLfHE\ncqxWJKJtAkEA6KIkwHe/Q23uWH5GP8DHtVkLVfohTumYkpb0rk05EYQ0dsWSNzWH\nXDH/kD6ayNq+fscnS8g+59onzvfhJ0bq6wJBAKNFkDEHenWY4js481sauvEgBVnb\nOMvSv/emLHQ39cVfNbhPHRzN2rWPe/CbZtO8GmJFSS/FyBZ9a+P1uryZLAECQAaw\nApZ12s25b0yj9KkIhbU05hqGokZ+eKBeLpKELcvPHSL88wMbStTfqxUed5ymjStf\n1kVbcFOB9fsBLTvP0hkCQFCON0l1VjFli+vqfN0lypgIqCf85V6FZFN19creGCCd\n76pX/X2FIBbUSDN1z48SM5I/RKdCkTx7FY+509q2Mek=\n-----END RSA PRIVATE KEY-----"),t.sign(n, CryptoJS.SHA256, "sha256") || "");var i = function(e) {return CryptoJS.HmacSHA256(e, "dsa_secret_key_2025").toString()}(s), l = u(s + "_param"), f = e.url.includes("?") ? "&" : "?";e.url += "".concat(f, "data=").concat(encodeURIComponent(l), "&verify=").concat(i, "&t=").concat(o)}return e}

观察上方的核心代码后,可以将其改写为如下形式:

//需要u函数,JSEncrypt,CryptoJS,所以改写如下: 
//你要从网页中将 jsencrypt.min.js Copy到本地,并且补一下window
// 如果改写不太懂的话 可以去看我录制的视频,里面有详细讲解,这里就不再赘述
window = globalThis;let CryptoJS = require('./CryptoJS')
let JSEncrypt = require('./jsencrypt.min.js')function u(e) {var n = new JSEncrypt;return n.setPublicKey("-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1vKwZUIv7pgpJUXXPpDlD4+VE\non3a0ANOrNmqAESrcGfkmYzDCo2JeuYezhBGjBNjwVmSct/Y3BBOCRGT2bvtCJGd\nS12RMvHbFcdbwS/Adh48+rhLiMNYXLm+7pI3e2k6TlScxKa7EeeZpVtew/Cv5z6o\nl0llNPp6BdqAlOa8DwIDAQAB\n-----END PUBLIC KEY-----"),n.encrypt(e) || ""
}function encrypt(page, timestamp){let obj = {};let a = "fsymmetry_challenge";let s = "".concat(page, "_").concat(a, "_").concat(timestamp);obj["X-Auth-Key"] = u(s)let t = new JSEncrypt;t.setPrivateKey("-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC1vKwZUIv7pgpJUXXPpDlD4+VEon3a0ANOrNmqAESrcGfkmYzD\nCo2JeuYezhBGjBNjwVmSct/Y3BBOCRGT2bvtCJGdS12RMvHbFcdbwS/Adh48+rhL\niMNYXLm+7pI3e2k6TlScxKa7EeeZpVtew/Cv5z6ol0llNPp6BdqAlOa8DwIDAQAB\nAoGAS0GaWI9AsFAFEXBgoz/jkMf14DKTgEFEJVexeNLMnNuawhCNuBSOIMCaO2Zk\nWfpWaygdUeYs6M3UGKRruXhf92g/BRmJK5FzR0kWW4qw6WwlYob3TPc3c9MFOjmp\nVtWQ0VSeEPrnBNoQRccKl0dGBnToHGuV+KEuKx8oWZc/JM0CQQDH/cvlx0BKz2zN\n6PM8FidAvc+Wgon8YW81KJgC7iJIrK9FOpctOE3L1pdF7guOQNVGRqN4HCIgLfHE\ncqxWJKJtAkEA6KIkwHe/Q23uWH5GP8DHtVkLVfohTumYkpb0rk05EYQ0dsWSNzWH\nXDH/kD6ayNq+fscnS8g+59onzvfhJ0bq6wJBAKNFkDEHenWY4js481sauvEgBVnb\nOMvSv/emLHQ39cVfNbhPHRzN2rWPe/CbZtO8GmJFSS/FyBZ9a+P1uryZLAECQAaw\nApZ12s25b0yj9KkIhbU05hqGokZ+eKBeLpKELcvPHSL88wMbStTfqxUed5ymjStf\n1kVbcFOB9fsBLTvP0hkCQFCON0l1VjFli+vqfN0lypgIqCf85V6FZFN19creGCCd\n76pX/X2FIBbUSDN1z48SM5I/RKdCkTx7FY+509q2Mek=\n-----END RSA PRIVATE KEY-----")obj["X-Signature"] = t.sign(s, CryptoJS.SHA256, "sha256")obj["verify"] = function(e) {return CryptoJS.HmacSHA256(e, "dsa_secret_key_2025").toString()}(s);obj["data"] = u(s + "_param");return obj
}

至此,JS 逆向的核心代码已经完成。接下来,我们就可以进行请求测试,验证生成的参数是否正确。

二、请求测试

完整代码实现:

# -*- coding: utf-8 -*-
"""
@File    : t23.py
@Author  : bb_bcxlc
@Date    : 2025-10-22 21:28
@Blog    : https://blog.csdn.net/xw1680
@Tool    : PyCharm
@Desc    : 
"""
import timeimport requests
import execjsheaders = {"accept": "application/json, text/plain, */*","accept-language": "zh-CN,zh;q=0.9","user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36","x-auth-key": "","x-signature": ""
}
cookies = {"sessionid": "写你自己的sessionid"
}
url_template = "https://www.spiderdemo.cn/authentication/api/fsymmetry_challenge/page/{}/"
ctx = execjs.compile(open('./t23.js', 'r', encoding='utf-8').read())
sum_ = 0for i in range(1, 101):timestamp = int(time.time() * 1000)result = ctx.call("encrypt", i, timestamp)headers["x-auth-key"] = result["X-Auth-Key"]headers["x-signature"] = result["X-Signature"]data = result["data"]verify = result["verify"]params = {"challenge_type": "fsymmetry_challenge","data": data,"verify": verify,"t": f"{timestamp}"}response = requests.get(url_template.format(i), headers=headers, cookies=cookies, params=params)sum_ += sum(response.json().get("page_data"))print(response.text)print("100页的和为: ", sum_)

三、视频讲解

https://www.bilibili.com/video/BV1CJshzTEix/?spm_id_from=333.1387.homepage.video_card.click&vd_source=aea7edccf28831a3bbbb6e836a563988

感谢 spiderdemo 提供本次练习用的靶场环境。若需实操练习,请访问 (spiderdemo)。

http://www.dtcms.com/a/520150.html

相关文章:

  • 机器学习之数字识别
  • 网站开发群安阳网站设计多少钱
  • 7. Prometheus告警配置-alertmanger
  • 自动签到之实现掘金模拟签到
  • 【探寻C++之旅】C++11 深度解析:重塑现代 C++ 的关键特性
  • 【unity】运行时加载并修改ScriptableObject类型资源对象的值会怎样
  • Spring Boot 实现 DOCX 转 PDF(基于 docx4j 的轻量级开源方案)
  • 服装企业官方网站建设网站运营收入
  • Spring Boot Actuator深度解析与实战
  • 如何做 行业社交类网站网站 建设在作用
  • 线程3 JavaEE(阻塞队列,线程池)
  • K8s中,deployment 是如何从 yaml 文件最终部署成功 pod 的
  • RK3588 使用 FFmpeg 硬件解码输出到 DRM Prime (DMA Buf) 加速数据传输
  • 基于蚁群算法的PID参数整定方法及MATLAB实现
  • 排序算法大全——插入排序
  • 手搓一个CUDA JIT编译器
  • 网站引导页模板互联网公司排名全球
  • JDK 9 List.of(...)
  • 做一个vue3 v-model 双向绑定的弹窗
  • 为超过10亿条记录的订单表新增字段
  • 哪里做网站最便宜WordPress功能模块排版
  • 每日算法刷题Day78:10.23:leetcode 一般树7道题,用时1h30min
  • 薄膜测厚选CWL法还是触针法?针对不同厚度与材质的台阶仪技术选型指南
  • WPF-MVVM的简单入门(第一个MVVM程序)
  • blender拓扑建模教程
  • asp.net手机网站开发教程翻译网站建设方案
  • 佛山建设网站公司哪家好特斯拉ceo进厂拧螺丝
  • 如何做新网站保留域名wordpress基础
  • C# 实现 Modbus TCP 通信
  • 《Git:从入门到精通(七)——Git分支管理与协作开发实战》