Выбирать особо было не из чего, т.к. на машине стояла только одна игра Newer Winter Nigths. И решено было писать тренер к ней.

Трейнер это программа, которая позволяет изменять значения в игре, например жизни. Сам я читеров не люблю 
и втихомолку сильно бью *(. Но так я думал до поры до времени, пока не припёрло к стенке,
т.е. к монстру. И набросал я быстренько (ну это как посмотреть) свой
трейнер.

С помощью ArtMoney я нашел тот блок памяти, в котором хранятся очки жизни. Было бы всё просто, если бы не одно НО: при каждой новой загрузке это место меняется, это происходит по тому, что программа выделяет память динамически и раз на раз не приходится. Ну, раз программа находит нужное место то и я его смогу найти.)

Примечание: ArtMoney найдёт не одно значение, у меня их было 10. Меняйте эти значения и смотрите за остальными, некоторые ячейки тоже поменяют своё значение на введённое вами. Та ячейка, которая изменяет собой больше ячеек и есть искомая.

Запускаем SoftICE, и ставил брейкпоинт (в дальнейшем БР) на доступ к памяти в ячейку, которую мы нашли. (У меня по адресу 10AEFC70, поэту команда для SoftICE’а выглядит так: bpmd 10AEFC70. Но обратите внимание что под окном кода должно быть написано, что вы в модуле nwmain.text, иначе у вас не чего не выйдет). Жмём F5, когда выскочит
SoftICE.

Это должна быть инструкция по адресу 607C87 MOV AX, [ECX+A8]. Т.е. какое-то значение (ECX)+А8 ссылается на жизнь. Следующим шагом нужно отыскать то место где регистр ECX получает своё значение. Чуть ниже этой инструкции стоит команда RET- это выход из функции. Выполняем пошагово (F8) пока не выйдем из функции, и не уведем следующее:

5D05E1 mov ecx, esi
5D05E3 call [edx+00000009C]

Ставим БР по адресу 5D05E1 командой «bpx 5D05E1» или двойным щелчком на строке и нажимаем F5 несколько раз, пока ESI не будет равен найденному значению минус A8 (10AEFC70-A8=10AEFBC8). Двигаем код в окне вверх (CTRL+вверх), ища, где ESI получает своё значение, должны дойти вот досюда:

005D0071 mov ecx,ebx
005D0073 call [edx+00030]
005D0076 mov esi,eax

Значит, функция возвращает в регистре EAX наше число, но не стоит сразу ковырять внутренности функции, поставив БР по адресу 005D0071, мы увидим, что
EBX=10AEFBC8. Чуть выше видим:

005D0049 mov ebx,[esp+00050]

Снова ставим БР на участок памяти ESP+50 «bpmb esp+20 w»(w означает, что отслеживать только запись по этому адресу). Наберём в командной строке «? ESP+50» чтобы получить точный адрес, затем «d ESp+50» чтобы видеть этот участок. Жмём F5 столько раз, пока на этом участке не появится наше значение в перевёрнутом виде, т.е. C8FBAE10 (руки бы оторвать тому, кто это придумал %).
Как дальше разворачивались дела я не помню, но вы должны выйти на:

CALL 005CF000 (например, по адресу 5D8D6A, 5D8866)

После выполнения этой функции EAX содержит наше число. Значит способ получения этого числа в этой функции. Вот содержимое этой функции
(примечание: все числа после точки с запетой приведены конкретно для моего примера, у вас они могут быть другие):

5CF000:
mov eax,ecx
mov ecx,[000862360] ; ECX=A6BA590 (7)
mov edx,[eax][00030]
mov ecx,[ecx][00004] ; ECX=[A6BA594]=79A6058 (6)
push edx
call .0005BB7E0

5BB7E0:
mov eax,[esp][00004]
mov ecx,[ecx][00004] ; ECX=[79A605C]=7A58008 (5)
push eax
call .0005C2B10 

5C2B10:
push ecx
mov edx,[esp][00008]
mov ecx,[ecx][000010074] ; ECX=[7A6807C]=79F1CB8 (4)
lea eax,[esp][00000]
push eax
push edx
mov d,[esp][00008],000000000
call .0004259D0

