Hyper-V debugging for beginners

Hyper-V debugging for beginners

В статье приводится исследование работы гипервизора Hyper-V 3.0, входящего в состав Windows Server 2012.

Автор: Gerhart

В статье приводится исследование работы гипервизора Hyper-V 3.0, входящего в состав Windows Server 2012. Для исследования использовались VMware Workstation 9, Windows Server 2012, Windows 7 x86, WinDBG 6.2, IDA PRO. В VMware необходимо создать виртуальную машину, установив тип гостевой ОС - Hyper-V, поставить количество процессоров и ядер в 1, активировать опцию Virtualize Intel VT-x/EPT, установить Windows Server 2012 (или Hyper-V Server 2012), активировать роль Hyper-V и установить в качестве гостевой по отношению к Hyper-V ОС Windows 7 x86.

1. Термины и определения

- Гипервизор – компонент Hyper-V, зависящий от производителя процессора (hvix64.exe для Intel и hvax64.exe для AMD). В статье рассматривается гипервизор для процессоров Intel.

- Гипервызов (hypercall) – вызов заданной функции в гипервизоре с помощью инструкции vmcall.

- Root-partition (root-раздел) – Windows Server 2012 с включенным компонентом Hyper-V.

- VMCS (virtual-machine control structure) – структура, определяющая логику работы гипервизора.

- VMX root – режим, в котором работает гипервизор.

- VMX non-root – режим, в котором работает операционная система и обслуживаемое ею прикладное программное обеспечение.

- VM exit – переход из VMX non-root в VMX root. Происходит при выполнении инструкций или условий, заданных в VMCS или заложенных непосредственно в логику работы процессора.

2. Отладка

Hyper-V состоит из нескольких компонентов, краткое описание которых можно найти в (1). Для отладки всех компонентов за исключением гипервизора можно использовать стандартные методы, однако для подключения к гипервизору необходимо выполнить несколько дополнительных действий по настройке root-раздела.

Для отладки гипервизора Microsoft разрабатывала специальное расширение для WinDBG hvexts.dll, которое, к сожалению, не входит в состав дистрибутива отладчика и доступно только партнерам компании. Также в каталоге winxp, расположенного в папке с установленным WinDBG, есть расширение nvkd.dll, которое предназначено для отладки расширений виртуального коммутатора Hyper-V.

В MSDN (2), а также (3) приводится описание отладки гипервизора с помощью кабеля через com-порт, подразумевающее наличие двух физических машин. Однако гипервизор можно отлаживать, если запустить его в VMware и использовать эмулятор com-порта Free Virtual Serial Ports Configuration Utility от HHD-software (4). Для этого необходимо:

- создать com-порт для виртуальной машины (Hardware->Add->Serial port->Output to named pipe)

- в root-partition выполнить команды для настройки параметров отладки гипервизора и самой ОС:

bcdedit /hypervisorsettings serial DEBUGPORT:1 BAUDRATE:115200
bcdedit /set hypervisordebug on
bcdedit /set hypervisorlaunchtype auto
bcdedit /set dbgtransport kdhvcom.dll
bcdedit /dbgsettings serial DEBUGPORT:1 BAUDRATE:115200
bcdedit /debug on
Bcdedit /set bootdebug on (в дальнейшем потребуется для изучения процесса загрузки гипервизора)

- перезагрузить Windows Server 2012. Загрузка остановится в ожидании подключения отладчика.

- запустить Free Virtual Serial Ports, выбрать Pipe и нажать Create. В поле Pipe name указать тот же самое значение, что и для виртуальной машины - \\.\pipe\com_1. Нажать Create.

В случае успешного подключения к именованному каналу программа создаст виртуальный com-порт

- Запустить vmdemux (находится в каталоге установки WinDBG), указав имя этого порта в качестве одного из параметров:

vmdemux.exe -src com:port=com3,baud=115200

В случае успешного соединения получаем:

Созданный именованный канал \\.\pipe\Vm1 необходимо использовать для подключения отладчика:

WinDBG.exe -b -k com:port=\\.\pipe\Vm1,pipe,reconnect,resets=0

При этом отладчик подключится к root-partition. Затем необходимо несколько раз выполнить команду g, после чего vmdemux должен выдать:

После этого с помощью IDA PRO можно подключиться непосредственно к гипервизору через именованный канал \\.\pipe\Vm0 , выбрав в качестве отладчика WinDBG и указав в process options строку подключения:

