Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Textnew2

.pdf
Скачиваний:
13
Добавлен:
06.02.2018
Размер:
1.48 Mб
Скачать

forgdt dw lengthgdt-1 ; информация для рег. GDTR; предел для 5x8 dd BASEADR+mgdt ; двойное слово для базового адреса GDT

selcs equ 8h

; селектор для регистра CS

selds equ 10h

; селектор для регистра DS

selss

equ 18h

; селектор для регистра SS

seles

equ 20h

; селектор для регистра ES

 

align 16

 

endata: ; метка конца сегмента данных

 

align 16

 

mgdt:

; начало глобальной таблицы дескрипторов

des_null dd 0,0 ; нулевой дескриптор должен быть пустым

des_cs dw endcode-1

; дескриптор для регистра CS; поле для limit

dw BASEADR

 

db 0, 98h, 0, 0

 

des_ds dw endata-1

; дескриптор для регистра DS; поле для limit

dw BASEADR

 

db 0, 92h, 0,0

 

des_ss dw 7C00h ; дескриптор для регистра SS; поле для limit db 0,0,0, 92h, 0,0

des_es dw 3999 ; дескриптор для регистра ES; предел для длины 4000 db 00,80h,0bh, 92h, 0,0

lengthgdt equ $-mgdt

; НАЧАЛО ТАБЛИЦЫ ДЕСКРИПТОРОВ ПРЕРЫВАНИЙ idt: ;label qword

dscint0 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint1 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint2 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint3 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint4 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint5 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint6 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint7 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint8 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

161

dscint9 dw interp9, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint10 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint11 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint12 dw interp, selcs ; вентиль прерывания db 0, 86h, 0, 0

dscint13 dw interp13, selcs ; вентиль прерывания db 0, 8Eh, 0, 0

; конец таблицы дескрипторов прерываний -------------------------------

Рис. 7.6.1. Внесистемная обработка прерываний от клавиатуры в защищенном режиме

Главная часть данной программ отличается от предыдущей тем, что за командой загрузки регистра GDTR записана команда lidt [foridt], которая загружает регистр IDTR из области данных с именем foridt. В этой области младшее слово содержит значение, задающее максимальное смещение внутри таблицы из 14-ти дескрипторов. Старшая часть этой области содержит базовый адрес таблицы дескрипторов прерываний. С этой целью операндом директивы DD записано выражение BASEADR+idt, что и определяет корректно требуемый адрес.

Сами дескрипторы прерываний собраны в таблицу, обозначенную в исходной программе меткой idt. Отдельные дескрипторы для удобства наблюдения читателем обозначены именами вида dscintномер. (В действительности такие обозначения излишни, так как ссылки на эти имена не используются). Каждый подобный дескриптор описывается в программе двумя строками текста. В первой - директивой DW определяются значения младших двух байтов смешения для обработчика прерывания, который автоматически вызывается через этот дескриптор, и селектор сегмента машинных команд, в котором обработчик размещен. Вторая строка описания задается директивой DB и определяет оставшиеся четыре байта дескриптора. В 5-м байте дескрипторов записаны коды типа дескриптора прерывания, причем для всех дескрипторов, кроме предназначенного для номера прерывания по нарушению общей защиты, это значение равно 86h. Для обработчика нарушения общей защиты код типа выбран 8Eh, чтобы обеспечить автоматическое запоминание 32-битных регистров при возникновения прерывания.

После команд перехода в защищенный режим и установки селекторов дескрипторов в сегментных регистрах, выполняется команда вызова программного прерывания с номером 5. Тем самым запускается в работу обработчик interp, который выдает сообщение о появлении прерывания. Затем установкой в порту 21h разрешается прохождение сигналов от клавиатуры через КНП и далее командой STI разрешается поступление сигналов через аппаратный вход процессора.

162

Пара команд, начиная с метки waitf, многократно проверяет значение служебного поля flagexit. Пока в этом поле не появится ненулевое значение, цикл повторяется неопределенно много раз. Тем самым обеспечивается активное ожидание установки указанного поля. Как только оно будет установлено, следующими командами в регистр EBX заносится большое смещение, заведомо большего чем размер сегмента данных, и выполняется попытка записи по этому смещению. Это запись, на самом деле, предназначена для демонстрации срабатывания общей защиты.

В результате (хотя и неявно!) запускается подпрограмма обработчика общей защиты, имеющая в исходном тексте имя interp13. После такого срабатывания наша программа переходит в бесконечный цикл с меткой повторения dalee, аналогично рассмотренным ранее.

