IT-ИМПУЛЬС
Контакты Меню

Hyper-V sockets internals: исследуем внутренние механизмы работы сокетов Hyper-V

Содержание статьи Хакер — это в первую очередь программист-исследователь, человек, который глубоко интересуется информационными технологиями, старается проникнуть в суть вещей, найти самые неочевидные и недокументированные возможности. Нам, команде «Хакера», не дают покоя лавры Марка Руссиновича (мы про книгу «Внутреннее устройство Microsoft Windows» :)), поэтому посвятим эту огромную статью оригинальному исследованию внутренних механизмов сокетов Hyper-V.

Технология виртуализации Hyper-V, представленная компанией Microsoft довольно давно, получает немало дополнений и улучшений с каждым новым выпуском серверной версии Windows. Так, в Windows Server 2016 был интегрирован новый протокол коммуникаций под названием сокеты Hyper-V, который позволяет подключаться к виртуальным машинам из родительской операционной системы, используя в качестве транспорта не привычный стек TCP/IP, а шину VMBus. Первое ее применение было реализовано в технологии PowerShell Direct. Помимо этого, Microsoft предоставила разработчикам возможность использовать в своих продуктах сокеты Hyper-V, выпустив новую версию SDK для Visual Studio. Прочитав эту статью, ты узнаешь особенности работы сокетов Hyper-V, поймешь, каким образом они используются в технологии PowerShell Direct и как реализовано их взаимодействие с ядром Windows.

 Термины и определения
  • Root-раздел (родительский раздел, root ОС) — Windows Server 2016 с установленным компонентом Hyper-V.
  • Гостевая ОС (дочерний раздел, гостевая ОС, guest ОС) — виртуальная машина c Windows Server 2016 Gen2.
  • Hyper-V TLFS – Hyper-V Top Level Functional Specification.

 Введение

2006 год. Конференция WinHEC. Microsoft активно продвигает свой вариант гипервизора (еще даже без названия, его обозначали просто как Windows hypervisor) и намекает на то, что разработчики смогут создавать свои решения на базе новой технологии виртуализации.







WWW

Предыдущее исследование Hyper-V под названием Исследуем внутренние механизмы работы Hyper-V (в двух частях) читай тут:

Действительно, в этом направлении были сделаны определенные шаги, и у разработчиков появились:

  • заголовочные файлы hvgdk.h, vid.h, VidDefs.h (Windows WDK 6.0, 7.1, Singularity ОС);
  • Hyper-V Top Level Functional Specification (TLFS);
  • документация на MSDN, которая в основном совпадала с TLFS, но содержала более детальную информацию.

На osronline.com архитектор Hyper-V Джейк Ошинс (Jake Oshins) отвечал на вопросы разработчиков драйверов, касающиеся среды Hyper-V.

Все же опубликованной информации было явно недостаточно для того, чтобы кто-то начал разрабатывать новые продукты на базе Hyper-V (вспоминается только LiveCloudKd от moonsols, да и то, похоже, большую часть информации разработчики просто отреверсили). Возможно, в связи с этим политика Microsoft резко изменилась:

  • заголовочные файлы были убраны из WDK;
  • исчезла документация из MSDN, связанная с Hyper-V (на osronline.com для формальности провели опрос, нужна документация или нет);
  • из WinDBG исчезли расширения, связанные с Hyper-V (network virtualization kernel debugger extension nvkd.dll), hvexts.dll, на который ссылался WinDBG при подключении к hvix64.exe (hvax64.exe), так и не был выложен в общий доступ;
  • архитектор Hyper-V Джейк Ошинс исчез с форума osronline.com.

Тем не менее Microsoft самостоятельно стала разрабатывать Linux Integration Services — набор позволяющих запускать Linux внутри Hyper-V компонентов и драйверов, исходные коды которых интегрированы в ядро Linux (2.6.32 и выше) и, соответственно, выложены в открытый доступ.

TLFS оставался единственным источником информации о внутреннем устройстве гипервизора, однако выпущенная в феврале 2017 года спецификация для Windows Server 2016 содержит уже 238 страниц, а не 420, как это было в предыдущей спецификации для Windows Server 2012 R2 (из 23 разделов осталось 16, исчезли описания многих гипервызовов, однако появились два раздела, описывающих работу VSM (Virtual Secure Mode) и вложенной виртуализации).

Но в 2016 году заголовочный файл ws2def.h (core definitions for the Winsock2 specification) в Windows SDK 10.0.10586 дополнили строчкой

#define AF_HYPERV 34

а в Windows SDK 10.0.14393 появился файл HvSocket.h. В каких же целях это было сделано?

В Windows Server 2016 добавили новую функцию — PowerShell Direct, которая позволяет выполнять PowerShell-команды в гостевой операционной системе без сетевого соединения, передавая все необходимые данные через шину VMBus. Этот механизм работает, используя так называемые сокеты Hyper-V, которые были интегрированы в сетевой стек Windows.

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

Рассмотрим, каким образом подсистема виртуализации была интегрирована с сетевым стеком Windows, затем разберем работу приложения, использующего сокеты Hyper-V, и узнаем, как устроена технология PowerShell Direct. Windows 10 тоже поддерживает такие сокеты, но далее она практически не рассматривается — акцент сделан именно на серверную ОС, хотя предположительно существенной разницы в реализации быть не должно.

Перед прочтением статьи рекомендуем ознакомиться с разделом 7 «Сеть» книги «Внутреннее устройство Microsoft Windows», 6-е издание, часть 1 (в 5-м издании книги этот раздел еще не был опубликован), а также со статьей «Сетевой программный интерфейс Windows Vista/2008: внутреннее устройство, использование и взлом», ранее доступной на wasm.ru. Теперь же ее можно найти на различных сайтах (например, тут). MSDN довольно подробно освещает затронутые в статье темы, но без привязки к сокетам Hyper-V.

 Компоненты операционной системы

Сперва посмотрим список провайдеров (Layered Service Provider — LSP), установленных в операционной системе. Видим две записи, в имени которых содержится Hyper-V.




PS C:\Windows\system32> netsh winsock show catalog Winsock Catalog Provider Entry ------------------------------------------------------ Entry Type: Base Service Provider Description: Hyper-V RAW Provider ID: {1234191B-4BF7-4CA7-86E0-DFD7C32B5445} Provider Path: %SystemRoot%\system32\mswsock.dll Catalog Entry ID: 1001 Version: 2 Address Family: 34 Max Address Length: 36 Min Address Length: 36 Socket Type: 1 Protocol: 1 Service Flags: 0x20026 Protocol Chain Length: 1

