Blog. Just Blog

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

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

Игра Peggle Deluxe - увлекательная смесь из арканоида, зумы и пинбола, отличная убивалка свободного времени. Авторство принадлежит знаменитой компании PopCap Games, так что и методы борьбы с первой линией защиты стандартные. Почему с первой? Потому что дальше, как в игре, начинается самое интересное.

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

Сообщение незарегистрированной версии
Сообщение незарегистрированной версии

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

Ограничение незарегистрированной версии
Ограничение незарегистрированной версии

Ограничение незарегистрированной версии
Ограничение незарегистрированной версии

Почему игра требует регистрацию даже после снятия враппера? Ответ прост. Это игра их собственного производства, распространяли авторы ее через свой сайт, навесная защита была тоже их разработки. Совместить внешнюю и внутреннюю проверки регистрации при наличии исходников не составит никакого труда. Ладно, возвращаемся к исследованиям. Исполняемый файл ничем не упакован, отправляем его в дизассемблер. Поиском по строке "Trial" выходим на следующий код:
  1. .text:0041CF05                 mov     large fs:0, eax
  2. .text:0041CF0B                 mov     esi, ecx
  3. .text:0041CF0D                 mov     ecx, [esi+878h]
  4. .text:0041CF13                 test    ecx, ecx
  5. .text:0041CF15                 jz      loc_41D247
  6. ; Записать в регистр EDI значение из переданного аргумента
  7. .text:0041CF1B                 mov     edi, [ebp+arg_0]
  8. .text:0041CF1E                 push    edi
  9. .text:0041CF1F                 call    sub_4038C0
  10. .text:0041CF24                 test    al, al
  11. .text:0041CF26                 jnz     short loc_41CF45
  12. ; Первое значение для сравнения
  13. .text:0041CF28                 cmp     edi, 0Bh
  14. .text:0041CF2B                 jz      short loc_41CF45
  15. ; Второе значение для сравнения
  16. .text:0041CF2D                 cmp     edi, 9
  17. .text:0041CF30                 jnz     loc_41D247
  18. .text:0041CF36                 mov     ecx, esi
  19. .text:0041CF38                 call    sub_41CE00
  20. .text:0041CF3D                 test    al, al
  21. .text:0041CF3F                 jnz     loc_41D247
  22. .text:0041CF45 loc_41CF45:
  23. .text:0041CF45                 mov     eax, [esi]
  24. .text:0041CF47                 mov     edx, [eax+134h]
  25. .text:0041CF4D                 push    0Eh
  26. .text:0041CF4F                 mov     ecx, esi
  27. .text:0041CF51                 call    edx
  28. ; Записать в EAX значение из EDI
  29. .text:0041CF53                 mov     eax, edi
  30. .text:0041CF55                 sub     eax, 9
  31. .text:0041CF58                 jz      loc_41D11C
  32. .text:0041CF5E                 sub     eax, 1
  33. .text:0041CF61                 jz      loc_41D058
  34. .text:0041CF67                 sub     eax, 1
  35. ; Если после всех вычитаний осталось ненулевое значение, то перепрыгнуть 
  36. ; вывод триальных окон
  37. .text:0041CF6A                 jnz     loc_41D247
  38. .text:0041CF70                 push    offset aClose   ; "Close"
  39. .text:0041CF75                 lea     ecx, [ebp+var_98]
  40. .text:0041CF7B                 call    sub_40D9B0
  41. .text:0041CF80                 push    offset aTheRestOfAdven
  42. ; "The rest of Adventure mode is locked in"...
  43. .text:0041CF85                 lea     ecx, [ebp+var_60]
  44. .text:0041CF88                 mov     [ebp+var_4], 9
  45. .text:0041CF8F                 call    sub_40D9B0
  46. .text:0041CF94                 push    offset aTrialVersion
  47. ; "Trial Version"
  48. .text:0041CF99                 lea     ecx, [ebp+var_B4]
  49. .text:0041CF9F                 mov     byte ptr [ebp+var_4], 0Ah
  50. .text:0041CFA3                 call    sub_40D9B0
  51. .text:0041CFA8                 push    3
  52. .text:0041CFAA                 lea     eax, [ebp+var_98]
  53. .text:0041CFB0                 push    eax
