Компьютерные науки - Учебники на русском языке - Скачать бесплатно
для выбора сектора, введите номер дорожки 0, номер сектора 2, после чего
нажмите клавишу F6 для отображения всей таблицы.
Листинг программы 5.1 показывает определение на языке Паскаль той
структуры, в которой ТРФ хранится на диске. Листинг 5.3 включает несколько
программ для чтения, расшифровки и обслуживания ТРФ.
Вспомним, что нумерация кластеров начинается с 2. Это означает, что
первые два элемента ТРФ (с номерами 0 и 1) не используются для хранения
информации о размещении данных. Они зарезервированы для хранения очень
важной информации - сведений о формате дискеты. Код формата хранится в
первом байте ТРФ. Соответствие кодов различным форматам приведено в
следующей таблице:
ФОРМАТ КОД ФОРМАТА
_________________________
D - 8 FF
S - 8 FE
D - 9 FD
S - 9 FC
QD - 9 F9
QD - 15 F9
_________________________
Управляющие программы определяют формат дискеты путем чтения первого
байта ТРФ. Программа, находящаяся в загрузочной записи, обращается к ТРФ
еще и для того, чтобы определить где находятся два системных файла,
IBMBIO.COM и IBMDOS.COM. С этим связаны различия в загрузочных программах
ДОС версии 1.00 и версии 1.10.
Простые арифметические вычисления показывают, что на односторонней
дискете находится 313 односекторных кластеров (со 2 по 314), а на
двухсторонней дискете - 315 двухсекторных кластеров. Для односторонней
дискеты такое число получается вычитанием из общего числа в 320 секторов 1
сектора загрузочной записи, 2 секторов ТРФ и 4 секторов справочника. Для
двухсторонних дискет, из общего числа 640 секторов вычитается 1 сектор
загрузочной записи, 2 сектора ТРФ и 7 секторов справочника, что дает 630
секторов или 315 двухсекторных кластеров. В одном 512-байтовом секторе
можно разместить 340 элементов ТРФ, так что в конце секторов ТРФ остается
немного свободного места.
В листинге программы 5.3 приведены процедуры на языке Паскаль,
работающие с ТРФ. Эти процедуры могут быть использованы в больших
программах. Их изучение поможет вам лучше разобраться в структуре ТРФ и в
ее использовании.
Но это еще не все, что касается ТРФ. В этой таблице используются
определенные коды специального назначения. Как мы уже видели,
неиспользуемый кластер в ТРФ отмечается нулевым значением соответствующего
элемента. Любое значение элемента таблицы в диапазоне от 2 до 314 (для
односторонних дискет) или до 316 (для двухсторонних) является допустимым
указателем на очередной элемент в цепочке, описывающей размещение файла.
Восемь наибольших значений, которые могут храниться в элементе ТРФ,
шестнадцатиричные числа от FF8 до FFF или десятичные от 4088 до 4095,
используются для обозначения конца цепочки описания. Хотя необходим всего
один код конца файла, резервируется восемь кодов на случай, если возникнет
необходимость в каком-либо расширении. Восемь шестнадцатиричных кодов от
FF0 до FF7 или десятичных от 4080 до 4087 оставлены для обозначения особых
типов зарезервированных кластеров, например, кластеров с дефектными
секторами.
На поверхности дискеты могут встречаться участки с дефектами
магнитного покрытия, которые нельзя эффективно использовать для хранения
данных. Такие области выявляются во время форматирования дискеты и
исключаются из числа областей, которые будут использоваться в дальнейшем.
В первой версии ДОС 1.00, эти дефектные сектора оформлялись в виде
скрытого файла с именем "badtrack.". Начиная с версии ДОС 1.10
используется более изящный метод.
Значение элемента ТРФ 4087 (или шестнадцатиричное FF7) используется
для обозначения неиспользуемого кластера. Одно из достоинств такого метода
пометки дефектных кластеров заключается в том, что не используется лишний
элемент справочника. Программа DiskLook может быть использована для
определения точного положения дефектных секторов на дискете, чего нельзя
сделать стандартными средствами ДОС.
Любые другие значения элементов ТРФ, от 314 (или 316) до 4080,
являются недопустимыми.
В организации ТРФ могут возникать определенные дефекты. Два наиболее
заметных дефекта - это "беспризорные" кластеры и перекрещивающиеся файлы.
Если элемент ТРФ содержит значение, указывающее, что элемент используется
(значение не равно нулю) и, в то же время, этот элемент не входит ни в
одну из цепочек определения размещения файлов, то такой элемент ТРФ
становится как бы "беспризорным". Во втором случае, может случиться так,
что две или больше различные цепочки, определяющие размещение файлов,
приводят к одному и тому же кластеру. Такие файлы называются
перекрещивающимися. Стандартная программа ДОС СНКDSK обнаруживает оба типа
нарушений в структуре ТРФ и возвращает "беспризорные" кластеры в общий пул
свободных и доступных для использования кластеров.
"Беспризорные" кластеры чаще всего возникают, когда программы
начинают создавать файл (так что для него выделяются кластеры), но не
закрывают его, вследствие чего не завершается создание элемента
справочника для данного файла. Такое часто происходило с первой версией
компилятора языка Паскаль в ДОС при обнаружении ошибки в исходной
программе. Регулярное использование программы СНКDSК позволит избавляться
от "беспризорных" кластеров, независимо от причин их появления. Нужно
включать вызов СНКDSK в файл пакетной обработки с любой программой, в
результате работы которой могут возникать "беспризорные" кластеры.
Если "беспризорные" кластеры встречаются достаточно часто, то
перекрещивающиеся файлы возникают очень редко. Мне довелось их наблюдать
только тогда, когда я специально их создавал. Если такая ситуация все же
Вам встретится, нужно скопировать каждый из файлов на другую дискету для
последующего восстановления (если оно потребуется). После этого, копии
всех файлов будут содержать информацию из общих секторов, хотя она может
принадлежать только одному из них.
Нажав клавишу F9 при работе с программой Disklook Вы можете
просмотреть карту диска с отмеченным на ней положением "беспризорных"
кластеров и общих кластеров пересекающихся файлов.
При работе ДОС в памяти хранится копия ТРФ для каждого используемого
дисковода. Когда в таблице производится какое-либо изменение, она
записывается в обе копии на дискете. При новом обращении к дискете ДОС
считывает ТРФ чтобы установить формат дискеты.
Теперь, познакомившись со структурами справочников и таблиц
размещения файлов, можно рассмотреть форматы файлов данных. Сначала
рассмотрим два наиболее распространенных формата: текстовые файлы ( в
кодах ASCII) и формат файлов с фиксированной длиной записи. Затем мы
рассмотрим два формата хранения программ: ".COM" файлы и ".EXE" файлы.
5.8. Размещение файлов
Теперь можем рассмотреть метод выделения секторов данных для файлов.
Этот метод довольно прост.
При записи на дискету данных файла для нее по одному выделяются
кластеры секторов дискеты. Когда необходим очередной кластер для данных,
то выбирается доступный кластер с наименьшим номером. Такая простая схема
используется как при создании файла, так и при его удлинении (путем
добавления данных в конец файла).
В отличие от некоторых других операционных систем (таких как
р-система Калифорнийского университета в Сан-Диего), ДОС выделяет место на
дискете по одному кластеру, когда в этом есть необходимость, не заботясь о
том, чтобы все данные файла хранились в одной непрерывной области диска.
При разработке любой схемы распределения дискового пространства в любой
операционной системе приходится выбирать между свободным выделением
пространства по одному кластеру, в результате чего файл может оказаться
"размазанным" по всей дискете, и выделением места большими непрерывными
сегментами , что усложняет задачу управления пространством на дискете.
Операционная система ДОС использует более простой первый метод.
Если дискета пуста, то все доступное на ней место представляет собой
одну большую чистую область. Когда файлы копируются на такую дискету, они
оформляются в виде удобно размещенных непрерывных фрагментов дискового
пространства. Но впоследствии, если файлы будут удлиняться, то
дополнительное место будет выделяться в первых свободных кластерах,
которые могут находится в любом месте дискеты.
Когда файлы копируются на новую дискету и оставляются без изменения,
их размещение на дискете остается экономичным. Но если создаются или
удаляются какие-либо данные, то использование места на дискете становится
весьма запутанным.
Это наиболее вероятно происходит, когда программа создает
одновременно два файла. Если файлы увеличиваются параллельно, то их
расположение на дискете обязательно будет перемежающимся.
Изучение динамики распределения пространства на дискете может
оказаться довольно интересным и поучительным. Если у Вас есть моя утилита
Disklook, то можно изучать распределение пространства на Вашей дискете,
устанавливать расположение пространства всех файлов и проверять,
фрагментировано ли пространство на диске.
Итак, схема распределения пространства на дискете в ДОС довольно
хороша, она равномерно использует все пространство и не требует нашего
вмешательства. Однако, все эти преимущества достигаются ценою
"размазывания" файлов по дискете. Из-за этого может увеличиться время
доступа к файлу, поскольку магнитная головка чтения/записи должна
перемещаться по всей дискете для поиска нужных дорожек. Поскольку
обращения к дискете являются наиболее медленными операциями и основными
ограничивающими факторами производительности IBM/PC, такое размещение
файлов может породить проблемы, хотя обычно этого не случается.
По этой причине, необходимо уделять внимание потенциальной проблеме
фрагментации пространства, занимаемого файлами на дискете. Обычно эти
проблемы Вас волновать не будут, но иногда они могут приобрести
чрезвычайное значение. В тех случаях, когда фрагментация файлов покажется
Вам нежелательной, скопируйте файл на новую дискету. Это же полезно
делать, если Вам покажется, что дисковод слишком долго выполняет поиск
дорожек при чтении файла.
При копировании файлов на новую дискету можно заодно улучшить их
последовательность, разместив в начале часто использующиеся файлы или
переставив их в таком порядке, в каком Вам хотелось бы их видеть в
распечатке справочника. В некоторых случаях упорядочение данных может
оказаться особенно полезным: файлы на дискете, хранящей корреспонденцию,
лучше размещать в хронологическом порядке, для дискеты с множеством
вспомогательных файлов предпочтительнее всего алфавитный порядок,
ускоряющий просмотр. Полезно бывает, также, упорядочивать файлы по
расширениям, которые обычно обозначают тип файла или способ его
использования. Такое упорядочивание можно производить либо вручную, либо с
помощью какой-либо сервисной программы (такого рода программы имеются в
составе пакета "Norton Utilities" (служебные программы Нортона)).
5.9.Формат текстового файла
Наиболее часто использующийся и один из самых важных форматов для
файлов данных, размещаемых на дискетах,- это текстовый файл в коде ASCII.
Такие файлы содержат обычную алфавитную информацию, тексты или отчеты,
состоящие из алфавитных символов.
Текстовые файлы являются, вероятно, наибольшим приближением к
универсальному формату для всех персональных компьютеров. Такой формат
поддерживается практически всеми персональными компьютерами и имеет
множество различных применений.
Большинство текстовых редакторов, включая и текстовые процессоры,
используют текстовый формат. Редактор, поставляемый в составе ДОС, ЕDLIN,
создает и использует текстовые файлы.
Текстовый формат используется для хранения исходного текста программ
(исходной, нетранслировавшейся версии). Практически все языковые
процессоры (такие как интерпретатор языка БЕЙСИК, компилятор языка Паскаль
или Ассемблер) ожидают ввода входного файла в текстовом формате в коде
ASCII.
Интерпретатор языка БЕЙСИК для IBM/PC работает с тремя форматами
файлов программ на языке БЕЙСИК одним из которых является стандартный
текстовый формат в коде ASCII. Для записи программы на языке БЕЙСИК в
текстовый файл в коде ASCII нужно использовать ключ "А" в операторе SAVE:
10 SAVE "PROGRAM.BAS",А
Файлы пакетной обработки, одно из самых мощных и полезных
вспомогательных средств ДОС, также хранятся в текстовом формате.
Чтобы понять структуру файлов, необходимо сначала понять саму
кодировку, принятую в Американском стандартном коде для обмена информацией
(ASCII). Код ASCII в основном состоит из кодов символов. Всем символам
присваиваются определенные комбинации битов, например, код для заглавной
буквы "А" будет выражаться шестнадцатиричным числом 41.
Существует 128 различных кодов в системе ASCII, поскольку в ней
используются семибитовые коды. Большинство компьютеров, включая IBM/PC,
используют восьмибитовую кодировку символов, так что коды ASCII составляют
лишь половину из ансамбля 256 возможных кодов. Для большинства
компьютеров, и для IBM/PC, коды ASCII занимают первые 128 кодов, от CHR$
(0) до СHR$ (127).
Вообще говоря, имеется три различных категории кодов ASCII и две из
них будут нас интересовать. Прежде всего, имеются коды ASCII,
соответствующие буквам, знакам препинания, цифрам и т.п. Это первая из
категории кодов ASCII и, конечно, они будут использоваться во всех наших
работах, выполняемых с помощью персонального компьютера.
Следующую категорию я бы назвал символами форматирования.
Использование этих символов следует уже из их названий, например, "возврат
на шаг", "возврат каретки" и "перевод строки". Форматирующие символы
разрабатывались специально для управления устройством печати. Эта
категория нам также понадобится и мы еще к ней вернемся.
Третью категорию символов ASCII можно назвать символами связи. Они
имеют такие имена как "начало-текста" или "конец-передачи" и используются
главным образом в качестве управляющих сигналов при использовании кода
ASCII для передачи информации по линиям связи. Эти символы позволяют
управлять процессами установления связи и передачи данных. Полное
разъяснение назначения этих символов тесно связано с изложением принципов
осуществления взаимодействия по линиям связи, что не входит в сферу
изложения данной книги. Поскольку эта категория символов не используется
среди данных или в структуре файлов, она больше не будет нас интересовать.
Подведем некоторые итоги: одна из трех категорий кодов ASCII
используется для кодировки данных, таких как буквы, цифры и знаки
пунктуации. Вторая используется для форматирования, т.е., для указания
того, где заканчивается одна строка и начинается другая; эти коды
используются не только для управления форматом печати, но и для
структурирования файлов. И, наконец, третья категория для управления
передачей данных и никак не связаны с форматом текстовых файлов.
Обычные символы текста в коде ASCII имеют значения от CHR$(32) до
CHR$(126). Символы ASCII двух специальных категорий - форматирующие и
символы связи,- имеют коды от CHR$(0) до CHR$(31). Все эти коды должны
использоваться для различных управляющих функций и они обычно используются
отдельно от кодов данных. Если Ваша программа попытается непосредственно
использовать эти коды, могут происходить странные вещи. Некоторые ситуации
описываются в приложении 4.
Ниже приведены три простые программы на языке Бейсик, которые
демонстрируют ряд экспериментов со специальными кодами ASCII от 0 до 31:
10 REM вывод на экран дисплея
20 FOR I=0 TO 31
30 PRINT CHR$(I)
40 NEXT I
10 REM вывод на печать
20 FOR I=0 TO 31
30 LPRINT CHR$(I)
40 NEXT I
10 REM манипулирование режимом экрана
20 DEF SEG=&HB800 ' цветная графика
30 DEF SEG=&HB000 ' монохромный
40 FOR I=0 NJ 31
50 POKE 1*2,I
60 NEXT I
Теперь рассмотрим текстовые файлы в коде ASCII. Это файлы, содержащие
текст, который состоит из обычных символов, например, букв алфавита.
Символы текстового файла организуются в строки, подобно тому как они
записываются на бумаге.
Границы строк отмечаются форматирующими символами кода ASCII: возврат
каретки, CHR$(13), и перевод строки, CHR$(10). Можно было бы использовать
любой символ для обозначения конца строки, но используются именно эти два
символа, поскольку они управляют процессом перехода к новой строке на
печатающем устройстве.
Одна из причин использования двух символов, обозначающих конец
строки, состоит в обеспечении возможности наложения строк. Наиболее часто
такая возможность используется для выполнения подчеркивания. При этом на
печать в конце строки выдается только возврат каретки, а затем
накладывающиеся символы. Поскольку перевод строки не выполнялся, новые
символы печатаются поверх уже напечатанных. Такую операцию можно
произвести с печатающим устройством, но нельзя с дисплеем.
Таким образом, физические команды, необходимые печатающему устройству
для завершения строки, используются также в качестве логических меток
конца строки в файле. Такой прием позволяет копировать текстовые файлы
непосредственно на печатающее устройство, причем текст будет
распечатываться правильно. Вы можете сами проверить это, использовав
команду COPY для пересылки файла непосредственно на устройство печати или
на экран дисплея следующими командами:
COPY имя файла LPT1
COPY имя файла CON
Длина строки текстового файла ничем не ограничивается, строка
продолжается, пока не встретятся символы возврата каретки и перевода
строки. Однако, большинство программ, работающих с текстовыми файлами,
ограничивают длину строки 255 символами, что обычно значительно больше,
чем может потребоваться (некоторые редакторы и текстовые процессоры
ограничивают длину строки данных таким образом, чтобы они укладывались в
строку экрана дисплея: 80 символов).
Формат текстового файла определяется не только разделением на строки.
Все файлы в коде ASCII имеют один общий элемент - маркер конца файла,
значение которого CHR$(26). Этот маркер однозначно указывает конец файла.
Обнаружение положения кода CHR$(26) позволяет точно установить размер
файла, отличающийся от размера, указанного в справочнике.
С клавиатуры этот специальный символ с кодом CHR$(26) можно ввести,
нажав клавишу "Z" и удерживая в нажатом состоянии клавишу "CONTROL"
(управление). Если в любом тексте, связанном с персональными компьютерами,
Вам встретится сочетание "CONTROL-Z", то имеется в виду функция конца
файла, или значение CHR$(26) .
Кроме описанных трех универсальных кодов, возврата каретки, перевода
строки и конца файла, имеются и другие форматирующие коды ASCII, которые
используются с различной интенсивностью. Символ табуляции (CHR$(9))
используется для замены нескольких пробелов, следующих один за другим.
Поскольку не существует общепринятого количества позиций табуляции, этот
символ используется не так широко, как можно было бы ожидать.
Символ перевода страницы (CHR$(12)) обозначает переход к началу новой
страницы. Большинство устройств печати отрабатывает этот код
соответствующим образом и некоторые программы редактирования используют
его для разделения текста на страницы. Использование этого символа также
далеко от универсальности. Его стоит применять только для управления
устройством печати.
Программы редактирования и, особенно, текстовые процессоры требуют
большего числа символов форматирования и они определяют собственные коды
для своих целей, таких как отметка границ параграфов, подчеркивания и тому
подобного. Поскольку универсальных кодов для таких функций не существует,
их включение в текстовой файл не позволит другим программам его
использовать.
Прежде чем завершить рассмотрение текстовых файлов необходимо сделать
еще ряд замечаний, касающихся обычных символов текста. Любая система
кодирования определяет упорядоченную последовательность - своего рода
эквивалент алфавитного порядка символов. В коде ASCII все обычные знаки
пунктуации предшествуют буквенным кодам. Заглавные буквы располагаются
перед строчными. Кроме того, в отличие от кода EBCDIC, используемого
универсальными компьютерами фирмы "ИБМ", цифры располагаются перед
буквами.
Коды заглавных букв отличаются от кодов строчных букв на 32 (на 32
меньше). Этот факт можно использовать в программах преобразования
текстовых данных из верхнего регистра в нижний или наоборот.
5.10. Форматы записей данных
После текстовых файлов в коде ASCII наиболее распространены файлы с
фиксированной длиной записей. В таком файле логические единицы информации
называются записями и размер этих записей одинаков во всем файле. Файлы
такого формата создаются интерпретатором БЕЙСИКА в качестве произвольных
файлов, такой же формат имеют прямые файлы Паскаля и другие нетекстовые
файлы. При выполнении каждой операции чтения или записи (обозначаемых в
языке БЕЙСИК как INPUT# и WRITE#) пересылается одна запись.
Если текстовые файлы включают метки, отмечающие концы строк и конец
файла, то в файле с фиксированой длиной записей, записи ничем не
отделяются друг от друга. В таких файлах хранятся только данные без
каких-либо разделителей.
Поскольку записи в таком файле имеют одинаковую длину, можно
использовать простейшие арифметические вычисления для определения места,
где заканчивается одна запись и начинается другая. Служебные программы ДОС
для чтения и записи информации в файл, описанные в главе 4, используют
такой метод как при последовательном, так и при произвольном обращении к
файлу. Когда Ваши программы обращаются к файлам, ДОС выполняет все функции
поиска записей, так что Вашим программам не нужно заботится ни о каких
подробностях, связанных с поиском.
Рассмотрим пример. Если длина записей файла 100 и у ДОС запрашивается
чтение записи с номером 24, то ДОС вычисляет смещение в байтах от начала
файла умножением 24 х 100. Разделив полученное значение 2400 на размер
сектора, 512 байт, получим частное 4 и остаток 352. Это значит, что запись
расположена в пятом секторе файла (пропускаются 4 сектора) со смещением
352 байта.
(Такой способ подсчета предполагает, что нумерация записей начинается
с нуля. Если номер первой записи единица, то необходимо соответствующим
образом изменить формулу, хотя сам метод остается неизменным).
В файлах с фиксированной длиной записи ДОС не приходится заниматься
выравниванием записей - они располагаются одна за другой. Если длина
записи не укладывается точно в 512-байтовый размер сектора, то некоторые
записи могут размещаться в нескольких секторах, что несколько снижает
эффективность операций чтения и записи.
В нашем примере со 100-байтными записями, очередная, 25 запись,
частично размещается в 5-ом, а частично - в 6-ом секторе. Чтобы записать в
файл эту запись, необходимо сначала считать, а затем записать обратно на
диск два сектора.
Если Вы будете выбирать длину записи так, чтобы она была кратна 512,
то скорость чтения и записи несколько повысится. ДОС, однако, позволяет
работать с записями разной длины и, если не считать некоторого снижения
эффективности при чтении/записи записей, располагающихся в нескольких
секторах, Вам не нужно заботиться о размере записи.
Как Вы, вероятно, догадались, существует множество других форматов
файлов, помимо текстовых файлов и файлов с фиксированной длиной записи. Мы
так подробно остановились на этих двух форматах в связи с их широким
распространением. Другие форматы хранения данных обычно имеют структурные
требования, усложняющие процесс их чтения и записи, что может потребовать
использования специальных программ для работы с ними. Существенное
достоинство этих двух форматов заключается в их гибкости - в самых
различных ситуациях - так что они могут использоваться в очень многих
приложениях. Поскольку они поддерживаются очень многими программами, в том
числе различными языковыми процессорами и операционными системами, их
очень удобно использовать. На практике получается, что в связи с удобством
работы с текстовым форматом и широкой поддержкой его различными
программами редактирования и языками программирования, можно обнаружить
случаи приспосабливания форматов данных программ к текстовому формату в
коде ASCII.
5.11. Форматы программных файлов.
Существует два формата файлов для хранения программ. Для них
зарезервированы два расширения имен: ".COM" и ".EXE". Подробности
организации программных файлов потребуют полного изложения организации
ДОС, так что мы не будем сейчас их описывать, отметив только наиболее
важные моменты. (Файлы программ БЕЙСИКА с расширением ".BAS" строго говоря
не являются программными файлами - это файлы данных, которые считываются и
используются интерпретатором БЕЙСИКА.)
Программные файлы типа COM с расширением имени файла ".COM" - это
непосредственно загружаемые программы. В этих файлах хранится точный образ
задач в том виде, в котором они находятся в памяти. Для них не требуется
практически никакой подготовки к запуску после их загрузки в память
средствами ДОС. Если Вы будете просматривать файл типа COM, например,
программой DiskLook, Вы увидите команды машинного языка и данные,
образующие программу, и ничего больше. Когда ДОС подготавливает программу
из файла типа COM к выполнению, она формирует специальный
сегмент-приставку, необходимый для всех программ, загружает программу,
устанавливает регистры сегментов, присваивая им стандартные значения, и
передает управление программе.
Программные файлы типа EXE с расширением имени файла ".EXE" несколько
отличаются от файлов типа COM. Чтобы загрузить их для выполнения требуется
определенная подготовка. Наиболее важная часть такой подготовки называется
перемещением.
Программа может содержать адреса, значения которых должны меняться в
зависимости от того, в каком месте памяти находится программа. Если
программе это не требуется, то она называется само-перемещаемой. Программы
типа COM должны быть само-перемещаемыми, а перемещение программ типа EXE
должно выполняться специальной программой ДОС - загрузчиком.
Программы типа EXE начинаются специальной двухбайтовой меткой, 4D5A,
которая используется для различения программ разных типов. За этой меткой
следует определенная управляющая информация, включающая таблицу,
указывающую какое перемещение необходимо. Далее размещается собственно
программа.
Помимо перемещения, во время загрузки для программ типа EXE может
быть определен сегмент стека. Программы типа EXE могут определять куда они
должны загружаться - в верхнюю или в нижнюю часть доступной памяти.
В связи с простотой формата COM, можно создавать относительно
короткие и простые файлы типа COM в машинных кодах непосредственно, либо с
помощью программ для модификации содержимого файлов на дискетах, таких как
DEBUG или SecMod, либо путем написания короткой программы на БЕЙСИКЕ,
которая будет записывать байты программы прямо в файл типа COM. Такого
рода программ создано уже очень много.
Формат типа COM проще, компактнее и быстрее загружается в память. Для
программ, которым не нужны специальные услуги, обеспечиваемые форматом
типа EXE, предпочтительнее использовать формат типа COM.
Приложение 5.1. Текст программы анализа структуры справочника
(Паскаль).
const
directory_sectors_single_sided=4;
directory_sectors_double_sided=7;
directory_entries_single_sided=64; {4*16}
directory_entries_double_sided=112; {7*16}
hidden_attribute =wrd(2);
system_attribute =wrd(4);
{========================================================}
type
directory_entry_type=
record
filename : string(8);
extension : string(3);
attribute : byte; {hidden=2; system=4}
reserved : array[1..10] of byte;
creation_time : word; {hour*2048+minute*32+
secs}
creation_date : word; {(year-1980)*512+
month*32+day}
starting_cluster_number : word;
file_size : array[1..2] of word;
end;
{==========================================================}
complete_directory_type=array[1..112] of directory_entry_type;
Приложение 5.2. Текст программы анализа структуры таблицы размещения
файлов (Паскаль).
const
minimum_cluster = 2;
maximum_cluster_single_sided = 314;
maximum_cluster_double_sided = 316;
usual_fat_end_of_file = 4095;
minimum_fat_end_of_file = 4088;
bad_cluster_fat = 4087;
single_sided_indicator_byte = 254;
double_sided_indicator_byte = 255;
{==========================================================}
type
fat_sector_type=array[0..511] of byte;
fat_sector_structured_type=
record
scrambled_fat_pair: array[0..158]
of
record
scrambled_byte_1 : byte;
scrambled_byte_2 : byte;
scrambled_byte_3 : byte;
end;
unused_bytes_of_sector : array[1..35] of byte;
end;
Глава 6. ВЫБОРКА ИЗ ПЗУ
Программы, размещаемые в пассивном запоминающем устройстве (ПЗУ )
компьютера IBM/PC, определяют эффективность работы персонального
компьютера в целом. В данной главе мы излагаем самые общие сведения,
касающиеся ПЗУ. Мы рассмотрим назначение ПЗУ, специфику анализа и
декодирования (реконструкции) хранящейся там информации и, наконец,
сравним между собой две первые версии ПЗУ компьютера IBM/PC. Все эти
материалы являются базой для последующих пяти глав, в которых
функциональные возможности IBM/PC рассматриваются более детально.
Здесь важно иметь в виду три аспекта. Все о чем пойдет речь в данной
главе в полной мере относится только к IBM/PC и, вообще говоря, не
распространяется на все семейство компьютеров, работающих под управлением
MS-DOS. Для остальных компьютеров семейства PC эти сведения следует
рассматривать применительно к конкретному компьютеру (поскольку каждой
модели присущи свои особенности). Тем не менее, имеются все основания
считать, что прерывания или вызовы функции обслуживания для большинства
компьютеров совпадают. Поэтому, несмотря на возможное изменение адресов
программ обслуживания, находящихся в ПЗУ компьютера той или иной модели
материалы настоящей и последующих пяти глав сохраняют свою актуальность.
6.1. Организация ПЗУ и его использование
Программы, хранящиеся в ПЗУ, представляют собой чрезвычайно важную
компоненту IBM/PC, поскольку являются основой управляющей программы,
делающей ПЭВМ эффективным инструментом обработки данных. Сам факт их
размещения в ПЗУ превращает эти программы в стабильный и надежный
инструмент - они не могут быть стерты или уничтожены. Кроме того, они
доступны для любой программы, выполняемой на IBM/PC, независимо от
специфики используемой операционной системы.
Большинство программ обслуживания, размещаемых в ПЗУ, допускают
перемещение. Эти программы написаны таким образом, что при необходимости
их можно легко переместить, соответственно перестроив обращение к ним.
Специфика обращения к программам ПЗУ состоит в том, что их активизация
производится всегда через систему прерываний. Изменив таблицу векторов
прерываний, можно легко изменить точку входа в ту или иную программу
обслуживания. Эти вопросы уже рассматривались нами при изучении материала
главы 3.
Как уже говорилось выше, все пространство памяти ПЗУ компьютера
IBM/PC делится на три части, под которые отводятся старшие адреса
миллионного пространства памяти.
Самые старшие адреса памяти, соответствующие началу параграфа
сегмента с адресом FE20/16, и охватывающие 8Кбайт до конца памяти
отводятся под BIOS, или Базовую Систему Ввода-Вывода. Базовая Система
Ввода-Вывода (BIOS) выполняет все операции по обслуживанию периферийных
устройств IBM/PC, таких как дисплеи или накопители на гибких магнитных
дисках. Все программы операционной системы, обеспечивающие
функционирование ПЭВМ ориентируются на использование BIOS. Область памяти
ПЗУ, содержащая BIOS, представляет особый интерес для тех, кто хотел бы в
полной мере использовать возможности IBM/PC. В последующих пяти главах по
мере рассмотрения конкретных функций IBM/PC мы будем детально обсуждать
служебные обращения к BIOS, обеспечивающие реализацию этих функций.
Справочное руководство по техническому обслуживанию персонального
компьютера содержит полный текст первой версии BIOS; при этом комментарии
хорошо поясняют, что делается и как делается; вопрос, почему это делается,
раскрыт слабо. Если Вы имеете навыки интерпретации листингов на языке
ассемблера, то при изучении листинга BIOS Вы получите ответы на любые
вопросы о принципах функционирования ПЭВМ.
Вторая часть ПЗУ, соответствует параграфу с шестнадцатеричным адресом
F600 и занимает 32 Кбайт памяти. Она содержит (хранит) программы
интерпретатора языка Бейсик. В ПЗУ хранится ядро интерпретатора языка
Бейсик для IBM/PC. Все кассетные версии языка Бейсик соответствуют
Бейсик-ПЗУ; основные функциональные возможности дисковой версии Бейсика, а
также расширенный Бейсик (BASICA) фактически реализованы в ПЗУ.
Практически все, что делают две указанные выше версии Бейсика - это
расширение версии Бейсик-ПЗУ. Соответственно, Бейсик-ПЗУ - это полная
версия языка Бейсик, содержащая основные программы, требуемые для дисковых
версий.
С появлением в справочном руководстве по техническому обслуживанию
текста программ BIOS не имеет смысла приводить текст программ Бейсик-ПЗУ;
однако в ряде случаев мы будем использовать фрагменты этой программы для
иллюстрации методов исследования содержимого ПЗУ.
Сама по себе область ПЗУ, содержащая Бейсик, не представляет особого
интереса, однако коль скоро в этой области расположено множество полезных
программ, то почему бы ими не воспользоваться в своей собственной
разработке? Поиск и декодирование нужных фрагментов Бейсика с последующим
их тщательным документированием представляет собой весьма трудоемкую
задачу, нами в данной работе не решаемую.
Подпрограмма или функция Бейсика может быть непосредственно
активизирована из программы пользователя с помощью механизма прерываний. В
главе 11 эти вопросы рассматриваются с необходимой полнотой.
Третий и последний участок ПЗУ нами здесь не рассматривается. Фирма
IBM оставляет за собой право расширять состав программ ПЗУ; пользователи
также могут встраивать в ПЗУ свои программы, расширяя таким образом
функциональные возможности IBM/PC. Дополнительные программы можно включать
в любое место миллионного адресного пространства, однако блок,
начинающийся с шестнадцатиричного адреса параграфа F400 и занимающий
8Кбайт, вплотную прилегающий к участку памяти, отведенного под Бейсик
специально выделен для таких программ. Если требуется свыше 8Кбайт памяти,
то программу можно сместить в область памяти с более низкими адресами.
Именно посредством включения в ПЗУ специализированных программ можно
превратить IBM/PC в специализированный компьютер. Прежде чем приступить к
более глубокому изучению BIOS мы продемонстрируем (в двух последующих
параграфах) способы исследования содержимого ПЗУ.
6.2. Анализ содержимого ПЗУ средствами программы DEBUG
В данной главе мы рассмотрим особенности анализа программ и данных,
размещаемых в ПЗУ. Несмотря на то, что предлагаемая методика ориентируется
главным образом на программы, расположенные в ПЗУ, она может
использоваться также и в случае программ, хранящихся на дискете. Поэтому
все, о чем пойдет здесь речь применимо как к компонентам DOS,
расположенным на дискете, так и к компонентам, входящим в состав других
программ.
Доступ к любому фрагменту ПЗУ может быть осуществлен также средствами
Бейсика или Паскаля. Методы адресации объектов памяти, рассмотренные в
главе 3, позволяют работая на Бейсике или Паскале, обратиться к любой
ячейке памяти. Ниже приводится пример программы на Бейсике, отображающий
содержимое ПЗУ.
100 REM отображение фрагмента ПЗУ в шестн. формате
110 PARAGRAPH=&HF600
120 DEF SEG=PARAGRAPH
130 PRINT
140 PRINT "Отображение содержимого параграфа",HEX$(PARAGRAPH)
150 PRINT
160 PRINT "Десятичное Шестнадцатеричное"
170 PRINT "Смещение Смещение Содержимое ПЗУ";
180 OFFSET%=0
190 FOR LINES%=1 TO 16
200 PRINT
210 PRINT OFFSET%, HEX$(OFFSET%)
Хотя приведенная программа позволяет нам судить о содержимом ПЗУ,
смысл данных остается "за кадром". Мы, таким образом, подошли к очень
важному и необходимому инструменту, поставленному в составе DOS -
программе DEBUG.
Программа DEBUG позволяет осуществлять три вида действий в отношении
содержимого ПЗУ: выборку произвольного участка памяти и отображать ее
содержимое в двух форматах:
1)шестнадцатеричный / ASCII.
2)формат не связанный с деассемблированием.
Программа DEBUG может быть также использована для исследования
программ, хранящихся на дискете. В последнем случае, правда,
представляется более эффективным использовать программу Disklook,
записанную на дискету, прилагаемую к данной книге или программу SecMod
входящую в состав программ Norton Utility. Прежде чем двигаться дальше,
поясним смысл термина "деассемблирование".
Программы, представленные на языке машинных команд, чрезвычайно
сложны для восприятия человеком. Даже в случае компьютеров, имеющих
сравнительно простой формат машинных команд, этот язык весьма сложен для
понимания. Микропроцессор 8088 регламентирует очень сложную структуру
языка машинной команды. Это положение справедливо в отношении большинства
микропроцессоров - их проектировщики жертвуют простотой для достижения
максимальной производительности и компактности кода. Вследствие этого,
требуется приложить значительные усилия для просмотра и понимания
листинга, представленного на языке машинных команд в шестнадцатеричном
коде.
Процесс деассемблирования, реализуемый программой DEBUG, значительно
облегчает процедуру интерпретации машинного языка. Деассемблирование - это
процесс трансляции или преобразования инструкций машинного языка,
представленных в абсолютном шестнадцатеричном виде в символическую нотацию
языка ассемблера. Так, например, на языке ассемблера можно записать INC AX
(увеличить содержимое регистра AX на единицу); ассемблер переведет эту
конструкцию в команду на машинном языке с кодом 40/16. Функция
деассемблера состоит в том, чтобы привести команду машинного языка с кодом
40/16 обратно к виду INC AX.
Шестнадцатеричный код 40 сравнительно легко может быть преобразован и
вручную, при наличии таблицы кодов команд микропроцессора 8088. С
большинством кодов остальных команд дело обстоит не так просто, поскольку
в ряде случаев смысл команды изменяется в зависимости от значения
конкретных битов. В случае использования команды DEBUG все запутанные
случаи декодирования преодолеваются в режимах команды деассемблирования.
Несмотря на то, что деассемблер может выполнить преобразование команд
машинного языка к более удобным командам языка ассемблера, целый ряд
весьма существенных компонентов программы, написанной на языке ассемблера
реконструкции не поддается. Так, очевидно, не могут быть восстановлены
весьма полезные комментарии программиста. Кроме того, не представляется
возможным восстановить оригинальные символические имена адресов памяти.
Таким образом, вместо оригинальной авторской конструкции типа
IMP FINISH; выполнено, перейти к завершающей процедуре
деассемблер сгенерирует строку вида
IMP OE6C
Пользователь видит перед собой команду перехода, однако ее смысл
остается для него неясным.
Деассемблер, безусловно, не в состоянии полностью заменить
программиста, особенно, в том, что касается смысла деассемблируемой
программы. Но он существенно упрощает задачу, когда предъявляет
пользователю команду, а также помогает отличить область памяти, содержащую
данные от области памяти, содержащую команды.
Для иллюстрации особенностей исследования программ ПЗУ возьмем
программу Бейсика и используем ее в качестве объекта приложения программы
DEBUG. Все дополнительные сведения будут сообщены ниже, либо проясняться в
ходе рассмотрения примера. Для детального изучения программы DEBUG -
следует внимательно прочесть соответствующие разделы руководства по
операционной системе; этот материал не прост для усвоения, однако он
содержит многие важные технические детали.
Для пользователей, не работавших с программой (командой) DEBUG,
сообщим некоторые дополнительные сведения. Подобно остальным командам, эта
команда запускается путем набора на клавиатуре ее имени. В процессе ее
выполнения запросам на ввод исполнительных операторов предшествуют лишь
"тире": обычный запрос операционной системы (символ "А") в этом случае не
используется.
Большинство действий с программой DEBUG отображаются на экране
дисплея, однако может возникнуть потребность в их распечатке для
последующей работы. Собственно программа DEBUG не выводит результаты своей
работы на принтер.
В этой связи, однако, следует напомнить, что существует два способа
получить твердую копию. Первый способ заключается в использовании для
копирования текущего кадра клавиши PrtSc (Печатать содержимое экрана);
второй - состоит в использовании принтера для эхо-отображения путем
одновременного нажатия клавиш Ctrl и PrtSc.
Итак, мы приняли решение об исследовании участка ПЗУ, содержащего
Бейсик; нам также известно, что адрес начала этой программы соответствует
шестнадцатеричному адресу F600. Можно запускать программу DEBUG и
приступать к непосредственной работе с этой программой.
Предположим теперь, что адрес объекта наших поисков (F600) нам
неизвестен. В этом случае поиск может вестись в двух направлениях. Первое
- это методичное использование операции D (означающее "дамп" или
"отображение") до тех пор, пока не появится что-нибудь интересное. Второе
- более эффективное направление - состоит в предъявлении программе DEBUG
образца объекта, который, по нашему мнению должен находиться в памяти.
В качестве образца объекта поиска выберем сообщение об ошибке,
выдаваемое Бейсиком: "Illegal Function call" (Неверный вызов функции). На
рис 6.1 показана процедура запуска программы DEBUG и оператор поиска
указанного выше сообщения программы Бейсик. Предположим, для
определенности, что в качестве начального адреса просмотра (поиска)
программе DEBUG был передан адрес параграфа F000 (лежащий немного ниже
адреса начала Бейсика), а также размер (длина) просматриваемого участка
(L) равный 65535 байтам (`FFFF'в шестнадцатеричной системе счисления).
Программа DEBUG сообщает, что интересующий нас текстовой объект найден.
Это сообщение формируется в форме сегментированного адреса F000: 63F4,
означающего, что относительно параграфа F000 берется шестнадцатеричное
смещение 63F4. Выполнив действия над адресами, мы получим полный
20-байтовый адрес.
F000
+
63F4
_____________
F63F4
A > DEBUG
- S F000 : 0000 L FFFF "Illegal Function call"
F000 : 63F4
Рис. 6.1. Запуск программы DEBUG и поиск сообщения
Значение адреса искомого текстового объекта говорит о том, что
найденное сообщение расположено недалеко от начального параграфа
размещения Бейсика F600. Если представить 20-ти битовый адрес F63F4 в
сегментированном виде, используя в качестве базы параграф F600, то будем
иметь адрес F600:03F4. Таким образом, относительное смещение от начала
Бейсика составляет только 3F4/16 или 1012/16 байтов.
Вводя команду D F600 : 03E0 (вывод на экран содержимого области
памяти, начиная с адреса - т.е. области памяти, начинающейся несколько
раньше найденного сообщения - относительно базового адреса параграфа F600)
можно отобразить содержимое близлежащего пространства памяти, включающего
область найденного сообщения. На рисунке 6.2. представлен результат
выполнения этой команды. Программа DEBUG выводит на устройство отображения
- в шестнадцатеричном и символьном (ASCII) форматах - содержимое участка
памяти длиной 80 байт, причем этот участок памяти содержит искомое
сообщение. Нам таким образом представлено искомое сообщение, а также целый
ряд других сообщений об ошибках Бейсика, т.е. именно то, что - как
предполагалось - должно находиться где-то поблизости. При необходимости с
помощью программы DEBUG можно продолжить отображение на экране последующих
участков памяти; для этого следует ввести команду D, не указывая адреса
начала ячейки.
Рисунок 6.2. иллюстрирует ряд возможных особенностей, которые следует
иметь в виду, используя команду "D" в рамках программы DEBUG. Одна из этих
особенностей состоит в том, что в левую часть поля вывода помещается
шестнадцатеричная информация, а в правую - информация в коде ASCII. Вторая
особенность заключается в том, что кодовая комбинация не имеющая
символьного представления в коде ASCII изображается в правой части поля
вывода с помощью точки. Отсюда, в частности, вытекает важное следствие:
для того, чтобы использовать эхо-печать программа DEBUG контролирует
поступающие на ее вход кодовые комбинации, не имеющие представления в коде
ASCII; эти комбинации могут быть восприняты принтером в качестве
управляющего кода. Вследствие этого, нельзя расчитывать, что правая часть
поля вывода (информация в коде ASCII) будет давать объективную картину.
Все, что может быть приведено к стандартным печатным символам, или
является печатным символом (со старшим битом в единичном состоянии) будет
отпечатано, все остальное будет преобразовано в точки.(Здесь уместно
заметить, что программа DiskLook, входящая в состав пакета программ,
записанного на дискету, прилагаемую к настоящей книге, отображает в
точности все шестнадцатеричные коды и коды ASCII, поскольку выводит
информацию только на экран и не использует эхо-печать).
До сих пор мы рассматривали способы отображения данных, или поиска
априори известных данных, хранящихся в ПЗУ с помощью программы DEBUG. Ниже
мы будем решать существенно более сложную задачу - задачу
деассемблирования и интерпретации программного кода на машинном языке.
6.3. Анализ содержимого ПЗУ - метод деассемблирования
Наш следующий шаг в решении задачи исследования содержимого ПЗУ и
использования программы DEBUG состоит в знакомстве с особенностями
процесса деассемблирования. Команда "U" программы DEBUG - означает
"деассемблирование" - осущеcтвляет преобразование произвольных кодов
памяти в мнемонические коды языка ассемблера. Деассемблер реализует:
перевод абсолютных шестнадцатеричных кодов программ, в коды команд на
языке ассемблера (например: AX или DS). Деассемблер не делает две вещи.
Первое. Деассемблер не интерпретирует смысл программы и не обучает
пользователя. Для понимания листинга, выдаваемого деассемблером,
необходимо либо знать язык ассемблера, либо иметь желание покопаться в
справочном руководстве и, установив смысл мнемонических обозначений,
постараться понять суть дела. (Для этой цели могут быть использованы
следующие источники: "Руководство по языку ассемблера фирмы IBM",
справочники по системе команд микропроцессора 8086/8088, такие как
"Учебное пособие по микропроцессору 8086" С.Морса или "Описание
микропроцессора 8086" Ректора и Алекси).
Для понимания программы на языке ассемблера (или программы,
приведенной к языку ассемблера), знания одного языка Ассемблер
недостаточно, необходимо также иметь некоторое представление о
функциональной ориентации программы.
Постижение программы представляет собой занимательное умственное
упражнение, своего рода головоломку. Со временем эта процедура становится
все проще и проще, отчасти потому, что времени на поиск смыслового
описания команды уходит все меньше и меньше, а отчасти потому, что
усваиваются приемы программирования на ассемблере и способы достижения
целей. Далее мы остановимся на этих вопросах более подробно.
Второе, что не под силу программе деассемблера - это установка так
называемой абсолютной синхронизации. Известно, что команды машинного языка
для микропроцессора INTEL 8086/8088 имеют переменную длину - от одного до
шести байтов. После того как деассемблеру сообщена конкретная позиция
памяти он приступает к процедуре прямого декодирования, не отличая кодов
команд от данных. Достаточно ошибиться в выборе исходной позиции памяти
(например, попасть не на границу между командами или в область данных) и
результат окажется неверным.
Если начальная точка набора команд известна, то никаких проблем не
возникает. Если какие-либо предварительные сведения отсутствуют, то
следует провести необходимое исследование. Первое, что необходимо сделать
- это выбрать исходную точку в наборе команд и запустить операцию
деассемблирования. После этого следует предпринять попытку
интерпретировать результат работы деассемблера. Если осмысленные
программные участки отсутствуют (ниже мы приводим некоторые соображения по
их выявлению),то следует повторить процедуру, сместив начальную точку на
один, два или три байта по отношению к предыдущей попытке. Мы таким
образом пытаемся отыскать точку синхронизации, расположенную на стыке двух
команд, после чего может быть восстановлен оригинальный набор команд. Если
очередная попытка закончилась неудачно, то следует попробовать еще раз.
Поскольку большинство команд имеют длину один или два байта, то точку
синхронизации отыскать как правило несложно. Кроме того, необходимо
учитывать, что процессу деассемблирования присущ элемент
самосинхронизации. Даже если в качестве начальной точки для
деассемблирования выбрана точка, расположенная внутри команды, то часто
случается, что процесс деассемблирования при обработке последуюющих команд
самопроизвольно попадает в нужную точку. После этого все идет гладко, по
крайней мере в сторону увеличения адресов. (В обратную сторону это
приходится делать вручную).
Выполняя деассемблирование программы необходимо внимательно следить
за данными (это нечто противоположное командам), которые деассемблер
превращает в причудливые ассемблерные конструкции. Основной и наиболее
быстрый способ обнаружения данных состоит в использовании команды "D", для
представления информации в шестнадцатеричном коде и коде ASCII. Здесь в
первую очередь следует обращать внимание на самые очевидные вещи -
содержательно осмысленную последовательность символов ASCII (фразы),
такую, например, как "неверный вызов функции". После этого можно
приступать к выявлению более тонких признаков данных.
В обычных программах (т.е. в программах, размещаемых вне ПЗУ) наличие
полей, заполненных шестнадцатеричными нулями является признаком рабочей
области данных, то есть такой области, которая будет использоваться в ходе
выполнения программы; в процессе деассемблирования данные этой области
неактуальны. ПЗУ не может использоваться для хранения рабочих данных,
поскольку запись в эту память невозможна. Таким образом, наличие полей,
состоящих из шестнадцатеричных нулей нетипично для ПЗУ-программ.
Признаком возможного наличия данных являются байты или двухбайтовые
слова, для которых второй или старший байт содержит 0 или F. Дело в том,
что значительное число констант программ являются малыми числами,
положительными (т.е. начинающихся с 0), либо отрицательными (т.е.
начинающихся с F). Если будет обнаружена последовательность байтов,
удовлетворяющая этим требованиям, то весьма вероятно, что найдена область
памяти, используемая для хранения одного или двухбайтовых чисел. Эта часть
программы не может содержать команды.
Пытаясь осмыслить структуру листинга, выдаваемого деассемблером,
необходимо иметь в виду следующие положения. Обычные программы, прошедшие
этап редактирования связей, будут хранить команды в одном месте, а данные
в другом. Такой подход является элементом хорошего стиля программирования;
именно так работает редактор связей дисковой операционной системы.
Описанный принцип характерен для программ всех типов, однако для программ
типа EXE он справедлив в большей степени чем для программ типа COM. С
другой стороны, ПЗУ-программы, подобные тем, которые рассматривались нами
выше, часто разрабатываются вне концепций технологии программирования.
Данные в этих программах могут следовать вперемешку с командами.
Если предположительно обнаружен участок программы, в котором вслед за
командами следуют некоторые данные, то существует по крайней мере один
достоверный способ отличить конец последовательности команд от
расположенных вслед за ними данными, и таким образом избежать
необходимости рассматривать недостоверный протокол деассемблирования.
Последняя команда фактического участка программы должна представлять собой
ту или иную разновидность команды перехода или ветвления. Команды
ветвления включают любые команды переходов, кроме команд условных
переходов. В качестве общеупотребительной команды завершения программы
принято использовать команду возврата (RET) (возврат из вызванной ранее
подпрограммы).
Могут использоваться также и команды CALL (Вызов) и INT (Прерывание);
хотя их использование в качестве команды завершения последовательности
команд встречается достаточно редко.
Существует ряд особенностей, на которые следует обращать внимание при
решении вопроса подлинности предъявленного участка программы после его
деассемблирования. В первую очередь следует обратить внимание на
используемые регистры. Если операции реализуются на регистрах,
используемых как правило для выполнения арифметических операций (AX или
AL, AH;BX или BL, BH; CX или CL, CH; DX или DL, DH), но никакие действия в
отношении результата не предпринимаются, то это вызывает подозрение. Здесь
следует быть более внимательным. Один из результатов выполнения
арифметических операций состоит в установке флагов - следовательно,
естественно ожидать наличие команд условных переходов по состоянию флага,
таких как JNC (команда перехода по условию отсутствия установленных битов
флага).
Рассмотрим аспект некорректности использования регистров в
реконструируемой программе. Программы не используют регистры некоторым
хаотичным образом - им присуща определенная дисциплина. Поэтому весьма
непривычно, скажем, видеть загрузку или непосредственную ссылку на
сегментные регистры - особенно сегментный регистр программы (CS) и
сегментный регистр стека памяти (SS). Загрузка сегмента регистра данных
(DS) более вероятна, но также встречается редко. С другой стороны,
дополнительный сегментный регистр (ES) практически постоянно используется
программами, поэтому его загрузка - дело обычное. Если загрузка регистра
CS или SS все-таки обнаружена, то это должно быть сделано в программной
секции, формирующей операционную структуру выполняемой программы; в этом
случае вероятнее всего, что в одном месте будет произведена загрузка
нескольких сегментных регистров, в частности, CS, SS и DS.
Попытка реконструировать программу по листингу, выданному
деассемблером или даже просто попытка убедиться в подлинности
предъявленного набора команд, представляет собой захватывающее умственное
упражнение. Для этого,кстати, не требуется специальная подготовка. Как
правило, достаточно бывает просто здравого смысла. Усвоив все наши
рекомендации и приобретя некоторые практические навыки, Вы, при
необходимости, сможете все это проделать. В следующем параграфе мы
выполним декодирование (реконструкцию) программы Бейсик-ПЗУ; читатель,
таким образом, получит представление о том, как это делается и убедится в
том, что это не очень сложно.
6.4. Анализ содержимого ПЗУ - реконструкция интерпретатора
В качестве примера попробуем деассемблировать фрагмент Бейсик-ПЗУ.
Начнем с исходного параграфа, т.е. параграфа с адресом F600(16). На
рисунке 6.3 представлен результат деассемблирования первых 32 байтов.
Шестнадцатеричные нули в конце фрагмента программы (преобразованы
деассемблером в команды ADD (Сложить)) представляют собой данные. Они
"хорошо" иллюстрируют высказанное ранее положение о том, что нулевые
данные нетипичны для ПЗУ-программ. Команда DB (определить байт),
предшествующая полю нулей, появилась как реакция деассемблера на кодовую
комбинацию, которая не может быть преобразована в команду ассемблера
(такая ситуация, благодаря широкому набору команд, встречается довольно
редко). Итак, являются ли все данные, которым предшествует команда
безусловного перехода собственно осмысленными командами? Предварительный
ответ на этот вопрос может быть положительным, поскольку для завершения
выполнения подпрограммы используется команда возврата (RET). Пока все идет
хорошо.
Можно ли приписать какие-то осмысленные действия первым пяти командам
|