Если расшифровать Service Flags в соответствии с описанием структуры WSAPROTOCOL_INFO из MSDN, то получим 0x20026 = XP1_GUARANTEED_DELIVERY | XP1_GUARANTEED_ORDER | XP1_GRACEFUL_CLOSE | XP1_IFS_HANDLES.

В реестре, соответственно, для каждого провайдера (32-битного и 64-битного) создано по одному разделу:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters\Protocol_Catalog9\Catalog_Entries\000000000001 и HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters\Protocol_Catalog9\Catalog_Entries64\000000000001

В разделе HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Winsock\Parameters был добавлен транспорт VMBus (irda и RFCOMM в Windows Server 2016 в инсталляции по умолчанию отсутствуют и есть только в Windows 10).


Ключ HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\vmbus\Parameters содержит подраздел Winsock с параметром HelperDllName, в котором указано имя библиотеки wshhyperv.dll, подгружаемой основным провайдером mswsock.dll на этапе создания сокета.




В 6-м издании книги Windows Internals написано, что «транспортный протокол RAW не является настоящим протоколом и не осуществляет никакой инкапсуляции пользовательских данных. Это позволяет клиенту непосредственно контролировать содержимое фреймов, отправляемых и получаемых по сетевому интерфейсу». В нашем случае используется протокол Hyper-V RAW. Однако, несмотря на то что в названии протокола содержится RAW, при вызове функции socket в качестве второго ее параметра (тип сокета) указывается SOCK_STREAM (stream socket), хотя в WinSock2.h присутствует отдельный тип сокета — SOCK_RAW.

Появился новый NPI-провайдер — hvsocket.sys. Он присутствует в импорте драйверов vmbus.sys, vmbusr.sys и netio.sys и загружается вместе с первым из указанных модулей. Регистрируется как провайдер драйвером vmbusr.sys при вызове импортированной функции hvsocket!HvSocketProviderStart, которая, в свою очередь, вызывает netio!NmrRegisterProvider. Подробно работа с провайдерами была описана в упомянутой выше статье с WASM.

С помощью WinDBG можно получить список всех регистрируемых провайдеров и их клиентов. Для провайдеров достаточно написать скрипт (ставим bp на netio!NmrRegisterProvider и записываем параметры в лог):

__Windbg> bu netio!NmrRegisterProvider__ __Windbg>.logopen D:\ida_files\2016\log.txt__ __Windbg>bp netio!NmrRegisterProvider ".echo \**********bp netio!NmrRegisterProvider\********; .echo kc;kc; .echo dps rcx;dps rcx; .echo NpiId GUID; dt _GUID poi(rcx+28h); .echo NPI_MODULEID_TYPE GUID; dt _GUID poi(rcx+30h)+8; g"__ NTSTATUS NmrRegisterProvider( _In_  PNPI_PROVIDER_CHARACTERISTICS ProviderCharacteristics, _In_  PVOID                         ProviderContext, _Out_ PHANDLE                       NmrProviderHandle );
typedef struct _NPI_REGISTRATION_INSTANCE { USHORT        Version; USHORT        Size; PNPIID        NpiId; PNPI_MODULEID ModuleId; ULONG         Number; const VOID    *NpiSpecificCharacteristics; } NPI_REGISTRATION_INSTANCE, *PNPI_REGISTRATION_INSTANCE; typedef struct _NPI_MODULEID { USHORT            Length; NPI_MODULEID_TYPE Type; union { GUID Guid; LUID IfLuid; }; } NPI_MODULEID, *PNPI_MODULEID;

Регистрация hvsocket.sys как провайдера будет выглядеть так:

__kc__ # Call Site 00 NETIO!NmrRegisterProvider 01 hvsocket!HvSocketProviderStart 02 vmbusr!RootDeviceAdd 03 Wdf01000!FxDriverDeviceAdd::Invoke 04 Wdf01000!FxDriver::AddDevice 05 nt!PpvUtilCallAddDevice 06 nt!PnpCallAddDevice 07 nt!PipCallDriverAddDevice 08 nt!PipProcessDevNodeTree 09 nt!PiProcessStartSystemDevices 0a nt!PnpDeviceActionWorker 0b nt!ExpWorkerThread 0c nt!PspSystemThreadStartup 0d nt!KiStartSystemThread __dps rcx__ fffff806`0dece010 00000000`00480000 fffff806`0dece018 fffff806`0ded1640 hvsocket!HvSocketNotifyAttachClient - ProviderAttachClient fffff806`0dece020 fffff806`0ded18c0 hvsocket!HvSocketNotifyDetachClient - ProviderDetachClient fffff806`0dece028 fffff806`0ded19a0 hvsocket!HvSocketNotifyCleanupClientContext - ProviderCleanUpBindingContext fffff806`0dece030 00000000`00280000 - Begin of NPI_REGISTRATION_INSTANCE (Version+Size) fffff806`0dece038 fffff806`0decc3e0 hvsocket!NPI_TRANSPORT_LAYER_ID - pointer to NpiId (GUID NPIID) - dt _GUID poi(rcx+28h) fffff806`0dece040 fffff806`0decc3f0 hvsocket!NPI_MS_VMBUS_MODULEID - pointer to ModuleId - dt _GUID poi(rcx+30h)+8 fffff806`0dece048 00000000`00000000 - Number fffff806`0dece050 fffff806`0decc2e0 hvsocket!VmbusTlProviderCharacteristics - NpiSpecificCharacteristics fffff806`0dece058 00000000`00000000 fffff806`0dece060 00000500`00000000 fffff806`0dece068 0000ef8b`4509d61c fffff806`0dece070 00000000`00000000 fffff806`0dece078 00000000`00000000 fffff806`0dece080 00000000`00000000 fffff806`0dece088 fffff803`6c322884 nt!EtwRegisterClassicProvider __NpiId GUID__ ntdll!_GUID {2227e804-8d8b-11d4-abad-009027719e09} +0x000 Data1 : 0x2227e804 +0x004 Data2 : 0x8d8b +0x006 Data3 : 0x11d4 +0x008 Data4 : [8] "???" NPI_MODULEID_TYPE GUID ntdll!_GUID {eb004a27-9b1a-11d4-9123-0050047759bc} +0x000 Data1 : 0xeb004a27 +0x004 Data2 : 0x9b1a +0x006 Data3 : 0x11d4 +0x008 Data4 : [8] "???"