Поясню словами, что тут происходит. Есть функция, одним из параметров которой является числовое значение. В процессе проверок из него вычитается 11 (0Bh в шестнадцатеричной системе счисления), если оставшееся значение больше нуля, то сообщения о триальных ограничениях не выводятся. Если значение нулевое, то выводится. Тут патчить ничего не будем, так как получатся только местные косметические правки. По перекрестным ссылкам находим вызов функции, в которой одним из параметров передается значение 0Bh:
  1. .text:004AFD75                 mov     ecx, [esi+4]
  2. ; Вызывать функцию проверки
  3. .text:004AFD78                 call    sub_405D90
  4. ; Если AL=0, то перепрыгнуть вывод триального сообщения
  5. .text:004AFD7D                 test    al, al
  6. .text:004AFD7F                 jz      short loc_4AFD9E
  7. .text:004AFD81                 mov     ecx, [esi+4]
  8. ; Показать триальное сообщение
  9. .text:004AFD84                 push    0Bh
  10. .text:004AFD86                 call    sub_41CEE0
  11. .text:004AFD8B                 mov     ecx, [ebp+var_C]
  12. .text:004AFD8E                 mov     large fs:0, ecx
  13. .text:004AFD95                 pop     ecx
  14. .text:004AFD96                 pop     edi
  15. .text:004AFD97                 pop     esi
  16. .text:004AFD98                 mov     esp, ebp
  17. .text:004AFD9A                 pop     ebp
  18. .text:004AFD9B                 retn    4
  19. .text:004AFD9E ; -----------------------------------------------------
  20. .text:004AFD9E loc_4AFD9E:
  21. .text:004AFD9E                 mov     eax, [esi+4]
  22. .text:004AFDA1                 mov     dword ptr [eax+760h], 1
  23. .text:004AFDAB                 mov     ecx, [esi+4]
  24. .text:004AFDAE                 mov     edx, [esi+30h]
Посмотрим функцию проверки sub_405D90. По перекрестным ссылкам видно, что она вызывается из нескольких мест. Велик соблазн впечатать в нее начало что-то типа XOR EAX,EAX и RET, но пока не будем этого делать, а выясним, при каких условиях она возвращает правильный результат. Для наглядности я немного сокращу код.
  1. .text:00405D90 sub_405D90      proc near
  2. .text:00405D90                 push    esi
  3. .text:00405D91                 mov     esi, ecx
  4. ; Вызывать функцию проверки
  5. .text:00405D93                 call    sub_4057D0
  6. .text:00405D98                 test    al, al
  7. ; Если AL!=0, то вернуть нужный результат
  8. .text:00405D9A                 jnz     loc_405E28
  9. .text:00405DA0                 mov     ecx, esi
  10. .text:00405DA2                 call    sub_4057F0
  11. .text:00405DA7                 test    al, al
  12. .text:00405DA9                 jz      short loc_405DAF
  13. .text:00405DAB                 mov     al, 1
  14. .text:00405DAD                 pop     esi
  15. .text:00405DAE                 retn
  16. .text:00405DAF ; -----------------------------------------
  17. .text:00405DAF loc_405DAF:
  18. .text:00405DAF                 mov     ecx, [esi+878h]
  19. .text:00405DB5                 test    ecx, ecx
  20. ...
  21. ...
  22. ...
  23. .text:00405E23                 cmp     [ecx+30h], edx
  24. .text:00405E26                 jge     short loc_405E2A
  25. .text:00405E28 loc_405E28:
  26. ; Правильный результат проверки
  27. .text:00405E28                 xor     al, al
  28. .text:00405E2A loc_405E2A:
  29. .text:00405E2A                 pop     esi
  30. .text:00405E2B                 retn
  31. .text:00405E2B sub_405D90      endp
