我把一个 180MB 的程序「拆开」看了:用 REA 读懂 Google 的 agy 命令行
文章目录
部分视频或图片暂未收录,已在正文保留提示;需要时可查看原文。查看原文 ↗
保姆级跟练。不需要会编程,每一步都写明命令贴在哪里、做完会看到什么。
这篇在讲什么
你电脑里的每个软件,本质上都是一个「黑盒子」:你点按钮,它干活,但它内部怎么判断、怎么决定,外面看不到。逆向分析,就是把黑盒子打开,看清里面的齿轮怎么转。
REA(Reverse Engineer Anything)是一个开源工具,专门帮 AI 助手(Codex、Claude Code 这类)做这件事。最近它在 GitHub 趋势榜上很火。之前蓝哥写过用它逆向恐龙小游戏的教程,那篇研究的是网页游戏。
这篇换个更大的目标:Google 官方 AI 编程工具 Antigravity 的命令行程序 agy——一个 180MB 的 Go 语言程序,足有几万个小程序拼起来那么大。而且它出厂前被「擦掉名字」了(技术上叫剥离符号表,就是把零件上的标签全撕了),正常手段根本读不动。
跟着这篇做完,你会看到:
-
这个程序怎么判断「你登录了没有」
-
它怎么检查「你的账号有没有资格用」,以及一个诚实得有点可爱的细节:查不出来就放行
-
它在代码里藏了哪些没写进文档的开关
先声明边界:全程只读不改,分析的是自己电脑上合法安装的软件,纯粹学习目的。程序里翻到的账号密钥类内容,本文一律不引用。
准备:三样工具
按 Command + 空格,搜索「终端」并打开。后面的命令都贴在这里,按回车执行。
① Node.js —— 运行 REA 的发动机。终端里贴:
node --version
显示版本号就是有了。REA 对版本挑剔:只认 22、24、26 系列。提示 command not found 就去 Node.js 官网下载 24 系列 LTS 安装包装一下。
② REA 本体。贴:
npm install --global rea-agents@6.4.0
注意一定要带 @6.4.0。我实测裸装 npm i -g rea-agents 会装到一个旧版本,功能不全,这个坑帮你踩过了。
③ Ghidra —— 美国国安局开源的反汇编器,REA 靠它读二进制程序。这里有个官方文档不会主动告诉你的事:Ghidra 的 Mac 安装包缺了一个关键零件(反编译引擎的 Mac 版),要自己编译补上。三行命令:
brew install openjdk@21 cd ~/tools && curl -L -o ghidra.zip https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_12.1.4_build/ghidra_12.1.4_PUBLIC_20260921.zip && unzip -q ghidra.zip cd ghidra_12.1.4_PUBLIC/support/gradle && JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home ./gradlew buildNatives
第三行跑约两分钟,看到 BUILD SUCCESSFUL 就齐了。最后告诉 REA 它们在哪:
export GHIDRA_INSTALL_DIR=~/tools/ghidra_12.1.4_PUBLIC export JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home rea doctor --provider ghidra --json
看到一串 "ok":true 就是环境通了。
第 1 步:先看「身份证」
REA 有几条命令不需要任何重型工具,先给目标拍个证件照:
rea inspect-macho ~/.local/bin/agy --json
输出里是这个程序的「骨架图」:代码放哪段、数据放哪段。就是这步发现了一件怪事——正常 Go 程序里有个固定叫 __gopclntab 的零件,这个程序里却改名叫 __lrodata_gopcln。再跑一句系统自带的检查:
go version -m ~/.local/bin/agy
谜底揭开:go1.28-20260921-RC02 —— 这是 Google 内部版的 Go 编译器,公网上根本下载不到,难怪零件名字都对不上。这就像拆一台机器,发现螺丝规格是工厂特供的。
第 2 步:名字被擦了,但「通讯录」还在
这是全程最关键的一步,先说原理(用大白话):
Go 程序编译时,会留一张「通讯录」(就是那个改名了的零件),记录每个功能模块的地址和名字。擦标签的程序把对外的名单删了,但这张内部通讯录还在——因为程序崩溃时要靠它定位问题。
现成的恢复工具(gore、redress)这时候全部失灵:它们只会按标准名字找通讯录,找到不到就两手一摊。
解法是自己把通讯录抠出来读。我把这个过程做成了一个开源小工具(用的全是 Go 官方自带库):
git clone https://github.com/VallierDev/rea-go-seeds cd rea-go-seeds go run . ~/.local/bin/agy ./out
15 秒后,157,992 个功能模块的名字全部恢复。光从名字就能看出它内部长什么样:登录、聊天、插件、浏览器控制、自动更新……包路径里甚至能看到 Google 内部仓库的目录结构。
第 3 步:把通讯录交给 Ghidra
REA 有个很妙的设计:分析前可以喂一份「种子文件」,告诉 Ghidra 哪个地址是什么功能。格式就是「地址 + 名字」一行一条。上一步的 out/seeds.txt 已经按这个格式写好了,接上就行:
export REA_GHIDRA_SEED_FILE=$PWD/out/seeds.txt rea analyze ~/.local/bin/agy --provider ghidra --json
180MB 的程序,分析约 46 分钟,可以去喝杯咖啡。跑完之后,这个「被擦掉名字」的程序在 Ghidra 里以全部真实名字打开。之后的体验就完全不同了:想知道它怎么检查资格?直接搜 waitForEligibility 就行,不用在十几万个匿名零件里猜。
第 4 步:替大家问了三个问题
Q1:它怎么判断我登录了没有?
反编译出来的逻辑很人性化:先看你有没有存过的登录凭证,有而且没过期就直接用,连浏览器都不打开;实在不行才弹浏览器走授权。还有个小细节:它专门给 macOS 钥匙串做了「熔断」——如果钥匙串 500 毫秒没响应,就留个标记文件,下次启动直接绕开走文件存储。显然官方被「钥匙串弹窗卡死」这个坑咬过。
Q2:它怎么检查账号资格?
这是最有趣的发现。程序会向服务器问一句「我的账号有资格吗」,但代码里白纸黑字写着:等 30 秒(命令行模式 60 秒)还没等到结论,就当你有资格,继续干活。日志原文是 "Timed out waiting for the account-eligibility verdict; sending anyway"——「等不到资格结论,照样发送」。
也就是说,这个检查是客户端的自我约束,不是服务器的门禁。我们之前用抓包方法观察到「服务器不拦」,这次从代码层面找到了互证。
Q3:它还藏了什么?
-
四套服务器环境地址(正式版/每日版/两个测试版),一个环境变量就能切换
-
一个藏在 Cloud Run 上的自动更新服务器
-
11 个故意对用户隐藏的命令行开关,包括一整套「远程控制你本地 Chrome 浏览器」的接口
-
一批还没发布的模型代号(RIFTRUNNER、INFINITYBLOOM 这种内部花名),以及 1650 个占位型号——像是给未来两年的产品线留好了坑位
卡住了,怎么继续?
-
npm 装完功能对不上 → 卸载后按第 ② 步带版本号重装
-
rea doctor 说 Node 版本不行 → 换 22/24/26 系列;只是提示的话 CLI 实际能用
-
Ghidra 报缺 decompile 文件 → 就是第 ③ 步的编译没跑,补上即可
-
恢复工具读不出名字 → 目标是不是标准工具链编的;用第 2 步的工具再试
-
分析跑很久 → 大程序正常,46 分钟那次是 180MB;小工具几分钟
这套方法还能用在哪里
把「擦了名字的 Go 程序」变回「有名有姓可阅读」,这套流程对任何 Go 写的命令行工具都成立——Docker、kubectl、Hugo 这些你天天用的工具都是 Go 写的,自己编译的版本去掉符号后,都可以用同样的方法恢复阅读。工具在 GitHub 开源,欢迎自取:
github.com/VallierDev/rea-go-seeds
里面有:恢复工具本体(150 行,零依赖)、给 REA 批量提需求的会话脚本、这次实操的完整查询清单,以及一份更硬核的技术版教程(含每条结论的反编译出处)。
如果这篇对你有点帮助,欢迎 关注 + 收藏 + 转发。







