Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

5 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

VDI Bridge

Covert bidirectional data channel over remote desktop audio and keyboard

PowerShell Channel Modulation FEC Zero Dependencies


Transfers files, clipboard content, and arbitrary data between a remote desktop session and the host machine using audio encoding and keyboard injection. Bypasses clipboard restrictions, file transfer policies, and DLP controls.

Built for Citrix VDI (also works on any remote desktop environment that passes audio output to the host: VMware Horizon, RDP with audio redirection, Amazon WorkSpaces, etc.)


Architecture

 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚     Remote Desktop (VDI)    β”‚                        β”‚        Host Machine         β”‚
 β”‚                             β”‚                        β”‚                             β”‚
 β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β™« audio output      β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
 β”‚  β”‚  vdi.ps1              β”‚  β”‚ ════════════════════►  β”‚  β”‚  host.ps1             β”‚  β”‚
 β”‚  β”‚                       β”‚  β”‚   multi-carrier OFDM   β”‚  β”‚                       β”‚  β”‚
 β”‚  β”‚  AudioEngine encodes  β”‚  β”‚   80 carriers, stereo  β”‚  β”‚  WASAPI loopback      β”‚  β”‚
 β”‚  β”‚  data as sound        β”‚  β”‚                        β”‚  β”‚  FFT demodulation     β”‚  β”‚
 β”‚  β”‚                       β”‚  β”‚                        β”‚  β”‚                       β”‚  β”‚
 β”‚  β”‚  stdin reads injected β”‚  β”‚    ⌨ keyboard input   β”‚  β”‚  SendInput injects    β”‚  β”‚
 β”‚  β”‚  keystrokes           β”‚  β”‚ ◄════════════════════  β”‚  β”‚  into remote window   β”‚  β”‚
 β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚   base64 over keybd    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Remote desktop environments block direct file transfer and clipboard sharing as a security control. But two channels are always open:

Channel Direction Method Speed
Audio output VDI β†’ HOST Sound plays on host speakers/loopback ~1-4 KB/s
Keyboard input HOST β†’ VDI Keystrokes reach the remote session ~200-400 B/s

VDI Bridge encodes data into these channels. No microphone, no network, no additional software.


Signal Processing Pipeline

                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                          β”‚              TRANSMIT (VDI side)                β”‚
                          β”‚                                                 β”‚
  raw data ──► GZip ──► RS(255,191) ──► Interleave ──► OFDM Modulate ──► WAV
                          β”‚          (16-deep)        (80 carriers)         β”‚
                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                              β”‚
                                    β™« Citrix audio codec β™«
                                              β”‚
                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                          β”‚              RECEIVE (HOST side)                β”‚
                          β”‚                                                 β”‚
  WAV ──► WASAPI ──► FFT ──► Demodulate ──► Deinterleave ──► RS Decode ──► GZip ──► data
                          β”‚   (per symbol)                                  β”‚
                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Audio Protocol

Carrier Layout

  250 Hz          600   700   800   900  ...  8400  8500 Hz
    β”‚               β”‚     β”‚     β”‚     β”‚         β”‚     β”‚
    β–Ό               β–Ό     β–Ό     β–Ό     β–Ό         β–Ό     β–Ό
  β”Œβ”€β”€β”€β”€β”          β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β”    β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β”
  β”‚SYNCβ”‚          β”‚ C0β”‚ β”‚ C1β”‚ β”‚ C2β”‚ β”‚ C3β”‚ ...β”‚C79β”‚ β”‚C80β”‚      350 Hz
  β”‚toneβ”‚          β”‚   β”‚ β”‚   β”‚ β”‚   β”‚ β”‚   β”‚    β”‚   β”‚ β”‚   β”‚      β”Œβ”€β”€β”€β”€β”
  β”‚40msβ”‚          β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜    β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜      β”‚END β”‚
  β””β”€β”€β”€β”€β”˜          ◄────── 100 Hz spacing ──────►              β”‚toneβ”‚
                  ◄──── 80 data carriers ─────►               β””β”€β”€β”€β”€β”˜
Parameter Value
Sample rate 48,000 Hz
Data carriers 80 (stereo bulk) / 16 (mono control)
Carrier range 600 - 8,500 Hz
Carrier spacing 100 Hz
Symbol duration 10 ms (480 samples)
Sync tone 250 Hz, 40 ms
End marker 350 Hz, 20 ms

