Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ rustflags = ["-C", "target-feature=+crt-static"]

[target.i686-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]

[target.aarch64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ What actually happened.
## Environment

- **OS**: [e.g. Windows 11 23H2, Windows 10 22H2]
- **Architecture**: x64 / x86
- **Architecture**: x64 / x86 / ARM64
- **rdprrap Version**:
- **termsrv.dll Version**: [e.g. 10.0.26100.xxxx]

Expand Down
107 changes: 99 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ jobs:
# The release x64 job additionally runs an offset-finder smoke test
# against the runner's own C:\Windows\System32\termsrv.dll — that
# exercises the pattern-matching pipeline on a real, current Windows
# build (hosted runner image ships Server 2022, 10.0.20348.x).
# build. Artifact names include the runner alias because windows-latest
# can overlap with an explicitly pinned image during GitHub image rollovers.
build-windows:
runs-on: ${{ matrix.os }}
strategy:
Expand All @@ -48,11 +49,11 @@ jobs:
- release
# Additional row: pin one extra release x64 build to windows-2025 so
# the offset-finder smoke test also hits a Server 2025 termsrv.dll
# alongside whatever windows-latest currently maps to (Server 2022
# as of 2026-Q2). Two distinct Windows lines without doubling the
# whole matrix. Server 2019 coverage used to live here, but that
# image was retired by GitHub Actions on 2025-06-30 — using it now
# hangs jobs in the queue indefinitely.
# alongside whatever windows-latest currently maps to. When
# windows-latest also maps to Server 2025, this still verifies both
# the moving alias and the pinned image without artifact-name
# collisions. Server 2019 coverage used to live here, but that image
# was retired by GitHub Actions on 2025-06-30.
include:
- os: windows-2025
target: x86_64-pc-windows-msvc
Expand All @@ -64,7 +65,7 @@ jobs:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}-${{ matrix.profile }}
key: ${{ matrix.os }}-${{ matrix.target }}-${{ matrix.profile }}

