- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 170
- Reaction score
- 7
Found this deep in some kernel research—anyone else looking into reading XInput directly via DMA?
Most people are still relying on hooking user-mode APIs or middleman software for controller inputs, but that is just asking for detection in titles with strict integrity checks. This approach targets xusb22.sys in winlogon.exe memory space. By grabbing the driver globals and following the chain to the gamepadInformation struct, you can pull state data straight from the kernel without ever triggering a handle-based detection.
Technical Notes:
I have been testing this to read controller input for an aimlock setup on a secondary machine using a DMA board. No input lag issues so far since you are just doing simple memory reads.
Has anyone tried mapping this to a KMBox yet to handle the output side? I am currently looking into potential ways to refine the signature scanning to be more robust against OS updates. Drop your experiences or any refinements you have made to the logic below.
Most people are still relying on hooking user-mode APIs or middleman software for controller inputs, but that is just asking for detection in titles with strict integrity checks. This approach targets xusb22.sys in winlogon.exe memory space. By grabbing the driver globals and following the chain to the gamepadInformation struct, you can pull state data straight from the kernel without ever triggering a handle-based detection.
Code:
#define XINPUT_DPAD_UP 0x0001
#define XINPUT_DPAD_DOWN 0x0002
#define XINPUT_DPAD_LEFT 0x0004
#define XINPUT_DPAD_RIGHT 0x0008
#define XINPUT_START 0x0010
#define XINPUT_SHARE 0x0020
#define XINPUT_L3 0x0040
#define XINPUT_R3 0x0080
#define XINPUT_LB 0x0100
#define XINPUT_RB 0x0200
#define XINPUT_XBOX 0x0400
#define XINPUT_A 0x1000
#define XINPUT_B 0x2000
#define XINPUT_X 0x4000
#define XINPUT_Y 0x8000
#define XINPUT_LT 0x10000
#define XINPUT_RT 0x20000
struct XInputState {
uint16_t buttons;
uint8_t lt;
uint8_t rt;
int16_t lx;
int16_t ly;
int16_t rx;
int16_t ry;
};
bool Controller::InitController() {
winlogonPID = mem.GetProcessID(("winlogon.exe"));
PVMMDLL_MAP_MODULEENTRY moduleInfo = mem.GetProcessModuleInformation(winlogonPID | VMMDLL_PID_PROCESS_WITH_KERNELMEMORY, ("xusb22.sys"));
if (!moduleInfo)
return false;
uint64_t driverGlobals = mem.Read<uint64_t>(winlogonPID | VMMDLL_PID_PROCESS_WITH_KERNELMEMORY, mem.FindSignature(winlogonPID | VMMDLL_PID_PROCESS_WITH_KERNELMEMORY, ("48 8B 0D ? ? ? ? 8B D3"), moduleInfo->vaBase, moduleInfo->vaBase + moduleInfo->cbImageSize, 7));
if (driverGlobals < 0xFFFF000000000000)
return false;
uint64_t xenonBusInformation = mem.Read<uint64_t>(winlogonPID | VMMDLL_PID_PROCESS_WITH_KERNELMEMORY, driverGlobals + 0x48);
if (xenonBusInformation < 0xFFFF000000000000)
return false;
uint64_t gamepadInformation = mem.Read<uint64_t>(winlogonPID | VMMDLL_PID_PROCESS_WITH_KERNELMEMORY, xenonBusInformation + 0x30);
if (gamepadInformation < 0xFFFF000000000000)
return false;
xInputControllerDevice = mem.Read<uint64_t>(winlogonPID | VMMDLL_PID_PROCESS_WITH_KERNELMEMORY, gamepadInformation + 0x88) + mem.Read<uint8_t>(winlogonPID | VMMDLL_PID_PROCESS_WITH_KERNELMEMORY, mem.FindSignature(winlogonPID | VMMDLL_PID_PROCESS_WITH_KERNELMEMORY, ("66 89 43 ? 8A 43"), moduleInfo->vaBase, moduleInfo->vaBase + moduleInfo->cbImageSize) + 0x3);
if (xInputControllerDevice < 0xFFFF000000000000)
return false;
return true;
}
void Controller::UpdateState() {
previousState = state;
mem.Read(winlogonPID | VMMDLL_PID_PROCESS_WITH_KERNELMEMORY, xInputControllerDevice, &state, sizeof(XInputState));
}
void Controller::UpdatePressedState() {
mem.Read(winlogonPID | VMMDLL_PID_PROCESS_WITH_KERNELMEMORY, xInputControllerDevice, &state, sizeof(XInputState));
}
bool Controller::IsButtonDown(uint32_t button) {
if (xInputControllerDevice < 0xFFFF000000000000)
return false;
if (std::chrono::system_clock::now() - lastDown > std::chrono::milliseconds(100)) {
UpdateState();
lastDown = std::chrono::system_clock::now();
}
if (button == XINPUT_LT)
return state.lt > 128;
if (button == XINPUT_RT)
return state.rt > 128;
return state.buttons & (uint16_t)button;
}
bool Controller::IsButtonPressed(uint32_t button) {
if (xInputControllerDevice < 0xFFFF000000000000)
return false;
if (std::chrono::system_clock::now() - lastPressed > std::chrono::milliseconds(100)) {
UpdatePressedState();
lastPressed = std::chrono::system_clock::now();
}
if (button == XINPUT_LT) {
bool cur = state.lt > 128;
bool prev = previousState.lt > 128;
if (cur)
previousState.lt = state.lt;
else
previousState.lt = 0;
return cur && !prev;
}
if (button == XINPUT_RT) {
bool cur = state.rt > 128;
bool prev = previousState.rt > 128;
if (cur)
previousState.rt = state.rt;
else
previousState.rt = 0;
return cur && !prev;
}
bool current = state.buttons & (uint16_t)button;
bool previous = previousState.buttons & (uint16_t)button;
bool isPressed = current && !previous;
if (current)
previousState.buttons |= (uint16_t)button;
else
previousState.buttons &= ~(uint16_t)button;
return isPressed;
}
Technical Notes:
- Compatibility: This should be stable across current Windows builds. Requires a physical controller plugged in. If you are using a PlayStation controller, make sure to use DS4 to emulate XInput.
- Memory Logic: The init function uses a signature scan (48 8B 0D ? ? ? ? 8B D3) to locate the driver globals. If your specific Windows build has a different module base, you might need to adjust the signature offset.
- Performance: Note the 100ms polling rate logic in IsButtonDown. You might want to tune that depending on whether you are using this for a triggerbot or just menu navigation.
I have been testing this to read controller input for an aimlock setup on a secondary machine using a DMA board. No input lag issues so far since you are just doing simple memory reads.
Has anyone tried mapping this to a KMBox yet to handle the output side? I am currently looking into potential ways to refine the signature scanning to be more robust against OS updates. Drop your experiences or any refinements you have made to the logic below.