Blog. Just Blog

Исследование защиты игры Altar Xonix

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Темная сторона Силы | Автор: ManHunter
Скриншот игры Altar Xonix
Скриншот игры Altar Xonix

Altar Xonix - разновидность классический игры в Xonix. В этой игре надо управлять персонажем, чтобы отрезать от игрового поля как можно больше площади и при этом не попасться движущимся противникам. Игра написана более 20 лет назад, но офсайт, как ни странно, еще жив. Только вот никаких упоминаний об этой игре там нет, как и ссылок на загрузку. К счастью, у меня в закромах нашлась какая-то версия. И как вы, наверное, уже догадались, она просит денег.

Забираем с файлообменника дистрибутив, устанавливаем, запускаем, смотрим. Из внешних проявлений триальности в глаза бросается надпись "UnRegistered", а из функциональных - ограничение на 5 уровней в незарегистрированной версии игры.

Основной исполняемый файл ничем не упакован, отправляем его на разбор в дизассемблер. Нам надо найти место, где появляется нехорошая надпись.
  1. .text:004036B9                 cmp     edx, [ebp+var_B0]
  2. .text:004036BF                 jg      loc_40359E
  3. ; Проверить флаг регистрации
  4. .text:004036C5                 cmp     dword_460AB4, 0
  5. .text:004036CC                 jnz     short loc_4036F5
  6. .text:004036CE                 push    0               ; HGDIOBJ
  7. .text:004036D0                 push    0FFFFFFh        ; COLORREF
  8. .text:004036D5                 mov     eax, dword_460444
  9. .text:004036DA                 push    244h            ; int
  10. .text:004036DF                 push    2A8h            ; int
  11. .text:004036E4                 push    offset aUnregistered
  12. ; "UnRegistered"
  13. .text:004036E9                 mov     ecx, [eax+60h]
  14. .text:004036EC                 push    ecx             ; int
  15. .text:004036ED                 call    sub_45650C
  16. .text:004036F2                 add     esp, 18h
  17. .text:004036F5 loc_4036F5:
  18. .text:004036F5                 dec     [ebp+var_90]
  19. .text:004036FB                 lea     eax, [ebp+var_4]
Тут проверяется значение переменной dword_460AB4, если там ненулевое значение, то нехорошая надпись не выводится. Поищем код, где эта переменная инициализируется нужным значением. Оно такое единственное.
  1. .text:00405A7E                 cmp     dword_459440, 7
  2. .text:00405A85                 jnz     short loc_405A93
  3. ; Игра зарегистрирована
  4. .text:00405A87                 mov     dword_460AB4, 1
  5. .text:00405A91                 jmp     short loc_405A9A
  6. .text:00405A93 ; --------------------------------------------
  7. .text:00405A93 loc_405A93:
  8. ; Игра в триальном режиме
  9. .text:00405A93                 xor     eax, eax
  10. .text:00405A95                 mov     dword_460AB4, eax
  11. .text:00405A9A loc_405A9A:
  12. .text:00405A9A                 cmp     dword_460AB4, 0
  13. .text:00405AA1                 jnz     short loc_405ACB
  14. .text:00405AA3                 mov     dword_4593B0, 5
  15. .text:00405AAD                 mov     dword_4593C8, 5
Тут проверяется еще одна переменная dword_459440, если ее значение равно 7, то взводится флаг регистрации, в противном случае флаг сбрасывается на нулевое значение. Для достижения эффекта регистрации достаточно заNOPить условный переход по адресу 00405A85 и тогда игра всегда будет считать себя зарегистрированной. Это удобно на случай, если захочется записать игру на флешку.

Теперь задача посложнее. Разберем алгоритм проверки регистрационных данных. Как формируется проверяемое значение 7? Если посмотреть выше в листинге, то там выполняется несколько однотипных фрагментов кода, после каждого из которых счетчик правильных шагов увеличивается на единицу или выполняется условный переход сразу на финальную проверку. Логично предположить, что это и есть алгоритм валидации серийника. Но в самой игре нет никаких инструментов для регистрации, более того, из-за кривой обработки видеорежимов ее нереально запустить под отладчиком, чтобы пройти эти проверки в пошаговом режиме.

