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

Dos7book

.pdf
Скачиваний:
76
Добавлен:
09.02.2015
Размер:
5.1 Mб
Скачать

Глава 9: Примеры композиции исполняемых файлов

Commander. Последней операцией файла AUTOEXEC.BAT является безусловный переход на конечную метку END.

Представленный вариант файла AUTOEXEC.BAT надо поместить в корневой каталог сменного загрузочного носителя. В других каталогах того же носителя должны быть размещены все те файлы, которые приходится вызывать из строк файла AUTOEXEC.BAT. Предполагается, что в корневом каталоге находится командный интерпретатор Command.com, в каталоге \DOS\MS7 – файлы Attrib.exe, Find.exe и Label.exe, в каталоге \DOS\OTH – файлы Reassign.com и Blue.com, в каталоге \DOS\DRV – файл Tdsk.exe (версии 2.42), в каталоге \DOS\VC4 – файлы

Vc.com и Vc.ovl. Разумеется, в тех же каталогах допустимо наличие других файлов.

Если Вы намерены использовать иное расположение файлов или иную структуру каталогов, то в тексте файла AUTOEXEC.BAT все отличающиеся пути и ссылки должны быть соответствующим образом скорректированы.

9.10Эксперименты с линейной адресацией

Широко распространено мнение, что современные 32-разрядные процессоры при работе в реальном режиме не позволяют обращаться к памяти сверх 1088 кбайт. Хотя официальные справочные данные это мнение не опровергают, тем не менее оно неверно. С начала 1990-х годов известны сообщения об использовании недокументированных свойств 32-разрядных процессоров для доступа к расширенной памяти. Автором идеи считают Томаса Родена.

Анализ кода популярного драйвера Himem.sys (5.04-01) показывает, что там имеются все элементы, необходимые для осуществления обмена данными с расширенной памятью в реальном режиме. Вероятно, драйвер Himem.sys именно так и действует, но официальных подтверждений того нет. Несмотря на солидный возраст вопроса, идея линейной 32-разрядной адресации в реальном режиме осталась на уровне малоизвестных частных предложений и примеров.

Сейчас проблема состоит не в раскрытии сокровенной истины, а в демонстрации конкретных путей осуществления тех возможностей, которые уже давно предоставлены программам реального режима. С этой целью в разделе 9.10 приведены тексты двух небольших программ: GS_limit.com снимает сегментную защиту для регистра GS, а GS_dump.com выводит дамп участка памяти по заданному 32-разрядному линейному адресу. Действие программ наглядно иллюстрирует рис. 7: та область памяти, которая только что была закрыта сегментной защитой, становится доступной после вызова программы GS_limit.com.

– 549 –

Глава 9: Примеры композиции исполняемых файлов

Рис. 7

Тот, кто решит воспользоваться предлагаемыми программами, получит уникальный шанс увидеть все "закоулки" и "завороты" 32-разрядного адресного пространства.

9.10-01 Программа включения/отключения сегментной защиты.

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

все надежды воспользоваться возможностями префикса разрядности адреса (7.02-07) в реальном режиме. Но ситуация не настолько безнадежна, как сначала кажется. Все современные процессоры, начиная с модели 80386, позволяют изменять размер сегмента, и его можно задать равным верхнему пределу адресации. Тогда любой вычисленный линейный адрес будет сочтен допустимым, и сегментная защита фактически будет отключена.

Отключение сегментной защиты хорошо это или плохо? С одной стороны хаос и крах субординации в многозадачных операционных системах. С другой стороны новые перспективы для сервисных и диагностических программ, действующих в однозадачной операционной среде. Как любой эффективный инструмент, отключение сегментной защиты может быть и очень полезным, и очень опасным. Именно потому разработчики процессоров подошли к вопросам управления сегментной защитой с большой осторожностью. Они оставили такую возможность только для программ защищенного режима, действующих на высшем (нулевом) уровне привилегий.

– 550 –

Глава 9: Примеры композиции исполняемых файлов

