Из журнала Info Guide #6, Рязань, 04.12.04 Декомпиляция программ Оживим старые программы! Много уже давно начатых проектов прос- таивает. Авторы,навсегда уходя в даун,заб- расывают свои утилиты,плюют на пользовате- лей и стирают исходники. У вас душа не бо- лит за эти программы? Иногда не хватает, казалось бы, мелочи-другой,а написаны горы кода, заново делать то же самое обидно... Так вот,давным-давно существует возмож- ность оживить любой когда-либо написанный для ZX Spectrum проект! Не очень делался акцент на том факте, что (и потому многие не знают или не пред- ставляют в объёме) ныне существующие исхо- дники ALASM,PT3,PkUnZip,UnRar,RIP,Pusher - декомпилированные. Версия ZX Word, адапти- рованная ZS'ом из Самары,основана на деко- мпилированном авторском коде, который ZS, правда, не распространял. Движок ZIP-депа- кера в его же MMCM плейере тоже основан на декомпилированном коде, который тоже пока не распространялся (by Volga Soft). JPEGIF laboratory от Sam Style начиналась с деко- мпиляции JPEG viewer'а by Alien Factory. И так далее. Иногда декомпиляция происходила по недоразумению, из-за того,что исходники считали утерянными (движок JPEG в JPEGIF, UnRar ). Реже авторы восстанавливали свою программу,случайно затертую (Pusher). Ино- гда старые исходники из-за жадности их де- ржателей оказывались устаревшими,поскольку программу уже много раз ломали и дописыва- ли прямо по машинному коду, без исходника (ALASM). В последнем случае оказывается полезным совет: если что-то дописываете по машинному коду,огранизуйте все эти измене- ния в виде исходника ( INCBIN оригинальный код,потом куча ORG с кусочками программы), а то потом их все не найдёте,когда появит- ся настоящий исходник! Вообще, с исходниками могут происходить самые странные события - например, мой ис- ходник Worms demo когда-то переводился из MASM в ALASM ручным переписыванием на бу- мажку, потом с бумажки. Бывает, пропадает один модуль исходника на диске автора (EWS v1.1, STS v5.2). Или опять-таки у автора неизвестно куда пропадает исходник целиком (Hrip) ... Декомпиляция в дамп. Прервите загрузчик нужной вам программы после распаковки всех кодовых блоков, выг- рузите главный кодовый блок (программу как таковую) с нужным стартовым адресом. Этот блок вы будете декомпилировать. Как прер- вать загрузчик - другая тема.Обычно систе- мные программы не защищены, достаточно за- грузить бейсик в STS, найти вход и выход, потом поставить точку останова на выход. Если у вас теневой отладчик (STS в ПЗУ,те- невой сервис-минитор или Unreal Speccy ), то ещё проще. Программу во время работы лучше не выгружать - она может быть в это время неполноценная,с уничтоженной инициа- лизацией, неправильным содержимым перемен- ных и т.п. Если в программе используются сложные структуры,которые наверняка лежат вне про- граммы (например,заголовок формата,с кото- рым оперирует программа - как в PT3 ), до- бавьте нулей в конец этого кодового блока. А то потом придется вручную искать все об- ращения к переменным вне программы,элемен- тарно можно какое-нибудь прозевать,и стру- ктура станет неперемещаемой! Загрузите кодовый блок в ZX Turbo Dis- assembler(ZXD) by Алексей Иноземцев. Суще- ствует 2 версии ZXD: 1.0 (авторская,выгру- жает в ZX-ASM, точнее,в тексте с табуляци- ями,особыми для ZX-ASM ) и 1.1 (cracked by Viper/TL - выгружает в ascii, но мне не нравится там цветовая гамма). Я пробовал пользоваться первой, например,для декомпи- ляции Mr Gluk Reset Service, а второй - для декомпиляции Pefrect Commander. STS'ом и DASM'ом (by Jaguar/PHD) лучше не пытать- ся - о том,что память под метки кончилась, вам будет весьма неприятно узнать,когда вы уже наберёте все адреса блоков данных в программе. Так вот,о чём это я. Пометьте средства- ми ZXD все данные (однобайтные DEFB, двух- байтные DEFW и длинные подгружаемые INCL), чтобы отделить их от кодов Z80. После первой грубой расстановки нажмите Make Labels в меню, и по меткам наверняка сможете найти ошибки в своем разбиении.Чем больше вы их исправите еще в ZXD, тем ме- ньше придется вручную переводить из ассем- блера в коды и наоборот на следующих эта- пах, в ALASM. Сохраните проект на всякий случай (там и будет лежать ваш способ разбиения) и де- компилируйте программу на диск. Если вы пользовались ZXD 1.0, то тексты нужно перевести в нормальный вид, заменив нестандартные ZX-ASM'овские табуляции про- белами. Это можно делать в ACEdit (я делал там). Оживление дампа. Кучу полученных текстов загрузите в ALASM командами impOrt. Желательно загру- зить их в память сразу все. Каждый файл займет чуть меньше половины ALASM'овской странички, но объединяйте их с запасом - примерно по полтора файла на страничку, т.к. они распухнут от комментариев и длин- ных меток. Режьте по границам процедур. Большие процедуры, какие найдете (если изначально не ясно, то вообще все метки, стоящие после безусловных RET и безуслов- ных JR/JP ), пометьте "закладкой", отделив метку процедуры от строки.Переходить между ними можно курсором в режиме Ext+L. Пока будете гулять по программе из кон- ца в конец,замечайте неоптимизированности, неиспользуемые фрагменты и повторы,выделя- йте из сдвигом строки вправо (это я выде- ляю вправо, а вы можете по-другому). НИЧЕГО НЕ МЕНЯЙТЕ В ПРОГРАММЕ СЕЙЧАС! Наоборот,периодически проверяйте,что кодо- вый блок, который вы получаете при ассемб- лировании,идентичен тому,который вы выгру- зили на первом этапе.Для этого есть неско- лько программ "File Comparer". Я пользуюсь такой программой от Mayhem. Раньше сравни- вал с помощью написания куска кода с XOR (неудобно, когда блок занимает больше, чем половину памяти). PT3 (самую мою первую декомпиляцию) я не сравнивал. Из-за этого сохранил больше ста промежуточных версий декомпилированных исходников (вдруг затру или попорчу что-то - откуда взять, с чем сверить?) Потом в течение кучи версий раз- гребал ошибки. Урок мне и вам. Отдельно от предыдущего действия (до или после, но не одновременно - отвлечё- тесь,как в случае погони за двумя зайцами) переведите все константы в удобный и поня- тный вид. Если константа используется в программе как буква,то пускай будет буквой (ZXD по умолчанию делает так),иначе число. Иногда десятичное понятнее 16-ричного. На любителя - двоичные. Естественно, лишние нули после # тоже ни к чему. В ZXD ошибка - отрицательные смещения для IX и IY у него увеличены на 256. Не удивляйтесь, когда не будет компилировать- ся. Ещё может понадобиться заменить XH на HX и тому подобное. Для всех встреченных меток выясните ча- стоту использования.Это делается так:заме- ните символ L в имени метки на что-нибудь другое и перекомпилируйте программу.(Можно сразу для нескольких меток,если компилиру- ется долго. Под эмулятором с кнопкой turbo все быстро.) Потом восстановите, как было. Результат (сколько раз выскочила ошибка), чтобы он не пропал,запишите над меткой.Вот так: ;3 L867F 2 ошибки "not defined" и "wrong short" следует считать за одну. Если ошибок на глазок больше 10, то пишите "many", это проще,чем выяснять, сколько их точно. Если число маленькое, то метка может оказаться локальной для данной процедуры (проверьте, заменив ее имя на месте и в ближайших об- ращениях и перекомпилировав),тогда пометь- те ее так: _867F (а цифру уберите - для локальных она не пригодится). Для не локальных меток цифра 1 или 2 может означать,что процедура выде- лена в процедуру неэкономно. Но в каждом случае нужно прикинуть и сравнить длину кода с подпрограммой снаружи и с подпрог- раммой, внесенной inline (вместо вызова по CALL просто вписать ее содержимое). Метки, которые совершенно понятны (типа процедур CLS и DHL ),переименуйте,замените все обращения через поиск с заменой. Можете напороться на метки, стоящие со- вершенно не в тему. Они там оказались: а) из-за того, что вы перевели вручную кусок кода в данные; б) из-за того, что часть программы рабо- тает не с тех адресов ( DISP ); в) из-за того, что часть программы зати- рается во время работы данными; г) из-за того, что это не адрес, а конс- танта (наподобие #7ffd ); д) ... В общем,каждый случай требует проверки. На то в ALASM есть функция поиска текста. Просто поищите обращения к метке по всем модулям программы. Если это всё-таки DISP, оформите соответствующий участок как DISP- ENT, а метки придётся расставить (прита- щить из других мест программы, т.к. они по логике вещей уже где-то определены) вруч- ную. Бывает,метка используется исключительно в неиспользуемых процедурах программы (они могли оказаться в коде случайно, даже с другой системой адресов! Например,автор не очистил память при компиляции);в этом слу- чае пометьте метку как-нибудь,потом ее мо- жно будет удалить. Например,все такие мет- ки, равно как и адреса данных, внешние по отношение к программе,я кладу в начале са- мого первого (главного) исходника. Переменные в теле программы ZXD оформ- ляет так: LD BC,0 L67B4 EQU $-#0002 L67B5 EQU $-#0001 Их я обычно исправляю на такое: L67B4=$+1 L67B5=$+2 LD BC,0 Впрочем,метку L67B5, если она использу- ется в программе всего один раз, лучше в этом месте заменить на выражение L67B4+1. Аналогично для внешних данных (там сосед- ние адреса могут быть определены,например, при очистке LDIR'ом. Имеет смысл назвать меткой только меньший адрес при такой очи- стке). Ни в коем случае нельзя автоматизиро- вать эти процессы. Потому что во время них вы знакомитесь с программой.Программа вещь тонкая. Программу нужно понять. В результате у вас будет,вероятно,пере- мещаемый (проверьте,запуская с разным чис- лом NOP'ов в разных местах),однако,непоня- тный исходник.Но,поскольку он уже оформлен удобно для использования (похож на прог- рамму,а не на дамп),разобраться в нём лег- че. Обычно на расставление меток в средней программе на 7000 строк (где-то 900 меток) уходит пара недель. Это достаточно творче- ский процесс, но время почти всегда такое. Можете оставить несколько процедур без на- звания, если ваши планируемые изменения в программе должны быть в других местах. По- том разберётесь, в процессе оптимизации (ведь вы какие-то участки уже пометили сдвигом вправо - оптимизировать?) и наво- рачивания. Все изменения на начальном этапе (когда вы ещё не очень разобрались в программе и можете её ухудшить) помечайте, например, сдвигом строк влево. Это опять-таки мой личный способ,но он оправдал себя. Я поме- чаю сдвигами изменения и в собственных программах,а задвигаю строчки обратно тог- да, когда уверен в их правильности и опти- мальности. Через месяц декомпилированный исходник никто не отличит по виду от авторского.Же- лательно его распространить, ведь вы сами можете так же забить на проект, как автор. A. Coder