Вы находитесь на странице: 1из 6

«Лаборатория Касперского» подготовила задачи для желающих войти в команду.

Можно
решить одну из двух задач на выбор или обе.

Присылайте результаты на tatyana.derevyanko@kaspersky.com, а команда экспертов выберет


самые крутые варианты и пригласит их авторов на финальное собеседование. Кроме того,
авторы трех лучших решений получат бесплатные билеты на онлайн-конференцию Podlodka
Teamlead Crew.

Задача #1: про системное


программирование

Описание задачи
Sand - игрушечный sandbox, реализованный с использованием технологии VTX. Он может
запускать 32-битный код под контролем гипервизора.
Требуется внести ряд улучшений в его работу.
Замечание:
руководство по настройке рабочего окружения использует материалы из
статьи:  http://ncmiller.github.io/2016/05/14/linux-and-qemu.html

Рабочее окружение
Для тестирования гипервизора потребуется компьютер с поддержкой технологии
аппаратной виртуализации VTX и расширением EPT.

Предполагается, что на компьютере установлена операционная система GNU/Linux, а в ядро


Linux загружены модули kvm и kvm_intel.

Замечание:
Проверить, поддерживаются ли процессором необходимые расширения, можно просмотрев
содержимое файла /proc/cpuinfo. В поле flags должны встречаться значения vmx и ept.

Так как гипервизор – модуль ядра, тестировать его на рабочей машине непосредственно
может быть плохой идеей. Поэтому для экспериментов будет использоваться Qemu/KVM.
Создадим минимальную сборку системы GNU/Linux для запуска в Qemu, а модуль ядра
будем линковать в ядро гостевой Linux.

Для сборки ядра потребуется установить несколько дополнительных пакетов.

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

Потребуются следующие пакеты: 

ncurses-dev build-essential libssl-dev libelf-dev

Замечание:
Возможно, при сборке выяснится, что упущены еще какие-то зависимости. Тогда требуемые
пакеты также следует установить.

● Создадим папку sand в удобном месте на файловой системе,


● В папке sand создаем подкаталоги build и source.
● Скачиваем архив с исходным кодом ядра Linux версии 5.5.11 с сайта kernel.org в
sand/source и разархивируем.
● Скачиваем архив с исходным кодом свежего busybox c сайта busybox.net в sand/source и
разархивируем.
● Создадим в папке sand/build подкаталоги linux-5.5.11 и busybox-1.31.1
● Находясь в папке sand, запомним положение на файловой системе в переменную
окружения PROJ=`pwd`
● Перейдем в каталог sand/source/linux-5.5.11 и сконфигурируем сборку:

make O=$PROJ/build/linux-5.5.11 x86_64_defconfig


make O=$PROJ/build/linux-5.5.11 kvmconfig

● Соберем ядро, параметр -j зависит от количества ядер в системе, не переборщите:

make O=$PROJ/build/linux-5.5.11 -j4

● Перейдем в каталог sand/source/busybox-1.31.1 и сконфигурируем сборку:

make O=$PROJ/build/busybox-1.31.1 defconfig


make O=$PROJ/build/busybox-1.31.1 menuconfig

Надо включить опцию "Build BusyBox as a static binary" в Busybox Settings, опцию можно найти
через поиск Kbuild (введите '/', как в vi).

● Соберем busybox, для этого перейдем в sand/build/busybox-1.31.1 и введем:

make -j4
make install

● Создадим минимальную файловую систему, для этого создадим папку


sand/build/initramfs и перейдем в нее
● Введем следующие команды:

mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}