Тут первым делом вызывается функция следующего уровня вложенности, если она вернула в регистре AL не нулевое значение, то выполняется условный переход сразу же на правильный результат. Она совсем небольшая:
  1. .text:004057D0 sub_4057D0      proc near
  2. .text:004057D0                 mov     ecx, [ecx+830h]
  3. .text:004057D6                 test    ecx, ecx
  4. .text:004057D8                 jz      short loc_4057DF
  5. .text:004057DA                 jmp     sub_431760
  6. .text:004057DF ; ---------------------------------------
  7. .text:004057DF loc_4057DF:
  8. .text:004057DF                 xor     al, al
  9. .text:004057E1                 retn
  10. .text:004057E1 sub_4057D0      endp
Тут никаких вложенных вызовов нет, но по перекрестным ссылкам видно, что эта функция вызывается из достаточно большого количества мест. Впечатываем в ее начало по адресу 004057D0 пару команд MOV AL,1 и RET, сохраняем изменения, проверяем. Теперь никаких сообщений не появляется, все помощники и уровни доступны.

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

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

Основной игровой ресурс - шарики, с них и начнем. Дизассемблер не закрываем, он еще понадобится. Запускаем игрушку в отладчике, параллельно запускаем инструмент для поиска и отслеживания изменений значений в памяти - ArtMoney.

Отслеживание изменения значения в памяти
Отслеживание изменения значения в памяти

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

Ставим точку останова на запись в память
Ставим точку останова на запись в память

По определенному ранее адресу устанавливаем хардварную точку останова на запись DWORD. Теперь при любом изменении количества шариков мы узнаем место в коде, где это происходит. Отпускаем игру на выполнение и играем до потери шарика.

Точка останова сработала
Точка останова сработала

Когда точка останова сработает, мы окажемся на адресе, где в ячейку памяти заносится новое значение. Как видим, тут к ячейке памяти прибавляется значение регистра ECX. Я не любитель изучать код в отладчике, поэтому переходим в дизассемблер.
  1. .text:0045D880                 push    ebp
  2. .text:0045D881                 mov     ebp, esp
  3. .text:0045D883                 push    esi
  4. .text:0045D884                 mov     esi, ecx
  5. .text:0045D886                 mov     eax, [esi+128h]
  6. ; Записать в регистр ECX значение из параметров
  7. .text:0045D88C                 mov     ecx, [ebp+arg_0]
  8. ; Увеличить количество шаров на переданное значение
  9. .text:0045D88F                 add     [esi+eax*4+17Ch], ecx
  10. .text:0045D896                 test    ecx, ecx
  11. .text:0045D898                 lea     eax, [esi+eax*4+17Ch]
  12. .text:0045D89F                 jle     short loc_45D8D0
  13. .text:0045D8A1                 cmp     [ebp+arg_8], 0
  14. .text:0045D8A5                 jz      short loc_45D8BE
  15. .text:0045D8A7                 mov     ecx, [ebp+arg_4]
  16. .text:0045D8AA                 mov     edx, [esi+104h]
  17. .text:0045D8B0                 push    ecx
Вот тут все гораздо нагляднее. Операция изменения количества шариков выделена в отдельную функцию. Второе слагаемое в регистр ECX заносится из параметров, передаваемых этой функции. Осталось выявить все места, где эта функция вызывается и найти код, где слагаемое имеет отрицательное значение. Надеюсь, вам не надо напоминать школьный курс математики и объяснять, почему именно так. По перекрестным ссылкам на вызываемую функцию выходим на следующий код:
  1. .text:0046AD82                 push    1
  2. .text:0046AD84                 push    0
  3. ; Регистр EDI = -1
  4. .text:0046AD86                 or      edi, 0FFFFFFFFh
  5. .text:0046AD89                 push    edi
  6. .text:0046AD8A                 mov     ecx, esi
  7. ; Изменить количество оставшихся шариков
  8. .text:0046AD8C                 call    sub_45D880
