Init: Migrate SimpleRemoter (Since v1.3.1) to Gitea
This commit is contained in:
614
docs/AgentManual.md
Normal file
614
docs/AgentManual.md
Normal file
@@ -0,0 +1,614 @@
|
||||
# YAMA 代理商运营手册
|
||||
|
||||
> 下级授权管理与 FRP 代理配置
|
||||
|
||||
---
|
||||
|
||||
## 目标读者
|
||||
|
||||
- 有下级客户需要管理的代理商
|
||||
- 需要为下级生成授权
|
||||
- 需要提供 FRP 代理服务
|
||||
|
||||
> 基础功能请参阅[日常使用手册](UserManual.md)
|
||||
|
||||
---
|
||||
|
||||
## 第一部分:代理商角色
|
||||
|
||||
### 1. 您的角色定位
|
||||
|
||||
作为代理商,您在授权链中扮演中间角色:
|
||||
|
||||
```
|
||||
您的上级(为您提供授权)
|
||||
│
|
||||
↓
|
||||
您(代理商)
|
||||
│
|
||||
↓
|
||||
您的下级(您为他们提供授权和服务)
|
||||
```
|
||||
|
||||
**您的职责:**
|
||||
- 管理您的下级客户
|
||||
- 为下级生成和分发授权
|
||||
- 可选:为下级提供 FRP 代理服务
|
||||
- 提供技术支持和售后服务
|
||||
|
||||
### 2. 权限范围
|
||||
|
||||
您为下级授权时,受以下限制:
|
||||
|
||||
| 限制项 | 规则 |
|
||||
|--------|------|
|
||||
| 有效期 | 不超过您自己的有效期 |
|
||||
| 并发数限制 | 不超过上级为您设置的并发数限制 |
|
||||
| 功能权限 | 不超过您被授予的权限 |
|
||||
|
||||
**示例:**
|
||||
- 您的有效期到 2026-12-31
|
||||
- 您可以为下级设置最长到 2026-12-31 的有效期
|
||||
- 上级为您设置的并发数限制是 500
|
||||
- 您为下级设置的并发数限制不能超过 500
|
||||
|
||||
### 3. 收益模式参考
|
||||
|
||||
常见的代理商收益模式:
|
||||
|
||||
| 模式 | 说明 |
|
||||
|------|------|
|
||||
| 按授权收费 | 每个授权收取一次性费用 |
|
||||
| 按并发数收费 | 根据分配的并发数定价 |
|
||||
| 按服务期限收费 | 月付/季付/年付不同价格 |
|
||||
| FRP 增值服务 | 为无服务器的下级提供 FRP 代理 |
|
||||
| 技术支持服务 | 提供部署、运维支持 |
|
||||
|
||||
---
|
||||
|
||||
## 第二部分:下级管理流程
|
||||
|
||||
### 4. 发展下级的标准流程
|
||||
|
||||
```
|
||||
步骤 1:下级联系您,表达需求
|
||||
↓
|
||||
步骤 2:下级下载并运行 YAMA.exe
|
||||
↓
|
||||
步骤 3:下级发送序列号给您
|
||||
↓
|
||||
步骤 4:您评估需求,生成授权
|
||||
↓
|
||||
步骤 5:您将 *.lic 文件发送给下级
|
||||
↓
|
||||
步骤 6:下级导入授权,开始使用
|
||||
```
|
||||
|
||||
### 5. 获取下级序列号
|
||||
|
||||
#### 5.1 指导下级获取序列号
|
||||
|
||||
指导下级完成以下步骤:
|
||||
1. 下载并运行 YAMA.exe(首次运行会提示未授权,属正常现象)
|
||||
2. 点击菜单 **其他** → **申请授权**
|
||||
3. 首次会显示使用条款,确认本软件仅限合法正当使用
|
||||
4. 点击"确认"后显示序列号并复制
|
||||
|
||||
> **提示**:导入授权后,该菜单会变为 **其他** → **序列号**
|
||||
|
||||
**序列号格式:** `XXXX-XXXX-XXXX-XXXX`(16 位十六进制,分 4 组)
|
||||
|
||||
#### 5.2 序列号传递建议
|
||||
|
||||
| 方式 | 建议 |
|
||||
|------|------|
|
||||
| 微信/QQ | 直接复制文本发送 |
|
||||
| 邮件 | 主题注明"YAMA序列号" |
|
||||
| 截图 | 确保序列号完整清晰 |
|
||||
|
||||
**重要**:序列号是生成授权的必要信息,请下级准确提供。
|
||||
|
||||
### 6. 评估下级需求
|
||||
|
||||
在生成授权前,了解下级的需求:
|
||||
|
||||
| 问题 | 影响 |
|
||||
|------|------|
|
||||
| 预计受管端数量 | 决定分配的并发数 |
|
||||
| 计划使用期限 | 决定授权有效期 |
|
||||
| 有无公网服务器 | 决定是否需要分配 FRP |
|
||||
|
||||
---
|
||||
|
||||
## 第三部分:生成授权
|
||||
|
||||
### 7. 打开授权生成界面
|
||||
|
||||
点击菜单 **工具** → **口令生成**
|
||||
|
||||
### 8. 填写授权信息
|
||||
|
||||
#### 8.1 基本信息
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| 序列号 | 下级提供的设备序列号 | b40f-638f-ebc8-6d54 |
|
||||
| 备注 | 便于识别的说明 | "张三-华东区代理" |
|
||||
|
||||
**备注建议格式:** `客户名-地区/用途`
|
||||
|
||||
#### 8.2 有效期设置
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| 起始日期 | 授权生效日期(可以是今天或将来某天) |
|
||||
| 结束日期 | 授权过期日期(不超过您的有效期) |
|
||||
|
||||
**常见期限类型:**
|
||||
|
||||
| 类型 | 期限 | 适用场景 |
|
||||
|------|------|---------|
|
||||
| 试用期 | 7-15 天 | 新客户体验 |
|
||||
| 月付 | 1 个月 | 短期需求 |
|
||||
| 季付 | 3 个月 | 一般客户 |
|
||||
| 半年付 | 6 个月 | 稳定客户 |
|
||||
| 年付 | 12 个月 | 长期合作 |
|
||||
|
||||
#### 8.3 并发数设置
|
||||
|
||||
设置下级可同时管理的受管端数量上限。
|
||||
|
||||
**规划建议:**
|
||||
- 小型客户:10-50
|
||||
- 中型客户:50-200
|
||||
- 大型客户:200+
|
||||
- 预留 10-20% 余量应对增长
|
||||
|
||||
### 9. 分配 FRP 端口
|
||||
|
||||
#### 9.1 何时需要分配 FRP
|
||||
|
||||
在以下情况为下级分配 FRP:
|
||||
- 下级没有公网服务器
|
||||
- 下级希望简化网络配置
|
||||
- 下级网络环境复杂
|
||||
|
||||
#### 9.2 分配方式
|
||||
|
||||
**自动分配(推荐):**
|
||||
1. 勾选"FRP 代理"选项
|
||||
2. 系统自动从可用端口池分配
|
||||
3. 无需手动指定端口号
|
||||
|
||||
**手动指定:**
|
||||
1. 勾选"FRP 代理"选项
|
||||
2. 取消"自动分配"
|
||||
3. 输入指定端口号
|
||||
|
||||
**注意**:手动指定需确保端口在您配置的范围内且未被占用。
|
||||
|
||||
#### 9.3 记录分配信息
|
||||
|
||||
建议维护一份分配记录表:
|
||||
|
||||
| 下级 | 序列号 | 端口 | 并发数 | 有效期 | 备注 |
|
||||
|------|-------|------|--------|--------|------|
|
||||
| 张三 | b40f-... | 20001 | 100 | 2026-12-31 | 华东区 |
|
||||
| 李四 | a12c-... | 20002 | 50 | 2026-06-30 | 试用 |
|
||||
|
||||
### 10. 生成授权
|
||||
|
||||
1. 确认所有信息无误
|
||||
2. 点击"生成"按钮
|
||||
3. 授权信息保存到本地数据库
|
||||
|
||||
### 11. 发送给下级
|
||||
|
||||
有两种方式将授权发送给下级:
|
||||
|
||||
#### 方式 A:在线发送(推荐)
|
||||
|
||||
如果下级使用您通过 **工具** → **主控生成** 分发的程序:
|
||||
|
||||
1. 下级程序已硬编码您的地址,启动后会自动连接到您
|
||||
2. 您在主机列表中可以看到已连接但未授权的下级
|
||||
3. 右键点击该主机 → **发送授权**
|
||||
4. 选择已生成的授权记录
|
||||
5. 通知下级重启程序
|
||||
|
||||
**下级重启流程(如授权包含 FRP):**
|
||||
- 第一次重启:验证授权,提示"授权成功",同时收到 FRP 配置
|
||||
- 第二次重启:应用 FRP 配置,开始使用 FRP 代理
|
||||
|
||||
**优点:** 无需传输文件,操作简便,适合大量下级管理
|
||||
|
||||
#### 方式 B:离线发送(lic 文件)
|
||||
|
||||
如果下级无法先连接到您(如网络原因):
|
||||
|
||||
1. 在授权生成界面点击"导出"按钮
|
||||
2. 生成 `*.lic` 文件(建议命名:`客户名_日期.lic`)
|
||||
3. 通过微信/QQ/邮件发送给下级
|
||||
4. 附带导入说明:
|
||||
|
||||
```
|
||||
请按以下步骤导入授权:
|
||||
1. 运行 YAMA.exe
|
||||
2. 点击菜单"工具" → "导入口令..."
|
||||
3. 选择 *.lic 文件
|
||||
4. 重启程序使授权生效
|
||||
|
||||
如有问题请联系我。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第四部分:FRP 代理服务
|
||||
|
||||
> 为没有公网服务器的下级提供代理服务
|
||||
|
||||
### 12. FRP 工作原理
|
||||
|
||||
当下级没有公网服务器时,可以通过您的 FRP 服务实现连接:
|
||||
|
||||
```
|
||||
下级的受管端
|
||||
│
|
||||
│ 连接
|
||||
↓
|
||||
您的 FRP 服务器(公网)
|
||||
│
|
||||
│ 转发
|
||||
↓
|
||||
下级的 YAMA 程序
|
||||
```
|
||||
|
||||
**数据流向:**
|
||||
1. 受管端连接您的服务器的指定端口(如 20001)
|
||||
2. FRP 服务将流量转发给下级的 YAMA
|
||||
3. 下级 YAMA 通过 FRP 客户端接收数据
|
||||
|
||||
### 13. 配置 FRP 服务
|
||||
|
||||
#### 13.1 系统要求
|
||||
|
||||
| 要求 | 说明 |
|
||||
|------|------|
|
||||
| 操作系统 | Windows 10 / Server 2016 或更高版本 |
|
||||
| 架构 | 必须是 64 位系统 |
|
||||
| 网络 | 服务器具有公网 IP |
|
||||
|
||||
**注意**:32 位系统或 Windows 7/Server 2012 等旧系统不支持 FRP 功能。
|
||||
|
||||
#### 13.2 打开配置界面
|
||||
|
||||
点击菜单 **扩展** → **下级FRP代理设置**
|
||||
|
||||
#### 13.3 启用服务
|
||||
|
||||
勾选 **"启用为下级提供 FRP 代理"**
|
||||
|
||||
#### 13.4 选择 FRPS 模式
|
||||
|
||||
**方式 A:本地运行 FRPS(推荐)**
|
||||
|
||||
勾选"FRPS 运行在本机"
|
||||
|
||||
| 优点 | 说明 |
|
||||
|------|------|
|
||||
| 简单 | 程序自动管理 FRPS |
|
||||
| 集成 | 无需额外部署 |
|
||||
| 便捷 | 配置自动生效 |
|
||||
|
||||
**方式 B:使用外部 FRPS**
|
||||
|
||||
如果您已有独立的 FRPS 服务器:
|
||||
1. 取消勾选"FRPS 运行在本机"
|
||||
2. 填写 FRPS 服务器地址
|
||||
3. 填写 FRPS 端口
|
||||
4. 填写认证 Token(需与 FRPS 配置一致)
|
||||
|
||||
#### 13.5 设置端口和密钥
|
||||
|
||||
| 设置项 | 说明 | 建议值 |
|
||||
|--------|------|-------|
|
||||
| FRPS 端口 | FRPS 服务监听端口 | 7000 |
|
||||
| 认证 Token | 安全密钥 | 使用复杂随机字符串 |
|
||||
|
||||
**Token 建议:** 使用 16 位以上包含字母数字的随机字符串。
|
||||
|
||||
#### 13.6 设置分配范围
|
||||
|
||||
| 设置项 | 说明 | 建议值 |
|
||||
|--------|------|-------|
|
||||
| 起始端口 | 分配给下级的最小端口 | 20000 |
|
||||
| 结束端口 | 分配给下级的最大端口 | 29999 |
|
||||
|
||||
**容量计算:** 端口范围 20000-29999 可容纳 10000 个下级。
|
||||
|
||||
### 14. 开放防火墙
|
||||
|
||||
FRP 服务需要开放以下端口:
|
||||
|
||||
| 端口 | 用途 |
|
||||
|------|------|
|
||||
| 7000(FRPS 端口) | FRP 服务监听 |
|
||||
| 20000-29999(分配范围) | 下级映射端口 |
|
||||
|
||||
#### 14.1 Windows 防火墙
|
||||
|
||||
1. 打开"Windows Defender 防火墙"
|
||||
2. 点击"高级设置"
|
||||
3. 选择"入站规则" → "新建规则"
|
||||
4. 选择"端口" → "TCP"
|
||||
5. 输入端口:`7000, 20000-29999`
|
||||
6. 选择"允许连接"
|
||||
7. 完成
|
||||
|
||||
#### 14.2 云服务器安全组
|
||||
|
||||
以阿里云/腾讯云为例:
|
||||
|
||||
1. 登录云控制台
|
||||
2. 找到安全组配置
|
||||
3. 添加入站规则:
|
||||
- 协议:TCP
|
||||
- 端口:7000
|
||||
- 端口:20000-29999
|
||||
4. 保存配置
|
||||
|
||||
### 15. 验证 FRP 服务
|
||||
|
||||
#### 15.1 检查运行状态
|
||||
|
||||
如果选择"本地运行 FRPS":
|
||||
- 状态栏应显示 FRP 服务状态
|
||||
- 可查看日志文件 `Bin/frps.log`
|
||||
|
||||
#### 15.2 测试下级连接
|
||||
|
||||
1. 为测试下级生成授权(包含 FRP)
|
||||
2. 下级导入授权
|
||||
3. 确认下级状态栏显示 FRP 连接成功
|
||||
4. 下级生成受管程序并测试
|
||||
|
||||
---
|
||||
|
||||
## 第五部分:授权管理
|
||||
|
||||
### 16. 查看已发放授权
|
||||
|
||||
点击菜单 **工具** → **授权管理**
|
||||
|
||||
授权列表显示以下信息:
|
||||
|
||||
| 列名 | 说明 |
|
||||
|------|------|
|
||||
| 序列号 | 下级设备序列号 |
|
||||
| 备注 | 自定义说明 |
|
||||
| 有效期 | 起始日期 - 结束日期 |
|
||||
| 并发数 | 分配的并发上限 |
|
||||
| FRP 端口 | 分配的 FRP 端口(如有) |
|
||||
| 状态 | 有效/过期/禁用 |
|
||||
|
||||
**筛选功能:**
|
||||
- 按状态筛选:有效/过期/全部
|
||||
- 按关键词搜索:序列号、备注
|
||||
|
||||
### 17. 授权续期
|
||||
|
||||
当下级授权即将过期时:
|
||||
|
||||
1. 在授权列表中找到目标授权
|
||||
2. 双击或点击"编辑"
|
||||
3. 修改结束日期
|
||||
4. 保存更改
|
||||
|
||||
**下级更新授权:**
|
||||
- 下级重启程序即可自动获取新的有效期(通过网络验证)
|
||||
- 通常**无需重新发送 lic 文件**
|
||||
|
||||
> **注意**:只有当下级的序列号(SN)发生变化时,才需要重新生成并发送授权文件。
|
||||
|
||||
### 18. 授权撤销
|
||||
|
||||
#### 18.1 何时需要撤销
|
||||
|
||||
- 下级未按时付费
|
||||
- 下级违反使用协议
|
||||
- 下级主动要求取消
|
||||
|
||||
#### 18.2 撤销方式
|
||||
|
||||
**方式一:禁用授权**
|
||||
1. 在授权列表选中目标
|
||||
2. 点击"禁用"
|
||||
3. 下级下次验证时授权失效
|
||||
|
||||
**方式二:删除授权**
|
||||
1. 在授权列表选中目标
|
||||
2. 点击"删除"
|
||||
3. 授权记录从数据库移除
|
||||
|
||||
#### 18.3 撤销后的影响
|
||||
|
||||
- 下级程序在下次联网验证时失效
|
||||
- 已连接的受管端保持连接直到断开
|
||||
- 下级无法生成新的受管程序
|
||||
|
||||
### 19. 端口回收
|
||||
|
||||
当下级不再使用 FRP 时:
|
||||
|
||||
1. 记录下级占用的端口号
|
||||
2. 撤销或删除该下级授权
|
||||
3. 端口自动回到可用池
|
||||
4. 可分配给新下级使用
|
||||
|
||||
---
|
||||
|
||||
## 第六部分:日常运维
|
||||
|
||||
### 20. 监控下级状态
|
||||
|
||||
#### 20.1 定期检查
|
||||
|
||||
建议定期检查:
|
||||
- 下级授权到期情况
|
||||
- FRP 端口使用率
|
||||
- 异常连接情况
|
||||
|
||||
#### 20.2 日志查看
|
||||
|
||||
FRP 相关日志位置:
|
||||
- `Bin/frps.log` - FRPS 服务日志
|
||||
- 包含下级连接/断开记录
|
||||
|
||||
### 21. 常见问题处理
|
||||
|
||||
| 下级反馈 | 可能原因 | 处理方法 |
|
||||
|---------|---------|---------|
|
||||
| 无法导入授权 | 序列号不匹配 | 确认序列号,重新生成 |
|
||||
| 授权显示无效 | 有效期问题 | 检查日期设置 |
|
||||
| FRP 连接失败 | 防火墙/网络 | 检查端口开放 |
|
||||
| 授权已过期 | 到期未续费 | 续期后下级重启程序即可 |
|
||||
| 并发数超限 | 超过分配限制 | 升级授权或释放连接 |
|
||||
| 受管端连不上 | 地址端口错误 | 检查下级设置 |
|
||||
|
||||
### 22. 备份与恢复
|
||||
|
||||
#### 22.1 定期备份
|
||||
|
||||
建议备份以下数据:
|
||||
|
||||
| 数据 | 位置 | 说明 |
|
||||
|------|------|------|
|
||||
| 配置信息 | 注册表 `HKCU\Software\YAMA` | 可导出为 .reg 文件 |
|
||||
| 授权数据库 | `%APPDATA%\YAMA\licenses.db` | SQLite 数据库 |
|
||||
|
||||
**备份频率:** 每周或每次重要操作后
|
||||
|
||||
**注册表导出方法:**
|
||||
1. 运行 `regedit`
|
||||
2. 导航到 `HKEY_CURRENT_USER\Software\YAMA`
|
||||
3. 右键 → 导出 → 保存为 .reg 文件
|
||||
|
||||
#### 22.2 恢复方法
|
||||
|
||||
1. 停止 YAMA 程序
|
||||
2. 双击 .reg 文件导入注册表配置
|
||||
3. 将 `licenses.db` 复制到 `%APPDATA%\YAMA\`
|
||||
4. 重新启动程序
|
||||
5. 验证数据恢复正确
|
||||
|
||||
---
|
||||
|
||||
## 第七部分:进阶功能
|
||||
|
||||
### 23. 下级成为代理商
|
||||
|
||||
如果您的授权支持,您的下级也可以发展自己的下级:
|
||||
|
||||
```
|
||||
您(一级代理)
|
||||
│
|
||||
└── 下级 A(二级代理)
|
||||
│
|
||||
├── A 的下级 1
|
||||
└── A 的下级 2
|
||||
```
|
||||
|
||||
**前提条件:**
|
||||
- 您的授权支持多级
|
||||
- 为下级开通代理权限
|
||||
|
||||
### 24. 批量管理
|
||||
|
||||
#### 24.1 批量生成
|
||||
|
||||
如需为多个下级生成授权:
|
||||
1. 收集所有序列号
|
||||
2. 逐个生成授权
|
||||
3. 批量导出文件
|
||||
4. 分别发送
|
||||
|
||||
#### 24.2 批量续期
|
||||
|
||||
对于到期的授权:
|
||||
1. 筛选即将过期的授权
|
||||
2. 逐个编辑延长有效期
|
||||
3. 通知下级重启程序以更新授权
|
||||
|
||||
> 续期无需重新发送 lic 文件,下级重启即可通过网络获取新的有效期。
|
||||
|
||||
### 25. 数据统计
|
||||
|
||||
建议自行记录统计以下数据:
|
||||
|
||||
| 统计项 | 用途 |
|
||||
|--------|------|
|
||||
| 下级数量 | 业务规模 |
|
||||
| 端口使用率 | 资源规划 |
|
||||
| 到期预警 | 续费提醒 |
|
||||
| 收入记录 | 财务管理 |
|
||||
|
||||
---
|
||||
|
||||
## 附录
|
||||
|
||||
### A. 定价建议参考
|
||||
|
||||
定价时可考虑以下因素:
|
||||
|
||||
| 因素 | 说明 |
|
||||
|------|------|
|
||||
| 并发数档位 | 10/50/100/500/不限 |
|
||||
| 有效期 | 月/季/半年/年 |
|
||||
| FRP 服务 | 是否包含、带宽限制 |
|
||||
| 技术支持 | 基础/高级/VIP |
|
||||
|
||||
### B. 常用话术模板
|
||||
|
||||
**获取序列号:**
|
||||
```
|
||||
请运行 YAMA.exe,点击菜单"其他" → "申请授权"。
|
||||
首次需确认使用条款,之后即可查看序列号。
|
||||
将序列号发送给我即可。
|
||||
```
|
||||
|
||||
**发送授权文件:**
|
||||
```
|
||||
附件是您的授权文件,请按以下步骤导入:
|
||||
1. 运行 YAMA.exe
|
||||
2. 点击菜单"工具" → "导入口令..."
|
||||
3. 选择附件中的 *.lic 文件
|
||||
4. 关闭并重启程序
|
||||
5. 检查状态栏确认授权生效
|
||||
|
||||
如有问题随时联系我。
|
||||
```
|
||||
|
||||
**续期通知:**
|
||||
```
|
||||
您的 YAMA 授权将于 [日期] 到期。
|
||||
如需续期,请在到期前联系我。
|
||||
续期后重启程序即可自动更新,无需重新导入文件。
|
||||
```
|
||||
|
||||
### C. 技术支持
|
||||
|
||||
如遇到本手册未涵盖的问题:
|
||||
|
||||
| 渠道 | 联系方式 |
|
||||
|------|---------|
|
||||
| QQ | 962914132 |
|
||||
| Telegram | [@doge_grandfather](https://t.me/doge_grandfather) |
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [快速部署指南](QuickStart.md) - 首次部署
|
||||
- [多级网络搭建指南](NetworkSetup.md) - 网络架构详解
|
||||
- [日常使用手册](UserManual.md) - 远程管理功能
|
||||
- [定制化开发指南](CustomizationGuide.md) - 二次开发(技术型客户)
|
||||
157
docs/BuildScript.md
Normal file
157
docs/BuildScript.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# SimpleRemoter 编译脚本
|
||||
|
||||
命令行编译脚本,支持完整编译和增量编译。
|
||||
|
||||
## 环境要求
|
||||
|
||||
- Visual Studio 2019 或 2022
|
||||
- v142 工具集 (MSVC v142 - VS 2019 C++ build tools)
|
||||
- MFC 库 (C++ MFC for v142 build tools)
|
||||
|
||||
脚本自动检测以下 VS 安装路径:
|
||||
- `C:\Program Files\Microsoft Visual Studio\18\*` (VS2019 Insiders)
|
||||
- `C:\Program Files*\Microsoft Visual Studio\2019\*`
|
||||
- `C:\Program Files*\Microsoft Visual Studio\2022\*`
|
||||
|
||||
## 脚本文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `build.ps1` | PowerShell 主脚本 |
|
||||
| `build.cmd` | 批处理快捷入口 |
|
||||
|
||||
## 命令参数
|
||||
|
||||
### PowerShell (`build.ps1`)
|
||||
|
||||
```powershell
|
||||
.\build.ps1 [-Config <配置>] [-Platform <平台>] [-ServerOnly] [-Clean] [-Publish]
|
||||
```
|
||||
|
||||
| 参数 | 可选值 | 默认值 | 说明 |
|
||||
|------|--------|--------|------|
|
||||
| `-Config` | `Release`, `Debug` | `Release` | 编译配置 |
|
||||
| `-Platform` | `x64`, `x86`, `all` | `x64` | 目标平台 |
|
||||
| `-ServerOnly` | 开关 | 否 | 仅编译主控程序,跳过依赖项 |
|
||||
| `-Clean` | 开关 | 否 | 清理后重新编译 |
|
||||
| `-Publish` | 开关 | 否 | 发版模式:重编译所有 + UPX 压缩 |
|
||||
|
||||
### 批处理 (`build.cmd`)
|
||||
|
||||
```cmd
|
||||
build [release|debug] [x64|x86|all] [server|clean|publish]
|
||||
```
|
||||
|
||||
- 参数顺序任意
|
||||
- 不区分大小写
|
||||
|
||||
## 使用示例
|
||||
|
||||
### PowerShell
|
||||
|
||||
```powershell
|
||||
# 完整编译 (Release x64)
|
||||
.\build.ps1
|
||||
|
||||
# 编译 x86 + x64 两个平台
|
||||
.\build.ps1 -Platform all
|
||||
|
||||
# Debug 模式编译
|
||||
.\build.ps1 -Config Debug
|
||||
|
||||
# 仅编译主控程序 (跳过依赖项,适合快速重编译)
|
||||
.\build.ps1 -ServerOnly
|
||||
|
||||
# 清理后重新编译
|
||||
.\build.ps1 -Clean
|
||||
|
||||
# 组合使用
|
||||
.\build.ps1 -Config Debug -Platform all -Clean
|
||||
```
|
||||
|
||||
### 批处理
|
||||
|
||||
```cmd
|
||||
build # 完整编译 Release x64
|
||||
build server # 仅主控程序
|
||||
build debug x86 # Debug x86
|
||||
build release all clean # 清理后编译所有平台
|
||||
build publish # 发版模式
|
||||
```
|
||||
|
||||
## 编译流程
|
||||
|
||||
### 完整编译 (默认)
|
||||
|
||||
```
|
||||
Step 1: 编译客户端项目 (始终 Release, x86 + x64)
|
||||
├── ServerDll.dll ← ClientDll_vs2015.vcxproj
|
||||
├── ghost.exe ← ghost_vs2015.vcxproj
|
||||
├── TestRun.exe ← TestRun_vs2015.vcxproj
|
||||
├── TinyRun.dll ← TinyRun.vcxproj
|
||||
└── SCLoader.exe ← SCLoader.vcxproj
|
||||
|
||||
Step 2: 编译主控程序 (使用 -Config 指定的配置)
|
||||
└── Yama.exe ← 2015Remote_vs2015.vcxproj
|
||||
```
|
||||
|
||||
**注意:** 客户端项目始终使用 Release 模式编译,因为主控程序资源中引用的是 `Release\` 目录下的文件。`-Config Debug` 只影响主控程序本身。
|
||||
|
||||
主控程序 Yama 会将 Step 1 编译的 DLL/EXE 内嵌到资源中,因此**首次编译必须执行完整编译**。
|
||||
|
||||
### 发版模式 (-Publish)
|
||||
|
||||
```
|
||||
Step 1: 重新编译所有客户端项目 (Release x86 + x64)
|
||||
Step 2: 重新编译主控程序 (Release x64)
|
||||
Step 3: UPX 压缩 (upx --best Yama_x64.exe)
|
||||
```
|
||||
|
||||
发版模式会:
|
||||
- 强制 Release x64 配置
|
||||
- 强制 Clean 重新编译(确保所有文件是最新的)
|
||||
- 使用 UPX 压缩最终输出(约 70% 压缩率)
|
||||
|
||||
UPX 路径:`server/2015Remote/res/3rd/upx.exe`
|
||||
|
||||
### 仅主控 (-ServerOnly)
|
||||
|
||||
跳过 Step 1,直接编译 Yama。适用于:
|
||||
- 依赖项已编译完成
|
||||
- 仅修改了主控程序代码
|
||||
|
||||
## 输出文件
|
||||
|
||||
| 平台 | 输出路径 |
|
||||
|------|---------|
|
||||
| x64 | `Bin\Yama_x64.exe` |
|
||||
| x86 | `Bin\Yama_x86.exe` |
|
||||
|
||||
客户端项目输出:
|
||||
|
||||
| 项目 | x86 输出 | x64 输出 |
|
||||
|------|---------|---------|
|
||||
| ServerDll | `Release\ServerDll.dll` | `x64\Release\ServerDll.dll` |
|
||||
| ghost | `Release\ghost.exe` | `x64\Release\ghost.exe` |
|
||||
| TestRun | `Release\TestRun.exe` | `x64\Release\TestRun.exe` |
|
||||
| TinyRun | `Release\TinyRun.dll` | `x64\Release\TinyRun.dll` |
|
||||
| SCLoader | `Release\SCLoader.exe` | `x64\Release\SCLoader.exe` |
|
||||
|
||||
## 常见问题
|
||||
|
||||
### v142 build tools cannot be found
|
||||
|
||||
安装 VS2019 构建工具:
|
||||
1. 打开 Visual Studio Installer
|
||||
2. 修改 VS2022 或安装 VS2019
|
||||
3. 勾选 `MSVC v142 - VS 2019 C++ x64/x86 build tools`
|
||||
|
||||
### MFC libraries are required
|
||||
|
||||
安装 MFC 库:
|
||||
1. 打开 Visual Studio Installer
|
||||
2. 勾选 `C++ MFC for v142 build tools (x86 and x64)`
|
||||
|
||||
### MSBuild not found
|
||||
|
||||
确保安装了 Visual Studio 2019 或 2022,且包含 C++ 桌面开发工作负载。
|
||||
272
docs/CrashProtection.md
Normal file
272
docs/CrashProtection.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# 崩溃保护与 MTBF 统计设计文档
|
||||
|
||||
## 概述
|
||||
|
||||
本文档描述服务端代理进程的崩溃保护机制和可靠性统计功能。
|
||||
|
||||
## 功能目标
|
||||
|
||||
1. **崩溃保护**:防止代理进程连续崩溃导致的无限重启循环
|
||||
2. **崩溃统计**:记录崩溃次数、时间、退出代码等信息
|
||||
3. **MTBF 计算**:收集数据用于计算平均故障间隔时间(Mean Time Between Failures)
|
||||
|
||||
---
|
||||
|
||||
## 一、崩溃保护机制
|
||||
|
||||
### 1.1 触发条件
|
||||
|
||||
| 参数 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| 窗口期 | 5 分钟 | `CRASH_WINDOW_MS = 300000` |
|
||||
| 阈值 | 3 次 | `CRASH_THRESHOLD = 3` |
|
||||
| 快速崩溃定义 | 30 秒内退出 | `FAST_CRASH_TIME_MS = 30000` |
|
||||
|
||||
**触发逻辑**:在 5 分钟窗口期内,代理进程发生 3 次快速崩溃(启动后 30 秒内异常退出),则触发崩溃保护。
|
||||
|
||||
### 1.2 判定标准
|
||||
|
||||
只有同时满足以下条件才计入崩溃:
|
||||
- 退出代码 ≠ 0(正常退出不计入)
|
||||
- 运行时间 < 30 秒(长时间运行后退出不计入)
|
||||
|
||||
**设计原则**:程序正常退出必须返回 0,任何非 0 退出代码都视为异常退出。包括:
|
||||
- Windows 异常(如 `0xC0000005` ACCESS_VIOLATION)
|
||||
- 自定义错误码(如 `1001` CONFIG_ERROR)
|
||||
- 其他非零退出码
|
||||
|
||||
### 1.3 保护行为
|
||||
|
||||
触发崩溃保护后:
|
||||
1. 服务停止自动重启代理进程
|
||||
2. 写入 `AgentCrashProtected = 1` 到配置
|
||||
3. 下次程序启动时检测到此标志,切换到普通运行模式
|
||||
4. 弹出提示框告知用户
|
||||
|
||||
### 1.4 状态持久化
|
||||
|
||||
崩溃窗口状态持久化到注册表,确保服务重启后状态不丢失:
|
||||
|
||||
配置 section: `[crash]`
|
||||
|
||||
| 配置键 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| `winCount` | int | 窗口期内崩溃次数 |
|
||||
| `winStart` | string (uint64) | 窗口起始时间 (GetTickCount64) |
|
||||
|
||||
**服务重启时的恢复逻辑**:
|
||||
|
||||
```
|
||||
加载保存的状态
|
||||
↓
|
||||
检查 savedFirstCrashTime <= 当前时间?
|
||||
├─ 否 → 系统重启过,清除状态
|
||||
└─ 是 → 检查是否仍在 5 分钟窗口内?
|
||||
├─ 否 → 窗口已过期,清除状态
|
||||
└─ 是 → 恢复状态,继续计数
|
||||
```
|
||||
|
||||
**注意**:`GetTickCount64()` 在系统重启后会重置为 0,因此如果保存的时间戳大于当前时间,说明系统重启过,需要清除状态重新开始。
|
||||
|
||||
---
|
||||
|
||||
## 二、崩溃统计
|
||||
|
||||
### 2.1 记录内容
|
||||
|
||||
每次崩溃时记录以下信息(section: `[crash]`):
|
||||
|
||||
| 配置键 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| `count` | int | 总崩溃次数 | `15` |
|
||||
| `lastTime` | string | 最后崩溃时间 | `"2024-01-15 10:30:45"` |
|
||||
| `lastCode` | string | 最后退出代码 | `"0xC0000005 (ACCESS_VIOLATION)"` |
|
||||
| `lastRunMs` | string (uint64) | 最后运行时间(ms) | `"25000"` |
|
||||
|
||||
### 2.2 退出代码描述
|
||||
|
||||
常见的 Windows 异常代码:
|
||||
|
||||
| 代码 | 描述 |
|
||||
|------|------|
|
||||
| `0xC0000005` | ACCESS_VIOLATION(访问违规) |
|
||||
| `0xC0000094` | INTEGER_DIVIDE_BY_ZERO(整数除零) |
|
||||
| `0xC00000FD` | STACK_OVERFLOW(栈溢出) |
|
||||
| `0xC0000409` | STACK_BUFFER_OVERRUN(栈缓冲区溢出) |
|
||||
| `0xC000001D` | ILLEGAL_INSTRUCTION(非法指令) |
|
||||
| `0x80000003` | BREAKPOINT(断点) |
|
||||
|
||||
正常退出:
|
||||
|
||||
| 代码 | 描述 |
|
||||
|------|------|
|
||||
| `0` | NORMAL(正常退出) |
|
||||
|
||||
自定义错误代码(1000-1999):
|
||||
|
||||
| 代码 | 描述 |
|
||||
|------|------|
|
||||
| `1001` | CONFIG_ERROR(配置错误) |
|
||||
| `1002` | NETWORK_ERROR(网络错误) |
|
||||
| `1003` | AUTH_FAILED(认证失败) |
|
||||
| `1004` | RESTART_REQUEST(请求重启) |
|
||||
| `1005` | MANUAL_STOP(手动停止) |
|
||||
|
||||
---
|
||||
|
||||
## 三、MTBF 统计
|
||||
|
||||
### 3.1 数据收集
|
||||
|
||||
| 配置键 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| `starts` | int | 启动次数 |
|
||||
| `totalRunMs` | string (uint64) | 累计运行时间(ms) |
|
||||
| `count` | int | 崩溃次数 |
|
||||
|
||||
**注意**:`totalRunMs` 和 `lastRunMs` 使用字符串存储 64 位整数,避免 32 位整数的限制(最大约 49 天)。
|
||||
|
||||
### 3.2 计算公式
|
||||
|
||||
**MTBF(平均故障间隔时间)**:
|
||||
```
|
||||
MTBF = 累计运行时间 / 崩溃次数
|
||||
```
|
||||
|
||||
**失败率(Failure Rate)**:
|
||||
```
|
||||
失败率 = 崩溃次数 / 启动次数
|
||||
```
|
||||
|
||||
### 3.3 示例
|
||||
|
||||
假设:
|
||||
- 启动次数:100 次
|
||||
- 崩溃次数:5 次
|
||||
- 累计运行时间:30 天
|
||||
|
||||
计算结果:
|
||||
- MTBF = 30 天 / 5 = 6 天(平均每 6 天发生一次崩溃)
|
||||
- 失败率 = 5 / 100 = 5%(每次启动有 5% 概率崩溃)
|
||||
|
||||
---
|
||||
|
||||
## 四、回调函数设计
|
||||
|
||||
### 4.1 回调类型
|
||||
|
||||
| 回调 | 触发时机 | 参数 |
|
||||
|------|---------|------|
|
||||
| `onAgentStart` | 代理进程启动成功 | processId, sessionId |
|
||||
| `onAgentExit` | 代理进程退出(任何原因) | exitCode, runtimeMs |
|
||||
| `onCrash` | 检测到快速崩溃 | exitCode, runtimeMs |
|
||||
| `onCrashWindowChange` | 崩溃窗口状态变化 | crashCount, firstCrashTime |
|
||||
| `onCrashProtection` | 触发崩溃保护 | 无 |
|
||||
|
||||
### 4.2 调用顺序
|
||||
|
||||
正常退出:
|
||||
```
|
||||
onAgentExit(exitCode=0, runtime)
|
||||
```
|
||||
|
||||
快速崩溃:
|
||||
```
|
||||
onAgentExit(exitCode, runtime)
|
||||
↓
|
||||
onCrash(exitCode, runtime)
|
||||
↓
|
||||
[如果 count >= 3]
|
||||
onCrashProtection()
|
||||
↓
|
||||
onCrashWindowChange(count, startTime)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、配置键汇总
|
||||
|
||||
Section: `[crash]`
|
||||
|
||||
| 配置键 | 类型 | 用途 | 持久性 |
|
||||
|--------|------|------|--------|
|
||||
| `count` | int | 总崩溃次数 | 永久 |
|
||||
| `lastTime` | string | 最后崩溃时间 | 永久 |
|
||||
| `lastCode` | string | 最后退出代码 | 永久 |
|
||||
| `lastRunMs` | string | 最后运行时间 | 永久 |
|
||||
| `totalRunMs` | string | 累计运行时间 | 永久 |
|
||||
| `starts` | int | 启动次数 | 永久 |
|
||||
| `winCount` | int | 窗口期崩溃次数 | 临时* |
|
||||
| `winStart` | string | 窗口起始时间 | 临时* |
|
||||
| `protected` | int | 崩溃保护标志 | 一次性** |
|
||||
|
||||
\* 临时:窗口期过期或系统重启后自动清除
|
||||
\** 一次性:程序启动时读取并清除
|
||||
|
||||
---
|
||||
|
||||
## 六、文件改动
|
||||
|
||||
| 文件 | 改动内容 |
|
||||
|------|---------|
|
||||
| `CrashReport.h` | 配置键定义、退出代码常量、MTBF 计算函数 |
|
||||
| `ServerSessionMonitor.h` | 回调类型定义、监控器结构体 |
|
||||
| `ServerSessionMonitor.cpp` | 崩溃检测逻辑、回调调用 |
|
||||
| `ServerServiceWrapper.cpp` | 回调实现、状态持久化/恢复 |
|
||||
| `2015Remote.cpp` | 启动时检查崩溃保护标志 |
|
||||
|
||||
---
|
||||
|
||||
## 七、使用场景示例
|
||||
|
||||
### 场景 1:连续崩溃
|
||||
|
||||
```
|
||||
T=00:00 代理启动 (startCount=1)
|
||||
T=00:05 代理崩溃 (crashCount=1, 窗口开始)
|
||||
T=00:10 代理重启 (startCount=2)
|
||||
T=00:15 代理崩溃 (crashCount=2)
|
||||
T=00:20 代理重启 (startCount=3)
|
||||
T=00:25 代理崩溃 (crashCount=3, 触发保护!)
|
||||
→ 服务停止重启代理
|
||||
→ 写入 AgentCrashProtected=1
|
||||
T=00:30 用户手动启动程序
|
||||
→ 检测到保护标志,切换到普通模式
|
||||
→ 弹出提示框
|
||||
```
|
||||
|
||||
### 场景 2:服务重启后恢复
|
||||
|
||||
```
|
||||
T=00:00 代理崩溃 (crashCount=1)
|
||||
T=00:30 代理崩溃 (crashCount=2, 保存到注册表)
|
||||
T=01:00 服务重启(非系统重启)
|
||||
→ 加载状态: count=2, elapsed=1分钟
|
||||
→ 仍在5分钟窗口内,恢复状态
|
||||
T=01:30 代理崩溃 (crashCount=3, 触发保护!)
|
||||
```
|
||||
|
||||
### 场景 3:窗口过期
|
||||
|
||||
```
|
||||
T=00:00 代理崩溃 (crashCount=1)
|
||||
T=02:00 代理崩溃 (crashCount=2)
|
||||
T=08:00 代理崩溃
|
||||
→ 距离第一次崩溃已超过5分钟
|
||||
→ 重置窗口: crashCount=1, 新窗口开始
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、设计决策记录
|
||||
|
||||
| 问题 | 决策 | 理由 |
|
||||
|------|------|------|
|
||||
| 窗口期时长 | 5 分钟,不可配置 | 足够检测连续崩溃,避免误判 |
|
||||
| 崩溃阈值 | 3 次,不可配置 | 平衡敏感度和容错 |
|
||||
| 快速崩溃定义 | 30 秒 | 覆盖大多数初始化场景 |
|
||||
| 系统重启处理 | 清除窗口状态 | 系统重启后问题可能已修复 |
|
||||
| 统计数据重置 | 暂不实现 | 需求不明确,后续按需添加 |
|
||||
| 日志记录 | 使用 Mprintf | 与现有日志系统一致 |
|
||||
| 非零退出码 | 都算崩溃 | 正常退出必须返回 0 |
|
||||
| lastRunMs 类型 | string (uint64) | 支持超过 24 天的运行时间 |
|
||||
280
docs/CustomCursor_Design.md
Normal file
280
docs/CustomCursor_Design.md
Normal file
@@ -0,0 +1,280 @@
|
||||
# 自定义光标支持设计方案
|
||||
|
||||
**状态:已实现** ✓
|
||||
|
||||
## 概述
|
||||
|
||||
扩展远程桌面的光标支持,使其能够显示应用程序自定义光标和动画光标,而不仅仅是 16 种标准系统光标。
|
||||
|
||||
## 现有实现
|
||||
|
||||
### 数据流
|
||||
|
||||
```
|
||||
客户端 (ScreenCapture.h):
|
||||
getCurrentCursorIndex() → 0-15 或 -1
|
||||
帧数据: [算法(1)] [光标位置(8)] [光标索引(1)] [图像数据...]
|
||||
|
||||
服务端 (ScreenSpyDlg.cpp):
|
||||
cursorIndex == -1 → 回退到标准箭头
|
||||
cursorIndex 0-15 → 使用 m_CursorInfo.getCursorHandle(index)
|
||||
```
|
||||
|
||||
### 限制
|
||||
|
||||
- 仅支持 16 种标准系统光标 (IDC_ARROW, IDC_HAND, IDC_IBEAM 等)
|
||||
- 自定义光标显示为箭头
|
||||
- 动画光标不支持
|
||||
|
||||
## 设计方案
|
||||
|
||||
### 方案概述
|
||||
|
||||
采用**独立命令传输 + 哈希缓存 + 节流机制**:
|
||||
|
||||
1. 新增 `CMD_CURSOR_IMAGE` 命令传输光标位图
|
||||
2. 使用位图哈希避免重复传输
|
||||
3. 节流机制防止动画光标过载
|
||||
4. 光标索引 `-2` 表示使用已缓存的自定义光标
|
||||
|
||||
### 光标索引定义
|
||||
|
||||
| 索引值 | 含义 |
|
||||
|-------|------|
|
||||
| 0-15 | 标准系统光标(现有) |
|
||||
| -1 (255) | 不支持,显示箭头(现有,向后兼容) |
|
||||
| -2 (254) | 使用缓存的自定义光标(新增) |
|
||||
|
||||
### CMD_CURSOR_IMAGE 数据格式
|
||||
|
||||
```
|
||||
字节偏移 大小 字段
|
||||
────────────────────────────
|
||||
0 1 CMD_CURSOR_IMAGE (命令字节)
|
||||
1 4 hash (位图哈希,用于去重)
|
||||
5 2 hotspotX (热点X坐标)
|
||||
7 2 hotspotY (热点Y坐标)
|
||||
9 1 width (光标宽度,最大255)
|
||||
10 1 height (光标高度,最大255)
|
||||
11 N BGRA位图数据 (N = width * height * 4)
|
||||
|
||||
典型大小: 32x32光标 = 11 + 4096 = 4107 字节
|
||||
```
|
||||
|
||||
### 客户端逻辑 (ScreenCapture.h / ScreenManager.cpp)
|
||||
|
||||
```cpp
|
||||
// 新增状态变量
|
||||
DWORD m_lastCursorHash = 0; // 上次发送的光标哈希
|
||||
DWORD m_lastCursorSendTime = 0; // 上次发送时间
|
||||
const DWORD CURSOR_THROTTLE = 50; // 最小发送间隔 (ms)
|
||||
|
||||
// 每帧处理逻辑
|
||||
int cursorIndex = getCurrentCursorIndex();
|
||||
if (cursorIndex == -1) {
|
||||
// 非标准光标,获取位图
|
||||
CursorBitmapInfo info;
|
||||
if (GetCurrentCursorBitmap(&info)) {
|
||||
DWORD hash = CalculateBitmapHash(info);
|
||||
DWORD now = GetTickCount();
|
||||
|
||||
// 哈希变化且超过节流时间 → 发送光标图像
|
||||
if (hash != m_lastCursorHash &&
|
||||
(now - m_lastCursorSendTime) > CURSOR_THROTTLE) {
|
||||
SendCursorImage(info, hash);
|
||||
m_lastCursorHash = hash;
|
||||
m_lastCursorSendTime = now;
|
||||
}
|
||||
cursorIndex = -2; // 使用自定义光标
|
||||
}
|
||||
}
|
||||
// 帧数据中写入 cursorIndex
|
||||
```
|
||||
|
||||
### 服务端逻辑 (ScreenSpyDlg.cpp)
|
||||
|
||||
```cpp
|
||||
// 新增成员变量
|
||||
HCURSOR m_hCustomCursor = NULL; // 缓存的自定义光标
|
||||
DWORD m_customCursorHash = 0; // 当前光标哈希
|
||||
|
||||
// 处理 CMD_CURSOR_IMAGE
|
||||
case CMD_CURSOR_IMAGE: {
|
||||
DWORD hash = *(DWORD*)(buffer + 1);
|
||||
if (hash != m_customCursorHash) {
|
||||
WORD hotX = *(WORD*)(buffer + 5);
|
||||
WORD hotY = *(WORD*)(buffer + 7);
|
||||
BYTE width = buffer[9];
|
||||
BYTE height = buffer[10];
|
||||
LPBYTE bitmapData = buffer + 11;
|
||||
|
||||
// 销毁旧光标
|
||||
if (m_hCustomCursor) {
|
||||
DestroyCursor(m_hCustomCursor);
|
||||
}
|
||||
|
||||
// 创建新光标
|
||||
m_hCustomCursor = CreateCursorFromBitmap(
|
||||
hotX, hotY, width, height, bitmapData);
|
||||
m_customCursorHash = hash;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 处理帧数据中的光标索引
|
||||
BYTE cursorIndex = buffer[2 + sizeof(POINT)];
|
||||
HCURSOR cursor;
|
||||
if (cursorIndex == 254) { // -2
|
||||
cursor = m_hCustomCursor ? m_hCustomCursor : LoadCursor(NULL, IDC_ARROW);
|
||||
} else if (cursorIndex == 255) { // -1
|
||||
cursor = LoadCursor(NULL, IDC_ARROW);
|
||||
} else {
|
||||
cursor = m_CursorInfo.getCursorHandle(cursorIndex);
|
||||
}
|
||||
```
|
||||
|
||||
### 获取光标位图实现
|
||||
|
||||
```cpp
|
||||
struct CursorBitmapInfo {
|
||||
WORD hotspotX;
|
||||
WORD hotspotY;
|
||||
BYTE width;
|
||||
BYTE height;
|
||||
std::vector<BYTE> bgraData; // width * height * 4
|
||||
};
|
||||
|
||||
bool GetCurrentCursorBitmap(CursorBitmapInfo* info) {
|
||||
CURSORINFO ci = { sizeof(CURSORINFO) };
|
||||
if (!GetCursorInfo(&ci) || ci.flags != CURSOR_SHOWING)
|
||||
return false;
|
||||
|
||||
ICONINFO iconInfo;
|
||||
if (!GetIconInfo(ci.hCursor, &iconInfo))
|
||||
return false;
|
||||
|
||||
info->hotspotX = (WORD)iconInfo.xHotspot;
|
||||
info->hotspotY = (WORD)iconInfo.yHotspot;
|
||||
|
||||
BITMAP bm;
|
||||
GetObject(iconInfo.hbmColor ? iconInfo.hbmColor : iconInfo.hbmMask,
|
||||
sizeof(BITMAP), &bm);
|
||||
|
||||
info->width = (BYTE)min(bm.bmWidth, 255);
|
||||
info->height = (BYTE)min(bm.bmHeight, 255);
|
||||
|
||||
// 获取 BGRA 位图数据
|
||||
// ... (使用 GetDIBits)
|
||||
|
||||
// 清理
|
||||
DeleteObject(iconInfo.hbmColor);
|
||||
DeleteObject(iconInfo.hbmMask);
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
### 哈希计算
|
||||
|
||||
```cpp
|
||||
DWORD CalculateBitmapHash(const CursorBitmapInfo& info) {
|
||||
// 使用 FNV-1a 或简单的累加哈希
|
||||
DWORD hash = 2166136261; // FNV offset basis
|
||||
const BYTE* data = info.bgraData.data();
|
||||
size_t len = info.bgraData.size();
|
||||
|
||||
for (size_t i = 0; i < len; i += 16) { // 每16字节采样一次,加速
|
||||
hash ^= data[i];
|
||||
hash *= 16777619; // FNV prime
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
```
|
||||
|
||||
## 数据流图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 客户端 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ getCurrentCursorIndex() │
|
||||
│ │ │
|
||||
│ ├─ 0-15 ──→ 帧数据: cursorIndex = 0-15 │
|
||||
│ │ │
|
||||
│ └─ -1 ──→ GetCurrentCursorBitmap() │
|
||||
│ │ │
|
||||
│ ├─ 计算哈希 │
|
||||
│ │ │
|
||||
│ ├─ 哈希变化 && 超过节流? │
|
||||
│ │ │ │
|
||||
│ │ ├─ Yes ──→ 发送 CMD_CURSOR_IMAGE │
|
||||
│ │ │ 更新 lastHash, lastTime │
|
||||
│ │ │ │
|
||||
│ │ └─ No ──→ 跳过发送 │
|
||||
│ │ │
|
||||
│ └─ 帧数据: cursorIndex = -2 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 服务端 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ 收到 CMD_CURSOR_IMAGE: │
|
||||
│ └─ hash != m_customCursorHash? │
|
||||
│ ├─ Yes ──→ CreateCursorFromBitmap() → m_hCustomCursor │
|
||||
│ └─ No ──→ 忽略 (已缓存) │
|
||||
│ │
|
||||
│ 收到帧数据: │
|
||||
│ └─ cursorIndex: │
|
||||
│ ├─ 0-15 ──→ m_CursorInfo.getCursorHandle(index) │
|
||||
│ ├─ -1 ──→ LoadCursor(IDC_ARROW) (向后兼容) │
|
||||
│ └─ -2 ──→ m_hCustomCursor │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 动画光标支持
|
||||
|
||||
本方案自然支持动画光标:
|
||||
|
||||
| 机制 | 说明 |
|
||||
|-----|------|
|
||||
| **自动检测** | 动画每帧位图哈希不同,自动触发传输 |
|
||||
| **节流保护** | 50ms 节流 ≈ 最高 20fps,防止过载 |
|
||||
| **带宽控制** | 32x32 动画 @ 10fps ≈ 40 KB/s,可接受 |
|
||||
|
||||
## 修改文件清单
|
||||
|
||||
| 文件 | 修改内容 |
|
||||
|-----|---------|
|
||||
| `common/commands.h` | 新增 `CMD_CURSOR_IMAGE = 75` |
|
||||
| `client/CursorInfo.h` | 新增 `GetCurrentCursorBitmap()`, `CalculateBitmapHash()` |
|
||||
| `client/ScreenCapture.h` | 发送逻辑:检测、节流、发送 CMD_CURSOR_IMAGE |
|
||||
| `client/ScreenManager.cpp` | 处理 CMD_CURSOR_IMAGE(如果服务端发给客户端需要) |
|
||||
| `server/2015Remote/ScreenSpyDlg.h` | 新增 `m_hCustomCursor`, `m_customCursorHash` |
|
||||
| `server/2015Remote/ScreenSpyDlg.cpp` | 处理 CMD_CURSOR_IMAGE,更新光标显示逻辑 |
|
||||
|
||||
## 向后兼容性
|
||||
|
||||
- 旧客户端:继续发送 `-1`,服务端显示箭头(现有行为)
|
||||
- 新客户端 + 旧服务端:发送 `-2` 和 CMD_CURSOR_IMAGE,旧服务端忽略新命令,`-2` 当作 `-1` 处理
|
||||
- 新客户端 + 新服务端:完整自定义光标支持
|
||||
|
||||
## 可调参数
|
||||
|
||||
```cpp
|
||||
// client/ScreenCapture.h
|
||||
const DWORD CURSOR_THROTTLE = 50; // 节流间隔 (ms)
|
||||
// 50ms → 最高 20fps,动画流畅
|
||||
// 100ms → 最高 10fps,省带宽
|
||||
|
||||
const BYTE MAX_CURSOR_SIZE = 64; // 最大光标尺寸
|
||||
// 超过此尺寸的光标将被缩放或回退到标准光标
|
||||
```
|
||||
|
||||
## 测试用例
|
||||
|
||||
1. **标准光标** - 箭头、手形、I形等,应使用索引 0-15
|
||||
2. **自定义静态光标** - 应用程序自定义光标,只传输一次
|
||||
3. **动画光标** - Windows 忙碌动画,应持续更新(受节流限制)
|
||||
4. **快速切换** - 频繁切换光标,节流应生效
|
||||
5. **大尺寸光标** - 超过 64x64 的光标,应正确处理或回退
|
||||
589
docs/CustomizationGuide.md
Normal file
589
docs/CustomizationGuide.md
Normal file
@@ -0,0 +1,589 @@
|
||||
# YAMA 定制化开发指南
|
||||
|
||||
> 面向技术型客户的二次开发与品牌定制
|
||||
|
||||
---
|
||||
|
||||
## 目标读者
|
||||
|
||||
- 有一定编程基础的客户
|
||||
- 希望打造自己品牌的服务商
|
||||
- 承接远程解决方案外包的开发者
|
||||
|
||||
---
|
||||
|
||||
## 第一部分:定制化概述
|
||||
|
||||
### 1. 可定制内容
|
||||
|
||||
| 类别 | 可定制项 | 难度 | 需要重编译 |
|
||||
|------|---------|------|-----------|
|
||||
| 品牌外观 | 程序名称、图标、版本信息 | 简单 | 是 |
|
||||
| 菜单控制 | 隐藏/显示特定菜单项 | 简单 | 是 |
|
||||
| 工具栏 | 隐藏/显示特定按钮 | 简单 | 是 |
|
||||
| 右键菜单 | 隐藏/显示特定功能 | 简单 | 是 |
|
||||
| 界面文字 | 修改翻译/添加语言 | 中等 | 否 |
|
||||
| 功能定制 | 添加新功能、修改行为 | 高级 | 是 |
|
||||
|
||||
### 2. 定制方式
|
||||
|
||||
#### 2.1 配置文件定制(推荐)
|
||||
|
||||
修改 `UIBranding.h` 文件重新编译。
|
||||
|
||||
**适合场景:**
|
||||
- 品牌名称修改
|
||||
- 隐藏不需要的功能
|
||||
- 快速生成定制版本
|
||||
|
||||
#### 2.2 资源替换定制
|
||||
|
||||
替换图标、图片等资源文件。
|
||||
|
||||
**适合场景:**
|
||||
- 更换程序图标
|
||||
- 自定义启动画面
|
||||
|
||||
#### 2.3 语言文件定制
|
||||
|
||||
修改语言文件实现界面文字定制。
|
||||
|
||||
**适合场景:**
|
||||
- 修改界面用语
|
||||
- 添加新语言支持
|
||||
|
||||
#### 2.4 源码级定制
|
||||
|
||||
修改源代码实现深度定制。
|
||||
|
||||
**适合场景:**
|
||||
- 添加新功能
|
||||
- 修改现有行为
|
||||
- 深度集成
|
||||
|
||||
---
|
||||
|
||||
## 第二部分:品牌定制
|
||||
|
||||
### 3. UIBranding.h 配置文件
|
||||
|
||||
品牌定制的核心是修改 `server/2015Remote/UIBranding.h` 文件。
|
||||
|
||||
**文件位置:** `server/2015Remote/UIBranding.h`
|
||||
|
||||
**编码要求:** UTF-8 with BOM(MSVC 要求)
|
||||
|
||||
#### 3.1 快速定制(4 项核心设置)
|
||||
|
||||
最少只需修改以下 4 项即可完成基本品牌定制:
|
||||
|
||||
```cpp
|
||||
// 程序名称(窗口标题)
|
||||
#define BRAND_APP_NAME "YourBrand"
|
||||
|
||||
// 启动画面 Logo(建议大写)
|
||||
#define BRAND_SPLASH_NAME "YOURBRAND"
|
||||
|
||||
// 托盘图标提示
|
||||
#define BRAND_TRAY_TIP "YourBrand 远程管理"
|
||||
|
||||
// 版权信息
|
||||
#define BRAND_COPYRIGHT "Copyright (C) 2024 YourCompany"
|
||||
```
|
||||
|
||||
#### 3.2 完整品牌设置
|
||||
|
||||
| 配置项 | 说明 | 字符限制 |
|
||||
|--------|------|---------|
|
||||
| `BRAND_APP_NAME` | 窗口标题、关于对话框 | 可用中文 |
|
||||
| `BRAND_VERSION` | 版本号(建议 X.Y.Z 格式) | 可用中文 |
|
||||
| `BRAND_SPLASH_NAME` | 启动画面 Logo 文字 | 可用中文 |
|
||||
| `BRAND_TRAY_TIP` | 托盘图标悬停提示 | 可用中文 |
|
||||
| `BRAND_COPYRIGHT` | 版权声明 | 可用中文 |
|
||||
| `BRAND_SERVICE_DISPLAY` | Windows 服务显示名 | 可用中文 |
|
||||
| `BRAND_LICENSE_DESC` | 许可证文件描述 | 可用中文 |
|
||||
|
||||
#### 3.3 文件和系统标识
|
||||
|
||||
以下配置有字符限制,仅支持 ASCII:
|
||||
|
||||
| 配置项 | 说明 | 限制 |
|
||||
|--------|------|------|
|
||||
| `BRAND_DUMP_PREFIX` | 崩溃转储文件前缀 | 仅 ASCII |
|
||||
| `BRAND_EXE_NAME` | 默认 EXE 文件名 | 仅 ASCII |
|
||||
| `BRAND_LICENSE_PREFIX` | 许可证文件前缀 | 仅 ASCII |
|
||||
| `BRAND_DB_NAME` | 数据库文件名 | 仅 ASCII |
|
||||
| `BRAND_DATA_FOLDER` | 数据目录名 | 仅 ASCII |
|
||||
| `BRAND_SERVICE_NAME` | Windows 服务名(无空格) | 仅 ASCII |
|
||||
| `BRAND_REGISTRY_KEY` | 注册表键名(无空格) | 仅 ASCII |
|
||||
|
||||
**示例:**
|
||||
|
||||
```cpp
|
||||
#define BRAND_DUMP_PREFIX "MyRemote"
|
||||
#define BRAND_EXE_NAME "MyRemote.exe"
|
||||
#define BRAND_LICENSE_PREFIX "MyRemote"
|
||||
#define BRAND_SERVICE_NAME "MyRemoteService"
|
||||
#define BRAND_REGISTRY_KEY "MyRemote"
|
||||
```
|
||||
|
||||
### 4. 隐藏菜单项
|
||||
|
||||
通过设置宏为 `1` 可以隐藏对应菜单项。
|
||||
|
||||
> **完整列表**:以下仅列出常用配置项,完整列表请查看 `UIBranding.h` 文件。
|
||||
|
||||
#### 4.1 主菜单项(常用)
|
||||
|
||||
```cpp
|
||||
// 文件菜单
|
||||
#define HIDE_MENU_SETTINGS 0 // 参数设置
|
||||
#define HIDE_MENU_WALLET 0 // 钱包
|
||||
#define HIDE_MENU_NETWORK 0 // 网络
|
||||
|
||||
// 工具菜单
|
||||
#define HIDE_MENU_INPUT_PASSWORD 0 // 输入密码
|
||||
#define HIDE_MENU_IMPORT_LICENSE 0 // 导入许可证
|
||||
#define HIDE_MENU_GEN_AUTH 0 // 生成授权
|
||||
#define HIDE_MENU_GEN_MASTER 0 // 生成Master
|
||||
#define HIDE_MENU_LICENSE_MGR 0 // 许可证管理
|
||||
|
||||
// 参数菜单
|
||||
#define HIDE_MENU_KBLOGGER 0 // 键盘记录
|
||||
#define HIDE_MENU_LOGIN_NOTIFY 0 // 登录通知
|
||||
|
||||
// 扩展菜单
|
||||
#define HIDE_MENU_FRPS_FOR_SUB 0 // 下级FRP
|
||||
```
|
||||
|
||||
#### 4.2 使用示例
|
||||
|
||||
隐藏高级功能,创建简化版本:
|
||||
|
||||
```cpp
|
||||
// 隐藏 ShellCode 相关功能
|
||||
#define HIDE_MENU_SHELLCODE_C 1
|
||||
#define HIDE_MENU_SHELLCODE_BIN 1
|
||||
#define HIDE_MENU_SHELLCODE_LOAD_TEST 1
|
||||
#define HIDE_MENU_SHELLCODE_OBFS 1
|
||||
#define HIDE_MENU_SHELLCODE_AES_C 1
|
||||
#define HIDE_MENU_SHELLCODE_AES_BIN 1
|
||||
|
||||
// 隐藏开发相关功能
|
||||
#define HIDE_MENU_V2_PRIVATEKEY 1
|
||||
#define HIDE_MENU_RCEDIT 1
|
||||
```
|
||||
|
||||
### 5. 隐藏工具栏按钮(常用)
|
||||
|
||||
```cpp
|
||||
#define HIDE_TOOLBAR_TERMINAL 0 // 终端管理
|
||||
#define HIDE_TOOLBAR_PROCESS 0 // 进程管理
|
||||
#define HIDE_TOOLBAR_DESKTOP 0 // 桌面管理
|
||||
#define HIDE_TOOLBAR_FILE 0 // 文件管理
|
||||
#define HIDE_TOOLBAR_AUDIO 0 // 语音管理
|
||||
#define HIDE_TOOLBAR_VIDEO 0 // 视频管理
|
||||
#define HIDE_TOOLBAR_SERVICE 0 // 服务管理
|
||||
#define HIDE_TOOLBAR_REGISTER 0 // 注册表管理
|
||||
#define HIDE_TOOLBAR_KEYBOARD 0 // 键盘记录
|
||||
#define HIDE_TOOLBAR_SETTINGS 0 // 参数设置
|
||||
#define HIDE_TOOLBAR_BUILD 0 // 生成服务端
|
||||
```
|
||||
|
||||
### 6. 隐藏右键菜单项(常用)
|
||||
|
||||
```cpp
|
||||
// 远程桌面选项
|
||||
#define HIDE_CTX_VIRTUAL_DESKTOP 0 // 虚拟桌面
|
||||
#define HIDE_CTX_GRAY_DESKTOP 0 // 灰度桌面
|
||||
#define HIDE_CTX_REMOTE_DESKTOP 0 // 远程桌面
|
||||
#define HIDE_CTX_H264_DESKTOP 0 // H264桌面
|
||||
|
||||
// 系统操作
|
||||
#define HIDE_CTX_MACHINE_SHUTDOWN 0 // 关机
|
||||
#define HIDE_CTX_MACHINE_REBOOT 0 // 重启
|
||||
#define HIDE_CTX_MACHINE_LOGOUT 0 // 注销
|
||||
|
||||
// 执行命令
|
||||
#define HIDE_CTX_EXECUTE_DOWNLOAD 0 // 下载执行
|
||||
#define HIDE_CTX_EXECUTE_UPLOAD 0 // 上传执行
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第三部分:视觉定制
|
||||
|
||||
### 7. 图标替换
|
||||
|
||||
#### 7.1 图标文件位置
|
||||
|
||||
图标资源位于 `server/2015Remote/res/` 目录:
|
||||
|
||||
| 文件 | 用途 | 尺寸要求 |
|
||||
|------|------|---------|
|
||||
| `2015Remote.ico` | 主程序图标 | 多尺寸 ICO |
|
||||
| `tray_online.ico` | 托盘在线图标 | 16x16, 32x32 |
|
||||
| `tray_offline.ico` | 托盘离线图标 | 16x16, 32x32 |
|
||||
|
||||
#### 7.2 图标格式要求
|
||||
|
||||
ICO 文件应包含多种尺寸:
|
||||
- 16x16(小图标、托盘)
|
||||
- 32x32(标准图标)
|
||||
- 48x48(大图标)
|
||||
- 256x256(高清图标)
|
||||
|
||||
**推荐工具:**
|
||||
- IcoFX
|
||||
- GIMP
|
||||
- 在线 ICO 生成器
|
||||
|
||||
#### 7.3 替换步骤
|
||||
|
||||
1. 准备新图标文件
|
||||
2. 备份原有图标
|
||||
3. 将新图标复制到 `res/` 目录,保持文件名一致
|
||||
4. 重新编译程序
|
||||
|
||||
### 8. 启动画面定制
|
||||
|
||||
启动画面的 Logo 文字由 `BRAND_SPLASH_NAME` 配置:
|
||||
|
||||
```cpp
|
||||
#define BRAND_SPLASH_NAME "YOURBRAND"
|
||||
```
|
||||
|
||||
**建议:**
|
||||
- 使用大写字母,视觉效果更好
|
||||
- 保持简短(建议 8 个字符以内)
|
||||
- 可以使用中文
|
||||
|
||||
---
|
||||
|
||||
## 第四部分:界面文字定制
|
||||
|
||||
### 9. 语言文件机制
|
||||
|
||||
YAMA 使用 INI 格式的语言文件实现多语言支持。
|
||||
|
||||
#### 9.1 语言文件位置
|
||||
|
||||
**源代码位置:**
|
||||
```
|
||||
server/2015Remote/lang/
|
||||
├── en_US.ini # 英文翻译
|
||||
├── zh_TW.ini # 繁体中文翻译
|
||||
└── (其他语言)
|
||||
```
|
||||
|
||||
**运行时位置:** 程序会从程序同目录的 `lang/` 文件夹读取语言文件。
|
||||
|
||||
**发布时:** 将 `lang/` 文件夹与程序放在同一目录。
|
||||
|
||||
#### 9.2 文件格式
|
||||
|
||||
```ini
|
||||
; 注释行
|
||||
简体中文原文=翻译文本
|
||||
另一条原文=另一条翻译
|
||||
```
|
||||
|
||||
**编码要求:** GB2312(不是 UTF-8!)
|
||||
|
||||
#### 9.3 添加翻译
|
||||
|
||||
由于语言文件必须使用 GB2312 编码,建议使用以下方法添加翻译:
|
||||
|
||||
**方法 A:使用支持 GB2312 编码的编辑器**
|
||||
|
||||
1. 使用 Notepad++、VS Code(安装编码插件)等编辑器
|
||||
2. 打开语言文件,确认编码为 GB2312
|
||||
3. 添加新的翻译条目
|
||||
4. 保存时确保保持 GB2312 编码
|
||||
|
||||
**方法 B:使用 PowerShell 命令**
|
||||
|
||||
```powershell
|
||||
# 追加单条翻译
|
||||
$gb2312 = [System.Text.Encoding]::GetEncoding('GB2312')
|
||||
$content = "新功能=New Feature`r`n"
|
||||
[System.IO.File]::AppendAllText('lang\en_US.ini', $content, $gb2312)
|
||||
```
|
||||
|
||||
> **注意**:直接用记事本编辑可能导致编码错误,请使用专业编辑器。
|
||||
|
||||
### 10. 创建新语言包
|
||||
|
||||
1. 复制 `en_US.ini` 作为模板
|
||||
2. 重命名为新语言代码(如 `ja_JP.ini`)
|
||||
3. 翻译所有条目
|
||||
4. 保存为 GB2312 编码
|
||||
|
||||
---
|
||||
|
||||
## 第五部分:开发环境
|
||||
|
||||
### 11. 环境要求
|
||||
|
||||
| 组件 | 版本要求 |
|
||||
|------|---------|
|
||||
| Visual Studio | 2019 或 2022 |
|
||||
| Windows SDK | 10.0.19041.0 或更高 |
|
||||
| C++ 标准 | C++17 |
|
||||
| 工具集 | v142 (MSVC v142) |
|
||||
| MFC 库 | C++ MFC for v142 build tools |
|
||||
|
||||
### 12. 项目结构
|
||||
|
||||
```
|
||||
SimpleRemoter/
|
||||
├── server/2015Remote/ # 主控程序源码
|
||||
│ ├── UIBranding.h # 品牌配置
|
||||
│ ├── 2015RemoteDlg.cpp # 主对话框
|
||||
│ ├── SettingDlg.cpp # 设置页面
|
||||
│ ├── BuildDlg.cpp # 生成对话框
|
||||
│ ├── res/ # 资源文件
|
||||
│ └── lang/ # 语言文件
|
||||
├── client/ # 受管端源码
|
||||
├── common/ # 公共代码
|
||||
├── linux/ # Linux 客户端
|
||||
└── Bin/ # 编译输出
|
||||
```
|
||||
|
||||
### 13. 关键文件说明
|
||||
|
||||
| 文件 | 功能 |
|
||||
|------|------|
|
||||
| `UIBranding.h` | 品牌和功能开关配置 |
|
||||
| `2015RemoteDlg.cpp` | 主窗口、菜单、状态栏 |
|
||||
| `SettingDlg.cpp` | 设置对话框 |
|
||||
| `BuildDlg.cpp` | 生成受管程序对话框 |
|
||||
| `CPasswordDlg.cpp` | 授权生成对话框 |
|
||||
| `FrpsForSubDlg.cpp` | FRP 代理配置 |
|
||||
| `ScreenSpyDlg.cpp` | 远程桌面窗口 |
|
||||
| `FileManagerDlg.cpp` | 文件管理窗口 |
|
||||
| `TerminalDlg.cpp` | 远程终端窗口 |
|
||||
|
||||
---
|
||||
|
||||
## 第六部分:编译与发布
|
||||
|
||||
### 14. 编译脚本
|
||||
|
||||
使用提供的编译脚本:
|
||||
|
||||
```powershell
|
||||
# 完整编译(先编译依赖项,再编译主控)
|
||||
.\build.ps1
|
||||
|
||||
# 仅编译主控程序(跳过依赖项,适合快速重编译)
|
||||
.\build.ps1 -ServerOnly
|
||||
|
||||
# 发版模式(重编译所有 + UPX 压缩)
|
||||
.\build.ps1 -Publish
|
||||
|
||||
# Debug 模式
|
||||
.\build.ps1 -Config Debug
|
||||
|
||||
# 编译 32 位和 64 位
|
||||
.\build.ps1 -Platform all
|
||||
|
||||
# 清理后重编译
|
||||
.\build.ps1 -Clean
|
||||
```
|
||||
|
||||
### 15. 编译输出
|
||||
|
||||
| 输出文件 | 位置 |
|
||||
|---------|------|
|
||||
| 64 位主控 | `Bin/Yama_x64.exe` |
|
||||
| 32 位主控 | `Bin/Yama_x86.exe` |
|
||||
|
||||
### 16. 发布准备
|
||||
|
||||
#### 16.1 发布检查清单
|
||||
|
||||
- [ ] 修改 `UIBranding.h` 中的品牌信息
|
||||
- [ ] 替换程序图标(如需要)
|
||||
- [ ] 使用 Release 配置编译
|
||||
- [ ] 使用 `-Publish` 进行 UPX 压缩
|
||||
- [ ] 测试所有功能正常
|
||||
- [ ] 测试授权导入功能
|
||||
- [ ] 测试受管程序生成
|
||||
|
||||
#### 16.2 文件清单
|
||||
|
||||
发布时需要包含:
|
||||
|
||||
```
|
||||
YourBrand/
|
||||
├── YourBrand.exe # 主程序(重命名)
|
||||
└── lang/ # 语言文件(可选)
|
||||
├── en_US.ini
|
||||
└── zh_TW.ini
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第七部分:常见定制场景
|
||||
|
||||
### 17. 场景一:简单品牌替换
|
||||
|
||||
**需求:** 更换程序名称和图标
|
||||
|
||||
**步骤:**
|
||||
1. 修改 `UIBranding.h` 中的品牌名称
|
||||
2. 替换 `res/` 目录中的图标文件
|
||||
3. 编译:`.\build.ps1 -Publish`
|
||||
4. 重命名输出文件
|
||||
|
||||
### 18. 场景二:功能精简版
|
||||
|
||||
**需求:** 创建只保留基本功能的精简版
|
||||
|
||||
**步骤:**
|
||||
1. 在 `UIBranding.h` 中隐藏高级功能:
|
||||
|
||||
```cpp
|
||||
// 隐藏开发相关
|
||||
#define HIDE_MENU_SHELLCODE_C 1
|
||||
#define HIDE_MENU_SHELLCODE_BIN 1
|
||||
#define HIDE_MENU_V2_PRIVATEKEY 1
|
||||
#define HIDE_MENU_RCEDIT 1
|
||||
|
||||
// 隐藏高级功能
|
||||
#define HIDE_TOOLBAR_AUDIO 1
|
||||
#define HIDE_TOOLBAR_VIDEO 1
|
||||
#define HIDE_TOOLBAR_REGISTER 1
|
||||
#define HIDE_TOOLBAR_KEYBOARD 1
|
||||
```
|
||||
|
||||
2. 编译发布
|
||||
|
||||
### 19. 场景三:多品牌版本
|
||||
|
||||
**需求:** 为不同客户生成不同品牌的版本
|
||||
|
||||
**方案:**
|
||||
1. 创建多个 `UIBranding.h` 副本(如 `UIBranding_ClientA.h`)
|
||||
2. 编译前复制对应配置文件覆盖 `UIBranding.h`
|
||||
3. 编译并重命名输出
|
||||
4. 或编写脚本自动化此过程
|
||||
|
||||
---
|
||||
|
||||
## 第八部分:最佳实践
|
||||
|
||||
### 20. 版本管理建议
|
||||
|
||||
#### 20.1 分支策略
|
||||
|
||||
```
|
||||
main (或 master) # 上游原版
|
||||
├── custom/base # 基础定制
|
||||
├── custom/clientA # 客户 A 定制
|
||||
└── custom/clientB # 客户 B 定制
|
||||
```
|
||||
|
||||
#### 20.2 保持同步
|
||||
|
||||
定期从上游合并更新:
|
||||
|
||||
```bash
|
||||
git fetch upstream
|
||||
git merge upstream/main
|
||||
# 解决冲突后提交
|
||||
```
|
||||
|
||||
### 21. 定制文档
|
||||
|
||||
建议记录定制内容:
|
||||
|
||||
| 记录项 | 说明 |
|
||||
|--------|------|
|
||||
| 品牌配置 | 修改了哪些 `UIBranding.h` 配置 |
|
||||
| 隐藏功能 | 隐藏了哪些菜单/按钮 |
|
||||
| 图标变更 | 替换了哪些图标文件 |
|
||||
| 源码修改 | 如有源码修改,记录位置和原因 |
|
||||
|
||||
### 22. 测试清单
|
||||
|
||||
每次定制后测试:
|
||||
|
||||
- [ ] 程序能正常启动
|
||||
- [ ] 品牌信息显示正确
|
||||
- [ ] 图标显示正确
|
||||
- [ ] 授权导入功能正常
|
||||
- [ ] 受管程序生成正常
|
||||
- [ ] 远程桌面功能正常
|
||||
- [ ] 文件管理功能正常
|
||||
- [ ] 隐藏的功能确实不显示
|
||||
|
||||
---
|
||||
|
||||
## 附录
|
||||
|
||||
### A. 配置项速查表
|
||||
|
||||
| 配置项 | 文件 | 说明 |
|
||||
|--------|------|------|
|
||||
| 程序名称 | `UIBranding.h` | `BRAND_APP_NAME` |
|
||||
| 版本号 | `UIBranding.h` | `BRAND_VERSION` |
|
||||
| 启动 Logo | `UIBranding.h` | `BRAND_SPLASH_NAME` |
|
||||
| 托盘提示 | `UIBranding.h` | `BRAND_TRAY_TIP` |
|
||||
| 版权信息 | `UIBranding.h` | `BRAND_COPYRIGHT` |
|
||||
| 隐藏菜单 | `UIBranding.h` | `HIDE_MENU_*` |
|
||||
| 隐藏工具栏 | `UIBranding.h` | `HIDE_TOOLBAR_*` |
|
||||
| 隐藏右键菜单 | `UIBranding.h` | `HIDE_CTX_*` |
|
||||
| 界面翻译 | `lang/*.ini` | 键值对 |
|
||||
|
||||
### B. 资源文件清单
|
||||
|
||||
| 资源 | 位置 | 格式 |
|
||||
|------|------|------|
|
||||
| 主程序图标 | `res/2015Remote.ico` | ICO |
|
||||
| 托盘图标 | `res/tray_*.ico` | ICO |
|
||||
| 工具栏图标 | `res/toolbar.bmp` | BMP |
|
||||
|
||||
### C. 常见问题
|
||||
|
||||
| 问题 | 原因 | 解决方案 |
|
||||
|------|------|---------|
|
||||
| 编译报错 | VS 版本/工具集 | 确认使用 VS2019+ 和 v142 工具集 |
|
||||
| 中文乱码 | 文件编码错误 | UIBranding.h 需 UTF-8 with BOM |
|
||||
| 图标不显示 | 格式不正确 | 确认 ICO 包含多尺寸 |
|
||||
| 翻译不生效 | 编码错误 | 语言文件需 GB2312 编码 |
|
||||
| 菜单仍显示 | 未重编译 | 修改 UIBranding.h 后需重编译 |
|
||||
|
||||
### D. 注意事项
|
||||
|
||||
**请勿修改以下配置**(会导致功能异常):
|
||||
|
||||
```cpp
|
||||
// 修改会导致所有已发放许可证失效
|
||||
#define BRAND_LICENSE_MAGIC "YAMA"
|
||||
|
||||
// 修改会导致单实例检测失败
|
||||
#define BRAND_EVENT_PREFIX "YAMA"
|
||||
|
||||
// 修改会导致超管密码配置失效
|
||||
#define BRAND_ENV_VAR "YAMA_PWD"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 技术支持
|
||||
|
||||
| 渠道 | 联系方式 |
|
||||
|------|---------|
|
||||
| QQ | 962914132 |
|
||||
| Telegram | [@doge_grandfather](https://t.me/doge_grandfather) |
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [快速部署指南](QuickStart.md) - 基础部署
|
||||
- [多级网络搭建指南](NetworkSetup.md) - 网络架构
|
||||
- [日常使用手册](UserManual.md) - 功能使用
|
||||
- [代理商运营手册](AgentManual.md) - 授权管理
|
||||
233
docs/FILE_TRANSFER_V2.md
Normal file
233
docs/FILE_TRANSFER_V2.md
Normal file
@@ -0,0 +1,233 @@
|
||||
# V2 文件/文本传输功能清单
|
||||
|
||||
## 一、文件传输
|
||||
|
||||
| 方向 | 触发方式 | 断点续传 | 进度显示 | 备注 |
|
||||
|------|----------|:--------:|:--------:|------|
|
||||
| 服务端 → 客户端 | 文件管理器拖放/Ctrl+V | ✅ | ✅ | 支持大文件(>4GB) |
|
||||
| 客户端 → 服务端 | 文件管理器下载 | ✅ | ✅ | 支持大文件(>4GB) |
|
||||
| 客户端A → 客户端B (C2C) | 远程桌面 Ctrl+C/Ctrl+V | ✅ | ✅ | 自动捕获目标目录 |
|
||||
|
||||
## 二、文本传输
|
||||
|
||||
| 方向 | 触发方式 | 说明 |
|
||||
|------|----------|------|
|
||||
| 服务端 → 客户端 | 远程桌面剪贴板同步 | `COMMAND_SCREEN_SET_CLIPBOARD` |
|
||||
| 客户端 → 服务端 | 远程桌面剪贴板同步 | `COMMAND_SCREEN_GET_CLIPBOARD` |
|
||||
| 客户端A → 客户端B (C2C) | 远程桌面 Ctrl+C/Ctrl+V | 自动粘贴 |
|
||||
|
||||
## 三、C2C 传输流程
|
||||
|
||||
```
|
||||
┌─────────────┐ Ctrl+V ┌─────────────┐ CLIPBOARD_V2 ┌─────────────┐
|
||||
│ 远程桌面B │ ───────────────>│ 服务端 │ ──────────────────>│ 远程桌面A │
|
||||
│ (目标) │ │ │ │ (源) │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
▲ │ │
|
||||
│ │ C2C_PREPARE │
|
||||
│<──────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────┐ │
|
||||
│ SEND_FILE_V2/C2C_TEXT │ 服务端 │ SEND_FILE_V2/C2C_TEXT │
|
||||
│<────────────────────────│ (转发) │<──────────────────────────┘
|
||||
│ └─────────────┘
|
||||
```
|
||||
|
||||
## 四、协议命令
|
||||
|
||||
| 命令 | 值 | 用途 |
|
||||
|------|:--:|------|
|
||||
| `COMMAND_SEND_FILE_V2` | 85 | V2文件数据包 |
|
||||
| `COMMAND_FILE_RESUME` | 86 | 断点续传响应 |
|
||||
| `COMMAND_CLIPBOARD_V2` | 87 | C2C剪贴板请求 |
|
||||
| `COMMAND_FILE_QUERY_RESUME` | 88 | 断点续传查询 |
|
||||
| `COMMAND_C2C_PREPARE` | 89 | C2C准备接收通知 |
|
||||
| `COMMAND_C2C_TEXT` | 90 | C2C文本传输 |
|
||||
| `COMMAND_FILE_COMPLETE_V2` | 91 | V2文件完成校验包 |
|
||||
| `COMMAND_C2C_PREPARE_RESP` | 92 | C2C准备响应(返回目标目录) |
|
||||
|
||||
## 五、核心特性
|
||||
|
||||
- **断点续传**: 基于 transferID + 文件特征匹配,支持传输中断后恢复
|
||||
- **大文件支持**: 使用 uint64_t 存储文件大小和偏移,突破4GB限制
|
||||
- **自动目标目录**: C2C传输自动捕获目标客户端当前焦点窗口的目录
|
||||
- **自动粘贴**: C2C文本传输完成后自动模拟 Ctrl+V 完成粘贴
|
||||
- **版本协商**: 能力位 `CLIENT_CAP_V2` + 版本日期双重检测
|
||||
- **文件完整性校验**: 传输完成后发送 SHA-256 校验包,接收端自动验证
|
||||
- **服务端开关**: 参数菜单可控制是否启用 V2(默认关闭)
|
||||
- **自动清理**: 状态文件超过 7 天自动删除
|
||||
|
||||
## 5.1 文件完整性校验
|
||||
|
||||
每个文件传输完成后,发送端计算并发送 `COMMAND_FILE_COMPLETE_V2` 校验包:
|
||||
|
||||
```cpp
|
||||
struct FileCompletePacketV2 {
|
||||
uint8_t cmd; // = 91
|
||||
uint64_t transferID;
|
||||
uint64_t srcClientID;
|
||||
uint64_t dstClientID;
|
||||
uint32_t fileIndex;
|
||||
uint64_t fileSize;
|
||||
uint8_t sha256[32]; // SHA-256 哈希
|
||||
}; // 69 bytes
|
||||
```
|
||||
|
||||
**工作流程**:
|
||||
1. 发送端在发送文件数据同时计算 SHA-256
|
||||
2. 文件最后一个块发送完成后,发送 `FILE_COMPLETE_V2` 校验包
|
||||
3. 接收端在接收数据时同步计算 SHA-256(流式计算,无需重读文件)
|
||||
4. 收到校验包后对比哈希值,输出校验结果
|
||||
|
||||
**支持的传输方向**:
|
||||
| 方向 | 校验位置 |
|
||||
|------|----------|
|
||||
| 客户端 → 服务端 | 服务端校验 |
|
||||
| 服务端 → 客户端 | 客户端校验 |
|
||||
| 客户端A → 客户端B (C2C) | 服务端转发,客户端B校验 |
|
||||
|
||||
**校验失败处理**:
|
||||
- 校验失败时自动删除损坏文件
|
||||
- 日志输出期望和实际的 SHA-256 哈希值
|
||||
|
||||
**断点续传与校验**:
|
||||
- 断点续传时无法使用流式 SHA-256(缺少历史数据)
|
||||
- 自动切换为从文件重新计算完整 SHA-256
|
||||
|
||||
## 六、相关文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `common/commands.h` | 协议命令定义 |
|
||||
| `common/file_upload.h/cpp` | V2传输核心实现 |
|
||||
| `client/KernelManager.cpp` | 客户端主连接处理 |
|
||||
| `client/ScreenManager.cpp` | 客户端远程桌面会话处理 |
|
||||
| `server/2015Remote/2015RemoteDlg.cpp` | 服务端主逻辑 |
|
||||
| `server/2015Remote/ScreenSpyDlg.cpp` | 服务端远程桌面会话处理 |
|
||||
| `server/2015Remote/CDlgFileSend.cpp` | 文件传输进度对话框 |
|
||||
|
||||
## 七、使用说明
|
||||
|
||||
### C2C 文件传输
|
||||
1. 打开两个远程桌面窗口(源A和目标B)
|
||||
2. 在源A上选中文件,按 Ctrl+C
|
||||
3. 按 X 切换到目标B
|
||||
4. 按 Ctrl+V,文件自动传输到B当前目录
|
||||
|
||||
### C2C 文本传输
|
||||
1. 在源A上复制文本(Ctrl+C)
|
||||
2. 按 X 切换到目标B
|
||||
3. 在目标B的应用中按 Ctrl+V,文本自动粘贴
|
||||
|
||||
### 断点续传
|
||||
- 传输中断后,重新执行相同操作
|
||||
- 系统自动检测已传输部分,从断点继续
|
||||
- 状态文件保存在 `%TEMP%\FileTransfer\`
|
||||
- 状态文件超过 7 天自动删除
|
||||
|
||||
### 状态文件说明
|
||||
|
||||
| 后缀 | 用途 | 说明 |
|
||||
|------|------|------|
|
||||
| `.resume` | 断点续传状态 | 记录 transferID、文件列表、已接收字节数 |
|
||||
| `.recv` | C2C 接收方目录 | 记录目标客户端当前焦点窗口目录 |
|
||||
| `.send` | C2C 发送方目录 | 记录源客户端剪贴板文件路径 |
|
||||
|
||||
所有状态文件统一存放在 `%TEMP%\FileTransfer\` 目录,文件名格式为 `{transferID}.{后缀}`。
|
||||
|
||||
---
|
||||
|
||||
## 八、代码审查报告
|
||||
|
||||
### 8.1 审查范围
|
||||
|
||||
| 文件 | 改动行数 | 审查结果 |
|
||||
|------|:--------:|:--------:|
|
||||
| `common/commands.h` | +16 | ✅ |
|
||||
| `common/file_upload.h` | +288 | ✅ |
|
||||
| `SimplePlugins/file_upload.cpp` | 核心实现 | ✅ |
|
||||
| `client/KernelManager.cpp` | +294 | ✅ |
|
||||
| `client/ScreenManager.cpp` | +360 | ✅ |
|
||||
| `server/2015Remote/2015RemoteDlg.cpp` | +698 | ✅ |
|
||||
| `server/2015Remote/CDlgFileSend.cpp` | +116 | ✅ |
|
||||
| `server/2015Remote/context.h` | +34 | ✅ |
|
||||
|
||||
### 8.2 代码质量评估
|
||||
|
||||
#### 优点
|
||||
|
||||
1. **协议设计清晰** - 结构体使用 `#pragma pack(push, 1)` 确保跨平台对齐,注释详尽
|
||||
2. **版本兼容性** - V1/V2 协议共存,旧客户端仍可使用
|
||||
3. **能力协商机制完善** - `CLIENT_CAP_V2` 能力位 + 版本日期双重检测
|
||||
4. **断点续传设计** - `transferID + fileIndex` 组合键唯一标识,状态持久化
|
||||
5. **线程安全** - 使用 `std::mutex` 保护共享状态(`g_fileStatesV2Mtx` 等)
|
||||
6. **智能指针** - `std::unique_ptr` 管理内存,RAII 处理文件句柄
|
||||
7. **SHA-256 校验** - 使用 Windows bcrypt API,流式计算
|
||||
|
||||
#### 待改进项
|
||||
|
||||
| 优先级 | 问题 | 位置 | 建议 |
|
||||
|:------:|------|------|------|
|
||||
| 🟠 中 | 路径穿越未验证 | `file_upload.cpp:712` | `GetRelativePath` 添加 `..` 检查 |
|
||||
| 🟠 中 | 变量命名误导 | `file_upload.cpp:2463` | `utf8Name` 实际是 ANSI,应改名 |
|
||||
| 🟡 低 | 重复代码 | `KernelManager.cpp` / `ScreenManager.cpp` | C2C_TEXT 处理逻辑可提取公共函数 |
|
||||
|
||||
### 8.3 安全审查
|
||||
|
||||
| 检查项 | 状态 | 说明 |
|
||||
|--------|:----:|------|
|
||||
| 缓冲区溢出 | ✅ | 有长度校验 `if (len < sizeof(...) + nameLength + dataLength)` |
|
||||
| 认证校验 | ✅ | 使用 hash + hmac 验证 |
|
||||
| 路径穿越 | ⚠️ | `GetRelativePath` 未检查 `..` 序列(实际风险低) |
|
||||
| 客户端ID伪造 | ✅ | 通过 `FindHost` 验证客户端存在 |
|
||||
| 文件句柄泄漏 | ✅ | RAII 析构函数处理 |
|
||||
| 整数溢出 | ✅ | 使用 `uint64_t` 支持大文件 |
|
||||
|
||||
**路径穿越风险分析**:
|
||||
- `GetRelativePath` 未过滤 `..` 序列
|
||||
- 实际风险较低:C2C 由服务端键盘钩子触发,非任意用户输入
|
||||
- 建议后续版本加固
|
||||
|
||||
### 8.4 边界条件测试建议
|
||||
|
||||
| 场景 | 测试点 |
|
||||
|------|--------|
|
||||
| 大文件 | >4GB 文件传输和断点续传 |
|
||||
| 网络抖动 | 传输中断后恢复 |
|
||||
| 并发传输 | 多个 C2C 同时进行 |
|
||||
| 客户端重启 | `.resume` 文件恢复 + SHA-256 重算 |
|
||||
| 目录结构 | 深层嵌套目录创建 |
|
||||
| 空文件 | 0 字节文件传输和校验 |
|
||||
|
||||
---
|
||||
|
||||
## 九、提交评估
|
||||
|
||||
### 9.1 总体评估:✅ 可发布
|
||||
|
||||
| 指标 | 状态 | 说明 |
|
||||
|------|:----:|------|
|
||||
| 功能完整性 | ✅ | C2C/断点续传/SHA-256 校验均已实现 |
|
||||
| 编译通过 | ✅ | 无编译错误 |
|
||||
| 向后兼容 | ✅ | V1 协议仍可用,能力协商正确 |
|
||||
| 崩溃风险 | ✅ | 无明显崩溃点 |
|
||||
| 数据安全 | ✅ | HMAC 认证、长度校验完善 |
|
||||
|
||||
### 9.2 问题分类
|
||||
|
||||
| 严重程度 | 数量 | 是否阻塞提交 |
|
||||
|:--------:|:----:|:------------:|
|
||||
| 🔴 致命 | 0 | - |
|
||||
| 🟠 中等 | 2 | 否 |
|
||||
| 🟡 低危 | 1 | 否 |
|
||||
|
||||
### 9.3 后续改进建议
|
||||
|
||||
1. **路径安全加固** - 在 `GetRelativePath` 添加 `..` 过滤
|
||||
2. **重复代码重构** - 提取 C2C_TEXT 处理为公共函数
|
||||
3. **变量命名修正** - `utf8Name` → `ansiName`
|
||||
4. **日志级别控制** - 添加 verbose 模式开关
|
||||
|
||||
---
|
||||
|
||||
*审查日期: 2026-02-27*
|
||||
562
docs/FeatureLimits_Design.md
Normal file
562
docs/FeatureLimits_Design.md
Normal file
@@ -0,0 +1,562 @@
|
||||
# 下级功能限制方案设计
|
||||
|
||||
## 一、背景
|
||||
|
||||
当前的 UI 定制化(UIBranding.h)是编译时配置,适用于开发商自己定制品牌。但在生成下级主控时,上级可能希望限制下级可用的功能(如禁止下级使用"生成Master"功能),这需要运行时配置机制。
|
||||
|
||||
### 需求场景
|
||||
|
||||
```
|
||||
超级管理员(完整功能)
|
||||
│
|
||||
├─→ 生成主控A(禁用:钱包、生成Master)
|
||||
│ │
|
||||
│ └─→ 主控A的下级(继承禁用 + 可能更多限制)
|
||||
│
|
||||
└─→ 生成主控B(禁用:授权管理、ShellCode)
|
||||
```
|
||||
|
||||
### 设计目标
|
||||
|
||||
1. 生成主控时可选择禁用哪些功能
|
||||
2. 功能限制固化到生成的程序中
|
||||
3. 下级只能继承限制,不能解除
|
||||
4. 与现有 UIBranding 编译时配置兼容
|
||||
|
||||
---
|
||||
|
||||
## 二、技术方案
|
||||
|
||||
### 2.1 存储位置
|
||||
|
||||
复用现有的 `g_UpperHash` 数组未使用空间,无需新增标记区域。
|
||||
|
||||
```
|
||||
g_UpperHash[260] 当前布局:
|
||||
├─ [0-99] MASTER_HASH_STR 标记(用于搜索定位)
|
||||
├─ [100-163] 64字节上级哈希
|
||||
└─ [164-259] 未使用(96字节) ← 用于存储功能标志
|
||||
```
|
||||
|
||||
### 2.2 扩展布局
|
||||
|
||||
```
|
||||
g_UpperHash[260] 扩展布局:
|
||||
├─ [0-99] MASTER_HASH_STR 标记
|
||||
├─ [100-163] 64字节上级哈希
|
||||
├─ [164-167] 版本标识 "FLG1"
|
||||
├─ [168-175] MenuFlags 主菜单位图 (64位,支持64个菜单项)
|
||||
├─ [176-183] ToolbarFlags 工具栏位图 (64位)
|
||||
├─ [184-191] ContextFlags 右键菜单位图 (64位)
|
||||
└─ [192-259] 预留 (68字节)
|
||||
```
|
||||
|
||||
### 2.3 向后兼容
|
||||
|
||||
| 场景 | [164-167] 值 | 行为 |
|
||||
|------|-------------|------|
|
||||
| 旧版本程序 | 全零 | 无运行时限制,仅编译时配置生效 |
|
||||
| 新版本 + 无限制 | "FLG1" + 全零位图 | 所有功能可用 |
|
||||
| 新版本 + 有限制 | "FLG1" + 非零位图 | 按位图隐藏功能 |
|
||||
|
||||
---
|
||||
|
||||
## 三、数据结构
|
||||
|
||||
### 3.1 功能标志结构体
|
||||
|
||||
```cpp
|
||||
// FeatureFlags.h
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define FEATURE_FLAGS_VERSION "FLG1"
|
||||
#define FEATURE_FLAGS_OFFSET 164 // g_UpperHash 中的偏移
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct FeatureFlags {
|
||||
char Version[4]; // "FLG1" 版本标识
|
||||
uint64_t MenuFlags; // 主菜单位图 (64位,支持64个菜单项)
|
||||
uint64_t ToolbarFlags; // 工具栏位图 (64位)
|
||||
uint64_t ContextFlags; // 右键菜单位图 (64位)
|
||||
char Reserved[68]; // 预留扩展
|
||||
} FeatureFlags; // 4 + 8 + 8 + 8 + 68 = 96 bytes
|
||||
#pragma pack(pop)
|
||||
```
|
||||
|
||||
### 3.2 位图定义(完整映射表)
|
||||
|
||||
位图定义与 UIBranding.h 中的宏一一对应,便于实施时查找。
|
||||
|
||||
```cpp
|
||||
// ===== MenuFlags 位定义 (uint64_t) =====
|
||||
// 与 UIBranding.h 中 HIDE_MENU_* 宏对应
|
||||
|
||||
// 文件菜单 [0-3]
|
||||
#define MF_SETTINGS (1ULL << 0) // HIDE_MENU_SETTINGS
|
||||
#define MF_NOTIFY_SETTINGS (1ULL << 1) // HIDE_MENU_NOTIFY_SETTINGS
|
||||
#define MF_WALLET (1ULL << 2) // HIDE_MENU_WALLET
|
||||
#define MF_NETWORK (1ULL << 3) // HIDE_MENU_NETWORK
|
||||
|
||||
// 工具菜单 [4-10]
|
||||
#define MF_INPUT_PASSWORD (1ULL << 4) // HIDE_MENU_INPUT_PASSWORD
|
||||
#define MF_IMPORT_LICENSE (1ULL << 5) // HIDE_MENU_IMPORT_LICENSE
|
||||
#define MF_RCEDIT (1ULL << 6) // HIDE_MENU_RCEDIT
|
||||
#define MF_GEN_AUTH (1ULL << 7) // HIDE_MENU_GEN_AUTH
|
||||
#define MF_GEN_MASTER (1ULL << 8) // HIDE_MENU_GEN_MASTER
|
||||
#define MF_LICENSE_MGR (1ULL << 9) // HIDE_MENU_LICENSE_MGR
|
||||
#define MF_V2_PRIVATEKEY (1ULL << 10) // HIDE_MENU_V2_PRIVATEKEY
|
||||
|
||||
// ShellCode子菜单 [11-19]
|
||||
#define MF_SHELLCODE_C (1ULL << 11) // HIDE_MENU_SHELLCODE_C
|
||||
#define MF_SHELLCODE_BIN (1ULL << 12) // HIDE_MENU_SHELLCODE_BIN
|
||||
#define MF_SHELLCODE_LOAD_TEST (1ULL << 13) // HIDE_MENU_SHELLCODE_LOAD_TEST
|
||||
#define MF_SHELLCODE_OBFS (1ULL << 14) // HIDE_MENU_SHELLCODE_OBFS
|
||||
#define MF_SHELLCODE_OBFS_BIN (1ULL << 15) // HIDE_MENU_SHELLCODE_OBFS_BIN
|
||||
#define MF_SHELLCODE_OBFS_TEST (1ULL << 16) // HIDE_MENU_SHELLCODE_OBFS_TEST
|
||||
#define MF_SHELLCODE_AES_C (1ULL << 17) // HIDE_MENU_SHELLCODE_AES_C
|
||||
#define MF_SHELLCODE_AES_BIN (1ULL << 18) // HIDE_MENU_SHELLCODE_AES_BIN
|
||||
#define MF_SHELLCODE_AES_TEST (1ULL << 19) // HIDE_MENU_SHELLCODE_AES_TEST
|
||||
|
||||
// 参数菜单 [20-27]
|
||||
#define MF_KBLOGGER (1ULL << 20) // HIDE_MENU_KBLOGGER
|
||||
#define MF_LOGIN_NOTIFY (1ULL << 21) // HIDE_MENU_LOGIN_NOTIFY
|
||||
#define MF_ENABLE_LOG (1ULL << 22) // HIDE_MENU_ENABLE_LOG
|
||||
#define MF_PRIVACY_WALLPAPER (1ULL << 23) // HIDE_MENU_PRIVACY_WALLPAPER
|
||||
#define MF_FILE_V2 (1ULL << 24) // HIDE_MENU_FILE_V2
|
||||
#define MF_HOOK_WIN (1ULL << 25) // HIDE_MENU_HOOK_WIN
|
||||
#define MF_RUN_AS_SERVICE (1ULL << 26) // HIDE_MENU_RUN_AS_SERVICE
|
||||
#define MF_RUN_AS_USER (1ULL << 27) // HIDE_MENU_RUN_AS_USER
|
||||
|
||||
// 扩展菜单 [28-37]
|
||||
#define MF_HISTORY_CLIENTS (1ULL << 28) // HIDE_MENU_HISTORY_CLIENTS
|
||||
#define MF_BACKUP_DATA (1ULL << 29) // HIDE_MENU_BACKUP_DATA
|
||||
#define MF_IMPORT_DATA (1ULL << 30) // HIDE_MENU_IMPORT_DATA
|
||||
#define MF_RELOAD_PLUGINS (1ULL << 31) // HIDE_MENU_RELOAD_PLUGINS
|
||||
#define MF_PLUGIN_REQUEST (1ULL << 32) // HIDE_MENU_PLUGIN_REQUEST
|
||||
#define MF_FRPS_FOR_SUB (1ULL << 33) // HIDE_MENU_FRPS_FOR_SUB
|
||||
#define MF_CHANGE_LANG (1ULL << 34) // HIDE_MENU_CHANGE_LANG
|
||||
#define MF_CHOOSE_LANG_DIR (1ULL << 35) // HIDE_MENU_CHOOSE_LANG_DIR
|
||||
#define MF_LOCATION_QQWRY (1ULL << 36) // HIDE_MENU_LOCATION_QQWRY
|
||||
#define MF_LOCATION_IP2REGION (1ULL << 37) // HIDE_MENU_LOCATION_IP2REGION
|
||||
|
||||
// 帮助菜单 [38-42]
|
||||
#define MF_IMPORTANT (1ULL << 38) // HIDE_MENU_IMPORTANT
|
||||
#define MF_FEEDBACK (1ULL << 39) // HIDE_MENU_FEEDBACK
|
||||
#define MF_WHAT_IS_THIS (1ULL << 40) // HIDE_MENU_WHAT_IS_THIS
|
||||
#define MF_MASTER_TRAIL (1ULL << 41) // HIDE_MENU_MASTER_TRAIL
|
||||
#define MF_REQUEST_AUTH (1ULL << 42) // HIDE_MENU_REQUEST_AUTH
|
||||
|
||||
// [43-63] 预留
|
||||
|
||||
|
||||
// ===== ToolbarFlags 位定义 (uint64_t) =====
|
||||
// 与 UIBranding.h 中 HIDE_TOOLBAR_* 宏对应,按索引顺序
|
||||
|
||||
#define TF_TERMINAL (1ULL << 0) // HIDE_TOOLBAR_TERMINAL
|
||||
#define TF_PROCESS (1ULL << 1) // HIDE_TOOLBAR_PROCESS
|
||||
#define TF_WINDOW (1ULL << 2) // HIDE_TOOLBAR_WINDOW
|
||||
#define TF_DESKTOP (1ULL << 3) // HIDE_TOOLBAR_DESKTOP
|
||||
#define TF_FILE (1ULL << 4) // HIDE_TOOLBAR_FILE
|
||||
#define TF_AUDIO (1ULL << 5) // HIDE_TOOLBAR_AUDIO
|
||||
#define TF_VIDEO (1ULL << 6) // HIDE_TOOLBAR_VIDEO
|
||||
#define TF_SERVICE (1ULL << 7) // HIDE_TOOLBAR_SERVICE
|
||||
#define TF_REGISTER (1ULL << 8) // HIDE_TOOLBAR_REGISTER
|
||||
#define TF_KEYBOARD (1ULL << 9) // HIDE_TOOLBAR_KEYBOARD
|
||||
#define TF_SETTINGS (1ULL << 10) // HIDE_TOOLBAR_SETTINGS
|
||||
#define TF_BUILD (1ULL << 11) // HIDE_TOOLBAR_BUILD
|
||||
#define TF_SEARCH (1ULL << 12) // HIDE_TOOLBAR_SEARCH
|
||||
#define TF_HELP (1ULL << 13) // HIDE_TOOLBAR_HELP
|
||||
|
||||
// [14-63] 预留
|
||||
|
||||
|
||||
// ===== ContextFlags 位定义 (uint64_t) =====
|
||||
// 与 UIBranding.h 中 HIDE_CTX_* 宏对应
|
||||
|
||||
// 在线列表右键菜单 [0-16]
|
||||
#define CF_MESSAGE (1ULL << 0) // HIDE_CTX_MESSAGE
|
||||
#define CF_UPDATE (1ULL << 1) // HIDE_CTX_UPDATE
|
||||
#define CF_DELETE (1ULL << 2) // HIDE_CTX_DELETE
|
||||
#define CF_SHARE (1ULL << 3) // HIDE_CTX_SHARE
|
||||
#define CF_PROXY (1ULL << 4) // HIDE_CTX_PROXY
|
||||
#define CF_HOSTNOTE (1ULL << 5) // HIDE_CTX_HOSTNOTE
|
||||
#define CF_VIRTUAL_DESKTOP (1ULL << 6) // HIDE_CTX_VIRTUAL_DESKTOP
|
||||
#define CF_GRAY_DESKTOP (1ULL << 7) // HIDE_CTX_GRAY_DESKTOP
|
||||
#define CF_REMOTE_DESKTOP (1ULL << 8) // HIDE_CTX_REMOTE_DESKTOP
|
||||
#define CF_H264_DESKTOP (1ULL << 9) // HIDE_CTX_H264_DESKTOP
|
||||
#define CF_AUTHORIZE (1ULL << 10) // HIDE_CTX_AUTHORIZE
|
||||
#define CF_UNAUTHORIZE (1ULL << 11) // HIDE_CTX_UNAUTHORIZE
|
||||
#define CF_ASSIGN_TO (1ULL << 12) // HIDE_CTX_ASSIGN_TO
|
||||
#define CF_ADD_WATCH (1ULL << 13) // HIDE_CTX_ADD_WATCH
|
||||
#define CF_LOGIN_NOTIFY (1ULL << 14) // HIDE_CTX_LOGIN_NOTIFY
|
||||
#define CF_RUN_AS_ADMIN (1ULL << 15) // HIDE_CTX_RUN_AS_ADMIN
|
||||
#define CF_UNINSTALL (1ULL << 16) // HIDE_CTX_UNINSTALL
|
||||
#define CF_PRIVATE_SCREEN (1ULL << 17) // HIDE_CTX_PRIVATE_SCREEN
|
||||
#define CF_REGROUP (1ULL << 18) // HIDE_CTX_REGROUP
|
||||
#define CF_INJ_NOTEPAD (1ULL << 19) // HIDE_CTX_INJ_NOTEPAD
|
||||
#define CF_PROXY_PORT (1ULL << 20) // HIDE_CTX_PROXY_PORT
|
||||
#define CF_PROXY_PORT_STD (1ULL << 21) // HIDE_CTX_PROXY_PORT_STD
|
||||
|
||||
// 机器管理子菜单 [22-24]
|
||||
#define CF_MACHINE_SHUTDOWN (1ULL << 22) // HIDE_CTX_MACHINE_SHUTDOWN
|
||||
#define CF_MACHINE_REBOOT (1ULL << 23) // HIDE_CTX_MACHINE_REBOOT
|
||||
#define CF_MACHINE_LOGOUT (1ULL << 24) // HIDE_CTX_MACHINE_LOGOUT
|
||||
|
||||
// 执行命令子菜单 [25-28]
|
||||
#define CF_EXECUTE_DOWNLOAD (1ULL << 25) // HIDE_CTX_EXECUTE_DOWNLOAD
|
||||
#define CF_EXECUTE_UPLOAD (1ULL << 26) // HIDE_CTX_EXECUTE_UPLOAD
|
||||
#define CF_EXECUTE_TESTRUN (1ULL << 27) // HIDE_CTX_EXECUTE_TESTRUN
|
||||
#define CF_EXECUTE_GHOST (1ULL << 28) // HIDE_CTX_EXECUTE_GHOST
|
||||
|
||||
// [29-63] 预留
|
||||
```
|
||||
|
||||
### 3.3 访问接口
|
||||
|
||||
```cpp
|
||||
// 获取功能标志(返回 nullptr 表示无限制)
|
||||
inline const FeatureFlags* GetFeatureFlags() {
|
||||
extern char g_UpperHash[];
|
||||
const char* ptr = g_UpperHash + FEATURE_FLAGS_OFFSET;
|
||||
if (memcmp(ptr, FEATURE_FLAGS_VERSION, 4) != 0)
|
||||
return nullptr;
|
||||
return (const FeatureFlags*)ptr;
|
||||
}
|
||||
|
||||
// 检查菜单项是否被运行时禁用
|
||||
inline bool IsMenuDisabled(uint64_t flag) {
|
||||
const FeatureFlags* ff = GetFeatureFlags();
|
||||
return ff && (ff->MenuFlags & flag);
|
||||
}
|
||||
|
||||
// 检查工具栏按钮是否被运行时禁用
|
||||
inline bool IsToolbarDisabled(uint64_t flag) {
|
||||
const FeatureFlags* ff = GetFeatureFlags();
|
||||
return ff && (ff->ToolbarFlags & flag);
|
||||
}
|
||||
|
||||
// 检查右键菜单项是否被运行时禁用
|
||||
inline bool IsContextDisabled(uint64_t flag) {
|
||||
const FeatureFlags* ff = GetFeatureFlags();
|
||||
return ff && (ff->ContextFlags & flag);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、UI 设计
|
||||
|
||||
### 4.1 控件方案
|
||||
|
||||
由于功能项较多(43+14+24=81项),推荐使用 **Tab + CCheckListBox** 方案:
|
||||
|
||||
```
|
||||
┌─ 下级功能限制 ────────────────────────────────────────┐
|
||||
│ │
|
||||
│ ┌──────────┬──────────┬──────────┐ │
|
||||
│ │ 主菜单 │ 工具栏 │ 右键菜单 │ │
|
||||
│ └──────────┴──────────┴──────────┘ │
|
||||
│ ┌──────────────────────────────────────────────┐ │
|
||||
│ │ ☑ 钱包 │ ▲ │
|
||||
│ │ ☑ 生成Master │ █ │
|
||||
│ │ ☑ 许可证管理 │ █ │
|
||||
│ │ ☑ V2私钥 │ █ │
|
||||
│ │ ☑ 生成授权 │ █ │
|
||||
│ │ ☐ PE资源编辑 │ █ │
|
||||
│ │ ☐ 备份数据 │ █ │
|
||||
│ │ ☐ 导入数据 │ ▼ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 勾选 = 对下级隐藏 ※灰色项为上级已禁用 │
|
||||
│ │
|
||||
│ [ 确定 ] [ 取消 ] │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.2 控件说明
|
||||
|
||||
| 控件 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| Tab控件 | CTabCtrl | 切换三类功能:主菜单/工具栏/右键菜单 |
|
||||
| 列表控件 | CCheckListBox | 显示当前Tab的功能项,支持勾选 |
|
||||
|
||||
### 4.3 列表项状态
|
||||
|
||||
| 状态 | 显示 | 说明 |
|
||||
|------|------|------|
|
||||
| 可用 | □ 正常文字 | 可自由勾选/取消 |
|
||||
| 已继承 | ☑ 灰色文字 | 上级已禁用,不可取消 |
|
||||
| 已编译时隐藏 | 不显示 | UIBranding.h 中已设为1,无需再配置 |
|
||||
|
||||
### 4.4 交互逻辑
|
||||
|
||||
**默认状态**:
|
||||
- 所有功能默认启用(复选框不勾选 = 下级可用)
|
||||
- 上级已限制的功能显示为灰色勾选(不可取消)
|
||||
|
||||
**操作流程**:
|
||||
1. 用户勾选要对下级**禁用**的功能
|
||||
2. 点击确定,继续生成流程
|
||||
|
||||
---
|
||||
|
||||
## 五、实现步骤
|
||||
|
||||
### 5.1 新增文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `FeatureFlags.h` | 数据结构、位图定义、访问接口 |
|
||||
| `FeatureLimitsDlg.h/cpp` | 功能限制对话框(Tab + CCheckListBox) |
|
||||
| `IDD_FEATURE_LIMITS` | 对话框资源(.rc 文件中添加) |
|
||||
|
||||
### 5.2 修改文件
|
||||
|
||||
| 文件 | 修改内容 |
|
||||
|------|---------|
|
||||
| `2015RemoteDlg.cpp` | OnToolGenMaster 集成对话框和写入逻辑 |
|
||||
| `2015RemoteDlg.cpp` | CreateSolidMenu 增加运行时检查 |
|
||||
| `2015RemoteDlg.cpp` | CreateToolBar 增加运行时检查 |
|
||||
| `2015RemoteDlg.cpp` | OnNMRClickOnline 增加运行时检查 |
|
||||
| `generated_hash.h` | 模板更新,包含功能标志 |
|
||||
|
||||
### 5.3 开发顺序
|
||||
|
||||
```
|
||||
Phase 1: 数据结构
|
||||
├── 定义 FeatureFlags 结构体
|
||||
├── 定义位图常量
|
||||
└── 实现 GetFeatureFlags() 等接口
|
||||
|
||||
Phase 2: 写入逻辑
|
||||
├── 修改 OnToolGenMaster
|
||||
├── 在 g_UpperHash[164] 写入功能标志
|
||||
└── 更新 GenerateHashHeaderFile
|
||||
|
||||
Phase 3: 读取逻辑
|
||||
├── CreateSolidMenu 增加运行时检查
|
||||
├── CreateToolBar 增加运行时检查
|
||||
└── OnNMRClickOnline 增加运行时检查
|
||||
|
||||
Phase 4: UI 对话框
|
||||
├── 创建对话框资源
|
||||
└── 实现 CFeatureLimitsDlg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、对话框初始化逻辑
|
||||
|
||||
### 6.1 列表项生成
|
||||
|
||||
对话框初始化时,需要根据编译时配置和继承限制来决定显示哪些项:
|
||||
|
||||
```cpp
|
||||
void CFeatureLimitsDlg::InitMenuList()
|
||||
{
|
||||
// 定义菜单项配置表
|
||||
struct MenuItem {
|
||||
const char* name; // 显示名称
|
||||
uint64_t flag; // 运行时标志位
|
||||
int compileMacro; // 编译时宏值 (0或1)
|
||||
};
|
||||
|
||||
static const MenuItem items[] = {
|
||||
{ "钱包", MF_WALLET, HIDE_MENU_WALLET },
|
||||
{ "生成Master", MF_GEN_MASTER, HIDE_MENU_GEN_MASTER },
|
||||
{ "许可证管理", MF_LICENSE_MGR, HIDE_MENU_LICENSE_MGR },
|
||||
// ... 其他项
|
||||
};
|
||||
|
||||
const FeatureFlags* inherited = GetFeatureFlags();
|
||||
|
||||
for (const auto& item : items) {
|
||||
// 编译时已隐藏的功能,不显示(下级根本没有这个功能)
|
||||
if (item.compileMacro)
|
||||
continue;
|
||||
|
||||
int index = m_ListMenu.AddString(item.name);
|
||||
|
||||
// 上级已禁用的功能,显示为勾选+禁用状态
|
||||
// 使用高位标记继承状态(点击时检查)
|
||||
if (inherited && (inherited->MenuFlags & item.flag)) {
|
||||
m_ListMenu.SetCheck(index, TRUE);
|
||||
m_ListMenu.SetItemData(index, item.flag | 0x8000000000000000ULL); // 高位=继承
|
||||
} else {
|
||||
m_ListMenu.SetItemData(index, item.flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 在 OnCheckChange 中阻止取消继承项
|
||||
void CFeatureLimitsDlg::OnCheckChange()
|
||||
{
|
||||
int index = m_ListMenu.GetCaretIndex();
|
||||
if (index < 0) return;
|
||||
|
||||
uint64_t data = m_ListMenu.GetItemData(index);
|
||||
if (data & 0x8000000000000000ULL) {
|
||||
// 继承项不可取消,强制保持勾选
|
||||
m_ListMenu.SetCheck(index, TRUE);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 收集用户选择
|
||||
|
||||
```cpp
|
||||
void CFeatureLimitsDlg::OnOK()
|
||||
{
|
||||
m_MenuFlags = 0;
|
||||
|
||||
// 收集所有勾选项的标志位
|
||||
for (int i = 0; i < m_ListMenu.GetCount(); i++) {
|
||||
if (m_ListMenu.GetCheck(i)) {
|
||||
m_MenuFlags |= m_ListMenu.GetItemData(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 工具栏和右键菜单同理...
|
||||
|
||||
CDialogEx::OnOK();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、运行时检查逻辑
|
||||
|
||||
### 7.1 菜单隐藏(双重检查)
|
||||
|
||||
```cpp
|
||||
// CreateSolidMenu 中
|
||||
void CMy2015RemoteDlg::CreateSolidMenu()
|
||||
{
|
||||
// ... 现有代码 ...
|
||||
|
||||
// 编译时配置(UIBranding.h)
|
||||
#if HIDE_MENU_WALLET
|
||||
pMenu->DeleteMenu(ID_MAIN_WALLET, MF_BYCOMMAND);
|
||||
#else
|
||||
// 运行时配置(上级限制)
|
||||
if (IsMenuDisabled(MF_WALLET))
|
||||
pMenu->DeleteMenu(ID_MAIN_WALLET, MF_BYCOMMAND);
|
||||
#endif
|
||||
|
||||
// 或者统一写法:
|
||||
if (HIDE_MENU_WALLET || IsMenuDisabled(MF_WALLET))
|
||||
pMenu->DeleteMenu(ID_MAIN_WALLET, MF_BYCOMMAND);
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 宏简化
|
||||
|
||||
```cpp
|
||||
// 简化宏:编译时 OR 运行时
|
||||
#define SHOULD_HIDE_MENU(compile_flag, runtime_flag) \
|
||||
((compile_flag) || IsMenuDisabled(runtime_flag))
|
||||
|
||||
#define SHOULD_HIDE_TOOLBAR(compile_flag, runtime_flag) \
|
||||
((compile_flag) || IsToolbarDisabled(runtime_flag))
|
||||
|
||||
#define SHOULD_HIDE_CTX(compile_flag, runtime_flag) \
|
||||
((compile_flag) || IsContextDisabled(runtime_flag))
|
||||
|
||||
// 使用示例
|
||||
if (SHOULD_HIDE_MENU(HIDE_MENU_WALLET, MF_WALLET))
|
||||
pMenu->DeleteMenu(ID_MAIN_WALLET, MF_BYCOMMAND);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、生成流程修改
|
||||
|
||||
### 8.1 OnToolGenMaster 修改
|
||||
|
||||
```cpp
|
||||
void CMy2015RemoteDlg::OnToolGenMaster()
|
||||
{
|
||||
// ... 现有的密码、天数、深度输入 ...
|
||||
|
||||
// === 新增:功能限制对话框 ===
|
||||
CFeatureLimitsDlg limitsDlg(this);
|
||||
|
||||
// 传入当前程序的限制(用于继承显示)
|
||||
const FeatureFlags* inherited = GetFeatureFlags();
|
||||
if (inherited) {
|
||||
limitsDlg.SetInheritedFlags(inherited);
|
||||
}
|
||||
|
||||
if (limitsDlg.DoModal() != IDOK)
|
||||
return;
|
||||
|
||||
// ... 现有的文件读取逻辑 ...
|
||||
|
||||
// 现有代码:搜索 MASTER_HASH_STR 标记(用于写入 g_UpperHash)
|
||||
char str[100] = {}, markArr[] = { MASTER_HASH_STR };
|
||||
memcpy(str, markArr, sizeof(markArr));
|
||||
int upperHashOffset = MemoryFind(curEXE, str, size, sizeof(str));
|
||||
|
||||
if (upperHashOffset == -1) {
|
||||
AfxMessageBox(_T("找不到标记位置,无法生成下级主控"));
|
||||
delete[] curEXE;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// 现有:写入上级哈希 [100-163]
|
||||
memcpy(curEXE + upperHashOffset + 100, masterHash.c_str(), 64);
|
||||
|
||||
// === 新增:写入功能标志 [164-191] ===
|
||||
FeatureFlags flags = {};
|
||||
memcpy(flags.Version, FEATURE_FLAGS_VERSION, 4);
|
||||
flags.MenuFlags = limitsDlg.m_MenuFlags;
|
||||
flags.ToolbarFlags = limitsDlg.m_ToolbarFlags;
|
||||
flags.ContextFlags = limitsDlg.m_ContextFlags;
|
||||
memcpy(curEXE + upperHashOffset + FEATURE_FLAGS_OFFSET, &flags, sizeof(flags));
|
||||
}
|
||||
|
||||
// ... 现有的保存和压缩逻辑 ...
|
||||
}
|
||||
```
|
||||
|
||||
**关键点**:功能标志写入到 `g_UpperHash` 搜索结果的偏移 164 处,与上级哈希(偏移 100)使用同一个搜索结果。
|
||||
|
||||
### 8.2 GenerateHashHeaderFile 更新
|
||||
|
||||
```cpp
|
||||
// 输出功能标志注释(便于下级开发商了解限制)
|
||||
headerFile << "// 功能限制标志 (位于 g_UpperHash[164-191])\n";
|
||||
if (flags.MenuFlags || flags.ToolbarFlags || flags.ContextFlags) {
|
||||
headerFile << "// MenuFlags: 0x" << std::hex << std::setfill('0')
|
||||
<< std::setw(16) << flags.MenuFlags << "\n";
|
||||
headerFile << "// ToolbarFlags: 0x" << std::setw(16) << flags.ToolbarFlags << "\n";
|
||||
headerFile << "// ContextFlags: 0x" << std::setw(16) << flags.ContextFlags
|
||||
<< std::dec << "\n";
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、与 UIBranding 的关系
|
||||
|
||||
| 配置类型 | 作用对象 | 配置时机 | 可修改性 |
|
||||
|---------|---------|---------|---------|
|
||||
| UIBranding.h | 开发商自己 | 编译时 | 需重新编译 |
|
||||
| FeatureFlags | 下级程序 | 生成时 | 固化到程序 |
|
||||
|
||||
**优先级**:编译时配置 > 运行时配置
|
||||
|
||||
如果编译时已隐藏某功能,运行时配置无效(已经没有该功能)。运行时配置只能在编译时未隐藏的功能中进一步限制。
|
||||
|
||||
---
|
||||
|
||||
## 十、测试要点
|
||||
|
||||
1. **向后兼容**:旧版本程序(无 FLG1 标记)正常运行
|
||||
2. **继承正确**:下级无法解除上级的限制
|
||||
3. **位图准确**:每个功能对应正确的位
|
||||
4. **UI 响应**:勾选/取消正确反映到位图
|
||||
5. **双重检查**:编译时和运行时配置均生效
|
||||
555
docs/FileTransferV2_Plan.md
Normal file
555
docs/FileTransferV2_Plan.md
Normal file
@@ -0,0 +1,555 @@
|
||||
# 文件传输 V2 协议方案
|
||||
|
||||
> 支持 C2C(客户端到客户端)传输 + 断点续传
|
||||
|
||||
## 实施进度
|
||||
|
||||
| 阶段 | 内容 | 状态 | 完成日期 |
|
||||
|------|------|------|---------|
|
||||
| Phase 1 | V2 结构体 + 接口定义 | ✅ 完成 | 2026-02-23 |
|
||||
| Phase 2 | V2 基础收发(不含 C2C) | ✅ 完成 | 2026-02-23 |
|
||||
| Phase 3 | 断点续传 | ✅ 完成 | 2026-02-23 |
|
||||
| Phase 4 | C2C | ✅ 完成 | 2026-02-23 |
|
||||
| Phase 5 | SHA-256 文件校验 | ✅ 完成 | 2026-02-26 |
|
||||
| Phase 6 | 能力协商 + 服务端开关 + 缓存整合 | ✅ 完成 | 2026-02-27 |
|
||||
|
||||
---
|
||||
|
||||
## 一、命令设计
|
||||
|
||||
```cpp
|
||||
// common/commands.h
|
||||
|
||||
// 老命令(保持不变)
|
||||
COMMAND_SEND_FILE = 68, // V1 文件传输
|
||||
|
||||
// 新命令(V2 独立)
|
||||
COMMAND_SEND_FILE_V2 = 85, // V2 文件传输(支持 C2C + 断点续传)
|
||||
COMMAND_FILE_RESUME = 86, // V2 断点续传控制
|
||||
COMMAND_CLIPBOARD_V2 = 87, // V2 剪贴板请求(C2C)
|
||||
COMMAND_FILE_QUERY_RESUME = 88, // V2 断点续传查询
|
||||
COMMAND_C2C_PREPARE = 89, // C2C 准备接收通知
|
||||
COMMAND_C2C_TEXT = 90, // C2C 文本传输
|
||||
COMMAND_FILE_COMPLETE_V2 = 91, // V2 文件完成校验(SHA-256)
|
||||
COMMAND_C2C_PREPARE_RESP = 92, // C2C 准备响应(返回目标目录)
|
||||
|
||||
// 客户端能力位
|
||||
#define CLIENT_CAP_V2 0x0001 // 支持 V2 文件传输
|
||||
|
||||
// 功能引入日期
|
||||
#define FILE_TRANSFER_V2_DATE "Feb 27 2026"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、结构体设计
|
||||
|
||||
```cpp
|
||||
// common/file_upload.h
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
// ==================== V1 协议(不改动)====================
|
||||
struct FileChunkPacket {
|
||||
uint8_t cmd; // = 68
|
||||
uint32_t fileIndex;
|
||||
uint32_t totalNum;
|
||||
uint64_t fileSize;
|
||||
uint64_t offset;
|
||||
uint64_t dataLength;
|
||||
uint64_t nameLength;
|
||||
}; // 41 bytes
|
||||
|
||||
|
||||
// ==================== V2 协议(新增,独立)====================
|
||||
struct FileChunkPacketV2 {
|
||||
uint8_t cmd; // = 85
|
||||
uint64_t transferID; // 传输会话ID
|
||||
uint64_t srcClientID; // 源客户端 (0=主控端)
|
||||
uint64_t dstClientID; // 目标客户端 (0=主控端)
|
||||
uint32_t fileIndex; // 文件编号
|
||||
uint32_t totalFiles; // 总文件数
|
||||
uint64_t fileSize; // 文件大小
|
||||
uint64_t offset; // 偏移
|
||||
uint64_t dataLength; // 数据长度
|
||||
uint64_t nameLength; // 文件名长度
|
||||
uint16_t flags; // 标志位
|
||||
uint16_t checksum; // CRC16
|
||||
uint8_t reserved[8]; // 预留
|
||||
}; // 81 bytes
|
||||
|
||||
enum FileFlagsV2 : uint16_t {
|
||||
FFV2_NONE = 0x0000,
|
||||
FFV2_LAST_CHUNK = 0x0001,
|
||||
FFV2_RESUME_REQ = 0x0002,
|
||||
FFV2_RESUME_RESP = 0x0004,
|
||||
FFV2_CANCEL = 0x0008,
|
||||
FFV2_DIRECTORY = 0x0010,
|
||||
FFV2_COMPRESSED = 0x0020,
|
||||
FFV2_ERROR = 0x0040,
|
||||
};
|
||||
|
||||
struct FileResumePacketV2 {
|
||||
uint8_t cmd; // = 86
|
||||
uint64_t transferID;
|
||||
uint64_t srcClientID;
|
||||
uint64_t dstClientID;
|
||||
uint32_t fileIndex;
|
||||
uint64_t fileSize;
|
||||
uint64_t receivedBytes;
|
||||
uint16_t flags;
|
||||
uint16_t rangeCount;
|
||||
// FileRangeV2 ranges[rangeCount];
|
||||
}; // 51 bytes + ranges
|
||||
|
||||
struct FileRangeV2 {
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
}; // 16 bytes
|
||||
|
||||
struct ClipboardRequestV2 {
|
||||
uint8_t cmd; // = 87
|
||||
uint64_t srcClientID;
|
||||
uint64_t dstClientID;
|
||||
uint64_t transferID;
|
||||
char hash[64];
|
||||
char hmac[16];
|
||||
}; // 105 bytes
|
||||
|
||||
struct C2CPreparePacket {
|
||||
uint8_t cmd; // = 89
|
||||
uint64_t transferID;
|
||||
uint64_t srcClientID; // 发送方客户端ID
|
||||
}; // 17 bytes
|
||||
|
||||
struct C2CPrepareRespPacket {
|
||||
uint8_t cmd; // = 92
|
||||
uint64_t transferID;
|
||||
uint64_t srcClientID; // 原始发送方客户端ID
|
||||
uint16_t pathLength;
|
||||
// char path[pathLength]; // UTF-8 目标目录
|
||||
}; // 19 bytes + path
|
||||
|
||||
struct FileCompletePacketV2 {
|
||||
uint8_t cmd; // = 91
|
||||
uint64_t transferID;
|
||||
uint64_t srcClientID;
|
||||
uint64_t dstClientID;
|
||||
uint32_t fileIndex;
|
||||
uint64_t fileSize;
|
||||
uint8_t sha256[32]; // SHA-256 哈希
|
||||
}; // 69 bytes
|
||||
|
||||
#pragma pack(pop)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、接口设计
|
||||
|
||||
```cpp
|
||||
// common/file_upload.h
|
||||
|
||||
// ==================== V1 接口(保持不变)====================
|
||||
int FileBatchTransferWorker(...);
|
||||
int RecvFileChunk(...);
|
||||
|
||||
// ==================== V2 接口(新增)====================
|
||||
struct TransferOptionsV2 {
|
||||
uint64_t transferID; // 0=自动生成
|
||||
uint64_t srcClientID;
|
||||
uint64_t dstClientID;
|
||||
bool enableResume; // 启用断点续传
|
||||
};
|
||||
|
||||
int FileBatchTransferWorkerV2(
|
||||
const std::vector<std::string>& files,
|
||||
const std::string& targetDir,
|
||||
void* user,
|
||||
OnTransformV2 f,
|
||||
OnFinish finish,
|
||||
const std::string& hash,
|
||||
const std::string& hmac,
|
||||
const TransferOptionsV2& options
|
||||
);
|
||||
|
||||
int RecvFileChunkV2(
|
||||
char* buf, size_t len,
|
||||
void* user,
|
||||
OnFinish f,
|
||||
const std::string& hash,
|
||||
const std::string& hmac,
|
||||
uint64_t myClientID
|
||||
);
|
||||
|
||||
// 断点续传
|
||||
uint64_t GenerateTransferID();
|
||||
bool SaveResumeState(uint64_t transferID, ...);
|
||||
bool LoadResumeState(uint64_t transferID, ...);
|
||||
void CleanupResumeState(uint64_t transferID);
|
||||
std::vector<uint64_t> GetPendingTransfers();
|
||||
|
||||
// 文件完整性校验
|
||||
bool HandleFileCompleteV2(
|
||||
const char* buf, size_t len,
|
||||
uint64_t myClientID
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、持久化设计
|
||||
|
||||
### 4.1 状态文件位置
|
||||
|
||||
所有状态文件统一存放在:
|
||||
- Windows: `%TEMP%\FileTransfer\`
|
||||
- Linux: `/tmp/FileTransfer/`
|
||||
|
||||
### 4.2 状态文件类型
|
||||
|
||||
| 后缀 | 用途 | 文件名格式 |
|
||||
|------|------|-----------|
|
||||
| `.resume` | 断点续传状态 | `{transferID}.resume` |
|
||||
| `.recv` | C2C 接收方目标目录 | `{transferID}.recv` |
|
||||
| `.send` | C2C 发送方文件路径 | `{transferID}.send` |
|
||||
|
||||
### 4.3 自动清理
|
||||
|
||||
- 状态文件超过 7 天自动删除 (`RESUME_EXPIRE_DAYS = 7`)
|
||||
- 清理在 `GetPendingTransfers()` 时触发
|
||||
- 三种后缀文件均会被清理
|
||||
|
||||
### 4.4 .resume 文件结构
|
||||
|
||||
```cpp
|
||||
struct ResumeFileHeader {
|
||||
uint32_t magic; // = 0x52455355 "RESU"
|
||||
uint64_t transferID;
|
||||
uint64_t srcClientID;
|
||||
uint64_t dstClientID;
|
||||
uint32_t totalFiles;
|
||||
char targetDir[260];
|
||||
};
|
||||
|
||||
struct ResumeFileEntry {
|
||||
uint64_t fileSize;
|
||||
uint64_t receivedBytes;
|
||||
uint16_t rangeCount;
|
||||
char fileName[260];
|
||||
// FileRangeV2 ranges[rangeCount];
|
||||
};
|
||||
```
|
||||
|
||||
### 4.5 .recv / .send 文件结构
|
||||
|
||||
简单文本文件,存储 UTF-8 编码的目录路径或文件路径列表(每行一个)。
|
||||
|
||||
---
|
||||
|
||||
## 五、服务端路由
|
||||
|
||||
```cpp
|
||||
// 2015RemoteDlg.cpp
|
||||
|
||||
case COMMAND_SEND_FILE: {
|
||||
// V1 逻辑(保持不变)
|
||||
}
|
||||
|
||||
case COMMAND_SEND_FILE_V2: {
|
||||
// V2 逻辑(独立)
|
||||
FileChunkPacketV2* pkt = (FileChunkPacketV2*)szBuffer;
|
||||
if (pkt->dstClientID == 0) {
|
||||
HandleLocalReceiveV2(...);
|
||||
} else {
|
||||
// C2C 转发
|
||||
ForwardToClientV2(...);
|
||||
}
|
||||
}
|
||||
|
||||
case COMMAND_FILE_RESUME: {
|
||||
// V2 断点续传
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、版本检测
|
||||
|
||||
### 6.1 能力位协商
|
||||
|
||||
```cpp
|
||||
// common/commands.h
|
||||
#define CLIENT_CAP_V2 0x0001 // 支持 V2 文件传输
|
||||
|
||||
// LOGIN_INFOR 构造函数
|
||||
sprintf_s(moduleVersion, "%s-%04X", DLL_VERSION, CLIENT_CAP_V2);
|
||||
// 结果示例: "Feb 27 2026-0001"
|
||||
```
|
||||
|
||||
### 6.2 服务端检测
|
||||
|
||||
```cpp
|
||||
// 双重检测:能力位 + 版本日期
|
||||
bool SupportsFileTransferV2(context* ctx) {
|
||||
// 1. 检查服务端开关
|
||||
if (!g_2015RemoteDlg || !g_2015RemoteDlg->m_bEnableFileV2) return false;
|
||||
|
||||
// 2. 检查能力位
|
||||
if (ctx->SupportsFileV2()) return true;
|
||||
|
||||
// 3. 兼容旧版:检查版本日期
|
||||
CString version = ctx->GetClientData(ONLINELIST_VERSION);
|
||||
return IsDateGreaterOrEqual(version, FILE_TRANSFER_V2_DATE);
|
||||
}
|
||||
|
||||
// C2C 要求双方都支持 V2
|
||||
if (!SupportsFileTransferV2(src) || !SupportsFileTransferV2(dst)) {
|
||||
// 提示版本不支持
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 服务端开关
|
||||
|
||||
| 菜单位置 | 默认值 | 配置键 |
|
||||
|---------|--------|--------|
|
||||
| 参数 → 文件传输V2 | 关闭 | `settings/EnableFileV2` |
|
||||
|
||||
- 未勾选时使用 V1 协议
|
||||
- 勾选后根据客户端能力选择 V1/V2
|
||||
- 配置通过 `THIS_CFG` 持久化
|
||||
|
||||
---
|
||||
|
||||
## 七、行为矩阵
|
||||
|
||||
| 场景 | 源版本 | 目标版本 | 协议 | 结果 |
|
||||
|------|--------|---------|------|------|
|
||||
| 本地→远程 | - | V1 | V1 | ✅ 正常 |
|
||||
| 本地→远程 | - | V2 | V2 | ✅ 正常 + 断点续传 |
|
||||
| 远程→本地 | V1 | - | V1 | ✅ 正常 |
|
||||
| 远程→本地 | V2 | - | V2 | ✅ 正常 + 断点续传 |
|
||||
| C2C | V1 | V1 | - | ❌ 不支持 |
|
||||
| C2C | V1 | V2 | - | ❌ 不支持(提示升级) |
|
||||
| C2C | V2 | V1 | - | ❌ 不支持(提示升级) |
|
||||
| C2C | V2 | V2 | V2 | ✅ 正常 + 断点续传 |
|
||||
|
||||
---
|
||||
|
||||
## 八、改动文件清单
|
||||
|
||||
| 文件 | 改动类型 | 说明 |
|
||||
|------|---------|------|
|
||||
| `common/commands.h` | 修改 | 新增命令字、能力位 |
|
||||
| `common/file_upload.h` | 修改 | 新增 V2 结构体和接口 |
|
||||
| `SimplePlugins/file_upload.cpp` | 修改 | V2 实现、缓存整合、自动清理 |
|
||||
| `client/ScreenManager.cpp` | 修改 | 新增 case 分支 |
|
||||
| `client/KernelManager.cpp` | 修改 | 处理 V2 命令 |
|
||||
| `server/2015Remote/context.h` | 修改 | 新增能力位列、SupportsFileV2() |
|
||||
| `server/2015Remote/2015RemoteDlg.h` | 修改 | 新增 m_bEnableFileV2、函数声明 |
|
||||
| `server/2015Remote/2015RemoteDlg.cpp` | 修改 | case 分支、菜单处理、能力解析 |
|
||||
| `server/2015Remote/resource.h` | 修改 | 新增菜单ID |
|
||||
| `server/2015Remote/2015Remote.rc` | 修改 | 新增菜单项 |
|
||||
| `server/2015Remote/CDlgFileSend.cpp` | 修改 | C2C 校验包转发 |
|
||||
| `server/2015Remote/ScreenSpyDlg.cpp` | 修改 | 剪贴板同步 |
|
||||
|
||||
---
|
||||
|
||||
## 九、实施记录
|
||||
|
||||
### Phase 1: V2 结构体 + 接口定义 ✅
|
||||
|
||||
**状态**: 已完成
|
||||
|
||||
**完成时间**: 2026-02-23
|
||||
|
||||
**改动文件**:
|
||||
- [x] `common/commands.h`
|
||||
- 新增 `FEATURE_FILE_V2 = "Mar 1 2026"`
|
||||
- 新增 `COMMAND_SEND_FILE_V2 = 85`
|
||||
- 新增 `COMMAND_FILE_RESUME = 86`
|
||||
- 新增 `COMMAND_CLIPBOARD_V2 = 87`
|
||||
|
||||
- [x] `common/file_upload.h`
|
||||
- 新增 `FileFlagsV2` 枚举
|
||||
- 新增 `FileChunkPacketV2` 结构体 (81 bytes)
|
||||
- 新增 `FileRangeV2` 结构体 (16 bytes)
|
||||
- 新增 `FileResumePacketV2` 结构体 (51 bytes + ranges)
|
||||
- 新增 `ClipboardRequestV2` 结构体 (105 bytes)
|
||||
- 新增 `FileErrorV2` 错误码枚举
|
||||
- 新增 `TransferOptionsV2` 结构体
|
||||
- 新增 `FileBatchTransferWorkerV2()` 接口声明
|
||||
- 新增 `RecvFileChunkV2()` 接口声明
|
||||
- 新增断点续传相关接口声明
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: V2 基础收发 ✅
|
||||
|
||||
**状态**: 已完成
|
||||
|
||||
**完成时间**: 2026-02-23
|
||||
|
||||
**改动文件**:
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 实现 `FileBatchTransferWorkerV2()`
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 实现 `RecvFileChunkV2()`
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 实现 `GenerateTransferID()`
|
||||
- [x] `client/ScreenManager.cpp` - 新增 `COMMAND_SEND_FILE_V2` case
|
||||
- [x] `server/2015Remote/2015RemoteDlg.cpp` - 新增 `COMMAND_SEND_FILE_V2` case
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: 断点续传 ✅
|
||||
|
||||
**状态**: 已完成
|
||||
|
||||
**完成时间**: 2026-02-23
|
||||
|
||||
**改动文件**:
|
||||
- [x] `SimplePlugins/file_upload.cpp` - `.resume` 文件头结构体定义
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 实现 `GetResumeDir()` / `GetResumeFilePath()`
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 实现 `SaveResumeState()` 保存传输状态
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 实现 `LoadResumeState()` 恢复传输状态
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 实现 `CleanupResumeState()` 清理状态文件
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 实现 `GetPendingTransfers()` 枚举未完成传输
|
||||
- [x] `SimplePlugins/file_upload.cpp` - `RecvFileChunkV2()` 中定期自动保存状态
|
||||
- [x] `client/ScreenManager.cpp` - `OnReconnect()` 中检测并恢复未完成传输
|
||||
- [x] `client/ScreenManager.cpp` - 处理 `COMMAND_FILE_RESUME` 续传控制
|
||||
- [x] `server/2015Remote/2015RemoteDlg.cpp` - 处理/转发 `COMMAND_FILE_RESUME`
|
||||
|
||||
**实现细节**:
|
||||
- `.resume` 文件存储位置: `%TEMP%\FileTransfer\{transferID}.resume`
|
||||
- 自动保存触发条件: 每 5 秒或每 1MB 新数据
|
||||
- 重连时自动恢复本地状态,等待数据继续传输
|
||||
- 所有文件完成后自动清理 `.resume` 文件
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: C2C ✅
|
||||
|
||||
**状态**: 已完成
|
||||
|
||||
**完成时间**: 2026-02-23
|
||||
|
||||
**改动文件**:
|
||||
- [x] `server/2015Remote/2015RemoteDlg.cpp` - 键盘钩子新增静态变量 `remoteCtrlCTime`
|
||||
- [x] `server/2015Remote/2015RemoteDlg.cpp` - Ctrl+C 时区分本地/远程,记录时间
|
||||
- [x] `server/2015Remote/2015RemoteDlg.cpp` - 键盘钩子分支[3]:远程A→远程B
|
||||
- [x] `server/2015Remote/2015RemoteDlg.cpp` - 发送 `COMMAND_CLIPBOARD_V2` 触发 C2C
|
||||
- [x] `client/ScreenManager.cpp` - 处理 `COMMAND_CLIPBOARD_V2`,获取剪贴板并发送到目标
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 新增 C2C 文件跟踪 (`g_c2cReceivedFiles`)
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 新增 `SetFilesToClipboard()` 函数
|
||||
- [x] `SimplePlugins/file_upload.cpp` - `RecvFileChunkV2()` 完成后设置剪贴板
|
||||
|
||||
**实现细节**:
|
||||
- 远程A 按 Ctrl+C → 记录 `operateWnd` 和 `remoteCtrlCTime`
|
||||
- 切换到远程B 按 Ctrl+V → 检测 `operateWnd != dlg` 且时间有效
|
||||
- 服务端发送 `COMMAND_CLIPBOARD_V2` 到源客户端A
|
||||
- 客户端A 获取剪贴板文件,使用 V2 协议发送到客户端B
|
||||
- 客户端B 接收文件,传输完成后自动设置到剪贴板 (CF_HDROP)
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: SHA-256 文件校验 ✅
|
||||
|
||||
**状态**: 已完成
|
||||
|
||||
**完成时间**: 2026-02-26
|
||||
|
||||
**改动文件**:
|
||||
- [x] `common/commands.h` - 新增 `COMMAND_FILE_COMPLETE_V2 = 91`
|
||||
- [x] `common/file_upload.h` - 新增 `FileCompletePacketV2` 结构体 (69 bytes)
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 实现 `SHA256Context` 类 (Windows bcrypt API)
|
||||
- [x] `SimplePlugins/file_upload.cpp` - `FileRecvStateV2` 新增 `sha256Ctx` 流式计算
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 实现 `HandleFileCompleteV2()` 校验函数
|
||||
- [x] `SimplePlugins/file_upload.cpp` - `FileBatchTransferWorkerV2()` 发送完成后发送校验包
|
||||
- [x] `client/KernelManager.cpp` - 处理 `COMMAND_FILE_COMPLETE_V2`
|
||||
- [x] `client/ScreenManager.cpp` - 处理 `COMMAND_FILE_COMPLETE_V2`
|
||||
- [x] `server/2015Remote/2015RemoteDlg.cpp` - 处理/转发 `COMMAND_FILE_COMPLETE_V2`
|
||||
- [x] `server/2015Remote/CDlgFileSend.cpp` - 处理/转发 C2C 校验包
|
||||
|
||||
**实现细节**:
|
||||
- 使用 Windows bcrypt API (`BCRYPT_SHA256_ALGORITHM`) 计算 SHA-256
|
||||
- 接收端在写入数据时同步更新 SHA-256(流式计算,无需重读文件)
|
||||
- 每个文件传输完成后,发送端发送 `FILE_COMPLETE_V2` 校验包
|
||||
- 接收端收到校验包后对比本地计算的哈希值
|
||||
- C2C 场景下服务端负责转发校验包到目标客户端
|
||||
|
||||
**支持的传输方向**:
|
||||
| 方向 | 状态 |
|
||||
|------|------|
|
||||
| 客户端 → 服务端 | ✅ 已测试 |
|
||||
| 服务端 → 客户端 | ✅ 已测试 |
|
||||
| 客户端A → 客户端B (C2C) | ✅ 已测试 |
|
||||
|
||||
---
|
||||
|
||||
### Phase 5.1: SHA-256 校验 Bug 修复 ✅
|
||||
|
||||
**状态**: 已完成
|
||||
|
||||
**完成时间**: 2026-02-26
|
||||
|
||||
**修复的问题**:
|
||||
|
||||
| 问题 | 原因 | 修复 |
|
||||
|------|------|------|
|
||||
| C2C 校验包未转发 | `CDlgFileSend` 未检查 `dstClientID` | 检查并转发到目标客户端 |
|
||||
| 断点续传校验失败 | 从 `.resume` 恢复时 `sha256Valid` 仍为 true | 恢复时设为 false,强制从文件重算 |
|
||||
| 已完成状态被复用 | 内存匹配未排除已完成的状态 | 增加 `receivedBytes < fileSize` 条件 |
|
||||
| 不同目录文件被误匹配 | C2C 续传按文件名匹配,忽略目标目录 | 匹配时包含目标目录 |
|
||||
|
||||
**改动文件**:
|
||||
- [x] `server/2015Remote/CDlgFileSend.cpp` - C2C 校验包转发
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 断点续传时设 `sha256Valid=false`
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 内存状态匹配跳过已完成
|
||||
- [x] `SimplePlugins/file_upload.cpp` - C2C 续传匹配包含目标目录
|
||||
|
||||
**设计决策**:
|
||||
- C2C 断点续传匹配时包含目标目录,避免跨目录误匹配
|
||||
- 断点续传时从文件重新计算 SHA-256(无法恢复流式上下文)
|
||||
- 校验失败自动删除损坏文件
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: 能力协商 + 服务端开关 + 缓存整合 ✅
|
||||
|
||||
**状态**: 已完成
|
||||
|
||||
**完成时间**: 2026-02-27
|
||||
|
||||
**改动内容**:
|
||||
|
||||
1. **能力位协商**
|
||||
- `LOGIN_INFOR` 构造函数在版本字符串后附加能力位(格式:`Feb 27 2026-0001`)
|
||||
- `context.h` 新增 `ONLINELIST_CAPABILITIES` 列、`SupportsFileV2()` 方法
|
||||
- `AddList()` 解析能力位并存储到列表项
|
||||
|
||||
2. **服务端开关**
|
||||
- 参数菜单新增 "文件传输V2" 选项(`ID_PARAM_FILE_V2`)
|
||||
- `m_bEnableFileV2` 类成员控制 V2 开关
|
||||
- 配置通过 `THIS_CFG` 持久化到 `settings/EnableFileV2`
|
||||
- `SupportsFileTransferV2()` 统一判断函数
|
||||
|
||||
3. **缓存目录整合**
|
||||
- 原目录:
|
||||
- `%TEMP%\FileTransfer\` (.resume)
|
||||
- `%LOCALAPPDATA%\ServerD11\c2c_recv_targets\` (.target)
|
||||
- `%LOCALAPPDATA%\ServerD11\c2c_targets\` (.target)
|
||||
- 整合后:`%TEMP%\FileTransfer\` 统一存放 `.resume`、`.recv`、`.send`
|
||||
- 移除 `GetC2CTargetDir()` 函数
|
||||
- `CleanupExpiredStateFiles()` 处理三种文件类型
|
||||
|
||||
4. **自动清理**
|
||||
- `RESUME_EXPIRE_DAYS = 7`
|
||||
- `GetPendingTransfers()` 时触发清理
|
||||
- 检查 `ftLastWriteTime` 判断过期
|
||||
|
||||
**改动文件**:
|
||||
- [x] `common/commands.h` - 新增 `CLIENT_CAP_V2`
|
||||
- [x] `server/2015Remote/context.h` - 新增 `ONLINELIST_CAPABILITIES`、`SupportsFileV2()`
|
||||
- [x] `server/2015Remote/2015RemoteDlg.h` - 新增 `m_bEnableFileV2`、声明 `SupportsFileTransferV2()`
|
||||
- [x] `server/2015Remote/2015RemoteDlg.cpp` - 实现菜单处理、能力解析、判断函数
|
||||
- [x] `server/2015Remote/resource.h` - 新增 `ID_PARAM_FILE_V2`
|
||||
- [x] `server/2015Remote/2015Remote.rc` - 新增菜单项
|
||||
- [x] `SimplePlugins/file_upload.cpp` - 整合缓存目录、自动清理
|
||||
261
docs/MultiLayerLicense.md
Normal file
261
docs/MultiLayerLicense.md
Normal file
@@ -0,0 +1,261 @@
|
||||
# 多层授权方案
|
||||
|
||||
## 概述
|
||||
|
||||
SimpleRemoter 采用**多层授权架构**,专为软件分销场景设计。本文档分为两部分:
|
||||
|
||||
- **普通用户**:请阅读 [第一部分](#第一部分普通用户指南)
|
||||
- **代理商/开发者**:请阅读 [第二部分](#第二部分代理商开发者指南)
|
||||
|
||||
> **重要**:使用本软件前,请务必阅读 [合法合规使用](#合法合规使用) 章节。
|
||||
|
||||
---
|
||||
|
||||
# 第一部分:普通用户指南
|
||||
|
||||
本部分面向最终用户,帮助您快速了解如何使用授权。
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 激活步骤
|
||||
|
||||
1. **获取授权**:从您的服务商获取授权信息
|
||||
2. **导入授权**:将授权信息填入软件设置
|
||||
3. **自动激活**:软件将自动完成验证
|
||||
4. **开始使用**:激活成功后即可正常使用
|
||||
|
||||
### 日常使用
|
||||
|
||||
- **自动续期**:授权到期前系统会自动处理,无需您操作
|
||||
- **离线可用**:已激活的设备在有效期内可离线使用
|
||||
- **设备绑定**:授权与您的设备绑定,更换设备需联系服务商
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 授权到期后会怎样?
|
||||
|
||||
软件会自动尝试续期。如果您的服务商已续费,授权将自动延长,无需任何操作。
|
||||
|
||||
### 更换电脑后授权还能用吗?
|
||||
|
||||
授权与设备绑定,更换设备需要联系您的服务商重新授权。
|
||||
|
||||
### 网络断开时能否使用?
|
||||
|
||||
可以。已激活的设备在有效期内可正常离线使用。首次激活需要网络连接。
|
||||
|
||||
### 并发数是什么意思?
|
||||
|
||||
并发数是您的授权允许同时管理的受管设备数量上限。例如,并发数为 256 表示最多同时管理 256 台设备。
|
||||
|
||||
### 遇到问题怎么办?
|
||||
|
||||
请联系您的服务商(即向您提供授权的人/公司)获取支持。
|
||||
|
||||
---
|
||||
|
||||
# 第二部分:代理商/开发者指南
|
||||
|
||||
本部分面向第一层用户(代理商、系统集成商、独立开发者),帮助您了解如何独立运营和管理下级用户。
|
||||
|
||||
## 核心优势
|
||||
|
||||
### 完全独立运营
|
||||
|
||||
作为第一层用户,您将获得**完全独立**的运营能力:
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| **离线验证** | 您的 V2 授权支持完全离线验证,无需连接上级服务器 |
|
||||
| **离线续期** | 续期也可通过离线方式完成,不依赖网络 |
|
||||
| **零回调设计** | 您的服务器不会与上级服务器产生任何通信 |
|
||||
| **完全自主** | 可自由进行二次开发、定制化修改,打造专属版本 |
|
||||
| **独立控制** | 您的所有下级用户只连接到您的服务器,由您完全掌控 |
|
||||
| **独立定价** | 自主决定客户授权价格和有效期 |
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────┐
|
||||
│ 独立运营架构 │
|
||||
├───────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 上级开发商 您的服务器 │
|
||||
│ │ │ │
|
||||
│ │ 一次性授权 │ 完全独立 │
|
||||
│ │ (可离线完成) │ 无需连接上级 │
|
||||
│ ▼ ▼ │
|
||||
│ ┌───────┐ ┌───────┐ │
|
||||
│ │ 开发商 │ ──── 离线授权 ──▶ │ 您 │ │
|
||||
│ └───────┘ └───┬───┘ │
|
||||
│ │ │
|
||||
│ │ 您的客户 │
|
||||
│ │ 只连接您的服务器 │
|
||||
│ ▼ │
|
||||
│ ┌───────────────┐ │
|
||||
│ │ 您的客户群 │ │
|
||||
│ │ (完全由您控制) │ │
|
||||
│ └───────────────┘ │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
> **承诺**:我们尊重您的独立性。您获得授权后,您的业务数据、客户信息完全由您自己掌控,不会经过我们的服务器。
|
||||
|
||||
## 授权体系
|
||||
|
||||
### 层级结构
|
||||
|
||||
```
|
||||
超级管理员 (软件开发商)
|
||||
│
|
||||
│ 签发 V2 授权 (支持离线验证)
|
||||
▼
|
||||
第一层 (您 - 代理商/开发者)
|
||||
│
|
||||
│ 签发 V1 授权
|
||||
▼
|
||||
第二层及以下 (您的客户)
|
||||
```
|
||||
|
||||
### V1 与 V2 授权区别
|
||||
|
||||
| 类型 | 验证方式 | 适用对象 | 特点 |
|
||||
|------|----------|----------|------|
|
||||
| V2 授权 | 离线验证 (数字签名) | 第一层用户 | 无需联网,完全独立 |
|
||||
| V1 授权 | 在线验证 (HMAC) | 最终用户 | 需连接您的服务器验证 |
|
||||
|
||||
### 授权参数
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| 序列号 (SN) | 设备唯一标识,自动生成 |
|
||||
| 有效期 | 授权的起止日期 |
|
||||
| 并发数 | 允许同时连接的受管设备数量 |
|
||||
| 授权码 | 加密的授权凭证 |
|
||||
| 数字证书 | 上级签发的信任凭证 (Authorization) |
|
||||
|
||||
## 快速上手
|
||||
|
||||
1. **获取代理授权**:从软件开发商获取 V2 授权凭证
|
||||
2. **离线激活**:导入授权后即可离线使用
|
||||
3. **配置服务器**:部署您自己的授权验证服务
|
||||
4. **生成客户授权**:使用授权工具为客户创建 V1 授权
|
||||
5. **分发给客户**:客户配置后将只连接到您的服务器
|
||||
|
||||
## 适用场景
|
||||
|
||||
- **软件代理商**:分销软件,赚取差价
|
||||
- **系统集成商**:整合到您的解决方案中
|
||||
- **独立开发者**:基于本软件进行定制开发
|
||||
- **企业 IT 部门**:内部部署,统一管理
|
||||
|
||||
## 安全特性
|
||||
|
||||
### 密码学保障
|
||||
|
||||
采用 **ECDSA P-256** 椭圆曲线数字签名算法:
|
||||
- 与 HTTPS、主流数字货币使用相同级别的加密技术
|
||||
- 目前没有已知的有效攻击方法
|
||||
- 暴力破解在计算上不可行
|
||||
|
||||
### 多层验证机制
|
||||
|
||||
```
|
||||
验证流程:
|
||||
|
||||
设备绑定检查 ──────▶ 失败 ──▶ 拒绝
|
||||
│
|
||||
▼ 通过
|
||||
时效性检查 ────────▶ 失败 ──▶ 拒绝
|
||||
│
|
||||
▼ 通过
|
||||
数字签名验证 ──────▶ 失败 ──▶ 拒绝
|
||||
│
|
||||
▼ 通过
|
||||
授权链完整性检查 ──▶ 失败 ──▶ 拒绝
|
||||
│
|
||||
▼ 通过
|
||||
授权成功
|
||||
```
|
||||
|
||||
### 防护能力
|
||||
|
||||
| 攻击方式 | 防护措施 | 效果 |
|
||||
|----------|----------|------|
|
||||
| 伪造授权 | 数字签名验证 | 无法伪造 |
|
||||
| 复制授权 | 设备绑定 | 无法复制到其他设备 |
|
||||
| 修改有效期 | 签名保护 | 修改后签名失效 |
|
||||
| 替换验证模块 | 模块互锁 | 触发异常检测 |
|
||||
|
||||
### 给潜在破解者的说明
|
||||
|
||||
> 破解本软件所需的时间和技术成本,远超购买正版授权的费用。与其花费大量精力在可能失败的尝试上,不如购买正版,获得稳定可靠的使用体验和技术支持。
|
||||
|
||||
## 代理商常见问题
|
||||
|
||||
### 我的授权会被撤销吗?
|
||||
|
||||
正常合规使用情况下不会。授权撤销仅针对严重违规行为(如用于违法用途)。
|
||||
|
||||
### 续期如何操作?
|
||||
|
||||
支持离线续期。您可以通过离线方式获取新的授权凭证,无需服务器联网。
|
||||
|
||||
### 客户更换设备怎么处理?
|
||||
|
||||
您可以使用授权工具为客户重新生成授权,绑定到新设备。
|
||||
|
||||
### 我的客户数据安全吗?
|
||||
|
||||
完全安全。您的客户只连接到您的服务器,所有数据由您掌控,不经过任何第三方。
|
||||
|
||||
---
|
||||
|
||||
# 合法合规使用
|
||||
|
||||
**本章节适用于所有用户,请务必阅读。**
|
||||
|
||||
## 使用须知
|
||||
|
||||
本软件为**远程管理工具**,旨在帮助用户合法管理自有设备或经授权的设备。使用时必须遵守:
|
||||
|
||||
1. **合法授权**:仅在您拥有或获得明确授权的设备上使用
|
||||
2. **知情同意**:确保被管理设备的所有者/使用者知晓并同意
|
||||
3. **遵守法律**:遵守您所在地区关于远程访问、数据隐私的法律法规
|
||||
4. **正当用途**:仅用于合法的 IT 管理、技术支持、设备监控等目的
|
||||
|
||||
## 禁止行为
|
||||
|
||||
以下行为被严格禁止,违反者将承担全部法律责任:
|
||||
|
||||
- 未经授权访问他人计算机或网络
|
||||
- 用于窃取数据、监视他人隐私
|
||||
- 传播恶意软件或进行网络攻击
|
||||
- 任何违反当地法律法规的行为
|
||||
|
||||
## 免责声明
|
||||
|
||||
1. **工具中立性**:本软件是中立的技术工具,其用途取决于使用者。开发者提供的是合法的远程管理解决方案。
|
||||
|
||||
2. **使用者责任**:用户对使用本软件的行为及其后果承担全部责任。开发者不对任何滥用、非法使用或不当使用导致的损失负责。
|
||||
|
||||
3. **法律合规**:购买和使用本软件即表示您确认将仅用于合法目的,并承诺遵守所有适用的法律法规。
|
||||
|
||||
4. **代理商责任**:代理商在分发授权时,有责任告知最终用户上述使用须知。
|
||||
|
||||
5. **技术支持范围**:我们仅为合法合规的使用场景提供技术支持。
|
||||
|
||||
> **警告**:非法入侵计算机信息系统在大多数国家和地区属于刑事犯罪,可能面临严重的法律后果,包括罚款和监禁。
|
||||
|
||||
---
|
||||
|
||||
# 技术支持
|
||||
|
||||
如有授权相关问题:
|
||||
- **最终用户**:请联系您的服务商
|
||||
- **代理商/开发者**:请联系软件开发商
|
||||
|
||||
---
|
||||
|
||||
*本文档仅为概要说明,具体实现细节受知识产权保护。*
|
||||
|
||||
*使用本软件即表示您已阅读、理解并同意本文档所述的所有条款。*
|
||||
668
docs/NetworkSetup.md
Normal file
668
docs/NetworkSetup.md
Normal file
@@ -0,0 +1,668 @@
|
||||
# YAMA 多级网络搭建指南
|
||||
|
||||
> 构建总控 → 二级 → 受管端的监控网络
|
||||
|
||||
---
|
||||
|
||||
## 目标读者
|
||||
|
||||
- 需要搭建多级监控网络的客户
|
||||
- 有多个下级代理/分支机构需要管理
|
||||
- 需要统一管理和分级授权
|
||||
|
||||
---
|
||||
|
||||
## 第一部分:架构概述
|
||||
|
||||
### 1. 网络拓扑
|
||||
|
||||
```
|
||||
您(总控/一级)
|
||||
│
|
||||
├── 二级控制端 A ──→ 受管端 A1, A2, A3...
|
||||
│
|
||||
├── 二级控制端 B ──→ 受管端 B1, B2, B3...
|
||||
│
|
||||
└── 二级控制端 C ──→ 受管端 C1, C2, C3...
|
||||
```
|
||||
|
||||
### 2. 角色说明
|
||||
|
||||
| 角色 | 职责 | 权限 |
|
||||
|------|------|------|
|
||||
| 总控(您) | 管理二级、分配授权、提供 FRP 代理 | 完整权限 |
|
||||
| 二级控制端 | 管理受管端、使用远程功能 | 受限于您的授权 |
|
||||
| 受管端 | 被远程管理的电脑 | 无主动权限 |
|
||||
|
||||
### 3. 授权链
|
||||
|
||||
授权采用链式传递机制:
|
||||
|
||||
```
|
||||
您的上级 → 授权给您
|
||||
↓
|
||||
您 → 授权给二级
|
||||
↓
|
||||
二级(可选)→ 授权给三级
|
||||
```
|
||||
|
||||
**关键限制:**
|
||||
- 下级有效期不能超过您的有效期
|
||||
- 下级的并发数限制不能超过上级为您设置的限制
|
||||
- 下级的功能权限不超过您的权限
|
||||
|
||||
---
|
||||
|
||||
## 第二部分:准备工作
|
||||
|
||||
### 4. 服务器规划
|
||||
|
||||
根据您的资源和需求,选择以下方案之一:
|
||||
|
||||
#### 方案 A:集中式(推荐新手)
|
||||
|
||||
```
|
||||
您的服务器(公网)
|
||||
│
|
||||
│ [FRPS 服务 :7000]
|
||||
│ [映射端口 :20001, :20002, ...]
|
||||
│
|
||||
├── 二级 A(通过 FRP 端口 20001 接收受管端连接)
|
||||
├── 二级 B(通过 FRP 端口 20002 接收受管端连接)
|
||||
└── 二级 C(通过 FRP 端口 20003 接收受管端连接)
|
||||
```
|
||||
|
||||
**特点:**
|
||||
- 您有 1 台公网服务器
|
||||
- 所有二级通过 FRP 连接您的服务器
|
||||
- 二级无需公网 IP
|
||||
- 您负责维护 FRP 服务
|
||||
|
||||
**适用场景:** 二级客户没有技术能力或公网服务器
|
||||
|
||||
#### 方案 B:分布式
|
||||
|
||||
```
|
||||
您的服务器 二级 A 服务器 二级 B 服务器
|
||||
│ │ │
|
||||
│ [监听端口] [监听端口]
|
||||
│ │ │
|
||||
└─────授权─────→ A B
|
||||
│ │
|
||||
受管端 A1-An 受管端 B1-Bn
|
||||
```
|
||||
|
||||
**特点:**
|
||||
- 您和各二级都有自己的公网服务器
|
||||
- 各二级直接使用自己的公网 IP
|
||||
- 性能更好,不依赖您的服务器
|
||||
|
||||
**适用场景:** 二级有自己的服务器资源
|
||||
|
||||
#### 方案 C:混合式
|
||||
|
||||
```
|
||||
您的服务器
|
||||
│
|
||||
│ [FRPS 服务]
|
||||
│
|
||||
├── 二级 A ─(FRP)─┘ ← 无服务器,使用 FRP
|
||||
│
|
||||
└── 二级 B(有服务器)← 直接使用自己的 IP
|
||||
│
|
||||
受管端 B1-Bn
|
||||
```
|
||||
|
||||
**特点:** 灵活配置,根据各二级实际情况分配
|
||||
|
||||
### 5. 端口规划
|
||||
|
||||
建议预先规划端口分配:
|
||||
|
||||
| 用途 | 建议端口 | 说明 |
|
||||
|------|---------|------|
|
||||
| 主控监听 | 6543 | 受管端连接端口 |
|
||||
| FRPS 服务 | 7000 | FRP 服务器监听 |
|
||||
| 二级映射范围 | 20000-29999 | 为各二级分配的端口 |
|
||||
|
||||
**容量估算:**
|
||||
- 端口范围 20000-29999 可容纳约 10000 个二级
|
||||
- 实际建议每台服务器不超过 500 个二级(带宽考虑)
|
||||
|
||||
### 6. 信息收集
|
||||
|
||||
在开始配置之前,收集各二级的信息:
|
||||
|
||||
| 信息 | 用途 | 获取方式 |
|
||||
|------|------|---------|
|
||||
| 设备序列号 | 生成授权 | 二级运行 YAMA 后获取 |
|
||||
| 计划并发数 | 授权配置 | 与二级协商确定 |
|
||||
| 有无公网服务器 | 决定是否需要 FRP | 询问二级 |
|
||||
| 使用期限 | 授权有效期 | 与二级协商确定 |
|
||||
|
||||
---
|
||||
|
||||
## 第三部分:配置 FRP 服务
|
||||
|
||||
> 如果所有二级都有公网服务器,可跳过本部分
|
||||
|
||||
### 7. 系统要求
|
||||
|
||||
FRP 服务功能需要满足以下条件:
|
||||
|
||||
| 要求 | 说明 |
|
||||
|------|------|
|
||||
| 操作系统 | Windows 10 / Server 2016 或更高版本 |
|
||||
| 架构 | 64 位 |
|
||||
| 网络 | 服务器具有公网 IP |
|
||||
|
||||
> **注意**:32 位系统或 Windows 7/Server 2012 等旧系统不支持 FRP 功能。
|
||||
|
||||
### 8. 启用 FRP 代理功能
|
||||
|
||||
#### 8.1 打开配置界面
|
||||
|
||||
点击菜单栏 **扩展** → **下级FRP代理设置**
|
||||
|
||||
#### 8.2 启用服务
|
||||
|
||||
勾选 **"启用为下级提供 FRP 代理"**
|
||||
|
||||
#### 8.3 选择 FRPS 模式
|
||||
|
||||
**方式 A:本地运行 FRPS(推荐)**
|
||||
|
||||
勾选 **"FRPS 运行在本机"**
|
||||
|
||||
| 优点 | 说明 |
|
||||
|------|------|
|
||||
| 简单 | 无需额外部署 FRPS |
|
||||
| 集成 | 程序自动管理 FRPS 进程 |
|
||||
| 免配置 | 无需手动配置 FRPS |
|
||||
|
||||
**方式 B:使用外部 FRPS**
|
||||
|
||||
如果您已有 FRPS 服务器:
|
||||
|
||||
1. 取消勾选 "FRPS 运行在本机"
|
||||
2. 填写 FRPS 服务器地址
|
||||
3. 填写 FRPS 端口
|
||||
4. 填写认证 Token(需与 FRPS 配置一致)
|
||||
|
||||
#### 8.4 设置端口和认证
|
||||
|
||||
| 设置项 | 说明 | 建议值 |
|
||||
|--------|------|-------|
|
||||
| FRPS 端口 | FRPS 服务监听端口 | 7000 |
|
||||
| 认证 Token | FRPS 认证密钥 | 使用复杂字符串 |
|
||||
|
||||
#### 8.5 设置分配范围
|
||||
|
||||
| 设置项 | 说明 | 建议值 |
|
||||
|--------|------|-------|
|
||||
| 起始端口 | 分配给二级的端口起始 | 20000 |
|
||||
| 结束端口 | 分配给二级的端口结束 | 29999 |
|
||||
|
||||
每个二级占用 1 个端口,端口在范围内自动递增分配。
|
||||
|
||||
### 9. 保存并启动
|
||||
|
||||
1. 点击"确定"保存配置
|
||||
2. 如选择本地 FRPS,程序会自动启动 FRPS 服务
|
||||
3. 状态栏中间会显示 FRPS 信息(如 `FRPS :7000`)
|
||||
|
||||
> **显示优先级**:如果您同时有上级配置的 FRP 和本机 FRPS,状态栏优先显示上级配置的 FRP 地址。
|
||||
|
||||
### 10. 开放防火墙
|
||||
|
||||
FRPS 服务需要开放以下端口:
|
||||
|
||||
| 端口 | 协议 | 用途 |
|
||||
|------|------|------|
|
||||
| 7000(或您配置的端口) | TCP | FRPS 服务端口 |
|
||||
| 20000-29999(或您配置的范围) | TCP | 二级映射端口 |
|
||||
|
||||
**Windows 防火墙设置:**
|
||||
|
||||
1. 打开"Windows Defender 防火墙" → "高级设置"
|
||||
2. "入站规则" → "新建规则"
|
||||
3. 选择"端口" → "TCP"
|
||||
4. 输入端口:`7000, 20000-29999`
|
||||
5. 选择"允许连接" → 完成
|
||||
|
||||
**云服务器安全组:**
|
||||
|
||||
在云控制台添加入站规则:
|
||||
- TCP 7000(FRPS)
|
||||
- TCP 20000-29999(映射范围)
|
||||
|
||||
---
|
||||
|
||||
## 第四部分:为二级生成授权
|
||||
|
||||
### 11. 获取二级序列号
|
||||
|
||||
#### 11.1 二级操作步骤
|
||||
|
||||
指导二级完成以下操作:
|
||||
|
||||
1. 下载并运行 YAMA.exe(首次运行会提示未授权,属正常现象)
|
||||
2. 点击菜单 **其他** → **申请授权**
|
||||
3. 首次会显示使用条款,确认本软件仅限合法正当使用
|
||||
4. 点击"确认"后显示序列号
|
||||
5. 复制序列号并发送给您
|
||||
|
||||
> **提示**:导入授权后,该菜单会变为 **其他** → **序列号**
|
||||
|
||||
**序列号格式:** `XXXX-XXXX-XXXX-XXXX`
|
||||
|
||||
#### 11.2 序列号传递建议
|
||||
|
||||
- 微信/QQ:直接复制发送
|
||||
- 邮件:注明"YAMA 序列号"
|
||||
- 截图:包含完整序列号
|
||||
|
||||
### 12. 打开授权生成界面
|
||||
|
||||
点击菜单栏 **工具** → **口令生成**
|
||||
|
||||
### 13. 填写授权信息
|
||||
|
||||
#### 13.1 基本信息
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| 序列号 | 二级提供的设备序列号 | b40f-638f-ebc8-6d54 |
|
||||
| 备注 | 便于识别的说明(可选) | "张三 - 华东区" |
|
||||
|
||||
#### 13.2 有效期设置
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| 起始日期 | 授权生效日期 |
|
||||
| 结束日期 | 授权过期日期 |
|
||||
|
||||
**常见期限设置:**
|
||||
|
||||
| 类型 | 期限 | 说明 |
|
||||
|------|------|------|
|
||||
| 试用 | 7-15 天 | 短期验证 |
|
||||
| 月付 | 1 个月 | 按月收费 |
|
||||
| 季付 | 3 个月 | 按季收费 |
|
||||
| 年付 | 12 个月 | 按年收费 |
|
||||
|
||||
> **重要**:结束日期不能超过您自己授权的有效期。
|
||||
|
||||
#### 13.3 并发数设置
|
||||
|
||||
设置二级可同时管理的受管端数量。
|
||||
|
||||
**注意事项:**
|
||||
- 为二级设置的并发数限制不能超过上级为您设置的限制
|
||||
- 可根据二级需求灵活分配
|
||||
|
||||
### 14. 分配 FRP 端口(如需要)
|
||||
|
||||
如果二级需要使用您的 FRP 代理:
|
||||
|
||||
#### 14.1 勾选 FRP 代理
|
||||
|
||||
在授权生成界面勾选 **"FRP 代理"** 选项
|
||||
|
||||
#### 14.2 端口分配方式
|
||||
|
||||
| 方式 | 说明 | 推荐场景 |
|
||||
|------|------|---------|
|
||||
| 自动分配 | 系统自动选择可用端口 | 大多数情况 |
|
||||
| 手动指定 | 自己输入端口号 | 特殊需求 |
|
||||
|
||||
#### 14.3 记录分配信息
|
||||
|
||||
建议记录二级与端口的对应关系:
|
||||
|
||||
| 二级 | 序列号 | 分配端口 | 有效期 |
|
||||
|------|-------|---------|-------|
|
||||
| 张三 | b40f-... | 20001 | 2026-12-31 |
|
||||
| 李四 | a12c-... | 20002 | 2026-12-31 |
|
||||
|
||||
### 15. 生成授权
|
||||
|
||||
1. 确认所有信息无误
|
||||
2. 点击"生成"按钮
|
||||
3. 授权信息保存到本地数据库
|
||||
|
||||
### 16. 发送授权给二级
|
||||
|
||||
有两种方式将授权发送给二级:
|
||||
|
||||
#### 方式 A:在线发送(推荐)
|
||||
|
||||
如果二级使用您通过 **工具** → **主控生成** 分发的程序:
|
||||
|
||||
1. 二级程序已硬编码您的地址,启动后会自动连接到您
|
||||
2. 您在主机列表中可以看到已连接但未授权的二级
|
||||
3. 右键点击该主机 → **发送授权**
|
||||
4. 选择已生成的授权记录
|
||||
5. 通知二级重启程序
|
||||
|
||||
**二级重启流程(如授权包含 FRP):**
|
||||
- 第一次重启:验证授权,提示"授权成功",同时收到 FRP 配置
|
||||
- 第二次重启:应用 FRP 配置,开始使用 FRP 代理
|
||||
|
||||
**优点:** 无需传输文件,操作简便
|
||||
|
||||
#### 方式 B:离线发送(lic 文件)
|
||||
|
||||
如果二级无法先连接到您(如网络原因),可导出授权文件:
|
||||
|
||||
1. 在授权生成界面点击"导出"按钮
|
||||
2. 生成 `*.lic` 文件并发送给二级
|
||||
3. 二级通过 **工具** → **导入口令...** 导入
|
||||
4. 重启程序使授权生效
|
||||
|
||||
**文件命名建议:** `二级名称_日期.lic`,如 `张三_20260407.lic`
|
||||
|
||||
---
|
||||
|
||||
## 第五部分:二级部署
|
||||
|
||||
### 17. 二级获取授权
|
||||
|
||||
根据您选择的授权发送方式:
|
||||
|
||||
#### 方式 A:在线接收(推荐)
|
||||
|
||||
如果二级使用您分发的程序:
|
||||
1. 运行您分发的程序,自动连接到您
|
||||
2. 等待您通过右键菜单发送授权
|
||||
3. 收到授权后重启程序,提示"授权成功"
|
||||
4. 如授权包含 FRP 配置,需再次重启以应用 FRP
|
||||
|
||||
#### 方式 B:离线导入(lic 文件)
|
||||
|
||||
如果收到 lic 文件:
|
||||
1. 运行 YAMA.exe
|
||||
2. 点击菜单 **工具** → **导入口令...**
|
||||
3. 选择 `*.lic` 文件
|
||||
4. 看到"导入成功"提示后重启程序
|
||||
5. 如授权包含 FRP 配置,需再次重启以应用 FRP
|
||||
|
||||
### 18. 二级验证
|
||||
|
||||
二级需要验证以下内容:
|
||||
|
||||
#### 18.1 授权状态
|
||||
|
||||
查看状态栏显示的:
|
||||
- 有效期信息
|
||||
- 并发数限制
|
||||
|
||||
#### 18.2 FRP 连接(如有)
|
||||
|
||||
如果授权包含 FRP 配置:
|
||||
- 状态栏应显示 FRP 连接地址
|
||||
- 连接成功会显示分配的端口
|
||||
|
||||
#### 18.3 地址和端口
|
||||
|
||||
检查设置页面(菜单栏 **菜单 → 设置**):
|
||||
- 地址应为 FRP 服务器地址或自有公网地址
|
||||
- 端口应为分配的 FRP 端口或自有监听端口
|
||||
|
||||
### 19. 二级生成受管程序
|
||||
|
||||
二级需要生成自己的受管程序:
|
||||
|
||||
1. 打开 **工具** → **主控生成**
|
||||
2. 地址和端口已自动填入(来自 FRP 配置或设置)
|
||||
3. 选择载荷类型
|
||||
4. 生成并分发给目标电脑
|
||||
|
||||
**重要**:二级生成的受管程序会连接到二级的地址端口,而非您的服务器。
|
||||
|
||||
---
|
||||
|
||||
## 第六部分:日常管理
|
||||
|
||||
### 20. 监控网络状态
|
||||
|
||||
#### 20.1 查看二级在线状态
|
||||
|
||||
二级程序运行时会连接到您这里进行授权验证,您可以在主机列表中看到已连接的二级数量。
|
||||
|
||||
**主机列表显示内容:**
|
||||
- 直接连接到您的受管端
|
||||
- 已连接的二级控制端
|
||||
|
||||
#### 20.2 FRP 连接监控
|
||||
|
||||
如需查看更详细的连接记录,可查看 FRP 日志:
|
||||
- 日志位置:程序目录下的 `frps.log`
|
||||
- 包含二级连接/断开的时间记录
|
||||
|
||||
### 21. 授权管理
|
||||
|
||||
#### 21.1 查看已发放授权
|
||||
|
||||
点击菜单 **工具** → **授权管理**
|
||||
|
||||
列表显示:
|
||||
- 序列号
|
||||
- 备注
|
||||
- 有效期
|
||||
- 并发数
|
||||
- FRP 端口(如有)
|
||||
|
||||
#### 21.2 授权续期
|
||||
|
||||
**一级给二级续期:**
|
||||
|
||||
1. 在列表中找到需要续期的授权
|
||||
2. 双击或点击"编辑"
|
||||
3. 修改结束日期并保存
|
||||
|
||||
**二级更新授权:**
|
||||
- 二级重启程序即可自动获取新的有效期(通过网络验证)
|
||||
- 通常**无需重新发送 lic 文件**
|
||||
|
||||
> **注意**:只有当二级的序列号(SN)发生变化时,才需要重新生成并发送授权文件。
|
||||
|
||||
---
|
||||
|
||||
#### 21.3 全链条续期(开发者续期一级)
|
||||
|
||||
如果您是从开发者获得授权的一级用户,当开发者给您续期后:
|
||||
|
||||
**自动续期机制:**
|
||||
|
||||
```
|
||||
开发者给一级续期
|
||||
│
|
||||
↓
|
||||
一级重启程序,V2 验证获取新的 Authorization
|
||||
│
|
||||
↓
|
||||
二级连接一级时,自动获取新的 Authorization
|
||||
│
|
||||
↓
|
||||
全链条有效期自动延长
|
||||
```
|
||||
|
||||
**关键点:**
|
||||
|
||||
| 层级 | 授权来源 | 续期方式 |
|
||||
|------|---------|---------|
|
||||
| 一级 | 开发者(V2 ECDSA) | 开发者续期后,一级重启程序自动获取 |
|
||||
| 二级 | 一级(V1 HMAC) + Authorization | 一级更新后,二级重启程序自动获取 |
|
||||
|
||||
**双重控制机制:**
|
||||
|
||||
二级用户同时受两个授权控制:
|
||||
|
||||
1. **一级的 V1 HMAC 授权** - 一级直接签发给二级
|
||||
- 控制二级是否能连接到一级
|
||||
- 有效期由一级设定
|
||||
|
||||
2. **开发者的 Authorization** - 通过一级传递给二级
|
||||
- 控制整个授权链的最终有效期
|
||||
- 有效期由开发者设定
|
||||
|
||||
两个授权都必须有效,二级才能正常运行。
|
||||
|
||||
**续期示例:**
|
||||
|
||||
| 场景 | 一级 V1 有效期 | Authorization 有效期 | 二级状态 |
|
||||
|------|--------------|---------------------|---------|
|
||||
| 正常 | 2027-12-31 | 2027-12-31 | ✅ 正常运行 |
|
||||
| 一级未给二级续期 | 2026-12-31 (过期) | 2027-12-31 | ❌ 连接失败 |
|
||||
| 开发者未给一级续期 | 2027-12-31 | 2026-12-31 (过期) | ❌ 验证失败 |
|
||||
|
||||
> **提示**:当开发者给一级续期后,一级需要重启程序以获取新的 Authorization。之后二级重启程序即可自动获取更新。
|
||||
|
||||
#### 21.4 授权撤销
|
||||
|
||||
如需撤销二级授权:
|
||||
1. 在列表中选择目标授权
|
||||
2. 点击"删除"或"禁用"
|
||||
3. 二级下次连接验证时将失效
|
||||
|
||||
### 22. 故障处理
|
||||
|
||||
| 问题 | 排查步骤 |
|
||||
|------|---------|
|
||||
| 二级无法连接 FRP | 1. 检查 FRPS 是否运行<br>2. 检查防火墙<br>3. 检查端口是否正确 |
|
||||
| 授权显示无效 | 1. 确认序列号匹配<br>2. 检查有效期<br>3. 重新导出导入 |
|
||||
| 并发数超限 | 1. 检查当前连接数<br>2. 升级授权或释放连接 |
|
||||
|
||||
---
|
||||
|
||||
## 第七部分:扩展网络
|
||||
|
||||
### 23. 添加新二级
|
||||
|
||||
重复第四部分步骤:
|
||||
1. 获取新二级序列号
|
||||
2. 生成授权(端口自动递增)
|
||||
3. 导出并发送
|
||||
4. 指导二级部署
|
||||
|
||||
### 24. 二级发展三级
|
||||
|
||||
如果您的授权允许,二级也可以发展下级:
|
||||
|
||||
```
|
||||
您(一级)
|
||||
│
|
||||
└── 二级 A
|
||||
│
|
||||
├── 三级 A1 ──→ 受管端
|
||||
└── 三级 A2 ──→ 受管端
|
||||
```
|
||||
|
||||
**限制条件:**
|
||||
- 需要您的授权支持多级
|
||||
- 三级的权限不超过二级
|
||||
- 二级需要为三级提供 FRP(如需要)
|
||||
|
||||
### 25. 网络容量规划
|
||||
|
||||
| 规模 | 建议配置 |
|
||||
|------|---------|
|
||||
| 小型(<50 二级) | 单服务器即可 |
|
||||
| 中型(50-200 二级) | 考虑带宽升级 |
|
||||
| 大型(>200 二级) | 分布式部署,多服务器 |
|
||||
|
||||
**带宽估算:**
|
||||
- 每个活跃远程桌面:1-5 Mbps
|
||||
- 每个空闲连接:<10 Kbps
|
||||
- 建议预留 50% 余量
|
||||
|
||||
---
|
||||
|
||||
## 附录
|
||||
|
||||
### A. 部署检查清单
|
||||
|
||||
**总控端**
|
||||
- [ ] YAMA.exe 已部署
|
||||
- [ ] 您的授权已导入
|
||||
- [ ] 防火墙已开放主控端口
|
||||
- [ ] FRPS 已启动(如需要)
|
||||
- [ ] 防火墙已开放 FRPS 端口范围
|
||||
|
||||
**二级控制端**
|
||||
- [ ] 二级已获取序列号
|
||||
- [ ] 您已生成授权
|
||||
- [ ] 授权文件已发送给二级
|
||||
- [ ] 二级已导入授权并重启
|
||||
- [ ] 二级 FRP 已连接(如需要)
|
||||
- [ ] 二级已生成受管程序
|
||||
|
||||
**受管端**
|
||||
- [ ] 受管程序已生成
|
||||
- [ ] 已传输到目标电脑
|
||||
- [ ] 已成功上线
|
||||
|
||||
### B. 网络拓扑示例
|
||||
|
||||
**完整示例:集中式 FRP 方案**
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ 您的服务器 │
|
||||
│ 公网 IP: 1.2.3.4 │
|
||||
│ │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌───────────────┐ │
|
||||
│ │ YAMA │ │ FRPS │ │ 防火墙 │ │
|
||||
│ │ (主控) │ │ :7000 │ │ 开放端口说明 │ │
|
||||
│ └─────────┘ └─────────┘ │───────────────│ │
|
||||
│ │ │ │ 6543 主控端口│ │
|
||||
│ │ │ │ 7000 FRPS端口│ │
|
||||
│ │ │ │ 20000+ 映射端口│ │
|
||||
│ │ │ └───────────────┘ │
|
||||
└───────┼──────────────┼───────────────────────────┘
|
||||
│ │
|
||||
│ │
|
||||
─────┴──────────────┴─────────────────
|
||||
互联网
|
||||
──────────────────────────────────────
|
||||
│ │ │
|
||||
│ │ │
|
||||
┌───────┴───┐ ┌─────┴─────┐ ┌─────┴─────┐
|
||||
│ 二级 A │ │ 二级 B │ │ 二级 C │
|
||||
│ FRP :20001 │ │ FRP :20002 │ │ FRP :20003 │
|
||||
└───────┬───┘ └─────┬─────┘ └─────┬─────┘
|
||||
│ │ │
|
||||
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
|
||||
│ 受管端 │ │ 受管端 │ │ 受管端 │
|
||||
│ A1-A10 │ │ B1-B20 │ │ C1-C5 │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
```
|
||||
|
||||
### C. 常见问题
|
||||
|
||||
| 问题 | 原因 | 解决方案 |
|
||||
|------|------|---------|
|
||||
| 二级无法连接 FRP | 端口未开放 | 检查防火墙和安全组,开放 7000 和映射端口 |
|
||||
| 授权显示无效 | 序列号不匹配 | 确认二级发送的序列号正确 |
|
||||
| 并发数超限 | 超过授权限制 | 升级授权或减少同时在线的受管端数量 |
|
||||
| FRP 连接不稳定 | 网络问题 | 检查服务器带宽,考虑分布式部署 |
|
||||
| 二级生成的受管端连不上 | 地址端口错误 | 检查二级设置,确认 FRP 配置正确 |
|
||||
|
||||
---
|
||||
|
||||
## 技术支持
|
||||
|
||||
| 渠道 | 联系方式 |
|
||||
|------|---------|
|
||||
| QQ | 962914132 |
|
||||
| Telegram | [@doge_grandfather](https://t.me/doge_grandfather) |
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [快速部署指南](QuickStart.md) - 首次部署
|
||||
- [日常使用手册](UserManual.md) - 远程管理功能详解
|
||||
- [代理商运营手册](AgentManual.md) - 授权管理详解
|
||||
- [定制化开发指南](CustomizationGuide.md) - 二次开发与品牌定制
|
||||
431
docs/NotifyFeature_Design.md
Normal file
431
docs/NotifyFeature_Design.md
Normal file
@@ -0,0 +1,431 @@
|
||||
# 通知功能设计方案
|
||||
|
||||
## 概述
|
||||
|
||||
在主对话框菜单"菜单(&F)"下增加"通知(&N)"功能,支持配置 SMTP 邮件服务,当主机上线且匹配指定条件时自动发送邮件通知。
|
||||
|
||||
## 系统要求
|
||||
|
||||
- **PowerShell 5.1+**: 内置于 Windows 10 / Server 2016+
|
||||
- Windows 7/8.1 需手动安装 WMF 5.1
|
||||
- 不支持 PowerShell 的系统将禁用此功能(对话框控件灰显)
|
||||
|
||||
## 核心特性
|
||||
|
||||
1. **SMTP 配置**: 支持 Gmail 等主流邮件服务(需应用专用密码)
|
||||
2. **触发规则**: 主机上线时匹配指定列的文本
|
||||
3. **备注优先**: 匹配"计算机名"列时,优先使用备注(如有)
|
||||
4. **多关键词匹配**: 分号分隔,任一匹配即触发(大小写不敏感)
|
||||
5. **频率控制**: 同一主机 60 分钟内只通知一次
|
||||
6. **线程安全**: 使用 mutex 保护配置访问
|
||||
7. **异步发送**: 邮件发送不阻塞主线程
|
||||
8. **多语言支持**: 支持简体中文、英文、繁体中文
|
||||
|
||||
---
|
||||
|
||||
## 数据结构
|
||||
|
||||
### 常量定义
|
||||
|
||||
```cpp
|
||||
#define NOTIFY_COOLDOWN_MINUTES 15 // 同一主机通知冷却时间(分钟)
|
||||
```
|
||||
|
||||
### 通知类型枚举
|
||||
|
||||
```cpp
|
||||
enum NotifyTriggerType {
|
||||
NOTIFY_TRIGGER_NONE = 0,
|
||||
NOTIFY_TRIGGER_HOST_ONLINE = 1, // 主机上线
|
||||
// 未来可扩展:
|
||||
// NOTIFY_TRIGGER_HOST_OFFLINE = 2,
|
||||
// NOTIFY_TRIGGER_FILE_TRANSFER = 3,
|
||||
};
|
||||
```
|
||||
|
||||
### 单条通知规则
|
||||
|
||||
```cpp
|
||||
struct NotifyRule {
|
||||
bool enabled; // 是否启用
|
||||
NotifyTriggerType triggerType; // 触发类型
|
||||
int columnIndex; // 列编号 (0-based)
|
||||
std::string matchPattern; // 匹配字符串,分号分隔
|
||||
};
|
||||
```
|
||||
|
||||
### SMTP 配置
|
||||
|
||||
```cpp
|
||||
struct SmtpConfig {
|
||||
std::string server; // smtp.gmail.com
|
||||
int port; // 587
|
||||
bool useSSL; // true
|
||||
std::string username; // 发件人邮箱
|
||||
std::string password; // 应用专用密码 (XOR 加密存储)
|
||||
std::string recipient; // 收件人 (可选,为空则发给自己)
|
||||
|
||||
// 获取实际收件人
|
||||
std::string GetRecipient() const {
|
||||
return recipient.empty() ? username : recipient;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 完整通知配置
|
||||
|
||||
```cpp
|
||||
struct NotifyConfig {
|
||||
SmtpConfig smtp;
|
||||
std::vector<NotifyRule> rules; // 规则列表,支持多条
|
||||
|
||||
// 频率控制:记录每个主机最后通知时间 (仅内存,不持久化)
|
||||
std::unordered_map<uint64_t, time_t> lastNotifyTime;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 配置对话框 UI
|
||||
|
||||
```
|
||||
┌─ 通知设置 ──────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ ⚠️ Warning: Requires Windows 10 or later with PowerShell │
|
||||
│ 5.1+ (仅不支持时显示) │
|
||||
│ │
|
||||
│ ┌─ SMTP 配置 ────────────────────────────────────────────┐ │
|
||||
│ │ 服务器: [smtp.gmail.com ] 端口: [587 ] │ │
|
||||
│ │ ☑ 使用 SSL/TLS │ │
|
||||
│ │ 用户名: [your@gmail.com ] │ │
|
||||
│ │ 密码: [**************** ] │ │
|
||||
│ │ (Gmail 需使用应用专用密码) │ │
|
||||
│ │ 收件人: [ ] [测试] │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ 通知规则 ────────────────────────────────────────────┐ │
|
||||
│ │ ☑ 启用通知 │ │
|
||||
│ │ 触发条件: [主机上线 ▼] │ │
|
||||
│ │ 匹配列: [3 - 计算机名 ▼] │ │
|
||||
│ │ 关键词: [CEO;CFO;财务;服务器 ] │ │
|
||||
│ │ (多个关键词用分号分隔,匹配任一项即触发通知) │ │
|
||||
│ │ 提示: 同一主机 60 分钟内仅通知一次 │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [确定] [取消] │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**备注说明**: 收件人为空时,邮件发送给发件人自己(自我通知)。
|
||||
|
||||
---
|
||||
|
||||
## 邮件内容格式
|
||||
|
||||
邮件使用 HTML 格式发送 (`-BodyAsHtml`)。
|
||||
|
||||
### 主题
|
||||
|
||||
```
|
||||
[SimpleRemoter] Host Online: WIN-PC01 matched "服务器"
|
||||
```
|
||||
|
||||
### 正文 (HTML)
|
||||
|
||||
```html
|
||||
<b>Host Online Notification</b><br><br>
|
||||
Trigger Time: 2026-03-11 15:30:45<br>
|
||||
Match Rule: ComputerName contains "服务器"<br><br>
|
||||
<b>Host Information:</b><br>
|
||||
IP Address: 192.168.1.100<br>
|
||||
Location: 中国-北京<br>
|
||||
Computer Name: WIN-SERVER-01<br>
|
||||
OS: Windows Server 2022<br>
|
||||
Version: 1.2.7<br>
|
||||
<br>--<br><i>This email was sent automatically by SimpleRemoter</i>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 配置持久化
|
||||
|
||||
**存储位置**: `%APPDATA%\YAMA\notify.ini`
|
||||
|
||||
```ini
|
||||
[SMTP]
|
||||
Server=smtp.gmail.com
|
||||
Port=587
|
||||
UseSSL=1
|
||||
Username=your@gmail.com
|
||||
Password=<XOR加密后的字符串>
|
||||
Recipient=
|
||||
|
||||
[Rule_0]
|
||||
Enabled=1
|
||||
TriggerType=1
|
||||
ColumnIndex=3
|
||||
MatchPattern=CEO;CFO;服务器
|
||||
|
||||
; 未来扩展多规则
|
||||
;[Rule_1]
|
||||
;Enabled=1
|
||||
;...
|
||||
```
|
||||
|
||||
**注意**: `lastNotifyTime` 仅保存在内存中,服务端重启后清空。
|
||||
|
||||
---
|
||||
|
||||
## 核心逻辑
|
||||
|
||||
### PowerShell 检测
|
||||
|
||||
```cpp
|
||||
bool DetectPowerShellSupport() {
|
||||
std::string cmd = "powershell -NoProfile -Command "
|
||||
"\"Get-Command Send-MailMessage -ErrorAction SilentlyContinue\"";
|
||||
DWORD exitCode = 1;
|
||||
ExecutePowerShell(cmd, &exitCode, true); // hidden
|
||||
return (exitCode == 0);
|
||||
}
|
||||
```
|
||||
|
||||
### 频率控制 + 匹配检查 (线程安全)
|
||||
|
||||
```cpp
|
||||
bool NotifyManager::ShouldNotify(context* ctx, std::string& outMatchedKeyword,
|
||||
const CString& remark)
|
||||
{
|
||||
if (!m_powerShellAvailable) return false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
const NotifyRule& rule = m_config.GetRule();
|
||||
if (!rule.enabled) return false;
|
||||
if (rule.matchPattern.empty()) return false;
|
||||
if (!m_config.smtp.IsValid()) return false;
|
||||
|
||||
uint64_t clientId = ctx->GetClientID();
|
||||
time_t now = time(nullptr);
|
||||
|
||||
// 冷却检查
|
||||
auto it = m_config.lastNotifyTime.find(clientId);
|
||||
if (it != m_config.lastNotifyTime.end()) {
|
||||
if (now - it->second < NOTIFY_COOLDOWN_MINUTES * 60) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取匹配文本 (计算机名列优先使用备注)
|
||||
CString colText;
|
||||
if (rule.columnIndex == ONLINELIST_COMPUTER_NAME && !remark.IsEmpty()) {
|
||||
colText = remark;
|
||||
} else {
|
||||
colText = ctx->GetClientData(rule.columnIndex);
|
||||
}
|
||||
if (colText.IsEmpty()) return false;
|
||||
|
||||
// 大小写不敏感匹配
|
||||
std::string colLower = ToLower(CT2A(colText, CP_UTF8));
|
||||
for (const auto& kw : SplitString(rule.matchPattern, ';')) {
|
||||
std::string kwLower = ToLower(Trim(kw));
|
||||
if (!kwLower.empty() && colLower.find(kwLower) != std::string::npos) {
|
||||
outMatchedKeyword = Trim(kw);
|
||||
m_config.lastNotifyTime[clientId] = now;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
### 异步邮件发送
|
||||
|
||||
```cpp
|
||||
void NotifyManager::SendNotifyEmailAsync(const std::string& subject,
|
||||
const std::string& body)
|
||||
{
|
||||
if (!m_powerShellAvailable) return;
|
||||
|
||||
SmtpConfig smtp;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (!m_config.smtp.IsValid()) return;
|
||||
smtp = m_config.smtp;
|
||||
}
|
||||
|
||||
std::thread([this, smtp, subject, body]() {
|
||||
std::ostringstream ps;
|
||||
ps << "powershell -NoProfile -ExecutionPolicy Bypass -Command \"";
|
||||
ps << "$pass = ConvertTo-SecureString '" << EscapePowerShell(smtp.password)
|
||||
<< "' -AsPlainText -Force; ";
|
||||
ps << "$cred = New-Object PSCredential('" << smtp.username << "', $pass); ";
|
||||
ps << "Send-MailMessage ";
|
||||
ps << "-From '" << smtp.username << "' ";
|
||||
ps << "-To '" << smtp.GetRecipient() << "' ";
|
||||
ps << "-Subject '" << EscapePowerShell(subject) << "' ";
|
||||
ps << "-Body '" << EscapePowerShell(body) << "' ";
|
||||
ps << "-SmtpServer '" << smtp.server << "' ";
|
||||
ps << "-Port " << smtp.port << " ";
|
||||
if (smtp.useSSL) ps << "-UseSsl ";
|
||||
ps << "-Credential $cred -Encoding UTF8 -BodyAsHtml\"";
|
||||
|
||||
DWORD exitCode;
|
||||
ExecutePowerShell(ps.str(), &exitCode, true); // hidden
|
||||
}).detach();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 集成点
|
||||
|
||||
在 `2015RemoteDlg.cpp` 的 `AddList()` 函数中:
|
||||
|
||||
```cpp
|
||||
// 现有代码
|
||||
m_ClientIndex[id] = m_HostList.size();
|
||||
m_HostList.push_back(ContextObject);
|
||||
|
||||
// ========== 新增:通知检查 (带异常保护) ==========
|
||||
try {
|
||||
std::string matchedKeyword;
|
||||
CString remark = m_ClientMap->GetClientMapData(ContextObject->GetClientID(), MAP_NOTE);
|
||||
if (GetNotifyManager().ShouldNotify(ContextObject, matchedKeyword, remark)) {
|
||||
std::string subject, body;
|
||||
GetNotifyManager().BuildHostOnlineEmail(ContextObject, matchedKeyword, subject, body);
|
||||
GetNotifyManager().SendNotifyEmailAsync(subject, body);
|
||||
}
|
||||
} catch (...) {
|
||||
TRACE("[Notify] Exception in notification check\n");
|
||||
}
|
||||
// =================================================
|
||||
|
||||
ShowMessage(_TR("操作成功"), ...);
|
||||
```
|
||||
|
||||
初始化在 `OnInitDialog()` 中:
|
||||
|
||||
```cpp
|
||||
m_ClientMap->LoadFromFile(GetDbPath());
|
||||
|
||||
// 初始化通知管理器
|
||||
GetNotifyManager().Initialize();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 文件改动清单
|
||||
|
||||
| 文件 | 改动类型 | 说明 |
|
||||
|------|---------|------|
|
||||
| `resource.h` | 修改 | 新增菜单ID、对话框ID、控件ID (33028-33029, 332, 2408-2432) |
|
||||
| `2015Remote.rc` | 修改 | 添加菜单项、对话框模板、右键菜单"上线提醒" |
|
||||
| `NotifyConfig.h` | **新增** | 配置结构体定义 |
|
||||
| `NotifySettingsDlg.h` | **新增** | 配置对话框类声明 (继承 CDialogLangEx) |
|
||||
| `NotifySettingsDlg.cpp` | **新增** | 配置对话框实现,显式设置控件文本支持翻译 |
|
||||
| `NotifyManager.h` | **新增** | 通知管理器声明 (单例,线程安全) |
|
||||
| `NotifyManager.cpp` | **新增** | 检测、发送、配置读写逻辑 |
|
||||
| `2015RemoteDlg.h` | 修改 | 添加 `OnMenuNotifySettings()`, `OnOnlineLoginNotify()` 方法 |
|
||||
| `2015RemoteDlg.cpp` | 修改 | 菜单处理 + 上线时调用检查 + 右键快捷添加主机 |
|
||||
| `lang/en_US.ini` | 修改 | 添加英文翻译 |
|
||||
| `lang/zh_TW.ini` | 修改 | 添加繁体中文翻译 |
|
||||
|
||||
**实际代码量**: 约 1400+ 行
|
||||
|
||||
---
|
||||
|
||||
## 多语言支持
|
||||
|
||||
### 语言文件编码
|
||||
|
||||
**重要**: 语言文件使用 **GB2312** 编码,不是 UTF-8!
|
||||
|
||||
- 位置: `server/2015Remote/lang/*.ini`
|
||||
- 格式: `简体中文=翻译文本`
|
||||
|
||||
### 源文件编码
|
||||
|
||||
含中文字符的 C++ 源文件必须使用 **UTF-8 with BOM**,否则 MSVC 会报错 `C2001: 常量中有换行符`。
|
||||
|
||||
### 翻译示例
|
||||
|
||||
```ini
|
||||
; en_US.ini
|
||||
通知设置=Notify Settings
|
||||
SMTP 配置=SMTP Configuration
|
||||
测试邮件发送成功!=Test email sent successfully!
|
||||
测试邮件发送失败,请检查SMTP配置=Test email failed. Please check SMTP settings.
|
||||
|
||||
; zh_TW.ini
|
||||
通知设置=通知設定
|
||||
SMTP 配置=SMTP 配置
|
||||
测试邮件发送成功!=測試郵件發送成功!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 测试邮件
|
||||
|
||||
- **成功**: 显示 "测试邮件发送成功!"
|
||||
- **失败**: 显示 "测试邮件发送失败,请检查SMTP配置"
|
||||
- 详细错误信息使用 `TRACE()` 记录到调试日志
|
||||
|
||||
---
|
||||
|
||||
## 安全考虑
|
||||
|
||||
| 风险 | 处理方式 |
|
||||
|------|---------|
|
||||
| 密码明文存储 | XOR 混淆后存储 (非安全加密,仅防止直接可见) |
|
||||
| PowerShell 注入 | 转义单引号 `'` → `''`,转义换行 |
|
||||
| 邮件发送失败 | 静默失败,异步执行不影响主流程 |
|
||||
| 并发访问 | 使用 `std::mutex` 保护配置读写 |
|
||||
| 通知异常 | try-catch 包裹,异常不影响主机上线 |
|
||||
|
||||
---
|
||||
|
||||
## 列编号与匹配说明
|
||||
|
||||
| 列编号 | 列名称 | 匹配说明 |
|
||||
|--------|--------|----------|
|
||||
| 0 | IP地址 | 匹配原始 IP |
|
||||
| 1 | 地址 | 匹配端口号 |
|
||||
| 2 | 地理位置 | 匹配位置信息 |
|
||||
| 3 | 计算机名 | **优先匹配备注**,无备注时匹配计算机名 |
|
||||
| 4 | 操作系统 | 匹配 OS 信息 |
|
||||
| 5 | CPU | 匹配 CPU 信息 |
|
||||
| 6 | 摄像头 | 匹配有/无 |
|
||||
| 7 | 延迟 | 匹配 RTT 值 |
|
||||
| 8 | 版本 | 匹配客户端版本 |
|
||||
| 9 | 安装时间 | 匹配安装时间 |
|
||||
| 10 | 活动窗口 | 匹配当前窗口标题 |
|
||||
| 11 | 客户端类型 | 匹配类型标识 |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
1. 打开菜单 "菜单(&F)" → "通知(&N)"
|
||||
2. 配置 SMTP:
|
||||
- 服务器: `smtp.gmail.com`
|
||||
- 端口: `587`
|
||||
- 勾选 SSL/TLS
|
||||
- 用户名: 你的 Gmail 地址
|
||||
- 密码: [Gmail 应用专用密码](https://myaccount.google.com/apppasswords)
|
||||
3. 点击"测试"验证配置
|
||||
4. 配置规则:
|
||||
- 勾选"启用通知"
|
||||
- 选择"计算机名"列
|
||||
- 输入关键词: `CEO;CFO;财务部`
|
||||
5. 点击确定保存
|
||||
|
||||
当有主机上线且其备注或计算机名包含任一关键词时,你将收到邮件通知。
|
||||
|
||||
### 快捷添加主机
|
||||
|
||||
右键点击在线主机列表中的主机,选择"上线提醒"可快速将该主机添加到通知关键词列表:
|
||||
|
||||
- 自动使用主机备注(如有)或计算机名作为关键词
|
||||
- 自动去重,已存在的主机不会重复添加
|
||||
- 添加后自动启用通知规则
|
||||
327
docs/QuickStart.md
Normal file
327
docs/QuickStart.md
Normal file
@@ -0,0 +1,327 @@
|
||||
# YAMA 快速部署指南
|
||||
|
||||
> 10 分钟完成首次部署
|
||||
|
||||
---
|
||||
|
||||
## 目标读者
|
||||
|
||||
- 已有公网服务器的客户
|
||||
- 首次使用 YAMA
|
||||
- 希望快速验证功能
|
||||
|
||||
---
|
||||
|
||||
## 开始之前
|
||||
|
||||
### 您需要准备
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 公网服务器 | Windows 服务器,能从外网访问 |
|
||||
| 服务器公网 IP | 受管端将连接此地址 |
|
||||
| 授权文件 | `*.lic` 文件,由您的上级提供 |
|
||||
| YAMA.exe | 主控程序,从上级或官方渠道获取 |
|
||||
|
||||
### 系统要求
|
||||
|
||||
| 场景 | 操作系统要求 |
|
||||
|------|-------------|
|
||||
| 使用 FRP 代理 | Windows 10 / Server 2016 或更高版本(64位) |
|
||||
| 不使用 FRP(自有公网服务器) | Windows 7 / Server 2008 R2 或更高版本 |
|
||||
| 受管端(被控电脑) | Windows 7 或更高版本 |
|
||||
|
||||
> **提示**:如果您的授权文件包含 FRP 配置,说明您将使用 FRP 代理,请确保使用 64 位的 Windows 10 或更高版本。
|
||||
|
||||
---
|
||||
|
||||
## 第一步:部署主控端(3 分钟)
|
||||
|
||||
### 1.1 上传程序
|
||||
|
||||
将 `YAMA.exe` 上传到您的服务器,建议放置到独立目录:
|
||||
|
||||
```
|
||||
C:\YAMA\
|
||||
└── YAMA.exe
|
||||
```
|
||||
|
||||
程序运行后会自动创建以下数据:
|
||||
- **配置信息** - 存储在注册表 `HKCU\Software\YAMA`
|
||||
- **数据文件** - 存储在 `%APPDATA%\YAMA\` 目录(如 `licenses.db` 授权数据库)
|
||||
- **语言文件** - 程序同目录下的 `lang\` 文件夹(可选)
|
||||
|
||||
### 1.2 首次运行
|
||||
|
||||
双击运行 `YAMA.exe`,首次运行时程序会提示**未授权**状态,部分功能不可用。
|
||||
|
||||
> 如果您使用的是上级分发的程序,程序会自动连接到上级,您可以直接等待上级在线发送授权,跳过以下序列号获取步骤。
|
||||
|
||||
**获取序列号(离线授权方式):**
|
||||
1. 点击菜单 **其他** → **申请授权**
|
||||
2. 首次会显示使用条款,确认本软件仅限合法正当使用
|
||||
3. 点击"确认"后显示序列号(格式:`XXXX-XXXX-XXXX-XXXX`)
|
||||
4. 复制序列号发送给上级以获取授权
|
||||
|
||||
> **提示**:导入授权后,该菜单会变为 **其他** → **序列号**
|
||||
|
||||
### 1.3 获取授权
|
||||
|
||||
根据您上级的分发方式,有两种获取授权的方式:
|
||||
|
||||
**方式 A:在线接收(如使用上级分发的程序)**
|
||||
1. 运行上级分发给您的程序,自动连接到上级
|
||||
2. 等待上级通过在线方式发送授权
|
||||
3. 收到授权后重启程序,提示"授权成功"
|
||||
4. 如授权包含 FRP 配置,需再次重启以应用 FRP
|
||||
|
||||
**方式 B:离线导入(如收到 lic 文件)**
|
||||
1. 点击菜单栏 **工具** → **导入口令...**
|
||||
2. 选择您的 `*.lic` 授权文件
|
||||
3. 看到"导入成功"提示后,**关闭并重新启动程序**
|
||||
4. 如授权包含 FRP 配置,需再次重启以应用 FRP
|
||||
|
||||
### 1.4 验证授权状态
|
||||
|
||||
重启后,检查窗口底部的状态栏:
|
||||
|
||||
| 状态栏位置 | 显示内容 |
|
||||
|-----------|---------|
|
||||
| 左侧 | 当前分组主机数 / 总主机数 |
|
||||
| 中间 | FRP 连接地址(如授权包含 FRP 配置) |
|
||||
| 右侧 | 运行信息和授权信息(有效期、并发数) |
|
||||
|
||||
如果状态栏右侧显示授权有效期信息,说明授权已生效。
|
||||
|
||||
---
|
||||
|
||||
## 第二步:配置网络(2 分钟)
|
||||
|
||||
### 2.1 检查地址设置
|
||||
|
||||
1. 点击菜单栏 **菜单** → **设置**
|
||||
2. 查看"公网地址"栏位
|
||||
|
||||
**地址设置说明:**
|
||||
|
||||
| 情况 | 地址显示 | 说明 |
|
||||
|------|---------|------|
|
||||
| 使用 FRP 代理 | FRP 服务器地址(自动填入) | 受管端将连接 FRP 服务器 |
|
||||
| 自有公网服务器 | 您服务器的公网 IP | 受管端直连您的服务器 |
|
||||
|
||||
> **提示**:如果您的授权包含 FRP 配置,地址和端口会自动从授权文件中读取,通常无需手动修改。
|
||||
|
||||
### 2.2 确认监听端口
|
||||
|
||||
默认监听端口为 `6543`。如需修改,请在设置页面更改。
|
||||
|
||||
### 2.3 开放防火墙
|
||||
|
||||
**Windows 防火墙:**
|
||||
|
||||
1. 打开"Windows Defender 防火墙"
|
||||
2. 点击"高级设置"
|
||||
3. 选择"入站规则" → "新建规则"
|
||||
4. 选择"端口" → "TCP" → 输入端口号(如 `6543`)
|
||||
5. 选择"允许连接" → 完成
|
||||
|
||||
**云服务器安全组(阿里云/腾讯云等):**
|
||||
|
||||
1. 登录云控制台
|
||||
2. 找到安全组设置
|
||||
3. 添加入站规则:协议 TCP,端口 `6543`(或您设置的端口)
|
||||
|
||||
> **注意**:如果使用 FRP 代理,受管端连接的是 FRP 服务器而非您的服务器,此时您的服务器防火墙无需开放监听端口。
|
||||
|
||||
---
|
||||
|
||||
## 第三步:生成受管程序(2 分钟)
|
||||
|
||||
### 3.1 打开生成对话框
|
||||
|
||||
点击菜单栏 **工具** → **主控生成**
|
||||
|
||||
### 3.2 确认连接参数
|
||||
|
||||
生成对话框会自动填入地址和端口:
|
||||
|
||||
| 参数 | 来源 |
|
||||
|------|------|
|
||||
| IP 地址 | 自动从 FRP 配置或设置中读取 |
|
||||
| 端口 | 自动从 FRP 配置或设置中读取 |
|
||||
|
||||
> **重要**:请确认这些参数正确。受管端将使用这些参数连接主控。
|
||||
|
||||
### 3.3 选择载荷类型
|
||||
|
||||
| 类型 | 适用场景 | 推荐度 |
|
||||
|------|---------|-------|
|
||||
| ghost.exe | 独立运行程序,便于测试 | **新手推荐** |
|
||||
| TestRun - Windows 服务 | 开机自启,稳定运行 | **生产环境推荐** |
|
||||
| TestRun - 内存DLL | 内存运行,隐蔽性更好 | 进阶 |
|
||||
| ghost.exe - Windows 服务 | 开机自启,独立程序 | 备选 |
|
||||
|
||||
**首次使用建议选择 "ghost.exe"**,便于测试和排查问题。
|
||||
|
||||
**生产环境建议选择 "TestRun - Windows 服务"**,支持开机自启且运行稳定。
|
||||
|
||||
### 3.4 生成文件
|
||||
|
||||
1. 确认参数无误后,点击"确定"
|
||||
2. 选择保存位置
|
||||
3. 记录生成的文件路径
|
||||
|
||||
生成完成后,在指定位置会看到受管程序文件。
|
||||
|
||||
---
|
||||
|
||||
## 第四步:部署受管端(3 分钟)
|
||||
|
||||
### 4.1 传输文件
|
||||
|
||||
将生成的受管程序文件复制到目标电脑。传输方式可以是:
|
||||
- U 盘拷贝
|
||||
- 网络共享
|
||||
- 远程桌面拖拽
|
||||
- 其他文件传输方式
|
||||
|
||||
### 4.2 运行受管程序
|
||||
|
||||
在目标电脑上双击运行受管程序。
|
||||
|
||||
**运行后的预期行为:**
|
||||
- TestRun / ghost.exe:拷贝自身到目标目录 → 创建开机启动项 → 自我删除原文件 → 后台运行
|
||||
- 服务类型:需要管理员权限,会注册为 Windows 服务并自动启动
|
||||
|
||||
### 4.3 验证连接
|
||||
|
||||
回到主控端,查看主机列表:
|
||||
|
||||
- 等待几秒钟后,主机列表中出现新主机即表示部署成功
|
||||
|
||||
**如果受管端未上线,请检查:**
|
||||
|
||||
| 可能原因 | 解决方案 |
|
||||
|---------|---------|
|
||||
| 网络不通 | 检查目标电脑能否访问主控地址和端口 |
|
||||
| 防火墙拦截 | 确认主控端口已开放 |
|
||||
| IP/端口错误 | 重新生成受管程序,确认参数正确 |
|
||||
| FRP 未启动 | 检查 FRP 服务状态 |
|
||||
| 文件被占用 | 目标电脑已部署过旧版本,新文件无法覆盖。用任务管理器结束占用进程后重新部署(重启无效,旧版本会自动运行) |
|
||||
|
||||
---
|
||||
|
||||
## 第五步:测试远程功能
|
||||
|
||||
### 5.1 远程桌面
|
||||
|
||||
1. 在主机列表中**右键单击**在线主机,选择"远程桌面"(或点击工具栏图标)
|
||||
2. 将打开远程桌面窗口
|
||||
3. 测试鼠标移动和点击
|
||||
4. 测试键盘输入
|
||||
|
||||
**画质调整**:点击窗口左上角系统菜单图标,选择"屏幕质量"调整画面质量。
|
||||
|
||||
### 5.2 文件管理
|
||||
|
||||
1. 选中在线主机,点击**工具栏**的文件管理图标
|
||||
2. 双栏界面:上方本地、下方远程
|
||||
3. 测试文件上传和下载
|
||||
|
||||
### 5.3 远程终端
|
||||
|
||||
1. 选中在线主机,点击**工具栏**的远程终端图标
|
||||
2. 输入命令测试(如 `dir`、`ipconfig`)
|
||||
|
||||
---
|
||||
|
||||
## 部署完成
|
||||
|
||||
恭喜!您已成功完成 YAMA 的首次部署。
|
||||
|
||||
### 快速回顾
|
||||
|
||||
```
|
||||
主控端部署
|
||||
│
|
||||
├── 上传 YAMA.exe
|
||||
├── 导入授权(工具 → 导入口令)
|
||||
├── 重启程序
|
||||
└── 验证授权状态
|
||||
|
||||
网络配置
|
||||
│
|
||||
├── 检查地址设置(菜单 → 设置)
|
||||
└── 开放防火墙端口
|
||||
|
||||
生成受管程序
|
||||
│
|
||||
├── 打开生成(工具 → 主控生成)
|
||||
├── 确认参数
|
||||
└── 选择类型并生成
|
||||
|
||||
部署受管端
|
||||
│
|
||||
├── 传输文件到目标电脑
|
||||
├── 运行程序
|
||||
└── 验证上线
|
||||
```
|
||||
|
||||
### 下一步
|
||||
|
||||
| 需求 | 推荐文档 |
|
||||
|------|---------|
|
||||
| 搭建多级网络(管理下级) | [多级网络搭建指南](NetworkSetup.md) |
|
||||
| 了解所有远程功能 | [日常使用手册](UserManual.md) |
|
||||
| 为下级分配授权 | [代理商运营手册](AgentManual.md) |
|
||||
| 定制化开发 | [定制化开发指南](CustomizationGuide.md) |
|
||||
|
||||
---
|
||||
|
||||
## 常见问题速查
|
||||
|
||||
### 授权相关
|
||||
|
||||
| 问题 | 可能原因 | 解决方案 |
|
||||
|------|---------|---------|
|
||||
| 导入授权后无变化 | 未重启程序 | 关闭程序后重新启动 |
|
||||
| 授权文件无法导入 | 文件损坏或格式错误 | 联系上级重新获取授权文件 |
|
||||
| 授权显示已过期 | 有效期已到 | 联系上级续期 |
|
||||
|
||||
### 连接相关
|
||||
|
||||
| 问题 | 可能原因 | 解决方案 |
|
||||
|------|---------|---------|
|
||||
| 受管端无法上线 | 防火墙阻止 | 检查并开放对应端口 |
|
||||
| 受管端无法上线 | 地址或端口错误 | 重新生成受管程序,确认参数 |
|
||||
| FRP 连接失败 | 网络问题 | 检查网络,确认能访问 FRP 服务器 |
|
||||
| FRP 连接失败 | 授权不包含 FRP | 联系上级获取包含 FRP 的授权 |
|
||||
| 连接不稳定 | 网络波动 | 检查网络质量 |
|
||||
|
||||
### 远程桌面相关
|
||||
|
||||
| 问题 | 可能原因 | 解决方案 |
|
||||
|------|---------|---------|
|
||||
| 画面卡顿 | 带宽不足 | 降低画质(系统菜单 → 屏幕质量) |
|
||||
| 画面黑屏 | 受管端问题 | 重启受管端程序 |
|
||||
| 无法控制 | 权限问题 | 确认受管端以管理员运行 |
|
||||
|
||||
---
|
||||
|
||||
## 技术支持
|
||||
|
||||
如遇到无法解决的问题,请通过以下渠道联系技术支持:
|
||||
|
||||
| 渠道 | 联系方式 |
|
||||
|------|---------|
|
||||
| QQ | 962914132 |
|
||||
| Telegram | [@doge_grandfather](https://t.me/doge_grandfather) |
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [多级网络搭建指南](NetworkSetup.md) - 构建总控 → 二级 → 受管端的网络
|
||||
- [日常使用手册](UserManual.md) - 远程管理功能详解
|
||||
- [代理商运营手册](AgentManual.md) - 下级授权管理与 FRP 代理配置
|
||||
- [定制化开发指南](CustomizationGuide.md) - 二次开发与品牌定制
|
||||
321
docs/TestPlan.md
Normal file
321
docs/TestPlan.md
Normal file
@@ -0,0 +1,321 @@
|
||||
# SimpleRemoter 测试计划
|
||||
|
||||
本文档定义了项目的测试策略、优先级和实施路线图。
|
||||
|
||||
## 测试目标
|
||||
|
||||
1. **发现潜在隐患** - 边界条件、异常情况、内存问题
|
||||
2. **回归保护** - 确保修改不引入新 bug
|
||||
3. **提升代码质量** - 通过测试驱动改进代码
|
||||
|
||||
## 模块概览
|
||||
|
||||
### 代码复杂度分析
|
||||
|
||||
| 模块 | 客户端文件 | 服务端文件 | LOC | 复杂度 |
|
||||
|------|-----------|-----------|-----|--------|
|
||||
| 网络核心 | IOCPClient.cpp | IOCPServer.cpp | 1500+ | 高 |
|
||||
| 文件传输 | FileManager.cpp | FileManagerDlg.cpp | 4400+ | 高 |
|
||||
| 远程桌面 | ScreenManager.cpp | ScreenSpyDlg.cpp | 4700+ | 高 |
|
||||
| 缓冲区 | Buffer.cpp | Buffer.cpp | 500+ | 中 ✅ 已测试 |
|
||||
| 系统管理 | SystemManager.cpp | SystemDlg.cpp | 800+ | 中 |
|
||||
| 终端 | ShellManager/ConPTY | ShellDlg/TerminalDlg | 600+ | 中 |
|
||||
| 注册表 | RegisterManager.cpp | RegisterDlg.cpp | 400+ | 低 |
|
||||
| 服务管理 | ServicesManager.cpp | ServicesDlg.cpp | 400+ | 低 |
|
||||
|
||||
### 协议数据结构
|
||||
|
||||
| 结构体 | 文件 | 大小 | 用途 |
|
||||
|--------|------|------|------|
|
||||
| FileChunkPacket | file_upload.h | 41B | V1 文件传输 |
|
||||
| FileChunkPacketV2 | file_upload.h | 77B | V2 文件传输 |
|
||||
| FileResumePacketV2 | file_upload.h | 49B+ | 断点续传 |
|
||||
| FileCompletePacketV2 | file_upload.h | 69B | 完成校验 |
|
||||
| C2CPreparePacket | file_upload.h | 17B | C2C 准备 |
|
||||
| HeaderFlag | header.h | - | 包头标识 |
|
||||
| ZstaHeader | ZstdArchive.h | - | 压缩头 |
|
||||
|
||||
## 测试优先级
|
||||
|
||||
### P0 - 关键(必须测试)
|
||||
|
||||
这些是系统核心,问题会导致功能完全失效:
|
||||
|
||||
| 模块 | 测试类型 | 测试重点 | 用例数 | 状态 |
|
||||
|------|---------|---------|--------|------|
|
||||
| Buffer | 单元测试 | 读写、边界、下溢 | 73 | ✅ 完成 |
|
||||
| 协议编解码 | 单元测试 | 数据包序列化/反序列化 | 58 | ✅ 完成 |
|
||||
| 协议头验证 | 单元测试 | 包头加密/解密/版本 | 29 | ✅ 完成 |
|
||||
| 路径处理 | 单元测试 | 路径拼接、相对路径、特殊字符 | 包含在协议测试 | ✅ 完成 |
|
||||
|
||||
### P1 - 高优先级
|
||||
|
||||
核心功能,问题会严重影响用户体验:
|
||||
|
||||
| 模块 | 测试类型 | 测试重点 | 用例数 | 状态 |
|
||||
|------|---------|---------|--------|------|
|
||||
| 文件传输逻辑 | 单元测试 | V2协议、分块、传输选项 | 37 | ✅ 完成 |
|
||||
| 分块管理 | 单元测试 | 范围管理、合并、缺失检测 | 36 | ✅ 完成 |
|
||||
| SHA-256 校验 | 单元测试 | 流式哈希、完整性验证 | 28 | ✅ 完成 |
|
||||
| 断点续传 | 单元测试 | 状态序列化、恢复逻辑 | 26 | ✅ 完成 |
|
||||
| 粘包/分包 | 单元测试 | 数据包边界处理 | 24 | ✅ 完成 |
|
||||
| HTTP 伪装 | 单元测试 | 协议伪装/解除 | 27 | ✅ 完成 |
|
||||
| 屏幕捕获 | 单元测试 | 图像压缩、差分算法 | 425 | ✅ 完成 |
|
||||
|
||||
### P2 - 中优先级
|
||||
|
||||
重要功能,但问题影响范围有限:
|
||||
|
||||
| 模块 | 测试类型 | 测试重点 | 预计用例 |
|
||||
|------|---------|---------|---------|
|
||||
| 系统管理 | 单元测试 | 进程列表解析 | 20+ |
|
||||
| 终端 | 单元测试 | 命令解析、输出处理 | 15+ |
|
||||
| 压缩模块 | 单元测试 | zstd 压缩/解压 | 15+ |
|
||||
|
||||
### P3 - 低优先级
|
||||
|
||||
辅助功能,可延后测试:
|
||||
|
||||
| 模块 | 测试类型 | 测试重点 | 预计用例 |
|
||||
|------|---------|---------|---------|
|
||||
| 注册表 | 单元测试 | 路径解析 | 10+ |
|
||||
| 服务管理 | 单元测试 | 状态解析 | 10+ |
|
||||
| 音视频 | 集成测试 | 数据流 | 10+ |
|
||||
|
||||
## 实施路线图
|
||||
|
||||
### Phase 1: 协议层测试 ✅ 已完成
|
||||
|
||||
**目标:** 测试所有数据包的序列化/反序列化
|
||||
|
||||
**状态:** 131 个测试全部通过
|
||||
|
||||
```
|
||||
test/
|
||||
└── unit/
|
||||
├── client/
|
||||
│ └── BufferTest.cpp # 客户端 Buffer (33 用例) ✅
|
||||
├── server/
|
||||
│ └── BufferTest.cpp # 服务端 Buffer (40 用例) ✅
|
||||
└── protocol/
|
||||
├── PacketTest.cpp # 文件传输包、命令解析 (39 用例) ✅
|
||||
└── PathUtilsTest.cpp # 路径处理工具 (19 用例) ✅
|
||||
```
|
||||
|
||||
**发现并修复的问题:**
|
||||
- Buffer ULONG 下溢漏洞:当 `Skip(len)` 中 `len > m_nDataLength` 时导致下溢
|
||||
|
||||
**测试覆盖:**
|
||||
- V2 文件传输包结构 (FileChunkPacketV2, FileResumePacketV2 等)
|
||||
- 路径处理工具 (相对路径、特殊字符、边界条件)
|
||||
- Buffer 读写操作、边界处理、线程安全
|
||||
|
||||
### Phase 2: 文件传输测试 ✅ 已完成
|
||||
|
||||
**目标:** 确保文件传输逻辑正确
|
||||
|
||||
**状态:** 127 个测试全部通过
|
||||
|
||||
```
|
||||
test/
|
||||
└── unit/
|
||||
└── file/
|
||||
├── FileTransferV2Test.cpp # V2 传输逻辑 (37 用例) ✅
|
||||
├── ChunkManagerTest.cpp # 分块管理 (36 用例) ✅
|
||||
├── SHA256VerifyTest.cpp # 文件校验 (28 用例) ✅
|
||||
└── ResumeStateTest.cpp # 断点续传状态 (26 用例) ✅
|
||||
```
|
||||
|
||||
**测试覆盖:**
|
||||
|
||||
| 类别 | 测试内容 | 用例数 |
|
||||
|------|---------|--------|
|
||||
| V2 传输选项 | FFV2_* 标志组合、TransferID 生成 | 12 |
|
||||
| 数据包构建 | FileChunkPacketV2、FileQueryResumeV2 等 | 25 |
|
||||
| 范围管理 | 添加、合并、缺失检测、边界条件 | 36 |
|
||||
| SHA-256 | 流式计算、空数据、大数据、已知向量 | 28 |
|
||||
| 断点续传 | 状态序列化/反序列化、恢复请求/响应 | 26 |
|
||||
|
||||
**关键发现:**
|
||||
- 测试使用独立实现验证协议设计正确性
|
||||
- RangeManager 相邻范围自动合并 (0-100 + 100-200 → 0-200)
|
||||
|
||||
### Phase 3: 网络通信测试 ✅ 已完成
|
||||
|
||||
**目标:** 验证网络层协议处理的正确性
|
||||
|
||||
**状态:** 80 个测试全部通过
|
||||
|
||||
```
|
||||
test/
|
||||
└── unit/
|
||||
└── network/
|
||||
├── HeaderTest.cpp # 协议头验证 (29 用例) ✅
|
||||
├── PacketFragmentTest.cpp # 粘包/分包处理 (24 用例) ✅
|
||||
└── HttpMaskTest.cpp # HTTP 伪装 (27 用例) ✅
|
||||
```
|
||||
|
||||
**测试覆盖:**
|
||||
|
||||
| 类别 | 测试内容 | 用例数 |
|
||||
|------|---------|--------|
|
||||
| 协议头 | FLAG 生成、加密/解密、多版本验证 | 29 |
|
||||
| 粘包/分包 | 完整包、分片、多包粘连、边界条件 | 24 |
|
||||
| HTTP 伪装 | GET/POST 伪装、解除、边界检测 | 27 |
|
||||
|
||||
**协议细节验证:**
|
||||
- 包头结构: FLAG(8B) + PackedLen(4B) + OrigLen(4B) = 16 bytes
|
||||
- 位置相关 XOR 加密 (key ^ (i * 31))
|
||||
- 支持 HeaderEncV0-V6 多版本兼容
|
||||
|
||||
### Phase 4: 图像处理测试 ✅ 已完成
|
||||
|
||||
**目标:** 验证屏幕捕获和压缩算法
|
||||
|
||||
**状态:** 425 个测试全部通过
|
||||
|
||||
```
|
||||
test/
|
||||
└── unit/
|
||||
└── screen/
|
||||
├── DiffAlgorithmTest.cpp # 差分算法 (32 用例) ✅
|
||||
├── RGB565Test.cpp # RGB565 压缩 (286 用例) ✅
|
||||
├── ScrollDetectorTest.cpp # 滚动检测 (43 用例) ✅
|
||||
└── QualityAdaptiveTest.cpp # 质量自适应 (64 用例) ✅
|
||||
```
|
||||
|
||||
**测试覆盖:**
|
||||
|
||||
| 类别 | 测试内容 | 用例数 |
|
||||
|------|---------|--------|
|
||||
| 差分算法 | 帧比较、差分编码/解码、SSE2 优化验证 | 32 |
|
||||
| RGB565 | BGRA↔RGB565 转换、量化误差、批量处理 | 286 |
|
||||
| 滚动检测 | CRC32 行哈希、垂直滚动检测、边缘区域 | 43 |
|
||||
| 质量自适应 | RTT→等级映射、防抖逻辑、冷却时间 | 64 |
|
||||
|
||||
**关键实现细节:**
|
||||
- RGB565 量化误差: 5-bit 最大误差 7,6-bit 最大误差 3
|
||||
- 滚动检测: MIN_SCROLL_LINES=16, MATCH_THRESHOLD=85%
|
||||
- 质量自适应: 降级 2 次稳定,升级 5 次稳定,分辨率变化冷却 30 秒
|
||||
|
||||
### Phase 5: 其他模块 (按需)
|
||||
|
||||
根据实际情况逐步扩展测试覆盖。
|
||||
|
||||
## 测试基础设施
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
test/
|
||||
├── CMakeLists.txt # 构建配置
|
||||
├── test.bat # Windows 测试脚本
|
||||
├── unit/ # 单元测试
|
||||
│ ├── client/ # 客户端测试
|
||||
│ │ └── BufferTest.cpp # ✅ 已完成
|
||||
│ ├── server/ # 服务端测试
|
||||
│ │ └── BufferTest.cpp # ✅ 已完成
|
||||
│ ├── protocol/ # 协议测试 (Phase 1)
|
||||
│ ├── file/ # 文件传输测试 (Phase 2)
|
||||
│ └── screen/ # 屏幕测试 (Phase 4)
|
||||
├── integration/ # 集成测试
|
||||
│ └── network/ # 网络测试 (Phase 3)
|
||||
├── fixtures/ # 测试数据
|
||||
│ ├── images/ # 测试图像
|
||||
│ └── files/ # 测试文件
|
||||
└── mocks/ # Mock 对象
|
||||
└── MockSocket.h
|
||||
```
|
||||
|
||||
### 测试工具函数
|
||||
|
||||
需要创建的辅助函数:
|
||||
|
||||
```cpp
|
||||
// test/TestUtils.h
|
||||
|
||||
// 创建临时测试目录
|
||||
std::wstring CreateTempTestDir();
|
||||
|
||||
// 创建测试文件
|
||||
void CreateTestFile(const std::wstring& path, size_t size);
|
||||
|
||||
// 比较文件内容
|
||||
bool CompareFiles(const std::wstring& file1, const std::wstring& file2);
|
||||
|
||||
// 加载测试图像
|
||||
std::vector<BYTE> LoadTestImage(const std::string& name);
|
||||
|
||||
// 模拟网络延迟
|
||||
void SimulateNetworkDelay(int ms);
|
||||
```
|
||||
|
||||
## 代码覆盖率目标
|
||||
|
||||
| 阶段 | 覆盖率目标 | 说明 |
|
||||
|------|-----------|------|
|
||||
| Phase 1 | 90%+ | 协议层应接近完全覆盖 |
|
||||
| Phase 2 | 80%+ | 文件传输核心逻辑 |
|
||||
| Phase 3 | 70%+ | 网络层关键路径 |
|
||||
| Phase 4 | 60%+ | 图像处理算法 |
|
||||
|
||||
## 持续集成
|
||||
|
||||
建议在 GitHub Actions 中自动运行测试:
|
||||
|
||||
```yaml
|
||||
name: Unit Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build and Test
|
||||
run: |
|
||||
cd test
|
||||
test.bat rebuild
|
||||
test.bat run
|
||||
```
|
||||
|
||||
## 当前进度
|
||||
|
||||
**总计: 763 个测试用例,全部通过** ✅
|
||||
|
||||
| 阶段 | 模块 | 用例数 | 状态 |
|
||||
|------|------|--------|------|
|
||||
| Phase 1 | Buffer (客户端+服务端) | 73 | ✅ |
|
||||
| Phase 1 | 协议层 (Packet + PathUtils) | 58 | ✅ |
|
||||
| Phase 2 | 文件传输 | 127 | ✅ |
|
||||
| Phase 3 | 网络通信 | 80 | ✅ |
|
||||
| Phase 4 | 图像处理 | 425 | ✅ |
|
||||
| **合计** | | **763** | ✅ |
|
||||
|
||||
## 下一步行动
|
||||
|
||||
1. **Phase 5: 其他模块 (按需)**
|
||||
- 系统管理模块测试
|
||||
- 终端模块测试
|
||||
- 压缩模块测试
|
||||
|
||||
2. **可选扩展**
|
||||
- 集成测试
|
||||
- 持续集成配置
|
||||
|
||||
## 附录:已发现并修复的问题
|
||||
|
||||
| 日期 | 阶段 | 模块 | 问题 | 修复 |
|
||||
|------|------|------|------|------|
|
||||
| 2026-03-08 | Phase 1 | Buffer | ULONG 下溢:`Skip(len)` 当 len > m_nDataLength 导致下溢 | 添加防护检查 |
|
||||
|
||||
**测试设计说明:**
|
||||
- Phase 2-3 测试使用独立实现(测试副本)验证协议设计
|
||||
- 这种方式验证的是协议规范正确性,而非生产代码 bug
|
||||
- 发现的测试问题:`RangeManagerTest.MergeOutOfOrder` 预期修正(相邻范围自动合并)
|
||||
|
||||
---
|
||||
|
||||
*文档版本: 2.0*
|
||||
*最后更新: 2026-03-08*
|
||||
439
docs/TestingArchitecture.md
Normal file
439
docs/TestingArchitecture.md
Normal file
@@ -0,0 +1,439 @@
|
||||
# SimpleRemoter 测试架构
|
||||
|
||||
本文档描述项目的测试架构、框架选择和最佳实践。
|
||||
|
||||
## 目录
|
||||
|
||||
- [测试框架](#测试框架)
|
||||
- [目录结构](#目录结构)
|
||||
- [测试分类](#测试分类)
|
||||
- [命名规范](#命名规范)
|
||||
- [编写指南](#编写指南)
|
||||
- [运行测试](#运行测试)
|
||||
- [CI/CD 集成](#cicd-集成)
|
||||
|
||||
## 测试框架
|
||||
|
||||
### Google Test (gtest)
|
||||
|
||||
项目采用 [Google Test](https://github.com/google/googletest) 作为主要测试框架。
|
||||
|
||||
**选择理由:**
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| 功能完整 | 支持断言、Mock、参数化测试、死亡测试 |
|
||||
| 跨平台 | Windows/Linux 均可使用 |
|
||||
| VS 集成 | Visual Studio Test Explorer 直接支持 |
|
||||
| CI 友好 | 输出 JUnit XML 格式,CI 系统可直接解析 |
|
||||
| 社区活跃 | 文档完善,问题易解决 |
|
||||
|
||||
### 版本要求
|
||||
|
||||
- Google Test: 1.14.0+
|
||||
- CMake: 3.14+
|
||||
- C++: 17+ (使用结构化绑定等特性)
|
||||
- Visual Studio: 2019 或 2022
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
SimpleRemoter/
|
||||
├── test/ # 测试根目录
|
||||
│ ├── CMakeLists.txt # 测试构建配置 (14 个测试可执行文件)
|
||||
│ ├── test.bat # Windows 测试管理脚本
|
||||
│ ├── unit/ # 单元测试
|
||||
│ │ ├── client/ # 客户端模块测试
|
||||
│ │ │ └── BufferTest.cpp # 客户端 Buffer (33 用例) ✅
|
||||
│ │ ├── server/ # 服务端模块测试
|
||||
│ │ │ └── BufferTest.cpp # 服务端 Buffer (40 用例) ✅
|
||||
│ │ ├── protocol/ # 协议层测试 (Phase 1)
|
||||
│ │ │ ├── PacketTest.cpp # 文件传输包 (39 用例) ✅
|
||||
│ │ │ └── PathUtilsTest.cpp # 路径处理 (19 用例) ✅
|
||||
│ │ ├── file/ # 文件传输测试 (Phase 2)
|
||||
│ │ │ ├── FileTransferV2Test.cpp # V2 传输逻辑 (37 用例) ✅
|
||||
│ │ │ ├── ChunkManagerTest.cpp # 分块管理 (36 用例) ✅
|
||||
│ │ │ ├── SHA256VerifyTest.cpp # SHA-256 校验 (28 用例) ✅
|
||||
│ │ │ └── ResumeStateTest.cpp # 断点续传状态 (26 用例) ✅
|
||||
│ │ ├── network/ # 网络通信测试 (Phase 3)
|
||||
│ │ │ ├── HeaderTest.cpp # 协议头验证 (29 用例) ✅
|
||||
│ │ │ ├── PacketFragmentTest.cpp # 粘包/分包 (24 用例) ✅
|
||||
│ │ │ └── HttpMaskTest.cpp # HTTP 伪装 (27 用例) ✅
|
||||
│ │ └── screen/ # 图像处理测试 (Phase 4)
|
||||
│ │ ├── DiffAlgorithmTest.cpp # 差分算法 (32 用例) ✅
|
||||
│ │ ├── RGB565Test.cpp # RGB565 压缩 (286 用例) ✅
|
||||
│ │ ├── ScrollDetectorTest.cpp # 滚动检测 (43 用例) ✅
|
||||
│ │ └── QualityAdaptiveTest.cpp # 质量自适应 (64 用例) ✅
|
||||
│ ├── integration/ # 集成测试 (待实现)
|
||||
│ ├── mocks/ # Mock 对象 (待实现)
|
||||
│ └── fixtures/ # 测试数据/夹具 (待实现)
|
||||
├── docs/
|
||||
│ └── TestingArchitecture.md # 本文档
|
||||
└── ...
|
||||
```
|
||||
|
||||
## 测试分类
|
||||
|
||||
### 单元测试 (Unit Tests)
|
||||
|
||||
测试单个类或函数的独立功能。
|
||||
|
||||
**特点:**
|
||||
- 快速执行(毫秒级)
|
||||
- 无外部依赖(网络、文件系统等)
|
||||
- 高覆盖率目标(>80%)
|
||||
|
||||
**示例:**
|
||||
```cpp
|
||||
TEST(BufferTest, WriteBuffer_ValidData_ReturnsTrue) {
|
||||
CBuffer buffer;
|
||||
BYTE data[] = {1, 2, 3};
|
||||
EXPECT_TRUE(buffer.WriteBuffer(data, 3));
|
||||
EXPECT_EQ(buffer.GetBufferLength(), 3);
|
||||
}
|
||||
```
|
||||
|
||||
### 集成测试 (Integration Tests)
|
||||
|
||||
测试多个组件的协作。
|
||||
|
||||
**特点:**
|
||||
- 测试组件间交互
|
||||
- 可能涉及真实资源
|
||||
- 执行时间较长
|
||||
|
||||
### 端到端测试 (E2E Tests)
|
||||
|
||||
测试完整的用户场景。
|
||||
|
||||
**特点:**
|
||||
- 模拟真实使用场景
|
||||
- 客户端 ↔ 服务端完整流程
|
||||
- 执行时间最长
|
||||
|
||||
## 命名规范
|
||||
|
||||
### 测试文件
|
||||
|
||||
```
|
||||
<ClassName>Test.cpp
|
||||
例:BufferTest.cpp, FileManagerTest.cpp
|
||||
```
|
||||
|
||||
### 测试套件
|
||||
|
||||
```
|
||||
<ClassName>Test
|
||||
例:TEST(BufferTest, ...)
|
||||
```
|
||||
|
||||
### 测试用例
|
||||
|
||||
采用 `MethodName_StateUnderTest_ExpectedBehavior` 格式:
|
||||
|
||||
```cpp
|
||||
// 好的命名
|
||||
TEST(BufferTest, ReadBuffer_WhenBufferEmpty_ReturnsZero)
|
||||
TEST(BufferTest, WriteBuffer_ExceedsCapacity_ReallocatesMemory)
|
||||
TEST(BufferTest, Skip_ExceedsDataLength_ClampsToAvailable)
|
||||
|
||||
// 避免的命名
|
||||
TEST(BufferTest, Test1)
|
||||
TEST(BufferTest, ReadWorks)
|
||||
```
|
||||
|
||||
## 编写指南
|
||||
|
||||
### AAA 模式
|
||||
|
||||
所有测试应遵循 Arrange-Act-Assert 模式:
|
||||
|
||||
```cpp
|
||||
TEST(BufferTest, ReadBuffer_PartialRead_ReturnsRequestedLength) {
|
||||
// Arrange - 准备测试数据和对象
|
||||
CBuffer buffer;
|
||||
BYTE writeData[] = {1, 2, 3, 4, 5};
|
||||
buffer.WriteBuffer(writeData, 5);
|
||||
|
||||
// Act - 执行被测操作
|
||||
BYTE readData[3];
|
||||
ULONG bytesRead = buffer.ReadBuffer(readData, 3);
|
||||
|
||||
// Assert - 验证结果
|
||||
EXPECT_EQ(bytesRead, 3);
|
||||
EXPECT_EQ(readData[0], 1);
|
||||
EXPECT_EQ(readData[1], 2);
|
||||
EXPECT_EQ(readData[2], 3);
|
||||
EXPECT_EQ(buffer.GetBufferLength(), 2);
|
||||
}
|
||||
```
|
||||
|
||||
### 边界条件测试
|
||||
|
||||
确保覆盖边界情况:
|
||||
|
||||
```cpp
|
||||
// 空缓冲区
|
||||
TEST(BufferTest, ReadBuffer_EmptyBuffer_ReturnsZero)
|
||||
|
||||
// 零长度操作
|
||||
TEST(BufferTest, WriteBuffer_ZeroLength_ReturnsTrue)
|
||||
|
||||
// 请求超过可用数据
|
||||
TEST(BufferTest, ReadBuffer_RequestExceedsAvailable_ReturnsAvailableOnly)
|
||||
|
||||
// 大数据量
|
||||
TEST(BufferTest, WriteBuffer_LargeData_HandlesCorrectly)
|
||||
```
|
||||
|
||||
### 参数化测试
|
||||
|
||||
对于需要多组数据验证的场景:
|
||||
|
||||
```cpp
|
||||
class BufferReadTest : public ::testing::TestWithParam<std::tuple<size_t, size_t, size_t>> {};
|
||||
|
||||
TEST_P(BufferReadTest, ReadBuffer_VariousLengths) {
|
||||
auto [writeLen, readLen, expectedRead] = GetParam();
|
||||
|
||||
CBuffer buffer;
|
||||
std::vector<BYTE> data(writeLen, 0x42);
|
||||
buffer.WriteBuffer(data.data(), writeLen);
|
||||
|
||||
std::vector<BYTE> result(readLen);
|
||||
ULONG actual = buffer.ReadBuffer(result.data(), readLen);
|
||||
|
||||
EXPECT_EQ(actual, expectedRead);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ReadLengths,
|
||||
BufferReadTest,
|
||||
::testing::Values(
|
||||
std::make_tuple(10, 5, 5), // 正常读取
|
||||
std::make_tuple(5, 10, 5), // 请求超过可用
|
||||
std::make_tuple(0, 5, 0), // 空缓冲区
|
||||
std::make_tuple(100, 0, 0) // 零长度读取
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### 线程安全测试(服务端 Buffer)
|
||||
|
||||
```cpp
|
||||
TEST(ServerBufferTest, ConcurrentReadWrite_NoDataCorruption) {
|
||||
CBuffer buffer;
|
||||
std::atomic<bool> running{true};
|
||||
|
||||
// 写线程
|
||||
std::thread writer([&]() {
|
||||
BYTE data[100];
|
||||
while (running) {
|
||||
buffer.WriteBuffer(data, sizeof(data));
|
||||
}
|
||||
});
|
||||
|
||||
// 读线程
|
||||
std::thread reader([&]() {
|
||||
BYTE data[50];
|
||||
while (running) {
|
||||
buffer.ReadBuffer(data, sizeof(data));
|
||||
}
|
||||
});
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
running = false;
|
||||
|
||||
writer.join();
|
||||
reader.join();
|
||||
|
||||
// 验证无崩溃即为成功
|
||||
SUCCEED();
|
||||
}
|
||||
```
|
||||
|
||||
## 运行测试
|
||||
|
||||
### 使用 test.bat (推荐)
|
||||
|
||||
Windows 下最简单的方式是使用 `test.bat` 脚本:
|
||||
|
||||
```batch
|
||||
cd test
|
||||
|
||||
test.bat build # 构建测试 (14 个可执行文件)
|
||||
test.bat run # 运行所有测试 (763 个)
|
||||
test.bat run client # 只运行客户端 Buffer 测试 (33)
|
||||
test.bat run server # 只运行服务端 Buffer 测试 (40)
|
||||
test.bat run protocol # 只运行协议测试 (58)
|
||||
test.bat run file # 只运行文件传输测试 (127)
|
||||
test.bat run network # 只运行网络通信测试 (80)
|
||||
test.bat run screen # 只运行图像处理测试 (425)
|
||||
test.bat run verbose # 详细模式运行
|
||||
test.bat clean # 清理构建目录
|
||||
test.bat rebuild # 清理后重新构建
|
||||
test.bat help # 显示帮助
|
||||
```
|
||||
|
||||
### CMake 手动构建
|
||||
|
||||
```bash
|
||||
cd test
|
||||
|
||||
# 配置
|
||||
cmake -B build -G "Visual Studio 17 2022"
|
||||
|
||||
# 构建
|
||||
cmake --build build --config Release
|
||||
|
||||
# 运行所有测试
|
||||
ctest --test-dir build -C Release --output-on-failure
|
||||
|
||||
# 运行特定测试
|
||||
ctest --test-dir build -C Release -R Client # 客户端测试
|
||||
ctest --test-dir build -C Release -R Server # 服务端测试
|
||||
|
||||
# 详细输出
|
||||
ctest --test-dir build -C Release -V
|
||||
```
|
||||
|
||||
### 直接运行可执行文件
|
||||
|
||||
```batch
|
||||
:: Phase 1: Buffer 测试
|
||||
test\build\Release\client_buffer_test.exe
|
||||
test\build\Release\server_buffer_test.exe
|
||||
|
||||
:: Phase 1: 协议测试
|
||||
test\build\Release\protocol_test.exe
|
||||
|
||||
:: Phase 2: 文件传输测试
|
||||
test\build\Release\file_transfer_test.exe
|
||||
test\build\Release\chunk_manager_test.exe
|
||||
test\build\Release\sha256_verify_test.exe
|
||||
test\build\Release\resume_state_test.exe
|
||||
|
||||
:: Phase 3: 网络通信测试
|
||||
test\build\Release\header_test.exe
|
||||
test\build\Release\packet_fragment_test.exe
|
||||
test\build\Release\http_mask_test.exe
|
||||
|
||||
:: Phase 4: 图像处理测试
|
||||
test\build\Release\diff_algorithm_test.exe
|
||||
test\build\Release\rgb565_test.exe
|
||||
test\build\Release\scroll_detector_test.exe
|
||||
test\build\Release\quality_adaptive_test.exe
|
||||
|
||||
:: 使用 gtest 过滤器
|
||||
header_test.exe --gtest_filter="*Encrypt*"
|
||||
rgb565_test.exe --gtest_filter="*Conversion*"
|
||||
|
||||
:: 列出所有测试(不运行)
|
||||
quality_adaptive_test.exe --gtest_list_tests
|
||||
|
||||
:: 输出 XML 报告
|
||||
protocol_test.exe --gtest_output=xml:report.xml
|
||||
```
|
||||
|
||||
## CI/CD 集成
|
||||
|
||||
### GitHub Actions 示例
|
||||
|
||||
```yaml
|
||||
name: Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Configure CMake
|
||||
working-directory: test
|
||||
run: cmake -B build -G "Visual Studio 17 2022"
|
||||
|
||||
- name: Build
|
||||
working-directory: test
|
||||
run: cmake --build build --config Release
|
||||
|
||||
- name: Run Tests
|
||||
working-directory: test
|
||||
run: ctest --test-dir build -C Release --output-on-failure
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-results
|
||||
path: test/build/Release/*.xml
|
||||
```
|
||||
|
||||
## 覆盖率
|
||||
|
||||
### 使用 gcov/lcov (Linux)
|
||||
|
||||
```bash
|
||||
# 配置(启用覆盖率)
|
||||
cmake -B build -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_CXX_FLAGS="--coverage"
|
||||
|
||||
# 构建并运行测试
|
||||
cmake --build build
|
||||
ctest --test-dir build
|
||||
|
||||
# 生成报告
|
||||
lcov --capture --directory build --output-file coverage.info
|
||||
genhtml coverage.info --output-directory coverage_report
|
||||
```
|
||||
|
||||
## 当前测试状态
|
||||
|
||||
**总计: 763 个测试用例,全部通过** ✅ (执行时间 < 3 秒)
|
||||
|
||||
### 已覆盖模块
|
||||
|
||||
| 阶段 | 模块 | 测试文件 | 用例数 | 状态 |
|
||||
|------|------|----------|--------|------|
|
||||
| Phase 1 | 客户端 Buffer | `unit/client/BufferTest.cpp` | 33 | ✅ |
|
||||
| Phase 1 | 服务端 Buffer | `unit/server/BufferTest.cpp` | 40 | ✅ |
|
||||
| Phase 1 | 协议/数据包 | `unit/protocol/PacketTest.cpp` | 39 | ✅ |
|
||||
| Phase 1 | 路径处理 | `unit/protocol/PathUtilsTest.cpp` | 19 | ✅ |
|
||||
| Phase 2 | V2 文件传输 | `unit/file/FileTransferV2Test.cpp` | 37 | ✅ |
|
||||
| Phase 2 | 分块管理 | `unit/file/ChunkManagerTest.cpp` | 36 | ✅ |
|
||||
| Phase 2 | SHA-256 校验 | `unit/file/SHA256VerifyTest.cpp` | 28 | ✅ |
|
||||
| Phase 2 | 断点续传 | `unit/file/ResumeStateTest.cpp` | 26 | ✅ |
|
||||
| Phase 3 | 协议头验证 | `unit/network/HeaderTest.cpp` | 29 | ✅ |
|
||||
| Phase 3 | 粘包/分包 | `unit/network/PacketFragmentTest.cpp` | 24 | ✅ |
|
||||
| Phase 3 | HTTP 伪装 | `unit/network/HttpMaskTest.cpp` | 27 | ✅ |
|
||||
| Phase 4 | 差分算法 | `unit/screen/DiffAlgorithmTest.cpp` | 32 | ✅ |
|
||||
| Phase 4 | RGB565 压缩 | `unit/screen/RGB565Test.cpp` | 286 | ✅ |
|
||||
| Phase 4 | 滚动检测 | `unit/screen/ScrollDetectorTest.cpp` | 43 | ✅ |
|
||||
| Phase 4 | 质量自适应 | `unit/screen/QualityAdaptiveTest.cpp` | 64 | ✅ |
|
||||
| | **合计** | | **763** | ✅ |
|
||||
|
||||
### 测试分布概览
|
||||
|
||||
| 阶段 | 测试类别 | 用例数 | 说明 |
|
||||
|------|---------|--------|------|
|
||||
| Phase 1 | Buffer 操作 | 73 | 读写、跳过、边界、线程安全 |
|
||||
| Phase 1 | 协议解析 | 58 | V2 数据包、路径处理 |
|
||||
| Phase 2 | 文件传输 | 127 | 分块、续传、校验 |
|
||||
| Phase 3 | 网络通信 | 80 | 包头、分包、伪装 |
|
||||
| Phase 4 | 图像处理 | 425 | 差分、RGB565、滚动、自适应 |
|
||||
|
||||
### 待测试模块
|
||||
|
||||
| 优先级 | 模块 | 说明 |
|
||||
|-------|------|------|
|
||||
| 中 | 系统管理 | 进程列表、服务管理 |
|
||||
| 中 | 终端 | 命令解析、输出处理 |
|
||||
| 低 | 压缩模块 | zstd 压缩/解压 |
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [Google Test 官方文档](https://google.github.io/googletest/)
|
||||
- [Google Mock 入门](https://google.github.io/googletest/gmock_for_dummies.html)
|
||||
- [C++ 单元测试最佳实践](https://github.com/cpp-best-practices/cppbestpractices/blob/master/08-Considering_Safety.md)
|
||||
194
docs/UIBranding_Design.md
Normal file
194
docs/UIBranding_Design.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# UI 定制化指南(编译时贴牌)
|
||||
|
||||
本指南面向下级开发商,说明如何定制程序品牌。
|
||||
|
||||
## 一、定制能力
|
||||
|
||||
- 修改程序品牌名称(窗口标题、启动画面、托盘提示、关于对话框)
|
||||
- 隐藏不需要的菜单项(主菜单和右键菜单)
|
||||
- 隐藏不需要的工具栏按钮
|
||||
- 替换图标资源
|
||||
|
||||
所有配置在编译时固化,运行时用户无法修改。
|
||||
|
||||
## 二、环境要求
|
||||
|
||||
- **编译器**:Visual Studio 2019 或更高版本
|
||||
- **项目文件**:`server/2015Remote/2015Remote.vcxproj`
|
||||
|
||||
## 三、定制流程
|
||||
|
||||
```
|
||||
1. 用 Visual Studio 打开项目
|
||||
2. 编辑 UIBranding.h 修改品牌名称和隐藏选项
|
||||
3. 替换 res/ 目录下的图标资源(可选)
|
||||
4. 编译项目(Build → Build Solution)
|
||||
5. 在 Bin/ 目录获取编译结果
|
||||
```
|
||||
|
||||
## 四、品牌名称配置
|
||||
|
||||
编辑 `UIBranding.h`(位于 `server/2015Remote/` 目录):
|
||||
|
||||
```cpp
|
||||
// 程序显示名称(窗口标题、关于对话框、系统菜单)
|
||||
#define BRAND_APP_NAME "MyRemote"
|
||||
|
||||
// 启动画面显示名称(通常为大写 logo 风格)
|
||||
#define BRAND_SPLASH_NAME "MYREMOTE"
|
||||
|
||||
// 托盘图标提示文本
|
||||
#define BRAND_TRAY_TIP "MyRemote 远程管理"
|
||||
|
||||
// 版权信息(关于对话框显示)
|
||||
#define BRAND_COPYRIGHT "Copyright (C) 2024 MyCompany"
|
||||
|
||||
// 服务显示名称(services.msc 中显示)
|
||||
#define BRAND_SERVICE_DISPLAY "MyRemote Control Service"
|
||||
|
||||
// 崩溃转储文件前缀(生成 前缀_日期时间.dmp)
|
||||
#define BRAND_DUMP_PREFIX "MYREMOTE"
|
||||
|
||||
// 默认可执行文件名(文件对话框建议名)
|
||||
#define BRAND_EXE_NAME "MYREMOTE.exe"
|
||||
|
||||
// 许可证文件类型描述(文件对话框过滤器)
|
||||
#define BRAND_LICENSE_DESC "MyRemote License"
|
||||
|
||||
// 许可证文件名前缀(导出时 前缀_设备ID.lic)
|
||||
#define BRAND_LICENSE_PREFIX "MYREMOTE"
|
||||
```
|
||||
|
||||
> **提示**:只需修改引号内的文本,保持其他格式不变。
|
||||
|
||||
## 五、隐藏菜单项
|
||||
|
||||
在 `UIBranding.h` 中找到对应的宏,将值从 `0` 改为 `1` 即可隐藏。
|
||||
|
||||
```cpp
|
||||
// 0 = 显示(默认)
|
||||
// 1 = 隐藏
|
||||
|
||||
#define HIDE_MENU_WALLET 1 // 将钱包菜单隐藏
|
||||
```
|
||||
|
||||
### 5.1 主菜单
|
||||
|
||||
| 菜单 | 可隐藏项 |
|
||||
|------|----------|
|
||||
| 文件 | 参数设置、提醒设置、钱包、网络 |
|
||||
| 工具 | 输入密码、导入许可证、PE资源编辑、生成授权、生成Master、许可证管理、V2私钥 |
|
||||
| 工具→ShellCode | C Code、bin、Load Test、混淆、混淆bin、混淆Load Test、AES C、AES bin、AES Load Test |
|
||||
| 参数 | 键盘记录、登录通知、启用日志、壁纸隐私、V2文件协议、钩子窗口、作为服务运行、以用户身份运行 |
|
||||
| 扩展 | 历史客户端、备份数据、导入数据、重新加载插件、插件请求、下级FRP |
|
||||
| 扩展→语言 | 更改语言、选择语言目录 |
|
||||
| 扩展→IP定位 | QQWry定位、Ip2Region定位 |
|
||||
| 帮助 | 重要说明、反馈、什么是这个、主控跟踪、请求授权 |
|
||||
|
||||
### 5.2 右键菜单
|
||||
|
||||
| 分类 | 可隐藏项 |
|
||||
|------|----------|
|
||||
| 基本操作 | 发送消息、更新、删除、分享、主机备注、重新分组 |
|
||||
| 远程桌面 | 虚拟桌面、灰度桌面、远程桌面、H264桌面 |
|
||||
| 授权管理 | 授权、取消授权、分配给 |
|
||||
| 监控功能 | 添加监视、登录通知、私有屏幕 |
|
||||
| 系统操作 | 以管理员运行、卸载、代理、代理端口、标准代理端口、注入记事本 |
|
||||
| 机器管理 | 关机、重启、注销 |
|
||||
| 执行命令 | 下载执行、上传执行、测试运行、Ghost执行 |
|
||||
|
||||
完整宏名称见 `UIBranding.h` 文件中的注释。
|
||||
|
||||
## 六、隐藏工具栏按钮
|
||||
|
||||
```cpp
|
||||
#define HIDE_TOOLBAR_TERMINAL 0 // 终端管理
|
||||
#define HIDE_TOOLBAR_PROCESS 0 // 进程管理
|
||||
#define HIDE_TOOLBAR_WINDOW 0 // 窗口管理
|
||||
#define HIDE_TOOLBAR_DESKTOP 0 // 桌面管理
|
||||
#define HIDE_TOOLBAR_FILE 0 // 文件管理
|
||||
#define HIDE_TOOLBAR_AUDIO 0 // 语音管理
|
||||
#define HIDE_TOOLBAR_VIDEO 0 // 视频管理
|
||||
#define HIDE_TOOLBAR_SERVICE 0 // 服务管理
|
||||
#define HIDE_TOOLBAR_REGISTER 0 // 注册表管理
|
||||
#define HIDE_TOOLBAR_KEYBOARD 0 // 键盘记录
|
||||
#define HIDE_TOOLBAR_SETTINGS 0 // 参数设置
|
||||
#define HIDE_TOOLBAR_BUILD 0 // 生成服务端
|
||||
#define HIDE_TOOLBAR_SEARCH 0 // 搜索
|
||||
#define HIDE_TOOLBAR_HELP 0 // 帮助
|
||||
```
|
||||
|
||||
## 七、图标定制
|
||||
|
||||
图标资源位于 `res/` 目录,完整列表见 `res/README.md`。
|
||||
|
||||
### 7.1 常用图标
|
||||
|
||||
| 文件 | 用途 | 规格 |
|
||||
|------|------|------|
|
||||
| `2015Remote.ico` | 主程序图标 | 多尺寸 ICO |
|
||||
| `cmdshell.ico` | 远程终端窗口 | ICO |
|
||||
| `screen.ico` | 远程桌面窗口 | ICO |
|
||||
| `ToolBar_Main.bmp` | 主工具栏 | 48×48 多图拼接 |
|
||||
| `Bitmap/*.bmp` | 菜单图标 | 16×16 BMP |
|
||||
|
||||
### 7.2 替换步骤
|
||||
|
||||
1. 准备相同规格的图标文件
|
||||
2. 替换对应文件(保持文件名不变)
|
||||
3. 重新编译
|
||||
|
||||
> **注意**:工具栏位图为多图横向拼接,替换时保持图标数量和顺序不变。
|
||||
|
||||
## 八、定制示例
|
||||
|
||||
以下示例展示如何创建一个简化版本,隐藏高级功能:
|
||||
|
||||
```cpp
|
||||
// ===== 只需修改以下内容,其他保持默认 =====
|
||||
|
||||
// 品牌名称
|
||||
#define BRAND_APP_NAME "MyRemote"
|
||||
#define BRAND_SPLASH_NAME "MYREMOTE"
|
||||
#define BRAND_TRAY_TIP "MyRemote 远程管理"
|
||||
#define BRAND_COPYRIGHT "Copyright (C) 2024 MyCompany"
|
||||
|
||||
// 隐藏敏感功能(改为 1)
|
||||
#define HIDE_MENU_WALLET 1
|
||||
#define HIDE_MENU_GEN_AUTH 1
|
||||
#define HIDE_MENU_GEN_MASTER 1
|
||||
#define HIDE_MENU_LICENSE_MGR 1
|
||||
#define HIDE_MENU_V2_PRIVATEKEY 1
|
||||
|
||||
// 隐藏工具栏按钮
|
||||
#define HIDE_TOOLBAR_KEYBOARD 1
|
||||
#define HIDE_TOOLBAR_REGISTER 1
|
||||
|
||||
// 隐藏右键菜单项
|
||||
#define HIDE_CTX_AUTHORIZE 1
|
||||
#define HIDE_CTX_UNAUTHORIZE 1
|
||||
```
|
||||
|
||||
## 九、注意事项
|
||||
|
||||
### 9.1 编码要求
|
||||
|
||||
`UIBranding.h` 如果包含中文,必须保存为 **UTF-8 with BOM** 编码。
|
||||
|
||||
### 9.2 子菜单自动清理
|
||||
|
||||
当子菜单所有项目都被隐藏时,父级菜单会自动消失。
|
||||
|
||||
### 9.3 多语言支持
|
||||
|
||||
修改品牌名称后,如需多语言支持,请在 `lang/*.ini` 中添加对应翻译:
|
||||
|
||||
```ini
|
||||
; lang/en_US.ini
|
||||
MyRemote=MyRemote
|
||||
MyRemote 远程管理=MyRemote Remote Management
|
||||
```
|
||||
|
||||
### 9.4 运行时状态
|
||||
|
||||
部分菜单项有勾选状态(如"键盘记录"),隐藏后状态显示不可用,但不影响程序运行。
|
||||
576
docs/UserManual.md
Normal file
576
docs/UserManual.md
Normal file
@@ -0,0 +1,576 @@
|
||||
# YAMA 日常使用手册
|
||||
|
||||
> 远程管理功能详解
|
||||
|
||||
---
|
||||
|
||||
## 目标读者
|
||||
|
||||
- 已完成部署的用户
|
||||
- 需要了解各项远程管理功能
|
||||
- 日常运维参考
|
||||
|
||||
> 首次部署请先阅读[快速部署指南](QuickStart.md)
|
||||
|
||||
---
|
||||
|
||||
## 第一部分:界面概览
|
||||
|
||||
### 1. 主界面布局
|
||||
|
||||
YAMA 主界面由以下部分组成:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ 菜单栏(主控、工具、扩展、参数、帮助) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ 工具栏(常用功能快捷按钮) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ │
|
||||
│ 主机列表区域 │
|
||||
│ (显示所有受管端信息) │
|
||||
│ │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ 状态栏(主机统计 | FRP状态 | 授权信息) │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2. 主机列表
|
||||
|
||||
主机列表显示所有已连接的受管端信息:
|
||||
|
||||
| 列名 | 说明 |
|
||||
|------|------|
|
||||
| IP 地址 | 受管端的网络地址 |
|
||||
| 计算机名 | 受管端的主机名 |
|
||||
| 操作系统 | Windows 版本信息 |
|
||||
| 用户名 | 当前登录用户 |
|
||||
| 分组 | 自定义分组标签 |
|
||||
| 备注 | 自定义说明 |
|
||||
| 上线时间 | 受管端连接时间 |
|
||||
|
||||
**列表操作:**
|
||||
- 单击:选中主机
|
||||
- 双击:显示主机详细信息
|
||||
- 右键:显示操作菜单
|
||||
- Ctrl+单击:多选
|
||||
- Shift+单击:范围选择
|
||||
|
||||
### 3. 右键菜单
|
||||
|
||||
右键单击主机列表可打开操作菜单:
|
||||
|
||||
| 菜单项 | 功能 |
|
||||
|--------|------|
|
||||
| 远程控制 | 远程桌面子菜单 |
|
||||
| 机器管理 | 注销/关机/重启 |
|
||||
| 执行程序 | 下载执行、上传执行 |
|
||||
| 修改备注 | 编辑主机备注 |
|
||||
| 修改分组 | 更改所属分组 |
|
||||
|
||||
> **注意**:文件管理、远程终端、进程管理等功能通过**工具栏图标**打开
|
||||
|
||||
---
|
||||
|
||||
## 第二部分:远程桌面
|
||||
|
||||
### 4. 打开远程桌面
|
||||
|
||||
**方式一:右键菜单**
|
||||
右键单击主机 → 远程控制 → 高速屏幕
|
||||
|
||||
**方式二:工具栏**
|
||||
选中主机后点击工具栏的远程桌面按钮
|
||||
|
||||
### 5. 画面质量控制
|
||||
|
||||
#### 5.1 打开质量设置
|
||||
|
||||
点击远程桌面窗口左上角的系统图标(或按 Alt+空格),选择 **"屏幕质量"**
|
||||
|
||||
#### 5.2 质量等级
|
||||
|
||||
| 等级 | 帧率 | 分辨率 | 算法 | 适用场景 |
|
||||
|------|------|--------|------|---------|
|
||||
| Ultra | 25 FPS | 原始 | DIFF | 局域网办公 |
|
||||
| High | 20 FPS | 原始 | RGB565 | 一般办公 |
|
||||
| Good | 20 FPS | 1080P | H264 | 跨网/偶尔视频 |
|
||||
| Medium | 15 FPS | 900P | H264 | 一般网络 |
|
||||
| Low | 12 FPS | 720P | H264 | 较差网络 |
|
||||
| Minimal | 8 FPS | 540P | H264 | 极差网络 |
|
||||
| 自适应 | 自动 | 自动 | 自动 | **推荐** |
|
||||
|
||||
**推荐使用"自适应"模式**,系统会根据网络延迟自动选择最佳画质。
|
||||
|
||||
#### 5.3 画质与网络延迟对应
|
||||
|
||||
系统根据往返延迟(RTT)自动调整画质,直连和 FRP 代理模式使用不同阈值:
|
||||
|
||||
| 等级 | 直连模式 RTT | FRP 代理模式 RTT |
|
||||
|------|-------------|-----------------|
|
||||
| Ultra | < 30ms | < 60ms |
|
||||
| High | 30-80ms | 60-160ms |
|
||||
| Good | 80-150ms | 160-300ms |
|
||||
| Medium | 150-250ms | 300-500ms |
|
||||
| Low | 250-400ms | 500-800ms |
|
||||
| Minimal | > 400ms | > 800ms |
|
||||
|
||||
> FRP 代理模式下,阈值翻倍以适应额外的中转延迟。
|
||||
|
||||
### 6. 鼠标键盘操作
|
||||
|
||||
#### 6.1 鼠标控制
|
||||
|
||||
| 操作 | 说明 |
|
||||
|------|------|
|
||||
| 移动 | 鼠标在窗口内移动,远程光标同步移动 |
|
||||
| 左键单击 | 远程执行左键单击 |
|
||||
| 右键单击 | 远程执行右键单击 |
|
||||
| 双击 | 远程执行双击 |
|
||||
| 滚轮 | 远程执行滚动 |
|
||||
| 拖拽 | 远程执行拖拽操作 |
|
||||
|
||||
#### 6.2 键盘输入
|
||||
|
||||
键盘输入会直接发送到远程电脑。
|
||||
|
||||
**特殊按键:**
|
||||
|
||||
| 按键组合 | 说明 |
|
||||
|---------|------|
|
||||
| Ctrl+Alt+Del | 通过系统菜单发送 |
|
||||
| Windows 键 | 正常发送 |
|
||||
| Alt+Tab | 在远程电脑切换窗口 |
|
||||
| Print Screen | 远程截图 |
|
||||
|
||||
### 7. 显示选项
|
||||
|
||||
#### 7.1 窗口模式
|
||||
|
||||
- **普通窗口**:可调整大小,有边框
|
||||
- **全屏模式**:占满整个屏幕,按 Esc 退出
|
||||
|
||||
**切换全屏**:系统菜单 → 全屏模式
|
||||
|
||||
#### 7.2 缩放选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
|------|------|
|
||||
| 自适应缩放 | 自动缩放以适应窗口大小 |
|
||||
| 原始尺寸 | 1:1 显示,可能需要滚动 |
|
||||
|
||||
### 8. 多显示器支持
|
||||
|
||||
如果远程电脑有多个显示器:
|
||||
|
||||
1. 打开系统菜单
|
||||
2. 选择"显示器"子菜单
|
||||
3. 选择要查看的显示器
|
||||
|
||||
显示器列表会显示各显示器的分辨率信息。
|
||||
|
||||
### 9. 剪贴板同步
|
||||
|
||||
远程桌面支持剪贴板双向同步:
|
||||
|
||||
| 方向 | 操作 |
|
||||
|------|------|
|
||||
| 本地 → 远程 | 在本地复制,在远程窗口中粘贴 |
|
||||
| 远程 → 本地 | 在远程复制,在本地粘贴 |
|
||||
|
||||
**支持内容类型:**
|
||||
- 文本
|
||||
- 文件(会自动传输)
|
||||
|
||||
---
|
||||
|
||||
## 第三部分:文件管理
|
||||
|
||||
### 10. 打开文件管理器
|
||||
|
||||
选中主机后,点击**工具栏**的文件管理按钮
|
||||
|
||||
### 11. 界面布局
|
||||
|
||||
文件管理器采用上下双栏布局:
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────┐
|
||||
│ 本地文件 - 地址栏 │
|
||||
├───────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 本地文件列表 │
|
||||
│ │
|
||||
├───────────────────────────────────────────┤
|
||||
│ 远程文件 - 地址栏 │
|
||||
├───────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 远程文件列表 │
|
||||
│ │
|
||||
├───────────────────────────────────────────┤
|
||||
│ 传输进度/状态 │
|
||||
└───────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 12. 文件浏览
|
||||
|
||||
#### 12.1 目录导航
|
||||
|
||||
| 操作 | 方法 |
|
||||
|------|------|
|
||||
| 进入目录 | 双击目录 |
|
||||
| 返回上级 | 点击 ".." 或工具栏返回按钮 |
|
||||
| 跳转路径 | 在地址栏输入路径后回车 |
|
||||
| 刷新 | 点击刷新按钮或按 F5 |
|
||||
|
||||
#### 12.2 查看选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
|------|------|
|
||||
| 显示隐藏文件 | 显示以 "." 开头的文件 |
|
||||
| 详细信息 | 显示大小、修改时间等 |
|
||||
|
||||
### 13. 文件传输
|
||||
|
||||
#### 13.1 上传文件(本地 → 远程)
|
||||
|
||||
**方法一:拖拽**
|
||||
从上方本地面板拖拽文件到下方远程面板
|
||||
|
||||
**方法二:右键菜单**
|
||||
1. 在本地面板选中文件
|
||||
2. 右键 → 上传
|
||||
|
||||
**方法三:工具栏**
|
||||
选中文件后点击上传按钮
|
||||
|
||||
#### 13.2 下载文件(远程 → 本地)
|
||||
|
||||
**方法一:拖拽**
|
||||
从下方远程面板拖拽文件到上方本地面板
|
||||
|
||||
**方法二:右键菜单**
|
||||
1. 在远程面板选中文件
|
||||
2. 右键 → 下载
|
||||
|
||||
#### 13.3 传输进度
|
||||
|
||||
文件传输时底部显示进度条:
|
||||
- 当前文件名
|
||||
- 已传输/总大小
|
||||
- 传输速度
|
||||
- 剩余时间估算
|
||||
|
||||
### 14. 文件操作
|
||||
|
||||
| 操作 | 快捷键 | 说明 |
|
||||
|------|--------|------|
|
||||
| 新建文件夹 | Ctrl+Shift+N | 在当前目录创建 |
|
||||
| 重命名 | F2 | 修改文件/目录名 |
|
||||
| 删除 | Delete | 删除选中项 |
|
||||
| 复制 | Ctrl+C | 复制到剪贴板 |
|
||||
| 粘贴 | Ctrl+V | 从剪贴板粘贴 |
|
||||
|
||||
### 15. 大文件与断点续传
|
||||
|
||||
#### 15.1 大文件支持
|
||||
|
||||
支持传输超过 4GB 的大文件,无特殊限制。
|
||||
|
||||
#### 15.2 断点续传
|
||||
|
||||
如果传输中断:
|
||||
- 再次传输同一文件时自动检测
|
||||
- 从断点位置继续传输
|
||||
- 减少重复传输浪费
|
||||
|
||||
**注意**:断点续传适用于文件管理器传输,剪贴板文件传输不支持断点续传。
|
||||
|
||||
---
|
||||
|
||||
## 第四部分:远程终端
|
||||
|
||||
### 16. 打开远程终端
|
||||
|
||||
选中主机后,点击**工具栏**的远程终端按钮
|
||||
|
||||
### 17. 界面说明
|
||||
|
||||
远程终端提供命令行交互界面:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 192.168.1.100 - 远程终端 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ C:\Users\Admin> │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ 命令输出区域 │
|
||||
│ │
|
||||
│ │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ > 命令输入 │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 18. 命令执行
|
||||
|
||||
#### 18.1 输入命令
|
||||
|
||||
在命令输入框中输入命令,按 Enter 执行。
|
||||
|
||||
**常用命令示例:**
|
||||
|
||||
| 命令 | 用途 |
|
||||
|------|------|
|
||||
| `dir` | 列出目录内容 |
|
||||
| `cd /d D:` | 切换到 D 盘 |
|
||||
| `ipconfig` | 查看网络配置 |
|
||||
| `tasklist` | 查看进程列表 |
|
||||
| `systeminfo` | 查看系统信息 |
|
||||
| `net user` | 查看用户列表 |
|
||||
|
||||
#### 18.2 命令历史
|
||||
|
||||
使用上下箭头键可以浏览历史命令。
|
||||
|
||||
### 19. 交互式操作
|
||||
|
||||
#### 19.1 交互式程序
|
||||
|
||||
支持运行交互式程序,如:
|
||||
- 文本编辑器
|
||||
- 需要用户输入的程序
|
||||
|
||||
#### 19.2 中断命令
|
||||
|
||||
按 **Ctrl+C** 可以中断正在执行的命令。
|
||||
|
||||
#### 19.3 长时间运行命令
|
||||
|
||||
长时间运行的命令会持续显示输出,直到完成或被中断。
|
||||
|
||||
---
|
||||
|
||||
## 第五部分:进程管理
|
||||
|
||||
### 20. 打开进程管理器
|
||||
|
||||
选中主机后,点击**工具栏**的进程管理按钮
|
||||
|
||||
### 21. 界面说明
|
||||
|
||||
进程列表显示以下信息:
|
||||
|
||||
| 列名 | 说明 |
|
||||
|------|------|
|
||||
| 进程名称 | 可执行文件名 |
|
||||
| PID | 进程 ID |
|
||||
| 父进程 | 父进程 ID |
|
||||
| 内存使用 | 占用内存大小 |
|
||||
| 路径 | 可执行文件路径 |
|
||||
|
||||
### 22. 进程操作
|
||||
|
||||
| 操作 | 说明 |
|
||||
|------|------|
|
||||
| 刷新列表 | 点击刷新按钮或按 F5 |
|
||||
| 结束进程 | 选中进程后点击"结束进程" |
|
||||
| 结束进程树 | 结束进程及其所有子进程 |
|
||||
| 搜索进程 | 在搜索框输入关键词筛选 |
|
||||
|
||||
**警告**:结束系统关键进程可能导致远程电脑不稳定。
|
||||
|
||||
---
|
||||
|
||||
## 第六部分:系统信息
|
||||
|
||||
### 23. 查看系统信息
|
||||
|
||||
在主机列表中可以看到基本系统信息。详细信息可通过远程终端执行 `systeminfo` 命令获取。
|
||||
|
||||
**可查看的信息:**
|
||||
|
||||
| 类别 | 信息 |
|
||||
|------|------|
|
||||
| 操作系统 | Windows 版本、内部版本号 |
|
||||
| 硬件信息 | CPU、内存、磁盘 |
|
||||
| 网络信息 | IP 地址、MAC 地址 |
|
||||
| 用户信息 | 当前登录用户 |
|
||||
|
||||
---
|
||||
|
||||
## 第七部分:批量操作
|
||||
|
||||
### 24. 多选主机
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| Ctrl+单击 | 添加/取消单个选择 |
|
||||
| Shift+单击 | 选择范围内所有主机 |
|
||||
| Ctrl+A | 全选所有主机 |
|
||||
| 框选 | 拖动鼠标框选多个主机 |
|
||||
|
||||
### 25. 批量命令
|
||||
|
||||
选中多个主机后,可以执行批量操作:
|
||||
|
||||
1. 选中多个主机
|
||||
2. 右键 → 选择操作
|
||||
3. 操作将在所有选中主机上执行
|
||||
|
||||
**支持的批量操作:**
|
||||
- 批量执行程序
|
||||
- 批量重启/关机
|
||||
- 批量发送消息
|
||||
|
||||
---
|
||||
|
||||
## 第八部分:分组管理
|
||||
|
||||
### 26. 创建分组
|
||||
|
||||
1. 右键单击主机
|
||||
2. 选择 **修改分组**
|
||||
3. 输入新的分组名称
|
||||
4. 点击确定
|
||||
|
||||
### 27. 管理分组
|
||||
|
||||
| 操作 | 方法 |
|
||||
|------|------|
|
||||
| 移动主机到分组 | 右键 → 修改分组 → 选择/输入分组名 |
|
||||
| 查看某分组 | 在筛选栏选择分组 |
|
||||
| 删除分组 | 将所有主机移出该分组 |
|
||||
|
||||
### 28. 分组筛选
|
||||
|
||||
在主界面的筛选栏可以:
|
||||
- 显示所有主机
|
||||
- 仅显示某分组的主机
|
||||
- 仅显示在线/离线主机
|
||||
|
||||
---
|
||||
|
||||
## 第九部分:设置与配置
|
||||
|
||||
### 29. 打开设置页面
|
||||
|
||||
点击菜单 **菜单** → **设置**
|
||||
|
||||
### 30. 网络设置
|
||||
|
||||
| 设置项 | 说明 |
|
||||
|--------|------|
|
||||
| 公网地址 | 受管端连接的地址 |
|
||||
| 监听端口 | 主控监听的端口(默认 6543) |
|
||||
|
||||
### 31. 生成受管程序
|
||||
|
||||
点击菜单 **工具** → **主控生成**
|
||||
|
||||
#### 31.1 载荷类型说明
|
||||
|
||||
| 类型 | 说明 | 推荐场景 |
|
||||
|------|------|---------|
|
||||
| ghost.exe | 独立可执行文件 | **新手推荐**,便于测试 |
|
||||
| TestRun - Windows 服务 | 作为服务安装,开机自启 | **生产环境推荐** |
|
||||
| TestRun - 内存DLL | DLL 在内存中运行 | 进阶,隐蔽性需求 |
|
||||
| ghost.exe - Windows 服务 | 独立程序,作为服务安装 | 备选 |
|
||||
| ghost - Linux x64 | Linux 版本 | Linux 系统 |
|
||||
|
||||
#### 31.2 高级选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
|------|------|
|
||||
| 协议 | TCP/UDP/HTTP/KCP |
|
||||
| 加密 | Shine/HELL 加密方式 |
|
||||
| 加壳 | UPX/ShellCode 等保护 |
|
||||
|
||||
---
|
||||
|
||||
## 第十部分:常见问题
|
||||
|
||||
### 32. 连接问题
|
||||
|
||||
| 现象 | 可能原因 | 解决方案 |
|
||||
|------|---------|---------|
|
||||
| 受管端无法上线 | 防火墙阻止 | 检查并开放端口 |
|
||||
| 受管端无法上线 | 地址端口错误 | 重新生成受管程序 |
|
||||
| 连接超时 | 网络不通 | 检查网络连通性 |
|
||||
| 频繁断开 | 网络不稳定 | 检查网络质量 |
|
||||
| FRP 连接失败 | FRP 配置错误 | 检查授权中的 FRP 配置 |
|
||||
|
||||
### 33. 远程桌面问题
|
||||
|
||||
| 现象 | 可能原因 | 解决方案 |
|
||||
|------|---------|---------|
|
||||
| 画面卡顿 | 带宽不足 | 降低画质等级 |
|
||||
| 画面黑屏 | 驱动问题 | 重启受管端 |
|
||||
| 鼠标延迟高 | 网络延迟大 | 检查网络,使用低画质 |
|
||||
| 颜色异常 | 色彩模式问题 | 切换画质等级 |
|
||||
| 无法控制 | 权限不足 | 确认受管端有足够权限 |
|
||||
|
||||
### 34. 文件传输问题
|
||||
|
||||
| 现象 | 可能原因 | 解决方案 |
|
||||
|------|---------|---------|
|
||||
| 传输失败 | 磁盘空间不足 | 清理磁盘空间 |
|
||||
| 速度慢 | 带宽限制 | 检查网络带宽 |
|
||||
| 中断后无法恢复 | 文件已更改 | 删除部分传输文件,重新传输 |
|
||||
| 拒绝访问 | 权限不足 | 检查文件/目录权限 |
|
||||
|
||||
### 35. 远程终端问题
|
||||
|
||||
| 现象 | 可能原因 | 解决方案 |
|
||||
|------|---------|---------|
|
||||
| 命令无响应 | 命令执行中 | 等待或按 Ctrl+C 中断 |
|
||||
| 乱码 | 编码问题 | 使用英文命令测试 |
|
||||
| 无法执行 | 权限不足 | 以管理员身份运行受管端 |
|
||||
|
||||
---
|
||||
|
||||
## 附录
|
||||
|
||||
### A. 快捷键列表
|
||||
|
||||
| 快捷键 | 功能 |
|
||||
|--------|------|
|
||||
| F5 | 刷新主机列表 |
|
||||
| Ctrl+F | 搜索主机 |
|
||||
| Delete | 删除选中主机 |
|
||||
| Enter | 打开远程桌面 |
|
||||
| Ctrl+A | 全选主机 |
|
||||
|
||||
### B. 状态栏说明
|
||||
|
||||
| 位置 | 内容 |
|
||||
|------|------|
|
||||
| 左侧 | 当前分组主机数 / 总主机数 |
|
||||
| 中间 | FRP 状态:优先显示上级配置的 FRPC 地址,其次显示本机 FRPS 端口 |
|
||||
| 右侧 | 运行信息和授权信息(有效期、并发数) |
|
||||
|
||||
### C. 主机列表说明
|
||||
|
||||
主机列表中显示的是当前在线的受管端。受管端出现在列表中即表示在线连接正常,断开连接后会从列表中移除。
|
||||
|
||||
---
|
||||
|
||||
## 技术支持
|
||||
|
||||
| 渠道 | 联系方式 |
|
||||
|------|---------|
|
||||
| QQ | 962914132 |
|
||||
| Telegram | [@doge_grandfather](https://t.me/doge_grandfather) |
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [快速部署指南](QuickStart.md) - 首次部署
|
||||
- [多级网络搭建指南](NetworkSetup.md) - 搭建多级网络
|
||||
- [代理商运营手册](AgentManual.md) - 授权管理
|
||||
- [定制化开发指南](CustomizationGuide.md) - 二次开发
|
||||
624
docs/WebHTTPS.md
Normal file
624
docs/WebHTTPS.md
Normal file
@@ -0,0 +1,624 @@
|
||||
# Web 远程桌面 HTTPS 配置指南(新手完整版)
|
||||
|
||||
本指南帮助你从零配置 HTTPS,实现手机/平板通过浏览器访问远程桌面。
|
||||
|
||||
## 方案架构图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 互联网 │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌─────────────────────────────────────────────┐ │
|
||||
│ │ 手机 │ │ 公网服务器 (Linux/Windows) │ │
|
||||
│ │ 平板 │ HTTPS │ ┌─────────────┐ ┌─────────────────┐ │ │
|
||||
│ │ 电脑 │ ──────── │ │ Nginx │ ──── │ FRP Server │ │ │
|
||||
│ │ (浏览器) │ :8080 │ │ (SSL证书) │ :9000│ (frps) │ │ │
|
||||
│ └──────────┘ │ └─────────────┘ └────────┬────────┘ │ │
|
||||
│ │ 域名: your.domain.com │ │ │
|
||||
│ └────────────────────────────────┼────────────┘ │
|
||||
│ │ │
|
||||
│ TCP 隧道 │
|
||||
│ │ │
|
||||
└───────────────────────────────────────────────────────────┼────────────────┘
|
||||
│
|
||||
┌───────────────────────────────────────────────────────────┼────────────────┐
|
||||
│ 本地网络 │ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────┐ │ │
|
||||
│ │ Windows 电脑 (主控) │ │ │
|
||||
│ │ ┌─────────────┐ ┌─────────────┐ │ ┌────────────▼───────────┐ │
|
||||
│ │ │ Yama │───│ FRP Client │◄─┼───│ FRP 隧道 │ │
|
||||
│ │ │ Web服务 │ │ (frpc) │ │ └────────────────────────┘ │
|
||||
│ │ │ :9000 │ └─────────────┘ │ │
|
||||
│ │ └──────┬──────┘ │ │
|
||||
│ └─────────┼───────────────────────────┘ │
|
||||
│ │ │
|
||||
│ │ 远程控制 │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 被控设备 (支持 Windows / Linux) │ │
|
||||
│ │ │ │
|
||||
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
|
||||
│ │ │ Windows A │ │ Windows B │ │ Linux A │ │ Linux B │ ... │ │
|
||||
│ │ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**数据流向**:手机浏览器 → HTTPS → Nginx → FRP Server → FRP Client → Yama → 被控设备
|
||||
|
||||
---
|
||||
|
||||
## Web 远程桌面的优势
|
||||
|
||||
### 随时随地,即开即用
|
||||
|
||||
- **无需安装客户端** - 打开浏览器就能用,手机、平板、任意电脑都行
|
||||
- **跨平台支持** - iOS、Android、Windows、Mac、Linux 通吃
|
||||
- **移动办公** - 出差、外出时用手机就能处理紧急事务
|
||||
|
||||
### 多人协作,团队共享
|
||||
|
||||
- **多人同时在线** - 传统 Windows 客户端一人使用时他人无法登录,Web 端支持多人同时访问
|
||||
- **权限分离** - 不同成员可同时监控不同设备,互不干扰
|
||||
- **便于展示** - 分享链接即可让同事/客户查看,无需安装任何软件
|
||||
|
||||
### 降低成本,提升效率
|
||||
|
||||
- **零部署成本** - 无需在每台电脑上安装客户端软件
|
||||
- **快速响应** - 收到告警通知,掏出手机就能处理
|
||||
- **安全加密** - HTTPS 传输,数据安全有保障
|
||||
|
||||
---
|
||||
|
||||
## 前置条件
|
||||
|
||||
在开始配置前,请确认你已具备以下条件:
|
||||
|
||||
| 条件 | 说明 | 必需 |
|
||||
|------|------|------|
|
||||
| **Yama 程序授权** | 需要有效的软件授权才能使用 Web 功能 | ✅ |
|
||||
| **Windows 电脑** | 用于运行 Yama 主控程序(见下方说明) | ✅ |
|
||||
| **公网服务器** | 用于运行 FRP + Nginx,需有固定公网 IP | ✅ |
|
||||
| **域名** | 用于申请 HTTPS 证书,可购买或免费获取 | ✅ |
|
||||
|
||||
> 💡 **关于 Windows 电脑**:Yama 主控程序需要在 Windows 上运行。如果你的公网服务器是 Windows 系统,可以直接在上面运行 Yama;如果公网服务器是 Linux,则需要另外准备一台 Windows 电脑。
|
||||
|
||||
### 没有这些条件?
|
||||
|
||||
如果你有 Web 远程桌面的需求,但缺少上述条件,欢迎联系作者:
|
||||
|
||||
- **咨询方案** - 我们可以提供服务器、域名等一站式解决方案
|
||||
- **购买授权** - 获取 Yama 程序授权,解锁全部功能
|
||||
- **技术支持** - 协助完成配置,快速上手使用
|
||||
|
||||
📬 **Telegram**:[@doge_grandfather](https://t.me/doge_grandfather)
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [为什么需要 HTTPS](#1-为什么需要-https)
|
||||
2. [技术准备](#2-技术准备)
|
||||
3. [获取域名](#3-获取域名)
|
||||
4. [配置域名解析](#4-配置域名解析)
|
||||
5. [申请 SSL 证书](#5-申请-ssl-证书)
|
||||
6. [安装配置 Nginx](#6-安装配置-nginx)
|
||||
7. [配置 FRP](#7-配置-frp)
|
||||
8. [启动服务](#8-启动服务)
|
||||
9. [手机访问测试](#9-手机访问测试)
|
||||
10. [常见问题](#10-常见问题)
|
||||
|
||||
---
|
||||
|
||||
## 1. 为什么需要 HTTPS
|
||||
|
||||
浏览器出于安全考虑,H264 视频解码功能只能在**安全上下文**中使用:
|
||||
|
||||
| 访问方式 | H264 远程桌面 |
|
||||
|----------|---------------|
|
||||
| `https://任意地址` | ✅ 支持 |
|
||||
| `http://localhost` 或 `http://127.0.0.1` | ✅ 支持 |
|
||||
| `http://其他地址`(包括内网 IP、公网 IP、域名) | ❌ 不支持 |
|
||||
|
||||
**简单说:**
|
||||
- **没有 HTTPS** → 只能在本机浏览器访问(localhost)
|
||||
- **配置 HTTPS** → 手机、平板、其他电脑都能访问
|
||||
|
||||
如果用 HTTP 从非本机访问,会提示:
|
||||
```
|
||||
Browser does not support H264: VideoDecoder not available
|
||||
```
|
||||
|
||||
本指南帮你配置 HTTPS,实现随时随地访问。
|
||||
|
||||
---
|
||||
|
||||
## 2. 技术准备
|
||||
|
||||
确认你已完成以下准备:
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| FRP 基础配置 | 已配置好 FRP 内网穿透(frps 在服务器运行,frpc 在本机运行) |
|
||||
| SSH 工具 | 如 PuTTY、Xshell、Windows Terminal,用于连接服务器执行命令 |
|
||||
| Yama Web 服务 | 已在 Yama 中启用 Web 服务(菜单 → 设置 → Web 服务) |
|
||||
|
||||
> 💡 如果还没配置 FRP,请先参考 FRP 官方文档完成基础配置。
|
||||
|
||||
**最终架构:**
|
||||
|
||||
```
|
||||
手机浏览器 ──HTTPS──> 你的域名:端口 ──> Nginx ──> FRP ──> 你的电脑
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 获取域名
|
||||
|
||||
### 方案一:购买域名(推荐)
|
||||
|
||||
便宜的域名一年只要几块钱:
|
||||
|
||||
| 注册商 | 网址 | 说明 |
|
||||
|--------|------|------|
|
||||
| 阿里云 | https://wanwang.aliyun.com | 国内,需实名 |
|
||||
| 腾讯云 | https://dnspod.cloud.tencent.com | 国内,需实名 |
|
||||
| Cloudflare | https://www.cloudflare.com | 国外,无需实名 |
|
||||
| Namesilo | https://www.namesilo.com | 国外,便宜 |
|
||||
|
||||
> 💡 **提示**:`.icu`、`.top`、`.xyz` 等后缀很便宜,首年通常 ¥5-10。
|
||||
|
||||
### 方案二:免费域名
|
||||
|
||||
| 服务 | 网址 | 说明 |
|
||||
|------|------|------|
|
||||
| DuckDNS | https://www.duckdns.org | 免费子域名,如 `xxx.duckdns.org` |
|
||||
| No-IP | https://www.noip.com | 免费子域名 |
|
||||
| Freenom | https://www.freenom.com | 免费顶级域名(不稳定) |
|
||||
|
||||
---
|
||||
|
||||
## 4. 配置域名解析
|
||||
|
||||
将域名指向你的**公网服务器 IP**。
|
||||
|
||||
### 4.1 登录域名管理面板
|
||||
|
||||
去你购买域名的网站,找到「DNS 解析」或「域名解析」。
|
||||
|
||||
### 4.2 添加 A 记录
|
||||
|
||||
| 字段 | 填写 |
|
||||
|------|------|
|
||||
| 记录类型 | A |
|
||||
| 主机记录 | `@`(代表根域名)或 `www` |
|
||||
| 记录值 | 你的服务器公网 IP,如 `123.45.67.89` |
|
||||
| TTL | 600(或默认) |
|
||||
|
||||
### 4.3 验证解析生效
|
||||
|
||||
等待 1-5 分钟,然后测试:
|
||||
|
||||
```bash
|
||||
ping your.domain.com
|
||||
```
|
||||
|
||||
如果返回你的服务器 IP,说明解析成功。
|
||||
|
||||
---
|
||||
|
||||
## 5. 申请 SSL 证书
|
||||
|
||||
使用 Let's Encrypt 免费证书。
|
||||
|
||||
### 5.1 SSH 连接到服务器
|
||||
|
||||
使用 SSH 工具连接到你的公网服务器。
|
||||
|
||||
### 5.2 安装 Certbot
|
||||
|
||||
```bash
|
||||
# Ubuntu / Debian
|
||||
apt update && apt install certbot -y
|
||||
|
||||
# CentOS
|
||||
yum install certbot -y
|
||||
```
|
||||
|
||||
### 5.3 申请证书
|
||||
|
||||
使用 DNS 验证方式,**不需要占用 80/443 端口**:
|
||||
|
||||
```bash
|
||||
certbot certonly --manual --preferred-challenges dns -d your.domain.com
|
||||
```
|
||||
|
||||
> ⚠️ 将 `your.domain.com` 替换为你的实际域名
|
||||
|
||||
### 5.4 按提示操作
|
||||
|
||||
1. **输入邮箱**:用于接收证书到期提醒
|
||||
2. **同意条款**:输入 `Y`
|
||||
3. **分享邮箱**:输入 `N`(可选)
|
||||
|
||||
### 5.5 添加 DNS TXT 记录
|
||||
|
||||
Certbot 会显示类似信息:
|
||||
|
||||
```
|
||||
Please deploy a DNS TXT record under the name:
|
||||
_acme-challenge.your.domain.com
|
||||
with the following value:
|
||||
aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890
|
||||
```
|
||||
|
||||
**去域名管理面板添加 TXT 记录:**
|
||||
|
||||
| 字段 | 填写 |
|
||||
|------|------|
|
||||
| 记录类型 | TXT |
|
||||
| 主机记录 | `_acme-challenge` |
|
||||
| 记录值 | Certbot 显示的那串字符 |
|
||||
| TTL | 600 |
|
||||
|
||||
### 5.6 验证 TXT 记录生效
|
||||
|
||||
```bash
|
||||
nslookup -type=TXT _acme-challenge.your.domain.com
|
||||
```
|
||||
|
||||
看到记录值后,**回到 Certbot 按回车**继续。
|
||||
|
||||
### 5.7 证书申请成功
|
||||
|
||||
成功后显示:
|
||||
|
||||
```
|
||||
Successfully received certificate.
|
||||
Certificate is saved at: /etc/letsencrypt/live/your.domain.com/fullchain.pem
|
||||
Key is saved at: /etc/letsencrypt/live/your.domain.com/privkey.pem
|
||||
```
|
||||
|
||||
**记住这两个路径,后面要用。**
|
||||
|
||||
### 5.8 设置自动续期(可选)
|
||||
|
||||
证书有效期 90 天,建议设置自动续期:
|
||||
|
||||
```bash
|
||||
# 方法一:直接添加定时任务(推荐)
|
||||
(crontab -l 2>/dev/null; echo "0 0 1 * * certbot renew --quiet && systemctl reload nginx") | crontab -
|
||||
|
||||
# 方法二:手动编辑
|
||||
crontab -e
|
||||
# 添加这行:0 0 1 * * certbot renew --quiet && systemctl reload nginx
|
||||
# 按 Ctrl+O 保存,Ctrl+X 退出
|
||||
```
|
||||
|
||||
> 💡 这会在每月 1 号自动检查并续期证书。
|
||||
|
||||
---
|
||||
|
||||
## 6. 安装配置 Nginx
|
||||
|
||||
### 6.1 安装 Nginx
|
||||
|
||||
```bash
|
||||
# Ubuntu / Debian
|
||||
apt update && apt install nginx -y
|
||||
|
||||
# CentOS
|
||||
yum install nginx -y && systemctl enable nginx
|
||||
```
|
||||
|
||||
### 6.2 删除默认配置(避免端口冲突)
|
||||
|
||||
```bash
|
||||
# Ubuntu / Debian
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# CentOS - 跳过此步(我们用 8080 端口,不冲突)
|
||||
```
|
||||
|
||||
### 6.3 创建配置文件
|
||||
|
||||
> ⚠️ **重要**:把命令中的 `your.domain.com` 替换为你的实际域名(共 4 处)
|
||||
|
||||
**Ubuntu / Debian:**
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/yama << 'EOF'
|
||||
server {
|
||||
listen 8080 ssl;
|
||||
server_name your.domain.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:9000;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# WebSocket 支持(重要)
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
# 长连接超时
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
**CentOS:**
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/conf.d/yama.conf << 'EOF'
|
||||
server {
|
||||
listen 8080 ssl;
|
||||
server_name your.domain.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:9000;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# WebSocket 支持(重要)
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
# 长连接超时
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
> 💡 **端口说明**:这里用 `8080`,你可以改成其他未被占用的端口(如 9443)。
|
||||
|
||||
### 6.4 启用配置(仅 Ubuntu/Debian 需要)
|
||||
|
||||
```bash
|
||||
ln -s /etc/nginx/sites-available/yama /etc/nginx/sites-enabled/
|
||||
```
|
||||
|
||||
> CentOS 已在上一步直接创建到 `conf.d` 目录,无需此步骤。
|
||||
|
||||
### 6.5 测试并启动
|
||||
|
||||
```bash
|
||||
nginx -t && systemctl restart nginx
|
||||
```
|
||||
|
||||
看到以下输出说明配置正确:
|
||||
```
|
||||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
```
|
||||
|
||||
如果报错,检查域名是否替换正确、证书路径是否存在。
|
||||
|
||||
---
|
||||
|
||||
## 7. 配置 FRP
|
||||
|
||||
### 7.1 FRP 服务端配置 (frps.toml)
|
||||
|
||||
在公网服务器上:
|
||||
|
||||
```toml
|
||||
bindPort = 7000
|
||||
```
|
||||
|
||||
### 7.2 FRP 客户端配置
|
||||
|
||||
#### 方式一:通过 Yama 程序配置(推荐)
|
||||
|
||||
1. 打开 Yama 主控程序
|
||||
2. 菜单 → 设置 → FRP 设置
|
||||
3. 启用 FRP
|
||||
4. 设置 Web 端口为 `9000`
|
||||
5. 保存
|
||||
|
||||
无需手动编辑配置文件。
|
||||
|
||||
#### 方式二:手动编辑 frpc.toml
|
||||
|
||||
在你的本机(Windows):
|
||||
|
||||
```toml
|
||||
serverAddr = "你的服务器IP或域名"
|
||||
serverPort = 7000
|
||||
|
||||
[[proxies]]
|
||||
name = "yama-web"
|
||||
type = "tcp"
|
||||
localPort = 9000
|
||||
remotePort = 9000
|
||||
```
|
||||
|
||||
> 💡 **说明**:FRP 把服务器的 9000 端口转发到你本机的 9000 端口。
|
||||
|
||||
---
|
||||
|
||||
## 8. 启动服务
|
||||
|
||||
### 8.1 开放防火墙端口(重要!)
|
||||
|
||||
很多新手卡在这一步。**服务器防火墙必须开放以下端口**:
|
||||
|
||||
```bash
|
||||
# 查看防火墙状态
|
||||
ufw status # Ubuntu
|
||||
firewall-cmd --state # CentOS
|
||||
|
||||
# Ubuntu - 开放端口
|
||||
ufw allow 8080 # Nginx HTTPS 端口
|
||||
ufw allow 7000 # FRP 端口
|
||||
ufw allow 9000 # FRP Web 转发端口
|
||||
|
||||
# CentOS - 开放端口
|
||||
firewall-cmd --permanent --add-port=8080/tcp
|
||||
firewall-cmd --permanent --add-port=7000/tcp
|
||||
firewall-cmd --permanent --add-port=9000/tcp
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
> ⚠️ **云服务器还需要在控制台安全组中开放这些端口!**(阿里云、腾讯云等)
|
||||
|
||||
### 8.2 启动顺序
|
||||
|
||||
按以下顺序启动:
|
||||
|
||||
1. **服务器上**:启动 FRP Server
|
||||
```bash
|
||||
./frps -c frps.toml
|
||||
```
|
||||
|
||||
2. **服务器上**:确认 Nginx 在运行
|
||||
```bash
|
||||
systemctl status nginx
|
||||
```
|
||||
|
||||
3. **本机上**:启动 Yama 主控程序
|
||||
|
||||
> 💡 **提示**:如果在 Yama 中启用了 FRP 功能,程序启动时会自动连接 FRP Server,无需手动启动 frpc。
|
||||
|
||||
### 8.3 流量路径
|
||||
|
||||
```
|
||||
手机浏览器
|
||||
│
|
||||
│ HTTPS (端口 8080)
|
||||
▼
|
||||
Nginx (SSL 终结)
|
||||
│
|
||||
│ HTTP (端口 9000)
|
||||
▼
|
||||
FRP Server
|
||||
│
|
||||
│ TCP 隧道
|
||||
▼
|
||||
FRP Client
|
||||
│
|
||||
│ 本地 (端口 9000)
|
||||
▼
|
||||
Yama WebService
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 手机访问测试
|
||||
|
||||
### 9.1 打开浏览器
|
||||
|
||||
在手机浏览器输入:
|
||||
|
||||
```
|
||||
https://your.domain.com:8080
|
||||
```
|
||||
|
||||
> ⚠️ 注意是 `https://`,不是 `http://`
|
||||
|
||||
### 9.2 登录
|
||||
|
||||
输入用户名密码登录。
|
||||
|
||||
### 9.3 连接远程桌面
|
||||
|
||||
选择设备,点击连接,享受远程桌面。
|
||||
|
||||
---
|
||||
|
||||
## 10. 常见问题
|
||||
|
||||
### Q: 浏览器提示"不安全"或证书错误
|
||||
|
||||
A: 检查域名是否和证书匹配,证书是否过期:
|
||||
```bash
|
||||
certbot certificates
|
||||
```
|
||||
|
||||
### Q: 无法访问,连接超时
|
||||
|
||||
A: 检查各服务是否运行:
|
||||
```bash
|
||||
systemctl status nginx
|
||||
netstat -tlnp | grep 8080
|
||||
netstat -tlnp | grep 9000
|
||||
```
|
||||
|
||||
### Q: WebSocket 连接失败
|
||||
|
||||
A: 确认 Nginx 配置中有 `Upgrade` 和 `Connection` 头配置。
|
||||
|
||||
### Q: 仍然提示 VideoDecoder not available
|
||||
|
||||
A:
|
||||
1. 确认使用 `https://` 访问
|
||||
2. 清除浏览器缓存后重试
|
||||
3. 换个浏览器试试(推荐 Chrome/Edge)
|
||||
|
||||
### Q: 证书过期了
|
||||
|
||||
A: 手动续期:
|
||||
```bash
|
||||
certbot renew
|
||||
systemctl reload nginx
|
||||
```
|
||||
|
||||
### Q: FRP 连接不上
|
||||
|
||||
A: 参考 [8.1 开放防火墙端口](#81-开放防火墙端口重要) 检查防火墙配置。
|
||||
|
||||
### Q: 云服务器(阿里云/腾讯云)无法访问
|
||||
|
||||
A: 除了服务器防火墙,还需要在**云控制台的安全组**中开放端口:
|
||||
1. 登录云服务器控制台
|
||||
2. 找到「安全组」设置
|
||||
3. 添加入站规则,开放 8080、7000、9000 端口
|
||||
|
||||
### Q: 如何测试各环节是否正常
|
||||
|
||||
A: 逐步排查:
|
||||
```bash
|
||||
# 1. 测试 Nginx 是否监听
|
||||
curl -k https://127.0.0.1:8080
|
||||
|
||||
# 2. 测试 FRP 是否转发
|
||||
curl http://127.0.0.1:9000
|
||||
|
||||
# 3. 测试域名是否解析正确
|
||||
ping your.domain.com
|
||||
|
||||
# 4. 测试外部访问(在手机上)
|
||||
# 打开 https://your.domain.com:8080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
| 步骤 | 操作位置 | 说明 |
|
||||
|------|----------|------|
|
||||
| 获取域名 | 域名注册商 | 购买或免费获取 |
|
||||
| 域名解析 | 域名管理面板 | A 记录指向服务器 IP |
|
||||
| 申请证书 | 服务器 | Let's Encrypt 免费证书 |
|
||||
| 配置 Nginx | 服务器 | SSL 终结 + 反向代理 |
|
||||
| 开放防火墙 | 服务器 + 云控制台 | 开放 8080、7000、9000 端口 |
|
||||
| 配置 FRP | 服务器 + 本机 | 内网穿透 |
|
||||
| 访问测试 | 手机 | `https://域名:端口` |
|
||||
|
||||
配置完成后,你就可以随时随地用手机访问远程桌面了!
|
||||
|
||||
---
|
||||
|
||||
**遇到问题?** 查看 [常见问题](#10-常见问题) 或逐步排查各环节。
|
||||
Reference in New Issue
Block a user