4259D0:
mov eax,[esp][00004] ; EAX=20C2
push esi
mov esi,eax
shr esi,01F ; ESI=0
and esi,001
mov edx,eax
and eax,000000FFF ; EAX=0C2
shl esi,00C ; ESI=0
add esi,eax ; ESI=0C2
mov eax,[ecx] ; EAX=[79F1CB8]=7A1EFF8 (3)
mov eax,[eax][esi]*4 ; EAX=[7A1F300]=7BD1E08 (2)
and edx,07FFFFFFF
test eax,eax
pop esi
je .000425A04
cmp [eax],edx
je .000425A13
mov eax,[eax][00008]
test eax,eax
jne .0004259F9
mov ecx,[esp][00008]
mov d,[ecx],000000000
mov al,001
retn 00008
mov edx,[eax][00004] ; EDX=10AEFBC8 (1)

Цифрами в скобках обозначены ключевые моменты кода. Мы видим, чтобы получить нужный адрес надо прочитать DWORD по адресу EAX+4. В свою очередь этот EAX=EAX+ESI*4, где ESI=0C2. И так далее. Чтобы проверить правильность наших рассуждений пишем в командной строке SoftICE’а «?
@(10AEFBC8+A8)», программа должна написать текущее количество жизни (первое число в шестнадцатеричное, а вот второе десятичное). Теперь надо заменить число 10AEFBC8 на то,
что мы получили, т.е. на @(7BD1E08+4). Соединяем все вместе — должно получится «? @(@(7BD1E08+4)+A8)». Аналогичным способом движемся выше до строки обозначенной (7), там адрес задан конкретно. Он будет всегда один и тем же. В итоге у меня получилось так: «? @(@(@(@(@(@(@(@(862360)+4)+4)+10074))+0C2*4)+4)+A8)». Вот в принципе и всё что касается нахождения нашей жизни 😉 Можно уже юзать это совместно с SoftICE’ом.

Но я пошёл дальше и написал трейнер. Здесь я не буду приводить полный исходник, его вы можете взять
здесь, а приведу лишь ключевые моменты программы, дабы не навязывать какой либо язык программирования.

Чтобы иметь возможность изменять данные в чужом процессе (программе) нужно знать её идентификатор процесса. Его можно получить используя функцию NtQuerySystemInformation(5, адрес_буфера, размер_буфера, 0), из библиотеки ntdll.dll. Чем больше процессов запущено, тем больше
нужен буфер. У меня он составляет 7 Кб. Буфер состоит из массива структур ProcessInfo. По 0 смещению задан размер структуры DWORD. А со смещением 3C ссылка на имя процесса в формате UniCODE. По смещению 44 находится идентификатор процесса. Затем открываем этот процесс функцией
OpenProcess(1F0FFF,0,идентификатор_процесса), она возвратит нам хендл на этот процесс. С помощью функции ReadProcessMemory(Handle, адресс_в_процессе_
откуда_читать, адресс_буфера_ куда_записать, сколько_прочитать,
0) сначала читаем по адресу 862360, прибавляем к этому значению 4 и снова читаем по полученному адресу, и т.д. Запись в память производится с помощью WriteProcessMemory(Handle, адрес_куда_записывать, адрес_буфера, сколько_записать, 0).

Процесса заморозки можно добиться, используя вызов по таймеру. Создаём таймер —
SetTimer(hWnd-идентификатор_окна, уникальный_номер, время_в_милисек, 0 или
адрес_процедуры_ которая_будет_вызываться). Если последний аргумент равен 0, то процедуре обработки окна будет приходить сообщение WM_TIMER, если таймеров много то отличить их можно по уникальному номеру, который был задан при создании таймера, он находится в переменной wParam. Не забудьте удалить таймер при выходе KillTimer(hWnd,
handle).

Ну, вот в принципе и всё, осталось нарисовать морду будущей проги и описать несколько функций. 

P.S. Функция NtQuerySystemInformation доступна только NT системам в Win9x нужно юзать CreateToolhelp32Snapshot, Process32First, Process32Next чтобы получить идентификатор нужного процесса.

Оставить мнение

Check Also

Атака NFCdrip использует NFC для передачи данных на сравнительно дальние дистанции

Исследователь продемонстрировал, что NFC можно использовать в преступных целях и на сравни…