Зато рядом с основным исполняемым файлом есть еще одно кривенькое поделие под названием Registration.exe. При его запуске открывается единственное окно с полем ввода серийника. На ввод левых данных регистратор реагирует фирменной надписью "UnRegistered" и ссылкой на покупку.

Сообщение о неправильной регистрации
Сообщение о неправильной регистрации

И вот это чудо под отладчиком прекрасно запускается. Но сперва дизассемблер. Надо найти место, где проверяется серийник и при каких условиях срабатывает регистрация.
  1. .text:00401B46                 cmp     dword_44F380, 7
  2. .text:00401B4D                 jnz     short loc_401B84
  3. .text:00401B4F                 mov     [ebp+var_58], 68h
  4. .text:00401B55                 mov     edx, offset aRegistered
  5. ; "  Registered"
  6. .text:00401B5A                 lea     eax, [ebp+var_3C]
  7. .text:00401B5D                 call    sub_44E0DC
  8. .text:00401B62                 inc     [ebp+var_4C]
  9. .text:00401B65                 mov     edx, [eax]
  10. .text:00401B67                 mov     eax, [esi+2D8h]
  11. .text:00401B6D                 call    @Controls@TControl@SetText
  12. ; Controls::TControl::SetText(System::AnsiString)
  13. .text:00401B72                 dec     [ebp+var_4C]
  14. .text:00401B75                 lea     eax, [ebp+var_3C]
  15. .text:00401B78                 mov     edx, 2
  16. .text:00401B7D                 call    sub_44E19C
  17. .text:00401B82                 jmp     short loc_401BEA
  18. .text:00401B84 ; ---------------------------------------------
  19. .text:00401B84 loc_401B84:
  20. .text:00401B84                 mov     [ebp+var_58], 74h
  21. .text:00401B8A                 mov     edx, offset aUnregistered
  22. ; "UnRegistered"
  23. .text:00401B8F                 lea     eax, [ebp+var_40]
  24. .text:00401B92                 call    sub_44E0DC
  25. .text:00401B97                 inc     [ebp+var_4C]
  26. .text:00401B9A                 mov     edx, [eax]
  27. .text:00401B9C                 mov     eax, [esi+2D8h]
Точно так же выполняется сравнение переменной с семеркой, по результатам сравнения или появляется надпись "Registered" или предложение метнуться в кассу. А выше в листинге точно такие же фрагменты кода с проверками и увеличением счетчика шагов, как и в основной игре. Запускаем регистратор под отладчиком, ставим точку остановка по адресу 004017B0, пробуем зарегистрировать какой-нибудь левотой. Начинается все со следующего кода:
  1. .text:004017B0                 cmp     dword ptr [ebx], 0
  2. .text:004017B3                 jz      short loc_4017BC
  3. .text:004017B5                 mov     ecx, [ebx]
  4. ; Получить длину серийника
  5. .text:004017B7                 mov     eax, [ecx-4]
  6. .text:004017BA                 jmp     short loc_4017BE
  7. .text:004017BC ; ---------------------------------------------
  8. .text:004017BC loc_4017BC:
  9. .text:004017BC                 xor     eax, eax
  10. .text:004017BE loc_4017BE:
  11. ; Длина серийника равна 12 символам?
  12. .text:004017BE                 cmp     eax, 0Ch
  13. ; Нет, переход на финальную проверку
  14. .text:004017C1                 jnz     loc_401B46
  15. .text:004017C7                 mov     [ebp+var_58], 20h
  16. ; Увеличить счетчик правильных шагов
  17. .text:004017CD                 inc     dword_44F380
  18. .text:004017D3                 push    1
