Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Гайд [DMA] Hardware — Чтение XInput напрямую через Kernel-память (C++)

Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
189
Реакции
5
Народ, кто сейчас ковыряет DMA-сетки под актуальный софт, держите годноту. Опять пришлось дампить вручную через kernel-память, чтобы нормально считать инпут с геймпада напрямую, минуя всякие прослойки.

Короче, суть простая: вытягиваем данные через winlogon.exe и xusb22.sys. Работает на всех актуальных версиях винды. Важный момент: геймпад должен быть воткнут физически, если гоняете на PlayStation, юзайте DS4 для эмуляции, иначе дрова не подхватят структуру.

Техническая часть:
  1. Architecture: Чистый C++, работаем через VMMDLL.
  2. Workflow: Ищем глобалы драйвера, вычисляем адрес структуры контроллера, дальше просто рипаем память по оффсетам.
  3. Compatibility: Нужен DMA-девайс (Screamer/Enigma/и аналоги) + KMBox для нормального ввода.

Код:
Expand Collapse Copy
#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;
}

Слил это, потому что сам юзаю похожий метод для внешних аимов, чтобы не палиться через хуки в юзермоде. По фактам — это самый надежный способ считывать инпут для макросов или AI-читов, когда нужно выжать максимум из мувмента и при этом оставаться андетект для античита.

Кто уже тестил этот подход на свежих патчах античитов? Есть просадки по ФПС при частом опросе памяти через VMMDLL или все норм? Кидайте свои фиксы, если кто допиливал структуру под новые версии драйверов xusb.
 
Назад
Сверху Снизу