Аналогично выполняется логирование регистрации NPI-клиентов. Единственное отличие — bp нужно ставить на netio!NmrRegisterClient. Интересно, что регистрация hvsocket.sys как клиента нигде не замечена. Единственные регистрируемые компоненты виртуализации — это NDIS!NPI_NDIS_VBUS_INTERFACE_ID (регистрация идет из NDIS!DriverEntry) и vmswitch!NPI_PKTCAP_INTERFACE_ID (регистрация из vmswitch!DriverEntry).

Полный список провайдеров и клиентов, регистрируемых Windows Server 2016, размещен на GitHub. Список провайдеров предоставлен в следующем формате.


В afd.sys есть функция afd!AfdTlNotifyAttachProvider (client module’s ClientAttachProvider callback function), которая работает со структурой AfdTlTransportListHead. Небольшой скрипт для pykd, который выводит часть элементов address family и функцию обработки каждого элемента:

__2: kd> !py D:\afd_parse_AfdTlTransportListHead.py__ cs:AfdTlTransportListHead address is 0xffffb089b4579040L ----Address family 0x0 [ AF_UNSPEC ] --Dispatch function tcpip!TcpTlProviderDispatch ----Address family 0x0 [ AF_UNSPEC ] --Dispatch function tcpip!UdpTlProviderDispatch ----Address family 0x0 [ AF_UNSPEC ] --Dispatch function tcpip!RawTlProviderDispatch ----Address family 0x22 [ AF_HYPERV ] --Dispatch function hvsocket!VmbusTlProviderDispatch

В принципе, аналогичную информацию должна выводить команда afd плагина mex для WinDBG, но на моем стенде она по какой-то причине не сработала (возможно, необходимы private symbols).

При старте vmbusr.sys видим запуск hvsocket!HvSocketProviderStart, после которой вызывается afd!AfdTlNotifyAttachProvider:

__0>kc__ # Call Site 00 NETIO!NmrClientAttachProvider 01 afd!AfdTlNotifyAttachProvider 02 NETIO!NmrpProposeAttachment 03 NETIO!NmrpAttachArray 04 NETIO!NmrpRegisterModule 05 NETIO!NmrRegisterProvider 06 hvsocket!HvSocketProviderStart 07 vmbusr!RootDeviceAdd

В hvsocket!HvSocketProviderStart происходит вызов следующих функций:

  • netio!NetioInitializeWorkQueue;
  • netio!NmrRegisterProvider;
  • hvsocket.sys — NPI-провайдер.

В netio!NmrpVerifyModule к упомянутым в статье с WASM проверкам добавился драйвер hvsocket.sys.

RtlInitString(&DestinationString, "\\systemroot\\system32\\drivers\\afd.sys"); RtlInitString(&v26, "\\systemroot\\system32\\drivers\\tdx.sys"); RtlInitString(&v24, "\\systemroot\\system32\\drivers\\tcpip.sys"); RtlInitString(&v27, "\\systemroot\\system32\\drivers\\hvsocket.sys");

С помощью скрипта afd_parse_AfdEndpointListHead_pykd.py в системе можно просмотреть список объектов, создаваемых при открытии каждого сокета. Если сокет закрывается, то объект исчезает из списка. Скрипт, в принципе, отображает то же самое, что и утилита tcpconnect из Sysinternals Suite (но она, к сожалению, не показывает открытые Hyper-V сокеты) или WinDBG-скрипт для отображения AFD endpoints, с дополнительным выводом драйвера и процедуры, обрабатывающей операции с сокетом, а также имени процесса и PID.

Например, содержимое списков в гостевой и root ОС после успешного выполнения командлета Enter-PSSession:

__kd> !py C:\Tools\Scripts\afd_parse_AfdEndpointListHead_pykd.py — в гостевой ОС__ afd!AfdEndpointListHead address is 0xfffff80490ef74e0L ----AfdEndpoint 0xfffff80490ef74e0L 0xda10 ----AfdEndpoint 0xffffd00d64e7c130L 0xafd2 tcpip!TcpTlProviderEndpointDispatch explorer.exe 0x3c0 ----AfdEndpoint 0xffffd00d6421af60L 0xafd2 hvsocket!VmbusTlProviderEndpointDispatch powershell.exe 0x920 ----AfdEndpoint 0xffffd00d64846ea0L 0xafd4 hvsocket!VmbusTlProviderListenDispatch powershell.exe 0x920 ----AfdEndpoint 0xffffd00d64d082a0L 0xafd1 tcpip!UdpTlProviderEndpointDispatch lsass.exe 0x204 ----AfdEndpoint 0xffffd00d642b8960L 0xafd1 tcpip!UdpTlProviderEndpointDispatch lsass.exe 0x204 ----AfdEndpoint 0xffffd00d640c8ba0L 0xafd4 hvsocket!VmbusTlProviderListenDispatch svchost.exe 0x35c (в состав процесса входит служба vmicsession) ----AfdEndpoint 0xffffd00d650c3c30L 0xaafd 0 explorer.exe 0x3c0 ----AfdEndpoint 0xffffd00d64340f60L 0xaafd 0 explorer.exe 0x3c0 __kd> !py D:\ida_files\afd_parse_AfdEndpointListHead_pykd.py — в родительской ОС__ afd!AfdEndpointListHead address is 0xfffff807668b74e0L ----AfdEndpoint 0xfffff807668b74e0L 0xe4a0 ----AfdEndpoint 0xffff958f202eb300L 0xafd2 hvsocket!VmbusTlProviderEndpointDispatch powershell.exe 0xcc4L ----AfdEndpoint 0xffff958f21d7eac0L 0xafd1 tcpip!UdpTlProviderMessageDispatch svchost.exe 0x438L ----AfdEndpoint 0xffff958f207dd9e0L 0xafd2 hvsocket!VmbusTlProviderEndpointDispatch powershell_ise 0x394L ----AfdEndpoint 0xffff958f1fbc5f60L 0xafd1 tcpip!UdpTlProviderMessageDispatch svchost.exe 0x498L

