Rogue XInput Capabilities Bug – Part 1


Greetings!

It’s time to shed some light on yet another interesting ViGEm/XUSB bug I had to wrap my head around for quite some time now. The related issue has been around since mid-summer 2017 but about half a year is still a good average time span for me fixing bugs 😅 Like the headphone-device-bug this one required some sniffing, disassembly, and trial and error before the puzzle pieces fell into place. So let’s dive into the details.

Summary of the issue

First a quick summary of the problem. This time it’s affecting the high-level API function XInputGetCapabilities, which reports features of the device to query. The passed XINPUT_CAPABILITIES structure contains information about available buttons, axes and vibration attributes which then let the caller decide if the device fulfills the desired requirements for interacting with the process/game. The XINPUT_GAMEPAD member of the structure of a successful request for a typical X360 controller looks like this:

FF F3 FF FF C0 FF C0 FF C0 FF C0 FF

I won’t go over every bit in detail; it basically describes that all buttons and axes expected to be on a XINPUT_DEVTYPE_GAMEPAD are reported present by the XInput sub-system. Now in case of a ViGEm-device this response suddenly becomes filled with random data:

Where by random I don’t mean “I have no clue what these values mean” (which I also didn’t by the time analyzing 😉) but truly baseless garbage that changes every time you re-plug the pad…

After a bit of testing this behavior was indeed unique to ViGEm and had to be addressed since popular solutions like the GameCube Emulator Dolphin failed to receive inputs because they relied on this function to work as expected. So my initial plan of simply ignoring the issue wasn’t an option 😏

The Analysis

ViGEm mimics a USB bus so the answer has to be hidden in the processing of the URBs (USB Request Blocks). But since I already ported over and implemented all the features I knew from maintaining the ScpVBus plus my own research I hit a dead end pretty soon, so I decided to take a look at the top of the stack first and fed the Interactive Disassembler with the user-mode XInput1_3.dll.

Now despite doing a fair amount of reverse engineering I usually avoid the Disassembler if possible. There seems to be a threshold in my brain where it starts to panic if we get too close to the CPU (on an instructional level) and tries to shut down 😖 Anyways, so I shoved this inner daemon aside and forced myself to navigate through the instructions. Thankfully Microsoft provides debug symbols for their OS, libraries and even drivers so I can at least work with resolved function names.

Right, so after some digging and following references we’ll find this gem of a function: DriverComm::GetCapabilities

Alright so at this point the DLL simply sends the I/O-Control-Code IOCTL_XINPUT_GET_CAPABILITIES to XUSB22.SYS via DeviceIoControl function and receives the desired capabilities via the output buffer. Time to abandon ship; we need to dive into deeper waters and take a peak at the driver itself 😮

Inspecting XUSB22.SYS we encounter this function:

bool __fastcall XInputControllerDevice::SendCapabiltiesRequest(XInputControllerDevice *this)

Here’s the key information of what’s going on:

Aha! The driver requests capability information from the controller via the control endpoint (that’s what WdfUsbDeviceWrapper::SendControlRequest tells us). Let’s see how ViGEm responds to control requests, shall we:

😬 Oh. Well. That’s not helping, is it 😅

To be continued… Rogue XInput Capabilities Bug – Part 2

Liked it? Take a second to support nefarius on Patreon!