- name: Build (${{ matrix.profile }})
run: |
Expand Down Expand Up @@ -229,14 +230,104 @@ jobs:
if: matrix.profile == 'release'
uses: actions/upload-artifact@v4
with:
name: rdprrap-${{ matrix.target }}
name: rdprrap-${{ matrix.os }}-${{ matrix.target }}
path: |
target/${{ matrix.target }}/release/*.dll
target/${{ matrix.target }}/release/offset-finder.exe
target/${{ matrix.target }}/release/rdprrap-installer.exe
target/${{ matrix.target }}/release/rdprrap-check.exe
target/${{ matrix.target }}/release/rdprrap-conf.exe

# ─── Windows ARM64: build + static artifact checks ───
#
# CI verifies ARM64 builds and PE/export shape. Runtime validation still
# needs real Windows ARM64: termwrap/umwrap/endpwrap should show ARM64
# patch-applied logs and termwrap must pass a concurrent-session smoke test.
build-windows-arm64:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
profile:
- debug
- release
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-pc-windows-msvc
components: clippy
- uses: Swatinem/rust-cache@v2
with:
key: aarch64-pc-windows-msvc-${{ matrix.profile }}

- name: Build ARM64 (${{ matrix.profile }})
run: |
$args = @("build", "--target", "aarch64-pc-windows-msvc")
if ("${{ matrix.profile }}" -eq "release") { $args += "--release" }
cargo @args

- name: Clippy ARM64
run: cargo clippy --target aarch64-pc-windows-msvc --all-targets -- -D warnings

- name: Install llvm-tools
if: matrix.profile == 'release'
run: rustup component add llvm-tools-preview

- name: Verify ARM64 artifacts
if: matrix.profile == 'release'
shell: pwsh
run: |
$base = "target/aarch64-pc-windows-msvc/release"
$sysroot = (& rustc --print sysroot).Trim()
$hostTriple = (& rustc -vV | Select-String "^host: ").ToString().Split()[1]
$readobj = Join-Path $sysroot "lib/rustlib/$hostTriple/bin/llvm-readobj.exe"
if (!(Test-Path $readobj)) {
Write-Error "llvm-readobj.exe not found at $readobj"
exit 1
}

function Assert-Export($dll, $name) {
$out = & $readobj --coff-exports $dll | Out-String
if ($out -notmatch [regex]::Escape($name)) {
Write-Error "$dll missing $name export"
exit 1
}
}

foreach ($name in @("termwrap_dll.dll", "endpwrap_dll.dll", "umwrap_dll.dll")) {
$dll = "$base/$name"
if (!(Test-Path $dll)) { Write-Error "$name not found"; exit 1 }
$info = & $readobj --file-headers $dll | Out-String
if ($info -notmatch "IMAGE_FILE_MACHINE_ARM64") {
Write-Error "$name is not ARM64"
exit 1
}
}

Assert-Export "$base/termwrap_dll.dll" "ServiceMain"
Assert-Export "$base/termwrap_dll.dll" "SvchostPushServiceGlobals"
Assert-Export "$base/endpwrap_dll.dll" "GetTSAudioEndpointEnumeratorForSession"
Assert-Export "$base/endpwrap_dll.dll" "DllGetClassObject"
Assert-Export "$base/endpwrap_dll.dll" "DllCanUnloadNow"

foreach ($name in @("offset-finder.exe", "rdprrap-installer.exe", "rdprrap-check.exe", "rdprrap-conf.exe")) {
if (!(Test-Path "$base/$name")) {
Write-Error "$name not found at $base/$name"
exit 1
}
}
Write-Host "ARM64 artifacts: OK"

- name: Upload ARM64 artifacts
if: matrix.profile == 'release'
uses: actions/upload-artifact@v4
with:
name: rdprrap-aarch64-pc-windows-msvc
path: |
target/aarch64-pc-windows-msvc/release/*.dll
target/aarch64-pc-windows-msvc/release/*.exe

# ─── cargo-deny: license + banned-deps + source-registry policy gate ───
deny:
runs-on: ubuntu-latest
Expand Down
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,30 @@ Thumbs.db
# AI config (actual files in .priv-storage/, root has symlinks)
.priv-storage/
CLAUDE.md
AGENTS.md
.cursorrules
.claude
.vscode
WORK_STATUS.md
CLAUDE.local.md
.mcp.json
CLAUDE.md.bak
AGENTS.md.bak
.cursorrules.bak
WORK_STATUS.md.bak
.gitignore.bak
.priv-storage/.allow-setup-reread
.codex/
.aider*
.continue/
.cline/
.roo/

# Backup toolkit temp files
tmp-igbkp/
tmp-igbkp/.work/
tmp-igbkp/output/
uninstall-backup-*/

# Environment
.env
Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,34 @@ and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2

## [Unreleased]

## [0.2.0] - 2026-05-13

### Added
- ARM64 Windows build scaffold: `aarch64-pc-windows-msvc` now type-checks
across the workspace, has static-CRT rustflags, and is covered by a
CI build/static-artifact job.
- Experimental ARM64 runtime patching for `umwrap-dll` and `endpwrap-dll`.
`patcher::arm64` now scans ARM64 `.pdata` function entries, resolves
ADR/ADRP+ADD policy-string references, and locates nearby BL calls.
`umwrap` replaces the policy BL call with `mov w0,#1`; `endpwrap` replaces
the referenced audio-capture function start with `mov w0,#1; ret`.
- Experimental ARM64 runtime patching for `termwrap-dll`. The ARM64 path
resolves the same termsrv policy strings via `.pdata`, patches DefPolicy
field checks, forces single-session/local-only checks false, forces
AppServer/NonRDP checks true, patches the PropertyDevice BL result false,
and patches SL policy query BL calls to return true. Real Windows ARM64 runtime
validation is still required before treating this as production-supported.

