13 KiB
How to Add Support for New Windows Builds
This guide explains the technical process for reverse engineering new Windows builds to extract the necessary RDP Wrapper configuration parameters.
Overview
When Microsoft releases new Windows updates, the termsrv.dll file changes, and RDP Wrapper needs updated offset configurations to function properly. This document covers two paths:
- Quick path — RDPWrapOffsetFinder (recommended): the compiled tool bundled in every release can generate a valid INI section in seconds on most builds.
- Manual path — disassembly and reverse engineering: used when the automated tool cannot find all offsets for a new or unusual build.
Quick path: RDPWrapOffsetFinder (automated)
TL;DR — run the tool, copy the generated INI block, open a PR.
RDPWrapOffsetFinder (by llccd, bundled in every release as RDPWrapOffsetFinder_x64.exe and RDPWrapOffsetFinder_x86.exe) performs automated static analysis on the installed termsrv.dll and emits a ready-to-use INI section.
Step A: Run RDPWrapOffsetFinder
# Locate termsrv.dll version (so you know what tag to add)
(Get-Item C:\Windows\System32\termsrv.dll).VersionInfo.ProductVersion
# Run the tool (run as Administrator; it reads the locked DLL via sharing flags)
.\RDPWrapOffsetFinder_x64.exe
On success the tool prints an INI block similar to:
[10.0.26100.3915]
SingleUserPatch.x64=1
SingleUserOffset.x64=0A1B2C
...
If the tool exits with Unsupported version, try the nosym (no-symbols) variant:
.\RDPWrapOffsetFinder_nosym_x64.exe
Step B: Use the autoupdate helper (optional)
The autoupdate/ directory in src-csharp/RDPOffsetFinder/ contains a convenience script that wraps the tool, appends the output to a local copy of rdpwrap.ini, and validates the result:
cd src-csharp\RDPOffsetFinder\RDPWrapOffsetFinder\autoupdate
autoupdate.bat
The script will:
- Run
RDPWrapOffsetFinder.exeagainst the current system'stermsrv.dll. - Merge the new section into
rdpwrap_template.ini. - Print the diff so you can review before committing.
Step C: Validate the generated block
Before opening a pull request:
# Quick sanity check — restart Terminal Services with the new INI
net stop TermService
Copy-Item .\rdpwrap_updated.ini "C:\Program Files\RDP Wrapper\rdpwrap.ini"
net start TermService
# Visual validation
.\RDPCheck.exe # should show "Installed", "Listening", and "Supported"
Step D: Submit a pull request
- Add (or update) the version block in
msi/rdpwrap.ini. - Commit with message:
ini: add support for 10.0.XXXXX.YYYY. - Open a pull request against the
mainbranch using the bug_report template.
Include: the output ofRDPWrapOffsetFinder.exe, yourtermsrv.dllversion, and a screenshot ofRDPCheck.exeshowing "Supported".
Security note: Never attach the actual
termsrv.dllbinary — this would infringe Microsoft's licence. The version string and SHA-256 hash are sufficient for reproducibility.
Manual path: disassembly and reverse engineering
Use this path when:
- RDPWrapOffsetFinder cannot find all offsets (prints partial output or crashes), or
- You want to understand why a patch works, not just where to apply it.
Prerequisites
Required Tools
Disassemblers (Choose one):
- Ghidra (Free, recommended) - NSA's reverse engineering tool
- IDA Pro (Commercial) - Industry standard
- x64dbg (Free) - Good for dynamic analysis
- Radare2 (Free) - Command-line focused
Supporting Tools:
- HxD or similar hex editor
- PE Explorer - For PE structure analysis
- Process Monitor - Runtime file/registry monitoring
- API Monitor - Function call tracing
- RDPCheck.exe - For testing configurations
Required Knowledge
- Assembly language (x86/x64)
- PE file format basics
- Windows API understanding
- Basic cryptography concepts
Step 1: Obtain the Target File
Extract termsrv.dll
# Navigate to System32 directory
cd C:\Windows\System32
# Copy termsrv.dll to analysis directory
copy termsrv.dll C:\Analysis\termsrv.dll
# Get file version information
Get-ItemProperty C:\Analysis\termsrv.dll | Select-Object VersionInfo
Determine Version Number
# PowerShell method
(Get-Item C:\Analysis\termsrv.dll).VersionInfo.ProductVersion
# Alternative: WMIC method
wmic datafile where name="C:\\Windows\\System32\\termsrv.dll" get Version
The version format will be: 10.0.XXXXX.YYYY (e.g., 10.0.26100.7623)
Step 2: Initial Analysis
Load in Disassembler
- Open termsrv.dll in your chosen disassembler
- Let it complete initial analysis (auto-analysis)
- Examine the import table for key functions
- Identify the main code sections
Key Function Identification
Search for these critical functions that RDP Wrapper needs to patch:
CSessionArbitrationHelper::IsSingleSessionPerUserEnabledCDefPolicy::QueryCEnforcementCore::GetInstanceOfTSLicenseCSLQuery::Initialize
Step 3: Finding Function Offsets
Method 1: String Reference Analysis
1. Search for relevant strings:
- "Terminal Services"
- "Session"
- "Licence"
- "Policy"
- Error messages related to licensing
2. Follow cross-references from strings to functions
3. Analyse the functions that reference these strings
Method 2: Import Table Analysis
1. Examine imported functions:
- GetTokenInformation
- WinStationQueryInformationW
- RegQueryValueExW
- Licence-related APIs
2. Find functions that call these imports
3. Trace backwards to find policy validation logic
Method 3: Pattern Matching
Look for specific assembly patterns that indicate the functions we need to patch:
Single User Patch Pattern
; Look for patterns like:
BB 01 00 00 00 ; mov ebx, 1 (single session enabled)
; Or:
B8 01 00 00 00 ; mov eax, 1
DefPolicy Patch Pattern
; Look for licence policy validation:
B8 01 00 00 00 ; mov eax, 1 (policy result)
89 81 38 06 00 00 ; mov [rcx+638h], eax (store result)
; Or similar patterns with different registers
Step 4: Extracting Configuration Parameters
Single User Offset
- Find
CSessionArbitrationHelper::IsSingleSessionPerUserEnabled - Look for the instruction that returns 1 (single session restriction)
- Note the file offset of this instruction
- The patch will change this to return 0 (allow multiple sessions)
DefPolicy Offset
- Find
CDefPolicy::Query - Look for licence validation logic
- Find where it sets the result to indicate "licenced"
- Note the offset for the instruction to patch
LocalOnly Offset
- Find
CEnforcementCore::GetInstanceOfTSLicense - Look for local connection restrictions
- Find the jump/conditional that enforces local-only policy
- Note the offset to patch this restriction
SLInit Parameters
- Find
CSLQuery::Initialize - Analyse the data structure it initializes
- Find the memory offsets for these fields:
bInitializedbServerSkulMaxUserSessionsbAppServerAllowedbRemoteConnAllowedbMultimonAllowedulMaxDebugSessionsbFUSEnabled
Step 5: Creating the Configuration
Basic INI Structure
[10.0.XXXXX.YYYY]
; Single user session patch
SingleUserPatch.x64=1
SingleUserOffset.x64=OFFSET_HEX
SingleUserCode.x64=PATCH_CODE
; Licence policy patch
DefPolicyPatch.x64=1
DefPolicyOffset.x64=OFFSET_HEX
DefPolicyCode.x64=PATCH_CODE
; Local-only restriction patch
LocalOnlyPatch.x64=1
LocalOnlyOffset.x64=OFFSET_HEX
LocalOnlyCode.x64=PATCH_CODE
; Software licensing hook
SLInitHook.x64=1
SLInitOffset.x64=OFFSET_HEX
SLInitFunc.x64=New_CSLQuery_Initialize
[10.0.XXXXX.YYYY-SLInit]
bServerSku.x64=OFFSET_HEX
bRemoteConnAllowed.x64=OFFSET_HEX
bFUSEnabled.x64=OFFSET_HEX
bAppServerAllowed.x64=OFFSET_HEX
bMultimonAllowed.x64=OFFSET_HEX
lMaxUserSessions.x64=OFFSET_HEX
ulMaxDebugSessions.x64=OFFSET_HEX
bInitialized.x64=OFFSET_HEX
Common Patch Codes
; Available patch codes (defined in [PatchCodes] section):
Zero=00 ; Set to zero
nop=90 ; No operation
jmpshort=EB ; Short jump
mov_eax_1_nop_2=B8010000009090 ; mov eax,1 + 2 NOPs
CDefPolicy_Query_eax_rcx_jmp=B80001000089813806000090EB ; Policy bypass
Step 6: Testing and Validation
Initial Testing
- Create a test INI file with your calculated offsets
- Back up the original rdpwrap.ini
- Replace with your test configuration
- Restart Terminal Services
- Run RDPCheck.exe to verify status
Dynamic Analysis
- Use x64dbg to attach to the running termsrv.exe process
- Set breakpoints at your calculated offsets
- Verify that your patches are being applied correctly
- Monitor for any crashes or unexpected behaviour
Validation Steps
# Stop Terminal Services
net stop TermService
# Apply new configuration
copy test_rdpwrap.ini C:\Program Files\RDP Wrapper\rdpwrap.ini
# Start Terminal Services
net start TermService
# Test with RDPCheck
RDPCheck.exe
# Test actual RDP connection
mstsc /v:localhost
Step 7: Documentation and Sharing
Document Your Findings
Create a detailed report including:
- Windows build version and SHA256 of termsrv.dll
- Methodology used
- Specific offsets found
- Testing results
- Any challenges encountered
Share with Community
- Post your configuration in a GitHub issue
- Include the termsrv.dll file (zipped) for verification
- Provide testing evidence (screenshots from RDPCheck)
- Document any system-specific requirements
Common Challenges
Address Space Layout Randomization (ASLR)
Modern Windows uses ASLR, but the relative offsets within the DLL remain constant. Always work with file offsets, not memory addresses.
Compiler Optimisations
Microsoft's compiler optimisations can:
- Inline functions
- Reorder code
- Change calling conventions
- Merge similar functions
Code Signing
Windows verifies code signatures, so:
- Patches must be applied at runtime, not to the file
- Use the RDP Wrapper's hooking mechanism
- Never modify the original termsrv.dll
Function Variations
The same logical function might be implemented differently across builds:
- Different assembly patterns
- Different register usage
- Inlined vs separate functions
Advanced Techniques
Comparative Analysis
When analysing a new build:
- Compare with a known working build
- Look for similar patterns and structures
- Use diff tools on disassembled code
Automated Pattern Detection
Some community members have created scripts to:
- Search for common assembly patterns
- Compare function signatures
- Suggest likely offset candidates
Binary Diffing
Tools like BinDiff can help identify:
- Changed functions between builds
- Similar code blocks
- Function renaming/reorganisation
Community Resources
Trusted Contributors
Community members known for accurate analysis:
- @Fabliv - Consistently provides verified configurations
- @sebaxakerhtc - Regular contributor with detailed analysis
- @maxpiva - Historical configurations and tools
Useful Repositories
- Main project:
stascorp/rdpwrap - Community tools: Various forks with analysis scripts
- Configuration databases: Community-maintained INI collections
Contributing Your Analysis
GitHub Issue Format
When posting a new configuration:
## Windows Build: 10.0.XXXXX.YYYY
**System Information:**
- Edition: Windows 11 Pro/Home/Enterprise
- Architecture: x64
- Installation: Clean/Update from X.X.X
**Analysis Results:**
[Paste your INI configuration here]
**Verification:**
- ✅ RDPCheck shows "Installed" and "Listening"
- ✅ Multiple simultaneous connections tested
- ✅ No crashes or stability issues
**Files:**
[Attach termsrv.dll.zip]
Testing by Others
Before a configuration is accepted:
- Multiple community members should test
- Verify on different system configurations
- Confirm no regressions on existing functionality
- Test edge cases (different user accounts, domain environments)
Conclusion
Adding support for new Windows builds requires:
- Technical reverse engineering skills
- Patience for trial-and-error testing
- Community collaboration for verification
- Detailed documentation for maintainability
While this process cannot be easily automated due to Microsoft's security measures and varying compilation patterns, the community has developed efficient workflows that typically produce working configurations within days of new Windows releases.
The key to success is methodical analysis, thorough testing, and collaboration with the experienced community members who have mastered this process.