В данной программе после разрешения прерываний от клавиатуры, по каждому нажатию на клавишу вызывается обработчик с именем interp9. Она выдает на экран сообщение о том, что нажата клавиша. Этот обработчик отличается от одноименного из программы рис. 8.4.1 только тем, что после чтения скан-кода проверяется, не от клавиши ли Esc он появился. Если была нажата именно эта клавиша, что и определяется проверкой, то обработчик устанавливает в единичное значение служебное поле flagexit. Последнее при очередной проверке этого поля в основной программе приведет к прекращению активного ожидания и к зацикливанию в этой главной части для наблюдения результатов вывода.

Существенной особенностью данной программы является наличие обработчика прерываний общей защиты. Этот обработчик, описанный как подпрограмма interp13, выводит содержимое регистров CS и EIP, которое они имели в момент определения внутреннего прерывания аппаратурой процессора. Эти значения автоматически запоминаются в стеке внутренней процедурой прерывания. Для доступа к ним используется регистр кадра процедуры EBP, установка которого осуществляется по обычным правилам доступа к кадру. Вывод содержимого регистров осуществляется в виде шестнадцатеричных кодов, которые предваряются поясняющей информацией, содержащейся в текстовых полях с именами msg_eip и msg_cs (выводятся тексты 'EIP=' и ' CS='). Вывод текстов производится уже знакомой читателю процедурой wiwtxt. Для вывода же шестнадцатеричных значений регистров используются процедуры writedword, writeword, предназначенные, соответственно, для вывода содержимого аргумента процедуры, который задается для них в регистрах EAX и AX. (Первая из них выводит значение 32-битного кода, вторая - значение 16-битного кода.)

В качестве вспомогательных, последние две процедуры используют подпрограммы с именами writehex и writesemibyte. Первая из них выводит в шестнадцатеричном виде 8-битное значение из регистра AL, вторая аналогично выводит младшую половину из регистра AL. Весь вывод осуществляется прямым доступом к видеопамяти, причем указание места в ней, с которой осуществляется вывод, задается переменной begpos. Последняя переменная постоянно наращивается после каждого вывода, чтобы обеспечить следующий вывод в дальнейшие позиции экрана. При этом выполняется проверка, не будет ли задавать эта переменная для следующего выво-

163

да область за пределами экрана. В последнем случае ее значение переустанавливается, чтобы продолжать вывод с начала верхней строки экрана. Начальное значение переменной begpos задает вывод с начала десятой строки экрана.

Заметим, что использование дескрипторов с кодом типа 86h для большинства номеров прерываний и соответствующее применение команды IRET - в качестве завершения обработчика - имело в рассмотренной программе причиной максимальную совместимость с более ранними программами (чтобы проще было ввести начинающего в предмет). В действительности, в подавляющем большинстве применений все обработчики запоминают и восстанавливают 32-битные регистры, что только и обеспечивает полноценную поддержку 32-исполняемых программ (такие программы обязательно используют при работе регистр EIP для указания следующей выполняемой команды). Для полноценных 32-битных обработчиков прерываний следует обязательно использовать код типа 8Eh (или код 8Fh для обработчиков ловушки) и завершать подпрограмму обработчиков командой, которая на ассемблере NASM задается мнемоникой IRETD.

Только что рассмотренную программу можно без большого труда модифицировать, чтобы она обеспечивала полную защиту от попыток чтения и записи данных вне запланированной программистом области данных. Для этого нужно так ее изменить, чтобы сегмент данных, описанный дескриптором, начинался с действительного адреса начала этого сегмента, а в качестве предела содержал предельное допустимое смещение в этой области данных относительно ее начала. Кроме того, (что, может быть, не очевидно начинающим) следует позаботиться, чтобы все ссылки на данные в этой области внутри программы задавали смещение относительно действительного начала этой области, а не относительно общего начала области машинных команд и размещенных далее данных. Последняя цель автоматически обеспечивается, если строит программу как многосегментную (тогда компилятор автоматически строит смещения относительно начала соответственного сегмента). К сожалению, описание многих сегментов в программе требует далее использования компоновщика, который, в свою очередь, для большинства распространенных программных инструментов, строит из многосегментных объектных модулей исполняемые программы только специальных форматов, рассчитанные на конкретный вид операционной системы. Нам же удобней воспользоваться простым двоичным кодом, формируемым компилятором NASM для односегментных программ.

