这是一个云手机远程控制服务。POST 请求需要 HMAC-SHA256 签名。
Token 由手机端 App 生成,启动后自动复制到剪贴板
curl "https://YOUR_URL/api/client?token=YOUR_TOKEN" -o cloudmanager.py
脚本里自动填入了 URL、Token、签名密钥,可以直接运行。
python3 cloudmanager.py
或在自己的代码里 import:
from cloudmanager import CloudManagerClient
c = CloudManagerClient()
print(c.status()) # 查看状态
print(c.shell("id")) # 执行命令
c.tap(540, 1170) # 点击屏幕
c.keyboard_text("hello") # 键盘输入
c.screenshot() # 截图
POST 请求必须带签名头。签名密钥从 token 派生,无需额外接口获取。
import hmac, hashlib, time, secrets
# 1. 从 token 派生签名密钥 (无需请求服务端)
sign_key = hmac.new(
YOUR_TOKEN.encode(),
b"cloudmanager-signing-v1",
hashlib.sha256
).hexdigest()
# 2. 每次 POST 请求生成签名
timestamp = str(int(time.time() * 1000)) # 毫秒级时间戳
nonce = secrets.token_hex(16) # 随机数
body = '{"cmd": "echo hi"}' # 请求体 JSON 字符串
body_hash = hashlib.sha256(body.encode()).hexdigest()
sign_data = f"POST{path}{timestamp}{nonce}{body_hash}"
# path 不含 query string,如 "/api/shell"
signature = hmac.new(sign_key.encode(), sign_data.encode(), hashlib.sha256).hexdigest()
# 3. 发送请求 (注意: 不需要 X-Body-Hash 头,服务端自己算 sha256)
headers = {
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json",
"X-Timestamp": timestamp,
"X-Nonce": nonce,
"X-Signature": signature,
}
POST /api/shell with body
GET /api/status 公开,返回在线状态 GET /api/devices 需要 token,列出 ADB 设备 POST /api/shell 需要 token + 签名,执行 root shell 命令 POST /api/command 需要 token + 签名,执行 ADB 命令 POST /api/screenshot 需要 token + 签名,截图 (PNG base64) POST /api/input 需要 token + 签名,输入操作 (tap/swipe/text/keyevent) POST /text 需要 token + 签名,中文输入 (剪贴板) POST /gesture 需要 token + 签名,手势 (scroll/fling) POST /multi 需要 token + 签名,多点触控 POST /file/upload 需要 token + 签名,上传文件 (base64) POST /file/download 需要 token + 签名,下载文件 (自动分块 >600KB) POST /file/download/chunk 需要 token + 签名,下载分块
POST /api/ai/connect 需要 token + 签名,AI 接入 (自动分配 codename)
body: {"clientInfo":"my-ai-bot"}
返回: {"sessionId":"ai-1","codename":"AI-1","heartbeatTimeoutSec":90}
POST /api/ai/heartbeat 保活, 每 25s 调用一次, 90s 不调自动断开
POST /api/ai/disconnect 主动断开
POST /api/ai/status 查询所有 AI 会话
POST /api/ai/chat/send AI 发消息给用户 (text/choice/task_output)
body: {"sessionId":"ai-1","type":"text","content":"你好","choices":["选项1","选项2"]}
POST /api/ai/chat/user-send 用户发消息给 AI (App 内部用)
POST /api/ai/chat/choice 用户回复选择题
POST /api/ai/chat/history 拉取聊天历史 (轮询模式)
body: {"sessionId":"ai-1","limit":50,"sinceId":0}
POST /api/ai/chat/clear 清空聊天记录
POST /api/ai/chat/delete 删除单条消息
POST /api/ai/chat/export 导出聊天记录
GET /api/ai/chat/stream SSE 长连接, 实时接收用户消息 (需 token)
POST /api/mcp/connect 连接 MCP 服务 (标准 JSON-RPC initialize 握手)
body: {"url":"http://127.0.0.1:8787/mcp"}
或: {"url":"builtin://local"} (内置工具集 fallback)
返回: {"sessionId":"...","serverInfo":{"name":"MT APK MCP","version":"0.1.0"}}
POST /api/mcp/tools 列出工具 (tools/list)
body: {"url":"http://127.0.0.1:8787/mcp"}
POST /api/mcp/call 调用工具 (tools/call)
body: {"url":"...","tool":"mt_apk_search","args":{"workspaceId":"xxx","query":"vip",...}}
POST /api/mcp/discover 自动发现本地 MCP 服务 (扫描 8787/3001/8080 等端口)
POST /api/mcp/list 列出已连接的 MCP 服务
POST /api/mcp/disconnect 断开 MCP 服务
安全: 只允许本地 (127.0.0.1) 和局域网 (10.x/192.168.x/172.16-31.x), 禁止公网
已验证: 可连接 MT管理器 MCP (17 个 APK 逆向工具: mt_apk_open/search/read_text/edit/build)
- HMAC-SHA256 签名验证 (所有 POST 请求) - 时间戳防重放 (5 分钟有效期) - Nonce 防重放 (5 分钟去重) - IP 频率限制 (60/min, unknown 180/min) - IP 黑名单 (失败 10 次封禁 30 分钟) - Token 加密存储 (EncryptedSharedPreferences) - signKey 从 Token 派生 (HKDF), 不持久化 - v58: 日志脱敏 (LogSanitizer) - token/secret/password/apikey → ***REDACTED*** - Bearer xxx → Bearer ***REDACTED*** - --network-secret 'xxx' → --network-secret '***' - /api/logs 输出前再次脱敏 (兜底)
1. SSH (serveo.net) 默认, 免配置, 随机域名
2. Tailscale Funnel 固定域名 cloudphone.tail7369aa.ts.net
3. Cloudflare Tunnel 最快, 随机域名
4. EasyTier (推荐) 去中心化 mesh VPN, 延迟最低
POST /api/easytier/start {"auto":true} 一键自动配置
POST /api/easytier/connect-info 获取连接信息
AI 端: easytier-core --no-tun --network-name 'xxx' --network-secret 'yyy' \
-p 'tcp://39.98.83.46:51010' --socks5 1080
然后: curl --socks5 127.0.0.1:1080 http://10.144.144.100:3000/api/status
- App 聊天 Tab: 和 AI 实时对话 - AI 接入后显示在 "在线" 列表 - 支持: 文本/选择题/任务输出/系统消息 - 长按消息: 删除 - 导出聊天记录 (通过 API) - v59: 修复输入框第一字符重复 bug (TextFieldValue + composition 严格检测) - v58.1: 修复中文消息发送阻塞 bug (字节流读取)
GET /api/logs 需要 token,被动拉取日志
参数: limit (默认 200, max 1000)
since (毫秒时间戳,只返回此时间之后的)
level (DEBUG/INFO/WARN/ERROR)
tag (按 tag 过滤,如 HttpServer/Security)
响应: {"ok":true,"data":{"logs":[...],"count":N}}
GET /api/logs/stream 需要 token,SSE 主动推送 (长连接)
参数: 同 /api/logs + follow (默认 true,false=只推历史后断开)
响应: text/event-stream,每条日志一个 data: 行
示例:
curl -N -H "Authorization: Bearer TOKEN" \\
"https://URL/api/logs/stream?level=WARN&follow=true"
v63: AI 自由编写 10 种语言脚本, 永久保存, 标签分类, 版本管理, 安全沙箱, 内置工具包。
统一存储路径: /data/local/tmp/cm_plugins/
scripts/ 脚本文件 meta/ 元数据 (JSON) versions/ 旧版本备份 sandbox/ 沙箱工作目录 builtin/ 内置通用工具
shell python js ruby lua php perl go rust java frida(动态分析) c
POST /api/plugins/install 安装/更新插件 (含标签+沙箱+版本)
body: {"name":"vip_unlock","type":"python","description":"VIP解锁模板",
"code":"#!/usr/bin/env python\n...","author":"glm-4.6",
"tags":["逆向","vip","通用"],"sandbox":true}
响应: {"ok":true,"data":{"name":"vip_unlock","version":1,"tags":[...],"sandbox":true}}
POST /api/plugins/list 列出所有插件 + 内置工具
响应: {"ok":true,"data":{"plugins":[...],"builtins":[...],"count":N,"builtin_count":M}}
POST /api/plugins/get 获取插件详情 (含代码)
body: {"name":"vip_unlock"}
POST /api/plugins/run 运行插件 (自动沙箱)
body: {"name":"vip_unlock","args":{"package":"com.xxx"},"timeout":30000}
响应: {"ok":true,"data":{"exitCode":0,"stdout":"...","sandbox_dir":"..."}}
POST /api/plugins/delete 删除插件 (含版本+沙箱)
POST /api/plugins/clear 清空所有插件
POST /api/plugins/clear_sandbox 清空沙箱目录
POST /api/plugins/search 按标签搜索
body: {"tag":"逆向"}
响应: {"ok":true,"data":{"plugins":[...],"count":N}}
POST /api/plugins/tags 列出所有标签
响应: {"ok":true,"data":{"tags":["逆向","vip","内存","抓包"],"count":4}}
POST /api/plugins/versions 列出插件所有版本
body: {"name":"vip_unlock"}
响应: {"ok":true,"data":{"versions":[{"version":1,"current":false},{"version":2,"current":true}]}}
POST /api/plugins/rollback 回滚到旧版本
body: {"name":"vip_unlock","version":1}
响应: {"ok":true,"data":{"rolled_back_to":1,"backup_of":2}}
POST /api/plugins/diff 对比两个版本
body: {"name":"vip_unlock","v1":1,"v2":2}
响应: {"ok":true,"data":{"diff":"..."}}
apk_dump_dex dump 加固 APK 的真实 dex (frida-dexdump) args: APK_PATH=/data/app/xxx.apk memory_scan 内存搜索 (找 VIP 标志位) args: PID=12345 PATTERN=isVip smali_patcher smali 修改重打包 (提示用 MT管理器 MCP) args: APK=xxx.apk METHOD=isVIP NEW_CODE="const/4 v0, 0x1" frida_tracer Frida 自动 trace 关键方法 args: PACKAGE=com.xxx METHODS=isVip,isPremium crypto_toolkit 加解密 (md5/sha256/aes_enc/aes_dec) args: ACTION=md5 DATA=hello [KEY=xxx] network_capture 网络抓包 (tcpdump) args: DURATION=30 PORT=443
- 名称校验: ^[a-zA-Z][a-zA-Z0-9_]{1,63}$ (防路径穿越)
- 代码限制: max 1MB
- 路径校验: script_path 必须在 scripts/ 或 builtin/ 下
- 沙箱: 默认在 sandbox// 运行, 隔离文件操作
- 超时: 1s ~ 5 分钟 (可配)
- 参数传递: env CM_ARG_ (base64 解码, 防 shell 注入)
- 审计日志: 所有插件调用记录到 LogBuffer
- v65 智能解释器查找: 自动检测 4 个路径
/system/bin → /system/xbin → /data/local/tmp/tbin → /data/data/com.termux/files/usr/bin
C 语言: gcc 优先, clang 备选, cc 兜底
不存在时返回友好错误 + 安装提示
封装常见操作,一行调用完成,返回结构化 JSON,不用拼 shell 命令。
POST /api/process/list 列出所有进程 (PID/PPID/USER/RSS/STAT/NAME)
body: {"filter":"frida"} 可选,按名字过滤
响应: {"ok":true,"data":{"processes":[{"pid":123,"name":"frida-server",...}],"count":1}}
POST /api/process/kill 杀进程
body: {"pid":123,"signal":"KILL"} signal 可选: TERM/KILL/HUP/INT (默认 TERM)
POST /api/apps/list 列出应用
body: {"type":"third"} type 可选: third/system/all (默认 all)
POST /api/apps/info 应用详情 (version/uid/dataDir/apkPath)
body: {"package":"com.tencent.mm"}
POST /api/apps/launch 启动应用
body: {"package":"com.tencent.mm","activity":"com.tencent.mm.ui.LauncherUI"}
activity 可选,不填用默认 LAUNCHER
POST /api/apps/force-stop 强制停止
body: {"package":"com.tencent.mm"}
POST /api/apps/uninstall 卸载
body: {"package":"com.tencent.mm"}
POST /api/apps/clear-data 清除应用数据
body: {"package":"com.tencent.mm"}
POST /api/file/ls 列目录 (返回 JSON 结构)
body: {"path":"/data/local/tmp","hidden":false}
POST /api/file/cp 复制 body: {"src":"...","dst":"..."}
POST /api/file/mv 移动 body: {"src":"...","dst":"..."}
POST /api/file/rm 删除 body: {"path":"...","recursive":true}
POST /api/file/mkdir 建目录 body: {"path":"..."}
POST /api/file/chmod 改权限 body: {"path":"...","mode":"755"}
POST /api/net/connections 所有 TCP 连接 (含 PID/进程名) POST /api/net/listen-ports 监听端口 POST /api/net/interfaces 网络接口 (IP/MAC/状态) POST /api/net/routes 路由表
POST /api/system/reboot 重启
POST /api/system/shutdown 关机
POST /api/system/lock 锁屏
POST /api/system/brightness 亮度 body: {"level":128} (0-255)
POST /api/system/volume 音量 body: {"stream":"media","level":8}
stream: media/ring/notification/alarm/system
POST /api/system/battery 电池状态 (level/temp/voltage/charging)
POST /api/system/info 系统信息 (model/android/abi/build/meminfo/disk)
POST /api/ui/dump dump UI 树,返回所有可见元素 (text/resourceId/bounds)
响应: {"ok":true,"data":{"elements":[{"text":"登录","bounds":{"cx":540,"cy":1200},...}]}}
POST /api/ui/click-text 按文本点击 (自动找元素+点中心)
body: {"text":"登录","partial":true}
partial=true 表示包含匹配 (默认),false=精确匹配
POST /api/ui/wait-text 等待文本出现 (轮询)
body: {"text":"加载完成","timeout":10000}
返回: {"found":true,"elapsed_ms":2340}
POST /api/setup/tools 下载预编译工具到 /data/local/tmp/tools/
body: {"tools":["frida-server","nmap","sqlmap","mitmproxy"]}
用 App 内置 HttpURLConnection 下载,不依赖 curl/wget
自动解压 .xz 文件
支持的工具:
- frida-server (Frida Hook 服务端)
- nmap (网络扫描)
- sqlmap (SQL 注入)
- mitmproxy (HTTPS MITM 代理)
# 之前 (要拼 shell 命令,效率低):
POST /api/shell
body: 用 ps + grep + awk + xargs kill 拼命令
# 现在 (一行 API 调用,返回结构化 JSON):
POST /api/process/kill
body: {"pid":1234,"signal":"KILL"}
# 之前 (找按钮点击要 dump + 算坐标):
POST /api/shell
body: 用 uiautomator dump + 解析 XML + 算坐标 + input tap
# 现在 (自动找元素+点击):
POST /api/ui/click-text
body: {"text":"登录"}
# 之前 (装工具要找 curl/wget):
POST /api/shell
body: 用 curl -o 下载 (但手机没 curl)
# 现在 (App 内置下载):
POST /api/setup/tools
body: {"tools":["frida-server"]}
1. 安装 tcpdump 抓包插件:
POST /api/plugins/install
body: {"name":"tcpdump_capture","type":"shell",
"description":"抓包 30s 到 /data/local/tmp/cap.pcap",
"code":"#!/system/bin/sh\\ntcpdump -i any -w /data/local/tmp/cap.pcap -s 0 -G 30 -W 1"}
2. 运行插件:
POST /api/plugins/run
body: {"name":"tcpdump_capture","timeout":60000}
3. 下载抓包文件:
POST /file/download
body: {"path":"/data/local/tmp/cap.pcap"}
所有插件运行时,通过 args 传入的参数会变成环境变量: CM_ARG_= 例如: args={"url":"https://api.example.com","count":"3"} 脚本里读: CM_ARG_URL / CM_ARG_COUNT (Bourne shell 语法)
云机管家内置 Alpine Linux chroot,可执行完整 Linux 命令 (python3/gcc/node/sqlite 等)。
POST /api/linux/setup 初始化 Alpine rootfs (首次约 30s, 3MB 下载)
自动: 下载 Alpine 3.19.7 aarch64 mini rootfs + SHA256 校验 + 解压到 /data/local/tmp/cm_linux/
自动: mount /proc /sys /dev (用完 umount)
POST /api/linux/exec 在 chroot 内执行 Linux 命令
body: {"cmd":"python3 -c 'import sys; print(sys.version)'","timeout":15000}
响应: {"ok":true,"data":{"exitCode":0,"stdout":"Python 3.x.x","stderr":"","timedOut":false}}
POST /api/linux/install 用 apk 包管理器安装包 (apk add)
body: {"packages":"python3 py3-requests gcc nodejs-current"}
响应: {"ok":true,"data":{"exitCode":0,"stdout":"Installing..."}}
POST /api/linux/status 查看 chroot 状态 (ready/rootfs路径/已装工具)
POST /api/linux/destroy 销毁 rootfs (清理 ~50MB 空间)
示例:
# 1. 初始化
POST /api/linux/setup {}
# 2. 执行 python3
POST /api/linux/exec {"cmd":"python3 -c 'import requests; print(requests.get(\"https://api.github.com\").status_code)'"}
# 3. 装 sqlite3
POST /api/linux/install {"packages":"sqlite"}
# 4. 用 sqlite3 操作数据库
POST /api/linux/exec {"cmd":"sqlite3 /tmp/test.db 'CREATE TABLE t(id INTEGER); INSERT INTO t VALUES(1); SELECT * FROM t;'"}
特性:
- chroot 隔离, 不影响 Android 系统
- mount/umount 严格配对 (防止泄露)
- 命令通过临时 .sh 文件传递 (v74.2 修复, 避免引号嵌套问题)
- PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
- DNS 自动从 Android 复制
- 配置文件 chmod 600, root 可读
如果手机装了 Termux,可以用 Termux 的解释器 (无需 chroot)。
POST /api/termux/setup 检查/初始化 Termux (装 bash/coreutils)
POST /api/termux/exec 在 Termux 环境执行命令
body: {"command":"pip install requests","timeout":30000}
响应: {"ok":true,"data":{"exitCode":0,"stdout":"...","stderr":"","timedOut":false}}
POST /api/termux/pkg-install 装 Termux 包 (pkg install)
body: {"packages":"python nodejs ruby"}
POST /api/termux/status 查看 Termux 是否安装
智能解释器查找 (v65):
按顺序查找: /system/bin → /system/xbin → /data/local/tmp/tbin → Termux usr/bin → Linux chroot
C 语言: gcc 优先, clang 备选, cc 兜底
不存在时返回友好错误 + 安装提示
完整 Frida 工具链: 启动 frida-server, 写脚本, 运行, 枚举 Java 类。
POST /api/frida/start 启动 frida-server (后台守护进程)
POST /api/frida/stop 停止 frida-server
POST /api/frida/status 查看状态 (running/port/binaryPath)
POST /api/frida/script/write 写 Frida 脚本到 /data/local/tmp/frida_scripts/
body: {"name":"hook_isvip","code":"Java.perform(()=>{...})"}
POST /api/frida/script/list 列出所有已保存脚本
POST /api/frida/script/run 运行脚本 (附加到指定 PID 的进程)
body: {"name":"hook_isvip","pid":1234}
注: 字段是 "name" (脚本名) + "pid" (进程 ID), 不是 scriptName/package
先用 /api/shell {"cmd":"pidof com.xxx"} 获取目标进程 PID
POST /api/frida/enumerate 枚举 Java 类 (附加到指定 PID)
body: {"pid":1234}
注: 字段是 "pid", 不是 "type"
返回该进程加载的所有 Java 类列表
Frida Inject (v37, 不需要 Python!):
POST /api/frida2/setup 下载/检查 frida-inject 二进制
POST /api/frida2/inject 注入 .so 或脚本到指定 PID
body: {"pid":1234,"script":"/data/local/tmp/hook.js","timeout":15}
注: 字段是 "pid" + "script" + "timeout" (秒)
POST /api/frida2/hook-so hook native 函数
body: {"pid":1234,"so":"libnative-lib.so","func":"Java_com_xxx_isVip","action":"trace"}
注: 字段是 "pid" + "so" (库名) + "func" (函数名) + "action" (trace/replace)
POST /api/frida2/exports 列 .so 导出符号
body: {"pid":1234,"so":"libnative-lib.so"}
注: 字段是 "pid" + "so"
POST /api/frida2/classes 列 Java 类 (Java.choose)
body: {"pid":1234,"filter":"com.xxx."}
注: 字段是 "pid" + "filter" (可选, 前缀过滤)
完整使用流程:
# 1. 启动 frida-server
POST /api/frida/start {}
# 2. 找目标进程 PID
POST /api/shell {"cmd":"pidof com.tencent.mm"}
# 3. 写 Frida 脚本
POST /api/frida/script/write
{"name":"hook_login","code":"Java.perform(()=>{var L=Java.use('com.xxx.Login');L.isVip.implementation=function(){return true;};});"}
# 4. 运行脚本 (用第 2 步获取的 PID)
POST /api/frida/script/run {"name":"hook_login","pid":12345}
# 5. 枚举 Java 类 (查看可 hook 的类)
POST /api/frida/enumerate {"pid":12345}
# 6. 停止 frida-server
POST /api/frida/stop {}
搜索/读写内存,支持 native heap (LibGDX/Unity 游戏)。
POST /api/mem/read 读内存 (/proc/pid/mem)
body: {"pid":1234,"addr":"0x7fff1234","size":64}
响应: {"ok":true,"data":{"pid":1234,"addr":"0x7fff1234","size":64,"data":"base64...","hex":"ab cd ef..."}}
POST /api/mem/write 写内存 (改游戏数值)
body: {"pid":1234,"addr":"0x7fff1234","data":"base64编码的数据"}
POST /api/mem/search 搜索 dalvik heap (int32/int16/int8/float)
body: {"pid":1234,"value":9999,"type":"int32","maxResults":100}
响应: {"ok":true,"data":{"addresses":["0x...","0x..."],"totalMatches":2}}
POST /api/mem/dump dump 内存块 (大块,1MB 内)
body: {"pid":1234,"addr":"0x7fff0000","size":65536}
POST /api/mem/search-all 搜索所有 rw-p 区域 (含 native heap, scudo)
body: {"pid":1234,"value":9999,"type":"int32","maxResults":100}
POST /api/mem/regions 列出所有内存区域 (按大小排序)
body: {"pid":1234}
响应: {"ok":true,"data":{"regions":[{"sizeMB":64,"start":"0x...","desc":"[anon:scudo:secondary]"}]}}
POST /api/mem/search-string 搜索字符串 (ASCII + UTF-16)
body: {"pid":1234,"keyword":"isVip","maxResults":50}
安全 (v74 修复):
- addrHex 必须匹配 ^(0x)?[0-9a-fA-F]+\$ (防 shell 注入)
- data 必须是合法 base64
- pid 必须 > 0 且 < 4194304
POST /api/decompile/apk 反编译 APK (unzip + 提取 dex/so + 找游戏引擎)
body: {"package":"com.xxx"}
响应: 自动识别 Unity/Cocos2d/LibGDX, 列 dex 文件, strings 搜游戏关键字
POST /api/decompile/dex 分析 dex (strings + 关键字搜索)
body: {"path":"/data/app/com.xxx/base.apk","keyword":"vip"}
响应: {"ok":true,"data":{"results":["com.xxx.VipUtils","isVip:Z"],"count":2}}
安全 (v74 修复):
- dexPath 校验 (只允许 [a-zA-Z0-9._/-], 禁止 ..)
- keyword 校验 (禁止 shell 元字符, 单引号转义)
POST /api/capture 抓包 (tcpdump)
body: {"duration":30,"filter":"port 443"}
响应: {"ok":true,"data":{"pcapPath":"/data/local/tmp/cap_xxx.pcap","output":"..."}}
filter 是 BPF 过滤器 (host/port/tcp/udp/src/dst 等)
POST /api/scan 端口扫描 (用 bash /dev/tcp, 不依赖 nmap)
body: {"host":"192.168.1.1","portStart":1,"portEnd":1000}
响应: {"ok":true,"data":{"openPorts":[22,80,443,8080],"count":4}}
安全 (v74 修复):
- filter 必须是合法 BPF (禁止 shell 元字符, 允许字母数字 () * : <> !=)
- host 校验 (只允许 [a-zA-Z0-9.-])
EasyTier --no-tun + socks5 模式下, HTTP body 默认明文。
加 X-AES-Encrypted: 1 头启用 AES-256-CBC 端到端加密。
算法:
密钥: SHA-256("aes-body-v1:" + token) → 32 字节 (AES-256)
IV: 每次请求随机 16 字节
模式: AES/CBC/PKCS5Padding
body = base64(IV(16) + ciphertext)
请求头:
Authorization: Bearer TOKEN
Content-Type: application/octet-stream
X-Timestamp: 毫秒时间戳
X-Nonce: 32 字符 hex
X-Signature: HMAC-SHA256(signKey, "POST" + path + ts + nonce + sha256(明文body))
X-AES-Encrypted: 1
body: base64(IV + AES-256-CBC(明文 JSON))
注意:
- 签名基于明文 body (服务端先 AES 解密再验签)
- 响应也是 AES 加密的 (同样的 IV+ciphertext 格式)
- 错误响应也可能加密 (401/403 等)
Python 示例:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import hashlib, base64, hmac, secrets, json, time
aes_key = hashlib.sha256(("aes-body-v1:" + TOKEN).encode()).digest()
iv = secrets.token_bytes(16)
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
plain = json.dumps({"cmd": "echo aes_test"})
ct = cipher.encrypt(pad(plain.encode(), 16))
aes_body = base64.b64encode(iv + ct).decode()
# 签名基于明文
body_hash = hashlib.sha256(plain.encode()).hexdigest()
sign_data = f"POST/api/shell{ts}{nonce}{body_hash}"
sig = hmac.new(signKey.encode(), sign_data.encode(), hashlib.sha256).hexdigest()
v74.2 起云手机端 EasyTier 默认用 AES-256-GCM 加密 mesh 流量。
云手机端配置 (自动生成):
[flags]
encryption_algorithm = "aes-256-gcm" ← v74.2 加强
no_tun = true ← 默认 socks5 模式
network-secret:
v74.2 升级为 64 位随机字母数字 (旧版 32 位)
生成器: SecureRandom, 字符集 [a-zA-Z0-9]
AI 端连接命令 (必须加 --encryption-algorithm aes-256-gcm, 否则连不上):
easytier-core --no-tun -i 10.144.144.1 \
--network-name 'cm-xxx' \
--network-secret '64位secret' \
-p 'tcp://39.98.83.46:51010' \
--socks5 1080 \
--encryption-algorithm aes-256-gcm
⚠ 重要: 两端必须用相同加密算法, 否则连接被拒绝 (Connection closed)
- aes-gcm (AES-128-GCM) — EasyTier 默认
- aes-256-gcm (AES-256-GCM) — v74.2 推荐
- chacha20 — 性能优先 (无 AES-NI 时)
- xor — 仅测试用, 不安全
双重加密 (最强):
1. EasyTier mesh 隧道层: aes-256-gcm (网络包加密)
2. HTTP body 应用层: AES-256-CBC (token 派生密钥)
攻击者即使拿到 mesh 节点, 也解不开 HTTP body
POST /multi 多点触控 (批量动作)
body: {
"actions": [
{"type":"swipe","x1":300,"y1":800,"x2":400,"y2":700,"duration":500},
{"type":"tap","x":900,"y":600,"delay":200},
{"type":"long_press","x":800,"y":500,"duration":1000,"delay":0}
]
}
支持类型: tap / swipe / long_press
delay: 动作间延迟 (毫秒)
POST /gesture 单手势 (简单版)
body: {"type":"scroll","x":540,"y":1000,"dy":-300} 滚动
body: {"type":"fling","x1":540,"y1":1500,"x2":540,"y2":500} 快速滑动
POST /api/input 标准输入 (input tap/swipe/keyevent/text)
body: {"action":"tap","x":540,"y":1170}
body: {"action":"swipe","x1":100,"y1":100,"x2":200,"y2":200,"duration":300}
body: {"action":"keyevent","keycode":3} ← HOME 键 (整数, 不是字符串)
body: {"action":"text","text":"hello"}
POST /text 中文输入 (Unicode 转义, 不依赖 IME)
body: {"text":"你好世界"}
原理: input text "\u4f60\u597d\u4e16\u754c"
用以下 Python 代码生成签名客户端 (无需服务端 /api/client 端点):
import hmac, hashlib, time, secrets, json, urllib.request
TOKEN = "your_token"
BASE = "http://10.144.144.100:3000"
# 1. 派生 signKey (和服务端相同算法)
signKey = hmac.new(TOKEN.encode(), b"cloudmanager-signing-v1", hashlib.sha256).hexdigest()
# 2. POST 请求 (带签名)
def post(path, body_dict):
body = json.dumps(body_dict).encode()
ts = str(int(time.time() * 1000))
nonce = secrets.token_hex(16)
body_hash = hashlib.sha256(body).hexdigest()
sign_data = f"POST{path}{ts}{nonce}{body_hash}"
sig = hmac.new(signKey.encode(), sign_data.encode(), hashlib.sha256).hexdigest()
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
"X-Timestamp": ts, "X-Nonce": nonce, "X-Signature": sig,
}
req = urllib.request.Request(BASE + path, data=body, method="POST", headers=headers)
with urllib.request.urlopen(req, timeout=30) as resp:
return resp.read().decode()
# 3. 调用 API
print(post("/api/shell", {"cmd": "id"}))
print(post("/api/apps/list", {}))
print(post("/api/screenshot", {}))
GET 公开:
/ 连接指南 (本文档)
/api/status 服务状态 (version/adb/tunnel)
/remote 远程桌面 (HTML)
GET 需 Token:
/api/devices ADB 设备列表
/api/logs 日志 (支持 limit/since/level/tag 过滤)
/api/logs/stream SSE 日志流 (长连接)
/api/ai/chat/stream SSE 聊天流 (AI 接收用户消息)
POST 需 Token + 签名 (113 个端点):
Shell: /api/shell /api/command /shell
Screenshot: /api/screenshot
Input: /api/input /text /gesture /multi /input
File: /file/upload /file/download /file/download/chunk
/api/file/ls /api/file/cp /api/file/mv /api/file/rm
/api/file/mkdir /api/file/chmod
Apps: /api/apps/list /api/apps/info /api/apps/launch
/api/apps/force-stop /api/apps/uninstall /api/apps/clear-data
Process: /api/process/list /api/process/kill
System: /api/system/reboot /api/system/shutdown /api/system/lock
/api/system/brightness /api/system/volume
/api/system/battery /api/system/info
Net: /api/net/connections /api/net/listen-ports
/api/net/interfaces /api/net/routes
UI: /api/ui/dump /api/ui/click-text /api/ui/wait-text
Memory: /api/mem/read /api/mem/write /api/mem/search /api/mem/dump
/api/mem/search-all /api/mem/regions /api/mem/search-string
Decompile: /api/decompile/apk /api/decompile/dex
Network: /api/capture /api/scan
Frida: /api/frida/start /api/frida/stop /api/frida/status
/api/frida/script/write /api/frida/script/list /api/frida/script/run
/api/frida/enumerate
/api/frida2/setup /api/frida2/inject /api/frida2/hook-so
/api/frida2/exports /api/frida2/classes
Termux: /api/termux/setup /api/termux/exec /api/termux/pkg-install /api/termux/status
Linux: /api/linux/setup /api/linux/exec /api/linux/install
/api/linux/status /api/linux/destroy
EasyTier: /api/easytier/start /api/easytier/stop
/api/easytier/status /api/easytier/connect-info
AI: /api/ai/connect /api/ai/disconnect /api/ai/heartbeat /api/ai/status
/api/ai/chat/send /api/ai/chat/user-send /api/ai/chat/choice
/api/ai/chat/history /api/ai/chat/clear /api/ai/chat/delete
/api/ai/chat/export
Plugins: /api/plugins/list /api/plugins/install /api/plugins/get
/api/plugins/run /api/plugins/delete /api/plugins/clear
/api/plugins/search /api/plugins/tags /api/plugins/rollback
/api/plugins/diff /api/plugins/versions /api/plugins/clear_sandbox
MCP: /api/mcp/list /api/mcp/connect /api/mcp/discover
/api/mcp/tools /api/mcp/call /api/mcp/disconnect
Setup: /api/setup/tools
1. 在云手机打开「云机管家」App, 主页显示状态 2. 切换到「隧道」Tab, 点击「启动 EasyTier」(默认推荐模式) 3. App 自动生成 NetworkName/NetworkSecret/Token, 复制到剪贴板 4. 在本地终端用 EasyTier 连接 (见下方命令) 5. 用 socks5 代理访问 App 的 HTTP API (http://10.144.144.100:3000)
# 1. 启动 EasyTier (用 App 给的 NetworkName/NetworkSecret)
easytier-core --no-tun -i 10.144.144.1 \
--network-name 'cm-XXXXXX' \
--network-secret '64位secret' \
-p 'tcp://39.98.83.46:51010' \
--socks5 1080 \
--encryption-algorithm aes-256-gcm
# 2. 测试连通性 (无需签名, GET /api/status 公开)
curl --socks5 127.0.0.1:1080 http://10.144.144.100:3000/api/status
# 期望: {"ok":true,"data":{"version":"3.42.x",...}}
# 3. 用 Python 客户端调 API (自动签名)
# 从 App 的「/」首页下载: curl "http://10.144.144.100:3000/api/client?token=YOUR_TOKEN"
# 或按本页"签名客户端 (Python) 生成"章节自己写
🏠 主页: - 显示云手机状态 (系统/CPU/内存/电池) - 隧道连接状态 (EasyTier/SSH/Tailscale/Cloudflare) - 快捷操作 (重启/锁屏/截图/录屏) - App 自启/保活设置 💬 聊天: - 与 AI 实时对话 (双向) - 支持文本/选择题/任务输出/系统消息 - AI 接入后显示在「在线」列表 - 长按消息可删除 - v59 修复输入框第一字符重复 bug - v75.4 修复 IME 重复字符 (KeyboardType.Uri) 🧠 Cairn (通用问题求解引擎): - 点击「安装」部署 Cairn (Alpine chroot + Python 3.11 + FastAPI) - 启动 Server (端口 8000) + Dispatcher (任务调度) - 新建项目: 填写标题/目标/已知信息, AI 自动探索 - 项目列表: 查看进度/状态/事实数/Intent数/Hint数 - 每个项目支持: 图谱(查看进度)/Hint(给AI提示)/停止/删除 - 设置: 调整 intent_timeout/reason_timeout 等 - 日志: 查看 server/dispatcher/setup 日志 - v75.21 修复: setup 并发竞态 + proxyToServer 错误响应被当成功 - v75.22 修复: addHint 缺 creator 字段 + prompt_group 默认改 mock - 详细使用方法见下方「🧠 Cairn 详细使用方法」 🛠 工具: - ADB Shell (执行 root 命令) - 文件管理 (ls/cp/mv/rm/mkdir/chmod) - 应用管理 (列表/启动/强制停止/卸载/清数据) - 进程管理 (列表/kill) - 系统控制 (亮度/音量/重启/锁屏) - 网络管理 (连接/端口/接口/路由) - UI 自动化 (dump/click-text/wait-text) - 截图 (全屏/区域选择) - 录屏 (MP4, 可选时长) ⚙️ 设置: - 隧道模式切换 (EasyTier/SSH/Tailscale/Cloudflare) - EasyTier 配置 (Peer/NetworkName/Secret) - 安全设置 (Token 重置/AES Body 加密) - 网络黑名单/IP 限流配置
Step 1: AI 通过 /api/ai/connect 接入
POST /api/ai/connect
body: {"clientInfo":"my-ai-bot"}
返回: {"sessionId":"ai-1","codename":"AI-1","heartbeatTimeoutSec":90}
Step 2: 每 25s 发心跳 (90s 不发自动断开)
POST /api/ai/heartbeat
body: {"sessionId":"ai-1"}
Step 3: AI 主动发消息给用户 (App 聊天界面会显示)
POST /api/ai/chat/send
body: {"sessionId":"ai-1","type":"text","content":"你好"}
type 可选: text / choice (附 choices 数组) / task_output / system
Step 4: 接收用户消息 (两种模式)
模式 A (轮询): POST /api/ai/chat/history {"sessionId":"ai-1","sinceId":0}
模式 B (SSE 长连接): GET /api/ai/chat/stream (需 token)
SSE 推荐长连接, 实时性更好
Step 5: 用完断开
POST /api/ai/disconnect {"sessionId":"ai-1"}
# 完整 Python 客户端示例 (自己实现签名)
import hmac, hashlib, time, secrets, json, urllib.request
TOKEN = "your_token_here"
BASE = "http://10.144.144.100:3000"
signKey = hmac.new(TOKEN.encode(), b"cloudmanager-signing-v1", hashlib.sha256).hexdigest()
def post(path, body_dict):
body = json.dumps(body_dict).encode()
ts = str(int(time.time() * 1000))
nonce = secrets.token_hex(16)
bh = hashlib.sha256(body).hexdigest()
sig = hmac.new(signKey.encode(), f"POST{path}{ts}{nonce}{bh}".encode(), hashlib.sha256).hexdigest()
req = urllib.request.Request(BASE + path, data=body, method="POST", headers={
"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json",
"X-Timestamp": ts, "X-Nonce": nonce, "X-Signature": sig,
})
with urllib.request.urlopen(req, timeout=30) as r:
return json.loads(r.read().decode())
# 截图
r = post("/api/screenshot", {})
print(r["data"]["image"][:50]) # base64 PNG
# 点击屏幕
post("/api/input", {"action":"tap","x":540,"y":1170})
# 输入文字 (英文)
post("/api/input", {"action":"text","text":"hello"})
# 中文输入 (Unicode 转义)
post("/text", {"text":"你好世界"})
# 执行 shell (root)
post("/api/shell", {"cmd":"id"})
# 列出应用
post("/api/apps/list", {"type":"third"})
# 启动应用
post("/api/apps/launch", {"package":"com.tencent.mm"})
# UI 自动化 (按文本点击)
post("/api/ui/click-text", {"text":"登录","partial":true})
# 文件列表
post("/api/file/ls", {"path":"/data/local/tmp"})
Q: curl --socks5 连不上? A: 1) 检查 EasyTier 两端 encryption_algorithm 是否一致 (默认 aes-256-gcm) 2) 检查 NetworkName/NetworkSecret 是否完全一致 3) App 端 EasyTier 是否在运行 (主页看状态) 4) 端口 1080 是否被占用 (lsof -i:1080) Q: API 返回 401 / "Invalid or missing token"? A: 1) 检查 Authorization: Bearer TOKEN 头是否带 2) Token 在 App 设置里查看, 区分大小写 3) Token 重置后旧 Token 立即失效 Q: API 返回 403 / "Invalid signature"? A: 1) signKey 算法: HMAC-SHA256(TOKEN, "cloudmanager-signing-v1").hexdigest() 2) 签名数据格式: "POST" + path + timestamp + nonce + sha256(body) 3) path 不含 query string, 如 "/api/shell" 不是 "/api/shell?foo=1" 4) body 是原始 JSON 字符串的 sha256, 不是 dict Q: API 返回 429 / "Rate limit"? A: IP 限流 60/min (unknown IP 180/min), 失败 10 次封禁 30 分钟 等待或换 IP Q: /api/shell 返回 timeout? A: 1) 命令执行超过 timeout (默认 30s) 2) RootManager shell 进程被卡 (重启 App) 3) 命令等待输入 (确保命令非交互式) Q: Cairn Tab 显示「未部署」? A: 1) 点「安装」按钮, 等待 5 步完成 (~5 分钟) 2) 失败看「日志」Tab 的 setup 日志 3) v75.21 修复了并发安装竞态, 不再会损坏 rootfs Q: EasyTier 启动失败? A: 1) 看主页状态栏错误提示 2) 网络不通: 试 SSH/Tailscale 模式 3) Peer 不可达: 改用其他 Peer (39.98.83.46:51010) Q: 聊天 Tab 输入框第一字符重复 (如「我我说」)? A: v59 已修复 (TextFieldValue + composition 严格检测) v75.4 进一步修复 IME 重复 (KeyboardType.Uri) 仍有问题: 切换系统输入法 (建议 Gboard) Q: AI 接入后 90s 自动断开? A: 必须每 25s 发一次 /api/ai/heartbeat 或用 SSE 长连接 (GET /api/ai/chat/stream), 心跳自动维持
1. Token 安全:
- 32 位随机字符 (v33 用 SecureRandom, 不是 Random)
- 加密存储 (EncryptedSharedPreferences, AES-256-GCM)
- 日志自动脱敏 (token/secret/password → ***REDACTED***)
- 错误 10 次自动封禁 IP 30 分钟
2. 通信安全:
- POST 强制 HMAC-SHA256 签名 (防篡改)
- 时间戳 5 分钟容差 (防重放)
- Nonce 5 分钟去重 (防重放)
- IP 频率限制 60/min (unknown IP 180/min)
- 路径白名单 (防穿越)
- shell 命令注入防护 (v74 修复 7 处)
3. EasyTier 安全 (v74.2):
- network-secret 64 位随机 (升级, 旧版 32 位)
- AES-256-GCM 加密 mesh 流量 (升级, 旧版默认 aes-gcm)
- secret 不出现在命令行 (用 -c config.toml)
- 配置文件 chmod 600 (只 root 可读)
- 停止时清理所有残留文件 (config/log/state)
4. AES Body 加密 (v73):
- AES-256-CBC, 密钥从 token 派生 (SHA-256("aes-body-v1:" + token))
- 每次请求随机 IV (16 字节)
- 签名基于明文 body (服务端先解密再验签)
- 响应也加密 (X-AES-Encrypted: 1 时)
5. 推荐配置 (最高安全):
- 隧道: EasyTier --encryption-algorithm aes-256-gcm
- 应用: X-AES-Encrypted: 1 (AES-256-CBC body)
- 攻击者即使破解 mesh, 也解不开 HTTP body
Cairn 是一个通用问题求解引擎 (General Purpose Problem Solving Engine),
设计思路类似 ReAct/Tree-of-Thoughts + 工具调用:
- 你给它一个目标和已知信息 (origin)
- 它通过 Intent (意图) → Fact (事实) 的迭代逐步逼近目标
- 每个 Intent 由 Worker (AI) 执行, 产出 Fact
- Fact 累积到一定程度后, Worker 判定目标达成 → 输出 complete
架构:
App (Android, 端口 3000) ←─签名的 HTTP API─→ AI/用户
↓
Cairn Server (chroot, 端口 8000) ← FastAPI + SQLite 存储项目/facts/intents
↓
Dispatcher (chroot, 单独进程) ← 调度 worker 执行 intent
↓
Worker (mock / claudecode / codex / pi) ← 实际执行 AI 任务
注意: 默认只有 mock-worker, 它只输出假数据用于测试调度链路.
要真正执行任务需要配置 claudecode/codex/pi worker (需对应 CLI 工具).
Step 1: 打开 Cairn Tab, 点击「安装 Cairn」(首次 ~5 分钟)
- 下载 Alpine 3.19.7 rootfs (3MB)
- 安装 Python 3.11 + pip + pydantic-core + docker 等依赖
- 解压 Cairn 源码到 /opt/cairn/Cairn-main/
- 生成 /etc/cairn-dispatch.yaml 配置
Step 2: 启动 Server (端口 8000)
- App 内点「启动 Server」按钮
- 后台启动 uvicorn (FastAPI)
- 5s 后刷新状态确认 serverRunning=true
Step 3: 启动 Dispatcher
- App 内点「启动 Dispatcher」按钮
- 后台启动 cairn dispatch --config /etc/cairn-dispatch.yaml
- 3s 后刷新状态确认 dispatcherRunning=true
Step 4: 新建项目
- 点「➕ 新建」按钮
- 填写:
标题: 简短描述 (如「渗透测试网站 A」)
目标 Goal: 想达成什么 (如「获取 admin 权限」)
已知信息 Origin: 已知条件 (如「目标是 https://example.com」)
- 勾选「启用 Bootstrap」(推荐, 让 AI 先理解任务)
Step 5: 跟踪进度
- 项目列表显示: 状态/事实数/Intent数/Hint数
- 点「图谱」: 查看事实和意图的关系图 (YAML 格式)
- 点「Hint」: 给 AI 提供额外提示 (如「试试 SQL 注入」)
- 点「停止」: 暂停项目 (可重新打开)
- 点「删除」: 删除项目
- 点「📋」: 查看 dispatcher/server/setup 日志
import hmac, hashlib, time, secrets, json, urllib.request
TOKEN = "your_token"
BASE = "https://your-app-url"
signKey = hmac.new(TOKEN.encode(), b"cloudmanager-signing-v1", hashlib.sha256).hexdigest()
def post(path, body_dict):
body = json.dumps(body_dict).encode()
ts = str(int(time.time() * 1000))
nonce = secrets.token_hex(16)
bh = hashlib.sha256(body).hexdigest()
sig = hmac.new(signKey.encode(), f"POST{path}{ts}{nonce}{bh}".encode(), hashlib.sha256).hexdigest()
req = urllib.request.Request(BASE + path, data=body, method="POST", headers={
"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json",
"X-Timestamp": ts, "X-Nonce": nonce, "X-Signature": sig,
})
with urllib.request.urlopen(req, timeout=30) as r:
return json.loads(r.read().decode())
# 1. 检查状态
print(post("/api/cairn/status", {}))
# {"deployed":true, "serverRunning":true, "dispatcherRunning":true, ...}
# 2. 部署 (首次)
print(post("/api/cairn/setup", {}))
# 3. 启动 Server + Dispatcher
print(post("/api/cairn/start-server", {}))
print(post("/api/cairn/start-dispatcher", {}))
# 4. 新建项目
r = post("/api/cairn/projects/create", {
"title": "渗透测试 example.com",
"goal": "找出 example.com 的真实可利用漏洞, 输出 PoC",
"origin": "目标是 https://example.com, 已知是 WordPress 5.8"
})
project_id = r["data"]["project"]["id"]
print(f"Project ID: {project_id}")
# 5. 查询项目进度
print(post("/api/cairn/projects/get", {"projectId": project_id}))
# 6. 给项目添加提示
print(post("/api/cairn/projects/hint", {
"projectId": project_id,
"content": "试试 wp-content/plugins/ 目录扫描"
}))
# 7. 查看图谱 (事实-意图关系)
print(post("/api/cairn/projects/graph", {"projectId": project_id}))
# 8. 停止/删除项目
print(post("/api/cairn/projects/stop", {"projectId": project_id}))
print(post("/api/cairn/projects/delete", {"projectId": project_id}))
默认 cairn-dispatch.yaml 只配了 mock-worker-1, 它只输出假数据.
要真正执行任务, 需要编辑 /data/local/tmp/cm_linux/etc/cairn-dispatch.yaml:
workers:
# mock worker (默认, 用于测试)
- name: "mock-worker-1"
type: "mock"
task_types: [reason, explore, bootstrap]
max_running: 1
priority: 0
# Claude Code worker (需 claude CLI)
- name: "claude-worker-1"
type: "claudecode"
task_types: [reason, explore, bootstrap]
max_running: 1
priority: 10 # 优先级更高
env:
ANTHROPIC_BASE_URL: "https://api.anthropic.com"
ANTHROPIC_AUTH_TOKEN: "sk-ant-xxx"
ANTHROPIC_MODEL: "claude-sonnet-4-5-20250929"
# OpenAI Codex worker (需 codex CLI)
- name: "codex-worker-1"
type: "codex"
task_types: [reason, explore, bootstrap]
max_running: 1
priority: 5
env:
OPENAI_API_KEY: "sk-xxx"
OPENAI_MODEL: "gpt-5"
修改后重启 dispatcher:
POST /api/cairn/stop-dispatcher {}
POST /api/cairn/start-dispatcher {}
Project (项目): - 一个独立的问题求解任务 - 状态: active (运行中) / stopped (已停止) / completed (已完成) - 包含 origin (已知信息) + goal (目标) + facts + intents + hints Fact (事实): - Worker 执行 Intent 后产出的确认结果 - 例: 「找到 SQL 注入点: /api/users?id=1」 - 是项目知识的累积 Intent (意图): - 一个待执行的任务步骤 - 从一个或多个 Fact 出发, 目标是产出新 Fact - 状态: open (待执行) / claimed (worker认领) / concluded (已完成) - 类型: bootstrap (初始理解) / reason (推理) / explore (探索) Hint (提示): - 用户给 AI 的额外指导 - 例: 「试试 SQL 注入」「检查 /admin 路径」 - 不强制 AI 执行, 但会影响后续推理 Bootstrap (引导): - 项目创建后第一个 Intent - AI 理解 origin + goal, 决定如何开始 - 完成后产出第一个 Fact Worker (执行者): - 实际执行 Intent 的实体 - 类型: mock (假数据) / claudecode (Claude) / codex (GPT) / pi (本地) - 配置在 cairn-dispatch.yaml
场景: 对 https://target.example.com 做黑盒渗透
Step 1: 创建项目
POST /api/cairn/projects/create
{
"title": "渗透测试 target.example.com",
"goal": "对 https://target.example.com 做黑盒渗透, 找出真实可利用漏洞
(SQL注入/XSS/SSRF/路径穿越/认证绕过等), 输出可复现 PoC",
"origin": "目标: https://target.example.com
已知: Web 服务, 可能用 Nginx + PHP
限制: 仅授权测试, 不破坏数据"
}
Step 2: 等待 bootstrap 完成 (Dispatcher 自动调度)
轮询 GET /api/cairn/projects/get
等 intents[0].concluded_at != null
Step 3: 给 AI 工具访问权限
- Cairn Worker 在 chroot 内运行, 有 curl/python3/gcc 等工具
- 但访问外网需要 chroot 内能 DNS 解析
- 一般 Cairn Server 自动配置 DNS (从 Android 复制 /etc/resolv.conf)
Step 4: 添加 Hint 引导 AI
POST /api/cairn/projects/hint
{"projectId": "xxx", "content": "先用 nmap 扫端口, 再用 nuclei 扫漏洞"}
Step 5: 等待完成
- 项目状态变 completed 时, 最终 Fact 包含漏洞报告
- 用 /api/cairn/projects/graph 查看完整推理过程
注意: 实际执行能力取决于 Worker 类型:
- mock: 不执行任何真实操作, 只输出假数据
- claudecode/codex: 真实调用 AI API, AI 决定执行什么命令
- 当前云手机默认只配 mock, 要真实渗透需配置 AI Worker
状态/管理:
POST /api/cairn/status 查看部署/Server/Dispatcher 状态
POST /api/cairn/setup 部署 Cairn (首次)
POST /api/cairn/progress 查看安装进度
POST /api/cairn/start-server 启动 Server
POST /api/cairn/stop-server 停止 Server
POST /api/cairn/start-dispatcher 启动 Dispatcher
POST /api/cairn/stop-dispatcher 停止 Dispatcher
项目管理:
POST /api/cairn/projects 列出所有项目
POST /api/cairn/projects/create 新建项目
POST /api/cairn/projects/get 查询项目详情
POST /api/cairn/projects/delete 删除项目
POST /api/cairn/projects/stop 停止项目
POST /api/cairn/projects/complete 标记完成
POST /api/cairn/projects/reopen 重新打开
POST /api/cairn/projects/title 修改标题
POST /api/cairn/projects/hint 添加提示
POST /api/cairn/projects/graph 查看图谱 (YAML)
日志:
POST /api/cairn/logs 查看日志
body: {"which": "server|dispatcher|setup", "lines": 100}
设置:
POST /api/cairn/settings 查看/修改设置
Q: Cairn Tab 显示「未部署」?
A: 点「安装」按钮, 等 5 步完成 (~5 分钟)
失败看「日志」Tab 的 setup 日志
v75.21 修复了并发安装竞态
Q: bootstrap 一直失败, 日志报 "mock setup failed: Expecting value"?
A: v75.22 修复: prompt_group 应为 "mock" (之前默认 "default" 导致 prompt 不匹配)
手动修复: 编辑 /data/local/tmp/cm_linux/etc/cairn-dispatch.yaml
把 prompt_group: "default" 改为 prompt_group: "mock"
重启 Dispatcher
Q: 项目一直 active 不完成?
A: mock-worker 默认 bootstrap 后会 complete, 但 reason/explore 阶段会无限循环
要真正完成项目需要配置真实 AI Worker (claudecode/codex)
Q: 怎么让 Cairn 真的执行命令?
A: 1) 配置真实 Worker (claudecode/codex/pi)
2) Worker 在 chroot 内运行, 有 curl/python3 等工具
3) Worker 输出 JSON: {"accepted":true,"data":{"fact":{"description":"..."},"complete":{"description":"..."}}}
Q: addHint 返回 422 "Field required"?
A: v75.22 修复: addHint 缺 creator 字段 (Cairn Server 要求)
之前 v75.21 的 proxyToServer bug 把 422 当成功, 暴露了这个 bug
Q: /api/cairn/projects/delete 返回错误?
A: DELETE 请求 Cairn Server 返回 204 空响应
v75.21 的 proxyToServer 已修复: 不再把空响应当成功
但代码可能仍报 "解析 Cairn 响应失败" (因为空 body 不是合法 JSON)
实际项目已删除, 可忽略错误