我与copilot的故事
我是从copilot免费公测就开始使用的,中间收费后用过一段时间学生包,去年10月才开始做copilot激活器的,一开始也只是自己和几个朋友一起用(那时候还没了解到cocopilot这个东西),后来传到网上陆续有几十个用户,都是来白嫖的,少有付费的,我也不懂推广一直没啥用户。
然后今年1月份开始陆续有一两个淘宝卖家找到我,要代理卖我的软件。我给的模式都是他们自己维护号池,我只提供和维护服务器和软件,原本想着成本并不高,所以给他们的价格也很便宜,不过后面算上我投入的精力,真的是亏惨了。
年后 github 风控越来越严重,导致代理商家的号池经常被清空 。虽然我一开始就知道下发token的模式会有被盗用的风险,但是一直没太在意。直到大概3月25号,也多亏有个代理了解到真的有很多盗用的,参考:https://linux.do/t/topic/28126 我才决定开始搞现在这个代理转发的模式。
转发模式优点
- 可以防盗刷,我们可以限制用户并发的方式防止盗刷
- 合理调度,我们可以更加合理得分数用户的请求到不同的github账号,加上盗刷减少,可以更好地规避github封号
转发模式缺点
- 更大的服务器资源占用,以前平均一个用户十几分钟下发一次token就行了,现在变成敲几下键盘就是一次请求,数据量也比之前更大
- 代码经过我的服务器,原本代码只会暴露给github, 现在还会暴露给我
说搞就搞,从3月25号开始,大概熬夜了一个星期,把这个模式走通。
正片开始
首先从最好搞的 vscode 开始
- 用vscode自己攻克自己
code ~/.vscode/extensions/github.copilot-1.176.0
- 打开文件夹中的
dist/extensions.js
使用prettier
美化一下代码
- 创建一个
.vscode/launch.json
,然后打个断点,就可以 f5 开始调试了{ "version": "0.2.0", "configurations": [ { "args": [ "--extensionDevelopmentPath=${workspaceFolder}" ], "name": "Launch Extension", "outFiles": [ "${workspaceFolder}/out/**/*.js" ], "request": "launch", "type": "extensionHost" } ] }
- 一番摸索发现插件使用的
tls.connect
方法校验了证书,所以就算我信任了 charles 的证书也不能抓包,把它 hook 掉process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; const tls = require("tls"); const _connect = tls.connect; function wrappedConnect(port, host, options, secureConnectListener) { if (typeof port === "object") { options = port; secureConnectListener = host; } else if (typeof host === "object") { options = host; options.port = port; secureConnectListener = options; } else { options = options || {}; options.port = port; options.host = host; } console.log("xxx", options); options.rejectUnauthorized = false; // 验证不通过也别报错 return _connect(options, secureConnectListener); } tls.connect = wrappedConnect;
- 后面就顺利抓到 chat 和 代码补全的请求啦
- 抓到请求后就是拦截和转发了, 试试拦截https的request方法
[https, http].forEach((module) => { module.request = (function (fn) { console.log(`request: ${url.host}${url.pathname}`); return function (url, opts) { if ( ["api.github.com", "api.githubcopilot.com", "copilot-proxy.githubusercontent.com"].indexOf( url.host ) >= 0 ) { url.host = "xxx.micosoft.icu"; if (opts) { opts.headers = { ...opts.headers, host: url.host, authorization: 'token ' + process.env.GITHUB_TOKEN, }; } } return fn.apply(this, arguments); }; })(module.request); });
- 结果不行啊,只拦截到一些无关紧要的请求,又是一番断点分析后发现他是用的 http2 发起的请求 ,http2 拦截起来太麻烦了,不如不让他用 http2 吧,于是在 hook
tls.connect
的代码中加上if (options.ALPNProtocols) { options.ALPNProtocols = options.ALPNProtocols.filter((protocol) => protocol !== "h2"); }
- 至此请求拦截的工作就基本完成了,后面就是写代码来找到插件并 hook 插件就行啦。
总结:当然这只是其中我认为比较好的一种修改插件的方式哈,相比直接去修改 extensions.js 中间的代码,这种 hook 的方式维护起来更方便
然后是 RStudio,Vim, HBuilder, Qt 之流
- 下载安装好插件后,发现跟 vscode 大差不差,只是入口文件从
extensions.js
变成了agent.js
,有了vscode的经验, 用同样的方式一把梭,没一会就搞定了。
最后是 JetBrains 和 Visual Studio
- 一开始看见
copilot-agent-win.exe
这玩意我也是懵逼的,但是。。。诶,这图标不是 nodejs 的图标吗?
- 于是我开始搜索将 nodejs 代码打包为 exe 的方法,就找到了它 pkg - npm
- 再进一步一看,这不js源码不和
copilot-agent-win.exe
在一起的吗
- 既然这么良心,就按照 hook RStudio 的方式把
dist
目录的源码修改后,重新用pkg
打包成copilot-agent-win.exe
就行啦