cp -av $PROJ/build/busybox-1.31.1/_install/* .

● Создадим init-скрипт:
touch init
chmod +x init

● Введем в него следующий код:

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
echo -e "Hello, Mr. Sandman!\n"
exec /bin/sh

● Соберем initramfs:
find . -print0 \
| cpio --null -ov --format=newc \
| gzip -9 > $PROJ/build/initramfs.cpio.gz

● Проверим, что система работает, запустим ее в qemu следующей командой из каталога


sand:

qemu-system-x86_64 \
-kernel build/linux-5.5.11/arch/x86_64/boot/bzImage \
-initrd build/initramfs.cpio.gz \
-nographic -append "console=ttyS0" \
-cpu host \
-machine q35 \
-enable-kvm

Замечание:
Выйти из терминала Qemu можно, нажав Ctrl+A, C и следом q.

Работа над задачей


Перейдем в sand/source/linux-5.5.11 и наложим патч 0001-Introduce-skeleton-of-VTX-
sandbox.patch (скачать можно тут).

Изменения будем делать в каталоге sand/source/linux-5.5.11/drivers/misc/sand, а сборку


будем запускать в sand/build/linux-5.5.11, вызывая make.

Для начала соберем утилиту sandctl, которая понадобится для работы на initramfs. Перейдем
в sand/source/linux-5.5.11/drivers/misc/sand и выполним команды.

gcc sandctl.c -o sandctl -static

cp sandctl $PROJ/build/initramfs/usr/bin

find . -print0 \
| cpio --null -ov --format=newc \
| gzip -9 > $PROJ/build/initramfs.cpio.gz

Пересоберем ядро после наложения патча, для этого, находясь в каталоге sand/build/linux-
5.5.11 выполним:

make -j4

Запустим Qemu, чтобы проверить, что все работает. Команда уже указывалась выше.

В гостевой сессии вызовем команду:

sandctl 0x83 0xC0 0x01 0xF4

Вывод должен быть примерно таким:

Will run the following code: 83 C0 1 F4


[ 50.113457] sand: Going to run 4 bytes of code
[ 50.114073] sand: Pin based exec control: 1f
[ 50.114568] sand: Cpu based VM exec control: 40061f2
[ 50.115155] sand: VM exit controls: 36ffb
[ 50.115617] sand: VM entry controls: 11fb
[ 50.116133] sand: VM Exit Reason c
[ 50.116550] sand: ip: 1003
Finished sandbox properly, state:
eax = 1
ebx = 0
ecx = 0
edx = 0

0x83 0xC0 0x01 0xF4 – программа, прибавляющая 1 к содержимому регистра eax и


вызывающая инструкцию HLT. Как результат, мы видим в eax виртуального процессора
единицу после исполнения. Мы также видим, что регистр eip виртуальной машины
сместился на 3 от точки входа 0x1000. А выход из виртуальной машины произошел из-за
инструкции HLT (VM Exit Reason c).

Теперь все готово, чтобы приступить к решению задач.

Важные ограничения и дополнения


На код накладываются следующие ограничения:

1. Он может использовать для хранения состояния только регистры и стек (растет


с адреса 4096 вниз).
2. Он не должен переходить по адресам ниже 4096 (там расположен стек) и выше
8192, там память не отображена.
3. Длина кода не может превышать 4096 байт.

Код запускается при помощи утилиты sandctl, ей в качестве аргумента передается


последовательность из байт в HEX-кодах. Это код, который требуется запустить.
Перед началом выполнения регистровое состояние sandbox сбрасывается в нуль. В
случае успешного исполнения кода под контролем гипервизора sandctl выводит
финальное состояние регистров eax, ebx, ecx и edx.

sandctl обращается к драйверу sand, который и реализует функциональность


гипервизора. Обращение происходит через вызов IOCTL misc-устройства /dev/sand.

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

1. Сделайте так, чтобы при деинициализации модуля на всех процессорах в системе


вызывалась инструкция VMXOFF.

2. Можно написать гостевой код, который никогда не завершится. sandbox так и


зависнет в контексте ядра. Сделайте возможность досрочного завершения такого
зависшего кода. Подойдет любой способ, позволяющий завершить sandbox раньше по
таймауту.

3. Ограничение #1 мешает писать функциональный код для исполнения в sandbox.


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

4. Чтобы гостевой код мог получать данные из внешнего мира, реализуйте обработку
гипер-вызовов. Напишите код, демонстрирующий получение значения от гипервизора и
выполнение вычислений с использованием этого значения.

5. sand_vm_launch выполняет не все требования System V x86_64 ABI, дополните


функцию так, чтобы сохранялись и восстанавливались все callee-saved ресурсы.

6. Sand не использует EPT (второй уровень трансляции адресов). В результате,


исполняемый в sandbox, не может управлять трансляцией адресов самостоятельно. К
тому же гость не может использовать физическую память выше 4 гигабайт. Сделайте
простое отображение гостевой физической памяти в хостовую через EPT, измените
гостевые таблицы трансляции, чтобы отобразить гостевые виртуальные адреса на
гостевые физические один-к-одному.

7. Если в госте исполнить INT3 (инструкция 0xCC), то получится Triple Fault. Реализуйте
возможность обработки простейших исключений в sandbox. Пусть в момент, когда
приходит гостевое исключение, хост получает уведомление через гипер-вызов.

Дополнительные вопросы

● Какие проблемы с организацией кода вы можете заметить. Попробуйте


исправить код на свой вкус.
● Есть ли в коде ошибки? Как их исправить?
● При сборке модуля objtool ругается на странную работу со стеком в функции
sand_vm_launch. Попробуйте при помощи аннотаций в коде объяснить objtool, что
происходит.

Задача #2: про справки


Гражданин обращается за справками c1, c2, …, cN в различные конторы k1, k2, …, km.

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

Целевых справок, которые требуется получить гражданину, может быть несколько.

Конторы могут обладать состоянием в отношении обращающегося, например, если


гражданин уже подавал справки c1, с2 и с3 в контору k1, то контора переходит в
состояние, когда она может выдать гражданину справку с4. При этом поданные в
контору справки ему не возвращаются.

Для получения справок гражданин подает заявление в контору. Заявление на получение


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

Пример описания правил работы контор:


k1[0] (с1, с2, с3) -> k1[1] (c0)

k2[0] () -> k2[1] (с1)

k2[0] () -> k2[0] (с4)

k3[0] () -> k3[1] (с2)

k3[1] (с1) -> k3[2] (c3)

k4[0] (с4) -> k4[1] (c1)

Требуется:

Определить, что целевые справки в принципе можно получить при заданных правилах
работы контор

Составить алгоритм получения целевых справок, гарантирующий, если это в принципе


возможно, получение всего целевого набора справок

Составить такой алгоритм получения целевых справок, чтобы количество обращений


гражданина в конторы было бы минимальным

Дополнительные условия:

Получение справок может занимать время и стоить денег.

Предложите алгоритм, позволяющий минимизировать время, затрачиваемое на


получение целевого набора справок.

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


набора справок.

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

Таким образом, может случится ситуация, при которой срок действия какой-либо
имеющейся на

руках справки истечет, при этом другие справки из целевого набора еще не будут
выданы.

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