com:port=\\.\pipe\Vm0,pipe,resets=0

В случае появления следующего сообщения выбрать Same.

Отладчик остановится внутри гипервизора:

Однако вышеописанный способ отладки является довольно медленным и относительно нестабильным (во время написания статьи отладчик при подключении к гипервизору через com-порт несколько раз просто зависал). В Windows Server 2012 появилась возможность отладки гипервизора по сети, и хотя в MSDN на момент создания статьи описание этого способа отсутствовало, тем не менее, немного покопавшись в справке к команде bcdedit, можно подобрать необходимые параметры.

Для этого в Windows Server 2012 необходимо прописать

bcdedit /set dbgtransport kdnet.dll
bcdedit /debug yes
bcdedit /dbgsettings net hostip:192.168.2.1 port:50002

в ответ команда выдаст строку подключения к root-partition

bcdedit /set hypervisordebug on
bcdedit /hypervisorsettings NET HOSTIP:192.168.2.1 PORT:50000

в ответ команда выдаст строку подключения к гипервизору

в VMware в настройках виртуальной машины установить тип адаптера Host Only, в настройках виртуальной сети настроить DHCP для этого адаптера и убедиться, что Windows Server 2012 нормально получает этот адрес, например, выполнив команду ipconfig /renew.

После этого запустить 2 экземпляра IDA PRO, выбрать тип отладки KernelMode, указать в Process Option->Connection string следующие строки, полученные в результате выполнения вышеприведенных команд:

net:port=50002,Key=2ryd8m5mtthis.yomvgm0wtjzp.2ip83bg5uczdf.1ya73ieco8mhj – root-раздел
net:port=50000,Key=2hxy6pt2onihj.hfak67vz3rei.14kocxhm1ucio.2lhd41tj99oa2 -hypervisor

тем самым получив возможность одновременной отладки root-раздела и гипервизора.

Опция bcdedit /dbgsettings nodhcp позволит отладчику в сетевом режиме использовать ip-адрес root-раздела. В этом случае настраивать DHCP в VMware не нужно.

Отладку гостевой по отношению Hyper-V ОС можно производить либо стандартным методом через виртуальный com-порт, либо используя отладочные возможности гипервизора. Пример второго варианта был приведен на форуме OSR Online (5), и для его настройки потребуется:

- скопировать файл kdvm.dll из Windows 8 в каталог C:\Windows\system32\kdvm.dll отлаживаемой Windows 7 (разумеется, файл необходимо брать из операционной системы идентичной разрядности). Для Windows 8.1\Windows Server 2012 R2 kdvm.dll необходимо брать из preview-билда, т.к. из RTM-версий файл был удалён

- в Windows 7 выполнить команды

bcdedit /set dbgtransport kdvm.dll
bcdedit /set {default} loadoptions host_ip="1.2.3.4",host_port="50005",encryption_key="1.2.3.4"
bcdedit /set debug on

- перезагрузить ОС.

- указать параметры скрипта hyperv-dbg.ps1 (скрипт в архиве был адаптирован и для Windows Server 2012 R2\Windows 8.1)

- выполнить скрипт hyperv-dbg.ps1 (запускать через Run as Administrator, или же отключить UAC, запустить gpedit.msc и установить опцию Computer configuration\Windows Settings\Security Settings\Local Policies\Security Options\User Account Control: Run All administrators in Admin Approval Mode в Disable) в root-разделе

- запустить WinDBG:

WinDBG -k net:port=50005,target=127.0.0.1,key=1.2.3.4

- выполнить команду break, после чего отладчик остановится внутри гостевой ОС:

Также для виртуальной машины VMware, в которой установлена Windows Server 2012, необходимо включить gdb-отладчик. Для этого в vmx-файле этой машины необходимо добавить строки

debugStub.listen.guest64 = "TRUE"
debugStub.hideBreakpoints= "TRUE"

3. Загрузка гипервизора

В исследовании были использованы checked-версии файлов hvloader.exe (6.2.9200.16384) и hvix64.exe (6.2.9200.16384). Перед отладкой необходимо загрузить в IDA PRO winload.exe, выбрать Debugger –> Select Debugger –> GDB, в Process Options в Host name указать 127.0.0.1 и порт 8864.

Благодаря установленной ранее опции загрузчика bootdebug on появляется возможность подключения на раннем этапе загрузки к winload.exe, который и производит запуск гипервизора. Для этого после старта ОС необходимо

