diabloidyobane/antigravity-mcp-research
GitHub: diabloidyobane/antigravity-mcp-research
关于 Codeium AntiGravity IDE 中 MCP 工具注册上限的逆向工程研究笔记,深入探讨了其客户端限制执行架构及多层自动更新防篡改机制。
Stars: 0 | Forks: 0
# 逆向工程 Codeium AntiGravity 的 MCP 工具上限
一份关于 Codeium AntiGravity IDE (`language_server_windows_x64.exe`) 中架构及二进制层面强制执行的 **Model Context Protocol (MCP) 工具注册上限**的研究笔记。本文描述了通过反汇编和静态分析所观察到的结果。**本文不包含可用的绕过代码、字节级补丁方案或补丁程序源码。** 其发布仅作为对现代 AI-IDE 产品如何编码和强制执行客户端限制的研究与探讨。
## 背景
Codeium AntiGravity 是一个基于 Electron 的 IDE,附带了一个进程外的 **language server** (LS) 二进制文件。LS 负责处理模型调度、工具注册以及 MCP 集成。IDE 端的 JavaScript 通过本地 socket 与 LS 通信;而 LS 则通过 HTTPS 与 Codeium 的云服务进行通信。
AntiGravity 的文档指出,该 IDE 在所有已注册的 MCP server 中最多支持 **100 个 MCP 工具**。当你超过此上限时,IDE 会静默截断,并且 LS 会记录一条“tool registration declined”消息。
对于有逆向工程好奇心的用户来说,一个有趣的问题是:这个 100 的限制究竟存在于哪里?有以下三种合理的推测:
1. **服务端**:每次工具注册都会与 Codeium 的后端进行一次往返通信,由后端根据账户层级进行门控。
2. **JS 端**:在打包的 Electron asar 中存在一个 `MAX_TOOLS = 100` 的常量。
3. **原生端**:在 language server 的编译代码中硬编码了一个立即数。
答案结果是 **(3),并且存在于多个位置,表现为两种不同的形式** —— 这正是本文要探讨的内容。
## 方法论
本次调查采用了一次直接的静态分析:
1. **锚定已文档化的常量。** 此上限为 `100 = 0x64`。在 x86-64 架构中,当值位于 `RAX` 中时,与 100 比较的指令会编译为 `48 83 F8 64` (`CMP RAX, 0x64`),对于其他寄存器也有类似的指令形式。直接在 LS 二进制文件上进行 AOB(Array of Bytes)扫描,可以找出所有检查上限的位置。
2. **通过交叉引用进行确认。** 上述字节序列会在许多不相关的地方匹配(任何与 100 进行比较的函数)。通过检查其与已知 MCP 相关字符串的接近程度,可以分离出真正的上限检查位置 —— 这些字符串包括工具注册日志消息、字面量 `mcp` / `tool_registry` 符号,以及包含 `RegisterTool` 回调的函数指针表。
3. **识别执行形态。** 一旦定位到该函数,其反汇编形式(`CMP RAX, 0x64; jcc `)便很容易辨认 —— 这是编译器针对 `if (count >= limit) return error;` 这种守卫语句的经典输出。
4. **统计调用点。** 遍历该函数及其内联调用图可以发现,相同的比较操作在多个注册/分发路径中被*复制*,而不是集中在一个 `CheckCap()` 辅助函数中。
没有使用调试器,没有使用插桩,也没有进行运行时追踪。仅仅使用了 IDA Pro、耐心以及对上限的公开文档。
## 发现
### 1. 上限在原生 LS 中进行客户端强制执行
LS 将 `100` 字面量作为多个 `CMP imm8` 立即数包含在内。该检查在每个工具注册调用路径中都是内联的,并未提取为一个共享的辅助函数。在比较之前没有观察到与 Codeium 后端的任何往返通信;该上限由 LS 在本地决定,并报告回 IDE 端的 JS。
这是一个**深思熟虑的设计选择**:它使得该上限能够离线工作(在不稳定的网络下,LS 依然能一致地执行该限制),并保持工具注册的低延迟。这种权衡意味着该上限在安全意义上是“软性的” —— 它不是经过服务器验证的授权,仅仅是客户端的一个天花板。
### 2. 共存着两个截然不同的数值阈值
在 LS 中扫描 MCP 代码附近的上限检查形态,揭示了**两个常量**:
- `0x64` (= 100) —— 已文档化的公开上限,在最高层的调用路径中强制执行
- `0x7F` (= 127) —— 第二个未文档化的常量,同样对工具注册路径进行门控,在类似于 SIMD 或展开循环的边界检查中重复出现
最简单的解释是:`0x64` 是公开的每账户上限,而 `0x7F` 是内部的“最大安全”上限(与低于 128 的最大 2 的幂次对齐的插槽数相匹配)。在没有符号文件的情况下,这只是猜测,但相邻代码中两个常量的并列出现强烈暗示了一种分层限制:
- “公开上限 = 100” —— 用户看到的内容以及文档中的内容
- “硬性上限 = 127” —— 内部安全护栏;推测 LS 的注册表分配了 128 个插槽,并在此限制之前触及了越界 (OOB)
如果属实,这是一种优秀的工程设计:公开上限是可调的,而内部常量则防止在公开上限发生更改时出现状态损坏。
### 3. 该上限在约 11 个不同的调用点进行强制执行
这是最令人惊讶的发现。该比较操作并不是从每个注册入口点调用一个 `CheckCap()` 函数,而是**内联在 11 个位置**,每个位置发出以下指令之一:
- `CMP , 0x7F` 后跟一个短条件跳转(`JB` 类别);在相邻代码中出现了约 8 次
- `CMP RAX, 0x64` 后跟 `JG`(有符号比较) —— 即已文档化上限的路径
- `CMP RAX, 0x64` 后跟长格式 `JNE` (`0F 85 ...`) —— 一种不同的控制流形态,位于一个单独的函数中
- `CMP RAX, 0x64` 后跟 `JNZ`,位于加载路径附近
最可能的解释:
1. **模板/泛型实例化。** MCP 工具注册表代码根据工具类型进行了模板化,编译器多次实例化相同的“注册一个工具”函数,将上限检查内联到每个实例中。
2. **LTO 驱动的内联。** 全程序优化认为 `CheckCap()` 辅助函数足够热点,因此将其内联到每个调用点。
3. **防御性重复。** 该上限在公共 API 层面(`RegisterTool`)和内部表插入原语处都进行了强制执行 —— 多层结构,每层都有各自的检查。
无论哪种情况,都会产生一个实际后果:该上限并不是“一个位置的一个常量”,而是*编译代码的一种属性*。在未来版本中更改公开上限将需要更新每一个位置。
这正是开发人员通常不会注意到的细节,因为他们绝不会去查看二进制文件 —— 但对于一次逆向工程来说,它立刻显得尤为突出。
### 4. IDE 的自动更新非常积极
与上限本身无关的是,LS 被包装在一个多层自动更新系统中,该系统旨在**针对本地修改进行自我修复**:
- **Squirrel updater** (`Update.exe`),在 IDE 外部进程运行,只要检测到服务器端版本更新,就可以替换 `language_server_windows_x64.exe`
- JS 层中的 **Electron `autoUpdater`**,在 IDE 启动时调用 `checkForUpdates()`
- **`package.json` 更新频道提示**,由 Electron shell 在运行时使用
三层结构,每一层都能独立恢复未经修改的二进制文件。从研究的角度来看,这值得注意,因为它告诉你 Codeium 的威胁模型将“用户修改 LS”视为一种真实的故障模式 —— 不仅仅是恶意的,还包括*偶然的*:一个高级用户发现了一些有趣的东西,修改了它,一周后他们的修改静默地消失了。这种三层设计确保了出于遥测/支持的目的,运行的是标准的 Codeium 版本,而不管用户在本地对其做了什么。
### 5. 相关发现(简述,未深入调查)
- **Token / 上下文窗口上限**在 LS 的其他地方被编码为类似 `CMP r32, imm32` 形式的 32 位立即数。它们独立于 MCP 上限,并存在于独立的代码路径中。
- **Agent 步骤限制**表现为单个非模板化的 `CMP RAX, imm8` 后跟 `JGE rel32`,位于 agentic-loop 调度器附近。只有一个位置,这与 MCP 上限的十一个位置形成鲜明对比 —— 意味着循环终止*确实*被提取为了单个函数,而 MCP 注册则没有。
- **Gemini MCP 路由**使用 Protocol Buffers 的 `function_calling_config.mode` 枚举。LS 嵌入了用于默认配置的原型 protobuf wire 字节;这是正常的 protobuf 用法,但在实际产品中看到它很有趣,因为它证实了 AntiGravity 的 Gemini 集成不仅仅是一个薄薄的 REST 包装器 —— LS 参与了 proto 序列化。
## 这告诉了我们什么
一些可以推广到其他 AI-IDE 产品的小经验:
1. **已文档化的限制通常存在于原生代码中。** “软性”上限 —— 工具数量、历史记录大小、Agent 步骤数 —— 通常是编译后 LS 中的普通立即数。它们的执行成本很低,并且可以抵抗 JS 篡改。如果你知道文档中记录的值,通过锚定 AOB 扫描可以轻易识别它们。
2. **内联强制执行是常态,而非例外。** 现代编译器(尤其是带有 LTO 的)会积极内联守卫检查。不要期望有一个可以一次性 hook 的干净 `EnforceLimit()` 调用;而是期望在所有需要它的地方找到复制的比较指令。
3. **自动更新是限制的一部分。** 存在于二进制文件中的上限,其持久性与二进制文件本身一样有限。Codeium 的三层更新架构使得该上限*在普通用户的机器上保持真实*,维持其有效性的并非是那些字节本身。这是一个真实的模式:“限制的完整性取决于更新管道的完整性。”
4. **客户端上限是用户体验的选择,而不是安全边界。** 100 个工具的上限在本地强制执行,是因为 Codeium 选择将其保留在本地 —— 为了性能、离线行为和简单性。它并非旨在抵抗坚定的本地用户。将其视为一种安全控制将是一种范畴错误。
## 本文不包含的内容
- 用于上限检查点的可用 AOB 字节模式
- 补丁程序、补丁脚本或逐步修改指南
- Codeium 二进制文件、内存转储或提取的代码
- 特定的文件偏移量或 RVA
- 任何能让读者进行修改的代码
如果你正试图绕过该上限 —— 不要指望本文能帮助你。它描述的是*那里有什么*,而不是关于*如何更改它*的说明。
## 为什么发布此文
三个原因:
1. **其架构很有趣。** 三层自动更新、已文档化限制的客户端强制执行、双常量分层、无处不在的内联执行 —— 这些都是在其他产品中值得识别的优秀工程设计模式。
2. **其方法论是可移植的。** 基于已文档化常量锚定的 AOB 扫描对于任何其公开文档披露了数字限制的闭源产品来说,都是一种快速的技术。在自动更新管道中识别分层也是可泛化的。
3. **出于理解目的的逆向工程是合法的。** 在大多数司法管辖区,合法修改你拥有的软件副本是允许的;重新分发修改或修改工具才是法律界限所在。本文始终停留在该界限“理解”的一侧。
## 免责声明
这是在个人机器上安装的 AntiGravity 个人版上进行的独立研究,出于作者自身的技术兴趣。本笔记按“原样”提供,仅供教育目的。
**不要利用此文来违反 Codeium 的服务条款。** 你对自己拥有的二进制文件进行的任何修改完全是你与适用许可之间的事;作者既不鼓励也不分发此类修改。
如果 Codeium 不希望本文存在,请联系作者,它将会被撤下 —— 没有必要为了一份文档而争吵,删除它对作者来说没有任何成本,而它的存在仅仅反映了作者对特定软件内部机制的好奇心。
## 许可证
MIT —— 见 [LICENSE](LICENSE)。该许可仅涵盖文本本身;它不授予对所讨论的任何第三方软件的任何权利。
标签:AI编程助手, AntiGravity, AOB扫描, Codeium, Electron, IDE, MCP, Model Context Protocol, URL提取, x86-64, 二进制分析, 二进制执行, 云安全监控, 云安全运维, 云资产清单, 反编译, 客户端限制, 情报收集, 汇编, 漏洞研究, 绕过研究, 语言服务器, 软件保护机制, 软件破解, 逆向工程, 静态分析