К циклу статей - пишем трейнер на Delphi_11.
-------------------
Ссылки на части: _часть 1 _часть 2 _часть 3
конвертер hex<->float
-------------------
Привет.
SCUM.v0.9.113.75065, работает, настроена и через Т - консоль взяты необходимые предметы, оружие и обмундирование ;-)
Для начала найдём код отбирающий у нас здоровье.
Запускаем игру, прыгаем с высоты, повреждаем здоровье, лечим (например бинтами или такой штукой #SpawnItem Phoenix_Tears <- антирадиация и лечение), на вкладке здоровья наблюдаем постепенное увеличение числа здоровья, по наведению мыши видно точное число, останавливаем игру.
Запускаем CheatEngine, выбираем тип поиска диапазон - чтоб число входило, тип значения float. Поиск. Далее периодически возобновляя игру - производим отсев по мере увеличения счётчика здоровья, добиваясь приемлемого количества результатов. У меня нашлось одно совпадающее значение.
Копируем адрес, закрываем CheatEngine. Запустим x64dbg, присоединимся к процессу , установим аппаратный бряк на найденный адрес и остановка в коде:
movss dword ptr ds:[rbx+388],xmm1
movss dword ptr ds:[rbx+380],xmm0 | new урон вкладка здоровье
divss xmm0,dword ptr ds:[rbx+378]
comiss xmm0,xmm7
а чуть ниже - запись значения в круговой счётчик (слева в низу игрового экрана )
minss xmm0,xmm8
...
movss dword ptr ds:[rbx+384],xmm0 | new здоровье круговой счётчик
Удаляем аппаратный бряк. Ставим бряки и комментарии чтоб не потерять. Можно понаблюдать изменения значений по адресам [rbx+388] и [rbx+384] в дампе, они находятся рядом. Бряки деактивируем на вкладке точек останова - чтоб не мешали. Патч здоровья будет из двух частей
оригинальный код:
патченный код:
код патча:
call scum.143F0F090
push rax | <-- начало патча здоровья часть 1
mov rax,0x42C80000
mov dword ptr ds:[rbx+0x380],eax |- запись значения на вкладке здоровья
mov rax,0x3F800000
mov dword ptr ds:[rbx+0x384],eax |- запись значения на круговой шкале
pop rax
nop
nop
nop
nop
nop
nop
nop
nop
nop | new урон вкладка здоровье
nop
nop
nop
nop
nop
nop
nop
divss xmm0,dword ptr ds:[rbx+378] | ↑-- конец патча здоровья часть 1 (строкой выше)
... |
movss xmm3,dword ptr ds:[14507E83C]
comiss xmm2,xmm3 (после этой строки)
nop | new здоровье круговой счётчик
nop
nop
nop
nop
nop
nop
nop
jae scum.141E8AD47 | ↑-- патча здоровья часть 2 (нупы выше)
xorps xmm1,xmm1
jmp scum.141E8AD50
вторая часть - занупленная строка movss dword ptr ds:[rbx+384],xmm0 | new здоровье круговой счётчик
и находится через 0x52 байта после начала первой части. Накодили патч, проверяем в игре, прыгаем, ломаем ноги, издеваемся над подопытным, работает - идём дальше (кровотечение по прежнему присутствует, об это далее).
Сохраним байты патча, выделим изменённые нами строки с помощью контекстного меню Двоичные операции->копировать, сохраним в текстовый файл:
//байты патча 1 здоровья
//часть 1
5048C7C00000C84289838003000048C7C00000803F8983840300005890909090909090909090909090909090
//часть 2
9090909090909090
Теперь вычислим адрес уникальной сигнатуры, от которой и будем отсчитывать смещения всех патчей. Восстановим все изменения в коде. Перейдём к месту патча с помощью бряка на вкладке точек останова, у меня с комментом <-- начало патча здоровья часть 1. Выделим четыре строки, идём в меню Модули->SigMake->Create signature и сохраняем в текстовый файл строки из полей - Data и Mask. Нажимаем Scan и убеждаемся в статус строке - что Found 1 references , то есть - результат в одном экземпляре, уникален.
//строка байт для поиска сигнатуры
\x0F\x28\xC8\xF3\x41\x0F\x59\xCC\xF3\x00\x00\x00\x00\x00\x00\x00\xF3
//маска
xxxxxxxxx???????x
У нас есть адрес сигнатуры, байт-строка и маска для поиска сигнатуры, адрес и данные (цепочки байт) для патча.
*****
Здесь можно прерваться , вернуться к статье написания трейнера часть 3, где мы кодим уже рабочие патчи, а после - дочитать о получении остальных данных.
*****
Продолжаем. Теперь выносливость. При полной шкале выносливости ищем в CheatEngine 0x3F800000
Бегая и прыгая уменьшаем число выносливости и параллельно в CheatEngine логично отсеиваем значения. Получив приемлемое количество результатов ( у меня 7), заморозкой выясняем верный.
Запускаем x64dbg, присоединяемся к игре и ставим аппаратный бряк записи на адрес.
Бряк срабатывает, попадаем в этот код:
minss xmm6,xmm11
movaps xmm9,xmm6
movaps xmm6,xmmword ptr ss:[rsp+80]
subss xmm7,xmm12
movaps xmm12,xmmword ptr ss:[rsp+20]
movss dword ptr ds:[rbx+3B0],xmm9 | <-- new усталость
movaps xmm9,xmmword ptr ss:[rsp+50]
а патч будет начинаться со строки subss xmm7,xmm12 и будет такой:
movaps xmm6,xmmword ptr ss:[rsp+80]
push rax | <-- patch 1 выносливость
mov rax,3F800000
mov dword ptr ds:[rbx+3B0],eax
pop rax
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
movss dword ptr ds:[rbx+3B4],xmm7 | ↑-- конец patch 1 выносливость (строкой выше)
То есть - просто вносим нужное значение. Проверяем в работе, выносливость не отнимается. Получим байты патча выносливости для трейнера:
5048C7C00000803F8983B0030000589090909090909090909090
вычислим смещение - адрес начала патча выносливости минус адрес сигнатуры (он же - начало патча здоровья).
Адрес сигнатуры смотрим по бряку - 00007FF6CA50ACE6 | 0F28C8 | movaps xmm1,xmm0 | <-- начало патча здоровья часть 1 (сигнатура)
Пример: 0x7FF6CA50BA03 - 0x7FF6CA50ACE6 = 0xD1D
смещение 0xD1D
Сохраняем всё в текстовый файл, для трейнера. Идём дальше.
**********
Теперь разберёмся с патронами, получаем с помощью консоли Т:
#SpawnItem Weapon_AK47 - калаш
#SpawnItem Magazine_AK47 - магазин
#SpawnItem Cal_7_62x39mm - пару, тройку пачек патронов,
заряжаем магазин, запускаем не патченную игру в CheatEngine. Заражен 31 патрон - это 0x1F , ищем диапазон значений от 0x1D до 0x1F, так как сохраняется в несколько адресов и некоторые значения отличаются на единицу. Далее отстреливая по одному патрону - ищем отсеиванием уменьшающееся значение. Нашли, на скриншоте их пять.
В x64dbg в дампах открываем адреса, если посмотреть внимательно - возле одного из адресов, через 0x10 байт от него находится такое же значение, не попавшее в поиск.
Ставим аппаратные бряки записи на них, стреляем и находим участки кода помеченные на скринах как патроны 3 _2 и патроны 3 _3
Помечаем их бряками и комментами, снимаем аппаратные бряки с этих двух адресов и смотрим - какие из оставшихся адресов изменяются при выстреле, ставим аппаратные бряки записи , стреляем и находим участки кода помеченные на скринах как new патроны 1 первый адрес и патроны 3 _1 , эти срабатывают в цикле. (не удивляйтесь странным комментам, просто их было очень много, в том числе от предыдущей версии ;-)
Помечаем их бряками и комментами и снимает аппаратные. Но мы могли бы попросить CheatEngine Найти инструкции обращающиеся к адресу и он найдёт всё те же участки кода плюс ещё пару тройку.
Патч будет довольно прост, но в трёх местах.
В начало функции с комментом патроны 3 _1 ставим ret
В начало функции с комментом патроны 3 _3 ставим ret
А в коде с комментом патроны 3 _2 выстрел - предварительно занупив строку lea eax,qword ptr ds:[r8-1] пишем в место неё lea eax,[r8] устраняя декремент.
И сохраняем байты и смещения частей патча (адреса у вас понятно будут другие, приведены примеры расчёта):
Адрес сигнатуры для отсчёта 0x140EEACE6
адрес первой части патча
-> 0141225D00 | C3 | ret | patch патроны 1
...
0141225D26 | mov dword ptr ds:[rbx+2D0],eax | патроны 3 _1
байты C3
смещение 0x141225D00 - 0x140EEACE6 = 0x33B01A
---
адрес второй части патча
-> 014122C1C1 | 41:8D00 | lea eax,qword ptr ds:[r8] | patch пароны 2
014122C1C4 | 90 | nop
014122C1C5 | mov dword ptr ds:[rcx+8],eax | патроны 3 _2 выстрел
байты 418D0090
смещение 0x14122C1C1 - 0x140EEACE6 = 0x3414DB
---
адрес третьей части патча
-> 013FB587F0 | C3 | ret | patch патроны 3
...
013FB58813 | 8979 08 | mov dword ptr ds:[rcx+8],edi | патроны 3 _3
байты C3
смещение отрицательное так как адрес выше сигнатуры 0x140EEACE6 - 0x13FB587F0 = 13924F6 то есть -0x13924F6
Данные для патча патронов получены, можно добавить в трейнер
**********
Теперь стрелы. Берём через консоль T - арбалет и пачку болтов:
#SpawnItem Weapon_BlackHawk_Crossbow
#SpawnItem Ammo_Crossbow_Bolt_Carbon
Смотрим в инвентаре количество и ищем в CheatEngine. Отсевиаем до приемлимого кол-ва результатов, у меня 3.Меняя в них значения, определяем верный. Ставим аппаратный бряк записи на найденный адрес, попадаем в код, помечаем его бряком и комментарием - "запись стрелы", а выше находим считывание и вычитание:
0141A5E79C | jmp scum.141A5E7A9
0141A5E79E | mov edi,dword ptr ds:[rcx+94C] | считываются стрелы
0141A5E7A4 | cmp edx,edi
0141A5E7A6 | cmovl edi,edx | отнимаются стрелы
0141A5E7A9 | cmp edi,dword ptr ds:[rcx+950]
0141A5E7AF | je scum.141A5E908
...
0141A5E838 | mov r14,qword ptr ss:[rsp+58]
0141A5E83D | mov rcx,rbx
0141A5E840 | mov dword ptr ds:[rbx+950],edi | запись стрелы
0141A5E846 | mov byte ptr ds:[rbx+958],1
Патч будет прост. Строку mov edi,dword ptr ds:[rcx+94C]
заменим на mov edi,C , а cmovl edi,edx просто занупаем.
Получае следующее:
0141A5E79C | jmp scum.141A5E7A9
0141A5E79E | mov edi,C |<-- считываются стрелы
0141A5E7A3 | nop |<--
0141A5E7A4 | cmp edx,edi
0141A5E7A6 | nop |<-- отнимаются стрелы
0141A5E7A7 | nop |<--
0141A5E7A8 | nop |<--
0141A5E7A9 | cmp edi,dword ptr ds:[rcx+950]
Аппаратный бряк убираем. Ну и сохраняем байты патча и определяем смещения в двух частях:
//адрес сигнатуры для отсчёта
0141D8ACE6 | <-- начало патча здоровья часть 1 (сигнатура)
//часть 1
//байты BF0C00000090
//смещение (отрицательное) 0141D8ACE6 - 0141A5E79E = 32C548
-0x32C548
//часть 2
//байты 909090
//смещение (отрицательное) 0141D8ACE6 - 0141A5E7A6 = 32C540
-0x32C540
В трейнере - добавим их к патчу патронов.
**********
Износ оружия предметов. Используем для этого калаш с патчем патронов.
Запускаем CheatEngine и ищем неизвестное значение 4 байта. Далее постреливая и калаша - ищем уменьшающееся значение, можно пару раз не стреляя, отсеять с типом "не изменилось". Получив приемлемое количество результатов, в CheatEngine переключаем отображение на float
убеждаемся что нашли верный адрес изменив значение например на 0x42480000 = 50% float
В x64dbg устанавливаем аппаратный бряк записи на адрес, делаем выстрел и остановка в коде:
0140FF4AB2 | movaps xmm9,xmmword ptr ss:[rsp+40]
0140FF4AB8 | mov rcx,rbx
0140FF4ABB | movss dword ptr ds:[rbx+1A8],xmm7 | износ оружия вещей
0140FF4AC3 | movss dword ptr ds:[rbx+1AC],xmm6
0140FF4ACB | call scum.141224C60
0140FF4AD0 | test al,al
0140FF4AD2 | jne scum.140FF4B46
Патч будет из 3 частей и размазан по свободным местам между кодом. Занупим строку movss dword ptr ds:[rbx+1A8],xmm7 и напишем первую часть вместо неё.
//1 часть
0140DB4AB8 | mov rcx,rbx
0140DB4ABB | push rax | <- износ оружия вещей
0140DB4ABC | jmp scum.140DB4C12 | <- переход ко 2 части патча
0140DB4AC1 | nop | <-
0140DB4AC2 | nop | <-
0140DB4AC3 | movss dword ptr ds:[rbx+1AC],xmm6
...
//2
0140DB4C10 | pop rdi
0140DB4C11 | ret
0140DB4C12 | mov eax,0x42C80000 | <- 2 часть
0140DB4C17 | mov dword ptr ds:[rbx+0x1A8],eax | <-
0140DB4C1D | jmp scum.140DB4C61 | <- переход к 3 части патча
...
//3
0140DB4C60 | ret
0140DB4C61 | pop rax | <- 3 часть
0140DB4C62 | jmp scum.140DB4AC3 | <- возврат из патча
Убираем аппаратный бряк. Проверяем в игре , пропатченный в X64dbg код, если порядок - то далее.
Получим данные патча:
/// адрес сигнатуры
01411DACE6 | push rax | <-- начало патча здоровья часть 1 (сигнатура)
//часть 1
байты 50E9510100009090
смещение (отрицательное)
1411DACE6 - 140DB4ABB = 42622B
-0x42622B
//часть 2
байты B80000C8428983A8010000EB42
смещение (отрицательное)
1411DACE6 - 140DB4C12 = 4260D4
-0x4260D4
//часть 3
байты 58E95CFEFFFF
смещение (отрицательное)
1411DACE6 - 140DB4C61 = 426085
-0x426085
Сохраняем всё это в текстовый файл и копаем дальше.
*******
Поправим повреждения авто и расход топлива. Берём через консоль T "лайка" (ниву) ; #SpawnVehicle BPC_Laika Максимальное значение топлива для лайка - 0x42480000 = 50.0 float Ищем в CheatEngine диапазон, примерно 42300000 - 42c80000 Далее периодически тратя бензин, отсеиваем значения. Получаем приемлемое их кол-во, у меня примерно около 15. Заглушим двигатель и понаблюдаем за изменениями в CheatEngine, не изменяется одно значение,
сядем в машину не заводя, увеличим значение в CheatEngine и видим изменения топлива на полосе-индикаторе, оно.
В x64dbg ставим аппаратный бряк записи на адрес и заводим двигатель, брякаемся в коде
01419E80F0 | xorps xmm0,xmm0
01419E80F3 | comiss xmm1,xmm0
01419E80F6 | jb scum.1419E8104
01419E80F8 | movss xmm0,dword ptr ds:[rcx+438]
01419E8100 | minss xmm0,xmm1
01419E8104 | movss dword ptr ds:[rcx+590],xmm0 |<- расход топлива
01419E810C | ret
Помечаем код бряком F2 и комментом "расход топлива", отключим аппаратный бряк.
Давайте определим максимальное число топлива, отключим все бряки, возьмём новое авто, активируем бряк "расход топлива" и заводим двигатель, остановка на бряке - movss dword ptr ds:[rcx+590],xmm0 | расход топлива
Видим в регистре xmm0 значение 0x42480000 = 50.0 float, полный бак для "лайка", ну и перейдём в дамп по адресу [rcx+590] , выполним в отладчике F7 один шаг и видим число из xmm0 записано по адресу.
Делаем патч, функция маленькая, занупим её всю оставив только ret , а начиная с первой строки функции пишем код:
push rax
mov eax,0x42480000
mov [rcx+0x438],eax
pop rax
получится примерно так:
01419E80F0 | push rax
01419E80F1 | mov eax,42480000
01419E80F6 | mov dword ptr ds:[rcx+438],eax
01419E80FC | pop rax
01419E80FD | nop
01419E80FE | nop
01419E80FF | nop
01419E8100 | nop
01419E8101 | nop
01419E8102 | nop
01419E8103 | nop
01419E8104 | nop | расход топлива
01419E8105 | nop
01419E8106 | nop
01419E8107 | nop
01419E8108 | nop
01419E8109 | nop
01419E810A | nop
01419E810B | nop
01419E810C | ret
место RET тут не существенно. Проверим патч в игре и сохраним его данные:
///адрес сигнатуры
01416EACE6 | <-- начало патча здоровья часть 1 (сигнатура)
байты патча
50B80000484289813804000058909090909090909090909090909090
смещение
1419E80F0 - 1416EACE6 = 0x2FD40A
Сохраняем в текстовый файл.
*******
Ну и здоровье авто.
В CheatEngine ищем диапазон где то 0x423B8000 - 0x443B8000
Бьёмся машиной о дерево наблюдая уменьшение полосы здоровья и отсеиваем в CheatEngine, при достижении приемлемого количества - бьёмся ещё раз и сразу выходим. В CheatEngine отсеиваем с типом "уменьшилось"
и постояв немного - отсеиваем с типом "изменилось", у меня осталось 3 значения , изменяя значения определяем рабочее.
На скриншоте 43A55211 = 330,6411438 float
В x64dbg ставим аппаратный бряк записи на адрес, стукаемся машиной об дерево и попадаем в код:
0141C82BDF | jmp scum.141C82BED
0141C82BE1 | movss xmm0,dword ptr ds:[rcx+1F8] | - в xmm0 вносит максимальное число здоровья
0141C82BE9 | minss xmm0,xmm3
0141C82BED | subss xmm2,xmm0
0141C82BF1 | andps xmm2,xmmword ptr ds:[144BA3EE0]
0141C82BF8 | comiss xmm2,dword ptr ds:[144FBCE80]
0141C82BFF | ja scum.141C82C06
0141C82C01 | comiss xmm0,xmm1
0141C82C04 | ja scum.141C82C29
0141C82C06 | mov rax,qword ptr ds:[rcx]
0141C82C09 | movss dword ptr ds:[rcx+1FC],xmm0 | здоровье авто
0141C82C11 | call qword ptr ds:[rax+160]
Комментарий "здоровье авто" и бряк на найденной строке, аппаратный бряк удаляем. С помощью бряка "здоровье авто" можно понаблюдать изменение значения в дампе по адресу [rcx+1FC]. Сидя в машине Shift+C открывает инвентарь, нажав 1 переходим к авто и можно посмотреть точный процент здоровья.
Возьмём новое авто, видим несколькими строками выше, в movss xmm0,dword ptr ds:[rcx+1F8] вносится в xmm0 максимальное здоровье 0x443B8000 или изменяя в дампе значение, приходим к 0x443B8000
А патч прост, достаточно занупать minss xmm0,xmm3
0141C82BDF | jmp scum.141C82BED
0141C82BE1 | movss xmm0,dword ptr ds:[rcx+1F8]
0141C82BE9 | nop | <-
0141C82BEA | nop | <-
0141C82BEB | nop | <-
0141C82BEC | nop | <-
0141C82BED | subss xmm2,xmm0
0141C82BF1 | andps xmm2,xmmword ptr ds:[144BA3EE0]
0141C82BF8 | comiss xmm2,dword ptr ds:[144FBCE80]
0141C82BFF | ja scum.141C82C06
0141C82C01 | comiss xmm0,xmm1
0141C82C04 | ja scum.141C82C29
0141C82C06 | mov rax,qword ptr ds:[rcx]
0141C82C09 | movss dword ptr ds:[rcx+1FC],xmm0 | здоровье авто
0141C82C11 | call qword ptr ds:[rax+160] т
Сохраним данные патча:
///адрес сигнатуры
01419BACE6 | <-- начало патча здоровья часть 1 (сигнатура)
байты патча 90909090
смещение
141C82BE9 - 1419BACE6 = 0x2C7F03
*******
Все патчи получены, идём доделывать трейнер , перейти - к часть 3.