- запустить WinDBG:

WinDBG.exe -b -k net:port=50002,key=2ryd8m5mtthis.yomvgm0wtjzp.2ip83bg5uczdf.
1ya73ieco8mhj

установка должна произойти внутри функции winload!DebugService2

- узнать адрес загрузки winload.exe

kd> lm
start end module name
00000000`007df000 00000000`00971000 winload (pdb symbols)

- запустить IDA PRO и загрузить ранее проанализированный модуль winload.exe, выбрать Debugger -> attach to process –> attach to process started on target и после остановки выполнить Edit –> Segments –> Rebase program, указав в поле Image base адрес загрузки winload.exe (0x007df000) и сохранить базу IDA PRO. При загрузке winload.exe ASLR не используется, поэтому адрес загрузки не будет меняться при перезапуске ОС, и при последующих загрузках в IDA PRO winload.exe сразу же будет размещаться по правильному адресу

- поставить в IDA PRO точку останова на winload!OslArchHypervisorSetup и продолжить отладку (F9). Также продолжить отладку в WinDBG:

kd> g

Winload проверяет, задан ли параметр загрузчика hypervisorlaunchtype (0x250000f0).

В случае если параметр задан и его значение равно 1 (Auto), происходит вызов функции HvlpLaunchHvLoader, которая загружает и передаёт управление модулю hvloader.exe, который в свою очередь должен будет загрузить файл гипервизора hvix64.exe и подготовить его к дальнейшей работе.

Функция BlBdStop отключила бы WinDBG, но отладке через gdb в VMware она помешать не может.

В функции Archpx64TransferTo64BitApplicationAsm происходит передача управления на hvlMain из hvloader.exe (адрес функции hvlMain содержится в ArchpChildAppEntryRoutine).

Для того чтобы нормально отлаживать hvloader.exe можно либо подгрузить ранее созданный idb файл, либо отключиться от текущей отладочной сессии и подключиться заново. Подгрузка файла hvloader.idb вызывает зависание IDA, так что придется воспользоваться вторым вариантом. Для этого производится замена первой инструкции HvlMain на EB FE 90, что зациклит код и даст возможность перезапустить IDA PRO, загрузить hvloader.exe и заново подключиться gdb-отладчиком к VMware. После этого необходимо вернуть измененные байты на место и выполнить rebase модуля. Для повышения скорости операций изменения кода можно применить простые скрипты на python’e (PatchHvLoader.py и RestoreHvLoader.py). База загрузки hvloader.exe не менялась и всегда была равна 0x971000, так что по аналогии с winload.exe один раз выполняется rebase, база сохраняется, а при последующих подключениях отладчиком модуль размещается по нужному адресу без выполнения дополнительных операций.

В hvloader.exe стоит обратить внимание на функцию BtPrepareHypervisorLaunch, которая и выполняет основные операции по загрузке гипервизора. Незадолго до вызова этой функции можно увидеть функцию BtLoadUpdateDll, которая загружает библиотеку обновления микрокода процессора mcupdate_GenuineIntel.dll. В функциях BtLoadUpdateDll и BtPrepareHypervisorLaunch сперва выполняется BtpIdentityPlatform, определяющая производителя процессора

и возвращающая указатель на структуру BtpPlatformTable и, соответственно, имена загружаемых файлов.

Указатели на функции VmxDetect и SvmDetect необходимы только для BtPrepareHypervisorLaunch. Эти функции вызываются сразу же после BtpIdentityPlatform в зависимости от платформы (VmxDetect для Intel и SvmDetect для AMD):

VmxDetect, например, определяет возможности процессора

и возвращает указатель на следующую платформозависимую функцию VmxValidate (SvmDetect возвращает SvmValidate) и т.п.

Дополнительно можно обратить внимание на вычисление случайного смещения адреса загрузки гипервизора относительно 0xFFFFF800 00000000 и последующее его перемещение путем вызова функции BtpLayoutHvImage.

В BtpAllocateAndBuildLoaderBlock происходит заполнение структуры BtpLoaderBlockPages (она же HvlpLoaderBlock в winload.exe), которая в дальнейшем будет использоваться при передаче управления на процедуру start из hvix64.exe.

Сообщение Rebase Hv by: 6282000 показывает смещение загрузки гипервизора относительно адреса 0xFFFFF800 00000000. Это смещение понадобится в тот момент, когда будет необходимо переключиться в IDA PRO с отладки winload.exe на hvix64.exe