Срабатывает первый шаг - проверка длины введенного серийного номера. Он должен быть ровно 12 символов. Перезапускаем программу в отладчике, формируем серийник нужной длины, например, "123456789012". Первая проверка пройдена. Переходим к следующей.
  1. ; Позиция символа в строке
  2. .text:004017D3                 push    1
  3. .text:004017D5                 push    ebx
  4. .text:004017D6                 call    unknown_libname_366
  5. .text:004017DB                 add     esp, 8
  6. .text:004017DE                 mov     eax, ebx
  7. .text:004017E0                 call    @System@AnsiString@Unique$qqrv
  8. .text:004017E5                 mov     edx, [ebx]
  9. .text:004017E7                 lea     eax, [ebp+var_C]
  10. .text:004017EA                 mov     dl, [edx]
  11. .text:004017EC                 call    unknown_libname_367
  12. .text:004017F1                 inc     [ebp+var_4C]
  13. .text:004017F4                 mov     eax, [eax]
  14. ; Перевести символ в число
  15. .text:004017F6                 call    @Sysutils@StrToInt
  16. ; Сохранить значение
  17. .text:004017FB                 mov     edi, eax
  18. ; Позиция символа в строке
  19. .text:004017FD                 push    0Ah
  20. .text:004017FF                 push    ebx
  21. .text:00401800                 call    unknown_libname_366
  22. .text:00401805                 add     esp, 8
  23. .text:00401808                 mov     eax, ebx
  24. .text:0040180A                 call    @System@AnsiString@Unique$qqrv
  25. .text:0040180F                 mov     edx, [ebx]
  26. .text:00401811                 lea     eax, [ebp+var_10]
  27. .text:00401814                 add     edx, 9
  28. .text:00401817                 mov     dl, [edx]
  29. .text:00401819                 call    unknown_libname_367
  30. .text:0040181E                 inc     [ebp+var_4C]
  31. .text:00401821                 mov     eax, [eax]
  32. ; Перевести символ в число
  33. .text:00401823                 call    @Sysutils@StrToInt
  34. ; Сложить два числа
  35. .text:00401828                 add     edi, eax
  36. .text:0040182A                 lea     eax, [ebp+var_10]
  37. ; Контрольное значение суммы
  38. .text:0040182D                 cmp     edi, 8
  39. .text:00401830                 setz    dl
  40. .text:00401833                 and     edx, 1
  41. .text:00401836                 push    edx
  42. .text:00401837                 mov     edx, 2
  43. .text:0040183C                 dec     [ebp+var_4C]
  44. .text:0040183F                 call    sub_44E19C
  45. .text:00401844                 dec     [ebp+var_4C]
  46. .text:00401847                 lea     eax, [ebp+var_C]
  47. .text:0040184A                 mov     edx, 2
  48. .text:0040184F                 call    sub_44E19C
  49. .text:00401854                 pop     ecx
  50. .text:00401855                 test    cl, cl
  51. .text:00401857                 jz      short loc_40185F
  52. ; Увеличить счетчик правильных шагов
  53. .text:00401859                 inc     dword_44F380
Под отладчиком видно, что берется первый символ серийника, переводится в числовое значение, затем берется 10-й символ серийника, переводится в число, после чего эти два числа суммируются. Результат должен быть равен 8. И таких блоков пять. В следующем берется второй и девятый символы, их сумма должна получиться 5. Третий и восьмой символ должны дать в сумме 4. По аналогии следующая пара проверяется на сумму 8, а центральная пара на значение суммы 3. Таким образом у нас вместе с проверкой длины должно получиться 6 правильных шагов. Заключительный седьмой шаг - сумма двух последних символов серийника в числовом представлении, результат должен равняться 10.

После замены пар цифр на нужные значения валидный серийник будет иметь вид "123412413755" или, если хочется красиво, то можно оставить только контрольные значения и последнюю пару: "000003845855". Запускаем регистратор, проверяем наши находки:

Игра успешно зарегистрирована
Игра успешно зарегистрирована

Все проверки пройдены, регистратор подтверждает их правильность. Запускаем игру, смотрим. Надпись "UnRegistered" пропала, ограничения по уровням тоже нет. Цель достигнута. Теперь можно расслабиться и поиграть.

