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外部依赖
| 类型 | 依赖 |
|---|---|
| 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风险提醒 风险提醒:黄色 · 留意使用
- 探针关闭 TLS 证书校验(有交代但仍属降级手法) — 目的正当(验证服务端接受 TLS)且无凭据无数据,文档也提示「需要证书审计请用别的工具」。风险在于使用者可能把该习惯带到有数据流的场景。请勿把 CERT_NONE 的探针模式复用到真实连接。
- 读入的客户端日志含账号与主机身份信息 — Windows App 日志包含微软账号邮箱、租户痕迹、主机名与 activity GUID。分析时应只提取需要的模式,不要把整段日志外发或粘进公开渠道。
- 对目标主机发起主动探测可能被视为扫描 — 对自有/授权主机属正常运维;对不属于自己的主机(例如想「验证一下同事的机器」)可能违反政策或触发告警。文档未提供授权判断流程,需使用者自行界定。
- 经 SSH 在 Windows 主机执行 PowerShell 具备任意执行能力 — 文档给的命令都是只读查询(Get-CimInstance/Get-WinEvent),但通道本身可执行任意命令;若 agent 越界改写命令,影响面不小。建议限定在文档给出的只读命令集内。
- 强依赖日志可读性,日志格式随版本变化 — 整套判据建立在 Microsoft 客户端日志的具体字符串上(如 FetchClientOptions、OneAuthError_InteractionRequired)。客户端升级后模式串可能变化,届时需要重新校准(文档已给出「工作 vs 故障对照」这一自校准方法)。
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…d5c4678cb5