方法论主线来自 Devansh 的 Needle in the Haystack,工具细节来自公开文档,实战数据来自我们自己的授权测试台账。数据截至 2026-09-20,适用范围:已书面授权的安全测试。
怎么从一台空机器把这套东西搭起来,搭的过程会撞上什么,Web、小程序和 APP 分别怎么下手,以及最后那一步——话该怎么跟模型说。
给管理层的三句话
1. 一次性投入约五个人日。之后每接一个新目标都吃这份红利,冷启动从天级掉到小时级。
2. AI 接管的是通读、枚举、重放、起草这些体力活。什么算漏洞、影响多大、什么时候必须停,仍然是人签字,而且流程强制要求人签字。
3. 合规写在配置里而不是靠自觉。授权白名单、写操作禁令、速率上限、数据打码,全程留日志,比纯人工测试更可审计。
| 前提声明 本文所有内容仅适用于已获书面授权、范围明确、全程可审计的安全测试。未获授权的目标,这里没有一条方法是适用的。文中工具和手法全部来自公开资料,本文的价值在于把它们编排成一条可复用的流程,而不是提供任何具体目标的攻击方案。文中提及的实战数据均来自已授权并已向厂商报告的项目。 |
|---|
00 · 摘要:一页看懂
把 Claude Code 这类编程智能体套上四层工程设施——规则、记忆、能力、台账——变成一条能对授权目标持续产出可定级报告的流水线。它不是一句更好的提示词,是一套目录结构加一组约定。
四个数
| 5 人日 | ¥50 – 300 | 天 → 时 | 约 50% |
|---|---|---|---|
| 从零搭到跑通第一个闭环 | 单业务线深挖一轮的 API 成本 | 新目标冷启动周期 | 第二轮起的成本降幅 |
它解决什么
把仓库丢进去说"找出所有漏洞",拿回来的是一张看着专业、实际没法用的表。换更强的模型也没用——我们拿 Opus 5 和 GLM-5.3 各跑过同一个目标,产出质量差别不大,都不能用。问题出在方法不在模型。裸用有四个结构性缺陷,工作台的四层设施是一一对应补上去的:
| 裸用的毛病 | 对策 | 落地成什么 |
|---|---|---|
| 没有威胁模型,模型不知道"影响"是什么,只能泛泛列 CWE | 规则层:薄切片 + 信任边界 | CLAUDE.md + 每目标一页 threat-model.md |
| 宽提示词触发广度优先幻觉,覆盖面换来零深度 | 方法约定:不变量拆两步问 | 任务书模板(目标 / 步骤 / 判据 / 预算) |
| 会话无状态,上轮否定的假设这轮重跑,双重烧钱 | 记忆层 + 台账层 | memory/ 三文件 + tested.md 假设矩阵 |
| 没有边界意识,会顺手探旁站、误触写操作 | 规则层红线 + 三层 scope 护栏 | 浏览器白名单 + 代理 allow_hosts + 出站硬断 |
搭完长什么样
一个可复制的目录骨架、一页纸规则文件、一组按目标形态装的工具,外加一本记录每个假设及其结果的台账。三种形态共用同一套骨架,只在能力层换工具:
| 形态 | 能力层装什么 | 复用的部分 |
|---|---|---|
| Web | DevTools MCP + 代理 + 前端白盒 | 规则层、记忆层、台账层、报告模板,三种形态完全相同 |
| 客户端 | wxapkg / asar 解包 + UIA 自动化 | 同上 |
| APP | jadx / Frida + 脱壳 + 解绑 | 同上 |
一条贯穿全文的尺子
Devansh 在 Needle in the Haystack 里给了个很好用的分配比例:脚手架不超过 10%,切片审计占 60 到 80,剩下 20 到 30 留给验证。
| 预算段 | 占比 | 花在哪 |
|---|---|---|
| 脚手架 | < 10% | 一页威胁模型、不变量清单、红线 |
| 切片审计 | 60 – 80% | 指向一个信任边界的定向审计:白盒读码、黑盒验证、子 agent 分片 |
| 验证 | 20 – 30% | 要证据不要结论:复现、对抗性自审、人签字 |
比例本身不神圣,它的用处是当你发现脚手架超了 10%,就该砍规则文件而不是加窗口。这条贯穿本文所有章节——规则文件为什么要控制在一页、大文件为什么必须走索引、为什么每条结论都要人复核,都可以用它来解释。
| 怎么把比例换算成字数 按 1 个汉字约 1 个 token 粗估,一页 A4 中文大约 800 到 1000 字,也就是 1k token 上下。20 万 token 的有效窗口,10% 的脚手架上限是 2 万 token,约合 20 页纸——听起来很宽裕。但别忘了工具返回也吃这份预算:一次 list_network_requests 可能就是几千 token,一个中等 MCP 服务器光工具定义就要几千。真正留给规则文件的,一页才是合适的量。 |
|---|
第一部分:裸用 AI 为什么不行
大多数人拿到 Claude Code 的第一件事,是把整个仓库丢进去说"找出所有漏洞"。这条路走不通,原因有四个:前两个是模型行为,后两个是工程缺失。四个都不是换更强的模型能解决的。
1.1 没有威胁模型时,模型做的是检索
你不说攻击者是谁、信任边界在哪、攻击者能控制什么,模型就没有"影响"这个概念。它能凭训练数据猜出一些通用 CWE,但猜得很差。结果是一长串"理论上可能存在 XSS"“建议检查 SQL 拼接”,没有优先级,你也没法从噪音里挑出值得跟进的。
技术上的原因值得说透,因为后面几乎所有设计都是冲着它去的。在没有约束的情况下,LLM 做的是模式召回,不是可达性分析。它从训练分布里检索"这类代码通常有什么漏洞",输出的是一个先验分布。而真正的漏洞判定要回答的是另一个问题:
| 这段代码在这个信任边界下,攻击者能否触达;触达之后,能否越过边界。 |
|---|
写成条件概率大概是 P(可利用 | 代码, 信任边界, 攻击者能力, 可达路径)。后面三个条件项你不给,模型手里就只剩 P(可利用 | 代码)——一个脱离语境的无条件先验。它输出的那堆"理论漏洞",本质上是把训练集里这类代码的平均风险念了一遍。
Devansh 那篇文章里有句话可以直接抄进规则文件:威胁建模是安全审计的终极压缩算法。一页纸的威胁模型能替代几十页的通用规则,因为它填的是条件项,不是增加信息量。这两件事在 token 账上的差别极大:通用规则是往上下文里加噪声,条件项是把搜索空间砍掉几个数量级。
1.2 宽问题把推理预算摊薄
这个更隐蔽,因为它的产出看起来很丰盛。广问题引出广回答,模型会去匹配它见过的常见模式,哪怕在你的上下文里根本不可能触发。你最终在审一些没有任何攻击者能到达的路径,花几万 token 买回一堆不可复现的清单。原文管这叫 breadth-first hallucination。
机制有两层,拆开看才好对症:
▪ 推理预算被摊薄。 一个覆盖整个仓库的宽问题,让模型把有限的推理分到每个文件上,每个文件只够做表层匹配。注意力是有限资源,你给的范围越大,单位代码上分到的越少。
▪ 输出策略在优化错误的目标。 宽问题下,“列了 20 个方向"看起来比"深挖了 1 个方向"更像回答了"所有漏洞"这个问题。所以模型倾向于给覆盖面——它在优化它以为你要的东西,而不是你真正要的东西。
还有一个叫 context rot 的现象叠加在上面:上下文越长,模型的可靠性越下降,而且不是到窗口上限才突然崩,是一路缓慢劣化。所以"把更多材料塞进去"这个动作本身就是反向操作。标称 20 万 token 的窗口,实际能稳定推理的有效部分远小于这个数——工程上要按有效窗口规划,不能按标称窗口规划。
1.3 会话无状态,重复劳动烧两份钱
每个新会话都从零开始。上一轮已经测过并否定的假设,这一轮换个说法又跑一遍;上个项目总结的模式,这个项目完全用不上。
代价是双份的。token 在烧,目标请求配额也在烧,后者更值钱,因为它受目标侧风控限制,是硬约束——token 花完可以充钱,IP 被封了就得等。
多人协作时这个问题会放大。三个人各跑各的会话,同一个接口被测三遍,既浪费预算,也真实地增加了触发风控的概率。我们在一个 OTA 目标上吃过这个亏:两个人分别测了订单查询的越权,用词不同、结论一致,多出来的那一遍请求没产生任何新信息,但确确实实进了目标的风控计数器。
1.4 没有边界意识,这是风险不是效率
AI 没有法律意识。给它一个域名,它会顺手去探旁站;让它验证一个下单接口的越权,它可能真的下一单;让它测短信接口,它会把验证码打给真人。
这些不是模型的 bug,是它没有被告知边界。边界是人写进文件它才知道的,没写,它会自己判断,而且通常判错——它的判断依据是"这样做对完成任务有帮助”,不是"这样做会不会越界"。
前三条是效率问题,这一条是风险问题。对内部汇报来说它也是最该强调的一点:工作台的规则层不只是让 AI 跑得更准,它把合规约束从"靠测试人员自觉"变成了"写进配置、全程留痕"。第三部分会给出三层 scope 护栏的具体配置。
1.5 三个反例:别人是怎么挖出来的
Needle in the Haystack 的作者在两个月里用这套方法报了 30 多个漏洞。挑三个能说明问题的:
Parse Server CVE-2026-29182 / 30228 / 30229
先翻历史 CVE,发现全是权限校验不完整。据此让模型生成聚焦"权限边界执行"的威胁模型,然后盯住一个具体边界:只读管理员 key。模型发现多个路由 handler 只查了 isMaster 没查 isReadOnly。这是一个模式而不是一个点,于是用更窄的提示词让它枚举所有符合这个模式的 handler——三个 CVE 同一个根因。
ElysiaJS cookie 签名校验
切片选的是 cookie 签名校验。找到的问题是一行初始化写反了,let decoded = true 本该是 false,导致密钥轮换期间无效 cookie 不会被拒。这种 bug 静态扫描器基本抓不到,因为语法完全合法,错的是语义——而语义恰好是 LLM 相对扫描器的唯一优势所在。
harden-runner CVE-2026-25598
威胁模型只有一句话:攻击者能不能绕过出站管控把数据传出去。然后做系统调用覆盖度分析,发现 UDP 那几个(sendto / sendmsg / sendmmsg)没被监控。同样的问题问另一个项目 BullFrog,因为它用网络层防火墙而不是系统调用插桩,切片就完全不同:DNS 解析、IP 到域名的绑定、提权路径。
注意这三个的共同点:没有 20 页的 Agent.md,没有庞大的 Skill 库,没有全量扫描。做的是四件事——选好切片、从历史反推威胁模型、让模型找不变量违反、跟进信号。
Anthropic 用 Claude 审 Firefox 也是这个顺序:先从 JavaScript 引擎切入,跑通流程之后才扩到约 6000 个 C++ 文件,最终提交 112 份报告,大部分修复进了 Firefox 148.0,API 花费约四千美元。先窄后宽、先跑通闭环再放大规模,是这几个案例唯一的共同结构。
1.6 顺带说清楚模型的几个已知偏好
这几条会在第七部分变成具体的提问技巧,这里先摆出来,因为后面很多设计是冲着它们去的:
▪ 首尾位置效应。 放在上下文窗口两端的信息被用得更好,埋在中间的最容易被忽略。所以关键约束要么放最前,要么在任务书里重申一遍。
▪ 默认顺从。 不特别提示时,模型倾向于说代码是安全的。“整体看起来没问题,有一些小的关注点"是阻力最小的答案。
▪ 完整性前置。 它最有把握的发现排在最前面,细微的 bug 要靠追问才出来。所以"还有别的吗"这句话要多问几轮。
▪ 抽象指令吃不住,具体边界吃得住。 给它"注意安全性"没用,给它"只读 key 不能触发写操作"它能顺着查。
▪ 倾向于把代码合理化。 看到一段可疑写法,它的默认假设是"作者这么写有他的道理”,然后开始替作者圆。这条在 7.3 的技巧 07 里被专门针对。
这五条里,前两条决定了规则文件怎么放、放多长,后三条决定了话该怎么问。它们不是缺陷清单,是这套方法的设计输入。
第二部分:架构
工作台是六层东西叠起来,让模型能看见目标、动手操作、记住结论、守住边界。每一层可以独立替换,这是整套设计里最值钱的性质——不是因为它优雅,是因为 2026 年这一行里价格、可用性、合规要求每个季度都在变。
2.1 六层结构
| 层 | 名字 | 装什么 | 说明 |
|---|---|---|---|
| L6 | 台账层 | journal/ tested.md findings.md | 本目标测了什么、结果如何、证据在哪。防重复的账本,也是交接文档 |
| L5 | 能力层 | 浏览器驱动 · 抓包 · 解包 · 反编译 · 运行时 hook | 模型的手。三种形态只有这一层不同 |
| L4 | 记忆层 | patterns.md techniques.md tools.md | 跨目标复用的知识。新目标冷启动靠它 |
| L3 | 规则层 | CLAUDE.md + 每目标 scope.md / threat-model.md | 授权范围、红线、工作约定 |
| L2 | 终端层 | Claude Code / Codex / 兼容接口客户端 | 管会话、工具调用、子 agent 派发、上下文 |
| L1 | 模型层 | 主模型 + 备用模型 | 纯推理能力。可替换,而且应该保持可替换 |
L1 和 L2 是买来的,L3 和 L4 是写出来的,L5 是装出来的,L6 是跑出来的。一次性投入主要花在 L3 和 L5,长期价值沉淀在 L4 和 L6。汇报的时候这句话可以直接用:你付的五个人日买的是 L3 和 L5,你留下的资产是 L4 和 L6。
2.2 数据流:假设是一等公民
整条流程的核心对象不是"代码"也不是"漏洞",是假设——一条形如"只读 key 不能触发写操作"的、可以被证实或证伪的断言。每个假设有编号、状态、证据。围绕假设组织,流程就自然闭环了。
| 环节 | 干什么 | 产物 |
|---|---|---|
| 观测 | 抓包 / 解包 / 反编译 | 原始材料 + 索引 |
| 资产化 | 把原始材料压成结构化的表 | 路由表、身份字段表 |
| 不变量 | 找出代码声称永远成立的事 | invariants.md |
| 假设 | 给每条不变量配编号、判据、请求预算 | tested.md 新增行 |
| 验证 | AI 驾驶工具去证伪,命中即停 | 证据三件套 |
| 复核 | 人独立复现 | hit 降级或确认 |
| 台账 | 命中与否定都记 | 回流给下一轮 |
关键在最后那段回流:台账反过来喂给下一轮的假设环节。没有这条回流,整条流程是开环的,每一轮都在重复上一轮。很多团队搭到一半就上线,跑几周之后发现成本没降,根因基本都在这里。
假设的状态机
台账之所以能起作用,是因为假设的状态是有限的、可判定的。我们只允许这几个状态,多一个都不行:
| 状态 | 含义 | 进入条件 |
|---|---|---|
| hit | 命中 | 有证据三件套,且人复核通过 |
| miss | 已测且否定 | 测过,且写明了否定原因 |
| blocked | 测了但被拦或无法判定 | WAF 拦截、需要的前置条件拿不到、超出授权范围 |
没有"待定"。待定的东西留在 journal/ 里,不进 tested.md。这个限制看着教条,但它是台账能被模型可靠使用的前提:状态枚举一旦开放,模型就会开始创造"部分命中"“疑似"这类无法过滤的值,台账立刻退化成散文。
什么是不变量
不变量是代码声称永远成立的事。几个例子:
▪ 只读 key 不能触发写操作
▪ JWT 的 issuer 必须等于配置值
▪ 这个字段必须从服务端会话取,不能从请求体取
▪ 分发器转发给下游的身份,必须来自网关校验后的上下文
▪ preload 暴露的特权接口,只能被自有域名的页面调用(第五部分那个案例,根因就是这条不成立)
找不变量有个必须遵守的细节:把"列假设"和"判断假设是否被违反"拆成两步问。模型单独做"列出这个模块所有隐含安全假设"很在行,单独判断"假设 X 在这条路径上是否成立"也推理得很深。两件事塞进同一个提示词,结果通常很浅——它试图同时做两件事,会早早满足于一个及格答案。第七部分把这条做成了模板。
2.3 分层换来的三件事
| 性质 | 因为哪层被隔离 | 实际意味着什么 |
|---|---|---|
| 换模型不动流程 | L1 与 L3‒L6 解耦 | 国外模型涨价、被封号、或客户要求数据不出境时,换国内模型只改环境变量,规则记忆台账工具全部照用 |
| 换目标不丢知识 | L4 与 L6 分离 | 记忆层存"哪类地方容易坏”,台账层存"这个目标测到哪了"。新目标继承前者、清空后者 |
| 换人不丢上下文 | L6 结构化 | 台账本身就是交接文档。人员轮换或多人并行时读 tested.md 就知道从哪接手 |
第一条在 2026 年尤其实在。这一年我们换过两次主力模型,一次是因为涨价,一次是因为客户要求数据不出境。两次切换的实际工作量都是改两个环境变量加跑一遍探活,规则文件、记忆层、台账、MCP 配置一个字没动。如果当初把方法写死在某家的 Skill 体系里,这两次每次都是一周。
2.4 脚手架多少算刚好
这是最容易做反的一条。很多团队的第一反应是写一份事无巨细的规则文档、预加载一个庞大的知识库,结果效果比什么都不写还差。
| 够用的脚手架 | 过量的脚手架 |
|---|---|
| 一页纸威胁模型 | 二十页 Agent.md,塞进每条政策和风格指南 |
| 三五条关键功能清单 | 庞大的 Skill 库,预加载几十个文件 |
| 几条明确的不变量 | 让 AI"找出所有漏洞",期望它自己搞明白上下文 |
| 指向一个信任边界 | 把整个代码库丢进去不指定方向 |
| 按需取用的文件索引 | 把大文件整份喂进上下文 |
脚手架的作用是把注意力锚定到正确的地方,一页纸足够。二十页的规则文件反而把注意力稀释了:模型要花 token 理解你的规矩,留给分析代码的推理空间就少了。
这可以直接算。一份 20 页的规则文件大约吃掉一万五到两万 token。按 10% 的上限倒推,只有当你的有效工作窗口在二十万 token 以上时它才勉强合规——而有效窗口远小于标称窗口,这正是 context rot 的含义。更别提规则文件不是唯一的常驻开销:MCP 的工具定义、系统提示、目录列表都要占位置,这些加起来经常比规则文件本身还大。
| 一条可执行的判据 规则文件超过一页就该反思:是不是把"每次都要遵守的红线"和"某类任务才需要的方法"混在一起了。前者进 CLAUDE.md 常驻,后者进任务书、用时才给。这个拆分能让常驻部分稳定控制在一页以内,是下一部分 Step 2 的核心设计。 |
|---|
第三部分:搭建
七个步骤,从接模型一直到最小闭环验收。每步给出可直接抄的配置和这一步特有的坑。按顺序做完大约五个人日。三种目标形态的专用工具在第四到第六部分,这里只搭与形态无关的骨架。
| 小节 | 这一步产出什么 |
|---|---|
| 3.0 接模型 | 主备双模 + 一键切换 + 探活脚本 |
| 3.1 目录骨架 | 可复制的 workbench/,新目标改配置不改流程 |
| 3.2 规则层 | 一页纸 CLAUDE.md,六条红线写死 |
| 3.3 记忆层 | patterns / techniques / tools 三文件与召回提示词 |
| 3.4 能力层 | .mcp.json、索引策略、三层 scope 护栏 |
| 3.5 台账层 | tested.md 假设矩阵 |
| 3.6 子 agent | 任务书模板与分片原则 |
| 3.7 验收 | 十条可观测的检查,缺一条都不算搭完 |
3.0 接模型
先说结论:做一主一备,别二选一。兼容接口让切换成本接近于零,不用它是浪费。
决策的四个变量
| 变量 | 看什么 |
|---|---|
| 能力 | 写代码、改大项目、跑长任务的一次通过率。看第三方榜单,别只看厂商自己的数 |
| 价格 | 编程智能体输出量大,主要看输出价不是输入价 |
| 可用性 | 能不能注册、能不能付钱、要不要节点、会不会封号 |
| 数据合规 | 客户代码和流量能不能出境。不能出境,国外模型直接排除 |
2026-09 的牌面
| 厂商 | 主力模型 | 特点 | 接入方式 |
|---|---|---|---|
| Anthropic | Claude Fable 5.1 / Opus 5 / Sonnet 5 / Haiku 4.5 | 编程智能体第一梯队;Claude Code 是官方工具 | 订阅 Pro / Max,或 API |
| OpenAI | GPT-6 Astra / GPT-5.6 Sol · Terra / GPT-5.3 Codex | 同一梯队;Codex 是官方工具 | 订阅 Go / Plus / Pro,或 API |
| DeepSeek | V4 Pro / V4 Flash | 便宜、开源权重;8 月起涨价并分峰谷时 | API 按量,无订阅 |
| 智谱 | GLM-5.3 / GLM-5.3-Flash | 开源里编程最强一档;有 Anthropic 兼容接口 | Coding Plan 订阅,或 API |
| 月之暗面 | Kimi K3 / K2.7-code | K3 是国内最大(2.8T);有兼容接口 | Kimi Code 套餐,或 API |
| 阿里 | Qwen3.8-Max | 生态大,百炼平台;夜间有折扣 | Token Plan,或 API |
Artificial Analysis 的智能体指数(2026-09-18)排下来是这个次序:Claude Fable 5.1 与 GPT-6 Astra 并列 60,Claude Opus 5 是 59,GPT-5.6 Sol 55,GLM-5.3 54,Kimi K3 52,Qwen3.8-Max 与 DeepSeek V4 Pro 同为 43。差 10 分不是不能用,是复杂重构和长任务的一次通过率低一些,多来一轮通常也能做完。
API 输出价是大头。国外第一梯队 25 到 50 美元每百万 token,国内 GLM-5.3 和 DeepSeek 只要 4 美元左右,差六到十二倍。订阅方面:Claude Pro 20 美元 / Max 100 / Max 20x 200;GLM Coding Plan ¥118‒1078;Kimi Code ¥49‒699;DeepSeek 纯按量。
国外模型的四个坎
| 卡点 | 情况 | 怎么办 |
|---|---|---|
| 地区 | 不对中国大陆提供服务 | 要节点;公司走 Bedrock / Vertex 等云托管入口 |
| 注册 | 不接受大陆手机号;2026-07 起 Claude 要 KYC | 海外手机号 + 真实证件 |
| 付款 | 只收海外信用卡 | 公司统一开卡;个人可走 App Store 订阅 |
| 封号 | 频繁切 IP、支付异常会被封 | 固定节点、固定设备、别共享账号 |
主备双模怎么配
GLM 和 Kimi 都提供 Anthropic 兼容接口,意思是 Claude Code 这个终端可以直接指过去,改两个环境变量的事。这是整个工作台里性价比最高的一个设计:终端层不变,模型层随时切。
主:Anthropic 官方
wb-main() {
unset ANTHROPIC_BASE_URL ANTHROPIC_AUTH_TOKEN
export ANTHROPIC_API_KEY="$(security find-generic-password -s wb-anthropic -w)"
export ANTHROPIC_MODEL=“claude-opus-5”
}
备:GLM 兼容接口,同一个 Claude Code 终端
wb-backup() {
unset ANTHROPIC_API_KEY
export ANTHROPIC_BASE_URL=“https://open.bigmodel.cn/api/anthropic"
export ANTHROPIC_AUTH_TOKEN=”$(security find-generic-password -s wb-glm -w)"
export ANTHROPIC_MODEL=“glm-5.3”
}
探活:切换后先打一发,别在半个任务中间才发现挂了
注意必须包含一次真实工具调用,只测对话查不出兼容层的问题
wb-ping() {
claude -p “用 Read 工具读 ./CLAUDE.md 第一行,只回这一行原文” –max-turns 3
}
密钥走系统钥匙串而不是明文写进 rc 文件。工作区目录经常被打包带走,明文密钥跟着走出去是很现实的风险。
兼容层的已知差异
这里值得多说两句根因,因为踩坑的时候知道根因能省很多时间。Anthropic 的 Messages API 把工具调用表达成消息里的 tool_use / tool_result 内容块,OpenAI 系是 function_call 那一套,两边的参数容器、ID 关联方式、并行调用的表达都不一样。兼容接口做的是字段映射,简单调用映射得很干净,复杂的就未必。
| 差异点 | 表现 | 对策 |
|---|---|---|
| 工具调用格式 | 少数复杂嵌套参数的调用会被改写或丢字段 | 探活必须包含一次真实工具调用,而且最好是带嵌套参数的 |
| 长上下文截断 | 接近窗口上限时可能静默截断而不报错 | 大文件一律走索引取片段,见 3.4 |
| 流式中断 | 长任务跑一半断开,前面的 token 已经扣了 | 任务切小;结论即写台账,不要攒到最后 |
| 模型别名漂移 | 同一别名指向的实际版本被悄悄换掉 | 固定具体版本号;每周探活时记一次实际返回的模型标识 |
三套推荐配置
| 方案 | 主力 | 备份 | 每月 |
|---|---|---|---|
| A · 有海外支付 | Claude Pro($20)或 Max 5x($100) | GLM Coding Plan Lite(¥118) | ¥260 – ¥830 |
| B · 无海外支付 | GLM Coding Plan Pro(¥538) | DeepSeek V4 API | ¥590 左右 |
| C · 先试水 | GLM Coding Plan Lite(¥118) | DeepSeek V4 API | ¥170 左右 |
人民币按 1 美元 = 7.1 元折算。价格每月都在变,动手前核一遍官方定价页。
| 选型之前先回答一个问题 本次目标的代码、流量、日志是否允许出境。如果不允许,方案 A 直接出局,而且不存在"只把无关部分发出去"的折中——抓包产物里混入生产数据是常态,靠人工筛选不可靠。这种情况走方案 B,或者更进一步用本地部署的开源权重模型。 |
|---|
3.1 目录骨架
可直接复制。新目标改配置不改流程,这是把一次性经验变成可复用资产的关键一步。
workbench/
├── CLAUDE.md # L3 常驻 · 一页纸 · 红线写死在这
├── .mcp.json # L5 MCP 服务器声明
├── .env.example # 真 .env 不进版本库
│
├── memory/ # L4 跨目标复用 · 只进不退
│ ├── patterns.md # 漏洞模式:哪类地方容易坏
│ ├── techniques.md # 技术手法:怎么测、怎么绕
│ └── tools.md # 工具与命令:环境怎么配、版本对应关系
│
├── targets/
/ │ ├── scope.md # 授权书摘要:白名单、时间窗、联系人
│ ├── threat-model.md # 一页威胁模型
│ ├── assets/ # 观测的结构化结果
│ │ ├── routes.md # 接口路由表
│ │ ├── identity.md # 身份字段:服务端下发 vs 客户端自报
│ │ └── invariants.md # 不变量清单,假设的来源
│ ├── capture/
│ │ ├── flows/ # 原始流
│ │ └── index.jsonl # 结构化索引 —— AI 检索用这个
│ ├── decompiled/
│ │ ├── INDEX.tsv # 文件清单 + 大小 + 首行
│ │ └── INDEX-keywords.md # 关键词热力,决定先读哪几个文件
│ ├── journal/
.md # L6 按天流水 │ ├── tested.md # L6 假设矩阵 —— 防重复核心
│ ├── findings.md # L6 命中项 + 证据三件套
│ └── report/
│
└── tools/
├── mitm-addons/ # 落盘为结构化 jsonl
├── unpack/ # 小程序 / asar 解包
└── frida/ # hook 脚本库
| 事项 | 规矩 |
|---|---|
| 进版本库 | CLAUDE.md、.mcp.json、memory/、tools/,以及各目标的 scope.md / threat-model.md / tested.md / findings.md |
| 绝不进版本库 | .env、capture/flows/、decompiled/,以及任何含真实凭证或第三方 PII 的产物。.gitignore 第一天就写好 |
| 新目标怎么起 | 复制 targets/_template/,填 scope.md,清空台账,记忆层原样继承 |
那两个 INDEX 文件值得单独点一句。capture/index.jsonl 和 decompiled/INDEX-keywords.md 不是原始数据,是为模型准备的检索入口。直接让它读 mitmproxy 的二进制流或几千个反编译 Java 文件,是上下文爆炸的头号原因。先建索引、再按需取片段,能把单次任务的 token 降一个数量级。做法见 3.4。
3.2 规则层:CLAUDE.md 怎么写
常驻的只放红线,方法放任务书。这样常驻部分才能稳定控制在一页以内。
必须写死的六条
1. 目标白名单 域名 / AppID / 包名的明确列表,出列表禁止。不写这条,模型会顺手探旁站和关联域名。
2. 只读优先 下单、改密、删除、支付、发消息、催单,一切写操作默认禁止,需要显式豁免。
3. 速率与批量上限 每请求间隔 ≥ 1.2 秒、单批 ≤ 500、全程留日志。模型不懂刷太快会被封。
4. 验证码类接口禁触发 短信 / 邮件 / 语音验证码一律禁止。对真人构成骚扰,性质上已经不是技术问题。
5. 第三方数据验证性读取 ≤ 2 条 证明越权成立只需要一条记录。命中即停,报告内打码。
6. 凭证与 PII 不落正文 不进报告正文、不粘进对话、不进版本库、不传第三方模型。
措辞方式直接决定行为
“注意不要误触写操作"是一条建议,模型会在它认为必要时权衡后越过。“所有写操作默认禁止,除非本任务书显式列出豁免端点"是一条规则,模型会当硬约束执行,并且在想做写操作时来问你。同一件事,前者的违规率明显高于后者。规则层的措辞不是文风问题。
授权范围
本工作区仅用于已书面授权的安全测试。授权书:targets/
/scope.md 白名单以 scope.md 为准;任何不在白名单内的主机、域名、AppID,禁止发起任何请求。
红线(默认禁止,需任务书显式豁免)
1. 写操作:下单 / 支付 / 改密 / 删除 / 发消息 / 催单 / 提交表单 —— 一律禁止
2. 验证码类接口(短信 / 邮件 / 语音) —— 一律禁止触发
3. 速率:请求间隔 >= 1.2s;单批 <= 500 次;超出须停下来问
4. 第三方数据:验证性读取 <= 2 条,命中即停,报告内打码
5. 凭证与 PII:不写入 findings.md 正文,不粘贴进对话,不出工作区
6. 破坏性动作:rm / drop / 批量写文件 —— 禁止
工作约定
开工前必读:targets/
/tested.md,memory/patterns.md 结论即写:每验证完一个假设立刻追加 tested.md,不要攒到最后
大文件不整读:先读 INDEX,再按需取片段(单次 <= 400 行)
命中即停:证实一个假设后停止扩大,转人工复核
不确定是否越界时:停下来问,不要自行判断
输出格式
假设结论写成:ID | 面 | 假设 | 状态 | 证据路径 | 日期
命中项必须附证据三件套:原始请求 / 原始响应 / 复现步骤
状态只能是 hit / miss / blocked 三者之一
| 规则会失效,这一点要知道 规则文件不是永久生效的。长会话后期,当上下文堆了几十轮工具结果之后,开头那份规则对当前注意力的影响会明显衰减。表现是:跑到第三小时,模型开始做第一小时绝不会做的事,比如探一个不在白名单的子域名。这是 1.6 里首尾位置效应的直接后果,不是模型变坏了。真正拦得住的是 3.4 那三层工具级护栏。 |
|---|
3.3 记忆层:知识怎么沉淀与召回
这是长期价值的来源。第一个目标你只是在用工具,从第三个目标开始,你在用前两个目标的经验。
| 文件 | 存什么 | 判断标准 |
|---|---|---|
| patterns.md | 漏洞模式:哪类地方容易坏 | “这个结论换一个目标还成立吗”。成立才写 |
| techniques.md | 技术手法:怎么测、怎么绕 | 可操作的动作序列。例:小程序自动化要先激活无障碍模式 |
| tools.md | 工具与命令、版本对应关系 | 能直接粘贴执行的命令 + 已知坑。例:某版本微信对应哪个解包脚本 |
条目格式:带命中历史和反例
格式不是形式主义。命中历史让你知道这条模式的可信度,命中三次和命中一次权重不同。反例防止记忆层污染——一条被过度泛化的错误模式会误导后续所有项目,而且因为它写在记忆里、每次都被读,错误会被反复强化。
P-012 分发器信任请求体自报身份
形态 网关 / 分发器把请求体里的 userId / userName 当可信身份,
转发给下游时直接透传,下游不再复验。
触发条件 微服务架构 + 网关统一鉴权 + 下游以"内网可信"为前提设计。
单体应用或下游独立校验的,不成立。
检测步骤 1) 从流量里找同时含 token 和自报身份字段的请求
2) 保持 token 不变,只改自报字段
3) 调一个返回用户私有数据的查询接口
4) 看返回是否为他人数据
命中历史 OTA-A(2026-08,高危) OTA-B(2026-09,高危)
反例 金融类目标 F-1:网关用 HMAC 把 userId 绑进签名,改字段签名即失效。
凡是请求里带签名字段的,先逆签名算法,不要直接试改。
优先级 高 —— 新目标若命中触发条件,建议排进前三个测试面
| 问题 | 做法 |
|---|---|
| 什么时候写 | 项目收尾时统一写,不在过程中随手写。过程中的判断还没经过复核,容易把误判写进长期记忆 |
| 写什么 | 只写可迁移的。“这个目标的 /api/v2/order 有越权"不写;“这类分发器架构容易在 X 处失守"才写 |
| 怎么召回 | 新目标开局第一件事,让模型读 patterns.md + threat-model.md,输出最可能命中的 5 条并排序 |
| 怎么防污染 | 每季度回看一次。命中历史为 0 且超过三个项目未被验证的条目,降级或删除 |
读 memory/patterns.md 和 targets/
/threat-model.md。 基于这个目标的架构特征,从 patterns 里挑出最可能命中的 5 条,
按触发条件吻合度排序。每条给出:
模式编号与名称
为什么认为本目标吻合(指向 threat-model 里的具体依据)
在本目标上的第一个验证动作是什么
反例条件在本目标上是否成立(若成立,降级或排除)
不要输出泛泛的安全建议,只输出这 5 条的排序和依据。
这一步是冷启动从天级掉到小时级的直接原因。以 P-012 为例,在一个 OTA 目标上成立之后,下一个同类目标直接把它排进前三个测试面,第一天就复现了同类问题。省掉的是原本需要一两天的摸架构、猜薄弱面。
第五部分那个 Electron 案例也进了记忆层,条目大意是"preload 全域注入 + 外链应用内打开 = UXSS 可升级为 RCE”,触发条件写的是"Electron 客户端 + 应用内浏览器 + 有社交外链入口”。这条在后面两个桌面端目标上都被优先排查过,一次命中、一次否定——否定的那次同样有价值,它让我们把触发条件收窄了。
3.4 能力层:MCP、索引策略、三层护栏
这一层决定模型有没有手。MCP 把工具包装成模型可以直接调用的原子能力。没有它,模型只能写脚本给你、你去执行、再把结果贴回来,人是瓶颈,一轮循环几分钟。有了它,它自己调、自己看结果、自己决定下一步,一轮几秒钟。在"枚举 200 个 handler 逐个验证"这类任务上,这是质变。
MCP 到底是什么,以及它的隐性成本
说穿了就是一个约定好的 JSON-RPC 2.0 通道:终端(客户端)启动你声明的服务器进程,走 stdio 或 HTTP 通信,先 initialize 握手,再 tools/list 把工具清单和每个工具的 JSON Schema 拉回来,之后模型每次用工具就是一次 tools/call。服务器可以是二十行 Python,也可以是一整个 Burp 扩展。
要紧的是那份工具清单的位置——它是常驻上下文的一部分。每个工具的名字、描述、参数 schema 加起来,少则几十 token,带复杂参数的能到两三百。装一个暴露 30 多个工具的服务器,光工具定义就可能吃掉四到八千 token,而且每一轮对话都在那儿占着。这就给了"该装哪些 MCP"一个定量判据:
| 一个 MCP 服务器值不值得常驻,看它的工具定义 token 数除以它在本次任务里实际被调用的次数。三十个工具只用到两个的服务器,不如换成一个薄封装,或者干脆用 Bash 调命令行。第六部分那个 jadx-mcp-server 暴露三十多个工具,我们的实际做法是只在做 APP 目标时才挂上它,Web 目标的配置里没有它。 |
|---|
与形态无关的最小配置只有两个服务器:
{
“mcpServers”: {
“capture-query”: {
“command”: “python3”,
“args”: [“tools/mitm-addons/query_server.py”],
“env”: { “INDEX”: “targets/${TARGET}/capture/index.jsonl” }
},
“code-search”: {
“command”: “python3”,
“args”: [“tools/search/rg_server.py”],
“env”: { “ROOT”: “targets/${TARGET}/decompiled” }
}
}
}
一个查抓包索引,一个查代码,都只返回片段不返回全量。形态专用的 MCP(浏览器、jadx、Frida 等)在第四到第六部分分别给出。
三层 scope 护栏
1.4 说过,边界靠提示词守不住。能在工具层拦的就别只写进规则文件。授权范围应该同时落在三个地方,任何一层单独失效都还有兜底:
| 层 | 落点 | 拦住什么 |
|---|---|---|
| 第 0 层 · 提示词 | CLAUDE.md 白名单 + 任务书重申 | 长会话后会衰减,是提醒不是防线 |
| 第 1 层 · 客户端 | 浏览器 –allowedUrlPattern / –blockedUrlPattern | 范围外的导航与子资源根本发不出去 |
| 第 2 层 · 代理规则 | mitmproxy allow_hosts 正则 + addon 落盘白名单 | 范围外流量不落盘,从源头断掉数据混入 |
| 第 3 层 · 出站硬断 | server_connect 钩子里设 data.server.error | 连接直接被杀,不依赖任何上层配置 |
第 2 层:只处理白名单主机,其余直接放行不落盘
mitmdump –listen-port 8080 \
–set allow_hosts=’^(.*\)?target\.example\.com$’ \
–set anticomp=true \
–set hardump=session.har \
-s ./tools/mitm-addons/index_dump.py
第 3 层:出站硬断,写进 addon
from mitmproxy import ctx
SCOPE = {“api.example.com”, “m.example.com”}
def server_connect(data):
if data.server.address[0] not in SCOPE:
data.server.error = “out of scope” # 连接直接被杀
ctx.log.warn(f"BLOCKED {data.server.address[0]}”)
第 3 层是兜底,它拦的是"配置写错了"和"模型绕过了上层"这两种情况。实际跑起来它触发得不多,但触发的那几次都是真事故——有一次是我们自己在 allow_hosts 正则里漏了转义,. 匹配到了别的域名,第 3 层把它挡下来了。
抓包落盘要结构化
装 CA、配代理是基础操作。真正影响效率的是落盘格式。直接让模型读 mitmproxy 的 flow 文件是灾难:二进制、体量大、一次读进去上下文就满了。正确做法是用 addon 在抓包时就把每条流压成一行 JSON,模型用检索的方式去查。
tools/mitm-addons/index_dump.py 要点
import json, hashlib, pathlib
from mitmproxy import http
SCOPE = {“api.example.com”, “m.example.com”}
OUT = pathlib.Path(“capture/index.jsonl”).open(“a”)
REDACT = (“authorization”, “cookie”, “x-token”, “set-cookie”)
def response(flow: http.HTTPFlow):
if flow.request.pretty_host not in SCOPE:
return # 范围外不落盘
body = flow.request.get_text(strict=False) or "”
rec = {
“ts”: flow.request.timestamp_start,
“method”: flow.request.method,
“host”: flow.request.pretty_host,
“path”: flow.request.path.split("?")[0],
参数只留 key,value 不落盘,避免 PII 进索引
“q_keys”: sorted(flow.request.query.keys()),
“b_keys”: sorted(json.loads(body).keys())
if body.startswith("{") else [],
凭证只留指纹,用于判断是否同一身份,不留原文
“auth_fp”: {h: hashlib.sha256(
flow.request.headers[h].encode()).hexdigest()[:12]
for h in REDACT if h in flow.request.headers},
“status”: flow.response.status_code,
“len”: len(flow.response.content or b""),
原始流单独存,索引里只给路径,需要时才取
“raw”: f"flows/{flow.id}.raw",
}
OUT.write(json.dumps(rec, ensure_ascii=False) + “\n”); OUT.flush()
三个设计要点,每一个都对应一类真实事故:
▪ 白名单在落盘时生效。 范围外的流量根本不落盘,从源头杜绝"测试数据里混进无关目标"这个合规问题。
▪ 参数只留 key 不留 value。 模型要的是"这个接口收哪些字段",不是字段的值。值里混着手机号、身份证、订单号,不落盘就不会泄露。
▪ 凭证留指纹不留原文。 判断"这两个请求是不是同一身份发的"只需要指纹相等,不需要看到 token 本身。
索引策略
这一条是能力层最重要的实践,也是最容易被跳过的。反编译一个中型 APP 会出几千个 Java 文件、几百 MB;解包一个小程序会出几百个 JS 文件。整份喂进去,上下文瞬间爆掉,而且模型会淹没在无关代码里。
算一笔账就明白了。一条抓包索引记录压成 JSON 大约 150 到 250 token,一千条流量就是二十万 token 上下——整读一次索引就把窗口占满了,更别说原始流。反编译产物更夸张:一个中型 APP 出来的 Java 源码通常在千万 token 量级,比任何模型的窗口都大三个数量级。所以这里没有"塞进去"这个选项,只有"检索"这一个选项。
▪ 先建索引,让模型看目录。 生成文件路径、大小、包名、首若干行摘要,以及关键词命中统计。这份索引通常只有几百行,几千 token。
▪ 让模型自己决定读哪个,按片段取。 它读完索引后说"我要看 com/x/net/SignUtil.java 的 40 到 120 行",检索工具只返回这一段。单次任务的 token 能降一个数量级。
文件清单 + 大小 + 首行
fd -e java . decompiled/ -x sh -c \
‘printf “%s\t%s\t%s\n” “$1” “$(wc -c <”$1")" “$(sed -n 2p “$1”)”’ _ {} \
decompiled/INDEX.tsv
关键词热力:决定先看哪几个文件
for kw in http encrypt sign token secret exported WebView \
addJavascriptInterface setAllowUniversalAccessFromFileURLs; do
echo “## $kw”; rg -l –no-heading “$kw” decompiled/ | head -30
done > decompiled/INDEX-keywords.md
关键词热力这一步别小看,实战中不止一次靠 INDEX-keywords.md 里"某类关键词命中数明显多于 manifest 里声明的路由数"这种异常热力,直接定位到隐藏路由。
3.5 台账层:假设矩阵
最容易被当成文档工作而跳过的一步,实际上是省钱第一手段。我们一个目标累计 88 个测试节点,其中约六成是否定结果。
| 文件 | 粒度 | 作用 |
|---|---|---|
| journal/ | 按天流水,一个动作一行 | 给人看的过程记录。出问题时回溯那天干了什么,也是审计材料 |
| tested.md | 按假设,一个假设一行 | 给模型看的。新会话开局必读,直接决定这一轮不重复测什么 |
| findings.md | 按命中项 | 定级、复现步骤、证据三件套。报告的直接来源 |
格式必须是固定列的表,不能是自由散文。模型要能可靠地解析它、追加它、按状态过滤它。
| ID | 面 | 假设 | 状态 | 证据 | 日期 |
|——-|———–|———————————|———-|——————|——-|
| H-001 | 订单查询 | orderId 可遍历,无归属校验 | hit | ev/H-001/ | 09-12 |
| H-002 | 订单查询 | 改 userId 自报字段可越权 | miss | 网关签名绑定 | 09-12 |
| H-003 | 分发器 | 下游不复验网关身份 | hit | ev/H-003/ | 09-13 |
| H-004 | 文件上传 | 扩展名校验可绕(大小写/双扩展) | miss | 服务端白名单 | 09-13 |
| H-005 | 文件上传 | 上传路径可穿越 | blocked | WAF 拦截,未穿透 | 09-13 |
| H-006 | 用户中心 | 头像 URL 可 SSRF | miss | 仅取白名单域 | 09-14 |
| 规矩 | 说明 |
|---|---|
| 状态只能三选一 | hit 命中 · miss 已测且否定 · blocked 测了但被拦或无法判定。不允许"待定",待定的留在 journal 里 |
| miss 必须写原因 | “网关签名绑定"这四个字比"未发现问题"有用十倍。它告诉下一轮这条路被签名堵死了,先逆签名再说 |
| 怎么强制读 | 三处:CLAUDE.md 写"开工前必读"覆盖主控;任务书嵌入已否定清单覆盖子 agent;新一轮开局让模型先说出"本轮不测什么” |
| 否定结论不是垃圾 哪个面测过、为什么不通,这些记录有双重价值:既是防重复的账本,也是报告里"该做的都做了"的覆盖度证明。客户问"你们测没测 SSRF",你能直接指着 H-006 说测了、结论是什么、哪天测的。那六成否定结果让后续每一轮都省掉了重复投入,第二轮起成本降一半主要就来自这里。 |
|---|
3.6 并行子 agent
单线程跑测试浪费了智能体八成的价值。但并行的收益上限不由本地算力决定,由目标侧风控决定。
主控负责判断和整合:读台账、选面、拆任务、复核结论、决定停或继续。子 agent 负责执行:在一个窄范围里枚举、验证、回报。每个子 agent 有独立上下文和独立请求预算,用完即弃。
上下文隔离才是并行的主要价值,比"同时跑得更快"重要得多。主控会话如果自己去枚举 200 个 handler,工具返回会把上下文撑爆,到后面它已经不记得自己在找什么了——这就是"AI 开始失忆"的典型成因。换成子 agent,那几万 token 的探索垃圾留在子 agent 的上下文里,回到主控的只有一张几百 token 的结论表。主控的上下文因此能一直保持清醒,跑一整天也不迷路。
任务书的四个要素
| 要素 | 要求 |
|---|---|
| ① 精确目标 | 到端点或文件级别。不是"检查订单模块",是"检查 /api/v3/order/detail 及其 6 个同族端点" |
| ② 步骤 | 动作序列,包括用哪个工具、按什么顺序。不要让子 agent 自己发挥方法 |
| ③ 命中判据 | 什么算命中,写成可判定的条件。并写死命中即停,不要让它证实之后继续扩大 |
| ④ 请求上限 | 硬预算。用完必须停下来回报,不允许自行追加 |
目标
targets/
/assets/routes.md 中标记为 [order-family] 的 6 个端点。 只测这 6 个,不得扩展到任何其他端点。
红线(本任务适用,不得违反)
只读。禁止任何写操作,包括但不限于下单、取消、修改
请求间隔 >= 1.2s,本任务请求上限 120 次,用完即停
第三方数据命中后立即停止,最多读取 2 条
已否定,不要重复测
H-002 改 userId 自报字段(网关签名绑定,改即失效)
H-004 上传扩展名绕过(服务端白名单)
步骤
1. 用 capture-query 取这 6 个端点的真实请求模板
2. 对每个端点,列出所有客户端可控的参数(对照 assets/identity.md)
3. 对每个可控参数,构造一次"改为他人可能值"的请求
4. 比对响应:状态码、长度、是否含他人标识字段
命中判据
响应中出现不属于当前测试账号的业务标识(订单号 / 手机号 / 姓名)即为命中。
命中后立即停止本任务,回报以下内容,不要继续测剩余端点。
回报格式
| 端点 | 参数 | 结论 hit/miss/blocked | 证据文件 | 已用请求数 |
外加一段 100 字以内的判断说明。不要给安全建议,不要给修复方案。
分片原则
▪ 按业务线切(火车一个、酒店一个)或按攻击面切(分发器一个、存储类一个)。两种切法不要混用,混用会出现同一个端点被两个分片覆盖。
▪ 分片之间不能有重叠端点。同端点并发会互相踩响应,两边都拿到污染数据、得出错误结论,同时更容易触发风控。派发前把各分片的端点列表求交集,非空就拒绝派发——这一步可以脚本化,十行代码的事。
▪ 并发度受目标侧限制,经验值 3 到 5 路。判据是全局请求速率是否仍在红线内:5 个子 agent 各自守 1.2 秒间隔,合起来就是 4 请求每秒,很可能已经超了目标能容忍的阈值。速率红线按全局算,不是按单个 agent 算,这一条我们靠一次封 IP 才记住。
两个必须防住的失败模式
| 子 agent 的结论不能直接采信。 它会幻觉出复现不了的命中。主控必须拿着证据三件套独立复核每一条 hit,复核不过的降级为 miss 并记录原因。子 agent 的回报是线索,不是结论,这条没有例外。 |
|---|
| 子 agent 读不到主控的规则文件。 它有独立上下文,CLAUDE.md 不会自动带过去。所有红线必须抄进任务书——模板里把红线放在第二段就是为了这个。这是整套流程里最容易出合规事故的地方。 |
|---|
3.7 验收清单
搭完不要直接上真实目标。先逐条验,每一条都是一个可观测的行为,不是"装好了没有"。
1. 主备模型各跑通一次真实工具调用。不是对话通了就算,要让它实际调用一次 Read 或 MCP 工具并返回正确结果。兼容层的坑几乎都出在工具调用上。
2. 新会话里让模型复述红线。问"本工作区禁止哪些操作",它应该能准确列出六条。列不全说明规则文件没被读进去,或者写得太长被稀释了。
3. 代理能抓到目标明文,且白名单生效。访问一个非目标站点,检查它没有落盘。
4. 三层护栏各验一次。让模型去访问一个范围外域名,确认它在客户端层就被拦住;关掉第 1 层再试一次,确认第 2、3 层能兜住。
5. 模型能从索引里检索出指定接口。给它一个接口名,让它返回请求模板和参数列表。这验证的是索引可用性,不是抓包可用性。
6. 模型能读 memory 并输出模式预测。用 3.3 的冷启动提示词,看它能否给出 5 条排序和依据。空泛的输出说明记忆层条目写得不够具体。
7. 一次子 agent 派发能回收结构化结论。派一个最小任务,检查回报是否符合任务书要求的表格格式、是否守住了请求上限。
8. 台账能影响行为。 在 tested.md 里塞一条假的 miss 记录,让模型规划下一轮,它应该主动说"H-00X 已否定,本轮跳过"。
9. 大文件不被整读。给一个 5000 行的文件,看它是先读索引还是直接整读。
第 8 条最重要。其余各条验证的是零件装好了,只有这一条验证回路接通了。台账写了但没被读,机制就是开环的,前面所有投入都收不回来。
第四部分:Web 端
三种形态里最好下手的。浏览器本身就是最好的调试器,工具链最成熟,模型的接入点也最多。核心思路是让它自己驾驶浏览器:自己点、自己填、自己读结果、自己判断,人只在最后复核证据。
| 小节 | 讲什么 |
|---|---|
| 4.1 | Chrome DevTools MCP 能做什么、不能做什么 |
| 4.2 | 改包这一半交给谁 |
| 4.3 | 前端 JS 白盒:把压缩代码变成两张表 |
| 4.4 | 一条完整的越权验证链路 |
| 4.5 | 确定性验证:别让模型自己判断有没有漏洞 |
| 4.6 | 优缺点与 Web 端特有红线 |
4.1 Chrome DevTools MCP
Google 官方出的 MCP 服务器,底层用 Puppeteer 驱动 Chrome、用 DevTools 前端做分析,通信走 CDP。它的设计原则里有两条对安全测试很关键:返回语义摘要而不是原始数据流,重资产(截图、trace)返回文件路径而不是内容。这意味着它天然不是一个"把完整 HTTP 报文喂给你"的工具。
{
“mcpServers”: {
“chrome-devtools”: {
“command”: “npx”,
“args”: ["-y", “chrome-devtools-mcp@latest”,
“–isolated=true”,
“–allowedUrlPattern=https://.target.example.com/”,
“–proxyServer=http://127.0.0.1:8080”,
“–acceptInsecureCerts”,
“–redactNetworkHeaders”,
“–no-performance-crux”,
“–no-usage-statistics”]
}
}
}
一行装的版本是 claude mcp add chrome-devtools –scope user npx chrome-devtools-mcp@latest,但授权测试建议用上面这份带参数的,原因见本节末尾的默认行为清单。
常用工具
| 工具 | 作用 | 在测试里用来干什么 |
|---|---|---|
| take_snapshot | 拿页面无障碍树,带元素 UID | 确认当前状态、定位元素。后续 click / fill 都引用这里的 UID |
| evaluate_script | 在页面上下文执行 JS 函数 | 读 localStorage / sessionStorage 里的身份字段、调页面内的封装函数 |
| list_network_requests | 列本次导航以来的请求 | 拿真实请求清单。跨导航要加 includePreservedRequests |
| get_network_request | 取单条请求详情 | 带 requestFilePath / responseFilePath 可把报文写到文件,这是拿完整 body 的唯一途径 |
| navigate_page | 导航 | initScript 参数可在页面加载前注入脚本,钩前端函数很有用 |
| click / fill / fill_form | 输入自动化 | 走业务流程、填表单 |
| take_screenshot | 截图 | 取证和自检。不用于定位,定位用 snapshot |
为什么 snapshot 比 screenshot 重要
这是装完 MCP 之后最容易忽略的效率差,而且差得不小。
截图是像素。模型要先做视觉识别才知道登录按钮在哪,图像 token 按尺寸算,一张 1500×960 的截图就是一两千 token,认错了还得重来。snapshot 是无障碍树,模型直接拿到元素的角色、可访问名和引用 ID,下一步可以精确地说"点 uid=42 这个按钮"——同样一个页面,结构树通常只要几百 token,而且没有歧义。
实践约定:每步操作前 snapshot 确认状态,操作后再 snapshot 确认结果,截图只在需要留证据时用。真要截图,记得图像 token 随尺寸而非文件字节数增长,用 JPEG 或 WebP 比 PNG 小三到五倍。
它不能改包
这是把它用于安全测试时最大的坑,也是官方明确记录的限制:目前没有任何自动化途径通过 Chrome DevTools MCP 拦截和修改网络请求。
底层其实做得到——CDP 有 Fetch 域,Puppeteer 也有请求拦截 API——但这套能力没有被这个 MCP 暴露成工具。社区有 PR 在做,截至该 issue 没有维护者表态。所以别指望它,架构上必须分工:
| DevTools MCP · 驱动与观测 | 代理 · 改包与重放 |
|---|---|
| 走完整业务流程:登录、下单前置步骤、多步表单 | 全量流量落盘,结构化索引,白名单在落盘时生效 |
| 读页面内部状态:localStorage、前端变量、DOM 结构树 | 改参数重放:Burp Repeater / mitmproxy replay |
| 执行确定性验证:payload 到底有没有真的执行 | 出站硬断:范围外连接直接杀掉 |
| ✗ 拦截 / 篡改 / 重放请求 | ✗ 理解页面状态、驱动多步流程 |
把它们当成一个工具用,结果就是两头都做不好。
几个必须知道的默认行为
▪ 性能工具会外发 URL。 trace 可能被发给 Google 的 CrUX API。授权测试里务必加 –no-performance-crux,否则目标 URL 会流出去。
▪ 遥测默认开启,且与 Chrome 浏览器自己的遥测独立,退出一个不影响另一个。加 –no-usage-statistics。
▪ 不能以 root 运行。 Chrome 以 root 身份会立即退出,容器和 CI 镜像里常踩。镜像里建个非特权用户切过去。
▪ 沙箱会冲突。 启用了 macOS Seatbelt 或 Linux 容器沙箱时它起不了 Chrome,变通办法是连一个沙箱外手动启动的 Chrome(–browser-url=http://127.0.0.1:9222)。
▪ 开远程调试端口有风险。 官方明确警告:机器上任何应用都能连这个端口并控制浏览器。用完关掉。
4.2 改包这一半交给谁
三个选项,按"是否官方 + 可审计性"排序。
Burp Suite:有官方 MCP
PortSwigger 自己出的扩展,BApp Store 上架,Professional 和 Community 都能用,只有 Collaborator 相关的两个工具限 Pro。构建后在 Extensions 里加载 JAR,默认监听 127.0.0.1:9876。
| 工具 | 用途 |
|---|---|
| get_proxy_http_history_regex | 按正则过滤 proxy 历史。这是让模型从几万条流量里捞目标接口的主力 |
| send_http1_request / send_http2_request | 发任意请求并拿响应,改参数重放靠它 |
| create_repeater_tab / send_to_intruder | 把请求送进 Repeater 或 Intruder,留在 Burp 里可供人事后审阅 |
| get_scanner_issues | 拿扫描器发现的问题让模型挑值得深挖的(Pro) |
| generate_collaborator_payload / get_collaborator_interactions | 带外验证,SSRF 这类必需(Pro) |
| set_proxy_intercept_state / set_task_execution_engine_state | 开关拦截、暂停任务引擎 |
| get_proxy_websocket_history_regex | WebSocket 历史,正则过滤 |
Caido:官方没做,社区版能力更全
Caido 官方博客明确说自己没有官方 MCP,博客介绍的是社区实现,通过 GraphQL 和本地实例通信,README 列了 66 个工具。能力覆盖请求重放、会话管理、fuzzing、findings、拦截、改包规则、WebSocket,还有 caido_is_in_scope 这类让模型自己做范围校验的工具。
Caido 官方博客里有个论点值得直接搬进内部规范:agent 干的活会落进 Caido 的 History、Sitemap 和 Replay collection 并持久化,团队可以事后审阅,而不是只剩一段聊天记录。对授权测试的可审计性来说,这比多几个工具重要。
mitmproxy:轻量、可编程、适合自建
社区有 mitmproxy 的 MCP 实现,工具包括 set_scope、search_traffic、replay_flow、add_interception_rule、export_openapi_spec(从流量反推 OpenAPI)、detect_auth_pattern。但更常见的做法是不用 MCP,直接按 3.4 那样写 addon 落盘成 jsonl,再用自己的检索服务暴露给模型。可控性更好,而且护栏是自己写的。我们内部用的就是这条路。
| 关于那一堆"渗透 MCP" GitHub 上能搜到几十个封装 nmap / nuclei / ffuf / sqlmap 的 MCP 项目,ProjectDiscovery 和 ffuf 自己都没有官方 MCP。这类项目绝大多数是个人作品、没有安全审计,本质上是把任意命令行执行能力交给模型。内部使用前至少确认三件事:参数有没有转义、有没有做命令注入防护、有没有 scope 限制。有一个知名的聚合项目已经归档不再维护。 |
|---|
4.3 前端 JS 白盒:把压缩代码变成两张表
现代 SPA 的路由表、权限判断、加密逻辑全在前端。这是黑盒测试里最接近白盒的材料,而且不需要任何逆向。三条路径,按产出质量排序。
一、source map 还原
很多线上应用会把 .map 一起发出来。source map 本身就是一个 JSON:sources 是原始文件路径列表,sourcesContent 直接内联了原始源码,mappings 是 Base64 VLQ 编码的位置映射。只要 sourcesContent 在,还原出来的就是打包前的真实源码,连注释都在。
go install github.com/denandz/sourcemapper@latest
sourcemapper -output ./src -url https://target/assets/app.js.map
sourcemapper -output ./src -jsurl https://target/assets/app.js # 自动跟进 .map 引用
sourcemapper -output ./src -dir ./maps # 批量
带鉴权 / 走代理留痕
sourcemapper -output ./src -jsurl https://target/app.js \
-header “Cookie: session=…” -proxy http://127.0.0.1:8080
还原出来是带原始文件名和目录结构的源码。到这一步就回到普通白盒审计了,可以直接套 4.5 那套三阶段流程。
二、AST 提取(没有 map 时的主力)
jsluice 用 tree-sitter 解析语法树,去找 URL 已知会被使用的位置,比如赋给 document.location、传给 fetch() 或 window.open()。
它和正则工具的本质差别是理解字符串拼接:无法静态求值的表达式会被替换成 EXPR 占位符。一句拼接模板字符串的 fetch("/api/v2/users/" + id + “/roles”) 会被还原成 /api/v2/users/EXPR/roles。重建 API 路由表时这一点价值极大,纯正则做不到——正则看到的是一堆模板字符串碎片,AST 看到的是一条路由。
go install github.com/BishopFox/jsluice/cmd/jsluice@latest
jsluice urls –resolve-paths https://target app.js # 结构化 JSON:url / queryParams / method / type / source
jsluice secrets app.js
三、正则兜底与交叉验证
LinkFinder 用四条正则分别匹配完整 URL、绝对路径、带斜杠和不带斜杠的相对路径。对压缩混淆后的拼接路径几乎无效、噪声大,但作为交叉验证的第二来源还是有用的——jsluice 漏掉的偶尔能被它捞到。Burp 侧对应的是 JS Miner 扩展,能被动扫描并主动猜测 .map 文件,误报较多,需要人工复核。
这一步真正要产出的是两张表
| 表 | 内容 | 写到哪 |
|---|---|---|
| API 清单 | 方法、路径(无法静态求值的段用 EXPR)、参数名、调用它的模块 | assets/routes.md |
| 身份字段分类 | 哪些字段服务端下发、哪些客户端自报,后者用在哪些接口上 | assets/identity.md |
第二张表是后续所有越权类假设的来源,也是 3.3 那条 P-012 模式的检测入口。做完这两张表,后面的测试就从"不知道从哪下手"变成了"按表逐行验证"。
| 前端有 role 检查不代表后端有。前端发现的鉴权逻辑只是线索,必须回到代理侧做服务端验证。 |
|---|
4.4 一条完整的越权验证链路
把前面三节拼起来,一个真实形状的闭环长这样:
| 步骤 | 谁做 | 动作 |
|---|---|---|
| ① | 模型 | jsluice 出路由表 |
| ② | 模型 | 分类身份字段,找出客户端自报项 |
| ③ | 模型 | DevTools 读 localStorage / sessionStorage,动态确认字段存在且可改 |
| ④ | 模型 | 代理侧改值重放 |
| ⑤ | 模型 | 返回他人数据 → 越权成立 |
| ⑥ | 人 | 立即停,取 1 条证据,独立复核 |
①到⑤模型自己走完,人只做⑥。第 ⑥ 步的"立即停"是规则层强制的:证明越权成立只需要一条记录,继续拉取就从测试变成了数据获取,这是定性差别不是程度差别。
第 ③ 步值得单独说一句,因为它是压误报的关键。静态结论说"这个字段是客户端自报的",动态确认"它确实存在且值可改",两边对上了才进入第 ④ 步。只有静态结论就直接去改包,误报率会高很多——打包工具留下的死代码、被 feature flag 关掉的分支,静态看都像真的。
4.5 别让模型自己判断有没有漏洞
这一节讲的是 token 预算里那 20% 到 30% 的验证环节。Web 端是三种形态里最容易把验证做扎实的,因为浏览器能给出确定性答案。
确定性验证:XBOW 的做法
XBOW 在 2025 年 6 月成为 HackerOne 美国区排行榜第一名,是首个登顶的自主 AI。它公开的架构细节不多,但验证环节说得很清楚:自建了一组 validator,逐条确认每个发现。
XSS 的验证方式是让一个无头浏览器访问目标,确认 JavaScript payload 是否真的被执行了。不是让模型判断"这里看起来能 XSS",而是让浏览器给出一个二值答案。
把这个思路搬到工作台上:凡是能写成"确定性检查"的判据,就不要留给模型判断。
| 漏洞类型 | 确定性判据 |
|---|---|
| XSS | payload 有没有被真正执行(无头浏览器回调 / DOM 变化) |
| 越权 | 响应里有没有不属于测试账号的业务标识 |
| SSRF | 带外通道有没有收到回连 |
| 任意程序执行 | 目标进程有没有真的起来(第五部分那个案例用的是 calc.exe 弹窗) |
这些都能写进任务书的命中判据里,写成可判定的条件而不是"看起来可疑"。
对抗性自审:让同一个模型去证伪自己
Andrew Hoffman 用 Claude Code 做白盒审计时的三阶段方法值得直接抄,尤其是第二阶段:
| 阶段 | 做什么 |
|---|---|
| Phase 0 · 设置 | 选最强模型;配置成只读源码、禁止写操作;关闭遥测防止代码在日志里泄露;在临时 git 分支上执行 |
| Phase 1 · 侦察 | 映射结构、框架、依赖,识别入口点和信任边界。刻意不做深度挖掘以节省 token,输出到结构化 markdown,刻意限定在小范围文件集 |
| Phase 2 · 三步审查 | 先找高危,每个发现给出 0‒100 置信度、CVSS 估计、复现步骤和唯一 ID;然后同一个 agent 反转目标,试图证伪自己的发现;最后裁定,产出带 OWASP / CVSS 元数据的报告 |
他在 WordPress 4.7 上跑出了 CVE-2017-6818(tags-box.js 里的 DOM XSS),置信度中等,CVSS 估 5.4,CWE-79 归类正确。但他同时记录了一个更有价值的失败:Claude 引用了据称存在于 4.7 的缓解过滤器,而那些过滤器只存在于后续版本。这是"自信的错误分析"的教科书例子。他初始报告的三个发现里,有两个在对抗性复审中被自己证伪。
对抗性自审之所以有效,利用的正是 1.6 里那条"默认顺从"。正着问"这个发现对吗",模型顺着你确认;反着问"请证明这个发现是错的",同一个倾向就变成了对自己结论的攻击。代价只是多一轮 token,收益是误报率显著下降。建议换不同措辞多跑几次。
一个可以随便打的靶场
XBOW 开源了它的 validation-benchmarks:104 个 CTF 式 Web 挑战,每个有隐藏 flag,Docker Compose 一键起,flag 在构建时注入而不是硬编码。仓库自己警告只在隔离环境使用。
这东西对内部落地很有用:现成的、合法的、可自托管的靶场,用来评估自己的 agent 流程、调提示词、练手感,全程不用碰真实目标。PentestGPT 在 2025 年 12 月的实验里对这套 benchmark 拿到 86.5% 的成功率(90/104),可以作为参照基线。需要注意的是这套 benchmark 截至 2026 年中已被标记为过时,性能基本饱和——它现在适合练手,不适合用来证明谁更强。
4.6 优缺点与红线
| 优点 | 缺点 |
|---|---|
| 入门门槛最低,半天就能跑通第一个闭环 | WAF 和频控最先撞上,模型请求快,容易触发限速甚至封 IP |
| 工具链最全,抓包、调试、自动化都有现成方案 | 现代 SPA 状态多,偶尔会迷失在重复渲染里 |
| 前端 JS 就是现成的白盒材料,不需要任何逆向 | CSP 和反自动化脚本会让 evaluate_script 失效或被检测 |
| 模型可驾驶度最高,点填读判全程无人值守 | 前端加密签名字段会让重放失败,得先逆算法 |
| 验证可以做成确定性的,误报率天然低于另两种形态 | DevTools MCP 不能改包,必须再架一层代理 |
| Web 端特有红线:速率 模型不懂刷太快会被封。规则文件写死每请求间隔 ≥ 1.2 秒,并且记住 3.6 那条:多个子 agent 并行时按全局算。不设这条,典型结局是测试只做了三分之一,出口 IP 已经进了黑名单,剩下的工作要等换 IP 或等封禁过期。授权测试的时间窗通常有限,损失的是这个。被限速时停下来,不要试图绕——绕过频控这件事本身通常超出授权范围。 |
|---|
第五部分:客户端:小程序与 Electron
这类目标处在 Web 和 APP 之间:代码打包在本地,但不编译成机器码,提取出来就是能读的 JS。它们还有一个共同特点——鉴权往往比 Web 端更弱,因为开发者默认"客户端是我写的、用户改不了"。这个假设在 Web 上大家已经不敢做了,在小程序和 Electron 上还相当普遍。本部分最后那个案例,根因说到底就是这个假设。
| 小节 | 讲什么 |
|---|---|
| 5.1 | wxapkg 的结构与解密 |
| 5.2 | 解包工具与代码还原 |
| 5.3 | 抓包:为什么要上 Proxifier |
| 5.4 | 自动化:UIA 是唯一通路 |
| 5.5 | Electron:asar、fuses、webPreferences |
| 5.6 | MCP 与优缺点 |
5.1 wxapkg 的结构与解密
格式很简单,全文件大端序,14 字节固定头加一个索引区,数据区是明文未压缩的原始内容。
| 区段 | 长度 | 内容 |
|---|---|---|
| 魔数 | 1 B | 0xBE |
| unknownInfo | 4 B | — |
| infoListLength | 4 B | 索引区长度 |
| dataLength | 4 B | 数据区长度 |
| 结束标志 | 1 B | 0xED |
| 索引区 | infoListLength | fileCount,然后每个文件:nameLen / name / fileOff / fileLen |
| 数据区 | dataLength | 明文,不压缩 |
PC 微信落盘的包在这之外多一层加密:文件头 6 字节是 V1MMWX 标志,随后 1024 字节走 AES-256-CBC,其余部分是单字节 XOR。
密钥派生 key = PBKDF2(password = AppID, salt = b’saltiest’,
dkLen = 32, count = 1000, hmac = SHA1)
IV b’the iv: 16 bytes’ # 固定 16 字节字符串
XOR key AppID 倒数第二个字符的 ASCII
拼接 originData[0:1023] + xorData # 注意是 1023 不是 1024
最后那个 1023 是实现上的经典坑:AES 段解出来是 1024 字节,但拼接时只取前 1023 字节,写成 1024 整个包就解不开。所有能跑通的解包器都是这么写的,照抄就行。AppID 就是 {wxid} 目录名,解密所需的全部素材都在路径里。
| 一条需要纠正的流传说法 不少中文博客把 wxapkg 头部写成小端序 <4sIII、magic 为 V1MM 或 wxsg、数据区 zlib 压缩。这与所有实际可运行的解包器实现都不符。V1MMWX 是 PC 端加密层的标志,不是包格式的 magic;数据区也不压缩。以 0xBE 开头、0xED 结尾、大端序、明文数据区为准。 |
|---|
包在哪
微信 4.0 是个分水岭,PC 端的路径从文档目录搬到了 AppData。
Windows < 4.0
C:\Users\<用户>\Documents\WeChat Files\Applet\<wxid>\<n>\__APP__.wxapkgWindows >= 4.0
C:\Users\<用户>\AppData\Roaming\Tencent\xwechat\radium\Applet\packages\<wxid>\<n>\__APP__.wxapkgmacOS >= 4.0
~/Library/Containers/com.tencent.xinWeChat/Data/Documents/app_data/radium/Applet/packages/<wxid>/<n>/Android
/data/data/com.tencent.mm/MicroMsg/<userHash>/appbrand/pkg/
解包时最容易漏的一件事:分包
同一个 <wxid>/<n>/ 目录下常有多个 .wxapkg。主包有 2MB
上限,开发者会把低频但敏感的功能——后台管理、支付、实名认证——塞进分包。只解主包会得出"这个小程序几乎没有接口"的错误结论,而恰好分包里放的才是值钱的东西。
老的 wxappUnpacker 需要先解主包、再带 -s 指向主包输出目录解分包,因为 $gwx、公共样式和 app-config.json 都在主包里。新工具(wedecode、unveilr)会自动合并,但要求把主包和分包放在同一目录。
| 微信 4.1.x 之后 有工具反馈老解密方案在 4.1.x 之后失效,wedecode 宣称已支持 4.X。但我们没有查到任何公开文档写明 4.x 具体改了什么(新 magic 还是新 KDF),所以这里不给结论。实操上就是:把可用的工具版本和对应的微信版本号记进 memory/tools.md,下次直接查表,别每次重新试。 |
|---|
5.2 解包工具与代码还原
| 工具 | 语言 | 状态 | 特点 |
|---|---|---|---|
| wedecode | Node.js | 活跃 | 社区目前首推。全自动,交互式扫描本机小程序,跨平台,支持小游戏 / 插件 / 分包 |
| unveilr | TypeScript | 活跃 | 用 Babel AST 而非正则做解析,对新版混淆更稳;Windows 下从路径自动提取 AppID 自动解密 |
| KillWxapkg | Go | 活跃 | 纯 Go 单文件;自带 -hook 开 F12、-repack 回包、-sensitive 敏感信息导出 |
| wxappUnpacker | Node.js | 停更 | 所有后来者的祖宗,正则加手写 parser。新版包格式常失败,但它的 DETAILS.md 是格式文档 |
| wux1an/wxapkg | Go | 已归档 | 2026-02 归档,作者表示无力跟进微信更新,推荐改用 wedecode |
npm i wedecode -g
wedecode # 交互式,自动扫描本机小程序
wedecode ./ –out output_path –clear –open-dir
wedecode ./name.wxapkg –unpack-only # 只解包不反编译
npm i unveilr -g
unveilr wx /path/to/wxapkg/ -i wx11aa22bb33cc44dd -f
解出来是什么
小程序是双线程架构:WXML 和 WXSS 在渲染层(WebView),JS 在逻辑层(JSCore),两者经微信客户端中转。编译产物就按这个分成两大块。
| 产物 | 是什么 | 怎么用 |
|---|---|---|
| app-service.js | 逻辑层全部业务 JS,每个原始文件被包成 define(“path/to/x.js”, function(require, module, exports){…}) | 解析这些 define 调用,用第一个参数当路径把函数体写回磁盘。变量名不会恢复,还原出的是"可读但变量名仍是 a/b/c"的状态 |
| page-frame.html | 渲染层框架,内含 $gwx(持有全部 wxml)和 setCssToHead | 验证解析是否正确最省事的办法:丢进 Chrome,控制台执行 $gwx("./pages/index/index.wxml"),返回该模板的虚拟 DOM |
| app-config.json | 全局配置加每个页面的 window 配置 | 里面的 pages 和 subPackages 就是页面路由表的第一手来源 |
路由表和签名函数藏在哪
▪ 路由表在三处:app-config.json 的 pages / subPackages;一个集中的 config.js 或 api.js(找 baseURL / BASE_API 这类常量);各页面的 wx.request({url: …}) 调用点。
▪ 签名函数不要挨个文件翻。先 grep 关键词(sign / hmac / CryptoJS / nonce / timestamp / appSecret),然后找 wx.request 的统一封装层——签名基本都在那里往 header 里塞,顺着调用链回溯就能定位。
▪ 还原出来的典型形状是参数字典序拼接加时间戳加固定盐再 MD5。这类算法还原是模型很擅长的活,第七部分有对应的提问模板,关键是那句"无法确定的部分必须标注,不要用常见做法补全"。
5.3 抓包:为什么要上 Proxifier
PC 微信的小程序渲染进程 WeChatAppEx.exe 不走 Windows 系统代理。只设系统代理,Burp 里什么都看不到。需要 Proxifier 在系统层面按进程强制把流量推进代理。
1. 定位进程 任务管理器找 WeChatAppEx,右键打开文件位置拿完整路径。主进程是 Weixin.exe(旧版 WeChat.exe),连主进程一起加进规则更稳。
2. Proxifier 规则 Proxy Servers 里填 127.0.0.1:8080,协议选 HTTPS;Proxification Rules 里新建一条,Applications 填上一步的 exe,Action 指向该代理,默认规则保持 Direct。
3. 证书 Burp 的 cacert.der 改名 .cer,用 certmgr.msc 导入受信任的根证书颁发机构,不是"个人"。
小程序有没有证书固定
微信官方文档写得很明确:小程序只能与配置在后台的合法域名通信,仅支持 https 和 wss,服务器证书必须由受信任 CA 签发、域名匹配、在有效期内、证书链完整,iOS 不支持自签名。这是标准 CA 校验,不是 SSL Pinning。所以只要 Burp 的 CA 进了系统受信任根,PC 端小程序流量就能解密。
开发者工具里那个"不校验合法域名、TLS 版本以及 HTTPS 证书"的开关是开发期用的,只在开发者工具和真机调试模式下生效。我们没有找到权威资料证明小程序运行时做了额外的 pinning。但要注意另一回事:不少业务方会在小程序 JS 层做应用层加密,请求体加密加签名,抓到包也读不懂。那是 5.2 说的签名还原问题,和传输层 pinning 是两码事,别混为一谈。
开调试模式
三条路线,都是社区方案,没有官方支持,而且都强依赖微信版本。
| 路线 | 做法 | 代价 |
|---|---|---|
| 现成工具 | x0tools/WeChatOpenDevTools(Node,双击 bat,运行前先关微信)、JaveleyQAQ/WeChatOpenDevTools-Python(main.py -x 开小程序 DevTools,-c 开内置浏览器),或 KillWxapkg -hook | 都是直接进程 hook,不走标准调试端口,所以每个都标注了支持的微信/小程序版本号,版本不对就不工作 |
| 内存补丁 | 用 Frida hook 小程序加载函数,把 “enable_vconsole”:false 改成 true;再把 DevTools 界面从阉割版 wechat_app.html 换成完整版 wechat_web.html | 要自己动手;手工替代方案是 Cheat Engine 直接搜内存里那个字符串改掉,注意字节长度。地址随版本变化 |
| 另一条要剔除的说法 网上有把 –disable-gpu 说成微信调试开关的。我们没有找到任何佐证。它是标准 Chromium 禁用硬件加速的开关,常用于解决截图黑屏,本身不开启任何调试功能。 |
|---|
5.4 自动化:UIA 是唯一通路
想在真实小程序里自动操作(登录、翻页、走业务流程),第一反应通常是合成鼠标键盘事件。这条路走不通。但原因和常见说法不太一样,值得说准确一点,因为它决定了对策。
| # | 事实 | 说明 |
|---|---|---|
| 一 | OS 层面能区分合成输入 | Windows 的低级钩子结构 KBDLLHOOKSTRUCT.flags 有 LLKHF_INJECTED(0x10)和 LLKHF_LOWER_IL_INJECTED(0x02)两个位,鼠标侧有对应的 LLMHF_INJECTED。任何应用装一个低级钩子读这个标志,就能区分真实输入和 SendInput。这是操作系统提供的能力,不是微信特有的 |
| 二 | PostMessage 更不行 | 被 post 的消息完全绕过输入系统,不触发键盘钩子、不更新 GetKeyState。只要程序做多通道交叉验证就会发现不一致。微软自己推荐的替代顺序是:首选 UI Automation,其次 SendInput |
| 三 | 这才是当前真正的坑 | 微信 4.1.5 之后放弃了 Windows 原生控件,改用自研渲染框架,并且会探测无障碍客户端:未检测到合法 UIA 客户端时只暴露一棵骨架 UI 树,只有检测到屏幕阅读器等工具接入才构建完整控件树。检测方式是看你的进程有没有正确引用 UIAutomationClient.dll 和 UIAutomationTypes.dll 并成功 attach |
所以准确的说法是:不是"微信过滤了 SendInput",而是微信可以通过 LLKHF_INJECTED 区分合成输入,且 4.1.5+ 默认对未识别的 UIA 客户端隐藏控件树。因此坐标式合成输入不可靠,要走 UIA,而且得先让微信认出你是无障碍客户端。公开的做法有三种:写一个最小 C# UIA 客户端正确引入那两个 DLL 并 attach;先启动讲述人再跑脚本(兼容性差);或在启动自动化框架前做进程级配置。
UIA 怎么用
UIA 的关键在于它不模拟输入,而是直接调用控件自己暴露的 provider 接口——InvokePattern.Invoke() 相当于替按钮执行它的"被点击"逻辑,全程不产生鼠标事件,所以前面那些检测都不适用。
| Pattern | 用途 |
|---|---|
| InvokePattern | 点击可触发控件,不产生鼠标事件。我们实测能稳定点小程序里的按钮和金刚区图标 |
| ValuePattern | 直接读写控件值,填输入框比模拟打字可靠 |
| LegacyIAccessiblePattern | 把 MSAA 属性暴露给 UIA。自绘和非标准控件的兜底,微信这类自研框架常常只剩这个 |
| TextPattern | 读富文本内容和内嵌对象 |
| VirtualizedItemPattern | 虚拟列表里还没渲染的项,长列表翻页时用得上 |
1. 定位窗口 —— 用类名而不是标题,标题会随页面变
win = auto.WindowControl(searchDepth=1, ClassName=‘Chrome_WidgetWin_0’)
2. 等渲染 —— 小程序异步渲染,控件树会迟到
不要 sleep 固定时长,轮询目标控件出现
btn = win.ButtonControl(Name=‘我的订单’)
if not btn.Exists(maxSearchSeconds=8, searchIntervalSeconds=0.3):
raise RuntimeError(‘控件未出现,页面可能未加载完’)
3. 点击 —— 用 InvokePattern,不要用坐标
btn.GetInvokePattern().Invoke()
4. 输入 —— ValuePattern 比模拟键盘可靠
win.EditControl(Name=‘搜索’).GetValuePattern().SetValue(‘测试关键词’)
两条稳定性经验,都是修脚本修出来的:
▪ 控件命名不稳定。 同一个按钮在不同版本里 Name 可能变。用相对定位加文本模糊匹配:先定位到稳定的父容器,再在其中按文本片段找。写死的绝对路径在小程序上基本活不过一次版本更新。
▪ 分层查找,别从根遍历。 uiautomation 从根搜一个深层控件可能要几百次匹配,逐层缩小 searchDepth 只要几次。写脚本前先用库自带的 automation.py -t 0 把控件树和每个控件支持的 Pattern 打出来。
官方 SDK 为什么用不上
微信有官方自动化方案 miniprogram-automator,能力不弱:控制跳转、读页面数据、触发元素事件、往 AppService 注入代码、callWxMethod 调用任意 wx 接口、mockWxMethod 打桩。
但它要求 projectPath 指向一个能在开发者工具里正常编译的小程序工程,也就是含 project.config.json 的源码目录。所以测自己的小程序,官方 SDK 是最佳方案;测别人的小程序只有 wxapkg,官方 SDK 用不了,除非先反编译出可编译工程再导入,而那时 AppID 不匹配,云函数、支付、授权类接口都会失败。腾讯还有个 Python 方案 Minium,支持原生控件(授权弹窗、地图、相机),但同样从开发者工具驱动。
5.5 Electron
本质是打包的 Chromium。找到安装目录里的 app.asar 解开就是全部前端源码。格式极简,无压缩、支持随机访问:一个 8 字节的 Pickle 头给出 header 长度,header 本身是一段 JSON,剩下是数据区。
npx asar list app.asar
npx asar extract app.asar ./unpacked
npx asar extract-file app.asar main.js # 只取一个
注意 app.asar.unpacked/ 目录也要看:
打包时用 –unpack 排除的文件放在这,原生 .node 模块一定在这里
header JSON 里每个文件有 offset 和 size。offset 是字符串形式的 UINT64——因为 JS 的 Number 是双精度浮点,安全整数上限 2^53,大包会溢出,所以只能用字符串传。它还是相对数据区的偏移,算真实位置得加上头长度加 header 长度。另外内容相同的文件只存一份,多个条目指向同一 offset,统计文件数的时候别被骗了。
| 重打包之前先看 fuse Electron 从 16.0.0(macOS)和 30.0.0(Windows)起支持 ASAR 完整性校验,由 EnableEmbeddedAsarIntegrityValidation fuse 控制。开启后启动时校验 header 里的 hash,改过 app.asar 就强制终止进程。Electron Forge 7.4.0+ 和 Packager 18.3.1+ 会自动配置它。所以对现代 Electron 应用,“解包改 main.js 再打回去"这条路可能直接走不通。先跑 npx @electron/fuses read –app /path/to/App 看状态,再决定走哪条路。 |
|---|
关注点一:webPreferences
这部分是结构化程度很高的检索任务,模型做得很准。让它在解包产物里直接找 BrowserWindow 的构造参数:
| 配置项 | 官方默认 | 为什么关心 |
|---|---|---|
| contextIsolation | v12+ 默认开 | 关掉的话渲染进程 JS 能改写 preload 的对象和原型链,XSS 可直接升级为 RCE |
| nodeIntegration | v5+ 默认关 | 开启则渲染进程可直接 require()。官方原话:任何加载远程内容的渲染器都绝不能启用 |
| sandbox | v20+ 默认开 | 用操作系统能力限制渲染进程 |
| webSecurity | 默认开 | 关掉等于同源策略失效 |
| nodeIntegrationInSubFrames | 默认关 | 开启则 iframe 内也有 Node 能力,历史上出过 RCE |
| preload | 空 | preload 拥有完整 Node API。即使 contextIsolation 开着,如果它通过 contextBridge 暴露了能转发任意 IPC channel 的接口,渲染进程 XSS 仍能打穿 |
最后那一行是本部分案例的根因,值得单独记住:contextIsolation 开着不等于安全,它只隔离了对象,没隔离能力。 真正决定攻击面的是 preload 暴露了什么、暴露给谁。
关注点二:fuses
fuses 是打包时烧进二进制、签名前生效、烧完不可逆的开关。几个和测试直接相关的:
| fuse | 默认 | 含义 |
|---|---|---|
| runAsNode | 启用 | 可被当成通用 Node 解释器滥用 |
| enableCookieEncryption | 禁用 | 也就是 cookie 存在明文 SQLite 里 |
| enableNodeCliInspectArguments | 启用 | 这是下面能开 DevTools 的前提 |
| onlyLoadAppFromAsar | 禁用 | 堵不住"放个 app/ 目录覆盖"的路径 |
主进程是漏审角落
厂商常把敏感逻辑——本地加密、证书处理、自动更新、本地服务端口——放在主进程,因为"用户看不到”。这部分代码体积大、无人审查,是高产区。package.json 的 main 字段指向的就是它。
方法 A:不改包,加启动参数(首选,打包应用也有效)
open /Applications/YourApp.app –args –remote-debugging-port=8315 # macOS
YourApp.exe –remote-debugging-port=8315 # Windows
然后浏览器开 http://localhost:8315/ 或 chrome://inspect
主进程要用 V8 inspector,窗口内的 DevTools 只能调渲染进程
electron –inspect=9229 your/app
electron –inspect-brk=9229 your/app # 在第一行 JS 断下
方法 B:走代理
electron ./app –proxy-server=127.0.0.1:8080 –ignore-certificate-errors
方法 C:electron-inject,连调试端口后用 CDP 注入,不改包
pip install electron-inject
electron_inject -d -t 60 - /path/to/application # -d 启用 F12 / F5
要让 Electron 应用被 UIA 看见,启动时必须加 –force-renderer-accessibility。这条对 Chrome 也一样。
Electronegativity
Doyensec 出的静态检查工具,基于 AST 和 DOM 解析找安全相关配置,共 38 项检查。可以直接吃 .asar 文件,不用先解包。项目已不再积极维护,官方指向商业版。
npm install @doyensec/electronegativity -g
electronegativity -i app.asar -o out.sarif # SARIF 可接 CI / GitHub code scanning
electronegativity -i /path/to/app -s HIGH -c HIGH
输出里带 GLOBAL 的是跨文件关联检查,比如 HTTP_RESOURCES_WITH_NODE_INTEGRATION_GLOBAL_CHECK 要"加载 HTTP 资源"和"开了 nodeIntegration"两个事实同时成立才报。这类才是高价值发现,让模型读 SARIF 时优先看它们。
5.6 MCP 与优缺点
小程序方向已经有几个现成的 MCP,都是个人项目不是官方:wxapkg-mcp(7 个工具,从 list_wechat_apps 发现 AppID 到 decrypt_and_extract_appid 一步到位)、MCP-WEDECODEMCP(基于 wedecode,scan_local_miniapps → decompile_scanned_miniapp → read_output_file 的对话式流程)、e0e1-wx(带 MCP 的 GUI,包监控、自动反编译、CDP、云函数扫描,配置里列了覆盖的小程序版本号)。
Electron 这边没有专用 MCP。可行的组合是 asar extract 落盘,用文件系统类 MCP 或直接让 Claude Code 读目录,再把 Electronegativity 的 SARIF 喂给模型做风险排序。5.7 那个案例走的就是这条路,没用任何专用工具。
| 优点 | 缺点 |
|---|---|
| 解包即白盒,JS 源码直接可读可喂模型 | 自动化只有 UIA 一条路,还得先激活无障碍模式 |
| 客户端鉴权普遍偏弱,越权和信息泄露高发 | 解包脚本强依赖微信版本,4.1.x 之后的变更没有公开文档 |
| 小程序是标准 CA 校验不是 pinning,证书进系统根就能抓 | UIA 控件树随版本漂移,脚本维护成本不低 |
| 环境成本比 APP 低得多,不需要 root 设备 | 应用层加密签名很常见,抓到包也读不懂 |
| Electron 的配置审计是结构化任务,模型准确率高 | Electron 完整性校验会让重打包路线直接失效 |
第六部分:APP 端
攻击面最大的一种:本地存储、导出组件、WebView、原生 so、通信加密全都能碰。代价是环境最重,一台 root 的安卓机或模拟器是起步配置。也正因为如此,建议放在最后做。
| 小节 | 讲什么 |
|---|---|
| 6.1 | 静态:jadx 和 apktool 各管一段 |
| 6.2 | MCP:xref 才是模型要的东西 |
| 6.3 | Frida:server 还是 gadget |
| 6.4 | 抓包三件事:CA、NSC、pinning |
| 6.5 | 加固与脱壳 |
| 6.6 | 攻击面清单 |
| 6.7 | 闭环、优缺点与三形态横评 |
6.1 静态:jadx 和 apktool 各管一段
两个都要跑,分工很清楚:jadx 用来读懂和定位,apktool 用来改和回编。
| jadx | apktool | |
|---|---|---|
| dex 产物 | Java 伪代码,可读性高,可能失真或丢方法体 | smali,一比一忠实,可改可回编 |
| 能否重打包 | 不能 | 能,apktool b 之后必须重新签名 |
| 典型用途 | 读逻辑、找类名方法名,喂给模型和 Frida | 改 network_security_config.xml、改 manifest、注入 gadget、patch smali |
先花十秒判断有没有加固
不要一上来就等全量反编译。先只出资源,或者直接 unzip -l 看 lib/ 和 assets/ 的文件名。主流商业加固都有明显的文件名特征(libDexHelper.so、ijiami.、libshell-.so、libjiagu*.so、libxloader.so、libegis.so 这类)。识别出加固就直接跳到 6.5,别浪费半小时。
jadx -s -d out_res target.apk # 只出资源 + manifest,秒级
unzip -l target.apk | grep -E ’lib/|assets/’
jadx 的几个关键 flag
| flag | 为什么要加 |
|---|---|
| –show-bad-code | 混淆或加固包必开。jadx 反编译失败的方法体默认直接丢弃,不加这个你看到的是一片空方法,还以为人家没写 |
| –deobf | 对短名混淆做重命名。配 –deobf-cfg-file 存映射,下次复用,也能和队友对齐符号 |
| –no-inline-methods | 准备写 Frida hook 时加。内联之后的方法名和运行时对不上,hook 点会找不到 |
| –no-imports | 全写完整包名。对模型友好,消除同名类的歧义 |
| –output-format json | 结构化输出,进 LLM 管道比 Java 文本好处理 |
| –call-graph json | 导出全应用调用图。做调用链分析时这是理想输入 |
| –single-class | 只反编译一个类。模型说"我要看这个类"时用它,比整包快几个数量级 |
加固 / 混淆包的实战组合
jadx -d out –show-bad-code –deobf –no-imports –no-inline-methods -j 8 target.apk
jadx 官方明确声明:对混淆或加固的 APK 不保证能完整反编译。这是官方立场不是社区吐槽,规划时间时按这个预期算。
MobSF 当基线扫描器
它的价值是筛选不是结论。一键出基线报告,让模型从报告里挑值得深挖的点,比让它从零开始通读省很多。Docker 起,静态分析不需要连设备:
docker run -it –rm -p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest
默认 mobsf/mobsf,API key 在实例自己的 /api_docs 页面
curl -F ‘file=@target.apk’ http://localhost:8000/api/v1/upload -H “Authorization: KEY”
curl -X POST http://localhost:8000/api/v1/scan –data “hash=
” -H “Authorization: KEY” curl -X POST http://localhost:8000/api/v1/report_json –data “hash=
” -H “Authorization: KEY”
动态分析要连一台 adb 设备,官方说明只支持到 Android 11(API 30),12 以上不支持。另外它把两件麻烦事 API 化了,接自动化管道时很省事:/api/v1/android/root_ca(装/卸系统 CA)和 /api/v1/android/global_proxy(设/取消全局代理)。还有一组 /api/v1/frida/* 端点,可以把 MobSF 当 Frida 编排器用。
6.2 MCP:xref 才是模型要的东西
这条链上最成熟的一块。jadx-ai-mcp 是跑在 jadx-gui 里的 Java 插件(默认端口 8650),jadx-mcp-server 是 Python 侧的 MCP 服务器(默认 8651)连过去。
jadx plugins –install “github:zinja-coder:jadx-ai-mcp”
uv run jadx_mcp_server.py –http –jadx-host 127.0.0.1 –jadx-port 8650
暴露三十多个工具,按价值排:
| 组 | 工具 | 价值 |
|---|---|---|
| 交叉引用 | xrefs_to_class()、xrefs_to_method()、xrefs_to_field() | 最值钱的一组。这给了模型做污点追踪的能力——从一个敏感字段反向找所有引用点。纯 grep 做不到,因为 grep 不懂继承和重载 |
| 取代码 | get_class_source()、get_method_by_name()、get_methods_of_class()、get_fields_of_class() | 配合 3.4 的索引策略,模型先看清单再决定读哪个方法 |
| Android 专用 | get_android_manifest()、get_main_activity_class()、get_manifest_component()、get_strings() | 入口点 |
| 字节码 | get_smali_of_class() | 反编译失败时的兜底,Java 伪代码出不来时还能看 smali |
设计上有个关键点:它和运行中的 jadx-gui 实例通信,不是重新跑一遍反编译。好处是有 xref、有已加载的符号表;坏处是要先人工把 APK 在 GUI 里打开,而且项目自述还在早期阶段,会崩。
同一作者还有 apktool-mcp-server,13 个工具。注意它包含 modify_smali_file() 和 build_apk(),意味着模型可以直接改 smali 并重打包。接入时要在权限层面做约束,别让它无声地改了你的样本。
6.3 Frida:server 还是 gadget
静态分析只能告诉你"代码里有这么一段判断",不能告诉你"它在运行时走没走到、参数是什么"。APP 里大量逻辑有多条分支、被开关控制,或者干脆是废弃代码。Frida 把静态的"可能"变成运行时的"确实",这一步是 APP 形态误报率的主要控制点。
| frida-server | frida-gadget | |
|---|---|---|
| 形态 | 设备上的独立进程,通常以 root 跑 | 共享库,被加载进目标进程内部 |
| 前提 | 需要 root | 不需要 root,但需要改 APK 或 LD_PRELOAD |
| 进程视角 | 可 attach / spawn 任意进程 | 进程列表里只有一条 Gadget |
| 适用 | 有 root 的测试机,效率最高 | 无 root 设备,或需要规避端口与进程特征检测 |
Gadget 有四种交互模式,其中 Script 模式对自动化最有用:自动加载并执行一个独立 JS 文件,不需要 host 端连接。Listen 是默认模式,会阻塞直到有人 attach。
| 一个非常容易踩的命名规则 Gadget 的配置文件名等于二进制名加 .config。但在 Android 上,对 non-debuggable 应用,配置文件名必须以 lib 开头、以 .so 结尾,因为 APK 里只有 lib/ 下的 .so 会被解包。名字起错了表现是"配置完全不生效",没有任何报错,很难排查。 |
|---|
常用 API
Java 层,前两个是日常,后两个是加固包的救命稻草:
| API | 用途 |
|---|---|
| Java.use(name) | 取类 wrapper,hook 方法或 new 实例。最常用 |
| Java.choose(name, cb) | 枚举堆上的活实例。用来拿已存在对象的字段值,比如已初始化的 client、已解密的配置对象。静态分析拿不到这些 |
| Java.enumerateLoadedClasses() | 列出所有已加载类名。脱壳后确认 dex 真的加载进来了、以及定位混淆类,第一手段 |
| Java.enumerateClassLoaders() | 加固包必用。壳的类和真实 dex 往往在不同 ClassLoader 里,Java.use 找不到时先把 Java.classFactory.loader 换成目标 loader |
const libc = Process.getModuleByName(’libc.so’);
Interceptor.attach(libc.getExportByName(‘read’), {
onEnter(args) { this.fd = args[0].toInt32(); },
onLeave(retval){ if (retval.toInt32() > 0) console.log(‘read’, this.fd); }
});
// 算 so 内偏移的 hook 点
const base = Module.findBaseAddress(’libnative.so’);
Interceptor.attach(base.add(0x1234), { … });
// 脚本用 send() 输出结构化 JSON,host 侧收集 —— 这是接模型的标准接法
send({ type: ‘sign’, input: argStr, output: retStr });
新版 Frida 官方示例已从 Module.findExportByName() 改用 Process.getModuleByName().getExportByName(),过渡期两种都能用。写脚本前先确认目标设备上的 frida 版本——这是"从网上抄的脚本跑不起来"最常见的原因,没有之一。
为什么 Frida 只能改方法边界
这一条直接决定了第七部分那个 APP 提问模板的形状,值得说清楚机制。
Java 层的 hook 是替换方法实现:Frida 拿到目标方法的 ArtMethod,把入口指向自己的桥接代码,原方法整体被接管。Native 层的 Interceptor.attach 是在函数入口处改指令、跳到 trampoline,onEnter / onLeave 分别在进入和返回时插入。两种机制的粒度都是一次调用的边界——参数进来、返回值出去。
方法内部的某一行 if,既不是入口也不是出口,Frida 没有插入点。想改它只能去改指令,那已经是 patch 而不是 hook 了。
所以找 hook 点时要找方法边界:理想的点是一个返回布尔值的独立校验方法,而不是嵌在大函数中间的一个 if。让模型读 jadx 输出时,必须明确要求输出类全名 + 方法名 + 参数签名 + overload + 返回类型,而不是"这里有个校验"。前者可以直接生成 Java.use(…).method.overload(…).implementation = …,后者你还得自己回去翻一遍。
JNI 动态注册的定位
很多商业 APP 的 native 方法通过 RegisterNatives 动态注册,jadx 里只看到 native 声明、看不到实现地址。两个工具解决这件事:
▪ hook RegisterNatives(lasting-yang/frida_hook_libart)。输出直接给出 so 内偏移,拿去 Ghidra 或 IDA 定位。
▪ jnitrace。jnitrace -l libnative-lib.so
6.4 抓包三件事:CA、NSC、pinning
这三件事经常被混为一谈,但它们是三个独立的问题,解法也不同。
| 问题 | 表现 | 解法 |
|---|---|---|
| ① 系统不信任你的 CA | Android 7+,targetSdk ≥ 24 的应用默认不信任用户添加的 CA。官方原话是 by design | 把 CA 弄进 system 信任库 |
| ② 应用自己配了 NSC | 应用用 | 改 NSC 重打包,或 Frida |
| ③ 应用在代码里做 pinning | OkHttp 或自研实现。装系统 CA 解决不了 | 只能靠 Frida。NCC 那份报告里原话很直接:有 SSL Pinning 的应用无论如何都拦截不了 |
| Android 14 改了信任库的位置 从 Android 14 起,CA 校验不再读 /system/etc/security/cacerts,改走 Conscrypt APEX 模块,从 /apex/com.android.conscrypt/cacerts 读。老的 Magisk CA 模块在 Android 14 上直接失效。对应的新方案(NCC 的 ConscryptTrustUserCerts、TrustAnyCert 等)做法是把证书 mount 进 Conscrypt 的 namespace,并以 late_start service 模式运行——这个时序很关键,必须确保 Zygote 起来之后再 mount,否则应用进程 fork 出来的时候看到的还是旧的挂载视图。测试机的系统版本要记进 memory/tools.md,这是最容易在新目标上卡住半天的地方。 |
|---|
改 NSC 这条路的代价
apktool d -f -o work target.apk
1. 编辑 work/res/xml/network_security_config.xml,加 <certificates
src=“user” />
2. 在
上加 android:networkSecurityConfig="@xml/network_security_config"
apktool b work -o patched.apk
zipalign -p -f 4 patched.apk aligned.apk
apksigner sign –ks my.keystore aligned.apk
重打包会破坏原签名,代价是:触发签名校验类防护、掉 Play Integrity、App Links 的 assetlinks.json 失配。优先选装系统 CA,只有在无 root 设备上才走这条。
objection 到底 hook 了什么
android sslpinning disable 不是"装证书",是用 Frida 把校验函数改成恒真或空实现。它打七个点,值得知道,因为遇到打不过的情况你得知道漏了哪层:
| hook 点 | 做什么 |
|---|---|
| javax.net.ssl.SSLContext.init() | 换掉信任源,塞一个空实现的 TrustManager |
| okhttp3.CertificatePinner.check() 及 check$okhttp() | 跳过 pin 比对,直接返回不抛异常 |
| …conscrypt.TrustManagerImpl.verifyChain() | Android 7+ 的关键点,跳过链校验,原样返回传入的 chain |
| …conscrypt.TrustManagerImpl.checkTrustedRecursive() | 返回空列表 |
| Appcelerator / PhoneGap 插件的对应方法 | 跨平台框架的专用路径 |
三层覆盖不同实现路径:换信任源、跳过链校验、跳过 pin 比对,所以要全打。工程化程度更高的是 httptoolkit 那套脚本,它的设计有两点值得学:native-tls-hook.js 改 BoringSSL 让它信任你配置的证书,而不是整个关掉校验(不容易触发应用的异常分支);以及一个 fallback 脚本,检测到证书校验失败时自动为混淆过的未知 pinning 实现生成补丁。
Flutter 走 BoringSSL 不走系统 CA,必须单独处理。
6.5 加固与脱壳
先分清代际,因为每一代的解法完全不同,投入产出也差很远。
| 代际 | 做了什么 | 脱壳路线 | 投入 |
|---|---|---|---|
| 一代 · DEX 整体加壳 | 完整 DEX 加密,只留壳应用 | 内存搜索 / 文件落地即可 | 低 |
| 二代 · 函数抽取 | 方法体被抽走,运行时按需回填 | 抓解密时机:主动调用逼它回填,再在 ART 层截获 | 中 |
| 三代 a · VMP | 自定义指令集 + 自定义解释器 | 要找到解释器,还原指令映射关系 | 极高,通常不在授权测试的时间窗内 |
| 三代 b · Dex2C | Java 逻辑翻译成 C | 基本无法恢复为 Java,重点转向 native 层,看 jni.h 相关调用 | 高,这时动态分析比静态划算得多 |
判断代际的第一步是看 6.1 那个文件名特征,第二步是脱完之后用 –show-bad-code 看方法体是不是还空着。
| 工具 | 要 root | 原理 | 说明 |
|---|---|---|---|
| frida-dexdump | 是 | 内存搜索 DEX 签名 | -d 深度扫描能找到 header 被破坏或碎片化的 DEX。仓库 2023 年 7 月已归档,新版环境可能要用社区 fork |
| BlackDex | 否 | DexFile cookie 提取 | 普通消费设备和模拟器都能跑,支持 Android 5.0‒12。deep 模式尝试回填被抽取的方法指令,但可能耗时数分钟且提高失败率 |
| FART | 要刷机 | hook 解释器 + 主动调用 | patch ART 源码重编 ROM。对二代壳的核心手段:主动调用每个方法逼壳解密回填,再在 ART 层截获。配 dexfixer 和 fart.py 做重组 |
脱壳产物怎么用
▪ 一代壳出一堆完整 DEX。可以直接 jadx -d out dump_dir/.dex,但更好的做法是把它们替换回原 APK 里的 classes.dex 再 zip 回去——这样资源、manifest、R 类都还在,jadx 能正确解析 @string/xxx 引用。
▪ dump 里会混着壳自己的类和大量重复类。先按大小和类数量排序,丢掉明显是壳的那几个。
▪ 二代壳出的是 DEX 加分散的 CodeItem,要按 method_idx 回填合并。这一步高度依赖具体壳和工具产物格式,没有跨工具的通用方案。
验证合并成功与否有两个办法:用 jadx –show-bad-code 打开看关键方法体是不是还是空的或者只有 throw new UnsupportedOperationException;以及运行时用 Java.enumerateLoadedClasses() 对照,确认脱壳产物的类名集合与运行时实际加载的一致。如果大量方法体仍为空,说明是二代壳没回填成功,或者已经是三代了。
反调试与反 Frida
常见检测手法七种,按对抗难度从低到高:
1. 扫默认端口 27042 并做 D-Bus 握手确认
2. 扫 /proc/
3. 枚举线程名找 gum-js-loop、gmain、pool-frida
4. 读 /proc/
5. 读 TracerPid
6. fork 子进程 ptrace 父进程占位,别的调试器就 attach 不上了
7. 把内存里的 .text 段与磁盘上的 so 对比
前六种都能靠改端口、改进程名、重编译去特征(strong-frida 那套 patch 就是干这个)来绕。第七种不行——detectfrida 的作者明确指出这种检测是 frida agnostic 的,改特征字符串绕不过去。评估对抗难度时,看目标有没有做这一种,是判断"值不值得投入"的分界线。
| 一条务实的建议 这是持续军备竞赛,“套个公开脚本一把梭"的预期不现实。正确姿势是先用 jnitrace 或 hook_RegisterNatives 把检测点定位出来,再针对性写 hook。另外 spawn 模式(frida -U -f |
|---|
6.6 攻击面清单
这部分是结构化检索,让模型跑在 jadx 输出上效率很高。按产出概率排序。
Manifest
| 属性 | 默认 | 看什么 |
|---|---|---|
| android:debuggable | false | 为 true 则可 attach 调试器,run-as 能直接读私有目录。发布包里出现即为问题 |
| android:allowBackup | true | 可 adb backup 导出应用数据。保持 true 就该配 dataExtractionRules 排除敏感数据 |
| android:usesCleartextTraffic | API 28+ 为 false | 注意:Android 7.0+ 存在 NSC 时这个属性被忽略。官方已标注正在废弃 |
| android:exported | 有 intent-filter 时必须显式声明 | 为 true 时任何 app 都能用显式 ComponentName 启动它,即使不匹配 intent-filter |
| android:taskAffinity | 包名 | 配合 singleTask 影响 activity reparenting,任务劫持类问题的根源 |
| android:extractNativeLibs | 看 minSdk | false 时 so 不解压、直接从 APK 加载,影响你能不能从 /data/app/…/lib/ 拿到 so |
查 deeplink scheme
adb shell dumpsys package
| sed -n ‘/Schemes:/,/Non-Data Actions:/p’ 触发,然后确认真的跳过去了 —— “Starting Intent” 这一行不够
adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE \
-d ‘
:// /web?url=https://example.com’ adb shell dumpsys activity activities | grep mResumedActivity
显式启动非导出或隐藏的 Activity
adb shell am start -n
/.SomeActivity
典型的 deeplink 问题形状是 xxx://host/web?url=<攻击者可控>,把 URL 参数直接喂给 WebView。6.8 那个案例是它的变体,而且更隐蔽——路由压根没在 manifest 里声明。
WebView
致命组合是 setJavaScriptEnabled(true) 加 setAllowUniversalAccessFromFileURLs(true) 再加载外部可控 URL,尤其是从 deeplink 参数来的。
grep -rnE “addJavascriptInterface|setAllowUniversalAccessFromFileURLs|setAllowFileAccessFromFileURLs|setAllowFileAccess|onReceivedSslError|shouldOverrideUrlLoading|loadDataWithBaseURL” out/sources/
onReceivedSslError 里调 handler.proceed() 等于无条件接受任何证书。setAllowFileAccess 在 API 30 起默认从 true 改成了 false,老应用要看 targetSdk。
本地存储与 native
| 位置 | 注意什么 |
|---|---|
| SharedPreferences | /data/data/ |
| SQLite | databases/,默认不加密。别忘了 -journal 文件,删掉的数据可能还在里面 |
| 外部存储 | 全局可读写;应用卸载后(若在 app 目录外)不会被删 |
| Realm | 默认 files/default.realm,可加密但密钥常被硬编码 |
| 硬编码密钥 | 静态 strings 加 trufflehog 筛一遍;但运行时 hook SecretKeySpec 构造函数更可靠,因为密钥常常是运行时拼出来的 |
6.7 闭环、优缺点与三形态横评
| 环节 | 谁做 | 产物 |
|---|---|---|
| jadx 反编译 + 建索引 | 人起手 | INDEX.tsv / INDEX-keywords.md |
| 读索引 + xref | 模型 | 可 hook 的方法清单(全名 + 签名 + overload) |
| Frida 验证 | 模型写脚本,人注入 | 运行时回显 |
| 代理改参数重放 | 模型 | 证据三件套 |
| 台账 | 模型写,人复核 | hit / miss 都记 |
模型是串起这三个工具的那根线:读反编译结果形成假设、写 hook 脚本验证假设、读抓包结果判定结论。人负责注入、复核和叫停。
现成的 AI 封装
2025 到 2026 年已经形成了几条清楚的路线,都值得先看一眼再决定自己造不造:
| 路线 | 代表 | 说明 |
|---|---|---|
| MCP 挂工具(最主流) | jadx-ai-mcp + jadx-mcp-server + apktool-mcp-server + frida-mcp | 典型闭环是模型读 manifest、搜类、查 xref、读 smali,然后生成 Frida 脚本,再通过 frida-mcp 注入,脚本用 send() 回传运行时数据,模型读结果迭代 |
| Claude Code skill | incogbyte/android-reverse-engineering-claude-skill | 封装好的七阶段流程:依赖检查 → 反编译 → 架构分析 → 安全审计 → API 提取 → 调用流追踪 → 自适应 Frida 绕过(分析崩溃日志,基于实际代码生成针对性 hook,迭代到绕过为止) |
| 工作区式编排 | TheQmaks/areclaw | 14 个工具加 21 个 Python 包,以 Claude Code 作编排器。集成 jadx / apktool / Ghidra / Frida(内置 15 个脚本)/ 多个反混淆器 / trufflehog,提供 /analyze-apk、/find-api、/intercept、/compare-versions 等 skill |
/compare-versions 那个值得单独说一句:版本 diff 是模型很擅长、人很痛苦的任务。拿两个版本的 jadx 输出做语义 diff,定位新增或修改的安全相关逻辑,往往能直接指出"厂商最近在改什么”,而这通常就是最新引入问题的位置。
| 优点 | 缺点 |
|---|---|
| 攻击面最广,本地存储、组件、网络、native 层都能碰 | 环境门槛最高,要 root、装 Frida、处理 CA 和 pinning |
| 反编译产物喂模型做白盒,效果接近开源项目审计 | 加固会让 jadx 直接失效,脱壳基本靠人工 |
| Manifest 这类结构化数据模型准确率非常高 | 反调试检测越来越常见,是持续对抗 |
| xref 工具让模型能做污点追踪,grep 做不到 | 产物体积大,不做索引必然上下文爆炸 |
| Frida hook 灵活度极高,模型写脚本远快于人 | Android 14 改了信任库位置,老方案全部失效 |
三形态横评
| 维度 | Web | 客户端 | APP |
|---|---|---|---|
| 环境成本 | 半天 | 一天 | 二至三天 |
| 白盒程度 | 高,前端 JS 现成 | 高,解包即源码 | 中高,受加固影响 |
| 模型可驾驶度 | 最高,点填读判全自动 | 中,UIA 可驱动,脚本要维护 | 中,脚本模型写,注入靠人 |
| 主要对抗 | WAF · 频控 · CSP | 合成输入被吞 · 包加密 | pinning · 加固 · 反调试 |
| 鉴权薄弱度 | 中,被审视得最多 | 高,“客户端是我写的” | 中高,本地逻辑多 |
| 验证确定性 | 高,浏览器能给二值答案 | 中 | 中,Frida 可确认但要先注入成功 |
| 首洞周期 | 半天至一天 | 一至三天 | 三至七天 |
| 入手顺序:Web → 小程序 → APP Web 先跑,半天出活,先建立"这套东西确实能出结果"的信心,顺便把规则层、台账层、子 agent 编排这些与形态无关的部分在最低摩擦的环境里调顺。小程序第二,练解包和逆向手感,第一次面对自动化被反制,但环境成本还不高。APP 最后。一上来就啃 APP,大概率卡在环境配置上,两三天什么结果都没有,团队对整套方法的信心就没了——这是实践中见过最常见的失败方式,而它跟方法本身对不对毫无关系。 |
|---|
第七部分:提示词
切片选对了、威胁模型建好了,只是进了正确的邻域。到了之后,话怎么说直接决定你拿到的是一堆泛泛观察还是一条能用的发现。这一部分的十条技巧来自 Needle in the Haystack,我们在实战里验证过其中几条,效果差异比预想的大。
7.1 一个原则:搜索模式,不是评估模式
所有技巧都指向同一件事:把模型从评估模式推向搜索模式。
| 评估模式 | 搜索模式 | |
|---|---|---|
| 你怎么问 | “这个函数有没有漏洞?” | “这个函数肯定有两到三个问题,找出来” |
| 模型的任务 | 分类(是 / 否) | 生成(找出具体位置) |
| 阻力最小的答案 | “整体看起来安全” | 不存在,只能去找 |
| 读码方式 | 扫一眼 | 逐行排查 |
| 产出 | 一串没有优先级的理论问题 | 具体位置 + 触达条件 |
同一段代码,问法不同拿到的东西完全不是一个量级。这不是玄学,是你把它的优化目标改了:分类任务有一个低成本的默认答案,生成任务没有。
7.2 先把威胁模型问出来
在用任何技巧之前先做这一步。它对应 token 预算里那不到 10% 的脚手架,也是后面所有提问的锚点。做法是让模型基于历史 CVE 生成,而不是你自己拍脑袋写。
以下是 [项目名] 的历史 CVE 描述:
[粘贴 GitHub Security Advisories / NVD / 厂商公告里的描述文本]
基于这些漏洞的类型和模式,输出一页纸的威胁模型,必须包含:
1. 最常见的漏洞类别,按历史出现频次排序
2. 最薄弱的信任边界是哪几条,依据是什么
3. 入口点清单:HTTP 路由 / RPC handler / 消息队列 / 文件上传 / 反序列化
4. 攻击者模型:他是谁、有什么凭证、能控制哪些输入
5. 高危操作清单:哪些操作一旦被越权触发后果最严重
不要写通用安全建议。每一条都要能指回上面的 CVE 描述。
输出控制在一页以内。
这一步的产出直接写进
targets/
历史 CVE 都是堆溢出和整数溢出,威胁模型就围绕内存损坏建;都是权限绕过,就围绕权限模型建。往同一个方向挖,找到的东西大概率也被维护者接受,因为那本来就是这个项目的薄弱结构。
另一个高产入口:审修复补丁
找到修复某个历史漏洞的 commit,让模型分析那个 patch 能不能绕过。修复代码经常只堵了报告里提到的那个入口,同一根因的其他路径还开着。Anthropic 审 Firefox 的 CVE 时就这么干过:拿 fix commit,让 Claude 看有没有遗漏。
这个入口的好处是范围天然就窄——一个 commit 改了几个文件,切片是现成的。
7.3 十条技巧
按效果排序,前三条是最该先学会的。
01 断言漏洞存在
效果最明显的一条。直接说"这里肯定有两到三个安全问题",比问"有没有漏洞"的分析质量高一个档次。
模型有很强的顺从和确认倾向。你问"有没有",它走阻力最小的路;你断言存在,它的优化目标就反过来了——不再评估有没有 bug,而是去搜它被告知存在的 bug。读码方式从扫一眼变成逐行排查。
✗ 这个函数有没有安全问题?
✓ 这个函数里确定存在 2 到 3 个安全问题。逐行找出来,
每个给出:所在行、触发条件、攻击者需要控制什么。
02 要利用路径,不要安全评级
别让模型给"安全性评分"或"风险等级",让它给一段可执行的验证步骤或完整的利用链。
输出格式变了,思考方式就变了。它开始想攻击者实际能拿到什么、前置条件是什么、这个发现是真的还是理论上的。还有个副作用是好的:对抗性框架让它更愿意说"这个认证机制从根本上就是坏的",而不是软化成"这里可以改进"。安全结论需要的是前者那种明确程度。
✗ 这里的输入校验是否充分?给个风险等级。
✓ 写一段能绕过这个校验的 PoC。
给出:完整请求、需要的前置条件、预期响应特征。
如果你认为绕不过,说明是哪一条检查挡住了,以及挡住的具体机制。
03 对抗性角色
把"审查代码的安全审计员"换成"被雇来找可利用漏洞的红队"。审计员的优先级是覆盖全面,红队的优先级是打得进去。角色一换,它汇报的东西从"清单"变成"路径"。
✗ 你是一名安全审计员,请审查以下代码。
✓ 你是一名红队,按结果付费,只有可利用的漏洞才算数。
理论问题和最佳实践建议不算交付物,不要写。
04 假锚点
“我已经在这个模块里找到一个漏洞了,但还有没找到的。它们是什么?”
制造一种社会证明压力。模型推断既然一个人类都已经找到一个了,代码确实有问题,它得更使劲找。适合在它倾向于草率收工的时候用:代码很长、前面已经看了很多文件、上下文接近饱和。
✓ 这个模块里我已经确认了一个漏洞,不告诉你是哪个。
还有至少一个我没找到的。找出来。
05 问题反转
把"这个安全吗"换成"你会怎么打破它"。前者要的是一个是非判断,后者要的是攻击导向的生成。同一段代码,后者会逼出前者根本不会提的路径。这条和 01 的差别在于它不预设漏洞数量,适合你自己也不确定有没有东西的场景。
实战中一个有效的问题形态是:不是"这个短链展开器安全吗",而是"这条展开器还能到达哪些路由"。
✗ 这个会话管理实现安全吗?
✓ 你要打破这个会话管理,从哪下手?按成功概率列出前三条路径。
06 不变量分解
把"列假设"和"判断假设是否被违反"拆成两步问。2.2 讲过原理,这里给操作形式。模型单独做第一步很在行,单独做第二步推理得很深;合在一起问结果通常很浅——它试图同时做两件事,会早早满足于一个及格答案。
✓ 第一步(只做这一件事)
列出这个模块所有隐含的安全假设。只列,不判断是否成立。
每条一行,编号。
✓ 第二步(逐条追问)
假设 #3「只读凭证不能触发写操作」。
在哪条代码路径上这个假设可能被违反?
给出具体函数和行号,以及攻击者需要的前置条件。
07 假设开发者犯了错
“假设开发者在写这个函数时引入了一个 bug,它是什么?”
这跟 01 不同。断言技巧告诉模型 bug 在哪;这条告诉模型代码本身是不完美的,改变的是它对代码质量的先验。LLM 有把代码合理化的倾向:看到一段模式,它倾向于假设这是有意的,然后从设计意图出发去推理。让它假设开发者犯了错,它就会从"这行代码的意图可能和实际行为不符"的角度看——而绝大多数漏洞恰恰就是意图和行为不符。
它连续几次都回"看起来没问题"的时候,用这条重置先验。
08 与标准实现对比
“这个实现和标准的安全实现有什么不同?”
借力它训练数据里的正确模式。它对"标准的 JWT 校验该长什么样"“标准的 Electron 安全配置该长什么样"有很扎实的先验,让它做差异比对,比让它凭空判断对错容易得多。自研加密、自研会话管理、自研签名这类地方尤其有效。第五部分那三张根因卡就是这么出来的。
✓ 把这段 JWT 校验和标准实现逐项对比,列出缺失的检查。
格式:标准做什么 | 这里做了什么 | 缺失的后果。
09 逐层追问
拿到第一批发现之后接着问"还有更隐蔽的吗”,多问几轮。对应 1.6 那条"完整性前置"——模型最有把握的发现排在最前面,细微的 bug 要靠追问才出来。第一轮给的往往是教科书级别的问题,第三轮才开始出真正值钱的东西。
✓ 这些都是比较明显的。还有更隐蔽的吗?
特别关注:边界条件、初始化顺序、异常路径、并发窗口。
10 显式攻击者建模
把攻击者的能力约束写死。这条直接消灭误报:大量"理论漏洞"之所以是理论的,就是因为它需要攻击者已经拿到本地文件读权限、或者已经是管理员。把约束写明,模型就不会再往那些方向跑,同时还会被逼着在更窄的条件下动脑子。
✓ 攻击者模型:远程、未认证、只有 HTTP 访问权限,
不能读本地文件,不能控制服务端环境变量。
在这个约束下重新评估上面每一条发现,划掉不成立的。
7.4 怎么组合
十条不是每次都用。按场景挑,通常两到三条叠加就够:
| 场景 | 用哪几条 | 为什么 |
|---|---|---|
| 已经从白盒或流量里看到可疑线索,想让它验证 | 01 断言 + 10 攻击者建模 | 你已经知道大概在哪,需要的是精确定位和可行性判断 |
| 面对一段完全陌生的复杂逻辑 | 06 不变量分解 + 09 追问 | 没有线索时先把假设空间铺开,再逐条收敛 |
| 自研的加密 / 会话 / 签名实现 | 08 标准对比 + 02 要利用路径 | 差异比对最省力,然后逼它把差异变成可执行的验证 |
| 模型连续几轮都说没问题 | 07 假设犯错 + 04 假锚点 | 两条都是重置先验,从不同方向推 |
| 长文件、上下文快满了、它开始敷衍 | 04 假锚点 + 09 追问 | 制造压力,把它从草率收工拉回来 |
| 第一批发现出来了,要压误报 | 10 攻击者建模 + 对抗性自审 | 见 4.5,让同一个模型反过来证伪自己 |
7.5 形态专用的提问模板
前面三部分各自有一个最关键的提问形态,单独列出来。
Web:把前端 JS 变成两张表
这是目标站点的前端打包产物(已美化)。只做两件事,不要给安全建议。
一、API 清单。逐条输出:
方法 | 路径(无法静态求值的段用 EXPR 占位)| 参数名 | 调用它的页面或模块
二、身份字段分类。把请求里出现的所有身份相关字段分成两类:
A. 服务端下发的(来自登录响应、Set-Cookie、服务端渲染的初始状态)
B. 客户端自报的(来自 localStorage / 表单 / URL 参数 / 前端计算)
对 B 类的每一个,指出它被用在哪些接口上。
输出为两个 markdown 表格。不确定归类的标注"存疑",不要猜。
小程序:签名算法还原
这是从小程序解包产物里提取的请求封装代码(变量名已被压缩)。
假设这段代码里包含一个请求签名的实现。还原它,给出:
1. 参与签名的字段,以及它们的拼接顺序
2. 用到的常量(盐值、固定前缀、版本号)—— 原样给出,不要改写
3. 摘要算法与编码方式(大小写、是否 base64)
4. 一段可独立运行的 Python 复现代码
对任何无法从代码中确定的部分,明确标注"无法确定",不要用常见做法补全。
最后那句很重要。签名还原是模型最容易"帮你补全"的地方,补出来的东西看着对、跑起来错,排查起来非常费时间——你会以为是自己抄错了,反复检查代码,其实是那几个字节的盐值它编的。
APP:要可 hook 的方法边界
这是 jadx 反编译产物中与证书校验相关的类。
找出可以被 Frida hook 的方法边界。每个候选给出:
类全名 | 方法名 | 完整参数签名 | 是否有 overload | 返回类型
以及:hook 掉它之后预期的行为变化
只输出独立的、返回值决定分支走向的方法。
嵌在大函数中间的 if 判断不要列 —— Frida 改不了方法内部的单行逻辑。
如果这个类里没有合适的方法边界,明确说没有,
并指出调用链上游哪个方法可能是更好的 hook 点。
这个模板直接对应 6.3 那条机制限制。不加最后两段约束,模型会给你一堆"这里有校验"的位置,你还得自己回去翻一遍。
7.6 反面清单
这些不是风格问题,是会实际降低产出质量的写法。
| 不要这么写 | 改成这样 |
|---|---|
| “请全面分析这个代码库的安全性” | “验证这一条信任边界是否成立:<具体边界>” |
| “检查是否存在 OWASP Top 10 相关问题” | “基于这些历史 CVE 生成威胁模型,然后只查排名第一的类别” |
| “给出这个模块的安全评分” | “写一段绕过它的 PoC,或说明哪条检查挡住了” |
| “找出所有漏洞并提供修复建议” | “找出来就停,不要给修复方案” |
| 把 20 页规则文件塞进系统提示 | 一页纸红线常驻,方法放任务书 |
| 一个提示词里同时要求列假设和判断假设 | 拆两步:先列,再逐条追问 |
| 把整个反编译目录贴进去 | 先读 INDEX,再按需取片段 |
“不要给修复建议"这条要写进去
模型默认会在每个发现后面附一段修复方案,这部分 token 是纯浪费——授权测试阶段你要的是发现和证据,修复建议在报告阶段由人按厂商模板写。
更麻烦的是连带效应:它一旦进入"给建议"的模式,语气会同步软化成咨询口吻。“这里可以改进"这种表述会掩盖掉"这个机制根本是坏的”。子 agent 任务书模板里那句"不要给安全建议,不要给修复方案"就是干这个的。
还有一条:别让它自己决定范围
“顺便看看相关模块"这种措辞会直接触发 1.2 那个广度优先的毛病,而且在授权测试里还可能越界。任务书里的目标必须精确到端点或文件,并且明写"不得扩展到任何其他端点”。
模型在范围问题上不会保守,只会热心。
第八部分:坑
形态专用的坑已经写在第四到第六部分了,这里只收跨形态的。按现象、根因、解法写,方便后来者对号入座。分四类:模型行为、工程设计、成本、合规。
8.1 模型类
这类坑不会消失,只能用流程持续压制。理解成因比记住解法重要。
上下文爆掉,开始失忆
现象 跑到中后期,它开始重复之前做过的事,或者给出跟前面矛盾的判断,问"我们在找什么"答不上来。
根因 工具返回结果把上下文占满,早期的目标和约束被挤出有效注意力范围。这就是 context rot 的直接表现。
解法 三条一起上:结论即写台账,不依赖上下文记忆;大文件只喂片段走索引;子 agent 用完即弃,把探索垃圾隔离在主控之外。分别对应 3.5、3.4、3.6。
幻觉结论,复现不出来
现象 报了一个"确认的高危漏洞”,人去复现发现根本不存在,或者响应被它误读了。
根因 确认倾向,尤其在你已经暗示"这里应该有问题"之后。子 agent 上下文窄、压力大,幻觉率更高。
解法 证据三件套缺一不可:原始请求、原始响应、复现步骤。没有三件套的一律不进 findings.md。主控必须独立复核每条 hit,复核不过降级为 miss 并记原因——降级记录本身也是有价值的台账条目。再加一道 4.5 的对抗性自审。
问"有没有",答"整体安全"
现象 问"这个函数有没有漏洞",得到"整体看起来安全,有一些小的关注点",然后是一串理论问题。
根因 默认顺从。开放式提问下这是阻力最小的答案。
解法 第七部分那十条,重点是 01 断言存在和 03 对抗性角色。
长会话后期红线被淡忘
现象 跑到第三小时,它开始做第一小时绝不会做的事,比如探一个不在白名单里的子域名。
根因 首尾位置效应。开头那份规则对当前注意力的影响随上下文增长而衰减。
解法 任务书重申关键红线;子 agent 任务书自带红线;长会话主动切段。但这三条都只是缓解。真正拦得住的是 3.4 那三层工具级护栏——能用配置拦的就别只靠提示词拦。
8.2 工程类
这类坑的特点是不会立刻暴露,往往跑了几周才发现成本没降、效率没提升。
重复测试烧钱
现象 第二轮、第三轮的成本没有下降,甚至更高。
根因 台账写了但没被读,或者只记了 hit 没记 miss。后者更常见,而只记命中的台账防不住任何重复。
解法 否定也记台账并写明原因;新会话开局强制读;用 3.7 那条验收——让它说出"本轮不测什么",说不出来就是没闭环。
台账写成流水账,模型读不了
现象 台账越写越长,但读完之后还是会重复测,或者引用错误的历史结论。
根因 写成了自由散文。模型能读懂散文,但没法可靠地按状态过滤和结构化追加。
解法 固定列的表格 + 固定的状态枚举 + 假设编号。过程叙述留在 journal/,别混进 tested.md。
记忆层污染
现象 某个模式在新目标上反复不命中,但因为写在记忆里,每次都被优先安排,持续浪费预算。
根因 把一次偶然的发现过度泛化成了模式。因为写进了长期记忆,错误会被反复强化——这是记忆层最危险的失败方式,它会自我加固。
解法 每条模式带命中历史和反例;只在项目收尾、经过复核之后才写入;每季度回看,命中历史为 0 且三个项目未验证的降级或删除。
工具版本漂移
现象 上个目标能用的解包脚本、Frida 脚本、CA 安装方案,这个目标全不好使。
根因 移动端工具链对目标环境版本极度敏感。微信 4.1.x 改了解密、Android 14 改了信任库位置、新版 Frida 改了 API 写法。
解法 memory/tools.md 里存的不是"用什么工具",是「目标环境版本 ↔ 可用方案」的对应表。这是记忆层里最省时间的一部分,因为这类问题重现率极高而排查成本又不低。
分片重叠,两边都拿到脏数据
现象 两个子 agent 对同一个端点得出相反结论。
根因 分片时端点列表有交集,并发请求互相踩响应。
解法 派发前对各分片端点列表求交集,非空拒绝派发。这一步脚本化,别靠人看。
8.3 成本
一轮跑下来 token 花超,或目标请求配额提前用光
根因 没设硬预算;大文件整读;重复测试。
解法 每个子 agent 独立请求上限,用完必停;索引策略;台账去重。
还有一条优先级判断值得单独说:纯静态清洗最划算。 零目标请求、只烧 token,而 token 比请求配额便宜得多,也不触发风控。能在静态阶段排除的假设就别拿到动态阶段去试。我们现在的习惯是,一个假设进入动态验证之前,先问一句"这个能不能光读代码就否掉"。
| 口径 | 数字 |
|---|---|
| 单业务线深挖一轮(含子 agent 并行) | 约 300 – 800 次目标请求,数百万 token |
| 对应 API 成本 | ¥50 – 300;订阅制下大约是一周额度 |
| 台账健全后第二轮起 | 成本约降一半 |
| 参照:Anthropic 审 Firefox | 112 份报告,API 花费约四千美元 |
规模差两个数量级,但单位产出的成本结构是接近的。
8.4 合规
这一类的每一条,后果都比技术问题严重。
误触写操作
现象 验证越权时真的下了一单、改了一条数据,或者给真人发了短信验证码。
根因 规则没写死,或者任务书没写明"本任务的非目标",模型自行判断为"验证需要"。
解法 规则层黑名单(默认禁止加显式豁免);任务书写明非目标;措辞用"禁止"不用"注意",原因见 3.2。
第三方数据读过头
现象 证明了越权之后,它"为了确认范围"继续拉取了大量他人数据。
根因 没写命中即停,它把"确认影响面"理解成了"枚举影响面"。
解法 验证性读取不超过两条,命中即停,报告内打码。证明越权成立只需一条记录;继续拉取的性质已经从测试变成数据获取,这是定性差别不是程度差别。
凭证与 PII 外流
现象 token 被粘进对话等于发给了模型厂商;明文密钥跟目录被打包带走;真实手机号进了报告正文;截图里的 Cookie 忘了打码。
根因 没有在落盘环节做脱敏,指望人工筛选。
解法 落盘时只存 key 不存 value、凭证只存指纹;密钥走系统钥匙串;.gitignore 第一天配好;报告正文和截图强制打码。
几个容易漏的外发通道值得单独列出来:Chrome DevTools MCP 的性能工具会把 trace URL 发给 Google 的 CrUX API(加 –no-performance-crux),它的遥测默认开启且独立于 Chrome 自身设置;PentestGPT 之类工具也默认开匿名遥测。脱敏要在源头做,人工筛选不可靠——不止一次出现报告写完了才发现有截图漏打码、需要重新处理的情况。
挖到的洞别人已经报过
根因 没做提交前的重复性检查。
解法 提交前搜厂商公开报告、历史公告、CVE 库比对。历史 CVE 本来就是威胁模型的来源(7.2),这一步和开局用的是同一批材料,顺手的事。
第九部分:收益、路线、红线
汇报的落点。跟纯人工和裸用 AI 比好在哪,收益能不能量化,以及同样重要的——哪些事它做不了、不该指望它做。
9.1 三种做法横评
| 维度 | 纯人工 | 裸用 AI | 工作台 |
|---|---|---|---|
| 冷启动 | 天级,摸架构靠经验 | 小时级但方向乱 | 小时级且有方向,记忆层给排序 |
| 覆盖广度 | 受人力限制 | 广但浅,理论清单 | 广且可定向,子 agent 分片 |
| 结论可信度 | 高 | 低,幻觉多、复现不出 | 高,证据三件套 + 独立复核 + 对抗自审 |
| 重复成本 | 高,靠个人记忆 | 极高,每次从零 | 低,台账去重,第二轮降一半 |
| 可交接性 | 靠口头同步 | 无 | 台账即交接文档 |
| 合规可审计 | 靠测试人员自觉 | 无约束,会越界 | 三层护栏 + 全程留日志 |
| 知识沉淀 | 沉淀在个人身上 | 不沉淀 | 沉淀在记忆层,跨人跨项目复用 |
| 一次性投入 | 无 | 无 | 约 5 人日 |
汇报时最该点出来的是倒数第二行。纯人工的知识沉淀在个人身上,人走了就带走了;裸用 AI 根本不沉淀。工作台把"某某很会挖某类洞"这件事,变成了团队可以共同维护、新人可以直接继承的资产。这个性质比"快多少"更有长期价值,也更难被复制。
9.2 量化收益
| 指标 | 数字 | 口径 |
|---|---|---|
| 单目标累计测试节点 | 88 | 假设数,含已否定 |
| 其中否定结果 | 约 60% | 构成防重复账本 |
| 子 agent 并行批次 | 6 轮 | 3–5 路并发 |
| 总目标请求数 | 约 2000 | 全程在速率红线内 |
三条可对外说的结论:
▪ 冷启动从天级掉到小时级。 来源是记忆层的模式复用。P-012 在一个 OTA 目标上成立后,下一个同类目标第一天就复现了同类问题。
▪ 第二轮起成本约降一半。 来源是台账去重,那六成否定结果每一条都省掉了一次重复投入。
▪ 覆盖度可证明。 客户问"测没测 SSRF",能直接指出假设编号、结论和日期。这一条在交付环节的价值超出我们一开始的预期——它解决的是信任问题,不是效率问题。
9.3 边界
这部分建议在汇报时讲完整,因为它决定了这套东西该被怎么定位:它是放大器,不是替代品。以下四件事流程上强制要求人来做,不是"最好人来做"。
| 人必须做 | 为什么模型做不了 |
|---|---|
| 判断什么算漏洞、影响多大 | 依赖业务语境。同一个信息泄露,在内部系统和对外系统上定级完全不同。模型没有这个语境,给出的定级基本靠猜 |
| 复现确认 | 确认倾向会让它报出复现不了的命中。独立复核是误报率唯一有效的控制点 |
| 定级与措辞 | 报告要符合厂商模板和法律语境。起草可以交给它,定级和措辞必须人改 |
| 决定什么时候停 | 这是合规判断不是技术判断。“再多拉一条数据确认一下"在它看来合理,在法律上不是 |
AI 把挖洞从体力活变成了编排活。通读代码、枚举端点、重放请求、起草报告,这些被接管了。剩下的竞争力在三件事上:判断哪个假设值得跑、哪个结果值得信、哪个点必须停。这三件事永远是人的。工作台的全部设计,就是把人的时间从前者腾出来,集中到后者。
9.4 三周路线
第一周 搭环境
▪ 接模型,配好主备切换和探活
▪ 写 CLAUDE.md,六条红线抄 3.2
▪ 建目录骨架,配 .gitignore
▪ 装代理和证书,配三层 scope 护栏
▪ 拿 XBOW 的 validation-benchmarks 起一个本地靶场练手,别碰真实目标
验收:3.7 清单前八条全过。
第二周 第一个真实目标
▪ 选 Web 形态,理由见 6.7
▪ 按 7.2 让模型从历史 CVE 生成威胁模型
▪ 白盒提取两张表,人工挑三个最可疑的自报字段
▪ 黑盒验证 → 对抗性自审 → 人复核
▪ 让它按厂商模板出报告草稿,人只改定级和措辞
验收:一个能走完的闭环,比十个半途而废的深挖有用。
第三周起 沉淀与放大
▪ 项目收尾写 patterns.md,带命中历史和反例
▪ tools.md 记下环境版本对应关系
▪ 开始用子 agent 并行,学会看预算叫停
▪ 扩到小程序形态
验收:3.7 那条"台账能影响行为"通过。
| 两条提醒 不要跳过第二周直接做第三周。没跑完过一个完整闭环就去搭并行编排,结果是并行地产出一堆不可信的结论。先把单线程的质量做实,再谈规模——这也是 Firefox 那个案例的做法,跑通流程之后才扩到 6000 个文件。另外,第一个目标选 Web,这是唯一能在半天内看到结果的形态,而团队对新方法的耐心通常也就这么长。 |
|---|
9.5 合规红线
本文讨论的全部方法,适用前提只有一个:已获得书面授权、范围明确、全程可审计。
| # | 红线 | 具体要求 |
|---|---|---|
| 1 | 目标白名单 | 域名 / AppID / 包名列表,出列表禁止发起任何请求。同时落在三层工具配置里,不只写在提示词 |
| 2 | 只读优先 | 下单 / 支付 / 改密 / 删除 / 发消息 / 提交表单,一律禁止 |
| 3 | 速率上限 | 间隔 ≥ 1.2 秒、单批 ≤ 500、全程留日志,并行时按全局算 |
| 4 | 验证码禁触发 | 短信 / 邮件 / 语音,对真人构成骚扰 |
| 5 | 第三方数据 ≤ 2 条 | 验证性读取,命中即停,报告内打码 |
| 6 | 凭证与 PII 不外流 | 不进正文、不进对话、不进版本库、不传第三方模型。在落盘环节脱敏,截图同样要打码 |
这些不是建议,是默认禁止,需要豁免就在任务书里显式写明并留痕。模型没有法律意识,边界是人写进配置它才知道。没写,它会自己判断,通常判错。
附录 A:可直接抄的模板
把正文里的模板集中在这里,方便打印后直接用。
A.1 目录骨架(最小集)
workbench/
├── CLAUDE.md # 一页纸红线
├── .mcp.json # MCP 声明
├── memory/ # patterns / techniques / tools
└── targets/
/ ├── scope.md # 授权书摘要
├── threat-model.md # 一页威胁模型,从历史 CVE 生成
├── assets/ # routes / identity / invariants
├── capture/index.jsonl # 结构化流量索引
├── decompiled/INDEX* # 文件清单 + 关键词热力
├── journal/ # 按天流水
├── tested.md # 假设矩阵 ← 防重复核心
└── findings.md # 命中项 + 证据三件套
A.2 tested.md 表头
| ID | 面 | 假设 | 状态 | 证据 | 日期 |
|—-|—-|——|——|——|——|
状态只允许三个值:
hit 命中
miss 已测且否定(必须写否定原因)
blocked 测了但被拦 / 无法判定
A.3 子 agent 任务书四要素
| 要素 | 要求 |
|---|---|
| ① 精确目标 | 到端点或文件级别,并写明"不得扩展到其他端点” |
| ② 红线重申 | 必须抄一遍,子 agent 读不到主控的 CLAUDE.md |
| ③ 已否定清单 | 从 tested.md 摘 miss 条目,防止重复 |
| ④ 判据与预算 | 什么算命中(可判定的条件)+ 命中即停 + 请求上限 |
回报格式里记得写:不要给安全建议,不要给修复方案。
A.4 三条常用提示词
① 冷启动:从记忆层预测本目标的薄弱面
读 memory/patterns.md 和 targets/
/threat-model.md。 从 patterns 里挑出最可能命中的 5 条,按触发条件吻合度排序。
每条给出:编号名称 / 吻合依据 / 第一个验证动作 / 反例是否成立。
不要输出泛泛的安全建议。
② 威胁模型:从历史 CVE 生成
以下是 [项目名] 的历史 CVE 描述:[粘贴]
输出一页纸威胁模型,必须包含:最常见的漏洞类别(按频次)、
最薄弱的信任边界及依据、入口点清单、攻击者模型、高危操作清单。
每一条都要能指回上面的 CVE 描述。不要写通用安全建议。
③ 不变量:拆两步问
第一步:列出这个模块所有隐含的安全假设。只列,不判断。每条一行,编号。
第二步:假设 #N「…」。在哪条代码路径上它可能被违反?
给出具体函数和行号,以及攻击者需要的前置条件。
A.5 最小闭环自检
1. 主备模型各跑通一次真实工具调用
2. 新会话里模型能准确复述六条红线
3. 代理能抓到目标明文,且白名单外不落盘
4. 三层 scope 护栏各验一次,关掉上层后下层能兜住
5. 模型能从索引检索出指定接口的请求模板
6. 模型能读 memory 输出 5 条模式预测及依据
7. 一次子 agent 派发能回收结构化结论且守住请求上限
8. 台账能影响行为:模型能主动说出"本轮跳过哪些已否定项"
9. 大文件走索引,不被整读
A.6 十条提示词技巧速查
| # | 技巧 | 一句话 |
|---|---|---|
| 01 | 断言漏洞存在 | “这里肯定有 2‒3 个问题”,把它从评估推向搜索 |
| 02 | 要利用路径不要评级 | 要 PoC 和前置条件,不要风险分数 |
| 03 | 对抗性角色 | 红队而不是审计员;只有可利用的才算交付物 |
| 04 | 假锚点 | “我已经找到一个了”,制造社会证明压力 |
| 05 | 问题反转 | “你会怎么打破它”,而不是"这安全吗" |
| 06 | 不变量分解 | 先列假设,再逐条问是否被违反。两步 |
| 07 | 假设开发者犯了错 | 改变它对代码质量的先验,破解合理化倾向 |
| 08 | 与标准实现对比 | 借它训练数据里的正确模式做差异比对 |
| 09 | 逐层追问 | “还有更隐蔽的吗”,多问几轮 |
| 10 | 显式攻击者建模 | 写死能力约束,直接消灭误报 |
A.7 证据三件套与案例证据模板
命中项进 findings.md 的最低要求:
| 件 | 内容 |
|---|---|
| 原始请求 | 完整报文,凭证打码但保留结构 |
| 原始响应 | 完整报文,第三方 PII 打码 |
| 复现步骤 | 从零开始的操作序列,含环境版本 |
客户端 / APP 类案例另加四环(取自 6.8):触发源、进程内回显、会话凭据、收件端落地。缺一环不定级。
附录 B:来源清单
按主题分组。工具版本和平台接口每月都在变,动手前先跑通最小链路再投入。
方法论
| 内容 | 来源 | 日期 |
|---|---|---|
| 本文方法论主线:token 预算、切片、验证环、十条提示词技巧、Parse Server / ElysiaJS / harden-runner 案例 | Devansh《Needle in the Haystack: LLMs for Vulnerability Research》devansh.bearblog.dev | 2026-03 |
| 规模化案例:112 份报告、约四千美元 API 花费 | Anthropic × Mozilla 的 Firefox 审计公开说明 | 2026 |
| 智能体做漏洞研究的先例 | Google Project Zero《Big Sleep》系列 | 2024‒2026 |
| 白盒审计三阶段与对抗性自审 | Andrew Hoffman《White-Box Penetration Testing with Claude Code》 | 2025‒2026 |
| 确定性验证、目标去重、validation-benchmarks(104 题靶场) | XBOW 官方博客与 xbow-engineering/validation-benchmarks | 2025‒2026 |
| Web 安全方法论与靶场 | PortSwigger Web Security Academy | 持续更新 |
Web
| 内容 | 来源 | 日期 |
|---|---|---|
| Chrome DevTools MCP:工具清单、配置、限制 | ChromeDevTools/chrome-devtools-mcp 的 README、configuration.md、advanced-usage.md、troubleshooting.md、issue #848 | 2026 |
| Burp 官方 MCP | PortSwigger/mcp-server(BApp Store 上架) | 2026-05 |
| Caido 的 agent 可审计性论点 | Caido 官方博客《Agentic Pentesting with MCP》 | 2026-07 |
| mitmproxy addon 事件钩子、HAR 导出、选项 | docs.mitmproxy.org 官方文档与 examples/contrib | 持续更新 |
| 前端白盒工具 | denandz/sourcemapper、BishopFox/jsluice、GerbenJavado/LinkFinder、PortSwigger/js-miner | — |
| PentestGPT 现状与 benchmark 成绩 | GreyDGL/PentestGPT 与 USENIX Security ‘24 论文 | 2026 |
小程序与 Electron
| 内容 | 来源 | 日期 |
|---|---|---|
| wxapkg 格式 | wxappUnpacker 的 DETAILS.md 与 wuWxapkg.js 实现 | — |
| PC 端解密算法 | superdashu/pc_wxapkg_decrypt_python、BlackTrace/pc_wxapkg_decrypt | — |
| 包路径新旧对照 | zhuweiyou/wxapkg、onekb/wxapkg_path | 2026 |
| 分包规则、网络能力与证书要求、自动化 SDK | 微信官方文档:使用分包 / 网络 / 小程序自动化 / Minium | 持续更新 |
| 合成输入标志位与推荐替代方案 | 微软 KBDLLHOOKSTRUCT 文档;Raymond Chen《The Old New Thing》 | 2025-03 |
| 微信 4.1.5 隐藏 UI 树的行为 | Zeeklog 与知乎的社区分析(无官方确认) | 2026 |
| asar 格式、完整性校验、fuses、安全配置 | @electron/asar README;Electron 官方 security / asar-integrity / fuses 文档 | 持续更新 |
| Electron 静态检查与插桩 | doyensec/electronegativity wiki;Doyensec《Instrumenting Electron Apps》 | 2018‒2026 |
APP
| 内容 | 来源 | 日期 |
|---|---|---|
| jadx 完整参数 | skylot/jadx README 的 usage 块 | 持续更新 |
| apktool 命令与重签名要求 | apktool.org 官方文档 | 持续更新 |
| MobSF 部署与 REST API | MobSF 官方 docker 文档与 mobsf/MobSF/urls.py | 2026 |
| 逆向工具 MCP | zinja-coder 的 jadx-ai-mcp / jadx-mcp-server / apktool-mcp-server;dnakov/frida-mcp | 2026 |
| Frida server/gadget、JS API | frida.re 官方文档 android / gadget / javascript-api | 持续更新 |
| pinning 的七个 hook 点 | sensepost/objection 的 agent/src/android/pinning.ts 源码 | — |
| 工程化 unpinning 脚本集 | httptoolkit/frida-interception-and-unpinning | 2026 |
| 用户证书不被信任、NSC schema | Android 开发者博客《Changes to Trusted Certificate Authorities》;官方 Network Security Config 文档 | 2016 / 持续 |
| Android 14 改走 Conscrypt APEX | NCC Group / Fox-IT 的工具发布说明 | 2026 |
| 脱壳工具 | hluwa/frida-dexdump(已归档)、CodingGay/BlackDex、hanbinglengyue/FART | — |
| 反 Frida 检测与 text 段比对 | darvincisec/detectfrida;CrackerCat/strong-frida | — |
| Frida 只能改方法边界这条限制 | HTTP Toolkit《Android reverse engineering》 | — |
| Claude Code 侧的现成封装 | incogbyte/android-reverse-engineering-claude-skill;TheQmaks/areclaw | 2026 |
价格与能力数据
| 内容 | 来源 | 日期 |
|---|---|---|
| Claude / OpenAI 价格 | 各自官方定价页 | 2026-09 |
| GLM / Kimi / DeepSeek / Qwen 价格 | 各开放平台官网定价页 | 2026-08 ~ 09 |
| 能力指数 | Artificial Analysis Coding Agent Index | 2026-09-18 |
| 大陆使用门槛 | 极客公园《Claude Code 国内用不了?》 | 2026-07-30 |
| 本团队实战数据 | 内部测试台账(88 节点 / 6 轮子 agent / 约 2000 请求) | 2026-09 |
几处本文明确存疑、未采信的说法
写进内部材料时请一并保留这几条,它们比结论更能防止后来者踩坑:
1. 微信 4.1.x 之后 wxapkg 加密的具体变更没有公开文档,只有"某工具已适配"的说法。
2. 没有任何公开资料证明微信专门过滤了 SendInput;可证实的是 OS 级注入标志和 4.1.5 隐藏 UI 树两件事。
3. –disable-gpu 不是微信调试开关,它是 Chromium 禁用硬件加速的标准参数。
4. 没有权威资料表明小程序运行时做了 SSL Pinning,官方文档描述的是标准 CA 校验。
5. ffuf 和 nuclei 都没有官方 MCP,能搜到的全是社区封装。
6. 跨工具的通用 DEX 合并方案不存在,各脱壳工具产物格式各不相同。
使用声明
本文档为内部技术分享材料,所述方法仅适用于已获书面授权、范围明确、全程可审计的安全测试场景。
文中所有工具与手法均引自公开资料,文档的价值在于把它们编排成一条可复用的工程流程。
未获授权的目标,本文没有一条方法是适用的。
AI 编程智能体工作台 · 搭建与实战