Поэтому мы рассмотрим решение, обеспечиваемое как бы "ручной работой". А именно, будем записывать все именованные ссылки для работы программы в защищенном режиме с помощью специальной формы, имеющей вид

имя+corrt,

где служебное значение corrt определено ниже как corrt EQU _start-data

после метки data, которая, в свою очередь, задает начало области данных. Метка _start должна находится в самом начале программы. Из этих конструкций легко видеть, что значение corrt задает в виде абсолютной константы(не зависящей от

164

размещения программы в памяти) величину смещения области данных с меткой data от общего начала машинных кодов и данных, причем величину, взятую с обратным (отрицательным) знаком. Тогда запись имя+corrt будет всегда давать смещение именованной области данных, обозначаемой именем имя, именно от начала области данных.

Модифицированная программа приведена на рис. 7.6.2 отдельными фрагментами и командами, которые отличают ее от программы с рис. 7.6.1 (вместе с некоторыми строками, которые входят и в исходную программу и приводится для удобства восприятия текста, а также в помощь для восстановления полного текста этой программы).

BASEADR EQU 8000h segment .text

_start:cli

. . .

lidt [foridt] ; загрузка регистра IDTR; здесь без изменения - реальный режим! lgdt [forgdt] ; загрузка регистра GDTR; здесь без изменения - реальный ре-

жим!

mov eax,cr0

. . .

wiwod: mov cx, lenmsg; вывод сообщения о PMODE

 

mov di, 800

 

 

mov si, msg+corrt

; модифицировано

 

. . .

 

waitf: cmp byte [flagexit+corrt], 0

; модифицировано

 

je waitf

 

;;

mov al, [des_ds+corrt]

; новая команда

 

mov al, [hhh+corrt]

; новая команда

dalee: jmp dalee

 

;----- end main program

 

wiwtxt:

. .

mov al,[attr+corrt] ; атрибут для текста ; модифицировано

rr:movsb ; пересылка байта из сообщ. на экран, инкр.SI и DI

. . .

interp9: ; обработчик прерываний с номером 9

. . .

 

 

mov byte [flagexit+corrt],1

; модифицировано

noexit:

call writehex ; вывод скан-кода клавиши

mov di, [begpos+corrt]

; модифицировано

mov BYTE [es:di],' '

 

add word [begpos+corrt],2

; модифицировано

165

cmp word [begpos+corrt], 3997; модифицировано jle dal

mov word [begpos+corrt], 0 ; модифицировано

. . .

interp: ; proc near ; процедура для вывода на экран сообщения

. . .

 

mov si, msgintp+corrt

; модифицировано

. . .

 

interp13: ; процедура вывода сообщения о нарушении общей защиты

. . . ; модифицированы следующие строки mov word [begpos+corrt], 160 ; начало 1-й строки экрана

mov esi, msg_eip+corrt ;offset mov di, [begpos+corrt]

mov ecx, 4

mov BYTE [attr+corrt], 0Ch ; red on black call wiwtxt ; вывод на экран текста 'EIP=' add word[begpos+corrt],8

mov eax, [ebp+8] ; в стеке на [ebp+8] - значение EIP call writedword ; к процедуре для вывода EAX mov esi, msg_cs+corrt ; offset

mov di, [begpos+corrt] mov ecx, 4

mov AL, 0Ch ; red on black

call wiwtxt ; вывод на экран текста ' CS=' add word [begpos+corrt],8

mov ax, [ebp+12] ; в стеке на [ebp+12] - значение CS

. . .

writesemibyte: ;proc

 

 

. . .

 

 

hex: add dl,'A'-10

 

 

wiwo:mov di, [begpos+corrt]

; модифицировано

add word [begpos+corrt],2

; модифицировано

mov [es:di],dl

 

 

mov al,[attr+corrt]

 

; модифицировано

. . .

 

 

endcode:

 

; метка конца сегмента команд

hhh db 137

 

; новое поле

;;data segment

; сегмент данных

data:

 

 

corrt EQU _start-data

; информация для регистра IDTR в защищенном режиме

. . .

endata: ; метка конца сегмента данных

166

 

align 16

mgdt:

; начало глобальной таблицы дескрипторов

des_null dd 0,0 ; нулевой дескриптор должен быть пустым des_cs dw endcode-1; ; дескриптор для регистра CS; поле для limit

