Конференция "Игры" » Dll хук (перехват) метода EnumDevices интерфейса DirectInput8 [Delphi]
 
  • smakss (21.12.18 02:03) [0]
    Привет. Поставил себе задачу скрытия от DirectInput приложения игровых устройств. Написал перекрывающую библиотеку "dinput8.dll". Но после вызова метода EnumDevices из игрового приложения и вывода сообщения "Hook Work!" выдаётся Accees violation. Никак не пойму почему.


    library dinput8;
    uses Windows, Classes;

    type
     TDirectInput8 = class
       //function CreateDevice(const rguid: TGUID; out lplpDirectInputDevice: IDirectInputDevice8A; pUnkOuter: IUnknown): HResult; virtual; stdcall; abstract;
       function EnumDevices (dwDevType: DWORD; lpCallback: Pointer; pvRef: Pointer; dwFlags: DWORD): HResult; virtual; stdcall; abstract;
       //function GetDeviceStatus(const rguidInstance: TGUID): HResult; virtual; stdcall; stdcall; abstract;
       //function RunControlPanel(hwndOwner: HWND; dwFlags: DWORD): HResult; virtual; stdcall; abstract;
       //function Initialize(hinst: THandle; dwVersion: DWORD): HResult; virtual; stdcall; abstract;
       //function FindDevice(const rguidClass: TGUID; ptszName: PAnsiChar; out pguidInstance: TGUID): HResult; virtual; stdcall; abstract;
       //function EnumDevicesBySemantics(ptszUserName: PAnsiChar; const lpdiActionFormat: TDIActionFormatA; lpCallback: TDIEnumDevicesBySemanticsCallbackA; pvRef: Pointer; dwFlags: DWORD): HResult; virtual; stdcall; abstract;
       //function ConfigureDevices(lpdiCallback: TDIConfigureDevicesCallback; const lpdiCDParams: TDIConfigureDevicesParamsA; dwFlags: DWORD; pvRefData: Pointer): HResult; virtual; stdcall; abstract;
     end;

    // Замена методов по указателям в памяти
    function ReplaceMethod(pTargetAddr: Pointer; pNewAddr: Pointer; var pOrigAddr: Pointer): Boolean;
    type
     TJumP = packed record // Смещение на другой метод
       bJmp: Byte;
       dwAddress: DWord;
     end;

    var
     dwLength: Cardinal;
     gJump: TJump;
     dwProtect: DWord;

     // Получить размер памяти, занимаемой методом
     function getMethodSize(MPtr: Pointer): Integer;
     const MAX_SIZE = 5000;
     var
       ch: Byte;
       i: Integer;
     begin
       i := 0; Result := 0;
       repeat
         CopyMemory(@ch, Ptr(DWORD(MPtr)+i), 1);
         inc(i);
       until (ch = $C3) or (i >= MAX_SIZE);
       if i < MAX_SIZE then Result := i;
     end;

    begin Result := False;
     dwLength := getMethodSize(pTargetAddr);

     // Сохранить код старого метода
     pOrigAddr := GetMemory(dwLength); // Выделить память для оригинального метода
     CopyMemory(pOrigAddr, pTargetAddr, dwLength); // Скопировать оригинальный метод в новое место

     gJump.bJmp := $E9;
     gJump.dwAddress := DWord(pNewAddr) - DWord(pTargetAddr) - 5;

     // Изменить участок памяти текущего процесса
     if VirtualProtectEx(GetCurrentProcess(), pTargetAddr, dwLength, PAGE_READWRITE, dwProtect) then begin
       CopyMemory(pTargetAddr, @gJump, sizeOf(TJump)); // Поменять вместо оригинального метода смещение на другой метод
       VirtualProtectEx(GetCurrentProcess(), pTargetAddr, dwLength, dwProtect, dwProtect);
       Result := True;
     end;
    end;

    // *****************************************************************************

    var
     DirectInput8CreateOrig: function (hinst: THandle; dwVersion: DWORD; const riidltf: TGUID; out ppvOut{: Pointer}; punkOuter: IUnknown): HResult; stdcall; // external 'dinput8.dll' Name 'DirectInput8Create';
     DInput: TDirectInput8;
     EnumOrig: function (dwDevType: DWORD; lpCallback: Pointer; pvRef: Pointer; dwFlags: DWORD): HResult; stdcall;

    // Заменяемый хук-метод перечисления устройств через DirectInput
    function EnumDevices(dwDevType: DWORD; lpCallback: Pointer; pvRef: Pointer; dwFlags: DWORD): HResult; stdcall;
    begin
       MessageBox(0, 'Hook Work!', nil, 0);
       Result := 0;
       //Result := EnumOrig(dwDevType, lpCallback, pvRef, dwFlags);
    end;

    // Перекрытие создания главного объекта DirectInput
    function DirectInput8Create(hinst: THandle; dwVersion: DWORD; const riidltf: TGUID; var ppvOut: Pointer; punkOuter: IUnknown): HResult; stdcall;
    var
     LibHandle: THandle;
     PrcAddr:   Pointer;

     addr_proc: function (dwDevType: DWORD; lpCallback: Pointer; pvRef: Pointer; dwFlags: DWORD): HResult of object; stdcall;

    begin
     Result := 0; PrcAddr := nil;

     LibHandle := LoadLibrary('C:\Windows\System32\dinput8.dll');
     if LibHandle <> 0 then begin
       PrcAddr := GetProcAddress(LibHandle, 'DirectInput8Create');
       if PrcAddr <> nil then begin
         DirectInput8CreateOrig := PrcAddr;
         Result := DirectInput8CreateOrig(hinst, dwVersion, riidltf, ppvOut, punkOuter);

         DInput := ppvOut;
         addr_proc := DInput.EnumDevices;

         ReplaceMethod(@addr_proc, @EnumDevices, PrcAddr);
         EnumOrig := PrcAddr;

       end;
       //FreeLibrary(LibHandle);
     end;
    end;

    Exports DirectInput8Create;
    begin
    end.

  • Rouse_ © (21.12.18 11:12) [1]
    // Скопировать оригинальный метод в новое место

    Молодец, а ничего что там релоки, которые тоже править надо? Да и RET (0xC3) их несколько может быть ибо ветвления. Да и новый код, который ты переместил должен быть на странице с PAGE_EXECUTE правами
  • Rouse_ © (21.12.18 11:24) [2]
    ЗЫ: я уж не говорю что выходы могут быть сразу с правкой стека типа
    75B1B23B: C2 04 00                                   RET 0x4
    75B1B2DA: C2 08 00                                   RET 0x8
    75B1B496: C2 0C 00                                   RET 0xC
    75B1B9B1: C2 20 00                                   RET 0x20
  • Rouse_ © (21.12.18 11:29) [3]
    В частности в твоем случае пролог функции выглядит вот так, обрати внимание на опкоды инструкций слева:

                                             loc_C34CE2C:            ; DllRelease()
    E8 A6 F9 FF FF                            call    _DllRelease@0
    8B 4D FC                                  mov     ecx, [ebp+var_4]
    8B 85 E8 FC FF FF                         mov     eax, [ebp+var_318]
    5F                                        pop     edi
    5E                                        pop     esi
    33 CD                                     xor     ecx, ebp
    5B                                        pop     ebx
    E8 2E 1E 01 00                            call    @__security_check_cookie@4 ; __security_check_cookie(x)
    C9                                        leave
    C2 14 00                                  retn    14h
                                             _DirectInput8Create@20 endp

  • smakss (21.12.18 14:55) [4]
    Благодарю за помощь. Я, к сожалению, ещё не работаю на таком уровне отладки. И ассемблер почти не знаю. Поэтому и спросил на форуме. Даже если закомментировать часть кода копирования оригинального метода и просто затереть его, всё равно выдаётся ошибка Accees violation.

    Ниже прикладываю простой пример опроса устройств и подмены метода опроса с ошибкой. Может, кому удастся исправить и пояснить.
    https://drive.google.com/file/d/1wdaZ_jSvx5MI0xaFXhi6cB41Dq6x776t/view
  • smakss (21.12.18 15:17) [5]
    Раз очень сложно перенести оригинальный метод в новый участок памяти, то может есть способ заменить указатель на сам метод, вроде:
    DInput8.EnumDevices := @MyEnumDevices?
  • Rouse_ © (21.12.18 15:58) [6]
    Изучи вот эти две статьи, должны дать общее представление к подходу:
    https://habr.com/post/178393/
    https://habr.com/post/181157/

    Грубо, копировать тело функции тебе не нужно, достаточно организовать трамплин описанный во второй статье + общая метода в седьмой главе первой статьи.
  • smakss (21.12.18 18:21) [7]
    Сейчас попробовал несколько способов через Detours (https://github.com/MahdiSafsafi/delphi-detours-library) и тоже получил Accees violation. Я понял так, что у меня проблема с точным определением позиции метода EnumDevices в памяти COM интерфейса IDirectInput8.

    Кто-то решал похожую проблему (https://www.unknowncheats.me/forum/c-and-c/153692-send-keys-game-directinput-hook.html) но только с методом GetDeviceState, а искал он этот метод COM-интерфейса в памяти через расчёт смещения от метода DirectInput8Create. То есть всё совсем не так просто, как начал делать я.
 
Конференция "Игры" » Dll хук (перехват) метода EnumDevices интерфейса DirectInput8 [Delphi]
Есть новые Нет новых   [134427   +28][b:0][p:0.005]