The 600-8500 Hz range sits inside the speech band that remote desktop audio codecs preserve. Below 600 Hz most codecs apply high-pass filtering. The 100 Hz spacing provides enough spectral separation for reliable FFT bin extraction through lossy compression.

Frame Structure

  β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€ ─ ─┬──────┐ β”Œβ”€β”€β”€β”€β”€β”
  β”‚ SYNC β”‚ β”‚gapβ”‚ β”‚ REF β”‚ β”‚ REF β”‚ β”‚ SYM  β”‚ SYM  β”‚ SYM  β”‚     β”‚ SYM  β”‚ β”‚ END β”‚
  β”‚250 Hzβ”‚ β”‚   β”‚ β”‚  1  β”‚ β”‚  2  β”‚ β”‚  0   β”‚  1   β”‚  2   β”‚ ... β”‚  N   β”‚ β”‚350Hzβ”‚
  β”‚ 40ms β”‚ β”‚   β”‚ β”‚10ms β”‚ β”‚10ms β”‚ β”‚ 10ms β”‚ 10ms β”‚ 10ms β”‚     β”‚ 10ms β”‚ β”‚20ms β”‚
  β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”˜ └──────┴──────┴──────┴─ ─ β”€β”΄β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”˜
                  β–²               β–²                                    β–²
                  β”‚               β”‚                                    β”‚
           phase/amplitude   data payload                        transmission
            reference        (all carriers                         complete
                              in parallel)

Sync detection: Sliding-window Goertzel filter on 250 Hz. Fires when energy crosses the threshold.

Reference symbols: Two known-phase symbols establish baselines for coherent demodulation. Initial phase per carrier: (index % 4) * Ο€/2.

Pilot insertion: Every 200 data symbols, a pilot recalibrates phase drift from the codec's adaptive processing.

Modulation Schemes

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                    bits per carrier per symbol                      β”‚
  β”‚                                                                     β”‚
  β”‚  OOK      β–  β–‘        1 bit     ~1.0 KB/s    most reliable           β”‚
  β”‚  DQPSK    β–  β–  β–‘ β–‘    2 bits    ~2.0 KB/s    good conditions         β”‚
  β”‚  4-ASK    β–  β–  β–‘ β–‘    2 bits    ~2.0 KB/s    amplitude keying        β”‚
  β”‚  D8PSK    β–  β–  β–  β–‘    3 bits    ~3.0 KB/s    high quality path       β”‚
  β”‚  16-QAM   β–  β–  β–  β–     4 bits    ~4.0 KB/s    near-lossless only      β”‚
  β”‚                                                                     β”‚
  β”‚  ◄── reliable ──────────────────────────── fast ──►                 β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

OOK (On-Off Keying) is the default. Carrier present = 1, absent = 0. Survives amplitude distortion and spectral reshaping from lossy codecs.

DQPSK (Differential QPSK) encodes 2 bits in the phase delta between symbols. Differential encoding avoids needing absolute phase reference, which the audio path destroys.

D8PSK / 16-QAM pack 3-4 bits per carrier. Use .\vdi.ps1 test to find the ceiling for your environment.

Stereo Encoding

File transfers use stereo audio. Left and right channels carry independent data streams, doubling throughput. The WASAPI loopback on the host captures and demodulates both channels separately.

Control messages (ping, clipboard commands) use mono with 16 carriers for maximum reliability per carrier.


Error Correction

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚         Reed-Solomon RS(255, 191)        β”‚
  β”‚                                          β”‚
  β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
  β”‚   β”‚    191 data bytes  β”‚ 64 parity    β”‚  β”‚
  β”‚   β”‚                    β”‚   bytes      β”‚  β”‚
  β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
  β”‚                                          β”‚
  β”‚   Corrects up to 32 byte errors/block    β”‚
  β”‚   16-deep interleaving across blocks     β”‚
  β”‚   ~33% overhead                          β”‚
  β”‚   GF(2^8) with polynomial 0x11D          β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The interleaver spreads consecutive bytes across 16 RS blocks. A burst error (dropped audio packet, codec glitch across several symbols) becomes scattered single-byte errors across multiple blocks. RS handles those easily.

SHA-256 checksum (first 4 bytes) is transmitted in the header before bulk data. The receiver verifies after FEC decoding.


Operations

Ping