dw BASEADR db 0, 98h, 0, 0

des_ds dw endata-data-1 ; дескриптор для регистра DS; поле для limit dw BASEADR+data

db 0, 92h, 0,0

. . .

idt: ;label qword

.. .

;конец таблицы дескрипторов прерываний ----------

Рис. 7.6.2. Программа обработка прерываний от клавиатуры в защищенном режиме с разделенными сегментами

Кроме уже описанных дополнений к исходной программе, в нее перед зацикливанием главной части введены команды

;;mov al, [des_ds+corrt] mov al, [hhh+corrt]

Практически достаточно использовать любую из них, а другую закомментировать. Любая из этих команд вызывает прерывание по нарушению общей зашиты. В первой из них делается попытка занести в регистр AL содержимое первого байта из дескриптора для сегмента данных (аналогичная команда в предыдущей программы беспрепятственно бы выполнилась). Но для нового варианта область памяти, содержащая таблицу GDT, вынесена за пределы сегмента данных, описанных в сегменте данных. Именно, метка endata поставлена за используемыми программой данными, но перед областью памяти для дескрипторов сегментов памяти. Тем самым попытка дотянуться в командах программы до данных этой области связана с попыткой использования смещения, большего чем поле предела в соответствующей дескрипторе. (Читателю рекомендуется внимательно рассмотреть листинг последней программы, чтобы заметить, что поле, задающее смещение байта данных во втором операнде действительно имеет значение, большее чем записанное в поле предела указанного дескриптора.

Вторая из рассматриваемых команд задает обращение к именованной области hhh, которая помещена в памяти до области данных, описанной дескриптором сегмента данных. Поэтому попытка ее выполнения также вызывает нарушение общей защиты.

7.7. Построение простейщего начального загрузчика

167

Для запуска рассмотренных выше демонстрационных программ рекомендовалось использовать автоматический загрузчик от операционной системы OS/2, находящийся на инсталляционной дискете этой ОС. С учетом, что не каждый читатель может найти такую инсталляционную дискету, мы рассмотрим построение функционального аналога этого загрузчика.

Начальный системный загрузчик для OS/2 размещается в загрузочном секторе логического диска, причем он построен таким образом, чтобы работать для любого формата логического диска, допустимого этой ОС. В частности, он обеспечивает функционирование и для логического диска, входящего в состав жесткого диска. Такое повышенное требование не могло не отразиться на его структуре. Мы будет рассматривать упрощенный вариант, но упрощенный не до конца возможностей, чтобы "по пути" показать типовые приемы построения более универсальных загрузчиков.

В соответствие с документацией на современные магнитные носители информации, начальный сектор любого такого диска (отдельной дискеты, логического диска как части жесткого) имеет стандартизованный формат [4, 21]. Этот формат предполагает, что в первых байтах сектора находится код команды JMP, которая обеспечивает обход последующих байтов, содержащих характеристики логического носителя информации. Начиная с третьего байта (базируясь с нулевого) этого сектора, размещаются 8 байтов, задающие идентификацию системы форматирования диска, а последующие составляют так называемый Bios Parameter Block (BPB). Этот блок в современном варианте имеет не менее 51 байта.

Программа рассматриваемого нами упрощенного загрузчика приведена на рис. 7.7.1. В самом начале она содержит упомянутую команду перехода на собственно программу загрузки, начинающуюся за блоком характеристик BPB и имеющую метку начала beg. Поле идентификации проще всего взять фиксированное от какой-либо системы форматирования, так как оно, в частности, рассматривается системой проверки логических ярлыков дисков (меток тома) и при произвольном содержании может быть заключено, что диск не имеет метки. В нашем примере это поле с именем Oem.

SEGMENT .text

start: jmp SHORT beg ; abJmpCode nop

Oem DB '*wazZIHC'; 'IBT 2003' ; abOem SectSiz DW 512 ; число байтов на сектор ClustSiz DB 1 ; число секторов на кластер

ResSecs DW 1 ; число секторов, находящихся перед таблицей FAT ;(загрузочный и зарезервированные)

FatCnt DB 2 ; число экземпляров таблицы FAT

RootSiz DW 0e0h ; максимальное число 32-байтовых записей ; оглавления, это число равно 224 для 3.5" дискеты

TotSecs DW 0b40h ; общее число секторов на данном логическом

168

;диске, если диск содержит более >32Mб, то здесь 0 и следует

;использовать BigTotSects

Media DB 0f0h ; идентификатор типа логического диска, ; этот код - для дискеты

FatSize DW 9 ; число секторов в одной таблице FAT TrkSize DW 12h ; число секторов на дорожке (track) HeadCnt DW 2 ; число головок чтения/записи на диске HidnSec DW 0,0 ; число скрытых секторов

TotSectV DD 0b40h ; 32-битное общее число секторов на томе

LogDriv

DB 0

 

; код дисковода ; для Floppy равно 0

Reserv1

DB 0

 

 

SigntrDB 29H ; ')'

; сигнатура расширенного формата BPB

VolSerN DD 2003 ; серийный номер, задаваемый при форматировании

VolLabel DB 'IBTSYSTEM '; имя метки тома

 

DB 'FAT16

'

; идентификатор файловой системы

Dat0

DW 0

 

; для числа секторов от начала диска до конца корневого каталога

(Root)

 

 

 

beg:

cli

 

 

 

; установка сегментных регистров для доступа к стеку и данным

 

xor bx,bx ; установить Стек на начало памяти с 7C000h (28K)

 

mov ss,bx

; установить сегмент Стека на нулевой адрес

 

mov sp,07C00h ; установить регистр SP на конец Стека

 

sti

 

;

 

 

mov dx,007C0h ; сегментный адрес границы 28K

 

mov ds,dx

; установить сегмент данных на границу 28K

;подготовка данных для последующего чтения с загрузочного диска mov al,[FatCnt] ; число таблиц FAT

cbw ; расширить код до всего регистра AX

mul word [FatSize]; умножит на число секторов в FAT

;теперь в AX - секторов во всех FAT

add ax,[ResSecs] ; добавить число зарезерв. перед FAT секторов ;теперь в AX - порядковый номер нач. сектора в области ROOT

push ax

 

xchg cx,ax

; перенести это число обменов регистров в рег. CX

mov ax,32

; AX=размер записи оглавления (в байтах)

mul word[RootSiz]

; теперь в AX размер корневого (Root) оглавления (в байтах) mov bx,[SectSiz]; BX=размер секторов (в байтах)

add ax,bx

; AX +=

 

dec ax

;

(Sectors Size - 1)

div bx

; разделить на размер сектора

; теперь в AX - сколько секторов в Root с учетом возм. неполных

169

push ax ; запомнили это число в стеке

add ax,cx ; AX = число секторов от начала диска до конца Root ; это же - порядковый номер первого сектора в обл. данных диска

mov [Dat0],ax ; сохранит это важное число в области Dat0

;подготовка аргументов для чтения секторов диска, содержащих

;область корневого оглавления диска (ROOT) в область буфера

mov ax,01000h ; const = 4K (to AX)

mov es,ax ; сегментный адрес 64K от начала - в ES

; ( отсюда будет buffer для чтения с диска)

xor di,di; DI := 0 (этот регистр OFFSET в буфере для чтения)

pop cx

; CX=сколько секторов занимает Root

pop ax

; AX=порядковый номер первого сектора в ROOT

;AX=порядковый номер первого сектора в области ROOT диска

;(далее в этом регистре будет порядковый номер следующего

;сектора области ROOT, CX=сколько секторов занимает Root

;вызов процедуры чтения секторов диска, содержащих область

;корневого оглавления диска в область буфера для оглавления call ReadFlopp

;начало действий последовательного просмотра записей оглавления

;в этом буфере на предмет нахождения записи для фиксированного

;имени файла

xor bx,bx ; BX := 0

mov cx,[RootSiz]; CX=число записей Root оглавления povt: mov di,bx

;DI устанавливается на начало последовательности

;прочитанных записей из Root

push cx ; сохранить размер Root в стеке для использ. в LOOP

mov cx,11

; CX= размер поля имени в записи оглавления

mov si, fname

; SI установить на начале поля фиксированного

 

; имени файла

repe cmpsb

; сравнивать, отвечает ли текущая запись

 

; оглавления требуемому файлу

pop cx

 

; восстановить размер Root в регистр CX

je short met1

; по рез. равенства сравнения - переход на met1

add bx, byte 32; увеличить BX, чтобы он показывал на ; следующую записть оглавления

loop povt

; повторять для перебора всех записей в Root (по их числу в CX) met1: jcxz nextc ; если прошли все записи, значит не найдено

; сюда попадаем, только если имя требуемого файла обнаружено в записи

170

Соседние файлы в предмете Операционные системы