You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rdpwrap/RdpCoexist/README.md

80 lines
4.2 KiB

# RdpCoexist
A small, portable **governance/ops shell** that lets **AnyDesk and RDP run side by
side** (concurrent RDP sessions + local console) on modern Windows — **without
modifying `System32\termsrv.dll` on disk**.
It does *not* reimplement the wrapper. It drives
[`sergiye/rdpWrapper`](https://github.com/sergiye/rdpWrapper) (a portable, .NET
Framework 4.7.2, single-file rewrite of RDP Wrapper that redirects the
`TermService` `ServiceDll` to a non-destructive proxy — `TermWrap` — with a
built-in offset engine) and wraps it with the operational pieces it lacks.
> Approach: stop RDP service → point `TermService` `ServiceDll` at a wrapper in a
> portable folder → set `fDenyTSConnections=0`, `fSingleSessionPerUser=0`,
> `EnableConcurrentSessions=1` → start service. The original `termsrv.dll` is
> **never touched**, so SFC / WRP / Windows Update leave it alone.
## Why a shell (the 5 gaps this closes)
| # | Gap in the bare engine | What this shell adds |
|---|------------------------|----------------------|
| 1 | No license grant → cannot repackage/redistribute the engine binary | Engine is **fetched from its official release** and **SHA256-pinned**; nothing is bundled here (`.gitignore` blocks `*.exe`/`*.dll`) |
| 2 | No self-heal after Windows Update replaces `termsrv.dll` | `-Heal` + a **SYSTEM startup scheduled task**: verify, and only re-install if the listener dropped |
| 3 | Verification is GUI-only | `-Verify` returns a **machine-checkable verdict** (`COEXIST-OK` requires a real listener **and** ≥2 active sessions, not just registry values) |
| 4 | Engine phones home for updates → version drift | Engine is **always** invoked with `-offline`; version is pinned in `config.psd1` |
| 5 | Engine DLL is commonly flagged `HackTool:Win32/RDPWrap` | Idempotent **Defender exclusion** for the engine folder (skipped gracefully if exclusions are policy-locked) |
## Layout
```
RdpCoexist/
├─ RdpCoexist.cmd self-elevating launcher (UAC)
├─ RdpCoexist.ps1 -Apply | -Revert | -Verify | -Heal | -Status
├─ config.psd1 version pin, SHA256 pin, paths, options
└─ modules/
├─ Common.psm1 logging, admin check, GPO-override warning
├─ Engine.psm1 download + hash-verify + drive the engine
├─ Defender.psm1 idempotent exclusion add/remove
├─ Verify.psm1 listener + active-session verdict
├─ Heal.psm1 scheduled-task register/unregister
└─ LocalUser.psm1 optional dedicated RDP account
```
Engine binary, logs, and state live under `%ProgramData%\RdpCoexist\` — never in this repo.
## Usage
1. Edit `config.psd1`. At minimum set **`EngineSha256`** to the hash of the engine
release you vetted (or run the first `-Apply` with `-TrustOnFirstUse`, which
prints the observed hash for you to paste back and lock).
2. Run:
```
RdpCoexist.cmd -Apply :: fetch+verify engine, install, register heal task, verify
RdpCoexist.cmd -Verify :: print current verdict
RdpCoexist.cmd -Revert :: uninstall engine, remove task + exclusion
```
`COEXIST-OK` only appears once **two** sessions are actually active (e.g. console +
one RDP), because a listener alone does not prove coexistence.
## Requirements & caveats
- **Admin** required (the launcher elevates).
- **.NET Framework 4.7.2** — built into supported Windows 10/11; no install needed.
- **Domain GPO**: if `fSingleSessionPerUser` / `fDenyTSConnections` are enforced by
policy, they revert on GP refresh. The shell warns; resolve with IT.
- **Same-user dual session** trips license locks in OneDrive/Outlook/Office. Prefer
the optional dedicated RDP account (`CreateRdpUser`), keeping the console user for AnyDesk.
- **Portability vs. resilience**: the engine is copied under `%ProgramData%` by
default. Pointing `ServiceDll` at removable media means RDP dies if the media is
pulled — don't.
- Concurrent-session enablement changes the effective RDP session limit; confirm your
own licensing position before use.
## Configuration keys
See `config.psd1` for the annotated list. Values shipped here are **neutral
placeholders** — set the version pin, hash pin, and (if used) the RDP user name to
your own before running.