Что интересно, мы можем увидеть еще один сокет, создаваемый процессом svchost.exe на ранних этапах загрузки операционной системы:

__kd> !py D:\ida_files\ParseAfdEndpointListHead.py__ --------------------------------------------------------------------------------------------- ----AfdEndpoint 0xffffd1851637e130L 0xafd0 tcpip!TcpTlProviderEndpointDispatch svchost.exe 0x410L ----AfdEndpoint 0xffffd1851627ed60L 0xafd0 hvsocket!VmbusTlProviderEndpointDispatch svchost.exe 0x384L ----AfdEndpoint 0xffffd185161d7330L 0xafd0 tcpip!TcpTlProviderEndpointDispatch wininit.exe 0x2a4L

Этот сокет создается сервисом RPC, а именно функцией RPCRT4!TransportProtocol::HandlePnPStateChange. Стек:

__kd> k — bp on wshhyperv.dll load__ 13 mswsock!SockGetTdiName+0x2b1 14 mswsock!SockSocket+0x117 15 mswsock!WSPSocket+0x220 16 WS2_32!WSASocketW+0x1f0 17 RPCRT4!TransportProtocol::OpenAddressChangeRequestSocket+0x43 18 RPCRT4!TransportProtocol::VerifyProtocolIsFunctional+0x14 19 RPCRT4!TransportProtocol::HandleProtocolChange+0x100 1a RPCRT4!TransportProtocol::HandlePnPStateChange+0x72 1b RPCRT4!ProcessNewAddressEvent+0x21 1c RPCRT4!COMMON_AddressChangeThreadPoolCallback+0x25 1d KERNELBASE!BasepTpIoCallback+0x50 1e ntdll!TppIopExecuteCallback+0x118 1f ntdll!TppWorkerThread+0x8ed 20 KERNEL32!BaseThreadInitThunk+0x14 21 ntdll!RtlUserThreadStart+0x21

В функции RPCRT4!TransportProtocol::HandlePnPStateChange вызывается ws2_32!WSAEnumProtocols, по результатам которой производится перебор протоколов в таблице rpcrt4!TransportProtocolArray. Для каждого элемента таблицы вызывается TransportProtocol::HandleProtocolChange (второй параметр — структура WSAPROTOCOL_INFOW). Размер каждого элемента TransportProtocolArray — 72 байта. Но тип этого сокета — 0xafd0. Структура _AFD_CONNECTION, описывающая состояние такого сокета, до конца не заполнена, и по смещению +e0 от начала размещения структуры есть только нули. Чтобы была возможность подключаться к сокетам Hyper-V, его тип должен быть по крайней мере 0xafd2.

В символах rpcrt4.dll присутствует достаточно много функций, работающих с сокетами Hyper-V.

HVSOCKET_QueryClientID HVSOCKET_BuildAddressVector HVSOCKET_Open HVSOCKET_QueryClientAddress HVSOCKET_REsolveAddress HVSOCKET_ResolveVmId HVSOCKET_ServerListen HVSOCKET_SetSocketOption

Цели добавления поддержки в библиотеку RPC неизвестны. В соответствии с MSDN PowerShell Direct должен работать локально, да и фактически оказывается, что RPC он не использует.


Возможно, эти функции необходимы для работы среды в контейнерах Docker либо для будущей поддержки удаленной работы PowerShell Direct.

 Работа сокетов Hyper-V

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

В соответствии с MSDN сокеты Hyper-V поддерживают следующие вызовы: socket, bind, connect, send, listen, accept.


Однако на практике видно, что поддерживается большее число команд.

Серверная часть приложения выполняет socket, bind, listen, accept, recv, closesocket.

Клиентская часть — socket, connect, send, recv, shutdown, closesocket.

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

В целом логика взаимодействия (с точки зрения взаимодействия с ядром) выглядит следующим образом.










Разберем основные функции на примере приложения. Какие-то отличия по одинаковым вызовам отметим по ходу разбора, а некоторые особенности клиентской части будут отражены далее при разборе PowerShell Direct.

В сокетах Hyper-V нет IP-адресов, зато есть заранее определенные GUID’ы.


В нашем случае мы возьмем HV_GUID_PARENT. Второй GUID, который нам понадобится, — это специально сгенерированный нами GUID для сервиса PowerShell. Для этого мы запускаем PowerShell-скрипт следующего содержания:

$friendlyName = "HV Socket Application" # Create a new random GUID and add it to the services list then add the name as a value $service = New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices" -Name ((New-Guid).Guid) $service.SetValue("ElementName", $friendlyName) # Copy GUID to clipboard for later use Write-Host "Service GUID: " $service.PSChildName

и запоминаем полученный GUID. Но, в принципе, можно использовать существующие GUID’ы, которые уже созданы на этапе установки Windows в том же разделе реестра:

HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices

VM Session Service 1 и VM Session Service 2 используются для работы PowerShell Direct (второй GUID — до внедрения механизма Hyper-V socket duplication. Если в рамках одной и той же PowerShell-сессии с помощью New-PSSession открывается два соединения, то используются два GUID’а).


Если попытаться открыть три соединения сразу, то встретишь ошибку.


и откроется всего две сессии. При отправке сообщений гостевой ОС мы можем увидеть оба GUID’а.




Но у нас будет всего один канал для коммуникации, и, соответственно, необходим один GUID: B1D00D3E-FE10-4570-AD62-7648779D7A1B.

int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);

Вызов WSAStartup пропустим, так как он не имеет специфических параметров для работы с сокетами Hyper-V, перейдем сразу к функции socket.

 Socket

Каким образом происходит заполнение параметров, посмотри в исходном коде приложения ServerExample, а мы перейдем непосредственно к вызову соответствующей API-функции.

ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_HYPERV; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = HV_PROTOCOL_RAW; ListenSocket = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);

Код скомпилируется в следующее.


Из ws2_32!WSASocketA вызывается ws2_32!WSASocketW, из которой затем вызывается ws2_32!DPROVIDER__Initialize.

