windows-remote-desktop-connection-doctor
Diagnose Windows App (Microsoft Remote Desktop / Azure Virtual Desktop / W365 / direct PC) connection issues on macOS. Analyze transport protocol selection (UDP Shortpath vs WebSocket), detect VPN/proxy interference, parse Windows App logs for Shortpath failures, and resolve stuck "Configuring remote PC..." dialogs caused by expired Microsoft accounts, server reboots, or client-side auth poisoning. Use when VDI connections are slow or stuck, when direct PC connections fail to connect, when transport shows WebSocket instead of UDP, when RDP Shortpath fails, or when Windows App is frozen at a progress dialog.
1实现原理 · 为什么它能做到
它按「连接类型」先分流再取证:AVD/WVD/W365 走 Connection Info 面板 + 网关健康检查 + Shortpath 日志;直连 PC 没有该面板,必须先跑 RDP 协议探针证明服务端活着,再查客户端身份/日志。分流决定后续用什么证据。
| Scenario | Key Characteristic | Primary Evidence Source | |---|---|---| | **AVD/WVD/W365** | User connects to a cloud desktop through a workspace/gateway | Connection Info panel, gateway health checks, UDP Shortpath logs | | **Direct PC** | User connects to a named PC by hostname or IP (e.g., a home workstation) | RDP protocol probe, Windows App log auth chain, Windows-side reboot events |
核心取证手段是解析 Windows App 客户端日志——因为传输协商细节「没有任何网络层测试能揭示」,这是该 skill 不可替代的信息源。
This is the most critical step. Windows App logs contain transport negotiation details that no network-level test can reveal.
自带探针脚本用「无凭据」方式证明服务端 RDP 栈健康:发 X.224 Connection Request + RDP_NEG_REQ,解析 RDP_NEG_RSP 得到协商的加密协议,再把同一 socket 升级到 TLS。
RDP_NEG_REQ_PACKET = bytes.fromhex("030000130ee000000000000100080003000000")
探针刻意关闭 TLS 证书校验,且文档明确交代理由与局限——目的是验证「服务端接受 TLS」而非认证服务端。
> **Note on TLS verification:** The probe intentionally disables certificate verification so it can confirm the RDP stack accepts TLS even with self-signed or domain-issued certificates. It does not authenticate the server. For a full certificate audit, use a separate TLS inspection tool.
诊断纪律要求「先证明客户端还是服务端」——探针成功则问题在客户端(认证/应用状态/代理/身份),失败则问题在服务端或网络。这是把二元归因做成一条可执行的成败判据。
A successful probe reports `RDP server <host>:<port> is reachable and healthy (RDP + TLS).` and means the problem is **client-side** (auth, app state, proxy, or identity). A failed probe means the problem is **server-side or network** (PC off, firewall, port unreachable, TLS interception).
它把六类根因写成可对照的证据签名表(VPN/代理干扰、ISP 限 UDP、客户端健康检查失败、服务端未开 Shortpath、客户端身份/账号毒化、服务端重启),每类都给「证据 → 修复 → 验证」三段。
#### Category E: Client Identity / Expired Microsoft Account Poisoning (Direct PC and AVD)
验证阶段要求看客户端行为证据而非只看「能不能连上」:日志里要出现输入事件、且不得再有 UserCancelled(8)。
2. The Windows App log for the new session should show mouse/keyboard input events (`IHAddMouseEventToPDU`, `IHAddKeyboardEventToPDU`) and no `UserCancelled(8)` errors.
2核心能力
3外部依赖
| Kind | Dependency / Endpoint |
|---|---|
| cli | python3(运行自带 RDP 探针与文档内联的 STUN 检测脚本) |
| cli | macOS 网络工具(ifconfig / netstat / scutil / lsof / ps / env / tailscale) |
| cli | grep(日志模式提取,文档给成套 grep -E 模板) |
| cli | ssh + PowerShell -EncodedCommand(经 WSL/Tailscale 到 Windows 主机取证) |
| cli | curl(读 ShadowRocket 配置 API 以核对代理状态) |
| api | Azure Virtual Desktop 网关/工作区端点(日志中出现,用于 DIRECT 规则配置示例) |
| network | STUN 公共服务器(测试 UDP 是否被 ISP 封锁) |
| network | 目标 RDP 主机(用户指定的 host:3389 或自定义端口,探针与日志分析的对象) |
| network | Windows 侧 RDP 主机(经 SSH/Tailscale 执行 PowerShell 取证) |
| network | Tailscale(netcheck/status,用于 NAT 类型与 UDP 能力评估) |
4管控建议与判定依据
- scripts/: 执行本地脚本或命令: python3 scripts/probe_rdp_server.py <host> [port](仓库自带;对目标主机发 TCP 连接 + RDP
- 网络端点: 声明外部网络访问: TCP 连接到用户指定 RDP 主机:3389(或自定义端口),发送 RDP 协商包并升级 TLS
5第二遍独立确认
- [ok] 探针「无凭据」声明是否有反例(是否真的不读认证材料) — 反例检索失败:probe_rdp_server.py 的 import 为 socket/ssl/struct/sys/typing,无 os.environ/getenv、无 open()、无 keychain 调用;全流程是 create_connection → sendall(19 字节固定握手包) → recv → wrap_socket。凭据类 token 在全目录 grep(password/token/secret/api_key/credential)除文档描述「认证失败」的语义词外零命中。声明成立。
- [ok] TLS 校验关闭是否为「未交代的松绑」 — 已充分交代:代码内注释 'We intentionally disable verification because the diagnostic goal is to confirm the RDP stack accepts TLS, not to validate the certificate chain.';reference 内有专门引用块说明 'The probe intentionally disables certificate verification... It does not authenticate the server. For a full certificate audit, use a separate TLS inspection tool.'。属有意识、有边界说明的设计,不是隐藏降级。已记入 security.injection_surface 与 grade.reason。
- [ok] 是否漏记外发目标(域名/IP 全量回查) — 提取结果:stun.l.google.com:19302、stun1.l.google.com:19302、rdweb.wvd.microsoft.com、<prefix>-rdgateway-r1.wvd.microsoft.com、*.wvd.microsoft.com(DIRECT 规则示例)、13.104.0.0/14(IP-CIDR 示例)、<local-ip>:8080(ShadowRocket 配置 API)、<host>:3389(用户指定目标)。均已归入 external_deps 的 12 个条目;无未记载的隐蔽外发。
- [ok] frontmatter 的 allowed-tools 声明与文档行为是否一致 — 声明为 `allowed-tools: Read, Grep, Bash`。文档要求的动作是:读日志(Read/grep)、跑 ifconfig/netstat/scutil/lsof/tailscale/ps(Bash)、跑探针与内联 python(Bash)——全部落在声明范围内;且文档不要求写文件(无 chmod/编辑配置/生成文件的步骤,AVD 的 DIRECT 规则是以「给用户的修法」形式给出,由用户在代理工具里配置,而非本 skill 直接改配置)。声明与行为一致,且比同仓库的 tunnel-doctor(含 Edit)更保守。
- [ok] 是否有未实现的宣称(能力 vs 文档) — 逐项抽查成立:承诺「分析传输协议选择」→ 有 Connection Info 字段表与日志 FetchClientOptions/WebRTC 条目;承诺「解析日志找 Shortpath 失败」→ 有日志模式→含义对照表与 GUID 聚合方法;承诺「解决卡在 Configuring remote PC」→ 有 Category E 完整签名集与修复步骤;承诺「检测 VPN/代理干扰」→ 有 198.18.0.x、STUN/TURN routed through VPN 的判据与 DIRECT 规则修法。均有对应实现/流程支撑,未见夸大。
- [ok] 文档内部是否存在自相矛盾(如是否同时建议放宽校验又不建议) — 抽查与 tunnel-doctor 类似的张力点:本 skill 未出现 ssh -o StrictHostKeyChecking=no;唯一放宽是探针的 CERT_NONE,且被解释为「验证服务端接受 TLS」的必需手法并有明确局限说明。未发现未消解的内部矛盾。
- [ok] pin commit 与 skill.path 是否与任务书表格一致 — git rev-parse HEAD = d5c4678cb5d4fd6acc9c922690df035dbd33d247,与任务书表格『库内 HEAD』逐字一致;目录位于仓库根,相对路径 windows-remote-desktop-connection-doctor;本目录最后一次提交 878f947(2026-09-07)。GitHub API 复核 MIT / stars 1392 / pushed 2026-09-15T09:30:04Z。
- [ok] evidence 逐字性是否经磁盘级复核 — 【evidence 逐字性校正】首稿该条 quote 把源码第 152 行的 grep 模板截断且过度转义(把 `\|` 写成 `\\|`),导致未与源文逐字匹配;已改为源码中的真实前缀(截到 `"$LATEST_LOG"` 为止,避开后续含反斜杠的 `grep -v` 段)。该 grep 模板确实存在,结论未变,属引用精度问题。
6结论
c5c2e8a79a426da5…