Когда в защищенном режиме на высшем уровне привилегий уже действует ядро операционной системы, тогда у всех других программ просто нет шансов изменить ситуацию. Но пока компьютер работает в реальном режиме, шансы есть: нужно лишь "нырнуть" в защищенный режим, изменить там предел сегмента в "теневом" регистре процессора, а потом "вынырнуть" обратно. После этого в реальном режиме можно будет пользоваться линейной 32-разрядной адресацией относительно соответствующего сегментного регистра без риска "напороться" на срабатывание сегментной защиты. Такова, вкратце, основная мысль, реализуемая предлагаемой ниже программой.

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

Вследствие выбора сегментного регистра GS предлагаемая программа получила название GS_limit.com. Она отличается от аналогов, во-первых, тем, что способна выполнять свою миссию не только в "голой" DOS, но и после загрузки драйвера Himem.sys. Во-вторых, программа GS_limit.com способна не только снимать защиту сегмента GS, но и восстанавливать ее обратно. Для устранения предела сегмента GS надо ввести команду

GS_limit off

Восстановление стандартного 64-килобайтного предела сегмента GS

производится командой

GS_limit on

Если ни один из двух упомянутых параметров (OFF или ON) не указан, то программа выведет справку помощи (help).

Чтобы скомпилировать исполняемый файл программы GS_limit.com, нужно

сначала набрать с помощью программы редактирования приведенный ниже ассемблерный текст и сохранить его в отдельном файле, например, под именем GS_limit.scr. Словесные комментарии в ассемблируемых строках при наборе текста можно опустить. Обратите внимание на пустую строку в ассемблерном тексте восьмую с конца. Она служит командой выхода из режима ассемблирования (7.01-04) и потому в файле GS_limit.scr обязательно должна быть сохранена. Потом файл GS_limit.scr надо переслать на исполнение отладчику Debug.exe через перенаправление ввода, например, так:

Debug.exe < GS_limit.scr

– 551 –

Глава 9: Примеры композиции исполняемых файлов

В результате исполнения содержащихся в ассемблерном тексте команд отладчик создаст в текущем каталоге исполняемый файл GS_limit.com длиной 662 байта. Для этого файл GS_limit.scr должен содержать следующие строки:

a 100

;************ Программа GS_limit.com *************

;********** Секция 1: проверка памяти и параметров ; 110 - адрес перехода со строки 104

cmp

SP,2010

; 100

Выделено

менее 8 кбайт?

jbe

0110

;*104

Åñëè äà,

оставим как есть

mov

SP,1FFE

; 106

Вершину стека - на 8 кбайт

mov

BX,0200

; 109

Запросим

8 кбайт памяти

mov

AH,4A

; 10C

Вызов функции создания

int

21

; 10E

 

свободного

MCB

cmp byte ptr

[005E],46

;=110

Имеется ли параметр

"F" ?

jz

0148

;*115

Åñëè äà,

то переход

вперед

cmp byte ptr

[005E],4E

; 117

Имеется ли параметр

"N" ?

jz

0148

;*11C

Åñëè äà,

то переход

вперед

mov

DX,0317

;*11E

Если параметров нет, то

mov

AL,01

; 121

переход на индикацию

jmp

020A

;*123

сообщения 0317 и на выход

;********** Секция 2: данные, указатели, дескрипторы

;126 - заполнен от 17D, вызван от 187, 1F7

;128 - заполнен от 181, обращения от 190, 1E5

;12A - псевдодескриптор GDT, обращение от 1CA

;12C - линейный адрес GDT, получен от 1B2

;126 Место адреса вызова Himem

dw 0000,0000

; 12A Размер GDT (3 дескриптора)

db

18

00

 

 

 

 

 

 

 

 

 

 

 

; 12C Адрес GDT (смещение = 130)

db

30

01

00

00

 

 

 

 

 

 

 

 

 

; 130 "Пустой" дескриптор 0000

db

00

00

00

00

00

00

00

00

 

 

 

 

 

; 138 4-Гбайтный дескриптор 0008

db

FF FF 00 00

00

93

8F

00

 

 

 

 

 

; 140 64-кбайтный дескриптор 0010

db

FF FF 00

00

00

93

00

00

 

;******** Секция 3: проверки процессора и защиты

;148 - адрес перехода со строк 115, 11C

;162 - адрес перехода со строки 158

