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) и намекает на то, что разработчики смогут создавать свои решения на базе новой технологии виртуализации.
Предыдущее исследование 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
Источник: xakep.ru