Возвращаемся в winload.exe

В функции HvlpTransferToHypervisor производится переход на функцию start из hvix64.exe.

Инструкция jmp r8 передает выполнение на код, расположенный по адресу, указанному в HvlpBelow1MbPage (0x1000)

В rdx ранее из структуры hvLoaderBlock был помещен адрес функции start из hvix64.exe

Далее в IDA PRO необходимо выполнить загрузку hvix64.idb (по аналогии с hvloader.exe), которая производится следующим образом:

- вставка инструкции jmp $ (EB FE) в начало процедуры start в hvix64.exe;

- завершение отладки winload.exe через Debugger->Detach from process;

- загрузка файла hvix64.exe в IDA PRO;

- подключение к gdb отладчику vmware;

- восстановление измененных байт на оригинальные (0F 32);

- выполнение операции Edit->Segment->Rebase program c указанием в качестве Image Base 0xFFFFF800 00000000 + значение, которые было выдано отладчиком в строке Rebase Hv by: 6282000.

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

Затем vmptrld, последующее заполнение VMCS необходимыми значениями, и в последнюю очередь vmlaunch.

После vmlaunch попадаем в HvlpReturnFromHypervisor. При отладке через GDB будет видно, что после выполнения первой инструкции cpuid, вызывающей VM exit, производится переход непосредственно на HOST_RIP.

Возврат из процедуры HvlpReturnFromHypervisor передает управление на следующую за HvlpTransferToHypervisor инструкцию.

После завершения функции HvlpLaunchHypervisor производится запуск ядра Windows через OslArchTransferToKernel.

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

[0] Hypervisor initialized.
[0] Root Vp created.
MTRR map: number of ranges = 6 (default=UC)
Base=0x0000000000000000, Size=0x00000000000a0000, Type=WB, Synth=0
Base=0x00000000000a0000, Size=0x0000000000020000, Type=UC, Synth=0
Base=0x00000000000c0000, Size=0x000000000000c000, Type=WP, Synth=0
Base=0x00000000000cc000, Size=0x0000000000024000, Type=UC, Synth=0
Base=0x00000000000f0000, Size=0x0000000000010000, Type=WP, Synth=0
Base=0x0000000000100000, Size=0x00000000bff00000, Type=WB, Synth=0
----------------------
[0] Root Vp started.
[1] Root Vp created.
[1] Root Vp started.
[2] Root Vp created.
[2] Root Vp started.
[3] Root Vp created.
[3] Root Vp started.
MTRR map: number of ranges = 6 (default=UC)
Base=0x0000000000000000, Size=0x00000000000a0000, Type=WB, Synth=0
Base=0x00000000000a0000, Size=0x0000000000020000, Type=UC, Synth=0
Base=0x00000000000c0000, Size=0x000000000000c000, Type=WP, Synth=0
Base=0x00000000000cc000, Size=0x0000000000024000, Type=UC, Synth=0
Base=0x00000000000f0000, Size=0x0000000000010000, Type=WP, Synth=0
Base=0x0000000000100000, Size=0x00000000bff00000, Type=WB, Synth=0

Стоит отметить, что процесс загрузки гипервизора в Windows Server 2012 существенно отличается от Windows Server 2008 R2, в котором подготовка и непосредственно запуск гипервизора производились драйвером hvboot.sys, который запускался после загрузки ядра Windows. При этом активация гипервизора инструкцией vmlaunch производилась в драйвере hvboot.sys, а следующий VM exit обрабатывался уже в hvix64.exe.

Поиск символьной информации

При загрузке hvix64.exe в IDA PRO получаем около трех тысяч функций с именами типа sub_FFFFF8000XXXXX, поскольку Microsoft, к сожалению, не предоставляет символьную информацию для гипервизора. Для облегчения исследования гипервизора сперва можно попробовать определить часть функций без их детального изучения.

В первую очередь стоит с помощью bindiff сравнить файлы hvix64.exe с файлами hvloader.exe и winload.exe, для которых символьная информация предоставлена. Сравнение показывает, что функции работы с сетью (e1000_) и USB, криптография и некоторые другие функции в точности совпадают с теми, что присутствуют в winload.exe. Это поможет установить назначение порядка 500 функций. Тот же bindiff позволяет перенести имена совпавших функций из одной idb базы в другую. Однако к этому методу стоит отнестись с осторожностью и не переносить сразу все полностью совпавшие функции. По крайней мере стоит проанализировать результат путем визуального сравнения графов совпавших функций (Ctrl+E).