### Fixed
- `rdprrap-installer install --force` now actually replaces existing
installed wrapper DLLs before the race-safe `CREATE_NEW` copy. Without
`--force`, an existing destination DLL now fails early with a clear
"use --force" message instead of surfacing a lower-level Win32 create error.
- `offset-finder` no longer treats ARM64 PE32+ images as x64. Pure ARM64
`termsrv.dll` images now get an ARM64 string/function/BL-site report;
ARM64EC/ARM64X hybrid images still report unsupported until separately
validated.

## [0.1.3] - 2026-04-23

### Fixed (license compliance — continued)
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ git clone https://github.com/kernalix7/rdprrap.git
cd rdprrap
rustup target add x86_64-pc-windows-msvc
rustup target add i686-pc-windows-msvc
rustup target add aarch64-pc-windows-msvc
cargo build --release
```

Expand Down
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ members = [
resolver = "2"

[workspace.package]
version = "0.1.3"
version = "0.2.0"
edition = "2021"
license = "MIT"
# Internal workspace — the crates here are DLL proxies and glue binaries
Expand Down
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ RDP Wrapper rewritten in Rust.
| **termwrap-dll** | Core RDP patching — multi-session support, policy bypass for Home/non-Server editions. 7 patch types: DefPolicy, SingleUser, LocalOnly, NonRDP, PropertyDevice, SLPolicy, CSLQuery::Initialize |
| **umwrap-dll** | USB/camera PnP device redirection for all SKUs (legacy + modern Windows) |
| **endpwrap-dll** | Audio recording redirection (TSAudioCaptureAllowed) |
| **patcher** | Shared library — PE parsing, x86/x64 disassembly, runtime pattern matching, 14 verified bytecode patches |
| **offset-finder** | Standalone CLI tool for offset detection (pelite-based, no PDB required) |
| **patcher** | Shared library — PE parsing, x86/x64 disassembly, ARM64 `.pdata` function scanning, runtime pattern matching, 18 bytecode patches |
| **ARM64 support** | `aarch64-pc-windows-msvc` build target with experimental ARM64 `termwrap`/`umwrap`/`endpwrap` runtime patchers. Real Windows ARM64 validation is still required before calling it production-supported |
| **offset-finder** | Standalone CLI tool for x86/x64/ARM64 offset detection (pelite-based, no PDB required) |
| **rdprrap-installer** | Rust CLI installer/uninstaller — service registration, registry setup, firewall rules, cohort service restart, install-dir ACL hardening (replaces Delphi `RDPWInst.exe`) |
| **rdprrap-check** | RDP connection tester — loopback `127.0.0.2` via `mstsc.exe`, NLA guard RAII, 44 disconnect-reason codes (replaces `RDPCheck.exe`) |
| **rdprrap-conf** | Configuration GUI — native-windows-gui panel for diagnostics + runtime RDP settings (Enable/Port/SingleSession/HideUsers/AllowCustom/AuthMode/Shadow), replaces `RDPConf.exe` |
Expand All @@ -25,8 +26,8 @@ RDP Wrapper rewritten in Rust.
| Disassembler | [iced-x86](https://crates.io/crates/iced-x86) (pure Rust) |
| PE Parsing | [pelite](https://crates.io/crates/pelite) |
| Windows API | [windows-rs](https://crates.io/crates/windows) |
| Target | x86_64-pc-windows-msvc, i686-pc-windows-msvc |
| CI | GitHub Actions (Linux check + Windows x64/x86 build) |
| Target | x86_64-pc-windows-msvc, i686-pc-windows-msvc, aarch64-pc-windows-msvc |
| CI | GitHub Actions (Linux check + Windows x64/x86 build, ARM64 build/static artifact checks) |

## Quick Start

Expand All @@ -42,6 +43,7 @@ cd rdprrap

rustup target add x86_64-pc-windows-msvc
rustup target add i686-pc-windows-msvc
rustup target add aarch64-pc-windows-msvc

cargo build --release
```
Expand Down Expand Up @@ -72,7 +74,7 @@ Additional flags:
| Flag | Effect |
|------|--------|
| `--source DIR` | Directory to copy DLLs from (defaults to the installer's own directory) |
| `--force` | Reinstall even if ServiceDll already points to the wrapper |
| `--force` | Reinstall and replace existing wrapper DLLs even if ServiceDll already points to the wrapper |
| `--skip-firewall` | Do not add/remove firewall rules |
| `--skip-restart` | Do not restart TermService (apply changes manually or on reboot) |
| `--disable-nla` | Set `UserAuthentication=0` (opt-in, required for legacy clients) |
Expand Down Expand Up @@ -100,7 +102,8 @@ rdprrap/
│ │ ├── pe.rs # PE header/section/import/exception table parsing
│ │ ├── pattern.rs # 4-byte aligned string pattern matching in .rdata
│ │ ├── disasm.rs # iced-x86 decoder wrapper, xref search, branch helpers
│ │ └── patch.rs # WriteProcessMemory wrapper, NOP fill, 14 bytecode constants
│ │ ├── arm64.rs # ARM64 .pdata function scan + ADR/ADRP/ADD/BL helpers
│ │ └── patch.rs # WriteProcessMemory wrapper, NOP fill, 18 bytecode constants
│ ├── termwrap-dll/ # cdylib: termsrv.dll proxy (core RDP)
│ │ └── src/patches/ # DefPolicy, SingleUser, LocalOnly, NonRDP, PropertyDevice, SLPolicy
│ ├── umwrap-dll/ # cdylib: umrdp.dll proxy (USB/camera redirection)
Expand All @@ -110,7 +113,7 @@ rdprrap/
│ ├── rdprrap-check/ # Binary: RDP loopback tester (mstsc + NLA guard)
│ └── rdprrap-conf/ # Binary: configuration GUI (native-windows-gui)
├── .github/
│ └── workflows/ci.yml # Linux check + Windows x64/x86 build matrix
│ └── workflows/ci.yml # Linux check + Windows x64/x86 build matrix + ARM64 static checks
└── docs/ # Korean documentation
```

Expand All @@ -122,6 +125,7 @@ rdprrap/
4. Patch offsets found at runtime:
- **x64**: Scan `.rdata` for known strings → search exception table for LEA xrefs → backtrace unwind chains to function start
- **x86**: Scan `.text` for function prologues (`8B FF 55 8B EC`) → follow branches → match PUSH/MOV immediates to string RVAs
- **ARM64**: `.pdata` function ranges plus ADR/ADRP+ADD string references locate policy checks. `termwrap` patches ARM64 DefPolicy, SingleUser, LocalOnly, AppServer/NonRDP, PropertyDevice, and SL policy paths with ARM64-specific bytecodes; `umwrap` patches PnP/camera BL calls to `mov w0,#1`; `endpwrap` patches the referenced audio-capture function start to `mov w0,#1; ret`.

## Patch Types (termsrv.dll)

Expand All @@ -142,7 +146,7 @@ cargo clippy --all-targets -- -D warnings # Lint
cargo fmt --check # Format check
```

CI runs automatically on push/PR: Linux check + Windows x64/x86 full build.
CI runs automatically on push/PR: Linux check + Windows x64/x86 full build + ARM64 build/static artifact checks.

## Contributing

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Instead, please report them through [GitHub Security Advisories](https://github.
2. **Steps to Reproduce** — Detailed steps to reproduce the issue
3. **Impact** — The potential impact of the vulnerability
4. **Affected Components** — Which crates/DLLs of rdprrap are affected
5. **Environment** — Windows version, architecture (x64/x86), termsrv.dll version
5. **Environment** — Windows version, architecture (x64/x86/ARM64), termsrv.dll version

### Response Timeline

Expand Down
Loading
Loading