Исследование защиты игры Altar Xonix
Скриншот игры Altar Xonix
Altar Xonix - разновидность классический игры в Xonix. В этой игре надо управлять персонажем, чтобы отрезать от игрового поля как можно больше площади и при этом не попасться движущимся противникам. Игра написана более 20 лет назад, но офсайт, как ни странно, еще жив. Только вот никаких упоминаний об этой игре там нет, как и ссылок на загрузку. К счастью, у меня в закромах нашлась какая-то версия. И как вы, наверное, уже догадались, она просит денег.
Забираем с файлообменника дистрибутив, устанавливаем, запускаем, смотрим. Из внешних проявлений триальности в глаза бросается надпись "UnRegistered", а из функциональных - ограничение на 5 уровней в незарегистрированной версии игры.
Основной исполняемый файл ничем не упакован, отправляем его на разбор в дизассемблер. Нам надо найти место, где появляется нехорошая надпись.
Code (Assembler) : Убрать нумерацию
- .text:004036B9 cmp edx, [ebp+var_B0]
- .text:004036BF jg loc_40359E
- ; Проверить флаг регистрации
- .text:004036C5 cmp dword_460AB4, 0
- .text:004036CC jnz short loc_4036F5
- .text:004036CE push 0 ; HGDIOBJ
- .text:004036D0 push 0FFFFFFh ; COLORREF
- .text:004036D5 mov eax, dword_460444
- .text:004036DA push 244h ; int
- .text:004036DF push 2A8h ; int
- .text:004036E4 push offset aUnregistered
- ; "UnRegistered"
- .text:004036E9 mov ecx, [eax+60h]
- .text:004036EC push ecx ; int
- .text:004036ED call sub_45650C
- .text:004036F2 add esp, 18h
- .text:004036F5 loc_4036F5:
- .text:004036F5 dec [ebp+var_90]
- .text:004036FB lea eax, [ebp+var_4]
Code (Assembler) : Убрать нумерацию
- .text:00405A7E cmp dword_459440, 7
- .text:00405A85 jnz short loc_405A93
- ; Игра зарегистрирована
- .text:00405A87 mov dword_460AB4, 1
- .text:00405A91 jmp short loc_405A9A
- .text:00405A93 ; --------------------------------------------
- .text:00405A93 loc_405A93:
- ; Игра в триальном режиме
- .text:00405A93 xor eax, eax
- .text:00405A95 mov dword_460AB4, eax
- .text:00405A9A loc_405A9A:
- .text:00405A9A cmp dword_460AB4, 0
- .text:00405AA1 jnz short loc_405ACB
- .text:00405AA3 mov dword_4593B0, 5
- .text:00405AAD mov dword_4593C8, 5
Теперь задача посложнее. Разберем алгоритм проверки регистрационных данных. Как формируется проверяемое значение 7? Если посмотреть выше в листинге, то там выполняется несколько однотипных фрагментов кода, после каждого из которых счетчик правильных шагов увеличивается на единицу или выполняется условный переход сразу на финальную проверку. Логично предположить, что это и есть алгоритм валидации серийника. Но в самой игре нет никаких инструментов для регистрации, более того, из-за кривой обработки видеорежимов ее нереально запустить под отладчиком, чтобы пройти эти проверки в пошаговом режиме.
Зато рядом с основным исполняемым файлом есть еще одно кривенькое поделие под названием Registration.exe. При его запуске открывается единственное окно с полем ввода серийника. На ввод левых данных регистратор реагирует фирменной надписью "UnRegistered" и ссылкой на покупку.
Сообщение о неправильной регистрации
И вот это чудо под отладчиком прекрасно запускается. Но сперва дизассемблер. Надо найти место, где проверяется серийник и при каких условиях срабатывает регистрация.
Code (Assembler) : Убрать нумерацию
- .text:00401B46 cmp dword_44F380, 7
- .text:00401B4D jnz short loc_401B84
- .text:00401B4F mov [ebp+var_58], 68h
- .text:00401B55 mov edx, offset aRegistered
- ; " Registered"
- .text:00401B5A lea eax, [ebp+var_3C]
- .text:00401B5D call sub_44E0DC
- .text:00401B62 inc [ebp+var_4C]
- .text:00401B65 mov edx, [eax]
- .text:00401B67 mov eax, [esi+2D8h]
- .text:00401B6D call @Controls@TControl@SetText
- ; Controls::TControl::SetText(System::AnsiString)
- .text:00401B72 dec [ebp+var_4C]
- .text:00401B75 lea eax, [ebp+var_3C]
- .text:00401B78 mov edx, 2
- .text:00401B7D call sub_44E19C
- .text:00401B82 jmp short loc_401BEA
- .text:00401B84 ; ---------------------------------------------
- .text:00401B84 loc_401B84:
- .text:00401B84 mov [ebp+var_58], 74h
- .text:00401B8A mov edx, offset aUnregistered
- ; "UnRegistered"
- .text:00401B8F lea eax, [ebp+var_40]
- .text:00401B92 call sub_44E0DC
- .text:00401B97 inc [ebp+var_4C]
- .text:00401B9A mov edx, [eax]
- .text:00401B9C mov eax, [esi+2D8h]
Code (Assembler) : Убрать нумерацию
- .text:004017B0 cmp dword ptr [ebx], 0
- .text:004017B3 jz short loc_4017BC
- .text:004017B5 mov ecx, [ebx]
- ; Получить длину серийника
- .text:004017B7 mov eax, [ecx-4]
- .text:004017BA jmp short loc_4017BE
- .text:004017BC ; ---------------------------------------------
- .text:004017BC loc_4017BC:
- .text:004017BC xor eax, eax
- .text:004017BE loc_4017BE:
- ; Длина серийника равна 12 символам?
- .text:004017BE cmp eax, 0Ch
- ; Нет, переход на финальную проверку
- .text:004017C1 jnz loc_401B46
- .text:004017C7 mov [ebp+var_58], 20h
- ; Увеличить счетчик правильных шагов
- .text:004017CD inc dword_44F380
- .text:004017D3 push 1
Code (Assembler) : Убрать нумерацию
- ; Позиция символа в строке
- .text:004017D3 push 1
- .text:004017D5 push ebx
- .text:004017D6 call unknown_libname_366
- .text:004017DB add esp, 8
- .text:004017DE mov eax, ebx
- .text:004017E0 call @System@AnsiString@Unique$qqrv
- .text:004017E5 mov edx, [ebx]
- .text:004017E7 lea eax, [ebp+var_C]
- .text:004017EA mov dl, [edx]
- .text:004017EC call unknown_libname_367
- .text:004017F1 inc [ebp+var_4C]
- .text:004017F4 mov eax, [eax]
- ; Перевести символ в число
- .text:004017F6 call @Sysutils@StrToInt
- ; Сохранить значение
- .text:004017FB mov edi, eax
- ; Позиция символа в строке
- .text:004017FD push 0Ah
- .text:004017FF push ebx
- .text:00401800 call unknown_libname_366
- .text:00401805 add esp, 8
- .text:00401808 mov eax, ebx
- .text:0040180A call @System@AnsiString@Unique$qqrv
- .text:0040180F mov edx, [ebx]
- .text:00401811 lea eax, [ebp+var_10]
- .text:00401814 add edx, 9
- .text:00401817 mov dl, [edx]
- .text:00401819 call unknown_libname_367
- .text:0040181E inc [ebp+var_4C]
- .text:00401821 mov eax, [eax]
- ; Перевести символ в число
- .text:00401823 call @Sysutils@StrToInt
- ; Сложить два числа
- .text:00401828 add edi, eax
- .text:0040182A lea eax, [ebp+var_10]
- ; Контрольное значение суммы
- .text:0040182D cmp edi, 8
- .text:00401830 setz dl
- .text:00401833 and edx, 1
- .text:00401836 push edx
- .text:00401837 mov edx, 2
- .text:0040183C dec [ebp+var_4C]
- .text:0040183F call sub_44E19C
- .text:00401844 dec [ebp+var_4C]
- .text:00401847 lea eax, [ebp+var_C]
- .text:0040184A mov edx, 2
- .text:0040184F call sub_44E19C
- .text:00401854 pop ecx
- .text:00401855 test cl, cl
- .text:00401857 jz short loc_40185F
- ; Увеличить счетчик правильных шагов
- .text:00401859 inc dword_44F380
После замены пар цифр на нужные значения валидный серийник будет иметь вид "123412413755" или, если хочется красиво, то можно оставить только контрольные значения и последнюю пару: "000003845855". Запускаем регистратор, проверяем наши находки:
Игра успешно зарегистрирована
Все проверки пройдены, регистратор подтверждает их правильность. Запускаем игру, смотрим. Надпись "UnRegistered" пропала, ограничения по уровням тоже нет. Цель достигнута. Теперь можно расслабиться и поиграть.
Игра успешно зарегистрирована
Но, как вы наверное помните, я люблю получать от игр удовольствие, то есть играть со всеми возможными читами, трейнерами, кодами и прочим. Вот и здесь я решил сделать вечные жизни.
Как я упомянул ранее, игра написана очень криво в части работы с видеорежимами. При переключении по Alt-Tab вернуться в игру невозможно, она наглухо виснет. Из-за этого невозможно воспользоваться программами типа ArtMoney, чтобы выявить ячейку памяти с нужным счетчиком, а также невозможно полноценно запустить игру под отладчиком. Остается статический анализ. Открываем readme.txt, читаем.
After every 10.000 points Xoni gets additional life.
То есть в программе должна быть какая-то проверка с участием числа 10000 и следующая за ней операция инкремента счетчика жизней. Переводим десятичные 10000 в шестнадцатеричную систему счисления, получаем 2710h. Поиском по листингу в дизассемблере сразу же натыкаемся на вот такой код:
Code (Assembler) : Убрать нумерацию
- ; Сравнить значение счетчика с 10.000
- .text:004041C7 cmp dword_460A6C, 2710h
- .text:004041D1 jl short loc_4041E1
- ; Увеличить счетчик жизней
- .text:004041D3 inc dword_4593A8
- ; Обнулить промежуточный счетчик
- .text:004041D9 xor edx, edx
- .text:004041DB mov dword_460A6C, edx
- .text:004041E1 loc_4041E1:
- .text:004041E1 mov eax, dword_460444
- .text:004041E6 mov esi, [ebx+4]
Перекрестные ссылки на переменную
Более правильный способ заключается в том, что надо найти все участки кода, где счетчик жизней уменьшается. Для этого в дизассемблере переходим на адрес 004593A8 и открываем перекрестные ссылки на эту переменную. Кстати, там же можно увидеть ее начальное значение 3, что соответствует стартовому количеству жизней. Но вернемся к списку ссылок. Как видите, тут есть несколько команд инициализиации переменной начальным значением 3, команда сравнения, одна ранее найденная команда увеличения счетчика, а также целая пачка команд DEC, то есть уменьшения счетчика на единицу. Кстати, тоже вариант патча, можно заменить начальное значение счетчика жизней при инициализации на какое-нибудь огромное значение.
Но лучше решить проблему радикально. Поочередно проходим по всем ссылкам, где есть команда DEC, и целиком заменяем их на команды-пустышки NOP. Таким образом ни при каких условиях счетчик жизней уменьшаться не будет. Сохраняем изменения, запускаем игру и наслаждаемся процессом.
Просмотров: 748 | Комментариев: 5
Метки: исследование защиты, игры
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(27.09.2023 в 19:29):
А можно взять и положить рядом пропатченный файл, чтобы играть с бессмертием или без него.
user
(27.09.2023 в 19:23):
Тренер удобно сделать в виде лоадера:
T=2000: ; HOW MANY ATTEMPTS IT HAS FOR FINDING THE SEARCH BYTES
; BEFORE QUITTING (default is 8000h)
F=Altar Xonix.exe: ; PROCESS TO PATCH (*OPTIONAL-NAME OF PROCESS FOR LOAD*)
O=trainer!.exe: ; LOADER TO CREATE
R: ; RESUME PROCESS
d=7d0: ; msecs,hex, Resume & delay (7d0h=~2 secs)
P=403FB7/75/EB:;000035B7:
P=403FE0/75/EB:;000035E0:
P=40400C/75/EB:;0000360C:
P=404038/75/EB:;00003638:
P=4045F7/75/EB:;00003BF7:
P=4049C1/75/EB:;00003FC1:
P=404BBF/75/EB:;000041BF:
P=404F53/75/EB:;00004553:
P=40535F/75/EB:;0000495F:
P=407309/75/EB:;00006909:
w=7d0: ;;(~2 secs) ; msecs,hex, if wrong patch -terminate after
B=400000: ; Orig. PE base. If not 0 then use ASLR
h=Altar Xonix Trainer ; Loader messages window title
h1=Now starting trainer\n Ok? ; Loader logo MBox text
n: ; types target filename if errors
$
Это скрипт на ремейка RPP v.1.5.1.006a
(сгенерённый тренер будет работать и в Win7x64+)
T=2000: ; HOW MANY ATTEMPTS IT HAS FOR FINDING THE SEARCH BYTES
; BEFORE QUITTING (default is 8000h)
F=Altar Xonix.exe: ; PROCESS TO PATCH (*OPTIONAL-NAME OF PROCESS FOR LOAD*)
O=trainer!.exe: ; LOADER TO CREATE
R: ; RESUME PROCESS
d=7d0: ; msecs,hex, Resume & delay (7d0h=~2 secs)
P=403FB7/75/EB:;000035B7:
P=403FE0/75/EB:;000035E0:
P=40400C/75/EB:;0000360C:
P=404038/75/EB:;00003638:
P=4045F7/75/EB:;00003BF7:
P=4049C1/75/EB:;00003FC1:
P=404BBF/75/EB:;000041BF:
P=404F53/75/EB:;00004553:
P=40535F/75/EB:;0000495F:
P=407309/75/EB:;00006909:
w=7d0: ;;(~2 secs) ; msecs,hex, if wrong patch -terminate after
B=400000: ; Orig. PE base. If not 0 then use ASLR
h=Altar Xonix Trainer ; Loader messages window title
h1=Now starting trainer\n Ok? ; Loader logo MBox text
n: ; types target filename if errors
$
Это скрипт на ремейка RPP v.1.5.1.006a
(сгенерённый тренер будет работать и в Win7x64+)
ManHunter
(28.08.2023 в 00:34):
Да этих ксониксов за все время нарисовали столько, что не перечислить. Я в ксоникс играл еще на монохромном Robotron'е с псевдографическим терминалом и на "Микроше", который грузился с кассетного магнитофона.
brute
(26.08.2023 в 12:51):
да это же Sex Xonix!
Добавить комментарий
Заполните форму для добавления комментария
К тому же игра в пропатченном виде крути-верти уже покалеченная и оригинал не заменит.
Как вариант, конечно, можно использовать и CRACKER с соответствующим CRK-файлом.
В общем, дело вкуса.