- PVSM.RU - https://www.pvsm.ru -
В августе 2016 года, без каких-либо официальных объявлений со стороны Google, были обнаружены исходники новой операционной системы Fuchsia. Эта ОС основана на микроядре под названием Zircon, которое в свою очередь основано на LK (Little Kernel) [1].
Fuchsia is not Linux
Я не настоящий сварщик являюсь разработчиком и/или экспертом Zircon. Тест под катом является компиляцией частичных переводов: официальной документации Zircon vDSO [2] и статьи Admiring the Zircon Part 1: Understanding Minimal Process Creation [3] от @depletionmode [4], куда было добавлено немного отсебятины (которая убрана под спойлеры). Поэтому конструктивные предложения по улучшению статьи, как и всегда, приветствуются.
vDSO в Zircon является единственным средством доступа к системным вызовам (syscalls) [5].
А разве нельзя из нашего кода напрямую вызвать инструкции процессора SYSENTER/SYSCALL? Нет, эти инструкции процессора не являются частью системного ABI. Пользовательскому коду запрещено напрямую выполнять такие инструкции.
Желающих узнать больше деталей о таком архитектурном шаге приглашаю под кат.
Аббревиатура vDSO расшифровывается virtual Dynamic Shared Object:
Поддержка vDSO в качестве единственного контролируемого ABI для приложений пользовательского режима реализуется двумя способами:
ZX_VM_PERM_EXECUTE
), ядро требует, что бы смещение и размер строго совпадали с исполняемым сегментом vDSO. Это (в том числе) гарантирует только одно проецирование vDSO в память процесса. После первого успешного проецирования vDSO в процесс его уже нельзя удалить. А попытка повторного проецирования vDSO в память процесса, попытки удаления спроецированного VMO для vDSO или проецирование с неправильными смещением и/или размером завершаются с ошибкой ZX_ERR_ACCESS_DENIED
.Для запуска исполнения первой нити (thread) новосозданного процесса используется системный вызов zx_process_start [8]. Последним параметром этого системного вызова (смотри arg2 в документации) передается аргумент для первой нити создаваемого процесса. По принятому соглашению загрузчик программ отображает vDSO в адресное пространство нового процесса (в случайное место, выбранное системой) и передает базовый адрес отображения аргументом arg2 в первую нить (thread) создаваемого процесса. Этот адрес является адресом заголовка ELF-файла, по которому могут быть найдены необходимые именованные функции для совершения системных вызовов.
vDSO это обычная разделяемая библиотека EFL, которая может быть рассмотрена, как любая другая. Но для vDSO намеренно выбрано небольшое подмножество из всего формата ELF. Это дает несколько преимуществ:
Вся память vDSO представлена двумя последовательными сегментами, каждый из которых содержит выровненные целые страницы:
Весь образ vDSO состоит только из страниц этих двух сегментов. Для отображения памяти vDSO требуются только два значения, извлеченные из заголовков ELF: количество страниц в каждом сегменте.
Некоторые системные вызовы просто возвращают значения, которые являются постоянными (значения должны запрашиваться во время выполнения и не могут быть скомпилированы в код пользовательского режима). Эти значения либо фиксируются в ядре во время компиляции, либо определяются ядром во время начальной загрузки (загрузочные параметры и параметры аппаратного обеспечения). Например: zx_system_get_version() [9], zx_system_get_num_cpus() [10] и zx_ticks_per_second() [11]. На возвращаемое значение последней функции, например, влияет параметр командной строки ядра [12].
Интересно, что и в описании функции zx_system_get_num_cpus() [10] так же явно указано, что ОС не поддерживает горячее изменение количества процессоров:
This number cannot change during a run of the system, only at boot time.
Это, как минимум, косвенно указывает на то, что ОС не позиционируется, как серверная.
Поскольку эти значения постоянны, то и нет смысла платить за реальные системные вызовы в ядро ОС. Вместо этого их реализация — простые функции C++, которые возвращают данные, считанные из сегмента констант vDSO. Значения, зафиксированные во время компиляции (такие как строка версии системы), просто компилируются в vDSO.
Для значений, определенных во время загрузки, ядро должно изменить содержимое vDSO. Это выполняется с помощью кода, исполняемого на раннем этапе, который формирует VMO vDSO, прежде чем ядро запустит первый пользовательский процесс (и передаст ему дескриптор VMO). Во время компиляции смещения из образа vDSO (vdso_constants [13]) извлекается из ELF-файла, а затем встраиваются в ядро. А во время загрузки ядро временно отображает страницы, охватывающие vdso_constants [13], в свое собственное адресное пространство для до-инициализации структуры правильными значениями (для текущего запуска системы).
Одна из важнейших причин — безопасность. То есть, если злоумышленнику удастся исполнить произвольный (shell-) код, ему придется использовать функции vDSO для вызова системных функций. Первой преградой будет вышеупомянутая рандомизация адреса загрузки vDSO для каждого создаваемого процесса. И поскольку за VMO (virtual memory object) vDSO'а отвечает ядро ОС, оно может выбрать отображение совершенно другого vDSO в конкретный процесс, тем самым запрещая опасные (и не нужные конкретному процессу) системные вызовы. Например: можно запретить драйверам [14] порождать дочерние процессы или обрабатывать проецирование областей MMIO. Это отличный инструмент уменьшения поверхности атаки.
Замечание: на текущий момент поддержка нескольких vDSO активно разрабатывается. Уже существует реализация концепции (proof-of-concept) и простые тесты, но требуется больше работы для улучшения надежности реализации и определения того, какие варианты будут доступны. Текущая концепция предоставляет варианты образа vDSO, которые экспортируют только подмножество полного интерфейса системных вызовов vDSO.
Следуют отметить, что подобные техники уже успешно используются в других ОС. Например, в Windows есть ProcessSystemCallDisablePolicy [15]:
Win32k System Call Disable Restriction to restrict ability to use NTUser and GDI
Автор: kITerE
Источник [16]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/google/304734
Ссылки в тексте:
[1] LK (Little Kernel): https://github.com/littlekernel/lk/wiki/Introduction
[2] официальной документации Zircon vDSO: https://fuchsia.googlesource.com/zircon/+/master/docs/vdso.md
[3] Admiring the Zircon Part 1: Understanding Minimal Process Creation: https://depletionmode.com/zircon-process.html
[4] @depletionmode: https://twitter.com/depletionmode
[5] системным вызовам (syscalls): https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls.md
[6] VMO, Virtual Memory Object: https://fuchsia.googlesource.com/zircon/+/master/docs/concepts.md#shared-memory_virtual-memory-objects-vmos
[7] zx_vmar_map: https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/vmar_map.md
[8] zx_process_start: https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/process_start.md
[9] zx_system_get_version(): https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/system_get_version.md
[10] zx_system_get_num_cpus(): https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/system_get_num_cpus.md
[11] zx_ticks_per_second(): https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/ticks_per_second.md
[12] командной строки ядра: https://fuchsia.googlesource.com/zircon/+/master/docs/kernel_cmdline.md#vdso_soft_ticks_bool
[13] vdso_constants: https://fuchsia.googlesource.com/zircon/+/master/kernel/lib/vdso/include/lib/vdso-constants.h
[14] драйверам: https://fuchsia.googlesource.com/zircon/+/HEAD/docs/ddk/device-model.md
[15] Windows есть ProcessSystemCallDisablePolicy: https://docs.microsoft.com/en-us/windows/security/threat-protection/overview-of-threat-mitigations-in-windows-10#table-4---functions-available-to-developers-for-building-mitigations-into-apps
[16] Источник: https://habr.com/post/435482/?utm_campaign=435482
Нажмите здесь для печати.