备份: 完整开发状态(含反混淆脚本和临时文件)
This commit is contained in:
BIN
extension/cursorpro-0.4.5.vsix
Normal file
BIN
extension/cursorpro-0.4.5.vsix
Normal file
Binary file not shown.
BIN
extension/hummingbird-cursorpro-2.0.0.vsix
Normal file
BIN
extension/hummingbird-cursorpro-2.0.0.vsix
Normal file
Binary file not shown.
120
extension/out/ANALYSIS.md
Normal file
120
extension/out/ANALYSIS.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# CursorPro 反混淆分析报告
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
deobfuscated/
|
||||
├── extension.js # 扩展主入口
|
||||
├── api/
|
||||
│ └── client.js # API 客户端
|
||||
├── utils/
|
||||
│ ├── account.js # 账号管理工具
|
||||
│ └── sqlite.js # SQLite 数据库操作
|
||||
└── webview/
|
||||
└── provider.js # Webview 提供者
|
||||
```
|
||||
|
||||
## 功能分析
|
||||
|
||||
### 1. extension.js - 扩展入口
|
||||
- **cleanServiceWorkerCache()**: 清理 Cursor 的 Service Worker 缓存
|
||||
- **activate()**: 注册 webview provider 和状态栏
|
||||
- **updateUsageStatusBar()**: 更新状态栏显示使用量
|
||||
|
||||
### 2. api/client.js - API 客户端
|
||||
与远程服务器通信,主要 API:
|
||||
|
||||
| 函数 | 端点 | 说明 |
|
||||
|------|------|------|
|
||||
| `verifyKey()` | POST /api/verify | 验证激活码 |
|
||||
| `switchAccount()` | POST /api/switch | 切换账号 |
|
||||
| `getSeamlessStatus()` | GET /api/seamless/status | 获取无缝模式状态 |
|
||||
| `injectSeamless()` | POST /api/seamless/inject | 注入无缝模式 |
|
||||
| `getProxyConfig()` | GET /api/proxy-config | 获取代理配置 |
|
||||
|
||||
**默认 API 服务器**: `https://api.cursorpro.com` (从混淆代码中提取)
|
||||
|
||||
### 3. utils/account.js - 账号管理
|
||||
|
||||
**getCursorPaths()** - 返回 Cursor 配置路径:
|
||||
|
||||
| 平台 | 数据库路径 |
|
||||
|------|-----------|
|
||||
| Windows | `%APPDATA%/Cursor/User/globalStorage/state.vscdb` |
|
||||
| macOS | `~/Library/Application Support/Cursor/User/globalStorage/state.vscdb` |
|
||||
| Linux | `~/.config/Cursor/User/globalStorage/state.vscdb` |
|
||||
|
||||
**writeAccountToLocal()** - 写入账号数据到本地:
|
||||
- 修改 SQLite 数据库中的认证 token
|
||||
- 更新 storage.json 中的设备 ID
|
||||
- 写入 machineid 文件
|
||||
- Windows: 写入注册表
|
||||
|
||||
**关键数据库字段**:
|
||||
```
|
||||
cursorAuth/accessToken - 访问令牌
|
||||
cursorAuth/refreshToken - 刷新令牌
|
||||
cursorAuth/WorkosCursorSessionToken - WorkOS 会话令牌
|
||||
cursorAuth/cachedEmail - 缓存邮箱
|
||||
cursorAuth/stripeMembershipType - 会员类型
|
||||
telemetry.serviceMachineId - 服务机器ID
|
||||
telemetry.devDeviceId - 设备ID
|
||||
```
|
||||
|
||||
### 4. utils/sqlite.js - SQLite 操作
|
||||
通过 `sqlite3` 命令行工具直接操作 Cursor 的 VSCode 状态数据库:
|
||||
- `sqliteGet()` - 读取单个值
|
||||
- `sqliteSet()` - 写入单个值
|
||||
- `sqliteSetBatch()` - 批量写入 (使用事务)
|
||||
|
||||
### 5. webview/provider.js - Webview 界面
|
||||
实现侧边栏 UI,提供:
|
||||
- 激活码验证界面
|
||||
- 使用统计显示
|
||||
- 无缝模式配置
|
||||
- 代理设置
|
||||
- 账号切换功能
|
||||
|
||||
## 工作原理
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ CursorPro 工作流程 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. 用户输入激活码 │
|
||||
│ ↓ │
|
||||
│ 2. 发送到远程 API 服务器验证 │
|
||||
│ ↓ │
|
||||
│ 3. 服务器返回账号数据 (token, email, 设备ID等) │
|
||||
│ ↓ │
|
||||
│ 4. 写入本地 Cursor 配置文件: │
|
||||
│ - state.vscdb (SQLite 数据库) │
|
||||
│ - storage.json │
|
||||
│ - machineid │
|
||||
│ ↓ │
|
||||
│ 5. 提示重启 Cursor 生效 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 安全风险分析
|
||||
|
||||
1. **远程服务器控制**: 所有账号数据来自 `api.cursorpro.com`
|
||||
2. **本地文件修改**: 直接操作 Cursor 数据库和配置文件
|
||||
3. **设备指纹伪造**: 替换 machineId, devDeviceId 等标识
|
||||
4. **进程控制**: 可强制关闭 Cursor 进程
|
||||
|
||||
## 混淆技术分析
|
||||
|
||||
原代码使用了以下混淆技术:
|
||||
|
||||
1. **字符串数组 + 解密函数**: 所有字符串存储在数组中,通过 RC4 算法解密
|
||||
2. **十六进制变量名**: `_0x50c5e9`, `_0x2b0b` 等
|
||||
3. **控制流平坦化**: 使用 switch-case 打乱代码执行顺序
|
||||
4. **死代码注入**: 插入无用的条件分支
|
||||
5. **Base64 + RC4 双重编码**: 字符串先 Base64 再 RC4 加密
|
||||
|
||||
---
|
||||
|
||||
*此分析仅供安全研究和学习目的*
|
||||
File diff suppressed because one or more lines are too long
263
extension/out/seamless.js
Normal file
263
extension/out/seamless.js
Normal file
@@ -0,0 +1,263 @@
|
||||
'use strict';
|
||||
|
||||
// ============================================
|
||||
// CursorPro 无感换号模块 - 详细分析
|
||||
// ============================================
|
||||
|
||||
const vscode = require('vscode');
|
||||
const client = require('./api/client');
|
||||
const account = require('./utils/account');
|
||||
|
||||
/**
|
||||
* ============================================
|
||||
* 无感换号 (Seamless Mode) 工作原理
|
||||
* ============================================
|
||||
*
|
||||
* 核心思路:
|
||||
* 1. 用户配置一个"账号池",包含多个 Cursor 账号的 token
|
||||
* 2. 当检测到当前账号额度用尽或即将用尽时
|
||||
* 3. 自动从账号池中选择下一个可用账号
|
||||
* 4. 无缝切换到新账号,用户无感知
|
||||
*
|
||||
* 关键 API 端点:
|
||||
* - /api/seamless/status 获取无缝模式状态
|
||||
* - /api/seamless/config 获取/更新无缝配置
|
||||
* - /api/seamless/inject 注入无缝模式到本地
|
||||
* - /api/seamless/restore 恢复原始设置
|
||||
* - /api/seamless/accounts 获取账号池列表
|
||||
* - /api/seamless/token 获取指定账号的 token
|
||||
* - /api/seamless/switch 切换到指定账号
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// 无缝模式配置结构
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* @typedef {Object} SeamlessConfig
|
||||
* @property {boolean} enabled - 是否启用无缝模式
|
||||
* @property {string} mode - 切换模式: 'auto' | 'manual'
|
||||
* @property {number} switchThreshold - 切换阈值 (剩余额度百分比)
|
||||
* @property {string[]} accountPool - 账号池 (userKey 列表)
|
||||
* @property {number} currentIndex - 当前使用的账号索引
|
||||
*/
|
||||
const defaultSeamlessConfig = {
|
||||
enabled: false,
|
||||
mode: 'auto', // 自动切换
|
||||
switchThreshold: 10, // 当剩余额度低于 10% 时切换
|
||||
accountPool: [],
|
||||
currentIndex: 0
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// 无缝模式核心函数
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 获取无缝模式状态
|
||||
* 检查服务端是否支持无缝模式,以及当前用户是否有权使用
|
||||
*/
|
||||
async function getSeamlessStatus() {
|
||||
return client.request('/api/seamless/status');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取无缝模式配置
|
||||
* 从服务端获取用户的无缝模式配置
|
||||
*/
|
||||
async function getSeamlessConfig() {
|
||||
return client.request('/api/seamless/config');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新无缝模式配置
|
||||
* @param {SeamlessConfig} config - 新的配置
|
||||
*/
|
||||
async function updateSeamlessConfig(config) {
|
||||
return client.request('/api/seamless/config', 'POST', config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户切换状态
|
||||
* 检查指定用户当前的使用状态,判断是否需要切换
|
||||
* @param {string} userKey - 用户标识
|
||||
*/
|
||||
async function getUserSwitchStatus(userKey) {
|
||||
return client.request('/api/seamless/user-status?key=' + encodeURIComponent(userKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入无缝模式
|
||||
* 将无缝模式的配置写入本地 Cursor
|
||||
*
|
||||
* 这是无感换号的核心!
|
||||
* 它会修改 Cursor 的认证配置,使其指向一个代理服务器
|
||||
* 代理服务器会自动处理账号切换
|
||||
*
|
||||
* @param {string} apiUrl - 无缝模式的 API 代理地址
|
||||
* @param {string} userKey - 用户标识
|
||||
*/
|
||||
async function injectSeamless(apiUrl, userKey) {
|
||||
const result = await client.request('/api/seamless/inject', 'POST', {
|
||||
api_url: apiUrl,
|
||||
user_key: userKey
|
||||
});
|
||||
|
||||
if (result.success && result.data) {
|
||||
// 将返回的账号数据写入本地
|
||||
// 这里的关键是:写入的 token 是代理服务器的 token
|
||||
// 代理服务器会根据使用情况自动切换真实账号
|
||||
await account.writeAccountToLocal(result.data);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复原始设置
|
||||
* 移除无缝模式,恢复到单账号模式
|
||||
*/
|
||||
async function restoreSeamless() {
|
||||
return client.request('/api/seamless/restore', 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取账号池列表
|
||||
* 返回用户配置的所有账号
|
||||
*/
|
||||
async function getSeamlessAccounts() {
|
||||
return client.request('/api/seamless/accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步账号池
|
||||
* 将本地账号列表同步到服务端
|
||||
* @param {Array} accounts - 账号列表
|
||||
*/
|
||||
async function syncSeamlessAccounts(accounts) {
|
||||
return client.request('/api/seamless/accounts', 'POST', { accounts });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号的 Token
|
||||
* @param {string} userKey - 用户标识
|
||||
*/
|
||||
async function getSeamlessToken(userKey) {
|
||||
return client.request('/api/seamless/token?key=' + encodeURIComponent(userKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动切换到指定账号
|
||||
* @param {string} userKey - 要切换到的账号标识
|
||||
*/
|
||||
async function switchSeamlessToken(userKey) {
|
||||
const result = await client.request('/api/seamless/switch', 'POST', {
|
||||
mode: 'seamless',
|
||||
userKey: userKey
|
||||
});
|
||||
|
||||
if (result.success && result.data) {
|
||||
await account.writeAccountToLocal(result.data);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 无感换号流程图
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
*
|
||||
* ┌─────────────────────────────────────────────────────────────────┐
|
||||
* │ 无感换号工作流程 │
|
||||
* ├─────────────────────────────────────────────────────────────────┤
|
||||
* │ │
|
||||
* │ ┌──────────────┐ │
|
||||
* │ │ 用户请求 │ │
|
||||
* │ │ (使用 Cursor) │ │
|
||||
* │ └──────┬───────┘ │
|
||||
* │ │ │
|
||||
* │ ▼ │
|
||||
* │ ┌──────────────┐ ┌──────────────┐ │
|
||||
* │ │ Cursor 客户端 │────▶│ 代理服务器 │ (CursorPro API) │
|
||||
* │ │ (本地修改后) │ │ │ │
|
||||
* │ └──────────────┘ └──────┬───────┘ │
|
||||
* │ │ │
|
||||
* │ ▼ │
|
||||
* │ ┌──────────────┐ │
|
||||
* │ │ 检查当前账号 │ │
|
||||
* │ │ 额度是否充足 │ │
|
||||
* │ └──────┬───────┘ │
|
||||
* │ │ │
|
||||
* │ ┌───────────────┼───────────────┐ │
|
||||
* │ │ │ │ │
|
||||
* │ ▼ ▼ ▼ │
|
||||
* │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
* │ │ 账号 A │ │ 账号 B │ │ 账号 C │ (账号池) │
|
||||
* │ │ 额度:5% │ │ 额度:80% │ │ 额度:60% │ │
|
||||
* │ └─────────┘ └────┬────┘ └─────────┘ │
|
||||
* │ │ │
|
||||
* │ ▼ │
|
||||
* │ ┌──────────────┐ │
|
||||
* │ │ 使用账号 B │ (额度最充足) │
|
||||
* │ │ 转发请求 │ │
|
||||
* │ └──────┬───────┘ │
|
||||
* │ │ │
|
||||
* │ ▼ │
|
||||
* │ ┌──────────────┐ │
|
||||
* │ │ Cursor API │ │
|
||||
* │ │ (官方服务器) │ │
|
||||
* │ └──────┬───────┘ │
|
||||
* │ │ │
|
||||
* │ ▼ │
|
||||
* │ ┌──────────────┐ │
|
||||
* │ │ 返回结果给 │ │
|
||||
* │ │ 用户 │ │
|
||||
* │ └──────────────┘ │
|
||||
* │ │
|
||||
* │ 用户全程无感知,只要账号池中有任一账号有额度,就能继续使用 │
|
||||
* │ │
|
||||
* └─────────────────────────────────────────────────────────────────┘
|
||||
*
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// 无感换号的技术实现细节
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 关键技术点:
|
||||
*
|
||||
* 1. 代理注入
|
||||
* - 修改本地 Cursor 的 API 端点指向代理服务器
|
||||
* - 所有请求先经过代理,代理决定使用哪个真实账号
|
||||
*
|
||||
* 2. Token 管理
|
||||
* - 代理服务器维护账号池的所有 token
|
||||
* - 根据各账号的额度情况动态选择
|
||||
*
|
||||
* 3. 切换策略
|
||||
* - 自动模式:当前账号额度 < 阈值时自动切换
|
||||
* - 手动模式:用户手动选择要使用的账号
|
||||
*
|
||||
* 4. 本地写入的数据
|
||||
* - accessToken: 代理服务器生成的特殊 token
|
||||
* - refreshToken: 用于刷新代理 token
|
||||
* - 设备 ID: 统一使用代理分配的 ID,避免被检测
|
||||
*/
|
||||
|
||||
const seamlessModule = {
|
||||
getSeamlessStatus,
|
||||
getSeamlessConfig,
|
||||
updateSeamlessConfig,
|
||||
getUserSwitchStatus,
|
||||
injectSeamless,
|
||||
restoreSeamless,
|
||||
getSeamlessAccounts,
|
||||
syncSeamlessAccounts,
|
||||
getSeamlessToken,
|
||||
switchSeamlessToken
|
||||
};
|
||||
|
||||
module.exports = seamlessModule;
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cursorpro",
|
||||
"displayName": "CursorPro",
|
||||
"displayName": "蜂鸟Pro",
|
||||
"description": "Cursor 账号管理与换号工具",
|
||||
"version": "0.4.5",
|
||||
"publisher": "cursorpro",
|
||||
"publisher": "fnpro",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cursorpro/cursorpro-extension"
|
||||
"url": "https://github.com/fnpro/fnpro-extension"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.80.0"
|
||||
@@ -25,18 +25,18 @@
|
||||
"commands": [
|
||||
{
|
||||
"command": "cursorpro.showPanel",
|
||||
"title": "CursorPro: 打开控制面板"
|
||||
"title": "蜂鸟Pro: 打开控制面板"
|
||||
},
|
||||
{
|
||||
"command": "cursorpro.switchAccount",
|
||||
"title": "CursorPro: 立即换号"
|
||||
"title": "蜂鸟Pro: 立即换号"
|
||||
}
|
||||
],
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "cursorpro-sidebar",
|
||||
"title": "CursorPro",
|
||||
"title": "蜂鸟Pro",
|
||||
"icon": "media/icon.svg"
|
||||
}
|
||||
]
|
||||
@@ -51,7 +51,7 @@
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "CursorPro",
|
||||
"title": "蜂鸟Pro",
|
||||
"properties": {
|
||||
"cursorpro.cursorPath": {
|
||||
"type": "string",
|
||||
@@ -62,7 +62,7 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"vscode:prepublish": "echo skip",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"lint": "eslint src --ext ts"
|
||||
|
||||
Reference in New Issue
Block a user