Кольца демо-процессора - ассемблерный код, который запускает инструкции ring 0 - PullRequest
1 голос
/ 25 июня 2019

Я хочу создать демонстрацию, чтобы объяснить процессорам звонки и системные вызовы студентам. Я думал в презентации делать что-то вроде этого:

  1. Напишите некоторый код сборки, который пытается выполнить некоторый код, который может выполняться только в кольце 0 (например, получить доступ к диску напрямую)
  2. увидеть код сбоя и переписать его для использования системных вызовов (например, чтение файла с помощью системных вызовов)
  3. если это не так сложно: запустите исходный код в кольце 0

Я могу использовать Linux или Windows, что угодно

Любые идеи о том, где я могу найти код, который поможет мне? какие защищенные инструкции можно использовать?

Спасибо!

1 Ответ

5 голосов
/ 26 июня 2019

Одной привлекающей внимание демонстрацией может быть выключение компьютера.

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

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


ACPI определяет четыре глобальных состояния системы, G0 - G3 , где G3 - механическое отключение (т. Е. Вилка была извлечена, батарея извлечена), а G2 - мягкое выключение .
Программное обеспечение может ввести только G2 , и это можно сделать, войдя в режим сна S5 .

Состояние сна контролируется PCH (набор микросхем Intel) через регистр ввода-вывода (PM1a_CNT_BLK), этот регистр находится в блоке ACPI, определенном в пространстве конфигурации PCI функции 31 устройства 2 (контроллер PM).
Нужно прочитать базовый адрес блока и затем добавить четыре (4), чтобы получить адрес интересующего регистра.
Я не буду делать это программно, вместо этого программа сборкиожидая символ с этим адресом.

Для получения адреса регистра PM1a_CNT_BLK можно использовать /proc/ioports следующим образом:

sudo cat /proc/ioports | grep 'PM1a_CNT_BLK' | cut -f3 -d' ' | cut -f1 -d'-'

Это дает шестнадцатеричный адрес регистра.Если ничего не распечатано, вероятно, что чипсет не поддерживается.

В любом случае адрес может быть 1804.


Состояние сна контролируется битами 10:12 (SLP_TYP) регистра и битом 13 (* 1053).*).
SLP_TYP - это 3-битное значение для выбора состояния для ввода ( S5 равно 7), а бит 13 - это бит разрешения.
Регистр имеет другое значение, которое должнобыть сохранено , поэтому необходимо выполнить операцию чтения-изменения-записи.

Использование инструкций in и out невозможно для кольца 3, если TSS процесса не имеет IOPL (уровень привилегий IO) 3 (или порты включены в карте портов IO).
IOPL сообщает, какие кольца могут использовать in и out, значение X означает, что все кольца могут X или меньше.

Эта программа пытается выключить компьютер и при желании установить IOPL на заданное значение (через символ IOPL):

BITS 64

GLOBAL _start

SECTION .text

_start:
 ;Set the IOPL, only if greater than 0 (since 0 is the default)
%if IOPL > 0
 lea rsi, [rsp-80h]             ;We don't care about the pt_regs struct and we use the RED ZONE
 mov edi, IOPL                  ;IOPL to set
 mov eax, 172                  
 syscall                        ;Set iopl

 and eax, 0fh                   ;Just keep the last nibble, it can be 0 (success), 10 (invalid IOPL) or 15 (insufficient OS permissions)
 test eax, eax                  ;Test for errors
 mov edi, eax                   ;We exit with status 10 or 15 if the iopl syscall failed
 jnz .exit
%endif 

 ;Power off the PC
 mov dx, PM1a_CNT_BLK
 in eax, dx                     ;Read the current value
 and eax, 0ffffc003h            ;Clear SLP_TYP and SLP_EN
 or eax, (7 << 10) | (1 << 13)  ;Set SLP_TYP to 7 and SLP_EN to 1
 out dx, eax                    ;Power off

 ;This is just for safety, execution should STOP BEFORE arriving here. This exits the process with status
 ;0
 xor edi, edi

 ;Exit the process with a numerical status as specified in RDI
.exit:
 mov eax, 60
 syscall