Далее стоит определить функции обработки прерываний\исключений, которые являются стандартными для процессоров архитектуры x86. Для этого был написан небольшой скрипт на python (ParseIDT.py) для парсинга IDT, который необходимо запускать в IDA PRO, предварительно подключившись через отладочный модуль WinDBG к гипервизору.

В случае если часть ISR не была найдена, следует проверить вкладку List of problems в IDA PRO, так как эти процедуры могут не быть найденными при автоматическом анализе кода, который производит IDA.

Далее можно определить процедуру выхода VM exit, прочитав значения полей VMCS. Это можно сделать изучив процедуру заполнения VMCS в hvix64.exe или же воспользоваться скриптом display-vmcs.py, который в контексте гипервизора считывает все поля VMCS и выводит их значения.

Hypercall

Microsoft выпустила документ Hypervisor Top-Level Functional Specification: Windows Server 2012 (6), описывающий принципы работы Hyper-V 3.0.

Каждая виртуальная машина, а также непосредственно ОС с установленным компонентом Hyper-V, представляется в виде раздела (partition). У каждого раздела есть свой идентификатор, который должен быть уникальным в рамках хост-сервера.

Для каждого раздела при создании задаются привилегии (структура HV_PARTITION_PRIVILEGE_MASK), которые и определяют возможность выполнения конкретных гипервызовов.

Узнать привилегии можно, выполнив в root-partition следующий код в ring0:

WinHvGetPartitionId(&PartID);//PartID – идентификатор раздела

WinHvGetPartitionProperty(PartID,HvPartitionPropertyPrivilegeFlags,&HvProp);//результат возвращается в HvProp.

HvPartitionPropertyPrivilegeFlags – одно из значений перечисления HV_PARTITION_PROPERTY_CODE, которыми оперируют функции, экспортируемые драйвером winhv.sys.

HV_STATUS
WinHvGetPartitionProperty(
__in HV_PARTITION_ID PartitionId,
__in HV_PARTITION_PROPERTY_CODE PropertyCode,
__out PHV_PARTITION_PROPERTY PropertyValue
);

Также, при необходимости, эти привилегии можно поменять, вызвав в root-partition следующую функцию:

HV_STATUS
WinHvSetPartitionProperty(
__in HV_PARTITION_ID PartitionId,
__in HV_PARTITION_PROPERTY_CODE PropertyCode,
__in HV_PARTITION_PROPERTY PropertyValue
);

Значение HvPartitionPropertyPrivilegeFlags для root-partition 000039FF00001FFF:

AccessVpRunTimeMsr

AccessPartitionReferenceCounter

AccessSynicMsrs

AccessSyntheticTimerMsrs

AccessApicMsrs

AccessHypercallMsrs

AccessVpIndex

AccessResetMsr

AccessStatsMsr

AccessPartitionReferenceTsc

AccessGuestIdleMsr

AccessFrequencyMsrs

AccessDebugMsrs

CreatePartitions

AccessPartitionId

AccessMemoryPool

AdjustMessageBuffers

PostMessages

SignalEvents

CreatePort

ConnectPort

AccessStats

Debugging

CpuManagement

ConfigureProfiler

Значение HvPartitionPropertyPrivilegeFlags для child-partition 000008B000000E7F:

AccessVpRunTimeMsr

AccessPartitionReferenceCounter

AccessSynicMsrs

AccessSyntheticTimerMsrs

AccessApicMsrs

AccessHypercallMsrs

AccessVpIndex

AccessPartitionReferenceTsc

AccessGuestIdleMsr

AccessFrequencyMsrs

PostMessages

SignalEvents

ConnectPort

Debugging

В гостевой ОС привилегии можно получить, поместив в EAX 0x40000003 и выполнив инструкцию CPUID (в документе Hypervisor Top-Level Functional Specification 3.0a дана расшифровка результатов cpuid).

