决定使用新版idea了。之前因为熟悉旧版UI一直没换,还在2023.3,下载了新的发现之前的激活工具失效了,于是…
原网站:CodeKey Run
使用方法:
管理员权限打开powershell(win+x键),执行命令:
# 远程下载脚本并运行
irm ckey.run|iex
# 查看脚本代码
irm ckey.run
ckey.run 是一段面向 Windows 的 PowerShell 脚本。它并不是简单地把激活码写入 IDE,而是通过修改 JetBrains 产品的 JVM 启动参数,加载 ja-netfilter Java Agent,再为不同产品写入许可证文件。
从代码结构看,脚本主要完成四件事:识别本机产品、准备 Java Agent、修改 VM Options、生成并保存许可证。
整体执行流程
脚本入口是 Main 函数,执行顺序如下:
Main
├── 显示欢迎信息
├── 检查并申请管理员权限
├── 读取许可证名称和到期时间
├── 清理 JetBrains VM_OPTIONS 环境变量
├── 创建 .jb_run 工作目录
├── 下载 ja-netfilter、配置文件和插件
├── 扫描已安装的 JetBrains 产品
├── 修改产品的 *.vmoptions
├── 请求并写入 *.key 许可证文件
└── 打开 ckey.run 网站
真正完成启动链修改的是下面三个调用:
Create_Work_Dir
File_Download
Process_Vm_Options
管理员权限处理
脚本首先判断当前 PowerShell 是否具有管理员权限。如果没有,它会启动一个提权后的 PowerShell,并让新进程重新从网站获取脚本。
需要管理员权限的主要原因有两个:
- 删除机器级环境变量;
- 修改 JetBrains 安装目录中的 VM Options 文件。
这种重新拉取脚本的方式也意味着,本地看到的脚本和提权后实际运行的内容之间没有固定版本关系。
支持的 JetBrains 产品
脚本使用一个哈希表数组保存产品名称和产品代码:
| 产品 | 目录识别名称 | 许可证产品代码 |
|---|---|---|
| IntelliJ IDEA | idea | II,PCWMP,PSI |
| CLion | clion | CL,PSI,PCWMP |
| PhpStorm | phpstorm | PS,PCWMP,PSI |
| GoLand | goland | GO,PSI,PCWMP |
| PyCharm | pycharm | PC,PSI,PCWMP |
| WebStorm | webstorm | WS,PCWMP,PSI |
| Rider | rider | RD,PDB,PSI,PCWMP |
| DataGrip | datagrip | DB,PSI,PDB |
| RubyMine | rubymine | RM,PCWMP,PSI |
| AppCode | appcode | AC,PCWMP,PSI |
| DataSpell | dataspell | DS,PSI,PDB,PCWMP |
| RustRover | rustrover | RR,PSI,PCWP |
Is_Product 函数通过目录名是否包含产品名称来判断产品类型:
if ($prd_dir_name.ToLower().Contains($prd.name)) {
return $prd
}
这种识别方式简单直接,但属于模糊匹配。如果目录名称中碰巧包含相同字符串,也可能被当作目标产品。
清理 VM_OPTIONS 环境变量
JetBrains 产品允许通过环境变量指定额外的 VM Options 文件,例如:
IDEA_VM_OPTIONS
PYCHARM_VM_OPTIONS
CLION_VM_OPTIONS
Remove_Env 会根据产品名称拼出对应变量名,并分别检查用户级和机器级环境变量:
$upper_key2 = "$($prd.name.ToUpper())_VM_OPTIONS"
[Environment]::SetEnvironmentVariable($upper_key2, $null, $env_scope)
发现变量后,脚本直接将其删除。这样可以避免外部 VM Options 覆盖脚本后续写入的 Java Agent 参数,但也会清除用户原有的 JVM 定制配置。
.jb_run 工作目录
脚本在公共用户目录中创建 .jb_run:
%PUBLIC%\.jb_run\
├── ja-netfilter.jar
├── config\
│ ├── dns.conf
│ ├── env.conf
│ ├── native.conf
│ ├── power.conf
│ └── url.conf
└── plugins\
├── dns.jar
├── env.jar
├── native.jar
├── power.jar
├── url.jar
├── hideme.jar
└── privacy.jar
每次运行时,Create_Work_Dir 都会先删除旧目录,再重新创建目录结构:
if (Test-Path $script:dir_work) {
Remove-Item $script:dir_work -Recurse -Force
}
因此旧的 Agent、插件和配置都会被当前下载版本覆盖。
下载 ja-netfilter 与插件
File_Download 一共下载 13 个文件:
- 1 个
ja-netfilter.jar; - 5 个规则配置文件;
- 7 个功能插件 JAR。
脚本使用 .NET 的 HttpClient 发起请求,并将响应内容直接写入目标文件:
$response = $obj_http_client.GetAsync($file.url).Result
$content = $response.Content.ReadAsByteArrayAsync().Result
[System.IO.File]::WriteAllBytes($file.save_path, $content)
下载 JAR 后,脚本会计算 SHA-1:
$sha1 = [BitConverter]::ToString(
[Security.Cryptography.SHA1]::Create().ComputeHash($content)
)
不过这个值只用于调试输出,没有与预设哈希值比较,因此不能发现文件是否被替换。
HTTP 客户端还把代理设置为空:
$handler.Proxy = [System.Net.GlobalProxySelection]::GetEmptyWebProxy()
也就是说,请求不会使用系统代理配置。
如何定位 JetBrains 安装目录
JetBrains 会在下面的目录保存本地产品信息:
%USERPROFILE%\AppData\Local\JetBrains\
脚本遍历这里的子目录,并读取其中的 .home 文件。.home 保存产品的实际安装路径,脚本再在该路径下查找 bin 目录:
产品缓存目录
└── .home
↓
JetBrains 实际安装目录
└── bin
├── idea64.exe.vmoptions
├── pycharm64.exe.vmoptions
└── idea.properties
如果 idea.properties 定义了 idea.config.path,脚本会优先使用该自定义配置目录;否则使用默认的 Roaming 路径:
%USERPROFILE%\AppData\Roaming\JetBrains\<产品目录>
VM Options 修改逻辑
脚本的关键操作是向 JetBrains 的 *.vmoptions 文件加入 Java Agent 参数。
它先使用正则表达式删除已有的 Java Agent:
$pattern = '^-javaagent:.*[/\\]*\.jar.*'
Revert_Vm_Options 会过滤所有匹配行:
$filtered_lines = $lines | Where-Object {
-not $script:regex.IsMatch($_)
}
之后,Append_Vm_Options 追加新的 Agent 参数,使其指向:
%PUBLIC%/.jb_run/ja-netfilter.jar
修改后的启动关系可以概括为:
启动 JetBrains IDE
↓
JVM 读取 *.vmoptions
↓
加载 ja-netfilter.jar
↓
ja-netfilter 加载 config 与 plugins
↓
插件介入 IDE 的相关网络请求和运行逻辑
-javaagent 是 JVM 提供的标准插桩机制。Java Agent 可以在类加载时转换字节码,因此脚本的核心并不是“修改 IDE 文件”,而是在 IDE 启动时注入一个额外的运行组件。
脚本使用的正则会删除所有 -javaagent 行,不区分它属于 ja-netfilter、性能分析器还是其他工具,因此可能误删用户已有的 Agent 配置。
许可证文件生成
Create_Key 负责生成产品许可证文件。它先确定产品配置目录,然后构造两个路径:
<产品配置目录>\<产品名>64.exe.vmoptions
<产品配置目录>\<产品名>.key
如果已经存在 .key 文件,脚本会先删除。随后构造 JSON 请求:
{
"assigneeName": "",
"expiryDate": "2099-12-31",
"licenseName": "ckey.run",
"productCode": "产品代码集合"
}
其中许可证名称和到期日期允许用户输入,默认值分别为 ckey.run 和 2099-12-31。
请求通过 PostAsync 发送到许可证生成接口,返回内容被直接保存为 .key 文件:
$key_bytes = $response.Content.ReadAsByteArrayAsync().Result
[System.IO.File]::WriteAllBytes($file_key, $key_bytes)
这里没有对返回内容进行格式、长度或签名校验。
disabled_plugins.txt 处理
许可证写入成功后,脚本会检查产品目录中的 disabled_plugins.txt,并删除下面这一行:
com.intellij.modules.ultimate
该文件用于记录被禁用的插件或模块。删除这条记录后,对应模块不会继续处于禁用列表中。
这套脚本的本质
整套逻辑可以分为两个部分:
Java Agent 部分
通过修改 *.vmoptions,让 JetBrains IDE 每次启动时加载 ja-netfilter.jar。这是脚本能够持续介入 IDE 运行过程的基础。
许可证文件部分
根据不同 JetBrains 产品的代码,从远端获取对应 .key 文件并写入产品配置目录。
两者结合后,IDE 启动时先加载 Agent,再读取本地许可证文件。脚本所谓的“激活”不是一次性的注册表修改,而是由启动参数、Java Agent、插件配置和许可证文件共同组成。
代码层面的几个问题
下载文件没有真正的完整性校验
脚本虽然计算 SHA-1,却没有可信的目标值用于比对。只要服务器正常返回内容,任何 JAR 都会被保存并在 IDE 启动时加载。
修改范围过大
用于清理 VM Options 的正则会删除全部 Java Agent 配置,可能影响调试器、监控工具和性能分析工具。
缺少备份和回滚
环境变量、VM Options、许可证文件和工作目录都会被直接修改或删除,脚本没有保存原始状态。
远端响应直接落盘
下载的 JAR 和接口返回的许可证内容都没有进行签名验证。脚本把远端服务器当作完全可信来源。
工作目录与启动参数强绑定
VM Options 持续引用 %PUBLIC%\.jb_run\ja-netfilter.jar。如果只删除 .jb_run 而不恢复启动参数,IDE 可能因为找不到 Agent 而无法正常启动。
总结
从实现上看,ckey.run 是一个自动化的 JetBrains Java Agent 部署与许可证写入脚本。它完成了产品发现、环境清理、文件下载、启动参数注入、许可证请求以及插件状态调整。
其关键链路是:
扫描 JetBrains 产品
↓
下载 ja-netfilter 组件
↓
修改 *.vmoptions
↓
IDE 启动时加载 Java Agent
↓
读取远端生成的本地许可证文件
理解这条链路后,就能看出脚本的大部分函数都在为两件事服务:保证 Java Agent 被加载,以及为每个产品准备对应的许可证文件。