Играя в Unreal Tournament (ut99) я использую тактику «ожидание противника в засаде», таких игроков называют camper.
Я мирно cтою за ящиком и слушаю музыку.
Как только раздаётся сигнал «респавна» (воскрешение игрока).
Я говорю: — Привет!

Во время игры я слушаю музыку. Громкость звуков (в игре) установлены ниже, из-за этого звук respawn’a слышен неотчетливо. Я задумался. Нельзя-ли сделать, что-то в виде информационного датчика?
Зародившееся идея успешно воплотилась. Об этом мой рассказ.
Сформировался такой план.
-
Запуская UnrealTournament.exe внедрять функцию датчика.
-
Отслеживаются регистры процессора.
-
При воскрешении игрока — мигнуть диодом клавиатуры.
В 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 работает динамически (в памяти запущенного процесса).
План предварительной подготовки.
-
Найти в модуле пространство для записи функции Trigger.
-
С помощью hex-редактора отредактировать Galaxy.dll.
-
Загрузить в 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»):
-
81FEF4206710 cmp esi, 106720F4
-
Пока не введен весь код — неизвестен точный адрес (т.е. смещение) на которую нужно делать условный переход — поэтому, временно — вводим nop (ничего не делать) две команды; это как набросок — резервирование.
-
Здесь располагается переход на выход, пока адрес неизвестен,
временно пропускаем «nop» две команды. -
81FF12040000 cmp edi, 00000412
-
«nop» две команды
-
81FF26040000 cmp edi, 00000426
-
«nop» две команды
-
81FF4A040000 cmp edi, 0000044A
-
«nop» две команды
-
81FF4C040000 cmp edi, 0000044C
-
«nop» две команды
-
81FF4D040000 cmp edi, 0000044D
-
«nop» две команды
-
81FF46040000 cmp edi, 00000446
-
«nop» две команды
-
81FF4E040000 cmp edi, 0000044E
-
«nop» две команды
-
81FF4F040000 cmp edi, 0000044F
-
«nop» две команды
-
81FF48040000 cmp edi, 00000448
-
«nop» две команды
-
81FF50040000 cmp edi, 00000450
-
«nop» две команды
-
81FF8F0C0000 cmp edi, 00000C8F
-
«nop» две команды
-
FF15B4BE6710 call dword ptr [1067BEB4]
-
вызов функции воспроизведения звука, она перенесена из места редактирования
-
E9A1B5FDFF jmp 000117C8
-
это безусловный переход, прыгаем в место адрес которого неизменен.
-
893DE0F06510 mov dword ptr [1065F0E0], edi
-
это переменная в которую мы заносим данные из регистра «EDI»
-
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/
Добавить комментарий