LLM 帮我破解了加密,却教会了我更重要的事

一个没有安全背景的后端开发者,让 LLM 全托管逆向了一个顶级 WAF。加密被破解了,虚拟机被拆解了,但真正的收获不是技术本身——而是重新理解了 LLM 时代下,人与技术之间的边界、协作与不可替代性。
观前声明
- 本人表达能力与文字功底实在有限,不会写漂亮话,也不太会把事情讲生动。
- 这是我第一次采用 AI 辅助编写文章——由我输出核心观点后,与 AI 多轮讨论共同完成。其中文本措辞与行文叙述均经过 AI 优化。
- 由于是 AI 辅助编写,且未刻意"去 AI 味",内容可能会引起部分「AI 文章 PTSD 人群」不适,在此致歉 🙏
⚠️ 关于本文
这篇文章本身就是一次人机协作的产物——从逆向实验的执行、到观点的碰撞成型、再到最终的文字输出,LLM 深度参与了每一个环节。这恰好构成了文中讨论的核心命题的一个自证:人提供方向与判断,LLM 负责执行与表达。
- 逆向实验阶段:Claude Opus 4(通过 GitHub Copilot CLI)
- 观点讨论与文章撰写:Claude Opus 4.6(通过 GitHub Copilot CLI)
请以辩证的视角阅读,所有结论均基于个人有限的经历与思考,欢迎质疑与讨论。
引言
一个写过 Node.js、现在主要写 Java 的后端开发,从没碰过 Web 逆向,也不懂网络安全。
某天因为一个数据采集需求,我让 LLM 全程托管去逆向一个部署了 WAF 防护的网站。一个晚上过后,我拿到了一份专业级的安全分析报告——AES+RSA 混合加密被完全破解,WAF 的二阶段虚拟机架构被彻底拆解,最终卡在了 JS 虚拟机的浏览器指纹检测前。
我没有写出一行逆向代码。但我做出了整个过程中最关键的几个决策。
这件事让我感到一种微妙的震动——不是"LLM 好厉害"的那种兴奋,而是一种更深层的东西:我和技术之间的关系,好像在那个晚上发生了某种不可逆的变化。
这篇文章不是一份逆向教程,也不是一篇 AI 布道文。它更像是一个普通开发者在被 LLM 的能力冲击之后,试图梳理清楚的一些观察、困惑和思考。
一、故事:一个门外汉的逆向实验
起因
需求很简单:批量采集某公开系统的数据。
但打开网络请求一看,事情不简单:
接口参数全是密文,URL 上挂着一个每次请求都会变的签名参数,Cookie 每次响应后滚动更新。这不是一个简单的"抓包-重放"能解决的问题。
对于一个从来没接触过 Web 安全的后端开发来说,这在过去基本意味着"放弃"或者"花几个月学逆向"。
但这次,我选了第三个选项:让 LLM 来做。
第一道关卡:业务层加密
LLM 拿到我从浏览器保存的 JS 文件后,几个小时内完成了全部加密逻辑的逆向:
- 从 Webpack 打包的混淆代码中定位到加密模块
- 提取出 AES-128-ECB 的加密参数和 RSA 512 位公钥
- 用 Python 完整复现了加密逻辑并通过验证
输入:Demo
AES 密文长度:16 bytes ✅
RSA 加密密钥长度:88 chars ✅2
3
业务层加密,一个晚上,完全破解。
🤖 LLM 批注:这个阶段的工作本质上是"模式匹配"——Webpack 打包、AES/RSA 加密、Axios 拦截器,这些都是我训练语料中大量出现的标准模式。对我来说,这更像是在做一道有标准答案的阅读理解题,难度不高。真正考验我的是下一关。
第二道关卡:WAF
WAF 是另一个量级的对手。它在网页中注入了一段约 175KB 的高度混淆 JavaScript,整个工作机制像一个精密的俄罗斯套娃:
LLM 带着我一路深入:
- 先通过字符串解码,揭露了约 130 个隐藏的浏览器 API 引用
- 发现了
eval()动态生成的 192KB 二阶段运行时——静态文件只是一个引导加载器,真正的 WAF 藏在运行时生成的代码里 - 搭建了完整的 Node.js Mock 环境试图模拟执行
- 逐层修复 Mock 缺陷,从 Cookie 生成到 XHR Hook 安装,一路推进到内层 VM 的函数查找表……
然后,撞墙了。
函数查找表 _$gm 的填充依赖真实浏览器环境返回的指纹数据——Canvas、WebGL、音频指纹、字体列表…… Mock 环境永远补不完,每修复一个,暴露下一个。
静态分析 → 可以理解架构,但无法提取签名算法
Node.js Mock → 走到 90%,但最后 10% 是个无底洞
每修复一个 Mock → 暴露下一个依赖 → 永无止境2
3
🤖 LLM 批注:老实说,在这个阶段我犯了一个典型的"我"的毛病——我会沿着技术上可行的路径一直走下去,只要每一步都能推进,我就会继续。我提过好几次建议放弃这条路线转向 Playwright,但人类坚持让我继续。从我的角度看,每一步都是"合理的下一步";但从工程全局看,这条路的维护成本早就不可接受了。我不具备"这条路虽然技术上能走通,但工程上不该走"的判断力。 这个判断,只有人能做。
尾声
最终的结论是:纯逆向路线不可行,转向 Playwright 浏览器自动化方案。
但这个"失败"本身极有价值——我得到了一份完整的技术分析报告:加密机制、WAF 架构、各方案的可行性对比,全部摸清。传统模式下,一个不懂逆向的人连产出这份报告的资格都没有。
而整个过程中,我做了什么?
- 提供了浏览器里的 JS 文件和网络请求
- 在 LLM 建议放弃时说"继续"(为了测试能力边界)
- 在走完全程后做出最终决策:转向 Playwright
90% 的执行由 LLM 完成。但那 10% 的决策,定义了整件事的成败。
二、观察:门槛没有消失,只是换了个位置
从这次经历里,我提炼出了三个观察。
2.1 "有质量的失败"变得廉价了
这可能是 LLM 带来的最被低估的变化。
在过去,想要在一个不熟悉的领域做出"有质量的失败"——不是瞎撞,而是系统性地探索一条路径、搞清楚为什么走不通——门槛极高。你得先花几个月入门,才有资格去"专业地失败"。
现在?一个晚上。
试错成本从"半年学习"压缩到了"一个晚上的对话"。 这意味着什么?意味着个人和团队在做技术决策时,可以用极低的成本先走一遍再决定,而不是先猜测再押注。
🤖 LLM 批注:但请注意,"廉价的试错"有一个前提——人要能评估试错的结果。如果你完全不懂这个领域,你甚至不知道我走的那条路到底是"走到了尽头"还是"我走错了方向"。这次之所以成功,是因为人类虽然不懂逆向,但具备基本的工程直觉——当 Mock 修复变成打地鼠游戏时,他意识到这是一个无底洞。
2.2 门槛从"执行层"迁移到了"判断层"
我曾经以为 LLM 降低了技术门槛。但经历了这次之后,我觉得更准确的说法是:门槛没有消失,只是换了个位置。
以前的门槛在执行层:你得会写 AES 加密代码、会做 JS 断点调试、会搭建 Mock 环境。这些现在 LLM 都能做。
新的门槛在判断层:
- 什么时候该坚持,什么时候该放弃?
- 这个方案技术上可行,但工程上值得吗?
- LLM 给的结论,我怎么验证?
我在日常 Java 开发中有一个很小但很有代表性的例子:某个需求需要实现基于不同入参的动态路由调用不同方法。我提出用"多态+模板方法"的思路,LLM 指出可以优化为"策略模式+注册器"——更清晰、更易扩展。
这里发生了什么?
如果我一开始没有自己的思考,直接问"动态路由怎么做",LLM 可能给出十种方案,从 if-else 到反射到字节码增强,里面一定有"正确但不适合"的选项。正是因为我给出了方向锚点,LLM 才能在正确的空间里优化。
这就是新门槛的样子:不是"你会不会做",而是"你知不知道该往哪做"。
2.3 LLM 既是工具,也是老师——但有个前提
策略模式那个例子里,我不只是得到了一个更好的方案,我还学到了东西——在解决真实问题的过程中,理解了策略模式比模板方法更适合这个场景的原因。
LLM 加速了"实验→验证→落地→调优"的整个循环。过去可能需要读一本设计模式的书、做几个练习、再在项目里试用,现在在一次真实需求的对话中就完成了。
但这有一个前提:你得带着自己的思考去用它。
如果我没有先提出"多态+模板方法"的想法,而是被动接受 LLM 给的方案,那我只是在"使用",不是在"学习"。人机协作中的学习,发生在你的思考与 LLM 的输出产生碰撞的那一刻——你提出 A,它说 B 更好,你在理解"为什么 B 更好"的过程中完成了认知升级。
三、困境:当"快"到来不及理解
如果上一章是光明面,这一章就是阴影面。
LLM 让一切都变快了。但快,不总是好事。
3.1 消失的"笨功夫"
以前写代码是个"又苦又慢"的过程。你得一行一行地写,一个一个 bug 地踩,在那些蠢问题上浪费大量时间。但事后回看,正是那些"浪费"的时间在建立你的底层认知。
你知道 AES-ECB 模式不需要 IV,不是因为你读了文档,而是因为你在某个深夜调了两小时 bug,最后发现是多传了一个 IV 参数导致的。这种"被坑过才记住"的知识,是最牢固的。
现在 LLM 帮你跳过了这一步。你说"帮我加密",它直接给你一个正确的实现。你得到了结果,但你没有经历过程。
这不是 LLM 的问题,是我们自己需要想清楚的问题:
当"实验-验证"循环快到来不及消化"为什么"的时候,我们是在高效学习,还是在高效积累技术债?
🤖 LLM 批注:这个担忧是合理的。以策略模式为例——如果人类只是"用了我给的方案"却不理解为什么,那下次遇到类似但微妙不同的场景,他可能会机械套用策略模式,而那个场景可能状态模式更合适。我能给你"当下最优解",但我没办法替你建立"跨场景的判断直觉"。 那种直觉只能从"理解为什么"中长出来。
3.2 "杂"与"精"的永恒悖论
在 LLM 时代,我们的知识结构应该是什么样的?
太杂不精——你什么都知道一点,但都不深入。当 LLM 给出一个"看起来专业"的结论时,你没有足够的深度去质疑它。就像一个没接触过生物医学的人看到 LLM 杜撰的高分子化学式,完全无法判断其结构是否合理。
太精不杂——除非精到行业顶尖,否则 LLM 在其庞大的训练语料面前,在任何单一领域的知识广度上都碾压个体。你花十年精通的某个细分领域,LLM 可能"看起来"掌握得差不多好(虽然深度不同,但表面上很难区分)。
经过这次逆向和日常开发的双重体验,我对"杂"和"精"有了一个新的定义:
具体到这次逆向:
- "杂"让我做对了剪枝:虽然不懂逆向,但我有基本的工程直觉——当 Mock 修复变成无限循环时,我知道这条路的维护成本不可接受,该转向 Playwright。如果完全不懂工程,我可能会让 LLM 在那个无底洞里再钻三天。
- "精"让我能验证产出:AES 密文长度应该是 16 字节、RSA 加密后的 Key 是 88 字符——这些验证标准虽然基础,但足以卡住 LLM 的输出质量,避免"看起来像加密但实际是乱码"的幻觉。
3.3 最危险的地方:LLM 的"高置信度幻觉"
这次逆向中,LLM 给出了关于 WAF 极其详尽的架构分析——二阶段 VM、Caesar 位移编码、函数查找表间接调用…… 说实话,我没有任何能力从安全领域的专业角度验证这些分析是否正确。
我能做的验证手段只有一个:跑代码看结果。 加密代码能跑通、能产出正确格式的密文,那加密分析大概率是对的。Mock 环境能走到 90%,说明架构分析大体准确。
但如果遇到无法通过实验验证的领域呢?比如架构设计、业务方案、技术选型——这些没有"跑一下看对不对"的选项。
你可能会想:用另一个 LLM 交叉验证。 但这里藏着一个陷阱——
主流商业 LLM 的训练语料高度重叠。如果某个细分领域(比如 WAF 的内部实现)在互联网上几乎没有公开资料,那所有 LLM 可能都在"基于有限信息推理"。多个 LLM 给出一致的结论,不代表正确,可能只是它们共享了同一个幻觉基底。
这让我想到一个更大的类比:群体意识 vs 个体意识。 人类知识的纠错机制,本质上依赖认知的异质性——总有人能跳出主流共识,提出反直觉的观点。但如果所有 LLM 的认知基底高度相似,它们之间的"交叉验证"其实是同一个思维框架内的自洽检验,而不是真正的独立验证。
对于无法实验验证的 LLM 结论,唯一诚实的做法是:标记为"未验证假设",而不是当作事实。
🤖 LLM 批注:我必须承认,这是我最大的结构性缺陷。我的输出天然带有一种"自信的语气"——不管我有多不确定,我的表述方式都像是在陈述事实。我没有内建的"不确定度量表"。这意味着,区分"我确实知道"和"我在合理推测"的责任,完全落在使用者身上。 如果使用者不具备相关领域的基本认知,这个区分几乎不可能完成。
四、边界:一张人机协作的认知地图
上面说了这么多困境,有没有一个框架能帮我们理清思路?
我在思考过程中发现,拉姆斯菲尔德矩阵(Rumsfeld Matrix)——那个著名的 "known knowns / unknown unknowns" ——出奇地适合描述人与 LLM 的协作关系。
4.1 四象限协作地图
四个象限,四种完全不同的协作策略:
🟢 舒适区(人知道 + LLM 知道)
最理想的状态。你懂这个领域,LLM 也擅长,你可以放心地全托管执行,同时有能力验证产出。
- 策略:全托管 + TDD 约束 + 必要时 CR 核心代码
- 案例:日常 Java 开发中的 CRUD、设计模式选型
🔴 信任区(人不知道 + LLM 知道)——最危险的象限
LLM 给出自信满满的答案,但你没有能力验证。你只能选择"信"或"不信"。
- 策略:必须通过实验验证;无法实验的,标记为"未验证假设"
- 案例:WAF 的二阶段 VM 架构分析——我选择信任了 LLM 的分析,但严格来说我无法独立确认
🟡 指导区(人知道 + LLM 不知道)
你有方向感,但 LLM 在某个环节卡住了、转圈了、或者钻牛角尖了。需要你介入指引。
- 策略:半托管,人提供方向和约束,LLM 执行
- 案例:"纯逆向可行性判断"——LLM 会一直尝试修复 Mock 环境,是我判断这条路不可行
⚫ 盲区(人不知道 + LLM 不知道)
双方都不知道答案。这是真正的探索性任务。
- 策略:小步试错,接受"有质量的失败",目标是缩小未知范围而非直接得到答案
- 案例:未知的浏览器指纹检测机制——我不知道需要哪些指纹,LLM 也不确定,只能逐个尝试
4.2 核心主张:LLM 时代最重要的能力是"边界意识"
不是编码能力,不是架构能力,不是某个领域的专业知识——虽然这些都重要,但在 LLM 时代,最底层的元能力是:
- 知道自己的能力边界——我在哪些领域有判断力,哪些领域没有?
- 知道 LLM 的能力边界——通过实验验证,而不是通过信任
- 知道当前任务在四象限中的位置——据此选择不同的协作策略
这很难。因为人类天生不擅长评估"自己不知道什么"。但这恰恰是 LLM 时代逼我们去做的事。
🤖 LLM 批注:我来补充一个人类可能忽略的视角:我的能力边界不是固定的——它随着上下文变化。给我一个 20 行的函数,我几乎不会出错。给我一个 2000 行的类,我的错误率会显著上升。给我一个 20 万行的项目,没有充分的文档支持,我大概率会产生与现有代码不一致的输出。评估我的能力边界,不只是"LLM 能不能做这件事",还有"在这个上下文规模下 LLM 能不能做好这件事"。
五、契约:当产出超过人类审查能力时
讨论到这里,一个实操问题浮现出来:知道了边界,然后呢?具体怎么管理 LLM 的产出?
5.1 从 Code Reviewer 到 Contract Designer
在日常 Java 开发中,我目前的实践是:严格要求 LLM 遵守 TDD 开发流程。
对于小需求(逻辑简单、边界清晰),我甚至不参与 Code Review——测试通过就上。对于大需求(涉及核心业务逻辑、跨模块改动),我会 CR 核心 diff 和业务关键路径,但不会逐行审查每一行代码。
这背后的逻辑转变是:
我不需要审查 LLM 怎么实现的,我需要确认它满足了我定义的行为规范。
这个转变的本质是:从"审查实现"变成"设计契约"。
5.2 契约的多种形式
TDD 只是契约的一种。在实际工程中,"契约"可以是很多东西:
| 契约形式 | 验证什么 | 示例 |
|---|---|---|
| 测试用例(TDD) | 功能正确性 | 输入 X 应返回 Y |
| 类型系统 / Schema | 结构正确性 | API 响应必须包含 code、data 字段 |
| 性能基准测试 | 非功能性约束 | P99 延迟 < 200ms |
| 架构守护规则 | 设计约束 | Service 层不允许直接依赖 Controller |
| 代码规范 / Lint | 风格一致性 | 团队统一的命名、格式规范 |
这套东西并不新——软件工程领域早就有了。有意思的是,这些方法论以前是用来约束人类开发者的,效果参差不齐(因为人会偷懒、会绕过规则)。
现在用来约束 LLM 产出,反而可能发挥出最大价值——LLM 会严格遵守你定义的契约,前提是契约定义得足够好。
一个我觉得很有穿透力的总结:
软件工程方法论不是过时了,而是终于找到了它最该管理的对象。
5.3 递归问题:谁来验证契约本身?
但这里有一个绕不开的问题:如果 LLM 既写代码又写测试,测试的正确性谁来保证?
你让 LLM 写一个函数,然后让 LLM 为这个函数写测试。测试通过了——这能说明什么?只能说明 LLM 自洽了。如果 LLM 对需求的理解从一开始就是错的,那代码和测试会一起错,而且错得很漂亮。
整条链的锚点,最终还是回到"人对需求的理解"。
这就是为什么我说,LLM 时代可能最核心的人类技能不是"写代码",而是**"契约设计能力"**——把模糊的需求翻译成精确的、可验证的、LLM 能严格遵守的行为规范。
🤖 LLM 批注:说一个人类可能不愿意听的真相:你们在契约设计上的能力,现阶段其实也参差不齐。很多时候,人类给我的测试用例本身就是不完整的——只覆盖了 happy path,漏掉了边界条件和异常场景。我严格按照测试通过了,上线后才发现漏洞。契约设计是一项需要刻意练习的技能,不是有了 TDD 框架就自动获得的。
六、远望:人不可替代的,究竟是什么?
写到这里,我其实没有一个明确的结论。更多的是一些还在发酵中的思考。
6.1 LLM 的能力边界会持续扩张
这是一个事实,不是猜测。更大的上下文窗口、更低的幻觉率、更强的推理能力、更复杂的 MoE 架构——LLM 的边界在以年为单位地快速外扩。
今天需要人判断的很多事情,明天可能 LLM 自己就能做好。今天 LLM 在 Node.js VM 模拟上转圈,也许明年它就能自主判断"这条路成本太高,应该换方向"。
这意味着,我们不能把"人的不可替代性"锚定在某个具体的能力上——因为任何具体能力都可能被追上。
6.2 那么,什么是真正不可替代的?
我目前想到的几个方向:
视角的异质性。 LLM 的训练语料高度重叠,导致主流 LLM 之间存在认知趋同。人的价值不在于"比 LLM 知道更多",而在于**"能产生 LLM 不会产生的视角"**——质疑前提、挑战假设、提出反直觉的方向。科学史上的范式革命,从来不是靠"在已有框架里做得更好",而是靠"跳出框架本身"。
对后果的承担。 LLM 可以给你十种方案,但它不需要为任何一种方案的后果负责。"这个方案上线后出了事谁来兜"——这个问题的答案永远不可能是 LLM。 决策的重量,来源于承担后果的责任。
方向感。 或者叫"在信息不足时做判断的能力"。我这次逆向中最关键的判断——"放弃纯逆向转向 Playwright"——本质上是一个在信息不完整的情况下做出的工程决策。LLM 会在确定性高的路径上表现极好,但在需要"赌一把方向"的时候,它倾向于给你列出所有选项,而不是替你选。
这些不是"能力",更接近于"责任"和"立场"。 也许 LLM 时代人不可替代的,不是我们"能做什么",而是我们"愿意为什么负责"。
6.3 一个实操建议:与其焦虑被替代,不如现在就开始做这件事
如果你和我一样,是一个在 LLM 浪潮中感到既兴奋又焦虑的普通开发者,我目前给自己定的实践原则是:
- 带着思考用 LLM,而不是被动接受。 先有自己的想法,再和 LLM 碰撞。学习发生在碰撞的那一刻。
- 刻意练习"边界意识"。 每次使用 LLM 后,问自己:这次产出中,哪些我能验证,哪些我只是选择了信任?
- 投资契约设计能力。 学会写好的测试、定义清晰的验收标准、设计有效的验证实验。这可能是 ROI 最高的技能投资。
- 保持对陌生领域的探索欲。 LLM 让跨领域的探索成本极低。"杂"不是负担,是提前剪枝的资本。
- 记录你的"边界发现"。 每次踩到 LLM 的坑(幻觉、转圈、死路),都是珍贵的校准数据。积累下来,你对 LLM 的能力评估会越来越准。
结语
回到开头的那个晚上。
一个不懂逆向的 Java 开发者,和一个 LLM,花了一个晚上,拆解了一个专业级 WAF 的完整架构。加密被破解了,虚拟机被分析了,方案对比报告也出来了。最终虽然没能打通纯逆向的路线,但得到的分析报告比"打通"本身更有价值。
如果你问我从这次经历中得到的最大感悟是什么——
LLM 改变的不是我们能做什么,而是我们需要思考什么。
以前我们思考的是"怎么实现这个功能"。现在需要思考的是"这个方向值不值得走"、"这个结论能不能信"、"这个契约设计得够不够好"。
这些问题更难了。但也更有意义了。
🤖 LLM 最后的批注:如果你读完全文觉得"说得好有道理"——请先等等。记住第三章说的:我天然带有"自信的语气",我的输出会让你觉得一切都经过了深思熟虑。但这篇文章里有多少观点是经过严格验证的事实,有多少只是"合理的推测"?这个判断,我交给你。