CPUID 40000003 called
EAX = 00000E7F (00001110 01111111)
Bit 0: VP Runtime (HV_X64_MSR_VP_RUNTIME)
Bit 1: Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT)
Bit 2: Basic SynIC MSRs (HV_X64_MSR_SCONTROL through HV_X64_MSR_EOM and HV_X64_MSR_SINT0 through HV_X64_MSR_SINT15)
Bit 3: Synthetic Timer MSRs (HV_X64_MSR_STIMER0_CONFIG through HV_X64_MSR_STIMER3_COUNT)
Bit 4: APIC access MSRs (HV_X64_MSR_EOI, HV_X64_MSR_ICR and HV_X64_MSR_TPR)
Bit 5: Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL)
Bit 6: Access virtual processor index MSR (HV_X64_MSR_VP_INDEX)
EBX = 000008B0 (00001000 10110000)
Bit 4: PostMessages
Bit 5: SignalEvents
Bit 7: ConnectPort
Bit 11: Debugging
ECX = 00000002 (00000000 00000010)
Maximum Processor Power State is C2
EDX = 000007B2 (00000111 10110010)
Bit 1: Guest debugging support is available
Bit 4: Support for passing hypercall input parameter block via XMM registers is available
Bit 5: Support for a virtual guest idle state is available

В гипервизоре привилегии раздела, который выполнил операцию, вызвавшую VM exit, можно получить, если вычислить значение gs:0, прочитав значение поля HOST_GS_BASE в VMCS или же IA32_GS_BASE MSR:

WINDBG>rdmsr 0xc0000101
msr[c0000101] = fffff800`05464000

затем получить значение, на которое указывает gs:82e8, и перейти по смещению 0xd8.

WINDBG>dc poi(fffff800`05464000+82e8)+0xd8
00000080`04dd70d8 00001fff 000039ff 00000000 ffffe800 .....9..........
00000080`04dd70e8 00000001 00000000 00000000 00000000 ................

В данном случае VM exit был произведен из root-partition.

Гипервизор в каждом из разделов формирует специальную страницу для выполнения гипервызовов. Ее адрес можно получить, прочитав MSR 0x40000001 (HV_X64_MSR_HYPERCALL):

kd> rdmsr 0x40000001
msr[40000001] = 00000000`1ffb1001
kd> !dc 00000000`1ffb1001
#1ffb1000 c3c1010f 90909090 90909090 90909090 ................
#1ffb1010 90909090 90909090 90909090 90909090 ................

Как можно заметить, 0xc3c1010f – опкоды инструкций vmcall; ret

В Windows Server 2012 произошли следующие изменения в экспорте драйвера winhv.sys по сравнению с Windows Server 2008 R2:

Добавлено

Удалено

WinHvAddLogicalProcessor

WinHvAttachDevice

WinHvDetachDevice

WinHvGetLogicalProcessorProperty

WinHvGetLogicalProcessorRegisters

WinHvGetNextQueuedPort

WinHvGetSystemInformation

WinHvInjectSyntheticMachineCheckEvent

WinHvMapDeviceInterrupt

WinHvPrepareForSleep

WinHvProcessorIndexToLpIndex

WinHvProcessorNumberToVpIndex

WinHvRemoveLogicalProcessor

WinHvSetLogicalProcessorProperty

WinHvSetLogicalProcessorRegisters

WinHvUnmapDeviceInterrupt

WinHvOnInterrupt

WinHvReclaimInterruptVector

WinHvSupplyInterruptVector

Для того, чтобы иметь возможность использовать экспортируемые функции winhv.sys можно либо динамически вычислять адреса функций (7), либо создать lib-файл (8). Рассмотрим второй вариант.

При объявлении функций как stdcall (для 32-битной версии драйвера) в def-файле необходимо дополнительно указать ординалы функций, иначе при загрузке драйвера не будет найдена импортируемая функция (по какой-то причине в таблицу импорта драйвера hyperv3.sys функция попадает с постфиксом @число, даже если в def-файле прописать WinHvGetPartitionProperty@16=WinHvGetPartitionProperty):

WinHvGetPartitionProperty@16 @42
Для создания def-файла используем вывод команды dumpbin:
dumpbin /exports winhv.sys

(В Windows Server 2012 R2 в root-разделе используется драйвер winhvr.sys, поэтому def-файл для работы драйвера в этой ОС необходимо формировать именно с него).

Для сборки 64-битного драйвера правки вносить не нужно.

После правки def-файла необходимо заново сформировать lib-файл с помощью команды (для x86):

"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\lib.exe" /def:D:\hyperv3\winhv.def /OUT:D:\ hyperv3\winhv.lib /machine:x86

Для x64 (выполняется 1 раз для конкретной версии winhv.sys):