pushf

 

;=148 Загрузим

â

стек 2 копии

pushf

 

;

149

исходного

состояния флагов

pop

CX

;

14A

Выгрузим

копию в CX

– 552 –

 

Глава 9:

Примеры композиции исполняемых файлов

xor

CH,70

; 14B

Инвертируем

áèòû 0C, 0D, 0E

push

CX

; 14E

Перешлем измененные биты

popf

 

; 14F

через

стек в регистр

pushf

 

; 150

флагов, а потом снова

pop

AX

; 151

через

ñòåê â AX

popf

 

; 152

Вернем исходные состояния

xor

AH,CH

; 153

Установим несовпавшие биты

test

AH,40

; 155

Наш процессор 16-битовый?

jz

0162

;*158

Если нет, проверим защиту

mov

AL,04

; 15A

Åñëè äà, òî

переход на

mov

DX,024F

;*15C

выход

с индикацией

jmp

020A

;*15F

сообщения 024F

test

AH,30

;=162

Установлен защищенный режим?

jz

016F

;*165

Если нет, следуем дальше

mov

AL,08

; 167

Åñëè äà, òî

переход на

mov

DX,0271

;*169

выход

с индикацией

jmp

020A

;*16C

сообщения 0271

 

;********* Секция 4: подготовка адресной линии A20

;16F - адрес перехода со строки 165

;18B - адрес перехода со строки 176

mov

AX,4300

;=16F

Проверим, установлен ли

int

2F

; 172

драйвер Himem.sys

cmp

AL,80

; 174

Если нет, перейдем и

jnz

018B

;*176

обратимся к A20 напрямую

mov

AX,4310

; 178

Если да, запросим адрес

int

2F

; 17B

вызова функций Himem.sys

mov

[0126],BX

;*17D

Сохраним в памяти

mov

[0128],ES

;*181

полученный адрес вызова

mov

AH,05

; 185

Вызовем функцию активизации

call far

[0126]

;*187

адресной линии A20

call

021C

;=18B

Проверим A20 и перейдем,

jnz

01A7

;*18E

если линия A20 активна

mov word ptr

[0128],0001

;*190

Метка: линия A20 не активна

mov

AL,FF

; 196

Запросим активизацию линии

call

022F

;*198

A20 у подпрограммы 022F

call

021C

;*19B

Проверим A20 и перейдем,

jnz

01A7

;*19E

если линия A20 активна

mov

AL,02

; 1A0

Если линия A20 не активна,

mov

DX,029C

;*1A2

то переход на выход с

jmp

020A

;*1A5

индикацией сообщения 029C

;*********** Секция 5: подготовка таблицы GDT

; 1A7 - адрес перехода со строк 18E, 19E

 

 

;=1A7

Префикс 32-битового операнда

db

66

 

 

 

 

 

– 553 –

Глава 9:

Примеры композиции исполняемых файлов

xor

AX,AX

 

; 1A8

Обнулим регистр EAX

mov

AX,CS

 

; 1AA

Запишем CS в AX

mov

CL,04

 

; 1AC

Сдвиг на 4 бита превратит

db

66

 

 

 

shl

AX,CL

 

; 1AF

сегментный адрес в линейный

db

66

 

 

 

add

[012C],AX

;*1B2

Вычислим линейный адрес GDT

;******** Секция 6: подготовка селектора и прерываний

; 1C3 - адрес перехода со строки 1BE

mov

CX,0008

; 1B6

0008 - 4-Гбайтный селектор

cmp byte ptr

[005E],46

; 1B9

Указан ли параметр "F" ?

jz

01C3

 

;*1BE

Если да, оставим CX=0008

mov

CX,0010

; 1C0

Если нет, пусть CX=0010

cli

 

 

;=1C3

Запретим прерывания

mov

AL,80

 

; 1C4

Отсылка байта 80h

out

70,AL

 

; 1C6

â ïîðò 70h

in

AL,71

 

; 1C8

запретит NMI

;********** Секция 7: переходы к PM и обратно

 

 

 

; 1CA

= Lgdt fword ptr [012A]

db

0F 01

16 2A 01

 

 

 

 

; 1CF