__WINDBG>dc poi(esp+4) L100 — второй параметр функции DPROVIDER::Initialize (значение реестра Protocol_Catalog9\Catalog_Entries\000000000001)__ 0122b48c 00020026 00000000 00000000 00000000 &............... 0122b49c 00000008 1234191b 4ca74bf7 d7dfe086 ......4..K.L.... 0122b4ac 45542bc3 000003e9 00000001 00000000 .+TE............ 0122b4bc 00000000 00000000 00000000 00000000 ................ 0122b4cc 00000000 00000000 00000002 00000022 ............"... 0122b4dc 00000024 00000024 00000001 00000001 $...$........... 0122b4ec 00000000 00000000 00000000 00000000 ................ 0122b4fc 00000000 00790048 00650070 002d0072 ....H.y.p.e.r.-. 0122b50c 00200056 00410052 00000057 00000000 V. .R.A.W....... 0122b51c 00000000 00000000 00000000 00000000 ................

Далее видим инициализацию указателей на вспомогательные функции.


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

Далее с помощью LoadLibraryEx загружается mswsock.dll, затем GetProcAddress возвращает адрес функции mswsock!WSPStartup, после чего выполнение передается на эту функцию. Внутри выполняются mswsock_initialize, затем ws32SQMinit, WahCreateContextTable.

После завершения процедуры mswsock!WSPStartup вызывается процедура mswsock!WSPSocket (через call esi), из которой вызывается функция mswsock!socksocket, а из нее mswsock!sockGetTdiName, при этом первым параметром идет

__WINDBG>dtx _GUID poi(esp+4)__ (*((_GUID *)0xf6f4a0)) : {1234191B-4BF7-4CA7-86E0-DFD7C32B5445} [Type: _GUID] __WINDBG> dc poi(esp+4)__ 00f6f4a0 1234191b 4ca74bf7 d7dfe086 45542bc3 ..4..K.L.....+TE — GUID Hyper-V RAW (тот, что выводит netsh)

Затем вызывается mswsock!SockLoadTransportList, которая считывает значение раздела реестра:

SYSTEM\CurrentControlSet\Services\Winsock\Parameters\Transports

Возвращаются следующие значения:

__WINDBG>dc @ebx — (в ebx указатель на блок памяти, переданный mswsock!SockLoadTransportList)__ 01224ee0 006d0076 00750062 00000073 00730050 v.m.b.u.s...P.s. 01224ef0 00680063 00640065 00540000 00700063 c.h.e.d...T.c.p. 01224f00 00700069 00540000 00700063 00700069 i.p...T.c.p.i.p. 01224f10 00000036 abab0000 abababab feeeabab 6..............

Вызывается mswsock!SockLoadHelperDll, запрашивается значение HKLM\System\CurrentControlSet\Services\vmbus\Parameters\Winsock\HelperDllName, и загружается библиотека C:\Windows\SysWoW64\wshhyperv.dll (приложение ServerExample скомпилировано как 32-битное).

При возврате из mswsock!SockGetTdiName возвращается wshhyperv!WSHOpenSocket2, которая содержит только проверки правильности передачи параметров сокета.


Далее последовательно GetCurrentProcess\OpenProcessToken и затем GetTokenInformation. Можем увидеть, что в качестве _TOKEN_INFORMATION_CLASS передается 0x1D:

__WINDBG>dt _TOKEN_INFORMATION_CLASS @esp+8__ combase!_TOKEN_INFORMATION_CLASS 1d ( TokenIsAppContainer ) — похоже на адаптацию сокетов для приложений, скомпилированных с опцией \APPCONTAINER

После этого результат записывается в переменную mswsock!SockIsAppContainter. Видна инициализация строки \Device\Afd\Endpoint, которая передается ntdll!NtCreateFile.

__WINDBG>dtx OBJECT_ATTRIBUTES poi(@esp+0x8) (третий параметр ntdll!NtCreateFile)__ (*((OBJECT_ATTRIBUTES *)0xf6f348)) [Type: OBJECT_ATTRIBUTES] [+0x000] Length : 0x18 [Type: unsigned long] [+0x004] RootDirectory : 0x0 [Type: void *] [+0x008] ObjectName : 0xf6f33c : "\Device\Afd\Endpoint" [Type: _UNICODE_STRING *] [+0x00c] Attributes : 0x42 [Type: unsigned long] [+0x010] SecurityDescriptor : 0x0 [Type: void *] [+0x014] SecurityQualityOfService : 0x0 [Type: void *] __WINDBG>dtx _IO_STATUS_BLOCK @esp+0xc -r (четвертый параметр ntdll!NtCreateFile)__ (*((_IO_STATUS_BLOCK *)0xf6f30c)) [Type: _IO_STATUS_BLOCK] — неинициализированная структура [+0x000] Status : 16184168 [Type: long] [+0x000] Pointer : 0xf6f368 [Type: void *] [+0x004] Information : 0x0 [Type: unsigned long] — после выполнения будет возвращен статус операции (FILE_CREATED, FILE_OPENED, FILE_OVERWRITTEN, FILE_SUPERSEDED, FILE_EXISTS, FILE_DOES_NOT_EXIST)

Далее выполнение переходит в ядро драйвера afd.sys. При инициализации этот драйвер регистрирует обработчики IRP:

__WINDBG>!drvobj afd 2__ Driver object (ffffda8527de19c0) is for: \Driver\AFD DriverEntry: fffff803db38a000 afd!GsDriverEntry DriverStartIo: 00000000 DriverUnload: fffff803db34c380 afd!AfdUnload AddDevice: 00000000 Dispatch routines: [00] IRP_MJ_CREATE fffff803db357e90 afd!AfdDispatch [01] IRP_MJ_CREATE_NAMED_PIPE fffff803db357e90 afd!AfdDispatch [02] IRP_MJ_CLOSE fffff803db357e90 afd!AfdDispatch [03] IRP_MJ_READ fffff803db357e90 afd!AfdDispatch [04] IRP_MJ_WRITE fffff803db357e90 afd!AfdDispatch __WINDBG>kn__ Child-SP RetAddr Call Site 00 afd!AfdDispatch 01 nt!IopParseDevice+0x1655 02 nt!ObpLookupObjectName+0x8b2 03 nt!ObOpenObjectByNameEx+0x1dd 04 nt!IopCreateFile+0x3d9 05 nt!NtCreateFile+0x79 06 nt!KiSystemServiceCopyEnd+0x13 07 ntdll!NtCreateFile+0x14

Соответственно, после вызова ntdll!NtCreateFile попадем в Afd!AfdDispatch. Первый параметр обработчика:

