Оповідь про компресорі, який можна називати, але не пам'ятаю, як

Вашій увазі надається не зовсім новорічна історія, в якій є зав'язка, інтрига, детективне розслідування, гонитва, підступність, мудрість древніх і щасливий фінал. Під катом вас чекають археологічні розкопки Хабра епохи перебудови і щіпка асемблера x86 за смаком.


Зав'язка

Я люблю грати в старі комп'ютерні ігри, які випущені в кінці 80-х – початку 90-х. У них є свій неповторний олдовый антураж, переважання ігрової механіки і сюжету над виражальними засобами та інші речі, за які люди люблять олдскул. Ще коли я вчився в старших класах, мені попалася гра Skycat від Gamos, яка представляла собою 2d-шутер з елементами головоломки. Мені було цікаво в неї грати, проте я не міг пройти навіть перший рівень. В ті часи я вже захоплювався програмуванням на Паскалі і встиг упертися в обмежені можливості графіки модуля
GRAPH.TPU
, тому потихеньку став долучатися до світу асемблера x86. І ось в один прекрасний день, подивившись на IDA їм. тов. Гильфанова і на файл
SKYCAT.EXE
вагою в якихось 15 кілобайт, я прийняв необачне рішення: провести повний reverse engineering непрохідною гри, щоб дізнатися, що у неї там всередині. Цим захоплюючим процесом я займаюся вже багато років в дуже неквапливому режимі, і по його завершенні планую поділитися результатами з спільнотою (сподіваюся, що не через стільки ж років, зараз справа набагато ближче до успішного кінця, ніж до початку). Але ця стаття буде не зовсім про те.

На новорічні свята-2016 я вирішив потішити себе грою в Sid meier's Civilization на самому складному рівні. Як ви вже зрозуміли, геймер я досить криворукий, тому після пари невдалих партій я вирішив пошукати заныканный в папці з грою трейнер,
CIVHELP.EXE
, який робить досить прості речі – змінює в сейві рік і скарбницю. Подивившись на розмір файлу 9 кілобайт і свій в міру успішний досвід зворотної розробки SkyCat, я прийняв ще одне необачне рішення – а чого не поколупати-то, справді! IDA застерегла мене сполученням «Possibly packed file, continue?», але марно. Під капотом почалися чудеса.

Інтрига

Після аналізу файлу IDA видала невтішну картину – у файлі була виявлена жалюгідна щіпка інструкцій на 300 байт вазі, все інше було детектировано як запаковані дані. Ну що ж, не біда – завантажимо в Turbo Debugger, поставимо крапку останова якраз після розпакування, запустимо, зробимо дамп пам'яті. І…


Я отримав пачку нулів замість розпакованої програми. Таке відбувається, коли у виконуваному файлі стоїть яка-небудь захист від налагодження, або ж код буде по ходу виконання програми. У будь-якому випадку, на халяву розпакувати файл не вийде, треба хоча б трохи подивитися код распаковщика.

Щоб не втомлювати читача, у якого від новорічних свят напевно злегка притупилося увагу, опишу коротко, що відбувається в цих 300 спартанських байтах. Спочатку відбувається переміщення коду распаковщика в старші адреси пам'яті (щоб при розпакуванні не потерти власний код), а на колишньому місці йде розпакування сегмента упакованих даних і передача управління в нього. Ці маніпуляції здалися мені не особливо складними, але ось код распаковщика здався знайомим. Аналогічна штука распаковывала звукові файли в SkyCat!

Голос робота в заставці зберігається в запакованому файлі

Я був, м'яко кажучи, здивований. Поліз звіряти асемблерний код распаковщика звуку в SkyCat і коду CIVHELP. Вони виявилися ідентичними з точністю до назви міток. Висновок напрошувався сам собою – розпакувальник повинен бути достатньо відомим, щоб потрапити в дві програми від різних авторів. Чекати третього пришестя таємничого распаковщика я не став і вирішив дізнатися ім'я винуватця торжества. Ось тільки як це зробити, маючи на руках купку асемблерного коду?

Детективне розслідування

Розпакувальник виявився влаштований не дуже складно, тому расковыряв його код при аналізі SkyCat, я зрозумів його принцип роботи: читати з вхідного потоку керуюче слово, з цього слова читати побітові команди – яке копіювання байта з вхідного потоку у вихідний, яке копіювання частини вже декодованого потоку у вихідний, – і за вичерпання керуючого слова знову зачерпувати його з вхідного потоку, поки той не вичерпається. Якщо читач не боїться давніх пророцтв на страшному мові асемблера x86, то з отриманим кодом може ознайомитися тут. Залишок університетських знань в голові підказав, що на такій ідеї побудований алгоритм Лемпеля-Зіва і всі його численні нащадки. Відповідно, коло моїх пошуків звузився (нехай і незначно) до сімейства LZ, залишивши за бортом таких чудових человекопараходов, Хаффман Барроуз, Уїллер, Фано і Шеннон. Вивчивши значний список алгоритмів сімейства LZ, я не знайшов жодної реалізації, використала підхід мого таємничого декомпресора – не використовувати при розпакуванні словників, не розбазарювати по декілька біт на індикацію копіювання одиничного символу та інші тонкі моменти.