= mov EAX,CR0

db

0F 20

C0

 

 

or

AL,01

 

; 1D2

Установим бит защищенного

 

 

 

; 1D4

режима (= mov CR0,EAX)

db

0F 22

C0

 

 

 

 

 

; 1D7

Загрузим GS (=mov GS,CX)

db

8E E9

 

 

 

and

AL,FE

 

; 1D9

Сбросим бит защищенного

 

 

 

; 1DB

режима (= mov CR0,EAX)

db

0F 22

C0

 

 

;********** Секция 8: возврат к прежнему состоянию

; 1F5 - адрес перехода со строки 1EC

mov

AL,7F

 

; 1DE

Посылка байта 7Fh

out

70,AL

 

; 1E0

â ïîðò 70h

in

AL,71

 

; 1E2

снова разрешает NMI

sti

 

 

; 1E4

Разрешим прерывания

cmp word ptr

[0128],0001

;*1E5

Посмотрим метку линии A20

jb

01FB

 

;*1EA

Если 0000, ничего не делаем

ja

01F5

 

;*1EC

Если >, обратимся к Himem

mov

AL,FD

 

; 1EE

Если =0001, восстановим

call

022F

 

;*1F0

состояние линии A20 с

jmp

01FB

 

;*1F3

помощью подпрограммы 022F

mov

AH,06

 

;=1F5

Вызов функции драйвера Himem

call far

[0126]

 

;*1F7

для перекрытия линии A20

 

 

 

 

– 554 –

Глава 9: Примеры композиции исполняемых файлов

;********** Секция 9: вывод сообщения и возврат в DOS

;1FB - адрес перехода со строк 1EA, 1F3

;208 - адрес перехода со строки 203

;20A - адрес перехода со строк 123, 15F, 16C, 1A5

mov

DX,02E9

;=1FB

Сообщение "Предел поставлен"

cmp byte ptr

[005E],46

; 1FE

Указан ли параметр "F" ?

jnz

0208

;*203

Если да, то заменить на

mov

DX,02B9

; 205

сообщение "Предел снят"

mov

AL,00

;=208

Нулевой уровень ошибки

push

AX

;=20A

Точка входа для выведения

mov

AH,40

; 20B

сообщений на экран

mov

BX,DX

; 20D

Считаем в регистр CX

mov

CX,[BX-02]

; 20F

число выводимых знаков

mov

BX,0001

; 212

0001 = ссылка на STDOUT

int

21

; 215

Вывод сообщения в STDOUT

pop

AX

; 217

Вернем уровень ошибки в AL

mov

AH,4C

; 218

Вызов функции

int

21

; 21A

завершения программы

;******** Секция 10: подпрограмма проверки линии A20

; 21C - адрес вызова из строк 18B, 19B

push

DS

;=21C

Сохраним состояние DS

xor

SI,SI

; 21D

Обнулим регистр SI

mov

DS,SI

; 21F

Теперь DS:SI=0000:0000

mov

DI,F0F1

; 221

Установим ES:DI=F0F1:F0F0

mov

ES,DI

; 224

чтобы получился адрес

dec

DI

; 226

F0F10h + F0F0h = 100000h

cld

 

; 227

Зададим счет на увеличение

mov

CX,0010

; 228

Зададим предел 16 байт и

repz

 

; 22B

повторение до несовпадения

cmpsb

 

; 22C

Адреса "свернуты" ?

pop

DS

; 22D

Возврат результата через

ret

 

; 22E

состояние флага ZF

;****** Секция 11: подпрограмма управления линией A20

; 22F - адрес вызова из строк 198, 1F0

push

AX

;=22F

Сохраним команду в стеке

call

0241

;*230

Ждем готовности контроллера

mov

AL,D1

; 233

D1 - первый байт команды,

out

64,AL

; 235

посылаемый в порт 64h

call

0241

;*237

Ждем готовности контроллера

pop

AX

; 23A

Вернем команду в AX

out

60,AL

; 23B

и пошлем ее в порт 60h

call

0241

;*23D

Подождем срабатывания

ret

 

; 240

контроллера и вернемся

;******** Секция 12: подпрограмма ожидания готовности

– 555 –

Глава 9: Примеры композиции исполняемых файлов

;241 - адрес вызова из строк 230, 237, 23D

;245 - адрес цикла из строки 249

push

CX

 

 

;=241 Сохраним CX в стеке

mov

CX,FFFF

; 242 Запишем в CX число циклов

in

AL,64

 

;=245 Считаем байт из порта 64h

test

AL,02

 

; 247 Проверим состояние 2-го бита

loopnz

0245

 

;*249 Повторим, если порт занят

pop

CX

 

 

; 24B Восстановим состояние CX

ret

 

 

 

; 24C Возврат из подпрограммы

 

;*********** Секция 13: сообщения

db

20

00

 

 

 

; 24F - 1-е сообщение, вызываемое из строки 15C

db

0D

0A

09

"16-bit processor"

db

20

"can"

27 "t suit" 0D 0A

db

29

00

 

 

 

; 271 - 2-е сообщение, вызываемое из строки 169

db

0D

0A

09

"GS_limit can" 27 "t run"

db

20

"in protected mode" 0D 0A

db

1B

00

 

 

 

; 29C - 3-е сообщение, вызываемое из строки 1A2

db

0D

0A

09

"Line A20 control error" 0D 0A

db

2E

00

 

 

 

; 2B9 - 4-е сообщение, вызываемое из строки 205

db

0D

0A

09

"GS segment limit"

db

20

"protection is turned OFF" 0D 0A

db

2C

00

 

 

 

; 2E9 - 5-е сообщение, вызываемое из строки 1FB

db

0D

0A

09

"GS segment limit"

db

20

"protection is restored" 0D 0A

db

7F

00

 

 

 

; 317 - 6-е сообщение, вызываемое из строки 11E

db

0D

0A

"GS_limit.com removes the GS segment"

db

20

"limit protection or can restore it back"

db

0D

0A

"Usage examples:" 0A 0D 09 09 "GS_lim"

db

"it off"

0A 0D 09 09 "GS_limit on" 0D 0A

 

; 396 Конец программы

n GS_limit.com rBX

0000 rCX 0296 w

q

– 556 –

Глава 9: Примеры композиции исполняемых файлов

Начинается файл в секции 1 с обычных процедур высвобождения излишней памяти (примечание 5 к A.12-7) и проверки наличия параметров. Форма представления параметров в командной строке намеренно выбрана так, чтобы они оказались автоматически вписанными в первый блок FCB (примечание 4 к A.07-1) в составе PSP, причем сразу в нормализованном виде. Если ни один из параметров не указан, то в строке 123 происходит переход в секцию завершения, где выводится справка помощи, и затем программа завершается, оставляя код ошибки 01.

Если необходимый параметр указан, то исполнение продолжается со строки 148 в секции 3, где проверяется пригодность процессора для решения поставленной задачи, а также необходимое условие: работа процессора в реальном режиме. Смысл выполняемых проверок изложен в примечаниях 2 и 3 к разделу A.11-4. Если какое-либо из упомянутых условий не выполняется, то происходит переход в секцию завершения, где выводится на экран соответствующее сообщение об ошибке, и затем программа завершается, оставляя код ошибки 04 или 08.

Когда проверка состояния процессора пройдена, исполнение продолжается со строки 16F в секции 4, где выполняется подготовка линии A20 шины адреса. Открывать линию A20 шины адреса приходится по-разному в зависимости от того, установлен ли драйвер Himem.sys или нет. Если драйвер установлен, то необходимо действовать через его функцию AH=05 (A.12-3), как показано в строке 185. Если же драйвер Himem.sys не установлен, то приходится вызывать подпрограмму 022F из секции 11, которая попытается открыть линию A20 "руками" контроллера клавиатуры (подробнее в примечании 1 к A.11-3). Исходное и получившееся состояния линии A20 проверяются в строках 18B и 19B вызовами подпрограммы 021C из секции 10. Если попытки открыть линию A20 будут неудачны, то в строке 1A5 произойдет переход в секцию завершения, на экран будет выведено сообщение об ошибке, и на том исполнение программы закончится, оставив код уровня ошибки 02. Как правило, линию A20 так или иначе удается открыть, и тогда исполнение программы продолжается со строки 1A7 в секции 5.

Команды секции 5 подготавливают псевдодескриптор таблицы GDT, из которого будет считан ее адрес в регистр GDTR. В строке 1AC сегментный адрес CS сдвигом на 4 бита влево преобразуется в линейный адрес CS. Операция суммирования в строке 1B2 формирует линейный адрес таблицы GDT в шаблоне псевдодескриптора GDT (в строках 12A – 12F секции 2).

Расшифровку структуры каждого дескриптора в таблице GDT можно посмотреть в разделе A.12-2. Второй и третий дескрипторы в таблице GDT предназначены для сегментов данных, причем второй дескриптор (строка 138, селектор 0008), задающий 4-Гигабайтный размер сегмента, служит для снятия защиты с сегмента GS. Третий дескриптор (строка 140, селектор 0010), задающий стандартный 64-килобайтный размер сегмента, служит для восстановления сегментной защиты. Перед переходом в защищенный режим в регистре CX должен

– 557 –

Глава 9: Примеры композиции исполняемых файлов

быть подготовлен селектор именно того дескриптора, который соответствует поставленной задаче. Команды в первых четырех строках секции 6 (1B6 – 1C0) выбирают тот или другой селектор – 0008 или 0010. Команды в последних строках секции 6 непосредственно предшествуют переходу в защищенный режим и служат для запрета прерываний, включая NMI (подробнее в примечании 1 к 8.01-03).

Секция 7 начинается с команды LGDT (= Load Global Descriptor Table),

загружающей псевдодескриптор таблицы GDT в специальный (предназначенный только для него) регистр GDTR. Отладчик Debug.exe о существовании команды LGDT, конечно, не догадывается, и потому ее код (0F 01 16) введен как данные инструкцией DB. Последние 2 байта в команде LGDT – 2A 01 – это смещение псевдодескриптора относительно сегментного адреса в регистре DS, то есть просто номер строки 012A, с которой начинается шаблон псевдодескриптора в секции 2.

Собственно переход в защищенный режим можно было бы выполнить вызовом INT 15\AH=89h (8.01-78), но тогда пришлось бы готовить более громоздкую таблицу GDT, а потом, помимо прочего, перепрограммировать оба контроллера прерываний обратно для работы с таблицей прерываний реального режима. Чтобы избежать лишних сложностей, здесь переход в защищенный режим выполнен установлением в единицу младшего бита PE (= Protection Enable) в управляющем регистре CR0 (A.11-4). Для этого в строке 1CF содержимое регистра CR0 считывается, в нем устанавливается бит PE, а потом в строке 1D4 оно записывается обратно в регистр CR0. Команды считывания и записи в регистр CR0, описанные в примечании 1 к разделу 7.03-58, отладчику Debug.exe неизвестны, и потому в строках 1CF и 1D4 они введены как данные инструкцией DB.

Конечно, для полноценной работы в защищенном режиме одного установления бита PE недостаточно, но в данном случае большего не требуется. В защищенном режиме программа GS_limit.com должна выполнить всего одну операцию: записать в регистр GS тот селектор (0008 или 0010), который уже подготовлен в регистре CX. Код команды копирования содержимого регистра CX в регистр GS вводится в

строке 1D7 инструкцией DB, потому что отладчик Debug.exe "не знает" команд обращения к регистру GS (они показаны в примечании 2 к разделу 7.03-58). Запись селектора в регистр GS автоматически повлечет за собой то, ради чего написана программа GS_limit.com: занесение в "теневой" регистр процессора данных о размере сегмента GS из того дескриптора таблицы GDT, на который укажет подготовленный селектор.

Когда вершина достигнута, пора побеспокоиться о том, чтобы аккуратно с нее спуститься. Прежде всего надо сбросить бит PE в том коде, который был считан из управляющего регистра CR0 и с тех пор хранится в регистре EAX. Команда в строке 1DB запишет этот код обратно в регистр CR0 и осуществит тем самым возврат процессора в реальный режим. Потом команды в секции 8 снимут запрет прерываний и восстановят исходное состояние линии A20 адресной шины, причем

– 558 –

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]