__WINDBG>!devobj @rcx__ Device object (ffffda8527de29d0) is for: Afd \Driver\AFD DriverObject ffffda8527de19c0 Current Irp 00000000 RefCount 79 Type 00000011 Flags 00000050 Dacl ffffcb8a7e8ccd11 DevExt 00000000 DevObjExt ffffda8527de2b20 ExtensionFlags (0x00000800) DOE_DEFAULT_SD_PRESENT Characteristics (0x00020000) FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL Device queue is not busy. __WINDBG>!devstack ffffda8527de29d0 — в стеке только одно устройство__ !DevObj !DrvObj !DevExt ObjectName > ffffda8527de29d0 \Driver\AFD 00000000 Afd

Второй параметр Afd!AfdDispatch — это IRP.

__WINDBG>!irp @rdx__ Irp is active with 4 stacks 4 is current (= 0xffffda8529720378) No Mdl: System buffer=ffffda8527088910: Thread ffffda8528714080: Irp stack trace. cmd flg cl Device File Completion-Context >[IRP_MJ_CREATE(0), N/A(0)] 0 0 ffffda8527de29d0 ffffda8528bc3550 00000000-00000000 \Driver\AFD Args: ffff908026e7b5d0 03000020 00030000 00000039

Можем убедиться, что пакет отправлен приложением:

__WINDBG>!thread ffffda8528714080__ THREAD ffffda8528714080 Cid 0f74.0ca8 Teb: 0000000000ddf000 Win32Thread: ffffda85272b94e0 RUNNING on processor 0 IRP List: ffffda85297201d0: (0006,0310) Flags: 00000884 Mdl: 00000000 Not impersonating DeviceMap ffffca89863843b0 Owning Process ffffda8527121080 Image: ServerExample.exe Attached Process N/A Image: N/A Wait Start TickCount 416475 Ticks: 1 (0:00:00:00.015) Context Switch Count 3006 IdealProcessor: 0

Далее вызывается Afd!AfdCreate. Первый параметр — все тот же IRP. Далее — Afd!AfdCheckTDIFilter.

__WINDBG>r — параметры Afd!AfdCheckTDIFilter__ rcx=0000000000000001 rdx=0000000000000022 (Address Family - AF_HYPERV) r8=0000000000000001 r9=0000000000000000

В ней производится поиск семейства адресов AF_HYPERV, который был передан в качестве параметра, в структуре AfdTdiMapping (шесть элементов, размер элемента 20h байт). Структура содержит ссылки на стандартные сетевые устройства Windows:

\\Device\\Tcp \\Device\\Tcp6 \\Device\\Udp \\Device\\Udp6 \\Device\\RawIp \\Device\\RawIp6

Ни одно из этих устройств не используется для AF_HYPERV. Возвращается указатель на структуру AfdTdiMapping. Далее — Afd!AfdAllocateEndpoint, из которой вызывается Afd!AfdTlFindAndReferenceTransport.

rcx=0000000000000022 (Address Family - AF_HYPERV) rdx=000000000000001 r8=0000000000000001 r9=0000000000000001

В этой функции идет работа со структурой AfdTlTransportListHead. Она содержит связанный список указателей на объекты транспортов, инструкцией mov rbx,[rbx] происходит загрузка следующего элемента, и выполняется сравнение семейства адресов AF_HYPERV (0x22) с [rbx+16h]; если совпадет, то функция вернет адрес структуры:

__WINDBG>dps @rax__ ffffda85`27f6b8c0 fffff803`db337530 afd!AfdTlTransportListHead ffffda85`27f6b8c8 ffffda85`27dfeca0 ffffda85`27f6b8d0 00220000`00000006 ffffda85`27f6b8d8 00000001`00000001 ffffda85`27f6b8e0 ffffda85`27f6ba80 ffffda85`27f6b8e8 fffff803`db95c000 hvsocket!VmbusTlProviderDispatch ffffda85`27f6b8f0 11d49b1a`eb004a27 ffffda85`27f6b8f8 bc597704`50002391 ffffda85`27f6b900 ffffda85`27f6b96c ffffda85`27f6b908 00000000`00000000 ffffda85`27f6b910 62524d4e`02080006 ffffda85`27f6b918 8ab20db4`3a180386 __WINDBG>dt _GUID @rax+30h — содержит GUID NPI_MS_VMBUS_MODULEID__ ServerExample!_GUID {eb004a27-9b1a-11d4-9123-0050047759bc}

Далее вызывается afd!PplGenericAllocationFunction (выделение необходимой памяти), после заполнения необходимых структур вызывается nt!NtAllocatePoolEx. Затем вызывается nt!ObjDerefernceObject, при этом в rcx загружается указатель на процесс (ServerExample.exe).

__WINDBG>!object @rcx__ Object: ffffda8527121080 Type: (ffffda8527096f20) Process ObjectHeader: ffffda8527121050 (new version) HandleCount: 8 PointerCount: 207781

Далее увеличивается на единицу глобальная переменная AfdEndpointsOpened. На момент отладки

__WINDBG>dd afd!AfdEndpointsOpened L4__ fffff803`db3378e8 000009af 00000000 00000000 00000000

Идет проверка AfdEndpointListHead, не пустая ли она.


Затем в эту структуру вставляется новый элемент


AfdEndpointListHead, как мы видели ранее, содержит созданные объекты сокетов.

В принципе, основная функция afd!AfdAllocateEndpoint заключается в создании нового элемента типа _AFD_CONNECTION и его добавлении в массив AfdEndpointListHead. Затем меняется состояние сокета (записывается 0AFD).


Константы AFD, AFD1, AFD2, AFD4, AFD8, AAFD и подобные служат индикаторами состояния соединения. Но похоже, что полного соответствия с RFC 793, где описываются возможные состояния сокетов (LISTEN, SYN-SENT, SYN-RECEIVED, ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT), нет. Также байт слева от константы меняется в зависимости от типа вызова 0001afd0. Например, после вызова bind его значение станет равно трем.

__WINDBG>dc @rdi — указатель на структуру _AFD_CONNECTION__ ffffda85`2943e9e0 0001afd0 00000000 00000100 00000000 ................ ffffda85`2943e9f0 00000000 00000000 00000000 00000000 ................ ffffda85`2943ea00 00000000 00000000 27121080 ffffda85 ...........'....

Далее вызывается hvsocket!VmbusTlEndpointIsPrivileged, затем afd!AfdTLCreateEndpoint

