nightshift: why-annotator — Code Purpose Analysis
Repo: Microck/veyoff
Date: 2026-04-25
File: src/windows/veyoff-windows.cpp (2301 lines, single-file Windows C++20 application)
Summary
Veyoff is a Veyon screen-surveillance MITM proxy for Windows 10+. It intercepts RFB (VNC) traffic between a teacher's Veyon master and the local UltraVNC server, allowing a student to freeze or manipulate what the teacher sees. The entire application lives in a single 2301-line .cpp file with no external dependencies beyond Win32 APIs.
This analysis annotates why each major section exists and what design decisions it encodes.
Section-by-Section Annotation
1. Architecture Header (Lines 1-21)
Why this exists: The file opens with an ASCII architecture diagram showing the 3-hop proxy chain:
Veyon VncProxyServer → VEYOFF PROXY → Real UltraVNC
This is critical because veyoff operates as a transparent MITM — the Veyon service has no idea it's connecting to a proxy instead of the real VNC server. The comment block explains the automated setup sequence (registry redirect + service restart) that makes this work without user configuration.
Design decision: Everything in one file because this is a single-purpose binary that needs to be easily compiled on Windows with MinGW. No build system complexity beyond CMake.
2. Constants & Configuration (Lines 69-100)
Why kPortOffset = 50: The proxy needs to move the real VNC server to a different port. The offset of 50 was chosen to be far enough from the base port (11200) to avoid collisions with other sessions (Veyon uses sequential ports per session: 11200, 11201, ...). Adding 50 gives room for 50 concurrent sessions before overlap.
Why kPanicPressesRequired = 5 in kPanicWindowMs = 2000: The panic button (Ctrl+Alt+X) requires 5 presses within 2 seconds. This is deliberately hard to trigger accidentally — it launches the self-destruct sequence which deletes the executable, clears event logs, and removes prefetch files. The high threshold prevents a teacher watching remotely from seeing a student accidentally nuke the tool.
Why three overlay outline colors: The overlay indicators (amber/red/blue) correspond to presence levels: kNone, kAppOpen, kViewing, kFrozen. This lets a student know at a glance whether the teacher is actively watching, which affects behavior (e.g., a blue outline might mean "teacher connected but not viewing your screen"). The colors are configurable via INI settings because different wallpaper/monitor combinations make different colors more or less visible.
3. Utility Functions (Lines 136-215)
Why readU16/readU32/writeU16/writeU32 are manual: These implement big-endian byte ordering for the RFB protocol. RFB (Remote Framebuffer) is big-endian on the wire, while x86 is little-endian. Rather than using htonl/ntohl (which would work but obscure intent), custom functions make the protocol conversion explicit.
Why trySetWindowDisplayAffinity is forward-declared (line 214): This function uses SetWindowDisplayAffinity with WDA_EXCLUDEFROMCAPTURE to hide the overlay window from screen captures. The forward declaration exists because it's defined later but referenced in the blacklist window enumeration, which runs before the overlay section. WDA_EXCLUDEFROMCAPTURE (0x11) is conditionally defined (line 52) because older SDKs don't have it — it was added in Windows 10 2004.
4. Blacklist System (Lines 243-360)
Why a text-file blacklist: Window blacklisting (hiding specific app windows from the teacher's view) uses a simple line-delimited text file instead of a more structured format. This is deliberate — a student needs to be able to edit it quickly (the tray menu has "Edit Blacklist..." which opens Notepad). The file is watched for changes (via last_write_time polling) so edits take effect immediately without restart.
Why titleMatchesBlacklist uses case-insensitive substring matching: Window titles change frequently (e.g., "Discord - General" vs "discord - general"). Exact matching would be brittle. The *wildcard* pattern is implemented via find() — if the blacklist entry contains *, it splits on * and checks all parts exist in the title. Otherwise it's a simple substring check.
Why WindowMask stores a rect instead of just a title: When blacklisted windows are captured by the proxy (for the "keep everything except blacklisted windows" mode), the code needs to know the pixel region to blank out. It enumerates windows, gets their rects via GetWindowRect, and stores both the title match and the screen coordinates.
5. Screen Capture (Lines 362-458)
Why captureScreen uses GDI instead of DXGI: The capture function uses BitBlt from GDI rather than the newer DXGI Desktop Duplication API. This is because veyoff needs to capture the screen with specific windows excluded (via WDA_EXCLUDEFROMCAPTURE). DXGI captures what's on the display hardware, which respects SetWindowDisplayAffinity. GDI BitBlt can optionally include/exclude layered windows. The GDI approach gives more control over what's captured.
Why preserveRectsFromPreviousFrame exists: When a window is blacklisted, the proxy needs to show the teacher the screen without that window. It captures the real screen, then blanks out the blacklisted window regions. But if the blacklisted window moved, the previous frame's pixels would be stale. This function copies valid regions from the previous frame to avoid showing garbage pixels in areas where the blacklisted window used to be.
6. Veyon Registry & Service Management (Lines 459-580)
Why the registry redirect is necessary: Veyon hardcodes its VNC server port in the registry at HKLM\SOFTWARE\Veyon Solutions\Veyon\Network\VncServerPort. The Veyon service reads this on startup and connects its VncProxyServer to this port. Veyoff changes this port value to originalPort + 50, restarts the service, and then binds its own proxy on the original port. The Veyon service now connects to veyoff instead of the real VNC server.
Why waitForServiceState has a timeout: Windows service state transitions are asynchronous and can take time (especially SERVICE_STOPPED → SERVICE_RUNNING). The timeout prevents hangs if the service gets stuck in a transitional state.
Why cleanup happens on exit (normal flow) AND self-destruct (nuclear flow): Normal cleanup (restore registry + restart service) is the expected path. Self-destruct is the emergency path — it does cleanup PLUS removes all traces of veyoff from the system (event logs, prefetch, executable). The dual-path design means that even if the process crashes without cleanup, the registry value still points to the redirected port, which means Veyon continues working (just connecting to port+50 directly). Only a deliberate exit restores the original state.
7. RFB Proxy Core (Lines 587-760)
Why SharedState uses std::mutex instead of std::shared_mutex: The shared state (freeze flag, blacklist, framebuffers) is accessed by multiple threads (proxy threads, UI thread, overlay thread). A simple mutex is used instead of a read-write lock because all accesses are short (copy a flag, swap a pointer) — the overhead of shared_mutex wouldn't be worth it for critical sections this small.
Why RfbPixelFormat is a packed struct: The RFB protocol sends pixel format as a 16-byte fixed-layout structure. The struct mirrors the wire format exactly, with manual packing. This avoids serialization/deserialization overhead — the struct can be reinterpret_cast from the network buffer directly.
8. Proxy Session Management (Lines 761-920)
Why ProxySession stores two sockets: Each proxy session has a "client" socket (Veyon's connection to veyoff) and a "server" socket (veyoff's connection to the real UltraVNC). Two threads per session: serverThread reads from UltraVNC → processes → writes to Veyon, clientThread reads from Veyon → processes → writes to UltraVNC. The bidirectional proxy needs two threads because RFB has no request-response framing — both sides send messages independently.
Why readAndMaybeForward has a forward flag: In freeze mode, the proxy still needs to read server messages (to keep the TCP buffer from filling up) but should not forward them to the client. The forward flag enables this drain-without-forward behavior.
Why handleServerFramebufferUpdate is the most complex function: This is where the MITM magic happens. When the real VNC server sends a framebuffer update, veyoff intercepts it. In normal mode, it forwards it. In freeze mode, it discards it (the client sees the frozen frame). In blacklist mode, it captures the screen, blanks out blacklisted windows, and sends the modified frame instead. This function handles all three modes plus frame size changes, encoding negotiation, and copy-rect deduplication.
9. System Tray & Hotkeys (Lines 1400-1570)
Why the tray menu has "Self-Destruct" and "Quit" as separate options: "Quit" does normal cleanup (restore registry, restart service) and exits. "Self-Destruct" does cleanup PLUS removes all traces. The student needs both options — "Quit" for end-of-class (teacher expects normal service restoration), "Self-Destruct" for emergency situations (teacher is investigating).
Why hotkeys use MOD_NOREPEAT: Without MOD_NOREPEAT, holding Ctrl+Alt+F would fire multiple freeze/unfreeze toggles, causing flickering. The flag ensures each key press registers exactly once.
10. Self-Destruct Sequence (Lines 1476-1570)
Why it clears event logs AND prefetch: Windows records process execution in two places: the Event Log (Security log logs process creation with audit policies) and Prefetch files (C:\Windows\Prefetch\*.pf, which contain execution timestamps and frequency). Both are cleared to minimize forensic evidence.
Why it uses a batch file for self-deletion: A process cannot delete its own executable while running (Windows locks the file). The batch file approach works because cmd.exe is a separate process that polls until veyoff exits, then deletes everything. Using ping localhost as a sleep is a well-known Windows batch trick — timeout requires user interaction in some Windows versions, but ping always works.
Cross-Cutting Observations
Why everything is in an anonymous namespace: The entire implementation is in an anonymous namespace (namespace { ... }), making all symbols internal linkage. This is a C++ best practice for single-TU applications — it prevents symbol conflicts with Windows headers and eliminates any concern about symbol visibility.
Why there's no error recovery: The code uses a "fail and die" approach — if sendAll() fails, the proxy session ends. This is intentional: a broken connection means the teacher sees a disconnect, which is less suspicious than a frozen screen that doesn't respond to input. The Veyon service will attempt to reconnect, which creates a new proxy session.
Why FrameBuffer stores raw pixels instead of an image format: The proxy needs to modify individual pixels (blank out blacklisted windows, composite frozen frames). Working with raw RGBA pixels is the most efficient approach — no encode/decode overhead for each intercepted frame.
Generated by nightshift — why-annotator analysis
nightshift: why-annotator — Code Purpose Analysis
Repo: Microck/veyoff
Date: 2026-04-25
File:
src/windows/veyoff-windows.cpp(2301 lines, single-file Windows C++20 application)Summary
Veyoff is a Veyon screen-surveillance MITM proxy for Windows 10+. It intercepts RFB (VNC) traffic between a teacher's Veyon master and the local UltraVNC server, allowing a student to freeze or manipulate what the teacher sees. The entire application lives in a single 2301-line
.cppfile with no external dependencies beyond Win32 APIs.This analysis annotates why each major section exists and what design decisions it encodes.
Section-by-Section Annotation
1. Architecture Header (Lines 1-21)
Why this exists: The file opens with an ASCII architecture diagram showing the 3-hop proxy chain:
This is critical because veyoff operates as a transparent MITM — the Veyon service has no idea it's connecting to a proxy instead of the real VNC server. The comment block explains the automated setup sequence (registry redirect + service restart) that makes this work without user configuration.
Design decision: Everything in one file because this is a single-purpose binary that needs to be easily compiled on Windows with MinGW. No build system complexity beyond CMake.
2. Constants & Configuration (Lines 69-100)
Why
kPortOffset = 50: The proxy needs to move the real VNC server to a different port. The offset of 50 was chosen to be far enough from the base port (11200) to avoid collisions with other sessions (Veyon uses sequential ports per session: 11200, 11201, ...). Adding 50 gives room for 50 concurrent sessions before overlap.Why
kPanicPressesRequired = 5inkPanicWindowMs = 2000: The panic button (Ctrl+Alt+X) requires 5 presses within 2 seconds. This is deliberately hard to trigger accidentally — it launches the self-destruct sequence which deletes the executable, clears event logs, and removes prefetch files. The high threshold prevents a teacher watching remotely from seeing a student accidentally nuke the tool.Why three overlay outline colors: The overlay indicators (amber/red/blue) correspond to presence levels:
kNone,kAppOpen,kViewing,kFrozen. This lets a student know at a glance whether the teacher is actively watching, which affects behavior (e.g., a blue outline might mean "teacher connected but not viewing your screen"). The colors are configurable via INI settings because different wallpaper/monitor combinations make different colors more or less visible.3. Utility Functions (Lines 136-215)
Why
readU16/readU32/writeU16/writeU32are manual: These implement big-endian byte ordering for the RFB protocol. RFB (Remote Framebuffer) is big-endian on the wire, while x86 is little-endian. Rather than usinghtonl/ntohl(which would work but obscure intent), custom functions make the protocol conversion explicit.Why
trySetWindowDisplayAffinityis forward-declared (line 214): This function usesSetWindowDisplayAffinitywithWDA_EXCLUDEFROMCAPTUREto hide the overlay window from screen captures. The forward declaration exists because it's defined later but referenced in the blacklist window enumeration, which runs before the overlay section.WDA_EXCLUDEFROMCAPTURE(0x11) is conditionally defined (line 52) because older SDKs don't have it — it was added in Windows 10 2004.4. Blacklist System (Lines 243-360)
Why a text-file blacklist: Window blacklisting (hiding specific app windows from the teacher's view) uses a simple line-delimited text file instead of a more structured format. This is deliberate — a student needs to be able to edit it quickly (the tray menu has "Edit Blacklist..." which opens Notepad). The file is watched for changes (via
last_write_timepolling) so edits take effect immediately without restart.Why
titleMatchesBlacklistuses case-insensitive substring matching: Window titles change frequently (e.g., "Discord - General" vs "discord - general"). Exact matching would be brittle. The*wildcard*pattern is implemented viafind()— if the blacklist entry contains*, it splits on*and checks all parts exist in the title. Otherwise it's a simple substring check.Why
WindowMaskstores a rect instead of just a title: When blacklisted windows are captured by the proxy (for the "keep everything except blacklisted windows" mode), the code needs to know the pixel region to blank out. It enumerates windows, gets their rects viaGetWindowRect, and stores both the title match and the screen coordinates.5. Screen Capture (Lines 362-458)
Why
captureScreenuses GDI instead of DXGI: The capture function usesBitBltfrom GDI rather than the newer DXGI Desktop Duplication API. This is because veyoff needs to capture the screen with specific windows excluded (viaWDA_EXCLUDEFROMCAPTURE). DXGI captures what's on the display hardware, which respectsSetWindowDisplayAffinity. GDIBitBltcan optionally include/exclude layered windows. The GDI approach gives more control over what's captured.Why
preserveRectsFromPreviousFrameexists: When a window is blacklisted, the proxy needs to show the teacher the screen without that window. It captures the real screen, then blanks out the blacklisted window regions. But if the blacklisted window moved, the previous frame's pixels would be stale. This function copies valid regions from the previous frame to avoid showing garbage pixels in areas where the blacklisted window used to be.6. Veyon Registry & Service Management (Lines 459-580)
Why the registry redirect is necessary: Veyon hardcodes its VNC server port in the registry at
HKLM\SOFTWARE\Veyon Solutions\Veyon\Network\VncServerPort. The Veyon service reads this on startup and connects its VncProxyServer to this port. Veyoff changes this port value tooriginalPort + 50, restarts the service, and then binds its own proxy on the original port. The Veyon service now connects to veyoff instead of the real VNC server.Why
waitForServiceStatehas a timeout: Windows service state transitions are asynchronous and can take time (especiallySERVICE_STOPPED→SERVICE_RUNNING). The timeout prevents hangs if the service gets stuck in a transitional state.Why cleanup happens on exit (normal flow) AND self-destruct (nuclear flow): Normal cleanup (restore registry + restart service) is the expected path. Self-destruct is the emergency path — it does cleanup PLUS removes all traces of veyoff from the system (event logs, prefetch, executable). The dual-path design means that even if the process crashes without cleanup, the registry value still points to the redirected port, which means Veyon continues working (just connecting to port+50 directly). Only a deliberate exit restores the original state.
7. RFB Proxy Core (Lines 587-760)
Why
SharedStateusesstd::mutexinstead ofstd::shared_mutex: The shared state (freeze flag, blacklist, framebuffers) is accessed by multiple threads (proxy threads, UI thread, overlay thread). A simple mutex is used instead of a read-write lock because all accesses are short (copy a flag, swap a pointer) — the overhead ofshared_mutexwouldn't be worth it for critical sections this small.Why
RfbPixelFormatis a packed struct: The RFB protocol sends pixel format as a 16-byte fixed-layout structure. The struct mirrors the wire format exactly, with manual packing. This avoids serialization/deserialization overhead — the struct can bereinterpret_castfrom the network buffer directly.8. Proxy Session Management (Lines 761-920)
Why
ProxySessionstores two sockets: Each proxy session has a "client" socket (Veyon's connection to veyoff) and a "server" socket (veyoff's connection to the real UltraVNC). Two threads per session:serverThreadreads from UltraVNC → processes → writes to Veyon,clientThreadreads from Veyon → processes → writes to UltraVNC. The bidirectional proxy needs two threads because RFB has no request-response framing — both sides send messages independently.Why
readAndMaybeForwardhas aforwardflag: In freeze mode, the proxy still needs to read server messages (to keep the TCP buffer from filling up) but should not forward them to the client. Theforwardflag enables this drain-without-forward behavior.Why
handleServerFramebufferUpdateis the most complex function: This is where the MITM magic happens. When the real VNC server sends a framebuffer update, veyoff intercepts it. In normal mode, it forwards it. In freeze mode, it discards it (the client sees the frozen frame). In blacklist mode, it captures the screen, blanks out blacklisted windows, and sends the modified frame instead. This function handles all three modes plus frame size changes, encoding negotiation, and copy-rect deduplication.9. System Tray & Hotkeys (Lines 1400-1570)
Why the tray menu has "Self-Destruct" and "Quit" as separate options: "Quit" does normal cleanup (restore registry, restart service) and exits. "Self-Destruct" does cleanup PLUS removes all traces. The student needs both options — "Quit" for end-of-class (teacher expects normal service restoration), "Self-Destruct" for emergency situations (teacher is investigating).
Why hotkeys use
MOD_NOREPEAT: WithoutMOD_NOREPEAT, holding Ctrl+Alt+F would fire multiple freeze/unfreeze toggles, causing flickering. The flag ensures each key press registers exactly once.10. Self-Destruct Sequence (Lines 1476-1570)
Why it clears event logs AND prefetch: Windows records process execution in two places: the Event Log (Security log logs process creation with audit policies) and Prefetch files (
C:\Windows\Prefetch\*.pf, which contain execution timestamps and frequency). Both are cleared to minimize forensic evidence.Why it uses a batch file for self-deletion: A process cannot delete its own executable while running (Windows locks the file). The batch file approach works because
cmd.exeis a separate process that polls until veyoff exits, then deletes everything. Usingping localhostas a sleep is a well-known Windows batch trick —timeoutrequires user interaction in some Windows versions, butpingalways works.Cross-Cutting Observations
Why everything is in an anonymous namespace: The entire implementation is in an anonymous namespace (
namespace { ... }), making all symbols internal linkage. This is a C++ best practice for single-TU applications — it prevents symbol conflicts with Windows headers and eliminates any concern about symbol visibility.Why there's no error recovery: The code uses a "fail and die" approach — if
sendAll()fails, the proxy session ends. This is intentional: a broken connection means the teacher sees a disconnect, which is less suspicious than a frozen screen that doesn't respond to input. The Veyon service will attempt to reconnect, which creates a new proxy session.Why
FrameBufferstores raw pixels instead of an image format: The proxy needs to modify individual pixels (blank out blacklisted windows, composite frozen frames). Working with raw RGBA pixels is the most efficient approach — no encode/decode overhead for each intercepted frame.Generated by nightshift — why-annotator analysis