Это единственный вызов, где в параметрах присутствует отрицательное значение. Тут надо заменить второе слагаемое, заменив его значение на нулевое. Для этого патчим команду по адресу 0046AD86 на XOR EDI,EDI и не забываем забить NOP'ами ее остаток, чтобы не нарушить работоспособность кода. Теперь вместо вычитания количество шариков будет складываться с нулем, тем самым даруя вечную жизнь. А вот при получении бонусных шаров все будет отрабатывать как прежде. Сохраняем изменения, проверяем. Визуально количество шаров в обойме меняется, но в числовом значении их потери не учитываются.

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

Возвращаемся в ArtMoney, после активации зеленого шарика и получения бонусного пакета находим адрес в памяти, где записано количество оставшихся плюшек. Действия аналогичны тем, что описаны выше. Ставим в отладчике точку останова на изменение значения.

Точка останова сработала
Точка останова сработала

Тут для уменьшения значения также используется команда сложения с числом -1. В отладчике проверяем, что это не отдельная функция, а просто кусочек линейного кода.
  1. .text:00461585                 mov     ecx, [esi+128h]
  2. .text:0046158B                 lea     eax, [edi+edi]
  3. .text:0046158E                 add     ecx, eax
  4. .text:00461590                 cmp     dword ptr [esi+ecx*4+1D4h], 0
  5. .text:00461598                 lea     ecx, [esi+ecx*4+1D4h]
  6. .text:0046159F                 jle     short loc_4615A4
  7. ; Уменьшение количества бонусных действий
  8. .text:004615A1                 add     dword ptr [ecx], 0FFFFFFFFh
  9. .text:004615A4 loc_4615A4:
  10. .text:004615A4                 mov     edx, [esi+128h]
  11. .text:004615AA                 add     edx, eax
  12. .text:004615AC                 cmp     dword ptr [esi+edx*4+1D4h], 0
  13. .text:004615B4                 mov     ecx, esi
  14. .text:004615B6                 setnz   al
  15. .text:004615B9                 push    eax
  16. .text:004615BA                 push    edi
Забиваем NOP'ами всю команду по адресу 004615A1, после такой замены уменьшение количества бонусных действий выполняться не будет. Сохраняем финальные изменения, проверяем, все работает как надо. Все цели достигнуты, теперь можно играть в свое удовольствие.

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

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

Комментарии

Отзывы посетителей сайта о статье
Серый (16.09.2022 в 11:30):
Почитать описания и посмотреть скриншоты некоторых игр на официальном сайте PopCap (не обязательно):

http://web.archive.org/web/201...mes/pc-games
ManHunter (16.09.2022 в 11:21):
Спасибо!
Серый (16.09.2022 в 11:19):
85 PopCap Games official links
https://pastebin.com/tDrHB0ij
ManHunter (16.09.2022 в 10:59):
Серый, вот еще бы какую-нибудь стартовую страницу со списком установщиков, и было бы вообще замечательно.
Серый (16.09.2022 в 10:15):
Игра, как и все 85 установщиков игр PopCap, до сих пор доступна по официальному адресу:

static-www.ec.popcap.com/binaries/popcap_downloads/PeggleSetup.exe

Оптимальным вариантом регистрации обычному пользователю (всех 85 игр PopCap) является

Universal.PopCap.keygen.v1.2-THETA.zip
NobootRecord (08.09.2022 в 19:48):
Ооооо, легендарная игра, рубился в неё несколько лет назад на ноутбуке с Intel Pentium IV и Windows XP. Помнится, даже играл в пиратскую версию Peggle Nights Deluxe с русским интерфейсом. Это был 2017-2018 год, классные времена были...

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

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

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