Огромное количество микросхем от "простеньких", выполняющих логические операции типа И,ИЛИ,НЕ и др., до сверхинтегрированных контроллеров в которые "впихнута" чуть ли не вся машина (ПЭВМ). Всевозможные дополнительные платы (адаптеры, контроллеры), винчестеры и т.д. Все это призвано к жизни и к взаимодействию с целью облегчить существование своим хозяевам (пользователям, программистам).
Что же, картина получается непростая. Но говорить о сложности устройства компьютера, о сложности его частей, о сложности процессов их взаимодействия излишне - это и так понятно.
Если Вы дочитали до этого места то, может быть, подумали: а к чему это автор намекает на всякие сложности, очевидные же вещи. Если появился вопрос - дадим на него ответ. Любой человек, имевший возможность работать с компьютером, и пользователь, и программист, в один прекрасный день думает: а хорошо бы, если бы компьютер ("ящик") умел делать еще и то-то, и так-то. Установить бы дополнительную "платочку" в компьютер и получить желаемое.
"Ну,"- скажет критически настроенный читатель, - "это же надо собирать какие-то контроллеры, разбираться в микросхемах, понимать в управляющих диаграммах, то есть быть, как минимум, схемотехником."
Он прав, но не совсем. Конечно хорошо быть схемотехником (автор по профессии схемотехник и утверждает это обоснованно), но не обязательно, а вот программистом, для того чтобы применить опыт, описываемый далее, быть нужно.
Дело в том, что многие сетевые адаптеры имеют гнездо для установки ПЗУ (постоянного запоминающего устройства, rom, read only memory), позволяющего при наличии локальной сети осуществлять удаленную загрузку операционной системы. Но, как показала практика, в большинстве случаев эта возможность не используется. Если в это гнездо вместо микросхемы со старой ПЗУ (если она там вообще есть) установить новую, со своей программой, то компьютер обретет новые дополнительные свойства. Например, это может быть программа, защищающая компьютер от несанкционированного доступа, или антивирусная защита (монитор, ревизор и т.п.). Так что учиться схемотехнике не придется. Можно сразу приступить к разработке программы для ПЗУ.
Но в чем же здесь преимущество? Одни только сложности. Вынимай микросхему, ищи новою. Программу нужно еще как-то поместить в ПЗУ. Да и вообще, будет ли она выполняться? Кто или что сделает так, чтобы эта программа получила управление?
Вот именно об этом "механизме", заложенном в BIOS и обеспечивающем возможность выполнения программ, "прошитых" (так называют программы, записанные в ПЗУ с помощью программаторов) в ПЗУ, а также о том, каким образом необходимо оформлять эту программу, и хотелось рассказать заинтересованным читателям.
Отметим при этом, что преимущество программы, размещенной в ПЗУ, заключается в том, что она неуничтожаема и отработает еще задолго до того, как начнут свою работу по загрузке операционной системы с диска модули MAIN BOOT и BOOT. И хотя эти части системы наиболее часто подвержены атакам вирусов, мы хозяева положения и можем блокировать любую атаку или же осуществить восстановительные работы.
ВОЛШЕБНАЯ ВОЗМОЖНОСТЬ BIOS
Каким же образом машина узнает, что ей необходимо выполнить еще одну дополнительную программу? Задавшись вопросом, начинаем искать ответ. К счастью есть палочка-выручалочка - листинг BIOS, поставляемый в комплекте (за отдельную плату) с "персоналками". В этом листинге содержится информация, раскрывающая тайны функционирования машины. В частности, и ответ на наш вопрос.
Оказывается, что среди прочих важных дел, совершаемых подпрограммами BIOS, есть дело, заключающееся в проверке присутствия ПЗУ в адресном пространстве с С800:0 до E000:0.
Это пространство сканируется с шагом в 2048 байт на предмет наличия "подписи" 0AA55H. Но ведь может быть и совпадение. Чтобы не ошибиться, после того, как найдена "подпись", производится расчет контрольной суммы методом сложения по модулю 100H (сложение побайтно без учета переноса). Результат при этом должен получиться равным нулю. Но сколько же байтов необходимо просуммировать? Эта информация содержится в одном байте, следующем за подписью. Причем чтобы узнать размер ПЗУ в байтах, необходимо умножить это число на 200H.
Если все эти условия выполняются, BIOS считает, что перед ним ПЗУ, оформленное по правилам, и совершает последний шаг - передает управление на смещение +3 (отсчет начинается с нуля) относительно найденного сегмента. Следовательно, там должна располагаться первая выполняемая команда нашей программы. Обычно это команда jmp. Совершив все необходимое, наша программа должна вернуть управление обратно в BIOS с тем, чтобы та продолжила поиск аналогичных ПЗУ. Следовательно, наша программа должна заканчиваться командой retf.
Для того, чтобы было более ясно, о чем шла речь, ниже приводятся фрагменты кода BIOS, реализующие необходимые проверки и передачу управления.
Осталось обговорить еще одну тонкость. Каким образом обеспечить равенство нулю контрольной суммы? Ведь очевидно, что само собой это условие не будет выполняться. Идея проста - необходимо дополнить получившуюся сумму до нуля. Делается это размещением числа, равного разнице между 100H и получившейся контрольной суммой в байте (перед расчетом он должен быть равен нулю), следующем за последней командой программы. Отметим также, что расчет контрольной суммы ведется по количеству байт, равных размеру ПЗУ, а не по размеру получившегося кода программы.
Знаки вопроса означают, что конкретные значения будут известны только после написания программы.
Если ваша программа должна работать только на этапе старта компьютера, то ее переменные вы можете размещать в любой области conventional ОЗУ выше той, которая отведена под переменные BIOS (то есть в диапазоне сегментных адресов 0050:0000...9000:FFFF) - на этом этапе указанный диапазон свободен. Гораздо сложнее ситуация, когда ваша программа должна работать после того, как операционная система загружена: например, обслуживать прерывания или предоставлять свои процедуры для вызова обычными программами. В этом случае вам придется предусмотреть механизм, исключающий конфликты использования памяти.