Игра успешно зарегистрирована
Игра успешно зарегистрирована

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

Как я упомянул ранее, игра написана очень криво в части работы с видеорежимами. При переключении по Alt-Tab вернуться в игру невозможно, она наглухо виснет. Из-за этого невозможно воспользоваться программами типа ArtMoney, чтобы выявить ячейку памяти с нужным счетчиком, а также невозможно полноценно запустить игру под отладчиком. Остается статический анализ. Открываем readme.txt, читаем.


After every 10.000 points Xoni gets additional life.


То есть в программе должна быть какая-то проверка с участием числа 10000 и следующая за ней операция инкремента счетчика жизней. Переводим десятичные 10000 в шестнадцатеричную систему счисления, получаем 2710h. Поиском по листингу в дизассемблере сразу же натыкаемся на вот такой код:
  1. ; Сравнить значение счетчика с 10.000
  2. .text:004041C7                 cmp     dword_460A6C, 2710h
  3. .text:004041D1                 jl      short loc_4041E1
  4. ; Увеличить счетчик жизней
  5. .text:004041D3                 inc     dword_4593A8
  6. ; Обнулить промежуточный счетчик
  7. .text:004041D9                 xor     edx, edx
  8. .text:004041DB                 mov     dword_460A6C, edx
  9. .text:004041E1 loc_4041E1:
  10. .text:004041E1                 mov     eax, dword_460444
  11. .text:004041E6                 mov     esi, [ebx+4]
Логика абсолютно прозрачная. Помимо общего счетчика очков в игре есть промежуточный, который отвечает за начисление бонусной жизни при достижении очередных 10.000 очков. Как только нужный результат достигнут, увеличивается переменная по адресу 004593A8 и промежуточный счетчик обнуляется. Один из вариантов достижения бессмертия в игре - заменить пороговое значение счетчика на какое-нибудь небольшое, например, на 100 очков. В этом случае жизни будут накапливаться быстрее, чем тратиться, но иллюзия честной игры все-таки сохранится.

Перекрестные ссылки на переменную
Перекрестные ссылки на переменную

Более правильный способ заключается в том, что надо найти все участки кода, где счетчик жизней уменьшается. Для этого в дизассемблере переходим на адрес 004593A8 и открываем перекрестные ссылки на эту переменную. Кстати, там же можно увидеть ее начальное значение 3, что соответствует стартовому количеству жизней. Но вернемся к списку ссылок. Как видите, тут есть несколько команд инициализиации переменной начальным значением 3, команда сравнения, одна ранее найденная команда увеличения счетчика, а также целая пачка команд DEC, то есть уменьшения счетчика на единицу. Кстати, тоже вариант патча, можно заменить начальное значение счетчика жизней при инициализации на какое-нибудь огромное значение.

Но лучше решить проблему радикально. Поочередно проходим по всем ссылкам, где есть команда DEC, и целиком заменяем их на команды-пустышки NOP. Таким образом ни при каких условиях счетчик жизней уменьшаться не будет. Сохраняем изменения, запускаем игру и наслаждаемся процессом.

Поделиться ссылкой ВКонтакте
Просмотров: 748 | Комментариев: 5

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
user (27.09.2023 в 19:35):
Размер..
К тому же игра в пропатченном виде крути-верти уже покалеченная и оригинал не заменит.

Как вариант, конечно, можно использовать и CRACKER с соответствующим CRK-файлом.

В общем, дело вкуса.
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+)
ManHunter (28.08.2023 в 00:34):
Да этих ксониксов за все время нарисовали столько, что не перечислить. Я в ксоникс играл еще на монохромном Robotron'е с псевдографическим терминалом и на "Микроше", который грузился с кассетного магнитофона.
brute (26.08.2023 в 12:51):
да это же Sex Xonix!

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

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.06 сек. / MySQL: 2 (0.0043 сек.) / Память: 4.5 Mb
Наверх