__WINDBG>r__ rcx= ffffda852943e9e0 — указатель на объект _AFD_CONNECTION, с которым производится работа rdx=0000000000000022 — Address Family r8=0000000000000001 — Hyper-V RAW r9=ffffda8527f6b8c0 — указатель на один из элементов AfdTlTransportListHead

В этой функции в стеке обнуляются 50h байт и затем размещаются параметры для вызываемой функции hvsocket!VmbusTlProviderEndpoint:

__WINDBG>dps @rsp+20__ ffff9080`26e7b250 fffff803`db362930 afd!AfdTLCreateEndpointComplete ffff9080`26e7b258 ffffda85`297201d0 — IRP ffff9080`26e7b260 00000000`00000000 ffff9080`26e7b268 00000001`00010022 ffff9080`26e7b270 ffffda85`2943e9e0 — указатель на _AFD_CONNECTION struct ffff9080`26e7b278 ffffda85`27121080 — Process Object (it not really need. In hvsocket if it eq zero driver calls PsGetCurrentProcess) ffff9080`26e7b280 ffffda85`28714080 — pointer to THREAD structure of ServerExmple.exe process (if it zero later will be called ndis!NdisGetProcessObjectCompartmentId before in rcx load Process struct) ffff9080`26e7b288 ffffca89`8568e280 ffff9080`26e7b290 00000000`00000000 ffff9080`26e7b298 00000000`00000000

Затем в rax загружается указатель на hvsocket!VmBusTlProviderEndpoint, в r14 наш IRP:

__WINDBG>!irp @r14__ Irp is active with 4 stacks 4 is current (= 0xffffdf08c0c5b408) >[IRP_MJ_CREATE(0), N/A(0)] 0 0 ffffdf08bf161920 ffffdf08c0eb5780 00000000-00000000 \Driver\AFD Args: ffffa700e57f35d0 03000020 00030000 00000039

Выполняется вызов этой функции. Выполняются проверки структуры, переданной в rax, затем для объекта ETHREAD вызывается ndis!NdisGetThreadObjectCompartmentId, затем vmbus!VmbusTlCreateEndpoint (второй параметр — указатель на объект EPROCESS), далее hvsocket!VmbusTlCreateObjectFromLookasideList, в которой вызывается nt!ExpInterlockedPopEntrySList.

Затем hvsocket!VmbusTlInitializeObject (вызывает nt!KeInitializeEvent и nt!KeInitializeSpinLock). Происходит возврат из hvsocket!VmbusTlCreateObjectFromLookasideList, выделяется блок памяти размером 200h, тег Vnpi. Далее обнуляется блок памяти размером 38h, вызываются nt!KeEnterCriticalRegion и nt!ExAcquireFastMutexUnsafe и выполняется регистрация hvsocket!VmbusTlEndpointActionWorkQueueRoutine через netio!NetioInitializeWorkQueue.

После происходит возврат из vmbus!VmbusTlCreateEndpoint, затем вызывается afd!AfdTLCreateEndpointComplete(PIRP IRP), четвертый параметр — vmbus!VmbusTlProviderEndpointDispatch, в структуру _AFD_CONNECTION записывается адрес hvsocket!VmbusTlCreateEndpoint. Затем вызывается afd!ObDereferenceSecurityDescriptor, идет проверка успешного результата — в зависимости от этого либо выполняется nt!iofCompleteRequest, либо нет, — и происходит возврат из hvsocket.sys в afd.sys.

Значение, возвращенное vmbus!VmbusTlProviderEndpoint, — 103h. Поэтому далее вызывается AfdTLPendRequest, если результат выполнения не 103h, но сразу будет вызов afd!AfdCompleteTLEndpCreate, и только после этого идет afd!AfdCompleteTLEndpCreate и nt!IofComplheteRequest. Происходит возврат из afd!AfdCreate (в rax все тот же 103h).

__WINDBG>dc 0xf6f368 — после выполнения NtCreateFile__ 00f6f368 00000000 00000000 00f6f498 00000003 ................ 00f6f378 00000144 00000000 00000000 00000022 D..........."... 00f6f388 00000000 00000144 c0140000 00000020 ....D....... ... 00f6f398 80000000 00000001 00000039 00000022 ........9..."...

0x144 — это тот самый handle, который в конечном итоге вернет функция socket и который будет передан функции bind в качестве первого параметра. Этот handle создается с помощью функции nt!ObpCreateHandle, вызываемой из функции nt!ObOpenObjectByNameEx (ранее при выполнении NtCreateFile):

__WINDBG>k__ Child-SP RetAddr Call Site ffffd201`d9b6e820 fffff801`a72630e9 nt!ObOpenObjectByNameEx+0x310 ffffd201`d9b6e960 fffff801`a7262cf9 nt!IopCreateFile+0x3d9 ffffd201`d9b6ea00 fffff801`a6fd4493 nt!NtCreateFile+0x79 ffffd201`d9b6ea90 00007ff9`e3f46b74 nt!KiSystemServiceCopyEnd+0x13 00000000`00e3e0a8 00000000`58f9ae28 ntdll!NtCreateFile+0x14

До выполнения функции в списке объектов:

__!handle 0 f ffffda8527121080__ 013c: Object: ffffca8986a9e3d0 GrantedAccess: 00020019 (Inherit) Entry: ffffca8985cb14f0 Object: ffffca8986a9e3d0 Type: (ffffda852717d0e0) Key ObjectHeader: ffffca8986a9e3a0 (new version) HandleCount: 1 PointerCount: 32759 Directory Object: 00000000 Name: \REGISTRY\MACHINE\SYSTEM\SYSTEM\CONTROLSET001\SERVICES\WINSOCK2\PARAMETERS\NAMESPACE_CATALOG5 0140: Object: ffffda85299be6f0 GrantedAccess: 001f0003 (Audit) Entry: ffffca8985cb1500 Object: ffffda85299be6f0 Type: (ffffda8527087650) Event ObjectHeader: ffffda85299be6c0 (new version) HandleCount: 1 PointerCount: 1 0144: free handle, Entry address ffffca8985cb1510, Next Entry ffffca8985cb1520 0148: free handle, Entry address ffffca8985cb1520, Next Entry ffffca8985cb1530

После выполнения функции появляется новая запись:

