JS 逆向调试技巧

浏览器页面的执行流程决定了逆向的切入点: 核心目标是在「加密函数调用」这一环节断下,分析参数的生成逻辑。 在 Sources 面板中直接点击行号,最基础的断点方式。适合已知目标代码位置时使用。 右键行号 → Add conditional breakpoint,输入 JS 表达式,仅在条件为真时暂停。 在 Sources → XHR/fetch Breakpoints 中添加 URL 关键字(如 api、login),发起请求时自动在 send() 处断下。断下后查看 Call Stack 向上追溯加密逻辑。 操作步骤: 1. 打开 DevTools →

分享

官方文档:https://developer.chrome.com/docs/devtools/javascript/
适用场景:使用 Chrome DevTools 在浏览器中定位和分析加密参数生成逻辑

整体分析流程

浏览器页面的执行流程决定了逆向的切入点:

加载 HTML → 加载 JS → 运行初始化 → 用户触发事件
  → 调用加密函数 → XHR 发送请求 → 接收响应 → 解密渲染

核心目标是在「加密函数调用」这一环节断下,分析参数的生成逻辑。


断点类型

普通断点

在 Sources 面板中直接点击行号,最基础的断点方式。适合已知目标代码位置时使用。

条件断点

右键行号 → Add conditional breakpoint,输入 JS 表达式,仅在条件为真时暂停。

// 示例:只在参数包含 "sign" 时断下
param.includes('sign')

XHR/Fetch 断点

在 Sources → XHR/fetch Breakpoints 中添加 URL 关键字(如 apilogin),发起请求时自动在 send() 处断下。断下后查看 Call Stack 向上追溯加密逻辑。

操作步骤:

  1. 打开 DevTools → Sources → 右侧 XHR/fetch Breakpoints
  2. 点击 + 输入 URL 关键字(留空匹配所有请求)
  3. 触发目标请求,断点自动命中
  4. 在 Call Stack 中从下往上逐帧分析

事件监听断点

Sources → Event Listener Breakpoints,展开对应事件类型(如 Mouseclick),触发该事件时自动断下。常用于定位按钮点击后的加密逻辑。

常用事件类型 场景
click 登录/提交按钮
submit 表单提交
keydown 键盘输入触发
load 页面加载完成后的初始化

DOM 断点

右键目标 DOM 元素 → Break on → 选择类型:

  • subtree modifications:子节点变化
  • attribute modifications:属性变化
  • node removal:节点被移除

适合追踪动态更新的验证码、token 等元素的生成来源。

Logpoint(日志断点)

右键行号 → Add logpoint,在不暂停执行的情况下打印表达式值。适合生产环境或不方便暂停的场景。

// 示例 logpoint 表达式
"sign 值:", sign, "timestamp:", Date.now()

调用堆栈分析

断点命中后,Call Stack 面板显示完整调用链。从下往上阅读,下方是最早的调用者,上方是最近的调用者。

分析技巧:

  1. 跳过框架代码(webpackchunk 等文件),关注业务逻辑文件。
  2. 逐帧点击,观察右侧 Scope 面板中各层级的变量值。
  3. 找到参数从「明文」变为「加密值」的那一帧,即加密函数所在位置。

快速定位加密函数

全局搜索关键字

Ctrl + Shift + F(全局搜索),搜索策略:

