- C 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| vhidev-hid-takeover | ||
| .gitignore | ||
| README.md | ||
| vhidev-hid-takeover.sln | ||
vhidev-hid-takeover
Research PoC: reverse-engineering a third-party virtual HID driver
(vhidflt.sys / hardware id root\vhidev) and driving it from a plain
user-mode process to synthesize keyboard, mouse, consumer-control and
system-control input, with no vendor runtime involved.
Research use only. Run inside a disposable VM. The driver package in
resource/is a third-party WHQL-signed binary — this project does not redistribute it as a product, it is kept here only as the reverse-engineering target the PoC was written against. Loading it on a machine you care about and then injecting input into whatever window happens to have focus is a great way to lose that machine.
Background
The vhidev package exposes a KMDF root-enumerated bus driver that children
out multiple HID collections. One of those collections — the vendor-defined
COL05 — accepts a single 64-byte Output report whose first byte is a
fixed report id and whose second byte selects a virtual device:
[0] = 5 vendor report id (fixed)
[1] = selector:
1 -> keyboard (modifiers + 32-byte NKRO bitmap, bytes 2..34)
2 -> mouse (buttons, int8 X, int8 Y, int8 wheel, bytes 2..5)
3 -> consumer (16-bit LE usage code, bytes 2..3)
4 -> system (16-bit LE usage code, bytes 2..3)
[2..63] = payload (unused tail zeroed)
The driver's internal dispatcher routes the payload to the correct child HID collection, so the host OS sees four separate, perfectly ordinary HID devices delivering input. From the input-stack side it is indistinguishable from a real USB composite device.
This project answers two questions:
- Can the vendor package be installed and bound to the host without the
vendor's own installer? Yes —
install.creproduces whatdevcon install vhidflt.inf root\vhidevdoes using SetupAPI /UpdateDriverForPlugAndPlayDevicesdirectly, so no third-party installer or service is required. - Is the COL05 interface reachable from user mode without any additional
secret / handshake? Yes — once the devnode is bound, the HID child is
enumerated and
CreateFile+WriteFileof a 64-byte report is enough. Nothing authenticates the writer.
The second point is the actual research finding: a signed virtual-HID driver
intended for a specific product can be repurposed into a generic
system-wide input-injection primitive by any process that happens to have
SeLoadDriverPrivilege once (install) and admin + raw HID write access
afterwards.
Repository layout
vhidev-hid-takeover.sln Visual Studio solution (x64)
vhidev-hid-takeover/
app.manifest requestedExecutionLevel = requireAdministrator
header/
common.h logging + small platform helpers
install.h SetupAPI install / state check
hid.h COL05 report shape + high-level senders
ui.h console menu
source/
main.c boot: elevate → install if needed → open → menu
install.c devcon-style install via SetupAPI
hid.c 64-byte report builders + WriteFile loop
ui.c demo menu (keyboard typing, mouse paths,
vol/mute, play/pause, sleep, …)
resource/
vhidflt.inf vendor INF (install source)
vhidflt.sys vendor KMDF driver (WHQL-signed)
vhidflt.sys.i64 IDA Pro database from the RE pass
WdfCoinstaller01009.dll KMDF coinstaller
wudf.cat catalog
How it works end to end
main.cchecks for elevation (manifest already asks for it) and, if theroot\vhidevdevnode is not present, callsvhidev_install().vhidev_install()reads the class from the INF (SetupDiGetINFClassA), creates an empty devnode in a temporary device info set, stampsroot\vhidevas theSPDRP_HARDWAREID, registers it withDIF_REGISTERDEVICE, then kicksUpdateDriverForPlugAndPlayDevicesto copy the INF into the DriverStore and bind the driver. From here on the devnode is persistent across reboots.hid_open()enumerates the HID interface class (GUID_DEVINTERFACE_HID), opens each device, pulls preparsed data via HIDP, and picks the one whose top-level vendor-defined usage matches COL05.- The high-level helpers in
hid.c(hid_send_mouse,hid_send_keyboard,hid_send_consumer,hid_send_system) build the 64-byte selector-prefixed report andWriteFileit. ui.cis a small menu that exercises each path (type a string, draw a square with the mouse, mute, volume up/down, play/pause, sleep, …) so the PoC visibly works without an attached app.
Build
- Visual Studio 2022, Windows 10/11 SDK, platform x64.
- Open
vhidev-hid-takeover.sln, build Release. - Run
vhidev-hid-takeover.exeelevated. Theresource\directory must sit next to the exe (the vcxproj copies it to the output automatically).
Research notes
- The INF declares class
HIDClass; after install, there is no service to start — PnP starts the driver on demand when the first child is opened. vhidev_is_installed()usesCM_Locate_DevNodeAon the hardware id (root\vhidev) withCM_LOCATE_DEVNODE_NORMAL. Note that this expects a device instance id in general, but the specific call shape used here is the one that works reliably against the enumerator-created root node; earlier versions in the LeagueBot codebase got this wrong and re-installed on every launch, stacking duplicate devnodes.- The report-id / selector scheme means a single 64-byte write path serves four distinct HID classes. For fuzzing follow-ups this is a convenient single surface to target the driver's dispatcher.
What this project is not
- Not a product, not a cheat, not a redistribution of the vendor driver.
- Not a vulnerability disclosure. The behaviour exercised here is the driver's documented function — the point is that a signed, locally-installed virtual-HID driver is a generic input-injection primitive, which is intentional on the vendor's side and inherently high-privilege on the host.
License
No license granted. All rights reserved. Published for research reference.