__!handle 0 f 0xffffe382ff528480 — ServerExample.exe process object__ 0144: Object: ffffda8529a9bcf0 GrantedAccess: 0016019f (Audit) Entry: ffffca8985cb1510 Object: ffffda8529a9bcf0 Type: (ffffda852718cb00) File ObjectHeader: ffffda8529a9bcc0 (new version) HandleCount: 1 PointerCount: 2 Directory Object: 00000000 Name: \Endpoint {Afd} 0148: free handle, Entry address ffffca8985cb1520, Next Entry ffffca8985cb1530

Инструкцией mov rax, [rpb-58h] в rax кладется значение дескриптора.

Возвращаемся в usermode. Далее идет вызов mswsock!SockGetInformation (из нее происходит вызов ntdll!NtDeviceIoControlFile, которой в качестве handle файла передается возвращенный ранее 0x144).

NTSTATUS WINAPI NtDeviceIoControlFile( _In_  HANDLE           FileHandle, - 144 (\Device\Afd\Endpoint). На клиенте будет \Device\Afd _In_  HANDLE           Event, - 140 (Event) _In_  PIO_APC_ROUTINE  ApcRoutine, 0 _In_  PVOID            ApcContext, 0 _Out_ PIO_STATUS_BLOCK IoStatusBlock, F6F2E0 _In_  ULONG            IoControlCode, 1207B - AfdDispatchImmediateIrp () _In_  PVOID            InputBuffer, F6F2FC _In_  ULONG            InputBufferLength,10 _Out_ PVOID            OutputBuffer, F6F2FC _In_  ULONG            OutputBufferLength 10 );

Input buffer:

__WINDBG>dc F6F2FC__ 00f6f2fc 00000007 00f6f38c c0140000 00f6f348 ............H...

IOCTL-код — 1207Bh (afd!AfdDispatchImmediateIrp), но она не выполнится, так как задействован механизм FastIO (подробности далее на примере send), выполнится afd!AfdFastIoDeviceControl, при этом IRP-пакет не формируется, usermode-буфер и его длина передаются как третий и четвертый параметр этой функции:

__WINDBG>r__ rcx — объект \Endpoint {Afd} r8=0000000000f6f2fc r9=0000000000000010

После выполнения функции (результат записывается в тот же буфер):

__WINDBG>dc F6F2FC__ 00f6f2fc 00000007 00000000 00010000 00000000 ................

Далее функция ntdll!NtDeviceIoControlFile вызывается повторно, в input-буфере данные, которые были возвращены после предыдущего выполнения. Но после повтора результат не изменился. Если бы функция вернула 103h, то вызвалась бы mswsock!SockWaitForSingleObject, затем ws2_32!WahInsertHandleContext, далее выход из mswsock!SockSocket и возврат в mswsock!WSPSocket. Вход в mswsock!SockSetHandleContext. Далее вызывается wshhyperv!WSHGetSocketInformation, затем nt!NtDeviceIoControlFile с IOCTL 12047h (AfdDispatchImmediateIrp).

В качестве input-буфера передается (размер буфера — D4):

__WINDBG>dc 006FED58 006FED58+D4__ 00f6f358 00000000 00000022 00000001 00000001 ...."........... 00f6f368 00000024 00000024 00000000 00000000 $...$........... 00f6f378 00000000 00010000 00010000 00001000 ................ 00f6f388 00000000 000003e9 00020026 00000008 ........&....... 00f6f398 00000000 00000000 00000000 00000000 ................ 00f6f3a8 00000000 00000000 00000000 00000000 ................ 00f6f3b8 00000000 00000000 1234191b 4ca74bf7 ..........4..K.L 00f6f3c8 d7dfe086 45542bc3 00000004 656b6361 .....+TE....acke 00f6f3d8 00000000 00000000 00000000 00000000 ................ 00f6f3e8 00000000 00000000 00000000 00000000 ................ 00f6f3f8 00000000 012247f0 00000000 00000000 .....G"......... 00f6f408 00000000 00000000 00000000 00000000 ................ 00f6f418 00000000 00000000 00000000 d2ffd7d3 ................ 00f6f428 00000022

Возвращается (изменений после выполнения не произошло):

__WINDBG>dc 00F6F400 00F6F400+24__ 00f6f400 00000000 00000000 00000000 00000000 ................ 00f6f410 00000000 00000000 00000000 00000000 ................ 00f6f420 00000000 d2ffd7d3 ........

Оттуда идет вызов ws2_32!WPUModifyIFSHandle (вызывается ws2_32!WahInsertHandleContext, идет работа с массивом ws2_32!SockPrimes). Выход из mswsock!SockSetHandleContext. Возврат в приложение.

 Bind

Функция socket завершилась успешно, дальше выполняется bind:

iResult = bind(ListenSocket, hints.ai_addr, (int)hints.ai_addrlen);

Дальше usermode так подробно, как для socket, разбирать мы не будем, отметим только те моменты, которые специфичны именно для сокетов Hyper-V. Вызывается mswsock!WSPbind, далее mswsock!WahReferenceContextByHandle (HANDLE socket, PVOID SockContextTable). Адрес процедуры, содержащийся в ebx, сравнивается с адресом начала mswsock_Tcpip4_WSHGetSockaddrType либо mswsock_Tcpip6_WSHGetSockaddrType. Если адрес совпадает, то вызывается соответствующая процедура, если нет, то выполняется call ebx (в нашем случае wshhyperv!WSHGetSockaddrType).


Тип сокета вычисляется на основании GUID’а, заданного во время выполнения bind (у нас задан HV_GUID_ZERO).


На сервере в esi:

__WINDBG>dc @esi__ 00f6f990 00000022 00000000 00000000 00000000 "............... — HV_GUID_ZERO 00f6f9a0 00000000 b1d00d3e 4570fe10 487662ad ....>.....pE.bvH 00f6f9b0 1b7a9d77

На клиенте:

__WINDBG>dc esi — при этом в esi virtual machine ID GUID -6a964317-1d87-4a74-abf9-46a69b048900 (GUID, который возвращается на сервере при выполнении командлета Get-VM | select ID, Name)__ 00affb2c 00000022 6a964317 4a741d87 a646f9ab "....C.j..tJ..F. 00affb3c 0089049b b1d00d3e 4570fe10 487662ad ....>.....pE.bvH 00affb4c 1b7a9d77 6a964317 4a741d87 a646f9ab w.z..C.j..tJ..F. ???????????? 00aff

РАССЫЛКА ПОСЛЕДНИХ НОВОСТЕЙ