搜索词 说明
sign 签名参数名
encrypt / cipher 通用加密函数名
md5 / sha / aes 算法名称
CryptoJS 常见加密库标志
\.encode\( / btoa 编码函数

Initiator 堆栈

Network 面板中点击目标请求 → Initiator 标签页,直接显示请求发起的调用链,点击对应行跳转到源码。

搜索技巧

若直接搜索参数名找不到,尝试:

  • 搜索参数名的部分字符串
  • 搜索接口路径末段(如 /api/login → 搜 login
  • 搜索相邻参数名(和加密参数在同一个对象里的其他字段)

反调试对抗

无限 debugger

原因:网站在代码中插入循环触发的 debugger,打开 DevTools 就无法正常操作。

对抗方法:

方法 操作
禁用断点 DevTools 右上角 Deactivate breakpoints(蓝色圆圈图标)
替换函数 Console 执行 Function.prototype.constructor = function(){}
本地覆盖 下载 JS 文件,删除 debugger 相关代码,启用 Local Override
Hook 绕过 使用反无限 debugger Hook 脚本(见 hook.md)

开发者工具检测

常见检测手段:

  • 监听 F12/Ctrl+Shift+I 键盘事件
  • 检测 window.outerHeight - window.innerHeight 差值
  • 通过 console.log 对象检测 DevTools 是否打开
  • 利用计时差:debugger 在 DevTools 开启时执行时间远大于正常值

对抗方法:使用单独的 Chrome 配置文件,启动时带 --disable-devtools-autoupdate,或直接使用 Local Override 修改检测逻辑。


本地覆盖(Local Override)

适合:需要持久化修改 JS 文件(删除反调试、添加 Hook)的场景。

操作步骤:

  1. DevTools → Sources → Overrides 标签
  2. 点击 + Select folder for overrides,选择本地目录并授权
  3. Network 面板中右键目标 JS 文件 → Save for overrides
  4. 在 Sources 中找到覆盖文件,直接编辑保存
  5. 刷新页面,浏览器将加载本地版本而非网络版本

代码格式化

混淆或压缩过的代码可通过以下方式还原可读性:

  • DevTools 内置:Sources 面板左下角 {} 按钮(Pretty Print)
  • 在线工具js-beautify(支持本地 npm 安装)
  • 命令行npx prettier --write target.js

Scope 面板使用

断点暂停后,右侧 Scope 面板显示当前及上层作用域的所有变量。

  • Local:当前函数局部变量
  • Closure:闭包捕获的变量
  • Module:模块级变量
  • Global:全局变量

重点关注 Closure 层,加密 key、盐值等敏感数据常存储在闭包中。


Watch 表达式

在 Watch 面板添加任意 JS 表达式,断点暂停时自动求值显示:

// 示例
window._key
JSON.stringify(requestData)
btoa(signStr)

最佳实践

XHR/Fetch 断点是首选入口:在 DevTools Sources → Event Listener Breakpoints → XHR → fetch / XMLHttpRequest 勾选,下次请求时自动暂停,从调用栈往上追溯签名生成函数,比盲目搜索快 10 倍。

条件断点过滤无关请求:在断点上右键 → "Edit breakpoint" 添加条件(如 url.includes('/api/sign')),只在目标 URL 触发,避免其他请求干扰。

Scope 面板追踪局部变量:暂停后展开 Scope 面板(Local / Closure / Global),直接查看当前函数的所有变量值,比在 Console 输入变量名更全面,能看到对象的完整结构。

copy() 函数复制复杂对象copy(JSON.stringify(window._signData)) 将对象序列化后复制到剪贴板,方便粘贴到 Python 代码中测试,避免手动抄写长字符串。

Overrides 持久化修改:在 DevTools Sources → Overrides 本地覆盖 JS 文件,添加 console.logdebugger,即使刷新页面修改也会保留,适合需要多次调试的目标。


常见陷阱

陷阱:断点触发后堆栈太深找不到加密函数

现象: XHR 断点触发,Call Stack 有几十层,看不出哪里是签名逻辑。
原因: 现代框架(Vue/React)的请求拦截器、Promise 链会在实际发送前插入多层调用。
解决: 从最顶层(最近调用)往下查找包含 signhashtoken 字样的函数;或在 Network 请求的 Initiator 列直接点击跳转到触发该请求的代码行。

陷阱:debugger 语句导致 DevTools 卡死循环

现象: 打开 DevTools 后页面不断触发 debugger,无法正常调试。
原因: 网站注入了反调试代码,检测到 DevTools 后循环触发 debugger 语句。
解决: 在 Sources 页右键 → "Never pause here" 跳过该 debugger;或在 DevTools 设置中禁用 JavaScript 断点,先找到反调试函数并 patch 后再重新开启。

陷阱:Hook 注入时机太晚,目标函数已执行

现象: 在 Console 注入 Hook 后触发请求,Hook 没有被调用。
原因: 页面初始化时就执行了加密函数(如计算设备指纹),Console 注入时已错过。
解决: 使用 Tampermonkey(@run-at document-start)或 DevTools Snippets 在页面加载最早期注入;或用 Overrides 直接修改 JS 源文件在函数定义处插入 Hook。


参见

hook
RPC远程调用
混淆还原
代码片段大全

适合追踪某个变量在不同断点间的变化。

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog