При работе процессора i386+ в защищенном режиме используется два метода организации памяти:
- сегментная,
- страничная.
Операционная система Windows NT, о которой будет идти речь в дальнейшем, использует оба этих механизма.
В защищенном режиме рассматриваемого семейства процессоров может быть определено до 8192 (213) сегментов, причем, каждый из них может иметь размер до 4 Гбайт (232 байтов). К сожалению, семейство операционных систем Windows минимальным образом использует возможности сегментного режима: это семейство использует плоскую 32-разрядную модель памяти с размером линейного адресного пространства 4 Гбайта.
Процесс формирования линейного адреса в защищенном режиме показан на рисунке 1.1. Формат селектора показан на рисунке 1.2.
Каждый сегмент в процессоре описывается 8-байтной структурой данных – дескриптором сегмента. Дескриптор определяет положение элемента в памяти, размер занимаемой им области (лимит), его назначение и характеристики защиты. Для указания конкретного сегмента внутри таблицы дескрипторов используется 16-разрядный селектор. Он определяет номер записи внутри таблицы дескрипторов. Младшие 2 бита селектора определяют уровень привилегий режима, который может воспользоваться данным дескриптором. Третий бит определяет тип таблицы дескрипторов – глобальная или локальная. Остальные 13 разрядов селектора и определяют максимально возможное количество сегментов, к которым можно обратиться с помощью селектора.
Рисунок 1.1 – Формирование линейного адреса в защищенном режиме
Рисунок 1.2 – Формат селектора сегмента
Дескрипторы расположены в специальной таблице дескрипторов GDT – Global Descriptor Table, расположенной в памяти. Механизм работы с глобальной и локальной таблицами дескрипторов показан на рисунке 1.3.
Рисунок 1.3 – Схема работы с глобальной и локальной таблицами дескрипторов
В Windows NT определено 11 селекторов, из которых наиболее важными являются первые четыре:
Таблица 1.1 – Первые 4 сегмента Windows NT
Селектор Hex (bin) | Назначение | База | Предел | DPL | Тип |
08 (001000) | Code32 | FFFFFFFF | RE | ||
10 (010000) | Data32 | FFFFFFFF | RW | ||
1b (011011) | Code32 | FFFFFFFF | RE | ||
23 (100011) | Data32 | FFFFFFFF | RW |
Каждый из этих четырех селекторов позволяет адресовать все 4 Гбайта линейного адресного пространства, причем трансляция производится в одни и те же физические адреса.
Первые два селектора имеют требуемый уровень привилегий DPL = 0 (наивысший приоритет, DPL – Descriptor Privilege Level, уровень привилегий дескриптора) и используются драйверами и системными компонентами для доступа к системному коду, данным и стеку. (Здесь следует отметить, что из четырех возможных уровней привилегий 0 – 4, предоставляемых данным типом процессоров, в операционных системах Windows используются лишь два уровня – 0 и 3.)
Вторые два селектора используются кодом пользовательского режима для доступа к коду, данным и стеку пользовательского же режима. Эти селекторы являются константами для Windows NT.
Преобразование пары селектор:смещение для указанных селекторов дает 32-битный линейный адрес, совпадающий со значением смещения виртуального адреса. То есть, в данном случае виртуальный и линейный адреса совпадают.
Наличие в дескрипторе поля, определяющего возможность чтения/записи/исполнения кода, может навести на мысль о том, что именно на этом уровне может быть выполнена защита памяти от неверного использования. Например, код прикладной программы в пользовательском режиме находится в сегменте с селектором 1b. Для этого селектора разрешены только операции чтения и исполнения. То есть, используя селектор 1b, программа не сможет модифицировать свой собственный код. Однако, та же программа, обращаясь к данным или стеку (селектор 23) найдет свой код по тому же смещению, что и в сегменте 1b (так как для всех указанных сегментов трансляция производится в одни и те же физические адреса), но режим доступа в данном случае позволяет выполнять операции чтения и записи. Таким образом, базовый способ использования сегментации в операционных системах семейства Windows не обеспечивает надлежащей защиты кода.
Рисунок 1.4 – Схема преобразования виртуального адреса в физический
Схема формирования линейного адреса и преобразования его в физический показана на рисунке 1.4.
После формирования линейного адреса задействуется механизм страничной организации памяти. Здесь следует отметить следующее:
- каждый контекст памяти (адресное пространство процесса) представлено собственной таблицей трансляции линейного адреса в физический (адрес соответствующей таблицы загружается в управляющий регистр CR3),
- каждый элемент таблицы страниц содержит бит, указывающий на возможность доступа к странице из пользовательского режима (все страницы доступны из режима ядра),
- каждый элемент таблицы страниц содержит бит, указывающий на возможность записи в соответствующую страницу памяти.
Не надо забывать, что каждый элемент таблицы страниц содержит также бит, указывающий, представлена ли данная страница в оперативной памяти.
Два бита разрешения доступа и записи формируют следующий набор правил работы со страницами:
- страница всегда может быть прочитана из режима ядра,
- на страницу может быть произведена запись из режима ядра, только если установлен бит разрешения записи,
- страница может быть прочитана из пользовательского режима, только если установлен бит доступа к странице из пользовательского режима,
- на страницу может быть произведена запись из режима пользователя, только если установлены оба бита (разрешения записи и доступ из пользовательского режима),
- если страница может быть прочитана, она может быть исполнена.
По умолчанию страницы памяти с исполняемым кодом не имеют разрешения на запись. Поэтому при попытке использования селектора данных для модификации кода будет сгенерировано исключение.
Для того, чтобы ядро операционной системы (Windows NT) – компоненты самой операционной системы и драйверы – всегда располагались по фиксированным виртуальным адресам, независимо от текущего контекста памяти, в NT осуществляется одинаковая трансляция для верхних двух гигабайт диапазона виртуальных адресов.
Для защиты кода операционной системы соответствующие элементы таблицы трансляции виртуальных адресов в физические помечены как недоступные из пользовательского режима. |
Соответственно, диапазон виртуальных адресов 2 – 4 Гб называют системным адресным пространством (system address space), а диапазон 0 – 2 Гб – пользовательским адресным пространством (user address space).
Здесь следует избегать путаницы между терминами адресное пространство процесса – контекст памяти процесса – и пользовательское адресное пространство. Пользовательское адресное пространство – это виртуальное адресное пространство, в котором в принципе может находиться любой пользовательский процесс.