Звуковой датчик при респавне игрока

от автора

Играя в Unreal Tournament (ut99) я использую тактику «ожидание противника в засаде», таких игроков называют camper.

Я мирно cтою за ящиком и слушаю музыку.

Как только раздаётся сигнал «респавна» (воскрешение игрока).
Я говорю: — Привет!

Во время игры я слушаю музыку. Громкость звуков (в игре) установлены ниже, из-за этого звук respawn’a слышен неотчетливо. Я задумался. Нельзя-ли сделать, что-то в виде информационного датчика?

Зародившееся идея успешно воплотилась. Об этом мой рассказ.

Сформировался такой план.

  1. Запуская UnrealTournament.exe внедрять функцию датчика.

  2. Отслеживаются регистры процессора.

  3. При воскрешении игрока — мигнуть диодом клавиатуры.

В Unreal Tournament, в каталоге System находятся исполняемые и dll-модули.
Я выяснил, модуль Galaxy.dll, воспроизводит звук.

Набросок функции Trigger, на ЯП С с ассемблерной вставкой.
В итоге — функция будет преобразованна в шелл-код, который и будет внедрен.

Как действует Trigger?

Если регистр ESI содержит значение 0x106720F4, значит — нужно проверить регистр EDI, если он содержит одно из значений: 0x412, 0x426, 0x44A, 0x44C, значит сейчас будет сигнал.

void Trigger() { _asm cmp esi, 0x106720F4 _asm je NEXT _asm jmp BACK  NEXT: _asm cmp edi, 0x412 _asm je FOUND  _asm cmp edi, 0x426 _asm je FOUND  _asm cmp edi, 0x44A _asm je FOUND  _asm cmp edi, 0x44C _asm je FOUND  _asm cmp edi, 0x44D _asm je FOUND  _asm cmp edi, 0x446 _asm je FOUND  _asm cmp edi, 0x44E _asm je FOUND  _asm cmp edi, 0x44F _asm je FOUND  _asm cmp edi, 0x448 _asm je FOUND  _asm cmp edi, 0x450 _asm je FOUND  _asm cmp edi, 0xC8F _asm je FOUND  _asm jmp BACK  FOUND: //_asm mov [g_found], 1 BACK: //_asm jmp } 

Как понятно, Trigger работает динамически (в памяти запущенного процесса).

План предварительной подготовки.

  1. Найти в модуле пространство для записи функции Trigger.

  2. С помощью hex-редактора отредактировать Galaxy.dll.

  3. Загрузить в Ida (Интерактивный дизассемблер) — проверить.

Поиск пространства (для записи функции) в модуле Galaxy.dll (для тех кто знаком с ассемблером) не сложная процедура.
В Ida я задал текстовый поиск по-ключевому слову «align» (добавив двойку) (продолжить поиск далее нажимайте комбинацию клавиш Ctrl+T).

Align — директива ассемблера, выравнивает сегменты (вставляет любые символы).
В этом месте и вставим код функции Trigger.

Поиск не заставил долго ждать, в нашем распоряжении (2000h) 8192 байт!

Узнаем у Ida — где, в файле Galaxy.dll — находится адрес (смещение) относительно
физического расположения?

Установить курсор в интересующую позицию.
В пункте меню «Jmp» выбрать «Jmp to file offset…».

Копируем адрес, еще лучше записать в блокнотик.

Открыть файл Galaxy.dll в редакторе (в котором любите работать).

Если вы как и я используете QView — нажмите пару раз клавишу «Enter» — будет вид дизассемблера.
Нажмите клавишу «F2» показывать 32-битный код.
Нажмите «F5» введите адрес: 361BA нажмите клавишу «Enter».

Вы окажетесь в месте где мы запишем код функции Trigger.

Записываем код (в Qview работает клавиша «Tab»):

  1. 81FEF4206710 cmp esi, 106720F4

  2. Пока не введен весь код — неизвестен точный адрес (т.е. смещение) на которую нужно делать условный переход — поэтому, временно — вводим nop (ничего не делать) две команды; это как набросок — резервирование.

  3. Здесь располагается переход на выход, пока адрес неизвестен,
    временно пропускаем «nop» две команды.

  4. 81FF12040000 cmp edi, 00000412

  5. «nop» две команды

  6. 81FF26040000 cmp edi, 00000426

  7. «nop» две команды

  8. 81FF4A040000 cmp edi, 0000044A

  9. «nop» две команды

  10. 81FF4C040000 cmp edi, 0000044C

  11. «nop» две команды

  12. 81FF4D040000 cmp edi, 0000044D

  13. «nop» две команды

  14. 81FF46040000 cmp edi, 00000446

  15. «nop» две команды

  16. 81FF4E040000 cmp edi, 0000044E

  17. «nop» две команды

  18. 81FF4F040000 cmp edi, 0000044F

  19. «nop» две команды

  20. 81FF48040000 cmp edi, 00000448

  21. «nop» две команды

  22. 81FF50040000 cmp edi, 00000450

  23. «nop» две команды

  24. 81FF8F0C0000 cmp edi, 00000C8F

  25. «nop» две команды

  26. FF15B4BE6710 call dword ptr [1067BEB4]

  • вызов функции воспроизведения звука, она перенесена из места редактирования

  1. E9A1B5FDFF jmp 000117C8

  • это безусловный переход, прыгаем в место адрес которого неизменен.

  1. 893DE0F06510 mov dword ptr [1065F0E0], edi

  • это переменная в которую мы заносим данные из регистра «EDI»

  1. EBED jmp 0003621C

  • переход в пункт #25

Редактируем условные переходы.
Вот что получается:

Теперь идем в место откуда мы переходим в функцию Trigger.

Нажмите в «Qview» клавишу «F5», введите адрес 117C2 и нажмите «Enter».

Запишите:
a) E9F3490200 jmp 000361BA
b) 90 nop