Verifies the full bidirectional channel.

  VDI                          HOST
   β”‚                            β”‚
   │──── PING (16-carrier) ────►│
   β”‚                            β”‚ decode audio
   │◄──── PONG (keyboard) ──────│
   β”‚                            β”‚
   βœ“ channel OK
.\vdi.ps1 ping

Clipboard Transfer

  clip-out: VDI clipboard ──♫──► HOST clipboard
  clip-in:  VDI clipboard β—„β”€β”€βŒ¨β”€β”€ HOST clipboard

Small text (<44 chars) goes over mono control. Larger payloads and files use stereo bulk with FEC.

.\vdi.ps1 clip-out          # VDI clipboard β†’ HOST
.\vdi.ps1 clip-in           # HOST clipboard β†’ VDI

File Transfer

  file-out: VDI file ──[compress]──[FEC]──[modulate]──♫──► HOST saves file
  file-in:  VDI file ◄──[decode]──[base64]β”€β”€βŒ¨β”€β”€ HOST reads file
.\vdi.ps1 file-out C:\path\to\file.pdf                    # default OOK
.\vdi.ps1 file-out report.docx -Modulation DQPSK          # 2x speed
.\vdi.ps1 file-out data.xlsx -Modulation 16QAM            # 4x speed (clean audio only)
.\vdi.ps1 file-in C:\hostpath\tool.exe C:\local\tool.exe  # HOST β†’ VDI

Audio Diagnostics

Frequency sweep, amplitude sweep, phase sweep, carrier count, symbol rate, and modulation tests. Characterizes the actual audio path to determine max reliable modulation.

.\vdi.ps1 test

Throughput

Operation Direction Channel Typical Speed
File out VDI β†’ HOST 80-carrier stereo audio ~600-900 B/s (OOK)
File out VDI β†’ HOST 80-carrier stereo audio ~2.4-3.6 KB/s (16QAM)
File in HOST β†’ VDI Keyboard injection ~200-400 B/s
Clipboard Either Auto Near-instant (small text)
  100 KB file transfer time:
  
  OOK   β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β–   ~2 min
  DQPSK β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β– β–                       ~1 min
  D8PSK β– β– β– β– β– β– β– β– β– β– β– β– β–                              ~40 sec
  16QAM β– β– β– β– β– β– β– β– β– β–                                 ~30 sec

Setup

Requirements

  • Windows (both host and remote session)
  • PowerShell 5.1+ (built into Windows)
  • Remote desktop client with audio redirection enabled
  • Zero external dependencies

Running

Host machine (where the remote desktop client runs):

.\host.ps1                        # start listening
.\host.ps1 -Local                 # local test mode (no remote desktop)
.\host.ps1 -Modulation DQPSK     # match VDI modulation

Inside the remote session:

.\vdi.ps1 ping                    # verify channel
.\vdi.ps1 clip-out                # send clipboard to host
.\vdi.ps1 file-out secret.pdf     # send file to host

Start host.ps1 first. It is passive and responds to commands from the VDI side.


Detection Surface

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                     What's detectable                        β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚  Audible signal      Mute speakers / use headphones to       β”‚
  β”‚                      eliminate. WASAPI loopback still works. β”‚
  β”‚                                                              β”‚
  β”‚  Session recording   Citrix/RDP recordings capture the       β”‚
  β”‚                      audio if session recording is enabled.  β”‚
  β”‚                                                              β”‚
  β”‚  Endpoint telemetry  PowerShell + WASAPI COM calls on host.  β”‚
  β”‚                      Pure PowerShell on VDI, no binaries.    β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚                     What's NOT detectable                    β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚  Network traffic     None. Data moves over audio + keyboard. β”‚
  β”‚  Clipboard logs      Clipboard API is local on each side.    β”‚
  β”‚  File transfer logs  No shared drives or mapped paths used.  β”‚
  β”‚  Process injection   No DLL injection, hollowing, or hooks.  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Limitations

  • Throughput is bounded by what the remote desktop audio codec preserves. Aggressive low-bandwidth audio policies reduce the usable frequency range.
  • Keyboard injection needs the remote desktop window focused. If another window steals focus during HOSTβ†’VDI transfer, injection goes to the wrong target.
  • Files >1 MB work but take time. This tool targets specific file exfiltration, not bulk transfer.
  • The test diagnostic suite helps find the performance ceiling for a given environment.

About

Covert bidirectional data channel over remote desktop audio and keyboard

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages