ОС и сборка: что мешает пользовательскому режиму устанавливать селектор в произвольное значение? - PullRequest
0 голосов
/ 05 ноября 2019

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

mov eax, 0x10 
mov es, ax   #point selector to the item 2 in GDT with RPL 0, which is the data segment
les bx, [0]

Так что мне интересно, какой механизм препятствует успешному выполнению этого кода?

Ответы [ 2 ]

4 голосов
/ 05 ноября 2019

Инструкция mov es, ax вызовет ошибку общей защиты (#GP), поскольку текущий уровень привилегий (CPL) больше, чем уровень привилегий дескриптора (DPL), или запрошенный уровень привилегий (RPL) будет игнорироваться, посколькучисленно не выше, чем DPL. В вашем примере, поскольку он работает в пользовательском режиме, CPL равен 3. Это означает, что DPL дескриптора также должен быть равен 3, иначе инструкция сработает. Если DPL равен 3, то сбоя не будет, но RPL фактически игнорируется, поскольку он не может быть выше, чем DPL.

(Обратите внимание, что проверки уровня привилегий сегмента выполняются только тогда, когдарегистр сегмента загружен, поэтому из-за них может произойти сбой только команды mov es, ax. *

В документации по инструкции MOV в Руководстве разработчика программного обеспечения Intel объясняется, когда это приведет к ошибке #GP при загрузкерегистр сегмента:

IF DS, ES, FS, or GS is loaded with non-NULL selector
  THEN
      IF segment selector index is outside descriptor table limits
      or segment is not a data or readable code segment
      or ((segment is a data or nonconforming code segment)
      or ((RPL > DPL) and (CPL > DPL))
          THEN #GP(selector); FI;
  IF segment not marked present
      THEN #NP(selector);
  ELSE
      SegmentRegister ← segment selector;
      SegmentRegister ← segment descriptor; FI;
  FI;

Поведение самого высокого из используемых DPL и RPL задокументировано в Intel SDM Volume 3, «5.5 PRIVILEGE LEVELS»:

  • Запрошенный уровень привилегий (RPL) - [...] Даже если программа или задача, запрашивающая доступ к сегменту, имеет достаточные привилегии для доступа к сегменту, доступ запрещается, если RPL не имеет достаточного уровня привилегий. ,То есть, если RPL селектора сегмента численно больше, чем CPL, RPL переопределяет CPL, и наоборот. [...]

Поле RPL селектора позволяет повысить эффективный уровень привилегий только до численно более высокого или менее привилегированного уровня, чем DPL. Это не имеет никакого эффекта, если вы установите его на численно более низкий уровень.

Другими словами, если селектор 0x10 ссылается на сегмент данных режима ядра (DPL = 0), то ваш код будет аварийно завершаться. Если селектор 0x10 является сегментом данных пользовательского режима (DPL = 3), он обрабатывается так же, как если бы вы использовали вместо него 0x13 (RPL = 3).


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

2 голосов
/ 05 ноября 2019

В защищенном и 64-битном режиме mov Sreg, reg не работает с #GP(selector), если:

  • Если индекс селектора сегмента выходит за пределы таблицы дескрипторов.
  • ...
  • Если загружается регистр DS, ES, FS или GS и указанный сегмент является сегментом данных или несоответствующего кода, и либо RPL, либоCPL больше, чем DPL.

Но ОС контролирует содержимое GDT, а записи GDT имеют поле уровня дескриптора-привилегий, которое требуется даже для его загрузки всегмент рег. (https://wiki.osdev.org/Global_Descriptor_Table). ОС может сделать некоторые записи GDT непригодными для пользовательского пространства.

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

Если GDT находится в памяти, на которой пользовательское пространство не имеет привилегий записи, ОС может поддерживать контроль (то же самое для LDT, конечно). Некоторые ОСНапример, в Linux есть системный вызов modify_ldt, который привилегированное пользовательское пространство может использовать для запроса ОС на установку записей LDT.


Поскольку большинство ОС используют модель плоской памяти (base = 0 limit =-1) и выполнять защиту памяти с помощью подкачки, нет необходимости останавливать пользовательское пространство при настройке сегмента данных так, как они хотят (seg: отключение к линейному происходит до того, как virt-> phys. Т.е. линейные адреса являются виртуальными, если подкачка включена. )

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


Также обратите внимание, что ваша последовательность не использует недавно модифицированный регистр ES, вместо этого он овпишет это. Присмотритесь к https://www.felixcloutier.com/x86/lds:les:lfs:lgs:lss

les bx, [0]     # Load a seg:off from memory at DS:0 into ES:BX

Возможно, вы хотели mov bx, [es:0]

...