"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\amd64\lib.exe" /def:D:\hyperv3\winhv64.def /OUT:D:\hyperv3\winhv64.lib /machine:x64

В приложенном к статье драйвере есть примеры использования некоторых функций winhv.sys.

Попробуем в цикле от 0 до 0x100 последовательно выполнить гипервызов 0x41 (HvInitializePartition), с параметром PartitionID в ECX, равным значению итератора цикла, с установленным Fast битом (для передачи параметров через регистры). При этом в EAX возвращается результат выполнения гипервызова.

{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DBG_PRINT_LEVEL,"i %x VMCALL_EAX %x",i,ARCH_VMCALL_REG_MOD(i));
}
ARCH_VMCALL_REG_MOD PROC param1:DWORD
push esi
push edi
push ebx
xor edx,edx
mov ecx, param1
xor ebx,ebx
xor esi,esi
xor edi,edi
mov eax, 10041h
vmcall
pop ebx
pop edi
pop esi
ret
ARCH_VMCALL_REG_MOD ENDP

В итоге получаем

В случае если в качестве параметра в ecx был передан PartitionID активной виртуальной машины, то гипервизор возвращает результат – 6 (HV_STATUS_ACCESS_DENIED), в остальных случаях – d (HV_STATUS_INVALID_PARTITION_ID). Воспользовавшись этим фактом, а также тем, что ID каждого нового раздела вычисляется простым прибавлением 1 к ID предыдущего раздела, а ID root-partition всегда равен 1, можно установить количество активных виртуальных машин на хосте. Для этого слегка модифицируем код драйвера:

{
res = ARCH_VMCALL_REG_MOD(i);
if (res == HV_STATUS_INVALID_PARTITION_ID){
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DBG_PRINT_LEVEL,"PartitionID %x VMCALL_EAX %x \n",i,res);
}
}
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DBG_PRINT_LEVEL,"Number of active virtual machines: %x \n",counter);

и получим список ID активных разделов и их количество:

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

Эти данные возможно получить по двум следующим причинам:

- При создании раздела PartitionID генерируется простым прибавлением 1 к последнему использованному PartitionID.

- При обработке hypercall’a гипервизор сперва проверяет валидность переданного PartitionID, и только в случае, если был передан PartitionID активного раздела, происходит проверка прав на выполнения гипервызова.

Эту особенность работы гипервизора можно использовать для определения количества виртуальных машин, работающих на заданном хост-сервере. Имя хост-сервера можно подсмотреть в реестре гостевой ОС в разделе HKLM\Software\Microsoft\Virtual Machine\Guest\Parameter, где содержатся данные о хост ОС, передаваемые посредством Key Value Pair Integration Component, который обычно включен по умолчанию. Также перезагрузив контролируемую виртуальную машину во второй понедельник месяца и зафиксировав ее PartitionID (существует довольно высокая вероятность того, что он будет последним в списке активных VM), можно определить, ставят ли виртуальные соседи на свои серверы выходящие каждый второй вторник исправления безопасности. Впрочем, в реальности довольно сложно представить, что кому-то понадобится такая информация…

Такое поведение гипервизора можно было наблюдать и в версии 6.3.9431.0 (Windows Server 2012 R2 Preview), однако Microsoft признала это поведение как "unexpected behavior" и устранила его в сборке 6.3.9600.16384.

Обработка выполнения инструкции vmcall в гипервизоре выполняется примерно следующим образом:

- производится проверка кольца защиты, в котором была выполнена инструкция, если инструкция выполнялась в ring3, то обработка прекращается;

- если инструкция выполнялась в ring0, то производится проверка, работал ли при этом процессор в LongMode.

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

- в каждой процедуре загружается указатель на массив структур, содержащих параметры, необходимые для обработки каждого из гипервызовов от 0 до 8C (расшифровка кодов гипервызовов указана в Hypervisor Top-Level Functional Specification: Windows Server 2012. Appendix B: Hypercall Code Reference). Одним из элементов каждой структуры является указатель на процедуру обработки гипервызова:

- затем происходит проверка, каким способом гипервизору были переданы параметры, через память, или через регистры (в этом случае fast call бит в EAX перед гипервызовом должен быть равен 1).

- далее происходит вызов соответствующей функции.

Для сравнения приведены некоторые значение полей VMCS, полученные при помощи скрипта display-vmcs.py после VM exit:

Root partition

Child partition

