最近小编用上了 claude-science,要登录,但是我没账号,真是烦。
于是我想把登陆绕过去。用其他供应商的兼容 API key,比如 moonshot、deepseek 或者其他国产模型。但是,发现还是有一些坑的,记录一下。以下内容在“田螺姑娘”协助下完成,我是不懂的,好在她能力足够强,独自完成了任务。另外一个应该是A家没有做提高难度的封装什么的。
二进制锁死
claude-science 是个 Bun 单可执行文件,大小有158MB。JS 代码嵌在里面,按长度/偏移寻址,不能插入或删除一个字节——否则整个二进制启动就会崩溃,报SyntaxError。
这意味着所有修改只能是等长替换:改几个字符可以,塞新代码不行。
7 道关卡
原厂做了好几层防护。其实以下内容我也看不懂,没关系,实现了就可以的。需要我也可以分享我的结果。我在 v0.1.18 的时候使用opencode+hy3+oh my openagent摸清了 7 处需要改的地方,这次升级到 v0.1.20,大部分偏移变了,但思路没变:
P1 — 会话伪造。如果没有 OAuth 登录就没有 session,但程序是需要一个account_uuid的。于是我直接把返回 session 的那个函数替掉,让它永远返回的是{account_uuid:"local-owner", email:"owner@local"}。
坑:v0.1.18 的函数叫A9(),v0.1.20 叫R9(),长度也不一样(70B → 196B),得把整个 R9 和 qdz 两个函数一起替换,用/*xxxxx*/注释对齐字节数。世界是个草台班子呀!
P2 — 认证门。V=Y(q);if(!V)是检查 cookie 里有没有 token的。把Y(q)(4 字节函数调用)替换成1(1 + 三个空格),让 V 永远是 1,if(!V)就永远不成立啦。
P3 — 凭据解析。原本是throw Error("No credentials available...")。我把它改成了return{auth_token:process.env.ANTHROPIC_API_KEY,...},这样就直接走环境变量的 key。
v0.1.18 只改 60 字节就够了,v0.1.20 要把整个 throw 语句加后面的}一起替掉(100 字节),否则后半段字符串的垃圾字节会卡在 return 的对象字面量里面。
P4 — 签名校验。if(!O)throw new h8("This build requires signing in..."),把O改成1(一个字节),变成if(!1),这样就是恒假了。
P5 — require_token 默认值。zod schema 里require_token: C.boolean().default(!0),!0在 minify 后的 JS 里就是true。改成!1(false)。
P6 — 登录守卫。这关是最容易漏的。即使 P5 把默认值改成了 false,原厂还有一段:if(!W.require_token) process.stderr.write("disabling login auth is not available..."); process.exit(1);。也就是说,只要require_token为 false,就会直接退出。
于是我把if(!W.require_token(19 字节)替换成if(0&&W.require_tok(也 19 字节)。注意token变成tok也是为了凑等长。
P7 — 模型名白名单。原本只允许claude-和deepseek-开头的模型。正则从/^claude-/i改成/^.{1,99}/i,使其能够匹配任意非空字符串。
适配 v0.1.20 踩的坑
上面 7 个补丁在 v0.1.18 上是直接可用的。可是升到 v0.1.20 后就撞了三个问题:
P1 替换崩溃。原来 70 字节替换后接收到的字节是
{z=e7.readdirSync这种无效代码。因为 v0.1.18 的 A9 函数后面恰好是合法续文,v0.1.20 的 R9 后面就不是了。解决方案是把整个 R9+qdz 函数(196B)一起替换掉,这样就不再依赖边界巧合。P3 替换崩溃。同理,原 60 字节替换后留了半截错误消息字符串在 return 语句里。扩大到 100 字节,就把整个 throw 加下一行的
}一起替换了。P5 定位偏差。搜索前缀
require_token:C.boolean().default(!里面带了!,导致替换位置偏了一个字节——把0)替换成了!1,直接 SyntaxError。就改成不带!的前缀require_token:C.boolean().default(就好了。
自动化补丁脚本
整个流程写了 Python 脚本,所有偏移通过bytes.find()动态定位(不硬编码),每个补丁做完立刻校验字节相等:
[P3] OK: 108956056 (100B, equal length) [P4] OK: 103069897 (50B, equal length) [P5] OK: 102022973 (2B, equal length) [P1] OK: 110187297 (196B, equal length) [P2] OK: 111566617 (4B, equal length) [P6] OK: 111583254 (19B, equal length) [P7] OK: 101913087 (11B, equal length)新增:禁用自动更新
发现程序还是会自动更新,于是在补完 7 个补丁后,我加了个 P8,把自动更新关掉:
auto_update: C.boolean().default(!0)→default(!1)autoUpdate:!0→autoUpdate:!1
现在版本锁在 0.1.20,不会被官方悄悄更新覆盖。
验证
# 干净环境测试——必须输出正常env-iHOME=/home/zd200572PATH="$PATH"\ANTHROPIC_API_KEY=sk-dummy\OPERON_AUTH_OWNER_NONCE=xxx\./claude-science--version# claude-science 0.1.20 (release, public)# 启动守护进程./claude-science serve--port8733&curlhttp://127.0.0.1:8733/api/auth/status# {"authenticated":true, "account_uuid":"local-owner"}运行时要点
其实二进制破解只是第一步。其他的远程报错几乎都栽在这几件事上:
- env 没注入。不要直接 运行
./claude-science,要用它配套的 launcherclaude-science-ll(会 source~/.claude-science.env)。或者确保ANTHROPIC_API_KEY在进进程之前就设好。 - BASE_URL 填根域名。就比如
https://api.moonshot.cn/anthropic,二进制自己拼/v1/messages。带上完整路径会重复导致 404。 - 端口转发。daemon 默认监听
127.0.0.1:8731,远程用记得做端口转发哦。
感想
这种二进制补丁的活,看起来粗暴,但核心思路很朴素:搞清楚程序在哪些节点做了认证检查,每处改一个字节也好、几十个字节也好,只要等长,就能在不破坏结构的前提下把逻辑拧过来。
过程里最折磨的不是改代码,而是定位偏移和保证等长。你改错一个字节,误差可能不在当前行爆发,而是几十行之后才 SyntaxError——说它崩在哪,往往不是问题在哪。
如果你也用类似工具、类似的绕过需求,希望这份记录能让你少踩几个坑。
当然,也有有账号,切换供应商的方案,Release v1.2.0 — Desktop 重构为 CLI GUI 包装层 · YuntaoOvO/CSSwitch-Linux不过我尝试了没成功,期待大佬后期的进展。
深知Dario反华急先锋,我们要努力,超越他,让他们没有骄傲的资本!
最后分享我的结果,如果你需要的话,欢迎交流。链接:https://pan.baidu.com/s/1znsV3In07XBSRWII83fUDg?pwd=gkai 提取码:gkai 复制这段内容后打开百度网盘手机App,操作更方便哦