Наступна ідея (яка, взагалі-то, людині розумній прийшла б у голову першої) – сигнатурний аналіз. Різні програми часто залишають у файлах спеціальні байти-маркери, щоб зрозуміти, яка програма створила файл і/або якась програма зможе його прочитати/редагувати/запустити. Наприклад, виконувані файли DOS починаються з символів
C
, зображення BMP містять заголовок
BM
. Щодо SkyCat я був впевнений, що знаю призначення кожного байта, і тому там ніяких поміток бути не могло. У файлі
CIVHELP.EXE
після коду розпакування йшли 5 байт даних, які при конвертації в рядок виглядали як
*FAB*
. Побіжне гугление показало, що з цього клаптика тексту нічого комп'ютерного знайти мені не вдасться. Спроба сигнатурного аналізу встигла провалитися, не встигнувши початися.

В азарті я став читати на різних сайтах про продовжувачів справи Лемпеля та Зеева, вдивлятися в їхні вихідні коди, вивчати різну логіку роботи, але нічого схожого не було. Після кількох годин пошуків я натрапив на збірник різних компресорів з исходниками, став скачувати архіви один за іншим, розпаковувати, переглядати исходники… і раптом очей зачепився за знайомий асемблерний код!

Гонитва

; "Жаба" - програма, яка спочатку стрибає по пам'яті,
; потім розпаковує дані і нарешті запускає отриманий код
; Адаптація © Красильников 1991

Файл
FROG.ASM
містив таке забавне опис, а під ним – код того самого компресора! Здавалося б, мої пошуки закінчені, але у мене залишався ще ряд питань.
У папці з компресором лежав документ, який свідчив:
Ю. Д. Красильников. Програма стиснення/розтискання файлів і компресор .COM-файлів.
Мотивом для написання цих програм послужила опублікована раніше в «Софтпанораме» стаття В. Тараненко «Процедура пакування даних SQUEEZE». Процедура використовувала принцип роботи пакувальника LZEXE. Даний алгоритм відрізняє простота, ефективність і дуже висока швидкість розпакування даних.
Вже звідси слід було, що товариш Красильников не пропонував упаковку EXE-файлів, тому що це до нього вміли робити як мінімум товариш Тараненко і утиліта
LZEXE
. Подальший опис наголошувала, що
FROG.ASM
– взагалі допоміжна утиліта для розпакування, код COM-пакувальника написаний на мові C. Хто ж тоді запакував
CIVHELP.EXE
і якою програмою? Чому звукові файли в SkyCat були упаковані як прості дані?

Я вирішив копати в сторону LZEXE. Виявилося, що її автор, Фабріс Беллар – дуже крутий програміст, і мені має бути соромно, що я досі про нього нічого не знав. В голові промайнула таємнича рядок
*FAB*
, і стало ясно, що це підпис автора. Однак исходников свого чудового компресора Фабріс так і не надав. Деякою втіхою стало, що Митугу Куризоно створив утиліту
UNLZEXE
, яка раскукоживала результат життєдіяльності
LZEXE
до вихідного стану. Що ж, це нам і потрібно! Я розчохлив DosBox, набрав у командному рядку
UNLZEXE.EXE CIVHELP.EXE

і отримав повідомлення
CIVHELP.EXE is not LZEXE file


Підступність

– Та що ж таке! – подумав я. – Не може ж цей рядок
*FAB*
бути у файлі просто так!

На щастя, у моєму розпорядженні був файл
UNLZEXE.C
з досить лаконічним вихідним кодом. З нього стало зрозуміло, що для розпакування в EXE-файлі зміщення
1C
має розташовуватися рядок
"LZ91"
. Підправляємо HEX-редактором 4 байти – вуаля! Отримуємо з 8 кілобайт аж цілих 12, файл запускається, IDA показує велику купу коду і невелику купку человекочитаемых рядків. З якої причини в заголовку виявилися підступні байти, я не знаю. Фабріс стверджує, що випускав тільки дві версії компресора, і в обох сигнатури були на місці. Моє припущення – це було зроблено вручну для маскування слідів використання компресора. Навіщо було це робити при створенні маленького трейнера для сейвів – окрема загадка.

Мудрість стародавніх

Залишилося одне питання – походження компресора в грі SkyCat. Точної відповіді у мене немає, але є гіпотези. Описана вище програма-жаба була опублікована в «Софтпанораме» – бюлетені програмістів, заснованому в 1989 році Миколою Безруковим. Основними цілями «Софтпанорамы» були вільний обмін програмами у вихідних кодах і захист від комп'ютерних вірусів. Оскільки спільнота зародилося раніше, ніж у країні набув поширення Фідонет, спілкування відбувалося шляхом розсилки і копіювання дискет. Виходить такий доісторичний Хабр з ухилом до програмування, який заслуговує окремої серйозної статті (і дуже бажано, щоб її написав хтось із тодішніх активних учасників). Думаю, цілком ймовірно, що прочитавши один з випусків «Софтпанорамы» розробники вітчизняної компанії Gamos могли запозичити відкритий код у вітчизняного програміста Красильникова.

Щасливий фінал

Оскільки мої пошуки таємничого походження алгоритму увінчалися успіхом, я заспокоївся і залишився задоволений. А занурення в архів «Софтпанорамы» перенесло мене в незабутній світ новин програмування дев'яностих, що мене навіть в деякій мірі ощасливило. Раджу і вам причаститися до духу епохи.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.