• Партнер

  • Ты, конечно, не раз слышал про такую штуку, как аппаратная виртуализация –
    технологию, позволяющую запускать несколько гостевых ОС на одной машине (так
    называемом хосте). Исторически первой была программная виртуализация, затем
    разработчики процессоров призадумались, что неплохо было бы обеспечить
    аппаратную поддержку этого дела. Итак, с 2006-го года в нашем распоряжении
    оказались процессоры Intel и AMD с возможностями виртуализации. И
    пошло-поехало...

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

    Однако, ни одной статьи, выходящей за рамки аналитики, я так и не встретил.
    Пора закрыть этот пробел и сконцентрироваться, наконец, на деталях реализации
    гипервизора. Между тем, уже, наверное, каждому известно про
    "Голубую пилюлю" (Blue
    Pill) Рутковской
    , в которой возможности виртуализации успешно применяются,
    причем не в самых легитимных целях :). Пока кто-то создает экспериментальные
    руткиты на базе виртуализации, кто-то активно разрабатывает анти-руткиты. Так,
    Hypersight Rootkit Detector от North Security Labs – считай готовый анти-руткит
    для Windows. Для Linux тоже существует подобный проект – HookSafe, который был
    представлен на конференции ACM по компьютерной и сетевой безопасности (CCS
    2009).

    Такой интерес к виртуализации далеко не случаен – ведь для хакера это
    уникальная возможность скрытия своего присутствия в системе (наряду с более
    сложными, но и не менее красивыми, атаками на SMM и AMT). Конечно, чтобы сделать
    руткит (даже на базе виртуализации) на 100% недетектируемым придется напрячься,
    но игра стоит свеч! В конце концов, мы получает возможность тотального контроля
    над системой. Заинтересовало? Тогда вперед, осваивать такую непростую штуку как
    программирование гипервизоров.

    Прежде чем зарываться в документацию, нужно обрисовать задачу более подробно.
    Вообще, аппаратная виртуализация в процессорах Intel (именуемая Intel VT)
    отличается от аналогичной у AMD (AMD-V). Отличается – значит, код гипервизора (aka
    VMM – Virtual Machine Monitor) для AMD не будет работать на платформе Intel.
    Поэтому мы начнем с AMD и продолжим о Intel в последующих статьях. Чтобы ты мог
    уточнить для себя какие-то вещи, тебе потребуется дока от AMD, также можно
    почитать краткий обзор
    AMD-V
    от производителя
    .

     

    Теоретические основы

    Перед тем, как приступать к написанию кода, необходимо получить минимальную
    теоретическую базу. Для начала определимся с терминами и аббревиатурами, которые
    будут использоваться далее по тексту.

    • Гостевой режим – по аналогии с защищенным, реальным – режим
      работы процессора, в котором выполняется гостевая система.
    • Гость – виртуальная ОС, работающая в гостевом режиме под
      управлением гипервизора.
    • VMM (монитор виртуальных машин, гипервизор) – программное
      обеспечение, перехватывающее события в госте. Гипервизор представляет собой
      рычаг управления гостевыми системами.
    • Хост (по отношению к гостю) – система, на которой запущен
      гипервизор.
    • #VMEXIT – переход из режима гостя в режим хоста.
     

    Подробности

    Ты - обладатель процессора AMD. Как узнать, есть ли в нем поддержка
    аппаратной виртуализации? О том, что мы имеем соответствующий функционал,
    рапортует функция 80000001h инструкции CPUID (второй бит от нуля в регистре ecx,
    именуемый SVM, должен быть установлен). Функция, возвращающая 0 или 1,если
    возможности виртуализации недоступны или доступны, соответственно.

    IsSVMAvailableProc proc
    xor rax,rax
    mov eax,80000001h
    cpuid
    xor rax,rax
    bt ecx,2 ; проверяем бит SVM
    jnc if_zero ; прыгаем, если бит равен 0
    inc rax
    if_zero:
    ret
    IsSVMAvailableProc endp

    Убедившись, что в нашем распоряжении подходящий процессор, можно приступать к
    дальнейшему описанию. Забегая вперед, скажу, что включение виртуализации, как и,
    в общем-то, весь код нашего гипервизора, будет находиться в драйвере и работать
    в ring-0. В процессе освоения кодинга гипервизора от тебя потребуется
    представление о программировании драйверов под Windows, знание с/с++ и
    64-битного ассемблера на базовом уровне. Хотя я все равно постараюсь объяснить
    все максимально подробно.

     

    Инструкции управления виртуальными машинами

    По своей сути аппаратная виртуализации – это расширение архитектуры ЦП: набор
    инструкций + новый режим работы процессора. До того, как говорить о наборе
    команд, нужно разобраться с такой штукой, как VMCB. VMCB (Virtual
    Machine Control Block
    ) – управляющий блок виртуальной машины. Это основная
    структура данных, с которой нам предстоит работать. VMCB описывает виртуальную
    машину, которую мы будем запускать. Сразу о технических деталях: VMCB занимает
    одну страницу (4 килобайта) в непрерывной физической памяти. VMCB состоит из 2-х
    частей – область флагов (control area) и область состояния (state-save area).
    Рассмотрим структуру VMCB подробнее. VMM, как уже упоминалось, перехватывает
    события, происходящие в госте. Какие это будут события – определяется в области
    флагов. Мы можем перехватывать:

    1.Чтение/запись контрольных регистров (cr0-cr15). Первые 16 бит структуры
    VMCB как раз и отводятся на установку перехвата операции чтения для каждого из
    контрольных регистров. Каждый бит отвечает за свой контрольный регистр. Вторые
    16 бит VMCB отвечают за операцию записи в контрольные регистры.

    2. Чтение/запись отладочных регистров (dr0-15).

    3. Инструкции rdmsr/wrmsr для выбранных msr-регистров. Чтобы определить,
    какие msr подлежат контролю, используется так называемая MSR Permission Map (в
    переводе - карта разрешения msr, сокращенно - MSRPM). На каждый msr в ней
    отводится по 2 бита – для контроля операции чтения и записи. Физический адрес
    начала MSRPM хранится в VMCB.


    Структура карты разрешения MSR (MSRPM). Каждые 2 бита отвечают за отдельный
    MSR.

    Когда будет произведена запись/чтение в контролируемый msr – произойдет #VMEXIT,
    а подробная информация о событии запишется в поле exitinfo1 VMCB (оно будет рано
    0 – если выход спровоцировала rdmsr, и 1, если wrmsr).

    4. Инструкции работы с портами. Как и в случае с msr-регистрами, за контроль
    доступа к портам отвечает карта разрешения ввода-вывода (IOPM, I/O Permission
    Map). Там, конечно, все чуток сложнее, чем с msr. После #VMEXIT информация об
    исключении будет записана в поле exitinfo1, где будет содержаться информация об
    инструкции, которая вызвала исключение.


    Формат поля exitinfo1 в VMCB для перехваченных инструкций ввода-вывода

    5. Инструкции чтения/записи регистров ldtr,gdtr, tr,idtr.

    6. Исключения (0-31 векторы в IDT).

    7. Инструкции, отвечающие за аппаратную виртуализацию (VMRUN,VMSAVE,VMLOAD…).
    То есть, можно контролировать запуск других гипервизоров (они будут вложенными).
    Кстати, с помощью перехвата этих инструкций Hypersight Rootkit Detector и
    обнаруживает "Голубую пилюлю".

    8. Сигналы SMI, NMI, INIT…

    9. Еще много различных инструкций, таких как cpuid, iret, rsm и т.п.

    Все вышеперечисленные события – это условия #VMEXIT – возвращения из
    гостевого режима в режим хоста. Каждая причина #VMEXIT имеет свой код, который
    записывается в поле exitcode области флагов VMCB. Вот некоторые из этих кодов:

    62h – физическое SMI
    6Eh – произошла инструкция RDTSC
    70h – команда PUSHF
    71h – POPF
    72h – CPUID
    7F – гость выключился (Shutdown)
    80h – VMRUN
    81h - VMMCALL
    82h – VMLOAD
    83h - VMSAVE
    88h – ICEBP (инструкция с опкодом 0xF1)
    -1 – неверная VMCB

    Полную таблицу #VMEXIT-тов можно посмотреть в Appendix C. SVM Intercept Exit
    Codes в уже упоминаемом мной AMD64 Architecture Programmer's Manual Volume 2.

    Часть дефиниции cтруктуры VMCB (из сорцов Xen):

    struct vmcb_struct {
    // область флагов
    // первое слово – перехват чтения cr0-15
    // второе слово – перехват записи cr0-15
    u32 cr_intercepts; /* offset 0x00 */
    // первое слово – перехват чтения dr0-15
    // второе слово – перехват записи dr0-15
    u32 dr_intercepts; /* offset 0x04 */
    // поле установки перехватываемых исключений (векторы 0-31 в IDT)
    u32 exception_intercepts; /* offset 0x08 */
    // INTR, NMI, SMI....IDTR (запись/чтение), GDTR (запись/чтение), LDTR(запись/чтение)
    // TR(запись/чтение), инструкции RDTSC, RDPMC, PUSHF, POPF …
    u32 general1_intercepts; /* offset 0x0C */
    u32 general2_intercepts; /* offset 0x10 */


    // физический адрес карты разрешения ввода-вывода
    u64 iopm_base_pa; /* offset 0x40 */
    // физический адрес карты разрешения msr
    u64 msrpm_base_pa; /* offset 0x48 */
    // это поле нужно для команды rdtsc
    u64 tsc_offset; /* offset 0x50 */
    // идентификатор адресного пространства гостя, связано со сбросом TLB, пока это
    не нужно
    u32 guest_asid; /* offset 0x58 */
    u8 tlb_control; /* offset 0x5C */
    u8 res07[3];
    vintr_t vintr; /* offset 0x60 */
    u64 interrupt_shadow; /* offset 0x68 */
    // после #VMEXIT здесь окажется код причины выхода
    u64 exitcode; /* offset 0x70 */
    u64 exitinfo1; /* offset 0x78 */
    u64 exitinfo2; /* offset 0x80 */

    eventinj_t eventinj; /* offset 0xA8 */
    // используется для вложенного страничного преобразования (nested paging) – об
    этом расскажу в
    //другой раз
    u64 h_cr3; /* offset 0xB0 */
    lbrctrl_t lbr_control; /* offset 0xB8 */
    // оставшееся место – 832 байта - заполняется нулями – оно зарезервировано для
    дальнейшего //расширения
    u64 res09[104]; /* offset 0xC0 pad to save area */

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

    // начало области состояния
    svm_segment_register_t es; /* offset 1024 */
    svm_segment_register_t cs;
    svm_segment_register_t ss;
    svm_segment_register_t ds;
    svm_segment_register_t fs;
    svm_segment_register_t gs;
    svm_segment_register_t gdtr;
    svm_segment_register_t ldtr;
    svm_segment_register_t idtr;
    svm_segment_register_t tr;


    u64 efer; /* offset 1024 + 0xD0 */
    u64 res13[14];
    u64 cr4; /* loffset 1024 + 0x148 */
    u64 cr3;
    u64 cr0;
    u64 dr7;
    u64 dr6;
    u64 rflags;
    u64 rip;
    u64 res14[11];
    u64 rsp;
    u64 res15[3];
    u64 rax;
    u64 star;
    u64 lstar;
    u64 cstar;
    u64 sfmask;
    u64 kerngsbase;
    u64 sysenter_cs;
    u64 sysenter_esp;
    u64 sysenter_eip;
    u64 cr2;


    // регистры, связанные с трассировкой ветвлений
    u64 debugctlmsr;
    u64 lastbranchfromip;
    u64 lastbranchtoip;
    u64 lastintfromip;
    u64 lastinttoip;
    u64 res16[301]; // далее просто 2408 нулевых байт
    }


    Структура VMCB

    С VMCB кое-как разобрались. Теперь можно переходить к описанию инструкций.

    VMRUN (опкод команды - 0Fh, 01h, 0D8h) – инструкция запуска виртуальной
    машины. Это основная и самая важная команда в аппаратной виртуализации. VMRUN
    принимает в качестве аргумента в регистре rax физический адрес управляющего
    блока виртуальной машины (VMCB), который описывает состояние виртуальной машины.
    VMRUN доступна только с нулевого кольца (вообще, с третьего кольца из
    инструкций, составляющих сабжевое расширение архитектуры процессора, доступна
    только VMMCALL).

    Гипервизор настраивает структуру VMCB, устанавливает в ней перехватываемые
    инструкции, прерывания и т.д. Переход в режим гостя происходит посредством
    инструкции VMRUN. Состояние хоста сохраняется в области памяти, на которую
    указывает содержимое msr регистра VM_HSAVE_PA (PA – Physical Address, то есть
    здесь мы опять имеем дело с физическим адресом этого региона). В этой области
    памяти сохраняется минимальная информация, необходимая для возобновления работы
    хоста после выхода из гостя (регистры cs,rip, efer, cr0, cr3 …). Теперь, когда
    виртуальная машина успешно запущена, мы вернемся в режим хоста только при
    возникновении перехваченного гипервизором события (условия #VMEXIT). После #VMEXIT
    будет выполнена следующая за VMRUN инструкция в гипервизоре. Специально для тебя
    я сделал обобщающую схему вышеописанного (смотри картинку "Схема работы
    виртуализации AMD-V").


    Схема работы виртуализации AMD-V

    Две инструкции VMSAVE (0Fh, 01h, 0DBh) и VMLOAD (0Fh, 01h, 0DAh) дополняют
    VMRUN и служат для сохранения/загрузки части VMCB.

    VMMCALL (0Fh, 01h, 0D9h) – инструкция, позволяющая из гостевого режима
    перейти в хост. Доступна как на нулевом, так и на третьем кольце. Правда, я
    лично не понимаю смысла в этой инструкции. Если она не перехватывается, то
    возникает #UD. То есть, безусловного вызова гипервизора не происходит. Можно
    было бы, наверное, не вводить дополнительную инструкцию, использовать ту же
    CPUID (или другую, которую можно перехватить).

     

    Включение возможностей аппаратной виртуализации

    Все инструкции работы с аппаратной виртуализацией (за исключением SKINIT, там
    особый случай) требуют установки бита SVME (он 12-й) в регистре EFER (иначе мы
    получим исключение #UD – неверная инструкция). Что это за регистр – EFER?
    Расшифровывается аббревиатура как Extended Feature Enable Register – это msr,
    который отвечает за включение дополнительных возможностей проца (что видно из
    расшифровки), и он имеет адрес 0C0000080h. Приведенный ниже код включает
    возможности аппаратной виртуализации:

    sub rcx,rcx
    mov ecx, 0C0000080h ; адрес EFER
    rdmsr ; читаем EFER
    bts eax,12
    wrmsr


    MSR-регистр EFER. У него полно различных функций помимо включения аппаратной
    виртуализации.

    Кстати, установка бита SVME может быть заблокирована, поэтому после того, как
    мы записали msr, нужно снова прочитать его содержимое и проверить – установился
    ли заветный бит.

    За блокировку инструкций виртуализации отвечают msr-регистры – VM_CR и
    SVM_KEY (опционально). Бит SVMDIS, который четвертый в VM_CR, запрещает
    установку EFER.SVME, а LOCK (бит три) в том же регистре запрещает сброс SVMDIS и
    LOCK (получается, что LOCK – это защита для защиты). LOCK можно сбросить либо
    после перезагрузки, либо указать ключ в машинно-зависимом регистре SVM_KEY (если
    этот ключ был установлен перед блокированием виртуализации). Сама возможность
    блокировки, к слову, появилась в AMD-V не сразу, а только со второй ревизии
    (специально для параноиков:)).


    MSR-регистр VM_CR, позволяющий заблокировать установку бита SVME в EFER.

     

    Заключение

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

     

    WWW

    AMD64 Architecture Programmer's Manual Volume 2: System Programming:

    amd.com/us-en/assets/content_type/white_papers_and_tech_docs/24593.pdf
    . Теме
    виртуализации в этом мануале посвящена глава 15, Secure Virtual Machine.

    Hypersight Rootkit Detector (для Windows) – анти-руткит на основе аппаратной
    виртуализации. Фраза на главной странице "Blue Pill перестал быть невидимым" –
    заставляет познакомиться с сабжем поближе:

    northsecuritylabs.com/ru
    .

    Проект Blue Pill Джоанны Рутковской – руткит, использующий аппаратную
    виртуализацию (опенсорс):
    bluepillproject.org
    .

    В качестве дополнительной литературы можно почитать также ман AMD, целиком и
    полностью посвященный CPUID. CPUID Specification

    amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf
    .

    Проект Xen
    xen.org/products/projects.html
    .

    HookSafe – не так давно появившийся анти-руткит на основе гипервизора (для
    Linux). Исследователи работают над версией для Windows

    discovery.csc.ncsu.edu/pubs/ccs09-HookSafe.pdf
    .

    Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии