Музыкальный Проигрыватель
Как я взламывал Капитана Комика
Часть II. Музыкальный проигрыватель
Механизм, реализующий музыкальное сопровождение "Капитана Комика" работает следующим образом.
Резидентная программа music_player, висящая на 8-ом прерывании (прерывании таймера, которое обрабатывается каждые 55 миллисекунд) 18.2 раза в секунду анализирует содержимое специальной области данных. И в зависимости от результатов анализа производит некоторые действия.
Если в данный момент нет активной мелодии, просто передает управление оригинальной программе обработки 8-го прерывания.
В случае, когда мелодия активна, анализируется длительность текущей ноты. (Конечно, ноты, заданной в виде частоты). Если длительность ноты отработана, выбирает следующую ноту.
Если выбирается величина 0000h, выключает мелодию, сбрасывая значение соответствующих переменных.
После того, как таймер 8253 запрограммирован на требуемую новой нотой частоту, анализируется признак включенности звука. И, если звук включен, то устанавливается бит 8255 схемы, разрешающий динамик.
Установка значений переменных области данных производится программкой music_monitor. Передаваемая через регистры информация интерпретируется следующим образом:
ax - код операции;
cx - приоритет мелодии;
bx - адрес мелодии.
Что касается адреса мелодии. Поскольку в момент вызова не изменяется состояние регистра ds, то мы можем фиксировать ds как сегмент мелодии, следовательно, сама мелодия мелодия может располагаться в любом месте оперативной памяти.
Содержимое региста cx позволяет музыкальному монитору реализовать механизм приоритета мелодий: монитор сравнивает приоритет текущей мелодии с приоритетом загружаемой мелодии. И если последний выше, то происходит запуск загружаемой мелодии, если нет, - отрабатывается старая мелодия.
Обратим внимание: если мы в самой игрушке проходим границу
панорамы, раздается характерная мелодия. Попробуем в момент
игры этой мелодии нажать клавишу стрельбы (Insert) и убедиться,
что пока мелодия не доиграет, звука выстрелов не слышно, хотя
сам выстрел на экране происходит.
В регисте ax передается код операции, по которому монитор устанавливает значение некоторых переменных, считываемых резидентом. Поскольку операции монитора и конкретные переменные тесно взаимосвязаны, опишем область данных, обслуживающих музыкальный монитор, привязывая их к операциям.
old_int_8 - переменная типа dd (двойное слово); содержит старое
(оригинальное) значение адреса перехода на программу
обработки 8-го прерывания.
melody_status - переменная (байт) содержит статус мелодии;
операция 1 - выбрать новую мелодию - устанавливает
значение данной переменной в On (числовой
эквивалент - 1);
операция 3 - выключить мелодию - сбрасывает значение
этой переменной в Off (числовой эквивалент - 0);
также в значение Off данная переменная устанавливается
программой music_player когда мелодия завершается
естественным путем;
операция 4 - возвратить статус мелодии - возвращает в
регистре ah значение этой переменной, что позволяет
пользовательской программе, например, дождатьсся конца
мелодии.
sound_status - переменная (байт) содержит статус звука;
операция 0 - включить звук устанавливает переменную в
значение On;
операция 2 - выключить звук устанавливает переменную в
значение Off;
функциональные клавиши f1, f2 (запретить/разрешить звук)
приводят к воздествию именно на эту клавишу.
melody_priorty - приоритет мелодии (байт);
переменная содержит значение приоритета мелодии; чем
больше число, тем больший приоритет и, таким образом,
для мелодий с приоритетами 1 и 4 будет выполняться
вторая.
melody_seg,
ptr_crnt_note - сегмент мелодии, указатель на текущую ноту -
пара переменных (слово, слово) содержит 4-рех байтный
указатель на текущую ноту; операция 1 - установить новую
мелодию - устанавливает данные значения.
len_crnt_note - длительность текущей ноты - переменная (слово)
содержит остаток длительности текущей ноты, который
необходимо отыграть с данной частотой.
Теперь мы может проанализировать алгоритм, по которому функционирует music_player
|
сохранить регистры
|
загрузить сегмент мелодии
melody_seg
| нет
melody_status == On? ------------------------+
| |
декрементировать |
len_crnt_note |
нет | |
+____________ len_crnt_note == 0? |
| | |
| увеличить указатель следующей |
| ноты; выбрать след.ноту |
| | да |
| следующая нота == 0000h? ________________+ |
| | | |
| выключить динамик | |
| | | |
| запрограммировать таймер | |
| на генерацию частоты | |
| [ptr_crnt_note] | |
| сегмента melody_seg | |
| | | |
| выбрать длительность след.ноты | |
| и зафиксировать ее в | |
| len_crnt_note | |
| | | |
| здддддд sound_status == On? выключить |
| | | нет мелодию |
| включить | | |
| дмнамик +----------------------------------+ |
| | +------------------------------------------+
| | |
| | выключить динамик
| +---------------+
+---------------------+ восстановить регистры
|
передать управление по адресу
оригинального прерывания
|
мммммммомммммммм
Как же Капитан Комик работает с данным музыкальным проигрывателем?
В начале игрушки происходит установка вектора 8-го прерывания, запускающая программу music_player. После чего мы может вызывать программу music_monitor, допустим, следующим образом:
lea bx, <смещение мелодии>
mov ax, 1 ; операция - установить новую мелодию
mov cx, 4 ; приоритет мелодии - в Комике
; - самый высокий
call music_monitor
Для того, чтобы воспользоваться статусом мелодии, фрагмент кода будет выглядеть следующим образом:
mov ax, 4 ; код операции - получить статус мелодии
call music_monitor
jnz <адрес перехода в случае, если мелодия еще играет>
; команды, которые выполняются,
; когда мелодия уже отзвучала
...
Чтобы усложнить процесс взлома игрушки, Михаил Денио предпочитает вызывать music_monitor через INT 3, что не дает возможности протрассировать программу, либо поставить в ней точку останова (более подробно см. предыд. статью ).
В этом случае, в процедуре инициализации векторов прерываний, третье прерывание устанавливается на программу music_monitor, а вызов программы выглядит так:
mov ax, 2
int 3 ; для функции - выключить звук
Перед выходом из программы в операционную систему, безусловно, необходимо восстановить оригинальные значения прерываний.