一个「改个注册表值 + 加条防火墙规则」的小工具,我们迭代了 27 个版本。
不是我们菜。是 Windows 远程桌面这潭水,比你想的深。
服务器运维这行,3389 这个端口人人喊打——全网扫描器 24 小时盯着它爆破。改掉远程桌面端口,是最便宜的加固手段,网上教程一搜一大把:改两个注册表键,加条防火墙规则,完事。
老板说:写个小工具吧,C++ 的,改完自动加防火墙例外,TCP、UDP 都加。
我当时心想,这不是十分钟的事吗。
图样。
坑 1:黑窗一闪而过
第一版是控制台程序。用户双击,窗口闪一下就没了。「什么鬼,闪退?」
其实程序跑完了——控制台程序跑完就退,天经地义。但对双击的用户来说,这就是「闪退」。
修法:所有出口都加一句「按回车退出」,但只在该环境是真实交互终端时才停,脚本调用不受影响。
后来干脆上了 GUI,这个问题不复存在。但教训记下了:给小白用户的东西,ExitCode 是 0 还是 1 没人在乎,窗口关太快就是闪退。
坑 2:中文乱码
mingw 交叉编译,源码里 printf("中文"),到目标机器上全变成「锟斤拷锟斤拷」。
网上教程说 SetConsoleOutputCP(65001) 就行。不行。老 conhost、默认点阵字体、系统区域设置,任何一环不对都乱。
修法:全量宽字符输出,_setmode(_fileno(stdout), _O_U16TEXT) 配上 wprintf(L"..."),直接往控制台写 UTF-16,跟代码页彻底解耦。
一句话:在 Windows 上输出中文,要么全宽字符,要么等着收货。
坑 3:按钮丑得像 1998
客户反馈:「丑得能卖了。」
查了半天,不是代码的锅——Win32 程序不带 comctl32 v6 视觉样式清单,系统就按 Win98 经典样式渲染按钮,灰底立体老三样。
修法:嵌一个 manifest 声明依赖 Common Controls 6.0,按钮立刻变现代扁平样式。
但老系统上主题一变又打回原形,最后干脆整个界面自绘:渐变横幅自己画、圆角按钮自己画,不依赖系统主题,哪台机器跑出来都一个样。
结论:C++ 写 UI 不丢人,丢人的是拿默认控件糊弄 2026 年的用户。
坑 4:AI 生成的代码,藏着一行致命 bug
中途老板拿豆包生成的版本让我看。整体思路不错:紧凑布局、服务控制走 API、GDI 资源释放。但里面藏了一行:
ControlService(hSvc, SERVICE_CONTROL_START, &st); // 启动服务?SERVICE_CONTROL_START 根本不是合法的控制码。ControlService 只能停、只能 interrogate,要启动服务必须用 StartService()。这行代码的效果是:远程桌面服务停了,再也起不来——直到有人重启机器。
换句话说,用户点了「确认修改」,然后这台服务器就再也连不上了。
AI 写代码很快,但它不理解 Win32 服务控制器 1993 年就定下的 API 边界。AI 生成的系统级代码,每一行都得人过目。
坑 5:点一次加两条,点三次加六条
netsh advfirewall firewall add rule 这个命令,同名规则不去重。用户每点一次「确认修改」,就多两条(一 TCP 一 UDP);改几个端口下来,防火墙里堆了一排同名规则。
修法:每次提交先 delete rule name=... 把本工具名下所有旧规则清光,再 add 新的。现在点一百次,永远只有两条。
推而广之:所有「幂等」需求,靠先删后加,别指望 add 命令自己去重。
坑 6:最难的一个——「服务重启了,但端口没换」
这是整个项目里最阴的坑。表现是:
- 注册表改成功了(回读核对过)
- 防火墙规则也加上了
- 服务也「重启」了(状态显示 RUNNING)
- 但新端口连不上,旧端口还能连——或者干脆谁都连不上
第一次碰到时,我以为是注册表没写进去。后来才发现:服务状态 RUNNING ≠ 监听器换了端口。TermService 重启时,活跃会话可能让它「半死不活」地起来,监听器还挂在旧端口上。只有重启机器才能强制重绑。
修法分三层:
- 别信状态,信探测:改完后真实地对
127.0.0.1:新端口发一次 TCP 连接,连上了才算数。查监听表都不保险——RDP 监听器有时根本不进常规枚举,纯查表会误报。 - 探测要等:服务换口有几秒延迟,改完立刻探测,十有八九显示「没生效」——其实是还没轮到。改成持续观察 15 秒,每 1.5 秒探一次。
- 没绑上就自动再拉一轮:把 TermService / UmRdpService / SessionEnv(这个依赖最初还漏了)整条链完整停-启,循环重试,绑上为止。
运维的老话在这里完全应验:监控说它活着,和它真能接活,是两回事。
坑 7:用户输入永远是「恶意输入」
测试环节,老板在「新端口」框里敲了个 80,点确认。
把自己关外面了。80 是 HTTP 的端口,那台机器上正跑着网站,RDP 监听器一绑上去,web 服务和远程桌面一起完蛋。
修法三层拦截:
- 1024 以下系统保留端口,直接不给过
- 常见服务端口黑名单:80 / 443 / 22 / 3306 / 1433 / 6379 / 27017……撞了弹窗点名是哪个服务的
- 占用检测:直接查系统监听表,本机已有程序在听的端口,不管在不在黑名单,一律拦截
再加一条防呆:新端口和当前端口相同不给提交(改 3389 到 3389 没意义,还容易出幺蛾子);真要改回 3389,二次确认。
复盘
27 个版本,没有一行算法,全是和真实世界的摩擦:显示层是宽字符、视觉样式、自绘;系统层是服务 API 的边界、监听器生命周期、防火墙规则幂等;用户层是闪退、乱码、丑、乱输、把自己锁外面。
写这个小工具之前,我以为难点是注册表。写完才知道,难点是让一个不懂技术的人,在任何一台 Windows 上,安全地完成一次高危操作。同类的事之前在 .rdp 里勾好的剪贴板一连上就没了 那篇里也写过,都是这类小工具背后的琐碎。
对了,还有最后一个没解开的谜:极小概率下,一切检测都通过,用户还是会被关在外面,VNC 进去再点一次就好。根因未明,怀疑是云安全组或者虚拟化层的问题。
如果你也碰到过,欢迎留言。说不定第 28 版就靠你了。
七亿-远程端口更改工具 v1.0(内部迭代 27 版),Windows x64 绿色单文件,官网下载:soft.7ehl.com。同厂的 七亿批量远程管理工具 里也带这类远程端口/加固的一键处理。
评论