实测Claude在反编译任务中的表现,展示AI对复杂代码分析的能力,对需要代码审查和理解的开发者有参考价值。
最近,我一直在尝试"一次性"反汇编,利用 Claude 的无头模式在连续循环中运行。结果出人意料地积极。在过去三周采用这个工作流程以来,我在 Snowboard Kids 2 上的进展比之前三个月还要多。
在这个语境中,"一次性"意味着 Claude 遵循 prompt 后退出。你给它一个函数,它尝试匹配,然后你继续。缺少人类操控的反馈循环使得吞吐量明显增加。我让 Claude 运行 8 小时以上无人值守,它会很乐意处理函数,尝试找到匹配。不过这也带来了一些风险。我们都见过 LLM 失控的情况。没有人类在场干预,你可能几小时后回来发现 Claude 配额已用尽,却没有太多进展。但通过正确的框架,这些风险变得可控。
这篇文章的目的是记录我最终采用的工作流程,以及一些可能适用于你的项目的经验教训。
作为用户,我只需运行:
./tools/vacuum.sh
该脚本(最初因其的目的而命名为"vacuum"——吸尽简单函数)从此处开始处理一切。它会处理可匹配的函数,直到没有剩余为止,要么是因为每个函数都被匹配了,要么是被标记为太难了。
在幕后,系统有四个组件:
计分器选择下一个要尝试的函数,优先处理最可能匹配的;
Claude 使用提供的工具执行实际的反汇编;
工具为 Claude 提供反汇编函数所需的内容;
驱动程序管理生命周期:调用 Claude、处理失败、记录进展。
下面各节详细介绍了每个组件。
计分器的目的是为 Claude 找到下一个最容易反汇编的函数。Claude 在这项任务上不如人类能力强,所以最有效的做法是把我们的时间和精力花在最可能取得实质进展的地方。这也有助于为反汇编更复杂的函数奠定基础,这些函数往往会调用更简单的函数;理解这些依赖关系使得大型例程更容易推理。
早期,我使用了一个粗略的公式:
score = instruction_count + 3 * branch_count + 2 * jump_count + 2 * label_count + stack_size
理念是函数的复杂性,因此反汇编难度,主要由其指令计数决定。控制流构造(分支和标签)可能会增加难度,其他函数调用(跳转)也是如此。管理较大的栈可能也很复杂,所以把它算进去以求安全。
这种计分方法起初效果相当不错。一旦我记录了数百个匹配和失败的函数,我就切换到逻辑回归模型来调整初始权重。
有趣的是,该模型显示栈大小几乎没有预测价值,似乎会导致过度拟合。最终我将其删除。剩余的特征证明更稳健,但我对权重的初始猜测相差很大。
当我收集更多数据时,我定期重新训练该模型,这导致了其准确性的边际改进。
Claude 是这个操作的大脑,执行实际的函数反汇编。大多数这些匹配是使用 Opus 4.5 执行的。我没有很多数据来比较 Opus 和 Sonnet。然而,在我进行的一个简短实验中,Opus 能够匹配五个被 Sonnet 4.5 认为太难的七个函数中的五个。
完整的 prompt 在存储库中,但核心指令很直接:
为函数 X 创建一个匹配环境。
遵循该环境中的指令并使用提供的工具来反汇编该函数。
如果太难就放弃。如果经过十次以上尝试后没有进展,agent 应该继续。
如果匹配成功:集成到项目中,验证构建,然后提交。提交至关重要。这样即使 Claude 稍后搞坏了本地环境,也能保留进展。
如果匹配失败:添加到 difficult_functions 日志并退出。
"十次尝试后放弃"的阈值旨在防止 Claude 在进一步进展不太可能时浪费 token。这只是部分成功,因为 Claude 有时仍会进行数十次尝试。
我的工具方法与之前的文章基本保持不变:提供简单的、类似 Unix 的程序,Claude 可以组合它们来解决问题。我没有添加任何 MCP(Model Context Protocol)服务器。话虽如此,赋予 Claude 更多自主权凸显了这些工具中防守性编码的必要性:清晰的错误信息、优雅的失败、对误用的防护措施。来自工具的模糊错误可能会让 Claude 陷入兔子洞,浪费时间和 token。
例如,Claude 被指示使用单个脚本来构建和验证项目:build-and-verify.sh。为了减轻 Claude 倾向于误分类结果的情况,该脚本提供了处理失败和成功的明确说明:
BUILD HAS FAILED. Claude, you should treat this as a build failure. Adding new warnings or accepting a non-matching checksum count as failures.
类似地,当 Claude 在匹配环境和主项目之间移动时,有时会迷失方向。我们可以使用有问题目录中的 catchall (%:) make 规则来处理这个问题,它只是说:
You are in a matching environment for a specific function. Only use the tools explicitly listed in this directory's CLAUDE.md. If you're ready to build against the main project, you need to jump back two directory levels (cd ../..)
这种防守性工具策略已被证明比 prompt 工程在缓解特定 Claude 失败模式方面更有效。
另一个重要的考虑是 token 效率,由于 Claude 现在运行很长时间,这已变得更加相关。这最初促使 vacuum.sh 决定调用计分器然后将最便宜的函数传递给 Claude,而不是让 Claude 自己选择。对工具的调整也会有帮助。build-and-verify.sh 显著限制构建输出,以努力节省 token。
外部循环是一个简单的 bash 脚本,它反复调用 Claude,可选的最大迭代计数。逻辑如下:
使用下一个要匹配的函数调用 Claude;
如果 Claude 返回非零,退避,最终在五分钟间隔检查,以防我们遇到使用限制;
捕获 Ctrl-C,以便我们可以发信号停止,而不会在运行中杀死 Claude,浪费当前尝试;
将函数名和时间戳记录到 stdout,以保持对该过程的可见性;
将所有 Claude 输出附加到一个文件。这对调试失败的匹配非常宝贵;你可以准确看到 Claude 卡在了哪里。
我短暂地尝试了 Codex,当我开始沿着这条路走时,它获得了很多关注。结果令人失望。Codex(包括 5.1-codex-max)在有效反汇编和遵循指令方面都很吃力。
Git 相关的问题最有问题。这似乎是一个已知问题,尽管升级在我的情况下没有帮助。糟糕的反汇编策略加上不可靠的 Git 使用为一个痛苦的组合。
我还没有测试过 Gemini 或其他 agent。
传统的反汇编工作通常是多年期、团队项目。主要制约因素通常是少数专家的时间和可用性。编码 agent 改变了这种制约。对于 Snowboard Kids 2,目前的数据表明绝大多数函数都在 Claude Opus 4.5 的范围内;如果当前趋势持续,大约 79% 的函数应该是可匹配的。展望未来,限制因素可能是计算能力和对前沿模型的访问,而不是人工关注。
话虽如此,反汇编社区的不懈努力不能被夸大。我的项目根本不会存在,没有 Discord 上许多耐心人的支持,以及诸如 Splat、m2c、decomp-permuter、asm-differ 和许多其他工具。我们站在巨人的肩膀上。虽然角色可能会改变,但我认为人类专家在不久的将来不会变得不必要。
剩余的函数几乎肯定是最具挑战性的反汇编(除了一些 Claude 的怪癖)。即使 LLM 成功,输出也通常很粗糙:指针算术而不是数组访问、依赖 goto 语句的控制流、笨拙的临时变量以及其他影响代码清晰度的问题。如果目标是理解这些游戏如何工作(或修改它们)字节完美但丑陋的匹配对原始汇编来说并没有买我们多少东西。似乎可能未来的反汇编工作流程将更多地专注于清理和记录 LLM 输出,而不是从头开始编写代码,使用这些匹配作为基础,这与早期项目基于 m2c 输出的方式相同。
如果你走到了这一步,你可能对反汇编和 Snowboard Kids 2 感兴趣。为什么不帮忙呢?查看 Snowboard Kids 2 反汇编 GitHub 页面,看看你是否能击败 LLM!
有什么要说的吗?你可以在 Hacker News 上投票和/或加入讨论。你也可以在 Bluesky 上关注我,获取更多 Snowboard Kids 2 更新。
由 Anand Chowdhary 关于在循环中运行 Claude Code 的文章启发。↩︎
由 Anand Chowdhary 关于在循环中运行 Claude Code 的文章启发。↩︎
进度目前在 decomp.dev 上跟踪。在此之前,README.md 文件直接更新以反映进度。↩︎
进度目前在 decomp.dev 上跟踪。在此之前,README.md 文件直接更新以反映进度。↩︎
这是数据中证实的,尽管连接不如我最初预期的那样强。62.7% 的调用从更高复杂性流向更低复杂性函数。↩︎
这是数据中证实的,尽管连接不如我最初预期的那样强。62.7% 的调用从更高复杂性流向更低复杂性函数。↩︎
为什么我没有更新 prompt 来反映这一点?因为它让 Claude 困惑,它停止了提交变更。LLM 很奇怪。这需要更多实验。↩︎
为什么我没有更新 prompt 来反映这一点?因为它让 Claude 困惑,它停止了提交变更。LLM 很奇怪。这需要更多实验。↩︎