Всё готово. Проверим.

Загрузите Galaxy.dll в Ida (если вы еще не сделали этого).
Если загружен — обновите базу данных (нажмите в меню «Options» — «General» — «Reanalyze program»).
Также, ничего не случится, если вы вместо обновления — загрузите модуль «Galaxy.dll» снова.

Идем в функцию Trigger, нажмите в Ida клавишу «G», введите адрес 106361BA.

Cмотрим:

Нажмите на «; CODE XREF: sub_10610C00+BC2» и мы попадем в место откуда вызывается функция, помните — это последнее, что мы редактировали, адрес 106117C2.

Выберите в меню «Debugger» отладчик «Local Win32 Debugger».

Установите точку остановки (BPX) перед переходом (инструкция «jmp»).

Нажмите в меню «Debugger» «Start process», установите (чекбокс):
«Don’t display this message again» и нажмите «Yes».
Ida попросит указать имя исполняемого приложения, выберите «UnrealTournamet.exe».

Когда программа остановится в точке остановки (BPX).
Пройдите весь путь пошагово (нажимая клавишу F8).
Убедитесь в надежности исправленного участка.

Результат. Программа в сборе.
//////////////////////////////////////////////////////////////////////////////// // WinMain.cpp // // По умолчанию в UT99 звук воспроизводит: Galaxy.GalaxyAudioSubsystem. // Если, что-то будет не так - проверьте файл UnrealTournament.ini, // установлено-ли: AudioDevice=Galaxy.GalaxyAudioSubsystem  #include <windows.h> #include <winioctl.h>  char* GAppname="Resp2A Trigger UT99'"; char* GAppname_UT="Unreal Tournament";  void mb(char* s); void OnKbdLeds(); void ToggleLed(BOOL toggle, int led); char* appGetOpenFileName();  int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) { CreateMutex(NULL,0,GAppname); BOOL AlreadyRunning=(ERROR_ALREADY_EXISTS==GetLastError()); if(AlreadyRunning) return 1;  // Load config HKEY key; RegCreateKey(HKEY_CURRENT_USER, "Software\\MyCompany\\MyAppname",&key);  DWORD dwLen=MAX_PATH; static char CommandLine[MAX_PATH]; RegQueryValueEx(key,"CommandLine",NULL,NULL,(BYTE*)CommandLine,&dwLen);  if(!CommandLine[0]) { lstrcpy(CommandLine,appGetOpenFileName()); if(!CommandLine[0]) { RegCloseKey(key); return 0; } }  char CurrentDirectory[MAX_PATH]; lstrcpy(CurrentDirectory,CommandLine);  int i; for(i=lstrlen(CurrentDirectory)-1;i>0;--i) { if('\\'==CurrentDirectory[i-1] || '//'==CurrentDirectory[i-1]) break; } CurrentDirectory[i]=0;  PROCESS_INFORMATION pi; STARTUPINFO si; memset(&si,0,sizeof(STARTUPINFO)); si.cb=sizeof(STARTUPINFO); si.dwFlags=STARTF_FORCEOFFFEEDBACK;  char cmdLine[512]; wsprintf(cmdLine,"%s",CommandLine); //wsprintf(cmdLine,"%s 195.98.73.166:6666",CommandLine);  CreateProcess(NULL, cmdLine,NULL,NULL,0,0,NULL,CurrentDirectory,&si,&pi); if(!pi.hProcess) { CommandLine[0]=0; RegSetValueEx(key,"CommandLine",NULL,REG_SZ,(BYTE*)(CommandLine),MAX_PATH); RegCloseKey(key);  char buf[512]; wsprintf(buf,"Failed CreateProcess\n\n\"%s\"",CommandLine); mb(buf); } CloseHandle(pi.hThread);  // Save config RegSetValueEx(key,"CommandLine",NULL,REG_SZ,(BYTE*)(CommandLine),MAX_PATH); RegCloseKey(key);  HWND hWnd=FindWindow(NULL,GAppname_UT); while(!hWnd) { Sleep(1000); hWnd=FindWindow(NULL,GAppname_UT); }  /*------------------------------------------------------- The trigger -------------------------------------------------------*/ DWORD addrTrigger=0x106361BA;  unsigned char codeTrigger[]= "\x81\xFE\xF4\x20\x67\x10\x74\x02\xEB\x58\x81\xFF\x12\x04\x00\x00" "\x74\x5B\x81\xFF\x26\x04\x00\x00\x74\x53\x81\xFF\x4A\x04\x00\x00" "\x74\x4B\x81\xFF\x4C\x04\x00\x00\x74\x43\x81\xFF\x4D\x04\x00\x00" "\x74\x3B\x81\xFF\x46\x04\x00\x00\x74\x33\x81\xFF\x4E\x04\x00\x00" "\x74\x2B\x81\xFF\x4F\x04\x00\x00\x74\x23\x81\xFF\x48\x04\x00\x00" "\x74\x1B\x81\xFF\x50\x04\x00\x00\x74\x13\x81\xFF\x8F\x0C\x00\x00" "\x74\x0B\xFF\x15\xB4\xBE\x67\x10\xE9\xA1\xB5\xFD\xFF\x89\x3D\xE0" "\xF0\x65\x10\xEB\xED";  DWORD codeTrigger_Len=117;  /*------------------------------------------------------- The patch -------------------------------------------------------*/ DWORD addrPatch=0x106117C2;  // E9F349020090 unsigned char codePatch[]="\xE9\xF3\x49\x02\x00\x90"; DWORD codePatch_Len=6;  DWORD dwMagic=0; BYTE value[128];  BOOL bRet=FALSE; int n=14; // Wait init "UT" 14 seconds  while(n) { ReadProcessMemory(pi.hProcess, (LPVOID)addrPatch,&dwMagic,sizeof(dwMagic),NULL); if(dwMagic) { if(0xBEB415FF==dwMagic) { memcpy(&value,codeTrigger,codeTrigger_Len); bRet=WriteProcessMemory(pi.hProcess, (LPVOID)addrTrigger,&value,codeTrigger_Len,NULL);  if(bRet) { memcpy(&value,codePatch,codePatch_Len); bRet=WriteProcessMemory(pi.hProcess, (LPVOID)addrPatch,&value,codePatch_Len,NULL); } } break;  } // End "if dwMagic"  --n; Sleep(1000);  } // End "while"  if(!bRet) mb("Failed patch");  DWORD addrFound=0x1065F0E0; DWORD dwFound=0;  hWnd=FindWindow(NULL,GAppname_UT);  while(hWnd) { ReadProcessMemory(pi.hProcess, (LPVOID)addrFound,&dwFound,sizeof(dwFound),NULL); if(dwFound) { switch(dwFound) { case 0x412: dwFound=0xCDC31337; break;  case 0x426: dwFound=0xCDC31337; break;  case 0x44A: dwFound=0xCDC31337; break;  case 0x44C: dwFound=0xCDC31337; break;  case 0x44D: dwFound=0xCDC31337; break;  case 0x446: dwFound=0xCDC31337; break;  case 0x44E: dwFound=0xCDC31337; break;  case 0x44F: dwFound=0xCDC31337; break;  case 0x448: dwFound=0xCDC31337; break;  case 0x450: dwFound=0xCDC31337; break;  case 0xC8F: dwFound=0xCDC31337; //break; }  if(0xCDC31337==dwFound) { OnKbdLeds(); ToggleLed(1,1);  dwFound=0; bRet=WriteProcessMemory(pi.hProcess, (LPVOID)addrFound,&dwFound,sizeof(dwFound),NULL); }  } // End "if dwFound"  Sleep(20); // Give up  hWnd=FindWindow(NULL,GAppname_UT);  } // End "while"  return 0; }  void mb(char* s) { UINT uType=MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND | MB_SYSTEMMODAL;  int n=0; if(strstr(s,"Failed") || strstr(s,"Error")) ++n;  if(n) { uType &=~MB_ICONINFORMATION; uType |=MB_ICONWARNING; } MessageBox(GetActiveWindow(),s,GAppname,uType);  if(n) ExitProcess(n); }  /* void Trigger() { _asm cmp esi, 0x106720F4 _asm je NEXT _asm jmp BACK  NEXT: _asm cmp edi, 0x412 _asm je FOUND  _asm cmp edi, 0x426 _asm je FOUND  _asm cmp edi, 0x44A _asm je FOUND  _asm cmp edi, 0x44C _asm je FOUND  _asm cmp edi, 0x44D _asm je FOUND  _asm cmp edi, 0x446 _asm je FOUND  _asm cmp edi, 0x44E _asm je FOUND  _asm cmp edi, 0x44F _asm je FOUND  _asm cmp edi, 0x448 _asm je FOUND  _asm cmp edi, 0x450 _asm je FOUND  _asm cmp edi, 0xC8F _asm je FOUND  _asm jmp BACK  FOUND: //_asm mov [g_found], 1 BACK: //_asm jmp } */  #define IOCTL_KEYBOARD_SET_INDICATORS CTL_CODE(FILE_DEVICE_KEYBOARD, 2, METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_KEYBOARD_QUERY_INDICATORS CTL_CODE(FILE_DEVICE_KEYBOARD, 0x10, METHOD_BUFFERED,FILE_ANY_ACCESS)  void OnKbdLeds() { if(!DefineDosDevice(DDD_RAW_TARGET_PATH,"Kbd000000","\\Device\\KeyboardClass0")) mb("Failed DefineDosDevice");  HANDLE hDevice=CreateFile("\\\\.\\Kbd000000",GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL); if(INVALID_HANDLE_VALUE==hDevice) mb("Failed open kbd");  unsigned int InBuffer; DWORD OutBufferSize; unsigned char p[]={32};  for(int i=0; i<300; ++i) { InBuffer=0; InBuffer |=p[i] << 16; DeviceIoControl(hDevice,IOCTL_KEYBOARD_SET_INDICATORS,&InBuffer,sizeof(InBuffer),NULL,0,&OutBufferSize,NULL); Sleep(10); }  DefineDosDevice(DDD_REMOVE_DEFINITION,"Kbd000000",NULL); CloseHandle(hDevice); }  void ToggleLed(BOOL toggle, int led) { if(!DefineDosDevice(DDD_RAW_TARGET_PATH,"Kbd000000","\\Device\\KeyboardClass0")) mb("Failed DefineDosDevice");  HANDLE hDevice=CreateFile("\\\\.\\Kbd000000",GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL); if(INVALID_HANDLE_VALUE==hDevice) mb("Failed open kbd");  DWORD OutBufferSize; unsigned int InBuffer=0, output=0;  if(!DeviceIoControl(hDevice,IOCTL_KEYBOARD_QUERY_INDICATORS,&InBuffer,sizeof(InBuffer),&output, sizeof(output),&OutBufferSize, NULL)) { CloseHandle(hDevice); mb("Failed query kbd"); }  InBuffer=output;  if(toggle) InBuffer &= ~(led << 16); else InBuffer |=led << 16;  DeviceIoControl(hDevice,IOCTL_KEYBOARD_SET_INDICATORS,&InBuffer,sizeof(InBuffer),NULL,0,&OutBufferSize,NULL);  CloseHandle(hDevice); }  char* appGetOpenFileName() { static char fname[MAX_PATH];  OPENFILENAME ofn; memset(&ofn,0,sizeof(OPENFILENAME)); fname[0]=0;  ofn.lStructSize=sizeof(OPENFILENAME); ofn.hInstance=GetModuleHandle(NULL); ofn.lpstrFile=fname; ofn.lpstrInitialDir="D:\\Games\\ut99\\System"; ofn.nMaxFile=MAX_PATH; ofn.lpstrFileTitle=NULL; ofn.nMaxFileTitle=0; ofn.lpstrTitle="Select UnrealTournament.exe"; ofn.lpstrFilter="Applications (*.exe)\0*.exe\0"; ofn.Flags=OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;  GetOpenFileName(&ofn); return fname; }  //////////////////////////////////////////////////////////////////////////////// // <<eof>> WinMain.cpp //////////////////////////////////////////////////////////////////////////////// 

Пытливые умы конечно же спросят: — «как ты вычислил адрес, где происходят звуковые события?».

Об этом я расскажу во второй части.


ссылка на оригинал статьи https://habr.com/ru/post/709802/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *