Шукаємо коди рівнів у Prehistorik-2

Під катом – процес дослідження. Потрібно знання асемблера x86 хоча б на рівні читаю зі словником.
Головний інструмент - дизасемблер IDA Pro. Для дослідження DOS-програм цілком вистачить безкоштовної версії 5.0.
Багато різних DOS-утиліт є тут: http://www.exetools.com/, деякі з них будуть згадані далі у тексті.
Необов'язковий інструмент – відладчик. Можна використовувати якийсь із DOSівських відладчиків (вибір є за посиланням вище), їх загальний недолік — поділ ресурсів (як мінімум, екрану) з програмою, що налагоджується. Віддати всі ресурси програмі, але при цьому налагоджувати її дозволяє емулятор Bochs із вбудованим відладчиком.Крім того, можна обійтися без налагоджувальника.
Тобто залишок процедури loc_19960 не викликається, якщо передати в командному рядку параметр /ni. Пробуємо, бачимо, що з цим параметром гра пропускає нагадування про доблесну команду Hybrid і відразу переходить до власне гри. Робимо висновок, що весь seg002 – непотрібна для гри заставка, навішена на робочий exe'шник. Переписуємо в заголовку cs:ip на 0000:0003, ss:sp на 1681:0080, обрізаємо останні 5E90h байт відповідно скоригувавши поля розміру та додаткової пам'яті в заголовку.
Отримуємо файл pre2_2.exe розміром 39 520 байт. У ньому знову немає зв'язкових текстових рядків (усі рядки з pre2_1.exe виявилися пов'язані із заставкою Hybrid), що знову наводить на думку про упакованість. Той самий File Scanner показує "Diet 1.00,1.20" як пакувальник. Цей факт примітний тим, що diet може працювати у двох режимах: упаковка бінарників та упаковка даних. Спробувавши розпакувати файли рівнів (як найцікавіші дані з усіх) за допомогою diet, переконуємося, що дійсно файли рівнів теж упаковані diet, і, якщо нас зацікавить структура файлів рівнів, один шар (упаковку) ми вже знаємо.
Для зняття diet вдамося до універсального розпакувальника cup386. diet не містить антиналагоджувальних прийомів, запуск cup386 pre2_2.exe pre2unp.exe /1 призводить до розпакованого файлу pre2unp.exe розміром 91344 байт. Пошук текстових рядків показує, що pre2unp.exe – те, що нам потрібно.
Пошук кодів
Якщо збігу немає, то byte_251D6 встановлюється в 1 (як ми вже бачили, це відповідає неправильному коду), введений код і два попередніх змінних переміщаються на одну змінну назад. Після трьох неправильних спроб три змінні, що передують введеному коду, зберігатимуть попередніваріанти; тепер зрозуміло, звідки беруться вихідні дані першої перевірки.
Завершення процедури при правильному коді: якщо dx менше 10, воно записується як є в byte_1CDB6, а byte_251C3 стає нулем; інакше в byte_1CDB6 записується dx-10, а byte_251C3 стає одиницею. У будь-якому випадку byte_251D6 дорівнює 2, що означає кінець процедури.
Залишається зробити генератор кодів, що діє так само, як sub_19599 і/або з'ясувати, як пройти першу перевірку. Коротко про обчислення кодів: спочатку обчислюється змінна word_2435F на основі останніх 10h байт BIOS F000:FFF0 (що включають дату BIOS) та перших 80h байт всіх розширень BIOS в інтервалі від C000:0000 до F000:0000. Власне код рівня обчислюється наступним чином: Процедура sub_19599 досить незалежна від оточення (їй потрібно тільки тип процесора - для 386+ він завжди дорівнює 3), її можна просто скопіювати з дизассемблированного коду, отримавши приблизно такий генератор кодів: genpass.asm, genpass.com.
Трохи складніше з'ясувати магічні значення для першої перевірки: три коди, після введення яких можна просто ввести номер рівня (в hex і 1-based). Перевіряються три значення, що обчислюються за трьома кодами: пара dx:ax повинна дорівнювати 8E7136C8h, значення bx має бути 8BD1h. Аналіз коду, наведеного вище, показує, що dx:ax виходить на виході команди mul bp і 8E7136C8h - добуток двох 16-бітних чисел: третього коду та деякого виразу з першими двома кодами. bx - деякий вираз із другим і третім кодом. Розкладаючи 8E7136C8h на множники, отримуємо 2 3 * 7 * 4861 * 8779; щоб отримати два 16-бітних співмножники, 4861 і 8779 повинні входити в різні співмножники, а множники 2 3 *7, що залишилися, повинні бути якось розбиті на дві частини. Виходить досить маловаріантів (16 до відсіювання через 16-бітність). Переберемо кожен із варіантів; другий співмножник - третій код, знаючи його, знаходимо другий код bx, після чого перший співмножник дає добуток двох 8-бітних множників. Не всяке 16-бітове число можна представити у вигляді твору двох 8-бітних, тут відсіваються всі варіанти для третього коду, крім одного, який натомість дає два варіанти для першого коду (що відрізняються порядком 8-бітних множників). Код перебору: getmagic.c, результати: ADDE C0DE F00D та DEAD C0DE F00D.
Хардкорна конфа за С++. Ми запрошуємо лише профі.