Из журнала ZX Format #3, Санкт-Петербург, 03.1996 Пособие для мелкого пакостника или КАК СТАТЬ БЕССМЕРТНЫМ (C) Ржавельщик (А.А.М.) 1996 ________________________________ Итак, Вы успешно переписали игру на диск. Однако порубившись в нее часа четыре, сделали вы- вод, что если бы попыток было бы побольше, то, несомненно, джой- стик остался бы еще жив, а Га- лактика уже спасена. Отсюда де- лается простой, как кассетная нейтронная боеголовка, вывод: Ваша черная работа еще не закон- чена. Придется снова грузить мо- нитор и шарить голодными глазами по килобайтам программы. Здесь глобальное преимущество имеют владельцы Скорпионов, т.к. они могут оперировать по живому, сразу видя результат. Для прочих требуется преодолеть барьер де- компрессии и ксорки (т.е. прос- матривать игру нужно такой, ка- кой она является во время ее ра- боты). Для этого можно использо- вать сброс MAGIC'ом и программу @-CRACK (возможно, что полученый файл не будет работать как игра, но этого и не требуется, - он нужен только для просмотра). Ес- ли LOADER бейсиковый, то следует вместо последнего RANDOMIZE USR поставить выход в монитор. Тяже- лее всего будет владельцам 48К (неужели еще есть 48 с дисково- дом?), им советую пользоваться MONS - подобным монитором, кото- рый помещается в экранной облас- ти. Итак приступим. Первое: опре- делим диагноз.Допустим, мы ус- тановили, что игра страдает острой жизненной недостаточ- ностью. Тогда надо определить метод лечения - прибавление на- чальных ресурсов, либо удаление процедуры вычитания (чтобы знать когда остановиться, иначе можно переборщить и вместо бесконечной энергии получить,например,BATTLE COMMAND, в котором танк спокойно проходит сквозь все объекты, за- кончить игру за 5 минут и выбро- сить). Кстати, поиск бессмертия - лучший способ освоить ассемблер, однако если Вы совсем не знакомы с данным предметом, то придется трудновато. Посему запишите и запомните основные команды, на которые Вам придется обращать внимание. код: мнемоника: #3E,#DD: LD A,#DD регистр А=#DD (под #DD и #DDDD подразумевается любое число одно- и двубайтное ) #32,#DD,#DD: LD (#DDDD),A эквивалент POKE adr,A #3A,#DD,#DD: LD A,(#DDDD) LET A=PEEK adr #21,#DD,#DD: LD HL,#DDDD ---- #D6 #DD: SUB #DD A=A-#DD #3D: DEC A декремент А: А=А-1 #3C: INC A инкремент А: А=А+1 #35: DEC (HL) | Соответственно #34: INC (HL) | dec и inc содер- жимого ячейки памяти по адресу, который находится в HL. Далее - команды ассемблера CALL adr, JP adr и JR adr. Соот- ветственно - GO SUB adr и GO TO adr. Последние две команды раз- личаются способом задачи адреса - в JP он пишется напрямую, а в JR - по смещению на определенное кол-во байт. Это команды безус- ловного перехода. Есть команды условного перехода - т.е. пере- ход произойдет, если соблюдено определенное условие. Условием является состояние соответствую- щего бита регистра F -"флага". Наиболее часто используют два флага: Z - флаг "0" и C - флаг переноса. Флаг нуля поднимается, если в ходе расчета был получен результат = 0. Флаг переноса - если, например, большее число вычитается из меньшего (0-1). Команды выглядят соответственно так: JP Z adr, JP C adr - если переход происходит при поднятом флаге и JP NZ adr, JP NC adr - при сброшенном. Флаг определяет- ся по результату последней опе- рации, которая на него влияла. Команды переходов на флаги не влияют. Запомнили? Тогда идем дальше. Допустим, Вы хотите заморо- зить параметр, который задается конкретным числом, - количество жизней, например (если параметр показывается на экране полоской, то его численное значение опре- делить сложно). Запишите коли- чество жизней и все остальные числовые параметры, а также все надписи, сохранив шрифт (заглав- ные буквы имеют иные коды, неже- ли строчные). Теперь загрузите игру в ее рабочем виде и запус- тите монитор. Все игры при стар- те устанавливают свои начальные параметры, т.е. кол-во жизней, энергии, времени и т.д. Ввиду этого нужно для начала поискать процедуры стартовой настройки. Самое простое: введите поиск последовательности типа #3E NN #32 - это переводится как LD A, NN: LD (addr), A; здесь NN - количество жизней. То есть по адресу addr помещается коли- чество жизней. Возьмем для примера REX1.Там имеется 4 жизни и 99 единиц за- щитного поля. Комбинация #3E 04 #32 встречается там не один раз. Найдя ее, нужно осмотреть ок- рестности на предмет начальных установок других параметров. Ос- мотр показывает, что наиболее подозрительным местом будет ад- рес, по которому расположено следующее: LD A,#04 LD (#A063),A LD A,#02 LD (#A06C),A CALL #BA49 LD A,#63 <-- #63=99 ! LD (#A04D),A Очевидно, что это начальная установка, т.к. здесь выставля- ется не только кол-во жизней, но и энергия защиты (99 единиц). Тогда задаем поиск ссылок на ад- рес A063. Запомните один момент: в кодах младший байт числа идет первым (если число двухбайтное), т.е. искать нужно комбинацию #63,#A0. Ссылки на означенную комбинацию встречаются раз 5. Для нас имеют значение только те, которые изменяют содержимое памяти по этому адресу. Такой фрагмент имеется только один: LD A,(#A063) DEC A <-адрес #9C79=40057 LD (#A063),A CP #FF JP Z,9C.. Здесь мы видим, что если ко- личество запасных жизней станет меньше 0, произойдет переход на другой адрес. Это место уже нас- только подозрительно, что можно попробовать этот адрес (например поставить в загрузчике, перед последним RANDOMIZE USR, команду POKE 40057,0). Для окончательно- го приговора нужно посмотреть, что находится там, куда идет пе- реход под флагом Z. Там мы видим такое: LD HL,#AAED LD B,1 CALL #A13E Запомните, что комбинации по- добного вида обычно используют для печати сообщений. Смотрим дамп по адресу #AAED. А там на- ходятся 3 байта служебных симво- лов и текст: "GAME OVER. Вывод: подозреваемая процедура виновна в лишении жизней и приговарива- ется к коррекции путем замены DEC A на NOP (т.е. 0). Все. Вы бессмертны. На эту процедуру можно было выйти и по надписи GAME OVER. Это делается так: находим адрес надписи (т.е. адрес в памяти с которого начинаются ASCII коды надписи), затем ищем его упоми- нания. Если ссылок на адрес не найдено, то ищем упоминание ад- реса на 1 меньше. В данном при- мере адрес сдвинут на три байта назад. Найдя ссылку (осмыслен- ную) на адрес, ищем начало под- программы, в которой есть эта ссылка, затем ищем упоминание адреса входа в эту подпрограмму. Процедура вычитания жизней должна иметь выход на печать со- общения о том, что вычитать уже нечего (для данного примера - GAME OVER). Если охота, то можно еще бо- лее облегчить свою жизнь путем установки бесконечной защиты. Ищем упоминание адреса #A04D (4D A0). Его упоминают также не еди- ножды. Процедура, определенно уменьшающая содержимое памяти по данному адресу тоже только одна. LD A,(#A04D) SUB L JP C,#99EF LD (#A04D),A <-#99E8=39400 CALL #99FA RET SUB L - вычитание из А содер- жимого регистра L. Опыт показы- вает, что в данной ситуации удобнее убрать занесение нового значения в память - например за- менить #32 на #3A (это не внесет сбоев в работу программы). Тогда по адресу #99E8 будет LD A,(#A040). Либо можно заменить адрес на адрес в области ПЗУ - для этого достаточно стереть старший байт адреса (POKE 39402, 0). Последнее займет меньше мес- та в строке загрузчика, ввиду этого оно предпочтительней. Для наглядности посмотрим еще один пример - ARCANOID-2 В этой игре на первый взгляд дается три жиз- ни, однако поиски 3E 03 32 дают один подозрительный адрес: #9DE4. Если поискать его упоми- нания, то можно найти процедуры, которые меняют содержимое ячейки с данным адресом, однако делают это весьма странно: LD A,(#9DE4) SRL A INC A LD (#9DE4),A Это явно не уменьшение на 1, к тому же - нет проверки усло- вий. Остается предположить, что количество жизней не 3. Рассмот- рите внимательно игру: перед каждым вбрасыванием мячика вычи- тается одна попытка, а это зна- чит, что так происходит и при старте игры - т.е. на самом деле жизней 4. Ищем 3E 04 32 - имеет- ся два упоминания, и оба они по- хожи на начальные установки. Под подозрение попадают два адреса: #7F86 и #7815. Далее ищем ссылки на подозреваемых. #7F86 упомина- ется 3 раза (начальное занесение не считается), в числе упомина- ний: LD HL,#7F86;INC(HL) - похоже на прибавление одной жизни. LD HL,#7F86 DEC (HL) <-#926C=37484 JP NZ #82DC JP #FC7B Эта комбинация достаточно по- дозрительна, можно попробовать. Когда используется команда DEC (HL), ее нужно заменять на OR (HL) - код #B6=182. Проверка по- казала, что адрес верен (про- верьте сами). Если Вам уже все понятно, то сбрасывайте машину и - в бой. Для оставшихся я дам несколько практических советов: При поиске ссылок на сообще- ния будьте терпеливы - иногда отступать приходится очень дале- ко, например, в одной ленточной версии HERO QUEST пришлось отступить от надписи аж на 14 байт. Иногда сообщения печатают- ся из таблицы - тогда при вызове печати задается начальный адрес таблицы и номер сообщения (пос- мотрите внимательно REX1, там сделано именно так). Иногда над- пись выводит программка, которая стоит прямо перед текстом, - в этом случае адрес сообщения не указывается, но тогда такая программка должна стоять перед любой отдельной фразой. Иногда встречаются такие феноменально безграмотные игры, которые имеют множество почти одинаковых про- цедур отъема энергии (STORM HAWK, LICENSE TO KILL). Как пра- вило, здесь одна процедура - один вид опасности. Не стесняйтесь пользоваться методом перебора, - если дело не движется, то отыщите все подоз- рительные места и заморозьте их по очереди. Старайтесь искать легкий путь - всегда просмотрите полный дамп программы - так иногда попадают- ся секретные коды и пароли. На разных стадиях загрузки потыкай- те в клавиши, - бывает, что ха- керы, делавшие данную версию ставят тайный CHEAT MODE, напри- мер в 48 утюгов нужно при старте нажать и держать до конца заг- рузки клавиши, составляющие наз- вание одного из альбомов группы SEPULTURA, а в DIZZY Y (free copy) - FCM при загрузке второй части. Когда для вычитания применяют DEC A, то стирать лучше его. Ес- ли DEC (HL) - лучше заменить ад- рес, саму команду можно менять только на OR (HL) (код #B6=182), иначе будете получать GAME OVER сразу. Когда используется SUB dd (обычно SUB 1), нужно заменить аргумент на 0 (получить SUB 0). Если Вы не уверенны в коли- честве жизней - пробуйте оба числа. Иногда в программах при- меняются защитные меры - напри- мер проверка состояния основных процедур или дублирующие проце- дуры, которые выглядят иногда достаточно мирно, в то время, как "пустая" - буквально кричит о себе. Самое трудное - когда очевид- ных процедур присвоения и печати нет (например, Silk Worm). В та- ком случае можно скинуть игру при разных количествах жизней и методом сравнения найти адрес счетчика. Или, - только для ZS- 256, - использовать функции мо- нитора move и ch. (перенести ку- сок игры в дополнительное ОЗУ и сравнивать значения в разных по- ложениях). Когда значение показывают по- лоской на экране (скажем, уро- вень топлива), то оно может из- менятся совсем не так, как ка- жется. Так в TRANTOR энергия не вычитается, а прибавляется. Пожалуй, последнее - помните, что в ассемблере есть множество команд, и занести значение в па- мять можно разными способами (это делается через индексные регистры, через LDIR, через стек и т.д.). P.S. Вышеизложенная информа- ция отнюдь не претендует на пол- ноту, однако очень поможет Вам сделать первые шаги на хакерской тропе. Заниматься этим вслепую - чрезвычайно сложно, знаю по сво- ему опыту. Мне не приходилось видеть "Пособие для взломщиков" издан- ное типографским способом, а это - единственная книжка на данную тему, которая мне попадалась. Главный совет один: учите ас- семблер и тогда ни одна програм- ма не устоит перед Вами. Кстати, возьмите на вооруже- ние: игры одной фирмы имеют обычно почти идентичную структу- ру. P.P.S. Пожалуй, самая инте- ресная уловка сделана в XONIX(в одном из наших), - там коли- чество жизней определялось кодом числа,напечатанного на экране (ASCII код). ________________________________ Из журнала ZX Format #4, Санкт-Петербург, 15.06.1996 IMMORTAL #2 (C) Ржавельщик. ________________________________ Вместо предисловия. Продолжать эту рубрику изна- чально не входило в мои планы, однако недавно я наткнулся на интересную фразу в ON-LINE, а именно: "...прочитал вышеизло- женное и понял,что вряд-ли когда смогу объяснить что-нибудь не хакеру..." (за точность цитаты не ручаюсь, но смысл такой). После этого перечел я свой опус... м-да, некоторые пробелы имеются. Для начала закончу. В первой части я забыл упомянуть об одной маленькой гадости, которая носит название "двоично - десятичное представление чисел". Что кроет- ся за этим заковыристым терми- ном? А вот что: иногда, для удобства расчетов или для неких других целей число приводится в особый вид при помощи команды DAA (#27). При таком представле- нии полубайты содержат код деся- тичной цифры от 0 до 9. Таким образом в байт можно поместить числа до #99. Чaсто программу строят так, что она работает с "квазидесятичной" системой сче- та. Это значит, что содержимое регистра следует считать деся- тичным, т.е. #35=35 #99=99 и т.д. Короче, если в начале игры дается время 60 секунд, то ис- кать следует не #3e #3c #32 (LD a,60...), а #3e #60 #32 т.е. LD a,#60 !!!. Очень популярен сей изврат в программах с часами (в основном - в гонках). Пример: в Wec le Mons на старте счетчик показывает 66.0 с. Поиск LD A,66 не дал результата. Применив кон- цепцию двоично - десятичного представления (BCD) ищем LD A,#66... Облом - нет такой буквы в этом слове. Отсюда следует утюжок, что секунды выставляются вместе с десятыми долями, через регистровую пару. Следует отметить, что так как стандартные команды обработки двубайтных чисел здесь не приме- няются, на intel'овский стандарт расположения "длинного" числа в памяти часто забивают и байты могут стоять хоть в шахматном порядке. Найденная в конечном итоге процедура установки време- ни выглядела так: LD HL,#1066 LD (addr),HL Ну вот, пожалуй на этом можно закончить вводный курс анатомии программ для начинающих хакеров. Напоследок скажу только, что на- иболее сложную структуру обычно имеют текстовушки (если они в кодах) и вообще всяческие адвен- тюры, а самую неудобоваримую - откомпилированные с BASIC'а. ________________ А теперь, собственно, то, ра- ди чего я затеял продолжение этой статейки. Полагаю, что многие, воодуше- вившись перспективой легкой жиз- ни, лихо сломали все свои (и чу- жие) игры, вошли во вкус и, из- ломав в пыль все, до чего сумели дотянутья, уперлись дебугером в такой риторический вопрос: Куда ставить будем? А по этому поводу "...у меня есть библиотечка!". Возможно Вы уже попробовали спо- соб выбора INFINIT'а при помощи ADM'а (не слишком удобно, а?), при помощи теневика scorpoin'а (кхм... удобно конечно, но все же как то...) или сделали самую большую подборку игр жанра "щас я вам всем покажу". Дальнейшее посвящается вопро- су - куда будем ставить. Однако сразу скажу, что я как не соби- рался переписывать брошюрку "тайники ZX", так и не собираюсь писать пособие "Как сделать интру". Интрами я вообще не ба- луюсь (почти). Надеюсь, что не оскорблю Вас в лучших чувствах, если покажу как делается выбор бессмертия из BASIC'а. Наверняка Вам приходилось видеть в ленточ- ных версиях такой вариант: лоа- дер стопится нажатием на BREAK; из строки, содержащей REM и POKE's, удаляется REM и лоадер запускается снова. В дисковой версии LOAD'ер не очень то тор- мознешь, ввиду этого можно вста- вить в него такие строки: 30 PRINT "inf.lives ? (Y/N)" 40 LET K$=inkey$:IF K$="y" OR K$-"Y" THEN POKE addr,n: GOTO 60 50 GOTO 40 Запомнили? Хорошо. Никогда так не делайте - это безграмотно и громоздко. Более правильно за- дать этот коварный вопрос из ко- дов и считать ответ из кодов же. Особенно это актуально для 128-х игрушек с кодовым лоадером, в которых требуется изменить со- держимое нескольких страниц па- мяти. Вот пример кодовой менюшки для такого случая: LD BC,#000E - L5D83 LD DE,L5D9B ) XOR A ) печать CALL #203C ) LD BC,#0010 ) меню LD A,#00 ) LD DE,L5DAA ) CALL #203C - JR L5DBB DEFB #80 DEFB #16,#0A,#0A L5D9B DEFM "1.INF.LIVES" DEFB #FF DEFB #16,#0C,#0A L5DAA DEFM "2.NORMAL GAME" DEFB #FF LD BC,#F7FE - L5DBB IN A,(C) ) BIT 0,A ) опрос JR Z,L5DC9 ) BIT 1,A ) клавиш RET Z ) JR L5DBB - LD BC,#7FFD L5DC9 LD A,#14 DI OUT (C),A XOR A LD (#C7CA),A LD A,#16 DI LD BC,#7FFD OUT (C),A XOR A LD (#C472),A RET LD A,#02 CALL #1601 JR L5D83 Коментарии: процедура #203C выполняет печать сообщения по адресу DE, длиной BC в текущий поток, который устанавливается процедурой #1601(A содержит но- мер потока). Основной экран - поток 2, служебный - 1. Обратите внимание на процедуру по L5DC9 это и есть "обессмерчивание". Данный фрагмент вызывается CALL'ом после загрузки програм- мы, и по RET происходит запуск игры (кстати, этот фрагмент взят из моей версии ARMYMOVE1&2 в од- ном флаконе). Как видите, ничего сложного в такой менюшке нет, а на бейсике ее не реализовать, так как здесь производится переключение стра- ниц RAM. Естественно, возможны и другие варианты построения по- добной процедуры, особенно по части печати меню. Могу предста- вить, как расфыркались считающие себя профессионалами дочитав до CALL #203C. Обычно для оной цели используют нечто такого вида: LD HL,addr LD A,(HL) <-LOOP OR A RET Z RST #10 INC HL JR LOOP Оба варианта понимают управ- ляющие коды и используют пере- менные BASIC'а. Если же Вас в самый неудачный момент свалил страшный приступ лени, то можно попробовать скомпоновать BASIC и коды, чтобы упростить задачу пе- чати и опроса клавиш. Представ- ляю Вашему вниманию настоящий шедевр ленивости: 0 REM (кодовый лоадер) 15 PRINT AT PI*PI, PI*PI; "INF. LIVES (Y/N)": PAUSE NOT PI: IF INKEY$ ="y" THEN POKE VAL "23908",VAL "182" 20 BORDER NOT PI:CLEAR VAL "2449 9":RANDOMIZE USR VAL "23872" В кодах по 23907 стояло #3e #35, а после - LD (addr),A. Вы конечно помните, что #35<->DEC (HL), а 182<->OR (HL). При отве- те "Y" DEC менялось на OR и пос- ле загрузки ставилось в коды иг- рушки. Кстати, при всем велико- лепии BASIC'а, это явление пред- ставляло собой монолоадер. По принципу своего действия данная программка вплотную при- мыкает к столь любимым многими хакерами INTRO. Обычно INTRO грузится и отрабатывает до заг- рузки основной программы, изме- няя пару байт в лоадере. Так как оно имеет солидный объем (бегу- щая строка, музыка и т.д.) то Вы имеете возможность всласть поиз- деваться над пользователем, на- мекнув, что если он отгадает за- гадку и нажмет на клавиатуте от- вет (слово из 11-и букв), то, может быть, получит бессмертие. Для полноты курса рассмотрим примерчик: LD BC,#7FFE - LOOP IN A,(C) ) опрос BIT 0,A ) space JR NZ,L61ED - DI IM 1 CALL #6B2E LD HL,#4000 LD DE,#4001 LD BC,#1AFF LDIR RET NOP L61ED LD A,#FD IN A,(#FE) BIT 4,A ->"G" JR NZ,L621E LD A,#BF IN A,(#FE) BIT 1,A ->"L" JR NZ,L621E LD A,#FB IN A,(#FE) BIT 2,A ->"E" JR NZ,L621E LD A,#7F IN A,(#FE) BIT 3,A ->"N" JR NZ,L621E LD A,#22 LD (#5D73),A LD B,#27 OR #F8 L6217 OUT (#FE),A INC A DJNZ L6217 JR LOOP L621E Работает эта штучка так: если нажато "space", то продолжается загрузка, если нет - идет опрос клавиатуры. Когда нажата первая клавиша пароля, программа прове- ряет, нажата ли следующая. Если не нажата первая буква, то про- исходит выход из программы опро- са на зацикливание. Когда нажаты все буквы пароля, в загрузчике меняется байт и интра, весело помигав бордюром, выходит на за- цикливание. В бегущей строке, поближе к концу, говорится, что если пользователь знает как зо- вут вокалиста DEICIDE, он может это немедленно подтвердить и по- лучить за свой культурный уро- вень вечную жизнь. Одна тонкость: ничего, произ- водящего явно видимый эффект, последовательно с такой прог- раммкой ставить нельзя, так как каждая правильно нажатая клавиша увеличивает время выполнения цикла, что приведет к подторма- живанию в работе последовательно стоящих операций цикла (напри- мер, будет дергаться мультипли- кация). Это позволит выявить код методом перебора. Бегущая строка и музыка в этом примере стоят в цикле прерывания. При определенном навыке по- добную процедурку можно втиснуть и в саму игру, хотя пожалуй это уже дурной тон (тем более, что во многих играх и так есть фир- менный CHEAT MODE). Ну вот, теперь, кажется, дей- ствительно конец. Надеюсь, что прочитанное пошло Вам на пользу и Вы не собираетесь умирать от скуки. ________________________________