Может быть собран с nasm po.asm -DPM1a_CNT_BLK=$1 -DIOPL=$2 -felf64 -o po.o, где$1 - это адрес порта PM1a_CNT_BLK, как указано выше , но с префиксом 0x (в моем случае он становится 0x1804), а $2 - это число (0-3) для установки IOPLк.
IOPL устанавливается, если он не равен 0, поскольку 0 является значением по умолчанию (т. е. только кольцо 0 может использовать in и out)

Примечание :Передайте разумные значения символам, иначе программа не будет собрана.

Это интересно следующими способами:

  1. При запуске с IOPL, равным 0, программа аварийно завершает работу с #GP из-за использования in.
    Это демонстрирует механизм безопасности процессора.
  2. Если запустить с IOPL> 0, но не с правами root, произойдет сбой из-за недостаточных привилегий.
    Это демонстрирует механизм безопасности ОС, позволяющий только IOPL изменять только root.
  3. Если запустить с IOPL> 0, но <3 и в качестве пользователя root, он будет #GP из-за использования <code>in.
    Это показывает, что пользовательские программы выполняются на кольце 3 (IOPL недостаточно высок).
  4. При запуске с IOPL = 3 и в качестве пользователя root он выключит компьютер (или завершится с ошибкой и вернется, возможно, оставив систему в неизвестном состоянии).
    Это демонстрирует риск предоставления пользовательским программам доступа к оборудованию.,
  5. Если IOPL> 3, произойдет сбой из-за недопустимого значения IOPL.
    Это показывает, что есть только четыре кольца.

Я сделал репозиторий git с кодом и скриптом built.sh, который вы можете использовать для сборки и запуска другой версии программы.
Этот скрипт полезен , поскольку он преобразует состояние выхода po в удобную для пользователя строку , подходящую для экспериментов.

Скрипт ожидает адрес PM1a_CNT_BLK в качестве первого аргумента (с префиксом 0x) и IOPL в качестве второго.
Используйте это как:

./build.sh 0x1804 0
./build.sh 0x1804 3
sudo ./build.sh 0x1804 2
sudo ./build.sh 0x1804 4
sudo ./build.sh 0x1804 3

Конечно, измените адрес регистра.


Трюк с IOPL - это просто ... трюк. Это не делает программу работающей на Ring 0, она полезна для отладки, но не намного.

Чтобы запустить код в Ring 0, вам нужен LKM (загружаемый модуль ядра).
В том же репозитории я включил lkm dir с примером LKM.
При загрузке модуля попытка выключить компьютер (мгновенно).

Код минимальный:

#include <linux/module.h>   /* Needed by all modules */
#include <linux/kernel.h>   /* Needed for KERN_INFO */
#include <linux/fs.h>         /* Needed for KERN_INFO */
#include <asm/io.h>       /* Needed for inl and outl */

#define PM1a_CNT_BLK 0x1804

unsigned char bytes[10];

int __init lkm_init(void)
{
  unsigned int pm1a;

    printk(KERN_INFO "I'm going to power the computer off");


  pm1a = inl(PM1a_CNT_BLK);
  pm1a = ( pm1a & 0xffffc003 ) | ( 7 << 10 ) | ( 1 << 13 );
  outl(pm1a, PM1a_CNT_BLK);

  printk(KERN_WARNING "Powering off failed");

    return 0;
}


static void __exit lkm_exit(void)
{
}

module_init(lkm_init);
module_exit(lkm_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("M.Bloom");
MODULE_DESCRIPTION("Attempt to power down the computer");

Чтобы сделать LKM, сначала отредактируйте PM1a_CNT_BLK define , затем запустите make в том же каталоге (вам понадобятся заголовки ядра), Makefile является стандартным для *. загружаемые модули 1147 * Для загрузки модуля используйте insmod po в качестве пользователя root (это механизм безопасности ОС).

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

Вы можете использовать LKM в качестве каркаса для выполнения кода Ring 0, хотя при работе с памятью вы должны знать, как Linux обрабатывает виртуальную память.


И последнее замечание: если вы собираетесь проверить / использовать эти программы, обязательно закройте все приложения, запустите sync и, если хотите, переключитесь на запуск уровня 1 (для systemd это systemctl isolate rescue) или по крайней мере, остановить все критические службы.

...