CPU_BASED_VM_EXEC_CONTROL = 0xb6206dfa

Use TSC offsetting

HLT exiting

MWAIT exiting

RDPMC exiting

Use TPR shadow

Use I/O bitmaps

Use MSR bitmaps

MONITOR exiting

Activate secondary controls

IO_BITMAP_A = 0x4e06000

IO_BITMAP_A_HIGH = 0x0

IO_BITMAP_B = 0x4e07000

IO_BITMAP_B_HIGH = 0x0

EXCEPTION_BITMAP = 0x40000

MSR_BITMAP = 0x4e08000

MSR_BITMAP_HIGH = 0x0

PIN_BASED_VM_EXEC_CONTROL = 0x1f

External-interrupt exiting

NMI exiting

SECONDARY_VM_EXEC_CONTROL = 0x2a

Enable EPT

Enable RDTSCP

Enable VPID

VM_ENTRY_CONTROLS = 0x13ff

Load debug controls

IA-32e mode guest

VM_EXIT_CONTROLS = 0x3efff

Save debug controls

Host address space size

Acknowledge interrupt on exit

CPU_BASED_VM_EXEC_CONTROL = 0xb5a06dfa

Use TSC offsetting

HLT exiting

MWAIT exiting

RDPMC exiting

Use TPR shadow

MOV-DR exiting

Unconditional I/O exiting

Use MSR bitmaps

MONITOR exiting

Activate secondary controls

CR0_GUEST_HOST_MASK = 0xffffffe1

CR0_READ_SHADOW = 0x8001003b

CR4_GUEST_HOST_MASK = 0xfffff874

CR4_READ_SHADOW = 0x406f8

EXCEPTION_BITMAP = 0x40000

GUEST_CR0 = 0x8001003b

GUEST_CR3 = 0x185000

GUEST_CR4 = 0x426f9

GUEST_RIP = 0x839b1000

GUEST_RSP = 0x8870f8a4

HOST_CR0 = 0x80010031

PIN_BASED_VM_EXEC_CONTROL = 0x1f

External-interrupt exiting

NMI exiting

SECONDARY_VM_EXEC_CONTROL = 0x62

Enable EPT

Enable VPID

WBINVD exiting

VM_ENTRY_CONTROLS = 0x11ff

Load debug controls

VM_EXIT_CONTROLS = 0x3efff

Save debug controls

Host address space size

Acknowledge interrupt on exit

Например, можно увидеть, что для guest-partition гипервизор обрабатывает весь ввод/вывод (Unconditional I/O exiting), а для root-partition контролирует лишь определенные порты (Use I/O bitmaps).

WINDBG>!dc 0x4e06000 L250 - IO_BITMAP_A
# 4e06000 00000000 00000003 00000000 00000010 ................
# 4e06010 00000000 00000003 00000000 00000000 ................
# 4e06020 00000000 00000000 00000000 00000000 ................
…………………………………………………………………………………………………
# 4e06190 00000000 00000000 00000000 f1000000 ................

Если я не ошибся в расчетах, то для root-partition контролируются порты 20h,21h,44h, A0h, A1h, 1D5Fh, 1D64h, 1D65h, 1D66h, 1D67h.

Заключение

В статье были описаны действия, которые необходимо выполнить для создания стенда для исследования Hyper-V, а также очень кратко описаны некоторые моменты работы гипервизора. Надеюсь, эта информация пригодится начинающим исследователям безопасности гипервизора компании Microsoft.

Файлы к статье размещены по ссылке: http://yadi.sk/d/jJJGTL7xCuFAV

Источники:

1. http://msdn.microsoft.com/en-us/library/Windows/hardware/ff540654(v=vs.85).aspx

2. http://msdn.microsoft.com/en-us/library/cc768520%28v=bts.10%29.aspx

3. http://en.community.dell.com/techcenter/virtualization/w/wiki/3029.aspx

4. http://www.hhdsoftware.com/Downloads/free-virtual-serial-ports

5. http://ww.osronline.com/showthread.cfm?link=234398

6. http://www.microsoft.com/en-us/download/details.aspx?id=39289

7. http://alter.org.ua/docs/nt_kernel/procaddr/

8. http://www.osronline.com/showthread.cfm?link=132065

9. http://blog.cr4.sh/2012/07/vmware-gdb-stub-ida.html

SOC как супергерой: не спит, не ест, следит за безопасностью!

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

Подключитесь к экспертному сообществу!